From ee0d99607c14cb63d3ebdb3a970b508949fa8219 Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Fri, 22 Nov 2024 18:41:30 +0900 Subject: Rename 'digikam' folder to 'src' Signed-off-by: Michele Calgaro --- src/CMakeL10n.txt | 6 + src/COPYING-DOCS | 397 + src/DBSCHEMA.ODS | Bin 0 -> 15850 bytes src/DESIGN | 18 + src/Makefile.am | 25 + src/NEWS.0.9.0 | 397 + src/NEWS.0.9.1 | 167 + src/NEWS.0.9.2 | 158 + src/NEWS.0.9.3 | 142 + src/NEWS.0.9.4 | 236 + src/NEWS.0.9.5 | 103 + src/configure.in.bot | 133 + src/configure.in.in | 387 + src/digikam/Makefile.am | 170 + src/digikam/album.cpp | 543 + src/digikam/album.h | 455 + src/digikam/albumdb.cpp | 1599 + src/digikam/albumdb.h | 615 + src/digikam/albumdb_sqlite2.cpp | 144 + src/digikam/albumdb_sqlite2.h | 85 + src/digikam/albumfiletip.cpp | 588 + src/digikam/albumfiletip.h | 72 + src/digikam/albumfolderview.cpp | 1636 + src/digikam/albumfolderview.h | 132 + src/digikam/albumhistory.cpp | 337 + src/digikam/albumhistory.h | 83 + src/digikam/albumicongroupitem.cpp | 168 + src/digikam/albumicongroupitem.h | 59 + src/digikam/albumiconitem.cpp | 375 + src/digikam/albumiconitem.h | 76 + src/digikam/albumiconview.cpp | 2341 + src/digikam/albumiconview.h | 217 + src/digikam/albumiconviewfilter.cpp | 208 + src/digikam/albumiconviewfilter.h | 78 + src/digikam/albuminfo.h | 112 + src/digikam/albumitemhandler.cpp | 45 + src/digikam/albumitemhandler.h | 60 + src/digikam/albumlister.cpp | 640 + src/digikam/albumlister.h | 159 + src/digikam/albummanager.cpp | 1678 + src/digikam/albummanager.h | 472 + src/digikam/albumpropsedit.cpp | 376 + src/digikam/albumpropsedit.h | 88 + src/digikam/albumsettings.cpp | 1142 + src/digikam/albumsettings.h | 283 + src/digikam/albumthumbnailloader.cpp | 491 + src/digikam/albumthumbnailloader.h | 178 + src/digikam/albumwidgetstack.cpp | 282 + src/digikam/albumwidgetstack.h | 116 + src/digikam/cameralist.cpp | 276 + src/digikam/cameralist.h | 84 + src/digikam/cameratype.cpp | 191 + src/digikam/cameratype.h | 81 + src/digikam/constants.h | 37 + src/digikam/daboutdata.h | 293 + src/digikam/datefolderview.cpp | 482 + src/digikam/datefolderview.h | 88 + src/digikam/dcopiface.cpp | 52 + src/digikam/dcopiface.h | 96 + src/digikam/digikam.desktop | 159 + src/digikam/digikam_export.h | 45 + src/digikam/digikamapp.cpp | 2094 + src/digikam/digikamapp.h | 181 + src/digikam/digikamappprivate.h | 269 + src/digikam/digikamfirstrun.cpp | 183 + src/digikam/digikamfirstrun.h | 66 + src/digikam/digikamui.rc | 147 + src/digikam/digikamview.cpp | 1570 + src/digikam/digikamview.h | 194 + src/digikam/dio.cpp | 262 + src/digikam/dio.h | 54 + src/digikam/dio_p.h | 56 + src/digikam/dragobjects.cpp | 374 + src/digikam/dragobjects.h | 205 + src/digikam/firstrun.cpp | 100 + src/digikam/firstrun.h | 68 + src/digikam/folderitem.cpp | 269 + src/digikam/folderitem.h | 91 + src/digikam/folderview.cpp | 510 + src/digikam/folderview.h | 136 + src/digikam/icongroupitem.cpp | 307 + src/digikam/icongroupitem.h | 91 + src/digikam/iconitem.cpp | 171 + src/digikam/iconitem.h | 87 + src/digikam/iconview.cpp | 1969 + src/digikam/iconview.h | 170 + src/digikam/imageattributeswatch.cpp | 89 + src/digikam/imageattributeswatch.h | 97 + src/digikam/imageinfo.cpp | 347 + src/digikam/imageinfo.h | 280 + src/digikam/imagepreviewview.cpp | 686 + src/digikam/imagepreviewview.h | 115 + src/digikam/kdateedit.cpp | 378 + src/digikam/kdateedit.h | 150 + src/digikam/kdatepickerpopup.cpp | 160 + src/digikam/kdatepickerpopup.h | 129 + src/digikam/kdatetimeedit.cpp | 74 + src/digikam/kdatetimeedit.h | 98 + src/digikam/kipiinterface.cpp | 724 + src/digikam/kipiinterface.h | 187 + src/digikam/main.cpp | 138 + src/digikam/mediaplayerview.cpp | 252 + src/digikam/mediaplayerview.h | 72 + src/digikam/metadatahub.cpp | 858 + src/digikam/metadatahub.h | 362 + src/digikam/mimefilter.cpp | 88 + src/digikam/mimefilter.h | 70 + src/digikam/monthwidget.cpp | 423 + src/digikam/monthwidget.h | 76 + src/digikam/pixmapmanager.cpp | 252 + src/digikam/pixmapmanager.h | 82 + src/digikam/ratingfilter.cpp | 205 + src/digikam/ratingfilter.h | 75 + src/digikam/ratingpopupmenu.cpp | 82 + src/digikam/ratingpopupmenu.h | 49 + src/digikam/ratingwidget.cpp | 195 + src/digikam/ratingwidget.h | 72 + src/digikam/scanlib.cpp | 541 + src/digikam/scanlib.h | 172 + src/digikam/searchadvanceddialog.cpp | 664 + src/digikam/searchadvanceddialog.h | 90 + src/digikam/searchfolderview.cpp | 488 + src/digikam/searchfolderview.h | 87 + src/digikam/searchquickdialog.cpp | 200 + src/digikam/searchquickdialog.h | 87 + src/digikam/searchresultsitem.cpp | 87 + src/digikam/searchresultsitem.h | 57 + src/digikam/searchresultsview.cpp | 211 + src/digikam/searchresultsview.h | 82 + src/digikam/searchwidgets.cpp | 602 + src/digikam/searchwidgets.h | 391 + src/digikam/syncjob.cpp | 288 + src/digikam/syncjob.h | 108 + src/digikam/tageditdlg.cpp | 406 + src/digikam/tageditdlg.h | 85 + src/digikam/tagfilterview.cpp | 1429 + src/digikam/tagfilterview.h | 118 + src/digikam/tagfolderview.cpp | 1041 + src/digikam/tagfolderview.h | 107 + src/digikam/tagspopupmenu.cpp | 366 + src/digikam/tagspopupmenu.h | 78 + src/digikam/thumbnailsize.h | 90 + src/digikam/timelinefolderview.cpp | 308 + src/digikam/timelinefolderview.h | 81 + src/digikam/timelineview.cpp | 645 + src/digikam/timelineview.h | 86 + src/digikam/timelinewidget.cpp | 1718 + src/digikam/timelinewidget.h | 154 + src/digikam/upgradedb_sqlite2tosqlite3.cpp | 609 + src/digikam/upgradedb_sqlite2tosqlite3.h | 38 + src/digikam/welcomepageview.cpp | 193 + src/digikam/welcomepageview.h | 64 + src/imageplugins/Makefile.am | 5 + src/imageplugins/adjustcurves/Makefile.am | 33 + src/imageplugins/adjustcurves/adjustcurves.cpp | 677 + src/imageplugins/adjustcurves/adjustcurves.h | 143 + src/imageplugins/adjustcurves/adjustcurvestool.cpp | 659 + src/imageplugins/adjustcurves/adjustcurvestool.h | 145 + .../digikamimageplugin_adjustcurves.desktop | 52 + .../digikamimageplugin_adjustcurves_ui.rc | 20 + .../adjustcurves/imageplugin_adjustcurves.cpp | 70 + .../adjustcurves/imageplugin_adjustcurves.h | 56 + src/imageplugins/adjustlevels/Makefile.am | 34 + src/imageplugins/adjustlevels/adjustlevels.cpp | 913 + src/imageplugins/adjustlevels/adjustlevels.h | 153 + src/imageplugins/adjustlevels/adjustlevelstool.cpp | 901 + src/imageplugins/adjustlevels/adjustlevelstool.h | 160 + .../digikamimageplugin_adjustlevels.desktop | 52 + .../digikamimageplugin_adjustlevels_ui.rc | 20 + .../adjustlevels/imageplugin_adjustlevels.cpp | 71 + .../adjustlevels/imageplugin_adjustlevels.h | 56 + src/imageplugins/antivignetting/Makefile.am | 34 + src/imageplugins/antivignetting/antivignetting.cpp | 149 + src/imageplugins/antivignetting/antivignetting.h | 62 + .../antivignetting/antivignettingtool.cpp | 378 + .../antivignetting/antivignettingtool.h | 92 + .../digikamimageplugin_antivignetting.desktop | 50 + .../digikamimageplugin_antivignetting_ui.rc | 20 + .../antivignetting/imageeffect_antivignetting.cpp | 380 + .../antivignetting/imageeffect_antivignetting.h | 79 + .../antivignetting/imageplugin_antivignetting.cpp | 70 + .../antivignetting/imageplugin_antivignetting.h | 57 + src/imageplugins/blurfx/Makefile.am | 34 + src/imageplugins/blurfx/blurfx.cpp | 1445 + src/imageplugins/blurfx/blurfx.h | 191 + src/imageplugins/blurfx/blurfxtool.cpp | 402 + src/imageplugins/blurfx/blurfxtool.h | 93 + .../blurfx/digikamimageplugin_blurfx.desktop | 49 + .../blurfx/digikamimageplugin_blurfx_ui.rc | 20 + src/imageplugins/blurfx/imageeffect_blurfx.cpp | 388 + src/imageplugins/blurfx/imageeffect_blurfx.h | 80 + src/imageplugins/blurfx/imageplugin_blurfx.cpp | 70 + src/imageplugins/blurfx/imageplugin_blurfx.h | 56 + src/imageplugins/border/Makefile.am | 34 + src/imageplugins/border/border.cpp | 393 + src/imageplugins/border/border.h | 151 + src/imageplugins/border/bordertool.cpp | 668 + src/imageplugins/border/bordertool.h | 123 + .../border/digikamimageplugin_border.desktop | 50 + .../border/digikamimageplugin_border_ui.rc | 20 + src/imageplugins/border/imageeffect_border.cpp | 667 + src/imageplugins/border/imageeffect_border.h | 109 + src/imageplugins/border/imageplugin_border.cpp | 71 + src/imageplugins/border/imageplugin_border.h | 57 + src/imageplugins/border/patterns/Makefile.am | 5 + src/imageplugins/border/patterns/chalk-pattern.png | Bin 0 -> 18994 bytes .../border/patterns/craters-pattern.png | Bin 0 -> 17350 bytes src/imageplugins/border/patterns/dried-pattern.png | Bin 0 -> 20634 bytes .../border/patterns/granit-pattern.png | Bin 0 -> 20766 bytes src/imageplugins/border/patterns/ice-pattern.png | Bin 0 -> 18371 bytes src/imageplugins/border/patterns/leaf-pattern.png | Bin 0 -> 63395 bytes .../border/patterns/marble-pattern.png | Bin 0 -> 14715 bytes src/imageplugins/border/patterns/paper-pattern.png | Bin 0 -> 21785 bytes .../border/patterns/parque-pattern.png | Bin 0 -> 9835 bytes src/imageplugins/border/patterns/pine-pattern.png | Bin 0 -> 2868 bytes src/imageplugins/border/patterns/pink-pattern.png | Bin 0 -> 14947 bytes src/imageplugins/border/patterns/rain-pattern.png | Bin 0 -> 15876 bytes src/imageplugins/border/patterns/rock-pattern.png | Bin 0 -> 39841 bytes src/imageplugins/border/patterns/stone-pattern.png | Bin 0 -> 57784 bytes src/imageplugins/border/patterns/wall-pattern.png | Bin 0 -> 19335 bytes src/imageplugins/border/patterns/wood-pattern.png | Bin 0 -> 11893 bytes src/imageplugins/channelmixer/Makefile.am | 34 + src/imageplugins/channelmixer/channelmixer.cpp | 784 + src/imageplugins/channelmixer/channelmixer.h | 133 + src/imageplugins/channelmixer/channelmixertool.cpp | 774 + src/imageplugins/channelmixer/channelmixertool.h | 138 + .../digikamimageplugin_channelmixer.desktop | 51 + .../digikamimageplugin_channelmixer_ui.rc | 20 + .../channelmixer/imageplugin_channelmixer.cpp | 71 + .../channelmixer/imageplugin_channelmixer.h | 56 + src/imageplugins/charcoal/Makefile.am | 34 + src/imageplugins/charcoal/charcoal.cpp | 249 + src/imageplugins/charcoal/charcoal.h | 56 + src/imageplugins/charcoal/charcoaltool.cpp | 202 + src/imageplugins/charcoal/charcoaltool.h | 82 + .../charcoal/digikamimageplugin_charcoal.desktop | 50 + .../charcoal/digikamimageplugin_charcoal_ui.rc | 20 + src/imageplugins/charcoal/imageeffect_charcoal.cpp | 193 + src/imageplugins/charcoal/imageeffect_charcoal.h | 69 + src/imageplugins/charcoal/imageplugin_charcoal.cpp | 72 + src/imageplugins/charcoal/imageplugin_charcoal.h | 57 + src/imageplugins/colorfx/Makefile.am | 34 + src/imageplugins/colorfx/colorfxtool.cpp | 699 + src/imageplugins/colorfx/colorfxtool.h | 136 + .../colorfx/digikamimageplugin_colorfx.desktop | 40 + .../colorfx/digikamimageplugin_colorfx_ui.rc | 20 + src/imageplugins/colorfx/imageeffect_colorfx.cpp | 690 + src/imageplugins/colorfx/imageeffect_colorfx.h | 131 + src/imageplugins/colorfx/imageplugin_colorfx.cpp | 73 + src/imageplugins/colorfx/imageplugin_colorfx.h | 57 + src/imageplugins/coreplugin/Makefile.am | 52 + src/imageplugins/coreplugin/autocorrectiontool.cpp | 438 + src/imageplugins/coreplugin/autocorrectiontool.h | 128 + src/imageplugins/coreplugin/bcgtool.cpp | 366 + src/imageplugins/coreplugin/bcgtool.h | 115 + src/imageplugins/coreplugin/blurtool.cpp | 169 + src/imageplugins/coreplugin/blurtool.h | 81 + src/imageplugins/coreplugin/bwsepiatool.cpp | 1177 + src/imageplugins/coreplugin/bwsepiatool.h | 193 + .../coreplugin/digikamimageplugin_core.desktop | 47 + .../coreplugin/digikamimageplugin_core_ui.rc | 56 + src/imageplugins/coreplugin/hsl/Makefile.am | 26 + src/imageplugins/coreplugin/hsl/hsltool.cpp | 453 + src/imageplugins/coreplugin/hsl/hsltool.h | 126 + .../coreplugin/hsl/hspreviewwidget.cpp | 126 + src/imageplugins/coreplugin/hsl/hspreviewwidget.h | 64 + .../coreplugin/hsl/imageeffect_hsl.cpp | 428 + src/imageplugins/coreplugin/hsl/imageeffect_hsl.h | 116 + src/imageplugins/coreplugin/iccprooftool.cpp | 1310 + src/imageplugins/coreplugin/iccprooftool.h | 209 + .../coreplugin/imageeffect_autocorrection.cpp | 431 + .../coreplugin/imageeffect_autocorrection.h | 128 + src/imageplugins/coreplugin/imageeffect_bcg.cpp | 350 + src/imageplugins/coreplugin/imageeffect_bcg.h | 110 + src/imageplugins/coreplugin/imageeffect_blur.cpp | 147 + src/imageplugins/coreplugin/imageeffect_blur.h | 68 + .../coreplugin/imageeffect_bwsepia.cpp | 1183 + src/imageplugins/coreplugin/imageeffect_bwsepia.h | 195 + .../coreplugin/imageeffect_iccproof.cpp | 1284 + src/imageplugins/coreplugin/imageeffect_iccproof.h | 204 + src/imageplugins/coreplugin/imageeffect_redeye.cpp | 574 + src/imageplugins/coreplugin/imageeffect_redeye.h | 153 + src/imageplugins/coreplugin/imageeffect_rgb.cpp | 419 + src/imageplugins/coreplugin/imageeffect_rgb.h | 113 + src/imageplugins/coreplugin/imageplugin_core.cpp | 295 + src/imageplugins/coreplugin/imageplugin_core.h | 85 + src/imageplugins/coreplugin/ratiocrop/Makefile.am | 26 + .../coreplugin/ratiocrop/imageeffect_ratiocrop.cpp | 799 + .../coreplugin/ratiocrop/imageeffect_ratiocrop.h | 132 + .../coreplugin/ratiocrop/imageselectionwidget.cpp | 1422 + .../coreplugin/ratiocrop/imageselectionwidget.h | 176 + .../coreplugin/ratiocrop/ratiocroptool.cpp | 853 + .../coreplugin/ratiocrop/ratiocroptool.h | 135 + src/imageplugins/coreplugin/redeyetool.cpp | 587 + src/imageplugins/coreplugin/redeyetool.h | 157 + src/imageplugins/coreplugin/rgbtool.cpp | 440 + src/imageplugins/coreplugin/rgbtool.h | 119 + .../coreplugin/sharpnesseditor/Makefile.am | 32 + .../coreplugin/sharpnesseditor/clapack/LICENCE | 12 + .../coreplugin/sharpnesseditor/clapack/Makefile.am | 7 + .../coreplugin/sharpnesseditor/clapack/README | 2 + .../coreplugin/sharpnesseditor/clapack/abort_.c | 10 + .../coreplugin/sharpnesseditor/clapack/blaswrap.h | 158 + .../coreplugin/sharpnesseditor/clapack/clapack.h | 5079 ++ .../coreplugin/sharpnesseditor/clapack/close.c | 80 + .../coreplugin/sharpnesseditor/clapack/dgemm.c | 313 + .../coreplugin/sharpnesseditor/clapack/dger.c | 143 + .../coreplugin/sharpnesseditor/clapack/dgesv.c | 117 + .../coreplugin/sharpnesseditor/clapack/dgetf2.c | 157 + .../coreplugin/sharpnesseditor/clapack/dgetrf.c | 197 + .../coreplugin/sharpnesseditor/clapack/dgetrs.c | 159 + .../coreplugin/sharpnesseditor/clapack/dlaswp.c | 143 + .../coreplugin/sharpnesseditor/clapack/dscal.c | 62 + .../coreplugin/sharpnesseditor/clapack/dswap.c | 81 + .../coreplugin/sharpnesseditor/clapack/dtrsm.c | 404 + .../coreplugin/sharpnesseditor/clapack/endfile.c | 102 + .../coreplugin/sharpnesseditor/clapack/err.c | 240 + .../coreplugin/sharpnesseditor/clapack/f2c.h | 223 + .../coreplugin/sharpnesseditor/clapack/fio.h | 96 + .../coreplugin/sharpnesseditor/clapack/fmt.c | 470 + .../coreplugin/sharpnesseditor/clapack/fmt.h | 86 + .../coreplugin/sharpnesseditor/clapack/fmtlib.c | 40 + .../coreplugin/sharpnesseditor/clapack/fp.h | 28 + .../coreplugin/sharpnesseditor/clapack/idamax.c | 61 + .../coreplugin/sharpnesseditor/clapack/ieeeck.c | 150 + .../coreplugin/sharpnesseditor/clapack/ilaenv.c | 610 + .../coreplugin/sharpnesseditor/clapack/lsame.c | 101 + .../coreplugin/sharpnesseditor/clapack/open.c | 256 + .../coreplugin/sharpnesseditor/clapack/s_cmp.c | 40 + .../coreplugin/sharpnesseditor/clapack/s_copy.c | 47 + .../coreplugin/sharpnesseditor/clapack/s_stop.c | 37 + .../coreplugin/sharpnesseditor/clapack/sfe.c | 29 + .../coreplugin/sharpnesseditor/clapack/sig_die.c | 41 + .../coreplugin/sharpnesseditor/clapack/util.c | 39 + .../coreplugin/sharpnesseditor/clapack/wref.c | 266 + .../coreplugin/sharpnesseditor/clapack/wrtfmt.c | 321 + .../coreplugin/sharpnesseditor/clapack/wsfe.c | 69 + .../coreplugin/sharpnesseditor/clapack/xerbla.c | 58 + .../sharpnesseditor/imageeffect_sharpen.cpp | 696 + .../sharpnesseditor/imageeffect_sharpen.h | 102 + .../coreplugin/sharpnesseditor/matrix.cpp | 663 + .../coreplugin/sharpnesseditor/matrix.h | 129 + .../coreplugin/sharpnesseditor/refocus.cpp | 199 + .../coreplugin/sharpnesseditor/refocus.h | 67 + .../coreplugin/sharpnesseditor/sharpentool.cpp | 741 + .../coreplugin/sharpnesseditor/sharpentool.h | 111 + .../coreplugin/sharpnesseditor/unsharp.cpp | 127 + .../coreplugin/sharpnesseditor/unsharp.h | 58 + src/imageplugins/distortionfx/Makefile.am | 34 + .../digikamimageplugin_distortionfx.desktop | 50 + .../digikamimageplugin_distortionfx_ui.rc | 20 + src/imageplugins/distortionfx/distortionfx.cpp | 869 + src/imageplugins/distortionfx/distortionfx.h | 149 + src/imageplugins/distortionfx/distortionfxtool.cpp | 398 + src/imageplugins/distortionfx/distortionfxtool.h | 96 + .../distortionfx/imageeffect_distortionfx.cpp | 378 + .../distortionfx/imageeffect_distortionfx.h | 82 + .../distortionfx/imageplugin_distortionfx.cpp | 72 + .../distortionfx/imageplugin_distortionfx.h | 59 + src/imageplugins/emboss/Makefile.am | 34 + .../emboss/digikamimageplugin_emboss.desktop | 51 + .../emboss/digikamimageplugin_emboss_ui.rc | 20 + src/imageplugins/emboss/emboss.cpp | 127 + src/imageplugins/emboss/emboss.h | 75 + src/imageplugins/emboss/embosstool.cpp | 167 + src/imageplugins/emboss/embosstool.h | 82 + src/imageplugins/emboss/imageeffect_emboss.cpp | 170 + src/imageplugins/emboss/imageeffect_emboss.h | 69 + src/imageplugins/emboss/imageplugin_emboss.cpp | 72 + src/imageplugins/emboss/imageplugin_emboss.h | 57 + src/imageplugins/filmgrain/Makefile.am | 34 + .../filmgrain/digikamimageplugin_filmgrain.desktop | 52 + .../filmgrain/digikamimageplugin_filmgrain_ui.rc | 20 + src/imageplugins/filmgrain/filmgrain.cpp | 202 + src/imageplugins/filmgrain/filmgrain.h | 58 + src/imageplugins/filmgrain/filmgraintool.cpp | 195 + src/imageplugins/filmgrain/filmgraintool.h | 83 + .../filmgrain/imageeffect_filmgrain.cpp | 195 + src/imageplugins/filmgrain/imageeffect_filmgrain.h | 74 + .../filmgrain/imageplugin_filmgrain.cpp | 71 + src/imageplugins/filmgrain/imageplugin_filmgrain.h | 57 + src/imageplugins/freerotation/Makefile.am | 34 + .../digikamimageplugin_freerotation.desktop | 51 + .../digikamimageplugin_freerotation_ui.rc | 20 + src/imageplugins/freerotation/freerotation.cpp | 263 + src/imageplugins/freerotation/freerotation.h | 94 + src/imageplugins/freerotation/freerotationtool.cpp | 318 + src/imageplugins/freerotation/freerotationtool.h | 97 + .../freerotation/imageeffect_freerotation.cpp | 308 + .../freerotation/imageeffect_freerotation.h | 83 + .../freerotation/imageplugin_freerotation.cpp | 70 + .../freerotation/imageplugin_freerotation.h | 57 + src/imageplugins/hotpixels/Makefile.am | 36 + src/imageplugins/hotpixels/TODO | 4 + src/imageplugins/hotpixels/blackframelistview.cpp | 176 + src/imageplugins/hotpixels/blackframelistview.h | 127 + src/imageplugins/hotpixels/blackframeparser.cpp | 211 + src/imageplugins/hotpixels/blackframeparser.h | 102 + .../hotpixels/digikamimageplugin_hotpixels.desktop | 50 + .../hotpixels/digikamimageplugin_hotpixels_ui.rc | 20 + src/imageplugins/hotpixels/hotpixel.h | 74 + src/imageplugins/hotpixels/hotpixelfixer.cpp | 302 + src/imageplugins/hotpixels/hotpixelfixer.h | 98 + src/imageplugins/hotpixels/hotpixelstool.cpp | 276 + src/imageplugins/hotpixels/hotpixelstool.h | 113 + .../hotpixels/imageeffect_hotpixels.cpp | 279 + src/imageplugins/hotpixels/imageeffect_hotpixels.h | 104 + .../hotpixels/imageplugin_hotpixels.cpp | 72 + src/imageplugins/hotpixels/imageplugin_hotpixels.h | 56 + src/imageplugins/hotpixels/weights.cpp | 283 + src/imageplugins/hotpixels/weights.h | 90 + src/imageplugins/infrared/Makefile.am | 34 + .../infrared/digikamimageplugin_infrared.desktop | 52 + .../infrared/digikamimageplugin_infrared_ui.rc | 19 + src/imageplugins/infrared/imageeffect_infrared.cpp | 227 + src/imageplugins/infrared/imageeffect_infrared.h | 76 + src/imageplugins/infrared/imageplugin_infrared.cpp | 71 + src/imageplugins/infrared/imageplugin_infrared.h | 57 + src/imageplugins/infrared/infrared.cpp | 367 + src/imageplugins/infrared/infrared.h | 60 + src/imageplugins/infrared/infraredtool.cpp | 225 + src/imageplugins/infrared/infraredtool.h | 86 + src/imageplugins/inpainting/Makefile.am | 34 + .../digikamimageplugin_inpainting.desktop | 49 + .../inpainting/digikamimageplugin_inpainting_ui.rc | 20 + .../inpainting/imageeffect_inpainting.cpp | 466 + .../inpainting/imageeffect_inpainting.h | 116 + .../inpainting/imageplugin_inpainting.cpp | 97 + .../inpainting/imageplugin_inpainting.h | 57 + src/imageplugins/inpainting/inpaintingtool.cpp | 430 + src/imageplugins/inpainting/inpaintingtool.h | 133 + src/imageplugins/inserttext/Makefile.am | 34 + .../digikamimageplugin_inserttext.desktop | 50 + .../inserttext/digikamimageplugin_inserttext_ui.rc | 20 + src/imageplugins/inserttext/fontchooserwidget.cpp | 738 + src/imageplugins/inserttext/fontchooserwidget.h | 172 + .../inserttext/imageeffect_inserttext.cpp | 348 + .../inserttext/imageeffect_inserttext.h | 103 + .../inserttext/imageplugin_inserttext.cpp | 70 + .../inserttext/imageplugin_inserttext.h | 56 + src/imageplugins/inserttext/inserttexttool.cpp | 336 + src/imageplugins/inserttext/inserttexttool.h | 111 + src/imageplugins/inserttext/inserttextwidget.cpp | 622 + src/imageplugins/inserttext/inserttextwidget.h | 156 + src/imageplugins/lensdistortion/Makefile.am | 34 + .../digikamimageplugin_lensdistortion.desktop | 50 + .../digikamimageplugin_lensdistortion_ui.rc | 20 + .../lensdistortion/imageeffect_lensdistortion.cpp | 317 + .../lensdistortion/imageeffect_lensdistortion.h | 82 + .../lensdistortion/imageplugin_lensdistortion.cpp | 69 + .../lensdistortion/imageplugin_lensdistortion.h | 56 + src/imageplugins/lensdistortion/lensdistortion.cpp | 135 + src/imageplugins/lensdistortion/lensdistortion.h | 63 + .../lensdistortion/lensdistortiontool.cpp | 326 + .../lensdistortion/lensdistortiontool.h | 92 + src/imageplugins/lensdistortion/pixelaccess.cpp | 314 + src/imageplugins/lensdistortion/pixelaccess.h | 93 + src/imageplugins/noisereduction/Makefile.am | 34 + .../digikamimageplugin_noisereduction.desktop | 53 + .../digikamimageplugin_noisereduction_ui.rc | 20 + .../noisereduction/imageeffect_noisereduction.cpp | 553 + .../noisereduction/imageeffect_noisereduction.h | 82 + .../noisereduction/imageplugin_noisereduction.cpp | 70 + .../noisereduction/imageplugin_noisereduction.h | 56 + src/imageplugins/noisereduction/noisereduction.cpp | 809 + src/imageplugins/noisereduction/noisereduction.h | 257 + .../noisereduction/noisereductiontool.cpp | 536 + .../noisereduction/noisereductiontool.h | 92 + src/imageplugins/oilpaint/Makefile.am | 34 + .../oilpaint/digikamimageplugin_oilpaint.desktop | 51 + .../oilpaint/digikamimageplugin_oilpaint_ui.rc | 20 + src/imageplugins/oilpaint/imageeffect_oilpaint.cpp | 201 + src/imageplugins/oilpaint/imageeffect_oilpaint.h | 69 + src/imageplugins/oilpaint/imageplugin_oilpaint.cpp | 70 + src/imageplugins/oilpaint/imageplugin_oilpaint.h | 56 + src/imageplugins/oilpaint/oilpaint.cpp | 203 + src/imageplugins/oilpaint/oilpaint.h | 72 + src/imageplugins/oilpaint/oilpainttool.cpp | 208 + src/imageplugins/oilpaint/oilpainttool.h | 82 + src/imageplugins/perspective/Makefile.am | 35 + .../digikamimageplugin_perspective.desktop | 52 + .../digikamimageplugin_perspective_ui.rc | 20 + .../perspective/imageeffect_perspective.cpp | 252 + .../perspective/imageeffect_perspective.h | 89 + .../perspective/imageplugin_perspective.cpp | 69 + .../perspective/imageplugin_perspective.h | 56 + src/imageplugins/perspective/matrix.cpp | 177 + src/imageplugins/perspective/matrix.h | 106 + src/imageplugins/perspective/perspectivetool.cpp | 244 + src/imageplugins/perspective/perspectivetool.h | 100 + src/imageplugins/perspective/perspectivewidget.cpp | 839 + src/imageplugins/perspective/perspectivewidget.h | 172 + src/imageplugins/perspective/triangle.cpp | 65 + src/imageplugins/perspective/triangle.h | 57 + src/imageplugins/raindrop/Makefile.am | 34 + .../raindrop/digikamimageplugin_raindrop.desktop | 52 + .../raindrop/digikamimageplugin_raindrop_ui.rc | 20 + src/imageplugins/raindrop/imageeffect_raindrop.cpp | 259 + src/imageplugins/raindrop/imageeffect_raindrop.h | 70 + src/imageplugins/raindrop/imageplugin_raindrop.cpp | 69 + src/imageplugins/raindrop/imageplugin_raindrop.h | 56 + src/imageplugins/raindrop/raindrop.cpp | 457 + src/imageplugins/raindrop/raindrop.h | 105 + src/imageplugins/raindrop/raindroptool.cpp | 254 + src/imageplugins/raindrop/raindroptool.h | 82 + src/imageplugins/restoration/Makefile.am | 35 + .../digikamimageplugin_restoration.desktop | 52 + .../digikamimageplugin_restoration_ui.rc | 20 + .../restoration/imageeffect_restoration.cpp | 349 + .../restoration/imageeffect_restoration.h | 94 + .../restoration/imageplugin_restoration.cpp | 71 + .../restoration/imageplugin_restoration.h | 57 + src/imageplugins/restoration/restorationtool.cpp | 356 + src/imageplugins/restoration/restorationtool.h | 100 + src/imageplugins/sheartool/Makefile.am | 33 + .../sheartool/digikamimageplugin_sheartool.desktop | 53 + .../sheartool/digikamimageplugin_sheartool_ui.rc | 20 + .../sheartool/imageeffect_sheartool.cpp | 322 + src/imageplugins/sheartool/imageeffect_sheartool.h | 82 + .../sheartool/imageplugin_sheartool.cpp | 69 + src/imageplugins/sheartool/imageplugin_sheartool.h | 56 + src/imageplugins/sheartool/shear.cpp | 185 + src/imageplugins/sheartool/shear.h | 84 + src/imageplugins/sheartool/sheartool.cpp | 331 + src/imageplugins/sheartool/sheartool.h | 96 + src/imageplugins/superimpose/Makefile.am | 35 + .../digikamimageplugin_superimpose.desktop | 52 + .../digikamimageplugin_superimpose_ui.rc | 20 + src/imageplugins/superimpose/dirselectwidget.cpp | 182 + src/imageplugins/superimpose/dirselectwidget.h | 78 + .../superimpose/imageeffect_superimpose.cpp | 280 + .../superimpose/imageeffect_superimpose.h | 87 + .../superimpose/imageplugin_superimpose.cpp | 71 + .../superimpose/imageplugin_superimpose.h | 58 + src/imageplugins/superimpose/superimpose.cpp | 70 + src/imageplugins/superimpose/superimpose.h | 67 + src/imageplugins/superimpose/superimposetool.cpp | 271 + src/imageplugins/superimpose/superimposetool.h | 90 + src/imageplugins/superimpose/superimposewidget.cpp | 329 + src/imageplugins/superimpose/superimposewidget.h | 118 + src/imageplugins/texture/Makefile.am | 35 + .../texture/digikamimageplugin_texture.desktop | 52 + .../texture/digikamimageplugin_texture_ui.rc | 20 + src/imageplugins/texture/imageeffect_texture.cpp | 291 + src/imageplugins/texture/imageeffect_texture.h | 100 + src/imageplugins/texture/imageplugin_texture.cpp | 70 + src/imageplugins/texture/imageplugin_texture.h | 56 + src/imageplugins/texture/patterns/Makefile.am | 5 + .../texture/patterns/bluejean-texture.png | Bin 0 -> 18748 bytes .../texture/patterns/bricks-texture.png | Bin 0 -> 7888 bytes .../texture/patterns/bricks2-texture.png | Bin 0 -> 8867 bytes .../texture/patterns/burlap-texture.png | Bin 0 -> 7960 bytes .../texture/patterns/canvas-texture.png | Bin 0 -> 33037 bytes .../texture/patterns/cellwood-texture.png | Bin 0 -> 55637 bytes .../texture/patterns/fabric-texture.png | Bin 0 -> 14189 bytes .../texture/patterns/marble-texture.png | Bin 0 -> 19218 bytes .../texture/patterns/marble2-texture.png | Bin 0 -> 6551 bytes .../texture/patterns/metalwire-texture.png | Bin 0 -> 90343 bytes .../texture/patterns/modern-texture.png | Bin 0 -> 85181 bytes src/imageplugins/texture/patterns/moss-texture.png | Bin 0 -> 19894 bytes .../texture/patterns/paper-texture.png | Bin 0 -> 4435 bytes .../texture/patterns/paper2-texture.png | Bin 0 -> 182714 bytes .../texture/patterns/stone-texture.png | Bin 0 -> 13774 bytes src/imageplugins/texture/patterns/wall-texture.png | Bin 0 -> 69570 bytes src/imageplugins/texture/texture.cpp | 181 + src/imageplugins/texture/texture.h | 62 + src/imageplugins/texture/texturetool.cpp | 294 + src/imageplugins/texture/texturetool.h | 112 + src/imageplugins/whitebalance/Makefile.am | 33 + .../digikamimageplugin_whitebalance.desktop | 53 + .../digikamimageplugin_whitebalance_ui.rc | 20 + .../whitebalance/imageeffect_whitebalance.cpp | 842 + .../whitebalance/imageeffect_whitebalance.h | 168 + .../whitebalance/imageplugin_whitebalance.cpp | 71 + .../whitebalance/imageplugin_whitebalance.h | 57 + src/imageplugins/whitebalance/whitebalancetool.cpp | 850 + src/imageplugins/whitebalance/whitebalancetool.h | 174 + src/libs/Makefile.am | 9 + src/libs/curves/Makefile.am | 16 + src/libs/curves/imagecurves.cpp | 768 + src/libs/curves/imagecurves.h | 111 + src/libs/dialogs/Makefile.am | 33 + src/libs/dialogs/ctrlpaneldlg.cpp | 445 + src/libs/dialogs/ctrlpaneldlg.h | 110 + src/libs/dialogs/deletedialog.cpp | 309 + src/libs/dialogs/deletedialog.h | 140 + src/libs/dialogs/deletedialogbase.ui | 188 + src/libs/dialogs/dprogressdlg.cpp | 224 + src/libs/dialogs/dprogressdlg.h | 79 + src/libs/dialogs/iccprofileinfodlg.cpp | 60 + src/libs/dialogs/iccprofileinfodlg.h | 58 + src/libs/dialogs/imagedialog.cpp | 366 + src/libs/dialogs/imagedialog.h | 100 + src/libs/dialogs/imagedlgbase.cpp | 261 + src/libs/dialogs/imagedlgbase.h | 98 + src/libs/dialogs/imageguidedlg.cpp | 597 + src/libs/dialogs/imageguidedlg.h | 123 + src/libs/dialogs/rawcameradlg.cpp | 178 + src/libs/dialogs/rawcameradlg.h | 61 + src/libs/dimg/Makefile.am | 27 + src/libs/dimg/README | 95 + src/libs/dimg/dcolor.cpp | 281 + src/libs/dimg/dcolor.h | 152 + src/libs/dimg/dcolorblend.h | 179 + src/libs/dimg/dcolorcomposer.cpp | 436 + src/libs/dimg/dcolorcomposer.h | 133 + src/libs/dimg/dcolorpixelaccess.h | 78 + src/libs/dimg/ddebug.cpp | 92 + src/libs/dimg/ddebug.h | 73 + src/libs/dimg/dimg.cpp | 1700 + src/libs/dimg/dimg.h | 353 + src/libs/dimg/dimgprivate.h | 81 + src/libs/dimg/dimgscale.cpp | 2127 + src/libs/dimg/drawdecoding.h | 128 + src/libs/dimg/exposurecontainer.h | 65 + src/libs/dimg/filters/Makefile.am | 21 + src/libs/dimg/filters/bcgmodifier.cpp | 208 + src/libs/dimg/filters/bcgmodifier.h | 73 + src/libs/dimg/filters/colormodifier.cpp | 287 + src/libs/dimg/filters/colormodifier.h | 63 + src/libs/dimg/filters/dimggaussianblur.cpp | 327 + src/libs/dimg/filters/dimggaussianblur.h | 97 + src/libs/dimg/filters/dimgimagefilters.cpp | 977 + src/libs/dimg/filters/dimgimagefilters.h | 133 + src/libs/dimg/filters/dimgsharpen.cpp | 243 + src/libs/dimg/filters/dimgsharpen.h | 69 + src/libs/dimg/filters/dimgthreadedfilter.cpp | 170 + src/libs/dimg/filters/dimgthreadedfilter.h | 153 + src/libs/dimg/filters/hslmodifier.cpp | 240 + src/libs/dimg/filters/hslmodifier.h | 58 + src/libs/dimg/filters/icctransform.cpp | 831 + src/libs/dimg/filters/icctransform.h | 94 + src/libs/dimg/loaders/Makefile.am | 21 + src/libs/dimg/loaders/README | 42 + src/libs/dimg/loaders/dimgloader.cpp | 200 + src/libs/dimg/loaders/dimgloader.h | 97 + src/libs/dimg/loaders/dimgloaderobserver.h | 67 + src/libs/dimg/loaders/iccjpeg.c | 270 + src/libs/dimg/loaders/iccjpeg.h | 101 + src/libs/dimg/loaders/jp2kloader.cpp | 715 + src/libs/dimg/loaders/jp2kloader.h | 69 + src/libs/dimg/loaders/jp2ksettings.cpp | 139 + src/libs/dimg/loaders/jp2ksettings.h | 67 + src/libs/dimg/loaders/jpegloader.cpp | 676 + src/libs/dimg/loaders/jpegloader.h | 80 + src/libs/dimg/loaders/jpegsettings.cpp | 154 + src/libs/dimg/loaders/jpegsettings.h | 63 + src/libs/dimg/loaders/pngloader.cpp | 993 + src/libs/dimg/loaders/pngloader.h | 73 + src/libs/dimg/loaders/pngsettings.cpp | 102 + src/libs/dimg/loaders/pngsettings.h | 60 + src/libs/dimg/loaders/ppmloader.cpp | 178 + src/libs/dimg/loaders/ppmloader.h | 59 + src/libs/dimg/loaders/qimageloader.cpp | 126 + src/libs/dimg/loaders/qimageloader.h | 57 + src/libs/dimg/loaders/rawloader.cpp | 371 + src/libs/dimg/loaders/rawloader.h | 86 + src/libs/dimg/loaders/tiffloader.cpp | 806 + src/libs/dimg/loaders/tiffloader.h | 76 + src/libs/dimg/loaders/tiffsettings.cpp | 94 + src/libs/dimg/loaders/tiffsettings.h | 60 + src/libs/dmetadata/Makefile.am | 18 + src/libs/dmetadata/dmetadata.cpp | 544 + src/libs/dmetadata/dmetadata.h | 86 + src/libs/dmetadata/photoinfocontainer.h | 82 + src/libs/greycstoration/CImg.h | 36837 ++++++++ src/libs/greycstoration/LICENSE.txt | 505 + src/libs/greycstoration/Makefile.am | 20 + src/libs/greycstoration/greycstoration.h | 481 + src/libs/greycstoration/greycstorationiface.cpp | 473 + src/libs/greycstoration/greycstorationiface.h | 89 + src/libs/greycstoration/greycstorationsettings.h | 144 + src/libs/greycstoration/greycstorationwidget.cpp | 377 + src/libs/greycstoration/greycstorationwidget.h | 70 + src/libs/histogram/Makefile.am | 16 + src/libs/histogram/imagehistogram.cpp | 548 + src/libs/histogram/imagehistogram.h | 113 + src/libs/imageproperties/Makefile.am | 51 + .../imageproperties/cameraitempropertiestab.cpp | 555 + src/libs/imageproperties/cameraitempropertiestab.h | 69 + src/libs/imageproperties/imagedescedittab.cpp | 1763 + src/libs/imageproperties/imagedescedittab.h | 145 + .../imageproperties/imagepropertiescolorstab.cpp | 799 + .../imageproperties/imagepropertiescolorstab.h | 114 + .../imageproperties/imagepropertiesmetadatatab.cpp | 201 + .../imageproperties/imagepropertiesmetadatatab.h | 68 + .../imageproperties/imagepropertiessidebar.cpp | 150 + src/libs/imageproperties/imagepropertiessidebar.h | 93 + .../imagepropertiessidebarcamgui.cpp | 209 + .../imageproperties/imagepropertiessidebarcamgui.h | 87 + .../imageproperties/imagepropertiessidebardb.cpp | 368 + .../imageproperties/imagepropertiessidebardb.h | 117 + src/libs/imageproperties/imagepropertiestab.cpp | 601 + src/libs/imageproperties/imagepropertiestab.h | 66 + src/libs/imageproperties/navigatebartab.cpp | 146 + src/libs/imageproperties/navigatebartab.h | 85 + src/libs/imageproperties/navigatebarwidget.cpp | 112 + src/libs/imageproperties/navigatebarwidget.h | 70 + src/libs/imageproperties/talbumlistview.cpp | 528 + src/libs/imageproperties/talbumlistview.h | 109 + src/libs/jpegutils/Makefile.am | 22 + src/libs/jpegutils/jinclude.h | 90 + src/libs/jpegutils/jpegint.h | 811 + src/libs/jpegutils/jpegutils.cpp | 532 + src/libs/jpegutils/jpegutils.h | 44 + src/libs/jpegutils/libjpeg62.README | 385 + src/libs/jpegutils/transupp.cpp | 2527 + src/libs/jpegutils/transupp.h | 373 + src/libs/levels/Makefile.am | 17 + src/libs/levels/imagelevels.cpp | 715 + src/libs/levels/imagelevels.h | 105 + src/libs/lprof/Makefile.am | 15 + src/libs/lprof/cmshull.cpp | 1480 + src/libs/lprof/cmslm.cpp | 288 + src/libs/lprof/cmslnr.cpp | 560 + src/libs/lprof/cmsmatn.cpp | 323 + src/libs/lprof/cmsmkmsh.cpp | 346 + src/libs/lprof/cmsmntr.cpp | 371 + src/libs/lprof/cmsoutl.cpp | 284 + src/libs/lprof/cmspcoll.cpp | 1045 + src/libs/lprof/cmsprf.cpp | 439 + src/libs/lprof/cmsreg.cpp | 558 + src/libs/lprof/cmsscn.cpp | 422 + src/libs/lprof/cmssheet.cpp | 1746 + src/libs/lprof/lcmsprf.h | 485 + src/libs/sqlite2/Makefile.am | 43 + src/libs/sqlite2/README | 2 + src/libs/sqlite2/attach.c | 311 + src/libs/sqlite2/auth.c | 219 + src/libs/sqlite2/btree.c | 3584 + src/libs/sqlite2/btree.h | 156 + src/libs/sqlite2/btree_rb.c | 1488 + src/libs/sqlite2/build.c | 2156 + src/libs/sqlite2/copy.c | 110 + src/libs/sqlite2/date.c | 875 + src/libs/sqlite2/delete.c | 393 + src/libs/sqlite2/encode.c | 257 + src/libs/sqlite2/expr.c | 1662 + src/libs/sqlite2/func.c | 658 + src/libs/sqlite2/hash.c | 356 + src/libs/sqlite2/hash.h | 109 + src/libs/sqlite2/insert.c | 919 + src/libs/sqlite2/main.c | 1143 + src/libs/sqlite2/opcodes.c | 140 + src/libs/sqlite2/opcodes.h | 138 + src/libs/sqlite2/os.c | 1850 + src/libs/sqlite2/os.h | 191 + src/libs/sqlite2/pager.c | 2220 + src/libs/sqlite2/pager.h | 107 + src/libs/sqlite2/parse.c | 4035 + src/libs/sqlite2/parse.h | 130 + src/libs/sqlite2/pragma.c | 712 + src/libs/sqlite2/printf.c | 858 + src/libs/sqlite2/random.c | 97 + src/libs/sqlite2/select.c | 2434 + src/libs/sqlite2/shell.c | 1354 + src/libs/sqlite2/sqlite.h | 872 + src/libs/sqlite2/sqliteInt.h | 1274 + src/libs/sqlite2/table.c | 203 + src/libs/sqlite2/tokenize.c | 679 + src/libs/sqlite2/trigger.c | 764 + src/libs/sqlite2/update.c | 459 + src/libs/sqlite2/util.c | 1134 + src/libs/sqlite2/vacuum.c | 305 + src/libs/sqlite2/vdbe.c | 4921 ++ src/libs/sqlite2/vdbe.h | 112 + src/libs/sqlite2/vdbeInt.h | 303 + src/libs/sqlite2/vdbeaux.c | 1061 + src/libs/sqlite2/where.c | 1235 + src/libs/sqlite3/Makefile.am | 9 + src/libs/sqlite3/sqlite3.c | 86994 +++++++++++++++++++ src/libs/sqlite3/sqlite3.h | 5638 ++ src/libs/themeengine/Makefile.am | 12 + src/libs/themeengine/texture.cpp | 495 + src/libs/themeengine/texture.h | 83 + src/libs/themeengine/theme.cpp | 181 + src/libs/themeengine/theme.h | 112 + src/libs/themeengine/themeengine.cpp | 1132 + src/libs/themeengine/themeengine.h | 107 + src/libs/threadimageio/Makefile.am | 23 + src/libs/threadimageio/loadingcache.cpp | 256 + src/libs/threadimageio/loadingcache.h | 135 + src/libs/threadimageio/loadingcacheinterface.cpp | 73 + src/libs/threadimageio/loadingcacheinterface.h | 56 + src/libs/threadimageio/loadingdescription.cpp | 152 + src/libs/threadimageio/loadingdescription.h | 135 + src/libs/threadimageio/loadsavetask.cpp | 424 + src/libs/threadimageio/loadsavetask.h | 360 + src/libs/threadimageio/loadsavethread.cpp | 331 + src/libs/threadimageio/loadsavethread.h | 184 + src/libs/threadimageio/managedloadsavethread.cpp | 362 + src/libs/threadimageio/managedloadsavethread.h | 134 + src/libs/threadimageio/previewloadthread.cpp | 52 + src/libs/threadimageio/previewloadthread.h | 57 + src/libs/threadimageio/previewtask.cpp | 258 + src/libs/threadimageio/previewtask.h | 57 + src/libs/threadimageio/sharedloadsavethread.cpp | 65 + src/libs/threadimageio/sharedloadsavethread.h | 43 + src/libs/thumbbar/Makefile.am | 18 + src/libs/thumbbar/thumbbar.cpp | 1138 + src/libs/thumbbar/thumbbar.h | 239 + src/libs/thumbbar/thumbnailjob.cpp | 318 + src/libs/thumbbar/thumbnailjob.h | 88 + src/libs/whitebalance/Makefile.am | 16 + src/libs/whitebalance/blackbody.h | 539 + src/libs/whitebalance/whitebalance.cpp | 382 + src/libs/whitebalance/whitebalance.h | 72 + src/libs/widgets/Makefile.am | 12 + src/libs/widgets/common/Makefile.am | 25 + src/libs/widgets/common/colorgradientwidget.cpp | 161 + src/libs/widgets/common/colorgradientwidget.h | 73 + src/libs/widgets/common/curveswidget.cpp | 838 + src/libs/widgets/common/curveswidget.h | 132 + src/libs/widgets/common/dcursortracker.cpp | 109 + src/libs/widgets/common/dcursortracker.h | 76 + src/libs/widgets/common/dlogoaction.cpp | 96 + src/libs/widgets/common/dlogoaction.h | 56 + src/libs/widgets/common/dpopupmenu.cpp | 197 + src/libs/widgets/common/dpopupmenu.h | 83 + src/libs/widgets/common/filesaveoptionsbox.cpp | 182 + src/libs/widgets/common/filesaveoptionsbox.h | 72 + src/libs/widgets/common/histogramwidget.cpp | 1089 + src/libs/widgets/common/histogramwidget.h | 177 + src/libs/widgets/common/paniconwidget.cpp | 324 + src/libs/widgets/common/paniconwidget.h | 120 + src/libs/widgets/common/previewwidget.cpp | 640 + src/libs/widgets/common/previewwidget.h | 131 + src/libs/widgets/common/searchtextbar.cpp | 260 + src/libs/widgets/common/searchtextbar.h | 111 + src/libs/widgets/common/sidebar.cpp | 363 + src/libs/widgets/common/sidebar.h | 178 + src/libs/widgets/common/splashscreen.cpp | 160 + src/libs/widgets/common/splashscreen.h | 74 + src/libs/widgets/common/squeezedcombobox.cpp | 198 + src/libs/widgets/common/squeezedcombobox.h | 164 + src/libs/widgets/common/statusled.cpp | 84 + src/libs/widgets/common/statusled.h | 72 + src/libs/widgets/common/statusnavigatebar.cpp | 172 + src/libs/widgets/common/statusnavigatebar.h | 80 + src/libs/widgets/common/statusprogressbar.cpp | 171 + src/libs/widgets/common/statusprogressbar.h | 87 + src/libs/widgets/common/statuszoombar.cpp | 208 + src/libs/widgets/common/statuszoombar.h | 100 + src/libs/widgets/iccprofiles/Makefile.am | 23 + src/libs/widgets/iccprofiles/cietonguewidget.cpp | 816 + src/libs/widgets/iccprofiles/cietonguewidget.h | 115 + src/libs/widgets/iccprofiles/iccpreviewwidget.cpp | 83 + src/libs/widgets/iccprofiles/iccpreviewwidget.h | 71 + src/libs/widgets/iccprofiles/iccprofilewidget.cpp | 448 + src/libs/widgets/iccprofiles/iccprofilewidget.h | 79 + src/libs/widgets/imageplugins/Makefile.am | 22 + src/libs/widgets/imageplugins/imageguidewidget.cpp | 625 + src/libs/widgets/imageplugins/imageguidewidget.h | 132 + src/libs/widgets/imageplugins/imagepanelwidget.cpp | 335 + src/libs/widgets/imageplugins/imagepanelwidget.h | 117 + .../widgets/imageplugins/imagepaniconwidget.cpp | 198 + src/libs/widgets/imageplugins/imagepaniconwidget.h | 68 + .../widgets/imageplugins/imagepannelwidget.cpp | 477 + src/libs/widgets/imageplugins/imagepannelwidget.h | 123 + .../widgets/imageplugins/imageregionwidget.cpp | 473 + src/libs/widgets/imageplugins/imageregionwidget.h | 115 + src/libs/widgets/imageplugins/imagewidget.cpp | 347 + src/libs/widgets/imageplugins/imagewidget.h | 106 + .../widgets/imageplugins/listboxpreviewitem.cpp | 62 + src/libs/widgets/imageplugins/listboxpreviewitem.h | 80 + src/libs/widgets/metadata/Makefile.am | 17 + src/libs/widgets/metadata/exifwidget.cpp | 185 + src/libs/widgets/metadata/exifwidget.h | 73 + src/libs/widgets/metadata/gpswidget.cpp | 340 + src/libs/widgets/metadata/gpswidget.h | 95 + src/libs/widgets/metadata/iptcwidget.cpp | 167 + src/libs/widgets/metadata/iptcwidget.h | 69 + src/libs/widgets/metadata/makernotewidget.cpp | 210 + src/libs/widgets/metadata/makernotewidget.h | 70 + src/libs/widgets/metadata/mdkeylistviewitem.cpp | 94 + src/libs/widgets/metadata/mdkeylistviewitem.h | 63 + src/libs/widgets/metadata/metadatalistview.cpp | 283 + src/libs/widgets/metadata/metadatalistview.h | 85 + src/libs/widgets/metadata/metadatalistviewitem.cpp | 75 + src/libs/widgets/metadata/metadatalistviewitem.h | 59 + src/libs/widgets/metadata/metadatawidget.cpp | 454 + src/libs/widgets/metadata/metadatawidget.h | 120 + src/libs/widgets/metadata/worldmapwidget.cpp | 211 + src/libs/widgets/metadata/worldmapwidget.h | 73 + src/showfoto/Makefile.am | 42 + src/showfoto/main.cpp | 95 + src/showfoto/setup/Makefile.am | 14 + src/showfoto/setup/setup.cpp | 153 + src/showfoto/setup/setup.h | 78 + src/showfoto/setup/setupeditor.cpp | 246 + src/showfoto/setup/setupeditor.h | 63 + src/showfoto/setup/setuptooltip.cpp | 228 + src/showfoto/setup/setuptooltip.h | 59 + src/showfoto/showfoto.cpp | 1240 + src/showfoto/showfoto.desktop | 151 + src/showfoto/showfoto.h | 135 + src/showfoto/showfotoui.rc | 131 + src/tdeioslave/Makefile.am | 72 + src/tdeioslave/digikamalbums.cpp | 1969 + src/tdeioslave/digikamalbums.h | 108 + src/tdeioslave/digikamalbums.protocol | 45 + src/tdeioslave/digikamdates.cpp | 313 + src/tdeioslave/digikamdates.h | 54 + src/tdeioslave/digikamdates.protocol | 44 + src/tdeioslave/digikamsearch.cpp | 734 + src/tdeioslave/digikamsearch.h | 100 + src/tdeioslave/digikamsearch.protocol | 38 + src/tdeioslave/digikamtags.cpp | 325 + src/tdeioslave/digikamtags.h | 60 + src/tdeioslave/digikamtags.protocol | 44 + src/tdeioslave/digikamthumbnail.cpp | 635 + src/tdeioslave/digikamthumbnail.h | 76 + src/tdeioslave/digikamthumbnail.protocol | 38 + src/tdeioslave/sqlitedb.cpp | 197 + src/tdeioslave/sqlitedb.h | 58 + src/themedesigner/Makefile.am | 19 + src/themedesigner/main.cpp | 75 + src/themedesigner/mainwindow.cpp | 585 + src/themedesigner/mainwindow.h | 125 + src/themedesigner/themedicongroupitem.cpp | 105 + src/themedesigner/themedicongroupitem.h | 54 + src/themedesigner/themediconitem.cpp | 195 + src/themedesigner/themediconitem.h | 48 + src/themedesigner/themediconview.cpp | 304 + src/themedesigner/themediconview.h | 86 + src/tips | 267 + src/utilities/Makefile.am | 1 + src/utilities/batch/Makefile.am | 20 + src/utilities/batch/batchalbumssyncmetadata.cpp | 183 + src/utilities/batch/batchalbumssyncmetadata.h | 82 + src/utilities/batch/batchsyncmetadata.cpp | 166 + src/utilities/batch/batchsyncmetadata.h | 89 + src/utilities/batch/batchthumbsgenerator.cpp | 233 + src/utilities/batch/batchthumbsgenerator.h | 83 + src/utilities/batch/imageinfoalbumsjob.cpp | 125 + src/utilities/batch/imageinfoalbumsjob.h | 80 + src/utilities/batch/imageinfojob.cpp | 163 + src/utilities/batch/imageinfojob.h | 78 + src/utilities/cameragui/Makefile.am | 30 + src/utilities/cameragui/albumselectdialog.cpp | 417 + src/utilities/cameragui/albumselectdialog.h | 80 + src/utilities/cameragui/animwidget.cpp | 131 + src/utilities/cameragui/animwidget.h | 66 + src/utilities/cameragui/cameracontroller.cpp | 1227 + src/utilities/cameragui/cameracontroller.h | 111 + src/utilities/cameragui/camerafolderdialog.cpp | 138 + src/utilities/cameragui/camerafolderdialog.h | 68 + src/utilities/cameragui/camerafolderitem.cpp | 108 + src/utilities/cameragui/camerafolderitem.h | 69 + src/utilities/cameragui/camerafolderview.cpp | 169 + src/utilities/cameragui/camerafolderview.h | 83 + src/utilities/cameragui/cameraiconitem.cpp | 302 + src/utilities/cameragui/cameraiconitem.h | 81 + src/utilities/cameragui/cameraiconview.cpp | 900 + src/utilities/cameragui/cameraiconview.h | 141 + src/utilities/cameragui/camerainfodialog.cpp | 85 + src/utilities/cameragui/camerainfodialog.h | 50 + src/utilities/cameragui/cameraui.cpp | 1734 + src/utilities/cameragui/cameraui.h | 155 + src/utilities/cameragui/dkcamera.cpp | 113 + src/utilities/cameragui/dkcamera.h | 97 + .../cameragui/downloadsettingscontainer.h | 79 + src/utilities/cameragui/freespacewidget.cpp | 252 + src/utilities/cameragui/freespacewidget.h | 75 + src/utilities/cameragui/gpcamera.cpp | 1223 + src/utilities/cameragui/gpcamera.h | 107 + src/utilities/cameragui/gpiteminfo.cpp | 68 + src/utilities/cameragui/gpiteminfo.h | 80 + src/utilities/cameragui/mtqueue.h | 116 + src/utilities/cameragui/renamecustomizer.cpp | 532 + src/utilities/cameragui/renamecustomizer.h | 91 + src/utilities/cameragui/umscamera.cpp | 519 + src/utilities/cameragui/umscamera.h | 78 + src/utilities/hotplug/Makefile.am | 7 + src/utilities/hotplug/configure.in.in | 7 + src/utilities/hotplug/digikam-camera | 40 + src/utilities/hotplug/digikam-download.desktop.in | 27 + .../hotplug/digikam-gphoto2-camera.desktop.in | 27 + .../hotplug/digikam-mount-and-download.desktop.in | 27 + src/utilities/imageeditor/Makefile.am | 1 + src/utilities/imageeditor/canvas/Makefile.am | 28 + src/utilities/imageeditor/canvas/canvas.cpp | 1421 + src/utilities/imageeditor/canvas/canvas.h | 209 + .../imageeditor/canvas/colorcorrectiondlg.cpp | 186 + .../imageeditor/canvas/colorcorrectiondlg.h | 72 + src/utilities/imageeditor/canvas/dimginterface.cpp | 1270 + src/utilities/imageeditor/canvas/dimginterface.h | 200 + .../imageeditor/canvas/iccsettingscontainer.h | 82 + src/utilities/imageeditor/canvas/imageplugin.cpp | 68 + src/utilities/imageeditor/canvas/imageplugin.h | 70 + .../imageeditor/canvas/imagepluginloader.cpp | 278 + .../imageeditor/canvas/imagepluginloader.h | 79 + .../imageeditor/canvas/iofilesettingscontainer.h | 84 + src/utilities/imageeditor/canvas/undoaction.cpp | 185 + src/utilities/imageeditor/canvas/undoaction.h | 144 + src/utilities/imageeditor/canvas/undocache.cpp | 178 + src/utilities/imageeditor/canvas/undocache.h | 62 + src/utilities/imageeditor/canvas/undomanager.cpp | 253 + src/utilities/imageeditor/canvas/undomanager.h | 76 + src/utilities/imageeditor/editor/Makefile.am | 51 + .../imageeditor/editor/digikamimageplugin.desktop | 39 + .../imageeditor/editor/digikamimagewindowui.rc | 125 + .../imageeditor/editor/editorstackview.cpp | 233 + src/utilities/imageeditor/editor/editorstackview.h | 97 + src/utilities/imageeditor/editor/editortool.cpp | 436 + src/utilities/imageeditor/editor/editortool.h | 164 + .../imageeditor/editor/editortooliface.cpp | 147 + src/utilities/imageeditor/editor/editortooliface.h | 76 + .../imageeditor/editor/editortoolsettings.cpp | 331 + .../imageeditor/editor/editortoolsettings.h | 110 + src/utilities/imageeditor/editor/editorwindow.cpp | 1932 + src/utilities/imageeditor/editor/editorwindow.h | 263 + .../imageeditor/editor/editorwindowprivate.h | 143 + src/utilities/imageeditor/editor/imageiface.cpp | 444 + src/utilities/imageeditor/editor/imageiface.h | 198 + src/utilities/imageeditor/editor/imagewindow.cpp | 1263 + src/utilities/imageeditor/editor/imagewindow.h | 155 + .../imageeditor/editor/savingcontextcontainer.h | 89 + src/utilities/imageeditor/rawimport/Makefile.am | 27 + src/utilities/imageeditor/rawimport/rawimport.cpp | 223 + src/utilities/imageeditor/rawimport/rawimport.h | 88 + .../imageeditor/rawimport/rawpostprocessing.cpp | 137 + .../imageeditor/rawimport/rawpostprocessing.h | 63 + src/utilities/imageeditor/rawimport/rawpreview.cpp | 336 + src/utilities/imageeditor/rawimport/rawpreview.h | 108 + .../imageeditor/rawimport/rawsettingsbox.cpp | 741 + .../imageeditor/rawimport/rawsettingsbox.h | 91 + src/utilities/imageeditor/tools/Makefile.am | 19 + src/utilities/imageeditor/tools/imageprint.cpp | 814 + src/utilities/imageeditor/tools/imageprint.h | 122 + src/utilities/imageeditor/tools/imageresize.cpp | 650 + src/utilities/imageeditor/tools/imageresize.h | 82 + src/utilities/lighttable/Makefile.am | 28 + src/utilities/lighttable/lighttablebar.cpp | 881 + src/utilities/lighttable/lighttablebar.h | 153 + src/utilities/lighttable/lighttablepreview.cpp | 777 + src/utilities/lighttable/lighttablepreview.h | 125 + src/utilities/lighttable/lighttableview.cpp | 446 + src/utilities/lighttable/lighttableview.h | 137 + src/utilities/lighttable/lighttablewindow.cpp | 1676 + src/utilities/lighttable/lighttablewindow.h | 162 + src/utilities/lighttable/lighttablewindowprivate.h | 158 + src/utilities/lighttable/lighttablewindowui.rc | 83 + src/utilities/scripts/Makefile.am | 4 + src/utilities/scripts/digitaglinktree | 568 + src/utilities/scripts/digitaglinktree.1 | 182 + src/utilities/setup/Makefile.am | 29 + src/utilities/setup/cameraselection.cpp | 494 + src/utilities/setup/cameraselection.h | 88 + src/utilities/setup/setup.cpp | 266 + src/utilities/setup/setup.h | 81 + src/utilities/setup/setupcamera.cpp | 315 + src/utilities/setup/setupcamera.h | 73 + src/utilities/setup/setupcollections.cpp | 218 + src/utilities/setup/setupcollections.h | 66 + src/utilities/setup/setupdcraw.cpp | 150 + src/utilities/setup/setupdcraw.h | 67 + src/utilities/setup/setupeditor.cpp | 176 + src/utilities/setup/setupeditor.h | 63 + src/utilities/setup/setupgeneral.cpp | 313 + src/utilities/setup/setupgeneral.h | 67 + src/utilities/setup/setupicc.cpp | 727 + src/utilities/setup/setupicc.h | 86 + src/utilities/setup/setupidentity.cpp | 217 + src/utilities/setup/setupidentity.h | 60 + src/utilities/setup/setupiofiles.cpp | 137 + src/utilities/setup/setupiofiles.h | 63 + src/utilities/setup/setuplighttable.cpp | 133 + src/utilities/setup/setuplighttable.h | 59 + src/utilities/setup/setupmetadata.cpp | 238 + src/utilities/setup/setupmetadata.h | 67 + src/utilities/setup/setupmime.cpp | 280 + src/utilities/setup/setupmime.h | 66 + src/utilities/setup/setupmisc.cpp | 124 + src/utilities/setup/setupmisc.h | 58 + src/utilities/setup/setupplugins.cpp | 104 + src/utilities/setup/setupplugins.h | 56 + src/utilities/setup/setupslideshow.cpp | 165 + src/utilities/setup/setupslideshow.h | 64 + src/utilities/setup/setuptooltip.cpp | 272 + src/utilities/setup/setuptooltip.h | 59 + src/utilities/slideshow/Makefile.am | 17 + src/utilities/slideshow/slideshow.cpp | 679 + src/utilities/slideshow/slideshow.h | 91 + src/utilities/slideshow/slideshowsettings.h | 125 + src/utilities/slideshow/toolbar.cpp | 217 + src/utilities/slideshow/toolbar.h | 85 + 1087 files changed, 410276 insertions(+) create mode 100644 src/CMakeL10n.txt create mode 100644 src/COPYING-DOCS create mode 100644 src/DBSCHEMA.ODS create mode 100644 src/DESIGN create mode 100644 src/Makefile.am create mode 100644 src/NEWS.0.9.0 create mode 100644 src/NEWS.0.9.1 create mode 100644 src/NEWS.0.9.2 create mode 100644 src/NEWS.0.9.3 create mode 100644 src/NEWS.0.9.4 create mode 100644 src/NEWS.0.9.5 create mode 100644 src/configure.in.bot create mode 100644 src/configure.in.in create mode 100644 src/digikam/Makefile.am create mode 100644 src/digikam/album.cpp create mode 100644 src/digikam/album.h create mode 100644 src/digikam/albumdb.cpp create mode 100644 src/digikam/albumdb.h create mode 100644 src/digikam/albumdb_sqlite2.cpp create mode 100644 src/digikam/albumdb_sqlite2.h create mode 100644 src/digikam/albumfiletip.cpp create mode 100644 src/digikam/albumfiletip.h create mode 100644 src/digikam/albumfolderview.cpp create mode 100644 src/digikam/albumfolderview.h create mode 100644 src/digikam/albumhistory.cpp create mode 100644 src/digikam/albumhistory.h create mode 100644 src/digikam/albumicongroupitem.cpp create mode 100644 src/digikam/albumicongroupitem.h create mode 100644 src/digikam/albumiconitem.cpp create mode 100644 src/digikam/albumiconitem.h create mode 100644 src/digikam/albumiconview.cpp create mode 100644 src/digikam/albumiconview.h create mode 100644 src/digikam/albumiconviewfilter.cpp create mode 100644 src/digikam/albumiconviewfilter.h create mode 100644 src/digikam/albuminfo.h create mode 100644 src/digikam/albumitemhandler.cpp create mode 100644 src/digikam/albumitemhandler.h create mode 100644 src/digikam/albumlister.cpp create mode 100644 src/digikam/albumlister.h create mode 100644 src/digikam/albummanager.cpp create mode 100644 src/digikam/albummanager.h create mode 100644 src/digikam/albumpropsedit.cpp create mode 100644 src/digikam/albumpropsedit.h create mode 100644 src/digikam/albumsettings.cpp create mode 100644 src/digikam/albumsettings.h create mode 100644 src/digikam/albumthumbnailloader.cpp create mode 100644 src/digikam/albumthumbnailloader.h create mode 100644 src/digikam/albumwidgetstack.cpp create mode 100644 src/digikam/albumwidgetstack.h create mode 100644 src/digikam/cameralist.cpp create mode 100644 src/digikam/cameralist.h create mode 100644 src/digikam/cameratype.cpp create mode 100644 src/digikam/cameratype.h create mode 100644 src/digikam/constants.h create mode 100644 src/digikam/daboutdata.h create mode 100644 src/digikam/datefolderview.cpp create mode 100644 src/digikam/datefolderview.h create mode 100644 src/digikam/dcopiface.cpp create mode 100644 src/digikam/dcopiface.h create mode 100644 src/digikam/digikam.desktop create mode 100644 src/digikam/digikam_export.h create mode 100644 src/digikam/digikamapp.cpp create mode 100644 src/digikam/digikamapp.h create mode 100644 src/digikam/digikamappprivate.h create mode 100644 src/digikam/digikamfirstrun.cpp create mode 100644 src/digikam/digikamfirstrun.h create mode 100644 src/digikam/digikamui.rc create mode 100644 src/digikam/digikamview.cpp create mode 100644 src/digikam/digikamview.h create mode 100644 src/digikam/dio.cpp create mode 100644 src/digikam/dio.h create mode 100644 src/digikam/dio_p.h create mode 100644 src/digikam/dragobjects.cpp create mode 100644 src/digikam/dragobjects.h create mode 100644 src/digikam/firstrun.cpp create mode 100644 src/digikam/firstrun.h create mode 100644 src/digikam/folderitem.cpp create mode 100644 src/digikam/folderitem.h create mode 100644 src/digikam/folderview.cpp create mode 100644 src/digikam/folderview.h create mode 100644 src/digikam/icongroupitem.cpp create mode 100644 src/digikam/icongroupitem.h create mode 100644 src/digikam/iconitem.cpp create mode 100644 src/digikam/iconitem.h create mode 100644 src/digikam/iconview.cpp create mode 100644 src/digikam/iconview.h create mode 100644 src/digikam/imageattributeswatch.cpp create mode 100644 src/digikam/imageattributeswatch.h create mode 100644 src/digikam/imageinfo.cpp create mode 100644 src/digikam/imageinfo.h create mode 100644 src/digikam/imagepreviewview.cpp create mode 100644 src/digikam/imagepreviewview.h create mode 100644 src/digikam/kdateedit.cpp create mode 100644 src/digikam/kdateedit.h create mode 100644 src/digikam/kdatepickerpopup.cpp create mode 100644 src/digikam/kdatepickerpopup.h create mode 100644 src/digikam/kdatetimeedit.cpp create mode 100644 src/digikam/kdatetimeedit.h create mode 100644 src/digikam/kipiinterface.cpp create mode 100644 src/digikam/kipiinterface.h create mode 100644 src/digikam/main.cpp create mode 100644 src/digikam/mediaplayerview.cpp create mode 100644 src/digikam/mediaplayerview.h create mode 100644 src/digikam/metadatahub.cpp create mode 100644 src/digikam/metadatahub.h create mode 100644 src/digikam/mimefilter.cpp create mode 100644 src/digikam/mimefilter.h create mode 100644 src/digikam/monthwidget.cpp create mode 100644 src/digikam/monthwidget.h create mode 100644 src/digikam/pixmapmanager.cpp create mode 100644 src/digikam/pixmapmanager.h create mode 100644 src/digikam/ratingfilter.cpp create mode 100644 src/digikam/ratingfilter.h create mode 100644 src/digikam/ratingpopupmenu.cpp create mode 100644 src/digikam/ratingpopupmenu.h create mode 100644 src/digikam/ratingwidget.cpp create mode 100644 src/digikam/ratingwidget.h create mode 100644 src/digikam/scanlib.cpp create mode 100644 src/digikam/scanlib.h create mode 100644 src/digikam/searchadvanceddialog.cpp create mode 100644 src/digikam/searchadvanceddialog.h create mode 100644 src/digikam/searchfolderview.cpp create mode 100644 src/digikam/searchfolderview.h create mode 100644 src/digikam/searchquickdialog.cpp create mode 100644 src/digikam/searchquickdialog.h create mode 100644 src/digikam/searchresultsitem.cpp create mode 100644 src/digikam/searchresultsitem.h create mode 100644 src/digikam/searchresultsview.cpp create mode 100644 src/digikam/searchresultsview.h create mode 100644 src/digikam/searchwidgets.cpp create mode 100644 src/digikam/searchwidgets.h create mode 100644 src/digikam/syncjob.cpp create mode 100644 src/digikam/syncjob.h create mode 100644 src/digikam/tageditdlg.cpp create mode 100644 src/digikam/tageditdlg.h create mode 100644 src/digikam/tagfilterview.cpp create mode 100644 src/digikam/tagfilterview.h create mode 100644 src/digikam/tagfolderview.cpp create mode 100644 src/digikam/tagfolderview.h create mode 100644 src/digikam/tagspopupmenu.cpp create mode 100644 src/digikam/tagspopupmenu.h create mode 100644 src/digikam/thumbnailsize.h create mode 100644 src/digikam/timelinefolderview.cpp create mode 100644 src/digikam/timelinefolderview.h create mode 100644 src/digikam/timelineview.cpp create mode 100644 src/digikam/timelineview.h create mode 100644 src/digikam/timelinewidget.cpp create mode 100644 src/digikam/timelinewidget.h create mode 100644 src/digikam/upgradedb_sqlite2tosqlite3.cpp create mode 100644 src/digikam/upgradedb_sqlite2tosqlite3.h create mode 100644 src/digikam/welcomepageview.cpp create mode 100644 src/digikam/welcomepageview.h create mode 100644 src/imageplugins/Makefile.am create mode 100644 src/imageplugins/adjustcurves/Makefile.am create mode 100644 src/imageplugins/adjustcurves/adjustcurves.cpp create mode 100644 src/imageplugins/adjustcurves/adjustcurves.h create mode 100644 src/imageplugins/adjustcurves/adjustcurvestool.cpp create mode 100644 src/imageplugins/adjustcurves/adjustcurvestool.h create mode 100644 src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves.desktop create mode 100644 src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves_ui.rc create mode 100644 src/imageplugins/adjustcurves/imageplugin_adjustcurves.cpp create mode 100644 src/imageplugins/adjustcurves/imageplugin_adjustcurves.h create mode 100644 src/imageplugins/adjustlevels/Makefile.am create mode 100644 src/imageplugins/adjustlevels/adjustlevels.cpp create mode 100644 src/imageplugins/adjustlevels/adjustlevels.h create mode 100644 src/imageplugins/adjustlevels/adjustlevelstool.cpp create mode 100644 src/imageplugins/adjustlevels/adjustlevelstool.h create mode 100644 src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels.desktop create mode 100644 src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels_ui.rc create mode 100644 src/imageplugins/adjustlevels/imageplugin_adjustlevels.cpp create mode 100644 src/imageplugins/adjustlevels/imageplugin_adjustlevels.h create mode 100644 src/imageplugins/antivignetting/Makefile.am create mode 100644 src/imageplugins/antivignetting/antivignetting.cpp create mode 100644 src/imageplugins/antivignetting/antivignetting.h create mode 100644 src/imageplugins/antivignetting/antivignettingtool.cpp create mode 100644 src/imageplugins/antivignetting/antivignettingtool.h create mode 100644 src/imageplugins/antivignetting/digikamimageplugin_antivignetting.desktop create mode 100644 src/imageplugins/antivignetting/digikamimageplugin_antivignetting_ui.rc create mode 100644 src/imageplugins/antivignetting/imageeffect_antivignetting.cpp create mode 100644 src/imageplugins/antivignetting/imageeffect_antivignetting.h create mode 100644 src/imageplugins/antivignetting/imageplugin_antivignetting.cpp create mode 100644 src/imageplugins/antivignetting/imageplugin_antivignetting.h create mode 100644 src/imageplugins/blurfx/Makefile.am create mode 100644 src/imageplugins/blurfx/blurfx.cpp create mode 100644 src/imageplugins/blurfx/blurfx.h create mode 100644 src/imageplugins/blurfx/blurfxtool.cpp create mode 100644 src/imageplugins/blurfx/blurfxtool.h create mode 100644 src/imageplugins/blurfx/digikamimageplugin_blurfx.desktop create mode 100644 src/imageplugins/blurfx/digikamimageplugin_blurfx_ui.rc create mode 100644 src/imageplugins/blurfx/imageeffect_blurfx.cpp create mode 100644 src/imageplugins/blurfx/imageeffect_blurfx.h create mode 100644 src/imageplugins/blurfx/imageplugin_blurfx.cpp create mode 100644 src/imageplugins/blurfx/imageplugin_blurfx.h create mode 100644 src/imageplugins/border/Makefile.am create mode 100644 src/imageplugins/border/border.cpp create mode 100644 src/imageplugins/border/border.h create mode 100644 src/imageplugins/border/bordertool.cpp create mode 100644 src/imageplugins/border/bordertool.h create mode 100644 src/imageplugins/border/digikamimageplugin_border.desktop create mode 100644 src/imageplugins/border/digikamimageplugin_border_ui.rc create mode 100644 src/imageplugins/border/imageeffect_border.cpp create mode 100644 src/imageplugins/border/imageeffect_border.h create mode 100644 src/imageplugins/border/imageplugin_border.cpp create mode 100644 src/imageplugins/border/imageplugin_border.h create mode 100644 src/imageplugins/border/patterns/Makefile.am create mode 100644 src/imageplugins/border/patterns/chalk-pattern.png create mode 100644 src/imageplugins/border/patterns/craters-pattern.png create mode 100644 src/imageplugins/border/patterns/dried-pattern.png create mode 100644 src/imageplugins/border/patterns/granit-pattern.png create mode 100644 src/imageplugins/border/patterns/ice-pattern.png create mode 100644 src/imageplugins/border/patterns/leaf-pattern.png create mode 100644 src/imageplugins/border/patterns/marble-pattern.png create mode 100644 src/imageplugins/border/patterns/paper-pattern.png create mode 100644 src/imageplugins/border/patterns/parque-pattern.png create mode 100644 src/imageplugins/border/patterns/pine-pattern.png create mode 100644 src/imageplugins/border/patterns/pink-pattern.png create mode 100644 src/imageplugins/border/patterns/rain-pattern.png create mode 100644 src/imageplugins/border/patterns/rock-pattern.png create mode 100644 src/imageplugins/border/patterns/stone-pattern.png create mode 100644 src/imageplugins/border/patterns/wall-pattern.png create mode 100644 src/imageplugins/border/patterns/wood-pattern.png create mode 100644 src/imageplugins/channelmixer/Makefile.am create mode 100644 src/imageplugins/channelmixer/channelmixer.cpp create mode 100644 src/imageplugins/channelmixer/channelmixer.h create mode 100644 src/imageplugins/channelmixer/channelmixertool.cpp create mode 100644 src/imageplugins/channelmixer/channelmixertool.h create mode 100644 src/imageplugins/channelmixer/digikamimageplugin_channelmixer.desktop create mode 100644 src/imageplugins/channelmixer/digikamimageplugin_channelmixer_ui.rc create mode 100644 src/imageplugins/channelmixer/imageplugin_channelmixer.cpp create mode 100644 src/imageplugins/channelmixer/imageplugin_channelmixer.h create mode 100644 src/imageplugins/charcoal/Makefile.am create mode 100644 src/imageplugins/charcoal/charcoal.cpp create mode 100644 src/imageplugins/charcoal/charcoal.h create mode 100644 src/imageplugins/charcoal/charcoaltool.cpp create mode 100644 src/imageplugins/charcoal/charcoaltool.h create mode 100644 src/imageplugins/charcoal/digikamimageplugin_charcoal.desktop create mode 100644 src/imageplugins/charcoal/digikamimageplugin_charcoal_ui.rc create mode 100644 src/imageplugins/charcoal/imageeffect_charcoal.cpp create mode 100644 src/imageplugins/charcoal/imageeffect_charcoal.h create mode 100644 src/imageplugins/charcoal/imageplugin_charcoal.cpp create mode 100644 src/imageplugins/charcoal/imageplugin_charcoal.h create mode 100644 src/imageplugins/colorfx/Makefile.am create mode 100644 src/imageplugins/colorfx/colorfxtool.cpp create mode 100644 src/imageplugins/colorfx/colorfxtool.h create mode 100644 src/imageplugins/colorfx/digikamimageplugin_colorfx.desktop create mode 100644 src/imageplugins/colorfx/digikamimageplugin_colorfx_ui.rc create mode 100644 src/imageplugins/colorfx/imageeffect_colorfx.cpp create mode 100644 src/imageplugins/colorfx/imageeffect_colorfx.h create mode 100644 src/imageplugins/colorfx/imageplugin_colorfx.cpp create mode 100644 src/imageplugins/colorfx/imageplugin_colorfx.h create mode 100644 src/imageplugins/coreplugin/Makefile.am create mode 100644 src/imageplugins/coreplugin/autocorrectiontool.cpp create mode 100644 src/imageplugins/coreplugin/autocorrectiontool.h create mode 100644 src/imageplugins/coreplugin/bcgtool.cpp create mode 100644 src/imageplugins/coreplugin/bcgtool.h create mode 100644 src/imageplugins/coreplugin/blurtool.cpp create mode 100644 src/imageplugins/coreplugin/blurtool.h create mode 100644 src/imageplugins/coreplugin/bwsepiatool.cpp create mode 100644 src/imageplugins/coreplugin/bwsepiatool.h create mode 100644 src/imageplugins/coreplugin/digikamimageplugin_core.desktop create mode 100644 src/imageplugins/coreplugin/digikamimageplugin_core_ui.rc create mode 100644 src/imageplugins/coreplugin/hsl/Makefile.am create mode 100644 src/imageplugins/coreplugin/hsl/hsltool.cpp create mode 100644 src/imageplugins/coreplugin/hsl/hsltool.h create mode 100644 src/imageplugins/coreplugin/hsl/hspreviewwidget.cpp create mode 100644 src/imageplugins/coreplugin/hsl/hspreviewwidget.h create mode 100644 src/imageplugins/coreplugin/hsl/imageeffect_hsl.cpp create mode 100644 src/imageplugins/coreplugin/hsl/imageeffect_hsl.h create mode 100644 src/imageplugins/coreplugin/iccprooftool.cpp create mode 100644 src/imageplugins/coreplugin/iccprooftool.h create mode 100644 src/imageplugins/coreplugin/imageeffect_autocorrection.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_autocorrection.h create mode 100644 src/imageplugins/coreplugin/imageeffect_bcg.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_bcg.h create mode 100644 src/imageplugins/coreplugin/imageeffect_blur.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_blur.h create mode 100644 src/imageplugins/coreplugin/imageeffect_bwsepia.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_bwsepia.h create mode 100644 src/imageplugins/coreplugin/imageeffect_iccproof.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_iccproof.h create mode 100644 src/imageplugins/coreplugin/imageeffect_redeye.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_redeye.h create mode 100644 src/imageplugins/coreplugin/imageeffect_rgb.cpp create mode 100644 src/imageplugins/coreplugin/imageeffect_rgb.h create mode 100644 src/imageplugins/coreplugin/imageplugin_core.cpp create mode 100644 src/imageplugins/coreplugin/imageplugin_core.h create mode 100644 src/imageplugins/coreplugin/ratiocrop/Makefile.am create mode 100644 src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.cpp create mode 100644 src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.h create mode 100644 src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp create mode 100644 src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.h create mode 100644 src/imageplugins/coreplugin/ratiocrop/ratiocroptool.cpp create mode 100644 src/imageplugins/coreplugin/ratiocrop/ratiocroptool.h create mode 100644 src/imageplugins/coreplugin/redeyetool.cpp create mode 100644 src/imageplugins/coreplugin/redeyetool.h create mode 100644 src/imageplugins/coreplugin/rgbtool.cpp create mode 100644 src/imageplugins/coreplugin/rgbtool.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/Makefile.am create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/LICENCE create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/Makefile.am create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/README create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/abort_.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/blaswrap.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/clapack.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/close.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dgemm.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dger.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dgesv.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetf2.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrf.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrs.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dlaswp.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dscal.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dswap.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/dtrsm.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/endfile.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/err.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/f2c.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/fio.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/fmtlib.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/fp.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/idamax.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/ieeeck.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/ilaenv.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/lsame.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/open.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/s_cmp.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/s_copy.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/s_stop.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/sfe.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/sig_die.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/util.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/wref.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/wrtfmt.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/wsfe.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/clapack/xerbla.c create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.cpp create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/matrix.cpp create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/matrix.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/refocus.cpp create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/refocus.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/sharpentool.cpp create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/sharpentool.h create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/unsharp.cpp create mode 100644 src/imageplugins/coreplugin/sharpnesseditor/unsharp.h create mode 100644 src/imageplugins/distortionfx/Makefile.am create mode 100644 src/imageplugins/distortionfx/digikamimageplugin_distortionfx.desktop create mode 100644 src/imageplugins/distortionfx/digikamimageplugin_distortionfx_ui.rc create mode 100644 src/imageplugins/distortionfx/distortionfx.cpp create mode 100644 src/imageplugins/distortionfx/distortionfx.h create mode 100644 src/imageplugins/distortionfx/distortionfxtool.cpp create mode 100644 src/imageplugins/distortionfx/distortionfxtool.h create mode 100644 src/imageplugins/distortionfx/imageeffect_distortionfx.cpp create mode 100644 src/imageplugins/distortionfx/imageeffect_distortionfx.h create mode 100644 src/imageplugins/distortionfx/imageplugin_distortionfx.cpp create mode 100644 src/imageplugins/distortionfx/imageplugin_distortionfx.h create mode 100644 src/imageplugins/emboss/Makefile.am create mode 100644 src/imageplugins/emboss/digikamimageplugin_emboss.desktop create mode 100644 src/imageplugins/emboss/digikamimageplugin_emboss_ui.rc create mode 100644 src/imageplugins/emboss/emboss.cpp create mode 100644 src/imageplugins/emboss/emboss.h create mode 100644 src/imageplugins/emboss/embosstool.cpp create mode 100644 src/imageplugins/emboss/embosstool.h create mode 100644 src/imageplugins/emboss/imageeffect_emboss.cpp create mode 100644 src/imageplugins/emboss/imageeffect_emboss.h create mode 100644 src/imageplugins/emboss/imageplugin_emboss.cpp create mode 100644 src/imageplugins/emboss/imageplugin_emboss.h create mode 100644 src/imageplugins/filmgrain/Makefile.am create mode 100644 src/imageplugins/filmgrain/digikamimageplugin_filmgrain.desktop create mode 100644 src/imageplugins/filmgrain/digikamimageplugin_filmgrain_ui.rc create mode 100644 src/imageplugins/filmgrain/filmgrain.cpp create mode 100644 src/imageplugins/filmgrain/filmgrain.h create mode 100644 src/imageplugins/filmgrain/filmgraintool.cpp create mode 100644 src/imageplugins/filmgrain/filmgraintool.h create mode 100644 src/imageplugins/filmgrain/imageeffect_filmgrain.cpp create mode 100644 src/imageplugins/filmgrain/imageeffect_filmgrain.h create mode 100644 src/imageplugins/filmgrain/imageplugin_filmgrain.cpp create mode 100644 src/imageplugins/filmgrain/imageplugin_filmgrain.h create mode 100644 src/imageplugins/freerotation/Makefile.am create mode 100644 src/imageplugins/freerotation/digikamimageplugin_freerotation.desktop create mode 100644 src/imageplugins/freerotation/digikamimageplugin_freerotation_ui.rc create mode 100644 src/imageplugins/freerotation/freerotation.cpp create mode 100644 src/imageplugins/freerotation/freerotation.h create mode 100644 src/imageplugins/freerotation/freerotationtool.cpp create mode 100644 src/imageplugins/freerotation/freerotationtool.h create mode 100644 src/imageplugins/freerotation/imageeffect_freerotation.cpp create mode 100644 src/imageplugins/freerotation/imageeffect_freerotation.h create mode 100644 src/imageplugins/freerotation/imageplugin_freerotation.cpp create mode 100644 src/imageplugins/freerotation/imageplugin_freerotation.h create mode 100644 src/imageplugins/hotpixels/Makefile.am create mode 100644 src/imageplugins/hotpixels/TODO create mode 100644 src/imageplugins/hotpixels/blackframelistview.cpp create mode 100644 src/imageplugins/hotpixels/blackframelistview.h create mode 100644 src/imageplugins/hotpixels/blackframeparser.cpp create mode 100644 src/imageplugins/hotpixels/blackframeparser.h create mode 100644 src/imageplugins/hotpixels/digikamimageplugin_hotpixels.desktop create mode 100644 src/imageplugins/hotpixels/digikamimageplugin_hotpixels_ui.rc create mode 100644 src/imageplugins/hotpixels/hotpixel.h create mode 100644 src/imageplugins/hotpixels/hotpixelfixer.cpp create mode 100644 src/imageplugins/hotpixels/hotpixelfixer.h create mode 100644 src/imageplugins/hotpixels/hotpixelstool.cpp create mode 100644 src/imageplugins/hotpixels/hotpixelstool.h create mode 100644 src/imageplugins/hotpixels/imageeffect_hotpixels.cpp create mode 100644 src/imageplugins/hotpixels/imageeffect_hotpixels.h create mode 100644 src/imageplugins/hotpixels/imageplugin_hotpixels.cpp create mode 100644 src/imageplugins/hotpixels/imageplugin_hotpixels.h create mode 100644 src/imageplugins/hotpixels/weights.cpp create mode 100644 src/imageplugins/hotpixels/weights.h create mode 100644 src/imageplugins/infrared/Makefile.am create mode 100644 src/imageplugins/infrared/digikamimageplugin_infrared.desktop create mode 100644 src/imageplugins/infrared/digikamimageplugin_infrared_ui.rc create mode 100644 src/imageplugins/infrared/imageeffect_infrared.cpp create mode 100644 src/imageplugins/infrared/imageeffect_infrared.h create mode 100644 src/imageplugins/infrared/imageplugin_infrared.cpp create mode 100644 src/imageplugins/infrared/imageplugin_infrared.h create mode 100644 src/imageplugins/infrared/infrared.cpp create mode 100644 src/imageplugins/infrared/infrared.h create mode 100644 src/imageplugins/infrared/infraredtool.cpp create mode 100644 src/imageplugins/infrared/infraredtool.h create mode 100644 src/imageplugins/inpainting/Makefile.am create mode 100644 src/imageplugins/inpainting/digikamimageplugin_inpainting.desktop create mode 100644 src/imageplugins/inpainting/digikamimageplugin_inpainting_ui.rc create mode 100644 src/imageplugins/inpainting/imageeffect_inpainting.cpp create mode 100644 src/imageplugins/inpainting/imageeffect_inpainting.h create mode 100644 src/imageplugins/inpainting/imageplugin_inpainting.cpp create mode 100644 src/imageplugins/inpainting/imageplugin_inpainting.h create mode 100644 src/imageplugins/inpainting/inpaintingtool.cpp create mode 100644 src/imageplugins/inpainting/inpaintingtool.h create mode 100644 src/imageplugins/inserttext/Makefile.am create mode 100644 src/imageplugins/inserttext/digikamimageplugin_inserttext.desktop create mode 100644 src/imageplugins/inserttext/digikamimageplugin_inserttext_ui.rc create mode 100644 src/imageplugins/inserttext/fontchooserwidget.cpp create mode 100644 src/imageplugins/inserttext/fontchooserwidget.h create mode 100644 src/imageplugins/inserttext/imageeffect_inserttext.cpp create mode 100644 src/imageplugins/inserttext/imageeffect_inserttext.h create mode 100644 src/imageplugins/inserttext/imageplugin_inserttext.cpp create mode 100644 src/imageplugins/inserttext/imageplugin_inserttext.h create mode 100644 src/imageplugins/inserttext/inserttexttool.cpp create mode 100644 src/imageplugins/inserttext/inserttexttool.h create mode 100644 src/imageplugins/inserttext/inserttextwidget.cpp create mode 100644 src/imageplugins/inserttext/inserttextwidget.h create mode 100644 src/imageplugins/lensdistortion/Makefile.am create mode 100644 src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion.desktop create mode 100644 src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion_ui.rc create mode 100644 src/imageplugins/lensdistortion/imageeffect_lensdistortion.cpp create mode 100644 src/imageplugins/lensdistortion/imageeffect_lensdistortion.h create mode 100644 src/imageplugins/lensdistortion/imageplugin_lensdistortion.cpp create mode 100644 src/imageplugins/lensdistortion/imageplugin_lensdistortion.h create mode 100644 src/imageplugins/lensdistortion/lensdistortion.cpp create mode 100644 src/imageplugins/lensdistortion/lensdistortion.h create mode 100644 src/imageplugins/lensdistortion/lensdistortiontool.cpp create mode 100644 src/imageplugins/lensdistortion/lensdistortiontool.h create mode 100644 src/imageplugins/lensdistortion/pixelaccess.cpp create mode 100644 src/imageplugins/lensdistortion/pixelaccess.h create mode 100644 src/imageplugins/noisereduction/Makefile.am create mode 100644 src/imageplugins/noisereduction/digikamimageplugin_noisereduction.desktop create mode 100644 src/imageplugins/noisereduction/digikamimageplugin_noisereduction_ui.rc create mode 100644 src/imageplugins/noisereduction/imageeffect_noisereduction.cpp create mode 100644 src/imageplugins/noisereduction/imageeffect_noisereduction.h create mode 100644 src/imageplugins/noisereduction/imageplugin_noisereduction.cpp create mode 100644 src/imageplugins/noisereduction/imageplugin_noisereduction.h create mode 100644 src/imageplugins/noisereduction/noisereduction.cpp create mode 100644 src/imageplugins/noisereduction/noisereduction.h create mode 100644 src/imageplugins/noisereduction/noisereductiontool.cpp create mode 100644 src/imageplugins/noisereduction/noisereductiontool.h create mode 100644 src/imageplugins/oilpaint/Makefile.am create mode 100644 src/imageplugins/oilpaint/digikamimageplugin_oilpaint.desktop create mode 100644 src/imageplugins/oilpaint/digikamimageplugin_oilpaint_ui.rc create mode 100644 src/imageplugins/oilpaint/imageeffect_oilpaint.cpp create mode 100644 src/imageplugins/oilpaint/imageeffect_oilpaint.h create mode 100644 src/imageplugins/oilpaint/imageplugin_oilpaint.cpp create mode 100644 src/imageplugins/oilpaint/imageplugin_oilpaint.h create mode 100644 src/imageplugins/oilpaint/oilpaint.cpp create mode 100644 src/imageplugins/oilpaint/oilpaint.h create mode 100644 src/imageplugins/oilpaint/oilpainttool.cpp create mode 100644 src/imageplugins/oilpaint/oilpainttool.h create mode 100644 src/imageplugins/perspective/Makefile.am create mode 100644 src/imageplugins/perspective/digikamimageplugin_perspective.desktop create mode 100644 src/imageplugins/perspective/digikamimageplugin_perspective_ui.rc create mode 100644 src/imageplugins/perspective/imageeffect_perspective.cpp create mode 100644 src/imageplugins/perspective/imageeffect_perspective.h create mode 100644 src/imageplugins/perspective/imageplugin_perspective.cpp create mode 100644 src/imageplugins/perspective/imageplugin_perspective.h create mode 100644 src/imageplugins/perspective/matrix.cpp create mode 100644 src/imageplugins/perspective/matrix.h create mode 100644 src/imageplugins/perspective/perspectivetool.cpp create mode 100644 src/imageplugins/perspective/perspectivetool.h create mode 100644 src/imageplugins/perspective/perspectivewidget.cpp create mode 100644 src/imageplugins/perspective/perspectivewidget.h create mode 100644 src/imageplugins/perspective/triangle.cpp create mode 100644 src/imageplugins/perspective/triangle.h create mode 100644 src/imageplugins/raindrop/Makefile.am create mode 100644 src/imageplugins/raindrop/digikamimageplugin_raindrop.desktop create mode 100644 src/imageplugins/raindrop/digikamimageplugin_raindrop_ui.rc create mode 100644 src/imageplugins/raindrop/imageeffect_raindrop.cpp create mode 100644 src/imageplugins/raindrop/imageeffect_raindrop.h create mode 100644 src/imageplugins/raindrop/imageplugin_raindrop.cpp create mode 100644 src/imageplugins/raindrop/imageplugin_raindrop.h create mode 100644 src/imageplugins/raindrop/raindrop.cpp create mode 100644 src/imageplugins/raindrop/raindrop.h create mode 100644 src/imageplugins/raindrop/raindroptool.cpp create mode 100644 src/imageplugins/raindrop/raindroptool.h create mode 100644 src/imageplugins/restoration/Makefile.am create mode 100644 src/imageplugins/restoration/digikamimageplugin_restoration.desktop create mode 100644 src/imageplugins/restoration/digikamimageplugin_restoration_ui.rc create mode 100644 src/imageplugins/restoration/imageeffect_restoration.cpp create mode 100644 src/imageplugins/restoration/imageeffect_restoration.h create mode 100644 src/imageplugins/restoration/imageplugin_restoration.cpp create mode 100644 src/imageplugins/restoration/imageplugin_restoration.h create mode 100644 src/imageplugins/restoration/restorationtool.cpp create mode 100644 src/imageplugins/restoration/restorationtool.h create mode 100644 src/imageplugins/sheartool/Makefile.am create mode 100644 src/imageplugins/sheartool/digikamimageplugin_sheartool.desktop create mode 100644 src/imageplugins/sheartool/digikamimageplugin_sheartool_ui.rc create mode 100644 src/imageplugins/sheartool/imageeffect_sheartool.cpp create mode 100644 src/imageplugins/sheartool/imageeffect_sheartool.h create mode 100644 src/imageplugins/sheartool/imageplugin_sheartool.cpp create mode 100644 src/imageplugins/sheartool/imageplugin_sheartool.h create mode 100644 src/imageplugins/sheartool/shear.cpp create mode 100644 src/imageplugins/sheartool/shear.h create mode 100644 src/imageplugins/sheartool/sheartool.cpp create mode 100644 src/imageplugins/sheartool/sheartool.h create mode 100644 src/imageplugins/superimpose/Makefile.am create mode 100644 src/imageplugins/superimpose/digikamimageplugin_superimpose.desktop create mode 100644 src/imageplugins/superimpose/digikamimageplugin_superimpose_ui.rc create mode 100644 src/imageplugins/superimpose/dirselectwidget.cpp create mode 100644 src/imageplugins/superimpose/dirselectwidget.h create mode 100644 src/imageplugins/superimpose/imageeffect_superimpose.cpp create mode 100644 src/imageplugins/superimpose/imageeffect_superimpose.h create mode 100644 src/imageplugins/superimpose/imageplugin_superimpose.cpp create mode 100644 src/imageplugins/superimpose/imageplugin_superimpose.h create mode 100644 src/imageplugins/superimpose/superimpose.cpp create mode 100644 src/imageplugins/superimpose/superimpose.h create mode 100644 src/imageplugins/superimpose/superimposetool.cpp create mode 100644 src/imageplugins/superimpose/superimposetool.h create mode 100644 src/imageplugins/superimpose/superimposewidget.cpp create mode 100644 src/imageplugins/superimpose/superimposewidget.h create mode 100644 src/imageplugins/texture/Makefile.am create mode 100644 src/imageplugins/texture/digikamimageplugin_texture.desktop create mode 100644 src/imageplugins/texture/digikamimageplugin_texture_ui.rc create mode 100644 src/imageplugins/texture/imageeffect_texture.cpp create mode 100644 src/imageplugins/texture/imageeffect_texture.h create mode 100644 src/imageplugins/texture/imageplugin_texture.cpp create mode 100644 src/imageplugins/texture/imageplugin_texture.h create mode 100644 src/imageplugins/texture/patterns/Makefile.am create mode 100644 src/imageplugins/texture/patterns/bluejean-texture.png create mode 100644 src/imageplugins/texture/patterns/bricks-texture.png create mode 100644 src/imageplugins/texture/patterns/bricks2-texture.png create mode 100644 src/imageplugins/texture/patterns/burlap-texture.png create mode 100644 src/imageplugins/texture/patterns/canvas-texture.png create mode 100644 src/imageplugins/texture/patterns/cellwood-texture.png create mode 100644 src/imageplugins/texture/patterns/fabric-texture.png create mode 100644 src/imageplugins/texture/patterns/marble-texture.png create mode 100644 src/imageplugins/texture/patterns/marble2-texture.png create mode 100644 src/imageplugins/texture/patterns/metalwire-texture.png create mode 100644 src/imageplugins/texture/patterns/modern-texture.png create mode 100644 src/imageplugins/texture/patterns/moss-texture.png create mode 100644 src/imageplugins/texture/patterns/paper-texture.png create mode 100644 src/imageplugins/texture/patterns/paper2-texture.png create mode 100644 src/imageplugins/texture/patterns/stone-texture.png create mode 100644 src/imageplugins/texture/patterns/wall-texture.png create mode 100644 src/imageplugins/texture/texture.cpp create mode 100644 src/imageplugins/texture/texture.h create mode 100644 src/imageplugins/texture/texturetool.cpp create mode 100644 src/imageplugins/texture/texturetool.h create mode 100644 src/imageplugins/whitebalance/Makefile.am create mode 100644 src/imageplugins/whitebalance/digikamimageplugin_whitebalance.desktop create mode 100644 src/imageplugins/whitebalance/digikamimageplugin_whitebalance_ui.rc create mode 100644 src/imageplugins/whitebalance/imageeffect_whitebalance.cpp create mode 100644 src/imageplugins/whitebalance/imageeffect_whitebalance.h create mode 100644 src/imageplugins/whitebalance/imageplugin_whitebalance.cpp create mode 100644 src/imageplugins/whitebalance/imageplugin_whitebalance.h create mode 100644 src/imageplugins/whitebalance/whitebalancetool.cpp create mode 100644 src/imageplugins/whitebalance/whitebalancetool.h create mode 100644 src/libs/Makefile.am create mode 100644 src/libs/curves/Makefile.am create mode 100644 src/libs/curves/imagecurves.cpp create mode 100644 src/libs/curves/imagecurves.h create mode 100644 src/libs/dialogs/Makefile.am create mode 100644 src/libs/dialogs/ctrlpaneldlg.cpp create mode 100644 src/libs/dialogs/ctrlpaneldlg.h create mode 100644 src/libs/dialogs/deletedialog.cpp create mode 100644 src/libs/dialogs/deletedialog.h create mode 100644 src/libs/dialogs/deletedialogbase.ui create mode 100644 src/libs/dialogs/dprogressdlg.cpp create mode 100644 src/libs/dialogs/dprogressdlg.h create mode 100644 src/libs/dialogs/iccprofileinfodlg.cpp create mode 100644 src/libs/dialogs/iccprofileinfodlg.h create mode 100644 src/libs/dialogs/imagedialog.cpp create mode 100644 src/libs/dialogs/imagedialog.h create mode 100644 src/libs/dialogs/imagedlgbase.cpp create mode 100644 src/libs/dialogs/imagedlgbase.h create mode 100644 src/libs/dialogs/imageguidedlg.cpp create mode 100644 src/libs/dialogs/imageguidedlg.h create mode 100644 src/libs/dialogs/rawcameradlg.cpp create mode 100644 src/libs/dialogs/rawcameradlg.h create mode 100644 src/libs/dimg/Makefile.am create mode 100644 src/libs/dimg/README create mode 100644 src/libs/dimg/dcolor.cpp create mode 100644 src/libs/dimg/dcolor.h create mode 100644 src/libs/dimg/dcolorblend.h create mode 100644 src/libs/dimg/dcolorcomposer.cpp create mode 100644 src/libs/dimg/dcolorcomposer.h create mode 100644 src/libs/dimg/dcolorpixelaccess.h create mode 100644 src/libs/dimg/ddebug.cpp create mode 100644 src/libs/dimg/ddebug.h create mode 100644 src/libs/dimg/dimg.cpp create mode 100644 src/libs/dimg/dimg.h create mode 100644 src/libs/dimg/dimgprivate.h create mode 100644 src/libs/dimg/dimgscale.cpp create mode 100644 src/libs/dimg/drawdecoding.h create mode 100644 src/libs/dimg/exposurecontainer.h create mode 100644 src/libs/dimg/filters/Makefile.am create mode 100644 src/libs/dimg/filters/bcgmodifier.cpp create mode 100644 src/libs/dimg/filters/bcgmodifier.h create mode 100644 src/libs/dimg/filters/colormodifier.cpp create mode 100644 src/libs/dimg/filters/colormodifier.h create mode 100644 src/libs/dimg/filters/dimggaussianblur.cpp create mode 100644 src/libs/dimg/filters/dimggaussianblur.h create mode 100644 src/libs/dimg/filters/dimgimagefilters.cpp create mode 100644 src/libs/dimg/filters/dimgimagefilters.h create mode 100644 src/libs/dimg/filters/dimgsharpen.cpp create mode 100644 src/libs/dimg/filters/dimgsharpen.h create mode 100644 src/libs/dimg/filters/dimgthreadedfilter.cpp create mode 100644 src/libs/dimg/filters/dimgthreadedfilter.h create mode 100644 src/libs/dimg/filters/hslmodifier.cpp create mode 100644 src/libs/dimg/filters/hslmodifier.h create mode 100644 src/libs/dimg/filters/icctransform.cpp create mode 100644 src/libs/dimg/filters/icctransform.h create mode 100644 src/libs/dimg/loaders/Makefile.am create mode 100644 src/libs/dimg/loaders/README create mode 100644 src/libs/dimg/loaders/dimgloader.cpp create mode 100644 src/libs/dimg/loaders/dimgloader.h create mode 100644 src/libs/dimg/loaders/dimgloaderobserver.h create mode 100644 src/libs/dimg/loaders/iccjpeg.c create mode 100644 src/libs/dimg/loaders/iccjpeg.h create mode 100644 src/libs/dimg/loaders/jp2kloader.cpp create mode 100644 src/libs/dimg/loaders/jp2kloader.h create mode 100644 src/libs/dimg/loaders/jp2ksettings.cpp create mode 100644 src/libs/dimg/loaders/jp2ksettings.h create mode 100644 src/libs/dimg/loaders/jpegloader.cpp create mode 100644 src/libs/dimg/loaders/jpegloader.h create mode 100644 src/libs/dimg/loaders/jpegsettings.cpp create mode 100644 src/libs/dimg/loaders/jpegsettings.h create mode 100644 src/libs/dimg/loaders/pngloader.cpp create mode 100644 src/libs/dimg/loaders/pngloader.h create mode 100644 src/libs/dimg/loaders/pngsettings.cpp create mode 100644 src/libs/dimg/loaders/pngsettings.h create mode 100644 src/libs/dimg/loaders/ppmloader.cpp create mode 100644 src/libs/dimg/loaders/ppmloader.h create mode 100644 src/libs/dimg/loaders/qimageloader.cpp create mode 100644 src/libs/dimg/loaders/qimageloader.h create mode 100644 src/libs/dimg/loaders/rawloader.cpp create mode 100644 src/libs/dimg/loaders/rawloader.h create mode 100644 src/libs/dimg/loaders/tiffloader.cpp create mode 100644 src/libs/dimg/loaders/tiffloader.h create mode 100644 src/libs/dimg/loaders/tiffsettings.cpp create mode 100644 src/libs/dimg/loaders/tiffsettings.h create mode 100644 src/libs/dmetadata/Makefile.am create mode 100644 src/libs/dmetadata/dmetadata.cpp create mode 100644 src/libs/dmetadata/dmetadata.h create mode 100644 src/libs/dmetadata/photoinfocontainer.h create mode 100644 src/libs/greycstoration/CImg.h create mode 100644 src/libs/greycstoration/LICENSE.txt create mode 100644 src/libs/greycstoration/Makefile.am create mode 100644 src/libs/greycstoration/greycstoration.h create mode 100644 src/libs/greycstoration/greycstorationiface.cpp create mode 100644 src/libs/greycstoration/greycstorationiface.h create mode 100644 src/libs/greycstoration/greycstorationsettings.h create mode 100644 src/libs/greycstoration/greycstorationwidget.cpp create mode 100644 src/libs/greycstoration/greycstorationwidget.h create mode 100644 src/libs/histogram/Makefile.am create mode 100644 src/libs/histogram/imagehistogram.cpp create mode 100644 src/libs/histogram/imagehistogram.h create mode 100644 src/libs/imageproperties/Makefile.am create mode 100644 src/libs/imageproperties/cameraitempropertiestab.cpp create mode 100644 src/libs/imageproperties/cameraitempropertiestab.h create mode 100644 src/libs/imageproperties/imagedescedittab.cpp create mode 100644 src/libs/imageproperties/imagedescedittab.h create mode 100644 src/libs/imageproperties/imagepropertiescolorstab.cpp create mode 100644 src/libs/imageproperties/imagepropertiescolorstab.h create mode 100644 src/libs/imageproperties/imagepropertiesmetadatatab.cpp create mode 100644 src/libs/imageproperties/imagepropertiesmetadatatab.h create mode 100644 src/libs/imageproperties/imagepropertiessidebar.cpp create mode 100644 src/libs/imageproperties/imagepropertiessidebar.h create mode 100644 src/libs/imageproperties/imagepropertiessidebarcamgui.cpp create mode 100644 src/libs/imageproperties/imagepropertiessidebarcamgui.h create mode 100644 src/libs/imageproperties/imagepropertiessidebardb.cpp create mode 100644 src/libs/imageproperties/imagepropertiessidebardb.h create mode 100644 src/libs/imageproperties/imagepropertiestab.cpp create mode 100644 src/libs/imageproperties/imagepropertiestab.h create mode 100644 src/libs/imageproperties/navigatebartab.cpp create mode 100644 src/libs/imageproperties/navigatebartab.h create mode 100644 src/libs/imageproperties/navigatebarwidget.cpp create mode 100644 src/libs/imageproperties/navigatebarwidget.h create mode 100644 src/libs/imageproperties/talbumlistview.cpp create mode 100644 src/libs/imageproperties/talbumlistview.h create mode 100644 src/libs/jpegutils/Makefile.am create mode 100644 src/libs/jpegutils/jinclude.h create mode 100644 src/libs/jpegutils/jpegint.h create mode 100644 src/libs/jpegutils/jpegutils.cpp create mode 100644 src/libs/jpegutils/jpegutils.h create mode 100644 src/libs/jpegutils/libjpeg62.README create mode 100644 src/libs/jpegutils/transupp.cpp create mode 100644 src/libs/jpegutils/transupp.h create mode 100644 src/libs/levels/Makefile.am create mode 100644 src/libs/levels/imagelevels.cpp create mode 100644 src/libs/levels/imagelevels.h create mode 100644 src/libs/lprof/Makefile.am create mode 100644 src/libs/lprof/cmshull.cpp create mode 100644 src/libs/lprof/cmslm.cpp create mode 100644 src/libs/lprof/cmslnr.cpp create mode 100644 src/libs/lprof/cmsmatn.cpp create mode 100644 src/libs/lprof/cmsmkmsh.cpp create mode 100644 src/libs/lprof/cmsmntr.cpp create mode 100644 src/libs/lprof/cmsoutl.cpp create mode 100644 src/libs/lprof/cmspcoll.cpp create mode 100644 src/libs/lprof/cmsprf.cpp create mode 100644 src/libs/lprof/cmsreg.cpp create mode 100644 src/libs/lprof/cmsscn.cpp create mode 100644 src/libs/lprof/cmssheet.cpp create mode 100644 src/libs/lprof/lcmsprf.h create mode 100644 src/libs/sqlite2/Makefile.am create mode 100644 src/libs/sqlite2/README create mode 100644 src/libs/sqlite2/attach.c create mode 100644 src/libs/sqlite2/auth.c create mode 100644 src/libs/sqlite2/btree.c create mode 100644 src/libs/sqlite2/btree.h create mode 100644 src/libs/sqlite2/btree_rb.c create mode 100644 src/libs/sqlite2/build.c create mode 100644 src/libs/sqlite2/copy.c create mode 100644 src/libs/sqlite2/date.c create mode 100644 src/libs/sqlite2/delete.c create mode 100644 src/libs/sqlite2/encode.c create mode 100644 src/libs/sqlite2/expr.c create mode 100644 src/libs/sqlite2/func.c create mode 100644 src/libs/sqlite2/hash.c create mode 100644 src/libs/sqlite2/hash.h create mode 100644 src/libs/sqlite2/insert.c create mode 100644 src/libs/sqlite2/main.c create mode 100644 src/libs/sqlite2/opcodes.c create mode 100644 src/libs/sqlite2/opcodes.h create mode 100644 src/libs/sqlite2/os.c create mode 100644 src/libs/sqlite2/os.h create mode 100644 src/libs/sqlite2/pager.c create mode 100644 src/libs/sqlite2/pager.h create mode 100644 src/libs/sqlite2/parse.c create mode 100644 src/libs/sqlite2/parse.h create mode 100644 src/libs/sqlite2/pragma.c create mode 100644 src/libs/sqlite2/printf.c create mode 100644 src/libs/sqlite2/random.c create mode 100644 src/libs/sqlite2/select.c create mode 100644 src/libs/sqlite2/shell.c create mode 100644 src/libs/sqlite2/sqlite.h create mode 100644 src/libs/sqlite2/sqliteInt.h create mode 100644 src/libs/sqlite2/table.c create mode 100644 src/libs/sqlite2/tokenize.c create mode 100644 src/libs/sqlite2/trigger.c create mode 100644 src/libs/sqlite2/update.c create mode 100644 src/libs/sqlite2/util.c create mode 100644 src/libs/sqlite2/vacuum.c create mode 100644 src/libs/sqlite2/vdbe.c create mode 100644 src/libs/sqlite2/vdbe.h create mode 100644 src/libs/sqlite2/vdbeInt.h create mode 100644 src/libs/sqlite2/vdbeaux.c create mode 100644 src/libs/sqlite2/where.c create mode 100644 src/libs/sqlite3/Makefile.am create mode 100644 src/libs/sqlite3/sqlite3.c create mode 100644 src/libs/sqlite3/sqlite3.h create mode 100644 src/libs/themeengine/Makefile.am create mode 100644 src/libs/themeengine/texture.cpp create mode 100644 src/libs/themeengine/texture.h create mode 100644 src/libs/themeengine/theme.cpp create mode 100644 src/libs/themeengine/theme.h create mode 100644 src/libs/themeengine/themeengine.cpp create mode 100644 src/libs/themeengine/themeengine.h create mode 100644 src/libs/threadimageio/Makefile.am create mode 100644 src/libs/threadimageio/loadingcache.cpp create mode 100644 src/libs/threadimageio/loadingcache.h create mode 100644 src/libs/threadimageio/loadingcacheinterface.cpp create mode 100644 src/libs/threadimageio/loadingcacheinterface.h create mode 100644 src/libs/threadimageio/loadingdescription.cpp create mode 100644 src/libs/threadimageio/loadingdescription.h create mode 100644 src/libs/threadimageio/loadsavetask.cpp create mode 100644 src/libs/threadimageio/loadsavetask.h create mode 100644 src/libs/threadimageio/loadsavethread.cpp create mode 100644 src/libs/threadimageio/loadsavethread.h create mode 100644 src/libs/threadimageio/managedloadsavethread.cpp create mode 100644 src/libs/threadimageio/managedloadsavethread.h create mode 100644 src/libs/threadimageio/previewloadthread.cpp create mode 100644 src/libs/threadimageio/previewloadthread.h create mode 100644 src/libs/threadimageio/previewtask.cpp create mode 100644 src/libs/threadimageio/previewtask.h create mode 100644 src/libs/threadimageio/sharedloadsavethread.cpp create mode 100644 src/libs/threadimageio/sharedloadsavethread.h create mode 100644 src/libs/thumbbar/Makefile.am create mode 100644 src/libs/thumbbar/thumbbar.cpp create mode 100644 src/libs/thumbbar/thumbbar.h create mode 100644 src/libs/thumbbar/thumbnailjob.cpp create mode 100644 src/libs/thumbbar/thumbnailjob.h create mode 100644 src/libs/whitebalance/Makefile.am create mode 100644 src/libs/whitebalance/blackbody.h create mode 100644 src/libs/whitebalance/whitebalance.cpp create mode 100644 src/libs/whitebalance/whitebalance.h create mode 100644 src/libs/widgets/Makefile.am create mode 100644 src/libs/widgets/common/Makefile.am create mode 100644 src/libs/widgets/common/colorgradientwidget.cpp create mode 100644 src/libs/widgets/common/colorgradientwidget.h create mode 100644 src/libs/widgets/common/curveswidget.cpp create mode 100644 src/libs/widgets/common/curveswidget.h create mode 100644 src/libs/widgets/common/dcursortracker.cpp create mode 100644 src/libs/widgets/common/dcursortracker.h create mode 100644 src/libs/widgets/common/dlogoaction.cpp create mode 100644 src/libs/widgets/common/dlogoaction.h create mode 100644 src/libs/widgets/common/dpopupmenu.cpp create mode 100644 src/libs/widgets/common/dpopupmenu.h create mode 100644 src/libs/widgets/common/filesaveoptionsbox.cpp create mode 100644 src/libs/widgets/common/filesaveoptionsbox.h create mode 100644 src/libs/widgets/common/histogramwidget.cpp create mode 100644 src/libs/widgets/common/histogramwidget.h create mode 100644 src/libs/widgets/common/paniconwidget.cpp create mode 100644 src/libs/widgets/common/paniconwidget.h create mode 100644 src/libs/widgets/common/previewwidget.cpp create mode 100644 src/libs/widgets/common/previewwidget.h create mode 100644 src/libs/widgets/common/searchtextbar.cpp create mode 100644 src/libs/widgets/common/searchtextbar.h create mode 100644 src/libs/widgets/common/sidebar.cpp create mode 100644 src/libs/widgets/common/sidebar.h create mode 100644 src/libs/widgets/common/splashscreen.cpp create mode 100644 src/libs/widgets/common/splashscreen.h create mode 100644 src/libs/widgets/common/squeezedcombobox.cpp create mode 100644 src/libs/widgets/common/squeezedcombobox.h create mode 100644 src/libs/widgets/common/statusled.cpp create mode 100644 src/libs/widgets/common/statusled.h create mode 100644 src/libs/widgets/common/statusnavigatebar.cpp create mode 100644 src/libs/widgets/common/statusnavigatebar.h create mode 100644 src/libs/widgets/common/statusprogressbar.cpp create mode 100644 src/libs/widgets/common/statusprogressbar.h create mode 100644 src/libs/widgets/common/statuszoombar.cpp create mode 100644 src/libs/widgets/common/statuszoombar.h create mode 100644 src/libs/widgets/iccprofiles/Makefile.am create mode 100644 src/libs/widgets/iccprofiles/cietonguewidget.cpp create mode 100644 src/libs/widgets/iccprofiles/cietonguewidget.h create mode 100644 src/libs/widgets/iccprofiles/iccpreviewwidget.cpp create mode 100644 src/libs/widgets/iccprofiles/iccpreviewwidget.h create mode 100644 src/libs/widgets/iccprofiles/iccprofilewidget.cpp create mode 100644 src/libs/widgets/iccprofiles/iccprofilewidget.h create mode 100644 src/libs/widgets/imageplugins/Makefile.am create mode 100644 src/libs/widgets/imageplugins/imageguidewidget.cpp create mode 100644 src/libs/widgets/imageplugins/imageguidewidget.h create mode 100644 src/libs/widgets/imageplugins/imagepanelwidget.cpp create mode 100644 src/libs/widgets/imageplugins/imagepanelwidget.h create mode 100644 src/libs/widgets/imageplugins/imagepaniconwidget.cpp create mode 100644 src/libs/widgets/imageplugins/imagepaniconwidget.h create mode 100644 src/libs/widgets/imageplugins/imagepannelwidget.cpp create mode 100644 src/libs/widgets/imageplugins/imagepannelwidget.h create mode 100644 src/libs/widgets/imageplugins/imageregionwidget.cpp create mode 100644 src/libs/widgets/imageplugins/imageregionwidget.h create mode 100644 src/libs/widgets/imageplugins/imagewidget.cpp create mode 100644 src/libs/widgets/imageplugins/imagewidget.h create mode 100644 src/libs/widgets/imageplugins/listboxpreviewitem.cpp create mode 100644 src/libs/widgets/imageplugins/listboxpreviewitem.h create mode 100644 src/libs/widgets/metadata/Makefile.am create mode 100644 src/libs/widgets/metadata/exifwidget.cpp create mode 100644 src/libs/widgets/metadata/exifwidget.h create mode 100644 src/libs/widgets/metadata/gpswidget.cpp create mode 100644 src/libs/widgets/metadata/gpswidget.h create mode 100644 src/libs/widgets/metadata/iptcwidget.cpp create mode 100644 src/libs/widgets/metadata/iptcwidget.h create mode 100644 src/libs/widgets/metadata/makernotewidget.cpp create mode 100644 src/libs/widgets/metadata/makernotewidget.h create mode 100644 src/libs/widgets/metadata/mdkeylistviewitem.cpp create mode 100644 src/libs/widgets/metadata/mdkeylistviewitem.h create mode 100644 src/libs/widgets/metadata/metadatalistview.cpp create mode 100644 src/libs/widgets/metadata/metadatalistview.h create mode 100644 src/libs/widgets/metadata/metadatalistviewitem.cpp create mode 100644 src/libs/widgets/metadata/metadatalistviewitem.h create mode 100644 src/libs/widgets/metadata/metadatawidget.cpp create mode 100644 src/libs/widgets/metadata/metadatawidget.h create mode 100644 src/libs/widgets/metadata/worldmapwidget.cpp create mode 100644 src/libs/widgets/metadata/worldmapwidget.h create mode 100644 src/showfoto/Makefile.am create mode 100644 src/showfoto/main.cpp create mode 100644 src/showfoto/setup/Makefile.am create mode 100644 src/showfoto/setup/setup.cpp create mode 100644 src/showfoto/setup/setup.h create mode 100644 src/showfoto/setup/setupeditor.cpp create mode 100644 src/showfoto/setup/setupeditor.h create mode 100644 src/showfoto/setup/setuptooltip.cpp create mode 100644 src/showfoto/setup/setuptooltip.h create mode 100644 src/showfoto/showfoto.cpp create mode 100644 src/showfoto/showfoto.desktop create mode 100644 src/showfoto/showfoto.h create mode 100644 src/showfoto/showfotoui.rc create mode 100644 src/tdeioslave/Makefile.am create mode 100644 src/tdeioslave/digikamalbums.cpp create mode 100644 src/tdeioslave/digikamalbums.h create mode 100644 src/tdeioslave/digikamalbums.protocol create mode 100644 src/tdeioslave/digikamdates.cpp create mode 100644 src/tdeioslave/digikamdates.h create mode 100644 src/tdeioslave/digikamdates.protocol create mode 100644 src/tdeioslave/digikamsearch.cpp create mode 100644 src/tdeioslave/digikamsearch.h create mode 100644 src/tdeioslave/digikamsearch.protocol create mode 100644 src/tdeioslave/digikamtags.cpp create mode 100644 src/tdeioslave/digikamtags.h create mode 100644 src/tdeioslave/digikamtags.protocol create mode 100644 src/tdeioslave/digikamthumbnail.cpp create mode 100644 src/tdeioslave/digikamthumbnail.h create mode 100644 src/tdeioslave/digikamthumbnail.protocol create mode 100644 src/tdeioslave/sqlitedb.cpp create mode 100644 src/tdeioslave/sqlitedb.h create mode 100644 src/themedesigner/Makefile.am create mode 100644 src/themedesigner/main.cpp create mode 100644 src/themedesigner/mainwindow.cpp create mode 100644 src/themedesigner/mainwindow.h create mode 100644 src/themedesigner/themedicongroupitem.cpp create mode 100644 src/themedesigner/themedicongroupitem.h create mode 100644 src/themedesigner/themediconitem.cpp create mode 100644 src/themedesigner/themediconitem.h create mode 100644 src/themedesigner/themediconview.cpp create mode 100644 src/themedesigner/themediconview.h create mode 100644 src/tips create mode 100644 src/utilities/Makefile.am create mode 100644 src/utilities/batch/Makefile.am create mode 100644 src/utilities/batch/batchalbumssyncmetadata.cpp create mode 100644 src/utilities/batch/batchalbumssyncmetadata.h create mode 100644 src/utilities/batch/batchsyncmetadata.cpp create mode 100644 src/utilities/batch/batchsyncmetadata.h create mode 100644 src/utilities/batch/batchthumbsgenerator.cpp create mode 100644 src/utilities/batch/batchthumbsgenerator.h create mode 100644 src/utilities/batch/imageinfoalbumsjob.cpp create mode 100644 src/utilities/batch/imageinfoalbumsjob.h create mode 100644 src/utilities/batch/imageinfojob.cpp create mode 100644 src/utilities/batch/imageinfojob.h create mode 100644 src/utilities/cameragui/Makefile.am create mode 100644 src/utilities/cameragui/albumselectdialog.cpp create mode 100644 src/utilities/cameragui/albumselectdialog.h create mode 100644 src/utilities/cameragui/animwidget.cpp create mode 100644 src/utilities/cameragui/animwidget.h create mode 100644 src/utilities/cameragui/cameracontroller.cpp create mode 100644 src/utilities/cameragui/cameracontroller.h create mode 100644 src/utilities/cameragui/camerafolderdialog.cpp create mode 100644 src/utilities/cameragui/camerafolderdialog.h create mode 100644 src/utilities/cameragui/camerafolderitem.cpp create mode 100644 src/utilities/cameragui/camerafolderitem.h create mode 100644 src/utilities/cameragui/camerafolderview.cpp create mode 100644 src/utilities/cameragui/camerafolderview.h create mode 100644 src/utilities/cameragui/cameraiconitem.cpp create mode 100644 src/utilities/cameragui/cameraiconitem.h create mode 100644 src/utilities/cameragui/cameraiconview.cpp create mode 100644 src/utilities/cameragui/cameraiconview.h create mode 100644 src/utilities/cameragui/camerainfodialog.cpp create mode 100644 src/utilities/cameragui/camerainfodialog.h create mode 100644 src/utilities/cameragui/cameraui.cpp create mode 100644 src/utilities/cameragui/cameraui.h create mode 100644 src/utilities/cameragui/dkcamera.cpp create mode 100644 src/utilities/cameragui/dkcamera.h create mode 100644 src/utilities/cameragui/downloadsettingscontainer.h create mode 100644 src/utilities/cameragui/freespacewidget.cpp create mode 100644 src/utilities/cameragui/freespacewidget.h create mode 100644 src/utilities/cameragui/gpcamera.cpp create mode 100644 src/utilities/cameragui/gpcamera.h create mode 100644 src/utilities/cameragui/gpiteminfo.cpp create mode 100644 src/utilities/cameragui/gpiteminfo.h create mode 100644 src/utilities/cameragui/mtqueue.h create mode 100644 src/utilities/cameragui/renamecustomizer.cpp create mode 100644 src/utilities/cameragui/renamecustomizer.h create mode 100644 src/utilities/cameragui/umscamera.cpp create mode 100644 src/utilities/cameragui/umscamera.h create mode 100644 src/utilities/hotplug/Makefile.am create mode 100644 src/utilities/hotplug/configure.in.in create mode 100755 src/utilities/hotplug/digikam-camera create mode 100644 src/utilities/hotplug/digikam-download.desktop.in create mode 100644 src/utilities/hotplug/digikam-gphoto2-camera.desktop.in create mode 100644 src/utilities/hotplug/digikam-mount-and-download.desktop.in create mode 100644 src/utilities/imageeditor/Makefile.am create mode 100644 src/utilities/imageeditor/canvas/Makefile.am create mode 100644 src/utilities/imageeditor/canvas/canvas.cpp create mode 100644 src/utilities/imageeditor/canvas/canvas.h create mode 100644 src/utilities/imageeditor/canvas/colorcorrectiondlg.cpp create mode 100644 src/utilities/imageeditor/canvas/colorcorrectiondlg.h create mode 100644 src/utilities/imageeditor/canvas/dimginterface.cpp create mode 100644 src/utilities/imageeditor/canvas/dimginterface.h create mode 100644 src/utilities/imageeditor/canvas/iccsettingscontainer.h create mode 100644 src/utilities/imageeditor/canvas/imageplugin.cpp create mode 100644 src/utilities/imageeditor/canvas/imageplugin.h create mode 100644 src/utilities/imageeditor/canvas/imagepluginloader.cpp create mode 100644 src/utilities/imageeditor/canvas/imagepluginloader.h create mode 100644 src/utilities/imageeditor/canvas/iofilesettingscontainer.h create mode 100644 src/utilities/imageeditor/canvas/undoaction.cpp create mode 100644 src/utilities/imageeditor/canvas/undoaction.h create mode 100644 src/utilities/imageeditor/canvas/undocache.cpp create mode 100644 src/utilities/imageeditor/canvas/undocache.h create mode 100644 src/utilities/imageeditor/canvas/undomanager.cpp create mode 100644 src/utilities/imageeditor/canvas/undomanager.h create mode 100644 src/utilities/imageeditor/editor/Makefile.am create mode 100644 src/utilities/imageeditor/editor/digikamimageplugin.desktop create mode 100644 src/utilities/imageeditor/editor/digikamimagewindowui.rc create mode 100644 src/utilities/imageeditor/editor/editorstackview.cpp create mode 100644 src/utilities/imageeditor/editor/editorstackview.h create mode 100644 src/utilities/imageeditor/editor/editortool.cpp create mode 100644 src/utilities/imageeditor/editor/editortool.h create mode 100644 src/utilities/imageeditor/editor/editortooliface.cpp create mode 100644 src/utilities/imageeditor/editor/editortooliface.h create mode 100644 src/utilities/imageeditor/editor/editortoolsettings.cpp create mode 100644 src/utilities/imageeditor/editor/editortoolsettings.h create mode 100644 src/utilities/imageeditor/editor/editorwindow.cpp create mode 100644 src/utilities/imageeditor/editor/editorwindow.h create mode 100644 src/utilities/imageeditor/editor/editorwindowprivate.h create mode 100644 src/utilities/imageeditor/editor/imageiface.cpp create mode 100644 src/utilities/imageeditor/editor/imageiface.h create mode 100644 src/utilities/imageeditor/editor/imagewindow.cpp create mode 100644 src/utilities/imageeditor/editor/imagewindow.h create mode 100644 src/utilities/imageeditor/editor/savingcontextcontainer.h create mode 100644 src/utilities/imageeditor/rawimport/Makefile.am create mode 100644 src/utilities/imageeditor/rawimport/rawimport.cpp create mode 100644 src/utilities/imageeditor/rawimport/rawimport.h create mode 100644 src/utilities/imageeditor/rawimport/rawpostprocessing.cpp create mode 100644 src/utilities/imageeditor/rawimport/rawpostprocessing.h create mode 100644 src/utilities/imageeditor/rawimport/rawpreview.cpp create mode 100644 src/utilities/imageeditor/rawimport/rawpreview.h create mode 100644 src/utilities/imageeditor/rawimport/rawsettingsbox.cpp create mode 100644 src/utilities/imageeditor/rawimport/rawsettingsbox.h create mode 100644 src/utilities/imageeditor/tools/Makefile.am create mode 100644 src/utilities/imageeditor/tools/imageprint.cpp create mode 100644 src/utilities/imageeditor/tools/imageprint.h create mode 100644 src/utilities/imageeditor/tools/imageresize.cpp create mode 100644 src/utilities/imageeditor/tools/imageresize.h create mode 100644 src/utilities/lighttable/Makefile.am create mode 100644 src/utilities/lighttable/lighttablebar.cpp create mode 100644 src/utilities/lighttable/lighttablebar.h create mode 100644 src/utilities/lighttable/lighttablepreview.cpp create mode 100644 src/utilities/lighttable/lighttablepreview.h create mode 100644 src/utilities/lighttable/lighttableview.cpp create mode 100644 src/utilities/lighttable/lighttableview.h create mode 100644 src/utilities/lighttable/lighttablewindow.cpp create mode 100644 src/utilities/lighttable/lighttablewindow.h create mode 100644 src/utilities/lighttable/lighttablewindowprivate.h create mode 100644 src/utilities/lighttable/lighttablewindowui.rc create mode 100644 src/utilities/scripts/Makefile.am create mode 100644 src/utilities/scripts/digitaglinktree create mode 100644 src/utilities/scripts/digitaglinktree.1 create mode 100644 src/utilities/setup/Makefile.am create mode 100644 src/utilities/setup/cameraselection.cpp create mode 100644 src/utilities/setup/cameraselection.h create mode 100644 src/utilities/setup/setup.cpp create mode 100644 src/utilities/setup/setup.h create mode 100644 src/utilities/setup/setupcamera.cpp create mode 100644 src/utilities/setup/setupcamera.h create mode 100644 src/utilities/setup/setupcollections.cpp create mode 100644 src/utilities/setup/setupcollections.h create mode 100644 src/utilities/setup/setupdcraw.cpp create mode 100644 src/utilities/setup/setupdcraw.h create mode 100644 src/utilities/setup/setupeditor.cpp create mode 100644 src/utilities/setup/setupeditor.h create mode 100644 src/utilities/setup/setupgeneral.cpp create mode 100644 src/utilities/setup/setupgeneral.h create mode 100644 src/utilities/setup/setupicc.cpp create mode 100644 src/utilities/setup/setupicc.h create mode 100644 src/utilities/setup/setupidentity.cpp create mode 100644 src/utilities/setup/setupidentity.h create mode 100644 src/utilities/setup/setupiofiles.cpp create mode 100644 src/utilities/setup/setupiofiles.h create mode 100644 src/utilities/setup/setuplighttable.cpp create mode 100644 src/utilities/setup/setuplighttable.h create mode 100644 src/utilities/setup/setupmetadata.cpp create mode 100644 src/utilities/setup/setupmetadata.h create mode 100644 src/utilities/setup/setupmime.cpp create mode 100644 src/utilities/setup/setupmime.h create mode 100644 src/utilities/setup/setupmisc.cpp create mode 100644 src/utilities/setup/setupmisc.h create mode 100644 src/utilities/setup/setupplugins.cpp create mode 100644 src/utilities/setup/setupplugins.h create mode 100644 src/utilities/setup/setupslideshow.cpp create mode 100644 src/utilities/setup/setupslideshow.h create mode 100644 src/utilities/setup/setuptooltip.cpp create mode 100644 src/utilities/setup/setuptooltip.h create mode 100644 src/utilities/slideshow/Makefile.am create mode 100644 src/utilities/slideshow/slideshow.cpp create mode 100644 src/utilities/slideshow/slideshow.h create mode 100644 src/utilities/slideshow/slideshowsettings.h create mode 100644 src/utilities/slideshow/toolbar.cpp create mode 100644 src/utilities/slideshow/toolbar.h (limited to 'src') diff --git a/src/CMakeL10n.txt b/src/CMakeL10n.txt new file mode 100644 index 00000000..b326219c --- /dev/null +++ b/src/CMakeL10n.txt @@ -0,0 +1,6 @@ +##### create translation templates ############## + +tde_l10n_create_template( + CATALOG "messages/digikam" + SOURCES "." "tips" +) diff --git a/src/COPYING-DOCS b/src/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/src/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/src/DBSCHEMA.ODS b/src/DBSCHEMA.ODS new file mode 100644 index 00000000..4e016a4b Binary files /dev/null and b/src/DBSCHEMA.ODS differ diff --git a/src/DESIGN b/src/DESIGN new file mode 100644 index 00000000..9b16c519 --- /dev/null +++ b/src/DESIGN @@ -0,0 +1,18 @@ +This file is ment to help people get started hacking on digiKam. It will get +you up to speed on a couple of structures used. We only started to document +just before digiKam 0.8, so don't expect to much, but whenever you hack some +please update this file as well. + +scanlib +Scanlib is a library that takes care of scanning the filesystem for new files +and adds them in the database and checking for missing info in the database so +that it can be included: if date is empty, it adds the exif or modification +date (in that order) and the comment to database. If the file is not present +in the database, make sure to add the file to the database and insert the date +and comments. + +pixmapmanager +Since there are date based folders, the number of pixmaps which could be +kept in memory could potentially become too large. The pixmapmanager +maintains a fixed size cache of thumbnails and loads pixmaps on demand. + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..4741808b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,25 @@ +COMPILE_FIRST = libs + +SUBDIRS = libs utilities digikam showfoto tdeioslave imageplugins themedesigner + +EXTRA_DIST = AUTHORS COPYING ChangeLog INSTALL README TODO digikam.lsm + +AUTOMAKE_OPTIONS = foreign + +MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 configure.files + + +tip_DATA = tips +tipdir = $(kde_datadir)/digikam + +messages: rc.cpp + $(EXTRACTRC) `find . -name "*.ui"` >> rc.cpp + $(EXTRACTRC) `find . -name "*.rc"` >> rc.cpp + $(PREPARETIPS) > tips.cpp + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.cxx -o -name \*.ecpp -o -name \*.C`; \ + if test -n "$$LIST"; then \ + $(XGETTEXT) $$LIST -o $(podir)/digikam.pot; \ + fi + rm -f tips.cpp + +include ../admin/Doxyfile.am diff --git a/src/NEWS.0.9.0 b/src/NEWS.0.9.0 new file mode 100644 index 00000000..ad8e1cb1 --- /dev/null +++ b/src/NEWS.0.9.0 @@ -0,0 +1,397 @@ +---------------------------------------------------------------------------------------------------- +digiKam and DigikamImagePlugins 0.9.0 - Release date: 2006-18-12 + +NEW FEATURES : + +*** 0.9.0-beta1 ******************************************************* + +General : Sidebars are used at all to display metadata, comments & tags, and file properties. +General : Removed imlib2 and libkexif depencies. Add Exiv2 and LCMS depencies. +General : New API in digiKam core named DImg to manage image data in 8 and 16 bits colors depth. +General : New API in digiKam core named DMetadata to manage image metadata. +General : Ported all image filter algorithms to new DImg API. Support of 16 bits images. +General : New metadata viewers based on Exiv2 : Exif viewer, MakerNote viewer, IPTC viewer, + and GPS locator. +General : New ICC profiles profile viewer based on LCMS. +General : New powerful widget to display CIE color space used by ICC profiles. +General : New implementation to load/save images file using a separate thread. +General : Tags/Rating/DateTime/Comments/PhotographersID/Copyrights are now stored in Exif + and/or IPTC metadata tags. +General : digiKam comments are now store in JPEG file to JFIF section, Exif UserComments tags + and Caption Iptc tags. +General : New fast preview pictures mode embedded into main interface. + +Image Editor : Image Editor support 16 bits color depth images (RAW, TIFF, PNG, PNM). +Image Editor : Complete rewrite of image editor/showfoto gui implementation. Implementation + is now common and is more easy to maintain! +Image Editor : Add a progress bar to Image editor/showfoto about IO image files access. +Image Editor : Support of ICC color profiles management using LCMS into Image editor/showfoto. +Image Editor : digiKam setup is now available from Image Editor menu. +Image Editor : TIFF and JPEG Exif thumbnail are recomputed at saving pictures. +Image Editor : Iptc preview image are recomputed at saving pictures. + +Image Plugins : New powerfull Noise Reduction algorithm based on dcamnoise2 implementation. +Image Plugins : New powerfull Black & White converter tool using curves adjustements. +Image Plugins : New plugin to experiment ICC profiles to perform color corrections into images. +Image Plugins : New preview rendering modes in all color correction image plugins dialogs. +Image Plugins : Redesign/polishing of all image plugins dialogs. Histogram are available at all + when it's necessary. All dialogs used in DigikamImagePlugins are common in digiKam + core now. +Image Plugins : New zooming capabilities in image plugins dialogs. +Image Plugins : Ported all core image plugins to new DImg API. Support of 16 bits images. +Image Plugins : Ported all DigikamImagePlugins tools to new DImg API. Support of 16 bits images. + +Setup : Advanced settings to load RAW files in Image Editor using external dcraw instance. +Setup : Setup implementation are now common between Showfoto and digiKam. +Setup : New Identity setup page to set default strings informations about Photographer and + copyright used when IPTC tags are updated. + +CameraGUI : New option to set metadata DateTime/PhotographerId/Copyright Exif and/or IPTC tags + on the fly. +CameraGUI : JPEG Exif thumbnail and Iptc image preview are recomputed during Exif Auto-rotation. +CameraGUI : digiKam theme support. + +Showfoto : Image properties sidebar support +Showfoto : All images from a folder can be loaded at the same time. +Showfoto : Add 16 bits/color/pixel support (DImg API). +Showfoto : Thumbbar moved to the left by default in vertical mode. Image properties side bar to the + right to make a consistant GUI with digiKam main interface. +Showfoto : Thumbbar can be used to the bottom in horizontal mode. +Showfoto : New Exif Auto-rotate option like in digiKam ImageEditor. +Showfoto : New Set Orientation Exif flag to normal after Rotate/Flip like in digiKam ImageEditor. + +*** 0.9.0-beta2 ******************************************************* + +CameraGUI : Custom prefix index depand of current images selection in the camera icon items +CameraGUI : New option to show camera summary and documentations of Gphoto2 drivers. +CameraGUI : Option to upload pictures to camera using copy&paste or drag&drop. +CameraGUI : New option to start number sequence index with a custom value. +CameraGUI : New option to add postfix string to target download file name. +CameraGUI : New option to add camera name to target download file name. +CameraGUI : New option to increase/decrease the thumbnails size. +CameraGUI : New option to lock/unlock pictures from camera to prevent unwanted deletion. +CameraGUI : New option to convert JPEG files to lossless file format during download. +CameraGUI : New option to set date format of auto-created albums. +CameraGUI : New option to auto-create files extension-based sub-albums. + +Image Plugins : "Add Border" tool now respect aspect ratio of original image to render the decorative border. +Image Plugins : "Add Border" tool now use a border size depending of a percent value of original image size. + +General : New dcraw version detection at startup. +General : New option to force the refresh of Album contents. +General : Usability improvements of albums/pictures deletion. +General : New option to process a batch creation of all albums items thumbnails. +General : New option to "Select All", "Deselect", or "Invert Selection" of Tags in Tags Filter View and + Comments and Tags side bar tabs. +General : New option to set image as tag thumbnail on Tag Filters View using D&D. +General : New option to set a matching condition to use between tags in Tag Filters View + (OR or AND condition). + +Setup : The new batch thumbnail generator can be started when the Exif Auto-rotate option + has been changed. +Setup : New Album Items Tool Tip setup tab to set witch information to show over the images + using the pop-up window. +Setup : New color sheme theme 'Digikasa' from Sergio Di Rio Mare. + +*** 0.9.0-beta3 ******************************************************* + +Setup : New dcraw options set compatible with dcraw release >= 8.16. + +General : Automatic image Tags creation/set using IPTC Keywords contents. +General : New kipi plugin to set pictures GPS informations using a GPS device (available with + next kipi-plugins 0.1.3). +General : Removing external dcraw depency. Now digiKam include a full supported version of + dcraw program in core. + +CameraGUI : New advanced config dialog to set date/time format used to rename camera pictures + file name during download. + +*** 0.9.0-rc1 ********************************************************** + +General : dcraw implementation updated to version 8.41. +General : New kipi plugin to edit EXIF and IPTC metadata (available with next kipi-plugins 0.1.3). + +*** 0.9.0-rc2 ********************************************************** + +General : Exiv2 depency fixed to 0.12 release. +General : Fix broken compilation using "./configure -enable-final" option. + +ImageEditor : Fixed and improved Color Management View workflow. + +*** 0.9.0-final ********************************************************* + +General : About final release packaging, improve support of "./configure --enable-final" + option everywhere. + + +---------------------------------------------------------------------------------------------------- +digiKam and DigikamImagePlugins BUGFIX FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +*** 0.9.0-beta1 ******************************************************* + +001 ==> 87823 : Displaying images with ICC color profile when possible. +002 ==> 91811 : Find pictures by meta tag info, date. +003 ==> 91812 : Viewer/editor for IPTC meta data. +004 ==> 96459 : EXIF for RAW camera images. +005 ==> 116248 : Ask user which plugins should be enabled on first startup. +006 ==> 115441 : Resize EXIF-rotated images doesn't work. +007 ==> 115460 : Opening and closing right pane with tag filter also changes width of left pane and vv. +008 ==> 124688 : digiKam loading time is big because of : WARNING: Failed to find parent for Tag. +009 ==> 109253 : digiKam does not store comments in image EXIF tags. +010 ==> 115764 : Modify EXIF creation date. +011 ==> 121487 : "Tip of the day" text gets cut off. +012 ==> 120052 : Redraw problem with whatthis info of an image show and start scrolling with the mouse. +013 ==> 120053 : Whatthis info not closed when albumview is scrolled up or down with cursor keys. +014 ==> 123522 : Minolta's RAW files (mrw) aren't show in the view window. +015 ==> 121905 : Albums not displayed correctly and digiKam crashes with St9bad_alloc. +016 ==> 113997 : Option to store tags in image. +017 ==> 123742 : Preview-pictures seem to be handled differently by digiKam and Konqueror. +018 ==> 121308 : Mass storage and PTP usability. +019 ==> 121306 : Add copyright/license during download. +020 ==> 111560 : Be able to locate photos on a map. +021 ==> 122747 : Will not build with GCC 4.1. +022 ==> 103176 : 16bit/channel framework for digiKam. +023 ==> 109096 : Provide quick access to image comment. +024 ==> 109817 : Feature request for image properties dialog in image editor. +025 ==> 109992 : Preview in comments editor resizeable. +026 ==> 113103 : Show digiKam comments into digiKam image editor. +027 ==> 124939 : Improve import picture nomenclature. +028 ==> 113915 : Tags are lost after "Save As..."ing. +029 ==> 103245 : Image Effects are inaccessible from image editor. +030 ==> 125589 : Saving user preferences not possible. +031 ==> 103201 : Easy transport of albums, including tags, comments, etc. +032 ==> 119234 : Window views not updated when tag name changed in "Tag Filters". +033 ==> 103255 : Wish: *add* (not edit) EXIF headers like date, comment etc. +034 ==> 121800 : Can't setup camera correctly. +035 ==> 121784 : Accessing the camera in mass storage mode just lists jpegs and no other filetypes. +036 ==> 121371 : Missing display of EXIF infos in "Simple view". +037 ==> 123646 : Rating image with a keyboard shortcut. +038 ==> 119946 : Thumbnails not correctly rotated according to exif information. +039 ==> 106103 : Should support adding GPS EXIF header to images. +040 ==> 110598 : Synchronize digiKam comments to existing embedded comments. +041 ==> 118501 : Exifs lost when modifying the images. +042 ==> 122264 : Exif data fail to show up in recent SVN. +043 ==> 103489 : Sort and re-order EXIF information dialogs. +044 ==> 105670 : Option to print EXIF data with images. +045 ==> 109319 : Full EXIF info - exiftags style. +046 ==> 120963 : Optional album date change. +047 ==> 121370 : Can't save images sometimes after some modifications. +048 ==> 121646 : digiKam on PPC has problem identifying JPEG and tries to use dcraw with them. +049 ==> 120479 : Search problem for not tagged or commented images. +050 ==> 120775 : Search doesn't find pictures without rating. +051 ==> 112063 : When viewing image in fullscreen there is an ugly frame at the border of the screen. +052 ==> 120736 : Many imageplugins going busy for ever when not using rubber to change a value. +053 ==> 116154 : Show progress bar while saving large files. +054 ==> 120521 : Untranslatable message in tagfilterview.cpp. +055 ==> 119742 : Wish list -include move to trash in right click menu. +056 ==> 119201 : Sometimes digiKam fails to show newly downloaded photos. +057 ==> 117401 : digiKam's 'my albums' and 'my tags' does no longer remember it's state when restarted. +058 ==> 116520 : Slideshow should work for search results as well. +059 ==> 119073 : Wish list rescan option. +060 ==> 118543 : Enable-nfs-hack no longer works. +061 ==> 113807 : Is it possible to have the tags of the contextual menus "Assign Tag" and + "Remove Tag" sorted ? +062 ==> 116343 : "Not Tagged" has disappeared from the Tags Filter view when changing album library path. +063 ==> 117225 : digiKam requires at least libpng >= 1.2.7. +064 ==> 115423 : Thumbnails view jumps to top when new photos are added. +065 ==> 125732 : ICC settings get reset when disabled and re-enabled. +066 ==> 119741 : Restore image editor if already open. +067 ==> 122374 : Ignores read-only permission during saving. +068 ==> 121367 : Add properties tab to digiKam and (especially) Image Editor. +069 ==> 125926 : Directories with a '#' in their name are not properly detected when created outside digiKam. +070 ==> 125733 : Enabling 'Always apply ICM profiles' can lead to suprising results. +071 ==> 126326 : Camera download: auto-rotated images loose EXIF info when 'No EXIF information found.' + is written to console. +072 ==> 121242 : mimelnk/x-image-raw.desktop conflicts with tdegraphics-3.5.x. +073 ==> 127374 : Unsharp mask: max. radius way too small. +074 ==> 116485 : Mimimize button missing on "Compaq Flash Reader" camera window. +075 ==> 127577 : Raw display too dark and not rotated. +076 ==> 127634 : Communication between digiKam and its tdeioslaves broken if size_t != off_t. +077 ==> 126335 : Autoration of photos may confuse user because of changes made he is not aware of. +078 ==> 127759 : No scroll bar in color management/select profile. +079 ==> 114211 : Main Interface: image comments encoding unreadable after moving an album. +080 ==> 120241 : Main Interface: utf8 display and edit. +081 ==> 127905 : Wish: make comment field resizable. +082 ==> 127946 : Typo: the word Toogle should be Toggle. +083 ==> 117115 : Automatically rotate/flip using camera-provided information (EXIF) modifies image contents +084 ==> 128069 : Crash when moving an album to another album. +085 ==> 125779 : Use deflate compression for tiffs. +086 ==> 127972 : digiKam does not *add* EXIF:DateTimeOriginal when modifying date +087 ==> 127697 : Camera gui always puts *.JPG and *.NEF in the configure - digiKam dialog/File + Mime Types/ Image Files. +088 ==> 128373 : "Makernote - Simple" should show ISO. +089 ==> 113915 : Tags are lost after "Save As..."ing. +090 ==> 128283 : Thumbnail generation fails with raw images. +091 ==> 126112 : Do not open new window when camera is connected. +092 ==> 127846 : digiKam crash when 2nd gphoto camera dialog is closed. +093 ==> 93569 : Easier connection to USB disc cameras. +094 ==> 124952 : digiKam "Mount and Download" Problems. +095 ==> 129134 : Typo in missing files warning dialog "loose" should be "lose". +096 ==> 128914 : Differientiating view and edit / call for a integrated viewer. +097 ==> 128669 : Use embedded thumbnail for viewing RAW files. +098 ==> 127991 : digiKam image editor and raws (nef). +099 ==> 129450 : Cannot exit preview mode within empty album. +100 ==> 129610 : Unsupported initialization of CameraList object. +101 ==> 127584 : Minimum width of sidebar too large. +102 ==> 127763 : Color management, profile selection crashes. +103 ==> 126199 : Nikon D70 comments in jpegs are all shown as "charset="Ascii". +104 ==> 130381 : Automatic colorbalance and camera color balance checkboxes swapped. +105 ==> 117248 : Opening default app when camera inserted return KIOExec error. +106 ==> 130883 : Overexposure indicator in color management is saved as part of the image. +107 ==> 130798 : Editor saves some jpeg's 30% smaller than original, + even with jpeg compression option set at 100. +108 ==> 130525 : Saving large (>5M) jpg's result in corrupt file. +109 ==> 130920 : ICC, profiles, metadata, abbreviations. +110 ==> 124199 : digiKam crashed when I right-clicked in the (empty) "My Albums" view. +111 ==> 127826 : Album comment with no wordwrap. + +*** 0.9.0-beta2 ******************************************************* + +112 ==> 121691 : Problems with Downloading images from Camera. +113 ==> 127614 : Focus steal when typing custom prefix in renaming options sidebar. +114 ==> 131034 : Display a mini-review of the photo currently transfered, i.e better visual feedback. +115 ==> 124425 : Start index number counting on selected images instead of all images. +116 ==> 127272 : Crash when opening image in Image Editor. +117 ==> 96186 : Upload to camera not possible. +118 ==> 107724 : Handle EXIF Information-Wizzard missing. +119 ==> 130996 : More control over jpeg options. +120 ==> 121526 : Download Images from camera. +121 ==> 124060 : Use same menu mnemonic for Filter as Krita (and Photoshop, and ...). +122 ==> 131301 : Crash after add second image. +123 ==> 109820 : Utility script to export tag information of images into the filesystem. +124 ==> 117375 : Change file name without affecting extension. +125 ==> 131603 : Orientation of RAW-images (especially Canons *.cr2). +126 ==> 131532 : Minolta exception code can break EXIF rotation. +127 ==> 131550 : digiKam/showfoto can't show jpeg image under PowerPC. +128 ==> 131549 : Endianess problem under Linux-PowerPC (with png images at least). +129 ==> 132011 : Add search criteria to take sub-tags into account. +130 ==> 131920 : Can't create preview folders with unicode characters in the name. +131 ==> 132113 : Resize dialog limits image width/height to 4 digits. +132 ==> 132081 : Critical: ShowFoto silently aborts saving image when closed. +133 ==> 118535 : Add Border: use percent to designate border size. +134 ==> 131686 : Crash when viewing/rollover of Sony Alpha 100 raw (.arw) images. +135 ==> 132582 : Some pictures make digiKam crash (sample included). +136 ==> 122693 : Improvement proposals for the "add border" plugin. +137 ==> 124183 : Option to convert images when importing them from the camera. +138 ==> 128673 : Add keyboard shortcut to refresh album view or auto-refresh when pictures are renamed. +139 ==> 126427 : In "rename file" dialog, the 2nd picture is (and can't) not displayed. +140 ==> 131558 : Camera UI: renaming dialogue can't handle UTF-8 filenames. +141 ==> 119075 : Wish list new folder date option. +142 ==> 128817 : Configure timestamp naming format for directory names. +143 ==> 132660 : Shortcuts for ratings do not work as of 0.9.x beta. +144 ==> 126874 : digiKam does not support . +145 ==> 132957 : Crash using dcop action: album_forward. +146 ==> 130547 : Automatically download RAW-images into separate folder below the JPG-images. +147 ==> 128296 : Icon selector for tags wont let user select 'other icons'. +148 ==> 115161 : Image comments/tags: 'Recent tags' looks like a button but funktons like a menu. +149 ==> 121423 : Rename file loses Album thumbnail. +150 ==> 120308 : Batch creation of thumbnails. +151 ==> 120074 : New tag doesn't appear. +152 ==> 120075 : Can't restore system icons for tag. +153 ==> 133209 : Shift selection is not working in download screen. +154 ==> 131552 : Typo inconsisted use of ICC. +155 ==> 131553 : Typo indent must be intent? +156 ==> 118526 : Make it possible to remove an album's thumbnail. +157 ==> 120050 : Assigning an image as icon for a tag entry in 'Tag View' does not update + the icons of this tag in 'Tag Filters'. +158 ==> 129365 : Keyword-List view still uses old description after renaming keyword. +159 ==> 115154 : Tags filter misses 'deselect all Tags' in context menu. +160 ==> 133525 : digiKam doesn't compile (KDE forces -fno-exception, digiKam needs it). +161 ==> 115160 : 'Tag Filters' should use 'and' not 'or' or maybe better label 'Show Tags'. +162 ==> 120056 : Allow to assign via drag and drop tag thumbnails in 'Tag Filters'. +163 ==> 132694 : RAW Thumbnailing very slower and resource intensive. + +*** 0.9.0-beta3 ******************************************************* + +164 ==> 134013 : Tag menu extremly slow. +165 ==> 122653 : File-dialogue claims that pictures are not on the local-storage, yet they are. +166 ==> 134091 : dcraw -n option not valid for version > 8.15. +167 ==> 134224 : Prefix for image filename in camera dialog not working. +168 ==> 133359 : Google maps support to show satellite images of the photos. +169 ==> 131347 : Comments modified in digiKam Image Editor are not saved. +170 ==> 134351 : Error while make install. +171 ==> 132841 : Tag filtering works only a the second click on the tag filter list. +172 ==> 133359 : Google maps support to show satellite images of the photos. +173 ==> 134924 : Patch to allow compile with LDFLAGS="-Wl, --as-needed". +174 ==> 134841 : Weird behaviour of identity setup. +175 ==> 131382 : All thumbnails of album destroyed when using Tag Filters. +176 ==> 134869 : High CPU usage while displaying ICC Profile. +177 ==> 134761 : A rotated RAW image get saved straight with an inconsistent Exif orientation. +178 ==> 135236 : Right-click menu rename function cuts to the first period (not the extention one). +179 ==> 135307 : After deleting a file, user comments entered for pictures apply to the wrong picture. +180 ==> 135145 : Raw image converter fails on my raw files (cr2, crw, dng). +181 ==> 125727 : digiKam open with of raw file only shows application for octet-stream. +182 ==> 135430 : Typo automaticly should be automatically in raw image converter. +183 ==> 135060 : Folders without pictures in it cannot be assigned icons. + +*** 0.9.0-rc1 ********************************************************** + +184 ==> 103255 : Add (not edit) EXIF headers like date, comment etc. +185 ==> 91812 : Viewer/editor for IPTC meta data. +186 ==> 136138 : Set as album thumbnail doesn't change the icon immediately. +187 ==> 135851 : Wish to view IPTC in different order. +188 ==> 136162 : ISO Slider label is incorrectly labeled as "sensibility", should be sensitivity. +189 ==> 133026 : Crashes on systems using hyperthreading. +190 ==> 136260 : Awkward management of metadata and digiKam-tags and comments. +191 ==> 136769 : digiKam crashes when resetting Album icon (with no album selected). +192 ==> 136749 : Tags are not kept with images when album is moved in album hierarchy. +193 ==> 123623 : digiKam freezes or is very slow. +194 ==> 96993 : Ability to view next next image in folder in showfoto. +195 ==> 136643 : Showfoto can open CRW, but not shown in file dialogue. +196 ==> 137063 : Keyboard shortcut for 'paste' action not working. +197 ==> 137461 : Typo croping must be cropping. +198 ==> 137282 : Comments are lost when copying or moving an image to another album. +199 ==> 132805 : Crash when assinging keywords. +200 ==> 137204 : Crash when applying IPTC data. +201 ==> 135407 : Reproducible crash selecting photos from collection. +202 ==> 136086 : Crash when saving any type of files. +203 ==> 137612 : Showfoto doesn't refresh the photo preview when deleting an image. + +*** 0.9.0-rc2 ********************************************************** + +204 ==> 115125 : White Balance tool: remaining endianess issue on PowerPC. +205 ==> 137495 : showfoto crashes when doing any modification to a loaded directory. +206 ==> 137845 : Album Header cut off (squeezed) when entering two or more lines + in Album comment. +207 ==> 137886 : When a tag is moved in the left panel it's position is not updated + in the right panel. +208 ==> 135834 : Lowercasing camera filenames only works for first imported file. +209 ==> 134391 : digiKam camera gui dialog crashes if there is a filename without extension. +210 ==> 131947 : digiKam complains about invalid ICC profiles path. +211 ==> 130176 : Typos in digiKam.po and one plugin. +212 ==> 138252 : Display is not updates when switching color managed view on/off. +213 ==> 138253 : Keyboard shortcut for turning color managed display on/off. + +*** 0.9.0-final ********************************************************* + +214 ==> 137461 : Typo croping must be cropping. +215 ==> 135477 : View mode which shows the comment of an image. +216 ==> 137770 : digiKam doesn't keep original unix rights when modifying comments/tags/rating. +217 ==> 133091 : Changing date/time with numblock changes also the orientation. +218 ==> 137993 : Importing photos into albums results in time/date file override with current one. +219 ==> 138620 : Saving an image destroy another picture. +220 ==> 119205 : Drag & Drop of image into librarypath root dir ==> images are invisible. +221 ==> 121651 : Export menu not available in context menu from album. +222 ==> 131601 : Need preview pane (large) in main digikam window. +223 ==> 133276 : Make changing EXIF date of multiple files easier. +224 ==> 133590 : Usability: walking through photos using image View (F3). +225 ==> 122746 : Images are not shown anymore. black screen. +226 ==> 132470 : Plugins not available unless logged in as superuser. +227 ==> 113801 : Little problem with image files extensions. +228 ==> 127112 : Tools -> 'Gamma Adjustment...' fails silently when kgamma is not installed. +229 ==> 136258 : User comments in the EXIF/IPTC-data aren't carried over to the "Comments". +230 ==> 136932 : Access to a specific jpg photo crashed digiKam. +231 ==> 135442 : Add 'rename' entry to album RMB menu. +232 ==> 138540 : Images files are updated (but not modified) when setting new + metadatas albeit they are unset. +233 ==> 136256 : Tags are not filled from the IPTC-keywords. +234 ==> 136062 : Charset problem saving EXIF comments (JPEG files). +235 ==> 133567 : Crash when testing tags in digikam image editor (saveWithExiv2). +236 ==> 138300 : Crash on startup with one user's collection. +237 ==> 138747 : Crash on Add Camera. +238 ==> 138715 : Crash when quickly switching to previous/next image. +239 ==> 138616 : Cannot compile: cannot verify exiv2 version. +240 ==> 134999 : Crash in exiv2 when searching for new images. +241 ==> 136771 : Image editor crashes when using undo. + +---------------------------------------------------------------------------------------------------- diff --git a/src/NEWS.0.9.1 b/src/NEWS.0.9.1 new file mode 100644 index 00000000..957162db --- /dev/null +++ b/src/NEWS.0.9.1 @@ -0,0 +1,167 @@ +---------------------------------------------------------------------------------------------------- +digiKam and DigikamImagePlugins 0.9.1 - Release date: 2007-03-04 + +NEW FEATURES : + +General : New native JPEG2000 image loader using Jasper library witch + can support 16 bits color depth pictures. +General : New optimized layout to show Comments & Tags side bar contents. +General : Tags View from Comments & Tags side bar support drag & drop. +General : New Batch tool to sync all images metadata (EXIF/IPTC) with + digiKam database content. +General : Add a status bar to Album Gui with a progress bar, text bar, + and navigate bar. +General : New native SlideShow tool witch use the Image Preview feature. + RAW files can be slided very fast. + +AlbumGUI : Improvement of pop-up menu of Tags Filter View and Comment & Tags + about auto selection/deselection of parents/childs tags in Tags treeview. +AlbumGUI : Main Root album show a Welcome page for new users (like Kmail + or Konqueror). +AlbumGUI : Video files can be previewed using an embedded instance of + default KDE media player. +AlbumGUI : Preview picture mode use a memory cache to speed-up loading. +AlbumGUI : Preview picture mode pride a context pop-up menu. +AlbumGUI : Comments & Tags now support multiple selection of items to + apply properties. +AlbumGUI : New option to set the action to do when user right click on + picture thumb: Load on Image Editor or Show an embeded preview. +AlbumGUI : Prefer Exif DateTimeOriginal for sorting images + (DateTimeDigitized and DateTime only used as fallback) + +Image Plugins : All tools remember settings between sessions. +Image Plugins : All tools render properly preview of image using Color Managed View. +Image Plugins : All tools use the same keyboard shortcuts than PhotoShop. +Image Plugins : New option in all Color corrections Tools to show under-exposed + and over-exposed areas of corrected picture before to apply corrections. +Image Plugins : Brightness/Contrast/Gamma : settings value excursion are the same + than Photoshop. +Image Plugins : Add Border Tool : add new option to preserve aspect ratio. Border Width + can be set in pixels or in percents accordinly with this setting. +Image Plugins : Perspective Tool : add a grid and vertical/horizontal guide lines. +Image Plugins : Ratio-crop Tool : usability improvements from Jaromir Malenko. +Image Plugins : Auto Color Correction Tool : add new filter to perform + auto-exposure corrections. +Image Plugins : all plugins : capabilty to remember settings between plugin + sessions to store configuration in a settings file. + +Image Editor : Add new advanced options to keep ratio and alignment about print pictures. +Image Editor : Add 2 new advanced options to show under-exposed or over-exposed pixels. +Image Editor : Remove View->Histogram menu option. We have a better histogram + available in sidebar. +Image Editor : Color profiles are tested now to avoid invalid files. +Image Editor : JPEG/PNG/TIFF/JPEG2000 file save settings are now available in + File Save dialog. +Image Editor : JPEG file save settings include a red warning about this lossy + compression image format with a link to wikipedia to aware users. + +CameraGUI : New option to set metadata DateTime/PhotographerId/Copyright Exif and/or IPTC tags + on the fly. +CameraGUI : JPEG Exif thumbnail and Iptc image preview are recomputed during Exif Auto-rotation. +CameraGUI : digiKam theme support. + +AlbumGUI : Improvement of pop-up menu of Tags Filter View and Comment & Tags about + auto selection/deselection of parents/childs tags in Tags treeview. +AlbumGUI : Main Root album show a Welcome page for new users (like Kmail or Konqueror). +AlbumGUI : Video files can be previewed using an embedded instance of default KDE + media player. +AlbumGUI : Preview picture mode use a memory cache to speed-up loading. +AlbumGUI : Preview picture mode pride a context pop-up menu. +AlbumGUI : Comments & Tags now support multiple selection of items to apply properties. +AlbumGUI : New option to set the action to do when user right click on picture thumb: + Load on Image Editor or Show an embeded preview. +AlbumGUI : Prefer Exif DateTimeOriginal for sorting images (DateTimeDigitized + and DateTime only used as fallback) + +Image Plugins : All tools remember settings between sessions. +Image Plugins : All tools render properly preview of image using Color Managed View. +Image Plugins : All tools use the same keyboard shortcuts than PhotoShop. +Image Plugins : New option in all Color corrections Tools to show under-exposed and + over-exposed areas of corrected picture before to apply corrections. +Image Plugins : Brightness/Contrast/Gamma : settings value excursion are the same + than Photoshop. +Image Plugins : Add Border Tool : add new option to preserve aspect ratio. Border Width can be set + in pixels or in percents accordinly with this setting. +Image Plugins : Perspective Tool : add a grid and vertical/horizontal guide lines. +Image Plugins : Ratio-crop Tool : usability improvements from Jaromir Malenko. +Image Plugins : Auto Color Correction Tool : add new filter to perform auto-exposure corrections. + +Image Editor : Add new advanced options to keep ratio and alignment about print pictures. +Image Editor : Add 2 new advanced options to show under-exposed or over-exposed pixels. +Image Editor : Remove View->Histogram menu option. We have a better histogram available in sidebar. +Image Editor : Color profiles are tested now to avoid invalid files. +Image Editor : JPEG/PNG/TIFF/JPEG2000 file save settings are now available in File Save dialog. +Image Editor : JPEG file save settings include a red warning about this lossy compression image + format with a link to wikipedia to aware users. + +General : Native slideshow tool : new option to slide an album tree in recursive mode. +General : Native slideshow tool : add capability to print photograph informations during slide. +General : Native slideshow tool : preaparing slideshow is now cancelable. + + +---------------------------------------------------------------------------- +digiKam and DigikamImagePlugins BUGFIX FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 115157 : Usability: Image comments/tags dialog: hard to find/see all + already selected tags (and to 'de'select them). +002 ==> 132309 : Wish: enhance video support in digiKam. +003 ==> 115153 : Assigns tags not only to selected images with drag and drop. +004 ==> 131743 : Comments and tag edit widgets should retain focus when changing image. +005 ==> 140412 : Comments/Tags sidebar steels focus. +006 ==> 137491 : Editor image cache not flush when an image is modified outside digiKam. +007 ==> 138949 : Save/overwrite fails: wrong dialog. +008 ==> 139197 : Crash during import of raw imagefiles (DNG). +009 ==> 139313 : UTF-8 image comments are mangled when input from image editor. +010 ==> 136903 : If partition gets full during camera/card copy there is no + warning and empty files are created. +011 ==> 133955 : Rating search: less, equal, and larger comparison. +012 ==> 139547 : Tag hierarchy automatic fill. +013 ==> 138816 : gpcamera.cpp compile failure CameraList camList. +014 ==> 93542 : Improvements to digiKam print interface. +015 ==> 139658 : Tiff generated by photoshop is either ignored by digiKam image browser, + or kills digiKam when regenerating the database. +016 ==> 102032 : Wish: rating system for photos, show/hide/sort by rating. +017 ==> 111446 : Bad behaviour when out of disk space. +018 ==> 133516 : Tags set in Comments/Tags end up on the wrong picture. +019 ==> 140136 : Aspect ratio crop and selection widget improvements. +020 ==> 131600 : Set tag for multiple selection of images. +021 ==> 137545 : Helper lines / cross wanted. +023 ==> 131170 : Add grid/auxiliary lines for perspective correction tool. +024 ==> 138158 : Allow to edit multiple image tags/comments at same time. +025 ==> 140234 : Thumbnail click default action. +026 ==> 135141 : Border size in pixel. +027 ==> 137696 : Border extent should not follow image aspect ratio. +028 ==> 138444 : More clear/consistent color management terminology. +029 ==> 140176 : Showfoto crashes when selecting a filter. +030 ==> 130237 : Typos in digiKam plugin files. +031 ==> 138925 : External modules config dialog lacks a select all button. +032 ==> 140038 : digiKam Image Editor crash on right click after changing toolbar. +033 ==> 139766 : Crash when displaying EXIF metadata with Unicode comment +034 ==> 130017 : Wish: batch operation to save existing comments in the files. +035 ==> 140320 : View menu should better fit after Edit. +036 ==> 127617 : "Toggle Fullscreen" should be located in "View" menu instead + of "Settings" menu. +038 ==> 139264 : digiKam sorts pictures with wrong exif tag. +039 ==> 140417 : Crash digiKam when delete image. +040 ==> 110514 : Enhanced selection, refactored histogram. +041 ==> 140933 : Typo: Album Library Path. +042 ==> 141190 : digikam crash when invalid ICM file at color profile directory + and you enter into PREFERENCES->COLOR SPACE PROFILES. +043 ==> 123649 : JPEG/PNG quality settings at the "save" dialog. +044 ==> 116518 : Use KIPI plugin for displaying slideshows. +045 ==> 140304 : Start slideshow from the current image. +046 ==> 140303 : slideshow must be relocated to View menu from tools menu. +047 ==> 137503 : Tags are not written to image file automatically. +048 ==> 136254 : Editing tags does not change IPTC-keywords. +049 ==> 135655 : Proper full screen mode (no menu, toolbar, statusbar, sidebars). +050 ==> 142088 : Build fails when libkexiv2 is in $PREFIX, but not in $TDEDIR or standard include directories +051 ==> 140227 : remove or modify D&D tags menu from icon view area (improvements, not finished) +052 ==> 142109 : Turn under/over exposure display on/off by click on corresponding icons +053 ==> 141663 : Keep getting prompted to Apply Comments +054 ==> 139024 : Camera GUI new items selection doesn't work +055 ==> 139547 : Tag hierarchy automatic fill +056 ==> 141626 : Bugs in german translation +057 ==> 141786 : Confirmation dialog during rename to existing file does n... +058 ==> 141924 : Tags preview not updated in last image of album +059 ==> 141934 : Incorrect capitalized string: "back to album" +060 ==> 141961 : Long comments truncated in slideshow diff --git a/src/NEWS.0.9.2 b/src/NEWS.0.9.2 new file mode 100644 index 00000000..77fbbabc --- /dev/null +++ b/src/NEWS.0.9.2 @@ -0,0 +1,158 @@ +digiKam 0.9.2-final - Release date: 2007-06-13 + +NEW FEATURES: + + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +062 ==> 144590 : English labels need cleanup in digiKam. +063 ==> 146436 : digiKam try to include Exiv2 library headers. +064 ==> 146464 : Light Table does not deal with colour management. +065 ==> 142133 : Typo English documentation docbook. +066 ==> 146744 : Tag that contains '&' is not displayed correctly in menus. + +********************************************************************************************** + +digiKam 0.9.2-beta3 - Release date: 2007-06-03 + +NEW FEATURES: + +General : Light Table and Preview Mode can work with full image size instead a reduced one. + +---------------------------------------------------------------------------- + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +059 ==> 145198 : Light-table should also work with the full image. +060 ==> 146072 : Slideshow shows black screen. +061 ==> 146184 : Showfoto no filename specified . + +********************************************************************************************** + + +digiKam 0.9.2-beta2 - Release date: 2007-05-28 + +NEW FEATURES: + +General : digiKam has a new powerful tool to compare similar images side by side: + the Light Table. Demo and screenshots: + http://www.digikam.org/?q=node/221 + http://www.digikam.org/?q=node/222 + +---------------------------------------------------------------------------- + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +040 ==> 135048 : Easily compare similar images using a light-table. +041 ==> 145159 : Improvements to the light-table. +042 ==> 145204 : Small issues with the light-table. +043 ==> 145227 : Change ratings via short-cuts in the light-table. +044 ==> 145236 : Small wishes for the light-table. +045 ==> 145237 : Small wishes for the light-table (2). +046 ==> 145170 : Always allow zooming in/out in imageeditor. +047 ==> 145078 : Ctrl-Y untied from Redo. +048 ==> 145083 : Space and Shift-Space isn't used for navigation between images. +049 ==> 145077 : Ctrl-W and Ctrl-Q shortcuts not tied to proper actions:fixed +050 ==> 145558 : In menu view: window size/original size: ctrl-shift-z obsolete? +051 ==> 144640 : CTRL-P does nothing in Album GUI: fixed to print action +052 ==> 144643 : Ctrl-Shift-A does not deselect in Album GUI: fixed +053 ==> 144644 : Ctrl-0 does not set zoom to 100% in Preview mode: Alt+Ctrl+0 +054 ==> 144650 : Shift-Space doesn't work as PageUp. +055 ==> 145079 : Ctrl-A, Ctrl-Shift-A don't peform proper selection actions. +056 ==> 145627 : Showfoto /path/to/directory doesn't work, while the "open dir" feature exists. +057 ==> 146012 : Dragging an image over a tag in "tag filters" panel crashes digiKam. +058 ==> 146032 : Panning doesn't work in Light Table. + + +********************************************************************************************** + +digiKam 0.9.2-beta1 - Release date: 2007-05-04 + +NEW FEATURES: + +General : DigikamImagePlugins have been merged into digiKam. It more simple to release + on package for all. Image plugins translations are hosted to digikam.po file + instead a .po file for each tool. All tools still available like plugins at + the same place than image plugin core. +General : New depency to libkdcraw shared library used to decode RAW file. + This library is shared between digiKam and kipi-plugins. The internal + dcraw version used is 8.60. digiKam support now all recent digital + camera RAW files released at PMA 2007. +General : Make size of icons used in sidebars configurable in order to allow more + entries to be presented. +General : Make size of icons used in album icon view more configurable using a slider + in status bar. +General : Removing direct Exiv2 library depency. libkexiv2 interface is used everywhere + instead. + +Album GUI : Add Zoom/Scrooling functions with preview mode. + +Image Editor : Usability improvement : a new pan tool is available on the right bottom + corner of canvas to naviguate over large pictures. +Image Editor : Usability issue : Blowup and Resize tools have been merged. +Image Editor : Usability issue : Unsharp Mask, Refocus, and Sharpen tools have been merged + to a new Sharpness Editor. +Image Editor : Usability issue : Reorganize menu structure +Image Editor : Usability issue : persistant selection in all zoom mode. +Image Editor : Add new option to fit on current selection. + +Image Plugins : Red Eyes Correction tool have been completly re-writen. There is + a preview of effect and the capability to taint the eye pupil with + a customized color. The new eye pupil can be blured to smooth the + result. +Image Plugins : Solarize plugin is now a "Color Effects" pack including Solarize, Velvia (new + plugin), Neon, and Edge effects. +Image Plugins : Black & White converter now support a lots of B&W analog camera film + types (Agfa, Ilford, Kodak). A new 'strength' setting can simulate the + amount of Lens filters effect. +Image Plugins : Update internal CImg library to 1.1.9. The Greycstoration algorithm used + by Restoration, Inpainting and Blowup plugins is faster and optimized. + +Showfoto : The thumbbar is now resizable. The thumbnails contents can be redimensionned in live. +Showfoto : The thumbbar items can show a full configurable tool tip like digiKam album icon + items tool tip. + +---------------------------------------------------------------------------- + +digiKam BUGFIX FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 142443 : Red eye correction should change eye colour to an alternate colour. +002 ==> 138744 : Dcraw 8.45 supports Pentax K10D. +003 ==> 142427 : Rotate image tool too complicated. +004 ==> 141439 : A velvia similar plugin. AKA vivid saturation. +005 ==> 127583 : keywords, copyright, photographer info not saved to IPTC tags. +006 ==> 142571 : Auto-exposure result is different from the preview. +007 ==> 127377 : Restoration filter (CIMG) does not function properly. +008 ==> 131446 : Using inpainting plugin crashes digiKam. +009 ==> 103244 : Usability: Merge multiple similar menus into one. +010 ==> 139790 : Image Editor: the center of the photo is moving when zooming in or out. +011 ==> 106508 : Please change scaling behavior. +012 ==> 137236 : Disable autozoom when picture fits in window. +013 ==> 103645 : Zoom in with rectangle tool selection. +014 ==> 126127 : Enlarge small images when autozoom is activated. +015 ==> 104439 : Use left mouse button to scroll image. +016 ==> 137391 : Image navigation in image editor using the mouse. +017 ==> 102029 : Configurable size of icons in sidebars. +018 ==> 102029 : No/small icons in album tree. +019 ==> 131155 : Smooth increasing/decreasing size of thumbnails like in iPhoto. +020 ==> 89365 : Reorganize menu structure in Image Editor. +021 ==> 134037 : Respect current sort order when passing list to KIPI plugin. +022 ==> 139466 : Remove configuration of digikam image plugins. +023 ==> 119418 : "set as album thumbnail" should work for different albums too. +024 ==> 125916 : Problem with opening 16bit TIFF. +025 ==> 143578 : New Pan Tool crash. +026 ==> 118539 : Lossless image-editor for JPEGs. +027 ==> 133913 : sRGB profile white point may be incorrect. +028 ==> 116148 : Auto-scrolling when selecting large area. +029 ==> 134498 : Date stamp display option for photos in full-screen mode. +030 ==> 130525 : Saving large (>5M) jpg's result in corrupt file. +031 ==> 132047 : Faster display of images and/or prefetch wished for. +032 ==> 140131 : No zoom in image preview. +033 ==> 89365 : More standard menu structure. +034 ==> 144214 : The plural form of "child" is "children", not "childs". +035 ==> 124487 : No way to pause a slide show. +036 ==> 128975 : "Correct Exif Orientation Tag" does not change the mtime + of the image file. +037 ==> 139814 : The window of digiKam exceed the screen if the resolution is 800x600. +038 ==> 144481 : Vertical window size cannot be reduced to VGA resolution. +039 ==> 136254 : Editing tags does not change IPTC-keywords. diff --git a/src/NEWS.0.9.3 b/src/NEWS.0.9.3 new file mode 100644 index 00000000..6652db29 --- /dev/null +++ b/src/NEWS.0.9.3 @@ -0,0 +1,142 @@ + +********************************************************************************************** + +digiKam 0.9.3 - Release date: 2007-12-23 + +NEW FEATURES: + +AlbumGUI + +Drag&Drop : D&D from anywhere into album tree. When the albumview is flattened + (Include Album Sub-tree) images can be d&ded across horizontal separations. + That one satisfies wish #142774. + +Goto : A new quick navigation feature has been added: it is now possible (by RMB context menu) + to GOTO any other view with pertinent context, e.g. from date view right-click on an + image and GOTO the Album or Tag view that this image is associated with. + Try any other combination. + That covers wish #128231. + +AlbumGUI : Always force to show mediaplayer widget to preview video in embeded mode. + +********************************************************************************************** + +digiKam 0.9.3-RC1 - Release date: 2007-12-08 + +NEW FEATURES: + +AlbumGUI : Add new Filter Bar on bottom of Albums/Tags/Searches/TagsFilter view filter contents + based on string. + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +052 ==> 152522 : Support for *.3gp and *.mp4 video files in digiKam (already supported by KDE video players). +053 ==> 128231 : A way to view pictures recursively in albums and sub-albums at the same time would be cool. +054 ==> 133191 : Quick search/filter for albums like in amarok. +055 ==> 146364 : Incremental search for tags in all tag fields. +056 ==> 152843 : Live filter does not work if no text is displayed under thumbnails. +057 ==> 148629 : "Adjust Exif orientation tag" does not continue after error. +058 ==> 144165 : Numeric values in Histogram -> Statistics frame should be right-aligned. +059 ==> 148037 : Tiff images are not displayed with correct colours. +060 ==> 152961 : Cannot remove album from tree. +061 ==> 121314 : More options to display the album tree. +062 ==> 141085 : Some strings are untranslated in digiKam. +063 ==> 151403 : Crashed trying to save a image downloaded from the camera. +064 ==> 148772 : Histogram should refresh after editor save. + +********************************************************************************************** + +digiKam 0.9.3-beta3 - Release date: 2007-11-20 + +NEW FEATURES: + +AlbumGUI : Add Text pattern widget on status bar to filter Album contents based on items + name, comments, and tags strings. +ImageEditor : New black & white Green color tone filter. +LightTable : Several usability improvements. + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +047 ==> 150296 : Images in LightTable are not compared in right order +048 ==> 151956 : Red eye enhance feature no longer works. +049 ==> 110136 : Filter textbox in album tree like in amaroks collection list. +050 ==> 152192 : Resize really bad qualitatively. +051 ==> 150801 : Thumbnail and image view does not update after editing image. + +********************************************************************************************** + +digiKam 0.9.3-beta2 - Release date: 2007-11-01 + +NEW FEATURES: + +General : Updated internal CImg library to last stable 1.2.4 (released at 2007/09/26). +AlbumGUI : Add Rating filter widget on status bar. +AlbumGUI : Add MimeType filter widget on status bar. +AlbumGUI : Add tool to navigate from album, tag and date view to any other view. + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +040 ==> 147533 : New rating filter for the statusbar. +041 ==> 131963 : Add a file type filter tab to the right. +042 ==> 148993 : Filter images by rating in album view. +043 ==> 151357 : Ratings can exceed 5 stars. +044 ==> 144815 : Scroll left-pane to the selected album/date on start-up. +045 ==> 96894 : Easier navigation between albums, tags and collections. +046 ==> 147426 : Search for non-voted pics. + +********************************************************************************************** + +digiKam 0.9.3-beta1 - Release date: 2007-10-08 + +NEW FEATURES: + +General : Updated internal CImg library to last stable 1.2.3 (released at 2007/08/24). +General : Color scheme theme are now XML based (instead X11 format). +General : Camera interface is now used to import new pictures in collection. +CameraGUI : New options to Download pictures and Delete it from camera at the same time. +CameraGUI : Support of Drag & Drop to download files from camera gui to album gui. +CameraGUI : Add new Bargarph to indicate statistics about free space available on Album + Library Path. + +digiKam BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 120450 : Strange helper lines behavior in ratio crop tool. +002 ==> 147248 : Image info doesn't follow image in the editor. +003 ==> 147147 : digiKam does not apply tags to some selected images. +004 ==> 143200 : Renaming like crasy with F2 crash digiKam. +005 ==> 147347 : Showfoto crashes when opening twice multiple files. +006 ==> 147269 : digiKam finds but fails to use libkdcraw. +007 ==> 147263 : Some icons are missing. +008 ==> 146636 : Adjust levels: helper line for sliders. +009 ==> 147671 : Portability problem in configure script. +010 ==> 147670 : Compilation problems on NetBSD in greycstoration. +011 ==> 147362 : Tool-tip for zoom indicator is below the screen if window is maximised. +012 ==> 145017 : Deleting an image from within digiKam does not update the digiKam database. +013 ==> 148925 : Light Table thumb bar not updated when deleting images. +014 ==> 148971 : Awful menu entry "digiKamThemeDesigner". +015 ==> 148930 : digiKam-0.9.2 does not compile with lcms-1.17. +016 ==> 141774 : Autorotate does not work however kipi rotate works. +017 ==> 103350 : Original image is silently overwritten when saving. +018 ==> 144431 : Add option in Camera Download Dialog. +019 ==> 131407 : Use camera GUI also for import of images from different locations. +020 ==> 143934 : Cannot download all photos from camera. +021 ==> 147119 : Can't link against static libjasper, configure reports jasper is missing. +022 ==> 147439 : It is too easy to delete a search. +023 ==> 147687 : Error when downloading and converting images. +024 ==> 137590 : Be able to modifiy the extension of images in the interface. +025 ==> 139024 : Camera GUI new items selection doesn't work. +026 ==> 139519 : digiKam silently fails to import when out of disc space. +027 ==> 149469 : Excessive trash confirmation dialogs after album is deleted. +028 ==> 148648 : Color managed previews not working in all plugins. +029 ==> 126427 : In "rename file" dialog, the 2nd picture is (and can't) not displayed. +030 ==> 144336 : Selecting pictures on the camera lets the scroll-bar jump back to the top of the list. +031 ==> 136927 : Failed to download file DCP_4321.jpg. Do you want to continue? and when continue is + clicked the same warning comes for the next image and so on. +032 ==> 146083 : Bugs in drag and drop. +033 ==> 147854 : Put images into an emptied light-table. +034 ==> 149578 : libjpeg JPEG subsampling setting is not user-controlable. +035 ==> 149685 : Go to next photo after current photo deletion (vs. to previous photo). +036 ==> 148233 : Adding texture generates black image. +037 ==> 147311 : Ligth Table RAW images does not rotate as def. by exif info. +038 ==> 146773 : Metadata sources preference when sorting images. +039 ==> 140133 : Metadata Edit Picture Comments Syncs to IPTC and EXIF when option is de-selected. diff --git a/src/NEWS.0.9.4 b/src/NEWS.0.9.4 new file mode 100644 index 00000000..a7ea38bf --- /dev/null +++ b/src/NEWS.0.9.4 @@ -0,0 +1,236 @@ +********************************************************************************************** +digiKam 0.9.4 - Release date: 2008-07-16 + +NEW FEATURES: + +General : for packaging, an external libsqlite3 dependency can be used instead internal version. + (see B.K.O #160966) +General : Updated internal CImg library to last stable 1.2.9 (released at 2008/06/26). + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 164392 : Can't compile digikam with missing libkdcraw. +002 ==> 160617 : Same image gets opened to both sides of view when draggin image to right side. +003 ==> 165823 : Digikam crashes if last picture is removed from lighttable. +004 ==> 165921 : Translation bug: Missing german translation. +005 ==> 130906 : Allow user to click text of tags to tag an image (not checkbox only). +006 ==> 126871 : Click on parent folder in tree should generate a reaction. +007 ==> 162812 : Sharpen slider enhancement refresh previews continuously. +008 ==> 166274 : Rename files on download from camera. +009 ==> 166472 : Thumbnail bar gone in image editor when switching back from fullscreen. +010 ==> 166540 : Showfoto always prints filename. + +********************************************************************************************** +digiKam 0.9.4-RC2 - Release date: 2008-07-06 + +NEW FEATURES: + +General : external libsqlite3 dependency removed. sqlite3 source code is now included in digiKam core. + (see B.K.O #160966) + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 164301 : Filter Albums sub-tree names too, when using quickfilter on icon view. +002 ==> 164482 : Place filename of image in klipper/paste buffer from thumbnail right mouse button context menu. +003 ==> 164536 : Add adjust mid range color levels. +004 ==> 164615 : Image detail info about geometry (X-pixel / Y-pixel) missed. +005 ==> 149654 : Fullscreen icon changes image's zoom to 100% (should fit to screen). +006 ==> 162688 : Digikam RC5, can't see scroll bar sliders in dark theme. +007 ==> 161212 : Switch to full screen resets X11. +008 ==> 164432 : Compilation fails with gcc 3. + +********************************************************************************************** +digiKam 0.9.4-RC1 - Release date: 2008-06-17 + +NEW FEATURES: + +General : external libsqlite3 dependency removed. sqlite3 source code is now included in digiKam core. + (see B.K.O #160966) + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 162496 : When Digikam saves file as JPEG - JPG options are not always presented. +002 ==> 160740 : Handbook says that libgphoto2 is required but does not say whether it should be compiled with exif support. +003 ==> 148400 : Problem loading 16bit Grayscale TIFF image. +004 ==> 160925 : digiKam crash, I don't know why, but I have the bugreport. +005 ==> 162691 : 0.9.4 beta5 compile error imagehistogram.cpp. +006 ==> 160966 : Some searches don't work properly with the new sqlite3-3.5.8. +007 ==> 162245 : Assigned tag list not updated correctly when "show assigned tags" filter is active. +008 ==> 158866 : Advanced Search on Tags a mess. +009 ==> 162814 : digiKam crashes when using light table (page down). +010 ==> 163151 : Red eye correction doesn't work fine. +011 ==> 163227 : Searching for tag names in simple and advanced search doesn't work. +012 ==> 163474 : Config UI using external media. +013 ==> 156420 : Canon EOS 400D, no thumbnails displayed then digikam segfaults. +014 ==> 163118 : digikam-0.9.4_beta5 compilation hangs with gcc 4.3. + +********************************************************************************************** + +digiKam 0.9.4-beta5 - Release date: 2008-05-25 + +NEW FEATURES: + +General : English words review in whole GUI by Oliver Doer + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 162132 : Crash while saving tiff (possibly caused by present alpha channel). +002 ==> 155046 : light-table useability, possible improvements. +003 ==> 161085 : Zoom steps in image editor +004 ==> 162247 : The photos thumbnails list would be better if in the end of an album/date/tag start the next. +005 ==> 160523 : Crash when saving picture as new nameafter resizing. +006 ==> 162428 : debug info needed ? +007 ==> 147597 : Typos in the digiKam KDE3 PO file. + +********************************************************************************************** + +digiKam 0.9.4-beta4 - Release date: 2008-04-27 + +NEW FEATURES: + +General : Updated internal CImg library to last stable 1.2.8 (released at 2008/04/18). +General : New search text filter for all metadata sidebar tabs. +General : New dialog to list all RAW camera supported. A search camera model tool is + available to find easy a camera from list. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 121309 : Camera list sorted by brand. +002 ==> 137836 : Usability: Configure camera GUI add and auto-detect camera. +003 ==> 160323 : Changing the album name from sth to #sth and then to zsth causes the album to be deleted along with the files. +004 ==> 145083 : Space and Shift-Space isn't used for navigation between images. +005 ==> 158696 : digiKam image viewer crashes when "Save As" is clicked. +006 ==> 156564 : Light table crashes when choosing channel. +007 ==> 147466 : digiKam crashes on termination if an audio file played. +008 ==> 146973 : Crashes when clicking on preview image. +009 ==> 148976 : digiKam Deleted 2.3 g of Pictures. +010 ==> 145252 : Umask settings used for album directory, not for image files. +011 ==> 125775 : digiKam way to save and delete "Searches". +012 ==> 150908 : Canon g3 not recognized works version 0.9.1. +013 ==> 152092 : Showfoto crashes ( signal 11 ) on exit. +014 ==> 160840 : Wrong filtering on Album-Subtree-View. +015 ==> 160846 : Blurred preview of pics. +016 ==> 157314 : Zoom-slider has no steps. +017 ==> 161047 : Shift and wheel mouse doesn't work. +018 ==> 161087 : Conflicting directions of mouse scroll when zooming. +019 ==> 161084 : Not properly updates status bar info. + +********************************************************************************************** + +digiKam 0.9.4-beta3 - Release date: 2008-04-07 + +NEW FEATURES: + +AlbumGUI : Auto-completion in all search text filter. + +General : Creation of tags simplified everywhere. Multiple Tags hierarchy can be + created at the same time. Tags creation dialog re-designed. + +Showfoto : New open image file dialog with photo thumbnail/information. + +ImageEditor : New saveas image file dialog with photo thumbnail/information. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 159806 : Cannot display Nikon d3 raw files. +002 ==> 148935 : digiKam hot pixel plugin can not read canon RAW files. +003 ==> 158776 : Confirm tag delete if tag assigned to photos. +004 ==> 148520 : Change of rating causes high CPU load. +005 ==> 140227 : Remove or modify D&D tags menu from icon view area. +006 ==> 154421 : Live search doesn't search filenames. +007 ==> 118209 : digiKam hotplug script kde user detection problem. +008 ==> 138766 : JPEG Rotations: Give a warning when doing Lossy rotations (and offer to do lossless when applicable). +009 ==> 156007 : Canon Raw+Jpg Locks up download image window during thumbnail retrieval. +010 ==> 114465 : Simpler entry of tags. +011 ==> 159236 : Corrupted file when downloading from CF card via digikam. + +********************************************************************************************** + +digiKam 0.9.4-beta2 - Release date: 2008-03-23 + +NEW FEATURES: + +ImageEditor : Raw files can be decoded in 16 bits color depth without to use Color Management. + An auto-gamma and auto-white balance is processed using the same method than dcraw + with 8 bits color depth RAW image decoding. This usefull to to have a speed-up RAW + workflow with suitable images. +Showfoto : Added support of color theme schemes. +Showfoto : Added 2 options to setup images ordering with "File/Open Folder" action. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 146393 : No gamma adjustment when opening RAW file. +002 ==> 158377 : digiKam duplicates downloaded images while overwriting existing ones. +003 ==> 151122 : Opening Albums Has Become Very Slow. +004 ==> 145252 : Umask settings used for album directory, not for image files +005 ==> 144253 : Failed to update old Database to new Database format. +006 ==> 159467 : Misleading error message with path change in digikamrc file. +007 ==> 157237 : Connect the data when using the left tabs. +008 ==> 157309 : digiKam mouse scroll direction on lighttable. +009 ==> 158398 : Olympus raw images shown with wrong orientation. +010 ==> 152257 : Crash on saving images after editing. +011 ==> 153730 : Too many file format options when saving raw image from editor in digiKam. +012 ==> 151157 : Right and left keys goes to first picture and last picture without shortcuts. +013 ==> 151451 : Application crashed using edit. +014 ==> 139657 : Blueish tint in low saturation images converted to CMYK. +015 ==> 147600 : Showfoto Open folder - files shown in reverse order. +016 ==> 149851 : Showfoto asks if you want to save changes when deleting photo, if changes have been made. +017 ==> 138378 : Showfoto opens file browser window to last the folder used upon startup. +018 ==> 148964 : images are blur only inside showFoto. +019 ==> 146938 : Themes Don't Apply To All Panels. +020 ==> 135378 : Single-click on picture open it in editor. +021 ==> 142469 : Splash screen to be the very first thing. +022 ==> 147619 : Crash when viewing video. +023 ==> 136583 : Album icon does not show preview-image after selecting given image as preview. +024 ==> 129357 : Thumbnails not rotated for (D200)-NEF. +025 ==> 132047 : Faster display of images and/or prefetch wished for. +026 ==> 150259 : media-gfx/digikam-0.9.2 error/typo in showfoto.desktop. +027 ==> 150674 : Turn of live update of preview when moving points in curves dialog or in histogram. +028 ==> 141703 : Show file creation date in tooltip. +029 ==> 128377 : Adjust levels: histograms don't match, bad "auto level" function. +030 ==> 141755 : Sidebar: Hide unused tabs, Add tooltips. +031 ==> 146068 : Mixer does not work for green and blue selected as main channel and monochrome mode. +032 ==> 135931 : Reorganize Batch Processing tools from 'Image' and 'Tools' menus. +033 ==> 159664 : Background color of live/quick filter not updated on folder/date change. + +********************************************************************************************** + +digiKam 0.9.4-beta1 - Release date: 2008-03-09 + +NEW FEATURES: + +General : Color theme scheme are now supported everywhere in graphical interfaces. +General : Color theme scheme can be changed from Editor and LightTable. +General : Updated internal CImg library to last stable 1.2.7 (released at 2008/01/23). +General : Add capability to display count of items in all Album, Date, Tags, and + Tags Filter Folder View. + The number of items contained in virtual or physical albums can be + displayed on the right of album name. If a tree branch is collapsed, + parents album sum-up the number of items from all undisplayed children albums. + Count of items is performed in background by digiKam TDEIO-Slaves. + A new option from Setup/Album dialog page can toggle on/off this feature. + +AlbumGUI : Add a new tool to perform Date search around whole albums collection: Time-Line. +AlbumGUI : In Calendar View, selecting Year album show all pictures relevant. +AlbumGUI : Add a new status-bar indicator to report album icon view filtering status. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 96388 : Show number of images in the album. +002 ==> 155271 : Configure suggests wrong parameter for libjasper. +003 ==> 155105 : Broken png image present in albums folder causes digikam to crash when starting. +004 ==> 146760 : Providing a Timeline-View for quickly narrowing down the date of photos. +005 ==> 146635 : Ratio crop doesn't remember orientation. +006 ==> 144337 : There should be no "empty folders". +007 ==> 128293 : Aspect ratio crop does not respect aspect ratio. +008 ==> 157149 : digiKam crash at startup. +009 ==> 141037 : Search using Tag Name does not work +010 ==> 158174 : Precise aspect ratio crop feature +011 ==> 142055 : Which whitebalance is used +012 ==> 158558 : Delete Function in Tag Filters panel needs to make sure that the tag is + unselected when the tag is deleted. +013 ==> 120309 : Change screen backgroundcolor of image tools. +014 ==> 153775 : Download from camera: Renaming because of already existing file does not work. +015 ==> 154346 : Can't rename in digiKam. +016 ==> 154625 : Image-files are not renamed before they are saved to disk. +017 ==> 154746 : Album selection window's size is wrong. diff --git a/src/NEWS.0.9.5 b/src/NEWS.0.9.5 new file mode 100644 index 00000000..290a77f4 --- /dev/null +++ b/src/NEWS.0.9.5 @@ -0,0 +1,103 @@ +********************************************************************************************** +digiKam 0.9.5 - Release date: 2009-03-15 + +NEW FEATURES: + +General : Internal CImg library updated to 1.3.0. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 169037 : Crash when tagging photos. +002 ==> 173314 : digiKam crashes while exiting the photo-editing mode in KDE 3.5.10. +003 ==> 185279 : Raw pictures from Panasonic LX2 have totally wrong colours in digikam show modus. + +********************************************************************************************** +digiKam 0.9.5-beta3 - Release date: 2009-01-27 + +NEW FEATURES: + +Image Editor : New composition guide based on Diagonal Rules. + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 176477 : Files disappear while importing. +002 ==> 179134 : Compile-error with libkdcraw > 0.1.5. +003 ==> 162535 : Startup is extremely slow when famd is running. +004 ==> 179413 : Support restoring blown out highlights. +005 ==> 157313 : Option to reposition lighttable slide-list. +006 ==> 180671 : Scanner import does not work. +007 ==> 175387 : digikam-doc 0.9.4 sf.net tarball is outdated. +008 ==> 181712 : Ubuntu 8.10 - digiKam finds not the correct images. + +********************************************************************************************** +digiKam 0.9.5-beta2 - Release date: 2008-12-14 + +NEW FEATURES: + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 164573 : Better support for small screens. +002 ==> 175970 : digitaglinktree merges tags with same name in different subfolders. +003 ==> 108760 : Use collection image (or part of) as Tag/Album icon. +004 ==> 144078 : very slow avi startup. +005 ==> 146258 : Moving an album into waste basket didn't remove. +006 ==> 149165 : cannot edit anymore - sqlite lock? +007 ==> 146025 : Wrong image name when using info from EXIF. +008 ==> 167056 : Updating tags is slow when thumbnails are visible. +009 ==> 141960 : Problems with photos without EXIV data when updating me. +010 ==> 129379 : Renamed Album is shown multiple times although there is only on related picture directory. +011 ==> 171247 : Album creation error when name start by a number. +012 ==> 150906 : digiKam unable to connect to Panasonic LUMIX DMC-TZ3. +013 ==> 148812 : "Auto Rotate/Flip &Using Exif Orientation" fails with some images. +014 ==> 150342 : Camera image window keeps scrolling to the currently downloaded picture. +015 ==> 147475 : digiKam Slideshow - Pause button does not stay sticky / work. +016 ==> 148899 : Image Editor does not get the focus after clicking on an image. +017 ==> 148596 : Empty entries in the "back" drop-down when changing month (date view). +018 ==> 161387 : Unable to import photos with digiKam even though it's detected. +019 ==> 165229 : Thumbnail complete update does not work reliably, even for jpgs. + +********************************************************************************************** +digiKam 0.9.5-beta1 - Release date: 2008-11-05 + +NEW FEATURES: + + +Image Editor : All image plugin tool settings provide default buttons to reset values. +Image Editor : New Raw import tool to handle Raw pictures with customized decoding settings. +Image Editor : All image plugin dialogs are removed. All tools are embedded in editor window. + +General : libkdcraw dependency updated to 0.1.5 +General : TIFF/PNG/JPEG2000 metadata can be edited, changed (require Exiv2 >= 0.18). + +BUGFIXES FROM KDE BUGZILLA (alias B.K.O | http://bugs.kde.org): + +001 ==> 166867 : digiKam crashes when starting. +002 ==> 167026 : Crash on startup Directory ImageSubIfd0 not valid. +003 ==> 146870 : Don't reduce size of image when rotating. +004 ==> 167528 : Remove hotlinking URL from tips. +005 ==> 167343 : Albums view corrupted and unusable. +006 ==> 146033 : When switching with Page* image jumps. +007 ==> 127242 : Flashing 'histogram calculation in progress' is a bit annoying. +008 ==> 150457 : More RAW controls in color management pop-up please. +009 ==> 155074 : Edit Image should allow chance to adjust RAW conversion parameters. +010 ==> 155076 : RAW Conversion UI Needs to be more generic. +011 ==> 142975 : Better support for RAW photo handling. +012 ==> 147136 : When selecting pictures from folder showfoto got closed. +013 ==> 168780 : Loose the raw import. +014 ==> 160564 : No refresh of the number of pictures assigned to a tag after removing the tag from some pictures. +015 ==> 158144 : Showfoto crashes in settings window. +016 ==> 162845 : 'Ctrl+F6' Conflict with KDE global shortcut. +017 ==> 159523 : digiKam crashes while downloading images from Olympus MJU 810/Stylus 810 camera no xD card. +018 ==> 161369 : Quick filter indicator lamp is not working properly in recursive image folder view mode. +019 ==> 147314 : Renaming like crazy with F2 slows down my system. +020 ==> 164622 : Crash using timeline when switching from month to week to month view. +021 ==> 141951 : Blank empty context right-click menus. +022 ==> 116886 : Proposal for adding a new destripe/denoise technique. +023 ==> 163602 : Race condition during image download from Camera/ USB device: image corruption and/or loss. +024 ==> 165857 : Unreliable import when photo root is on network share. +025 ==> 147435 : Resize slider in sharpness dialog doesn't work correct. +026 ==> 168844 : Make the selection rectangle draggable over the image (not only resizable). +027 ==> 147151 : Compile error: multiple definition of `jpeg_suppress_tables'. +028 ==> 170758 : High load when tagging. +029 ==> 168003 : Drag&dropping a photo to a folder in Dolphin begins copying the whole system:/media. +030 ==> 142457 : Temp files not cleaned up after crashes. diff --git a/src/configure.in.bot b/src/configure.in.bot new file mode 100644 index 00000000..8a34d97b --- /dev/null +++ b/src/configure.in.bot @@ -0,0 +1,133 @@ +dnl Put here things to be done at the very end - telling users +dnl about additional packages to install. + +echo "" +echo "-- digiKam configure results -------------------" + +if test "x$included_sqlite3" = "xno"; then + if test "x$have_sqlite3" != "xyes"; then + echo "-- using internal libsqlite3...... NO" + echo "-- libsqlite3 library found....... NO" + echo "" + echo "digiKam have been set to be compiled using shared libsqlite3 library." + echo "digiKam needs libsqlite3 library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libsqlite3 website is http://www.sqlite.org" + echo "" + all_tests=bad + else + echo "-- using internal libsqlite3...... NO" + echo "-- libsqlite3 library found....... YES" + fi +else + echo "-- using internal libsqlite3...... YES" +fi + +if test "x$have_libgphoto2" != "xyes"; then + echo "-- libgphoto2 library found....... NO" + echo "" + echo "digiKam needs libgphoto2 library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libgphoto2 website is http://www.gphoto.org" + echo "" + all_tests=bad +else + echo "-- libgphoto2 library found....... YES" +fi + +if test "x$have_tiff" != "xyes"; then + echo "-- libtiff library found.......... NO" + echo "" + echo "digiKam needs libtiff library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libtiff website is http://www.remotesensing.org/libtiff" + echo "" + all_tests=bad +else + echo "-- libtiff library found..... .... YES" +fi + +if test "x$have_png" != "xyes"; then + echo "-- libpng library found........... NO" + echo "" + echo "digiKam needs libpng library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libpng website is http://www.libpng.org/pub/png/libpng.html" + echo "" + all_tests=bad +else + echo "-- libpng library found........... YES" +fi + +if test "x$have_jasper" != "xyes"; then + echo "-- libjasper library found........ NO" + echo "" + echo "digiKam needs libjasper library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libjasper website is http://www.ece.uvic.ca/~mdadams/jasper" + echo "Important note: libjasper has to be configured with --enable-shared=yes" + echo "as otherwise the required dynamic libraries are not created." + echo "" + all_tests=bad +else + echo "-- libjasper library found........ YES" +fi + +if test "x$have_lcms" != "xyes"; then + echo "-- liblcms library found.......... NO" + echo "" + echo "digiKam needs liblcms library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "liblcms website is http://www.littlecms.com" + echo "" + all_tests=bad +else + echo "-- liblcms library found.......... YES" +fi + +if test "x$have_libkipi" != "xyes"; then + echo "-- libkipi library found.......... NO" + echo "" + echo "digiKam needs libkipi library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libkipi website is http://www.kipi-plugins.org" + echo "" + all_tests=bad +else + echo "-- libkipi library found.......... YES" +fi + +if test "x$have_libkexiv2" != "xyes"; then + echo "-- libkexiv2 library found........ NO" + echo "" + echo "digiKam needs libkexiv2 library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libkexiv2 website is http://www.kipi-plugins.org" + echo "" + all_tests=bad +else + echo "-- libkexiv2 library found........ YES" +fi + +if test "x$have_libkdcraw" != "xyes"; then + echo "-- libkdcraw library found........ NO" + echo "" + echo "digiKam needs libkdcraw library development package." + echo "You need to install the right version first." + echo "Look depencies description from README for details." + echo "libkdcraw website is at http://www.kipi-plugins.org" + echo "" + all_tests=bad +else + echo "-- libkdcraw library found........ YES" +fi + +echo "------------------------------------------------" diff --git a/src/configure.in.in b/src/configure.in.in new file mode 100644 index 00000000..7095137f --- /dev/null +++ b/src/configure.in.in @@ -0,0 +1,387 @@ +#MIN_CONFIG(3) + +# ----------------------------------------------------------------- +# +# enable hidden visibility only if kde >= 3.3.2 and tdelibs has +# been compiled with visibility enabled +# +# ----------------------------------------------------------------- + +AC_LANG_PUSH(C++) +digikam_save_cppflags=$CPPFLAGS +CPPFLAGS="$CPPFLAGS $all_includes" +AC_MSG_CHECKING([if hidden visibility should be enabled]) +AC_COMPILE_IFELSE( + [ + #include + #include + int other_func( void ) + { + #if KDE_IS_VERSION(3,3,2) + #else + iam dying; + #endif + #ifdef __TDE_HAVE_GCC_VISIBILITY + #else + no, iam really dead; + #endif + return 0; + } + ], + [ AC_MSG_RESULT([yes]) + digikam_enable_hidden_visibility="yes" ], + [ AC_MSG_RESULT([no]) ] +) +CPPFLAGS=$digikam_save_cppflags +AC_LANG_POP(C++) + +if test "x$digikam_enable_hidden_visibility" = "xyes"; then + KDE_ENABLE_HIDDEN_VISIBILITY +fi + +# ----------------------------------------------------------------- +# +# pkg config check +# +# ----------------------------------------------------------------- + +AC_ARG_VAR(PKGCONFIGFOUND, [Path to pkg-config]) +AC_CHECK_PROG(PKGCONFIGFOUND, pkg-config,[yes]) + +# ----------------------------------------------------------------- +# +# sqlite2 type check +# +# ----------------------------------------------------------------- + +KDE_CHECK_TYPES + +# ------------------------------------------------------- +# +# Check endianness +# +# ------------------------------------------------------- + +AC_LANG_SAVE +AC_LANG_C +AC_C_BIGENDIAN +AC_LANG_RESTORE + +# ----------------------------------------------------------------- +# +# Check for liblcms +# +# ----------------------------------------------------------------- + +have_lcms_header='no' +KDE_CHECK_HEADER(lcms/lcms.h,have_lcms_header='yes',,) +if test "$have_lcms_header" = 'yes' +then + AC_DEFINE(LCMS_HEADER, , [The correct header]) +else + # Alternative! Debian does it this way... + KDE_CHECK_HEADER(lcms.h,have_lcms_header='yes',,) + if test "$have_lcms_header" = 'yes' + then + AC_DEFINE(LCMS_HEADER, , [The correct header]) + fi +fi + +LCMS_LIBS='' +have_lcms='no' +if test "$have_lcms_header" = 'yes' +then + saved_cflags="$CFLAGS" + saved_ldflags="$LDFLAGS" + saved_libs=$LIBS + LIBS="$LIBS -llcms" + CFLAGS="$CFLAGS $all_includes" + LDFLAGS="$LDFLAGS $all_libraries" + AC_TRY_LINK([ +#define inline __inline /* gcc is in ansi mode */ +#include LCMS_HEADER +#if LCMS_VERSION < 112 +choke! +#endif +], [ +cmsOpenProfileFromFile("foo", "r"); +], + [LCMS_LIBS='-llcms'; have_lcms='yes']) + LIBS=$saved_libs + CFLAGS=$saved_cflags + LDFLAGS=$saved_ldflags +fi + +if test -z "$LCMS_LIBS"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE digikam" +fi + +AC_SUBST(LCMS_LIBS) + +#------------------------------------------------------------------ +# +# Check for libgphoto2 +# +#------------------------------------------------------------------ + +KDE_PKG_CHECK_MODULES(GPHOTO2, libgphoto2 >= 2.5, + [have_libgphoto2=yes; have_gphoto25=yes], have_libgphoto2=no) +if test "x$have_libgphoto2" = "xno"; then + KDE_PKG_CHECK_MODULES(GPHOTO2, libgphoto2, + have_libgphoto2=yes, have_libgphoto2=no) +fi +if test "x$have_libgphoto2" = "xno"; then + AC_PATH_PROG(GPHOTO_CONFIG,gphoto2-config) + AC_PATH_PROG(GPHOTO_PORT_CONFIG,gphoto2-port-config) + if test -n "${GPHOTO_CONFIG}"; then + GPHOTO_VERSION="`$GPHOTO_CONFIG --version`" + case "${GPHOTO_VERSION}" in "libgphoto2 2.5"*) CXXFLAGS="$CXXFLAGS -DHAVE_GPHOTO25";; esac + GPHOTO_CFLAGS="`$GPHOTO_CONFIG --cflags`" + AC_SUBST(GPHOTO_CFLAGS) + LIB_GPHOTO="`$GPHOTO_CONFIG --libs` `$GPHOTO_PORT_CONFIG --libs`" + AC_SUBST(LIB_GPHOTO) + have_libgphoto2=yes + else + AC_MSG_WARN([gPhoto2 not found.]) + DO_NOT_COMPILE="digikam $DO_NOT_COMPILE" + fi +else + if test "x$have_gphoto25" = "xyes"; then + GPHOTO_CFLAGS="$GPHOTO2_CFLAGS -DHAVE_GPHOTO25" + else + GPHOTO_CFLAGS="$GPHOTO2_CFLAGS" + fi + LIB_GPHOTO="$GPHOTO2_LIBS" + AC_SUBST(GPHOTO_CFLAGS) + AC_SUBST(LIB_GPHOTO) +fi + +#------------------------------------------------------------------ +# +# Check for libkipi +# +#------------------------------------------------------------------ + +if test "$PKGCONFIGFOUND" = "yes" ; then + have_libkipi=no + + KDE_PKG_CHECK_MODULES(LIBKIPI, libkipi >= 0.1.5, + have_libkipi=yes, have_libkipi=no) + + if test "x$have_libkipi" = "xno"; then + LIBKIPI_CFLAGS="" + LIBKIPI_LIBS="" + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([found]) + fi +else + LIBKIPI_CFLAGS="" + LIBKIPI_LIBS="" + AC_MSG_RESULT([not found]) +fi +AC_SUBST(LIBKIPI_CFLAGS) +AC_SUBST(LIBKIPI_LIBS) + +if test "x$have_libkipi" != "xyes"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE digikam" +fi + +# -------------------------------------------------------------------- +# +# Check for libkexiv2 +# +# -------------------------------------------------------------------- + +if test "$PKGCONFIGFOUND" = "yes" ; then + have_libkexiv2=no + + KDE_PKG_CHECK_MODULES(LIBKEXIV2, libkexiv2 >= 0.1.6, + have_libkexiv2=yes, have_libkexiv2=no) + + if test "x$have_libkexiv2" = "xno"; then + LIBKEXIV2_CFLAGS="" + LIBKEXIV2_LIBS="" + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([found]) + fi +else + LIBKEXIV2_CFLAGS="" + LIBKEXIV2_LIBS="" + AC_MSG_RESULT([not found]) +fi +AC_SUBST(LIBKEXIV2_CFLAGS) +AC_SUBST(LIBKEXIV2_LIBS) + +if test "x$have_libkexiv2" != "xyes"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE digikam" +fi + +# -------------------------------------------------------------------- +# +# Check for libkdcraw +# +# -------------------------------------------------------------------- + +if test "$PKGCONFIGFOUND" = "yes" ; then + have_libkdcraw=no + + KDE_PKG_CHECK_MODULES(LIBKDCRAW, libkdcraw >= 0.1.5, + have_libkdcraw=yes, have_libkdcraw=no) + + if test "x$have_libkdcraw" = "xno"; then + LIBKDCRAW_CFLAGS="" + LIBKDCRAW_LIBS="" + AC_MSG_RESULT([not found]) + else + AC_MSG_RESULT([found]) + fi +else + LIBKDCRAW_CFLAGS="" + LIBKDCRAW_LIBS="" + AC_MSG_RESULT([not found]) +fi +AC_SUBST(LIBKDCRAW_CFLAGS) +AC_SUBST(LIBKDCRAW_LIBS) + +if test "x$have_libkdcraw" != "xyes"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE digikam" +fi + +#------------------------------------------------------------------ +# +# Check for libtiff +# +#------------------------------------------------------------------ + +have_tiff=no +KDE_CHECK_LIB(tiff, TIFFWriteScanline, + have_tiff=yes, + AC_MSG_WARN([TIFF library not found]), + -ljpeg -lz -lm) + +if test "x$have_tiff" = "xyes"; then + KDE_CHECK_HEADER(tiffio.h, have_tiff=yes, have_tiff=no) +fi + +if test "x$have_tiff" != "xyes"; then + AC_WARN([TIFF library not found, digiKam will not be compiled.]) + DO_NOT_COMPILE="digikam $DO_NOT_COMPILE" +else + LIB_TIFF="-ltiff" + AC_SUBST(LIB_TIFF) +fi + +#------------------------------------------------------------------ +# +# Check for libpng (with png_set_add_alpha() function) +# +#------------------------------------------------------------------ + +have_png=no +KDE_CHECK_LIB(png, png_set_add_alpha, + have_png=yes, + AC_MSG_WARN([digiKam requires libpng >= 1.2.7]), + -lpng -lz -lm) + +if test "x$have_png" != "xyes"; then + AC_WARN([digiKam requires libpng >= 1.2.7; digiKam will not be compiled.]) + DO_NOT_COMPILE="digikam $DO_NOT_COMPILE" +else + LIB_PNG="-lpng" + AC_SUBST(LIB_PNG) +fi + +#------------------------------------------------------------------ +# +# Check for libjasper (JPEG2000) +# +#------------------------------------------------------------------ + +have_jasper=no +KDE_CHECK_LIB(jasper, jas_init, + have_jasper=yes, + AC_MSG_WARN([digiKam requires libjasper >= 1.7.0]), + -ljasper) + +if test "x$have_jasper" != "xyes"; then + AC_WARN([digiKam requires libjasper >= 1.7.0; digiKam will not be compiled.]) + DO_NOT_COMPILE="digikam $DO_NOT_COMPILE" +else + LIB_JASPER="-ljasper" + AC_SUBST(LIB_JASPER) +fi + +#------------------------------------------------------------------ +# +# Check for local/shared sqlite3 +# +#------------------------------------------------------------------ + +LIB_SQLITE3="" + +AC_ARG_WITH(included-sqlite3, + AC_HELP_STRING([--without-included-sqlite3],[build digiKam using system sqlite3 library]), + [included_sqlite3=$withval], + [included_sqlite3=yes] +) + +if test x$included_sqlite3 = xno; then + if test x$PKGCONFIGFOUND = xyes; then + PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.5, have_sqlite3=yes,have_sqlite3=no) + + if test x$have_sqlite3 = xyes; then + ## AC_DEFINE(HAVE_SQLITE3, 1, [have SQLite3 database library]) + LIB_SQLITE3=`pkg-config --libs sqlite3` + else + # We don't support not having sqlite3 anymore + DO_NOT_COMPILE="digikam $DO_NOT_COMPILE" + fi + fi +fi + +AC_SUBST(LIB_SQLITE3) +AM_CONDITIONAL(with_included_sqlite3, [test x$included_sqlite3 = xyes]) + +############################################################################### +# END SQLITE CHECK +############################################################################### + + +#------------------------------------------------------------------ +# +# NFS is Evil (sqlite makes use of file locking for allowing +# multiple processes to access the database. but on many +# nfs implementations, this file locking is horribly broken and +# can end up locking the app or not allowing access to the app. +# since we use tdeioslaves which access the db too) +# +#------------------------------------------------------------------ + +AC_ARG_ENABLE(nfs-hack, + AC_HELP_STRING([--enable-nfs-hack], +[Enable a hack for album libraries on a nfs mount, + which causes the database to be saved in + $HOMEDIR/.trinity/share/apps/digikam/directoryname.db [default=disable]]), + [enable_nfs_hack=$enableval], + [enable_nfs_hack=no] +) + +if test "x$enable_nfs_hack" = "xyes"; then + AC_DEFINE(NFS_HACK, 1, [NFS hack enabled]) + AC_MSG_NOTICE([NFS hack enabled. Make sure you know what you are doing]) +fi + +#------------------------------------------------------------------ +# +# get the gcc version +# +# CImg.h version 1.2.8 do not compile fine with gcc 4.3.x +# See B.K.O #163118: digikam-0.9.4_beta5 compilation hangs with gcc 4.3 +# Using -fno-tree-pre is work around this problem. +# +#------------------------------------------------------------------ + +KDE_CHECK_COMPILER_FLAG(fno-tree-pre,[CXXFLAGS="-fno-tree-pre $CXXFLAGS"]) + diff --git a/src/digikam/Makefile.am b/src/digikam/Makefile.am new file mode 100644 index 00000000..6f0ac90f --- /dev/null +++ b/src/digikam/Makefile.am @@ -0,0 +1,170 @@ +METASOURCES = AUTO + +if with_included_sqlite3 + LIB_SQLITE3_LOCAL = $(top_builddir)/src/libs/sqlite3/libsqlite3.la + SQLITE3_INCLUDES = -I$(top_srcdir)/src/libs/sqlite3 +endif + +INCLUDES = -I$(top_srcdir)/src/libs/sqlite2 \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/thumbbar \ + -I$(top_srcdir)/src/libs/jpegutils \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/imageproperties \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_builddir)/src/libs/dialogs \ + -I$(top_srcdir)/src/utilities/cameragui \ + -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/utilities/setup \ + -I$(top_srcdir)/src/utilities/slideshow \ + -I$(top_srcdir)/src/utilities/batch \ + -I$(top_srcdir)/src/utilities/lighttable \ + $(SQLITE3_INCLUDES) \ + $(LIBKEXIV2_CFLAGS) \ + $(LIBKIPI_CFLAGS) \ + $(LIBKDCRAW_CFLAGS) \ + $(GPHOTO_CFLAGS) \ + $(all_includes) + + +# -- shared digiKam library rules ----------------------------------------------- + +lib_LTLIBRARIES = libdigikam.la + +# NOTE from Gilles (30-11-06): kdateedit.cpp and metadatahub.cpp must be placed on the top +# of source file list to unbreak compilation with './configure -enable-final' option. +# I suspect a problem with X11 header included into albumfolderview.cpp witch redefine 'enum' type. +libdigikam_la_SOURCES = kdateedit.cpp \ + metadatahub.cpp \ + digikamapp.cpp \ + album.cpp \ + albumdb.cpp \ + albumdb_sqlite2.cpp \ + albumicongroupitem.cpp \ + albumiconitem.cpp \ + albumiconview.cpp \ + albumiconviewfilter.cpp \ + albumitemhandler.cpp \ + albumfiletip.cpp \ + albumfolderview.cpp \ + albumhistory.cpp \ + albumlister.cpp \ + albummanager.cpp \ + albumpropsedit.cpp \ + albumsettings.cpp \ + albumthumbnailloader.cpp \ + albumwidgetstack.cpp \ + cameralist.cpp \ + cameratype.cpp \ + datefolderview.cpp \ + dcopiface.cpp \ + dcopiface.skel \ + digikamfirstrun.cpp \ + digikamview.cpp \ + dio.cpp \ + dragobjects.cpp \ + firstrun.cpp \ + folderitem.cpp \ + folderview.cpp \ + iconview.cpp \ + icongroupitem.cpp \ + iconitem.cpp \ + imageattributeswatch.cpp \ + imageinfo.cpp \ + imagepreviewview.cpp \ + kdatetimeedit.cpp \ + kdatepickerpopup.cpp \ + kipiinterface.cpp \ + mediaplayerview.cpp \ + mimefilter.cpp \ + monthwidget.cpp \ + pixmapmanager.cpp \ + ratingfilter.cpp \ + ratingpopupmenu.cpp \ + ratingwidget.cpp \ + scanlib.cpp \ + searchadvanceddialog.cpp \ + searchfolderview.cpp \ + searchquickdialog.cpp \ + searchresultsview.cpp \ + searchresultsitem.cpp \ + searchwidgets.cpp \ + syncjob.cpp \ + tageditdlg.cpp \ + tagfilterview.cpp \ + tagfolderview.cpp \ + tagspopupmenu.cpp \ + timelinefolderview.cpp \ + timelineview.cpp \ + timelinewidget.cpp \ + upgradedb_sqlite2tosqlite3.cpp \ + welcomepageview.cpp + +# NOTE: if local libsqlite3 is used LIB_SQLITE3 is null. +# if shared libsqlite3 is used LIB_SQLITE3_LOCAL is null. +libdigikam_la_LIBADD = $(LIB_SQLITE3) \ + $(LIB_SQLITE3_LOCAL) \ + $(LIB_TQT) \ + $(LIB_TDEPARTS) \ + $(LIB_TDEIO) \ + $(LIB_TDEABC) \ + $(LIB_TDEHTML) \ + $(LIBKIPI_LIBS) \ + $(LIBKEXIV2_LIBS) \ + $(LIB_TDEUTILS) \ + $(top_builddir)/src/libs/sqlite2/libsqlite2.la \ + $(top_builddir)/src/libs/thumbbar/libthumbbar.la \ + $(top_builddir)/src/libs/themeengine/libthemeengine.la \ + $(top_builddir)/src/libs/widgets/libwidgets.la \ + $(top_builddir)/src/libs/dialogs/libdialog.la \ + $(top_builddir)/src/libs/jpegutils/libjpegutils.la \ + $(top_builddir)/src/libs/dimg/libdimg.la \ + $(top_builddir)/src/libs/imageproperties/libimagepropertiesdigikam.la \ + $(top_builddir)/src/libs/threadimageio/libthreadimageio.la \ + $(top_builddir)/src/libs/greycstoration/libgreycstoration.la \ + $(top_builddir)/src/utilities/batch/libbatch.la \ + $(top_builddir)/src/utilities/slideshow/libslideshow.la \ + $(top_builddir)/src/utilities/cameragui/libcameragui.la \ + $(top_builddir)/src/utilities/imageeditor/canvas/libdimgcanvas.la \ + $(top_builddir)/src/utilities/imageeditor/editor/libdimgeditor.la \ + $(top_builddir)/src/utilities/setup/libsetup.la \ + $(top_builddir)/src/utilities/lighttable/liblighttable.la + +libdigikam_la_LDFLAGS = $(LIBKEXIV2_LIBS) $(all_libraries) $(KDE_RPATH) -no-undefined + +# -- main digiKam binary target file rules ---------------------------------------------- + +bin_PROGRAMS = digikam + +digikam_SOURCES = main.cpp + +digikam_LDADD = $(LIB_TQT) \ + $(LIB_TDECORE) \ + $(LIB_TDEUI) \ + $(LIBSOCKET) \ + $(LIB_TDEFILE) \ + $(LIB_TDEPARTS) \ + $(LIB_TDEUTILS) \ + libdigikam.la + +digikam_LDFLAGS = $(LIBKIPI_LIBS) $(LIBKDCRAW_LIBS) $(LIBKEXIV2_LIBS) $(KDE_RPATH) $(all_libraries) $(LIB_TDEUTILS) + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamui.rc + +dummy_digikam.cpp: + echo > dummy_digikam.cpp + +xdg_apps_DATA = digikam.desktop + +include_HEADERS = digikam_export.h + +include ../../admin/Doxyfile.am +noinst_HEADERS = dcopiface.h diff --git a/src/digikam/album.cpp b/src/digikam/album.cpp new file mode 100644 index 00000000..a6e3a295 --- /dev/null +++ b/src/digikam/album.cpp @@ -0,0 +1,543 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-15 + * Description : digiKam album types + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "albummanager.h" +#include "albumdb.h" +#include "album.h" + +namespace Digikam +{ + +Album::Album(Album::Type type, int id, bool root) +{ + m_parent = 0; + m_next = 0; + m_prev = 0; + m_firstChild = 0; + m_lastChild = 0; + m_clearing = false; + m_type = type; + m_id = id; + m_root = root; +} + +Album::~Album() +{ + if (m_parent) + m_parent->removeChild(this); + clear(); +} + +void Album::setParent(Album* parent) +{ + if (parent) + { + m_parent = parent; + parent->insertChild(this); + } +} + +Album* Album::parent() const +{ + return m_parent; +} + +Album* Album::firstChild() const +{ + return m_firstChild; +} + +Album* Album::lastChild() const +{ + return m_lastChild; +} + +Album* Album::next() const +{ + return m_next; +} + +Album* Album::prev() const +{ + return m_prev; +} + +void Album::insertChild(Album* child) +{ + if (!child) + return; + + if (!m_firstChild) + { + m_firstChild = child; + m_lastChild = child; + child->m_next = 0; + child->m_prev = 0; + } + else + { + m_lastChild->m_next = child; + child->m_prev = m_lastChild; + child->m_next = 0; + m_lastChild = child; + } +} + +void Album::removeChild(Album* child) +{ + if (!child || m_clearing) + return; + + if (child == m_firstChild) + { + m_firstChild = m_firstChild->m_next; + if (m_firstChild) + m_firstChild->m_prev = 0; + else + m_firstChild = m_lastChild = 0; + } + else if (child == m_lastChild) + { + m_lastChild = m_lastChild->m_prev; + if (m_lastChild) + m_lastChild->m_next = 0; + else + m_firstChild = m_lastChild = 0; + } + else + { + Album* c = child; + if (c->m_prev) + c->m_prev->m_next = c->m_next; + if (c->m_next) + c->m_next->m_prev = c->m_prev; + } +} + +void Album::clear() +{ + m_clearing = true; + + Album* child = m_firstChild; + Album* nextChild; + while (child) + { + nextChild = child->m_next; + delete child; + child = nextChild; + } + + m_firstChild = 0; + m_lastChild = 0; + m_clearing = false; +} + +int Album::globalID() const +{ + switch (m_type) + { + case (PHYSICAL): + return 10000 + m_id; + case(TAG): + return 20000 + m_id; + case(DATE): + return 30000 + m_id; + case(SEARCH): + return 40000 + m_id; + default: + DError() << "Unknown album type" << endl; + return -1; + } +} + +int Album::id() const +{ + return m_id; +} + +void Album::setTitle(const TQString& title) +{ + m_title = title; +} + +TQString Album::title() const +{ + return m_title; +} + +Album::Type Album::type() const +{ + return m_type; +} + +void Album::setExtraData(const void* key, void* value) +{ + m_extraMap.replace(key, value); +} + +void Album::removeExtraData(const void* key) +{ + m_extraMap.remove(key); +} + +void* Album::extraData(const void* key) const +{ + typedef TQMap Map; + Map::const_iterator it = m_extraMap.find(key); + if (it == m_extraMap.end()) + return 0; + + return it.data(); +} + +bool Album::isRoot() const +{ + return m_root; +} + +bool Album::isAncestorOf(Album* album) const +{ + bool val = false; + Album* a = album; + while (a && !a->isRoot()) + { + if (a == this) + { + val = true; + break; + } + a = a->parent(); + } + return val; +} + +// ------------------------------------------------------------------------------ + +PAlbum::PAlbum(const TQString& title, int id, bool root) + : Album(Album::PHYSICAL, id, root) +{ + setTitle(title); + m_caption = ""; + m_collection = ""; + m_date = TQDate::currentDate(); +} + +PAlbum::~PAlbum() +{ +} + +void PAlbum::setCaption(const TQString& caption) +{ + m_caption = caption; + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->setAlbumCaption(id(), m_caption); +} + +void PAlbum::setCollection(const TQString& collection) +{ + m_collection = collection; + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->setAlbumCollection(id(), m_collection); +} + +void PAlbum::setDate(const TQDate& date) +{ + m_date = date; + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->setAlbumDate(id(), m_date); +} + +TQString PAlbum::caption() const +{ + return m_caption; +} + +TQString PAlbum::collection() const +{ + return m_collection; +} + +TQDate PAlbum::date() const +{ + return m_date; +} + +TQString PAlbum::url() const +{ + TQString u(""); + if (isRoot()) + { + return "/"; + } + else if (parent()) + { + u = ((PAlbum*)parent())->url(); + if (!u.endsWith("/")) + u += '/'; + } + u += title(); + return u; +} + +KURL PAlbum::kurl() const +{ + KURL u; + u.setProtocol("digikamalbums"); + u.setUser(AlbumManager::instance()->getLibraryPath()); + // add an empty host. KURLDrag will eat away the user + // attribute if a host is not present. probably a URL + // specification + u.setHost(" "); + u.setPath(url()); + return u; +} + +TQString PAlbum::prettyURL() const +{ + TQString u = i18n("My Albums") + url(); + return u; +} + +TQString PAlbum::icon() const +{ + return m_icon; +} + +KURL PAlbum::iconKURL() const +{ + KURL u; + u.setPath( m_icon ); + return u; +} + +TQString PAlbum::folderPath() const +{ + KURL u(AlbumManager::instance()->getLibraryPath()); + u.addPath(url()); + return u.path(); +} + +// -------------------------------------------------------------------------- + +TAlbum::TAlbum(const TQString& title, int id, bool root) + : Album(Album::TAG, id, root) +{ + setTitle(title); +} + +TAlbum::~TAlbum() +{ +} + +TQString TAlbum::tagPath(bool leadingSlash) const +{ + if (isRoot()) + return leadingSlash ? "/" : ""; + + TQString u; + + if (parent()) + { + u = ((TAlbum*)parent())->tagPath(leadingSlash); + if (!parent()->isRoot()) + u += '/'; + } + + u += title(); + + return u; +} + +TQString TAlbum::prettyURL() const +{ + TQString u = i18n("My Tags") + tagPath(true); + return u; +} + +KURL TAlbum::kurl() const +{ + KURL url; + url.setProtocol("digikamtags"); + + if (isRoot()) + { + url.setPath("/"); + } + else if (parent()) + { + TAlbum *p = static_cast(parent()); + url.setPath(p->kurl().path(1)); + url.addPath(TQString::number(id())); + } + else + { + url = KURL(); + } + return url; +} + + +TQString TAlbum::icon() const +{ + return m_icon; +} + +// -------------------------------------------------------------------------- + +int DAlbum::m_uniqueID = 0; + +DAlbum::DAlbum(const TQDate& date, bool root, Range range) + : Album(Album::DATE, root ? 0 : ++m_uniqueID, root) +{ + m_date = date; + m_range = range; + + // Set the name of the date album + TQString dateTitle; + + if (m_range == Month) + dateTitle = m_date.toString("MMMM yyyy"); + else + dateTitle = m_date.toString("yyyy"); + + setTitle(dateTitle); +} + +DAlbum::~DAlbum() +{ +} + +TQDate DAlbum::date() const +{ + return m_date; +} + +DAlbum::Range DAlbum::range() const +{ + return m_range; +} + +KURL DAlbum::kurl() const +{ + TQDate endDate; + if (m_range == Month) + endDate = m_date.addMonths(1); + else + endDate = m_date.addYears(1); + + KURL u; + u.setProtocol("digikamdates"); + u.setPath(TQString("/%1/%2/%3/%4") + .arg(m_date.year()) + .arg(m_date.month()) + .arg(endDate.year()) + .arg(endDate.month())); + + return u; +} + +// -------------------------------------------------------------------------- + +SAlbum::SAlbum(int id, const KURL& url, bool simple, bool root) + : Album(Album::SEARCH, id, root), + m_kurl(url), m_simple(simple) +{ + setTitle(url.queryItem("name")); +} + +SAlbum::~SAlbum() +{ +} + +KURL SAlbum::kurl() const +{ + return m_kurl; +} + +bool SAlbum::isSimple() const +{ + return m_simple; +} + +// -------------------------------------------------------------------------- + +AlbumIterator::AlbumIterator(Album *album) +{ + m_root = album; + m_current = album ? album->firstChild() : 0; +} + +AlbumIterator::~AlbumIterator() +{ +} + +AlbumIterator& AlbumIterator::operator++() +{ + if (!m_current) + return *this; + + Album *album = m_current->firstChild(); + if ( !album ) + { + while ( (album = m_current->next()) == 0 ) + { + m_current = m_current->parent(); + + if ( m_current == m_root ) + { + // we have reached the root. + // that means no more children + m_current = 0; + break; + } + + if ( m_current == 0 ) + break; + } + } + + m_current = album; + return *this; +} + +Album* AlbumIterator::operator*() +{ + return m_current; +} + +Album* AlbumIterator::current() const +{ + return m_current; +} + +} // namespace Digikam diff --git a/src/digikam/album.h b/src/digikam/album.h new file mode 100644 index 00000000..ed088185 --- /dev/null +++ b/src/digikam/album.h @@ -0,0 +1,455 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-15 + * Description : digiKam album types + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file album.h */ + +#ifndef ALBUM_H +#define ALBUM_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +/** + * \class Album + * \brief Abstract base class for all album types + * + * A class which provides an abstraction for a type Album. This class is meant to + * be derived and every time a new Album Type is defined add a enum corresponding + * to that to Album::Type + * + * This class provides a means of building a tree representation for + * Albums @see Album::setParent(). + */ + +class Album +{ +public: + + enum Type + { + PHYSICAL=0, /**< PHYSICAL: A physical album type @see PAlbum */ + TAG, /**< TAG: A tag album type @see TAlbum */ + DATE, /**< DATE: A date album type @see DAlbum */ + SEARCH /**< SEARCH: A search album type @see SAlbum */ + }; + + /** + * Destructor + * + * this will also recursively delete all child Albums + */ + virtual ~Album(); + + /** + * Delete all child albums and also remove any associated extra data + */ + void clear(); + + /** + * @return the parent album for this album + */ + Album* parent() const; + + /** + * @return the first child of this album or 0 if no children + */ + Album* firstChild() const; + + /** + * @return the last child of this album or 0 if no children + */ + Album* lastChild() const; + + /** + * @return the next sibling of this album of this album or 0 + * if no next sibling + * @see AlbumIterator + */ + Album* next() const; + + /** + * @return the previous sibling of this album of this album or 0 if no + * previous sibling + * @see AlbumIterator + */ + Album* prev() const; + + /** + * @return the type of album + * @see Type + */ + Type type() const; + + /** + * Each album has a @p ID uniquely identifying it in the set of Albums of + * a Type + * + * \note The @p ID for a root Album is always 0 + * + * @return the @p ID of the album + * @see globalID() + */ + int id() const; + + /** + * An album ID is only unique among the set of all Albums of its Type. + * This is a global Identifier which will uniquely identifying the Album + * among all Albums + * + * \note If you are adding a new Album Type make sure to update + * this implementation. + * + * You can always get the @p ID of the album using something like + * + * \code + * int albumID = rootAlbum->globalID() - album->globalID(); + * \endcode + * + * @return the @p globalID of the album + * @see id() + */ + int globalID() const; + + /** + * @return the @p title aka name of the album + */ + TQString title() const; + + /** + * @return the kde url of the album + */ + virtual KURL kurl() const = 0; + + /** + * @return true is the album is a Root Album + */ + bool isRoot() const; + + /** + * @return true if the @p album is in the parent hierarchy + * + * @param album Album to check whether it belongs in the child + * hierarchy + */ + bool isAncestorOf(Album* album) const; + + /** + * This allows to associate some "extra" data to a Album. As one + * Album can be used by several objects (often views) which all need + * to add some data, you have to use a key to reference your extra data + * within the Album. + * + * That way a Album can hold and provide access to all those views + * separately. + * + * for eg, + * + * \code + * album->setExtraData( this, searchFolderItem ); + * \endcode + * + * and can later access the searchFolderItem by doing + * + * \code + * SearchFolderItem *item = static_cast(album->extraData(this)); + * \endcode + * + * Note: you have to remove and destroy the data you associated yourself + * when you don't need it anymore! + * + * @param key the key of the extra data + * @param value the value of the extra data + * @see extraData + * @see removeExtraData + */ + void setExtraData(const void* key, void *value); + + /** + * Remove the associated extra data associated with @p key + * + * @param key the key of the extra data + * @see setExtraData + * @see extraData + */ + void removeExtraData(const void* key); + + /** + * Retrieve the associated extra data associated with @p key + * + * @param key the key of the extra data + * @see setExtraData + * @see extraData + */ + void* extraData(const void* key) const; + +protected: + + /** + * Constructor + */ + Album(Album::Type type, int id, bool root); + + /** + * @internal use only + * + * Set a new title for the album + * + * @param title new title for the album + */ + void setTitle(const TQString& title); + + /** + * @internal use only + * + * Set the parent of the album + * + * @param parent set the parent album of album to @p parent + */ + void setParent(Album* parent); + + /** + * @internal use only + * + * Insert an Album as a child for this album + * + * @param child the Album to add as child + */ + void insertChild(Album* child); + + /** + * @internal use only + * + * Remove a Album from the children list for this album + * + * @param child the Album to remove + */ + void removeChild(Album* child); + +private: + + /** + * Disable copy and default constructor + */ + Album(); + Album(const Album&); + Album& operator==(const Album&); + +private: + + Type m_type; + int m_id; + bool m_root; + TQString m_title; + + Album* m_parent; + Album* m_firstChild; + Album* m_lastChild; + Album* m_next; + Album* m_prev; + bool m_clearing; + + TQMap m_extraMap; + + friend class AlbumManager; +}; + +/** + * \class PAlbum + * + * A Physical Album representation + */ + +class PAlbum : public Album +{ +public: + + PAlbum(const TQString& title, int id, bool root=false); + ~PAlbum(); + + void setCaption(const TQString& caption); + void setCollection(const TQString& collection); + void setDate(const TQDate& date); + + TQString caption() const; + TQString collection() const; + TQDate date() const; + TQString url() const; + TQString prettyURL() const; + TQString folderPath() const; + KURL kurl() const; + TQString icon() const; + KURL iconKURL() const; + +private: + + TQString m_collection; + TQString m_caption; + TQDate m_date; + TQString m_icon; + + friend class AlbumManager; +}; + +/** + * \class TAlbum + * + * A Tag Album representation + */ + +class TAlbum : public Album +{ +public: + + TAlbum(const TQString& title, int id, bool root=false); + ~TAlbum(); + + /** + * @return The tag path, e.g. "/People/Friend/John" if leadingSlash is true, + "People/Friend/John" if leadingSlash if false. + * The root TAlbum returns "/" resp. "". + */ + TQString tagPath(bool leadingSlash = true) const; + KURL kurl() const; + TQString prettyURL() const; + TQString icon() const; + +private: + + TQString m_icon; + int m_pid; + + friend class AlbumManager; +}; + +/** + * \class DAlbum + * + * A Date Album representation + */ + +class DAlbum : public Album +{ +public: + + enum Range + { + Month = 0, + Year + }; + + DAlbum(const TQDate& date, bool root=false, Range range=Month); + ~DAlbum(); + + TQDate date() const; + Range range() const; + KURL kurl() const; + +private: + + static int m_uniqueID; + + Range m_range; + + TQDate m_date; + + friend class AlbumManager; +}; + +/** + * \class SAlbum + * + * A Search Album representation + */ + +class SAlbum : public Album +{ +public: + + SAlbum(int id, const KURL& url, bool simple, bool root=false); + ~SAlbum(); + + KURL kurl() const; + bool isSimple() const; + +private: + + KURL m_kurl; + bool m_simple; + + friend class AlbumManager; +}; + +/** + * \class AlbumIterator + * + * Iterate over all children of this Album. + * \note It will not include the specified album + * + * Example usage: + * \code + * AlbumIterator it(album); + * while ( it.current() ) + * { + * DDebug() << "Album: " << it.current()->title() << endl; + * ++it; + * } + * \endcode + * + * \warning Do not delete albums using this iterator. + */ + +class AlbumIterator +{ +public: + + AlbumIterator(Album *album); + ~AlbumIterator(); + + AlbumIterator& operator++(); + Album* operator*(); + Album* current() const; + +private: + + AlbumIterator() {} + AlbumIterator(const AlbumIterator&) {} + AlbumIterator& operator=(const AlbumIterator&){ return *this; } + + Album* m_current; + Album* m_root; +}; + +} // namespace Digikam + +#endif /* ALBUM_H */ diff --git a/src/digikam/albumdb.cpp b/src/digikam/albumdb.cpp new file mode 100644 index 00000000..9b08d5c4 --- /dev/null +++ b/src/digikam/albumdb.cpp @@ -0,0 +1,1599 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-18 + * Description : database album interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file albumdb.cpp */ + +// C Ansi includes. + +extern "C" +{ +#include "sqlite3.h" +#include +} + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "albummanager.h" +#include "album.h" +#include "albumdb.h" +#include "albumsettings.h" + +namespace Digikam +{ + +typedef struct sqlite3_stmt sqlite3_stmt; +typedef struct sqlite3 sqleet3; // hehe. + +class AlbumDBPriv +{ + +public: + + AlbumDBPriv() + { + valid = false; + dataBase = 0; + } + + bool valid; + + sqleet3 *dataBase; + + IntList recentlyAssignedTags; +}; + +AlbumDB::AlbumDB() +{ + d = new AlbumDBPriv; +} + +AlbumDB::~AlbumDB() +{ + if (d->dataBase) + { + sqlite3_close(d->dataBase); + } + + delete d; +} + +bool AlbumDB::isValid() const +{ + return d->valid; +} + +void AlbumDB::setDBPath(const TQString& path) +{ + if (d->dataBase) + { + sqlite3_close(d->dataBase); + d->dataBase = 0; + } + + d->valid = false; + + sqlite3_open(TQFile::encodeName(path), &d->dataBase); + if (d->dataBase == 0) + { + DWarning() << "Cannot open database: " + << sqlite3_errmsg(d->dataBase) + << endl; + } + else + { + initDB(); + } +} + +void AlbumDB::initDB() +{ + d->valid = false; + + // Check if we have the required tables + + TQStringList values; + + if (!execSql( TQString("SELECT name FROM sqlite_master" + " WHERE type='table'" + " ORDER BY name;"), + &values )) + { + return; + } + + if (!values.contains("Albums")) + { + if (!execSql( TQString("CREATE TABLE Albums\n" + " (id INTEGER PRIMARY KEY,\n" + " url TEXT NOT NULL UNIQUE,\n" + " date DATE NOT NULL,\n" + " caption TEXT,\n" + " collection TEXT,\n" + " icon INTEGER);") )) + { + return; + } + + if (!execSql( TQString("CREATE TABLE Tags\n" + " (id INTEGER PRIMARY KEY,\n" + " pid INTEGER,\n" + " name TEXT NOT NULL,\n" + " icon INTEGER,\n" + " iconkde TEXT,\n" + " UNIQUE (name, pid));") )) + { + return; + } + + if (!execSql( TQString("CREATE TABLE TagsTree\n" + " (id INTEGER NOT NULL,\n" + " pid INTEGER NOT NULL,\n" + " UNIQUE (id, pid));") )) + { + return; + } + + if (!execSql( TQString("CREATE TABLE Images\n" + " (id INTEGER PRIMARY KEY,\n" + " name TEXT NOT NULL,\n" + " dirid INTEGER NOT NULL,\n" + " caption TEXT,\n" + " datetime DATETIME,\n" + " UNIQUE (name, dirid));") )) + { + return; + } + + + if (!execSql( TQString("CREATE TABLE ImageTags\n" + " (imageid INTEGER NOT NULL,\n" + " tagid INTEGER NOT NULL,\n" + " UNIQUE (imageid, tagid));") )) + { + return; + } + + if (!execSql( TQString("CREATE TABLE ImageProperties\n" + " (imageid INTEGER NOT NULL,\n" + " property TEXT NOT NULL,\n" + " value TEXT NOT NULL,\n" + " UNIQUE (imageid, property));") )) + { + return; + } + + if ( !execSql( TQString( "CREATE TABLE Searches \n" + " (id INTEGER PRIMARY KEY, \n" + " name TEXT NOT NULL UNIQUE, \n" + " url TEXT NOT NULL);" ) ) ) + { + return; + } + + if (!execSql( TQString("CREATE TABLE Settings \n" + "(keyword TEXT NOT NULL UNIQUE,\n" + " value TEXT);") )) + return; + else + setSetting("DBVersion","1"); + + // TODO: see which more indices are needed + // create indices + execSql("CREATE INDEX dir_index ON Images (dirid);"); + execSql("CREATE INDEX tag_index ON ImageTags (tagid);"); + + // create triggers + + // trigger: delete from Images/ImageTags/ImageProperties + // if Album has been deleted + execSql("CREATE TRIGGER delete_album DELETE ON Albums\n" + "BEGIN\n" + " DELETE FROM ImageTags\n" + " WHERE imageid IN (SELECT id FROM Images WHERE dirid=OLD.id);\n" + " DELETE From ImageProperties\n" + " WHERE imageid IN (SELECT id FROM Images WHERE dirid=OLD.id);\n" + " DELETE FROM Images\n" + " WHERE dirid = OLD.id;\n" + "END;"); + + // trigger: delete from ImageTags/ImageProperties + // if Image has been deleted + execSql("CREATE TRIGGER delete_image DELETE ON Images\n" + "BEGIN\n" + " DELETE FROM ImageTags\n" + " WHERE imageid=OLD.id;\n" + " DELETE From ImageProperties\n " + " WHERE imageid=OLD.id;\n" + " UPDATE Albums SET icon=null \n " + " WHERE icon=OLD.id;\n" + " UPDATE Tags SET icon=null \n " + " WHERE icon=OLD.id;\n" + "END;"); + + // trigger: delete from ImageTags if Tag has been deleted + execSql("CREATE TRIGGER delete_tag DELETE ON Tags\n" + "BEGIN\n" + " DELETE FROM ImageTags WHERE tagid=OLD.id;\n" + "END;"); + + // trigger: insert into TagsTree if Tag has been added + execSql("CREATE TRIGGER insert_tagstree AFTER INSERT ON Tags\n" + "BEGIN\n" + " INSERT INTO TagsTree\n" + " SELECT NEW.id, NEW.pid\n" + " UNION\n" + " SELECT NEW.id, pid FROM TagsTree WHERE id=NEW.pid;\n" + "END;"); + + // trigger: delete from TagsTree if Tag has been deleted + execSql("CREATE TRIGGER delete_tagstree DELETE ON Tags\n" + "BEGIN\n" + " DELETE FROM Tags\n" + " WHERE id IN (SELECT id FROM TagsTree WHERE pid=OLD.id);\n" + " DELETE FROM TagsTree\n" + " WHERE id IN (SELECT id FROM TagsTree WHERE pid=OLD.id);\n" + " DELETE FROM TagsTree\n" + " WHERE id=OLD.id;\n" + "END;"); + + // trigger: delete from TagsTree if Tag has been deleted + execSql("CREATE TRIGGER move_tagstree UPDATE OF pid ON Tags\n" + "BEGIN\n" + " DELETE FROM TagsTree\n" + " WHERE\n" + " ((id = OLD.id)\n" + " OR\n" + " id IN (SELECT id FROM TagsTree WHERE pid=OLD.id))\n" + " AND\n" + " pid IN (SELECT pid FROM TagsTree WHERE id=OLD.id);\n" + " INSERT INTO TagsTree\n" + " SELECT NEW.id, NEW.pid\n" + " UNION\n" + " SELECT NEW.id, pid FROM TagsTree WHERE id=NEW.pid\n" + " UNION\n" + " SELECT id, NEW.pid FROM TagsTree WHERE pid=NEW.id\n" + " UNION\n" + " SELECT A.id, B.pid FROM TagsTree A, TagsTree B\n" + " WHERE\n" + " A.pid = NEW.id AND B.id = NEW.pid;\n" + "END;"); + } + + d->valid = true; +} + +AlbumInfo::List AlbumDB::scanAlbums() +{ + AlbumInfo::List aList; + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + TQStringList values; + execSql( "SELECT A.id, A.url, A.date, A.caption, A.collection, B.url, I.name \n " + "FROM Albums AS A \n " + " LEFT OUTER JOIN Images AS I ON A.icon=I.id \n" + " LEFT OUTER JOIN Albums AS B ON B.id=I.dirid;", &values); + + TQString iconAlbumUrl, iconName; + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + AlbumInfo info; + + info.id = (*it).toInt(); + ++it; + info.url = *it; + ++it; + info.date = TQDate::fromString(*it, TQt::ISODate); + ++it; + info.caption = *it; + ++it; + info.collection = *it; + ++it; + iconAlbumUrl = *it; + ++it; + iconName = *it; + ++it; + + if (!iconName.isEmpty()) + { + info.icon = basePath + iconAlbumUrl + '/' + iconName; + } + + aList.append(info); + } + + return aList; +} + +TagInfo::List AlbumDB::scanTags() +{ + TagInfo::List tList; + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + TQStringList values; + execSql( "SELECT T.id, T.pid, T.name, A.url, I.name, T.iconkde \n " + "FROM Tags AS T LEFT OUTER JOIN Images AS I ON I.id=T.icon \n " + " LEFT OUTER JOIN Albums AS A ON A.id=I.dirid; ", &values ); + + TQString iconName, iconKDE, albumURL; + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TagInfo info; + + info.id = (*it).toInt(); + ++it; + info.pid = (*it).toInt(); + ++it; + info.name = *it; + ++it; + albumURL = *it; + ++it; + iconName = *it; + ++it; + iconKDE = *it; + ++it; + + if ( albumURL.isEmpty() ) + { + info.icon = iconKDE; + } + else + { + info.icon = basePath + albumURL + '/' + iconName; + } + + tList.append(info); + } + + return tList; +} + +SearchInfo::List AlbumDB::scanSearches() +{ + SearchInfo::List searchList; + + TQStringList values; + execSql( "SELECT id, name, url FROM Searches;", &values); + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + SearchInfo info; + + info.id = (*it).toInt(); + ++it; + info.name = (*it); + ++it; + info.url = (*it); + ++it; + + searchList.append(info); + } + + return searchList; +} + +void AlbumDB::beginTransaction() +{ + execSql( "BEGIN TRANSACTION;" ); +} + +void AlbumDB::commitTransaction() +{ + execSql( "COMMIT TRANSACTION;" ); +} + +int AlbumDB::addAlbum(const TQString& url, const TQString& caption, + const TQDate& date, const TQString& collection) +{ + if (!d->dataBase) + return -1; + + execSql( TQString("REPLACE INTO Albums (url, date, caption, collection) " + "VALUES('%1', '%2', '%3', '%4');") + .arg(escapeString(url), + date.toString(TQt::ISODate), + escapeString(caption), + escapeString(collection))); + + int id = sqlite3_last_insert_rowid(d->dataBase); + return id; +} + +void AlbumDB::setAlbumCaption(int albumID, const TQString& caption) +{ + execSql( TQString("UPDATE Albums SET caption='%1' WHERE id=%2;") + .arg(escapeString(caption), + TQString::number(albumID) )); +} + +void AlbumDB::setAlbumCollection(int albumID, const TQString& collection) +{ + execSql( TQString("UPDATE Albums SET collection='%1' WHERE id=%2;") + .arg(escapeString(collection), + TQString::number(albumID)) ); +} + +void AlbumDB::setAlbumDate(int albumID, const TQDate& date) +{ + execSql( TQString("UPDATE Albums SET date='%1' WHERE id=%2;") + .arg(date.toString(TQt::ISODate)) + .arg(albumID) ); +} + +void AlbumDB::setAlbumIcon(int albumID, TQ_LLONG iconID) +{ + execSql( TQString("UPDATE Albums SET icon=%1 WHERE id=%2;") + .arg(iconID) + .arg(albumID) ); +} + + +TQString AlbumDB::getAlbumIcon(int albumID) +{ + TQStringList values; + execSql( TQString("SELECT B.url, I.name \n " + "FROM Albums AS A \n " + " LEFT OUTER JOIN Images AS I ON I.id=A.icon \n " + " LEFT OUTER JOIN Albums AS B ON B.id=I.dirid \n " + "WHERE A.id=%1;") + .arg(albumID), &values ); + if (values.isEmpty()) + return TQString(); + + TQStringList::iterator it = values.begin(); + TQString url = *it; + ++it; + TQString icon = *it; + if (icon.isEmpty()) + return TQString(); + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + basePath += url; + basePath += '/' + icon; + + return basePath; +} + +void AlbumDB::deleteAlbum(int albumID) +{ + execSql( TQString("DELETE FROM Albums WHERE id=%1") + .arg(albumID) ); +} + +int AlbumDB::addTag(int parentTagID, const TQString& name, const TQString& iconKDE, + TQ_LLONG iconID) +{ + if (!d->dataBase) + return -1; + + if (!execSql( TQString("INSERT INTO Tags (pid, name) " + "VALUES( %1, '%2')") + .arg(parentTagID) + .arg(escapeString(name)))) + { + return -1; + } + + int id = sqlite3_last_insert_rowid(d->dataBase); + + if (!iconKDE.isEmpty()) + { + execSql( TQString("UPDATE Tags SET iconkde='%1' WHERE id=%2;") + .arg(escapeString(iconKDE), + TQString::number(id))); + } + else + { + execSql( TQString("UPDATE Tags SET icon=%1 WHERE id=%2;") + .arg(iconID) + .arg(id)); + } + + return id; +} + +void AlbumDB::deleteTag(int tagID) +{ + execSql( TQString("DELETE FROM Tags WHERE id=%1") + .arg(tagID) ); +} + +void AlbumDB::setTagIcon(int tagID, const TQString& iconKDE, TQ_LLONG iconID) +{ + if (!iconKDE.isEmpty()) + { + execSql( TQString("UPDATE Tags SET iconkde='%1', icon=0 WHERE id=%2;") + .arg(escapeString(iconKDE), + TQString::number(tagID))); + } + else + { + execSql( TQString("UPDATE Tags SET icon=%1 WHERE id=%2;") + .arg(iconID) + .arg(tagID)); + } +} + +TQString AlbumDB::getTagIcon(int tagID) +{ + TQStringList values; + execSql( TQString("SELECT A.url, I.name, T.iconkde \n " + "FROM Tags AS T \n " + " LEFT OUTER JOIN Images AS I ON I.id=T.icon \n " + " LEFT OUTER JOIN Albums AS A ON A.id=I.dirid \n " + "WHERE T.id=%1;") + .arg(tagID), &values ); + + if (values.isEmpty()) + return TQString(); + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + TQString iconName, iconKDE, albumURL, icon; + + TQStringList::iterator it = values.begin(); + + albumURL = *it; + ++it; + iconName = *it; + ++it; + iconKDE = *it; + ++it; + + if ( albumURL.isEmpty() ) + { + icon = iconKDE; + } + else + { + icon = basePath + albumURL + '/' + iconName; + } + + return icon; +} + +void AlbumDB::setTagParentID(int tagID, int newParentTagID) +{ + execSql( TQString("UPDATE Tags SET pid=%1 WHERE id=%2;") + .arg(newParentTagID) + .arg(tagID) ); +} + +int AlbumDB::addSearch(const TQString& name, const KURL& url) +{ + if (!d->dataBase) + return -1; + + TQString str("INSERT INTO Searches (name, url) \n" + "VALUES('$$@@$$', '$$##$$');"); + str.replace("$$@@$$", escapeString(name)); + str.replace("$$##$$", escapeString(url.url())); + + if (!execSql(str)) + { + return -1; + } + + return sqlite3_last_insert_rowid(d->dataBase); +} + +void AlbumDB::updateSearch(int searchID, const TQString& name, + const KURL& url) +{ + TQString str = TQString("UPDATE Searches SET name='$$@@$$', url='$$##$$' \n" + "WHERE id=%1") + .arg(searchID); + str.replace("$$@@$$", escapeString(name)); + str.replace("$$##$$", escapeString(url.url())); + + execSql(str); +} + +void AlbumDB::deleteSearch(int searchID) +{ + execSql( TQString("DELETE FROM Searches WHERE id=%1") + .arg(searchID) ); +} + +void AlbumDB::setSetting(const TQString& keyword, + const TQString& value ) +{ + execSql( TQString("REPLACE into Settings VALUES ('%1','%2');") + .arg(escapeString(keyword), + escapeString(value) )); +} + +TQString AlbumDB::getSetting(const TQString& keyword) +{ + TQStringList values; + execSql( TQString("SELECT value FROM Settings " + "WHERE keyword='%1';") + .arg(escapeString(keyword)), &values ); + + if (values.isEmpty()) + return TQString(); + else + return values[0]; +} + +bool AlbumDB::execSql(const TQString& sql, TQStringList* const values, + const bool debug) +{ + if ( debug ) + DDebug() << "SQL-query: " << sql << endl; + + if ( !d->dataBase ) + { + DWarning() << k_funcinfo << "SQLite pointer == NULL" + << endl; + return false; + } + + const char* tail; + sqlite3_stmt* stmt; + int error; + + //compile SQL program to virtual machine + error = sqlite3_prepare(d->dataBase, sql.utf8(), -1, &stmt, &tail); + if ( error != SQLITE_OK ) + { + DWarning() << k_funcinfo + << "sqlite_compile error: " + << sqlite3_errmsg(d->dataBase) + << " on query: " + << sql << endl; + return false; + } + + int cols = sqlite3_column_count(stmt); + + while ( true ) + { + error = sqlite3_step( stmt ); + + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + + //iterate over columns + for ( int i = 0; values && i < cols; i++ ) + { + *values << TQString::fromUtf8( (const char*)sqlite3_column_text( stmt, i ) ); + } + } + + sqlite3_finalize( stmt ); + + if ( error != SQLITE_DONE ) + { + DWarning() << "sqlite_step error: " + << sqlite3_errmsg( d->dataBase ) + << " on query: " + << sql << endl; + return false; + } + + return true; +} + +TQString AlbumDB::escapeString(TQString str) const +{ + str.replace( "'", "''" ); + return str; +} + +TQString AlbumDB::getItemCaption(TQ_LLONG imageID) +{ + TQStringList values; + + execSql( TQString("SELECT caption FROM Images " + "WHERE id=%1;") + .arg(imageID), + &values ); + + if (!values.isEmpty()) + return values[0]; + else + return TQString(); +} + +TQString AlbumDB::getItemCaption(int albumID, const TQString& name) +{ + TQStringList values; + + execSql( TQString("SELECT caption FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(name)), + &values ); + + if (!values.isEmpty()) + return values[0]; + else + return TQString(); +} + +TQDateTime AlbumDB::getItemDate(TQ_LLONG imageID) +{ + TQStringList values; + + execSql( TQString("SELECT datetime FROM Images " + "WHERE id=%1;") + .arg(imageID), + &values ); + + if (values.isEmpty()) + return TQDateTime(); + else + return TQDateTime::fromString(values[0], TQt::ISODate); +} + +TQDateTime AlbumDB::getItemDate(int albumID, const TQString& name) +{ + TQStringList values; + + execSql( TQString("SELECT datetime FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(name)), + &values ); + + if (values.isEmpty()) + return TQDateTime(); + else + return TQDateTime::fromString(values[0], TQt::ISODate); +} + +TQ_LLONG AlbumDB::getImageId(int albumID, const TQString& name) +{ + TQStringList values; + + execSql( TQString("SELECT id FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(name)), + &values ); + + if (values.isEmpty()) + return -1; + else + return (values[0]).toLongLong(); +} + +TQStringList AlbumDB::getItemTagNames(TQ_LLONG imageID) +{ + TQStringList values; + + execSql( TQString("SELECT name FROM Tags \n " + "WHERE id IN (SELECT tagid FROM ImageTags \n " + " WHERE imageid=%1) \n " + "ORDER BY name;") + .arg(imageID), + &values ); + + return values; +} + +IntList AlbumDB::getItemTagIDs(TQ_LLONG imageID) +{ + TQStringList values; + + execSql( TQString("SELECT tagid FROM ImageTags \n " + "WHERE imageID=%1;") + .arg(imageID), + &values ); + + IntList ids; + + if (values.isEmpty()) + return ids; + + for (TQStringList::iterator it=values.begin(); it != values.end(); ++it) + { + ids << (*it).toInt(); + } + return ids; +} + +bool AlbumDB::hasTags(const LLongList& imageIDList) +{ + IntList ids; + + if (imageIDList.isEmpty()) + return false; + + TQStringList values; + + TQString sql = TQString("SELECT count(tagid) FROM ImageTags " + "WHERE imageid=%1 ") + .arg(imageIDList.first()); + + LLongList::const_iterator iter = imageIDList.begin(); + ++iter; + + while (iter != imageIDList.end()) + { + sql += TQString(" OR imageid=%2 ") + .arg(*iter); + ++iter; + } + + sql += TQString(";"); + execSql( sql, &values ); + + if (values[0] == "0") + return false; + else + return true; +} + +IntList AlbumDB::getItemCommonTagIDs(const LLongList& imageIDList) +{ + IntList ids; + + if (imageIDList.isEmpty()) + return ids; + + TQStringList values; + + TQString sql = TQString("SELECT DISTINCT tagid FROM ImageTags " + "WHERE imageid=%1 ") + .arg(imageIDList.first()); + + LLongList::const_iterator iter = imageIDList.begin(); + ++iter; + + while (iter != imageIDList.end()) + { + sql += TQString(" OR imageid=%2 ") + .arg(*iter); + ++iter; + } + + sql += TQString(";"); + execSql( sql, &values ); + + if (values.isEmpty()) + return ids; + + for (TQStringList::iterator it=values.begin(); it != values.end(); ++it) + { + ids << (*it).toInt(); + } + return ids; +} + +void AlbumDB::setItemCaption(TQ_LLONG imageID,const TQString& caption) +{ + TQStringList values; + + execSql( TQString("UPDATE Images SET caption='%1' " + "WHERE id=%2;") + .arg(escapeString(caption), + TQString::number(imageID) )); +} + +void AlbumDB::setItemCaption(int albumID, const TQString& name, const TQString& caption) +{ + TQStringList values; + + execSql( TQString("UPDATE Images SET caption='%1' " + "WHERE dirid=%2 AND name='%3';") + .arg(escapeString(caption), + TQString::number(albumID), + escapeString(name)) ); +} + +void AlbumDB::addItemTag(TQ_LLONG imageID, int tagID) +{ + execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) " + "VALUES(%1, %2);") + .arg(imageID) + .arg(tagID) ); + + if (!d->recentlyAssignedTags.contains(tagID)) + { + d->recentlyAssignedTags.push_front(tagID); + if (d->recentlyAssignedTags.size() > 10) + d->recentlyAssignedTags.pop_back(); + } +} + +void AlbumDB::addItemTag(int albumID, const TQString& name, int tagID) +{ + execSql( TQString("REPLACE INTO ImageTags (imageid, tagid) \n " + "(SELECT id, %1 FROM Images \n " + " WHERE dirid=%2 AND name='%3');") + .arg(tagID) + .arg(albumID) + .arg(escapeString(name)) ); +} + +IntList AlbumDB::getRecentlyAssignedTags() const +{ + return d->recentlyAssignedTags; +} + +void AlbumDB::removeItemTag(TQ_LLONG imageID, int tagID) +{ + execSql( TQString("DELETE FROM ImageTags " + "WHERE imageID=%1 AND tagid=%2;") + .arg(imageID) + .arg(tagID) ); +} + +void AlbumDB::removeItemAllTags(TQ_LLONG imageID) +{ + execSql( TQString("DELETE FROM ImageTags " + "WHERE imageID=%1;") + .arg(imageID) ); +} + +TQStringList AlbumDB::getItemNamesInAlbum(int albumID, bool recurssive) +{ + TQStringList values; + + if (recurssive) + { + KURL url(getAlbumURL(albumID)); + execSql( TQString("SELECT Images.name " + "FROM Images " + "WHERE Images.dirid " + "IN (SELECT DISTINCT id " + "FROM Albums " + "WHERE url='%1' OR url LIKE '\%%2\%')") + .arg(escapeString(url.path())).arg(escapeString(url.path(1))), &values); + } + else + { + execSql( TQString("SELECT Images.name " + "FROM Images " + "WHERE Images.dirid=%1") + .arg(albumID), &values ); + } + return values; +} + +TQStringList AlbumDB::getAllItemURLsWithoutDate() +{ + TQStringList values; + execSql( TQString("SELECT Albums.url||'/'||Images.name " + "FROM Images, Albums " + "WHERE Images.dirid=Albums.Id " + "AND (Images.datetime is null or " + " Images.datetime == '');"), + &values ); + + TQString libraryPath = AlbumManager::instance()->getLibraryPath() + '/'; + for (TQStringList::iterator it = values.begin(); it != values.end(); + ++it) + { + *it = libraryPath + *it; + } + + return values; +} + +int AlbumDB::getOrCreateAlbumId(const TQString& folder) +{ + TQStringList values; + execSql( TQString("SELECT id FROM Albums WHERE url ='%1';") + .arg( escapeString(folder) ), &values); + + int albumID; + if (values.isEmpty()) + { + execSql( TQString ("INSERT INTO Albums (url, date) " + "VALUES ('%1','%2')") + .arg(escapeString(folder), + TQDateTime::currentDateTime().toString(TQt::ISODate)) ); + albumID = sqlite3_last_insert_rowid(d->dataBase); + } else + albumID = values[0].toInt(); + + return albumID; +} + +TQ_LLONG AlbumDB::addItem(int albumID, + const TQString& name, + const TQDateTime& datetime, + const TQString& comment, + int rating, + const TQStringList &keywordsList) +{ + execSql ( TQString ("REPLACE INTO Images " + "( caption , datetime, name, dirid ) " + " VALUES ('%1','%2','%3',%4) " ) + .arg(escapeString(comment), + datetime.toString(TQt::ISODate), + escapeString(name), + TQString::number(albumID)) ); + + TQ_LLONG item = sqlite3_last_insert_rowid(d->dataBase); + + // Set Rating value to item in database. + + if ( item != -1 && rating != -1 ) + setItemRating(item, rating); + + // Set existing tags in database or create new tags if not exist. + + if ( item != -1 && !keywordsList.isEmpty() ) + { + IntList tagIDs = getTagsFromTagPaths(keywordsList); + for (IntList::iterator it = tagIDs.begin(); it != tagIDs.end(); ++it) + { + addItemTag(item, *it); + } + } + + return item; +} + +IntList AlbumDB::getTagsFromTagPaths(const TQStringList &keywordsList, bool create) +{ + if (keywordsList.isEmpty()) + return IntList(); + + IntList tagIDs; + + TQStringList keywordsList2Create; + + // Create a list of the tags currently in database + + TagInfo::List currentTagsList; + + TQStringList values; + execSql( "SELECT id, pid, name FROM Tags;", &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TagInfo info; + + info.id = (*it).toInt(); + ++it; + info.pid = (*it).toInt(); + ++it; + info.name = *it; + ++it; + currentTagsList.append(info); + } + + // For every tag in keywordsList, scan taglist to check if tag already exists. + + for (TQStringList::const_iterator kwd = keywordsList.begin(); + kwd != keywordsList.end(); ++kwd ) + { + // split full tag "url" into list of single tag names + TQStringList tagHierarchy = TQStringList::split('/', *kwd); + if (tagHierarchy.isEmpty()) + continue; + + // last entry in list is the actual tag name + bool foundTag = false; + TQString tagName = tagHierarchy.back(); + tagHierarchy.pop_back(); + + for (TagInfo::List::iterator tag = currentTagsList.begin(); + tag != currentTagsList.end(); ++tag ) + { + // There might be multiple tags with the same name, but in different + // hierarchies. We must check them all until we find the correct hierarchy + if ((*tag).name == tagName) + { + int parentID = (*tag).pid; + + // Check hierarchy, from bottom to top + bool foundParentTag = true; + TQStringList::iterator parentTagName = tagHierarchy.end(); + + while (foundParentTag && parentTagName != tagHierarchy.begin()) + { + --parentTagName; + + foundParentTag = false; + + for (TagInfo::List::iterator parentTag = currentTagsList.begin(); + parentTag != currentTagsList.end(); ++parentTag ) + { + // check if name is the same, and if ID is identical + // to the parent ID we got from the child tag + if ( (*parentTag).id == parentID && + (*parentTag).name == (*parentTagName) ) + { + parentID = (*parentTag).pid; + foundParentTag = true; + break; + } + } + + // If we traversed the list without a match, + // foundParentTag will be false, the while loop breaks. + } + + // If we managed to traverse the full hierarchy, + // we have our tag. + if (foundParentTag) + { + // add to result list + tagIDs.append((*tag).id); + foundTag = true; + break; + } + } + } + + if (!foundTag) + keywordsList2Create.append(*kwd); + } + + // If tags do not exist in database, create them. + + if (create && !keywordsList2Create.isEmpty()) + { + for (TQStringList::iterator kwd = keywordsList2Create.begin(); + kwd != keywordsList2Create.end(); ++kwd ) + { + // split full tag "url" into list of single tag names + TQStringList tagHierarchy = TQStringList::split('/', *kwd); + + if (tagHierarchy.isEmpty()) + continue; + + int parentTagID = 0; + int tagID = 0; + bool parentTagExisted = true; + + // Traverse hierarchy from top to bottom + for (TQStringList::iterator tagName = tagHierarchy.begin(); + tagName != tagHierarchy.end(); ++tagName) + { + tagID = 0; + + // if the parent tag did not exist, we need not check if the child exists + if (parentTagExisted) + { + for (TagInfo::List::iterator tag = currentTagsList.begin(); + tag != currentTagsList.end(); ++tag ) + { + // find the tag with tag name according to tagHierarchy, + // and parent ID identical to the ID of the tag we found in + // the previous run. + if ((*tag).name == (*tagName) && (*tag).pid == parentTagID) + { + tagID = (*tag).id; + break; + } + } + } + + if (tagID != 0) + { + // tag already found in DB + parentTagID = tagID; + continue; + } + + // Tag does not yet exist in DB, add it + tagID = addTag(parentTagID, (*tagName), TQString(), 0); + + if (tagID == -1) + { + // Something is wrong in database. Abort. + break; + } + + // append to our list of existing tags (for following keywords) + TagInfo info; + info.id = tagID; + info.pid = parentTagID; + info.name = (*tagName); + currentTagsList.append(info); + + parentTagID = tagID; + parentTagExisted = false; + } + + // add to result list + tagIDs.append(tagID); + } + } + + return tagIDs; +} + +int AlbumDB::getItemAlbum(TQ_LLONG imageID) +{ + TQStringList values; + + execSql ( TQString ("SELECT dirid FROM Images " + "WHERE id=%1;") + .arg(imageID), + &values); + + if (!values.isEmpty()) + return values.first().toInt(); + else + return 1; +} + +TQString AlbumDB::getItemName(TQ_LLONG imageID) +{ + TQStringList values; + + execSql ( TQString ("SELECT name FROM Images " + "WHERE id=%1;") + .arg(imageID), + &values); + + if (!values.isEmpty()) + return values.first(); + else + return TQString(); +} + +bool AlbumDB::setItemDate(TQ_LLONG imageID, + const TQDateTime& datetime) +{ + execSql ( TQString ("UPDATE Images SET datetime='%1'" + "WHERE id=%2;") + .arg(datetime.toString(TQt::ISODate), + TQString::number(imageID)) ); + + return true; +} + +bool AlbumDB::setItemDate(int albumID, const TQString& name, + const TQDateTime& datetime) +{ + execSql ( TQString ("UPDATE Images SET datetime='%1'" + "WHERE dirid=%2 AND name='%3';") + .arg(datetime.toString(TQt::ISODate), + TQString::number(albumID), + escapeString(name)) ); + + return true; +} + +void AlbumDB::setItemRating(TQ_LLONG imageID, int rating) +{ + execSql ( TQString ("REPLACE INTO ImageProperties " + "(imageid, property, value) " + "VALUES(%1, '%2', '%3');") + .arg(imageID) + .arg("Rating") + .arg(rating) ); +} + +int AlbumDB::getItemRating(TQ_LLONG imageID) +{ + TQStringList values; + + execSql( TQString("SELECT value FROM ImageProperties " + "WHERE imageid=%1 and property='%2';") + .arg(imageID) + .arg("Rating"), + &values); + + if (!values.isEmpty()) + return values[0].toInt(); + else + return 0; +} + +TQStringList AlbumDB::getItemURLsInAlbum(int albumID) +{ + TQStringList values; + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + AlbumSettings::ImageSortOrder order = AlbumSettings::instance()->getImageSortOrder(); + + TQString sqlString; + switch(order) + { + case AlbumSettings::ByIName: + sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " + "WHERE Albums.id=%1 AND Albums.id=Images.dirid " + "ORDER BY Images.name COLLATE NOCASE;") + .arg(albumID); + break; + case AlbumSettings::ByIPath: + // Dont collate on the path - this is to maintain the same behaviour + // that happens when sort order is "By Path" + sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " + "WHERE Albums.id=%1 AND Albums.id=Images.dirid " + "ORDER BY Albums.url,Images.name;") + .arg(albumID); + break; + case AlbumSettings::ByIDate: + sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " + "WHERE Albums.id=%1 AND Albums.id=Images.dirid " + "ORDER BY Images.datetime;") + .arg(albumID); + break; + case AlbumSettings::ByIRating: + sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums, ImageProperties " + "WHERE Albums.id=%1 AND Albums.id=Images.dirid " + "AND Images.id = ImageProperties.imageid " + "AND ImageProperties.property='Rating' " + "ORDER BY ImageProperties.value DESC;") + .arg(albumID); + break; + default: + sqlString = TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " + "WHERE Albums.id=%1 AND Albums.id=Images.dirid;") + .arg(albumID); + break; + } + execSql( sqlString, &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) + { + *it = basePath + *it; + } + + return values; +} + +LLongList AlbumDB::getItemIDsInAlbum(int albumID) +{ + LLongList itemIDs; + + TQStringList itemNames = getItemNamesInAlbum(albumID); + + for (TQStringList::iterator it = itemNames.begin(); it != itemNames.end(); ++it) + { + TQ_LLONG id = getImageId(albumID, *it); + itemIDs.append(id); + } + + return itemIDs; +} + +TQStringList AlbumDB::getItemURLsInTag(int tagID, bool recursive) +{ + TQStringList values; + + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + TQString imagesIdClause; + if (recursive) + imagesIdClause = TQString("SELECT imageid FROM ImageTags " + " WHERE tagid=%1 " + " OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)") + .arg(tagID).arg(tagID); + else + imagesIdClause = TQString("SELECT imageid FROM ImageTags WHERE tagid=%1").arg(tagID); + + execSql( TQString("SELECT Albums.url||'/'||Images.name FROM Images, Albums " + "WHERE Images.id IN (%1) " + "AND Albums.id=Images.dirid;") + .arg(imagesIdClause), &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) + { + *it = basePath + *it; + } + + return values; +} + +LLongList AlbumDB::getItemIDsInTag(int tagID, bool recursive) +{ + LLongList itemIDs; + TQStringList values; + + if (recursive) + execSql( TQString("SELECT imageid FROM ImageTags " + " WHERE tagid=%1 " + " OR tagid IN (SELECT id FROM TagsTree WHERE pid=%2)") + .arg(tagID).arg(tagID), &values ); + else + execSql( TQString("SELECT imageid FROM ImageTags WHERE tagid=%1;") + .arg(tagID), &values ); + + for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) + { + itemIDs << (*it).toLong(); + } + + return itemIDs; +} + +TQString AlbumDB::getAlbumURL(int albumID) +{ + TQStringList values; + execSql( TQString("SELECT url from Albums where id=%1") + .arg( albumID), &values); + return values[0]; +} + +TQDate AlbumDB::getAlbumLowestDate(int albumID) +{ + TQStringList values; + execSql( TQString("SELECT MIN(datetime) FROM Images " + "WHERE dirid=%1 GROUP BY dirid") + .arg( albumID ), &values); + TQDate itemDate = TQDate::fromString( values[0], TQt::ISODate ); + return itemDate; +} + +TQDate AlbumDB::getAlbumHighestDate(int albumID) +{ + TQStringList values; + execSql( TQString("SELECT MAX(datetime) FROM Images " + "WHERE dirid=%1 GROUP BY dirid") + .arg( albumID ), &values); + TQDate itemDate = TQDate::fromString( values[0], TQt::ISODate ); + return itemDate; +} + +TQDate AlbumDB::getAlbumAverageDate(int albumID) +{ + TQStringList values; + execSql( TQString("SELECT datetime FROM Images WHERE dirid=%1") + .arg( albumID ), &values); + + int differenceInSecs = 0; + int amountOfImages = 0; + TQDateTime baseDateTime; + + for (TQStringList::iterator it = values.begin(); it != values.end(); ++it) + { + TQDateTime itemDateTime = TQDateTime::fromString( *it, TQt::ISODate ); + if (itemDateTime.isValid()) + { + ++amountOfImages; + if ( baseDateTime.isNull() ) + baseDateTime=itemDateTime; + else + differenceInSecs += itemDateTime.secsTo( baseDateTime ); + } + } + + if ( amountOfImages > 0 ) + { + TQDateTime averageDateTime; + averageDateTime.setTime_t( baseDateTime.toTime_t() - + (int)( differenceInSecs/amountOfImages ) ); + return ( averageDateTime.date() ); + } + else + return TQDate(); +} + +void AlbumDB::deleteItem(int albumID, const TQString& file) +{ + execSql( TQString("DELETE FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(albumID) + .arg(escapeString(file)) ); +} + +void AlbumDB::setAlbumURL(int albumID, const TQString& url) +{ + TQString u = escapeString(url); + + // first delete any stale albums left behind + execSql( TQString("DELETE FROM Albums WHERE url = '%1'") + .arg(u) ); + + // now update the album url + execSql( TQString("UPDATE Albums SET url = '%1' WHERE id = %2;") + .arg(u, TQString::number(albumID) )); +} + +void AlbumDB::setTagName(int tagID, const TQString& name) +{ + execSql( TQString("UPDATE Tags SET name='%1' WHERE id=%2;") + .arg(escapeString(name), TQString::number(tagID) )); +} + +void AlbumDB::moveItem(int srcAlbumID, const TQString& srcName, + int dstAlbumID, const TQString& dstName) +{ + + // first delete any stale database entries if any + deleteItem(dstAlbumID, dstName); + + execSql( TQString("UPDATE Images SET dirid=%1, name='%2' " + "WHERE dirid=%3 AND name='%4';") + .arg(TQString::number(dstAlbumID), escapeString(dstName), + TQString::number(srcAlbumID), escapeString(srcName)) ); +} + +int AlbumDB::copyItem(int srcAlbumID, const TQString& srcName, + int dstAlbumID, const TQString& dstName) +{ + // check for src == dest + if (srcAlbumID == dstAlbumID && srcName == dstName) + return -1; + + // find id of src image + TQStringList values; + execSql( TQString("SELECT id FROM Images " + "WHERE dirid=%1 AND name='%2';") + .arg(TQString::number(srcAlbumID), escapeString(srcName)), + &values); + + if (values.isEmpty()) + return -1; + + int srcId = values[0].toInt(); + + // first delete any stale database entries if any + deleteItem(dstAlbumID, dstName); + + // copy entry in Images table + execSql( TQString("INSERT INTO Images (dirid, name, caption, datetime) " + "SELECT %1, '%2', caption, datetime FROM Images " + "WHERE id=%3;") + .arg(TQString::number(dstAlbumID), escapeString(dstName), + TQString::number(srcId)) ); + + int dstId = sqlite3_last_insert_rowid(d->dataBase); + + // copy tags + execSql( TQString("INSERT INTO ImageTags (imageid, tagid) " + "SELECT %1, tagid FROM ImageTags " + "WHERE imageid=%2;") + .arg(TQString::number(dstId), TQString::number(srcId)) ); + + // copy properties (rating) + execSql( TQString("INSERT INTO ImageProperties (imageid, property, value) " + "SELECT %1, property, value FROM ImageProperties " + "WHERE imageid=%2;") + .arg(TQString::number(dstId), TQString::number(srcId)) ); + + return dstId; +} + +TQ_LLONG AlbumDB::lastInsertedRow() +{ + return sqlite3_last_insert_rowid(d->dataBase); +} + +} // namespace Digikam + diff --git a/src/digikam/albumdb.h b/src/digikam/albumdb.h new file mode 100644 index 00000000..9115684a --- /dev/null +++ b/src/digikam/albumdb.h @@ -0,0 +1,615 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-18 + * Description : database album interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file albumdb.h */ + +#ifndef ALBUMDB_H +#define ALBUMDB_H + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "albuminfo.h" +#include "digikam_export.h" + +namespace Digikam +{ + +typedef TQValueList IntList; +typedef TQValueList LLongList; + +class AlbumDBPriv; + +/** + * This class is responsible for the communication + * with the sqlite database. + */ +class DIGIKAM_EXPORT AlbumDB +{ +public: + + /** + * Constructor + */ + AlbumDB(); + + /** + * Destructor + */ + ~AlbumDB(); + + /** + * Check if the database interface is initialized properly. + * @return true if it's ready to use, else false. + */ + bool isValid() const; + + /** + * Makes a connection to the database and makes sure all tables + * are available. + * @param path The database to open + */ + void setDBPath(const TQString& path); + + /** + * Returns all albums and their attributes in the database + * @return a list of albums and their attributes + */ + AlbumInfo::List scanAlbums(); + + /** + * Returns all tags and their attributes in the database + * @return a list of tags and their attributes + */ + TagInfo::List scanTags(); + + /** + * Returns all searches from the database + * @return a list of searches from the database + */ + SearchInfo::List scanSearches(); + + /** + * Add a new album to the database with the given attributes + * @param url url of the album + * @param caption the album caption + * @param date the date for the album + * @param collection the album collection + * @return the id of the album added or -1 if it failed + */ + int addAlbum(const TQString& url, const TQString& caption, + const TQDate& date, const TQString& collection); + + /** + * Set a new url for the album. This will not affect the url + * of the subalbums. + * @param albumID the id of the album + * @param url the new url for the album + */ + void setAlbumURL(int albumID, const TQString& url); + + /** + * Set a caption for the album. + * @param albumID the id of the album + * @param caption the new caption for the album + */ + void setAlbumCaption(int albumID, const TQString& caption); + + /** + * Set a collection for the album. + * @param albumID the id of the album + * @param collection the new collection for the album + */ + void setAlbumCollection(int albumID, const TQString& collection); + + /** + * Set a date for the album. + * @param albumID the id of the album + * @param date the date for the album + */ + void setAlbumDate(int albumID, const TQDate& date); + + /** + * Set the icon for the album. + * @param albumID the id of the album + * @param iconID the id of the icon file + */ + void setAlbumIcon(int albumID, TQ_LLONG iconID); + + /** + * Get the fullpath for the album icon file + * @param albumID the id of the album + */ + TQString getAlbumIcon(int albumID); + + /** + * Deletes an album from the database. This will not delete the + * subalbums of the album. + * @param albumID the id of the album + */ + void deleteAlbum(int albumID); + + /** + * Adds a new tag to the database with given name, icon and parent id. + * @param parentTagID the id of the tag which will become the new tags parent + * @param name the name of the tag + * @param iconKDE the name of the icon file (this is filename which kde + * iconloader can load up) + * @param iconID the id of the icon file + * Note: if the iconKDE parameter is empty, then the iconID parameter is used + * @return the id of the tag added or -1 if it failed + */ + int addTag(int parentTagID, const TQString& name, + const TQString& iconKDE, TQ_LLONG iconID); + + /** + * Set a new name for the tag. + * @param tagID the id of the tag + * @param name the new name for the tag + */ + void setTagName(int tagID, const TQString& name); + + /** + * Set the icon for the tag. + * @param tagID the id of the tag + * @param iconKDE the filename for the kde icon file + * @param iconID the id of the icon file + * Note: Only one of the iconKDE or iconID parameters is used. + * if the iconKDE parameter is empty, then the iconID parameter is used + */ + void setTagIcon(int tagID, const TQString& iconKDE, TQ_LLONG iconID); + + /** + * Get the icon for the tag. + * @param tagID the id of the tag + * @return the path for the icon file. this could be either a simple filename + * which can be loaded by kiconloader or an absolute filepath to the file + */ + TQString getTagIcon(int tagID); + + /** + * Set the parent tagid for the tag. This is equivalent to reparenting + * the tag + * @param tagID the id of the tag + * @param newParentTagID the new parentid for the tag + */ + void setTagParentID(int tagID, int newParentTagID); + + /** + * Deletes a tag from the database. This will not delete the + * subtags of the tag. + * @param tagID the id of the tag + */ + void deleteTag(int tagID); + + /** + * Add a new search to the database with the given attributes + * @param name name of the search + * @param url url of the search + * @return the id of the album added or -1 if it failed + */ + int addSearch(const TQString& name, const KURL& url); + + /** + * Updates Search with new attributes + * @param searchID the id of the search + * @param name name of the search + * @param url url of the search + */ + void updateSearch(int searchID, const TQString& name, const KURL& url); + + /** + * Delete a search from the database. + * @param searchID the id of the search + */ + void deleteSearch(int searchID); + + void beginTransaction(); + void commitTransaction(); + + /** + * This adds a keyword-value combination to the database Settings table + * if the keyword already exists, the value will be replaced with the new + * value. + * @param keyword The keyword + * @param value The value + */ + void setSetting(const TQString& keyword, const TQString& value); + + /** + * This function returns the value which is stored in the database + * (table Settings). + * @param keyword The keyword for which the value has to be returned. + * @return The values which belongs to the keyword. + */ + TQString getSetting(const TQString& keyword); + + /** + * This is simple function to put a new Item in the database, + * without checking if it already exists, but since albumID+name + * has to be unique, it will simply replace the datetime and comment + * for an already existing item. + * @param albumID The albumID where the file is located. + * @param name The filename + * @param datetime The datetime to be stored. Should try to let that be + * the exif-datetime, but if not available the modification date. + * @param comment The user comment as found in the exif-headers of the + * file. + * @param rating The user rating as found in the iptc-headers of the + * file. + * @param keywords The user keywords as found in the iptc-headers of the + * file. + * @return the id of item added or -1 if it fails + */ + TQ_LLONG addItem(int albumID, const TQString& name, + const TQDateTime& datetime, + const TQString& comment, + int rating, + const TQStringList& keywordsList); + + /** + * Find the album of an item + * @param imageID The ID of the item + * @return The ID of the PAlbum of the item, or -1 if not found + */ + int getItemAlbum(TQ_LLONG imageID); + + /** + * Retrieve the name of the item + * @param imageID The ID of the item + * @return The name of the item, or a null string if not found + */ + TQString getItemName(TQ_LLONG imageID); + + /** + * Update the date of a item to supplied date + * @param imageID The ID of the item + * @param datetime The datetime to be stored. Should try to let that be + * the exif-datetime, but if not available the modification date. + * @return It will always return true. Maybe that will change. + */ + bool setItemDate(TQ_LLONG imageID, const TQDateTime& datetime); + + /** + * Update the date of a item to supplied date + * @param albumID The albumID where the file is located. + * @param name The filename + * @param datetime The datetime to be stored. Should try to let that be + * the exif-datetime, but if not available the modification date. + * @return It will always return true. Maybe that will change. + */ + bool setItemDate(int albumID, const TQString& name, + const TQDateTime& datetime); + + /** + * Set the caption for the item + * @param imageID the id of the item + * @param caption the caption for the item + */ + void setItemCaption(TQ_LLONG imageID, const TQString& caption); + + /** + * Set the caption for the item + * @param albumID the albumID of the item + * @param name the name of the item + * @param caption the caption for the item + */ + void setItemCaption(int albumID, const TQString& name, const TQString& caption); + + /** + * Add a tag for the item + * @param imageID the ID of the item + * @param tagID the tagID for the tag + */ + void addItemTag(TQ_LLONG imageID, int tagID); + + /** + * Add a tag for the item + * @param albumID the albumID of the item + * @param name the name of the item + * @param tagID the tagID for the tag + */ + void addItemTag(int albumID, const TQString& name, int tagID); + + /** + * Add tags for the item, create tags with the given paths if they do not yet exist + * @param tagPaths a list of tag paths + * @param create create new tags if necessary + * @returns a list of albumIDs of the tags in tagPaths + */ + IntList getTagsFromTagPaths(const TQStringList &tagPaths, bool create = true); + + /** + * Get a list of recently assigned tags (only last 6 tags are listed) + * @return the list of recently assigned tags + */ + IntList getRecentlyAssignedTags() const; + + /** + * Get the caption for the item + * @param imageID the id of the item + * @return the caption for the item + */ + TQString getItemCaption(TQ_LLONG imageID); + + /** + * Get the caption for the item + * @param albumID the albumID of the item + * @param name the name of the item + * @return the caption for the item + */ + TQString getItemCaption(int albumID, const TQString& name); + + /** + * Get the datetime for the item + * @param imageID the ID of the item + * @return the datetime for the item + */ + TQDateTime getItemDate(TQ_LLONG imageID); + + /** + * Get the datetime for the item + * @param albumID the albumID of the item + * @param name the name of the item + * @return the datetime for the item + */ + TQDateTime getItemDate(int albumID, const TQString& name); + + /** + * Get the imageId of the item + * @param albumId the albumID of the item + * @param name the name of the item + * @return the ImageId for the item + */ + TQ_LLONG getImageId(int albumID, const TQString& name); + + /** + * Get a list of names of all the tags for the item + * @param imageID the ID of the item + * @return the list of names of all tags for the item + */ + TQStringList getItemTagNames(TQ_LLONG imageID); + + /** + * Get a list of IDs of all the tags for the item + * @param imageID the ID of the item + * @return the list of IDs of all tags for the item + */ + IntList getItemTagIDs(TQ_LLONG imageID); + + /** + * Given a set of items (identified by their IDs), + * this will see if any of the items has a tag. + * @param imageIDList a list of IDs of the items + * @return true if at least one of the items has a tag + */ + bool hasTags(const LLongList& imageIDList); + + /** + * Given a set of items (identified by their IDs), + * get a list of ID of all common tags + * @param imageIDList a list of IDs of the items + * @return the list of common IDs of the given items + */ + IntList getItemCommonTagIDs(const LLongList& imageIDList); + + /** + * Remove a specific tag for the item + * @param imageID the ID of the item + * @param tagID the tagID for the tag + */ + void removeItemTag(TQ_LLONG imageID, int tagID); + + /** + * Update the rating of a item to supplied value + * @param imageID The ID of the item + * @param rating The rating value to be stored. + */ + void setItemRating(TQ_LLONG imageID, int rating); + + /** + * Get the item rating + * @param imageID the ID of the item + * @return the rating for the item + */ + int getItemRating(TQ_LLONG imageID); + + /** + * Remove all tags for the item + * @param imageID the ID of the item + */ + void removeItemAllTags(TQ_LLONG imageID); + + /** + * Deletes an item from the database. + * @param albumID The id of the album. + * @param file The filename of the file to delete. + */ + void deleteItem(int albumID, const TQString& file); + + /** + * This can be used to find out the albumID for a given + * folder. If it does not exist, it will be created and the + * new albumID will be returned. + * @param folder The folder for which you want the albumID + * @return It returns the albumID for that folder. + */ + int getOrCreateAlbumId(const TQString& folder); + + /** + * Returns all items for a given albumid. This is used to + * verify if all items on disk are consistent with the database + * in the scanlib class. + * @param albumID The albumID for which you want all items. + * @param recursive perform a recursive folder hierarchy parsing + * @return It returns a TQStringList with the filenames. + */ + TQStringList getItemNamesInAlbum(int albumID, bool recurssive=false); + + /** + * Given a albumID, get a list of the url of all items in the album + * @param albumID the id of the album + * @return a list of urls for the items in the album. The urls are the + * absolute path of the items + */ + TQStringList getItemURLsInAlbum(int albumID); + + /** + * Given a albumID, get a list of Ids of all items in the album + * @param albumID the id of the album + * @return a list of Ids for the items in the album. + */ + LLongList getItemIDsInAlbum(int albumID); + + /** + * Returns all items in the database without a date. This is used + * in the scanlib class which tries to find out the date of the + * items, so the database holds the date for each item. This was + * not the case untill the 0.8.0 release. + * @return The path (starting from albumPath and including the + * the filename of all items. + */ + TQStringList getAllItemURLsWithoutDate(); + + /** + * Given a tagid, get a list of the url of all items in the tag + * @param tagID the id of the tag + * @param recursive perform a recursive folder hierarchy parsing + * @return a list of urls for the items in the tag. The urls are the + * absolute path of the items + */ + TQStringList getItemURLsInTag(int tagID, bool recursive = false); + + /** + * Given a tagID, get a list of Ids of all items in the tag + * @param tagID the id of the tag + * @param recursive perform a recursive folder hierarchy parsing + * @return a list of Ids for the items in the tag. + */ + LLongList getItemIDsInTag(int tagID, bool recursive = false); + + /** + * Given an albumid, this returns the url for that albumdb + * @param albumID the id of the albumdb + * @return the url of the albumdb + */ + TQString getAlbumURL(int albumID); + + /** + * Returns the lowest/oldest date of all images for that album. + * @param albumID the id of the album to calculate + * @return the date. + */ + TQDate getAlbumLowestDate(int albumID); + + /** + * Returns the highest/newest date of all images for that album. + * @param albumID the id of the album to calculate + * @return the date. + */ + TQDate getAlbumHighestDate(int albumID); + + /** + * Returns the average date of all images for that album. + * @param albumID the id of the album to calculate + * @return the date. + */ + TQDate getAlbumAverageDate(int albumID); + + /** + * Move the attributes of an item to a different item. Useful when + * say a file is renamed + * @param srcAlbumID the id of the source album + * @param dstAlbumID the id of the destination album + * @param srcName the name of the source file + * @param dstName the name of the destination file + */ + void moveItem(int srcAlbumID, const TQString& srcName, + int dstAlbumID, const TQString& dstName); + + /** + * Copy the attributes of an item to a different item. Useful when + * say a file is copied. + * The operation fails (returns -1) of src and dest are identical. + * @param srcAlbumID the id of the source album + * @param dstAlbumID the id of the destination album + * @param srcName the name of the source file + * @param dstName the name of the destination file + * @return the id of item added or -1 if it fails + */ + int copyItem(int srcAlbumID, const TQString& srcName, + int dstAlbumID, const TQString& dstName); + + /** + * This will execute a given SQL statement to the database. + * @param sql The SQL statement + * @param values This will be filled with the result of the SQL statement + * @param debug If true, it will output the SQL statement + * @return It will return if the execution of the statement was succesfull + */ + bool execSql(const TQString& sql, TQStringList* const values = 0, + const bool debug = false); + + /** + * To be used only if you are sure of what you are doing + * @return the last inserted row in one the db table. + */ + TQ_LLONG lastInsertedRow(); + +private: + + /** + * Checks the available tables and creates them if they are not + * available. + */ + void initDB(); + + /** + * Escapes text fields. This is needed for all queries to the database + * which happens with an argument which is a text field. It makes sure + * a ' is replaced with '', as this is needed for sqlite. + * @param str String to escape + * @return The escaped string + */ + TQString escapeString(TQString str) const; + +private: + + AlbumDBPriv* d; +}; + +} // namespace Digikam + +#endif /* ALBUMDB_H */ diff --git a/src/digikam/albumdb_sqlite2.cpp b/src/digikam/albumdb_sqlite2.cpp new file mode 100644 index 00000000..35bb9935 --- /dev/null +++ b/src/digikam/albumdb_sqlite2.cpp @@ -0,0 +1,144 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-18 + * Description : SQlite version 2 database interface. + * + * Copyright (C) 2004 by Renchi Raju + + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +#include +} + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "albumdb_sqlite2.h" + +namespace Digikam +{ + +typedef struct sqlite_vm sqlite_vm; + +AlbumDB_Sqlite2::AlbumDB_Sqlite2() +{ + m_db = 0; + m_valid = false; +} + +AlbumDB_Sqlite2::~AlbumDB_Sqlite2() +{ + if (m_db) { + sqlite_close(m_db); + } +} + +void AlbumDB_Sqlite2::setDBPath(const TQString& path) +{ + if (m_db) { + sqlite_close(m_db); + m_db = 0; + m_valid = false; + } + + char *errMsg = 0; + m_db = sqlite_open(TQFile::encodeName(path), 0, &errMsg); + if (m_db == 0) + { + DWarning() << k_funcinfo << "Cannot open database: " + << errMsg << endl; + free(errMsg); + return; + } + + TQStringList values; + execSql("SELECT * FROM sqlite_master", &values); + m_valid = values.contains("Albums"); +} + +bool AlbumDB_Sqlite2::execSql(const TQString& sql, TQStringList* const values, + const bool debug) +{ + if ( debug ) + DDebug() << "SQL-query: " << sql << endl; + + if ( !m_db ) { + DWarning() << k_funcinfo << "SQLite pointer == NULL" + << endl; + return false; + } + + const char* tail; + sqlite_vm* vm; + char* errorStr; + int error; + + //compile SQL program to virtual machine + error = sqlite_compile( m_db, sql.local8Bit(), &tail, &vm, &errorStr ); + + if ( error != SQLITE_OK ) { + DWarning() << k_funcinfo << "sqlite_compile error: " + << errorStr + << " on query: " << sql << endl; + sqlite_freemem( errorStr ); + return false; + } + + int number; + const char** value; + const char** colName; + //execute virtual machine by iterating over rows + while ( true ) { + error = sqlite_step( vm, &number, &value, &colName ); + if ( error == SQLITE_DONE || error == SQLITE_ERROR ) + break; + //iterate over columns + for ( int i = 0; values && i < number; i++ ) { + *values << TQString::fromLocal8Bit( value [i] ); + } + } + + //deallocate vm resources + sqlite_finalize( vm, &errorStr ); + + if ( error != SQLITE_DONE ) { + DWarning() << k_funcinfo << "sqlite_step error: " + << errorStr + << " on query: " << sql << endl; + return false; + } + + return true; +} + +} // namespace Digikam + diff --git a/src/digikam/albumdb_sqlite2.h b/src/digikam/albumdb_sqlite2.h new file mode 100644 index 00000000..339289ba --- /dev/null +++ b/src/digikam/albumdb_sqlite2.h @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-18 + * Description : SQlite version 2 database interface. + * + * Copyright (C) 2004 by Renchi Raju + + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMDB_SQLITE2_H +#define ALBUMDB_SQLITE2_H + +// TQt includes. + +#include +#include +#include +#include + +namespace Digikam +{ + +typedef struct sqlite sqleet2; // hehe. + +typedef TQValueList IntList; +/** + * This class is responsible for the communication + * with the sqlite database. + */ +class AlbumDB_Sqlite2 +{ +public: + + /** + * Constructor + */ + AlbumDB_Sqlite2(); + + /** + * Destructor + */ + ~AlbumDB_Sqlite2(); + + /** + * Makes a connection to the database and makes sure all tables + * are available. + * @param path The database to open + */ + void setDBPath(const TQString& path); + + /** + * This will execute a given SQL statement to the database. + * @param sql The SQL statement + * @param values This will be filled with the result of the SQL statement + * @param debug If true, it will output the SQL statement + * @return It will return if the execution of the statement was succesfull + */ + bool execSql(const TQString& sql, TQStringList* const values = 0, + const bool debug = false); + + bool isValid() const { return m_valid; } + +private: + + sqleet2* m_db; + bool m_valid; +}; + +} // namespace Digikam + +#endif /* ALBUMDB_SQLITE2_H */ diff --git a/src/digikam/albumfiletip.cpp b/src/digikam/albumfiletip.cpp new file mode 100644 index 00000000..1950eab6 --- /dev/null +++ b/src/digikam/albumfiletip.cpp @@ -0,0 +1,588 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-19 + * Description : Album item file tip adapted from tdefiletip + * (konqueror - konq_iconviewwidget.cc) + * + * Copyright (C) 1998-1999 by Torben Weis + * Copyright (C) 2000-2002 by David Faure + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "albumiconview.h" +#include "albumiconitem.h" +#include "albumsettings.h" +#include "album.h" +#include "albumfiletip.h" + +namespace Digikam +{ + +class AlbumFileTipPriv +{ +public: + + AlbumFileTipPriv() : + maxStringLen(30), tipBorder(5) + { + corner = 0; + label = 0; + view = 0; + iconItem = 0; + } + + const uint maxStringLen; + const uint tipBorder; + + int corner; + + TQLabel *label; + + TQPixmap corners[4]; + + AlbumIconView *view; + + AlbumIconItem *iconItem; +}; + +AlbumFileTip::AlbumFileTip(AlbumIconView* view) + : TQFrame(0, 0, WStyle_Customize | WStyle_NoBorder | WStyle_Tool | + WStyle_StaysOnTop | WX11BypassWM) +{ + d = new AlbumFileTipPriv; + d->view = view; + hide(); + + setPalette(TQToolTip::palette()); + setFrameStyle(TQFrame::Plain | TQFrame::Box); + setLineWidth(1); + + TQVBoxLayout *layout = new TQVBoxLayout(this, d->tipBorder+1, 0); + + d->label = new TQLabel(this); + d->label->setMargin(0); + d->label->setAlignment(TQt::AlignHCenter | TQt::AlignVCenter); + + layout->addWidget(d->label); + layout->setResizeMode(TQLayout::Fixed); + + renderArrows(); +} + +AlbumFileTip::~AlbumFileTip() +{ + delete d; +} + +void AlbumFileTip::setIconItem(AlbumIconItem* iconItem) +{ + d->iconItem = iconItem; + + if (!d->iconItem || + !AlbumSettings::instance()->showToolTipsIsValid()) + { + hide(); + } + else + { + updateText(); + reposition(); + if (isHidden()) + show(); + } +} + +void AlbumFileTip::reposition() +{ + if (!d->iconItem) + return; + + TQRect rect = d->iconItem->clickToOpenRect(); + rect.moveTopLeft(d->view->contentsToViewport(rect.topLeft())); + rect.moveTopLeft(d->view->viewport()->mapToGlobal(rect.topLeft())); + + TQPoint pos = rect.center(); + // d->corner: + // 0: upperleft + // 1: upperright + // 2: lowerleft + // 3: lowerright + + d->corner = 0; + // should the tooltip be shown to the left or to the right of the ivi ? + +#if KDE_IS_VERSION(3,2,0) + TQRect desk = TDEGlobalSettings::desktopGeometry(rect.center()); +#else + TQRect desk = TQApplication::desktop()->screenGeometry( + TQApplication::desktop()->screenNumber(rect.center()) ); +#endif + + if (rect.center().x() + width() > desk.right()) + { + // to the left + if (pos.x() - width() < 0) + { + pos.setX(0); + d->corner = 4; + } + else + { + pos.setX( pos.x() - width() ); + d->corner = 1; + } + } + + // should the tooltip be shown above or below the ivi ? + if (rect.bottom() + height() > desk.bottom()) + { + // above + pos.setY( rect.top() - height() - 5); + d->corner += 2; + } + else + { + pos.setY( rect.bottom() + 5 ); + } + + move( pos ); +} + +void AlbumFileTip::renderArrows() +{ + int w = d->tipBorder; + + // -- left top arrow ------------------------------------- + + TQPixmap& pix0 = d->corners[0]; + pix0.resize(w, w); + pix0.fill(colorGroup().background()); + + TQPainter p0(&pix0); + p0.setPen(TQPen(TQt::black, 1)); + + for (int j=0; jcorners[1]; + pix1.resize(w, w); + pix1.fill(colorGroup().background()); + + TQPainter p1(&pix1); + p1.setPen(TQPen(TQt::black, 1)); + + for (int j=0; jcorners[2]; + pix2.resize(w, w); + pix2.fill(colorGroup().background()); + + TQPainter p2(&pix2); + p2.setPen(TQPen(TQt::black, 1)); + + for (int j=0; jcorners[3]; + pix3.resize(w, w); + pix3.fill(colorGroup().background()); + + TQPainter p3(&pix3); + p3.setPen(TQPen(TQt::black, 1)); + + for (int j=0; jtype() ) + { + case TQEvent::Leave: + case TQEvent::MouseButtonPress: + case TQEvent::MouseButtonRelease: + case TQEvent::FocusIn: + case TQEvent::FocusOut: + case TQEvent::Wheel: + hide(); + default: + break; + } + + return TQFrame::event(e); +} + +void AlbumFileTip::resizeEvent(TQResizeEvent* e) +{ + TQFrame::resizeEvent(e); + reposition(); +} + +void AlbumFileTip::drawContents(TQPainter *p) +{ + if (d->corner >= 4) + { + TQFrame::drawContents( p ); + return; + } + + TQPixmap &pix = d->corners[d->corner]; + + switch ( d->corner ) + { + case 0: + p->drawPixmap( 3, 3, pix ); + break; + case 1: + p->drawPixmap( width() - pix.width() - 3, 3, pix ); + break; + case 2: + p->drawPixmap( 3, height() - pix.height() - 3, pix ); + break; + case 3: + p->drawPixmap( width() - pix.width() - 3, height() - pix.height() - 3, pix ); + break; + } + + TQFrame::drawContents(p); +} + +void AlbumFileTip::updateText() +{ + TQString tip, str; + TQString unavailable(i18n("unavailable")); + + TQString headBeg("" + ""); + TQString headEnd(""); + + TQString cellBeg(""); + TQString cellMid("" + ""); + TQString cellEnd(""); + + TQString cellSpecBeg(""); + TQString cellSpecMid("" + ""); + TQString cellSpecEnd(""); + + tip = ""; + + AlbumSettings* settings = AlbumSettings::instance(); + const ImageInfo* info = d->iconItem->imageInfo(); + TQFileInfo fileInfo(info->kurl().path()); + KFileItem fi(KFileItem::Unknown, KFileItem::Unknown, info->kurl()); + DMetadata metaData(info->kurl().path()); + + // -- File properties ---------------------------------------------- + + if (settings->getToolTipsShowFileName() || + settings->getToolTipsShowFileDate() || + settings->getToolTipsShowFileSize() || + settings->getToolTipsShowImageType() || + settings->getToolTipsShowImageDim()) + { + tip += headBeg + i18n("File Properties") + headEnd; + + if (settings->getToolTipsShowFileName()) + { + tip += cellBeg + i18n("Name:") + cellMid; + tip += info->kurl().fileName() + cellEnd; + } + + if (settings->getToolTipsShowFileDate()) + { + TQDateTime modifiedDate = fileInfo.lastModified(); + str = TDEGlobal::locale()->formatDateTime(modifiedDate, true, true); + tip += cellBeg + i18n("Modified:") + cellMid + str + cellEnd; + } + + if (settings->getToolTipsShowFileSize()) + { + tip += cellBeg + i18n("Size:") + cellMid; + str = i18n("%1 (%2)").arg(TDEIO::convertSize(fi.size())) + .arg(TDEGlobal::locale()->formatNumber(fi.size(), 0)); + tip += str + cellEnd; + } + + TQSize dims; +#if KDCRAW_VERSION < 0x000106 + TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + TQString ext = fileInfo.extension(false).upper(); + + if (!ext.isEmpty() && rawFilesExt.upper().contains(ext)) + { + str = i18n("RAW Image"); + dims = metaData.getImageDimensions(); + } + else + { + str = fi.mimeComment(); + + KFileMetaInfo meta = fi.metaInfo(); + if (meta.isValid()) + { + if (meta.containsGroup("Jpeg EXIF Data")) + dims = meta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + else if (meta.containsGroup("General")) + dims = meta.group("General").item("Dimensions").value().toSize(); + else if (meta.containsGroup("Technical")) + dims = meta.group("Technical").item("Dimensions").value().toSize(); + } + } + + if (settings->getToolTipsShowImageType()) + { + tip += cellBeg + i18n("Type:") + cellMid + str + cellEnd; + } + + if (settings->getToolTipsShowImageDim()) + { + TQString mpixels; + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + str = (!dims.isValid()) ? i18n("Unknown") : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + tip += cellBeg + i18n("Dimensions:") + cellMid + str + cellEnd; + } + } + + // -- Photograph Info ---------------------------------------------------- + // NOTE: If something is changed here, please updated imageproperties section too. + + if (settings->getToolTipsShowPhotoMake() || + settings->getToolTipsShowPhotoDate() || + settings->getToolTipsShowPhotoFocal() || + settings->getToolTipsShowPhotoExpo() || + settings->getToolTipsShowPhotoMode() || + settings->getToolTipsShowPhotoFlash() || + settings->getToolTipsShowPhotoWB()) + { + PhotoInfoContainer photoInfo = metaData.getPhotographInformations(); + + if (!photoInfo.isEmpty()) + { + TQString metaStr; + tip += headBeg + i18n("Photograph Properties") + headEnd; + + if (settings->getToolTipsShowPhotoMake()) + { + str = TQString("%1 / %2").arg(photoInfo.make.isEmpty() ? unavailable : photoInfo.make) + .arg(photoInfo.model.isEmpty() ? unavailable : photoInfo.model); + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Make/Model:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoDate()) + { + if (photoInfo.dateTime.isValid()) + { + str = TDEGlobal::locale()->formatDateTime(photoInfo.dateTime, true, true); + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Created:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + else + metaStr += cellBeg + i18n("Created:") + cellMid + TQStyleSheet::escape( unavailable ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoFocal()) + { + str = photoInfo.aperture.isEmpty() ? unavailable : photoInfo.aperture; + + if (photoInfo.focalLength35mm.isEmpty()) + str += TQString(" / %1").arg(photoInfo.focalLength.isEmpty() ? unavailable : photoInfo.focalLength); + else + str += TQString(" / %1").arg(i18n("%1 (35mm: %2)").arg(photoInfo.focalLength).arg(photoInfo.focalLength35mm)); + + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Aperture/Focal:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoExpo()) + { + str = TQString("%1 / %2").arg(photoInfo.exposureTime.isEmpty() ? unavailable : photoInfo.exposureTime) + .arg(photoInfo.sensitivity.isEmpty() ? unavailable : i18n("%1 ISO").arg(photoInfo.sensitivity)); + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Exposure/Sensitivity:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoMode()) + { + + if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + str = unavailable; + else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + str = photoInfo.exposureMode; + else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty()) + str = photoInfo.exposureProgram; + else + str = TQString("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram); + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Mode/Program:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoFlash()) + { + str = photoInfo.flash.isEmpty() ? unavailable : photoInfo.flash; + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("Flash:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + if (settings->getToolTipsShowPhotoWB()) + { + str = photoInfo.whiteBalance.isEmpty() ? unavailable : photoInfo.whiteBalance; + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + metaStr += cellBeg + i18n("White Balance:") + cellMid + TQStyleSheet::escape( str ) + cellEnd; + } + + tip += metaStr; + } + } + + // -- digiKam properties ------------------------------------------ + + if (settings->getToolTipsShowAlbumName() || + settings->getToolTipsShowComments() || + settings->getToolTipsShowTags() || + settings->getToolTipsShowRating()) + { + tip += headBeg + i18n("digiKam Properties") + headEnd; + + if (settings->getToolTipsShowAlbumName()) + { + PAlbum* album = info->album(); + if (album) + tip += cellSpecBeg + i18n("Album:") + cellSpecMid + album->url().remove(0, 1) + cellSpecEnd; + } + + if (settings->getToolTipsShowComments()) + { + str = info->caption(); + if (str.isEmpty()) str = TQString("---"); + tip += cellSpecBeg + i18n("Caption:") + cellSpecMid + breakString(str) + cellSpecEnd; + } + + if (settings->getToolTipsShowTags()) + { + TQStringList tagPaths = info->tagPaths(false); + + str = tagPaths.join(", "); + if (str.isEmpty()) str = TQString("---"); + if (str.length() > d->maxStringLen) str = str.left(d->maxStringLen-3) + "..."; + tip += cellSpecBeg + i18n("Tags:") + cellSpecMid + str + cellSpecEnd; + } + + if (settings->getToolTipsShowRating()) + { + str.fill( 'X', info->rating() ); + if (str.isEmpty()) str = TQString("---"); + tip += cellSpecBeg + i18n("Rating:") + cellSpecMid + str + cellSpecEnd; + } + } + + tip += "
"; + + d->label->setText(tip); +} + +TQString AlbumFileTip::breakString(const TQString& input) +{ + TQString str = input.simplifyWhiteSpace(); + str = TQStyleSheet::escape(str); + uint maxLen = d->maxStringLen; + + if (str.length() <= maxLen) + return str; + + TQString br; + + uint i = 0; + uint count = 0; + + while (i < str.length()) + { + if (count >= maxLen && str[i].isSpace()) + { + count = 0; + br.append("
"); + } + else + { + br.append(str[i]); + } + + i++; + count++; + } + + return br; +} + +} // namespace Digikam diff --git a/src/digikam/albumfiletip.h b/src/digikam/albumfiletip.h new file mode 100644 index 00000000..6ec70aa9 --- /dev/null +++ b/src/digikam/albumfiletip.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-19 + * Description : Album item file tip adapted from tdefiletip + * (konqueror - konq_iconviewwidget.cc) + * + * Copyright (C) 1998-1999 by Torben Weis + * Copyright (C) 2000-2002 by David Faure + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMFILETIP_H +#define ALBUMFILETIP_H + +// TQt includes. + +#include +#include + +namespace Digikam +{ + +class AlbumIconView; +class AlbumIconItem; +class AlbumFileTipPriv; + +class AlbumFileTip : public TQFrame +{ +public: + + AlbumFileTip(AlbumIconView* view); + ~AlbumFileTip(); + + void setIconItem(AlbumIconItem* iconItem); + +protected: + + bool event(TQEvent *e); + void resizeEvent(TQResizeEvent* e); + void drawContents(TQPainter *p); + +private: + + void reposition(); + void renderArrows(); + void updateText(); + TQString breakString(const TQString& str); + +private: + + AlbumFileTipPriv *d; +}; + +} // namespace Digikam + +#endif /* ALBUMFILETIP_H */ diff --git a/src/digikam/albumfolderview.cpp b/src/digikam/albumfolderview.cpp new file mode 100644 index 00000000..6e9048ab --- /dev/null +++ b/src/digikam/albumfolderview.cpp @@ -0,0 +1,1636 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-06 + * Description : Albums folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if KDE_IS_VERSION(3,2,0) +#include +#else +#include +#endif + +// Local includes. + +#include "ddebug.h" +#include "digikamapp.h" +#include "albumlister.h" +#include "album.h" +#include "albumdb.h" +#include "albumpropsedit.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "thumbnailjob.h" +#include "thumbnailsize.h" +#include "folderitem.h" +#include "cameraui.h" +#include "dio.h" +#include "dragobjects.h" +#include "albumthumbnailloader.h" +#include "deletedialog.h" +#include "albumfolderview.h" +#include "albumfolderview.moc" + +// X11 C Ansi includes. + +extern "C" +{ +#include +} + +namespace Digikam +{ + +class AlbumFolderViewItem : public FolderItem +{ + +public: + + AlbumFolderViewItem(TQListView *parent, PAlbum *album); + AlbumFolderViewItem(TQListViewItem *parent, PAlbum *album); + + // special group item (collection/dates) + AlbumFolderViewItem(TQListViewItem* parent, const TQString& name, + int year, int month); + + PAlbum* album() const; + int id() const; + bool isGroupItem() const; + int compare(TQListViewItem *i, int col, bool ascending) const; + void refresh(); + void setOpen(bool o); + void setCount(int count); + int count(); + +private: + + bool m_groupItem; + + int m_count; + int m_year; + int m_month; + + PAlbum *m_album; +}; + +AlbumFolderViewItem::AlbumFolderViewItem(TQListView *parent, PAlbum *album) + : FolderItem(parent, album->title()) +{ + setDragEnabled(true); + m_album = album; + m_groupItem = false; + m_count = 0; +} + +AlbumFolderViewItem::AlbumFolderViewItem(TQListViewItem *parent, PAlbum *album) + : FolderItem(parent, album->title()) +{ + setDragEnabled(true); + m_album = album; + m_groupItem = false; + m_count = 0; +} + +// special group item (collection/dates) +AlbumFolderViewItem::AlbumFolderViewItem(TQListViewItem* parent, const TQString& name, + int year, int month) + : FolderItem(parent, name, true) +{ + m_album = 0; + m_year = year; + m_month = month; + m_groupItem = true; + m_count = 0; +} + +void AlbumFolderViewItem::refresh() +{ + if (!m_album) return; + + if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount() && + dynamic_cast(parent())) + { + if (isOpen()) + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count)); + else + { + int countRecursive = m_count; + AlbumIterator it(m_album); + while ( it.current() ) + { + AlbumFolderViewItem *item = (AlbumFolderViewItem*)it.current()->extraData(listView()); + if (item) + countRecursive += item->count(); + ++it; + } + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive)); + } + } + else + { + setText(0, m_album->title()); + } +} + +void AlbumFolderViewItem::setOpen(bool o) +{ + TQListViewItem::setOpen(o); + refresh(); +} + +PAlbum* AlbumFolderViewItem::album() const +{ + return m_album; +} + +int AlbumFolderViewItem::id() const +{ + if (m_groupItem) + { + if (m_year != 0 && m_month != 0) + { + return (m_year*(-100) + m_month*(-1)); + } + else + { + return ( - (AlbumSettings::instance()->getAlbumCollectionNames() + .findIndex(text(0)) ) ); + } + } + else + { + return m_album ? m_album->id() : 0; + } +} + +bool AlbumFolderViewItem::isGroupItem() const +{ + return m_groupItem; +} + +int AlbumFolderViewItem::compare(TQListViewItem *i, int col, bool ascending) const +{ + if (!m_groupItem || m_year == 0 || m_month == 0) + return TQListViewItem::compare(i, col, ascending); + + AlbumFolderViewItem* thatItem = dynamic_cast(i); + if (!thatItem) + return 0; + + int myWeight = m_year*100 + m_month; + int hisWeight = thatItem->m_year*100 + thatItem->m_month; + + if (myWeight == hisWeight) + return 0; + else if (myWeight > hisWeight) + return 1; + else + return -1; +} + +void AlbumFolderViewItem::setCount(int count) +{ + m_count = count; + refresh(); +} + +int AlbumFolderViewItem::count() +{ + return m_count; +} + +// ----------------------------------------------------------------------------- + +class AlbumFolderViewPriv +{ +public: + + AlbumFolderViewPriv() + { + albumMan = 0; + iconThumbJob = 0; + } + + AlbumManager *albumMan; + ThumbnailJob *iconThumbJob; + TQValueList groupItems; +}; + +AlbumFolderView::AlbumFolderView(TQWidget *parent) + : FolderView(parent, "AlbumFolderView") +{ + d = new AlbumFolderViewPriv(); + d->albumMan = AlbumManager::instance(); + d->iconThumbJob = 0; + + addColumn(i18n("My Albums")); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(false); + setAllColumnsShowFocus(true); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotAlbumsCleared())); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumIconChanged(Album*)), + this, TQ_SLOT(slotAlbumIconChanged(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotAlbumRenamed(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalPAlbumsDirty(const TQMap&)), + this, TQ_SLOT(slotRefresh(const TQMap&))); + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + + connect(loader, TQ_SIGNAL(signalThumbnail(Album *, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&))); + + connect(loader, TQ_SIGNAL(signalFailed(Album *)), + this, TQ_SLOT(slotThumbnailLost(Album *))); + + connect(loader, TQ_SIGNAL(signalReloadThumbnails()), + this, TQ_SLOT(slotReloadThumbnails())); + + connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(this, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); +} + +AlbumFolderView::~AlbumFolderView() +{ + if (d->iconThumbJob) + d->iconThumbJob->kill(); + + saveViewState(); + delete d; +} + +void AlbumFolderView::slotTextFolderFilterChanged(const TQString& filter) +{ + if (filter.isEmpty()) + { + collapseView(); + return; + } + + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList pList = d->albumMan->allPAlbums(); + for (AlbumList::iterator it = pList.begin(); it != pList.end(); ++it) + { + PAlbum* palbum = (PAlbum*)(*it); + + // don't touch the root Album + if (palbum->isRoot()) + continue; + + bool match = palbum->title().lower().contains(search); + bool doesExpand = false; + if (!match) + { + // check if any of the parents match the search + Album* parent = palbum->parent(); + while (parent && !parent->isRoot()) + { + if (parent->title().lower().contains(search)) + { + match = true; + break; + } + + parent = parent->parent(); + } + } + + if (!match) + { + // check if any of the children match the search + AlbumIterator it(palbum); + while (it.current()) + { + if ((*it)->title().lower().contains(search)) + { + match = true; + doesExpand = true; + break; + } + ++it; + } + } + + AlbumFolderViewItem* viewItem = (AlbumFolderViewItem*) palbum->extraData(this); + + if (match) + { + atleastOneMatch = true; + + if (viewItem) + { + viewItem->setVisible(true); + viewItem->setOpen(doesExpand); + } + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + viewItem->setOpen(false); + } + } + } + + emit signalTextFolderFilterMatch(atleastOneMatch); +} + +void AlbumFolderView::slotAlbumAdded(Album *album) +{ + if(!album) + return; + + PAlbum *palbum = dynamic_cast(album); + if(!palbum) + return; + + bool failed; + AlbumFolderViewItem* parent = findParent(palbum, failed); + if (failed) + { + DWarning() << k_funcinfo << " Failed to find Album parent " + << palbum->url() << endl; + return; + } + + AlbumFolderViewItem *item; + if (!parent) + { + // root album + item = new AlbumFolderViewItem(this, palbum); + palbum->setExtraData(this, item); + item->setOpen(true); + } + else + { + item = new AlbumFolderViewItem(parent, palbum); + palbum->setExtraData(this, item); + } + + setAlbumThumbnail(palbum); +} + +void AlbumFolderView::slotAlbumDeleted(Album *album) +{ + if(!album) + return; + + PAlbum* palbum = dynamic_cast(album); + if(!palbum) + return; + + if(!palbum->icon().isEmpty() && d->iconThumbJob) + d->iconThumbJob->removeItem(palbum->icon()); + + AlbumFolderViewItem* item = (AlbumFolderViewItem*) palbum->extraData(this); + if(item) + { + AlbumFolderViewItem *itemParent = dynamic_cast(item->parent()); + + if(itemParent) + itemParent->takeItem(item); + else + takeItem(item); + + delete item; + clearEmptyGroupItems(); + } +} + +void AlbumFolderView::slotAlbumRenamed(Album *album) +{ + PAlbum* palbum = dynamic_cast(album); + if(!palbum) + return; + + AlbumFolderViewItem* item = (AlbumFolderViewItem*) palbum->extraData(this); + if(item) + item->refresh(); + if (item->parent()) + item->parent()->sort(); +} + +void AlbumFolderView::slotAlbumsCleared() +{ + d->groupItems.clear(); + clear(); +} + +void AlbumFolderView::setAlbumThumbnail(PAlbum *album) +{ + if(!album) + return; + + AlbumFolderViewItem* item = (AlbumFolderViewItem*) album->extraData(this); + + if(!item) + return; + + // Either, getThumbnail returns true and loads an icon asynchronously. + // Then, for the time being, we set the standard icon. + // Or, no icon is associated with the album, then we set the standard icon anyway. + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + item->setPixmap(0, loader->getStandardAlbumIcon(album)); + loader->getAlbumThumbnail(album); +} + +void AlbumFolderView::setCurrentAlbum(Album *album) +{ + if(!album) return; + + AlbumFolderViewItem* item = (AlbumFolderViewItem*) album->extraData(this); + if(!item) return; + + setCurrentItem(item); + ensureItemVisible(item); +} + +void AlbumFolderView::slotGotThumbnailFromIcon(Album *album, + const TQPixmap& thumbnail) +{ + if(!album || album->type() != Album::PHYSICAL) + return; + + AlbumFolderViewItem* item = (AlbumFolderViewItem*)album->extraData(this); + + if(!item) + return; + + item->setPixmap(0, thumbnail); +} + +void AlbumFolderView::slotThumbnailLost(Album *) +{ + // we already set the standard icon before loading +} + +void AlbumFolderView::slotReloadThumbnails() +{ + AlbumList tList = d->albumMan->allPAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + PAlbum* album = (PAlbum*)(*it); + setAlbumThumbnail(album); + } +} + +void AlbumFolderView::slotAlbumIconChanged(Album* album) +{ + if(!album || album->type() != Album::PHYSICAL) + return; + + setAlbumThumbnail((PAlbum*)album); +} + +void AlbumFolderView::slotSelectionChanged() +{ + if (!active()) + return; + + TQListViewItem* selItem = 0; + TQListViewItemIterator it(this); + while(it.current()) + { + if(it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if(!selItem) + { + d->albumMan->setCurrentAlbum(0); + return; + } + + AlbumFolderViewItem *albumitem = dynamic_cast(selItem); + if(!albumitem) + { + d->albumMan->setCurrentAlbum(0); + return; + } + + d->albumMan->setCurrentAlbum(albumitem->album()); +} + +void AlbumFolderView::slotContextMenu(TQListViewItem *listitem, const TQPoint &, int) +{ + TDEActionMenu menuImport(i18n("Import")); + TDEActionMenu menuExport(i18n("Export")); + TDEActionMenu menuKIPIBatch(i18n("Batch Process")); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popmenu.insertItem(SmallIcon("albumfolder-new"), i18n("New Album..."), 10); + + AlbumFolderViewItem *item = dynamic_cast(listitem); + if (item && !item->album()) + { + // if collection/date return + return; + } + + // Root folder only shows "New Album..." + if(item && item->parent()) + { + popmenu.insertItem(SmallIcon("pencil"), i18n("Rename..."), 14); + popmenu.insertItem(SmallIcon("albumfolder-properties"), i18n("Album Properties..."), 11); + popmenu.insertItem(SmallIcon("reload_page"), i18n("Reset Album Icon"), 13); + popmenu.insertSeparator(); + + // Add KIPI Albums plugins Actions + TDEAction *action; + const TQPtrList& albumActions = DigikamApp::getinstance()->menuAlbumActions(); + if(!albumActions.isEmpty()) + { + TQPtrListIterator it(albumActions); + while((action = it.current())) + { + action->plug(&popmenu); + ++it; + } + } + + // Add All Import Actions + const TQPtrList importActions = DigikamApp::getinstance()->menuImportActions(); + if(!importActions.isEmpty()) + { + TQPtrListIterator it3(importActions); + while((action = it3.current())) + { + menuImport.insert(action); + ++it3; + } + menuImport.plug(&popmenu); + } + + // Add All Export Actions + const TQPtrList exportActions = DigikamApp::getinstance()->menuExportActions(); + if(!exportActions.isEmpty()) + { + TQPtrListIterator it4(exportActions); + while((action = it4.current())) + { + menuExport.insert(action); + ++it4; + } + menuExport.plug(&popmenu); + } + + // Add KIPI Batch processes plugins Actions + const TQPtrList& batchActions = DigikamApp::getinstance()->menuBatchActions(); + if(!batchActions.isEmpty()) + { + TQPtrListIterator it2(batchActions); + while((action = it2.current())) + { + menuKIPIBatch.insert(action); + ++it2; + } + menuKIPIBatch.plug(&popmenu); + } + + if(!albumActions.isEmpty() || !batchActions.isEmpty() || + !importActions.isEmpty()) + { + popmenu.insertSeparator(-1); + } + + if(AlbumSettings::instance()->getUseTrash()) + { + popmenu.insertItem(SmallIcon("edittrash"), + i18n("Move Album to Trash"), 12); + } + else + { + popmenu.insertItem(SmallIcon("edit-delete"), + i18n("Delete Album"), 12); + } + } + + switch(popmenu.exec((TQCursor::pos()))) + { + case 10: + { + albumNew(item); + break; + } + case 11: + { + albumEdit(item); + break; + } + case 12: + { + albumDelete(item); + break; + } + case 13: + { + TQString err; + d->albumMan->updatePAlbumIcon(item->album(), 0, err); + break; + } + case 14: + { + albumRename(item); + break; + } + default: + break; + } +} + +void AlbumFolderView::albumNew() +{ + AlbumFolderViewItem *item = dynamic_cast(selectedItem()); + if (!item) + { + item = dynamic_cast(firstChild()); + } + + if (!item) + return; + + albumNew(item); +} + +void AlbumFolderView::albumNew(AlbumFolderViewItem *item) +{ + AlbumSettings* settings = AlbumSettings::instance(); + if(!settings) + { + DWarning() << "AlbumFolderView: Couldn't get Album Settings" << endl; + return; + } + + TQDir libraryDir(settings->getAlbumLibraryPath()); + if(!libraryDir.exists()) + { + KMessageBox::error(0, + i18n("The album library has not been set correctly.\n" + "Select \"Configure Digikam\" from the Settings " + "menu and choose a folder to use for the album " + "library.")); + return; + } + + PAlbum *parent; + + if(!item) + parent = d->albumMan->findPAlbum(0); + else + parent = item->album(); + + if (!parent) + return; + + TQString title; + TQString comments; + TQString collection; + TQDate date; + TQStringList albumCollections; + + if(!AlbumPropsEdit::createNew(parent, title, comments, date, collection, + albumCollections)) + return; + + TQStringList oldAlbumCollections(AlbumSettings::instance()->getAlbumCollectionNames()); + if(albumCollections != oldAlbumCollections) + { + AlbumSettings::instance()->setAlbumCollectionNames(albumCollections); + resort(); + } + + TQString errMsg; + PAlbum* album = d->albumMan->createPAlbum(parent, title, comments, + date, collection, errMsg); + if (!album) + { + KMessageBox::error(0, errMsg); + return; + } + + // by this time the signalAlbumAdded has been fired and the appropriate + // AlbumFolderViewItem has been created. Now make this folderviewitem visible + AlbumFolderViewItem* newItem = (AlbumFolderViewItem*)album->extraData(this); + if (newItem) + { + if(item) + item->setOpen(true); + + ensureItemVisible(newItem); + } +} + +void AlbumFolderView::albumDelete() +{ + AlbumFolderViewItem *item = dynamic_cast(selectedItem()); + if(!item) + return; + + albumDelete(item); +} + +void AlbumFolderView::albumDelete(AlbumFolderViewItem *item) +{ + PAlbum *album = item->album(); + + if(!album || album->isRoot()) + return; + + // find subalbums + KURL::List childrenList; + addAlbumChildrenToList(childrenList, album); + + DeleteDialog dialog(this); + + // All subalbums will be presented in the list as well + if (!dialog.confirmDeleteList(childrenList, + childrenList.size() == 1 ? + DeleteDialogMode::Albums : DeleteDialogMode::Subalbums, + DeleteDialogMode::UserPreference)) + return; + + bool useTrash = !dialog.shouldDelete(); + + // Currently trash tdeioslave can handle only full paths. + // pass full folder path to the trashing job + KURL u; + u.setProtocol("file"); + u.setPath(album->folderPath()); + TDEIO::Job* job = DIO::del(u, useTrash); + connect(job, TQ_SIGNAL(result(TDEIO::Job *)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job *))); +} + +void AlbumFolderView::addAlbumChildrenToList(KURL::List &list, Album *album) +{ + // simple recursive helper function + if (album) + { + list.append(album->kurl()); + AlbumIterator it(album); + while(it.current()) + { + addAlbumChildrenToList(list, *it); + ++it; + } + } +} + +void AlbumFolderView::slotDIOResult(TDEIO::Job* job) +{ + if (job->error()) + job->showErrorDialog(this); +} + +void AlbumFolderView::albumRename() +{ + AlbumFolderViewItem *item = dynamic_cast(selectedItem()); + if(!item) + return; + + albumRename(item); +} + +void AlbumFolderView::albumRename(AlbumFolderViewItem* item) +{ + PAlbum *album = item->album(); + + if (!album) + return; + + TQString oldTitle(album->title()); + bool ok; + +#if KDE_IS_VERSION(3,2,0) + TQString title = KInputDialog::getText(i18n("Rename Album (%1)").arg(oldTitle), + i18n("Enter new album name:"), + oldTitle, &ok, this); +#else + TQString title = KLineEditDlg::getText(i18n("Rename Item (%1)").arg(oldTitle), + i18n("Enter new album name:"), + oldTitle, &ok, this); +#endif + + if (!ok) + return; + + if(title != oldTitle) + { + TQString errMsg; + if (!d->albumMan->renamePAlbum(album, title, errMsg)) + KMessageBox::error(0, errMsg); + } + + emit signalAlbumModified(); +} + +void AlbumFolderView::albumEdit() +{ + AlbumFolderViewItem *item = dynamic_cast(selectedItem()); + if(!item) + return; + + albumEdit(item); +} + +void AlbumFolderView::albumEdit(AlbumFolderViewItem* item) +{ + PAlbum *album = item->album(); + + if (!album) + return; + + TQString oldTitle(album->title()); + TQString oldComments(album->caption()); + TQString oldCollection(album->collection()); + TQDate oldDate(album->date()); + TQStringList oldAlbumCollections(AlbumSettings::instance()->getAlbumCollectionNames()); + + TQString title, comments, collection; + TQDate date; + TQStringList albumCollections; + + if(AlbumPropsEdit::editProps(album, title, comments, date, + collection, albumCollections)) + { + if(comments != oldComments) + album->setCaption(comments); + + if(date != oldDate && date.isValid()) + album->setDate(date); + + if(collection != oldCollection) + album->setCollection(collection); + + AlbumSettings::instance()->setAlbumCollectionNames(albumCollections); + resort(); + + // Do this last : so that if anything else changed we can + // successfuly save to the db with the old name + + if(title != oldTitle) + { + TQString errMsg; + if (!d->albumMan->renamePAlbum(album, title, errMsg)) + KMessageBox::error(0, errMsg); + } + + emit signalAlbumModified(); + } +} + +TQDragObject* AlbumFolderView::dragObject() +{ + AlbumFolderViewItem *item = dynamic_cast(dragItem()); + if(!item) + return 0; + + PAlbum *album = item->album(); + if(album->isRoot()) + return 0; + + AlbumDrag *a = new AlbumDrag(album->kurl(), album->id(), this); + if(!a) + return 0; + a->setPixmap(*item->pixmap(0)); + + return a; +} + +bool AlbumFolderView::acceptDrop(const TQDropEvent *e) const +{ + TQPoint vp = contentsToViewport(e->pos()); + AlbumFolderViewItem *itemDrop = dynamic_cast(itemAt(vp)); + AlbumFolderViewItem *itemDrag = dynamic_cast(dragItem()); + + if(AlbumDrag::canDecode(e)) + { + switch(AlbumSettings::instance()->getAlbumSortOrder()) + { + case(AlbumSettings::ByFolder): + { + // Allow dragging at the root, to move the album at the root + if(!itemDrop) + return true; + + // Dragging an item on itself makes no sense + if(itemDrag == itemDrop) + return false; + + // Dragging a parent on its child makes no sense + if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album())) + return false; + + return true; + } + case (AlbumSettings::ByCollection): + { + if (!itemDrop) + return false; + + // Only allow dragging onto Collection + if (itemDrop->isGroupItem()) + return true; + + return false; + } + default: + { + return false; + } + } + } + + if(itemDrop && !itemDrop->parent()) + { + // Do not allow drop images on album root + return false; + } + + if (itemDrop && itemDrop->isGroupItem()) + { + // do not allow drop on a group item + return false; + } + + if(ItemDrag::canDecode(e)) + { + return true; + } + + if (CameraItemListDrag::canDecode(e)) + { + return true; + } + + if(TQUriDrag::canDecode(e)) + { + return true; + } + + return false; +} + +void AlbumFolderView::contentsDropEvent(TQDropEvent *e) +{ + FolderView::contentsDropEvent(e); + + if(!acceptDrop(e)) + return; + + TQPoint vp = contentsToViewport(e->pos()); + AlbumFolderViewItem *itemDrop = dynamic_cast(itemAt(vp)); + + if(AlbumDrag::canDecode(e)) + { + AlbumFolderViewItem *itemDrag = dynamic_cast(dragItem()); + if(!itemDrag) + return; + + if (AlbumSettings::instance()->getAlbumSortOrder() + == AlbumSettings::ByFolder) + { + // TODO: Copy? + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + + if(id == 10) + { + PAlbum *album = itemDrag->album(); + PAlbum *destAlbum; + if(!itemDrop) + { + // move dragItem to the root + destAlbum = d->albumMan->findPAlbum(0); + } + else + { + // move dragItem below dropItem + destAlbum = itemDrop->album(); + } + TDEIO::Job* job = DIO::move(album->kurl(), destAlbum->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + } + } + else if (AlbumSettings::instance()->getAlbumSortOrder() + == AlbumSettings::ByCollection) + { + if (!itemDrop) + return; + + if (itemDrop->isGroupItem()) + { + PAlbum *album = itemDrag->album(); + if (!album) + return; + + album->setCollection(itemDrop->text(0)); + resort(); + } + } + + return; + } + + if (ItemDrag::canDecode(e)) + { + if (!itemDrop) + return; + + PAlbum *destAlbum = itemDrop->album(); + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + return; + + if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) + return; + + // Check if items dropped come from outside current album. + // This can be the case with reccursive content album mode. + KURL::List extUrls; + ImageInfoList extImgInfList; + for (TQValueList::iterator it = imageIDs.begin(); it != imageIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + if (info->albumID() != destAlbum->id()) + { + extUrls.append(info->kurlForKIO()); + extImgInfList.append(info); + } + } + + int id = 0; + char keys_return[32]; + XQueryKeymap(x11Display(), keys_return); + int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3); + int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4); + int key_3 = XKeysymToKeycode(x11Display(), 0xFFE1); + int key_4 = XKeysymToKeycode(x11Display(), 0xFFE2); + + if(extUrls.isEmpty()) + { + // Setting the dropped image as the album thumbnail + // If the ctrl key is pressed, when dropping the image, the + // thumbnail is set without a popup menu + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 12; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popMenu.insertItem(i18n("Set as Album Thumbnail"), 12); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if(id == 12) + { + TQString errMsg; + d->albumMan->updatePAlbumIcon(destAlbum, imageIDs.first(), errMsg); + } + return; + } + + // If shift key is pressed while dragging, move the drag object without + // displaying popup menu -> move + if (((keys_return[key_3 / 8]) && (1 << (key_3 % 8))) || + ((keys_return[key_4 / 8]) && (1 << (key_4 % 8)))) + { + id = 10; + } + // If ctrl key is pressed while dragging, copy the drag object without + // displaying popup menu -> copy + else if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 11; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 ); + popMenu.insertItem( SmallIcon("edit-copy"), i18n("&Copy Here"), 11 ); + if (imageIDs.count() == 1) + popMenu.insertItem(i18n("Set as Album Thumbnail"), 12); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + switch(id) + { + case 10: + { + TDEIO::Job* job = DIO::move(extUrls, destAlbum->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + + // In recurssive album contents mode, we need to force AlbumLister to take a care about + // moved items. This will have no incidence in normal mode. + ImageInfo* item; + for (ImageInfoListIterator it(extImgInfList); (item = it.current()) ; ++it) + { + AlbumLister::instance()->invalidateItem(item); + } + break; + } + case 11: + { + TDEIO::Job* job = DIO::copy(extUrls, destAlbum->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + case 12: + { + TQString errMsg; + d->albumMan->updatePAlbumIcon(destAlbum, imageIDs.first(), errMsg); + } + default: + break; + } + + return; + } + + // -- DnD from Camera GUI ------------------------------------------------ + + if(CameraItemListDrag::canDecode(e)) + { + Album *album = dynamic_cast(itemDrop->album()); + if (!album) return; + + CameraUI *ui = dynamic_cast(e->source()); + if (ui) + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popMenu.insertItem(SmallIcon("go-down"), i18n("Download from camera"), 10); + popMenu.insertItem(SmallIcon("go-down"), i18n("Download && Delete from camera"), 11); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("&Cancel")); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: // Download from camera + { + ui->slotDownload(true, false, album); + break; + } + case 11: // Download and Delete from camera + { + ui->slotDownload(true, true, album); + break; + } + default: + break; + } + } + } + + // -- DnD from an external source ---------------------------------------- + + if(TQUriDrag::canDecode(e)) + { + PAlbum* destAlbum = 0; + + if (itemDrop) + destAlbum = itemDrop->album(); + else + destAlbum = d->albumMan->findPAlbum(0); + + // B.K.O #119205: do not handle root album. + if (destAlbum->isRoot()) + return; + + KURL destURL(destAlbum->kurl()); + + KURL::List srcURLs; + KURLDrag::decode(e, srcURLs); + + char keys_return[32]; + XQueryKeymap(x11Display(), keys_return); + int id = 0; + + int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3); + int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4); + int key_3 = XKeysymToKeycode(x11Display(), 0xFFE1); + int key_4 = XKeysymToKeycode(x11Display(), 0xFFE2); + // If shift key is pressed while dropping, move the drag object without + // displaying popup menu -> move + if(((keys_return[key_3 / 8]) && (1 << (key_3 % 8))) || + ((keys_return[key_4 / 8]) && (1 << (key_4 % 8)))) + { + id = 10; + } + // If ctrl key is pressed while dropping, copy the drag object without + // displaying popup menu -> copy + else if(((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 11; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums")); + popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 ); + popMenu.insertItem( SmallIcon("edit-copy"), i18n("&Copy Here"), 11 ); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + switch(id) + { + case 10: + { + TDEIO::Job* job = DIO::move(srcURLs, destAlbum->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + case 11: + { + TDEIO::Job* job = DIO::copy(srcURLs, destAlbum->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + default: + break; + } + + return; + } +} + +void AlbumFolderView::albumImportFolder() +{ + AlbumSettings* settings = AlbumSettings::instance(); + TQDir libraryDir(settings->getAlbumLibraryPath()); + if(!libraryDir.exists()) + { + KMessageBox::error(0, + i18n("The album library has not been set correctly.\n" + "Select \"Configure digiKam\" from the Settings " + "menu and choose a folder to use for the album " + "library.")); + return; + } + + PAlbum* parent = 0; + if(selectedItem()) + { + AlbumFolderViewItem *folderItem = dynamic_cast(selectedItem()); + Album *album = folderItem->album(); + if (album && album->type() == Album::PHYSICAL) + { + parent = dynamic_cast(album); + } + } + if(!parent) + parent = dynamic_cast(d->albumMan->findPAlbum(0)); + + TQString libraryPath = parent->folderPath(); + + KFileDialog dlg(TQString(), "inode/directory", this, "importFolder", true); + dlg.setCaption(i18n("Select folders to import")); + dlg.setMode(KFile::Directory | KFile::Files); + if(dlg.exec() != TQDialog::Accepted) + return; + + KURL::List urls = dlg.selectedURLs(); + if(urls.empty()) + return; + + TDEIO::Job* job = DIO::copy(urls, parent->kurl()); + connect(job, TQ_SIGNAL(result(TDEIO::Job *)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job *))); +} + +void AlbumFolderView::selectItem(int id) +{ + PAlbum* album = d->albumMan->findPAlbum(id); + if(!album) + return; + + AlbumFolderViewItem *item = (AlbumFolderViewItem*)album->extraData(this); + if(item) + { + setSelected(item, true); + ensureItemVisible(item); + } +} + +AlbumFolderViewItem* AlbumFolderView::findParent(PAlbum* album, bool& failed) +{ + if (album->isRoot()) + { + failed = false; + return 0; + } + + switch(AlbumSettings::instance()->getAlbumSortOrder()) + { + case(AlbumSettings::ByFolder): + { + return findParentByFolder(album, failed); + } + case(AlbumSettings::ByCollection): + { + return findParentByCollection(album, failed); + } + case(AlbumSettings::ByDate): + { + return findParentByDate(album, failed); + } + } + + failed = true; + return 0; +} + +AlbumFolderViewItem* AlbumFolderView::findParentByFolder(PAlbum* album, bool& failed) +{ + AlbumFolderViewItem* parent = + (AlbumFolderViewItem*) album->parent()->extraData(this); + if (!parent) + { + failed = true; + return 0; + } + + failed = false; + return parent; +} + +AlbumFolderViewItem* AlbumFolderView::findParentByCollection(PAlbum* album, bool& failed) +{ + TQStringList collectionList = AlbumSettings::instance()->getAlbumCollectionNames(); + TQString collection = album->collection(); + + if (collection.isEmpty() || !collectionList.contains(collection)) + collection = i18n("Uncategorized Albums"); + + AlbumFolderViewItem* parent = 0; + + for (TQValueList::iterator it=d->groupItems.begin(); + it != d->groupItems.end(); ++it) + { + AlbumFolderViewItem* groupItem = *it; + if (groupItem->text(0) == collection) + { + parent = groupItem; + break; + } + } + + // Need to create a new parent item + if (!parent) + { + parent = new AlbumFolderViewItem(firstChild(), collection, 0, 0); + d->groupItems.append(parent); + } + + failed = false; + return parent; +} + +AlbumFolderViewItem* AlbumFolderView::findParentByDate(PAlbum* album, bool& failed) +{ + TQDate date = album->date(); + + TQString timeString = TQString::number(date.year()) + ", " + + TDEGlobal::locale()->calendar()->monthName(date, false); + + AlbumFolderViewItem* parent = 0; + + for (TQValueList::iterator it=d->groupItems.begin(); + it != d->groupItems.end(); ++it) + { + AlbumFolderViewItem* groupItem = *it; + if (groupItem->text(0) == timeString) + { + parent = groupItem; + break; + } + } + + // Need to create a new parent item + if (!parent) + { + parent = new AlbumFolderViewItem(firstChild(), timeString, + date.year(), date.month()); + d->groupItems.append(parent); + } + + failed = false; + return parent; +} + +void AlbumFolderView::resort() +{ + AlbumFolderViewItem* prevSelectedItem = dynamic_cast(selectedItem()); + if (prevSelectedItem && prevSelectedItem->isGroupItem()) + prevSelectedItem = 0; + + AlbumList pList(d->albumMan->allPAlbums()); + for (AlbumList::iterator it = pList.begin(); it != pList.end(); ++it) + { + PAlbum *album = (PAlbum*)(*it); + if (!album->isRoot() && album->extraData(this)) + { + reparentItem(static_cast(album->extraData(this))); + } + } + + // Clear any groupitems which have been left empty + clearEmptyGroupItems(); + + if (prevSelectedItem) + { + ensureItemVisible(prevSelectedItem); + setSelected(prevSelectedItem, true); + } +} + +void AlbumFolderView::reparentItem(AlbumFolderViewItem* folderItem) +{ + if (!folderItem) + return; + + PAlbum* album = folderItem->album(); + if (!album || album->isRoot()) + return; + + AlbumFolderViewItem* oldParent = dynamic_cast(folderItem->parent()); + + bool failed; + AlbumFolderViewItem* newParent = findParent(album, failed); + if (failed) + return; + + if (oldParent == newParent) + return; + + if (oldParent) + oldParent->removeItem(folderItem); + else + removeItem(folderItem); + + // insert into new parent + if (newParent) + newParent->insertItem(folderItem); + else + insertItem(folderItem); +} + +void AlbumFolderView::clearEmptyGroupItems() +{ + TQValueList deleteItems; + + for (TQValueList::iterator it=d->groupItems.begin(); + it != d->groupItems.end(); ++it) + { + AlbumFolderViewItem* groupItem = *it; + + if (!groupItem->firstChild()) + { + deleteItems.append(groupItem); + } + } + + for (TQValueList::iterator it=deleteItems.begin(); + it != deleteItems.end(); ++it) + { + d->groupItems.remove(*it); + delete *it; + } +} + +void AlbumFolderView::refresh() +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + AlbumFolderViewItem* item = dynamic_cast(*it); + if (item) + item->refresh(); + ++it; + } +} + +void AlbumFolderView::slotRefresh(const TQMap& albumsStatMap) +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + AlbumFolderViewItem* item = dynamic_cast(*it); + if (item) + { + if (item->album()) + { + int id = item->id(); + TQMap::const_iterator it2 = albumsStatMap.find(id); + if ( it2 != albumsStatMap.end() ) + item->setCount(it2.data()); + } + } + ++it; + } + + refresh(); +} + +} // namespace Digikam diff --git a/src/digikam/albumfolderview.h b/src/digikam/albumfolderview.h new file mode 100644 index 00000000..b02e57a9 --- /dev/null +++ b/src/digikam/albumfolderview.h @@ -0,0 +1,132 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-06 + * Description : Albums folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file albumfoldeview.h */ + +#ifndef _ALBUMFOLDERVIEW_H_ +#define _ALBUMFOLDERVIEW_H_ + +// KDE includes. + +#include + +// Local includes. + +#include "folderview.h" + +class TQPixmap; + +class KURL; + +namespace Digikam +{ + +class Album; +class PAlbum; +class AlbumFolderViewItem; +class AlbumFolderViewPriv; + +class AlbumFolderView : public FolderView +{ + TQ_OBJECT + +public: + + AlbumFolderView(TQWidget *parent); + ~AlbumFolderView(); + + void albumImportFolder(); + void resort(); + + void albumNew(); + void albumDelete(); + void albumEdit(); + void albumRename(); + + void setAlbumThumbnail(PAlbum *album); + + void setCurrentAlbum(Album *album); + void refresh(); + +signals: + + void signalAlbumModified(); + void signalTextFolderFilterMatch(bool); + +public slots: + + void slotTextFolderFilterChanged(const TQString&); + +private slots: + + void slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail); + void slotThumbnailLost(Album *album); + void slotReloadThumbnails(); + void slotSelectionChanged(); + + void slotAlbumAdded(Album *); + void slotAlbumDeleted(Album *album); + void slotAlbumsCleared(); + void slotAlbumIconChanged(Album* album); + void slotAlbumRenamed(Album *album); + + void slotContextMenu(TQListViewItem*, const TQPoint&, int); + + void slotDIOResult(TDEIO::Job* job); + + void slotRefresh(const TQMap&); + +protected: + + void contentsDropEvent(TQDropEvent *e); + TQDragObject* dragObject(); + bool acceptDrop(const TQDropEvent *e) const; + + void selectItem(int id); + +private: + + void albumNew(AlbumFolderViewItem *item); + void albumEdit(AlbumFolderViewItem *item); + void albumRename(AlbumFolderViewItem *item); + void albumDelete(AlbumFolderViewItem *item); + + void addAlbumChildrenToList(KURL::List &list, Album *album); + + AlbumFolderViewItem* findParent(PAlbum* album, bool& failed); + AlbumFolderViewItem* findParentByFolder(PAlbum* album, bool& failed); + AlbumFolderViewItem* findParentByCollection(PAlbum* album, bool& failed); + AlbumFolderViewItem* findParentByDate(PAlbum* album, bool& failed); + + void reparentItem(AlbumFolderViewItem* folderItem); + void clearEmptyGroupItems(); + +private: + + AlbumFolderViewPriv *d; +}; + +} // namespace Digikam + +#endif // _ALBUMFOLDEVIEW_H_ diff --git a/src/digikam/albumhistory.cpp b/src/digikam/albumhistory.cpp new file mode 100644 index 00000000..174c879e --- /dev/null +++ b/src/digikam/albumhistory.cpp @@ -0,0 +1,337 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : albums history manager. + * + * Copyright (C) 2004 by Joern Ahrens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumhistory.h" +#include "albumhistory.moc" + +namespace Digikam +{ + +/** + * Stores an album along with the sidebar view, where the album + * is selected + */ +class HistoryItem +{ +public: + + HistoryItem() + { + album = 0; + widget = 0; + }; + + HistoryItem(Album *a, TQWidget *w) + { + album = a; + widget = w; + }; + + bool operator==(const HistoryItem& item) + { + return (album == item.album) && (widget == item.widget); + } + + Album *album; + TQWidget *widget; +}; + +AlbumHistory::AlbumHistory() +{ + m_backwardStack = new AlbumStack; + m_forwardStack = new AlbumStack; + m_moving = false; +} + +AlbumHistory::~AlbumHistory() +{ + clearHistory(); + + delete m_backwardStack; + delete m_forwardStack; +} + +void AlbumHistory::clearHistory() +{ + AlbumStack::iterator iter = m_backwardStack->begin(); + AlbumStack::iterator end = m_backwardStack->end(); + for(; iter != end; ++iter) + delete *iter; + m_backwardStack->clear(); + + iter = m_forwardStack->begin(); + end = m_forwardStack->end(); + for(; iter != end; ++iter) + delete *iter; + m_forwardStack->clear(); + + m_moving = false; +} + +void AlbumHistory::addAlbum(Album *album, TQWidget *widget) +{ + if(!album || !widget || m_moving) + { + m_moving = false; + return; + } + + HistoryItem *item = new HistoryItem(album, widget); + + // Same album as before in the history + if(!m_backwardStack->isEmpty() && + *m_backwardStack->last() == *item) + { + delete item; + return; + } + + m_backwardStack->push_back(item); + + // The forward stack has to be cleared, if backward stack was changed + if(!m_forwardStack->isEmpty()) + { + AlbumStack::iterator iter = m_forwardStack->begin(); + for(; iter != m_forwardStack->end(); ++iter) + { + delete *iter; + } + m_forwardStack->clear(); + } +} + +void AlbumHistory::deleteAlbum(Album *album) +{ + if(!album || m_backwardStack->isEmpty()) + return; + + // Search all HistoryItems, with album and delete them + AlbumStack::iterator iter = m_backwardStack->begin(); + AlbumStack::iterator end = m_backwardStack->end(); + while(iter != end) + { + if((*iter)->album == album) + { + delete *iter; + iter = m_backwardStack->remove(iter); + } + else + { + ++iter; + } + } + iter = m_forwardStack->begin(); + end = m_forwardStack->end(); + while(iter != end) + { + if((*iter)->album == album) + { + delete *iter; + iter = m_forwardStack->remove(iter); + } + else + { + ++iter; + } + } + + if(m_backwardStack->isEmpty() && m_forwardStack->isEmpty()) + return; + + // If backwardStack is empty, then there is no current album. + // So make the first album of the forwardStack the current one. + if(m_backwardStack->isEmpty()) + forward(); + + // After the album is deleted from the history it has to be ensured, + // that neigboring albums are different + AlbumStack::iterator lhs = m_backwardStack->begin(); + AlbumStack::iterator rhs = lhs; + ++rhs; + while(rhs != m_backwardStack->end()) + { + if(*lhs == *rhs) + { + rhs = m_backwardStack->remove(rhs); + } + else + { + ++lhs; + rhs = lhs; + ++rhs; + } + } + + rhs = m_forwardStack->begin(); + while(rhs != m_forwardStack->end()) + { + if(*lhs == *rhs) + { + rhs = m_forwardStack->remove(rhs); + } + else + { + if(lhs == m_backwardStack->fromLast()) + { + lhs = m_forwardStack->begin(); + } + else + { + ++lhs; + rhs = lhs; + } + ++rhs; + } + } + + if(m_backwardStack->isEmpty() && !m_forwardStack->isEmpty()) + forward(); +} + +void AlbumHistory::getBackwardHistory(TQStringList &list) const +{ + if(m_backwardStack->isEmpty()) + return; + + AlbumStack::const_iterator iter = m_backwardStack->begin(); + for(; iter != m_backwardStack->fromLast(); ++iter) + { + list.push_front((*iter)->album->title()); + } +} + +void AlbumHistory::getForwardHistory(TQStringList &list) const +{ + if(m_forwardStack->isEmpty()) + return; + + AlbumStack::const_iterator iter; + for(iter = m_forwardStack->begin(); iter != m_forwardStack->end(); ++iter) + { + list.append((*iter)->album->title()); + } +} + +void AlbumHistory::back(Album **album, TQWidget **widget, unsigned int steps) +{ + *album = 0; + *widget = 0; + + if(m_backwardStack->count() <= 1 || steps > m_backwardStack->count()) + return; // Only the current album available + + while(steps) + { + m_forwardStack->push_front(m_backwardStack->last()); + m_backwardStack->erase(m_backwardStack->fromLast()); + --steps; + } + m_moving = true; + + HistoryItem *item = getCurrentAlbum(); + if(item) + { + *album = item->album; + *widget = item->widget; + } +} + +void AlbumHistory::forward(Album **album, TQWidget **widget, unsigned int steps) +{ + *album = 0; + *widget = 0; + + if(m_forwardStack->isEmpty() || steps > m_forwardStack->count()) + return; + + forward(steps); + + HistoryItem *item = getCurrentAlbum(); + if(item) + { + *album = item->album; + *widget = item->widget; + } +} + +void AlbumHistory::forward(unsigned int steps) +{ + if(m_forwardStack->isEmpty() || steps > m_forwardStack->count()) + return; + + while(steps) + { + m_backwardStack->push_back(m_forwardStack->first()); + m_forwardStack->erase(m_forwardStack->begin()); + --steps; + } + m_moving = true; +} + +HistoryItem* AlbumHistory::getCurrentAlbum() const +{ + if(m_backwardStack->isEmpty()) + return 0; + + return m_backwardStack->last(); +} + +void AlbumHistory::getCurrentAlbum(Album **album, TQWidget **widget) const +{ + *album = 0; + *widget = 0; + + if(m_backwardStack->isEmpty()) + return; + + HistoryItem *item = m_backwardStack->last(); + if(item) + { + *album = item->album; + *widget = item->widget; + } +} + +bool AlbumHistory::isForwardEmpty() const +{ + return m_forwardStack->isEmpty(); +} + +bool AlbumHistory::isBackwardEmpty() const +{ + // the last album of the backwardStack is the currently shown + // album, and therfore not really a previous album + return (m_backwardStack->count() <= 1) ? true : false; +} + +} // namespace Digikam + diff --git a/src/digikam/albumhistory.h b/src/digikam/albumhistory.h new file mode 100644 index 00000000..585dad84 --- /dev/null +++ b/src/digikam/albumhistory.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : Albums history manager. + * + * Copyright (C) 2004 by Joern Ahrens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMHISTORY_H +#define ALBUMHISTORY_H + +/** @file albumhistory.h */ + +// TQt includes. + +#include +#include +#include + +namespace Digikam +{ + +class Album; +class HistoryItem; + +/** + * Manages the history of the last visited albums. + * + * The user is able to navigate through the albums, he has + * opened during a session. + */ +class AlbumHistory : public TQObject +{ + TQ_OBJECT + +public: + + AlbumHistory(); + ~AlbumHistory(); + + void addAlbum(Album *album, TQWidget *widget = 0); + void deleteAlbum(Album *album); + void clearHistory(); + void back(Album **album, TQWidget **widget, unsigned int steps=1); + void forward(Album **album, TQWidget **widget, unsigned int steps=1); + void getCurrentAlbum(Album **album, TQWidget **widget) const; + + void getBackwardHistory(TQStringList &list) const; + void getForwardHistory(TQStringList &list) const; + + bool isForwardEmpty() const; + bool isBackwardEmpty() const; + +private: + + HistoryItem* getCurrentAlbum() const; + void forward(unsigned int steps=1); + + typedef TQValueList AlbumStack; + + AlbumStack *m_backwardStack; + AlbumStack *m_forwardStack; + bool m_moving; +}; + +} // namespace Digikam + +#endif /* ALBUMHISTORY_H */ diff --git a/src/digikam/albumicongroupitem.cpp b/src/digikam/albumicongroupitem.cpp new file mode 100644 index 00000000..07562f8b --- /dev/null +++ b/src/digikam/albumicongroupitem.cpp @@ -0,0 +1,168 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-25 + * Description : implementation to render album icons group item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "albummanager.h" +#include "album.h" +#include "themeengine.h" +#include "albumsettings.h" +#include "albumiconview.h" +#include "albumicongroupitem.h" + +namespace Digikam +{ + +AlbumIconGroupItem::AlbumIconGroupItem(AlbumIconView* view, int albumID) + : IconGroupItem(view), m_albumID(albumID), m_view(view) +{ +} + +AlbumIconGroupItem::~AlbumIconGroupItem() +{ +} + +int AlbumIconGroupItem::compare(IconGroupItem* group) +{ + AlbumIconGroupItem* agroup = (AlbumIconGroupItem*)group; + + PAlbum* mine = AlbumManager::instance()->findPAlbum(m_albumID); + PAlbum* his = AlbumManager::instance()->findPAlbum(agroup->m_albumID); + + if (!mine || !his) + return 0; + + const AlbumSettings *settings = m_view->settings(); + + switch (settings->getImageSortOrder()) + { + case(AlbumSettings::ByIName): + case(AlbumSettings::ByISize): + case(AlbumSettings::ByIPath): + case(AlbumSettings::ByIRating): + { + return mine->url().localeAwareCompare(his->url()); + } + case(AlbumSettings::ByIDate): + { + if (mine->date() < his->date()) + return -1; + else if (mine->date() > his->date()) + return 1; + else + return 0; + } + } + + return 0; +} + +void AlbumIconGroupItem::paintBanner() +{ + AlbumManager* man = AlbumManager::instance(); + PAlbum* album = man->findPAlbum(m_albumID); + + TQString dateAndComments; + TQString prettyURL; + + if (album) + { + TQDate date = album->date(); + + dateAndComments = i18n("%1 %2 - 1 Item", "%1 %2 - %n Items", count()) + .arg(TDEGlobal::locale()->calendar()->monthName(date, false)) + .arg(TDEGlobal::locale()->calendar()->year(date)); + + if (!album->caption().isEmpty()) + { + TQString caption = album->caption(); + dateAndComments += " - " + caption.replace("\n", " "); + } + + prettyURL = album->prettyURL(); + } + + TQRect r(0, 0, rect().width(), rect().height()); + + TQPixmap pix(m_view->bannerPixmap()); + + TQFont fn(m_view->font()); + fn.setBold(true); + int fnSize = fn.pointSize(); + bool usePointSize; + if (fnSize > 0) + { + fn.setPointSize(fnSize+2); + usePointSize = true; + } + else + { + fnSize = fn.pixelSize(); + fn.setPixelSize(fnSize+2); + usePointSize = false; + } + + TQPainter p(&pix); + p.setPen(ThemeEngine::instance()->textSelColor()); + p.setFont(fn); + + TQRect tr; + p.drawText(5, 5, r.width(), r.height(), + TQt::AlignLeft | TQt::AlignTop, prettyURL, + -1, &tr); + + r.setY(tr.height() + 2); + + if (usePointSize) + fn.setPointSize(m_view->font().pointSize()); + else + fn.setPixelSize(m_view->font().pixelSize()); + + fn.setBold(false); + p.setFont(fn); + + p.drawText(5, r.y(), r.width(), r.height(), + TQt::AlignLeft | TQt::AlignVCenter, dateAndComments); + + p.end(); + + r = rect(); + r = TQRect(iconView()->contentsToViewport(TQPoint(r.x(), r.y())), + TQSize(r.width(), r.height())); + + bitBlt(iconView()->viewport(), r.x(), r.y(), &pix, + 0, 0, r.width(), r.height()); +} + +} // namespace Digikam diff --git a/src/digikam/albumicongroupitem.h b/src/digikam/albumicongroupitem.h new file mode 100644 index 00000000..ce2f5c7e --- /dev/null +++ b/src/digikam/albumicongroupitem.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-25 + * Description : implementation to render album icons group item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMICONGROUPITEM_H +#define ALBUMICONGROUPITEM_H + +// Local includes. + +#include "icongroupitem.h" + +namespace Digikam +{ + +class AlbumIconView; + +class AlbumIconGroupItem : public IconGroupItem +{ +public: + + AlbumIconGroupItem(AlbumIconView* view, int albumID); + ~AlbumIconGroupItem(); + + int albumID() const { return m_albumID; } + + virtual int compare(IconGroupItem* group); + +protected: + + void paintBanner(); + +private: + + int m_albumID; + AlbumIconView* m_view; +}; + +} // namespace Digikam + +#endif /* ALBUMICONGROUPITEM_H */ diff --git a/src/digikam/albumiconitem.cpp b/src/digikam/albumiconitem.cpp new file mode 100644 index 00000000..8c97f084 --- /dev/null +++ b/src/digikam/albumiconitem.cpp @@ -0,0 +1,375 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-04-25 + * Description : implementation to render album icon item. + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2003-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "themeengine.h" +#include "thumbnailsize.h" +#include "imageinfo.h" +#include "albumsettings.h" +#include "icongroupitem.h" +#include "pixmapmanager.h" +#include "albumiconview.h" +#include "albumiconitem.h" + +namespace Digikam +{ + +class AlbumIconItemPriv +{ +public: + + AlbumIconItemPriv() + { + dirty = true; + info = 0; + view = 0; + } + + bool dirty; + + TQRect tightPixmapRect; + + ImageInfo *info; + + AlbumIconView *view; +}; + +static void dateToString(const TQDateTime& datetime, TQString& str) +{ + str = TDEGlobal::locale()->formatDateTime(datetime, true, false); +} + +AlbumIconItem::AlbumIconItem(IconGroupItem* parent, ImageInfo* info) + : IconItem(parent) +{ + d = new AlbumIconItemPriv; + d->view = (AlbumIconView*) parent->iconView(); + d->info = info; +} + +AlbumIconItem::~AlbumIconItem() +{ + delete d; +} + +TQString AlbumIconItem::squeezedText(TQPainter* p, int width, const TQString& text) +{ + TQString fullText(text); + fullText.replace("\n"," "); + TQFontMetrics fm(p->fontMetrics()); + int textWidth = fm.width(fullText); + + if (textWidth > width) + { + // start with the dots only + TQString squeezedText = "..."; + int squeezedWidth = fm.width(squeezedText); + + // estimate how many letters we can add to the dots on both sides + int letters = fullText.length() * (width - squeezedWidth) / textWidth; + if (width < squeezedWidth) letters=1; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + + if (squeezedWidth < width) + { + // we estimated too short + // add letters while text < label + do + { + letters++; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + } + while (squeezedWidth < width); + + letters--; + squeezedText = fullText.left(letters) + "..."; + } + else if (squeezedWidth > width) + { + // we estimated too long + // remove letters while text > label + do + { + letters--; + squeezedText = fullText.left(letters) + "..."; + squeezedWidth = fm.width(squeezedText); + } + while (letters && squeezedWidth > width); + } + + if (letters >= 5) + { + return squeezedText; + } + } + + return fullText; +} + +bool AlbumIconItem::isDirty() +{ + return d->dirty; +} + +ImageInfo* AlbumIconItem::imageInfo() const +{ + return d->info; +} + +int AlbumIconItem::compare(IconItem *item) +{ + const AlbumSettings *settings = d->view->settings(); + AlbumIconItem *iconItem = static_cast(item); + + switch (settings->getImageSortOrder()) + { + case(AlbumSettings::ByIName): + { + return d->info->name().localeAwareCompare(iconItem->d->info->name()); + } + case(AlbumSettings::ByIPath): + { + return d->info->kurl().path().compare(iconItem->d->info->kurl().path()); + } + case(AlbumSettings::ByIDate): + { + if (d->info->dateTime() < iconItem->d->info->dateTime()) + return -1; + else if (d->info->dateTime() > iconItem->d->info->dateTime()) + return 1; + else + return 0; + } + case(AlbumSettings::ByISize): + { + int mysize(d->info->fileSize()); + int hissize(iconItem->d->info->fileSize()); + if (mysize < hissize) + return -1; + else if (mysize > hissize) + return 1; + else + return 0; + } + case(AlbumSettings::ByIRating): + { + int myrating(d->info->rating()); + int hisrating(iconItem->d->info->rating()); + if (myrating < hisrating) + return 1; + else if (myrating > hisrating) + return -1; + else + return 0; + } + } + + return 0; +} + +TQRect AlbumIconItem::clickToOpenRect() +{ + if (d->tightPixmapRect.isNull()) + return rect(); + + TQRect pixmapRect = d->tightPixmapRect; + TQRect r = rect(); + + pixmapRect.moveBy(r.x(), r.y()); + return pixmapRect; +} + +void AlbumIconItem::paintItem() +{ + TQPixmap pix; + TQRect r; + const AlbumSettings *settings = d->view->settings(); + + if (isSelected()) + pix = *(d->view->itemBaseSelPixmap()); + else + pix = *(d->view->itemBaseRegPixmap()); + + ThemeEngine* te = ThemeEngine::instance(); + + TQPainter p(&pix); + p.setPen(isSelected() ? te->textSelColor() : te->textRegColor()); + + + d->dirty = true; + + TQPixmap *thumbnail = d->view->pixmapManager()->find(d->info->kurl()); + if (thumbnail) + { + r = d->view->itemPixmapRect(); + p.drawPixmap(r.x() + (r.width()-thumbnail->width())/2, + r.y() + (r.height()-thumbnail->height())/2, + *thumbnail); + d->tightPixmapRect.setRect(r.x() + (r.width()-thumbnail->width())/2, + r.y() + (r.height()-thumbnail->height())/2, + thumbnail->width(), thumbnail->height()); + d->dirty = false; + } + + if (settings->getIconShowRating()) + { + r = d->view->itemRatingRect(); + TQPixmap ratingPixmap = d->view->ratingPixmap(); + + int rating = d->info->rating(); + + int x, w; + x = r.x() + (r.width() - rating * ratingPixmap.width())/2; + w = rating * ratingPixmap.width(); + + p.drawTiledPixmap(x, r.y(), w, r.height(), ratingPixmap); + } + + if (settings->getIconShowName()) + { + r = d->view->itemNameRect(); + p.setFont(d->view->itemFontReg()); + p.drawText(r, TQt::AlignCenter, squeezedText(&p, r.width(), + d->info->name())); + } + + p.setFont(d->view->itemFontCom()); + + if (settings->getIconShowComments()) + { + TQString comments = d->info->caption(); + + r = d->view->itemCommentsRect(); + p.drawText(r, TQt::AlignCenter, squeezedText(&p, r.width(), comments)); + } + + p.setFont(d->view->itemFontXtra()); + + if (settings->getIconShowDate()) + { + TQDateTime date(d->info->dateTime()); + + r = d->view->itemDateRect(); + p.setFont(d->view->itemFontXtra()); + TQString str; + dateToString(date, str); + str = i18n("created : %1").arg(str); + p.drawText(r, TQt::AlignCenter, squeezedText(&p, r.width(), str)); + } + + if (settings->getIconShowModDate()) + { + TQDateTime date(d->info->modDateTime()); + + r = d->view->itemModDateRect(); + p.setFont(d->view->itemFontXtra()); + TQString str; + dateToString(date, str); + str = i18n("modified : %1").arg(str); + p.drawText(r, TQt::AlignCenter, squeezedText(&p, r.width(), str)); + } + + if (settings->getIconShowResolution()) + { + TQSize dims = d->info->dimensions(); + if (dims.isValid()) + { + TQString mpixels, resolution; + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + resolution = (!dims.isValid()) ? i18n("Unknown") : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + r = d->view->itemResolutionRect(); + p.drawText(r, TQt::AlignCenter, squeezedText(&p, r.width(), resolution)); + } + } + + if (settings->getIconShowSize()) + { + r = d->view->itemSizeRect(); + p.drawText(r, TQt::AlignCenter, + squeezedText(&p, r.width(), + TDEIO::convertSize(d->info->fileSize()))); + } + + p.setFont(d->view->itemFontCom()); + p.setPen(isSelected() ? te->textSpecialSelColor() : te->textSpecialRegColor()); + + if (settings->getIconShowTags()) + { + TQString tags = d->info->tagNames().join(", "); + + r = d->view->itemTagRect(); + p.drawText(r, TQt::AlignCenter, + squeezedText(&p, r.width(), tags)); + } + + if (this == d->view->currentItem()) + { + p.setPen(TQPen(isSelected() ? te->textSelColor() : te->textRegColor(), + 0, TQt::DotLine)); + p.drawRect(1, 1, pix.width()-2, pix.height()-2); + } + + p.end(); + + r = rect(); + r = TQRect(d->view->contentsToViewport(TQPoint(r.x(), r.y())), + TQSize(r.width(), r.height())); + + bitBlt(d->view->viewport(), r.x(), r.y(), &pix, + 0, 0, r.width(), r.height()); +} + +TQRect AlbumIconItem::thumbnailRect() const +{ + TQRect pixmapRect = d->view->itemPixmapRect(); + TQRect r = rect(); + + pixmapRect.moveBy(r.x(), r.y()); + return pixmapRect; +} + +} // namespace Digikam diff --git a/src/digikam/albumiconitem.h b/src/digikam/albumiconitem.h new file mode 100644 index 00000000..1f743b60 --- /dev/null +++ b/src/digikam/albumiconitem.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-04-25 + * Description : implementation to render album icon item. + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2003-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMICONITEM_H +#define ALBUMICONITEM_H + +// TQt includes. + +#include + +// Local includes. + +#include "iconitem.h" + +class TQPainter; +class TQString; + +namespace Digikam +{ + +class ImageInfo; +class AlbumIconView; +class AlbumIconItemPriv; + +class AlbumIconItem : public IconItem +{ + +public: + + AlbumIconItem(IconGroupItem* parent, ImageInfo* info); + ~AlbumIconItem(); + + ImageInfo* imageInfo() const; + + TQRect thumbnailRect() const; + + bool isDirty(); + + static TQString squeezedText(TQPainter* p, int width, const TQString& text); + + virtual int compare(IconItem *item); + virtual TQRect clickToOpenRect(); + +protected: + + virtual void paintItem(); + +private: + + AlbumIconItemPriv *d; +}; + +} // namespace Digikam + +#endif // ALBUMICONITEM_H diff --git a/src/digikam/albumiconview.cpp b/src/digikam/albumiconview.cpp new file mode 100644 index 00000000..88d6a3d1 --- /dev/null +++ b/src/digikam/albumiconview.cpp @@ -0,0 +1,2341 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : album icon view + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2002-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// C Ansi includes. + +extern "C" +{ +#include +#include +#include +} + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if KDE_IS_VERSION(3,2,0) +#include +#include +#else +#include +#endif + +// LibKipi includes. + +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "constants.h" +#include "ddebug.h" +#include "album.h" +#include "albummanager.h" +#include "dio.h" +#include "albumlister.h" +#include "albumfiletip.h" +#include "albumsettings.h" +#include "imagewindow.h" +#include "thumbnailsize.h" +#include "themeengine.h" +#include "dpopupmenu.h" +#include "tagspopupmenu.h" +#include "ratingpopupmenu.h" +#include "pixmapmanager.h" +#include "cameraui.h" +#include "dragobjects.h" +#include "dmetadata.h" +#include "albumdb.h" +#include "imageattributeswatch.h" +#include "deletedialog.h" +#include "albumiconitem.h" +#include "albumicongroupitem.h" +#include "loadingcacheinterface.h" +#include "lighttablewindow.h" +#include "statusprogressbar.h" +#include "metadatahub.h" +#include "albumiconview.h" +#include "albumiconview.moc" + +namespace Digikam +{ + +class AlbumIconViewPrivate +{ +public: + + void init() + { + imageLister = 0; + currentAlbum = 0; + albumSettings = 0; + pixMan = 0; + toolTip = 0; + } + + TQString albumTitle; + TQString albumDate; + TQString albumComments; + + TQRect itemRect; + TQRect itemRatingRect; + TQRect itemDateRect; + TQRect itemModDateRect; + TQRect itemPixmapRect; + TQRect itemNameRect; + TQRect itemCommentsRect; + TQRect itemResolutionRect; + TQRect itemSizeRect; + TQRect itemTagRect; + TQRect bannerRect; + + TQPixmap itemRegPixmap; + TQPixmap itemSelPixmap; + TQPixmap bannerPixmap; + TQPixmap ratingPixmap; + + TQFont fnReg; + TQFont fnCom; + TQFont fnXtra; + + TQDict itemDict; + + KURL itemUrlToFind; + + AlbumLister *imageLister; + Album *currentAlbum; + const AlbumSettings *albumSettings; + TQIntDict albumDict; + PixmapManager *pixMan; + + ThumbnailSize thumbSize; + + AlbumFileTip *toolTip; +}; + +AlbumIconView::AlbumIconView(TQWidget* parent) + : IconView(parent) +{ + d = new AlbumIconViewPrivate; + d->init(); + d->imageLister = AlbumLister::instance(); + d->pixMan = new PixmapManager(this); + d->toolTip = new AlbumFileTip(this); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + // -- Load rating Pixmap ------------------------------------------ + + TDEGlobal::dirs()->addResourceType("digikam_rating", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + TQString ratingPixPath = TDEGlobal::dirs()->findResourceDir("digikam_rating", "rating.png"); + ratingPixPath += "/rating.png"; + d->ratingPixmap = TQPixmap(ratingPixPath); + + TQPainter painter(&d->ratingPixmap); + painter.fillRect(0, 0, d->ratingPixmap.width(), d->ratingPixmap.height(), + ThemeEngine::instance()->textSpecialRegColor()); + painter.end(); + + // -- ImageLister connections ------------------------------------- + + connect(d->imageLister, TQ_SIGNAL(signalNewFilteredItems(const ImageInfoList&)), + this, TQ_SLOT(slotImageListerNewItems(const ImageInfoList&))); + + connect(d->imageLister, TQ_SIGNAL(signalDeleteFilteredItem(ImageInfo*)), + this, TQ_SLOT(slotImageListerDeleteItem(ImageInfo*)) ); + + connect(d->imageLister, TQ_SIGNAL(signalClear()), + this, TQ_SLOT(slotImageListerClear())); + + // -- Icon connections -------------------------------------------- + + connect(this, TQ_SIGNAL(signalDoubleClicked(IconItem*)), + this, TQ_SLOT(slotDoubleClicked(IconItem*))); + + connect(this, TQ_SIGNAL(signalReturnPressed(IconItem*)), + this, TQ_SLOT(slotDoubleClicked(IconItem*))); + + connect(this, TQ_SIGNAL(signalRightButtonClicked(IconItem*, const TQPoint &)), + this, TQ_SLOT(slotRightButtonClicked(IconItem*, const TQPoint &))); + + connect(this, TQ_SIGNAL(signalRightButtonClicked(const TQPoint &)), + this, TQ_SLOT(slotRightButtonClicked(const TQPoint &))); + + connect(this, TQ_SIGNAL(signalSelectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); + + connect(this, TQ_SIGNAL(signalShowToolTip(IconItem*)), + this, TQ_SLOT(slotShowToolTip(IconItem*))); + + // -- ThemeEngine connections --------------------------------------- + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + TQ_SLOT(slotThemeChanged())); + + // -- Pixmap manager connections ------------------------------------ + + connect(d->pixMan, TQ_SIGNAL(signalPixmap(const KURL&)), + TQ_SLOT(slotGotThumbnail(const KURL&))); + + // -- ImageAttributesWatch connections ------------------------------ + + ImageAttributesWatch *watch = ImageAttributesWatch::instance(); + + connect(watch, TQ_SIGNAL(signalImageTagsChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageAttributesChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImagesChanged(int)), + this, TQ_SLOT(slotAlbumImagesChanged(int))); + + connect(watch, TQ_SIGNAL(signalImageRatingChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageAttributesChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImageDateChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageAttributesChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImageCaptionChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageAttributesChanged(TQ_LLONG))); +} + +AlbumIconView::~AlbumIconView() +{ + delete d->pixMan; + delete d->toolTip; + delete d; +} + +void AlbumIconView::applySettings(const AlbumSettings* settings) +{ + if (!settings) + return; + + d->albumSettings = settings; + + d->imageLister->setNamesFilter(d->albumSettings->getAllFileFilter()); + + d->thumbSize = (ThumbnailSize::Size)d->albumSettings->getDefaultIconSize(); + + setEnableToolTips(d->albumSettings->getShowToolTips()); + + updateBannerRectPixmap(); + updateItemRectsPixmap(); + + d->imageLister->stop(); + clear(); + + d->pixMan->setThumbnailSize(d->thumbSize.size()); + + if (d->currentAlbum) + { + d->imageLister->openAlbum(d->currentAlbum); + } +} + +void AlbumIconView::setThumbnailSize(const ThumbnailSize& thumbSize) +{ + if ( d->thumbSize != thumbSize) + { + d->thumbSize = thumbSize; + d->pixMan->setThumbnailSize(d->thumbSize.size()); + + updateBannerRectPixmap(); + updateItemRectsPixmap(); + + IconItem *currentIconItem = currentItem(); + triggerRearrangement(); + setStoredVisibleItem(currentIconItem); + } +} + +void AlbumIconView::setAlbum(Album* album) +{ + if (!album) + { + d->currentAlbum = 0; + d->imageLister->stop(); + clear(); + + return; + } + + if (d->currentAlbum == album) + return; + + d->imageLister->stop(); + clear(); + + d->currentAlbum = album; + d->imageLister->openAlbum(d->currentAlbum); + + updateBannerRectPixmap(); + updateItemRectsPixmap(); +} + +void AlbumIconView::setAlbumItemToFind(const KURL& url) +{ + d->itemUrlToFind = url; +} + +void AlbumIconView::refreshIcon(AlbumIconItem* item) +{ + if (!item) + return; + + emit signalSelectionChanged(); +} + +void AlbumIconView::clear(bool update) +{ + emit signalCleared(); + + d->pixMan->clear(); + d->itemDict.clear(); + d->albumDict.clear(); + + IconView::clear(update); + + emit signalSelectionChanged(); +} + +void AlbumIconView::slotImageListerNewItems(const ImageInfoList& itemList) +{ + if (!d->currentAlbum || d->currentAlbum->isRoot()) + return; + + ImageInfo* item; + for (ImageInfoListIterator it(itemList); (item = it.current()); ++it) + { + KURL url( item->kurl() ); + url.cleanPath(); + + if (AlbumIconItem *oldItem = d->itemDict.find(url.url())) + { + slotImageListerDeleteItem(oldItem->imageInfo()); + } + + AlbumIconGroupItem* group = d->albumDict.find(item->albumID()); + if (!group) + { + group = new AlbumIconGroupItem(this, item->albumID()); + d->albumDict.insert(item->albumID(), group); + } + + if (!item->album()) + { + DWarning() << "No album for item: " << item->name() + << ", albumID: " << item->albumID() << endl; + continue; + } + + AlbumIconItem* iconItem = new AlbumIconItem(group, item); + item->setViewItem(iconItem); + + d->itemDict.insert(url.url(), iconItem); + } + + // Make the icon, specified by d->itemUrlToFind, the current one + // in the album icon view and make it visible. + // This is for example used after a "Go To", + // e.g. from tags (or date) view to folder view. + // Note that AlbumIconView::slotImageListerNewItems may + // be called several times after another, because images get + // listed in packages of 200. + // Therefore the item might not always be available in the very + // first call when there are sufficiently many images. + // Also, because of this, we cannot reset the item which is to be found, + // i.e. something like d->itemUrlToFind = 0, after the item was found, + // as then the visibility of this item is lost in a subsequent call. + if (!d->itemUrlToFind.isEmpty()) + { + AlbumIconItem* icon = findItem(d->itemUrlToFind.url()); + if (icon) + { + clearSelection(); + updateContents(); + setCurrentItem(icon); + ensureItemVisible(icon); + + // make the item really visible + // (the previous ensureItemVisible does not work) + setStoredVisibleItem(icon); + triggerRearrangement(); + } + } + + emit signalItemsAdded(); +} + +void AlbumIconView::slotImageListerDeleteItem(ImageInfo* item) +{ + if (!item->getViewItem()) + return; + + AlbumIconItem* iconItem = static_cast(item->getViewItem()); + + KURL url(item->kurl()); + url.cleanPath(); + + AlbumIconItem *oldItem = d->itemDict[url.url()]; + + if( oldItem && + (oldItem->imageInfo()->id() != iconItem->imageInfo()->id())) + { + return; + } + + //d->pixMan->remove(item->kurl()); + + emit signalItemDeleted(iconItem); + + delete iconItem; + item->setViewItem(0); + + d->itemDict.remove(url.url()); + + IconGroupItem* group = firstGroup(); + IconGroupItem* tmp; + + while (group) + { + tmp = group->nextGroup(); + + if (group->count() == 0) + { + d->albumDict.remove(((AlbumIconGroupItem*)group)->albumID()); + delete group; + } + + group = tmp; + } +} + +void AlbumIconView::slotImageListerClear() +{ + clear(); +} + +void AlbumIconView::slotDoubleClicked(IconItem *item) +{ + if (!item) return; + + if (d->albumSettings->getItemRightClickAction() == AlbumSettings::ShowPreview) + { + // icon effect takes too much time + //TDEIconEffect::visualActivate(viewport(), contentsRectToViewport(item->rect())); + signalPreviewItem(static_cast(item)); + } + else + { + TDEIconEffect::visualActivate(viewport(), contentsRectToViewport(item->rect())); + slotDisplayItem(static_cast(item)); + } +} + +void AlbumIconView::slotRightButtonClicked(const TQPoint& pos) +{ + if (!d->currentAlbum) + return; + + if (d->currentAlbum->isRoot() || + ( d->currentAlbum->type() != Album::PHYSICAL + && d->currentAlbum->type() != Album::TAG)) + { + return; + } + + TQPopupMenu popmenu(this); + TDEAction *paste = KStdAction::paste(this, TQ_SLOT(slotPaste()), 0); + TQMimeSource *data = kapp->clipboard()->data(TQClipboard::Clipboard); + + if(!data || !TQUriDrag::canDecode(data)) + { + paste->setEnabled(false); + } + + paste->plug(&popmenu); + popmenu.exec(pos); + delete paste; +} + +void AlbumIconView::slotRightButtonClicked(IconItem *item, const TQPoint& pos) +{ + if (!item) + return; + + AlbumIconItem* iconItem = static_cast(item); + + // -------------------------------------------------------- + + KMimeType::Ptr mimePtr = KMimeType::findByURL(iconItem->imageInfo()->kurl(), 0, true, true); + + TQValueVector serviceVector; + TDETrader::OfferList offers = TDETrader::self()->query(mimePtr->name(), "Type == 'Application'"); + + TQPopupMenu openWithMenu; + + TDETrader::OfferList::Iterator iter; + KService::Ptr ptr; + int index = 100; + + for( iter = offers.begin(); iter != offers.end(); ++iter ) + { + ptr = *iter; + openWithMenu.insertItem( ptr->pixmap(TDEIcon::Small), ptr->name(), index++); + serviceVector.push_back(ptr); + } + + // Obtain a list of all selected images. + // This is needed both for the goto tags submenu here and also + // for the "move to trash" and further actions below. + TQValueList selectedImageIDs; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *selItem = static_cast(it); + selectedImageIDs.append(selItem->imageInfo()->id()); + } + } + + // -------------------------------------------------------- + // Provide Goto folder and/or date pop-up menu + TQPopupMenu gotoMenu; + + gotoMenu.insertItem(SmallIcon("folder_image"), i18n("Album"), 20); + gotoMenu.insertItem(SmallIcon("date"), i18n("Date"), 21); + + TagsPopupMenu* gotoTagsPopup = new TagsPopupMenu(selectedImageIDs, 1000, TagsPopupMenu::DISPLAY); + int gotoTagId = gotoMenu.insertItem(SmallIcon("tag"), i18n("Tag"), gotoTagsPopup); + + // Disable the goto Tag popup menu, if there are no tags at all. + AlbumManager* man = AlbumManager::instance(); + if (!man->albumDB()->hasTags(selectedImageIDs)) + gotoMenu.setItemEnabled(gotoTagId, false); + + connect(gotoTagsPopup, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotGotoTag(int))); + + if (d->currentAlbum->type() == Album::PHYSICAL ) + { + // If the currently selected album is the same as album to + // which the image belongs, then disable the "Go To" Album. + // (Note that in recursive album view these can be different). + if (iconItem->imageInfo()->albumID() == d->currentAlbum->id()) + gotoMenu.setItemEnabled(20, false); + } + else if (d->currentAlbum->type() == Album::DATE ) + { + gotoMenu.setItemEnabled(21, false); + } + + // -------------------------------------------------------- + + DPopupMenu popmenu(this); + popmenu.insertItem(SmallIcon("viewimage"), i18n("View..."), 18); + popmenu.insertItem(SmallIcon("editimage"), i18n("Edit..."), 10); + popmenu.insertItem(SmallIcon("lighttableadd"), i18n("Add to Light Table"), 19); + // Note that the numbers 18, 10, 19 are used below in + // the switch(id) for popmenu.exec(pos); + // For the goto menu such a number is not needed, + // because only the above 20 and 21 of the goto popup are used, + // but it has to be provided. + popmenu.insertItem(SmallIcon("goto"), i18n("Go To"), &gotoMenu, 12); + // If there is more than one image selected, disable the goto menu entry. + if (selectedImageIDs.count() > 1) + { + popmenu.setItemEnabled(12, false); + } + + popmenu.insertItem(i18n("Open With"), &openWithMenu, 11); + + // Merge in the KIPI plugins actions ---------------------------- + + KIPI::PluginLoader* kipiPluginLoader = KIPI::PluginLoader::instance(); + KIPI::PluginLoader::PluginList pluginList = kipiPluginLoader->pluginList(); + + for (KIPI::PluginLoader::PluginList::const_iterator it = pluginList.begin(); + it != pluginList.end(); ++it) + { + KIPI::Plugin* plugin = (*it)->plugin(); + + if (plugin && (*it)->name() == "JPEGLossless") + { + DDebug() << "Found JPEGLossless plugin" << endl; + + TDEActionPtrList actionList = plugin->actions(); + + for (TDEActionPtrList::const_iterator iter = actionList.begin(); + iter != actionList.end(); ++iter) + { + TDEAction* action = *iter; + + if (TQString::fromLatin1(action->name()) + == TQString::fromLatin1("jpeglossless_rotate")) + { + action->plug(&popmenu); + } + } + } + } + + // -------------------------------------------------------- + + popmenu.insertItem(SmallIcon("pencil"), i18n("Rename..."), 15); + popmenu.insertSeparator(); + + // -------------------------------------------------------- + + if (d->currentAlbum) + { + if (d->currentAlbum->type() == Album::PHYSICAL ) + { + popmenu.insertItem(i18n("Set as Album Thumbnail"), 17); + popmenu.insertSeparator(); + } + else if (d->currentAlbum->type() == Album::TAG ) + { + popmenu.insertItem(i18n("Set as Tag Thumbnail"), 17); + popmenu.insertSeparator(); + } + } + + // -------------------------------------------------------- + + TDEAction *copy = KStdAction::copy(this, TQ_SLOT(slotCopy()), 0); + TDEAction *paste = KStdAction::paste(this, TQ_SLOT(slotPaste()), 0); + TQMimeSource *data = kapp->clipboard()->data(TQClipboard::Clipboard); + if(!data || !TQUriDrag::canDecode(data)) + { + paste->setEnabled(false); + } + copy->plug(&popmenu); + paste->plug(&popmenu); + + popmenu.insertSeparator(); + + // -------------------------------------------------------- + + popmenu.insertItem(SmallIcon("edittrash"), + i18n("Move to Trash", "Move %n Files to Trash" , selectedImageIDs.count() ), 16); + + popmenu.insertSeparator(); + + // Bulk assignment/removal of tags -------------------------- + + TagsPopupMenu* assignTagsPopup = new TagsPopupMenu(selectedImageIDs, 1000, TagsPopupMenu::ASSIGN); + TagsPopupMenu* removeTagsPopup = new TagsPopupMenu(selectedImageIDs, 1000, TagsPopupMenu::REMOVE); + + connect(assignTagsPopup, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotAssignTag(int))); + + connect(removeTagsPopup, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotRemoveTag(int))); + + popmenu.insertItem(i18n("Assign Tag"), assignTagsPopup); + + int removeTagId = popmenu.insertItem(i18n("Remove Tag"), removeTagsPopup); + + // Performance: Only check for tags if there are <250 images selected + // Also disable the remove Tag popup menu, if there are no tags at all. + if (selectedImageIDs.count() > 250 || + !man->albumDB()->hasTags(selectedImageIDs)) + popmenu.setItemEnabled(removeTagId, false); + + popmenu.insertSeparator(); + + // Assign Star Rating ------------------------------------------- + + RatingPopupMenu ratingMenu; + + connect(&ratingMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotAssignRating(int))); + + popmenu.insertItem(i18n("Assign Rating"), &ratingMenu); + + // -------------------------------------------------------- + + int id = popmenu.exec(pos); + + switch(id) + { + case 10: + { + slotDisplayItem(iconItem); + break; + } + + case 15: + { + slotRename(iconItem); + break; + } + + case 16: + { + slotDeleteSelectedItems(); + break; + } + + case 17: + { + slotSetAlbumThumbnail(iconItem); + break; + } + + case 18: + { + signalPreviewItem(iconItem); + break; + } + + case 19: + { + // add images to existing images in the light table + insertSelectionToLightTable(true); + break; + } + + case 20: // goto album + { + // send a signal to the parent widget (digikamview.cpp) + emit signalGotoAlbumAndItem(iconItem); + break; + } + + case 21: // goto date + { + // send a signal to the parent widget (digikamview.cpp) + emit signalGotoDateAndItem(iconItem); + break; + } + + + default: + break; + } + + //--------------------------------------------------------------- + + if (id >= 100 && id < 1000) + { + KService::Ptr imageServicePtr = serviceVector[id-100]; + KURL::List urlList; + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *selItem = static_cast(it); + urlList.append(selItem->imageInfo()->kurl()); + } + } + if (urlList.count()) + KRun::run(*imageServicePtr, urlList); + } + + serviceVector.clear(); + delete assignTagsPopup; + delete removeTagsPopup; + delete copy; + delete paste; +} + +void AlbumIconView::slotCopy() +{ + if (!d->currentAlbum) + return; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *albumItem = static_cast(it); + urls.append(albumItem->imageInfo()->kurl()); + kioURLs.append(albumItem->imageInfo()->kurlForKIO()); + imageIDs.append(albumItem->imageInfo()->id()); + } + } + albumIDs.append(d->currentAlbum->id()); + + if (urls.isEmpty()) + return; + + TQDragObject* drag = 0; + + drag = new ItemDrag(urls, kioURLs, albumIDs, imageIDs, this); + kapp->clipboard()->setData(drag); +} + +void AlbumIconView::slotPaste() +{ + TQMimeSource *data = kapp->clipboard()->data(TQClipboard::Clipboard); + if(!data) + return; + + Album *album = 0; + + // Check if we working on grouped items view. + if (groupCount() > 1) + { + AlbumIconGroupItem *grp = dynamic_cast(findGroup(TQCursor::pos())); + if (grp) + { + if(d->currentAlbum->type() == Album::PHYSICAL) + album = dynamic_cast(AlbumManager::instance()->findPAlbum(grp->albumID())); + else if(d->currentAlbum->type() == Album::TAG) + album = dynamic_cast(AlbumManager::instance()->findTAlbum(grp->albumID())); + } + } + if (!album) + album = d->currentAlbum; + + if(d->currentAlbum->type() == Album::PHYSICAL && TQUriDrag::canDecode(data)) + { + PAlbum* palbum = (PAlbum*)album; + + // B.K.O #119205: do not handle root album. + if (palbum->isRoot()) + return; + + KURL destURL(palbum->kurl()); + + KURL::List srcURLs; + KURLDrag::decode(data, srcURLs); + + TDEIO::Job* job = DIO::copy(srcURLs, destURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + } + else if(d->currentAlbum->type() == Album::TAG && ItemDrag::canDecode(data)) + { + TAlbum* talbum = (TAlbum*)album; + + // B.K.O #119205: do not handle root album. + if (talbum->isRoot()) + return; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (!ItemDrag::decode(data, urls, kioURLs, albumIDs, imageIDs)) + return; + + if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) + return; + + TQPtrList list; + for (TQValueList::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + list.append(info); + } + + changeTagOnImageInfos(list, TQValueList() << talbum->id(), true, true); + } +} + +void AlbumIconView::slotSetAlbumThumbnail(AlbumIconItem *iconItem) +{ + if(!d->currentAlbum) + return; + + if(d->currentAlbum->type() == Album::PHYSICAL) + { + PAlbum *album = static_cast(d->currentAlbum); + + TQString err; + AlbumManager::instance()->updatePAlbumIcon( album, + iconItem->imageInfo()->id(), + err ); + } + else if (d->currentAlbum->type() == Album::TAG) + { + TAlbum *album = static_cast(d->currentAlbum); + + TQString err; + AlbumManager::instance()->updateTAlbumIcon( album, + TQString(), + iconItem->imageInfo()->id(), + err ); + } +} + +void AlbumIconView::slotRename(AlbumIconItem* item) +{ + if (!item) + return; + + // Create a copy of the item. After entering the event loop + // in the dialog, we cannot be sure about the item's status. + ImageInfo renameInfo(*item->imageInfo()); + + TQFileInfo fi(item->imageInfo()->name()); + TQString ext = TQString(".") + fi.extension(false); + TQString name = fi.fileName(); + name.truncate(fi.fileName().length() - ext.length()); + + bool ok; + +#if KDE_IS_VERSION(3,2,0) + TQString newName = KInputDialog::getText(i18n("Rename Item (%1)").arg(fi.fileName()), + i18n("Enter new name (without extension):"), + name, &ok, this); +#else + TQString newName = KLineEditDlg::getText(i18n("Rename Item (%1)").arg(fi.fileName()), + i18n("Enter new name (without extension):"), + name, &ok, this); +#endif + + if (!ok) + return; + + KURL oldURL = renameInfo.kurlForKIO(); + KURL newURL = oldURL; + newURL.setFileName(newName + ext); + + TDEIO::CopyJob* job = DIO::rename(oldURL, newURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + connect(job, TQ_SIGNAL(copyingDone(TDEIO::Job *, const KURL &, const KURL &, bool, bool)), + this, TQ_SLOT(slotRenamed(TDEIO::Job*, const KURL &, const KURL&))); + + // The AlbumManager KDirWatch will trigger a DIO::scan. + // When this is completed, DIO will call AlbumLister::instance()->refresh(). + // Usually the AlbumLister will ignore changes to already listed items. + // So the renamed item need explicitly be invalidated. + d->imageLister->invalidateItem(&renameInfo); +} + +void AlbumIconView::slotRenamed(TDEIO::Job*, const KURL &, const KURL&newURL) +{ + // reconstruct file path from digikamalbums:// URL + KURL fileURL; + fileURL.setPath(newURL.user()); + fileURL.addPath(newURL.path()); + + // refresh thumbnail + d->pixMan->remove(fileURL); + // clean LoadingCache as well - be pragmatic, do it here. + LoadingCacheInterface::cleanFromCache(fileURL.path()); +} + +void AlbumIconView::slotDeleteSelectedItems(bool deletePermanently) +{ + KURL::List urlList; + KURL::List kioUrlList; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *iconItem = static_cast(it); + urlList.append(iconItem->imageInfo()->kurl()); + kioUrlList.append(iconItem->imageInfo()->kurlForKIO()); + } + } + + if (urlList.count() <= 0) + return; + + DeleteDialog dialog(this); + + if (!dialog.confirmDeleteList(urlList, + DeleteDialogMode::Files, + deletePermanently ? + DeleteDialogMode::NoChoiceDeletePermanently : + DeleteDialogMode::NoChoiceTrash)) + return; + + bool useTrash = !dialog.shouldDelete(); + + // trash does not like non-local URLs, put is not implemented + TDEIO::Job* job = DIO::del(useTrash ? urlList : kioUrlList, useTrash); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + + // The AlbumManager KDirWatch will trigger a DIO::scan. + // When this is completed, DIO will call AlbumLister::instance()->refresh(). +} + +void AlbumIconView::slotDeleteSelectedItemsDirectly(bool useTrash) +{ + // This method deletes the selected items directly, without confirmation. + // It is not used in the default setup. + + KURL::List kioUrlList; + KURL::List urlList; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *iconItem = static_cast(it); + kioUrlList.append(iconItem->imageInfo()->kurlForKIO()); + urlList.append(iconItem->imageInfo()->kurl()); + } + } + + if (kioUrlList.count() <= 0) + return; + + // trash does not like non-local URLs, put is not implemented + TDEIO::Job* job = DIO::del(useTrash ? urlList : kioUrlList , useTrash); + + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); +} + +void AlbumIconView::slotFilesModified() +{ + d->imageLister->refresh(); +} + +void AlbumIconView::slotFilesModified(const KURL& url) +{ + refreshItems(url); +} + +void AlbumIconView::slotImageWindowURLChanged(const KURL &url) +{ + IconItem* item = findItem(url.url()); + if (item) + setCurrentItem(item); +} + +void AlbumIconView::slotDisplayItem(AlbumIconItem *item) +{ + if (!item) return; + + AlbumSettings *settings = AlbumSettings::instance(); + + if (!settings) return; + + TQString currentFileExtension = item->imageInfo()->name().section( '.', -1 ); + TQString imagefilter = settings->getImageFileFilter().lower() + + settings->getImageFileFilter().upper(); + +#if KDCRAW_VERSION < 0x000106 + if (KDcrawIface::DcrawBinary::instance()->versionIsRight()) + { + // add raw files only if dcraw is available + imagefilter += settings->getRawFileFilter().lower() + + settings->getRawFileFilter().upper(); + } +#else + // add raw files only if dcraw is available + imagefilter += settings->getRawFileFilter().lower() + + settings->getRawFileFilter().upper(); +#endif + + // If the current item is not an image file. + if ( !imagefilter.contains(currentFileExtension) ) + { + KMimeType::Ptr mimePtr = KMimeType::findByURL(item->imageInfo()->kurl(), + 0, true, true); + TDETrader::OfferList offers = TDETrader::self()->query(mimePtr->name(), + "Type == 'Application'"); + + if (offers.isEmpty()) + return; + + KService::Ptr ptr = offers.first(); + // Run the dedicated app to show the item. + KRun::run(*ptr, item->imageInfo()->kurl()); + return; + } + + // Run Digikam ImageEditor with all image files in the current Album. + + ImageInfoList imageInfoList; + ImageInfo *currentImageInfo = 0; + + for (IconItem *it = firstItem() ; it ; it = it->nextItem()) + { + AlbumIconItem *iconItem = static_cast(it); + TQString fileExtension = iconItem->imageInfo()->kurl().fileName().section( '.', -1 ); + + if ( imagefilter.find(fileExtension) != -1 ) + { + ImageInfo *info = new ImageInfo(*iconItem->imageInfo()); + info->setViewItem(0); + imageInfoList.append(info); + if (iconItem == item) + currentImageInfo = info; + } + } + + ImageWindow *imview = ImageWindow::imagewindow(); + + imview->disconnect(this); + + connect(imview, TQ_SIGNAL(signalFileAdded(const KURL&)), + this, TQ_SLOT(slotFilesModified())); + + connect(imview, TQ_SIGNAL(signalFileModified(const KURL&)), + this, TQ_SLOT(slotFilesModified(const KURL&))); + + connect(imview, TQ_SIGNAL(signalFileDeleted(const KURL&)), + this, TQ_SLOT(slotFilesModified())); + + connect(imview, TQ_SIGNAL(signalURLChanged(const KURL&)), + this, TQ_SLOT(slotImageWindowURLChanged(const KURL &))); + + imview->loadImageInfos(imageInfoList, + currentImageInfo, + d->currentAlbum ? i18n("Album \"%1\"").arg(d->currentAlbum->title()) : TQString(), + true); + + if (imview->isHidden()) + imview->show(); + + imview->raise(); + imview->setFocus(); +} + +void AlbumIconView::insertSelectionToLightTable(bool addTo) +{ + // Run Light Table with all selected image files in the current Album. + // If addTo is false, the light table will be emptied before adding + // the images. + ImageInfoList imageInfoList; + + for (IconItem *it = firstItem() ; it ; it = it->nextItem()) + { + if ((*it).isSelected()) + { + AlbumIconItem *iconItem = static_cast(it); + ImageInfo *info = new ImageInfo(*iconItem->imageInfo()); + info->setViewItem(0); + imageInfoList.append(info); + } + } + + insertToLightTable(imageInfoList, imageInfoList.first(), addTo); +} + +void AlbumIconView::insertToLightTable(const ImageInfoList& list, ImageInfo* current, bool addTo) +{ + LightTableWindow *ltview = LightTableWindow::lightTableWindow(); + + ltview->disconnect(this); + + connect(ltview, TQ_SIGNAL(signalFileDeleted(const KURL&)), + this, TQ_SLOT(slotFilesModified())); + + connect(this, TQ_SIGNAL(signalItemsUpdated(const KURL::List&)), + ltview, TQ_SLOT(slotItemsUpdated(const KURL::List&))); + + if (ltview->isHidden()) + ltview->show(); + + ltview->raise(); + ltview->setFocus(); + // If addTo is false, the light table will be emptied before adding + // the images. + ltview->loadImageInfos(list, current, addTo); + ltview->setLeftRightItems(list, addTo); +} + +// ------------------------------------------------------------------------------ + +AlbumIconItem* AlbumIconView::firstSelectedItem() const +{ + AlbumIconItem *iconItem = 0; + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + if (it->isSelected()) + { + iconItem = static_cast(it); + break; + } + } + + return iconItem; +} + +const AlbumSettings* AlbumIconView::settings() const +{ + return d->albumSettings; +} + +ThumbnailSize AlbumIconView::thumbnailSize() const +{ + return d->thumbSize; +} + +void AlbumIconView::resizeEvent(TQResizeEvent *e) +{ + IconView::resizeEvent(e); + + if (d->bannerRect.width() != frameRect().width()) + updateBannerRectPixmap(); +} + +// -- DnD --------------------------------------------------- + +void AlbumIconView::startDrag() +{ + if (!d->currentAlbum) + return; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *albumItem = static_cast(it); + urls.append(albumItem->imageInfo()->kurl()); + kioURLs.append(albumItem->imageInfo()->kurlForKIO()); + imageIDs.append(albumItem->imageInfo()->id()); + } + } + albumIDs.append(d->currentAlbum->id()); + + if (urls.isEmpty()) + return; + + TQPixmap icon(DesktopIcon("image-x-generic", 48)); + int w = icon.width(); + int h = icon.height(); + + TQPixmap pix(w+4,h+4); + TQString text(TQString::number(urls.count())); + + TQPainter p(&pix); + p.fillRect(0, 0, w+4, h+4, TQColor(TQt::white)); + p.setPen(TQPen(TQt::black, 1)); + p.drawRect(0, 0, w+4, h+4); + p.drawPixmap(2, 2, icon); + TQRect r = p.boundingRect(2,2,w,h,TQt::AlignLeft|TQt::AlignTop,text); + r.setWidth(TQMAX(r.width(),r.height())); + r.setHeight(TQMAX(r.width(),r.height())); + p.fillRect(r, TQColor(0,80,0)); + p.setPen(TQt::white); + TQFont f(font()); + f.setBold(true); + p.setFont(f); + p.drawText(r, TQt::AlignCenter, text); + p.end(); + + TQDragObject* drag = 0; + + drag = new ItemDrag(urls, kioURLs, albumIDs, imageIDs, this); + if (drag) + { + drag->setPixmap(pix); + drag->drag(); + } +} + +void AlbumIconView::contentsDragMoveEvent(TQDragMoveEvent *event) +{ + if (!d->currentAlbum || (AlbumDrag::canDecode(event) || + !TQUriDrag::canDecode(event) && + !CameraDragObject::canDecode(event) && + !TagListDrag::canDecode(event) && + !TagDrag::canDecode(event) && + !CameraItemListDrag::canDecode(event) && + !ItemDrag::canDecode(event))) + { + event->ignore(); + return; + } + event->accept(); +} + +void AlbumIconView::contentsDropEvent(TQDropEvent *event) +{ + if (!d->currentAlbum || (AlbumDrag::canDecode(event) || + !TQUriDrag::canDecode(event) && + !CameraDragObject::canDecode(event) && + !TagListDrag::canDecode(event) && + !TagDrag::canDecode(event) && + !CameraItemListDrag::canDecode(event) && + !ItemDrag::canDecode(event))) + { + event->ignore(); + return; + } + + Album *album = 0; + + // Check if we working on grouped items view. + if (groupCount() > 1) + { + AlbumIconGroupItem *grp = dynamic_cast(findGroup(TQCursor::pos())); + if (grp) + { + if(d->currentAlbum->type() == Album::PHYSICAL) + album = dynamic_cast(AlbumManager::instance()->findPAlbum(grp->albumID())); + else if(d->currentAlbum->type() == Album::TAG) + album = dynamic_cast(AlbumManager::instance()->findTAlbum(grp->albumID())); + } + } + if (!album) + album = d->currentAlbum; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (ItemDrag::decode(event, urls, kioURLs, albumIDs, imageIDs)) + { + // Drag & drop inside of digiKam + + // Check if items dropped come from outside current album. + KURL::List extUrls; + ImageInfoList extImgInfList; + for (TQValueList::iterator it = imageIDs.begin(); it != imageIDs.end(); ++it) + { + ImageInfo *info = new ImageInfo(*it); + if (info->albumID() != album->id()) + { + extUrls.append(info->kurlForKIO()); + extImgInfList.append(info); + } + } + + if(extUrls.isEmpty()) + { + event->ignore(); + return; + } + else if (album->type() == Album::PHYSICAL) + { + PAlbum* palbum = (PAlbum*)album; + KURL destURL(palbum->kurl()); + + KURL::List srcURLs; + KURLDrag::decode(event, srcURLs); + + TQPopupMenu popMenu(this); + popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 ); + popMenu.insertItem( SmallIcon("edit-copy"), i18n("&Copy Here"), 11 ); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: + { + TDEIO::Job* job = DIO::move(srcURLs, destURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + case 11: + { + TDEIO::Job* job = DIO::copy(srcURLs, destURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + default: + break; + } + } + } + else if (TQUriDrag::canDecode(event) && album->type() == Album::PHYSICAL) + { + // Drag & drop outside of digiKam + PAlbum* palbum = (PAlbum*)album; + KURL destURL(palbum->kurl()); + + KURL::List srcURLs; + KURLDrag::decode(event, srcURLs); + + TQPopupMenu popMenu(this); + popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 ); + popMenu.insertItem( SmallIcon("edit-copy"), i18n("&Copy Here"), 11 ); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: + { + TDEIO::Job* job = DIO::move(srcURLs, destURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + case 11: + { + TDEIO::Job* job = DIO::copy(srcURLs, destURL); + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDIOResult(TDEIO::Job*))); + break; + } + default: + break; + } + } + else if(TagDrag::canDecode(event)) + { + TQByteArray ba = event->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TAlbum* talbum = man->findTAlbum(tagID); + + if (talbum) + { + TQPopupMenu popMenu(this); + + bool moreItemsSelected = false; + bool itemDropped = false; + + AlbumIconItem *albumItem = findItem(event->pos()); + if (albumItem) + itemDropped = true; + + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + if (it->isSelected() && it != albumItem) + { + moreItemsSelected = true; + break; + } + } + + if (moreItemsSelected) + popMenu.insertItem(SmallIcon("tag"), + i18n("Assign '%1' to &Selected Items").arg(talbum->tagPath().mid(1)), 10); + + if (itemDropped) + popMenu.insertItem(SmallIcon("tag"), + i18n("Assign '%1' to &This Item").arg(talbum->tagPath().mid(1)), 12); + + popMenu.insertItem(SmallIcon("tag"), + i18n("Assign '%1' to &All Items").arg(talbum->tagPath().mid(1)), 11); + + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("&Cancel")); + + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: // Selected Items + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + // always give a copy of the image infos (the "true"). Else there were crashes reported. + changeTagOnImageInfos(selectedImageInfos(true), TQValueList() << tagID, true, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + break; + } + case 11: // All Items + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + changeTagOnImageInfos(allImageInfos(true), TQValueList() << tagID, true, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + break; + } + case 12: // Dropped Item only. + { + AlbumIconItem *albumItem = findItem(event->pos()); + if (albumItem) + { + TQPtrList infos; + infos.append(albumItem->imageInfo()); + changeTagOnImageInfos(infos, TQValueList() << tagID, true, false); + } + break; + } + default: + break; + } + } + } + else if(TagListDrag::canDecode(event)) + { + TQByteArray ba = event->encodedData("digikam/taglist"); + TQDataStream ds(ba, IO_ReadOnly); + TQValueList tagIDs; + ds >> tagIDs; + + TQPopupMenu popMenu(this); + + bool moreItemsSelected = false; + bool itemDropped = false; + + AlbumIconItem *albumItem = findItem(event->pos()); + if (albumItem) + itemDropped = true; + + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + if (it->isSelected() && it != albumItem) + { + moreItemsSelected = true; + break; + } + } + + if (moreItemsSelected) + popMenu.insertItem(SmallIcon("tag"), i18n("Assign Tags to &Selected Items"), 10); + + if (itemDropped) + popMenu.insertItem(SmallIcon("tag"), i18n("Assign Tags to &This Item"), 12); + + popMenu.insertItem(SmallIcon("tag"), i18n("Assign Tags to &All Items"), 11); + + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("&Cancel")); + + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: // Selected Items + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + changeTagOnImageInfos(selectedImageInfos(true), tagIDs, true, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + break; + } + case 11: // All Items + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + changeTagOnImageInfos(allImageInfos(true), tagIDs, true, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + break; + } + case 12: // Dropped item only. + { + AlbumIconItem *albumItem = findItem(event->pos()); + if (albumItem) + { + TQPtrList infos; + infos.append(albumItem->imageInfo()); + changeTagOnImageInfos(infos, tagIDs, true, false); + } + break; + } + default: + break; + } + } + else if(CameraItemListDrag::canDecode(event)) + { + CameraUI *ui = dynamic_cast(event->source()); + if (ui) + { + TQPopupMenu popMenu(this); + popMenu.insertItem(SmallIcon("go-down"), i18n("Download from camera"), 10); + popMenu.insertItem(SmallIcon("go-down"), i18n("Download && Delete from camera"), 11); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("&Cancel")); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + switch(id) + { + case 10: // Download from camera + { + ui->slotDownload(true, false, album); + break; + } + case 11: // Download and Delete from camera + { + ui->slotDownload(true, true, album); + break; + } + default: + break; + } + } + } + else + { + event->ignore(); + } +} + +void AlbumIconView::changeTagOnImageInfos(const TQPtrList &list, const TQValueList &tagIDs, bool addOrRemove, bool progress) +{ + float cnt = list.count(); + int i = 0; + + d->imageLister->blockSignals(true); + AlbumManager::instance()->albumDB()->beginTransaction(); + for (TQPtrList::const_iterator it = list.begin(); it != list.end(); ++it) + { + MetadataHub hub; + + hub.load(*it); + + for (TQValueList::const_iterator tagIt = tagIDs.begin(); tagIt != tagIDs.end(); ++tagIt) + { + hub.setTag(*tagIt, addOrRemove); + } + + hub.write(*it, MetadataHub::PartialWrite); + hub.write((*it)->filePath(), MetadataHub::FullWriteIfChanged); + + if (progress) + { + emit signalProgressValue((int)((i++/cnt)*100.0)); + kapp->processEvents(); + } + } + d->imageLister->blockSignals(false); + AlbumManager::instance()->albumDB()->commitTransaction(); + + if (d->currentAlbum && d->currentAlbum->type() == Album::TAG) + { + d->imageLister->refresh(); + } + updateContents(); +} + +bool AlbumIconView::acceptToolTip(IconItem *item, const TQPoint &mousePos) +{ + AlbumIconItem *iconItem = dynamic_cast(item); + + if (iconItem && iconItem->clickToOpenRect().contains(mousePos)) + { + return true; + } + else + { + return false; + } +} + +void AlbumIconView::slotShowToolTip(IconItem* item) +{ + d->toolTip->setIconItem(dynamic_cast(item)); +} + +KURL::List AlbumIconView::allItems() +{ + KURL::List itemList; + + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + AlbumIconItem *item = (AlbumIconItem*) it; + itemList.append(item->imageInfo()->kurl()); + } + + return itemList; +} + +KURL::List AlbumIconView::selectedItems() +{ + KURL::List itemList; + + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *item = (AlbumIconItem*) it; + itemList.append(item->imageInfo()->kurl()); + } + } + + return itemList; +} + +TQPtrList AlbumIconView::allImageInfos(bool copy) const +{ + // Returns the list of ImageInfos of all items, + // with the extra feature that the currentItem is the first in the list. + TQPtrList list; + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + AlbumIconItem *iconItem = static_cast(it); + ImageInfo *info = iconItem->imageInfo(); + if (copy) + info = new ImageInfo(*info); + + if (iconItem == currentItem()) + list.prepend(info); + else + list.append(info); + } + return list; +} + +TQPtrList AlbumIconView::selectedImageInfos(bool copy) const +{ + // Returns the list of ImageInfos of currently selected items, + // with the extra feature that the currentItem is the first in the list. + TQPtrList list; + for (IconItem *it = firstItem(); it; it = it->nextItem()) + { + AlbumIconItem *iconItem = static_cast(it); + if (it->isSelected()) + { + ImageInfo *info = iconItem->imageInfo(); + if (copy) + info = new ImageInfo(*info); + + if (iconItem == currentItem()) + list.prepend(info); + else + list.append(info); + } + } + return list; +} + +void AlbumIconView::refresh() +{ + d->imageLister->stop(); + clear(); + + d->imageLister->openAlbum(d->currentAlbum); +} + +void AlbumIconView::refreshItems(const KURL::List& urlList) +{ + if (!d->currentAlbum || urlList.empty()) + return; + + // we do two things here: + // 1. refresh the imageinfo for the file + // 2. refresh the thumbnails + + for (KURL::List::const_iterator it = urlList.begin(); + it != urlList.end(); ++it) + { + AlbumIconItem* iconItem = findItem((*it).url()); + if (!iconItem) + continue; + + iconItem->imageInfo()->refresh(); + d->pixMan->remove(iconItem->imageInfo()->kurl()); + // clean LoadingCache as well - be pragmatic, do it here. + LoadingCacheInterface::cleanFromCache((*it).path()); + } + + emit signalItemsUpdated(urlList); + + // trigger a delayed rearrangement, in case we need to resort items + triggerRearrangement(); +} + +void AlbumIconView::slotGotThumbnail(const KURL& url) +{ + AlbumIconItem* iconItem = findItem(url.url()); + if (!iconItem) + return; + + iconItem->repaint(); +} + +void AlbumIconView::slotSelectionChanged() +{ + if (firstSelectedItem()) + emitItemsSelected(true); + else + emitItemsSelected(false); +} + +void AlbumIconView::slotSetExifOrientation( int orientation ) +{ + KURL::List urlList; + int i = 0; + + for (IconItem *it = firstItem(); it; it=it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *iconItem = static_cast(it); + urlList.append(iconItem->imageInfo()->kurl()); + } + } + + if (urlList.count() <= 0) return; + + TQStringList faildItems; + KURL::List::Iterator it; + float cnt = (float)urlList.count(); + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Revising Exif Orientation tags. Please wait...")); + + for( it = urlList.begin(); it != urlList.end(); ++it ) + { + DDebug() << "Setting Exif Orientation tag to " << orientation << endl; + + DMetadata metadata((*it).path()); + DMetadata::ImageOrientation o = (DMetadata::ImageOrientation)orientation; + metadata.setImageOrientation(o); + + if (!metadata.applyChanges()) + { + faildItems.append((*it).filename()); + } + else + { + ImageAttributesWatch::instance()->fileMetadataChanged((*it)); + } + + emit signalProgressValue((int)((i++/cnt)*100.0)); + kapp->processEvents(); + } + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + + if (!faildItems.isEmpty()) + { + if (faildItems.count() == 1) + { + KMessageBox::error(0, i18n("Failed to revise Exif orientation for file %1.") + .arg(faildItems[0])); + + } + else + { + KMessageBox::errorList(0, i18n("Failed to revise Exif orientation these files:"), + faildItems); + } + } + + refreshItems(urlList); +} + +TQRect AlbumIconView::itemRect() const +{ + return d->itemRect; +} + +TQRect AlbumIconView::itemRatingRect() const +{ + return d->itemRatingRect; +} + +TQRect AlbumIconView::itemDateRect() const +{ + return d->itemDateRect; +} + +TQRect AlbumIconView::itemModDateRect() const +{ + return d->itemModDateRect; +} + +TQRect AlbumIconView::itemPixmapRect() const +{ + return d->itemPixmapRect; +} + +TQRect AlbumIconView::itemNameRect() const +{ + return d->itemNameRect; +} + +TQRect AlbumIconView::itemCommentsRect() const +{ + return d->itemCommentsRect; +} + +TQRect AlbumIconView::itemResolutionRect() const +{ + return d->itemResolutionRect; +} + +TQRect AlbumIconView::itemTagRect() const +{ + return d->itemTagRect; +} + +TQRect AlbumIconView::itemSizeRect() const +{ + return d->itemSizeRect; +} + +TQRect AlbumIconView::bannerRect() const +{ + return d->bannerRect; +} + +TQPixmap* AlbumIconView::itemBaseRegPixmap() const +{ + return &d->itemRegPixmap; +} + +TQPixmap* AlbumIconView::itemBaseSelPixmap() const +{ + return &d->itemSelPixmap; +} + +TQPixmap AlbumIconView::bannerPixmap() const +{ + return d->bannerPixmap; +} + +TQPixmap AlbumIconView::ratingPixmap() const +{ + return d->ratingPixmap; +} + +TQFont AlbumIconView::itemFontReg() const +{ + return d->fnReg; +} + +TQFont AlbumIconView::itemFontCom() const +{ + return d->fnCom; +} + +TQFont AlbumIconView::itemFontXtra() const +{ + return d->fnXtra; +} + +void AlbumIconView::updateBannerRectPixmap() +{ + d->bannerRect = TQRect(0, 0, 0, 0); + + // Title -------------------------------------------------------- + TQFont fn(font()); + int fnSize = fn.pointSize(); + bool usePointSize; + if (fnSize > 0) + { + fn.setPointSize(fnSize+2); + usePointSize = true; + } + else + { + fnSize = fn.pixelSize(); + fn.setPixelSize(fnSize+2); + usePointSize = false; + } + + fn.setBold(true); + TQFontMetrics fm(fn); + TQRect tr = fm.boundingRect(0, 0, frameRect().width(), + 0xFFFFFFFF, TQt::AlignLeft | TQt::AlignVCenter, + "XXX"); + d->bannerRect.setHeight(tr.height()); + + if (usePointSize) + fn.setPointSize(font().pointSize()); + else + fn.setPixelSize(font().pixelSize()); + + fn.setBold(false); + fm = TQFontMetrics(fn); + + tr = fm.boundingRect(0, 0, frameRect().width(), + 0xFFFFFFFF, TQt::AlignLeft | TQt::AlignVCenter, + "XXX"); + + d->bannerRect.setHeight(d->bannerRect.height() + tr.height() + 10); + d->bannerRect.setWidth(frameRect().width()); + + d->bannerPixmap = ThemeEngine::instance()->bannerPixmap(d->bannerRect.width(), + d->bannerRect.height()); +} + +void AlbumIconView::updateItemRectsPixmap() +{ + d->itemRect = TQRect(0,0,0,0); + d->itemRatingRect = TQRect(0,0,0,0); + d->itemDateRect = TQRect(0,0,0,0); + d->itemModDateRect = TQRect(0,0,0,0); + d->itemPixmapRect = TQRect(0,0,0,0); + d->itemNameRect = TQRect(0,0,0,0); + d->itemCommentsRect = TQRect(0,0,0,0); + d->itemResolutionRect = TQRect(0,0,0,0); + d->itemSizeRect = TQRect(0,0,0,0); + d->itemTagRect = TQRect(0,0,0,0); + + d->fnReg = font(); + d->fnCom = font(); + d->fnXtra = font(); + d->fnCom.setItalic(true); + + int fnSz = d->fnReg.pointSize(); + if (fnSz > 0) + { + d->fnCom.setPointSize(fnSz-1); + d->fnXtra.setPointSize(fnSz-2); + } + else + { + fnSz = d->fnReg.pixelSize(); + d->fnCom.setPixelSize(fnSz-1); + d->fnXtra.setPixelSize(fnSz-2); + } + + int margin = 5; + int w = d->thumbSize.size() + 2*margin; + + TQFontMetrics fm(d->fnReg); + TQRect oneRowRegRect = fm.boundingRect(0, 0, w, 0xFFFFFFFF, + TQt::AlignTop | TQt::AlignHCenter, + "XXXXXXXXX"); + fm = TQFontMetrics(d->fnCom); + TQRect oneRowComRect = fm.boundingRect(0, 0, w, 0xFFFFFFFF, + TQt::AlignTop | TQt::AlignHCenter, + "XXXXXXXXX"); + fm = TQFontMetrics(d->fnXtra); + TQRect oneRowXtraRect = fm.boundingRect(0, 0, w, 0xFFFFFFFF, + TQt::AlignTop | TQt::AlignHCenter, + "XXXXXXXXX"); + + int y = margin; + + d->itemPixmapRect = TQRect(margin, y, w, d->thumbSize.size()+margin); + y = d->itemPixmapRect.bottom(); + + if (d->albumSettings->getIconShowRating()) + { + d->itemRatingRect = TQRect(margin, y, w, d->ratingPixmap.height()); + y = d->itemRatingRect.bottom(); + } + + if (d->albumSettings->getIconShowName()) + { + d->itemNameRect = TQRect(margin, y, w, oneRowRegRect.height()); + y = d->itemNameRect.bottom(); + } + + if (d->albumSettings->getIconShowComments()) + { + d->itemCommentsRect = TQRect(margin, y, w, oneRowComRect.height()); + y = d->itemCommentsRect.bottom(); + } + + + if (d->albumSettings->getIconShowDate()) + { + d->itemDateRect = TQRect(margin, y, w, oneRowXtraRect.height()); + y = d->itemDateRect.bottom(); + } + + if (d->albumSettings->getIconShowModDate()) + { + d->itemModDateRect = TQRect(margin, y, w, oneRowXtraRect.height()); + y = d->itemModDateRect.bottom(); + } + + if (d->albumSettings->getIconShowResolution()) + { + d->itemResolutionRect = TQRect(margin, y, w, oneRowXtraRect.height()); + y = d->itemResolutionRect.bottom() ; + } + + if (d->albumSettings->getIconShowSize()) + { + d->itemSizeRect = TQRect(margin, y, w, oneRowXtraRect.height()); + y = d->itemSizeRect.bottom(); + } + + if (d->albumSettings->getIconShowTags()) + { + d->itemTagRect = TQRect(margin, y, w, oneRowComRect.height()); + y = d->itemTagRect.bottom(); + } + + d->itemRect = TQRect(0, 0, w+2*margin, y+margin); + + d->itemRegPixmap = ThemeEngine::instance()->thumbRegPixmap(d->itemRect.width(), + d->itemRect.height()); + + d->itemSelPixmap = ThemeEngine::instance()->thumbSelPixmap(d->itemRect.width(), + d->itemRect.height()); +} + +void AlbumIconView::slotThemeChanged() +{ + TQPainter painter(&d->ratingPixmap); + painter.fillRect(0, 0, d->ratingPixmap.width(), d->ratingPixmap.height(), + ThemeEngine::instance()->textSpecialRegColor()); + painter.end(); + + updateBannerRectPixmap(); + updateItemRectsPixmap(); + + viewport()->update(); +} + +AlbumIconItem* AlbumIconView::findItem(const TQPoint& pos) +{ + return dynamic_cast(IconView::findItem(pos)); +} + +AlbumIconItem* AlbumIconView::findItem(const TQString& url) const +{ + return d->itemDict.find(url); +} + +AlbumIconItem* AlbumIconView::nextItemToThumbnail() const +{ + TQRect r(contentsX(), contentsY(), visibleWidth(), visibleHeight()); + IconItem *fItem = findFirstVisibleItem(r); + IconItem *lItem = findLastVisibleItem(r); + if (!fItem || !lItem) + return 0; + + AlbumIconItem* firstItem = static_cast(fItem); + AlbumIconItem* lastItem = static_cast(lItem); + AlbumIconItem* item = firstItem; + while (item) + { + if (item->isDirty()) + return item; + if (item == lastItem) + break; + item = (AlbumIconItem*)item->nextItem(); + } + + return 0; +} + +PixmapManager* AlbumIconView::pixmapManager() const +{ + return d->pixMan; +} + +void AlbumIconView::slotAlbumModified() +{ + d->imageLister->stop(); + clear(); + + d->imageLister->openAlbum(d->currentAlbum); + + updateBannerRectPixmap(); + updateItemRectsPixmap(); +} + +void AlbumIconView::slotGotoTag(int tagID) +{ + // send a signal to the parent widget (digikamview.cpp) to change + // to Tag view and the corresponding item + + emit signalGotoTagAndItem(tagID); +} + +void AlbumIconView::slotAssignTag(int tagID) +{ + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + changeTagOnImageInfos(selectedImageInfos(true), TQValueList() << tagID, true, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); +} + +void AlbumIconView::slotRemoveTag(int tagID) +{ + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Removing image tags. Please wait...")); + + changeTagOnImageInfos(selectedImageInfos(true), TQValueList() << tagID, false, true); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); +} + +void AlbumIconView::slotAssignRating(int rating) +{ + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image ratings. Please wait...")); + + int i = 0; + float cnt = (float)countSelected(); + rating = TQMIN(RatingMax, TQMAX(RatingMin, rating)); + + MetadataHub hub; + d->imageLister->blockSignals(true); + AlbumManager::instance()->albumDB()->beginTransaction(); + for (IconItem *it = firstItem() ; it ; it = it->nextItem()) + { + if (it->isSelected()) + { + AlbumIconItem *albumItem = dynamic_cast(it); + if (albumItem) + { + ImageInfo* info = albumItem->imageInfo(); + + hub.load(info); + hub.setRating(rating); + hub.write(info, MetadataHub::PartialWrite); + hub.write(info->filePath(), MetadataHub::FullWriteIfChanged); + + emit signalProgressValue((int)((i++/cnt)*100.0)); + kapp->processEvents(); + } + } + } + d->imageLister->blockSignals(false); + AlbumManager::instance()->albumDB()->commitTransaction(); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + updateContents(); +} + +void AlbumIconView::slotAssignRatingNoStar() +{ + slotAssignRating(0); +} + +void AlbumIconView::slotAssignRatingOneStar() +{ + slotAssignRating(1); +} + +void AlbumIconView::slotAssignRatingTwoStar() +{ + slotAssignRating(2); +} + +void AlbumIconView::slotAssignRatingThreeStar() +{ + slotAssignRating(3); +} + +void AlbumIconView::slotAssignRatingFourStar() +{ + slotAssignRating(4); +} + +void AlbumIconView::slotAssignRatingFiveStar() +{ + slotAssignRating(5); +} + +void AlbumIconView::slotDIOResult(TDEIO::Job* job) +{ + if (job->error()) + job->showErrorDialog(this); +} + +void AlbumIconView::slotImageAttributesChanged(TQ_LLONG imageId) +{ + AlbumIconItem *firstItem = static_cast(findFirstVisibleItem()); + AlbumIconItem *lastItem = static_cast(findLastVisibleItem()); + for (AlbumIconItem *item = firstItem; item; + item = static_cast(item->nextItem())) + { + if (item->imageInfo()->id() == imageId) + { + updateContents(); + return; + } + if (item == lastItem) + break; + } +} + +void AlbumIconView::slotAlbumImagesChanged(int /*albumId*/) +{ + updateContents(); +} + +} // namespace Digikam diff --git a/src/digikam/albumiconview.h b/src/digikam/albumiconview.h new file mode 100644 index 00000000..0239a390 --- /dev/null +++ b/src/digikam/albumiconview.h @@ -0,0 +1,217 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : album icon view + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2002-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMICONVIEW_H +#define ALBUMICONVIEW_H + +// KDE includes. + +#include +#include + +// Local includes. + +#include "iconview.h" +#include "imageinfo.h" +#include "albumitemhandler.h" + +class TQResizeEvent; +class TQDragMoveEvent; +class TQDropEvent; +class TQPoint; +class TQString; +class TQPixmap; + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class AlbumIconItem; +class AlbumSettings; +class ThumbnailSize; +class Album; +class PixmapManager; +class AlbumIconViewPrivate; + +class AlbumIconView : public IconView, + public AlbumItemHandler +{ + TQ_OBJECT + +public: + + AlbumIconView(TQWidget* parent); + ~AlbumIconView(); + + void setAlbum(Album* album); + + /** set the Url of item to select in Album View when all items will be reloaded + by setAlbum()*/ + void setAlbumItemToFind(const KURL& url); + + void setThumbnailSize(const ThumbnailSize& thumbSize); + ThumbnailSize thumbnailSize() const; + + void applySettings(const AlbumSettings* settings); + const AlbumSettings* settings() const; + + void refreshIcon(AlbumIconItem* item); + + AlbumIconItem* firstSelectedItem() const; + + KURL::List allItems(); + KURL::List selectedItems(); + + TQPtrList allImageInfos(bool copy) const; + TQPtrList selectedImageInfos(bool copy) const; + + void refresh(); + void refreshItems(const KURL::List& itemList); + + TQRect itemRect() const; + TQRect itemRatingRect() const; + TQRect itemDateRect() const; + TQRect itemModDateRect() const; + TQRect itemPixmapRect() const; + TQRect itemNameRect() const; + TQRect itemCommentsRect() const; + TQRect itemResolutionRect() const; + TQRect itemSizeRect() const; + TQRect itemTagRect() const; + TQRect bannerRect() const; + + TQPixmap* itemBaseRegPixmap() const; + TQPixmap* itemBaseSelPixmap() const; + TQPixmap bannerPixmap() const; + TQPixmap ratingPixmap() const; + + TQFont itemFontReg() const; + TQFont itemFontCom() const; + TQFont itemFontXtra() const; + + void clear(bool update=true); + + AlbumIconItem* findItem(const TQPoint& pos); + AlbumIconItem* findItem(const TQString& url) const; + AlbumIconItem* nextItemToThumbnail() const; + PixmapManager* pixmapManager() const; + + void insertSelectionToLightTable(bool addTo=false); + void insertToLightTable(const ImageInfoList& list, ImageInfo* current, bool addTo=false); + +signals: + + void signalPreviewItem(AlbumIconItem*); + void signalItemsAdded(); + void signalItemDeleted(AlbumIconItem*); + void signalCleared(); + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalItemsUpdated(const KURL::List&); + void signalGotoAlbumAndItem(AlbumIconItem *); + void signalGotoDateAndItem(AlbumIconItem *); + void signalGotoTagAndItem(int); + +public slots: + + void slotSetExifOrientation(int orientation); + void slotRename(AlbumIconItem* item); + void slotDeleteSelectedItems(bool deletePermanently = false); + void slotDeleteSelectedItemsDirectly(bool useTrash); + void slotDisplayItem(AlbumIconItem *item=0); + void slotAlbumModified(); + void slotSetAlbumThumbnail(AlbumIconItem *iconItem); + void slotCopy(); + void slotPaste(); + + void slotAssignRating(int rating); + void slotAssignRatingNoStar(); + void slotAssignRatingOneStar(); + void slotAssignRatingTwoStar(); + void slotAssignRatingThreeStar(); + void slotAssignRatingFourStar(); + void slotAssignRatingFiveStar(); + +protected: + + void resizeEvent(TQResizeEvent* e); + + // DnD + void startDrag(); + void contentsDragMoveEvent(TQDragMoveEvent *e); + void contentsDropEvent(TQDropEvent *e); + + bool acceptToolTip(IconItem *item, const TQPoint &mousePos); + +private slots: + + void slotImageListerNewItems(const ImageInfoList& itemList); + void slotImageListerDeleteItem(ImageInfo* item); + void slotImageListerClear(); + + void slotDoubleClicked(IconItem *item); + void slotRightButtonClicked(const TQPoint& pos); + void slotRightButtonClicked(IconItem *item, const TQPoint& pos); + + void slotGotThumbnail(const KURL& url); + void slotSelectionChanged(); + + void slotFilesModified(); + void slotFilesModified(const KURL& url); + void slotImageWindowURLChanged(const KURL &url); + + void slotShowToolTip(IconItem* item); + + void slotThemeChanged(); + + void slotGotoTag(int tagID); + + void slotAssignTag(int tagID); + void slotRemoveTag(int tagID); + + void slotDIOResult(TDEIO::Job* job); + void slotRenamed(TDEIO::Job*, const KURL &, const KURL&); + + void slotImageAttributesChanged(TQ_LLONG imageId); + void slotAlbumImagesChanged(int albumId); + +private: + + void updateBannerRectPixmap(); + void updateItemRectsPixmap(); + void changeTagOnImageInfos(const TQPtrList &list, const TQValueList &tagIDs, bool addOrRemove, bool progress); + +private: + + AlbumIconViewPrivate *d; +}; + +} // namespace Digikam + +#endif // ALBUMICONVIEW_H diff --git a/src/digikam/albumiconviewfilter.cpp b/src/digikam/albumiconviewfilter.cpp new file mode 100644 index 00000000..85a7c5fd --- /dev/null +++ b/src/digikam/albumiconviewfilter.cpp @@ -0,0 +1,208 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-11-27 + * Description : a bar to filter album contents + * + * Copyright (C) 2007-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "statusled.h" +#include "albumsettings.h" +#include "searchtextbar.h" +#include "ratingfilter.h" +#include "mimefilter.h" +#include "albumiconviewfilter.h" +#include "albumiconviewfilter.moc" + +namespace Digikam +{ + +class AlbumIconViewFilterPriv +{ +public: + + AlbumIconViewFilterPriv() + { + textFilter = 0; + mimeFilter = 0; + ratingFilter = 0; + led = 0; + } + + StatusLed *led; + + SearchTextBar *textFilter; + + MimeFilter *mimeFilter; + + RatingFilter *ratingFilter; +}; + +AlbumIconViewFilter::AlbumIconViewFilter(TQWidget* parent) + : TQHBox(parent) +{ + d = new AlbumIconViewFilterPriv; + + d->led = new StatusLed(this); + d->led->installEventFilter(this); + d->led->setLedColor(StatusLed::Gray); + TQWhatsThis::add(d->led, i18n("This LED indicates the global image filter status, " + "encompassing all status-bar filters and all tag filters from the right sidebar.\n\n" + "GRAY: no filter is active, all items are visible.\n" + "RED: filtering is on, but no items match.\n" + "GREEN: filter(s) matches at least one item.\n\n" + "Any mouse button click will reset all filters.")); + + d->textFilter = new SearchTextBar(this, "AlbumIconViewFilterTextFilter"); + d->textFilter->setEnableTextQueryCompletion(true); + TQToolTip::add(d->textFilter, i18n("Text quick filter (search)")); + TQWhatsThis::add(d->textFilter, i18n("Enter search patterns to quickly filter this view on file names, " + "captions (comments), and tags")); + + d->mimeFilter = new MimeFilter(this); + d->ratingFilter = new RatingFilter(this); + + setSpacing(KDialog::spacingHint()); + setMargin(0); + + connect(d->ratingFilter, TQ_SIGNAL(signalRatingFilterChanged(int, AlbumLister::RatingCondition)), + this, TQ_SLOT(slotRatingFilterChanged(int, AlbumLister::RatingCondition))); + + connect(d->mimeFilter, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotMimeTypeFilterChanged(int))); + + connect(d->textFilter, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotTextFilterChanged(const TQString&))); + + connect(AlbumLister::instance(), TQ_SIGNAL(signalItemsTextFilterMatch(bool)), + d->textFilter, TQ_SLOT(slotSearchResult(bool))); + + connect(AlbumLister::instance(), TQ_SIGNAL(signalItemsFilterMatch(bool)), + this, TQ_SLOT(slotItemsFilterMatch(bool))); +} + +AlbumIconViewFilter::~AlbumIconViewFilter() +{ + delete d; +} + +void AlbumIconViewFilter::readSettings() +{ + AlbumSettings *settings = AlbumSettings::instance(); + d->ratingFilter->setRatingFilterCondition((Digikam::AlbumLister::RatingCondition) + (settings->getRatingFilterCond())); + /* + Bug 181705: always enable filters + d->ratingFilter->setEnabled(settings->getIconShowRating()); + d->textFilter->setEnabled(settings->getIconShowName() || + settings->getIconShowComments() || + settings->getIconShowTags()); + */ +} + +void AlbumIconViewFilter::saveSettings() +{ + AlbumSettings::instance()->setRatingFilterCond(d->ratingFilter->ratingFilterCondition()); +} + +void AlbumIconViewFilter::slotRatingFilterChanged(int rating, AlbumLister::RatingCondition cond) +{ + AlbumLister::instance()->setRatingFilter(rating, cond); +} + +void AlbumIconViewFilter::slotMimeTypeFilterChanged(int mimeTypeFilter) +{ + AlbumLister::instance()->setMimeTypeFilter(mimeTypeFilter); +} + +void AlbumIconViewFilter::slotTextFilterChanged(const TQString& text) +{ + AlbumLister::instance()->setTextFilter(text); +} + +void AlbumIconViewFilter::slotItemsFilterMatch(bool match) +{ + TQStringList filtersList; + TQString message; + + if (!d->textFilter->text().isEmpty()) + filtersList.append(i18n("
Text")); + + if (d->mimeFilter->mimeFilter() != MimeFilter::AllFiles) + filtersList.append(i18n("
Mime Type")); + + if (d->ratingFilter->rating() != 0 || d->ratingFilter->ratingFilterCondition() != AlbumLister::GreaterEqualCondition) + filtersList.append(i18n("
Rating")); + + if (AlbumLister::instance()->tagFiltersIsActive()) + filtersList.append(i18n("
Tags")); + + if (filtersList.count() > 1) + message = i18n("Active filters:"); + else + message = i18n("Active filter:"); + + message.append(filtersList.join(TQString())); + + if (filtersList.isEmpty()) + { + TQToolTip::add(d->led, i18n("No active filter")); + d->led->setLedColor(StatusLed::Gray); + } + else + { + TQToolTip::add(d->led, message); + d->led->setLedColor(match ? StatusLed::Green : StatusLed::Red); + } +} + +bool AlbumIconViewFilter::eventFilter(TQObject *object, TQEvent *e) +{ + TQWidget *widget = static_cast(object); + + if (e->type() == TQEvent::MouseButtonRelease) + { + TQMouseEvent* event = static_cast(e); + if ( widget->rect().contains(event->pos()) && d->led->ledColor() != StatusLed::Gray) + { + // Reset all filters settings. + d->textFilter->setText(TQString()); + d->ratingFilter->setRating(0); + d->ratingFilter->setRatingFilterCondition(AlbumLister::GreaterEqualCondition); + d->mimeFilter->setMimeFilter(MimeFilter::AllFiles); + emit signalResetTagFilters(); + } + } + + return false; +} + +} // namespace Digikam diff --git a/src/digikam/albumiconviewfilter.h b/src/digikam/albumiconviewfilter.h new file mode 100644 index 00000000..8fdbaa64 --- /dev/null +++ b/src/digikam/albumiconviewfilter.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-11-27 + * Description : a bar to filter album contents + * + * Copyright (C) 2007-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMICONVIEWFILTER_H +#define ALBUMICONVIEWFILTER_H + +// TQt includes. + +#include "tqhbox.h" +#include "tqstring.h" + +// Local includes. + +#include "albumlister.h" + +class TQEvent; +class TQObject; + +namespace Digikam +{ + +class AlbumIconViewFilterPriv; + +class AlbumIconViewFilter : public TQHBox +{ + TQ_OBJECT + +public: + + AlbumIconViewFilter(TQWidget* parent); + ~AlbumIconViewFilter(); + + void readSettings(); + void saveSettings(); + +signals: + + void signalResetTagFilters(); + +private slots: + + void slotRatingFilterChanged(int, AlbumLister::RatingCondition); + void slotMimeTypeFilterChanged(int); + void slotTextFilterChanged(const TQString&); + void slotItemsFilterMatch(bool); + +private: + + bool eventFilter(TQObject *object, TQEvent *e); + +private: + + AlbumIconViewFilterPriv* d; +}; + +} // namespace Digikam + +#endif // ALBUMICONVIEWFILTER_H diff --git a/src/digikam/albuminfo.h b/src/digikam/albuminfo.h new file mode 100644 index 00000000..17e191e3 --- /dev/null +++ b/src/digikam/albuminfo.h @@ -0,0 +1,112 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : Album informations container. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMINFO_H +#define ALBUMINFO_H + +/** @file albuminfo.h */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +/** + * \class AlbumInfo + * A container class for transporting album information + * from the database to AlbumManager + */ +class AlbumInfo +{ +public: + + typedef TQValueList List; + + int id; + TQString url; + TQString caption; + TQString collection; + TQDate date; + TQString icon; + + /** + * needed for sorting + */ + bool operator<(const AlbumInfo& info) + { + return url < info.url; + } +}; + +/** + * \class TagInfo + * A container class for transporting tag information + * from the database to AlbumManager + */ +class TagInfo +{ +public: + + typedef TQValueList List; + + int id; + int pid; + TQString name; + TQString icon; +}; + +/** + * \class SearchInfo + * A container class for transporting search information + * from the database to AlbumManager + */ +class SearchInfo +{ +public: + + typedef TQValueList List; + + int id; + TQString name; + KURL url; + + /** + * needed for sorting + */ + bool operator<(const SearchInfo& info) + { + return id < info.id; + } +}; + +} // namespace Digikam + +#endif /* ALBUMINFO_H */ diff --git a/src/digikam/albumitemhandler.cpp b/src/digikam/albumitemhandler.cpp new file mode 100644 index 00000000..60dc8c1b --- /dev/null +++ b/src/digikam/albumitemhandler.cpp @@ -0,0 +1,45 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-10-15 + * Description : album item handler. + * + * Copyright (C) 2003 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "albummanager.h" +#include "albumitemhandler.h" + +namespace Digikam +{ + +AlbumItemHandler::AlbumItemHandler() +{ +} + +AlbumItemHandler::~AlbumItemHandler() +{ +} + +void AlbumItemHandler::emitItemsSelected(bool val) +{ + AlbumManager::instance()->emitAlbumItemsSelected(val); +} + +} // namespace Digikam diff --git a/src/digikam/albumitemhandler.h b/src/digikam/albumitemhandler.h new file mode 100644 index 00000000..e78042f5 --- /dev/null +++ b/src/digikam/albumitemhandler.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-09-26 + * Description : album item handler. + * + * Copyright (C) 2003 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMITEMHANDLER_H +#define ALBUMITEMHANDLER_H + +// KDE includes. + +#include + +namespace Digikam +{ + +/*! + AlbumItemHandler + An abstract class which returns the selected items and all items in + the current album. All views which handle album items should derive + from this. +*/ + +class AlbumItemHandler +{ +public: + + AlbumItemHandler(); + virtual ~AlbumItemHandler(); + + virtual KURL::List allItems() = 0; + virtual KURL::List selectedItems() = 0; + virtual void refresh() = 0; + virtual void refreshItems(const KURL::List& items) = 0; + +protected: + + void emitItemsSelected(bool val); +}; + +} // namespace Digikam + +#endif /* ALBUMITEMHANDLER_H */ diff --git a/src/digikam/albumlister.cpp b/src/digikam/albumlister.cpp new file mode 100644 index 00000000..63648773 --- /dev/null +++ b/src/digikam/albumlister.cpp @@ -0,0 +1,640 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-26 + * Description : Albums lister. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2007-2009 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +} + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "mimefilter.h" +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumlister.h" +#include "albumlister.moc" + +namespace Digikam +{ + +class AlbumListerPriv +{ +public: + + AlbumListerPriv() + { + untaggedFilter = false; + ratingFilter = 0; + filterTimer = 0; + job = 0; + currAlbum = 0; + namesFilter = "*"; + mimeTypeFilter = MimeFilter::AllFiles; + ratingCond = AlbumLister::GreaterEqualCondition; + matchingCond = AlbumLister::OrCondition; + recurseAlbums = false; + recurseTags = false; + } + + bool untaggedFilter; + + int ratingFilter; + int recurseAlbums; + int recurseTags; + + TQString namesFilter; + TQString textFilter; + + TQMap itemMap; + TQMap invalidatedItems; + TQMap dayFilter; + + TQValueList tagFilter; + + TQTimer *filterTimer; + + TDEIO::TransferJob *job; + + ImageInfoList itemList; + + Album *currAlbum; + + MimeFilter::TypeMimeFilter mimeTypeFilter; + + AlbumLister::MatchingCondition matchingCond; + + AlbumLister::RatingCondition ratingCond; +}; + +AlbumLister* AlbumLister::m_instance = 0; + +AlbumLister* AlbumLister::instance() +{ + if (!m_instance) + new AlbumLister(); + + return m_instance; +} + +AlbumLister::AlbumLister() +{ + m_instance = this; + + d = new AlbumListerPriv; + d->itemList.setAutoDelete(true); + d->filterTimer = new TQTimer(this); + + connect(d->filterTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotFilterItems())); +} + +AlbumLister::~AlbumLister() +{ + delete d->filterTimer; + delete d; + m_instance = 0; +} + +void AlbumLister::openAlbum(Album *album) +{ + d->currAlbum = album; + d->filterTimer->stop(); + emit signalClear(); + d->itemList.clear(); + d->itemMap.clear(); + + if (d->job) + { + d->job->kill(); + d->job = 0; + } + + if (!album) + return; + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << AlbumManager::instance()->getLibraryPath(); + ds << album->kurl(); + ds << d->namesFilter; + ds << AlbumSettings::instance()->getIconShowResolution(); + ds << d->recurseAlbums; + ds << d->recurseTags; + + // Protocol = digikamalbums -> tdeio_digikamalbums + d->job = new TDEIO::TransferJob(album->kurl(), TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + + connect(d->job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotResult(TDEIO::Job*))); + + connect(d->job, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotData(TDEIO::Job*, const TQByteArray&))); +} + +void AlbumLister::refresh() +{ + if (!d->currAlbum) + return; + + d->filterTimer->stop(); + + if (d->job) + { + d->job->kill(); + d->job = 0; + } + + d->itemMap.clear(); + ImageInfo* item; + for (ImageInfoListIterator it(d->itemList); (item = it.current()); ++it) + { + d->itemMap.insert(item->id(), item); + } + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << AlbumManager::instance()->getLibraryPath(); + ds << d->currAlbum->kurl(); + ds << d->namesFilter; + ds << AlbumSettings::instance()->getIconShowResolution(); + ds << d->recurseAlbums; + ds << d->recurseTags; + + d->job = new TDEIO::TransferJob(d->currAlbum->kurl(), TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + + connect(d->job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotResult(TDEIO::Job*))); + + connect(d->job, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotData(TDEIO::Job*, const TQByteArray&))); +} + +void AlbumLister::setDayFilter(const TQValueList& days) +{ + d->dayFilter.clear(); + + for (TQValueList::const_iterator it = days.begin(); it != days.end(); ++it) + d->dayFilter.insert(*it, true); + + d->filterTimer->start(100, true); +} + +bool AlbumLister::tagFiltersIsActive() +{ + if (!d->tagFilter.isEmpty() || d->untaggedFilter) + return true; + + return false; +} + +void AlbumLister::setTagFilter(const TQValueList& tags, const MatchingCondition& matchingCond, + bool showUnTagged) +{ + d->tagFilter = tags; + d->matchingCond = matchingCond; + d->untaggedFilter = showUnTagged; + d->filterTimer->start(100, true); +} + +void AlbumLister::setRatingFilter(int rating, const RatingCondition& ratingCond) +{ + d->ratingFilter = rating; + d->ratingCond = ratingCond; + d->filterTimer->start(100, true); +} + +void AlbumLister::setMimeTypeFilter(int mimeTypeFilter) +{ + d->mimeTypeFilter = (MimeFilter::TypeMimeFilter)mimeTypeFilter; + d->filterTimer->start(100, true); +} + +void AlbumLister::setTextFilter(const TQString& text) +{ + d->textFilter = text; + d->filterTimer->start(100, true); +} + +void AlbumLister::setRecurseAlbums(bool recursive) +{ + d->recurseAlbums = recursive; + refresh(); +} + +void AlbumLister::setRecurseTags(bool recursive) +{ + d->recurseTags = recursive; + refresh(); +} + +bool AlbumLister::matchesFilter(const ImageInfo* info, bool &foundText) +{ + if (d->dayFilter.isEmpty() && d->tagFilter.isEmpty() && d->textFilter.isEmpty() && + !d->untaggedFilter && d->ratingFilter==-1) + return true; + + bool match = false; + + if (!d->tagFilter.isEmpty()) + { + TQValueList tagIDs = info->tagIDs(); + TQValueList::iterator it; + + if (d->matchingCond == OrCondition) + { + for (it = d->tagFilter.begin(); it != d->tagFilter.end(); ++it) + { + if (tagIDs.contains(*it)) + { + match = true; + break; + } + } + } + else + { + // AND matching condition... + + for (it = d->tagFilter.begin(); it != d->tagFilter.end(); ++it) + { + if (!tagIDs.contains(*it)) + break; + } + + if (it == d->tagFilter.end()) + match = true; + } + + match |= (d->untaggedFilter && tagIDs.isEmpty()); + } + else if (d->untaggedFilter) + { + match = info->tagIDs().isEmpty(); + } + else + { + match = true; + } + + if (!d->dayFilter.isEmpty()) + { + match &= d->dayFilter.contains(TQDateTime(info->dateTime().date(), TQTime())); + } + + //-- Filter by rating --------------------------------------------------------- + + if (d->ratingFilter >= 0) + { + if (d->ratingCond == GreaterEqualCondition) + { + // If the rating is not >=, i.e it is <, then it does not match. + if (info->rating() < d->ratingFilter) + { + match = false; + } + } + else if (d->ratingCond == EqualCondition) + { + // If the rating is not =, i.e it is !=, then it does not match. + if (info->rating() != d->ratingFilter) + { + match = false; + } + } + else + { + // If the rating is not <=, i.e it is >, then it does not match. + if (info->rating() > d->ratingFilter) + { + match = false; + } + } + } + + // -- Filter by mime type ----------------------------------------------------- + + TQFileInfo fi(info->filePath()); + TQString mimeType = fi.extension(false).upper(); + + switch(d->mimeTypeFilter) + { + case MimeFilter::ImageFiles: + { + TQString imageFilesExt(AlbumSettings::instance()->getImageFileFilter()); + imageFilesExt.append(AlbumSettings::instance()->getRawFileFilter()); + if (!imageFilesExt.upper().contains(mimeType)) + match = false; + break; + } + case MimeFilter::JPGFiles: + { + if (mimeType != TQString("JPG") && mimeType != TQString("JPE") && + mimeType != TQString("JPEG")) + match = false; + break; + } + case MimeFilter::PNGFiles: + { + if (mimeType != TQString("PNG")) + match = false; + break; + } + case MimeFilter::TIFFiles: + { + if (mimeType != TQString("TIF") && mimeType != TQString("TIFF")) + match = false; + break; + } + case MimeFilter::NoRAWFiles: + { + TQString rawFilesExt(AlbumSettings::instance()->getRawFileFilter()); + if (rawFilesExt.upper().contains(mimeType)) + match = false; + break; + } + case MimeFilter::RAWFiles: + { + TQString rawFilesExt(AlbumSettings::instance()->getRawFileFilter()); + if (!rawFilesExt.upper().contains(mimeType)) + match = false; + break; + } + case MimeFilter::MoviesFiles: + { + TQString moviesFilesExt(AlbumSettings::instance()->getMovieFileFilter()); + if (!moviesFilesExt.upper().contains(mimeType)) + match = false; + break; + } + case MimeFilter::AudioFiles: + { + TQString audioFilesExt(AlbumSettings::instance()->getAudioFileFilter()); + if (!audioFilesExt.upper().contains(mimeType)) + match = false; + break; + } + default: // All Files: do nothing... + break; + } + + //-- Filter by text ----------------------------------------------------------- + + if (!d->textFilter.isEmpty()) + { + foundText = false; + if (info->name().lower().contains(d->textFilter.lower())) + { + foundText = true; + } + if (info->caption().lower().contains(d->textFilter.lower())) + foundText = true; + TQStringList tags = info->tagNames(); + for (TQStringList::const_iterator it = tags.constBegin() ; it != tags.constEnd() ; ++it) + { + if ((*it).lower().contains(d->textFilter.lower())) + foundText = true; + } + // check for folder names + PAlbum* palbum = AlbumManager::instance()->findPAlbum(info->albumID()); + if ((palbum && palbum->title().lower().contains(d->textFilter.lower()))) + { + foundText = true; + } + match &= foundText; + } + + return match; +} + +void AlbumLister::stop() +{ + d->currAlbum = 0; + d->filterTimer->stop(); + emit signalClear(); + + d->itemList.clear(); + d->itemMap.clear(); + + if (d->job) + { + d->job->kill(); + d->job = 0; + } +} + +void AlbumLister::setNamesFilter(const TQString& namesFilter) +{ + d->namesFilter = namesFilter; +} + +void AlbumLister::invalidateItem(const ImageInfo *item) +{ + d->invalidatedItems.insert(item->id(), item->id()); +} + +void AlbumLister::slotFilterItems() +{ + if (d->job) + { + d->filterTimer->start(100, true); + return; + } + + TQPtrList newFilteredItemsList; + TQPtrList deleteFilteredItemsList; + ImageInfo *item = 0; + bool matchForText = false; + bool match = false; + + for (ImageInfoListIterator it(d->itemList); + (item = it.current()); ++it) + { + bool foundText = false; + if (matchesFilter(item, foundText)) + { + match = true; + if (!item->getViewItem()) + newFilteredItemsList.append(item); + } + else + { + if (item->getViewItem()) + deleteFilteredItemsList.append(item); + } + + if (foundText) + matchForText = true; + } + + // This takes linear time - and deleting seems to take longer. Set wait cursor for large numbers. + bool setCursor = (3*deleteFilteredItemsList.count() + newFilteredItemsList.count()) > 1500; + if (setCursor) + kapp->setOverrideCursor(KCursor::waitCursor()); + + emit signalItemsTextFilterMatch(matchForText); + emit signalItemsFilterMatch(match); + + if (!deleteFilteredItemsList.isEmpty()) + { + for (ImageInfo *info=deleteFilteredItemsList.first(); info; info = deleteFilteredItemsList.next()) + emit signalDeleteFilteredItem(info); + } + if (!newFilteredItemsList.isEmpty()) + { + emit signalNewFilteredItems(newFilteredItemsList); + } + + if (setCursor) + kapp->restoreOverrideCursor(); +} + +void AlbumLister::slotResult(TDEIO::Job* job) +{ + d->job = 0; + + if (job->error()) + { + DWarning() << "Failed to list url: " << job->errorString() << endl; + d->itemMap.clear(); + d->invalidatedItems.clear(); + return; + } + + typedef TQMap ImMap; + + for (ImMap::iterator it = d->itemMap.begin(); + it != d->itemMap.end(); ++it) + { + emit signalDeleteItem(it.data()); + emit signalDeleteFilteredItem(it.data()); + d->itemList.remove(it.data()); + } + + d->itemMap.clear(); + d->invalidatedItems.clear(); + + emit signalCompleted(); +} + +void AlbumLister::slotData(TDEIO::Job*, const TQByteArray& data) +{ + if (data.isEmpty()) + return; + + TQ_LLONG imageID; + int albumID; + TQString name; + TQString date; + size_t size; + TQSize dims; + + ImageInfoList newItemsList; + ImageInfoList newFilteredItemsList; + + TQDataStream ds(data, IO_ReadOnly); + + while (!ds.atEnd()) + { + bool foundText = false; + + ds >> imageID; + ds >> albumID; + ds >> name; + ds >> date; + ds >> size; + ds >> dims; + + if (d->itemMap.contains(imageID)) + { + ImageInfo* info = d->itemMap[imageID]; + d->itemMap.remove(imageID); + + if (d->invalidatedItems.contains(imageID)) + { + emit signalDeleteItem(info); + emit signalDeleteFilteredItem(info); + d->itemList.remove(info); + } + else + { + if (!matchesFilter(info, foundText)) + { + emit signalDeleteFilteredItem(info); + } + continue; + } + } + + ImageInfo* info = new ImageInfo(imageID, albumID, name, + TQDateTime::fromString(date, TQt::ISODate), + size, dims); + + if (matchesFilter(info, foundText)) + newFilteredItemsList.append(info); + + newItemsList.append(info); + d->itemList.append(info); + } + + if (!newFilteredItemsList.isEmpty()) + emit signalNewFilteredItems(newFilteredItemsList); + + if (!newItemsList.isEmpty()) + emit signalNewItems(newItemsList); + + slotFilterItems(); +} + +} // namespace Digikam diff --git a/src/digikam/albumlister.h b/src/digikam/albumlister.h new file mode 100644 index 00000000..7c14e4c1 --- /dev/null +++ b/src/digikam/albumlister.h @@ -0,0 +1,159 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-26 + * Description : Albums lister. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2007-2009 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMLISTER_H +#define ALBUMLISTER_H + +/** @file albumlister.h */ + +// TQt includes. + +#include +#include + +// Local includes. + +#include "imageinfo.h" + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class Album; +class AlbumListerPriv; + +/** + * Manages imageinfo + * + * does listing of imageinfo for the current album and controls the lifetime + * of the imageinfo. tdeioslaves are used for listing the imageinfo + * corresponding to an album. Similar to the albummanager, frontend entities need + * to connect to the AlbumLister for notifications of new Images, deletion of + * Images or refreshing of currently listed Image. + */ +class AlbumLister : public TQObject +{ + TQ_OBJECT + +public: + + /** @enum MatchingCondition + * Possible logical matching condition used to sort tags id. + */ + enum MatchingCondition + { + OrCondition = 0, + AndCondition + }; + + /** @enum RatingCondition + * Possible conditions used to filter rating: >=, =, <=. + */ + enum RatingCondition + { + GreaterEqualCondition = 0, + EqualCondition, + LessEqualCondition + }; + +public: + + static AlbumLister* instance(); + + ~AlbumLister(); + + /** + * Opens an album to lists its items + */ + void openAlbum(Album *album); + void stop(); + + /** + * Reread an albums item list + */ + void refresh(); + + void setNamesFilter(const TQString& namesFilter); + + void setDayFilter(const TQValueList& days); + + void setTagFilter(const TQValueList& tags, const MatchingCondition& matchingCond, + bool showUnTagged=false); + + void setRatingFilter(int rating, const RatingCondition& ratingCond); + + void setMimeTypeFilter(int mimeTypeFilter); + + void setTextFilter(const TQString& text); + + void setRecurseAlbums(bool recursive); + void setRecurseTags(bool recursive); + + /** + * Trigger a recreation of the given ImageInfo object + * for the next refresh. + */ + void invalidateItem(const ImageInfo *item); + + bool tagFiltersIsActive(); + +signals: + + void signalNewItems(const ImageInfoList& items); + void signalDeleteItem(ImageInfo* item); + void signalNewFilteredItems(const ImageInfoList& items); + void signalDeleteFilteredItem(ImageInfo* item); + void signalClear(); + void signalCompleted(); + void signalItemsTextFilterMatch(bool); + void signalItemsFilterMatch(bool); + +private slots: + + void slotFilterItems(); + + void slotResult(TDEIO::Job* job); + void slotData(TDEIO::Job* job, const TQByteArray& data); + +private: + + AlbumLister(); + bool matchesFilter(const ImageInfo* info, bool& foundText); + +private: + + AlbumListerPriv *d; + + static AlbumLister *m_instance; + +}; + +} // namespace Digikam + +#endif /* ALBUMLISTER_H */ diff --git a/src/digikam/albummanager.cpp b/src/digikam/albummanager.cpp new file mode 100644 index 00000000..847ce99e --- /dev/null +++ b/src/digikam/albummanager.cpp @@ -0,0 +1,1678 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-15 + * Description : Albums manager interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#include + +// C Ansi includes. + +extern "C" +{ +#include +#include +#include +} + +// C++ includes. + +#include +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumdb.h" +#include "albumitemhandler.h" +#include "dio.h" +#include "albumsettings.h" +#include "scanlib.h" +#include "splashscreen.h" +#include "upgradedb_sqlite2tosqlite3.h" +#include "albummanager.h" +#include "albummanager.moc" + +namespace Digikam +{ + +typedef TQDict PAlbumDict; +typedef TQIntDict AlbumIntDict; +typedef TQValueList DDateList; + +class AlbumManagerPriv +{ + +public: + + AlbumManagerPriv() + { + db = 0; + dateListJob = 0; + albumListJob = 0; + tagListJob = 0; + rootPAlbum = 0; + rootTAlbum = 0; + rootDAlbum = 0; + rootSAlbum = 0; + itemHandler = 0; + currentAlbum = 0; + dirWatch = 0; + changed = false; + } + + bool changed; + + TQString libraryPath; + TQStringList dirtyAlbums; + + DDateList dbPathModificationDateList; + + KDirWatch *dirWatch; + + TDEIO::TransferJob *albumListJob; + TDEIO::TransferJob *dateListJob; + TDEIO::TransferJob *tagListJob; + + PAlbum *rootPAlbum; + TAlbum *rootTAlbum; + DAlbum *rootDAlbum; + SAlbum *rootSAlbum; + + PAlbumDict pAlbumDict; + AlbumIntDict albumIntDict; + + Album *currentAlbum; + AlbumDB *db; + AlbumItemHandler *itemHandler; + + TQValueList buildDirectoryModList(const TQFileInfo &dbFile) + { + // retrieve modification dates of all files in the database-file dir + TQValueList modList; + const TQFileInfoList *fileInfoList = dbFile.dir().entryInfoList(TQDir::Files | TQDir::Dirs ); + + // build list + TQFileInfoListIterator it(*fileInfoList); + TQFileInfo *fi; + + while ( (fi = it.current()) != 0 ) + { + if ( fi->fileName() != dbFile.fileName()) + { + modList << fi->lastModified(); + } + ++it; + } + + return modList; + } + +}; + +AlbumManager* AlbumManager::m_instance = 0; + +AlbumManager* AlbumManager::instance() +{ + return m_instance; +} + +AlbumManager::AlbumManager() +{ + m_instance = this; + + d = new AlbumManagerPriv; + d->db = new AlbumDB; +} + +AlbumManager::~AlbumManager() +{ + if (d->dateListJob) + { + d->dateListJob->kill(); + d->dateListJob = 0; + } + + if (d->albumListJob) + { + d->albumListJob->kill(); + d->albumListJob = 0; + } + + if (d->tagListJob) + { + d->tagListJob->kill(); + d->tagListJob = 0; + } + + delete d->rootPAlbum; + delete d->rootTAlbum; + delete d->rootDAlbum; + delete d->rootSAlbum; + + delete d->dirWatch; + + delete d->db; + delete d; + + m_instance = 0; +} + +AlbumDB* AlbumManager::albumDB() +{ + return d->db; +} + +void AlbumManager::setLibraryPath(const TQString& path, SplashScreen *splash) +{ + TQString cleanPath = TQDir::cleanDirPath(path); + + if (cleanPath == d->libraryPath) + return; + + d->changed = true; + + if (d->dateListJob) + { + d->dateListJob->kill(); + d->dateListJob = 0; + } + + if (d->albumListJob) + { + d->albumListJob->kill(); + d->albumListJob = 0; + } + + if (d->tagListJob) + { + d->tagListJob->kill(); + d->tagListJob = 0; + } + + delete d->dirWatch; + d->dirWatch = 0; + d->dirtyAlbums.clear(); + + d->currentAlbum = 0; + emit signalAlbumCurrentChanged(0); + emit signalAlbumsCleared(); + + d->pAlbumDict.clear(); + d->albumIntDict.clear(); + + delete d->rootPAlbum; + delete d->rootTAlbum; + delete d->rootDAlbum; + + d->rootPAlbum = 0; + d->rootTAlbum = 0; + d->rootDAlbum = 0; + d->rootSAlbum = 0; + + d->libraryPath = cleanPath; + + TQString dbPath = cleanPath + "/digikam3.db"; + +#ifdef NFS_HACK + dbPath = locateLocal("appdata", TDEIO::encodeFileName(TQDir::cleanDirPath(dbPath))); +#endif + + d->db->setDBPath(dbPath); + + // -- Locale Checking --------------------------------------------------------- + + TQString currLocale(TQTextCodec::codecForLocale()->name()); + TQString dbLocale = d->db->getSetting("Locale"); + + // guilty until proven innocent + bool localeChanged = true; + + if (dbLocale.isNull()) + { + DDebug() << "No locale found in database" << endl; + + // Copy an existing locale from the settings file (used < 0.8) + // to the database. + TDEConfig* config = TDEGlobal::config(); + config->setGroup("General Settings"); + if (config->hasKey("Locale")) + { + DDebug() << "Locale found in configfile" << endl; + dbLocale = config->readEntry("Locale"); + + // this hack is necessary, as we used to store the entire + // locale info LC_ALL (for eg: en_US.UTF-8) earlier, + // we now save only the encoding (UTF-8) + + TQString oldConfigLocale = ::setlocale(0, 0); + + if (oldConfigLocale == dbLocale) + { + dbLocale = currLocale; + localeChanged = false; + d->db->setSetting("Locale", dbLocale); + } + } + else + { + DDebug() << "No locale found in config file" << endl; + dbLocale = currLocale; + + localeChanged = false; + d->db->setSetting("Locale",dbLocale); + } + } + else + { + if (dbLocale == currLocale) + localeChanged = false; + } + + if (localeChanged) + { + // TODO it would be better to replace all yes/no confirmation dialogs with ones that has custom + // buttons that denote the actions directly, i.e.: ["Ignore and Continue"] ["Adjust locale"] + int result = + KMessageBox::warningYesNo(0, + i18n("Your locale has changed since this album " + "was last opened.\n" + "Old Locale : %1, New Locale : %2\n" + "This can cause unexpected problems. " + "If you are sure that you want to " + "continue, click 'Yes' to work with this album. " + "Otherwise, click 'No' and correct your " + "locale setting before restarting digiKam") + .arg(dbLocale) + .arg(currLocale)); + if (result != KMessageBox::Yes) + exit(0); + + d->db->setSetting("Locale",currLocale); + } + + // -- Check if we need to upgrade 0.7.x db to 0.8 db --------------------- + + if (!upgradeDB_Sqlite2ToSqlite3(d->libraryPath)) + { + KMessageBox::error(0, i18n("Failed to update the old Database to the new Database format\n" + "This error can happen if the Album Path '%1' does not exist or is write-protected.\n" + "If you have moved your photo collection, you need to adjust the 'Album Path' in digikam's configuration file.") + .arg(d->libraryPath)); + exit(0); + } + + // set an initial modification list to filter out KDirWatch signals + // caused by database operations + TQFileInfo dbFile(dbPath); + d->dbPathModificationDateList = d->buildDirectoryModList(dbFile); + + // -- Check if we need to do scanning ------------------------------------- + + TDEConfig* config = TDEGlobal::config(); + config->setGroup("General Settings"); + if (config->readBoolEntry("Scan At Start", true) || + d->db->getSetting("Scanned").isEmpty()) + { + ScanLib sLib(splash); + sLib.startScan(); + } +} + +TQString AlbumManager::getLibraryPath() const +{ + return d->libraryPath; +} + +void AlbumManager::startScan() +{ + if (!d->changed) + return; + d->changed = false; + + d->dirWatch = new KDirWatch(this); + connect(d->dirWatch, TQ_SIGNAL(dirty(const TQString&)), + this, TQ_SLOT(slotDirty(const TQString&))); + + KDirWatch::Method m = d->dirWatch->internalMethod(); + TQString mName("FAM"); + if (m == KDirWatch::DNotify) + mName = TQString("DNotify"); + else if (m == KDirWatch::Stat) + mName = TQString("Stat"); + else if (m == KDirWatch::INotify) + mName = TQString("INotify"); + DDebug() << "KDirWatch method = " << mName << endl; + + d->dirWatch->addDir(d->libraryPath); + + d->rootPAlbum = new PAlbum(i18n("My Albums"), 0, true); + insertPAlbum(d->rootPAlbum); + + d->rootTAlbum = new TAlbum(i18n("My Tags"), 0, true); + insertTAlbum(d->rootTAlbum); + + d->rootSAlbum = new SAlbum(0, KURL(), true, true); + + d->rootDAlbum = new DAlbum(TQDate(), true); + + refresh(); + + emit signalAllAlbumsLoaded(); +} + +void AlbumManager::refresh() +{ + scanPAlbums(); + scanTAlbums(); + scanSAlbums(); + scanDAlbums(); + + if (!d->dirtyAlbums.empty()) + { + KURL u; + u.setProtocol("digikamalbums"); + u.setPath(d->dirtyAlbums.first()); + d->dirtyAlbums.pop_front(); + + DIO::scan(u); + } +} + +void AlbumManager::scanPAlbums() +{ + // first insert all the current PAlbums into a map for quick lookup + typedef TQMap AlbumMap; + AlbumMap aMap; + + AlbumIterator it(d->rootPAlbum); + while (it.current()) + { + PAlbum* a = (PAlbum*)(*it); + aMap.insert(a->url(), a); + ++it; + } + + // scan db and get a list of all albums + AlbumInfo::List aList = d->db->scanAlbums(); + qHeapSort(aList); + + AlbumInfo::List newAlbumList; + + // go through all the Albums and see which ones are already present + for (AlbumInfo::List::iterator it = aList.begin(); it != aList.end(); ++it) + { + AlbumInfo info = *it; + info.url = TQDir::cleanDirPath(info.url); + + if (!aMap.contains(info.url)) + { + newAlbumList.append(info); + } + else + { + aMap.remove(info.url); + } + } + + // now aMap contains all the deleted albums and + // newAlbumList contains all the new albums + + // first inform all frontends of the deleted albums + for (AlbumMap::iterator it = aMap.begin(); it != aMap.end(); ++it) + { + // the albums have to be removed with children being removed first. + // removePAlbum takes care of that. + // So never delete the PAlbum using it.data(). instead check if the + // PAlbum is still in the Album Dict before trying to remove it. + + // this might look like there is memory leak here, since removePAlbum + // doesn't delete albums and looks like child Albums don't get deleted. + // But when the parent album gets deleted, the children are also deleted. + + PAlbum* album = d->pAlbumDict.find(it.key()); + if (!album) + continue; + + removePAlbum(album); + delete album; + } + + qHeapSort(newAlbumList); + for (AlbumInfo::List::iterator it = newAlbumList.begin(); it != newAlbumList.end(); ++it) + { + AlbumInfo info = *it; + if (info.url.isEmpty() || info.url == "/") + continue; + + // Despite its name info.url is a TQString. + // setPath takes care for escaping characters that are valid for files but not for URLs ('#') + KURL u; + u.setPath(info.url); + TQString name = u.fileName(); + // Get its parent + TQString purl = u.upURL().path(-1); + + PAlbum* parent = d->pAlbumDict.find(purl); + if (!parent) + { + DWarning() << k_funcinfo << "Could not find parent with url: " + << purl << " for: " << info.url << endl; + continue; + } + + // Create the new album + PAlbum* album = new PAlbum(name, info.id, false); + album->m_caption = info.caption; + album->m_collection = info.collection; + album->m_date = info.date; + album->m_icon = info.icon; + + album->setParent(parent); + d->dirWatch->addDir(album->folderPath()); + + insertPAlbum(album); + } + + if (!AlbumSettings::instance()->getShowFolderTreeViewItemsCount()) + return; + + // List albums using tdeioslave + + if (d->albumListJob) + { + d->albumListJob->kill(); + d->albumListJob = 0; + } + + KURL u; + u.setProtocol("digikamalbums"); + u.setPath("/"); + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << d->libraryPath; + ds << KURL(); + ds << AlbumSettings::instance()->getAllFileFilter(); + ds << 0; // getting dimensions (not needed here) + ds << 0; // recursive sub-album (not needed here) + ds << 0; // recursive sub-tags (not needed here) + + d->albumListJob = new TDEIO::TransferJob(u, TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + d->albumListJob->addMetaData("folders", "yes"); + + connect(d->albumListJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotAlbumsJobResult(TDEIO::Job*))); + + connect(d->albumListJob, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotAlbumsJobData(TDEIO::Job*, const TQByteArray&))); +} + +void AlbumManager::scanTAlbums() +{ + // list TAlbums directly from the db + // first insert all the current TAlbums into a map for quick lookup + typedef TQMap TagMap; + TagMap tmap; + + tmap.insert(0, d->rootTAlbum); + + AlbumIterator it(d->rootTAlbum); + while (it.current()) + { + TAlbum* t = (TAlbum*)(*it); + tmap.insert(t->id(), t); + ++it; + } + + // Retrieve the list of tags from the database + TagInfo::List tList = d->db->scanTags(); + + // sort the list. needed because we want the tags can be read in any order, + // but we want to make sure that we are ensure to find the parent TAlbum + // for a new TAlbum + + { + TQIntDict tagDict; + tagDict.setAutoDelete(false); + + // insert items into a dict for quick lookup + for (TagInfo::List::iterator it = tList.begin(); it != tList.end(); ++it) + { + TagInfo info = *it; + TAlbum* album = new TAlbum(info.name, info.id); + album->m_icon = info.icon; + album->m_pid = info.pid; + tagDict.insert(info.id, album); + } + tList.clear(); + + // also add root tag + TAlbum* rootTag = new TAlbum("root", 0, true); + tagDict.insert(0, rootTag); + + // build tree + TQIntDictIterator iter(tagDict); + for ( ; iter.current(); ++iter ) + { + TAlbum* album = iter.current(); + if (album->m_id == 0) + continue; + + TAlbum* parent = tagDict.find(album->m_pid); + if (parent) + { + album->setParent(parent); + } + else + { + DWarning() << "Failed to find parent tag for tag " + << iter.current()->m_title + << " with pid " + << iter.current()->m_pid << endl; + } + } + + // now insert the items into the list. becomes sorted + AlbumIterator it(rootTag); + while (it.current()) + { + TAlbum* album = (TAlbum*)it.current(); + TagInfo info; + info.id = album->m_id; + info.pid = album->m_pid; + info.name = album->m_title; + info.icon = album->m_icon; + tList.append(info); + ++it; + } + + // this will also delete all child albums + delete rootTag; + } + + for (TagInfo::List::iterator it = tList.begin(); it != tList.end(); ++it) + { + TagInfo info = *it; + + // check if we have already added this tag + if (tmap.contains(info.id)) + continue; + + // Its a new album. Find the parent of the album + TagMap::iterator iter = tmap.find(info.pid); + if (iter == tmap.end()) + { + DWarning() << "Failed to find parent tag for tag " + << info.name + << " with pid " + << info.pid << endl; + continue; + } + + TAlbum* parent = iter.data(); + + // Create the new TAlbum + TAlbum* album = new TAlbum(info.name, info.id, false); + album->m_icon = info.icon; + album->setParent(parent); + insertTAlbum(album); + + // also insert it in the map we are doing lookup of parent tags + tmap.insert(info.id, album); + } + + if (!AlbumSettings::instance()->getShowFolderTreeViewItemsCount()) + return; + + // List tags using tdeioslave + + if (d->tagListJob) + { + d->tagListJob->kill(); + d->tagListJob = 0; + } + + KURL u; + u.setProtocol("digikamtags"); + u.setPath("/"); + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << d->libraryPath; + ds << KURL(); + ds << AlbumSettings::instance()->getAllFileFilter(); + ds << 0; // getting dimensions (not needed here) + ds << 0; // recursive sub-album (not needed here) + ds << 0; // recursive sub-tags (not needed here) + + d->tagListJob = new TDEIO::TransferJob(u, TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + d->tagListJob->addMetaData("folders", "yes"); + + connect(d->tagListJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotTagsJobResult(TDEIO::Job*))); + + connect(d->tagListJob, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotTagsJobData(TDEIO::Job*, const TQByteArray&))); +} + +void AlbumManager::scanSAlbums() +{ + // list SAlbums directly from the db + // first insert all the current SAlbums into a map for quick lookup + typedef TQMap SearchMap; + SearchMap sMap; + + AlbumIterator it(d->rootSAlbum); + while (it.current()) + { + SAlbum* t = (SAlbum*)(*it); + sMap.insert(t->id(), t); + ++it; + } + + // Retrieve the list of searches from the database + SearchInfo::List sList = d->db->scanSearches(); + + for (SearchInfo::List::iterator it = sList.begin(); it != sList.end(); ++it) + { + SearchInfo info = *it; + + // check if we have already added this search + if (sMap.contains(info.id)) + continue; + + bool simple = (info.url.queryItem("1.key") == TQString::fromLatin1("keyword")); + + // Its a new album. + SAlbum* album = new SAlbum(info.id, info.url, simple, false); + album->setParent(d->rootSAlbum); + d->albumIntDict.insert(album->globalID(), album); + emit signalAlbumAdded(album); + } +} + +void AlbumManager::scanDAlbums() +{ + // List dates using tdeioslave + + if (d->dateListJob) + { + d->dateListJob->kill(); + d->dateListJob = 0; + } + + KURL u; + u.setProtocol("digikamdates"); + u.setPath("/"); + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << d->libraryPath; + ds << KURL(); + ds << AlbumSettings::instance()->getAllFileFilter(); + ds << 0; // getting dimensions (not needed here) + ds << 0; // recursive sub-album (not needed here) + ds << 0; // recursive sub-tags (not needed here) + + d->dateListJob = new TDEIO::TransferJob(u, TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + d->dateListJob->addMetaData("folders", "yes"); + + connect(d->dateListJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDatesJobResult(TDEIO::Job*))); + + connect(d->dateListJob, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotDatesJobData(TDEIO::Job*, const TQByteArray&))); +} + +AlbumList AlbumManager::allPAlbums() const +{ + AlbumList list; + if (d->rootPAlbum) + list.append(d->rootPAlbum); + + AlbumIterator it(d->rootPAlbum); + while (it.current()) + { + list.append(*it); + ++it; + } + + return list; +} + +AlbumList AlbumManager::allTAlbums() const +{ + AlbumList list; + if (d->rootTAlbum) + list.append(d->rootTAlbum); + + AlbumIterator it(d->rootTAlbum); + while (it.current()) + { + list.append(*it); + ++it; + } + + return list; +} + +AlbumList AlbumManager::allSAlbums() const +{ + AlbumList list; + if (d->rootSAlbum) + list.append(d->rootSAlbum); + + AlbumIterator it(d->rootSAlbum); + while (it.current()) + { + list.append(*it); + ++it; + } + + return list; +} + +AlbumList AlbumManager::allDAlbums() const +{ + AlbumList list; + if (d->rootDAlbum) + list.append(d->rootDAlbum); + + AlbumIterator it(d->rootDAlbum); + while (it.current()) + { + list.append(*it); + ++it; + } + + return list; +} + +void AlbumManager::setCurrentAlbum(Album *album) +{ + d->currentAlbum = album; + emit signalAlbumCurrentChanged(album); +} + +Album* AlbumManager::currentAlbum() const +{ + return d->currentAlbum; +} + +PAlbum* AlbumManager::findPAlbum(const KURL& url) const +{ + TQString path = url.path(); + path.remove(d->libraryPath); + path = TQDir::cleanDirPath(path); + + return d->pAlbumDict.find(path); +} + +PAlbum* AlbumManager::findPAlbum(int id) const +{ + if (!d->rootPAlbum) + return 0; + + int gid = d->rootPAlbum->globalID() + id; + + return (PAlbum*)(d->albumIntDict.find(gid)); +} + +TAlbum* AlbumManager::findTAlbum(int id) const +{ + if (!d->rootTAlbum) + return 0; + + int gid = d->rootTAlbum->globalID() + id; + + return (TAlbum*)(d->albumIntDict.find(gid)); +} + +SAlbum* AlbumManager::findSAlbum(int id) const +{ + if (!d->rootTAlbum) + return 0; + + int gid = d->rootSAlbum->globalID() + id; + + return (SAlbum*)(d->albumIntDict.find(gid)); +} + +DAlbum* AlbumManager::findDAlbum(int id) const +{ + if (!d->rootDAlbum) + return 0; + + int gid = d->rootDAlbum->globalID() + id; + + return (DAlbum*)(d->albumIntDict.find(gid)); +} + +Album* AlbumManager::findAlbum(int gid) const +{ + return d->albumIntDict.find(gid); +} + +TAlbum* AlbumManager::findTAlbum(const TQString &tagPath) const +{ + // handle gracefully with or without leading slash + bool withLeadingSlash = tagPath.startsWith("/"); + AlbumIterator it(d->rootTAlbum); + while (it.current()) + { + TAlbum *talbum = static_cast(*it); + if (talbum->tagPath(withLeadingSlash) == tagPath) + return talbum; + ++it; + } + return 0; + +} + + +PAlbum* AlbumManager::createPAlbum(PAlbum* parent, + const TQString& name, + const TQString& caption, + const TQDate& date, + const TQString& collection, + TQString& errMsg) +{ + if (!parent) + { + errMsg = i18n("No parent found for album."); + return 0; + } + + // sanity checks + if (name.isEmpty()) + { + errMsg = i18n("Album name cannot be empty."); + return 0; + } + + if (name.contains("/")) + { + errMsg = i18n("Album name cannot contain '/'."); + return 0; + } + + // first check if we have another album with the same name + Album *child = parent->m_firstChild; + while (child) + { + if (child->title() == name) + { + errMsg = i18n("An existing album has the same name."); + return 0; + } + child = child->m_next; + } + + TQString path = parent->folderPath(); + path += '/' + name; + path = TQDir::cleanDirPath(path); + + // make the directory synchronously, so that we can add the + // album info to the database directly + if (::mkdir(TQFile::encodeName(path), 0777) != 0) + { + if (errno == EEXIST) + errMsg = i18n("Another file or folder with same name exists"); + else if (errno == EACCES) + errMsg = i18n("Access denied to path"); + else if (errno == ENOSPC) + errMsg = i18n("Disk is full"); + else + errMsg = i18n("Unknown error"); // being lazy + + return 0; + } + + // Now insert the album properties into the database + path = path.remove(0, d->libraryPath.length()); + if (!path.startsWith("/")) + path.prepend("/"); + + int id = d->db->addAlbum(path, caption, date, collection); + if (id == -1) + { + errMsg = i18n("Failed to add album to database"); + return 0; + } + + PAlbum *album = new PAlbum(name, id, false); + album->m_caption = caption; + album->m_collection = collection; + album->m_date = date; + + album->setParent(parent); + + d->dirWatch->addDir(album->folderPath()); + + insertPAlbum(album); + + return album; +} + +bool AlbumManager::renamePAlbum(PAlbum* album, const TQString& newName, + TQString& errMsg) +{ + if (!album) + { + errMsg = i18n("No such album"); + return false; + } + + if (album == d->rootPAlbum) + { + errMsg = i18n("Cannot rename root album"); + return false; + } + + if (newName.contains("/")) + { + errMsg = i18n("Album name cannot contain '/'"); + return false; + } + + // first check if we have another sibling with the same name + Album *sibling = album->m_parent->m_firstChild; + while (sibling) + { + if (sibling->title() == newName) + { + errMsg = i18n("Another album with same name exists\n" + "Please choose another name"); + return false; + } + sibling = sibling->m_next; + } + + TQString oldURL = album->url(); + + KURL u = KURL::fromPathOrURL(album->folderPath()).upURL(); + u.addPath(newName); + u.cleanPath(); + + if (::rename(TQFile::encodeName(album->folderPath()), + TQFile::encodeName(u.path(-1))) != 0) + { + errMsg = i18n("Failed to rename Album"); + return false; + } + + // now rename the album and subalbums in the database + + // all we need to do is set the title of the album which is being + // renamed correctly and all the sub albums will automatically get + // their url set correctly + + album->setTitle(newName); + d->db->setAlbumURL(album->id(), album->url()); + + Album* subAlbum = 0; + AlbumIterator it(album); + while ((subAlbum = it.current()) != 0) + { + d->db->setAlbumURL(subAlbum->id(), ((PAlbum*)subAlbum)->url()); + ++it; + } + + // Update AlbumDict. basically clear it and rebuild from scratch + { + d->pAlbumDict.clear(); + d->pAlbumDict.insert(d->rootPAlbum->url(), d->rootPAlbum); + AlbumIterator it(d->rootPAlbum); + PAlbum* subAlbum = 0; + while ((subAlbum = (PAlbum*)it.current()) != 0) + { + d->pAlbumDict.insert(subAlbum->url(), subAlbum); + ++it; + } + } + + emit signalAlbumRenamed(album); + + return true; +} + +bool AlbumManager::updatePAlbumIcon(PAlbum *album, TQ_LLONG iconID, TQString& errMsg) +{ + if (!album) + { + errMsg = i18n("No such album"); + return false; + } + + if (album == d->rootPAlbum) + { + errMsg = i18n("Cannot edit root album"); + return false; + } + + d->db->setAlbumIcon(album->id(), iconID); + album->m_icon = d->db->getAlbumIcon(album->id()); + + emit signalAlbumIconChanged(album); + + return true; +} + +TAlbum* AlbumManager::createTAlbum(TAlbum* parent, const TQString& name, + const TQString& iconkde, TQString& errMsg) +{ + if (!parent) + { + errMsg = i18n("No parent found for tag"); + return 0; + } + + // sanity checks + if (name.isEmpty()) + { + errMsg = i18n("Tag name cannot be empty"); + return 0; + } + + if (name.contains("/")) + { + errMsg = i18n("Tag name cannot contain '/'"); + return 0; + } + + // first check if we have another album with the same name + Album *child = parent->m_firstChild; + while (child) + { + if (child->title() == name) + { + errMsg = i18n("Tag name already exists"); + return 0; + } + child = child->m_next; + } + + int id = d->db->addTag(parent->id(), name, iconkde, 0); + if (id == -1) + { + errMsg = i18n("Failed to add tag to database"); + return 0; + } + + TAlbum *album = new TAlbum(name, id, false); + album->m_icon = iconkde; + album->setParent(parent); + + insertTAlbum(album); + + return album; +} + +AlbumList AlbumManager::findOrCreateTAlbums(const TQStringList &tagPaths) +{ + IntList tagIDs; + + // find tag ids for tag paths in list, create if they don't exist + tagIDs = d->db->getTagsFromTagPaths(tagPaths); + + // create TAlbum objects for the newly created tags + scanTAlbums(); + + AlbumList resultList; + + for (IntList::iterator it = tagIDs.begin(); it != tagIDs.end(); ++it) + { + resultList.append(findTAlbum(*it)); + } + + return resultList; +} + +bool AlbumManager::deleteTAlbum(TAlbum* album, TQString& errMsg) +{ + if (!album) + { + errMsg = i18n("No such album"); + return false; + } + + if (album == d->rootTAlbum) + { + errMsg = i18n("Cannot delete Root Tag"); + return false; + } + + d->db->deleteTag(album->id()); + + Album* subAlbum = 0; + AlbumIterator it(album); + while ((subAlbum = it.current()) != 0) + { + d->db->deleteTag(subAlbum->id()); + ++it; + } + + removeTAlbum(album); + + d->albumIntDict.remove(album->globalID()); + delete album; + + return true; +} + +bool AlbumManager::renameTAlbum(TAlbum* album, const TQString& name, + TQString& errMsg) +{ + if (!album) + { + errMsg = i18n("No such album"); + return false; + } + + if (album == d->rootTAlbum) + { + errMsg = i18n("Cannot edit root tag"); + return false; + } + + if (name.contains("/")) + { + errMsg = i18n("Tag name cannot contain '/'"); + return false; + } + + // first check if we have another sibling with the same name + Album *sibling = album->m_parent->m_firstChild; + while (sibling) + { + if (sibling->title() == name) + { + errMsg = i18n("Another tag with same name exists\n" + "Please choose another name"); + return false; + } + sibling = sibling->m_next; + } + + d->db->setTagName(album->id(), name); + album->setTitle(name); + emit signalAlbumRenamed(album); + + return true; +} + +bool AlbumManager::moveTAlbum(TAlbum* album, TAlbum *newParent, TQString &errMsg) +{ + if (!album) + { + errMsg = i18n("No such album"); + return false; + } + + if (album == d->rootTAlbum) + { + errMsg = i18n("Cannot move root tag"); + return false; + } + + d->db->setTagParentID(album->id(), newParent->id()); + album->parent()->removeChild(album); + album->setParent(newParent); + + emit signalTAlbumMoved(album, newParent); + + return true; +} + +bool AlbumManager::updateTAlbumIcon(TAlbum* album, const TQString& iconKDE, + TQ_LLONG iconID, TQString& errMsg) +{ + if (!album) + { + errMsg = i18n("No such tag"); + return false; + } + + if (album == d->rootTAlbum) + { + errMsg = i18n("Cannot edit root tag"); + return false; + } + + d->db->setTagIcon(album->id(), iconKDE, iconID); + album->m_icon = d->db->getTagIcon(album->id()); + + emit signalAlbumIconChanged(album); + + return true; +} + +SAlbum* AlbumManager::createSAlbum(const KURL& url, bool simple) +{ + TQString name = url.queryItem("name"); + + // first iterate through all the search albums and see if there's an existing + // SAlbum with same name. (Remember, SAlbums are arranged in a flat list) + for (Album* album = d->rootSAlbum->firstChild(); album; album = album->next()) + { + if (album->title() == name) + { + SAlbum* sa = (SAlbum*)album; + sa->m_kurl = url; + d->db->updateSearch(sa->id(), url.queryItem("name"), url); + return sa; + } + } + + int id = d->db->addSearch(url.queryItem("name"), url); + if (id == -1) + return 0; + + SAlbum* album = new SAlbum(id, url, simple, false); + album->setTitle(url.queryItem("name")); + album->setParent(d->rootSAlbum); + + d->albumIntDict.insert(album->globalID(), album); + emit signalAlbumAdded(album); + + return album; +} + +bool AlbumManager::updateSAlbum(SAlbum* album, const KURL& newURL) +{ + if (!album) + return false; + + d->db->updateSearch(album->id(), newURL.queryItem("name"), newURL); + + TQString oldName = album->title(); + + album->m_kurl = newURL; + album->setTitle(newURL.queryItem("name")); + if (oldName != album->title()) + emit signalAlbumRenamed(album); + + return true; +} + +bool AlbumManager::deleteSAlbum(SAlbum* album) +{ + if (!album) + return false; + + emit signalAlbumDeleted(album); + + d->db->deleteSearch(album->id()); + + d->albumIntDict.remove(album->globalID()); + delete album; + + return true; +} + +void AlbumManager::insertPAlbum(PAlbum *album) +{ + if (!album) + return; + + d->pAlbumDict.insert(album->url(), album); + d->albumIntDict.insert(album->globalID(), album); + + emit signalAlbumAdded(album); +} + +void AlbumManager::removePAlbum(PAlbum *album) +{ + if (!album) + return; + + // remove all children of this album + Album* child = album->m_firstChild; + while (child) + { + Album *next = child->m_next; + removePAlbum((PAlbum*)child); + child = next; + } + + d->pAlbumDict.remove(album->url()); + d->albumIntDict.remove(album->globalID()); + + d->dirtyAlbums.remove(album->url()); + d->dirWatch->removeDir(album->folderPath()); + + if (album == d->currentAlbum) + { + d->currentAlbum = 0; + emit signalAlbumCurrentChanged(0); + } + + emit signalAlbumDeleted(album); +} + +void AlbumManager::insertTAlbum(TAlbum *album) +{ + if (!album) + return; + + d->albumIntDict.insert(album->globalID(), album); + + emit signalAlbumAdded(album); +} + +void AlbumManager::removeTAlbum(TAlbum *album) +{ + if (!album) + return; + + // remove all children of this album + Album* child = album->m_firstChild; + while (child) + { + Album *next = child->m_next; + removeTAlbum((TAlbum*)child); + child = next; + } + + d->albumIntDict.remove(album->globalID()); + + if (album == d->currentAlbum) + { + d->currentAlbum = 0; + emit signalAlbumCurrentChanged(0); + } + + emit signalAlbumDeleted(album); +} + +void AlbumManager::emitAlbumItemsSelected(bool val) +{ + emit signalAlbumItemsSelected(val); +} + +void AlbumManager::setItemHandler(AlbumItemHandler *handler) +{ + d->itemHandler = handler; +} + +AlbumItemHandler* AlbumManager::getItemHandler() +{ + return d->itemHandler; +} + +void AlbumManager::refreshItemHandler(const KURL::List& itemList) +{ + if (itemList.empty()) + d->itemHandler->refresh(); + else + d->itemHandler->refreshItems(itemList); +} + +void AlbumManager::slotAlbumsJobResult(TDEIO::Job* job) +{ + d->albumListJob = 0; + + if (job->error()) + { + DWarning() << k_funcinfo << "Failed to list albums" << endl; + return; + } +} + +void AlbumManager::slotAlbumsJobData(TDEIO::Job*, const TQByteArray& data) +{ + if (data.isEmpty()) + return; + + TQMap albumsStatMap; + TQDataStream ds(data, IO_ReadOnly); + ds >> albumsStatMap; + + emit signalPAlbumsDirty(albumsStatMap); +} + +void AlbumManager::slotTagsJobResult(TDEIO::Job* job) +{ + d->tagListJob = 0; + + if (job->error()) + { + DWarning() << k_funcinfo << "Failed to list tags" << endl; + return; + } +} + +void AlbumManager::slotTagsJobData(TDEIO::Job*, const TQByteArray& data) +{ + if (data.isEmpty()) + return; + + TQMap tagsStatMap; + TQDataStream ds(data, IO_ReadOnly); + ds >> tagsStatMap; + + emit signalTAlbumsDirty(tagsStatMap); +} + +void AlbumManager::slotDatesJobResult(TDEIO::Job* job) +{ + d->dateListJob = 0; + + if (job->error()) + { + DWarning() << k_funcinfo << "Failed to list dates" << endl; + return; + } + + emit signalAllDAlbumsLoaded(); +} + +void AlbumManager::slotDatesJobData(TDEIO::Job*, const TQByteArray& data) +{ + if (data.isEmpty()) + return; + + // insert all the DAlbums into a qmap for quick access + TQMap mAlbumMap; + TQMap yAlbumMap; + + AlbumIterator it(d->rootDAlbum); + while (it.current()) + { + DAlbum* a = (DAlbum*)(*it); + if (a->range() == DAlbum::Month) + mAlbumMap.insert(a->date(), a); + else + yAlbumMap.insert(a->date().year(), a); + ++it; + } + + TQMap datesStatMap; + TQDataStream ds(data, IO_ReadOnly); + ds >> datesStatMap; + + TQMap yearMonthMap; + for ( TQMap::iterator it = datesStatMap.begin(); + it != datesStatMap.end(); ++it ) + { + TQMap::iterator it2 = yearMonthMap.find(YearMonth(it.key().date().year(), it.key().date().month())); + if ( it2 == yearMonthMap.end() ) + { + yearMonthMap.insert( YearMonth(it.key().date().year(), it.key().date().month()), it.data() ); + } + else + { + yearMonthMap.replace( YearMonth(it.key().date().year(), it.key().date().month()), it2.data() + it.data() ); + } + } + + int year, month; + for ( TQMap::iterator it = yearMonthMap.begin(); + it != yearMonthMap.end(); ++it ) + { + year = it.key().first; + month = it.key().second; + + TQDate md(year, month, 1); + + // Do we already have this Month album + if (mAlbumMap.contains(md)) + { + // already there. remove Month album from map + mAlbumMap.remove(md); + + if (yAlbumMap.contains(year)) + { + // already there. remove from map + yAlbumMap.remove(year); + } + + continue; + } + + // Check if Year Album already exist. + DAlbum *yAlbum = 0; + AlbumIterator it2(d->rootDAlbum); + while (it2.current()) + { + DAlbum* a = (DAlbum*)(*it2); + if (a->date() == TQDate(year, 1, 1) && a->range() == DAlbum::Year) + { + yAlbum = a; + break; + } + ++it2; + } + + // If no, create Year album. + if (!yAlbum) + { + yAlbum = new DAlbum(TQDate(year, 1, 1), false, DAlbum::Year); + yAlbum->setParent(d->rootDAlbum); + d->albumIntDict.insert(yAlbum->globalID(), yAlbum); + emit signalAlbumAdded(yAlbum); + } + + // Create Month album + DAlbum *mAlbum = new DAlbum(md); + mAlbum->setParent(yAlbum); + d->albumIntDict.insert(mAlbum->globalID(), mAlbum); + emit signalAlbumAdded(mAlbum); + } + + // Now the items contained in the maps are the ones which + // have been deleted. + for (TQMap::iterator it = mAlbumMap.begin(); + it != mAlbumMap.end(); ++it) + { + DAlbum* album = it.data(); + emit signalAlbumDeleted(album); + d->albumIntDict.remove(album->globalID()); + delete album; + } + + for (TQMap::iterator it = yAlbumMap.begin(); + it != yAlbumMap.end(); ++it) + { + DAlbum* album = it.data(); + emit signalAlbumDeleted(album); + d->albumIntDict.remove(album->globalID()); + delete album; + } + + emit signalDAlbumsDirty(yearMonthMap); + emit signalDatesMapDirty(datesStatMap); +} + +void AlbumManager::slotDirty(const TQString& path) +{ + DDebug() << "Noticed file change in directory " << path << endl; + TQString url = TQDir::cleanDirPath(path); + url = TQDir::cleanDirPath(url.remove(d->libraryPath)); + + if (url.isEmpty()) + url = "/"; + + if (d->dirtyAlbums.contains(url)) + return; + + // is the signal for the directory containing the database file? + if (url == "/") + { + // retrieve modification dates + TQFileInfo dbFile(d->libraryPath); + TQValueList modList = d->buildDirectoryModList(dbFile); + + // check for equality + if (modList == d->dbPathModificationDateList) + { + DDebug() << "Filtering out db-file-triggered dir watch signal" << endl; + // we can skip the signal + return; + } + + // set new list + d->dbPathModificationDateList = modList; + } + + d->dirtyAlbums.append(url); + + if (DIO::running()) + return; + + KURL u; + u.setProtocol("digikamalbums"); + u.setPath(d->dirtyAlbums.first()); + d->dirtyAlbums.pop_front(); + + DIO::scan(u); +} + +} // namespace Digikam diff --git a/src/digikam/albummanager.h b/src/digikam/albummanager.h new file mode 100644 index 00000000..3244f5a2 --- /dev/null +++ b/src/digikam/albummanager.h @@ -0,0 +1,472 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-15 + * Description : Albums manager interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file albummanager.h */ + +#ifndef ALBUMMANAGER_H +#define ALBUMMANAGER_H + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQDate; + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class Album; +class PAlbum; +class TAlbum; +class DAlbum; +class SAlbum; +class AlbumDB; +class AlbumItemHandler; +class SplashScreen; +class AlbumManagerPriv; + +typedef TQValueList AlbumList; +typedef TQPair YearMonth; + +/** + * \class AlbumManager + * + * There are two primary managers which manage the listing and + * lifetime of Album and ImageInfo: AlbumManager and AlbumLister + * + * AlbumManager manages albums: does listing of albums and controls the lifetime of it. + * For PAlbums and TAlbums, the listing is done by reading the db directly and + * building the hierarchy of the albums. For DAlbums, since the listing takes + * time, the work is delegated to a tdeioslave. Interested frontend entities can + * connect to the albummanager to receive notifications of new Albums, when + * Albums are deleted and when the current album is changed. + * + * Additional operations are provided for: creating/deleting/rename Albums, + * updating icons and moving Albums. + * + */ +class DIGIKAM_EXPORT AlbumManager : public TQObject +{ + TQ_OBJECT + +public: + + /** + * Constructor + */ + AlbumManager(); + + /** + * Destructor + */ + ~AlbumManager(); + + /** + * A convenience function to get the instance of the AlbumManager + */ + static AlbumManager* instance(); + + /** + * returns a pointer to the current AlbumDB + */ + AlbumDB* albumDB(); + + /** @name Library path And Scanning + */ + //@{ + /** + * Set the @p libraryPath to the given path + * + * If the libraryPath is the same as the current path, nothing happens. + * Otherwise the currently listed albums are cleared. The albums in the new + * library path are not listed till you call startScan() + * @param path the new libraryPath + * @see startScan + */ + void setLibraryPath(const TQString& path, SplashScreen *splash=0); + + /** + * @return the current libraryPath + */ + TQString getLibraryPath() const; + + /** + * starts scanning the libraryPath and listing the albums. If the + * libraryPath has not changed since the lastscan, then nothing happens + * @see setLibraryPath + * @see refresh + */ + void startScan(); + + /** + * This is similar to startScan, except that it assumes you have run + * startScan at least once. It checks the database to see if any new albums + * have been added and updates them accordingly. Use this when a change in the + * filesystem is detected (but the album library path hasn't changed) + * @see startScan + */ + void refresh(); + //@} + + /** @name List of Albums and current Album + */ + //@{ + /** + * @return a list of all PAlbums + */ + AlbumList allPAlbums() const; + + /** + * @return a list of all TAlbums + */ + AlbumList allTAlbums() const; + + /** + * @return a list of all SAlbums + */ + AlbumList allSAlbums() const; + + /** + * @return a list of all SAlbums + */ + AlbumList allDAlbums() const; + + /** + * set the current album to @p album. Call this from views which show + * listing of albums. This also causes it to fire the signal + * signalAlbumCurrentChanged() + */ + void setCurrentAlbum(Album *album); + + /** + * @returns the current albumm + */ + Album* currentAlbum() const; + //@} + + /** @name Finding Albums + */ + //@{ + /** + * Given a complete file url (kde url with file protocol), it will try to find + * a PAlbum corresponding to it. + * \warning This should not be used, unless really necessary + * @return PAlbum correspoding to supplied @p url + * @param url the url we need to check + */ + PAlbum* findPAlbum(const KURL& url) const; + + /** + * @return a PAlbum with given ID + * @param id the id for the PAlbum + */ + PAlbum* findPAlbum(int id) const; + + /** + * @return a TAlbum with given ID + * @param id the id for the TAlbum + */ + TAlbum* findTAlbum(int id) const; + + /** + * @return a SAlbum with given ID + * @param id the id for the SAlbum + */ + SAlbum* findSAlbum(int id) const; + + /** + * @return a DAlbum with given ID + * @param id the id for the DAlbum + */ + DAlbum* findDAlbum(int id) const; + + /** + * @return a Album with the given globalID + * @param gid the global id for the album + */ + Album* findAlbum(int gid) const; + + /** + * @return a TAlbum with given tag path, or 0 if not found + * @param tagPath the tag path ("People/Friend/John") + */ + TAlbum* findTAlbum(const TQString &tagPath) const; + //@} + + /** @name Operations on PAlbum + */ + //@{ + /** + * Create a new PAlbum with supplied properties as a child of the parent + * This is equivalent to creating a new folder on the disk with supplied + * name in the parent's folder path. Also the supplied attributes are written + * out to the database + * \note the signalAlbumAdded will be fired before this function returns. Its + * recommended to connect to that signal to get notification of new album added + * @return the newly created PAlbum or 0 if it fails + * @param parent the parent album under which to create the new Album + * @param name the name of the new album + * @param caption the caption for the new album + * @param date the date for the new album + * @param collection the collection for the new album + * @param errMsg this will contain the error message describing why the + * operation failed + */ + PAlbum* createPAlbum(PAlbum* parent, const TQString& name, + const TQString& caption, const TQDate& date, + const TQString& collection, + TQString& errMsg); + + /** + * Renames a PAlbum. This is equivalent to actually renaming the corresponding + * folder on the disk. + * @return true if the operation succeeds, false otherwise + * @param album the Album which should be renamed + * @param newName the new name for the album + * @param errMsg this will contain the error message describing why the + * operation failed + */ + bool renamePAlbum(PAlbum* album, const TQString& newName, + TQString& errMsg); + + /** + * Update the icon for an album. The @p icon is the name (and not full path) + * of the file in the album + * @return true if the operation succeeds, false otherwise + * @param album the album for which icon should be changed + * @param iconID the filename of the new icon + * @param errMsg if the operation fails, this will contain the error message + * describing why the operation failed + */ + bool updatePAlbumIcon(PAlbum *album, TQ_LLONG iconID, TQString& errMsg); + //@} + + /** @name Operations on TAlbum + */ + //@{ + /** + * Create a new TAlbum with supplied properties as a child of the parent + * The tag is added to the database + * \note the signalAlbumAdded will be fired before this function returns. Its + * recommended to connect to that signal to get notification of new album added + * @return the newly created TAlbum or 0 if it fails + * @param parent the parent album under which to create the new Album + * @param name the name of the new album + * @param iconkde the iconkde for the new album (this is a filename which + * kde iconloader can load up + * @param errMsg this will contain the error message describing why the + * operation failed + */ + TAlbum* createTAlbum(TAlbum* parent, const TQString& name, + const TQString& iconkde, TQString& errMsg); + + /** + * A list of tag paths is supplied. + * If no corresponding TAlbum exists, a new one will be created. + * @param tagPath A list of tag paths + * @returns A list of all TAlbums for the list (already existing or newly created) + */ + AlbumList findOrCreateTAlbums(const TQStringList &tagPaths); + + /** + * Delete a TAlbum. + * The tag is removed from the database + * \note the signalAlbumDeleted will be fired before this function returns. Its + * recommended to connect to that signal to get notification of album deletes + * @return true if the operation succeeds or false otherwise + * @param album the TAlbum to delete + * @param errMsg this will contain the error message describing why the + * operation failed + */ + bool deleteTAlbum(TAlbum* album, TQString& errMsg); + + /** + * Renames a TAlbum. + * This updates the tag name in the database + * @return true if the operation succeeds, false otherwise + * @param album the Album which should be renamed + * @param name the new name for the album + * @param errMsg this will contain the error message describing why the + * operation failed + */ + bool renameTAlbum(TAlbum* album, const TQString& name, TQString& errMsg); + + /** + * Move a TAlbum to a new parent. + * This updates the tag parent ID in the database + * @return true if the operation succeeds, false otherwise + * @param album the Album which should be moved + * @param newParent the Parent Album to which album should be moved + * @param errMsg this will contain the error message describing why the + * operation failed + */ + bool moveTAlbum(TAlbum* album, TAlbum *newParent, TQString &errMsg); + + /** + * Update the icon for a TAlbum. + * @return true if the operation succeeds, false otherwise + * @param album the album for which icon should be changed + * @param iconKDE a simple filename which can be loaded by TDEIconLoader + * @param iconID id of the icon image file + * @param errMsg this will contain the error message describing why the + * operation failed + * \note if iconKDE is not empty then iconID is used. So if you want to set + * the icon to a file which can be loaded by TDEIconLoader, pass it in as + * iconKDE. otherwise pass a null TQString to iconKDE and set iconID + */ + bool updateTAlbumIcon(TAlbum* album, const TQString& iconKDE, + TQ_LLONG iconID, TQString& errMsg); + //@} + + /** @name Operations on SAlbum + */ + //@{ + /** + * Create a new SAlbum with supplied url. If an existing SAlbum with same name + * exists this function will return a pointer to that album, instead of creating + * a new one. A newly created search album is added to the database. For an + * existing SAlbum, the url is updated and written out to the database + * \note the signalAlbumAdded will be fired before this function returns. Its + * recommended to connect to that signal to get notification of new album added + * @return the newly created SAlbum or an existing SAlbum with same name + * @param url the url of the album + * @param simple indicates whether the Search album is of simple type or + * extended type + */ + SAlbum* createSAlbum(const KURL& url, bool simple); + + /** + * Update the url for a SAlbum + * @return true if the operation succeeds, false otherwise + * @param album the album to update + * @param newURL the new url of the album + */ + bool updateSAlbum(SAlbum* album, const KURL& newURL); + + /** + * Delete a SAlbum from the database + * \note the signalAlbumDeleted will be fired before this function returns. Its + * recommended to connect to that signal to get notification of album deletes + * @return true if the operation succeeds, false otherwise + * @param album the album to delete + */ + bool deleteSAlbum(SAlbum* album); + //@} + + void setItemHandler(AlbumItemHandler *handler); + AlbumItemHandler* getItemHandler(); + void refreshItemHandler(const KURL::List& itemList=KURL::List()); + void emitAlbumItemsSelected(bool val); + +signals: + + void signalAlbumAdded(Album* album); + void signalAlbumDeleted(Album* album); + void signalAlbumItemsSelected(bool selected); + void signalAlbumsCleared(); + void signalAlbumCurrentChanged(Album* album); + void signalAllAlbumsLoaded(); + void signalAllDAlbumsLoaded(); + void signalAlbumIconChanged(Album* album); + void signalAlbumRenamed(Album* album); + void signalTAlbumMoved(TAlbum* album, TAlbum* newParent); + void signalPAlbumDirty(PAlbum* album); + void signalPAlbumsDirty(const TQMap&); + void signalTAlbumsDirty(const TQMap&); + void signalDAlbumsDirty(const TQMap&); + void signalDatesMapDirty(const TQMap&); + +private slots: + + void slotDatesJobResult(TDEIO::Job* job); + void slotDatesJobData(TDEIO::Job* job, const TQByteArray& data); + void slotAlbumsJobResult(TDEIO::Job* job); + void slotAlbumsJobData(TDEIO::Job* job, const TQByteArray& data); + void slotTagsJobResult(TDEIO::Job* job); + void slotTagsJobData(TDEIO::Job* job, const TQByteArray& data); + void slotDirty(const TQString& path); + +private: + + void insertPAlbum(PAlbum *album); + void removePAlbum(PAlbum *album); + void insertTAlbum(TAlbum *album); + void removeTAlbum(TAlbum *album); + + /** + * Scan albums directly from database and creates new PAlbums + * It only create those PAlbums which haven't already been + * created + */ + void scanPAlbums(); + + /** + * Scan tags directly from database and creates new TAlbums + * It only create those TAlbums which haven't already been + * created + */ + void scanTAlbums(); + + /** + * Scan searches directly from database and creates new SAlbums + * It only create those SAlbums which haven't already been + * created + */ + void scanSAlbums(); + + /** + * Makes use of a TDEIO::Job to list dates from the database + * @see slotResult + * @see slotData + */ + void scanDAlbums(); + +private: + + static AlbumManager *m_instance; + + AlbumManagerPriv *d; +}; + +} // namespace Digikam + +#endif /* ALBUMMANAGER_H */ diff --git a/src/digikam/albumpropsedit.cpp b/src/digikam/albumpropsedit.cpp new file mode 100644 index 00000000..703d8c4a --- /dev/null +++ b/src/digikam/albumpropsedit.cpp @@ -0,0 +1,376 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-03-09 + * Description : Album properties dialog. + * + * Copyright (C) 2003-2004 by Renchi Raju + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +#include +#if KDE_IS_VERSION(3,2,0) +#include +#else +#include +#endif + +// Local includes. + +#include "album.h" +#include "albumdb.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumpropsedit.h" +#include "albumpropsedit.moc" + +namespace Digikam +{ + +class AlbumPropsEditPriv +{ + +public: + + AlbumPropsEditPriv() + { + titleEdit = 0; + collectionCombo = 0; + commentsEdit = 0; + datePicker = 0; + album = 0; + } + + TQStringList albumCollections; + + TQComboBox *collectionCombo; + + KLineEdit *titleEdit; + + KTextEdit *commentsEdit; + + KDatePicker *datePicker; + + PAlbum *album; +}; + +AlbumPropsEdit::AlbumPropsEdit(PAlbum* album, bool create) + : KDialogBase( Plain, create ? i18n("New Album") : i18n("Edit Album"), + Help|Ok|Cancel, Ok, + 0, 0, true, true ) +{ + d = new AlbumPropsEditPriv; + d->album = album; + setHelp("albumpropsedit.anchor", "digikam"); + + TQGridLayout *topLayout = new TQGridLayout( plainPage(), 2, 6, + 0, spacingHint() ); + + TQLabel *topLabel = new TQLabel( plainPage() ); + if (create) + { + topLabel->setText( i18n( "Create new Album in \"%1\"") + .arg(album->title())); + } + else + { + topLabel->setText( i18n( "\"%1\" Album Properties") + .arg(album->title())); + } + topLabel->setAlignment(TQt::AlignAuto | TQt::AlignVCenter | TQt::SingleLine); + topLayout->addMultiCellWidget( topLabel, 0, 0, 0, 1 ); + + // -------------------------------------------------------- + + TQFrame *topLine = new TQFrame( plainPage() ); + topLine->setFrameShape( TQFrame::HLine ); + topLine->setFrameShadow( TQFrame::Sunken ); + topLayout->addMultiCellWidget( topLine, 1, 1, 0, 1 ); + + // -------------------------------------------------------- + + TQLabel *titleLabel = new TQLabel( plainPage( ) ); + titleLabel->setText( i18n( "&Title:" ) ); + topLayout->addWidget( titleLabel, 2, 0 ); + + d->titleEdit = new KLineEdit( plainPage( ) ); + topLayout->addWidget( d->titleEdit, 2, 1 ); + titleLabel->setBuddy( d->titleEdit ); + + TQRegExp titleRx("[^/]+"); + TQValidator *titleValidator = new TQRegExpValidator(titleRx, this); + d->titleEdit->setValidator(titleValidator); + + TQLabel *collectionLabel = new TQLabel( plainPage( ) ); + collectionLabel->setText( i18n( "Co&llection:" ) ); + topLayout->addWidget( collectionLabel, 3, 0 ); + + d->collectionCombo = new TQComboBox( plainPage( ) ); + d->collectionCombo->setEditable(true); + topLayout->addWidget( d->collectionCombo, 3, 1 ); + collectionLabel->setBuddy( d->collectionCombo ); + + TQLabel *commentsLabel = new TQLabel( plainPage( ) ); + commentsLabel->setText( i18n( "Ca&ption:" ) ); + topLayout->addWidget( commentsLabel, 4, 0, TQt::AlignAuto|TQt::AlignTop ); + + d->commentsEdit = new KTextEdit( plainPage( ) ); + topLayout->addWidget( d->commentsEdit, 4, 1 ); + commentsLabel->setBuddy( d->commentsEdit ); + d->commentsEdit->setCheckSpellingEnabled(true); + d->commentsEdit->setWordWrap(TQTextEdit::WidgetWidth); + d->commentsEdit->setWrapPolicy(TQTextEdit::AtWhiteSpace); + + TQLabel *dateLabel = new TQLabel( plainPage( ) ); + dateLabel->setText( i18n( "Album &date:" ) ); + topLayout->addWidget( dateLabel, 5, 0, TQt::AlignAuto|TQt::AlignTop ); + + d->datePicker = new KDatePicker( plainPage( ) ); + topLayout->addWidget( d->datePicker, 5, 1 ); + dateLabel->setBuddy( d->datePicker ); + + TQHBox *buttonRow = new TQHBox( plainPage( ) ); + TQPushButton *dateLowButton = new TQPushButton( + i18n("Selects the date of the oldest image", + "&Oldest" ), buttonRow ); + TQPushButton *dateAvgButton = new TQPushButton( + i18n("Calculates the average date", + "&Average" ), buttonRow ); + TQPushButton *dateHighButton = new TQPushButton( + i18n("Selects the date of the newest image", + "Newest" ), buttonRow ); + + topLayout->addWidget( buttonRow, 6, 1); + + setTabOrder(d->titleEdit, d->collectionCombo); + setTabOrder(d->collectionCombo, d->commentsEdit); + setTabOrder(d->commentsEdit, d->datePicker); + d->commentsEdit->setTabChangesFocus(true); + d->titleEdit->selectAll(); + d->titleEdit->setFocus(); + + // Initialize --------------------------------------------- + + AlbumSettings *settings = AlbumSettings::instance(); + if (settings) + { + d->collectionCombo->insertItem( TQString() ); + TQStringList collections = settings->getAlbumCollectionNames(); + d->collectionCombo->insertStringList( collections ); + int collectionIndex = collections.findIndex( album->collection() ); + + if ( collectionIndex != -1 ) + { + // + 1 because of the empty item + d->collectionCombo->setCurrentItem(collectionIndex + 1); + } + } + + if (create) + { + d->titleEdit->setText( i18n("New Album") ); + d->datePicker->setDate( TQDate::currentDate() ); + } + else + { + d->titleEdit->setText( album->title() ); + d->commentsEdit->setText( album->caption() ); + d->datePicker->setDate( album->date() ); + } + + // -- slots connections ------------------------------------------- + + connect(d->titleEdit, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotTitleChanged(const TQString&))); + + connect(dateLowButton, TQ_SIGNAL( clicked() ), + this, TQ_SLOT( slotDateLowButtonClicked())); + + connect(dateAvgButton, TQ_SIGNAL( clicked() ), + this, TQ_SLOT( slotDateAverageButtonClicked())); + + connect(dateHighButton, TQ_SIGNAL( clicked() ), + this, TQ_SLOT( slotDateHighButtonClicked())); + + adjustSize(); +} + +AlbumPropsEdit::~AlbumPropsEdit() +{ + delete d; +} + +TQString AlbumPropsEdit::title() const +{ + return d->titleEdit->text(); +} + +TQString AlbumPropsEdit::comments() const +{ + return d->commentsEdit->text(); +} + +TQDate AlbumPropsEdit::date() const +{ + return d->datePicker->date(); +} + +TQString AlbumPropsEdit::collection() const +{ + TQString name = d->collectionCombo->currentText(); + + if (name.isEmpty()) + { + name = i18n( "Uncategorized Album" ); + } + + return name; +} + +TQStringList AlbumPropsEdit::albumCollections() const +{ + TQStringList collections; + AlbumSettings *settings = AlbumSettings::instance(); + if (settings) + { + collections = settings->getAlbumCollectionNames(); + } + + TQString currentCollection = d->collectionCombo->currentText(); + if ( collections.findIndex( currentCollection ) == -1 ) + { + collections.append(currentCollection); + } + + collections.sort(); + return collections; +} + +bool AlbumPropsEdit::editProps(PAlbum *album, TQString& title, + TQString& comments, TQDate& date, TQString& collection, + TQStringList& albumCollections) +{ + AlbumPropsEdit dlg(album); + + bool ok = dlg.exec() == TQDialog::Accepted; + + title = dlg.title(); + comments = dlg.comments(); + date = dlg.date(); + collection = dlg.collection(); + albumCollections = dlg.albumCollections(); + + return ok; +} + +bool AlbumPropsEdit::createNew(PAlbum *parent, + TQString& title, + TQString& comments, + TQDate& date, + TQString& collection, + TQStringList& albumCollections) +{ + AlbumPropsEdit dlg(parent, true); + + bool ok = dlg.exec() == TQDialog::Accepted; + + title = dlg.title(); + comments = dlg.comments(); + date = dlg.date(); + collection = dlg.collection(); + albumCollections = dlg.albumCollections(); + + return ok; +} + +void AlbumPropsEdit::slotTitleChanged(const TQString& newtitle) +{ + enableButtonOK(!newtitle.isEmpty()); +} + +void AlbumPropsEdit::slotDateLowButtonClicked() +{ + setCursor( KCursor::waitCursor() ); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQDate avDate = db->getAlbumLowestDate( d->album->id() ); + setCursor( KCursor::arrowCursor() ); + + if ( avDate.isValid() ) + d->datePicker->setDate( avDate ); +} + +void AlbumPropsEdit::slotDateHighButtonClicked() +{ + setCursor( KCursor::waitCursor() ); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQDate avDate = db->getAlbumHighestDate( d->album->id() ); + setCursor( KCursor::arrowCursor() ); + + if ( avDate.isValid() ) + d->datePicker->setDate( avDate ); +} + +void AlbumPropsEdit::slotDateAverageButtonClicked() +{ + setCursor( KCursor::waitCursor() ); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQDate avDate = db->getAlbumAverageDate( d->album->id() ); + setCursor( KCursor::arrowCursor() ); + + if ( avDate.isValid() ) + d->datePicker->setDate( avDate ); + else + KMessageBox::error( plainPage( ), + i18n( "Could not calculate an average."), + i18n( "Could Not Calculate Average" ) ); +} + +} // namespace Digikam + + diff --git a/src/digikam/albumpropsedit.h b/src/digikam/albumpropsedit.h new file mode 100644 index 00000000..cd8f7564 --- /dev/null +++ b/src/digikam/albumpropsedit.h @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-03-09 + * Description : Album properties dialog. + * + * Copyright (C) 2003-2004 by Renchi Raju + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMPROPSEDIT_H +#define ALBUMPROPSEDIT_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +class PAlbum; +class AlbumPropsEditPriv; + +class AlbumPropsEdit : public KDialogBase +{ + TQ_OBJECT + +public: + + AlbumPropsEdit(PAlbum* album, bool create=false); + ~AlbumPropsEdit(); + + TQString title() const; + TQString comments() const; + TQDate date() const; + TQString collection() const; + TQStringList albumCollections() const; + + static bool editProps(PAlbum *album, + TQString& title, + TQString& comments, + TQDate& date, + TQString& collection, + TQStringList& albumCollections); + + static bool createNew(PAlbum *parent, + TQString& title, + TQString& comments, + TQDate& date, + TQString& collection, + TQStringList& albumCollections); + +private slots: + + void slotTitleChanged(const TQString& newtitle); + void slotDateLowButtonClicked(); + void slotDateAverageButtonClicked(); + void slotDateHighButtonClicked(); + +private: + + AlbumPropsEditPriv* d; +}; + +} // namespace Digikam + +#endif /* ALBUMPROPSEDIT_H */ diff --git a/src/digikam/albumsettings.cpp b/src/digikam/albumsettings.cpp new file mode 100644 index 00000000..6b7d4315 --- /dev/null +++ b/src/digikam/albumsettings.cpp @@ -0,0 +1,1142 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-16-10 + * Description : albums settings interface + * + * Copyright (C) 2003-2004 by Renchi Raju + * Copyright (C) 2003-2007 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "thumbnailsize.h" +#include "mimefilter.h" +#include "albumlister.h" +#include "albumsettings.h" + +namespace Digikam +{ + +class AlbumSettingsPrivate +{ + +public: + + bool showSplash; + bool useTrash; + bool showTrashDeleteDialog; + bool sidebarApplyDirectly; + bool scanAtStart; + bool recursiveAlbums; + bool recursiveTags; + + bool iconShowName; + bool iconShowSize; + bool iconShowDate; + bool iconShowModDate; + bool iconShowComments; + bool iconShowResolution; + bool iconShowTags; + bool iconShowRating; + + bool showToolTips; + bool tooltipShowFileName; + bool tooltipShowFileDate; + bool tooltipShowFileSize; + bool tooltipShowImageType; + bool tooltipShowImageDim; + bool tooltipShowPhotoMake; + bool tooltipShowPhotoDate; + bool tooltipShowPhotoFocal; + bool tooltipShowPhotoExpo; + bool tooltipShowPhotoMode; + bool tooltipShowPhotoFlash; + bool tooltipShowPhotoWb; + bool tooltipShowAlbumName; + bool tooltipShowComments; + bool tooltipShowTags; + bool tooltipShowRating; + + bool exifRotate; + bool exifSetOrientation; + + bool saveIptcTags; + bool saveIptcPhotographerId; + bool saveIptcCredits; + + bool saveComments; + bool saveDateTime; + bool saveRating; + + bool previewLoadFullImageSize; + + bool showFolderTreeViewItemsCount; + + int thumbnailSize; + int treeThumbnailSize; + int ratingFilterCond; + + TQString currentTheme; + TQString albumLibraryPath; + TQString imageFilefilter; + TQString movieFilefilter; + TQString audioFilefilter; + TQString rawFilefilter; + TQString defaultImageFilefilter; + TQString defaultMovieFilefilter; + TQString defaultAudioFilefilter; + TQString defaultRawFilefilter; + + TQString author; + TQString authorTitle; + TQString credit; + TQString source; + TQString copyright; + + TQStringList albumCollectionNames; + + TDEConfig *config; + + AlbumSettings::AlbumSortOrder albumSortOrder; + AlbumSettings::ImageSortOrder imageSortOrder; + AlbumSettings::ItemRightClickAction itemRightClickAction; +}; + + +AlbumSettings* AlbumSettings::m_instance = 0; + +AlbumSettings* AlbumSettings::instance() +{ + return m_instance; +} + +AlbumSettings::AlbumSettings() +{ + d = new AlbumSettingsPrivate; + d->config = kapp->config(); + m_instance = this; + init(); +} + +AlbumSettings::~AlbumSettings() +{ + delete d; + m_instance = 0; +} + +void AlbumSettings::init() +{ + d->albumCollectionNames.clear(); + d->albumCollectionNames.append(i18n("Family")); + d->albumCollectionNames.append(i18n("Travel")); + d->albumCollectionNames.append(i18n("Holidays")); + d->albumCollectionNames.append(i18n("Friends")); + d->albumCollectionNames.append(i18n("Nature")); + d->albumCollectionNames.append(i18n("Party")); + d->albumCollectionNames.append(i18n("Todo")); + d->albumCollectionNames.append(i18n("Miscellaneous")); + d->albumCollectionNames.sort(); + + d->albumSortOrder = AlbumSettings::ByFolder; + d->imageSortOrder = AlbumSettings::ByIName; + d->itemRightClickAction = AlbumSettings::ShowPreview; + + d->defaultImageFilefilter = "*.jpg *.jpeg *.jpe " // JPEG + "*.jp2 *.jpx *.jpc *.pgx " // JPEG-2000 + "*.tif *.tiff " // TIFF + "*.png *.gif *.bmp *.xpm *.ppm *.pnm *.xcf *.pcx"; + + d->defaultMovieFilefilter = "*.mpeg *.mpg *.mpo *.mpe " // MPEG + "*.avi *.mov *.wmf *.asf *.mp4 *.3gp"; + + d->defaultAudioFilefilter = "*.ogg *.mp3 *.wma *.wav"; + + // RAW files estentions supported by dcraw program and + // defines to digikam/libs/dcraw/rawfiles.h +#if KDCRAW_VERSION < 0x000106 + d->defaultRawFilefilter = TQString(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + d->defaultRawFilefilter = TQString(KDcrawIface::KDcraw::rawFiles()); +#endif + + d->imageFilefilter = d->defaultImageFilefilter; + d->movieFilefilter = d->defaultMovieFilefilter; + d->audioFilefilter = d->defaultAudioFilefilter; + d->rawFilefilter = d->defaultRawFilefilter; + + d->thumbnailSize = ThumbnailSize::Medium; + d->treeThumbnailSize = 22; + + d->ratingFilterCond = AlbumLister::GreaterEqualCondition; + + d->showToolTips = true; + d->showSplash = true; + d->useTrash = true; + d->showTrashDeleteDialog = true; + d->sidebarApplyDirectly = false; + + d->iconShowName = false; + d->iconShowSize = false; + d->iconShowDate = true; + d->iconShowModDate = true; + d->iconShowComments = true; + d->iconShowResolution = false; + d->iconShowTags = true; + d->iconShowRating = true; + + d->tooltipShowFileName = true; + d->tooltipShowFileDate = false; + d->tooltipShowFileSize = false; + d->tooltipShowImageType = false; + d->tooltipShowImageDim = true; + d->tooltipShowPhotoMake = true; + d->tooltipShowPhotoDate = true; + d->tooltipShowPhotoFocal = true; + d->tooltipShowPhotoExpo = true; + d->tooltipShowPhotoMode = true; + d->tooltipShowPhotoFlash = false; + d->tooltipShowPhotoWb = false; + d->tooltipShowAlbumName = false; + d->tooltipShowComments = true; + d->tooltipShowTags = true; + d->tooltipShowRating = true; + + d->exifRotate = true; + d->exifSetOrientation = true; + + d->saveIptcTags = false; + d->saveIptcPhotographerId = false; + d->saveIptcCredits = false; + + d->saveComments = false; + d->saveDateTime = false; + d->saveRating = false; + + d->previewLoadFullImageSize = false; + + d->recursiveAlbums = false; + d->recursiveTags = true; + + d->showFolderTreeViewItemsCount = false; +} + +void AlbumSettings::readSettings() +{ + TDEConfig* config = d->config; + + // --------------------------------------------------------------------- + + config->setGroup("Album Settings"); + + d->albumLibraryPath = config->readPathEntry("Album Path", TQString()); + + TQStringList collectionList = config->readListEntry("Album Collections"); + if (!collectionList.isEmpty()) + { + collectionList.sort(); + d->albumCollectionNames = collectionList; + } + + d->albumSortOrder = AlbumSettings::AlbumSortOrder(config->readNumEntry("Album Sort Order", + (int)AlbumSettings::ByFolder)); + + d->imageSortOrder = AlbumSettings::ImageSortOrder(config->readNumEntry("Image Sort Order", + (int)AlbumSettings::ByIName)); + + d->itemRightClickAction = AlbumSettings::ItemRightClickAction(config->readNumEntry( + "Item Right Click Action", + (int)AlbumSettings::ShowPreview)); + + d->imageFilefilter = config->readEntry("File Filter", d->imageFilefilter); + d->movieFilefilter = config->readEntry("Movie File Filter", d->movieFilefilter); + d->audioFilefilter = config->readEntry("Audio File Filter", d->audioFilefilter); + d->rawFilefilter = config->readEntry("Raw File Filter", d->rawFilefilter); + d->thumbnailSize = config->readNumEntry("Default Icon Size", ThumbnailSize::Medium); + d->treeThumbnailSize = config->readNumEntry("Default Tree Icon Size", 22); + d->currentTheme = config->readEntry("Theme", i18n("Default")); + + d->ratingFilterCond = config->readNumEntry("Rating Filter Condition", + AlbumLister::GreaterEqualCondition); + + d->iconShowName = config->readBoolEntry("Icon Show Name", false); + d->iconShowResolution = config->readBoolEntry("Icon Show Resolution", false); + d->iconShowSize = config->readBoolEntry("Icon Show Size", false); + d->iconShowDate = config->readBoolEntry("Icon Show Date", true); + d->iconShowModDate = config->readBoolEntry("Icon Show Modification Date", true); + d->iconShowComments = config->readBoolEntry("Icon Show Comments", true); + d->iconShowTags = config->readBoolEntry("Icon Show Tags", true); + d->iconShowRating = config->readBoolEntry("Icon Show Rating", true); + + d->showToolTips = config->readBoolEntry("Show ToolTips", false); + d->tooltipShowFileName = config->readBoolEntry("ToolTips Show File Name", true); + d->tooltipShowFileDate = config->readBoolEntry("ToolTips Show File Date", false); + d->tooltipShowFileSize = config->readBoolEntry("ToolTips Show File Size", false); + d->tooltipShowImageType = config->readBoolEntry("ToolTips Show Image Type", false); + d->tooltipShowImageDim = config->readBoolEntry("ToolTips Show Image Dim", true); + d->tooltipShowPhotoMake = config->readBoolEntry("ToolTips Show Photo Make", true); + d->tooltipShowPhotoDate = config->readBoolEntry("ToolTips Show Photo Date", true); + d->tooltipShowPhotoFocal = config->readBoolEntry("ToolTips Show Photo Focal", true); + d->tooltipShowPhotoExpo = config->readBoolEntry("ToolTips Show Photo Expo", true); + d->tooltipShowPhotoMode = config->readBoolEntry("ToolTips Show Photo Mode", true); + d->tooltipShowPhotoFlash = config->readBoolEntry("ToolTips Show Photo Flash", false); + d->tooltipShowPhotoWb = config->readBoolEntry("ToolTips Show Photo WB", false); + d->tooltipShowAlbumName = config->readBoolEntry("ToolTips Show Album Name", false); + d->tooltipShowComments = config->readBoolEntry("ToolTips Show Comments", true); + d->tooltipShowTags = config->readBoolEntry("ToolTips Show Tags", true); + d->tooltipShowRating = config->readBoolEntry("ToolTips Show Rating", true); + + d->previewLoadFullImageSize = config->readBoolEntry("Preview Load Full Image Size", false); + + d->recursiveAlbums = config->readBoolEntry("Recursive Albums", false); + d->recursiveTags = config->readBoolEntry("Recursive Tags", true); + + d->showFolderTreeViewItemsCount = config->readBoolEntry("Show Folder Tree View Items Count", false); + + // --------------------------------------------------------------------- + + config->setGroup("EXIF Settings"); + + d->exifRotate = config->readBoolEntry("EXIF Rotate", true); + d->exifSetOrientation = config->readBoolEntry("EXIF Set Orientation", true); + + // --------------------------------------------------------------------- + + config->setGroup("Metadata Settings"); + + d->saveIptcTags = config->readBoolEntry("Save IPTC Tags", false); + d->saveIptcPhotographerId = config->readBoolEntry("Save IPTC Photographer ID", false); + d->saveIptcCredits = config->readBoolEntry("Save IPTC Credits", false); + + d->saveComments = config->readBoolEntry("Save EXIF Comments", false); + d->saveDateTime = config->readBoolEntry("Save Date Time", false); + d->saveRating = config->readBoolEntry("Save Rating", false); + + d->author = config->readEntry("IPTC Author", TQString()); + d->authorTitle = config->readEntry("IPTC Author Title", TQString()); + d->credit = config->readEntry("IPTC Credit", TQString()); + d->source = config->readEntry("IPTC Source", TQString()); + d->copyright = config->readEntry("IPTC Copyright", TQString()); + + // --------------------------------------------------------------------- + + config->setGroup("General Settings"); + + d->showSplash = config->readBoolEntry("Show Splash", true); + d->useTrash = config->readBoolEntry("Use Trash", true); + d->showTrashDeleteDialog = config->readBoolEntry("Show Trash Delete Dialog", true); + d->sidebarApplyDirectly = config->readBoolEntry("Apply Sidebar Changes Directly", false); + d->scanAtStart = config->readBoolEntry("Scan At Start", true); +} + +void AlbumSettings::saveSettings() +{ + TDEConfig* config = d->config; + + // --------------------------------------------------------------------- + + config->setGroup("Album Settings"); + + config->writePathEntry("Album Path", d->albumLibraryPath); + config->writeEntry("Album Collections", d->albumCollectionNames); + config->writeEntry("Album Sort Order", (int)d->albumSortOrder); + config->writeEntry("Image Sort Order", (int)d->imageSortOrder); + config->writeEntry("Item Right Click Action", (int)d->itemRightClickAction); + config->writeEntry("File Filter", d->imageFilefilter); + config->writeEntry("Movie File Filter", d->movieFilefilter); + config->writeEntry("Audio File Filter", d->audioFilefilter); + config->writeEntry("Raw File Filter", d->rawFilefilter); + config->writeEntry("Default Icon Size", TQString::number(d->thumbnailSize)); + config->writeEntry("Default Tree Icon Size", TQString::number(d->treeThumbnailSize)); + config->writeEntry("Rating Filter Condition", d->ratingFilterCond); + config->writeEntry("Theme", d->currentTheme); + + config->writeEntry("Icon Show Name", d->iconShowName); + config->writeEntry("Icon Show Resolution", d->iconShowResolution); + config->writeEntry("Icon Show Size", d->iconShowSize); + config->writeEntry("Icon Show Date", d->iconShowDate); + config->writeEntry("Icon Show Modification Date", d->iconShowModDate); + config->writeEntry("Icon Show Comments", d->iconShowComments); + config->writeEntry("Icon Show Tags", d->iconShowTags); + config->writeEntry("Icon Show Rating", d->iconShowRating); + + config->writeEntry("Show ToolTips", d->showToolTips); + config->writeEntry("ToolTips Show File Name", d->tooltipShowFileName); + config->writeEntry("ToolTips Show File Date", d->tooltipShowFileDate); + config->writeEntry("ToolTips Show File Size", d->tooltipShowFileSize); + config->writeEntry("ToolTips Show Image Type", d->tooltipShowImageType); + config->writeEntry("ToolTips Show Image Dim", d->tooltipShowImageDim); + config->writeEntry("ToolTips Show Photo Make", d->tooltipShowPhotoMake); + config->writeEntry("ToolTips Show Photo Date", d->tooltipShowPhotoDate); + config->writeEntry("ToolTips Show Photo Focal", d->tooltipShowPhotoFocal); + config->writeEntry("ToolTips Show Photo Expo", d->tooltipShowPhotoExpo); + config->writeEntry("ToolTips Show Photo Mode", d->tooltipShowPhotoMode); + config->writeEntry("ToolTips Show Photo Flash", d->tooltipShowPhotoFlash); + config->writeEntry("ToolTips Show Photo WB", d->tooltipShowPhotoWb); + config->writeEntry("ToolTips Show Album Name", d->tooltipShowAlbumName); + config->writeEntry("ToolTips Show Comments", d->tooltipShowComments); + config->writeEntry("ToolTips Show Tags", d->tooltipShowTags); + config->writeEntry("ToolTips Show Rating", d->tooltipShowRating); + + config->writeEntry("Preview Load Full Image Size", d->previewLoadFullImageSize); + + config->writeEntry("Recursive Albums", d->recursiveAlbums); + config->writeEntry("Recursive Tags", d->recursiveTags); + + config->writeEntry("Show Folder Tree View Items Count", d->showFolderTreeViewItemsCount); + + // --------------------------------------------------------------------- + + config->setGroup("EXIF Settings"); + + config->writeEntry("EXIF Rotate", d->exifRotate); + config->writeEntry("EXIF Set Orientation", d->exifSetOrientation); + + // --------------------------------------------------------------------- + + config->setGroup("Metadata Settings"); + + config->writeEntry("Save IPTC Tags", d->saveIptcTags); + config->writeEntry("Save IPTC Photographer ID", d->saveIptcPhotographerId); + config->writeEntry("Save IPTC Credits", d->saveIptcCredits); + + config->writeEntry("Save EXIF Comments", d->saveComments); + config->writeEntry("Save Date Time", d->saveDateTime); + config->writeEntry("Save Rating", d->saveRating); + + config->writeEntry("IPTC Author", d->author); + config->writeEntry("IPTC Author Title", d->authorTitle); + config->writeEntry("IPTC Credit", d->credit); + config->writeEntry("IPTC Source", d->source); + config->writeEntry("IPTC Copyright", d->copyright); + + // --------------------------------------------------------------------- + + config->setGroup("General Settings"); + + config->writeEntry("Show Splash", d->showSplash); + config->writeEntry("Use Trash", d->useTrash); + config->writeEntry("Show Trash Delete Dialog", d->showTrashDeleteDialog); + config->writeEntry("Apply Sidebar Changes Directly", d->sidebarApplyDirectly); + config->writeEntry("Scan At Start", d->scanAtStart); + + config->sync(); +} + +void AlbumSettings::setAlbumLibraryPath(const TQString& path) +{ + d->albumLibraryPath = path; +} + +TQString AlbumSettings::getAlbumLibraryPath() const +{ + return d->albumLibraryPath; +} + +void AlbumSettings::setShowSplashScreen(bool val) +{ + d->showSplash = val; +} + +bool AlbumSettings::getShowSplashScreen() const +{ + return d->showSplash; +} + +void AlbumSettings::setScanAtStart(bool val) +{ + d->scanAtStart = val; +} + +bool AlbumSettings::getScanAtStart() const +{ + return d->scanAtStart; +} + +void AlbumSettings::setAlbumCollectionNames(const TQStringList& list) +{ + d->albumCollectionNames = list; +} + +TQStringList AlbumSettings::getAlbumCollectionNames() +{ + return d->albumCollectionNames; +} + +bool AlbumSettings::addAlbumCollectionName(const TQString& name) +{ + if (d->albumCollectionNames.contains(name)) + return false; + + d->albumCollectionNames.append(name); + return true; +} + +bool AlbumSettings::delAlbumCollectionName(const TQString& name) +{ + uint count = d->albumCollectionNames.remove(name); + return (count > 0) ? true : false; +} + +void AlbumSettings::setAlbumSortOrder(const AlbumSettings::AlbumSortOrder order) +{ + d->albumSortOrder = order; +} + +AlbumSettings::AlbumSortOrder AlbumSettings::getAlbumSortOrder() const +{ + return d->albumSortOrder; +} + +void AlbumSettings::setImageSortOrder(const ImageSortOrder order) +{ + d->imageSortOrder = order; +} + +AlbumSettings::ImageSortOrder AlbumSettings::getImageSortOrder() const +{ + return d->imageSortOrder; +} + +void AlbumSettings::setItemRightClickAction(const ItemRightClickAction action) +{ + d->itemRightClickAction = action; +} + +AlbumSettings::ItemRightClickAction AlbumSettings::getItemRightClickAction() const +{ + return d->itemRightClickAction; +} + +void AlbumSettings::setImageFileFilter(const TQString& filter) +{ + d->imageFilefilter = filter; +} + +TQString AlbumSettings::getImageFileFilter() const +{ + return d->imageFilefilter; +} + +void AlbumSettings::setMovieFileFilter(const TQString& filter) +{ + d->movieFilefilter = filter; +} + +TQString AlbumSettings::getMovieFileFilter() const +{ + return d->movieFilefilter; +} + +void AlbumSettings::setAudioFileFilter(const TQString& filter) +{ + d->audioFilefilter = filter; +} + +TQString AlbumSettings::getAudioFileFilter() const +{ + return d->audioFilefilter; +} + +void AlbumSettings::setRawFileFilter(const TQString& filter) +{ + d->rawFilefilter = filter; +} + +TQString AlbumSettings::getRawFileFilter() const +{ + return d->rawFilefilter; +} + +bool AlbumSettings::addImageFileExtension(const TQString& newExt) +{ + if ( TQStringList::split(" ", d->imageFilefilter).contains(newExt) || + TQStringList::split(" ", d->movieFilefilter).contains(newExt) || + TQStringList::split(" ", d->audioFilefilter).contains(newExt) || + TQStringList::split(" ", d->rawFilefilter ).contains(newExt) ) + return false; + + d->imageFilefilter = d->imageFilefilter + ' ' + newExt; + return true; +} + +TQString AlbumSettings::getAllFileFilter() const +{ + return d->imageFilefilter + ' ' + + d->movieFilefilter + ' ' + + d->audioFilefilter + ' ' + + d->rawFilefilter; +} + +void AlbumSettings::setDefaultIconSize(int val) +{ + d->thumbnailSize = val; +} + +int AlbumSettings::getDefaultIconSize() const +{ + return d->thumbnailSize; +} + +void AlbumSettings::setDefaultTreeIconSize(int val) +{ + d->treeThumbnailSize = val; +} + +int AlbumSettings::getDefaultTreeIconSize() const +{ + return ((d->treeThumbnailSize < 8) || (d->treeThumbnailSize > 48)) ? 48 : d->treeThumbnailSize; +} + +void AlbumSettings::setRatingFilterCond(int val) +{ + d->ratingFilterCond = val; +} + +int AlbumSettings::getRatingFilterCond() const +{ + return d->ratingFilterCond; +} + +void AlbumSettings::setIconShowName(bool val) +{ + d->iconShowName = val; +} + +bool AlbumSettings::getIconShowName() const +{ + return d->iconShowName; +} + +void AlbumSettings::setIconShowSize(bool val) +{ + d->iconShowSize = val; +} + +bool AlbumSettings::getIconShowSize() const +{ + return d->iconShowSize; +} + +void AlbumSettings::setIconShowComments(bool val) +{ + d->iconShowComments = val; +} + +bool AlbumSettings::getIconShowComments() const +{ + return d->iconShowComments; +} + +void AlbumSettings::setIconShowResolution(bool val) +{ + d->iconShowResolution = val; +} + +bool AlbumSettings::getIconShowResolution() const +{ + return d->iconShowResolution; +} + +void AlbumSettings::setIconShowTags(bool val) +{ + d->iconShowTags = val; +} + +bool AlbumSettings::getIconShowTags() const +{ + return d->iconShowTags; +} + +void AlbumSettings::setIconShowDate(bool val) +{ + d->iconShowDate = val; +} + +bool AlbumSettings::getIconShowDate() const +{ + return d->iconShowDate; +} + +void AlbumSettings::setIconShowModDate(bool val) +{ + d->iconShowModDate = val; +} + +bool AlbumSettings::getIconShowModDate() const +{ + return d->iconShowModDate; +} + +void AlbumSettings::setIconShowRating(bool val) +{ + d->iconShowRating = val; +} + +bool AlbumSettings::getIconShowRating() const +{ + return d->iconShowRating; +} + +void AlbumSettings::setExifRotate(bool val) +{ + d->exifRotate = val; +} + +bool AlbumSettings::getExifRotate() const +{ + return d->exifRotate; +} + +void AlbumSettings::setExifSetOrientation(bool val) +{ + d->exifSetOrientation = val; +} + +bool AlbumSettings::getExifSetOrientation() const +{ + return d->exifSetOrientation; +} + +void AlbumSettings::setSaveIptcTags(bool val) +{ + d->saveIptcTags = val; +} + +bool AlbumSettings::getSaveIptcTags() const +{ + return d->saveIptcTags; +} + +void AlbumSettings::setSaveIptcPhotographerId(bool val) +{ + d->saveIptcPhotographerId = val; +} + +bool AlbumSettings::getSaveIptcPhotographerId() const +{ + return d->saveIptcPhotographerId; +} + +void AlbumSettings::setSaveIptcCredits(bool val) +{ + d->saveIptcCredits = val; +} + +bool AlbumSettings::getSaveIptcCredits() const +{ + return d->saveIptcCredits; +} + +void AlbumSettings::setIptcAuthor(const TQString& author) +{ + d->author = author; +} + +TQString AlbumSettings::getIptcAuthor() const +{ + return d->author; +} + +void AlbumSettings::setIptcAuthorTitle(const TQString& authorTitle) +{ + d->authorTitle = authorTitle; +} + +TQString AlbumSettings::getIptcAuthorTitle() const +{ + return d->authorTitle; +} + +void AlbumSettings::setIptcCredit(const TQString& credit) +{ + d->credit = credit; +} + +TQString AlbumSettings::getIptcCredit() const +{ + return d->credit; +} + +void AlbumSettings::setIptcSource(const TQString& source) +{ + d->source = source; +} + +TQString AlbumSettings::getIptcSource() const +{ + return d->source; +} + +void AlbumSettings::setIptcCopyright(const TQString& copyright) +{ + d->copyright = copyright; +} + +TQString AlbumSettings::getIptcCopyright() const +{ + return d->copyright; +} + +void AlbumSettings::setSaveComments(bool val) +{ + d->saveComments = val; +} + +bool AlbumSettings::getSaveComments() const +{ + return d->saveComments; +} + +void AlbumSettings::setSaveDateTime(bool val) +{ + d->saveDateTime = val; +} + +bool AlbumSettings::getSaveDateTime() const +{ + return d->saveDateTime; +} + +bool AlbumSettings::getSaveRating() const +{ + return d->saveRating; +} + +void AlbumSettings::setSaveRating(bool val) +{ + d->saveRating = val; +} + +void AlbumSettings::setShowToolTips(bool val) +{ + d->showToolTips = val; +} + +bool AlbumSettings::getShowToolTips() const +{ + return d->showToolTips; +} + +void AlbumSettings::setToolTipsShowFileName(bool val) +{ + d->tooltipShowFileName = val; +} + +bool AlbumSettings::getToolTipsShowFileName() const +{ + return d->tooltipShowFileName; +} + +void AlbumSettings::setToolTipsShowFileDate(bool val) +{ + d->tooltipShowFileDate = val; +} + +bool AlbumSettings::getToolTipsShowFileDate() const +{ + return d->tooltipShowFileDate; +} + +void AlbumSettings::setToolTipsShowFileSize(bool val) +{ + d->tooltipShowFileSize = val; +} + +bool AlbumSettings::getToolTipsShowFileSize() const +{ + return d->tooltipShowFileSize; +} + +void AlbumSettings::setToolTipsShowImageType(bool val) +{ + d->tooltipShowImageType = val; +} + +bool AlbumSettings::getToolTipsShowImageType() const +{ + return d->tooltipShowImageType; +} + +void AlbumSettings::setToolTipsShowImageDim(bool val) +{ + d->tooltipShowImageDim = val; +} + +bool AlbumSettings::getToolTipsShowImageDim() const +{ + return d->tooltipShowImageDim; +} + +void AlbumSettings::setToolTipsShowPhotoMake(bool val) +{ + d->tooltipShowPhotoMake = val; +} + +bool AlbumSettings::getToolTipsShowPhotoMake() const +{ + return d->tooltipShowPhotoMake; +} + +void AlbumSettings::setToolTipsShowPhotoDate(bool val) +{ + d->tooltipShowPhotoDate = val; +} + +bool AlbumSettings::getToolTipsShowPhotoDate() const +{ + return d->tooltipShowPhotoDate; +} + +void AlbumSettings::setToolTipsShowPhotoFocal(bool val) +{ + d->tooltipShowPhotoFocal = val; +} + +bool AlbumSettings::getToolTipsShowPhotoFocal() const +{ + return d->tooltipShowPhotoFocal; +} + +void AlbumSettings::setToolTipsShowPhotoExpo(bool val) +{ + d->tooltipShowPhotoExpo = val; +} + +bool AlbumSettings::getToolTipsShowPhotoExpo() const +{ + return d->tooltipShowPhotoExpo; +} + +void AlbumSettings::setToolTipsShowPhotoMode(bool val) +{ + d->tooltipShowPhotoMode = val; +} + +bool AlbumSettings::getToolTipsShowPhotoMode() const +{ + return d->tooltipShowPhotoMode; +} + +void AlbumSettings::setToolTipsShowPhotoFlash(bool val) +{ + d->tooltipShowPhotoFlash = val; +} + +bool AlbumSettings::getToolTipsShowPhotoFlash() const +{ + return d->tooltipShowPhotoFlash; +} + +void AlbumSettings::setToolTipsShowPhotoWB(bool val) +{ + d->tooltipShowPhotoWb = val; +} + +bool AlbumSettings::getToolTipsShowPhotoWB() const +{ + return d->tooltipShowPhotoWb; +} + +void AlbumSettings::setToolTipsShowAlbumName(bool val) +{ + d->tooltipShowAlbumName = val; +} + +bool AlbumSettings::getToolTipsShowAlbumName() const +{ + return d->tooltipShowAlbumName; +} + +void AlbumSettings::setToolTipsShowComments(bool val) +{ + d->tooltipShowComments = val; +} + +bool AlbumSettings::getToolTipsShowComments() const +{ + return d->tooltipShowComments; +} + +void AlbumSettings::setToolTipsShowTags(bool val) +{ + d->tooltipShowTags = val; +} + +bool AlbumSettings::getToolTipsShowTags() const +{ + return d->tooltipShowTags; +} + +void AlbumSettings::setToolTipsShowRating(bool val) +{ + d->tooltipShowRating = val; +} + +bool AlbumSettings::getToolTipsShowRating() const +{ + return d->tooltipShowRating; +} + +void AlbumSettings::setCurrentTheme(const TQString& theme) +{ + d->currentTheme = theme; +} + +TQString AlbumSettings::getCurrentTheme() const +{ + return d->currentTheme; +} + +void AlbumSettings::setUseTrash(bool val) +{ + d->useTrash = val; +} + +bool AlbumSettings::getUseTrash() const +{ + return d->useTrash; +} + +void AlbumSettings::setShowTrashDeleteDialog(bool val) +{ + d->showTrashDeleteDialog = val; +} + +bool AlbumSettings::getShowTrashDeleteDialog() const +{ + return d->showTrashDeleteDialog; +} + +void AlbumSettings::setApplySidebarChangesDirectly(bool val) +{ + d->sidebarApplyDirectly= val; +} + +bool AlbumSettings::getApplySidebarChangesDirectly() const +{ + return d->sidebarApplyDirectly; +} + +bool AlbumSettings::showToolTipsIsValid() const +{ + if (d->showToolTips) + { + if (d->tooltipShowFileName || + d->tooltipShowFileDate || + d->tooltipShowFileSize || + d->tooltipShowImageType || + d->tooltipShowImageDim || + d->tooltipShowPhotoMake || + d->tooltipShowPhotoDate || + d->tooltipShowPhotoFocal || + d->tooltipShowPhotoExpo || + d->tooltipShowPhotoMode || + d->tooltipShowPhotoFlash || + d->tooltipShowPhotoWb || + d->tooltipShowAlbumName || + d->tooltipShowComments || + d->tooltipShowTags || + d->tooltipShowRating) + return true; + } + + return false; +} + +TQString AlbumSettings::getDefaultImageFileFilter() const +{ + return d->defaultImageFilefilter; +} + +TQString AlbumSettings::getDefaultMovieFileFilter() const +{ + return d->defaultMovieFilefilter; +} + +TQString AlbumSettings::getDefaultAudioFileFilter() const +{ + return d->defaultAudioFilefilter; +} + +TQString AlbumSettings::getDefaultRawFileFilter() const +{ + return d->defaultRawFilefilter; +} + +void AlbumSettings::setPreviewLoadFullImageSize(bool val) +{ + d->previewLoadFullImageSize = val; +} + +bool AlbumSettings::getPreviewLoadFullImageSize() const +{ + return d->previewLoadFullImageSize; +} + +void AlbumSettings::setRecurseAlbums(bool val) +{ + d->recursiveAlbums = val; +} + +bool AlbumSettings::getRecurseAlbums() const +{ + return d->recursiveAlbums; +} + +void AlbumSettings::setRecurseTags(bool val) +{ + d->recursiveTags = val; +} + +bool AlbumSettings::getRecurseTags() const +{ + return d->recursiveTags; +} + +void AlbumSettings::setShowFolderTreeViewItemsCount(bool val) +{ + d->showFolderTreeViewItemsCount = val; +} + +bool AlbumSettings::getShowFolderTreeViewItemsCount() const +{ + return d->showFolderTreeViewItemsCount; +} + +} // namespace Digikam diff --git a/src/digikam/albumsettings.h b/src/digikam/albumsettings.h new file mode 100644 index 00000000..c3e3dcb7 --- /dev/null +++ b/src/digikam/albumsettings.h @@ -0,0 +1,283 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-16-10 + * Description : albums settings interface + * + * Copyright (C) 2003-2004 by Renchi Raju + * Copyright (C) 2003-2007 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMSETTINGS_H +#define ALBUMSETTINGS_H + +// TQt includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class AlbumSettingsPrivate; + +class DIGIKAM_EXPORT AlbumSettings +{ +public: + + enum AlbumSortOrder + { + ByFolder = 0, + ByCollection, + ByDate + }; + + enum ImageSortOrder + { + ByIName = 0, + ByIPath, + ByIDate, + ByISize, + ByIRating + }; + + enum ItemRightClickAction + { + ShowPreview = 0, + StartEditor + }; + + AlbumSettings(); + ~AlbumSettings(); + + void readSettings(); + void saveSettings(); + + bool showToolTipsIsValid() const; + + void setAlbumLibraryPath(const TQString& path); + TQString getAlbumLibraryPath() const; + + void setShowSplashScreen(bool val); + bool getShowSplashScreen() const; + + void setScanAtStart(bool val); + bool getScanAtStart() const; + + void setAlbumCollectionNames(const TQStringList& list); + TQStringList getAlbumCollectionNames(); + + bool addAlbumCollectionName(const TQString& name); + bool delAlbumCollectionName(const TQString& name); + + void setAlbumSortOrder(const AlbumSortOrder order); + AlbumSortOrder getAlbumSortOrder() const; + + void setImageSortOrder(const ImageSortOrder order); + ImageSortOrder getImageSortOrder() const; + + void setItemRightClickAction(const ItemRightClickAction action); + ItemRightClickAction getItemRightClickAction() const; + + void setImageFileFilter(const TQString& filter); + TQString getImageFileFilter() const; + + void setMovieFileFilter(const TQString& filter); + TQString getMovieFileFilter() const; + + void setAudioFileFilter(const TQString& filter); + TQString getAudioFileFilter() const; + + void setRawFileFilter(const TQString& filter); + TQString getRawFileFilter() const; + + bool addImageFileExtension(const TQString& ext); + TQString getAllFileFilter() const; + + void setDefaultIconSize(int val); + int getDefaultIconSize() const; + + void setDefaultTreeIconSize(int val); + int getDefaultTreeIconSize() const; + + void setRatingFilterCond(int val); + int getRatingFilterCond() const; + + void setIconShowName(bool val); + bool getIconShowName() const; + + void setIconShowSize(bool val); + bool getIconShowSize() const; + + void setIconShowComments(bool val); + bool getIconShowComments() const; + + void setIconShowResolution(bool val); + bool getIconShowResolution() const; + + void setIconShowTags(bool val); + bool getIconShowTags() const; + + void setIconShowDate(bool val); + bool getIconShowDate() const; + + void setIconShowModDate(bool val); + bool getIconShowModDate() const; + + void setIconShowRating(bool val); + bool getIconShowRating() const; + + void setExifRotate(bool val); + bool getExifRotate() const; + + void setExifSetOrientation(bool val); + bool getExifSetOrientation() const; + + void setSaveIptcTags(bool val); + bool getSaveIptcTags() const; + + void setSaveIptcPhotographerId(bool val); + bool getSaveIptcPhotographerId() const; + + void setSaveIptcCredits(bool val); + bool getSaveIptcCredits() const; + + void setIptcAuthor(const TQString& author); + TQString getIptcAuthor() const; + + void setIptcAuthorTitle(const TQString& authorTitle); + TQString getIptcAuthorTitle() const; + + void setIptcCredit(const TQString& credit); + TQString getIptcCredit() const; + + void setIptcSource(const TQString& source); + TQString getIptcSource() const; + + void setIptcCopyright(const TQString& copyright); + TQString getIptcCopyright() const; + + void setSaveComments(bool val); + bool getSaveComments() const; + + void setSaveDateTime(bool val); + bool getSaveDateTime() const; + + void setSaveRating(bool val); + bool getSaveRating() const; + + void setShowToolTips(bool val); + bool getShowToolTips() const; + + void setToolTipsShowFileName(bool val); + bool getToolTipsShowFileName() const; + + void setToolTipsShowFileDate(bool val); + bool getToolTipsShowFileDate() const; + + void setToolTipsShowFileSize(bool val); + bool getToolTipsShowFileSize() const; + + void setToolTipsShowImageType(bool val); + bool getToolTipsShowImageType() const; + + void setToolTipsShowImageDim(bool val); + bool getToolTipsShowImageDim() const; + + void setToolTipsShowPhotoMake(bool val); + bool getToolTipsShowPhotoMake() const; + + void setToolTipsShowPhotoDate(bool val); + bool getToolTipsShowPhotoDate() const; + + void setToolTipsShowPhotoFocal(bool val); + bool getToolTipsShowPhotoFocal() const; + + void setToolTipsShowPhotoExpo(bool val); + bool getToolTipsShowPhotoExpo() const; + + void setToolTipsShowPhotoMode(bool val); + bool getToolTipsShowPhotoMode() const; + + void setToolTipsShowPhotoFlash(bool val); + bool getToolTipsShowPhotoFlash() const; + + void setToolTipsShowPhotoWB(bool val); + bool getToolTipsShowPhotoWB() const; + + void setToolTipsShowAlbumName(bool val); + bool getToolTipsShowAlbumName() const; + + void setToolTipsShowComments(bool val); + bool getToolTipsShowComments() const; + + void setToolTipsShowTags(bool val); + bool getToolTipsShowTags() const; + + void setToolTipsShowRating(bool val); + bool getToolTipsShowRating() const; + + void setCurrentTheme(const TQString& theme); + TQString getCurrentTheme() const; + + void setUseTrash(bool val); + bool getUseTrash() const; + + void setShowTrashDeleteDialog(bool val); + bool getShowTrashDeleteDialog() const; + + void setApplySidebarChangesDirectly(bool val); + bool getApplySidebarChangesDirectly() const; + + TQString getDefaultImageFileFilter() const; + TQString getDefaultMovieFileFilter() const; + TQString getDefaultAudioFileFilter() const; + TQString getDefaultRawFileFilter() const; + + void setPreviewLoadFullImageSize(bool val); + bool getPreviewLoadFullImageSize() const; + + void setShowFolderTreeViewItemsCount(bool val); + bool getShowFolderTreeViewItemsCount() const; + + void setRecurseAlbums(bool val); + bool getRecurseAlbums() const; + + void setRecurseTags(bool val); + bool getRecurseTags() const; + + static AlbumSettings *instance(); + +private: + + void init(); + +private: + + static AlbumSettings* m_instance; + + AlbumSettingsPrivate* d; +}; + +} // namespace Digikam + +#endif // ALBUMSETTINGS_H diff --git a/src/digikam/albumthumbnailloader.cpp b/src/digikam/albumthumbnailloader.cpp new file mode 100644 index 00000000..1fe2065b --- /dev/null +++ b/src/digikam/albumthumbnailloader.cpp @@ -0,0 +1,491 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-14 + * Description : Load and cache tag thumbnails + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C includes. + +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "thumbnailjob.h" +#include "thumbnailsize.h" +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumthumbnailloader.h" +#include "albumthumbnailloader.moc" + +namespace Digikam +{ + +typedef TQMap > UrlAlbumMap; +typedef TQMap TagThumbnailMap; + +class AlbumThumbnailLoaderPrivate +{ +public: + AlbumThumbnailLoaderPrivate() + { + iconSize = AlbumSettings::instance()->getDefaultTreeIconSize(); + minBlendSize = 20; + iconAlbumThumbJob = 0; + iconTagThumbJob = 0; + //cache = new TQCache(101, 211); + } + + int iconSize; + int minBlendSize; + + ThumbnailJob *iconTagThumbJob; + + ThumbnailJob *iconAlbumThumbJob; + + UrlAlbumMap urlAlbumMap; + + TagThumbnailMap tagThumbnailMap; + + //TQCache *cache; +}; + +class AlbumThumbnailLoaderEvent : public TQCustomEvent +{ +public: + AlbumThumbnailLoaderEvent(int albumID, const TQPixmap &thumbnail) + : TQCustomEvent(TQEvent::User), + albumID(albumID), thumbnail(thumbnail) + {}; + + int albumID; + TQPixmap thumbnail; +}; + +AlbumThumbnailLoader *AlbumThumbnailLoader::m_instance = 0; + +AlbumThumbnailLoader *AlbumThumbnailLoader::instance() +{ + if (!m_instance) + m_instance = new AlbumThumbnailLoader; + return m_instance; +} + +void AlbumThumbnailLoader::cleanUp() +{ + delete m_instance; +} + +AlbumThumbnailLoader::AlbumThumbnailLoader() +{ + d = new AlbumThumbnailLoaderPrivate; + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumIconChanged(Album*)), + this, TQ_SLOT(slotIconChanged(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotIconChanged(Album*))); +} + + +AlbumThumbnailLoader::~AlbumThumbnailLoader() +{ + if (d->iconTagThumbJob) + d->iconTagThumbJob->kill(); + + if (d->iconAlbumThumbJob) + d->iconAlbumThumbJob->kill(); + //delete d->cache; + + delete d; + + m_instance = 0; +} + +TQPixmap AlbumThumbnailLoader::getStandardTagIcon(RelativeSize relativeSize) +{ + return loadIcon("tag", computeIconSize(relativeSize)); +} + +TQPixmap AlbumThumbnailLoader::getStandardTagRootIcon(RelativeSize relativeSize) +{ + return loadIcon("tag-folder", computeIconSize(relativeSize)); +} + +TQPixmap AlbumThumbnailLoader::getStandardTagIcon(TAlbum *album, RelativeSize relativeSize) +{ + if (album->isRoot()) + return getStandardTagRootIcon(relativeSize); + else + return getStandardTagIcon(relativeSize); +} + +TQPixmap AlbumThumbnailLoader::getStandardAlbumIcon(RelativeSize relativeSize) +{ + return loadIcon("folder", computeIconSize(relativeSize)); +} + +TQPixmap AlbumThumbnailLoader::getStandardAlbumRootIcon(RelativeSize relativeSize) +{ + return loadIcon("folder_image", computeIconSize(relativeSize)); +} + +TQPixmap AlbumThumbnailLoader::getStandardAlbumIcon(PAlbum *album, RelativeSize relativeSize) +{ + if (album->isRoot()) + return getStandardAlbumRootIcon(relativeSize); + else + return getStandardAlbumIcon(relativeSize); +} + +int AlbumThumbnailLoader::computeIconSize(RelativeSize relativeSize) +{ + if (relativeSize == SmallerSize) + { + // when size was 32 smaller was 20. Scale. + return lround(20.0 / 32.0 * (double)d->iconSize); + } + return d->iconSize; +} + +TQRect AlbumThumbnailLoader::computeBlendRect(int iconSize) +{ + // when drawing a 20x20 thumbnail in a 32x32 icon, starting point was (6,9). Scale. + double largerSize = iconSize; + double x = 6.0 / 32.0 * largerSize; + double y = 9.0 / 32.0 * largerSize; + double size = 20.0 / 32.0 * largerSize; + return TQRect(lround(x), lround(y), lround(size), lround(size)); +} + +TQPixmap AlbumThumbnailLoader::loadIcon(const TQString &name, int size) +{ + TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); + return iconLoader->loadIcon(name, TDEIcon::NoGroup, + size, TDEIcon::DefaultState, + 0, true); +} + +bool AlbumThumbnailLoader::getTagThumbnail(TAlbum *album, TQPixmap &icon) +{ + int size = computeIconSize(SmallerSize); + /* + if (size >= d->minBlendSize) + { + TQRect rect = computeBlendRect(size); + size = rect.width(); + } + */ + + if(!album->icon().isEmpty()) + { + if(album->icon().startsWith("/")) + { + KURL iconKURL; + iconKURL.setPath(album->icon()); + addURL(album, iconKURL); + icon = TQPixmap(); + return true; + } + else + { + icon = loadIcon(album->icon(), size); + return false; + } + } + else + { + icon = TQPixmap(); + return false; + } +} + +bool AlbumThumbnailLoader::getAlbumThumbnail(PAlbum *album) +{ + if(!album->icon().isEmpty() && d->iconSize > d->minBlendSize) + { + addURL(album, album->iconKURL()); + } + else + { + return false; + } + + return true; +} + +void AlbumThumbnailLoader::addURL(Album *album, const KURL &url) +{ + /* + TQPixmap* pix = d->cache->find(album->iconKURL().path()); + if (pix) + return pix; + */ + + // First check cached thumbnails. + // At startup, this is not relevant, as the views will add their requests in a row. + // This is to speed up context menu and IE imagedescedit + TagThumbnailMap::iterator ttit = d->tagThumbnailMap.find(album->globalID()); + if (ttit != d->tagThumbnailMap.end()) + { + // It is not necessary to return cached icon asynchronously - they could be + // returned by getTagThumbnail already - but this would make the API + // less elegant, it feels much better this way. + TQApplication::postEvent(this, new AlbumThumbnailLoaderEvent(album->globalID(), *ttit)); + return; + } + + // Check if the URL has already been added (ThumbnailJob will _not_ check this) + UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); + + if (it == d->urlAlbumMap.end()) + { + // use two IOslaves so that tag and album thumbnails are loaded + // in parallel and not first album, then tag thumbnails + if (album->type() == Album::TAG) + { + if(!d->iconTagThumbJob) + { + d->iconTagThumbJob = new ThumbnailJob(url, + d->iconSize, + true, + AlbumSettings::instance()->getExifRotate()); + connect(d->iconTagThumbJob, + TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + TQ_SLOT(slotGotThumbnailFromIcon(const KURL&, const TQPixmap&))); + connect(d->iconTagThumbJob, + TQ_SIGNAL(signalFailed(const KURL&)), + TQ_SLOT(slotThumbnailLost(const KURL&))); + } + else + { + d->iconTagThumbJob->addItem(url); + } + } + else + { + if(!d->iconAlbumThumbJob) + { + d->iconAlbumThumbJob = new ThumbnailJob(url, + d->iconSize, + true, + AlbumSettings::instance()->getExifRotate()); + connect(d->iconAlbumThumbJob, + TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + TQ_SLOT(slotGotThumbnailFromIcon(const KURL&, const TQPixmap&))); + connect(d->iconAlbumThumbJob, + TQ_SIGNAL(signalFailed(const KURL&)), + TQ_SLOT(slotThumbnailLost(const KURL&))); + } + else + { + d->iconAlbumThumbJob->addItem(url); + } + } + + // insert new entry to map, add album globalID + TQValueList &list = d->urlAlbumMap[url]; + list.remove(album->globalID()); + list.push_back(album->globalID()); + } + else + { + // only add album global ID to list which is already inserted in map + (*it).remove(album->globalID()); + (*it).push_back(album->globalID()); + } +} + +void AlbumThumbnailLoader::setThumbnailSize(int size) +{ + if (d->iconSize == size) + return; + + d->iconSize = size; + + // clear task list + d->urlAlbumMap.clear(); + // clear cached thumbnails + d->tagThumbnailMap.clear(); + + if (d->iconAlbumThumbJob) + { + d->iconAlbumThumbJob->kill(); + d->iconAlbumThumbJob= 0; + } + if (d->iconTagThumbJob) + { + d->iconTagThumbJob->kill(); + d->iconTagThumbJob= 0; + } + + emit signalReloadThumbnails(); +} + +int AlbumThumbnailLoader::thumbnailSize() const +{ + return d->iconSize; +} + +void AlbumThumbnailLoader::slotGotThumbnailFromIcon(const KURL &url, const TQPixmap &thumbnail) +{ + // We need to find all albums for which the given url has been requested, + // and emit a signal for each album. + + UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); + + if (it != d->urlAlbumMap.end()) + { + TQPixmap tagThumbnail; + + AlbumManager *manager = AlbumManager::instance(); + for (TQValueList::iterator vit = (*it).begin(); vit != (*it).end(); ++vit) + { + // look up with global id + Album *album = manager->findAlbum(*vit); + if (album) + { + if (album->type() == Album::TAG) + { + // create tag thumbnail if needed + if (tagThumbnail.isNull()) + { + tagThumbnail = createTagThumbnail(thumbnail); + d->tagThumbnailMap.insert(album->globalID(), tagThumbnail); + } + + emit signalThumbnail(album, tagThumbnail); + } + else + { + emit signalThumbnail(album, thumbnail); + } + } + } + + d->urlAlbumMap.remove(it); + } + +} + +void AlbumThumbnailLoader::customEvent(TQCustomEvent *e) +{ + // for cached thumbnails + + AlbumThumbnailLoaderEvent *atle = (AlbumThumbnailLoaderEvent *)e; + AlbumManager *manager = AlbumManager::instance(); + Album *album = manager->findAlbum(atle->albumID); + if (album) + { + if (atle->thumbnail.isNull()) + emit signalFailed(album); + else + emit signalThumbnail(album, atle->thumbnail); + } +} + +void AlbumThumbnailLoader::slotIconChanged(Album* album) +{ + if(!album || album->type() != Album::TAG) + return; + + d->tagThumbnailMap.remove(album->globalID()); +} + +TQPixmap AlbumThumbnailLoader::createTagThumbnail(const TQPixmap &albumThumbnail) +{ + // tag thumbnails are cropped + + TQPixmap tagThumbnail; + int thumbSize = TQMAX(albumThumbnail.width(), albumThumbnail.height()); + + if(!albumThumbnail.isNull() && thumbSize >= d->minBlendSize) + { + TQRect rect = computeBlendRect(thumbSize); + int w1 = albumThumbnail.width(); + int w2 = rect.width(); + int h1 = albumThumbnail.height(); + int h2 = rect.height(); + tagThumbnail.resize(w2,h2); + bitBlt(&tagThumbnail, 0, 0, &albumThumbnail, (w1-w2)/2, (h1-h2)/2, w2, h2); + } + else + { + tagThumbnail = albumThumbnail; + } + + return tagThumbnail; +} + +void AlbumThumbnailLoader::slotThumbnailLost(const KURL &url) +{ + // Same code as above, only different signal + + UrlAlbumMap::iterator it = d->urlAlbumMap.find(url); + + if (it != d->urlAlbumMap.end()) + { + AlbumManager *manager = AlbumManager::instance(); + for (TQValueList::iterator vit = (*it).begin(); vit != (*it).end(); ++vit) + { + Album *album = manager->findAlbum(*vit); + if (album) + emit signalFailed(album); + } + + d->urlAlbumMap.remove(it); + } +} + +TQPixmap AlbumThumbnailLoader::blendIcons(TQPixmap dstIcon, const TQPixmap &tagIcon) +{ + int dstIconSize = TQMAX(dstIcon.width(), dstIcon.height()); + + if (dstIconSize >= d->minBlendSize) + { + if(!tagIcon.isNull()) + { + TQRect rect = computeBlendRect(dstIconSize); + TQPainter p(&dstIcon); + p.drawPixmap(rect.x(), rect.y(), tagIcon, 0, 0, rect.width(), rect.height()); + p.end(); + } + return dstIcon; + } + else + { + return tagIcon; + } +} + +} // namespace Digikam diff --git a/src/digikam/albumthumbnailloader.h b/src/digikam/albumthumbnailloader.h new file mode 100644 index 00000000..9d08c5af --- /dev/null +++ b/src/digikam/albumthumbnailloader.h @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-14 + * Description : Load and cache tag thumbnails + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TAGTHUMBNAILLOADER_H +#define TAGTHUMBNAILLOADER_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +class TQCustomEvent; + +namespace Digikam +{ + +class Album; +class TAlbum; +class PAlbum; +class AlbumThumbnailLoaderPrivate; + +class AlbumThumbnailLoader : public TQObject +{ + TQ_OBJECT + +public: + + static AlbumThumbnailLoader *instance(); + static void cleanUp(); + + /** + * Change the size of the thumbnails. + * If the size differs from the current size, + * signalReloadThumbnails will be emitted. + */ + void setThumbnailSize(int size); + /** + * Get the current default icon size + */ + int thumbnailSize() const; + + /** + * Album thumbnail size is configurable via the settings menu. + * Some widgets use smaller icons than other widgets. + * These widgets do not need to know the currently set icon size from + * the setup and calculate a smaller size, but can simply request + * a relatively smaller icon. + * Depending on the user-chosen icon size, this size may in fact not + * be smaller than the normal size. + */ + enum RelativeSize + { + NormalSize, + SmallerSize + }; + + /** + * Request thumbnail for given album. + * The thumbnail will be loaded + * and returned asynchronously by the signals. + * If no thumbnail is associated with given album, + * no action will be taken, and false is returned. + * + */ + bool getAlbumThumbnail(PAlbum *album); + + /** + * Behaves similar to the above method. + * Tag thumbnails will be processed as appropriate. + * Tags may have associated an icon that is loaded + * synchronously by the system icon loader. + * In this case, icon is set to this icon, and false is returned. + * If no icon is associated with the tag, icon is set to null, + * and false is returned. + * If a custom icon is associated with the tag, + * it is loaded asynchronously, icon is set to null, + * and true is returned. + * Tag thumbnails are always smaller than album thumbnails - + * as small as an album thumbnail with SmallerSize. + * They are supposed to be blended into the standard tag icon + * obtained below, or used as is when SmallerSize is requested anyway. + * @return Returns true if icon is loaded asynchronously. + */ + bool getTagThumbnail(TAlbum *album, TQPixmap &icon); + + /** + * Return standard tag and album icons. + * The third methods check if album is the root, + * and returns the standard icon or the root standard icon. + */ + TQPixmap getStandardTagIcon(RelativeSize size = NormalSize); + TQPixmap getStandardTagRootIcon(RelativeSize size = NormalSize); + TQPixmap getStandardTagIcon(TAlbum *album, RelativeSize size = NormalSize); + + TQPixmap getStandardAlbumIcon(RelativeSize size = NormalSize); + TQPixmap getStandardAlbumRootIcon(RelativeSize size = NormalSize); + TQPixmap getStandardAlbumIcon(PAlbum *album, RelativeSize size = NormalSize); + + /** + * Blend tagIcon centered on dstIcon, where dstIcon is a standard + * icon of variable size and tagIcon is 12 pixels smaller. + * If height(dstIcon) < minBlendSize we return tagIcon verbatim. + */ + TQPixmap blendIcons(TQPixmap dstIcon, const TQPixmap &tagIcon); + + +signals: + + /** + * This signal is emitted as soon as a thumbnail has become available + * for given album. + * This class is a singleton, so any object connected to this + * signal might not actually have requested a thumbnail for given url + */ + void signalThumbnail(Album *album, const TQPixmap&); + + /** This signal is emitted if thumbnail generation for given album failed. + * Same considerations as above. + */ + void signalFailed(Album *album); + + /** + * Indicates that all album and tag thumbnails need to be reloaded. + * This is usually because the icon size has changed in the setup. + */ + void signalReloadThumbnails(); + +protected slots: + + void slotGotThumbnailFromIcon(const KURL&, const TQPixmap&); + void slotThumbnailLost(const KURL&); + void slotIconChanged(Album* album); + +protected: + + void customEvent(TQCustomEvent *e); + +private: + + AlbumThumbnailLoader(); + ~AlbumThumbnailLoader(); + AlbumThumbnailLoaderPrivate *d; + static AlbumThumbnailLoader *m_instance; + + void addURL(Album *album, const KURL &url); + TQPixmap loadIcon(const TQString &name, int size = 0); + TQPixmap createTagThumbnail(const TQPixmap &albumThumbnail); + int computeIconSize(RelativeSize size); + TQRect computeBlendRect(int iconSize); +}; + +} // namespace Digikam + +#endif // TAGTHUMBNAILLOADER_H diff --git a/src/digikam/albumwidgetstack.cpp b/src/digikam/albumwidgetstack.cpp new file mode 100644 index 00000000..79b19599 --- /dev/null +++ b/src/digikam/albumwidgetstack.cpp @@ -0,0 +1,282 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-13 + * Description : A widget stack to embedded album content view + * or the current image preview. + * + * Copyright (C) 2006-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "albumsettings.h" +#include "albumiconview.h" +#include "imagepreviewview.h" +#include "welcomepageview.h" +#include "mediaplayerview.h" +#include "albumwidgetstack.h" +#include "albumwidgetstack.moc" + +namespace Digikam +{ + +class AlbumWidgetStackPriv +{ + +public: + + AlbumWidgetStackPriv() + { + albumIconView = 0; + imagePreviewView = 0; + welcomePageView = 0; + mediaPlayerView = 0; + } + + AlbumIconView *albumIconView; + + ImagePreviewView *imagePreviewView; + + WelcomePageView *welcomePageView; + + MediaPlayerView *mediaPlayerView; +}; + +AlbumWidgetStack::AlbumWidgetStack(TQWidget *parent) + : TQWidgetStack(parent, 0, TQt::WDestructiveClose) +{ + d = new AlbumWidgetStackPriv; + + d->albumIconView = new AlbumIconView(this); + d->imagePreviewView = new ImagePreviewView(this); + d->welcomePageView = new WelcomePageView(this); + d->mediaPlayerView = new MediaPlayerView(this); + + addWidget(d->albumIconView, PreviewAlbumMode); + addWidget(d->imagePreviewView, PreviewImageMode); + addWidget(d->welcomePageView->view(), WelcomePageMode); + addWidget(d->mediaPlayerView, MediaPlayerMode); + + setPreviewMode(PreviewAlbumMode); + + // ----------------------------------------------------------------- + + connect(d->imagePreviewView, TQ_SIGNAL(signalNextItem()), + this, TQ_SIGNAL(signalNextItem())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalPrevItem()), + this, TQ_SIGNAL(signalPrevItem())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalEditItem()), + this, TQ_SIGNAL(signalEditItem())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalDeleteItem()), + this, TQ_SIGNAL(signalDeleteItem())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalBack2Album()), + this, TQ_SIGNAL(signalBack2Album())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalSlideShow()), + this, TQ_SIGNAL(signalSlideShow())); + + connect(d->imagePreviewView, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SLOT(slotZoomFactorChanged(double))); + + connect(d->imagePreviewView, TQ_SIGNAL(signalInsert2LightTable()), + this, TQ_SIGNAL(signalInsert2LightTable())); +} + +AlbumWidgetStack::~AlbumWidgetStack() +{ + delete d; +} + +void AlbumWidgetStack::slotEscapePreview() +{ + if (previewMode() == MediaPlayerMode) + d->mediaPlayerView->escapePreview(); +} + +AlbumIconView* AlbumWidgetStack::albumIconView() +{ + return d->albumIconView; +} + +ImagePreviewView* AlbumWidgetStack::imagePreviewView() +{ + return d->imagePreviewView; +} + +void AlbumWidgetStack::setPreviewItem(ImageInfo* info, ImageInfo *previous, ImageInfo *next) +{ + if (!info) + { + if (previewMode() == MediaPlayerMode) + d->mediaPlayerView->setMediaPlayerFromUrl(KURL()); + else if (previewMode() == PreviewImageMode) + { + d->imagePreviewView->setImageInfo(); + } + } + else + { + AlbumSettings *settings = AlbumSettings::instance(); + TQString currentFileExtension = TQFileInfo(info->kurl().path()).extension(false); + TQString mediaplayerfilter = settings->getMovieFileFilter().lower() + + settings->getMovieFileFilter().upper() + + settings->getAudioFileFilter().lower() + + settings->getAudioFileFilter().upper(); + if (mediaplayerfilter.contains(currentFileExtension) ) + { + setPreviewMode(MediaPlayerMode); + d->mediaPlayerView->setMediaPlayerFromUrl(info->kurl()); + } + else + { + // Stop media player if running... + if (previewMode() == MediaPlayerMode) + setPreviewItem(); + + d->imagePreviewView->setImageInfo(info, previous, next); + + // NOTE: No need to toggle imediatly in PreviewImageMode here, + // because we will receive a signal for that when the image preview will be loaded. + // This will prevent a flicker effect with the old image preview loaded in stack. + } + } +} + +int AlbumWidgetStack::previewMode(void) +{ + return id(visibleWidget()); +} + +void AlbumWidgetStack::setPreviewMode(int mode) +{ + if (mode != PreviewAlbumMode && mode != PreviewImageMode && + mode != WelcomePageMode && mode != MediaPlayerMode) + return; + + if (mode == PreviewAlbumMode || mode == WelcomePageMode) + { + d->albumIconView->setFocus(); + setPreviewItem(); + raiseWidget(mode); + emit signalToggledToPreviewMode(false); + } + else + { + raiseWidget(mode); + } +} + +void AlbumWidgetStack::previewLoaded() +{ + emit signalToggledToPreviewMode(true); +} + +void AlbumWidgetStack::slotZoomFactorChanged(double z) +{ + if (previewMode() == PreviewImageMode) + emit signalZoomFactorChanged(z); +} + +void AlbumWidgetStack::slotItemsUpdated(const KURL::List& list) +{ + // If item are updated from Icon View, and if we are in Preview Mode, + // We will check if the current item preview need to be reloaded. + + if (previewMode() == PreviewAlbumMode || + previewMode() == WelcomePageMode || + previewMode() == MediaPlayerMode) // What we can do with media player ? + return; + + if (list.contains(imagePreviewView()->getImageInfo()->kurl())) + d->imagePreviewView->reload(); +} + +void AlbumWidgetStack::increaseZoom() +{ + d->imagePreviewView->slotIncreaseZoom(); +} + +void AlbumWidgetStack::decreaseZoom() +{ + d->imagePreviewView->slotDecreaseZoom(); +} + +void AlbumWidgetStack::zoomTo100Percents() +{ + d->imagePreviewView->setZoomFactor(1.0); +} + +void AlbumWidgetStack::fitToWindow() +{ + d->imagePreviewView->fitToWindow(); +} + +void AlbumWidgetStack::toggleFitToWindowOr100() +{ + d->imagePreviewView->toggleFitToWindowOr100(); +} + +bool AlbumWidgetStack::maxZoom() +{ + return d->imagePreviewView->maxZoom(); +} + +bool AlbumWidgetStack::minZoom() +{ + return d->imagePreviewView->minZoom(); +} + +void AlbumWidgetStack::setZoomFactor(double z) +{ + d->imagePreviewView->setZoomFactor(z); +} + +void AlbumWidgetStack::setZoomFactorSnapped(double z) +{ + d->imagePreviewView->setZoomFactorSnapped(z); +} + +double AlbumWidgetStack::zoomFactor() +{ + return d->imagePreviewView->zoomFactor(); +} + +double AlbumWidgetStack::zoomMin() +{ + return d->imagePreviewView->zoomMin(); +} + +double AlbumWidgetStack::zoomMax() +{ + return d->imagePreviewView->zoomMax(); +} + +} // namespace Digikam diff --git a/src/digikam/albumwidgetstack.h b/src/digikam/albumwidgetstack.h new file mode 100644 index 00000000..c8a6f6ac --- /dev/null +++ b/src/digikam/albumwidgetstack.h @@ -0,0 +1,116 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-13 + * Description : A widget stack to embedded album content view + * or the current image preview. + * + * Copyright (C) 2006-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ALBUMWIDGETSTACK_H +#define ALBUMWIDGETSTACK_H + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "digikam_export.h" + +class KURL; + +namespace Digikam +{ + +class ImageInfo; +class AlbumIconView; +class ImagePreviewView; +class AlbumWidgetStackPriv; + +class DIGIKAM_EXPORT AlbumWidgetStack : public TQWidgetStack +{ +TQ_OBJECT + +public: + + enum AlbumWidgetStackMode + { + PreviewAlbumMode=0, + PreviewImageMode, + WelcomePageMode, + MediaPlayerMode + }; + +public: + + AlbumWidgetStack(TQWidget *parent=0); + ~AlbumWidgetStack(); + + AlbumIconView *albumIconView(); + ImagePreviewView *imagePreviewView(); + + void setPreviewItem(ImageInfo* info=0, ImageInfo *previous=0, ImageInfo *next=0); + int previewMode(void); + void setPreviewMode(int mode); + void previewLoaded(); + + void increaseZoom(); + void decreaseZoom(); + void fitToWindow(); + void toggleFitToWindowOr100(); + void zoomTo100Percents(); + bool maxZoom(); + bool minZoom(); + void setZoomFactor(double z); + void setZoomFactorSnapped(double z); + double zoomFactor(); + double zoomMin(); + double zoomMax(); + +signals: + + void signalNextItem(); + void signalPrevItem(); + void signalEditItem(); + void signalDeleteItem(); + void signalToggledToPreviewMode(bool); + void signalBack2Album(); + void signalSlideShow(); + void signalZoomFactorChanged(double); + void signalInsert2LightTable(); + +public slots: + + void slotEscapePreview(); + void slotItemsUpdated(const KURL::List&); + +private slots: + + void slotZoomFactorChanged(double); + +private: + + AlbumWidgetStackPriv* d; +}; + +} // namespace Digikam + +#endif /* ALBUMWIDGETSTACK_H */ diff --git a/src/digikam/cameralist.cpp b/src/digikam/cameralist.cpp new file mode 100644 index 00000000..6a380e0d --- /dev/null +++ b/src/digikam/cameralist.cpp @@ -0,0 +1,276 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-03 + * Description : Cameras list container + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "gpcamera.h" +#include "cameratype.h" +#include "cameralist.h" +#include "cameralist.moc" + +namespace Digikam +{ + +CameraList* CameraList::m_instance = 0; + +CameraList* CameraList::instance() +{ + return m_instance; +} + +class CameraListPrivate +{ +public: + + bool modified; + + TQPtrList clist; + TQString file; +}; + +CameraList::CameraList(TQObject *parent, const TQString& file) + : TQObject(parent) +{ + d = new CameraListPrivate; + d->clist.setAutoDelete(true); + d->file = file; + d->modified = false; + m_instance = this; +} + +CameraList::~CameraList() +{ + save(); + + d->clist.clear(); + delete d; + + m_instance = 0; +} + +bool CameraList::load() +{ + d->modified = false; + + TQFile cfile(d->file); + + if (!cfile.open(IO_ReadOnly)) + return false; + + TQDomDocument doc("cameralist"); + if (!doc.setContent(&cfile)) + return false; + + TQDomElement docElem = doc.documentElement(); + if (docElem.tagName()!="cameralist") + return false; + + for (TQDomNode n = docElem.firstChild(); + !n.isNull(); n = n.nextSibling()) + { + TQDomElement e = n.toElement(); + if (e.isNull()) continue; + if (e.tagName() != "item") continue; + + TQString title = e.attribute("title"); + TQString model = e.attribute("model"); + TQString port = e.attribute("port"); + TQString path = e.attribute("path"); + TQDateTime lastAccess = TQDateTime::currentDateTime(); + + if (!e.attribute("lastaccess").isEmpty()) + lastAccess = TQDateTime::fromString(e.attribute("lastaccess"), TQt::ISODate); + + CameraType *ctype = new CameraType(title, model, port, path, lastAccess); + insertPrivate(ctype); + } + + return true; +} + +bool CameraList::save() +{ + // If not modified don't save the file + if (!d->modified) + return true; + + TQDomDocument doc("cameralist"); + doc.setContent(TQString("")); + + TQDomElement docElem=doc.documentElement(); + + for (CameraType *ctype = d->clist.first(); ctype; + ctype = d->clist.next()) + { + TQDomElement elem = doc.createElement("item"); + elem.setAttribute("title", ctype->title()); + elem.setAttribute("model", ctype->model()); + elem.setAttribute("port", ctype->port()); + elem.setAttribute("path", ctype->path()); + elem.setAttribute("lastaccess", ctype->lastAccess().toString(TQt::ISODate)); + docElem.appendChild(elem); + } + + TQFile cfile(d->file); + if (!cfile.open(IO_WriteOnly)) + return false; + + TQTextStream stream(&cfile); + stream.setEncoding(TQTextStream::UnicodeUTF8); + stream << doc.toString(); + cfile.close(); + + return true; +} + +bool CameraList::changeCameraAccessTime(const TQString& cameraTitle, const TQDateTime& newDate) +{ + CameraType* cam = find(cameraTitle); + if (cam) + { + cam->setLastAccess(newDate); + d->modified = true; + save(); + return true; + } + + return false; +} + +void CameraList::insert(CameraType* ctype) +{ + if (!ctype) return; + + d->modified = true; + insertPrivate(ctype); +} + +void CameraList::remove(CameraType* ctype) +{ + if (!ctype) return; + + d->modified = true; + removePrivate(ctype); +} + +void CameraList::insertPrivate(CameraType* ctype) +{ + if (!ctype) return; + emit signalCameraAdded(ctype); + d->clist.append(ctype); +} + +void CameraList::removePrivate(CameraType* ctype) +{ + if (!ctype) return; + emit signalCameraRemoved(ctype); + d->clist.remove(ctype); +} + +TQPtrList* CameraList::cameraList() +{ + return &d->clist; +} + +CameraType* CameraList::find(const TQString& title) +{ + for (CameraType *ctype = d->clist.first(); ctype; + ctype = d->clist.next()) + { + if (ctype->title() == title) + return ctype; + } + return 0; +} + +CameraType* CameraList::autoDetect(bool& retry) +{ + retry = false; + + TQString model, port; + if (GPCamera::autoDetect(model, port) != 0) + { + retry = ( KMessageBox::warningYesNo(0, i18n("Failed to auto-detect camera; " + "please make sure it is connected " + "properly and is turned on. " + "Would you like to try again?")) + == KMessageBox::Yes ); + return 0; + } + + // check if the camera is already in the list + for (CameraType *ctype = d->clist.first(); ctype; + ctype = d->clist.next()) + { + // we can get away with checking only the model, as the auto-detection + // works only for usb cameras. so the port is always usb: + if (ctype->model() == model) + return ctype; + } + + // looks like a new camera + + // NOTE: libgphoto2 now (2.1.4+) expects port names to be + // something like "usb:001,012". but on linux these port numbers + // will change every time camera is reconnected. gphoto port funcs + // also allow regexp match, so the safe bet is to just pass in + // "usb:" and cross your fingers that user doesn't have multiple cameras + // connected at the same time (whack them if they do). + + if (port.startsWith("usb:")) + port = "usb:"; + + CameraType* ctype = new CameraType(model, model, port, "/", TQDateTime::currentDateTime()); + insert(ctype); + + return ctype; +} + +void CameraList::clear() +{ + + CameraType *ctype = d->clist.first(); + while (ctype) + { + remove(ctype); + ctype = d->clist.first(); + } +} + +} // namespace Digikam + + diff --git a/src/digikam/cameralist.h b/src/digikam/cameralist.h new file mode 100644 index 00000000..65ecbcea --- /dev/null +++ b/src/digikam/cameralist.h @@ -0,0 +1,84 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-03 + * Description : Cameras list container + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CAMERALIST_H +#define CAMERALIST_H + +// TQt includes. + +#include +#include + +class TQString; + +namespace Digikam +{ + +class CameraType; +class CameraListPrivate; + +class CameraList : public TQObject +{ + TQ_OBJECT + +public: + + CameraList(TQObject *parent, const TQString& file); + ~CameraList(); + + bool load(); + bool save(); + void clear(); + + void insert(CameraType* ctype); + void remove(CameraType* ctype); + + CameraType* find(const TQString& title); + CameraType* autoDetect(bool& retry); + TQPtrList* cameraList(); + + bool changeCameraAccessTime(const TQString& cameraTitle, const TQDateTime& newDate); + + static CameraList* instance(); + +signals: + + void signalCameraAdded(CameraType*); + void signalCameraRemoved(CameraType*); + +private: + + void insertPrivate(CameraType* ctype); + void removePrivate(CameraType* ctype); + +private: + + static CameraList *m_instance; + CameraListPrivate *d; + +}; + +} // namespace Digikam + +#endif /* CAMERALIST_H */ diff --git a/src/digikam/cameratype.cpp b/src/digikam/cameratype.cpp new file mode 100644 index 00000000..28d8383a --- /dev/null +++ b/src/digikam/cameratype.cpp @@ -0,0 +1,191 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-29 + * Description : Camera settings container. + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include + +// Local includes. + +#include "cameraui.h" +#include "cameratype.h" + +namespace Digikam +{ + +class CameraTypePrivate +{ +public: + + CameraTypePrivate() + { + action = 0; + } + + TQString title; + TQString model; + TQString port; + TQString path; + + TQDateTime lastAccess; + + TDEAction *action; + bool valid; + + TQGuardedPtr currentCameraUI; +}; + +CameraType::CameraType() +{ + d = new CameraTypePrivate; + d->valid = false; +} + +CameraType::CameraType(const TQString& title, const TQString& model, + const TQString& port, const TQString& path, + const TQDateTime& lastAccess, TDEAction *action) +{ + d = new CameraTypePrivate; + d->title = title; + d->model = model; + d->port = port; + d->path = path; + d->action = action; + d->lastAccess = lastAccess; + d->valid = true; +} + +CameraType::CameraType(const CameraType& ctype) +{ + d = new CameraTypePrivate; + d->title = ctype.d->title; + d->model = ctype.d->model; + d->port = ctype.d->port; + d->path = ctype.d->path; + d->action = ctype.d->action; + d->lastAccess = ctype.d->lastAccess; + d->valid = ctype.d->valid; +} + +CameraType::~CameraType() +{ + delete d; +} + +CameraType& CameraType::operator=(const CameraType& ctype) +{ + if (this != &ctype) + { + d->title = ctype.d->title; + d->model = ctype.d->model; + d->port = ctype.d->port; + d->path = ctype.d->path; + d->action = ctype.d->action; + d->lastAccess = ctype.d->lastAccess; + d->valid = ctype.d->valid; + } + return *this; +} + +void CameraType::setTitle(const TQString& title) +{ + d->title = title; +} + +void CameraType::setModel(const TQString& model) +{ + d->model = model; +} + +void CameraType::setPort(const TQString& port) +{ + d->port = port; +} + +void CameraType::setPath(const TQString& path) +{ + d->path = path; +} + +void CameraType::setLastAccess(const TQDateTime& lastAccess) +{ + d->lastAccess = lastAccess; +} + +void CameraType::setAction(TDEAction *action) +{ + d->action = action; +} + +void CameraType::setValid(bool valid) +{ + d->valid = valid; +} + +void CameraType::setCurrentCameraUI(CameraUI *cameraui) +{ + d->currentCameraUI = cameraui; +} + +TQString CameraType::title() const +{ + return d->title; +} + +TQString CameraType::model() const +{ + return d->model; +} + +TQString CameraType::port() const +{ + return d->port; +} + +TQString CameraType::path() const +{ + return d->path; +} + +TQDateTime CameraType::lastAccess() const +{ + return d->lastAccess; +} + +TDEAction* CameraType::action() const +{ + return d->action; +} + +bool CameraType::valid() const +{ + return d->valid; +} + +CameraUI *CameraType::currentCameraUI() const +{ + return d->currentCameraUI; +} + +} // namespace Digikam diff --git a/src/digikam/cameratype.h b/src/digikam/cameratype.h new file mode 100644 index 00000000..71ae0766 --- /dev/null +++ b/src/digikam/cameratype.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-29 + * Description : Camera settings container. + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CAMERATYPE_H +#define CAMERATYPE_H + +// TQt includes. + +#include +#include +#include + +class TDEAction; + +namespace Digikam +{ + +class CameraUI; +class CameraTypePrivate; + +class CameraType +{ +public: + + + CameraType(); + CameraType(const TQString& title, const TQString& model, + const TQString& port, const TQString& path, + const TQDateTime& lastAccess, TDEAction* action=0); + ~CameraType(); + + CameraType(const CameraType& ctype); + CameraType& operator=(const CameraType& type); + + void setTitle(const TQString& title); + void setModel(const TQString& model); + void setPort(const TQString& port); + void setPath(const TQString& path); + void setLastAccess(const TQDateTime& lastAccess); + void setAction(TDEAction *action); + void setValid(bool valid); + void setCurrentCameraUI(CameraUI *cameraui); + + TQString title() const; + TQString model() const; + TQString port() const; + TQString path() const; + TQDateTime lastAccess() const; + TDEAction* action() const; + bool valid() const; + CameraUI *currentCameraUI() const; + +private: + + CameraTypePrivate *d; +}; + +} // namespace Digikam + +#endif /* CAMERATYPE_H */ diff --git a/src/digikam/constants.h b/src/digikam/constants.h new file mode 100644 index 00000000..63cbcc80 --- /dev/null +++ b/src/digikam/constants.h @@ -0,0 +1,37 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-04-03 + * Description : Constants for Digikam-specific fields + * + * Copyright (C) 2007 by Brian Remedios + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +namespace Digikam +{ + +// Field value limits for all Digikam-specific fields (not EXIF/IPTC fields) + +static const int RatingMin = 0; +static const int RatingMax = 5; + +} // namespace Digikam + +#endif /* CONSTANTS_H */ diff --git a/src/digikam/daboutdata.h b/src/digikam/daboutdata.h new file mode 100644 index 00000000..5ff23f42 --- /dev/null +++ b/src/digikam/daboutdata.h @@ -0,0 +1,293 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-08 + * Description : digiKam about data. + * + * Copyright (C) 2008-2009 by Gilles Caulier + * Copyright (C) 2008-2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DABOUT_DATA_H +#define DABOUT_DATA_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// C Ansi includes. + +extern "C" +{ +#include +} + +// TQt includes. + +#include + +// Libkexiv2 includes. + +#include +#include + +// Libkdcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// KDE includes. + +#include +#include + +static const char digikam_version[] = "0.9.6"; +static const char showfoto_version[] = "0.9.6"; + +namespace Digikam +{ +static inline TQString libraryInfo() +{ +#if KDCRAW_VERSION < 0x000106 + TQString DcrawVer = KDcrawIface::DcrawBinary::internalVersion(); +#else + TQString librawVer = KDcrawIface::KDcraw::librawVersion(); +#endif + + TQString Exiv2Ver = KExiv2Iface::KExiv2::Exiv2Version(); + + TQString Kexiv2Ver; + +#if KEXIV2_VERSION <= 0x000106 + Kexiv2Ver = TQString(kexiv2_version); +#else + Kexiv2Ver = KExiv2Iface::KExiv2::version(); +#endif + + TQString libInfo = + TQString(I18N_NOOP("Using KExiv2 library version %1")).arg(Kexiv2Ver) + + TQString("\n") + + TQString(I18N_NOOP("Using Exiv2 library version %1")).arg(Exiv2Ver) + + TQString("\n") + + TQString(I18N_NOOP("Using KDcraw library version %1")).arg(KDcrawIface::KDcraw::version()) + + TQString("\n") + +#if KDCRAW_VERSION < 0x000106 + TQString(I18N_NOOP("Using Dcraw program version %1")).arg(DcrawVer) + +#else + TQString(I18N_NOOP("Using LibRaw version %1")).arg(librawVer) + +#endif + TQString("\n") + + TQString(I18N_NOOP("Using PNG library version %1")).arg(PNG_LIBPNG_VER_STRING); + + return libInfo; +} + +static inline const char* digiKamDescription() +{ + return I18N_NOOP("A Photo-Management Application for TDE"); +} + +static inline const char* showFotoDescription() +{ + return I18N_NOOP("TDE Photo Viewer and Editor"); +} + +static inline const char* themeDesignerDescription() +{ + return I18N_NOOP("A Color Theme Designer for digiKam"); +} + +static inline const char* copyright() +{ + return I18N_NOOP("(c) 2002-2009, digiKam developers team"); +} + +static inline const char* webProjectUrl() +{ + return "http://www.digikam.org"; +} + +static inline void authorsRegistration(TDEAboutData& aboutData) +{ + aboutData.addAuthor ( "Caulier Gilles", + I18N_NOOP("Main developer and coordinator"), + "caulier dot gilles at gmail dot com", + "http://www.digikam.org/?q=blog/3"); + + aboutData.addAuthor ( "Marcel Wiesweg", + I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de", + "http://www.digikam.org/?q=blog/8"); + + aboutData.addAuthor ( "Arnd Baecker", + I18N_NOOP("Developer"), + "arnd dot baecker at web dot de", + "http://www.digikam.org/?q=blog/133"); + + aboutData.addAuthor ( "Andi Clemens", + I18N_NOOP("Developer"), + "andi dot clemens at gmx dot net", + "http://www.digikam.org/?q=blog/135"); + + aboutData.addAuthor ( "Francisco J. Cruz", + I18N_NOOP("Developer"), + "fj dot cruz at supercable dot es", + "http://www.digikam.org/?q=blog/5"); + + aboutData.addAuthor ( "Renchi Raju", + I18N_NOOP("Developer (2002-2005)"), + "renchi at pooh dot tam dot uiuc dot edu", + 0); + + aboutData.addAuthor ( "Joern Ahrens", + I18N_NOOP("Developer (2004-2005)"), + "kde at jokele dot de", + "http://www.digikam.org/?q=blog/1"); + + aboutData.addAuthor ( "Tom Albers", + I18N_NOOP("Developer (2004-2005)"), + "tomalbers at kde dot nl", + "http://www.omat.nl/drupal/?q=blog/1"); + + aboutData.addAuthor ( "Ralf Holzer (2004)", + I18N_NOOP("Developer"), + "kde at ralfhoelzer dot com", + 0); + + aboutData.addCredit ( "Mikolaj Machowski", + I18N_NOOP("Bug reports and patches"), + "mikmach at wp dot pl", + 0); + + aboutData.addCredit ( "Achim Bohnet", + I18N_NOOP("Bug reports and patches"), + "ach at mpe dot mpg dot de", + 0); + + aboutData.addCredit ( "Luka Renko", + I18N_NOOP("Developer"), + "lure at kubuntu dot org", + 0); + + aboutData.addCredit ( "Angelo Naselli", + I18N_NOOP("Developer"), + "anaselli at linux dot it", + 0); + + aboutData.addCredit ( "Fabien Salvi", + I18N_NOOP("Webmaster"), + "fabien dot ubuntu at gmail dot com", + 0); + + aboutData.addCredit ( "Todd Shoemaker", + I18N_NOOP("Developer"), + "todd at theshoemakers dot net", + 0); + + aboutData.addCredit ( "Gregory Kokanosky", + I18N_NOOP("Developer"), + "gregory dot kokanosky at free dot fr", + 0); + + aboutData.addCredit ( "Rune Laursen", + I18N_NOOP("Danish translations"), + "runerl at skjoldhoej dot dk", + 0); + + aboutData.addCredit ( "Stefano Rivoir", + I18N_NOOP("Italian translations"), + "s dot rivoir at gts dot it", + 0); + + aboutData.addCredit ( "Jan Toenjes", + I18N_NOOP("German translations"), + "jan dot toenjes at web dot de", + 0); + + aboutData.addCredit ( "Oliver Doerr", + I18N_NOOP("German translations and beta tester"), + "oliver at doerr-privat dot de", + 0); + + aboutData.addCredit ( "Quique", + I18N_NOOP("Spanish translations"), + "quique at sindominio dot net", + 0); + + aboutData.addCredit ( "Marcus Meissner", + I18N_NOOP("Czech translations"), + "marcus at jet dot franken dot de", + 0); + + aboutData.addCredit ( "Janos Tamasi", + I18N_NOOP("Hungarian translations"), + "janusz at vnet dot hu", + 0); + + aboutData.addCredit ( "Jasper van der Marel", + I18N_NOOP("Dutch translations"), + "jasper dot van dot der dot marel at wanadoo dot nl", + 0); + + aboutData.addCredit ( "Anna Sawicka", + I18N_NOOP("Polish translations"), + "ania at kajak dot org dot pl", + 0); + + aboutData.addCredit ( "Charles Bouveyron", + I18N_NOOP("Beta tester"), + "c dot bouveyron at tuxfamily dot org", + 0); + + aboutData.addCredit ( "Richard Groult", + I18N_NOOP("Plugin contributor and beta tester"), + "Richard dot Groult at jalix dot org", + 0); + + aboutData.addCredit ( "Richard Taylor", + I18N_NOOP("Feedback and patches. Handbook writer"), + "rjt-digicam at thegrindstone dot me dot uk", + 0); + + aboutData.addCredit ( "Hans Karlsson", + I18N_NOOP("digiKam website banner and application icons"), + "karlsson dot h at home dot se", + 0); + + aboutData.addCredit ( "Aaron Seigo", + I18N_NOOP("Various usability fixes and general application polishing"), + "aseigo at kde dot org", + 0); + + aboutData.addCredit ( "Yves Chaufour", + I18N_NOOP("digiKam website, Feedback"), + "yves dot chaufour at wanadoo dot fr", + 0); + + aboutData.addCredit ( "Tung Nguyen", + I18N_NOOP("Bug reports, feedback and icons"), + "ntung at free dot fr", + 0); +} + +} // namespace Digikam + +#endif // DABOUT_DATA_H diff --git a/src/digikam/datefolderview.cpp b/src/digikam/datefolderview.cpp new file mode 100644 index 00000000..8e36742e --- /dev/null +++ b/src/digikam/datefolderview.cpp @@ -0,0 +1,482 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-27 + * Description : a folder view for date albums. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +#include +#if KDE_IS_VERSION(3,2,0) +#include +#endif + +// Local includes. + +#include "album.h" +#include "albumdb.h" +#include "albumsettings.h" +#include "ddebug.h" +#include "folderview.h" +#include "monthwidget.h" +#include "datefolderview.h" +#include "datefolderview.moc" + +namespace Digikam +{ + +class DateFolderItem : public FolderItem +{ + +public: + + DateFolderItem(TQListView* parent, DAlbum* album); + DateFolderItem(TQListViewItem* parent, DAlbum* album); + + ~DateFolderItem(); + + void refresh(); + + int compare(TQListViewItem *i, int, bool) const; + TQString date() const; + TQString name() const; + + DAlbum* album() const; + + int count() const; + void setCount(int v); + +private: + + int m_count; + + TQString m_name; + + DAlbum *m_album; +}; + +DateFolderItem::DateFolderItem(TQListView* parent, DAlbum* album) + : FolderItem(parent, TQString(), true) +{ + m_count = 0; + m_album = album; + m_name = TQString::number(album->date().year()); + setText(0, m_name); +} + +DateFolderItem::DateFolderItem(TQListViewItem* parent, DAlbum* album) + : FolderItem(parent, TQString()) +{ + m_count = 0; + m_album = album; +#if KDE_IS_VERSION(3,2,0) + m_name = TDEGlobal::locale()->calendar()->monthName(m_album->date(), false); +#else + m_name = TDEGlobal::locale()->monthName(m_album->date(), false); +#endif + setText(0, m_name); +} + +DateFolderItem::~DateFolderItem() +{ +} + +void DateFolderItem::refresh() +{ + if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount()) + setText(0, TQString("%1 (%2)").arg(m_name).arg(m_count)); + else + setText(0, m_name); +} + +int DateFolderItem::compare(TQListViewItem* i, int , bool ) const +{ + if (!i) + return 0; + + DateFolderItem* dItem = dynamic_cast(i); + if (m_album->date() == dItem->m_album->date()) + return 0; + else if (m_album->date() > dItem->m_album->date()) + return 1; + else + return -1; +} + +TQString DateFolderItem::date() const +{ + return m_album->date().toString(); +} + +TQString DateFolderItem::name() const +{ + return m_name; +} + +DAlbum* DateFolderItem::album() const +{ + return m_album; +} + +int DateFolderItem::count() const +{ + return m_count; +} + +void DateFolderItem::setCount(int v) +{ + m_count = v; + refresh(); +} + +// ----------------------------------------------------------------- + +class DateFolderViewPriv +{ +public: + + DateFolderViewPriv() + { + active = false; + listview = 0; + monthview = 0; + } + + bool active; + + TQString selected; + + FolderView *listview; + + MonthWidget *monthview; +}; + +DateFolderView::DateFolderView(TQWidget* parent) + : TQVBox(parent, "DateFolderView") +{ + d = new DateFolderViewPriv; + d->listview = new FolderView(this, "DateListView"); + d->monthview = new MonthWidget(this); + + d->listview->addColumn(i18n("My Calendar")); + d->listview->setResizeMode(TQListView::LastColumn); + d->listview->setRootIsDecorated(true); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAllDAlbumsLoaded()), + this, TQ_SLOT(slotAllDAlbumsLoaded())); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumsCleared()), + d->listview, TQ_SLOT(clear())); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalDAlbumsDirty(const TQMap&)), + this, TQ_SLOT(slotRefresh(const TQMap&))); + + connect(d->listview, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); +} + +DateFolderView::~DateFolderView() +{ + saveViewState(); + delete d; +} + +void DateFolderView::setActive(bool val) +{ + if (d->active == val) + return; + + d->active = val; + if (d->active) + { + slotSelectionChanged(); + } + else + { + d->monthview->setActive(false); + } +} + +void DateFolderView::slotAllDAlbumsLoaded() +{ + disconnect(AlbumManager::instance(), TQ_SIGNAL(signalAllDAlbumsLoaded()), + this, TQ_SLOT(slotAllDAlbumsLoaded())); + loadViewState(); +} + +void DateFolderView::slotAlbumAdded(Album* a) +{ + if (!a || a->type() != Album::DATE) + return; + + DAlbum* album = (DAlbum*)a; + TQDate date = album->date(); + + if (album->range() == DAlbum::Year) + { + DateFolderItem* item = new DateFolderItem(d->listview, album); + item->setPixmap(0, SmallIcon("date", AlbumSettings::instance()->getDefaultTreeIconSize())); + album->setExtraData(this, item); + return; + } + + TQString yr = TQString::number(date.year()); + TQListViewItem* parent = findRootItemByYear(yr); + + if (parent) + { + DateFolderItem* item = new DateFolderItem(parent, album); + item->setPixmap(0, SmallIcon("date", AlbumSettings::instance()->getDefaultTreeIconSize())); + album->setExtraData(this, item); + } +} + +void DateFolderView::slotAlbumDeleted(Album* a) +{ + if (!a || a->type() != Album::DATE) + return; + + DAlbum* album = (DAlbum*)a; + + DateFolderItem* item = (DateFolderItem*) album->extraData(this); + if (item) + { + delete item; + album->removeExtraData(this); + } +} + +void DateFolderView::slotSelectionChanged() +{ + if (!d->active) + return; + + d->monthview->setActive(false); + + TQListViewItem* selItem = 0; + TQListViewItemIterator it( d->listview ); + while (it.current()) + { + if (it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if (!selItem) + { + AlbumManager::instance()->setCurrentAlbum(0); + return; + } + + DateFolderItem* dateItem = dynamic_cast(selItem); + if (!dateItem) + { + AlbumManager::instance()->setCurrentAlbum(0); + return; + } + + AlbumManager::instance()->setCurrentAlbum(dateItem->album()); + + if (dateItem->album()->range() == DAlbum::Month) + { + TQDate date = dateItem->album()->date(); + d->monthview->setActive(true); + d->monthview->setYearMonth(date.year(), date.month()); + } +} + +void DateFolderView::loadViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + TQString selected; + if(config->hasKey("Last Selected Date")) + { + selected = config->readEntry("Last Selected Date"); + } + + TQStringList openFolders; + if(config->hasKey("Open Date Folders")) + { + openFolders = config->readListEntry("Open Date Folders"); + } + + DateFolderItem *item; + TQString id; + TQListViewItemIterator it(d->listview); + for( ; it.current(); ++it) + { + item = dynamic_cast(it.current()); + id = item->date(); + if(openFolders.contains(id)) + d->listview->setOpen(item, true); + else + d->listview->setOpen(item, false); + + if(id == selected) + d->listview->setSelected(item, true); + } +} + +void DateFolderView::saveViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + DateFolderItem *item = dynamic_cast(d->listview->selectedItem()); + if(item) + config->writeEntry("Last Selected Date", item->date()); + + TQStringList openFolders; + TQListViewItemIterator it(d->listview); + item = dynamic_cast(d->listview->firstChild()); + while(item) + { + // Storing the years only, a month cannot be open + if(item && d->listview->isOpen(item)) + openFolders.push_back(item->date()); + item = dynamic_cast(item->nextSibling()); + } + config->writeEntry("Open Date Folders", openFolders); +} + +void DateFolderView::gotoDate(const TQDate& dt) +{ + DateFolderItem *item = 0; + TQDate id; + + TQDate date = TQDate(dt.year(), dt.month(), 1); + + // Find that date in the side-bar list. + TQListViewItemIterator it(d->listview); + for( ; it.current(); ++it) + { + item = dynamic_cast(it.current()); + if (item->album()) + { + id = item->album()->date(); + if(id == date) + { + d->listview->setSelected(item, true); + d->listview->ensureItemVisible(item); + } + } + } +} + +void DateFolderView::setSelected(TQListViewItem *item) +{ + if(!item) + return; + + d->listview->setSelected(item, true); + d->listview->ensureItemVisible(item); +} + +TQListViewItem *DateFolderView::findRootItemByYear(const TQString& year) +{ + TQListViewItemIterator it(d->listview); + + while (it.current()) + { + DateFolderItem* item = dynamic_cast(*it); + if (item) + { + if (item->album()->range() == DAlbum::Year && item->name() == year) + return item; + } + ++it; + } + return 0; +} + +void DateFolderView::refresh() +{ + TQListViewItemIterator it(d->listview); + + while (it.current()) + { + DateFolderItem* item = dynamic_cast(*it); + if (item) + item->refresh(); + ++it; + } +} + +void DateFolderView::slotRefresh(const TQMap& yearMonthMap) +{ + TQListViewItemIterator it(d->listview); + + while (it.current()) + { + DateFolderItem* item = dynamic_cast(*it); + if (item) + { + TQDate date = item->album()->date(); + + if (item->album()->range() == DAlbum::Month) + { + TQMap::const_iterator it2 = yearMonthMap.find(YearMonth(date.year(), date.month())); + if ( it2 != yearMonthMap.end() ) + item->setCount(it2.data()); + } + else + { + int count = 0; + for ( TQMap::const_iterator it2 = yearMonthMap.begin(); + it2 != yearMonthMap.end(); ++it2 ) + { + if (it2.key().first == date.year()) + count += it2.data(); + } + item->setCount(count); + } + } + ++it; + } +} + +} // namespace Digikam diff --git a/src/digikam/datefolderview.h b/src/digikam/datefolderview.h new file mode 100644 index 00000000..2e467fe3 --- /dev/null +++ b/src/digikam/datefolderview.h @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-27 + * Description : a folder view for date albums. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DATEFOLDERVIEW_H +#define DATEFOLDERVIEW_H + +// TQt includes. + +#include + +// Local includes. + +#include "albummanager.h" +#include "folderitem.h" + +class TQListViewItem; + +namespace Digikam +{ + +class DateFolderViewPriv; +class DAlbum; + +class DateFolderView : public TQVBox +{ + TQ_OBJECT + +public: + + DateFolderView(TQWidget* parent); + ~DateFolderView(); + + void setActive(bool val); + + void setSelected(TQListViewItem *item); + + void gotoDate(const TQDate& dt); + void refresh(); + +private slots: + + void slotAllDAlbumsLoaded(); + void slotAlbumAdded(Album* album); + void slotAlbumDeleted(Album* album); + void slotSelectionChanged(); + void slotRefresh(const TQMap&); + +private: + + /** + * load the last view state from disk + */ + void loadViewState(); + + /** + * writes the view state to disk + */ + void saveViewState(); + + TQListViewItem *findRootItemByYear(const TQString& year); + + DateFolderViewPriv* d; +}; + +} // namespace Digikam + +#endif /* DATEFOLDERVIEW_H */ diff --git a/src/digikam/dcopiface.cpp b/src/digikam/dcopiface.cpp new file mode 100644 index 00000000..49f051d9 --- /dev/null +++ b/src/digikam/dcopiface.cpp @@ -0,0 +1,52 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-12 + * Description : a DCOP interface. + * + * Copyright (C) 2005 by Leonid Zeitlin + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "dcopiface.h" +#include "dcopiface.moc" + +namespace Digikam +{ + +DCOPIface::DCOPIface(TQObject *parent, const char *name) + : TQObject(parent, name), DCOPObject(name) +{ +} + +DCOPIface::~DCOPIface() +{ +} + +void DCOPIface::detectCamera() +{ + emit signalCameraAutoDetect(); +} + +void DCOPIface::downloadFrom( const TQString &folder) +{ + emit signalDownloadImages( folder ); +} + +} // namespace Digikam + diff --git a/src/digikam/dcopiface.h b/src/digikam/dcopiface.h new file mode 100644 index 00000000..9367e6b1 --- /dev/null +++ b/src/digikam/dcopiface.h @@ -0,0 +1,96 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-12 + * Description : a DCROP interface. + * + * Copyright (C) 2005 by Leonid Zeitlin + * Copyright (C) 2006 Tom Albers + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DCOPIFACE_H +#define DCOPIFACE_H + +// TQt includes. + +#include +#include + +namespace Digikam +{ + +/** +* This class implements a DCOP interface for DigiKam. +* At the moment it supports only one method, @ref cameraAutoDetect to open camera dialog +* +* @short DCOP interface for DigiKam +* @author Leonid Zeitlin +*/ + +class DCOPIface : public TQObject, public DCOPObject +{ + K_DCOP + TQ_OBJECT + +public: + + /** + * Standard constructor. + * @param parent Parent object reference, passed to @ref TQObject constructor + * @param name Specifis the name of the object, passed to @ref TQObject constructor + */ + DCOPIface(TQObject *parent = 0, const char *name = 0); + + /** + * Standard destructor + */ + ~DCOPIface(); + +signals: + + /** + * This signal is emitted when @ref cameraAutoDetect() is called via DCOP + */ + void signalCameraAutoDetect(); + + /** + * This signal is emitted when @ref downloadFrom() is called via DCOP + * @param folder the path passed tp downloadFrom earlier + */ + void signalDownloadImages( const TQString& folder); + + +public: + +k_dcop: + /** + * This method can be called via DCOP to auto-detect attached camera and show DigiKam camera dialog + * For example, a hotplug script can call it when a USB camera is attached to the computer + */ + ASYNC detectCamera(); + + /** + * This method can be called via DCOP to auto-detect attached camera and + * show DigiKam camera dialog. For example, a hotplug script can call it + * when a USB camera is attached to the computer + */ + ASYNC downloadFrom( const TQString &folder ); +}; + +} // namespace Digikam + +#endif // DCOPIFACE_H diff --git a/src/digikam/digikam.desktop b/src/digikam/digikam.desktop new file mode 100644 index 00000000..ba05db76 --- /dev/null +++ b/src/digikam/digikam.desktop @@ -0,0 +1,159 @@ +[Desktop Entry] +Type=Application +Categories=Qt;TDE;Graphics; +Exec=digikam -caption "%c" %i +Icon=digikam +X-DocPath=digikam/index.html +Name=DigiKam +Name[ast]=DigiKam +Name[be]=DigiKam +Name[bg]=DigiKam +Name[bs]=DigiKam +Name[ca]=DigiKam +Name[ca@valencia]=DigiKam +Name[cs]=DigiKam +Name[da]=DigiKam +Name[de]=DigiKam +Name[el]=DigiKam +Name[en_GB]=DigiKam +Name[es]=DigiKam +Name[et]=DigiKam +Name[eu]=DigiKam +Name[fi]=DigiKam +Name[fr]=DigiKam +Name[ga]=DigiKam +Name[gl]=DigiKam +Name[he]=DigiKam +Name[hne]=डिजीकैम +Name[hr]=DigiKam +Name[hu]=DigiKam +Name[is]=DigiKam +Name[it]=DigiKam +Name[ja]=DigiKam +Name[km]=DigiKam ​ +Name[lt]=DigiKam +Name[lv]=DigiKam +Name[mai]=DigiKam +Name[mr]=डिजिकॅम +Name[ms]=DigiKam +Name[nb]=DigiKam +Name[nds]=DigiKam +Name[ne]=डिजिकà¥à¤¯à¤¾à¤® +Name[nl]=DigiKam +Name[nn]=DigiKam +Name[pa]=ਡਿਜਿਕੈਮ +Name[pl]=DigiKam +Name[pt]=DigiKam +Name[pt_BR]=DigiKam +Name[ro]=DigiKam +Name[ru]=DigiKam +Name[sk]=DigiKam +Name[sl]=DigiKam +Name[sq]=DigiKam +Name[sv]=Digikam +Name[th]=ดิจิà¹à¸„ม +Name[tr]=DigiKam +Name[ug]=DigiKam +Name[uk]=DigiKam +Name[vi]=DigiKam +Name[x-test]=xxDigiKamxx +Name[zh_CN]=DigiKam +Name[zh_TW]=DigiKam +GenericName=Photo Management Program +GenericName[ast]=Programa de xestión de semeyes +GenericName[bg]=Управление на Ñнимки в KDE +GenericName[bs]=Program za upravljanje fotografijama +GenericName[ca]=Programa de gestió de fotografies +GenericName[ca@valencia]=Programa de gestió de fotografies +GenericName[cs]=Program pro správu fotografií +GenericName[da]=Program til fotohÃ¥ndtering +GenericName[de]=Fotoverwaltung +GenericName[el]=ΠÏόγÏαμμα διαχείÏισης φωτογÏαφιών +GenericName[en_GB]=Photo Management Program +GenericName[es]=Programa de gestión de fotos +GenericName[et]=Fotohaldur +GenericName[eu]=Argazkiak kudeatzeko programa +GenericName[fi]=Valokuva–hallintaohjelma +GenericName[fr]=Programme de gestion de photos +GenericName[ga]=Clár Bainisteoireachta Grianghraf +GenericName[gl]=Xestor de álbums de fotos +GenericName[hne]=फोटो पà¥à¤°à¤¬à¤‚धन पà¥à¤°à¥‹à¤—à¥à¤°à¤¾à¤® +GenericName[hr]=Program za upravljanje fotografijama +GenericName[hu]=FényképkezelÅ‘ program +GenericName[is]=Ljósmyndameðhöndlunarforrit +GenericName[it]=Programma di gestione fotografica +GenericName[ja]=フォト管ç†ãƒ—ログラム +GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ទូរសáŸáž–្ទ +GenericName[lt]=Nuotraukų tvarkymo programa +GenericName[lv]=FotogrÄfiju pÄrvaldÄ«bas programma +GenericName[mr]=फोटो वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤¨ कारà¥à¤¯à¤•à¥à¤°à¤® +GenericName[ms]=Program Pengurusan Gambar +GenericName[nb]=KDE Fotobehandling +GenericName[nds]=Fotopleeg-Programm +GenericName[nl]=Fotobeheerprogramma +GenericName[nn]=Fotohandsamingsprogram +GenericName[pa]=ਫੋਟੋ ਪਰਬੰਧ ਪਰੋਗਰਾਮ +GenericName[pl]=Program do zarzÄ…dzania zdjÄ™ciami +GenericName[pt]=Programa de Gestão de Fotos +GenericName[pt_BR]=Programa de gerenciamento de fotos +GenericName[ro]=Program pentru gestionarea fotografiilor +GenericName[ru]=Программа ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ñми +GenericName[sk]=Program na správu fotografií +GenericName[sl]=Program za upravljanje fotografij +GenericName[sq]=Program Për Menaxhimin e Fotove +GenericName[sv]=Fotohanteringsprogram +GenericName[th]=เครื่องมือจัดà¸à¸²à¸£à¸ à¸²à¸žà¸–่าย +GenericName[tr]=FotoÄŸraf Yönetim Aracı +GenericName[ug]=سۈرەت باشقۇرۇش باشقۇرۇش پروگراممىسى +GenericName[uk]=Програма Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ñ–Ñми +GenericName[x-test]=xxPhoto Management Programxx +GenericName[zh_CN]=照片管ç†ç¨‹åº +GenericName[zh_TW]=相片管ç†ç¨‹å¼ +Comment=Manage your photographs like a professional with the power of open source +Comment[ast]=Xestiona les tos semeyes como un profesional usando software llibre +Comment[bg]=Обработвайте Ñнимките Ñи профеÑионално Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰Ñ‚Ð° на ÑÐ²Ð¾Ð±Ð¾Ð´Ð½Ð¸Ñ Ñофтуер +Comment[bs]=Upravljajte fotografijama kao profesionalac snagom otvorenog izvornog koda +Comment[ca]=Gestioneu les vostres fotografies com un professional amb la potència del programari lliure +Comment[ca@valencia]=Gestioneu les vostres fotografies com un professional amb la potència del programari lliure +Comment[cs]=Spravujte své fotografie jako profesionál pomocí open source +Comment[da]=HÃ¥ndtér dine fotografier som en professionel med kraften fra open source +Comment[de]=Verwalten Sie Ihre Bilder wie ein Profi mit allen Möglichkeiten von Open Source. +Comment[el]=ΔιαχειÏιστείτε τις φωτογÏαφίες σας όπως οι επαγγελματίες με τη δÏναμη του ελεÏθεÏου Î»Î¿Î³Î¹ÏƒÎ¼Î¹ÎºÎ¿Ï +Comment[en_GB]=Manage your photographs like a professional with the power of open source +Comment[es]=Gestione sus fotos como un profesional usando software libre +Comment[et]=Fotode haldamine profina avatud lähtekoodiga tarkvara võimsust kasutades +Comment[eu]=Kudeatu zure argazkiak profesional baten antzera iturburu irekiaren indarrarekin +Comment[fi]=Ammattilaistasoinen avoimen lähdekoodin ohjelma valokuvien hallintaan +Comment[fr]=Gérez vos photos comme un professionnel avec la puissance de l'« Open Source » +Comment[gl]=Xestione as súas fotografías como un profesional coa potencia do software de fontes abertas +Comment[hne]=ओपन सोरà¥à¤¸ के पावर से आप मन अपन फोटो ल पà¥à¤°à¥‹à¤«à¥‡à¤¸à¤¨à¤² जइसन मैनेज कर सकथो +Comment[hr]=Upravljajte svojim fotografijama kao profesionalac uz moć slobodnog softvera +Comment[hu]=Kezelje fényképeit profiként a nyílt forrás erejével +Comment[is]=Sýslaðu með ljósmyndirnar þínar í krafti opins hugbúnaðar +Comment[it]=Gestisci le tue fotografie come un professionista con la forza del software libero +Comment[ja]=オープンソースã®åŠ›ã§ã‚ãªãŸã®å†™çœŸã‚’プロã®ã‚ˆã†ã«ç®¡ç†ã—ã¾ã™ +Comment[km]=គ្រប់គ្រង​​រូបážážâ€‹ážšáž”ស់​អ្នក​ ដូច​ជា​អ្នក​អាជីព​ដែល​មាន​ážáž¶áž˜â€‹áž–ល​លើ​កម្មវិធី​ប្រភព​កូដ​ចំហ +Comment[lt]=Tvarkykite savo nuotraukas kaip profesionalas su atviro kodo jÄ—ga +Comment[lv]=PÄrvaldiet savas fotogrÄfijas kÄ profesionÄlis, izmantojot atvÄ“rtÄ pirmkoda spÄ“ku +Comment[mr]=ओपन सोरà¥à¤¸à¤šà¥à¤¯à¤¾ बळाने तà¥à¤®à¤šà¥‡ फोटो वà¥à¤¯à¤¾à¤µà¤¸à¤¾à¤¯à¤¿à¤•à¤¾à¤ªà¥à¤°à¤®à¤¾à¤£à¥‡ वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¥€à¤¤ करा +Comment[nb]=HÃ¥ndter dine fotografier som en proff med kraften i Ã¥pen programvare +Comment[nds]=Pleeg Dien Fotos as'n Fachmann. - Mit de Knööv vun Apenborn. +Comment[nl]=Beheer uw foto's als een professional met de kracht van opensource +Comment[pa]=ਆਪਣੀਆਂ ਫੋਟੋਆਂ ਨੂੰ ਇੱਕ ਪਰੋਫੈਸ਼ਨਲ ਵਾਂਗ ਓਪਨ ਸੋਰਸ ਦੀ ਮੱਦਦ ਨਾਲ ਸੰਭਾਲੋ +Comment[pl]=ZarzÄ…dzaj swoimi zdjÄ™ciami jak profesjonalista z mocÄ… open source +Comment[pt]=Faça a gestão das suas fotografias como um profissional, graças ao 'software' livre +Comment[pt_BR]=Gerencie suas fotografias como um profissional, com o poder do código aberto +Comment[ro]=GestionaÈ›i-vă fotografiile ca un profesionist profitînd de puterea aplicaÈ›iilor cu sursă deschisă +Comment[ru]=УправлÑйте Ñвоей коллекцией фотографий на профеÑÑиональном уровне Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñвободного программного обеÑÐ¿ÐµÑ‡ÐµÐ½Ð¸Ñ +Comment[sk]=Spravujte svoje fotografie ako profesionál pomocou open source +Comment[sl]=Upravljajte s svojimi fotografijami kot profesionalec z moÄjo odprte kode +Comment[sq]=Menaxho fotografitë e tua si një profesionist me fuqinë e burimit të hapur +Comment[sv]=Hantera dina fotografier som ett proffs med kraftfull öppen källkod +Comment[th]=จัดà¸à¸²à¸£à¸ à¸²à¸žà¸–่ายของคุณดั่งมืออาชีพ ด้วยพลังสร้างสรรค์ของโอเพนซอร์ส +Comment[tr]=Açık kaynağın gücüyle fotoÄŸraflarınızı bir profesyonel gibi düzenleyin +Comment[ug]=ئوچۇق مەنبەلىك يۇمشاق دÛتاللارنىڭ كۈچىدىن پايدىلىنىپ سۈرەتلىرىڭىزنى كەسپىي خادىملاردەك باشقۇرالايسىز +Comment[uk]=Керуйте вашими фотографіÑми Ñк профеÑіонал за допомогою вільного програмного Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ +Comment[x-test]=xxManage your photographs like a professional with the power of open sourcexx +Comment[zh_CN]=ä¸“ä¸šç…§ç‰‡ç®¡ç† å½°æ˜¾å¼€æºåŠ›é‡ +Comment[zh_TW]=é‹ç”¨é–‹æ”¾åŽŸå§‹ç¢¼çš„力é‡åƒå°ˆæ¥­äººå£«èˆ¬ç®¡ç†æ‚¨çš„照片 +Terminal=false diff --git a/src/digikam/digikam_export.h b/src/digikam/digikam_export.h new file mode 100644 index 00000000..0d221918 --- /dev/null +++ b/src/digikam/digikam_export.h @@ -0,0 +1,45 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-31-01 + * Description : gcc export extension support + * + * Copyright (c) 2005 Laurent Montel + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef _DIGIKAM_EXPORT_H +#define _DIGIKAM_EXPORT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef __TDE_HAVE_GCC_VISIBILITY +#define DIGIKAM_EXPORT __attribute__ ((visibility("default"))) +#define DIGIKAMIMAGEPLUGINS_EXPORT DIGIKAM_EXPORT +#define DIGIKAMIMAGEEDITOR_EXPORT DIGIKAM_EXPORT +#define DIGIKAMIMAGEFILTERS_EXPORT DIGIKAM_EXPORT +#define DIGIKAMIMAGEWIDGET_EXPORT DIGIKAM_EXPORT +#else +#define DIGIKAM_EXPORT +#define DIGIKAMIMAGEPLUGINS_EXPORT +#define DIGIKAMIMAGEEDITOR_EXPORT +#define DIGIKAMIMAGEFILTERS_EXPORT +#define DIGIKAMIMAGEWIDGET_EXPORT +#endif +#endif /* _DIGIKAM_EXPORT_H */ + diff --git a/src/digikam/digikamapp.cpp b/src/digikam/digikamapp.cpp new file mode 100644 index 00000000..5b12575a --- /dev/null +++ b/src/digikam/digikamapp.cpp @@ -0,0 +1,2094 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : main digiKam interface implementation + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2006 by Tom Albers + * Copyright (C) 2002-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libKipi includes. + +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "ddebug.h" +#include "dlogoaction.h" +#include "album.h" +#include "albumlister.h" +#include "albumthumbnailloader.h" +#include "albumiconviewfilter.h" +#include "cameratype.h" +#include "cameraui.h" +#include "setup.h" +#include "setupplugins.h" +#include "setupeditor.h" +#include "setupicc.h" +#include "rawcameradlg.h" +#include "lighttablewindow.h" +#include "imagewindow.h" +#include "imageinfo.h" +#include "thumbnailsize.h" +#include "themeengine.h" +#include "scanlib.h" +#include "loadingcacheinterface.h" +#include "imageattributeswatch.h" +#include "batchthumbsgenerator.h" +#include "batchalbumssyncmetadata.h" +#include "dcopiface.h" +#include "digikamappprivate.h" +#include "digikamapp.h" +#include "digikamapp.moc" + +using TDEIO::Job; +using TDEIO::UDSEntryList; +using TDEIO::UDSEntry; + +namespace Digikam +{ + +DigikamApp* DigikamApp::m_instance = 0; + +DigikamApp::DigikamApp() + : TDEMainWindow( 0, "Digikam" ) +{ + d = new DigikamAppPriv; + m_instance = this; + d->config = kapp->config(); + d->config->setGroup("General Settings"); + + if(d->config->readBoolEntry("Show Splash", true) && + !kapp->isRestored()) + { + d->splashScreen = new SplashScreen("digikam-splash.png"); + d->splashScreen->show(); + } + + if(d->splashScreen) + d->splashScreen->message(i18n("Initializing...")); + + // Register image formats (especially for TIFF ) + KImageIO::registerFormats(); + + d->albumSettings = new AlbumSettings(); + d->albumSettings->readSettings(); + + d->albumManager = new Digikam::AlbumManager(); + + AlbumLister::instance(); + + d->cameraMediaList = new TDEPopupMenu; + + connect(d->cameraMediaList, TQ_SIGNAL( aboutToShow() ), + this, TQ_SLOT(slotCameraMediaMenu())); + + d->cameraList = new CameraList(this, locateLocal("appdata", "cameras.xml")); + + connect(d->cameraList, TQ_SIGNAL(signalCameraAdded(CameraType *)), + this, TQ_SLOT(slotCameraAdded(CameraType *))); + + connect(d->cameraList, TQ_SIGNAL(signalCameraRemoved(CameraType *)), + this, TQ_SLOT(slotCameraRemoved(CameraType *))); + + setupView(); + setupStatusBar(); + setupAccelerators(); + setupActions(); + + applyMainWindowSettings(d->config); + + // Check ICC profiles repository availability + + if(d->splashScreen) + d->splashScreen->message(i18n("Checking ICC repository")); + + d->validIccPath = SetupICC::iccRepositoryIsValid(); + +#if KDCRAW_VERSION < 0x000106 + // Check witch dcraw version available + + if(d->splashScreen) + d->splashScreen->message(i18n("Checking dcraw version")); + + KDcrawIface::DcrawBinary::instance()->checkSystem(); +#endif + + if(d->splashScreen) + d->splashScreen->message(i18n("Scan Albums")); + + d->albumManager->setLibraryPath(d->albumSettings->getAlbumLibraryPath(), d->splashScreen); + + // Read albums from database + if(d->splashScreen) + d->splashScreen->message(i18n("Reading database")); + + d->albumManager->startScan(); + + // Load KIPI Plugins. + loadPlugins(); + + // Load Themes + populateThemes(); + + setAutoSaveSettings(); + + d->dcopIface = new DCOPIface(this, "camera"); + + connect(d->dcopIface, TQ_SIGNAL(signalCameraAutoDetect()), + this, TQ_SLOT(slotDcopCameraAutoDetect())); + + connect(d->dcopIface, TQ_SIGNAL(signalDownloadImages( const TQString & )), + this, TQ_SLOT(slotDcopDownloadImages(const TQString &))); +} + +DigikamApp::~DigikamApp() +{ + ImageAttributesWatch::shutDown(); + + // Close and delete image editor instance. + + if (ImageWindow::imagewindowCreated()) + ImageWindow::imagewindow()->close(true); + + // Close and delete light table instance. + + if (LightTableWindow::lightTableWindowCreated()) + LightTableWindow::lightTableWindow()->close(true); + + if (d->view) + delete d->view; + + d->albumIconViewFilter->saveSettings(); + d->albumSettings->setRecurseAlbums(d->recurseAlbumsAction->isChecked()); + d->albumSettings->setRecurseTags(d->recurseTagsAction->isChecked()); + d->albumSettings->saveSettings(); + delete d->albumSettings; + + delete d->albumManager; + delete AlbumLister::instance(); + + ImageAttributesWatch::cleanUp(); + LoadingCacheInterface::cleanUp(); +#if KDCRAW_VERSION < 0x000106 + KDcrawIface::DcrawBinary::cleanUp(); +#endif + AlbumThumbnailLoader::cleanUp(); + + m_instance = 0; + + delete d; +} + +DigikamApp* DigikamApp::getinstance() +{ + return m_instance; +} + +void DigikamApp::show() +{ + // Remove Splashscreen. + + if(d->splashScreen) + { + d->splashScreen->finish(this); + delete d->splashScreen; + d->splashScreen = 0; + } + + // Display application window. + + TDEMainWindow::show(); + + // Report errors from ICC repository path. + + if(!d->validIccPath) + { + TQString message = i18n("

ICC profiles path seems to be invalid.

" + "

If you want to set it now, select \"Yes\", otherwise " + "select \"No\". In this case, \"Color Management\" feature " + "will be disabled until you solve this issue

"); + + if (KMessageBox::warningYesNo(this, message) == KMessageBox::Yes) + { + if (!setup(true)) + { + d->config->setGroup("Color Management"); + d->config->writeEntry("EnableCM", false); + d->config->sync(); + } + } + else + { + d->config->setGroup("Color Management"); + d->config->writeEntry("EnableCM", false); + d->config->sync(); + } + } + +#if KDCRAW_VERSION < 0x000106 + // Report errors from dcraw detection. + KDcrawIface::DcrawBinary::instance()->checkReport(); +#endif + + // Init album icon view zoom factor. + slotThumbSizeChanged(d->albumSettings->getDefaultIconSize()); +} + +const TQPtrList& DigikamApp::menuImageActions() +{ + return d->kipiImageActions; +} + +const TQPtrList& DigikamApp::menuBatchActions() +{ + return d->kipiBatchActions; +} + +const TQPtrList& DigikamApp::menuAlbumActions() +{ + return d->kipiAlbumActions; +} + +const TQPtrList DigikamApp::menuImportActions() +{ + TQPtrList importMenu; + importMenu = d->kipiFileActionsImport; + importMenu.append( d->albumImportAction ); + importMenu.append( d->addImagesAction ); + return importMenu; +} + +const TQPtrList DigikamApp::menuExportActions() +{ + return d->kipiFileActionsExport; +} + +void DigikamApp::autoDetect() +{ + // Called from main if command line option is set + + if(d->splashScreen) + d->splashScreen->message(i18n("Auto-detect camera")); + + TQTimer::singleShot(0, this, TQ_SLOT(slotCameraAutoDetect())); +} + +void DigikamApp::downloadFrom(const TQString &cameraGuiPath) +{ + // Called from main if command line option is set + + if (!cameraGuiPath.isNull()) + { + d->cameraGuiPath = cameraGuiPath; + + if(d->splashScreen) + d->splashScreen->message(i18n("Opening Download Dialog")); + + TQTimer::singleShot(0, this, TQ_SLOT(slotDownloadImages())); + } +} + +bool DigikamApp::queryClose() +{ + if (ImageWindow::imagewindowCreated()) + { + return ImageWindow::imagewindow()->queryClose(); + } + else + return true; +} + +void DigikamApp::setupView() +{ + if(d->splashScreen) + d->splashScreen->message(i18n("Initializing Main View")); + + d->view = new DigikamView(this); + setCentralWidget(d->view); + d->view->applySettings(); + + connect(d->view, TQ_SIGNAL(signalAlbumSelected(bool)), + this, TQ_SLOT(slotAlbumSelected(bool))); + + connect(d->view, TQ_SIGNAL(signalTagSelected(bool)), + this, TQ_SLOT(slotTagSelected(bool))); + + connect(d->view, TQ_SIGNAL(signalImageSelected(const TQPtrList&, bool, bool, const KURL::List&)), + this, TQ_SLOT(slotImageSelected(const TQPtrList&, bool, bool, const KURL::List&))); +} + +void DigikamApp::setupStatusBar() +{ + d->statusProgressBar = new StatusProgressBar(statusBar()); + d->statusProgressBar->setAlignment(TQt::AlignLeft|TQt::AlignVCenter); + d->statusProgressBar->setMaximumHeight(fontMetrics().height()+4); + statusBar()->addWidget(d->statusProgressBar, 100, true); + + //------------------------------------------------------------------------------ + + d->albumIconViewFilter = new AlbumIconViewFilter(statusBar()); + d->albumIconViewFilter->setMaximumHeight(fontMetrics().height()+4); + statusBar()->addWidget(d->albumIconViewFilter, 100, true); + + //------------------------------------------------------------------------------ + + d->statusZoomBar = new StatusZoomBar(statusBar()); + d->statusZoomBar->setMaximumHeight(fontMetrics().height()+4); + statusBar()->addWidget(d->statusZoomBar, 1, true); + + //------------------------------------------------------------------------------ + + d->statusNavigateBar = new StatusNavigateBar(statusBar()); + d->statusNavigateBar->setMaximumHeight(fontMetrics().height()+4); + statusBar()->addWidget(d->statusNavigateBar, 1, true); + + //------------------------------------------------------------------------------ + + connect(d->statusZoomBar, TQ_SIGNAL(signalZoomMinusClicked()), + d->view, TQ_SLOT(slotZoomOut())); + + connect(d->statusZoomBar, TQ_SIGNAL(signalZoomPlusClicked()), + d->view, TQ_SLOT(slotZoomIn())); + + connect(d->statusZoomBar, TQ_SIGNAL(signalZoomSliderChanged(int)), + this, TQ_SLOT(slotZoomSliderChanged(int))); + + connect(d->view, TQ_SIGNAL(signalThumbSizeChanged(int)), + this, TQ_SLOT(slotThumbSizeChanged(int))); + + connect(d->view, TQ_SIGNAL(signalZoomChanged(double, int)), + this, TQ_SLOT(slotZoomChanged(double, int))); + + connect(d->view, TQ_SIGNAL(signalTogglePreview(bool)), + this, TQ_SLOT(slotTogglePreview(bool))); + + connect(d->albumIconViewFilter, TQ_SIGNAL(signalResetTagFilters()), + this, TQ_SIGNAL(signalResetTagFilters())); + + connect(d->statusNavigateBar, TQ_SIGNAL(signalFirstItem()), + d->view, TQ_SLOT(slotFirstItem())); + + connect(d->statusNavigateBar, TQ_SIGNAL(signalNextItem()), + d->view, TQ_SLOT(slotNextItem())); + + connect(d->statusNavigateBar, TQ_SIGNAL(signalPrevItem()), + d->view, TQ_SLOT(slotPrevItem())); + + connect(d->statusNavigateBar, TQ_SIGNAL(signalLastItem()), + d->view, TQ_SLOT(slotLastItem())); + + connect(d->statusProgressBar, TQ_SIGNAL(signalCancelButtonPressed()), + this, TQ_SIGNAL(signalCancelButtonPressed())); +} + +void DigikamApp::setupAccelerators() +{ + d->accelerators = new TDEAccel(this); + + d->accelerators->insert("Exit Preview Mode", i18n("Exit Preview"), + i18n("Exit preview mode"), + Key_Escape, this, TQ_SIGNAL(signalEscapePressed()), + false, true); + + d->accelerators->insert("Next Image Key_Space", i18n("Next Image"), + i18n("Next Image"), + Key_Space, this, TQ_SIGNAL(signalNextItem()), + false, true); + + d->accelerators->insert("Previous Image SHIFT+Key_Space", i18n("Previous Image"), + i18n("Previous Image"), + SHIFT+Key_Space, this, TQ_SIGNAL(signalPrevItem()), + false, true); + + d->accelerators->insert("Previous Image Key_Backspace", i18n("Previous Image"), + i18n("Previous Image"), + Key_Backspace, this, TQ_SIGNAL(signalPrevItem()), + false, true); + + d->accelerators->insert("Next Image Key_Next", i18n("Next Image"), + i18n("Next Image"), + Key_Next, this, TQ_SIGNAL(signalNextItem()), + false, true); + + d->accelerators->insert("Previous Image Key_Prior", i18n("Previous Image"), + i18n("Previous Image"), + Key_Prior, this, TQ_SIGNAL(signalPrevItem()), + false, true); + + d->accelerators->insert("First Image Key_Home", i18n("First Image"), + i18n("First Image"), + Key_Home, this, TQ_SIGNAL(signalFirstItem()), + false, true); + + d->accelerators->insert("Last Image Key_End", i18n("Last Image"), + i18n("Last Image"), + Key_End, this, TQ_SIGNAL(signalLastItem()), + false, true); + + d->accelerators->insert("Copy Album Items Selection CTRL+Key_C", i18n("Copy Album Items Selection"), + i18n("Copy Album Items Selection"), + CTRL+Key_C, this, TQ_SIGNAL(signalCopyAlbumItemsSelection()), + false, true); + + d->accelerators->insert("Paste Album Items Selection CTRL+Key_V", i18n("Paste Album Items Selection"), + i18n("Paste Album Items Selection"), + CTRL+Key_V, this, TQ_SIGNAL(signalPasteAlbumItemsSelection()), + false, true); +} + +void DigikamApp::setupActions() +{ + // ----------------------------------------------------------------- + + d->cameraMenuAction = new TDEActionMenu(i18n("&Camera"), + "digitalcam", + actionCollection(), + "camera_menu"); + d->cameraMenuAction->setDelayed(false); + + // ----------------------------------------------------------------- + + d->themeMenuAction = new TDESelectAction(i18n("&Themes"), 0, actionCollection(), "theme_menu"); + connect(d->themeMenuAction, TQ_SIGNAL(activated(const TQString&)), + this, TQ_SLOT(slotChangeTheme(const TQString&))); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // ----------------------------------------------------------------- + + d->backwardActionMenu = new TDEToolBarPopupAction(i18n("&Back"), + "back", + ALT+Key_Left, + d->view, + TQ_SLOT(slotAlbumHistoryBack()), + actionCollection(), + "album_back"); + d->backwardActionMenu->setEnabled(false); + + connect(d->backwardActionMenu->popupMenu(), TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotAboutToShowBackwardMenu())); + + connect(d->backwardActionMenu->popupMenu(), TQ_SIGNAL(activated(int)), + d->view, TQ_SLOT(slotAlbumHistoryBack(int))); + + d->forwardActionMenu = new TDEToolBarPopupAction(i18n("Forward"), + "forward", + ALT+Key_Right, + d->view, + TQ_SLOT(slotAlbumHistoryForward()), + actionCollection(), + "album_forward"); + d->forwardActionMenu->setEnabled(false); + + connect(d->forwardActionMenu->popupMenu(), TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotAboutToShowForwardMenu())); + + connect(d->forwardActionMenu->popupMenu(), TQ_SIGNAL(activated(int)), + d->view, TQ_SLOT(slotAlbumHistoryForward(int))); + + d->newAction = new TDEAction(i18n("&New..."), + "albumfolder-new", + TDEStdAccel::shortcut(TDEStdAccel::New), + d->view, + TQ_SLOT(slotNewAlbum()), + actionCollection(), + "album_new"); + d->newAction->setWhatsThis(i18n("Creates a new empty Album in the database.")); + + d->albumSortAction = new TDESelectAction(i18n("&Sort Albums"), + 0, + 0, + actionCollection(), + "album_sort"); + + connect(d->albumSortAction, TQ_SIGNAL(activated(int)), + d->view, TQ_SLOT(slotSortAlbums(int))); + + // Use same list order as in albumsettings enum + TQStringList sortActionList; + sortActionList.append(i18n("By Folder")); + sortActionList.append(i18n("By Collection")); + sortActionList.append(i18n("By Date")); + d->albumSortAction->setItems(sortActionList); + + d->recurseAlbumsAction = new TDEToggleAction(i18n("Include Album Sub-Tree"), + 0, + this, + 0, + actionCollection(), + "albums_recursive"); + d->recurseAlbumsAction->setWhatsThis(i18n("Activate this option to recursively show all sub-albums below " + "the current album.")); + + connect(d->recurseAlbumsAction, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRecurseAlbums(bool))); + + d->recurseTagsAction = new TDEToggleAction(i18n("Include Tag Sub-Tree"), + 0, + this, + 0, + actionCollection(), + "tags_recursive"); + d->recurseTagsAction->setWhatsThis(i18n("Activate this option to show all images marked by the given tag " + "and its all its sub-tags.")); + + connect(d->recurseTagsAction, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotRecurseTags(bool))); + + d->deleteAction = new TDEAction(i18n("Delete"), + "edit-delete", + 0, + d->view, + TQ_SLOT(slotDeleteAlbum()), + actionCollection(), + "album_delete"); + + d->addImagesAction = new TDEAction( i18n("Add Images..."), + "albumfolder-importimages", + CTRL+Key_I, + this, + TQ_SLOT(slotAlbumAddImages()), + actionCollection(), + "album_addImages"); + d->addImagesAction->setWhatsThis(i18n("Adds new items to the current Album.")); + + d->albumImportAction = new TDEAction( i18n("Add Folders..."), + "albumfolder-importdir", + 0, + d->view, + TQ_SLOT(slotAlbumImportFolder()), + actionCollection(), + "album_importFolder"); + + d->propsEditAction = new TDEAction( i18n("Properties..."), + "albumfolder-properties", + 0, + d->view, + TQ_SLOT(slotAlbumPropsEdit()), + actionCollection(), + "album_propsEdit"); + d->propsEditAction->setWhatsThis(i18n("Edit Album Properties and Collection information.")); + + d->refreshAlbumAction = new TDEAction( i18n("Refresh"), + "rebuild", + Key_F5, + d->view, + TQ_SLOT(slotAlbumRefresh()), + actionCollection(), + "album_refresh"); + d->refreshAlbumAction->setWhatsThis(i18n("Refresh all album contents")); + + d->syncAlbumMetadataAction = new TDEAction( i18n("Synchronize Images with Database"), + "rebuild", + 0, + d->view, + TQ_SLOT(slotAlbumSyncPicturesMetadata()), + actionCollection(), + "album_syncmetadata"); + d->syncAlbumMetadataAction->setWhatsThis(i18n("Updates all image metadata of the current " + "album with the contents of the digiKam database " + "(image metadata will be over-written with data from the database).")); + + d->openInKonquiAction = new TDEAction( i18n("Open in File Manager"), + "konqueror", + 0, + d->view, + TQ_SLOT(slotAlbumOpenInKonqui()), + actionCollection(), + "album_openinkonqui"); + + // ----------------------------------------------------------- + + d->newTagAction = new TDEAction(i18n("New &Tag..."), "tag-new", + 0, d->view, TQ_SLOT(slotNewTag()), + actionCollection(), "tag_new"); + + d->editTagAction = new TDEAction(i18n("Edit Tag Properties..."), "tag-properties", + 0, d->view, TQ_SLOT(slotEditTag()), + actionCollection(), "tag_edit"); + + d->deleteTagAction = new TDEAction(i18n("Delete Tag"), "tag-delete", + 0, d->view, TQ_SLOT(slotDeleteTag()), + actionCollection(), "tag_delete"); + + // ----------------------------------------------------------- + + d->imagePreviewAction = new TDEToggleAction(i18n("View..."), + "viewimage", + Key_F3, + d->view, + TQ_SLOT(slotImagePreview()), + actionCollection(), + "image_view"); + + d->imageViewAction = new TDEAction(i18n("Edit..."), + "editimage", + Key_F4, + d->view, + TQ_SLOT(slotImageEdit()), + actionCollection(), + "image_edit"); + d->imageViewAction->setWhatsThis(i18n("Open the selected item in the image editor.")); + + d->imageLightTableAction = new TDEAction(i18n("Place onto Light Table"), + "lighttable", + CTRL+Key_L, + d->view, + TQ_SLOT(slotImageLightTable()), + actionCollection(), + "image_lighttable"); + d->imageLightTableAction->setWhatsThis(i18n("Place the selected items on the light table thumbbar.")); + + d->imageAddLightTableAction = new TDEAction(i18n("Add to Light Table"), + "lighttableadd", + SHIFT+CTRL+Key_L, + d->view, + TQ_SLOT(slotImageAddToLightTable()), + actionCollection(), + "image_add_to_lighttable"); + d->imageAddLightTableAction->setWhatsThis(i18n("Add selected items to the light table thumbbar.")); + + d->imageRenameAction = new TDEAction(i18n("Rename..."), + "pencil", + Key_F2, + d->view, + TQ_SLOT(slotImageRename()), + actionCollection(), + "image_rename"); + d->imageRenameAction->setWhatsThis(i18n("Change the filename of the currently selected item.")); + + // Pop up dialog to ask user whether to move to trash + d->imageDeleteAction = new TDEAction(i18n("Delete"), + "edittrash", + Key_Delete, + d->view, + TQ_SLOT(slotImageDelete()), + actionCollection(), + "image_delete"); + + // Pop up dialog to ask user whether to permanently delete + d->imageDeletePermanentlyAction = new TDEAction(i18n("Delete permanently"), + "edit-delete", + SHIFT+Key_Delete, + d->view, + TQ_SLOT(slotImageDeletePermanently()), + actionCollection(), + "image_delete_permanently"); + + // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. + // Power users may add them. + d->imageDeletePermanentlyDirectlyAction = new TDEAction(i18n("Delete permanently without confirmation"), + "edit-delete", + 0, + d->view, + TQ_SLOT(slotImageDeletePermanentlyDirectly()), + actionCollection(), + "image_delete_permanently_directly"); + + d->imageTrashDirectlyAction = new TDEAction(i18n("Move to trash without confirmation"), + "edittrash", + 0, + d->view, + TQ_SLOT(slotImageTrashDirectly()), + actionCollection(), + "image_trash_directly"); + + d->imageSortAction = new TDESelectAction(i18n("&Sort Images"), + 0, + 0, + actionCollection(), + "image_sort"); + + connect(d->imageSortAction, TQ_SIGNAL(activated(int)), + d->view, TQ_SLOT(slotSortImages(int))); + + // Use same list order as in albumsettings enum + TQStringList sortImagesActionList; + sortImagesActionList.append(i18n("By Name")); + sortImagesActionList.append(i18n("By Path")); + sortImagesActionList.append(i18n("By Date")); + sortImagesActionList.append(i18n("By File Size")); + sortImagesActionList.append(i18n("By Rating")); + d->imageSortAction->setItems(sortImagesActionList); + + // ----------------------------------------------------------------- + + TQSignalMapper *exifOrientationMapper = new TQSignalMapper( d->view ); + + connect(exifOrientationMapper, TQ_SIGNAL(mapped(int) ), + d->view, TQ_SLOT(slotImageExifOrientation(int))); + + d->imageExifOrientationActionMenu = new TDEActionMenu(i18n("Adjust Exif orientation tag"), + actionCollection(), + "image_set_exif_orientation"); + d->imageExifOrientationActionMenu->setDelayed(false); + + d->imageSetExifOrientation1Action = new TDEAction(i18n("Normal"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_normal"); + d->imageSetExifOrientation2Action = new TDEAction(i18n("Flipped Horizontally"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_flipped_horizontal"); + d->imageSetExifOrientation3Action = new TDEAction(i18n("Rotated Upside Down"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_rotated_upside_down"); + d->imageSetExifOrientation4Action = new TDEAction(i18n("Flipped Vertically"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_flipped_vertically"); + d->imageSetExifOrientation5Action = new TDEAction(i18n("Rotated Right / Horiz. Flipped"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_rotated_right_hor_flipped"); + d->imageSetExifOrientation6Action = new TDEAction(i18n("Rotated Right"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_rotated_right"); + d->imageSetExifOrientation7Action = new TDEAction(i18n("Rotated Right / Vert. Flipped"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_rotated_right_ver_flipped"); + d->imageSetExifOrientation8Action = new TDEAction(i18n("Rotated Left"), + 0, + d->imageExifOrientationActionMenu, + 0, + actionCollection(), + "image_set_exif_orientation_rotated_left"); + + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation1Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation2Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation3Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation4Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation5Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation6Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation7Action); + d->imageExifOrientationActionMenu->insert(d->imageSetExifOrientation8Action); + + connect(d->imageSetExifOrientation1Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation2Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation3Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation4Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation5Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation6Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation7Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + connect(d->imageSetExifOrientation8Action, TQ_SIGNAL(activated()), + exifOrientationMapper, TQ_SLOT(map())); + + exifOrientationMapper->setMapping(d->imageSetExifOrientation1Action, 1); + exifOrientationMapper->setMapping(d->imageSetExifOrientation2Action, 2); + exifOrientationMapper->setMapping(d->imageSetExifOrientation3Action, 3); + exifOrientationMapper->setMapping(d->imageSetExifOrientation4Action, 4); + exifOrientationMapper->setMapping(d->imageSetExifOrientation5Action, 5); + exifOrientationMapper->setMapping(d->imageSetExifOrientation6Action, 6); + exifOrientationMapper->setMapping(d->imageSetExifOrientation7Action, 7); + exifOrientationMapper->setMapping(d->imageSetExifOrientation8Action, 8); + + // ----------------------------------------------------------------- + + d->selectAllAction = new TDEAction(i18n("Select All"), + 0, + CTRL+Key_A, + d->view, + TQ_SLOT(slotSelectAll()), + actionCollection(), + "selectAll"); + + d->selectNoneAction = new TDEAction(i18n("Select None"), + 0, + CTRL+SHIFT+Key_A, + d->view, + TQ_SLOT(slotSelectNone()), + actionCollection(), + "selectNone"); + + d->selectInvertAction = new TDEAction(i18n("Invert Selection"), + 0, + CTRL+Key_Asterisk, + d->view, + TQ_SLOT(slotSelectInvert()), + actionCollection(), + "selectInvert"); + + // ----------------------------------------------------------- + + d->showMenuBarAction = KStdAction::showMenubar(this, TQ_SLOT(slotShowMenuBar()), actionCollection()); + + KStdAction::keyBindings(this, TQ_SLOT(slotEditKeys()), actionCollection()); + KStdAction::configureToolbars(this, TQ_SLOT(slotConfToolbars()), actionCollection()); + KStdAction::preferences(this, TQ_SLOT(slotSetup()), actionCollection()); + + // ----------------------------------------------------------- + + d->zoomPlusAction = new TDEAction(i18n("Zoom In"), + "zoom-in", + CTRL+Key_Plus, + d->view, + TQ_SLOT(slotZoomIn()), + actionCollection(), + "album_zoomin"); + + d->zoomMinusAction = new TDEAction(i18n("Zoom Out"), + "zoom-out", + CTRL+Key_Minus, + d->view, + TQ_SLOT(slotZoomOut()), + actionCollection(), + "album_zoomout"); + + d->zoomTo100percents = new TDEAction(i18n("Zoom to 100%"), + "zoom-original", + ALT+CTRL+Key_0, // NOTE: Photoshop 7 use ALT+CTRL+0. + d->view, + TQ_SLOT(slotZoomTo100Percents()), + actionCollection(), + "album_zoomto100percents"); + + d->zoomFitToWindowAction = new TDEAction(i18n("Fit to &Window"), + "view_fit_window", + CTRL+SHIFT+Key_E, + d->view, + TQ_SLOT(slotFitToWindow()), + actionCollection(), + "album_zoomfit2window"); + + // Do not use std KDE action for full screen because action text is too large for app. toolbar. + d->fullScreenAction = new TDEToggleAction(i18n("Full Screen"), + "view-fullscreen", + CTRL+SHIFT+Key_F, + this, + TQ_SLOT(slotToggleFullScreen()), + actionCollection(), + "full_screen"); + d->fullScreenAction->setWhatsThis(i18n("Switch the window to full screen mode")); + + d->slideShowAction = new TDEActionMenu(i18n("Slideshow"), "slideshow", + actionCollection(), "slideshow"); + + d->slideShowAction->setDelayed(false); + + d->slideShowAllAction = new TDEAction(i18n("All"), 0, Key_F9, + d->view, TQ_SLOT(slotSlideShowAll()), + actionCollection(), "slideshow_all"); + d->slideShowAction->insert(d->slideShowAllAction); + + d->slideShowSelectionAction = new TDEAction(i18n("Selection"), 0, ALT+Key_F9, + d->view, + TQ_SLOT(slotSlideShowSelection()), + actionCollection(), + "slideshow_selected"); + d->slideShowAction->insert(d->slideShowSelectionAction); + + d->slideShowRecursiveAction = new TDEAction(i18n("With All Sub-Albums"), 0, SHIFT+Key_F9, + d->view, + TQ_SLOT(slotSlideShowRecursive()), + actionCollection(), + "slideshow_recursive"); + d->slideShowAction->insert(d->slideShowRecursiveAction); + + d->quitAction = KStdAction::quit(this, + TQ_SLOT(slotExit()), + actionCollection(), + "app_exit"); + + d->rawCameraListAction = new TDEAction(i18n("Supported RAW Cameras"), + "kdcraw", + 0, + this, + TQ_SLOT(slotRawCameraList()), + actionCollection(), + "help_rawcameralist"); + + d->kipiHelpAction = new TDEAction(i18n("Kipi Plugins Handbook"), + "kipi", + 0, + this, + TQ_SLOT(slotShowKipiHelp()), + actionCollection(), + "help_kipi"); + + d->tipAction = KStdAction::tipOfDay(this, + TQ_SLOT(slotShowTip()), + actionCollection(), + "help_tipofday"); + + d->donateMoneyAction = new TDEAction(i18n("Donate..."), + 0, + 0, + this, + TQ_SLOT(slotDonateMoney()), + actionCollection(), + "help_donatemoney"); + + d->contributeAction = new TDEAction(i18n("Contribute..."), + 0, 0, + this, TQ_SLOT(slotContribute()), + actionCollection(), + "help_contribute"); + + new DLogoAction(actionCollection(), "logo_action" ); + + // -- Rating actions --------------------------------------------------------------- + + d->rating0Star = new TDEAction(i18n("Assign Rating \"No Stars\""), CTRL+Key_0, + d->view, TQ_SLOT(slotAssignRatingNoStar()), + actionCollection(), "ratenostar"); + d->rating1Star = new TDEAction(i18n("Assign Rating \"One Star\""), CTRL+Key_1, + d->view, TQ_SLOT(slotAssignRatingOneStar()), + actionCollection(), "rateonestar"); + d->rating2Star = new TDEAction(i18n("Assign Rating \"Two Stars\""), CTRL+Key_2, + d->view, TQ_SLOT(slotAssignRatingTwoStar()), + actionCollection(), "ratetwostar"); + d->rating3Star = new TDEAction(i18n("Assign Rating \"Three Stars\""), CTRL+Key_3, + d->view, TQ_SLOT(slotAssignRatingThreeStar()), + actionCollection(), "ratethreestar"); + d->rating4Star = new TDEAction(i18n("Assign Rating \"Four Stars\""), CTRL+Key_4, + d->view, TQ_SLOT(slotAssignRatingFourStar()), + actionCollection(), "ratefourstar"); + d->rating5Star = new TDEAction(i18n("Assign Rating \"Five Stars\""), CTRL+Key_5, + d->view, TQ_SLOT(slotAssignRatingFiveStar()), + actionCollection(), "ratefivestar"); + + // ----------------------------------------------------------- + + TDEAction* findAction = KStdAction::find(d->view, TQ_SLOT(slotNewQuickSearch()), + actionCollection(), "search_quick"); + findAction->setText(i18n("Search...")); + findAction->setIconSet(BarIcon("filefind")); + + TDEAction* advFindAction = KStdAction::find(d->view, TQ_SLOT(slotNewAdvancedSearch()), + actionCollection(), "search_advanced"); + advFindAction->setText(i18n("Advanced Search...")); + advFindAction->setShortcut("Ctrl+Alt+F"); + + new TDEAction(i18n("Light Table"), "idea", Key_L, + d->view, TQ_SLOT(slotLightTable()), actionCollection(), + "light_table"); + + new TDEAction(i18n("Scan for New Images"), "reload_page", 0, + this, TQ_SLOT(slotDatabaseRescan()), actionCollection(), + "database_rescan"); + + new TDEAction(i18n("Rebuild All Thumbnails..."), "reload_page", 0, + this, TQ_SLOT(slotRebuildAllThumbs()), actionCollection(), + "thumbs_rebuild"); + + new TDEAction(i18n("Update Metadata Database..."), "reload_page", 0, + this, TQ_SLOT(slotSyncAllPicturesMetadata()), actionCollection(), + "sync_metadata"); + + // ----------------------------------------------------------- + + // Provides a menu entry that allows showing/hiding the toolbar(s) + setStandardToolBarMenuEnabled(true); + + // Provides a menu entry that allows showing/hiding the statusbar + createStandardStatusBarAction(); + + // Load Cameras -- do this before the createGUI so that the cameras + // are plugged into the toolbar at startup + if (d->splashScreen) + d->splashScreen->message(i18n("Loading cameras")); + + loadCameras(); + + createGUI(TQString::fromLatin1( "digikamui.rc" ), false); + + // Initialize Actions --------------------------------------- + + d->deleteAction->setEnabled(false); + d->addImagesAction->setEnabled(false); + d->propsEditAction->setEnabled(false); + d->openInKonquiAction->setEnabled(false); + + d->imageViewAction->setEnabled(false); + d->imagePreviewAction->setEnabled(false); + d->imageLightTableAction->setEnabled(false); + d->imageAddLightTableAction->setEnabled(false); + d->imageRenameAction->setEnabled(false); + d->imageDeleteAction->setEnabled(false); + d->imageExifOrientationActionMenu->setEnabled(false); + d->slideShowSelectionAction->setEnabled(false); + + d->albumSortAction->setCurrentItem((int)d->albumSettings->getAlbumSortOrder()); + d->imageSortAction->setCurrentItem((int)d->albumSettings->getImageSortOrder()); + + d->recurseAlbumsAction->setChecked(d->albumSettings->getRecurseAlbums()); + d->recurseTagsAction->setChecked(d->albumSettings->getRecurseTags()); + slotRecurseAlbums(d->recurseAlbumsAction->isChecked()); + slotRecurseTags(d->recurseTagsAction->isChecked()); + + // Setting the filter condition also updates the tooltip. + // (So `setRating` is called first, as otherwise the filter value is not respected). + d->albumIconViewFilter->readSettings(); +} + +void DigikamApp::enableZoomPlusAction(bool val) +{ + d->zoomPlusAction->setEnabled(val); + d->statusZoomBar->setEnableZoomPlus(val); +} + +void DigikamApp::enableZoomMinusAction(bool val) +{ + d->zoomMinusAction->setEnabled(val); + d->statusZoomBar->setEnableZoomMinus(val); +} + +void DigikamApp::enableAlbumBackwardHistory(bool enable) +{ + d->backwardActionMenu->setEnabled(enable); +} + +void DigikamApp::enableAlbumForwardHistory(bool enable) +{ + d->forwardActionMenu->setEnabled(enable); +} + +void DigikamApp::slotAboutToShowBackwardMenu() +{ + d->backwardActionMenu->popupMenu()->clear(); + TQStringList titles; + d->view->getBackwardHistory(titles); + if(!titles.isEmpty()) + { + int id = 1; + TQStringList::Iterator iter = titles.begin(); + for(; iter != titles.end(); ++iter,++id) + { + d->backwardActionMenu->popupMenu()->insertItem(*iter, id); + } + } +} + +void DigikamApp::slotAboutToShowForwardMenu() +{ + d->forwardActionMenu->popupMenu()->clear(); + TQStringList titles; + d->view->getForwardHistory(titles); + + if(!titles.isEmpty()) + { + int id = 1; + TQStringList::Iterator iter = titles.begin(); + for(; iter != titles.end(); ++iter,++id) + { + d->forwardActionMenu->popupMenu()->insertItem(*iter, id); + } + } +} + +void DigikamApp::slotAlbumSelected(bool val) +{ + Album *album = d->albumManager->currentAlbum(); + + if(album && !val) + { + // Not a PAlbum is selected + d->deleteAction->setEnabled(false); + d->addImagesAction->setEnabled(false); + d->propsEditAction->setEnabled(false); + d->openInKonquiAction->setEnabled(false); + d->newAction->setEnabled(false); + d->albumImportAction->setEnabled(false); + } + else if(!album && !val) + { + // Groupitem selected (Collection/date) + d->deleteAction->setEnabled(false); + d->addImagesAction->setEnabled(false); + d->propsEditAction->setEnabled(false); + d->openInKonquiAction->setEnabled(false); + d->newAction->setEnabled(false); + d->albumImportAction->setEnabled(false); + + TDEAction *action; + for (action = d->kipiFileActionsImport.first(); action; + action = d->kipiFileActionsImport.next()) + { + action->setEnabled(false); + } + } + else if(album && !album->isRoot() && album->type() == Album::PHYSICAL) + { + // Normal Album selected + d->deleteAction->setEnabled(true); + d->addImagesAction->setEnabled(true); + d->propsEditAction->setEnabled(true); + d->openInKonquiAction->setEnabled(true); + d->newAction->setEnabled(true); + d->albumImportAction->setEnabled(true); + + TDEAction *action; + for (action = d->kipiFileActionsImport.first(); action; + action = d->kipiFileActionsImport.next()) + { + action->setEnabled(true); + } + } + else if(album && album->isRoot() && album->type() == Album::PHYSICAL) + { + // Root Album selected + d->deleteAction->setEnabled(false); + d->addImagesAction->setEnabled(false); + d->propsEditAction->setEnabled(false); + + if(album->type() == Album::PHYSICAL) + { + d->newAction->setEnabled(true); + d->openInKonquiAction->setEnabled(true); + d->albumImportAction->setEnabled(true); + } + else + { + d->newAction->setEnabled(false); + d->openInKonquiAction->setEnabled(false); + d->albumImportAction->setEnabled(false); + } + + TDEAction *action; + for (action = d->kipiFileActionsImport.first(); action; + action = d->kipiFileActionsImport.next()) + { + action->setEnabled(false); + } + } +} + +void DigikamApp::slotTagSelected(bool val) +{ + Album *album = d->albumManager->currentAlbum(); + if (!album) return; + + if(!val) + { + d->deleteTagAction->setEnabled(false); + d->editTagAction->setEnabled(false); + } + else if(!album->isRoot()) + { + d->deleteTagAction->setEnabled(true); + d->editTagAction->setEnabled(true); + + TDEAction *action; + for (action = d->kipiFileActionsImport.first(); action; + action = d->kipiFileActionsImport.next()) + { + action->setEnabled(false); + } + } + else + { + d->deleteTagAction->setEnabled(false); + d->editTagAction->setEnabled(false); + + TDEAction *action; + for (action = d->kipiFileActionsImport.first(); action; + action = d->kipiFileActionsImport.next()) + { + action->setEnabled(false); + } + } +} + +void DigikamApp::slotImageSelected(const TQPtrList& list, bool hasPrev, bool hasNext, + const KURL::List& listAll) +{ + TQPtrList selection = list; + KURL::List all = listAll; + int num_images = listAll.count(); + bool val = selection.isEmpty() ? false : true; + TQString text; + int index = 1; + + d->imageViewAction->setEnabled(val); + d->imagePreviewAction->setEnabled(val); + d->imageLightTableAction->setEnabled(val); + d->imageAddLightTableAction->setEnabled(val); + d->imageRenameAction->setEnabled(val); + d->imageDeleteAction->setEnabled(val); + d->imageExifOrientationActionMenu->setEnabled(val); + d->slideShowSelectionAction->setEnabled(selection.count() != 0); + + switch (selection.count()) + { + case 0: + d->statusProgressBar->setText(i18n("No item selected")); + break; + case 1: + { + KURL first = selection.first()->kurl(); + + for (KURL::List::iterator it = all.begin(); + it != all.end(); ++it) + { + if ((*it) == first) + break; + + index++; + } + + text = selection.first()->kurl().fileName() + + i18n(" (%1 of %2)") + .arg(TQString::number(index)) + .arg(TQString::number(num_images)); + d->statusProgressBar->setText(text); + break; + } + default: + d->statusProgressBar->setText(i18n("%1/%2 items selected") + .arg(selection.count()).arg(TQString::number(num_images))); + + break; + } + + d->statusNavigateBar->setNavigateBarState(hasPrev, hasNext); +} + +void DigikamApp::slotProgressBarMode(int mode, const TQString& text) +{ + d->statusProgressBar->progressBarMode(mode, text); +} + +void DigikamApp::slotProgressValue(int count) +{ + d->statusProgressBar->setProgressValue(count); +} + +void DigikamApp::slotExit() +{ + if (d->fullScreen) + { + slotToggleFullScreen(); + TQTimer::singleShot(0, this, TQ_SLOT(close())); + } + else + close(); +} + +TQString DigikamApp::convertToLocalUrl( const TQString& folder ) +{ + // This function is copied from k3b. + + KURL url( folder ); + if( !url.isLocalFile() ) + { +#if KDE_IS_VERSION(3,4,91) + // Support for system:/ and media:/ (c) Stephan Kulow + KURL mlu = TDEIO::NetAccess::mostLocalURL( url, 0 ); + if (mlu.isLocalFile()) + return mlu.path(); + + DWarning() << folder << " mlu " << mlu << endl; + + TQString path = mlu.path(); + + if ( mlu.protocol() == "system" && path.startsWith("/media") ) + path = path.mid(7); + else if (mlu.protocol() == "media") + path = path.mid(1); + else + return folder; // nothing to see - go on + + DDebug() << "parsed import path is: " << path << endl; + DCOPRef ref("kded", "mediamanager"); + DCOPReply reply = ref.call("properties", path); + if (reply.isValid()) { + TQStringList slreply; + reply.get(slreply); + if ((slreply.count()>=9) && !slreply[9].isEmpty()) + return slreply[9]; + else + return slreply[6]; + } + else + { + DWarning() << "dcop call failed\n"; + } + + return path; +#else +#ifndef UDS_LOCAL_PATH +#define UDS_LOCAL_PATH (72 | TDEIO::UDS_STRING) +#else + using namespace TDEIO; +#endif + TDEIO::UDSEntry e; + if( TDEIO::NetAccess::stat( url, e, 0 ) ) + { + const TDEIO::UDSEntry::ConstIterator end = e.end(); + for( TDEIO::UDSEntry::ConstIterator it = e.begin(); it != end; ++it ) + { + if( (*it).m_uds == UDS_LOCAL_PATH && !(*it).m_str.isEmpty() ) + return KURL::fromPathOrURL( (*it).m_str ).path(); + } + } +#endif + } + + return url.path(); +} + +void DigikamApp::slotDcopDownloadImages( const TQString& folder ) +{ + if (!folder.isNull()) + { + // activate window when called by media menu and DCOP + if (isMinimized()) + KWin::deIconifyWindow(winId()); + KWin::activateWindow(winId()); + + slotDownloadImages(folder); + } +} + +void DigikamApp::slotDcopCameraAutoDetect() +{ + // activate window when called by media menu and DCOP + if (isMinimized()) + KWin::deIconifyWindow(winId()); + KWin::activateWindow(winId()); + + slotCameraAutoDetect(); +} + +void DigikamApp::slotDownloadImages( const TQString& folder) +{ + if (!folder.isNull()) + { + d->cameraGuiPath = folder; + + TQTimer::singleShot(0, this, TQ_SLOT(slotDownloadImages())); + } +} + +void DigikamApp::slotDownloadImages() +{ + if (d->cameraGuiPath.isNull()) + return; + + // Fetch the contents of the device. This is needed to make sure that the + // media:/device gets mounted. + TDEIO::ListJob *job = TDEIO::listDir(KURL(d->cameraGuiPath), false, false); + TDEIO::NetAccess::synchronousRun(job,0); + + TQString localUrl = convertToLocalUrl(d->cameraGuiPath); + DDebug() << "slotDownloadImages: convertToLocalUrl " << d->cameraGuiPath << " to " << localUrl << endl; + + if (localUrl.isNull()) + return; + + bool alreadyThere = false; + + for (uint i = 0 ; i != actionCollection()->count() ; i++) + { + if (actionCollection()->action(i)->name() == d->cameraGuiPath) + alreadyThere = true; + } + + if (!alreadyThere) + { + TDEAction *cAction = new TDEAction( + i18n("Browse %1").arg(KURL(d->cameraGuiPath).prettyURL()), + "camera-photo", + 0, + this, + TQ_SLOT(slotDownloadImages()), + actionCollection(), + d->cameraGuiPath.latin1() ); + + d->cameraMenuAction->insert(cAction, 0); + } + + // the CameraUI will delete itself when it has finished + CameraUI* cgui = new CameraUI(this, + i18n("Images found in %1").arg(d->cameraGuiPath), + "directory browse","Fixed", localUrl, TQDateTime::currentDateTime()); + cgui->show(); + + connect(cgui, TQ_SIGNAL(signalLastDestination(const KURL&)), + d->view, TQ_SLOT(slotSelectAlbum(const KURL&))); + + connect(cgui, TQ_SIGNAL(signalAlbumSettingsChanged()), + this, TQ_SLOT(slotSetupChanged())); +} + +void DigikamApp::slotCameraConnect() +{ + CameraType* ctype = d->cameraList->find(TQString::fromUtf8(sender()->name())); + + if (ctype) + { + // check not to open two dialogs for the same camera + if (ctype->currentCameraUI() && !ctype->currentCameraUI()->isClosed()) + { + // show and raise dialog + if (ctype->currentCameraUI()->isMinimized()) + KWin::deIconifyWindow(ctype->currentCameraUI()->winId()); + KWin::activateWindow(ctype->currentCameraUI()->winId()); + } + else + { + // the CameraUI will delete itself when it has finished + CameraUI* cgui = new CameraUI(this, ctype->title(), ctype->model(), + ctype->port(), ctype->path(), ctype->lastAccess()); + + ctype->setCurrentCameraUI(cgui); + + cgui->show(); + + connect(cgui, TQ_SIGNAL(signalLastDestination(const KURL&)), + d->view, TQ_SLOT(slotSelectAlbum(const KURL&))); + + connect(cgui, TQ_SIGNAL(signalAlbumSettingsChanged()), + this, TQ_SLOT(slotSetupChanged())); + } + } +} + +void DigikamApp::slotCameraAdded(CameraType *ctype) +{ + if (!ctype) return; + + TDEAction *cAction = new TDEAction(ctype->title(), "camera-photo", 0, + this, TQ_SLOT(slotCameraConnect()), + actionCollection(), + ctype->title().utf8()); + d->cameraMenuAction->insert(cAction, 0); + ctype->setAction(cAction); +} + +void DigikamApp::slotCameraMediaMenu() +{ + d->mediaItems.clear(); + + d->cameraMediaList->clear(); + d->cameraMediaList->insertItem(i18n("No media devices found"), 0); + d->cameraMediaList->setItemEnabled(0, false); + + KURL kurl("media:/"); + TDEIO::ListJob *job = TDEIO::listDir(kurl, false, false); + + connect( job, TQ_SIGNAL(entries(TDEIO::Job*,const TDEIO::UDSEntryList&)), + this, TQ_SLOT(slotCameraMediaMenuEntries(TDEIO::Job*,const TDEIO::UDSEntryList&)) ); +} + +void DigikamApp::slotCameraMediaMenuEntries( Job *, const UDSEntryList & list ) +{ + int i = 0; + + for(TDEIO::UDSEntryList::ConstIterator it = list.begin() ; it != list.end() ; ++it) + { + TQString name; + TQString path; + + for ( UDSEntry::const_iterator et = (*it).begin() ; et != (*it).end() ; ++et ) + { + if ( (*et).m_uds == TDEIO::UDS_NAME) + name = ( *et ).m_str; + if ( (*et).m_uds == TDEIO::UDS_URL) + path = ( *et ).m_str; + + //DDebug() << ( *et ).m_str << endl; + } + + if (!name.isEmpty() && !path.isEmpty()) + { + //DDebug() << "slotCameraMediaMenuEntries: Adding " << name << ", path " << path << endl; + if (i == 0) + d->cameraMediaList->clear(); + + d->mediaItems[i] = path; + + d->cameraMediaList->insertItem(name, this, TQ_SLOT(slotDownloadImagesFromMedia(int)), 0, i); + d->cameraMediaList->setItemParameter(i, i); + i++; + } + } +} + +void DigikamApp::slotDownloadImagesFromMedia( int id ) +{ + slotDownloadImages( d->mediaItems[id] ); +} + +void DigikamApp::slotCameraRemoved(CameraType *ctype) +{ + if (!ctype) return; + + TDEAction *cAction = ctype->action(); + + if (cAction) + d->cameraMenuAction->remove(cAction); +} + +void DigikamApp::slotCameraAutoDetect() +{ + bool retry = false; + + CameraType* ctype = d->cameraList->autoDetect(retry); + + if (!ctype && retry) + { + TQTimer::singleShot(0, this, TQ_SLOT(slotCameraAutoDetect())); + return; + } + + if (ctype && ctype->action()) + { + ctype->action()->activate(); + } +} + +void DigikamApp::slotSetup() +{ + setup(); +} + +bool DigikamApp::setup(bool iccSetupPage) +{ + Setup setup(this, 0, iccSetupPage ? Setup::IccProfiles : Setup::LastPageUsed); + + // To show the number of KIPI plugins in the setup dialog. + + KIPI::PluginLoader::PluginList list = d->kipiPluginLoader->pluginList(); + setup.kipiPluginsPage()->initPlugins((int)list.count()); + + if (setup.exec() != TQDialog::Accepted) + return false; + + setup.kipiPluginsPage()->applyPlugins(); + + slotSetupChanged(); + + return true; +} + +void DigikamApp::slotSetupCamera() +{ + Setup setup(this, 0, Setup::Camera); + + // For to show the number of KIPI plugins in the setup dialog. + + KIPI::PluginLoader::PluginList list = d->kipiPluginLoader->pluginList(); + setup.kipiPluginsPage()->initPlugins((int)list.count()); + + if (setup.exec() != TQDialog::Accepted) + return; + + setup.kipiPluginsPage()->applyPlugins(); + + slotSetupChanged(); +} + +void DigikamApp::slotSetupChanged() +{ + // raw loading options might have changed + LoadingCacheInterface::cleanCache(); + + if(d->albumSettings->getAlbumLibraryPath() != d->albumManager->getLibraryPath()) + d->view->clearHistory(); + + d->albumManager->setLibraryPath(d->albumSettings->getAlbumLibraryPath()); + d->albumManager->startScan(); + + if(d->albumSettings->getShowFolderTreeViewItemsCount()) + d->albumManager->refresh(); + + d->view->applySettings(); + d->albumIconViewFilter->readSettings(); + + AlbumThumbnailLoader::instance()->setThumbnailSize(d->albumSettings->getDefaultTreeIconSize()); + + if (ImageWindow::imagewindowCreated()) + ImageWindow::imagewindow()->applySettings(); + + if (LightTableWindow::lightTableWindowCreated()) + LightTableWindow::lightTableWindow()->applySettings(); + + d->config->sync(); +} + +void DigikamApp::slotEditKeys() +{ + KKeyDialog* dialog = new KKeyDialog(); + dialog->insert( actionCollection(), i18n( "General" ) ); + + KIPI::PluginLoader::PluginList list = d->kipiPluginLoader->pluginList(); + + for( KIPI::PluginLoader::PluginList::Iterator it = list.begin() ; it != list.end() ; ++it ) + { + KIPI::Plugin* plugin = (*it)->plugin(); + + if ( plugin ) + dialog->insert( plugin->actionCollection(), (*it)->comment() ); + } + + dialog->configure(); + delete dialog; +} + +void DigikamApp::slotConfToolbars() +{ + saveMainWindowSettings(TDEGlobal::config()); + KEditToolbar *dlg = new KEditToolbar(actionCollection(), "digikamui.rc"); + + if(dlg->exec()) + { + createGUI(TQString::fromLatin1( "digikamui.rc" ), false); + applyMainWindowSettings(TDEGlobal::config()); + plugActionList( TQString::fromLatin1("file_actions_import"), d->kipiFileActionsImport ); + plugActionList( TQString::fromLatin1("image_actions"), d->kipiImageActions ); + plugActionList( TQString::fromLatin1("tool_actions"), d->kipiToolsActions ); + plugActionList( TQString::fromLatin1("batch_actions"), d->kipiBatchActions ); + plugActionList( TQString::fromLatin1("album_actions"), d->kipiAlbumActions ); + plugActionList( TQString::fromLatin1("file_actions_export"), d->kipiFileActionsExport ); + } + + delete dlg; +} + +void DigikamApp::slotToggleFullScreen() +{ + if (d->fullScreen) + { + setWindowState( windowState() & ~WindowFullScreen ); + menuBar()->show(); + statusBar()->show(); + topDock()->show(); + bottomDock()->show(); + leftDock()->show(); + rightDock()->show(); + d->view->showSideBars(); + + d->fullScreen = false; + } + else + { + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + bool fullScreenHideToolBar = config->readBoolEntry("FullScreen Hide ToolBar", false); + + menuBar()->hide(); + statusBar()->hide(); + if (fullScreenHideToolBar) + topDock()->hide(); + bottomDock()->hide(); + leftDock()->hide(); + rightDock()->hide(); + d->view->hideSideBars(); + + showFullScreen(); + d->fullScreen = true; + } +} + +void DigikamApp::slotShowTip() +{ +#if KDE_IS_VERSION(3,2,0) + TQStringList tipsFiles; + tipsFiles.append("digikam/tips"); + + tipsFiles.append("kipi/tips"); + + KTipDialog::showMultiTip(this, tipsFiles, true); +#else + KTipDialog::showTip(this, "digikam/tips", true); +#endif +} + +void DigikamApp::slotShowKipiHelp() +{ + TDEApplication::kApplication()->invokeHelp( TQString(), "kipi-plugins" ); +} + +void DigikamApp::slotRawCameraList() +{ + RawCameraDlg dlg(this); + dlg.exec(); +} + +void DigikamApp::loadPlugins() +{ + if(d->splashScreen) + d->splashScreen->message(i18n("Loading Kipi Plugins")); + + TQStringList ignores; + d->kipiInterface = new DigikamKipiInterface( this, "Digikam_KIPI_interface" ); + + ignores.append( "HelloWorld" ); + ignores.append( "KameraKlient" ); + + d->kipiPluginLoader = new KIPI::PluginLoader( ignores, d->kipiInterface ); + + connect( d->kipiPluginLoader, TQ_SIGNAL( replug() ), + this, TQ_SLOT( slotKipiPluginPlug() ) ); + + d->kipiPluginLoader->loadPlugins(); + + d->kipiInterface->slotCurrentAlbumChanged(d->albumManager->currentAlbum()); + + // Setting the initial menu options after all plugins have been loaded + d->view->slotAlbumSelected(d->albumManager->currentAlbum()); + + d->imagePluginsLoader = new ImagePluginLoader(this, d->splashScreen); +} + +void DigikamApp::slotKipiPluginPlug() +{ + unplugActionList( TQString::fromLatin1("file_actions_export") ); + unplugActionList( TQString::fromLatin1("file_actions_import") ); + unplugActionList( TQString::fromLatin1("image_actions") ); + unplugActionList( TQString::fromLatin1("tool_actions") ); + unplugActionList( TQString::fromLatin1("batch_actions") ); + unplugActionList( TQString::fromLatin1("album_actions") ); + + d->kipiImageActions.clear(); + d->kipiFileActionsExport.clear(); + d->kipiFileActionsImport.clear(); + d->kipiToolsActions.clear(); + d->kipiBatchActions.clear(); + d->kipiAlbumActions.clear(); + + KIPI::PluginLoader::PluginList list = d->kipiPluginLoader->pluginList(); + + int cpt = 0; + + for( KIPI::PluginLoader::PluginList::Iterator it = list.begin() ; it != list.end() ; ++it ) + { + KIPI::Plugin* plugin = (*it)->plugin(); + + if ( !plugin || !(*it)->shouldLoad() ) + continue; + + ++cpt; + + plugin->setup( this ); + TQPtrList* popup = 0; + + // Plugin category identification using TDEAction method based. + + TDEActionPtrList actions = plugin->actions(); + + // List of obsolete kipi-plugins to not load. + TQStringList pluginActionsDisabled; + pluginActionsDisabled << TQString("raw_converter_single"); // Obsolete Since 0.9.5 and new Raw Import tool. + + for( TDEActionPtrList::Iterator it2 = actions.begin(); it2 != actions.end(); ++it2 ) + { + if ( plugin->category(*it2) == KIPI::IMAGESPLUGIN ) + popup = &d->kipiImageActions; + + else if ( plugin->category(*it2) == KIPI::EXPORTPLUGIN ) + popup = &d->kipiFileActionsExport; + + else if ( plugin->category(*it2) == KIPI::IMPORTPLUGIN ) + popup = &d->kipiFileActionsImport; + + else if ( plugin->category(*it2) == KIPI::TOOLSPLUGIN ) + popup = &d->kipiToolsActions; + + else if ( plugin->category(*it2) == KIPI::BATCHPLUGIN ) + popup = &d->kipiBatchActions; + + else if ( plugin->category(*it2) == KIPI::COLLECTIONSPLUGIN ) + popup = &d->kipiAlbumActions; + + TQString actionName((*it2)->name()); + + // Plug the KIPI plugins actions in according with the TDEAction method. + + if (popup) + { + if (!pluginActionsDisabled.contains(actionName)) + popup->append( *it2 ); + else + DDebug() << "Plugin '" << actionName << "' disabled." << endl; + } + else + { + DDebug() << "No menu found for plugin '" << actionName << "' !!!" << endl; + } + } + + plugin->actionCollection()->readShortcutSettings(); + } + + // Create GUI menu in according with plugins. + + plugActionList( TQString::fromLatin1("file_actions_export"), d->kipiFileActionsExport ); + plugActionList( TQString::fromLatin1("file_actions_import"), d->kipiFileActionsImport ); + plugActionList( TQString::fromLatin1("image_actions"), d->kipiImageActions ); + plugActionList( TQString::fromLatin1("tool_actions"), d->kipiToolsActions ); + plugActionList( TQString::fromLatin1("batch_actions"), d->kipiBatchActions ); + plugActionList( TQString::fromLatin1("album_actions"), d->kipiAlbumActions ); +} + +void DigikamApp::loadCameras() +{ + d->cameraList->load(); + + d->cameraMenuAction->popupMenu()->insertSeparator(); + + d->cameraMenuAction->popupMenu()->insertItem(i18n("Browse Media"), d->cameraMediaList); + + d->cameraMenuAction->popupMenu()->insertSeparator(); + + d->cameraMenuAction->insert(new TDEAction(i18n("Add Camera..."), 0, + this, TQ_SLOT(slotSetupCamera()), + actionCollection(), + "camera_add")); +} + +void DigikamApp::populateThemes() +{ + if(d->splashScreen) + d->splashScreen->message(i18n("Loading themes")); + + ThemeEngine::instance()->scanThemes(); + d->themeMenuAction->setItems(ThemeEngine::instance()->themeNames()); + slotThemeChanged(); + ThemeEngine::instance()->slotChangeTheme(d->themeMenuAction->currentText()); +} + +void DigikamApp::slotChangeTheme(const TQString& theme) +{ + d->albumSettings->setCurrentTheme(theme); + ThemeEngine::instance()->slotChangeTheme(theme); +} + +void DigikamApp::slotThemeChanged() +{ + TQStringList themes(ThemeEngine::instance()->themeNames()); + int index = themes.findIndex(d->albumSettings->getCurrentTheme()); + if (index == -1) + index = themes.findIndex(i18n("Default")); + + d->themeMenuAction->setCurrentItem(index); +} + +void DigikamApp::slotDatabaseRescan() +{ + ScanLib sLib; + sLib.startScan(); + + d->view->refreshView(); + + if (ImageWindow::imagewindowCreated()) + ImageWindow::imagewindow()->refreshView(); + + if (LightTableWindow::lightTableWindowCreated()) + LightTableWindow::lightTableWindow()->refreshView(); +} + +void DigikamApp::slotRebuildAllThumbs() +{ + TQString msg = i18n("Rebuilding all image thumbnails can take some time.\n" + "Do you want to continue?"); + int result = KMessageBox::warningContinueCancel(this, msg); + if (result != KMessageBox::Continue) + return; + + BatchThumbsGenerator *thumbsGenerator = new BatchThumbsGenerator(this); + + connect(thumbsGenerator, TQ_SIGNAL(signalRebuildAllThumbsDone()), + this, TQ_SLOT(slotRebuildAllThumbsDone())); + + thumbsGenerator->exec(); +} + +void DigikamApp::slotRebuildAllThumbsDone() +{ + d->view->applySettings(); +} + +void DigikamApp::slotSyncAllPicturesMetadata() +{ + TQString msg = i18n("Updating the metadata database can take some time. \nDo you want to continue?"); + int result = KMessageBox::warningContinueCancel(this, msg); + if (result != KMessageBox::Continue) + return; + + BatchAlbumsSyncMetadata *syncMetadata = new BatchAlbumsSyncMetadata(this); + + connect(syncMetadata, TQ_SIGNAL(signalComplete()), + this, TQ_SLOT(slotSyncAllPicturesMetadataDone())); + + syncMetadata->exec(); +} + +void DigikamApp::slotSyncAllPicturesMetadataDone() +{ + d->view->applySettings(); +} + +void DigikamApp::slotDonateMoney() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=donation"); +} + +void DigikamApp::slotContribute() +{ + TDEApplication::kApplication()->invokeBrowser("http://www.digikam.org/?q=contrib"); +} + +void DigikamApp::slotRecurseAlbums(bool checked) +{ + AlbumLister::instance()->setRecurseAlbums(checked); +} + +void DigikamApp::slotRecurseTags(bool checked) +{ + AlbumLister::instance()->setRecurseTags(checked); +} + +void DigikamApp::slotZoomSliderChanged(int size) +{ + d->view->setThumbSize(size); +} + +void DigikamApp::slotThumbSizeChanged(int size) +{ + d->statusZoomBar->setZoomSliderValue(size); + d->statusZoomBar->setZoomTrackerText(i18n("Size: %1").arg(size)); +} + +void DigikamApp::slotZoomChanged(double zoom, int size) +{ + d->statusZoomBar->setZoomSliderValue(size); + d->statusZoomBar->setZoomTrackerText(i18n("zoom: %1%").arg((int)(zoom*100.0))); +} + +void DigikamApp::slotTogglePreview(bool t) +{ + // NOTE: if 't' is true, we are in Preview Mode, else we are in AlbumView Mode + + // This is require if ESC is pressed to go out of Preview Mode. + // imagePreviewAction is handled by F3 key only. + d->imagePreviewAction->setChecked(t); + + // Here, we will toggle some menu actions depending of current Mode. + + // Select menu. + d->selectAllAction->setEnabled(!t); + d->selectNoneAction->setEnabled(!t); + d->selectInvertAction->setEnabled(!t); + + // View menu + d->albumSortAction->setEnabled(!t); + d->imageSortAction->setEnabled(!t); + d->zoomTo100percents->setEnabled(t); + d->zoomFitToWindowAction->setEnabled(t); +} + +void DigikamApp::slotAlbumAddImages() +{ + TQString path = KFileDialog::getExistingDirectory(TDEGlobalSettings::documentPath(), this, + i18n("Select folder to parse")); + + if(path.isEmpty()) + return; + + // The folder contents will be parsed by Camera interface in "Directory Browse" mode. + downloadFrom(path); +} + +void DigikamApp::slotShowMenuBar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +} // namespace Digikam diff --git a/src/digikam/digikamapp.h b/src/digikam/digikamapp.h new file mode 100644 index 00000000..899603d6 --- /dev/null +++ b/src/digikam/digikamapp.h @@ -0,0 +1,181 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : main digiKam interface implementation + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2006 by Tom Albers + * Copyright (C) 2002-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIGIKAMAPP_H +#define DIGIKAMAPP_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "albumlister.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class ImageInfo; +class CameraType; +class DigikamAppPriv; + +class DIGIKAM_EXPORT DigikamApp : public TDEMainWindow +{ + TQ_OBJECT + +public: + + DigikamApp(); + ~DigikamApp(); + + virtual void show(); + + static DigikamApp* getinstance(); + + // KIPI Actions collections access. + const TQPtrList& menuImageActions(); + const TQPtrList& menuBatchActions(); + const TQPtrList& menuAlbumActions(); + + const TQPtrList menuImportActions(); + const TQPtrList menuExportActions(); + + void autoDetect(); + void downloadFrom(const TQString &cameraGuiPath); + void enableZoomPlusAction(bool val); + void enableZoomMinusAction(bool val); + void enableAlbumBackwardHistory(bool enable); + void enableAlbumForwardHistory(bool enable); + +signals: + + void signalEscapePressed(); + void signalNextItem(); + void signalPrevItem(); + void signalFirstItem(); + void signalLastItem(); + void signalCopyAlbumItemsSelection(); + void signalPasteAlbumItemsSelection(); + void signalCancelButtonPressed(); + void signalResetTagFilters(); + +protected: + + bool queryClose(); + +protected slots: + + void slotCameraMediaMenuEntries( TDEIO::Job *, const TDEIO::UDSEntryList & ); + +private: + + bool setup(bool iccSetupPage=false); + void setupView(); + void setupStatusBar(); + void setupActions(); + void setupAccelerators(); + void loadPlugins(); + void loadCameras(); + void populateThemes(); + +private slots: + + void slotAlbumAddImages(); + void slotAlbumSelected(bool val); + void slotTagSelected(bool val); + void slotImageSelected(const TQPtrList&, bool, bool, const KURL::List&); + void slotExit(); + void slotShowTip(); + void slotShowKipiHelp(); + void slotDonateMoney(); + void slotContribute(); + void slotRawCameraList(); + + void slotRecurseAlbums(bool); + void slotRecurseTags(bool); + + void slotAboutToShowForwardMenu(); + void slotAboutToShowBackwardMenu(); + + void slotSetup(); + void slotSetupCamera(); + void slotSetupChanged(); + + void slotKipiPluginPlug(); + + TQString convertToLocalUrl( const TQString& folder ); + void slotDownloadImages( const TQString& folder ); + void slotDownloadImages(); + void slotCameraConnect(); + void slotCameraMediaMenu(); + void slotDownloadImagesFromMedia( int id ); + void slotCameraAdded(CameraType *ctype); + void slotCameraRemoved(CameraType *ctype); + void slotCameraAutoDetect(); + void slotDcopDownloadImages(const TQString& folder); + void slotDcopCameraAutoDetect(); + void slotEditKeys(); + void slotConfToolbars(); + void slotShowMenuBar(); + void slotToggleFullScreen(); + + void slotDatabaseRescan(); + void slotRebuildAllThumbs(); + void slotRebuildAllThumbsDone(); + void slotSyncAllPicturesMetadata(); + void slotSyncAllPicturesMetadataDone(); + + void slotChangeTheme(const TQString& theme); + void slotThemeChanged(); + + void slotProgressBarMode(int, const TQString&); + void slotProgressValue(int); + + void slotZoomSliderChanged(int); + void slotThumbSizeChanged(int); + void slotZoomChanged(double, int); + void slotTogglePreview(bool); + +private: + + DigikamAppPriv *d; + + static DigikamApp *m_instance; +}; + +} // namespace Digikam + +#endif // DIGIKAMAPP_H diff --git a/src/digikam/digikamappprivate.h b/src/digikam/digikamappprivate.h new file mode 100644 index 00000000..cf5e43a9 --- /dev/null +++ b/src/digikam/digikamappprivate.h @@ -0,0 +1,269 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-31-01 + * Description : main digiKam interface implementation + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// libKipi includes. + +#include + +// Local includes. + +#include "albummanager.h" +#include "albumsettings.h" +#include "cameralist.h" +#include "imagepluginloader.h" +#include "splashscreen.h" +#include "kipiinterface.h" +#include "statuszoombar.h" +#include "statusprogressbar.h" +#include "statusnavigatebar.h" +#include "digikamview.h" + +class TDEToolBarPopupAction; +class TDEToggleAction; +class TDEActionMenu; +class TDESelectAction; + +namespace Digikam +{ + +class DCOPIface; +class SearchTextBar; +class AlbumIconViewFilter; + +class DigikamAppPriv +{ +public: + + DigikamAppPriv() + { + fullScreen = false; + validIccPath = true; + cameraMediaList = 0; + accelerators = 0; + config = 0; + newAction = 0; + deleteAction = 0; + imageDeletePermanentlyAction = 0; + imageDeletePermanentlyDirectlyAction = 0; + imageTrashDirectlyAction = 0; + albumSortAction = 0; + recurseAlbumsAction = 0; + recurseTagsAction = 0; + backwardActionMenu = 0; + forwardActionMenu = 0; + addImagesAction = 0; + propsEditAction = 0; + albumImportAction = 0; + openInKonquiAction = 0; + refreshAlbumAction = 0; + syncAlbumMetadataAction = 0; + newTagAction = 0; + deleteTagAction = 0; + editTagAction = 0; + imagePreviewAction = 0; + imageViewAction = 0; + imageLightTableAction = 0; + imageAddLightTableAction = 0; + imageSetExifOrientation1Action = 0; + imageSetExifOrientation2Action = 0; + imageSetExifOrientation3Action = 0; + imageSetExifOrientation4Action = 0; + imageSetExifOrientation5Action = 0; + imageSetExifOrientation6Action = 0; + imageSetExifOrientation7Action = 0; + imageSetExifOrientation8Action = 0; + imageRenameAction = 0; + imageDeleteAction = 0; + imageSortAction = 0; + imageExifOrientationActionMenu = 0; + selectAllAction = 0; + selectNoneAction = 0; + selectInvertAction = 0; + fullScreenAction = 0; + slideShowAction = 0; + slideShowAllAction = 0; + slideShowSelectionAction = 0; + slideShowRecursiveAction = 0; + rating0Star = 0; + rating1Star = 0; + rating2Star = 0; + rating3Star = 0; + rating4Star = 0; + rating5Star = 0; + quitAction = 0; + tipAction = 0; + rawCameraListAction = 0; + kipiHelpAction = 0; + donateMoneyAction = 0; + cameraMenuAction = 0; + themeMenuAction = 0; + albumSettings = 0; + albumManager = 0; + dcopIface = 0; + imagePluginsLoader = 0; + kipiInterface = 0; + cameraList = 0; + statusProgressBar = 0; + statusNavigateBar = 0; + statusZoomBar = 0; + kipiPluginLoader = 0; + view = 0; + splashScreen = 0; + zoomTo100percents = 0; + zoomFitToWindowAction = 0; + zoomPlusAction = 0; + zoomMinusAction = 0; + albumIconViewFilter = 0; + contributeAction = 0; + showMenuBarAction = 0; + } + + bool fullScreen; + bool validIccPath; + + // KIPI plugins support + TQPtrList kipiFileActionsExport; + TQPtrList kipiFileActionsImport; + TQPtrList kipiImageActions; + TQPtrList kipiToolsActions; + TQPtrList kipiBatchActions; + TQPtrList kipiAlbumActions; + + TQMap mediaItems; + + TQString cameraGuiPath; + + TDEPopupMenu *cameraMediaList; + + TDEAccel *accelerators; + + TDEConfig *config; + + // Album Actions + TDEAction *newAction; + TDEAction *deleteAction; + TDEAction *imageDeletePermanentlyAction; + TDEAction *imageDeletePermanentlyDirectlyAction; + TDEAction *imageTrashDirectlyAction; + TDEToolBarPopupAction *backwardActionMenu; + TDEToolBarPopupAction *forwardActionMenu; + + TDEAction *addImagesAction; + TDEAction *propsEditAction; + TDEAction *albumImportAction; + TDEAction *openInKonquiAction; + TDEAction *refreshAlbumAction; + TDEAction *syncAlbumMetadataAction; + + // Tag Actions + TDEAction *newTagAction; + TDEAction *deleteTagAction; + TDEAction *editTagAction; + + // Image Actions + TDEToggleAction *imagePreviewAction; + TDEAction *imageLightTableAction; + TDEAction *imageAddLightTableAction; + TDEAction *imageViewAction; + TDEAction *imageSetExifOrientation1Action; + TDEAction *imageSetExifOrientation2Action; + TDEAction *imageSetExifOrientation3Action; + TDEAction *imageSetExifOrientation4Action; + TDEAction *imageSetExifOrientation5Action; + TDEAction *imageSetExifOrientation6Action; + TDEAction *imageSetExifOrientation7Action; + TDEAction *imageSetExifOrientation8Action; + TDEAction *imageRenameAction; + TDEAction *imageDeleteAction; + TDEActionMenu *imageExifOrientationActionMenu; + + // Selection Actions + TDEAction *selectAllAction; + TDEAction *selectNoneAction; + TDEAction *selectInvertAction; + + // View Actions + TDEToggleAction *fullScreenAction; + TDEToggleAction *showMenuBarAction; + TDEActionMenu *slideShowAction; + TDEAction *slideShowAllAction; + TDEAction *slideShowSelectionAction; + TDEAction *slideShowRecursiveAction; + TDESelectAction *imageSortAction; + TDESelectAction *albumSortAction; + TDEToggleAction *recurseAlbumsAction; + TDEToggleAction *recurseTagsAction; + TDEAction *zoomPlusAction; + TDEAction *zoomMinusAction; + TDEAction *zoomFitToWindowAction; + TDEAction *zoomTo100percents; + + TDEAction *rating0Star; + TDEAction *rating1Star; + TDEAction *rating2Star; + TDEAction *rating3Star; + TDEAction *rating4Star; + TDEAction *rating5Star; + + // Application Actions + TDEAction *rawCameraListAction; + TDEAction *quitAction; + TDEAction *tipAction; + TDEAction *kipiHelpAction; + TDEAction *donateMoneyAction; + TDEAction *contributeAction; + TDEActionMenu *cameraMenuAction; + TDESelectAction *themeMenuAction; + + AlbumSettings *albumSettings; + AlbumManager *albumManager; + AlbumIconViewFilter *albumIconViewFilter; + SplashScreen *splashScreen; + DCOPIface *dcopIface; + ImagePluginLoader *imagePluginsLoader; + DigikamKipiInterface *kipiInterface; + DigikamView *view; + CameraList *cameraList; + StatusZoomBar *statusZoomBar; + StatusProgressBar *statusProgressBar; + StatusNavigateBar *statusNavigateBar; + KIPI::PluginLoader *kipiPluginLoader; +}; + +} // namespace Digikam diff --git a/src/digikam/digikamfirstrun.cpp b/src/digikam/digikamfirstrun.cpp new file mode 100644 index 00000000..e7a95c01 --- /dev/null +++ b/src/digikam/digikamfirstrun.cpp @@ -0,0 +1,183 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-01 + * Description : dialog displayed at the first digiKam run + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +#include +#include +} + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "firstrun.h" +#include "digikamfirstrun.h" +#include "digikamfirstrun.moc" + +namespace Digikam +{ + +using namespace std; + +DigikamFirstRun::DigikamFirstRun(TDEConfig* config, TQWidget* parent, + const char* name, bool modal, WFlags fl) + : KDialogBase(parent, name, modal, i18n( "Album Library Path" ), + Help|Ok|Cancel, Ok, true ) + +{ + setHelp("firstrundialog.anchor", "digikam"); + setWFlags(fl); + + m_config = config; + m_ui = new FirstRunWidget(this); + setMainWidget(m_ui); + m_ui->m_path->setURL(TQDir::homeDirPath() + + i18n("This is a path name so you should " + "include the slash in the translation","/Pictures")); + m_ui->m_path->setMode(KFile::Directory | KFile::LocalOnly); + + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + m_ui->m_pixLabel->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, + 128, TDEIcon::DefaultState, 0, true)); + m_ui->setMinimumSize(450, m_ui->sizeHint().height()); +} + +DigikamFirstRun::~DigikamFirstRun() +{ +} + +void DigikamFirstRun::slotOk() +{ + TQString albumLibraryFolder = m_ui->m_path->url(); + + if (albumLibraryFolder.isEmpty()) + { + KMessageBox::sorry(this, i18n("You must select a folder for digiKam to " + "use as the Album Library folder.")); + return; + } + + if (!albumLibraryFolder.startsWith("/")) + { + albumLibraryFolder.prepend(TQDir::homeDirPath()); + } + + if (KURL(albumLibraryFolder).equals(KURL(TQDir::homeDirPath()), true)) + { + KMessageBox::sorry(this, i18n("digiKam cannot use your home folder as " + "the Album Library folder.")); + return; + } + + TQDir targetPath(albumLibraryFolder); + + if (!targetPath.exists()) + { + int rc = KMessageBox::questionYesNo(this, + i18n("The folder you selected does not exist: " + + "

%1

" + "Would you like digiKam to create it?
") + .arg(albumLibraryFolder), + i18n("Create Folder?")); + + if (rc == KMessageBox::No) + { + return; + } + + if (!targetPath.mkdir(albumLibraryFolder)) + { + KMessageBox::sorry(this, + i18n("digiKam could not create the folder shown below. " + "Please select a different location." + "

%1

").arg(albumLibraryFolder), + i18n("Create Folder Failed")); + return; + } + } + + TQFileInfo path(albumLibraryFolder); + + if (!path.isWritable()) + { + KMessageBox::information(this, i18n("No write access for this path.\n" + "Warning: the comment and tag features " + "will not work.")); + return; + } + + m_config->setGroup("General Settings"); + m_config->writeEntry("Version", digikam_version); + + m_config->setGroup("Album Settings"); + m_config->writePathEntry("Album Path", albumLibraryFolder); + m_config->sync(); + + KDialogBase::accept(); + + TQString ErrorMsg, URL; + + if (kapp->startServiceByDesktopName("digikam", URL , &ErrorMsg) > 0) + { + DError() << ErrorMsg << endl; + KMessageBox::sorry(this, i18n("Cannot restart digiKam automatically.\n" + "Please restart digiKam manually.")); + } +} + +} // namespace Digikam + diff --git a/src/digikam/digikamfirstrun.h b/src/digikam/digikamfirstrun.h new file mode 100644 index 00000000..e8921f5b --- /dev/null +++ b/src/digikam/digikamfirstrun.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-02-01 + * Description : dialog displayed at the first digiKam run + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIGIKAMFIRSTRUN_H +#define DIGIKAMFIRSTRUN_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TDEConfig; +class KURLRequester; + +namespace Digikam +{ + +class FirstRunWidget; + +class DIGIKAM_EXPORT DigikamFirstRun : public KDialogBase +{ + TQ_OBJECT + +public: + + DigikamFirstRun(TDEConfig* config, TQWidget* parent = 0, const char* name = 0, + bool modal = true, WFlags fl = WDestructiveClose); + ~DigikamFirstRun(); + +protected slots: + + void slotOk(); + +private: + + TDEConfig *m_config; + FirstRunWidget *m_ui; +}; + +} // namespace Digikam + +#endif // DIGIKAMFIRSTRUN_H diff --git a/src/digikam/digikamui.rc b/src/digikam/digikamui.rc new file mode 100644 index 00000000..e4763abe --- /dev/null +++ b/src/digikam/digikamui.rc @@ -0,0 +1,147 @@ + + + + + + + &Album + + + + + + + + + + + + + + + + + + T&ag + + + + + + + + &Image + + + + + + + + + + + + + + &Edit + + + + + + + &View + + + + + + + + + + + + + + + &Tools + + + + + + + + + + + + + + + &Batch + + + + &Import + + + + + + + + + &Export + + + + &Settings + + + + + + + + + + + + &Help + + + + + + + + + + + + + + Main Toolbar + + + + + + + + + + + + + + + + + + + + diff --git a/src/digikam/digikamview.cpp b/src/digikam/digikamview.cpp new file mode 100644 index 00000000..a8fb42fb --- /dev/null +++ b/src/digikam/digikamview.cpp @@ -0,0 +1,1570 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : implementation of album view interface. + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2002-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "albummanager.h" +#include "album.h" +#include "albumwidgetstack.h" +#include "albumfolderview.h" +#include "albumiconview.h" +#include "albumiconitem.h" +#include "albumsettings.h" +#include "albumhistory.h" +#include "batchsyncmetadata.h" +#include "slideshow.h" +#include "sidebar.h" +#include "imagepropertiessidebardb.h" +#include "imageinfoalbumsjob.h" +#include "imagepreviewview.h" +#include "datefolderview.h" +#include "tagfolderview.h" +#include "searchfolderview.h" +#include "searchtextbar.h" +#include "statusprogressbar.h" +#include "tagfilterview.h" +#include "timelineview.h" +#include "timelinefolderview.h" +#include "thumbnailsize.h" +#include "dio.h" +#include "digikamapp.h" +#include "digikamview.h" +#include "digikamview.moc" + +namespace Digikam +{ + +class DigikamViewPriv +{ +public: + + DigikamViewPriv() + { + folderBox = 0; + tagBox = 0; + searchBox = 0; + tagFilterBox = 0; + folderSearchBar = 0; + tagSearchBar = 0; + searchSearchBar = 0; + tagFilterSearchBar = 0; + splitter = 0; + parent = 0; + iconView = 0; + folderView = 0; + albumManager = 0; + albumHistory = 0; + leftSideBar = 0; + rightSideBar = 0; + dateFolderView = 0; + timeLineView = 0; + tagFolderView = 0; + searchFolderView = 0; + tagFilterView = 0; + albumWidgetStack = 0; + selectionTimer = 0; + thumbSizeTimer = 0; + needDispatchSelection = false; + cancelSlideShow = false; + thumbSize = ThumbnailSize::Medium; + } + + bool needDispatchSelection; + bool cancelSlideShow; + + int initialAlbumID; + int thumbSize; + + TQSplitter *splitter; + + TQTimer *selectionTimer; + TQTimer *thumbSizeTimer; + + TQVBox *folderBox; + TQVBox *tagBox; + TQVBox *searchBox; + TQVBox *tagFilterBox; + + SearchTextBar *folderSearchBar; + SearchTextBar *tagSearchBar; + SearchTextBar *searchSearchBar; + SearchTextBar *tagFilterSearchBar; + + DigikamApp *parent; + + AlbumIconView *iconView; + AlbumFolderView *folderView; + AlbumManager *albumManager; + AlbumHistory *albumHistory; + AlbumWidgetStack *albumWidgetStack; + + Sidebar *leftSideBar; + ImagePropertiesSideBarDB *rightSideBar; + + DateFolderView *dateFolderView; + TimeLineView *timeLineView; + TagFolderView *tagFolderView; + SearchFolderView *searchFolderView; + TagFilterView *tagFilterView; +}; + +DigikamView::DigikamView(TQWidget *parent) + : TQHBox(parent) +{ + d = new DigikamViewPriv; + d->parent = static_cast(parent); + d->albumManager = AlbumManager::instance(); + d->leftSideBar = new Sidebar(this, "Digikam Left Sidebar", Sidebar::Left); + + d->splitter = new TQSplitter(this); + d->splitter->setFrameStyle( TQFrame::NoFrame ); + d->splitter->setFrameShadow( TQFrame::Plain ); + d->splitter->setFrameShape( TQFrame::NoFrame ); + d->splitter->setOpaqueResize(false); + + d->leftSideBar->setSplitter(d->splitter); + d->albumWidgetStack = new AlbumWidgetStack(d->splitter); + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1); + d->albumWidgetStack->setSizePolicy(rightSzPolicy); + d->iconView = d->albumWidgetStack->albumIconView(); + + d->rightSideBar = new ImagePropertiesSideBarDB(this, "Digikam Right Sidebar", d->splitter, + Sidebar::Right, true); + + // To the left. + + // Folders sidebar tab contents. + d->folderBox = new TQVBox(this); + d->folderView = new AlbumFolderView(d->folderBox); + d->folderSearchBar = new SearchTextBar(d->folderBox, "DigikamViewFolderSearchBar"); + d->folderBox->setSpacing(KDialog::spacingHint()); + d->folderBox->setMargin(0); + + // Tags sidebar tab contents. + d->tagBox = new TQVBox(this); + d->tagFolderView = new TagFolderView(d->tagBox); + d->tagSearchBar = new SearchTextBar(d->tagBox, "DigikamViewTagSearchBar"); + d->tagBox->setSpacing(KDialog::spacingHint()); + d->tagBox->setMargin(0); + + // Search sidebar tab contents. + d->searchBox = new TQVBox(this); + d->searchFolderView = new SearchFolderView(d->searchBox); + d->searchSearchBar = new SearchTextBar(d->searchBox, "DigikamViewSearchSearchBar"); + d->searchBox->setSpacing(KDialog::spacingHint()); + d->searchBox->setMargin(0); + + d->dateFolderView = new DateFolderView(this); + d->timeLineView = new TimeLineView(this); + + d->leftSideBar->appendTab(d->folderBox, SmallIcon("folder_image"), i18n("Albums")); + d->leftSideBar->appendTab(d->dateFolderView, SmallIcon("date"), i18n("Calendar")); + d->leftSideBar->appendTab(d->tagBox, SmallIcon("tag"), i18n("Tags")); + d->leftSideBar->appendTab(d->timeLineView, SmallIcon("clock"), i18n("Timeline")); + d->leftSideBar->appendTab(d->searchBox, SmallIcon("edit-find"), i18n("Searches")); + + // To the right. + + // Tags Filter sidebar tab contents. + d->tagFilterBox = new TQVBox(this); + d->tagFilterView = new TagFilterView(d->tagFilterBox); + d->tagFilterSearchBar = new SearchTextBar(d->tagFilterBox, "DigikamViewTagFilterSearchBar"); + d->tagFilterBox->setSpacing(KDialog::spacingHint()); + d->tagFilterBox->setMargin(0); + + d->rightSideBar->appendTab(d->tagFilterBox, SmallIcon("tag-assigned"), i18n("Tag Filters")); + + d->selectionTimer = new TQTimer(this); + + setupConnections(); + + d->albumManager->setItemHandler(d->iconView); + d->albumHistory = new AlbumHistory(); +} + +DigikamView::~DigikamView() +{ + if (d->thumbSizeTimer) + delete d->thumbSizeTimer; + + saveViewState(); + + delete d->albumHistory; + d->albumManager->setItemHandler(0); + delete d; +} + +void DigikamView::applySettings() +{ + AlbumSettings *settings = AlbumSettings::instance(); + d->iconView->applySettings(settings); + d->albumWidgetStack->imagePreviewView()->setLoadFullImageSize(settings->getPreviewLoadFullImageSize()); + refreshView(); +} + +void DigikamView::refreshView() +{ + d->folderView->refresh(); + d->dateFolderView->refresh(); + d->tagFolderView->refresh(); + d->tagFilterView->refresh(); + d->rightSideBar->refreshTagsView(); +} + +void DigikamView::setupConnections() +{ + // -- DigikamApp connections ---------------------------------- + + connect(d->parent, TQ_SIGNAL(signalEscapePressed()), + this, TQ_SLOT(slotEscapePreview())); + + connect(d->parent, TQ_SIGNAL(signalEscapePressed()), + d->albumWidgetStack, TQ_SLOT(slotEscapePreview())); + + connect(d->parent, TQ_SIGNAL(signalNextItem()), + this, TQ_SLOT(slotNextItem())); + + connect(d->parent, TQ_SIGNAL(signalPrevItem()), + this, TQ_SLOT(slotPrevItem())); + + connect(d->parent, TQ_SIGNAL(signalFirstItem()), + this, TQ_SLOT(slotFirstItem())); + + connect(d->parent, TQ_SIGNAL(signalLastItem()), + this, TQ_SLOT(slotLastItem())); + + connect(d->parent, TQ_SIGNAL(signalCopyAlbumItemsSelection()), + d->iconView, TQ_SLOT(slotCopy())); + + connect(d->parent, TQ_SIGNAL(signalPasteAlbumItemsSelection()), + d->iconView, TQ_SLOT(slotPaste())); + + connect(this, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(this, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + connect(d->parent, TQ_SIGNAL(signalCancelButtonPressed()), + this, TQ_SLOT(slotCancelSlideShow())); + + // -- AlbumManager connections -------------------------------- + + connect(d->albumManager, TQ_SIGNAL(signalAlbumCurrentChanged(Album*)), + this, TQ_SLOT(slotAlbumSelected(Album*))); + + connect(d->albumManager, TQ_SIGNAL(signalAllAlbumsLoaded()), + this, TQ_SLOT(slotAllAlbumsLoaded())); + + connect(d->albumManager, TQ_SIGNAL(signalAlbumItemsSelected(bool) ), + this, TQ_SLOT(slotImageSelected())); + + connect(d->albumManager, TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(d->albumManager, TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(d->albumManager, TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotAlbumRenamed(Album*))); + + connect(d->albumManager, TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotAlbumsCleared())); + + // -- IconView Connections ------------------------------------- + + connect(d->iconView, TQ_SIGNAL(signalItemsUpdated(const KURL::List&)), + d->albumWidgetStack, TQ_SLOT(slotItemsUpdated(const KURL::List&))); + + connect(d->iconView, TQ_SIGNAL(signalItemsAdded()), + this, TQ_SLOT(slotImageSelected())); + + connect(d->iconView, TQ_SIGNAL(signalItemsAdded()), + this, TQ_SLOT(slotAlbumHighlight())); + + connect(d->iconView, TQ_SIGNAL(signalPreviewItem(AlbumIconItem*)), + this, TQ_SLOT(slotTogglePreviewMode(AlbumIconItem*))); + + //connect(d->iconView, TQ_SIGNAL(signalItemDeleted(AlbumIconItem*)), + // this, TQ_SIGNAL(signalNoCurrentItem())); + + connect(d->iconView, TQ_SIGNAL(signalGotoAlbumAndItem(AlbumIconItem *)), + this, TQ_SLOT(slotGotoAlbumAndItem(AlbumIconItem *))); + + connect(d->iconView, TQ_SIGNAL(signalGotoDateAndItem(AlbumIconItem *)), + this, TQ_SLOT(slotGotoDateAndItem(AlbumIconItem *))); + + connect(d->iconView, TQ_SIGNAL(signalGotoTagAndItem(int)), + this, TQ_SLOT(slotGotoTagAndItem(int))); + + connect(d->folderView, TQ_SIGNAL(signalAlbumModified()), + d->iconView, TQ_SLOT(slotAlbumModified())); + + connect(d->iconView, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(d->iconView, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + // -- Sidebar Connections ------------------------------------- + + connect(d->leftSideBar, TQ_SIGNAL(signalChangedTab(TQWidget*)), + this, TQ_SLOT(slotLeftSidebarChangedTab(TQWidget*))); + + connect(d->rightSideBar, TQ_SIGNAL(signalFirstItem()), + this, TQ_SLOT(slotFirstItem())); + + connect(d->rightSideBar, TQ_SIGNAL(signalNextItem()), + this, TQ_SLOT(slotNextItem())); + + connect(d->rightSideBar, TQ_SIGNAL(signalPrevItem()), + this, TQ_SLOT(slotPrevItem())); + + connect(d->rightSideBar, TQ_SIGNAL(signalLastItem()), + this, TQ_SLOT(slotLastItem())); + + connect(this, TQ_SIGNAL(signalNoCurrentItem()), + d->rightSideBar, TQ_SLOT(slotNoCurrentItem())); + + connect(d->rightSideBar, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(d->rightSideBar, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + connect(d->tagFilterView, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(d->tagFilterView, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + connect(d->tagFolderView, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(d->tagFolderView, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + connect(d->parent, TQ_SIGNAL(signalResetTagFilters()), + d->tagFilterView, TQ_SLOT(slotResetTagFilters())); + + // -- Filter Bars Connections --------------------------------- + + connect(d->folderSearchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + d->folderView, TQ_SLOT(slotTextFolderFilterChanged(const TQString&))); + + connect(d->tagSearchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + d->tagFolderView, TQ_SLOT(slotTextTagFilterChanged(const TQString&))); + + connect(d->searchSearchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + d->searchFolderView, TQ_SLOT(slotTextSearchFilterChanged(const TQString&))); + + connect(d->tagFilterSearchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + d->tagFilterView, TQ_SLOT(slotTextTagFilterChanged(const TQString&))); + + connect(d->folderView, TQ_SIGNAL(signalTextFolderFilterMatch(bool)), + d->folderSearchBar, TQ_SLOT(slotSearchResult(bool))); + + connect(d->tagFolderView, TQ_SIGNAL(signalTextTagFilterMatch(bool)), + d->tagSearchBar, TQ_SLOT(slotSearchResult(bool))); + + connect(d->searchFolderView, TQ_SIGNAL(signalTextSearchFilterMatch(bool)), + d->searchSearchBar, TQ_SLOT(slotSearchResult(bool))); + + connect(d->tagFilterView, TQ_SIGNAL(signalTextTagFilterMatch(bool)), + d->tagFilterSearchBar, TQ_SLOT(slotSearchResult(bool))); + + // -- Preview image widget Connections ------------------------ + + connect(d->albumWidgetStack, TQ_SIGNAL(signalNextItem()), + this, TQ_SLOT(slotNextItem())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalPrevItem()), + this, TQ_SLOT(slotPrevItem())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalEditItem()), + this, TQ_SLOT(slotImageEdit())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalDeleteItem()), + this, TQ_SLOT(slotImageDelete())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalToggledToPreviewMode(bool)), + this, TQ_SLOT(slotToggledToPreviewMode(bool))); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalBack2Album()), + this, TQ_SLOT(slotEscapePreview())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalSlideShow()), + this, TQ_SLOT(slotSlideShowAll())); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalZoomFactorChanged(double)), + this, TQ_SLOT(slotZoomFactorChanged(double))); + + connect(d->albumWidgetStack, TQ_SIGNAL(signalInsert2LightTable()), + this, TQ_SLOT(slotImageAddToLightTable())); + + // -- Selection timer --------------- + + connect(d->selectionTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotDispatchImageSelected())); +} + +void DigikamView::loadViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup("MainWindow"); + + if(config->hasKey("SplitterSizes")) + d->splitter->setSizes(config->readIntListEntry("SplitterSizes")); + + d->initialAlbumID = config->readNumEntry("InitialAlbumID", 0); +} + +void DigikamView::saveViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup("MainWindow"); + config->writeEntry("SplitterSizes", d->splitter->sizes()); + + Album *album = AlbumManager::instance()->currentAlbum(); + if(album) + { + config->writeEntry("InitialAlbumID", album->globalID()); + } + else + { + config->writeEntry("InitialAlbumID", 0); + } +} + +void DigikamView::showSideBars() +{ + d->leftSideBar->restore(); + d->rightSideBar->restore(); +} + +void DigikamView::hideSideBars() +{ + d->leftSideBar->backup(); + d->rightSideBar->backup(); +} + +void DigikamView::slotFirstItem(void) +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->firstItem()); + d->iconView->clearSelection(); + d->iconView->updateContents(); + if (currItem) + d->iconView->setCurrentItem(currItem); +} + +void DigikamView::slotPrevItem(void) +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->currentItem()); + if (currItem) + { + if (currItem->prevItem()) + { + d->iconView->clearSelection(); + d->iconView->updateContents(); + d->iconView->setCurrentItem(currItem->prevItem()); + } + } +} + +void DigikamView::slotNextItem(void) +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->currentItem()); + if (currItem) + { + if (currItem->nextItem()) + { + d->iconView->clearSelection(); + d->iconView->updateContents(); + d->iconView->setCurrentItem(currItem->nextItem()); + } + } +} + +void DigikamView::slotLastItem(void) +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->lastItem()); + d->iconView->clearSelection(); + d->iconView->updateContents(); + if (currItem) + d->iconView->setCurrentItem(currItem); +} + +void DigikamView::slotAllAlbumsLoaded() +{ + disconnect(d->albumManager, TQ_SIGNAL(signalAllAlbumsLoaded()), + this, TQ_SLOT(slotAllAlbumsLoaded())); + + loadViewState(); + Album *album = d->albumManager->findAlbum(d->initialAlbumID); + d->albumManager->setCurrentAlbum(album); + + d->leftSideBar->loadViewState(); + d->rightSideBar->loadViewState(); + d->rightSideBar->populateTags(); + + slotAlbumSelected(album); +} + +void DigikamView::slotSortAlbums(int order) +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) return; + settings->setAlbumSortOrder( (AlbumSettings::AlbumSortOrder) order); + d->folderView->resort(); +} + +void DigikamView::slotNewAlbum() +{ + d->folderView->albumNew(); +} + +void DigikamView::slotDeleteAlbum() +{ + d->folderView->albumDelete(); +} + +void DigikamView::slotNewTag() +{ + d->tagFolderView->tagNew(); +} + +void DigikamView::slotDeleteTag() +{ + d->tagFolderView->tagDelete(); +} + +void DigikamView::slotEditTag() +{ + d->tagFolderView->tagEdit(); +} + +void DigikamView::slotNewQuickSearch() +{ + if (d->leftSideBar->getActiveTab() != d->searchBox) + d->leftSideBar->setActiveTab(d->searchBox); + d->searchFolderView->quickSearchNew(); +} + +void DigikamView::slotNewAdvancedSearch() +{ + if (d->leftSideBar->getActiveTab() != d->searchBox) + d->leftSideBar->setActiveTab(d->searchBox); + d->searchFolderView->extendedSearchNew(); +} + +void DigikamView::slotAlbumAdded(Album *album) +{ + if (!album->isRoot()) + { + switch (album->type()) + { + case Album::PHYSICAL: + { + d->folderSearchBar->lineEdit()->completionObject()->addItem(album->title()); + break; + } + case Album::TAG: + { + d->tagSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->tagFilterSearchBar->lineEdit()->completionObject()->addItem(album->title()); + break; + } + case Album::SEARCH: + { + d->searchSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->timeLineView->searchBar()->lineEdit()->completionObject()->addItem(album->title()); + break; + } + default: + { + // Nothing to do with Album::DATE + break; + } + } + } +} + +void DigikamView::slotAlbumDeleted(Album *album) +{ + d->albumHistory->deleteAlbum(album); + + // display changed tags + if (album->type() == Album::TAG) + d->iconView->updateContents(); + + /* + // For what is this needed? + Album *a; + TQWidget *widget; + d->albumHistory->getCurrentAlbum(&a, &widget); + + changeAlbumFromHistory(a, widget); + */ + + if (!album->isRoot()) + { + switch (album->type()) + { + case Album::PHYSICAL: + { + d->folderSearchBar->lineEdit()->completionObject()->removeItem(album->title()); + break; + } + case Album::TAG: + { + d->tagSearchBar->lineEdit()->completionObject()->removeItem(album->title()); + d->tagFilterSearchBar->lineEdit()->completionObject()->removeItem(album->title()); + break; + } + case Album::SEARCH: + { + d->searchSearchBar->lineEdit()->completionObject()->removeItem(album->title()); + d->timeLineView->searchBar()->lineEdit()->completionObject()->removeItem(album->title()); + break; + } + default: + { + // Nothing to do with Album::DATE + break; + } + } + } +} + +void DigikamView::slotAlbumRenamed(Album *album) +{ + // display changed names + + if (album == d->albumManager->currentAlbum()) + d->iconView->updateContents(); + + if (!album->isRoot()) + { + switch (album->type()) + { + case Album::PHYSICAL: + { + d->folderSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->folderView->slotTextFolderFilterChanged(d->folderSearchBar->lineEdit()->text()); + break; + } + case Album::TAG: + { + d->tagSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->tagFolderView->slotTextTagFilterChanged(d->tagSearchBar->lineEdit()->text()); + + d->tagFilterSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->tagFilterView->slotTextTagFilterChanged(d->tagFilterSearchBar->lineEdit()->text()); + break; + } + case Album::SEARCH: + { + d->searchSearchBar->lineEdit()->completionObject()->addItem(album->title()); + d->searchFolderView->slotTextSearchFilterChanged(d->searchSearchBar->lineEdit()->text()); + + d->timeLineView->searchBar()->lineEdit()->completionObject()->addItem(album->title()); + d->timeLineView->folderView()->slotTextSearchFilterChanged(d->timeLineView->searchBar()->lineEdit()->text()); + break; + } + default: + { + // Nothing to do with Album::DATE + break; + } + } + } +} + +void DigikamView::slotAlbumsCleared() +{ + d->iconView->clear(); + emit signalAlbumSelected(false); + + d->folderSearchBar->lineEdit()->completionObject()->clear(); + + d->tagSearchBar->lineEdit()->completionObject()->clear(); + d->tagFilterSearchBar->lineEdit()->completionObject()->clear(); + + d->searchSearchBar->lineEdit()->completionObject()->clear(); + d->timeLineView->searchBar()->lineEdit()->completionObject()->clear(); +} + +void DigikamView::slotAlbumHistoryBack(int steps) +{ + Album *album; + TQWidget *widget; + + d->albumHistory->back(&album, &widget, steps); + + changeAlbumFromHistory(album, widget); +} + +void DigikamView::slotAlbumHistoryForward(int steps) +{ + Album *album; + TQWidget *widget; + + d->albumHistory->forward(&album, &widget, steps); + + changeAlbumFromHistory(album, widget); +} + +void DigikamView::changeAlbumFromHistory(Album *album, TQWidget *widget) +{ + if (album && widget) + { + TQListViewItem *item = 0; + + // Check if widget is a vbox used to host folderview, tagview or searchview. + if (TQVBox *v = dynamic_cast(widget)) + { + if (v == d->folderBox) + { + item = (TQListViewItem*)album->extraData(d->folderView); + if(!item) return; + + d->folderView->setSelected(item, true); + d->folderView->ensureItemVisible(item); + } + else if (v == d->tagBox) + { + item = (TQListViewItem*)album->extraData(d->tagFolderView); + if(!item) return; + + d->tagFolderView->setSelected(item, true); + d->tagFolderView->ensureItemVisible(item); + } + else if (v == d->searchBox) + { + item = (TQListViewItem*)album->extraData(d->searchFolderView); + if(!item) return; + + d->searchFolderView->setSelected(item, true); + d->searchFolderView->ensureItemVisible(item); + } + } + else if (DateFolderView *v = dynamic_cast(widget)) + { + item = (TQListViewItem*)album->extraData(v); + if(!item) return; + v->setSelected(item); + } + else if (TimeLineView *v = dynamic_cast(widget)) + { + item = (TQListViewItem*)album->extraData(v->folderView()); + if(!item) return; + + v->folderView()->setSelected(item, true); + v->folderView()->ensureItemVisible(item); + } + + d->leftSideBar->setActiveTab(widget); + + d->parent->enableAlbumBackwardHistory(!d->albumHistory->isBackwardEmpty()); + d->parent->enableAlbumForwardHistory(!d->albumHistory->isForwardEmpty()); + } +} + +void DigikamView::clearHistory() +{ + d->albumHistory->clearHistory(); + d->parent->enableAlbumBackwardHistory(false); + d->parent->enableAlbumForwardHistory(false); +} + +void DigikamView::getBackwardHistory(TQStringList &titles) +{ + d->albumHistory->getBackwardHistory(titles); +} + +void DigikamView::getForwardHistory(TQStringList &titles) +{ + d->albumHistory->getForwardHistory(titles); +} + +void DigikamView::slotSelectAlbum(const KURL &) +{ + /* TODO + if (url.isEmpty()) + return; + + Album *album = d->albumManager->findPAlbum(url); + if(album && album->getViewItem()) + { + AlbumFolderItem_Deprecated *item; + item = static_cast(album->getViewItem()); + mFolderView_Deprecated->setSelected(item); + d->parent->enableAlbumBackwardHistory(!d->albumHistory->isBackwardEmpty()); + d->parent->enableAlbumForwardHistory(!d->albumHistory->isForwardEmpty()); + } + */ +} + +void DigikamView::slotGotoAlbumAndItem(AlbumIconItem* iconItem) +{ + KURL url( iconItem->imageInfo()->kurl() ); + url.cleanPath(); + + emit signalNoCurrentItem(); + + Album* album = iconItem->imageInfo()->album(); + + // Change the current album in list view. + d->folderView->setCurrentAlbum(album); + + // Change to (physical) Album view. + // Note, that this also opens the side bar if it is closed; this is + // considered as a feature, because it highlights that the view was changed. + d->leftSideBar->setActiveTab(d->folderBox); + + // Set the activate item url to find in the Album View after + // all items have be reloaded. + d->iconView->setAlbumItemToFind(url); + + // And finally toggle album manager to handle album history and + // reload all items. + d->albumManager->setCurrentAlbum(album); +} + +void DigikamView::slotGotoDateAndItem(AlbumIconItem* iconItem) +{ + KURL url( iconItem->imageInfo()->kurl() ); + url.cleanPath(); + TQDate date = iconItem->imageInfo()->dateTime().date(); + + emit signalNoCurrentItem(); + + // Change to Date Album view. + // Note, that this also opens the side bar if it is closed; this is + // considered as a feature, because it highlights that the view was changed. + d->leftSideBar->setActiveTab(d->dateFolderView); + + // Set the activate item url to find in the Album View after + // all items have be reloaded. + d->iconView->setAlbumItemToFind(url); + + // Change the year and month of the iconItem (day is unused). + d->dateFolderView->gotoDate(date); +} + +void DigikamView::slotGotoTagAndItem(int tagID) +{ + // FIXME: Arnd: don't know yet how to get the iconItem passed through ... + // FIXME: then we would know how to use the following ... + // KURL url( iconItem->imageInfo()->kurl() ); + // url.cleanPath(); + + emit signalNoCurrentItem(); + + // Change to Tag Folder view. + // Note, that this also opens the side bar if it is closed; this is + // considered as a feature, because it highlights that the view was changed. + d->leftSideBar->setActiveTab(d->tagBox); + + // Set the current tag in the tag folder view. + d->tagFolderView->selectItem(tagID); + + // Set the activate item url to find in the Tag View after + // all items have be reloaded. + // FIXME: see above + // d->iconView->setAlbumItemToFind(url); +} + +void DigikamView::slotAlbumSelected(Album* album) +{ + emit signalNoCurrentItem(); + + if (!album) + { + d->iconView->setAlbum(0); + emit signalAlbumSelected(false); + emit signalTagSelected(false); + return; + } + + if (album->type() == Album::PHYSICAL) + { + emit signalAlbumSelected(true); + emit signalTagSelected(false); + } + else if (album->type() == Album::TAG) + { + emit signalAlbumSelected(false); + emit signalTagSelected(true); + } + + d->albumHistory->addAlbum(album, d->leftSideBar->getActiveTab()); + d->parent->enableAlbumBackwardHistory(!d->albumHistory->isBackwardEmpty()); + d->parent->enableAlbumForwardHistory(!d->albumHistory->isForwardEmpty()); + + d->iconView->setAlbum(album); + if (album->isRoot()) + d->albumWidgetStack->setPreviewMode(AlbumWidgetStack::WelcomePageMode); + else + d->albumWidgetStack->setPreviewMode(AlbumWidgetStack::PreviewAlbumMode); +} + +void DigikamView::slotAlbumOpenInKonqui() +{ + Album *album = d->albumManager->currentAlbum(); + if (!album || album->type() != Album::PHYSICAL) + return; + + PAlbum* palbum = dynamic_cast(album); + + new KRun(palbum->folderPath()); // KRun will delete itself. +} + +void DigikamView::slotAlbumRefresh() +{ + d->iconView->refreshItems(d->iconView->allItems()); +} + +void DigikamView::slotImageSelected() +{ + // delay to slotDispatchImageSelected + d->needDispatchSelection = true; + d->selectionTimer->start(75, true); +} + +void DigikamView::slotDispatchImageSelected() +{ + if (d->needDispatchSelection) + { + // the list of copies of ImageInfos of currently selected items, currentItem first + TQPtrList list = d->iconView->selectedImageInfos(true); + // no copy needed for this one, as this list is just used for counting + // the total number of images + KURL::List listAll = d->iconView->allItems(); + + if (list.isEmpty()) + { + d->albumWidgetStack->setPreviewItem(); + emit signalImageSelected(list, false, false, listAll); + emit signalNoCurrentItem(); + } + else + { + d->rightSideBar->itemChanged(list); + + AlbumIconItem *selectedItem = d->iconView->firstSelectedItem(); + ImageInfo *previousInfo=0, *nextInfo=0; + if (selectedItem->prevItem()) + previousInfo = static_cast(selectedItem->prevItem())->imageInfo(); + if (selectedItem->nextItem()) + nextInfo = static_cast(selectedItem->nextItem())->imageInfo(); + + // we fed a list of copies + d->rightSideBar->takeImageInfoOwnership(true); + + if (!d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + d->albumWidgetStack->setPreviewItem(list.first(), previousInfo, nextInfo); + emit signalImageSelected(list, previousInfo, nextInfo, listAll); + } + + d->needDispatchSelection = false; + } +} + +void DigikamView::setThumbSize(int size) +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->albumWidgetStack->zoomMin(); + double zmax = d->albumWidgetStack->zoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + double z = a*size+b; + d->albumWidgetStack->setZoomFactorSnapped(z); + } + else if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + if (size > ThumbnailSize::Huge) + d->thumbSize = ThumbnailSize::Huge; + else if (size < ThumbnailSize::Small) + d->thumbSize = ThumbnailSize::Small; + else + d->thumbSize = size; + + emit signalThumbSizeChanged(d->thumbSize); + + if (d->thumbSizeTimer) + { + d->thumbSizeTimer->stop(); + delete d->thumbSizeTimer; + } + + d->thumbSizeTimer = new TQTimer( this ); + connect(d->thumbSizeTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotThumbSizeEffect()) ); + d->thumbSizeTimer->start(300, true); + } +} + +void DigikamView::slotThumbSizeEffect() +{ + emit signalNoCurrentItem(); + + d->iconView->setThumbnailSize(d->thumbSize); + toggleZoomActions(); + + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) + return; + settings->setDefaultIconSize(d->thumbSize); +} + +void DigikamView::toggleZoomActions() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + d->parent->enableZoomMinusAction(true); + d->parent->enableZoomPlusAction(true); + + if (d->albumWidgetStack->maxZoom()) + d->parent->enableZoomPlusAction(false); + + if (d->albumWidgetStack->minZoom()) + d->parent->enableZoomMinusAction(false); + } + else if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + d->parent->enableZoomMinusAction(true); + d->parent->enableZoomPlusAction(true); + + if (d->thumbSize >= ThumbnailSize::Huge) + d->parent->enableZoomPlusAction(false); + + if (d->thumbSize <= ThumbnailSize::Small) + d->parent->enableZoomMinusAction(false); + } +} + +void DigikamView::slotZoomIn() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + setThumbSize(d->thumbSize + ThumbnailSize::Step); + toggleZoomActions(); + emit signalThumbSizeChanged(d->thumbSize); + } + else if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + d->albumWidgetStack->increaseZoom(); + } +} + +void DigikamView::slotZoomOut() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + setThumbSize(d->thumbSize - ThumbnailSize::Step); + toggleZoomActions(); + emit signalThumbSizeChanged(d->thumbSize); + } + else if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + d->albumWidgetStack->decreaseZoom(); + } +} + +void DigikamView::slotZoomTo100Percents() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + d->albumWidgetStack->toggleFitToWindowOr100(); + } +} + +void DigikamView::slotFitToWindow() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + { + d->albumWidgetStack->fitToWindow(); + } +} + +void DigikamView::slotZoomFactorChanged(double zoom) +{ + toggleZoomActions(); + + double h = (double)ThumbnailSize::Huge; + double s = (double)ThumbnailSize::Small; + double zmin = d->albumWidgetStack->zoomMin(); + double zmax = d->albumWidgetStack->zoomMax(); + double b = (zmin-(zmax*s/h))/(1-s/h); + double a = (zmax-b)/h; + int size = (int)((zoom - b) /a); + + emit signalZoomChanged(zoom, size); +} + +void DigikamView::slotAlbumPropsEdit() +{ + d->folderView->albumEdit(); +} + +void DigikamView::slotAlbumSyncPicturesMetadata() +{ + Album *album = d->albumManager->currentAlbum(); + if (!album) + return; + + BatchSyncMetadata *syncMetadata = new BatchSyncMetadata(this, album); + + connect(syncMetadata, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + d->parent, TQ_SLOT(slotProgressBarMode(int, const TQString&))); + + connect(syncMetadata, TQ_SIGNAL(signalProgressValue(int)), + d->parent, TQ_SLOT(slotProgressValue(int))); + + connect(syncMetadata, TQ_SIGNAL(signalComplete()), + this, TQ_SLOT(slotAlbumSyncPicturesMetadataDone())); + + connect(d->parent, TQ_SIGNAL(signalCancelButtonPressed()), + syncMetadata, TQ_SLOT(slotAbort())); + + syncMetadata->parseAlbum(); +} + +void DigikamView::slotAlbumSyncPicturesMetadataDone() +{ + applySettings(); +} + +void DigikamView::slotAlbumImportFolder() +{ + d->folderView->albumImportFolder(); +} + +void DigikamView::slotAlbumHighlight() +{ + // TODO: + // Don't know what this is supposed to do. + // Perhaps some flashing or other eye kandy + /* + Album *album = d->albumManager->currentAlbum(); + if (!album || !album->type() == Album::PHYSICAL) + return; + + d->folderView->setAlbumThumbnail(dynamic_cast(album)); + */ +} + +void DigikamView::slotEscapePreview() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode || + d->albumWidgetStack->previewMode() == AlbumWidgetStack::WelcomePageMode) + return; + + AlbumIconItem *currItem = dynamic_cast(d->iconView->currentItem()); + slotTogglePreviewMode(currItem); +} + +void DigikamView::slotImagePreview() +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->currentItem()); + if (currItem) + slotTogglePreviewMode(currItem); +} + +// This method toggle between AlbumView and ImagePreview Modes, depending of context. +void DigikamView::slotTogglePreviewMode(AlbumIconItem *iconItem) +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode && iconItem) + { + // We will go to ImagePreview Mode. + ImageInfo *previousInfo=0, *nextInfo=0; + + if (iconItem->prevItem()) + previousInfo = static_cast(iconItem->prevItem())->imageInfo(); + + if (iconItem->nextItem()) + nextInfo = static_cast(iconItem->nextItem())->imageInfo(); + + d->albumWidgetStack->setPreviewItem(iconItem->imageInfo(), previousInfo, nextInfo); + } + else + { + // We go back to AlbumView Mode. + d->albumWidgetStack->setPreviewMode( AlbumWidgetStack::PreviewAlbumMode ); + } +} + +void DigikamView::slotToggledToPreviewMode(bool b) +{ + toggleZoomActions(); + + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + emit signalThumbSizeChanged(d->iconView->thumbnailSize().size()); + else if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewImageMode) + slotZoomFactorChanged(d->albumWidgetStack->zoomFactor()); + + emit signalTogglePreview(b); +} + +void DigikamView::slotImageEdit() +{ + AlbumIconItem *currItem = dynamic_cast(d->iconView->currentItem()); + if (currItem) + imageEdit(currItem); +} + +void DigikamView::imageEdit(AlbumIconItem *iconItem) +{ + AlbumIconItem *item; + + if (!iconItem) + { + item = d->iconView->firstSelectedItem(); + if (!item) return; + } + else + { + item = iconItem; + } + + d->iconView->slotDisplayItem(item); +} + +void DigikamView::slotImageExifOrientation(int orientation) +{ + d->iconView->slotSetExifOrientation(orientation); +} + +void DigikamView::slotLightTable() +{ + ImageInfoList empty; + d->iconView->insertToLightTable(empty, 0, true); +} + +void DigikamView::slotImageLightTable() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + // put images into an emptied light table + d->iconView->insertSelectionToLightTable(false); + } + else + { + ImageInfoList list; + ImageInfo *info = d->albumWidgetStack->imagePreviewView()->getImageInfo(); + list.append(info); + // put images into an emptied light table + d->iconView->insertToLightTable(list, info, false); + } +} + +void DigikamView::slotImageAddToLightTable() +{ + if (d->albumWidgetStack->previewMode() == AlbumWidgetStack::PreviewAlbumMode) + { + // add images to the existing images in the light table + d->iconView->insertSelectionToLightTable(true); + } + else + { + ImageInfoList list; + ImageInfo *info = d->albumWidgetStack->imagePreviewView()->getImageInfo(); + list.append(info); + // add images to the existing images in the light table + d->iconView->insertToLightTable(list, info, true); + } +} + +void DigikamView::slotImageRename(AlbumIconItem *iconItem) +{ + AlbumIconItem *item; + + if (!iconItem) + { + item = d->iconView->firstSelectedItem(); + if (!item) return; + } + else + { + item = iconItem; + } + + d->iconView->slotRename(item); +} + +void DigikamView::slotImageDelete() +{ + d->iconView->slotDeleteSelectedItems(false); +} + +void DigikamView::slotImageDeletePermanently() +{ + d->iconView->slotDeleteSelectedItems(true); +} + +void DigikamView::slotImageDeletePermanentlyDirectly() +{ + d->iconView->slotDeleteSelectedItemsDirectly(false); +} + +void DigikamView::slotImageTrashDirectly() +{ + d->iconView->slotDeleteSelectedItemsDirectly(true); +} + +void DigikamView::slotSelectAll() +{ + d->iconView->selectAll(); +} + +void DigikamView::slotSelectNone() +{ + d->iconView->clearSelection(); +} + +void DigikamView::slotSelectInvert() +{ + d->iconView->invertSelection(); +} + +void DigikamView::slotSortImages(int order) +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) + return; + settings->setImageSortOrder((AlbumSettings::ImageSortOrder) order); + d->iconView->slotRearrange(); +} + +void DigikamView::slotLeftSidebarChangedTab(TQWidget* w) +{ + // setActive means that selection changes are propagated, nothing more. + // Additionally, when it is set to true, the selectionChanged signal will be emitted. + // So this is the place which causes the behavior that when the left sidebar + // tab is changed, the current album is changed as well. + d->dateFolderView->setActive(w == d->dateFolderView); + d->folderView->setActive(w == d->folderBox); + d->tagFolderView->setActive(w == d->tagBox); + d->searchFolderView->setActive(w == d->searchBox); + d->timeLineView->setActive(w == d->timeLineView); +} + +void DigikamView::slotAssignRating(int rating) +{ + d->iconView->slotAssignRating(rating); +} + +void DigikamView::slotAssignRatingNoStar() +{ + d->iconView->slotAssignRating(0); +} + +void DigikamView::slotAssignRatingOneStar() +{ + d->iconView->slotAssignRating(1); +} + +void DigikamView::slotAssignRatingTwoStar() +{ + d->iconView->slotAssignRating(2); +} + +void DigikamView::slotAssignRatingThreeStar() +{ + d->iconView->slotAssignRating(3); +} + +void DigikamView::slotAssignRatingFourStar() +{ + d->iconView->slotAssignRating(4); +} + +void DigikamView::slotAssignRatingFiveStar() +{ + d->iconView->slotAssignRating(5); +} + +void DigikamView::slotSlideShowAll() +{ + ImageInfoList infoList; + AlbumIconItem* item = dynamic_cast(d->iconView->firstItem()); + while (item) + { + infoList.append(item->imageInfo()); + item = dynamic_cast(item->nextItem()); + } + + slideShow(infoList); +} + +void DigikamView::slotSlideShowSelection() +{ + ImageInfoList infoList; + AlbumIconItem* item = dynamic_cast(d->iconView->firstItem()); + while (item) + { + if (item->isSelected()) + infoList.append(item->imageInfo()); + item = dynamic_cast(item->nextItem()); + } + + slideShow(infoList); +} + +void DigikamView::slotSlideShowRecursive() +{ + Album *album = AlbumManager::instance()->currentAlbum(); + if(album) + { + AlbumList albumList; + albumList.append(album); + AlbumIterator it(album); + while (it.current()) + { + albumList.append(*it); + ++it; + } + + ImageInfoAlbumsJob *job = new ImageInfoAlbumsJob; + connect(job, TQ_SIGNAL(signalCompleted(const ImageInfoList&)), + this, TQ_SLOT(slotItemsInfoFromAlbums(const ImageInfoList&))); + job->allItemsFromAlbums(albumList); + } +} + +void DigikamView::slotItemsInfoFromAlbums(const ImageInfoList& infoList) +{ + ImageInfoList list = infoList; + slideShow(list); +} + +void DigikamView::slideShow(ImageInfoList &infoList) +{ + TDEConfig* config = kapp->config(); + config->setGroup("ImageViewer Settings"); + bool startWithCurrent = config->readBoolEntry("SlideShowStartCurrent", false); + + int i = 0; + float cnt = (float)infoList.count(); + emit signalProgressBarMode(StatusProgressBar::CancelProgressBarMode, + i18n("Preparing slideshow of %1 images. Please wait...") + .arg(infoList.count())); + + DMetadata meta; + SlideShowSettings settings; + settings.exifRotate = AlbumSettings::instance()->getExifRotate(); + settings.delay = config->readNumEntry("SlideShowDelay", 5) * 1000; + settings.printName = config->readBoolEntry("SlideShowPrintName", true); + settings.printDate = config->readBoolEntry("SlideShowPrintDate", false); + settings.printApertureFocal = config->readBoolEntry("SlideShowPrintApertureFocal", false); + settings.printExpoSensitivity = config->readBoolEntry("SlideShowPrintExpoSensitivity", false); + settings.printMakeModel = config->readBoolEntry("SlideShowPrintMakeModel", false); + settings.printComment = config->readBoolEntry("SlideShowPrintComment", false); + settings.loop = config->readBoolEntry("SlideShowLoop", false); + + d->cancelSlideShow = false; + for (ImageInfoList::iterator it = infoList.begin() ; + !d->cancelSlideShow && (it != infoList.end()) ; ++it) + { + ImageInfo *info = *it; + settings.fileList.append(info->kurl()); + SlidePictureInfo pictInfo; + pictInfo.comment = info->caption(); + + // Perform optimizations: only read pictures metadata if necessary. + if (settings.printApertureFocal || settings.printExpoSensitivity || settings.printMakeModel) + { + meta.load(info->kurl().path()); + pictInfo.photoInfo = meta.getPhotographInformations(); + } + + // In case of dateTime extraction from metadata failed + pictInfo.photoInfo.dateTime = info->dateTime(); + settings.pictInfoMap.insert(info->kurl(), pictInfo); + + emit signalProgressValue((int)((i++/cnt)*100.0)); + kapp->eventLoop()->processEvents(TQEventLoop::AllEvents & ~TQEventLoop::WaitForMore); + } + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + + if (!d->cancelSlideShow) + { + SlideShow *slide = new SlideShow(settings); + if (startWithCurrent) + { + AlbumIconItem* current = dynamic_cast(d->iconView->currentItem()); + if (current) + slide->setCurrent(current->imageInfo()->kurl()); + } + + slide->show(); + } +} + +void DigikamView::slotCancelSlideShow() +{ + d->cancelSlideShow = true; +} + +} // namespace Digikam diff --git a/src/digikam/digikamview.h b/src/digikam/digikamview.h new file mode 100644 index 00000000..cf929074 --- /dev/null +++ b/src/digikam/digikamview.h @@ -0,0 +1,194 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-16-10 + * Description : implementation of album view interface. + * + * Copyright (C) 2002-2005 by Renchi Raju + * Copyright (C) 2002-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIGIKAMVIEW_H +#define DIGIKAMVIEW_H + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "imageinfo.h" + +class KURL; + +namespace Digikam +{ +class AlbumIconItem; +class AlbumSettings; +class Album; +class DigikamViewPriv; + +class DigikamView : public TQHBox +{ + TQ_OBJECT + +public: + + DigikamView(TQWidget *parent); + ~DigikamView(); + + void applySettings(); + void refreshView(); + void clearHistory(); + void getForwardHistory(TQStringList &titles); + void getBackwardHistory(TQStringList &titles); + void showSideBars(); + void hideSideBars(); + void setThumbSize(int size); + +signals: + + void signalAlbumSelected(bool val); + void signalTagSelected(bool val); + void signalImageSelected(const TQPtrList& list, bool, bool, + const KURL::List& listAll); + void signalNoCurrentItem(); + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalThumbSizeChanged(int); + void signalZoomChanged(double, int); + void signalTogglePreview(bool); + void signalGotoAlbumAndItem(AlbumIconItem *); + void signalGotoDateAndItem(AlbumIconItem *); + void signalGotoTagAndItem(int tagID); + void signalChangedTab(TQWidget*); + +public slots: + + // View Action slots + void slotZoomIn(); + void slotZoomOut(); + void slotZoomTo100Percents(); + void slotFitToWindow(); + void slotSlideShowAll(); + void slotSlideShowSelection(); + void slotSlideShowRecursive(); + + // Album action slots + void slotNewAlbum(); + void slotSortAlbums(int order); + void slotDeleteAlbum(); + void slotSelectAlbum(const KURL &url); + void slotAlbumPropsEdit(); + void slotAlbumOpenInKonqui(); + void slotAlbumRefresh(); + void slotAlbumImportFolder(); + void slotAlbumHistoryBack(int steps=1); + void slotAlbumHistoryForward(int steps=1); + void slotAlbumAdded(Album *album); + void slotAlbumDeleted(Album *album); + void slotAlbumRenamed(Album *album); + void slotAlbumSyncPicturesMetadata(); + void slotAlbumSyncPicturesMetadataDone(); + void slotAlbumSelected(Album* album); + void slotGotoAlbumAndItem(AlbumIconItem* iconItem); + void slotGotoDateAndItem(AlbumIconItem* iconItem); + void slotGotoTagAndItem(int tagID); + + // Tag action slots + void slotNewTag(); + void slotDeleteTag(); + void slotEditTag(); + + // Search action slots + void slotNewQuickSearch(); + void slotNewAdvancedSearch(); + + // Image action slots + void slotImageLightTable(); + void slotImageAddToLightTable(); + void slotImagePreview(); + void slotImageEdit(); + void slotImageExifOrientation(int orientation); + void slotImageRename(AlbumIconItem* iconItem=0); + void slotImageDelete(); + void slotImageDeletePermanently(); + void slotImageDeletePermanentlyDirectly(); + void slotImageTrashDirectly(); + void slotSelectAll(); + void slotSelectNone(); + void slotSelectInvert(); + void slotSortImages(int order); + + // Image Rating slots + void slotAssignRating(int rating); + void slotAssignRatingNoStar(); + void slotAssignRatingOneStar(); + void slotAssignRatingTwoStar(); + void slotAssignRatingThreeStar(); + void slotAssignRatingFourStar(); + void slotAssignRatingFiveStar(); + + // Tools action slots. + void slotLightTable(); + +private: + + void toggleZoomActions(); + void setupConnections(); + void loadViewState(); + void saveViewState(); + void changeAlbumFromHistory(Album *album, TQWidget *widget); + void imageEdit(AlbumIconItem* iconItem=0); + void slideShow(ImageInfoList &infoList); + +private slots: + + void slotAllAlbumsLoaded(); + + void slotAlbumsCleared(); + void slotAlbumHighlight(); + + void slotImageSelected(); + void slotTogglePreviewMode(AlbumIconItem *iconItem=0); + void slotDispatchImageSelected(); + void slotItemsInfoFromAlbums(const ImageInfoList&); + + void slotLeftSidebarChangedTab(TQWidget* w); + + void slotFirstItem(void); + void slotPrevItem(void); + void slotNextItem(void); + void slotLastItem(void); + + void slotToggledToPreviewMode(bool); + void slotEscapePreview(); + void slotCancelSlideShow(); + + void slotThumbSizeEffect(); + void slotZoomFactorChanged(double); + +private: + + DigikamViewPriv* d; +}; + +} // namespace Digikam + +#endif // DIGIKAMVIEW_H diff --git a/src/digikam/dio.cpp b/src/digikam/dio.cpp new file mode 100644 index 00000000..f9639fcf --- /dev/null +++ b/src/digikam/dio.cpp @@ -0,0 +1,262 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-17 + * Description : low level files management interface. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +#include +#include +} + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albumsettings.h" +#include "albummanager.h" +#include "albumlister.h" +#include "albumdb.h" +#include "album.h" +#include "dio.h" +#include "dio_p.h" +#include "dio_p.moc" + +namespace DIO +{ + +TDEIO::Job* copy(const KURL& src, const KURL& dest) +{ + TDEIO::Job* job = TDEIO::copy(src, dest, true); + new Watch(job); + + return job; +} + +TDEIO::Job* copy(const KURL::List& srcList, const KURL& dest) +{ + TDEIO::Job* job = TDEIO::copy(srcList, dest, true); + new Watch(job); + + return job; +} + +TDEIO::Job* move(const KURL& src, const KURL& dest) +{ + TDEIO::Job* job = TDEIO::move(src, dest, true); + new Watch(job); + + return job; +} + +TDEIO::Job* move(const KURL::List& srcList, const KURL& dest) +{ + TDEIO::Job* job = TDEIO::move(srcList, dest, true); + new Watch(job); + + return job; +} + +TDEIO::Job* del(const KURL& src, bool useTrash) +{ + TDEIO::Job* job = 0; + + if (useTrash) + { + KURL dest("trash:/"); + + if (!KProtocolInfo::isKnownProtocol(dest)) + { + dest = TDEGlobalSettings::trashPath(); + } + + job = TDEIO::move( src, dest ); + } + else + { + job = TDEIO::del(src); + } + + new Watch(job); + return job; +} + +TDEIO::Job* del(const KURL::List& srcList, bool useTrash) +{ + TDEIO::Job* job = 0; + + if (useTrash) + { + KURL dest("trash:/"); + + if (!KProtocolInfo::isKnownProtocol(dest)) + { + dest = TDEGlobalSettings::trashPath(); + } + + job = TDEIO::move( srcList, dest ); + } + else + { + job = TDEIO::del(srcList); + } + + new Watch(job); + return job; +} + +TDEIO::CopyJob *rename(const KURL& src, const KURL& dest) +{ + TDEIO::CopyJob * job = TDEIO::move(src, dest, false); + new Watch(job); + + return job; + + /* + KURL srcdir; + srcdir.setDirectory(src.directory()); + KURL dstdir; + dstdir.setDirectory(dest.directory()); + Digikam::PAlbum* srcAlbum = Digikam::AlbumManager::instance()->findPAlbum(srcdir); + Digikam::PAlbum* dstAlbum = Digikam::AlbumManager::instance()->findPAlbum(dstdir); + if (!srcAlbum || !dstAlbum) + { + DWarning() << "Source Album " << src.directory() << " not found" << endl; + return false; + } + + TQString srcPath = Digikam::AlbumManager::instance()->getLibraryPath() + src.path(); + TQString dstPath = Digikam::AlbumManager::instance()->getLibraryPath() + dest.path(); + TQString newDstPath; + + bool overwrite = false; + + struct stat stbuf; + while (::stat(TQFile::encodeName(dstPath), &stbuf) == 0) + { + TDEIO::RenameDlg_Result result = + TDEIO::open_RenameDlg(i18n("Rename File"), srcPath, dstPath, + TDEIO::RenameDlg_Mode(TDEIO::M_SINGLE | + TDEIO::M_OVERWRITE), + newDstPath); + + dstPath = newDstPath; + + switch (result) + { + case TDEIO::R_CANCEL: + { + return false; + } + case TDEIO::R_OVERWRITE: + { + overwrite = true; + break; + } + default: + break; + } + + if (overwrite) + break; + } + + Digikam::AlbumDB* db = Digikam::AlbumManager::instance()->albumDB(); + if (::rename(TQFile::encodeName(srcPath), TQFile::encodeName(dstPath)) == 0) + { + db->moveItem(srcAlbum->id(), src.fileName(), + dstAlbum->id(), KURL(dstPath).fileName()); + return true; + } + + KMessageBox::error(0, i18n("Failed to rename file\n%1") + .arg(src.fileName()), i18n("Rename Failed")); + return false; + */ +} + +TDEIO::Job* scan(const KURL& albumURL) +{ + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << Digikam::AlbumManager::instance()->getLibraryPath(); + ds << albumURL; + ds << TQString(); + ds << 0; + ds << 0; + ds << 0; + + // extra parameter: trigger scan + ds << 1; + + TDEIO::Job* job = new TDEIO::TransferJob(albumURL, + TDEIO::CMD_SPECIAL, + ba, TQByteArray(), + false); + new Watch(job); + + return job; +} + +bool running() +{ + return (Watch::m_runCount != 0); +} + +Watch::Watch(TDEIO::Job* job) +{ + m_runCount++; + connect(job, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotDone(TDEIO::Job*))); +} + +void Watch::slotDone(TDEIO::Job*) +{ + Digikam::AlbumManager::instance()->refresh(); + Digikam::AlbumLister::instance()->refresh(); + m_runCount--; + + delete this; +} + +uint Watch::m_runCount = 0; + +} // namespace DIO diff --git a/src/digikam/dio.h b/src/digikam/dio.h new file mode 100644 index 00000000..a7c96d77 --- /dev/null +++ b/src/digikam/dio.h @@ -0,0 +1,54 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-17 + * Description : low level files management interface. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIO_H +#define DIO_H + +// KDE includes. + +#include + +namespace DIO +{ + + TDEIO::Job* copy(const KURL& src, const KURL& dest); + + TDEIO::Job* copy(const KURL::List& srcList, const KURL& dest); + + TDEIO::Job* move(const KURL& src, const KURL& dest); + + TDEIO::Job* move(const KURL::List& srcList, const KURL& dest); + + TDEIO::Job* del(const KURL& src, bool useTrash = true); + + TDEIO::Job* del(const KURL::List& srcList, bool useTrash = true); + + TDEIO::CopyJob* rename(const KURL& src, const KURL& dest); + + TDEIO::Job* scan(const KURL& albumURL); + + bool running(); + +} // namespace DIO + +#endif /* DIO_H */ diff --git a/src/digikam/dio_p.h b/src/digikam/dio_p.h new file mode 100644 index 00000000..660cad04 --- /dev/null +++ b/src/digikam/dio_p.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-17 + * Description : TQt Object to follow low level files management. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIO_P_H +#define DIO_P_H + +// TQt includes. + +#include + +namespace TDEIO +{ +class Job; +} + +namespace DIO +{ + +class Watch : public TQObject +{ + TQ_OBJECT + +public: + + Watch(TDEIO::Job* job); + + static uint m_runCount; + +private slots: + + void slotDone(TDEIO::Job* job); +}; + +} // namespace DIO + +#endif /* DIO_P_H */ diff --git a/src/digikam/dragobjects.cpp b/src/digikam/dragobjects.cpp new file mode 100644 index 00000000..14a3545f --- /dev/null +++ b/src/digikam/dragobjects.cpp @@ -0,0 +1,374 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-26 + * Description : Drag object info container + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2004 by Joern Ahrens + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "ddebug.h" +#include "dragobjects.h" + +namespace Digikam +{ + +ItemDrag::ItemDrag(const KURL::List &urls, + const KURL::List &kioURLs, + const TQValueList& albumIDs, + const TQValueList& imageIDs, + TQWidget* dragSource, const char* name) + : KURLDrag(urls, dragSource, name), + m_kioURLs(kioURLs), + m_albumIDs(albumIDs), m_imageIDs(imageIDs) +{ +} + +bool ItemDrag::canDecode(const TQMimeSource* e) +{ + return e->provides("digikam/item-ids") || + e->provides("digikam/album-ids") || + e->provides("digikam/image-ids") || + e->provides("digikam/digikamalbums"); +} + +bool ItemDrag::decode(const TQMimeSource* e, KURL::List &urls, KURL::List &kioURLs, + TQValueList& albumIDs, TQValueList& imageIDs) +{ + urls.clear(); + kioURLs.clear(); + albumIDs.clear(); + imageIDs.clear(); + + if (KURLDrag::decode(e, urls)) + { + TQByteArray albumarray = e->encodedData("digikam/album-ids"); + TQByteArray imagearray = e->encodedData("digikam/image-ids"); + TQByteArray kioarray = e->encodedData("digikam/digikamalbums"); + + if (albumarray.size() && imagearray.size() && kioarray.size()) + { + int id; + + TQDataStream dsAlbums(albumarray, IO_ReadOnly); + while (!dsAlbums.atEnd()) + { + dsAlbums >> id; + albumIDs.append(id); + } + + TQDataStream dsImages(imagearray, IO_ReadOnly); + while (!dsImages.atEnd()) + { + dsImages >> id; + imageIDs.append(id); + } + + KURL u; + TQDataStream dsKio(kioarray, IO_ReadOnly); + while (!dsKio.atEnd()) + { + dsKio >> u; + kioURLs.append(u); + } + + return true; + } + } + + return false; +} + +TQByteArray ItemDrag::encodedData(const char* mime) const +{ + TQCString mimetype(mime); + + if (mimetype == "digikam/album-ids") + { + TQByteArray byteArray; + TQDataStream ds(byteArray, IO_WriteOnly); + + TQValueList::const_iterator it; + for (it = m_albumIDs.begin(); it != m_albumIDs.end(); ++it) + { + ds << (*it); + } + + return byteArray; + } + else if (mimetype == "digikam/image-ids") + { + TQByteArray byteArray; + TQDataStream ds(byteArray, IO_WriteOnly); + + TQValueList::const_iterator it; + for (it = m_imageIDs.begin(); it != m_imageIDs.end(); ++it) + { + ds << (*it); + } + + return byteArray; + } + else if (mimetype == "digikam/digikamalbums") + { + TQByteArray byteArray; + TQDataStream ds(byteArray, IO_WriteOnly); + + KURL::List::const_iterator it; + for (it = m_kioURLs.begin(); it != m_kioURLs.end(); ++it) + { + ds << (*it); + } + + return byteArray; + } + else + { + return KURLDrag::encodedData(mime); + } +} + +const char* ItemDrag::format(int i) const +{ + if (i == 0) + return "text/uri-list"; + else if (i == 1) + return "digikam/item-ids"; + else if (i == 2) + return "digikam/album-ids"; + else if (i == 3) + return "digikam/image-ids"; + else if (i == 4) + return "digikam/digikamalbums"; + else + return 0; +} + +// ------------------------------------------------------------------------ + +TagDrag::TagDrag( int albumid, TQWidget *dragSource, + const char *name ) : + TQDragObject( dragSource, name ) +{ + mAlbumID = albumid; +} + +bool TagDrag::canDecode( const TQMimeSource* e ) +{ + return e->provides("digikam/tag-id"); +} + +const char* TagDrag::format( int i ) const +{ + if ( i == 0 ) + return "digikam/tag-id"; + + return 0; +} + +TQByteArray TagDrag::encodedData( const char* ) const +{ + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << mAlbumID; + return ba; +} + +// ------------------------------------------------------------------------ + +AlbumDrag::AlbumDrag(const KURL &url, int albumid, + TQWidget *dragSource, + const char *name) : + KURLDrag(url, dragSource, name) +{ + mAlbumID = albumid; +} + +bool AlbumDrag::canDecode( const TQMimeSource* e ) +{ + return e->provides("digikam/album-id"); +} + +const char* AlbumDrag::format( int i ) const +{ + if (i == 0) + return "text/uri-list"; + else if ( i == 1 ) + return "digikam/album-id"; + + return 0; +} + +TQByteArray AlbumDrag::encodedData(const char *mime) const +{ + TQCString mimetype( mime ); + if(mimetype == "digikam/album-id") + { + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << mAlbumID; + return ba; + } + else + { + return KURLDrag::encodedData(mime); + } +} + +bool AlbumDrag::decode(const TQMimeSource* e, KURL::List &urls, + int &albumID) +{ + urls.clear(); + albumID = -1; + + if(KURLDrag::decode(e, urls)) + { + TQByteArray ba = e->encodedData("digikam/album-id"); + if (ba.size()) + { + TQDataStream ds(ba, IO_ReadOnly); + if(!ds.atEnd()) + { + ds >> albumID; + } + return true; + } + } + + return false; +} + +// ------------------------------------------------------------------------ + +TagListDrag::TagListDrag(const TQValueList& tagIDs, TQWidget *dragSource, + const char *name) + : TQDragObject( dragSource, name ) +{ + m_tagIDs = tagIDs; +} + +bool TagListDrag::canDecode(const TQMimeSource* e) +{ + return e->provides("digikam/taglist"); +} + +TQByteArray TagListDrag::encodedData(const char*) const +{ + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << m_tagIDs; + return ba; +} + +const char* TagListDrag::format(int i) const +{ + if ( i == 0 ) + return "digikam/taglist"; + + return 0; +} + +// ------------------------------------------------------------------------ + +CameraItemListDrag::CameraItemListDrag(const TQStringList& cameraItemPaths, + TQWidget *dragSource, + const char *name) + : TQDragObject( dragSource, name ) +{ + m_cameraItemPaths = cameraItemPaths; +} + +bool CameraItemListDrag::canDecode(const TQMimeSource* e) +{ + return e->provides("digikam/cameraItemlist"); +} + +TQByteArray CameraItemListDrag::encodedData(const char*) const +{ + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << m_cameraItemPaths; + return ba; +} + +const char* CameraItemListDrag::format(int i) const +{ + if ( i == 0 ) + return "digikam/cameraItemlist"; + + return 0; +} + +// ------------------------------------------------------------------------ + +CameraDragObject::CameraDragObject(const CameraType& ctype, TQWidget *dragSource) + : TQStoredDrag("camera/unknown", dragSource) +{ + setCameraType(ctype); +} + +CameraDragObject::~CameraDragObject() +{ +} + +void CameraDragObject::setCameraType(const CameraType& ctype) +{ + TQByteArray byteArray; + TQDataStream ds(byteArray, IO_WriteOnly); + + ds << ctype.title(); + ds << ctype.model(); + ds << ctype.port(); + ds << ctype.path(); + ds << ctype.lastAccess(); + + setEncodedData(byteArray); +} + +bool CameraDragObject::canDecode(const TQMimeSource* e) +{ + return e->provides("camera/unknown"); +} + +bool CameraDragObject::decode(const TQMimeSource* e, CameraType& ctype) +{ + TQByteArray payload = e->encodedData("camera/unknown"); + if (payload.size()) + { + TQString title, model, port, path; + TQDateTime lastAccess; + + TQDataStream ds(payload, IO_ReadOnly); + ds >> title; + ds >> model; + ds >> port; + ds >> path; + ds >> lastAccess; + + ctype = CameraType(title, model, port, path, lastAccess); + + return true; + } + else + return false; +} + +} // namespace Digikam diff --git a/src/digikam/dragobjects.h b/src/digikam/dragobjects.h new file mode 100644 index 00000000..35a70f7a --- /dev/null +++ b/src/digikam/dragobjects.h @@ -0,0 +1,205 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-26 + * Description : Drag object info container. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2005 by Joern Ahrens + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DRAGOBJECTS_H +#define DRAGOBJECTS_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "cameratype.h" + +class TQWidget; + +namespace Digikam +{ + +/** + * Provides a drag object for a list of KURLs with its related database IDs. + * + * Images can be moved through ItemDrag. It is possible to move them on + * another application which understands KURLDrag, like konqueror, to + * copy the images. Digikam can use the IDs, if ItemDrag is dropped + * on digikam itself. + * The kioURLs are internally used with the digikamalbums tdeioslave. + * The "normal" KURLDrag urls are used for external drops (k3b, gimp, ...) + */ +class ItemDrag : public KURLDrag +{ +public: + + ItemDrag(const KURL::List& urls, const KURL::List& kioURLs, + const TQValueList& albumIDs, + const TQValueList& imageIDs, + TQWidget* dragSource=0, const char* name=0); + + static bool canDecode(const TQMimeSource* e); + static bool decode(const TQMimeSource* e, + KURL::List &urls, + KURL::List &kioURLs, + TQValueList& albumIDs, + TQValueList& imageIDs); + +protected: + + virtual const char* format(int i) const; + virtual TQByteArray encodedData(const char* mime) const; + +private: + + KURL::List m_kioURLs; + TQValueList m_albumIDs; + TQValueList m_imageIDs; +}; + + +/** + * Provides a drag object for a tag + * + * When a tag is moved through drag'n'drop an object of this class + * is created. + */ +class TagDrag : public TQDragObject +{ +public: + + TagDrag(int albumid, TQWidget *dragSource = 0, const char *name = 0); + static bool canDecode(const TQMimeSource* e); + +protected: + + TQByteArray encodedData( const char* ) const; + const char* format(int i) const; + +private: + int mAlbumID; +}; + +/** + * Provides a drag object for an album + * + * When an album is moved through drag'n'drop an object of this class + * is created. + */ +class AlbumDrag : public KURLDrag +{ +public: + AlbumDrag(const KURL &url, int albumid, + TQWidget *dragSource = 0, const char *name = 0); + static bool canDecode(const TQMimeSource* e); + static bool decode(const TQMimeSource* e, KURL::List &urls, + int &albumID); +protected: + + TQByteArray encodedData(const char*) const; + const char* format(int i) const; + +private: + int mAlbumID; +}; + +/** + * Provides a drag object for a list of tags + * + * When a tag is moved through drag'n'drop an object of this class + * is created. + */ +class TagListDrag : public TQDragObject +{ +public: + + TagListDrag(const TQValueList& tagIDs, TQWidget *dragSource = 0, + const char *name = 0); + static bool canDecode(const TQMimeSource* e); + +protected: + + TQByteArray encodedData( const char* ) const; + const char* format(int i) const; + +private: + + TQValueList m_tagIDs; +}; + +/** + * Provides a drag object for a list of camera items + * + * When a camera item is moved through drag'n'drop an object of this class + * is created. + */ +class CameraItemListDrag : public TQDragObject +{ +public: + + CameraItemListDrag(const TQStringList& cameraItemPaths, TQWidget *dragSource = 0, + const char *name = 0); + static bool canDecode(const TQMimeSource* e); + +protected: + + TQByteArray encodedData( const char* ) const; + const char* format(int i) const; + +private: + + TQStringList m_cameraItemPaths; +}; + +/** + * Provides a drag object for a camera object + * + * When a camera object is moved through drag'n'drop an object of this class + * is created. + */ +class CameraDragObject : public TQStoredDrag +{ + +public: + + CameraDragObject(const CameraType& ctype, TQWidget* dragSource=0); + ~CameraDragObject(); + + static bool canDecode(const TQMimeSource* e); + static bool decode(const TQMimeSource* e, CameraType& ctype); + +private: + + void setCameraType(const CameraType& ctype); + +}; + +} // namespace Digikam + +#endif /* DRAGOBJECTS_H */ diff --git a/src/digikam/firstrun.cpp b/src/digikam/firstrun.cpp new file mode 100644 index 00000000..43613034 --- /dev/null +++ b/src/digikam/firstrun.cpp @@ -0,0 +1,100 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-25 + * Description : a widget to use in first run dialog + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "firstrun.h" +#include "firstrun.moc" + +namespace Digikam +{ + +FirstRunWidget::FirstRunWidget( TQWidget* parent ) + : TQWidget( parent ) +{ + setName( "FirstRunWidget" ); + TQVBoxLayout *vlayout = new TQVBoxLayout( this, 0, 6 ); + + m_textLabel2 = new TQLabel( this ); + vlayout->addWidget( m_textLabel2 ); + + TQFrame *line1 = new TQFrame( this ); + line1->setFrameShape( TQFrame::HLine ); + line1->setFrameShadow( TQFrame::Sunken ); + line1->setFrameShape( TQFrame::HLine ); + vlayout->addWidget( line1 ); + + TQGridLayout *grid = new TQGridLayout( 0, 1, 1, 0, 6 ); + + m_pixLabel = new TQLabel( this ); + m_pixLabel->setAlignment( int( TQLabel::AlignTop ) ); + grid->addMultiCellWidget( m_pixLabel, 0, 1, 0, 0 ); + + m_path = new KURLRequester( this ); + m_path->setShowLocalProtocol( true ); + + grid->addWidget( m_path, 1, 1 ); + + m_textLabel1 = new TQLabel( this ); + m_textLabel1->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter ) ); + grid->addWidget( m_textLabel1, 0, 1 ); + + vlayout->addLayout( grid ); + vlayout->addItem( new TQSpacerItem( 16, 16, TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding ) ); + + languageChange(); + resize( TQSize(479, 149).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); +} + +FirstRunWidget::~FirstRunWidget() +{ +} + +void FirstRunWidget::languageChange() +{ + m_textLabel2->setText( i18n( "Albums Library Folder" ) ); + m_pixLabel->setText( TQString() ); + m_textLabel1->setText( i18n( "

digiKam will store the photo albums which you create in a " + "common Albums Library Folder. " + "Below, please select which folder you would like " + "digiKam to use as the common Albums Library Folder.

" + "

Do not use a mount path hosted by a remote computer.

") ); +} + +} // namespace Digikam diff --git a/src/digikam/firstrun.h b/src/digikam/firstrun.h new file mode 100644 index 00000000..e3803993 --- /dev/null +++ b/src/digikam/firstrun.h @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-25 + * Description : a widget to use in first run dialog + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIGIKAMFIRSTFIRSTRUNWIDGET_H +#define DIGIKAMFIRSTFIRSTRUNWIDGET_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQLabel; + +class KURLRequester; + +namespace Digikam +{ + +class DIGIKAM_EXPORT FirstRunWidget : public TQWidget +{ + TQ_OBJECT + +public: + + FirstRunWidget( TQWidget* parent = 0 ); + ~FirstRunWidget(); + +public: + + TQLabel *m_pixLabel; + KURLRequester *m_path; + +protected slots: + + virtual void languageChange(); + +private: + + TQLabel *m_textLabel1; + TQLabel *m_textLabel2; +}; + +} // namespace Digikam + +#endif // DIGIKAMFIRSTFIRSTRUNWIDGET_H diff --git a/src/digikam/folderitem.cpp b/src/digikam/folderitem.cpp new file mode 100644 index 00000000..45b875f8 --- /dev/null +++ b/src/digikam/folderitem.cpp @@ -0,0 +1,269 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : implementation of item folder + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "thumbnailsize.h" +#include "albumsettings.h" +#include "folderview.h" +#include "folderitem.h" + +namespace Digikam +{ + +FolderItem::FolderItem(TQListView* parent, const TQString& text, bool special) + : TQListViewItem(parent, text) +{ + m_special = special; + m_focus = false; +} + +FolderItem::FolderItem(TQListViewItem* parent, const TQString& text, bool special) + : TQListViewItem(parent, text) +{ + m_special = special; + m_focus = false; +} + +FolderItem::~FolderItem() +{ +} + +void FolderItem::setFocus(bool b) +{ + m_focus = b; +} + +bool FolderItem::focus() const +{ + return m_focus; +} + +void FolderItem::paintCell(TQPainter* p, const TQColorGroup& cg, int column, int width, int) +{ + FolderView *fv = dynamic_cast(listView()); + if (!fv) + return; + + TQFontMetrics fm(p->fontMetrics()); + + TQString t = text(column); + int margin = fv->itemMargin(); + int r = margin; + const TQPixmap* icon = pixmap(column); + + if (isSelected()) + { + p->drawPixmap(0, 0, fv->itemBasePixmapSelected()); + p->setPen(cg.highlightedText()); + } + else + { + p->drawPixmap(0, 0, fv->itemBasePixmapRegular()); + p->setPen(cg.text()); + } + + if (icon) + { + int xo = r; + int yo = (height() - icon->height())/2; + + p->drawPixmap( xo, yo, *icon ); + + r += icon->width() + 5 + fv->itemMargin(); + } + + if (m_special) + { + TQFont f(p->font()); + f.setItalic(true); + p->setFont(f); + + p->setPen(isSelected() ? cg.color(TQColorGroup::LinkVisited) : + cg.color(TQColorGroup::Link)); + } + + TQRect br; + p->drawText(r, 0, width-margin-r, height(), TQt::AlignLeft|TQt::AlignVCenter, t, -1, &br); + + if (m_special) + { + p->drawLine(br.right() + 2, height()/2, fv->width(), height()/2); + } + + if (m_focus) + { + p->setPen(cg.link()); + TQRect r = fv->itemRect(this); + p->drawRect(0, 0, r.width(), r.height()); + } +} + +void FolderItem::setup() +{ + widthChanged(); + + FolderView *fv = dynamic_cast(listView()); + int h = fv->itemHeight(); + if (h % 2 > 0) + h++; + + setHeight(h); +} + +int FolderItem::id() const +{ + return 0; +} + +// ------------------------------------------------------------------------------------ + +FolderCheckListItem::FolderCheckListItem(TQListView* parent, const TQString& text, + TQCheckListItem::Type tt) + : TQCheckListItem(parent, text, tt) +{ + m_focus = false; +} + +FolderCheckListItem::FolderCheckListItem(TQListViewItem* parent, const TQString& text, + TQCheckListItem::Type tt) + : TQCheckListItem(parent, text, tt) +{ + m_focus = false; +} + +FolderCheckListItem::~FolderCheckListItem() +{ +} + +void FolderCheckListItem::setFocus(bool b) +{ + m_focus = b; +} + +bool FolderCheckListItem::focus() const +{ + return m_focus; +} + +void FolderCheckListItem::paintCell(TQPainter* p, const TQColorGroup& cg, int column, int width, int) +{ + FolderView *fv = dynamic_cast(listView()); + if (!fv) + return; + + TQFontMetrics fm(p->fontMetrics()); + + TQString t = text(column); + int margin = fv->itemMargin(); + int r = margin; + const TQPixmap* icon = pixmap(column); + + int styleflags = TQStyle::Style_Default; + switch (state()) + { + case(TQCheckListItem::Off): + styleflags |= TQStyle::Style_Off; + break; + case(TQCheckListItem::NoChange): + styleflags |= TQStyle::Style_NoChange; + break; + case(TQCheckListItem::On): + styleflags |= TQStyle::Style_On; + break; + } + + if (isSelected()) + styleflags |= TQStyle::Style_Selected; + + if (isEnabled() && fv->isEnabled()) + styleflags |= TQStyle::Style_Enabled; + + if ((type() == TQCheckListItem::CheckBox) || + (type() == TQCheckListItem::CheckBoxController)) + { + int boxsize = fv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, fv); + int x = 3; + int y = (height() - boxsize)/2 + margin; + r += boxsize + 4; + + p->fillRect(0, 0, r, height(), cg.base()); + + fv->style().drawPrimitive(TQStyle::PE_CheckListIndicator, p, + TQRect(x, y, boxsize, height()), + cg, styleflags, TQStyleOption(this)); + } + + if (isSelected()) + { + p->drawPixmap(r, 0, fv->itemBasePixmapSelected()); + p->setPen(cg.highlightedText()); + } + else + { + p->drawPixmap(r, 0, fv->itemBasePixmapRegular()); + p->setPen(cg.text()); + } + + if (icon) + { + int xo = r; + int yo = (height() - icon->height())/2; + + p->drawPixmap( xo, yo, *icon ); + + r += icon->width() + fv->itemMargin(); + } + + p->drawText(r, 0, width-margin-r, height(), TQt::AlignLeft|TQt::AlignVCenter, t); + + if (m_focus) + { + p->setPen(cg.link()); + TQRect r = fv->itemRect(this); + p->drawRect(0, 0, r.width(), r.height()); + } +} + +void FolderCheckListItem::setup() +{ + widthChanged(); + + FolderView *fv = dynamic_cast(listView()); + int h = fv->itemHeight(); + if (h % 2 > 0) + h++; + + setHeight(h); +} + +} // namespace Digikam diff --git a/src/digikam/folderitem.h b/src/digikam/folderitem.h new file mode 100644 index 00000000..e99189a8 --- /dev/null +++ b/src/digikam/folderitem.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : implementation of item folder. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef FOLDERITEM_H +#define FOLDERITEM_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT FolderItem : public TQListViewItem +{ +public: + + FolderItem(TQListView* parent, const TQString& text, bool special=false); + FolderItem(TQListViewItem* parent, const TQString& text, bool special=false); + + virtual ~FolderItem(); + + virtual int id() const; + + void setFocus(bool b); + bool focus() const; + +protected: + + void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + void setup(); + +private: + + bool m_focus; + bool m_special; +}; + +// ------------------------------------------------------------------------------------ + +class FolderCheckListItem : public TQCheckListItem +{ +public: + + FolderCheckListItem(TQListView* parent, const TQString& text, + TQCheckListItem::Type tt); + FolderCheckListItem(TQListViewItem* parent, const TQString& text, + TQCheckListItem::Type tt); + virtual ~FolderCheckListItem(); + + void setFocus(bool b); + bool focus() const; + +protected: + + void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + void setup(); + +private: + + bool m_focus; +}; + +} // namespace Digikam + +#endif /* FOLDERITEM_H */ diff --git a/src/digikam/folderview.cpp b/src/digikam/folderview.cpp new file mode 100644 index 00000000..0e2d515d --- /dev/null +++ b/src/digikam/folderview.cpp @@ -0,0 +1,510 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-06 + * Description : implementation of folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "albummanager.h" +#include "albumsettings.h" +#include "albumthumbnailloader.h" +#include "ddebug.h" +#include "dragobjects.h" +#include "folderitem.h" +#include "themeengine.h" +#include "folderview.h" +#include "folderview.moc" + +namespace Digikam +{ + +class FolderViewPriv +{ +public: + + FolderViewPriv() + { + active = false; + dragItem = 0; + oldHighlightItem = 0; + } + + bool active; + + int itemHeight; + + TQPixmap itemRegPix; + TQPixmap itemSelPix; + + TQPoint dragStartPos; + + TQListViewItem *dragItem; + TQListViewItem *oldHighlightItem; +}; + +//----------------------------------------------------------------------------- + +FolderView::FolderView(TQWidget *parent, const char *name) + : TQListView(parent, name) +{ + + d = new FolderViewPriv; + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAllAlbumsLoaded()), + this, TQ_SLOT(slotAllAlbumsLoaded())); + + connect(AlbumThumbnailLoader::instance(), TQ_SIGNAL(signalReloadThumbnails()), + this, TQ_SLOT(slotIconSizeChanged())); + + setColumnAlignment(0, TQt::AlignLeft|TQt::AlignVCenter); + setShowSortIndicator(true); + fontChange(font()); +} + +FolderView::~FolderView() +{ + delete d; +} + +void FolderView::setActive(bool val) +{ + d->active = val; + + if (d->active) + slotSelectionChanged(); +} + +bool FolderView::active() const +{ + return d->active; +} + +int FolderView::itemHeight() const +{ + return d->itemHeight; +} + +TQRect FolderView::itemRect(TQListViewItem *item) const +{ + if(!item) + return TQRect(); + + TQRect r = TQListView::itemRect(item); + r.setLeft(r.left()+(item->depth()+(rootIsDecorated() ? 1 : 0))*treeStepSize()); + return r; +} + +TQPixmap FolderView::itemBasePixmapRegular() const +{ + return d->itemRegPix; +} + +TQPixmap FolderView::itemBasePixmapSelected() const +{ + return d->itemSelPix; +} + +void FolderView::resizeEvent(TQResizeEvent* e) +{ + TQListView::resizeEvent(e); + + int w = frameRect().width(); + int h = itemHeight(); + if (d->itemRegPix.width() != w || + d->itemRegPix.height() != h) + { + slotThemeChanged(); + } +} + +void FolderView::fontChange(const TQFont& oldFont) +{ + // this is bad, since the settings value might not always be the _real_ height of the thumbnail. + // (e.g. when it is blended, as for the tags) + d->itemHeight = TQMAX(AlbumThumbnailLoader::instance()->thumbnailSize() + 2*itemMargin(), fontMetrics().height()); + TQListView::fontChange(oldFont); + slotThemeChanged(); +} + +void FolderView::slotIconSizeChanged() +{ + d->itemHeight = TQMAX(AlbumThumbnailLoader::instance()->thumbnailSize() + 2*itemMargin(), fontMetrics().height()); + slotThemeChanged(); +} + +void FolderView::contentsMouseMoveEvent(TQMouseEvent *e) +{ + TQListView::contentsMouseMoveEvent(e); + + if(e->state() == NoButton) + { + if(TDEGlobalSettings::changeCursorOverIcon()) + { + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *item = itemAt(vp); + if (mouseInItemRect(item, vp.x())) + setCursor(KCursor::handCursor()); + else + unsetCursor(); + } + return; + } + + if(d->dragItem && + (d->dragStartPos - e->pos()).manhattanLength() > TQApplication::startDragDistance()) + { + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *item = itemAt(vp); + if(!item) + { + d->dragItem = 0; + return; + } + } +} + +void FolderView::contentsMousePressEvent(TQMouseEvent *e) +{ + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *item = itemAt(vp); + + // With Check Box item, we will toggle on/off item using middle mouse button. + // See B.K.O #130906 + FolderCheckListItem *citem = dynamic_cast(item); + if(citem && e->button() == MidButton && mouseInItemRect(item, e->pos().x())) + { + TQListView::contentsMousePressEvent(e); + citem->setOn(!citem->isOn()); + return; + } + + TQListView::contentsMousePressEvent(e); + + if(item && e->button() == LeftButton) + { + // Prepare D&D if necessary + d->dragStartPos = e->pos(); + d->dragItem = item; + return; + } +} + +void FolderView::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *item = itemAt(vp); + + TQListView::contentsMouseReleaseEvent(e); + + if(item && e->button() == LeftButton) + { + // See B.K.O #126871: collapse/expand treeview using left mouse button single click. + if (mouseInItemRect(item, e->pos().x())) + item->setOpen(!item->isOpen()); + } + + d->dragItem = 0; +} + +void FolderView::startDrag() +{ + TQDragObject *o = dragObject(); + if(o) + o->drag(); +} + +TQListViewItem* FolderView::dragItem() const +{ + return d->dragItem; +} + +void FolderView::contentsDragEnterEvent(TQDragEnterEvent *e) +{ + TQListView::contentsDragEnterEvent(e); + + e->accept(acceptDrop(e)); +} + +void FolderView::contentsDragLeaveEvent(TQDragLeaveEvent * e) +{ + TQListView::contentsDragLeaveEvent(e); + + if(d->oldHighlightItem) + { + FolderItem *fitem = dynamic_cast(d->oldHighlightItem); + if (fitem) + fitem->setFocus(false); + else + { + FolderCheckListItem *citem = dynamic_cast(d->oldHighlightItem); + if (citem) + citem->setFocus(false); + } + d->oldHighlightItem->repaint(); + d->oldHighlightItem = 0; + } +} + +void FolderView::contentsDragMoveEvent(TQDragMoveEvent *e) +{ + TQListView::contentsDragMoveEvent(e); + + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *item = itemAt(vp); + if(item) + { + if(d->oldHighlightItem) + { + FolderItem *fitem = dynamic_cast(d->oldHighlightItem); + if (fitem) + fitem->setFocus(false); + else + { + FolderCheckListItem *citem = dynamic_cast(d->oldHighlightItem); + if (citem) + citem->setFocus(false); + } + d->oldHighlightItem->repaint(); + } + + FolderItem *fitem = dynamic_cast(item); + if (fitem) + fitem->setFocus(true); + else + { + FolderCheckListItem *citem = dynamic_cast(item); + if (citem) + citem->setFocus(true); + } + d->oldHighlightItem = item; + item->repaint(); + } + e->accept(acceptDrop(e)); +} + +void FolderView::contentsDropEvent(TQDropEvent *e) +{ + TQListView::contentsDropEvent(e); + + if(d->oldHighlightItem) + { + FolderItem *fitem = dynamic_cast(d->oldHighlightItem); + if (fitem) + fitem->setFocus(false); + else + { + FolderCheckListItem *citem = dynamic_cast(d->oldHighlightItem); + if (citem) + citem->setFocus(false); + } + d->oldHighlightItem->repaint(); + d->oldHighlightItem = 0; + } +} + +bool FolderView::acceptDrop(const TQDropEvent *) const +{ + return false; +} + +bool FolderView::mouseInItemRect(TQListViewItem* item, int x) const +{ + if (!item) + return false; + + x += contentsX(); + + int offset = treeStepSize()*(item->depth() + (rootIsDecorated() ? 1 : 0)); + offset += itemMargin(); + int width = item->width(fontMetrics(), this, 0); + + int boxsize = 0; + FolderCheckListItem* citem = dynamic_cast(item); + if (citem && + ((citem->type() == TQCheckListItem::CheckBox) || (citem->type() == TQCheckListItem::CheckBoxController))) + boxsize = style().pixelMetric(TQStyle::PM_CheckListButtonSize, this); + + return (x > (offset + boxsize) && x < (offset + boxsize + width)); +} + +void FolderView::slotThemeChanged() +{ + int w = frameRect().width(); + int h = itemHeight(); + + d->itemRegPix = ThemeEngine::instance()->listRegPixmap(w, h); + d->itemSelPix = ThemeEngine::instance()->listSelPixmap(w, h); + + viewport()->update(); +} + +void FolderView::slotAllAlbumsLoaded() +{ + disconnect(AlbumManager::instance(), TQ_SIGNAL(signalAllAlbumsLoaded()), + this, TQ_SLOT(slotAllAlbumsLoaded())); + loadViewState(); +} + +void FolderView::loadViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + int selectedItem = config->readNumEntry("LastSelectedItem", 0); + + TQValueList openFolders; + if(config->hasKey("OpenFolders")) + { + openFolders = config->readIntListEntry("OpenFolders"); + } + + FolderItem *item = 0; + FolderItem *foundItem = 0; + TQListViewItemIterator it(this->lastItem()); + + for( ; it.current(); --it) + { + item = dynamic_cast(it.current()); + if(!item) + continue; + + // Start the album root always open + if(openFolders.contains(item->id()) || item->id() == 0) + setOpen(item, true); + else + setOpen(item, false); + + if(item->id() == selectedItem) + { + // Save the found selected item so that it can be made visible. + foundItem = item; + } + } + + // Important note: this cannot be done inside the previous loop + // because opening folders prevents the visibility. + // Fixes bug #144815. + // (Looks a bit like a bug in TQt to me ...) + if (foundItem) + { + setSelected(foundItem, true); + ensureItemVisible(foundItem); + } +} + +void FolderView::saveViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + FolderItem *item = dynamic_cast(selectedItem()); + if(item) + config->writeEntry("LastSelectedItem", item->id()); + else + config->writeEntry("LastSelectedItem", 0); + + TQValueList openFolders; + TQListViewItemIterator it(this); + for( ; it.current(); ++it) + { + item = dynamic_cast(it.current()); + if(item && isOpen(item)) + openFolders.push_back(item->id()); + } + config->writeEntry("OpenFolders", openFolders); +} + +void FolderView::slotSelectionChanged() +{ + TQListView::selectionChanged(); +} + +void FolderView::selectItem(int) +{ +} + +void FolderView::collapseView(CollapseMode mode) +{ + // collapse the whole list first + TQListViewItemIterator iter(this); + while (iter.current()) + { + iter.current()->setOpen(false); + iter.current()->setVisible(true); + iter++; + } + + // handle special cases + switch (mode) + { + case OmitRoot: + { + firstChild()->setOpen(true); + break; + } + case RestoreCurrentAlbum: + { + TQListViewItemIterator iter(this); + FolderItem* restoredItem = 0; + + while (iter.current()) + { + FolderItem* curItem = dynamic_cast(iter.current()); + + if (curItem) + { + if (curItem->id() == AlbumManager::instance()->currentAlbum()->id()) + { + curItem->setOpen(true); + restoredItem = curItem; + break; + } + } + iter++; + } + if (restoredItem) + ensureItemVisible(restoredItem); + break; + } + default: + break; + } +} + +} // namespace Digikam diff --git a/src/digikam/folderview.h b/src/digikam/folderview.h new file mode 100644 index 00000000..e32737ce --- /dev/null +++ b/src/digikam/folderview.h @@ -0,0 +1,136 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-28 + * Description : implementation of folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file foldeview.h */ + +#ifndef _FOLDERVIEW_H_ +#define _FOLDERVIEW_H_ + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" +#include "album.h" + +namespace Digikam +{ + +class FolderViewPriv; +class FolderItem; + +/** + * \class FolderView + * \brief Base class for a tree view + */ + + +class DIGIKAM_EXPORT FolderView : public TQListView +{ + TQ_OBJECT + +public: + + enum CollapseMode + { + /* + * Collapse the folder view and re-open the current viewed album (default mode) + * In this mode, all root items are collapsed, then the one containing + * the currently selected album is expand again. + * This mode will make sure that the selected album is visible in the folder tree. + */ + RestoreCurrentAlbum, + /* + * Collapse the folder view but omit the root item. + * In this mode all items in the folder view are collapsed, + * and the first root item is expanded again (My Tags / My Albums etc) + */ + OmitRoot + }; + + FolderView(TQWidget *parent, const char *name = "FolderView"); + virtual ~FolderView(); + + void setActive(bool val); + bool active() const; + + int itemHeight() const; + TQRect itemRect(TQListViewItem *item) const; + TQPixmap itemBasePixmapRegular() const; + TQPixmap itemBasePixmapSelected() const; + + virtual void collapseView(CollapseMode mode = RestoreCurrentAlbum); + +protected: + + void contentsMousePressEvent(TQMouseEvent *e); + void contentsMouseReleaseEvent(TQMouseEvent *e); + void contentsMouseMoveEvent(TQMouseEvent *e); + void contentsDragEnterEvent(TQDragEnterEvent *e); + void contentsDragMoveEvent(TQDragMoveEvent *e); + void contentsDragLeaveEvent(TQDragLeaveEvent * e); + void contentsDropEvent(TQDropEvent *e); + + virtual bool acceptDrop(const TQDropEvent *e) const; + + void startDrag(); + TQListViewItem* dragItem() const; + + void resizeEvent(TQResizeEvent* e); + void fontChange(const TQFont& oldFont); + + virtual void selectItem(int id); + + /** + * load the last state from the view from disk + */ + virtual void loadViewState(); + + /** + * writes the views state to disk + */ + virtual void saveViewState(); + +protected slots: + + virtual void slotSelectionChanged(); + virtual void slotAllAlbumsLoaded(); + +private slots: + + void slotThemeChanged(); + void slotIconSizeChanged(); + +private: + + bool mouseInItemRect(TQListViewItem* item, int x) const; + + FolderViewPriv *d; +}; + +} // namespace Digikam + +#endif // _FOLDERVIEW_H diff --git a/src/digikam/icongroupitem.cpp b/src/digikam/icongroupitem.cpp new file mode 100644 index 00000000..e16588fe --- /dev/null +++ b/src/digikam/icongroupitem.cpp @@ -0,0 +1,307 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icons group item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +#include + +// TQt includes. + +#include +#include + +// Local includes. + +#include "iconview.h" +#include "iconitem.h" +#include "icongroupitem.h" + +namespace Digikam +{ + +class IconGroupItemPriv +{ +public: + + IconGroupItemPriv() + { + view = 0; + firstItem = 0; + lastItem = 0; + y = 0; + count = 0; + clearing = false; + } + + IconView* view; + + IconItem* firstItem; + IconItem* lastItem; + + int y; + int count; + bool clearing; + + struct SortableItem + { + IconItem *item; + }; +}; + +IconGroupItem::IconGroupItem(IconView* parent) +{ + d = new IconGroupItemPriv; + d->view = parent; + m_next = 0; + m_prev = 0; + + parent->insertGroup(this); +} + +IconGroupItem::~IconGroupItem() +{ + clear(false); + d->view->takeGroup(this); + delete d; +} + +IconView* IconGroupItem::iconView() const +{ + return d->view; +} + +IconGroupItem* IconGroupItem::nextGroup() const +{ + return m_next; +} + +IconGroupItem* IconGroupItem::prevGroup() const +{ + return m_prev; +} + +TQRect IconGroupItem::rect() const +{ + TQRect r = d->view->bannerRect(); + r.moveBy(0, d->y); + return r; +} + +int IconGroupItem::y() const +{ + return d->y; +} + +IconItem* IconGroupItem::firstItem() const +{ + return d->firstItem; +} + +IconItem* IconGroupItem::lastItem() const +{ + return d->lastItem; +} + +void IconGroupItem::insertItem(IconItem* item) +{ + if (!item) + return; + + if (!d->firstItem) + { + d->firstItem = item; + d->lastItem = item; + item->m_prev = 0; + item->m_next = 0; + } + else + { + d->lastItem->m_next = item; + item->m_prev = d->lastItem; + item->m_next = 0; + d->lastItem = item; + } + + d->count++; + d->view->insertItem(item); +} + +void IconGroupItem::takeItem(IconItem* item) +{ + if (!item) + return; + + // take item triggers update + d->view->takeItem(item); + d->count--; + + if (item == d->firstItem) + { + d->firstItem = d->firstItem->m_next; + if (d->firstItem) + d->firstItem->m_prev = 0; + else + d->firstItem = d->lastItem = 0; + } + else if (item == d->lastItem) + { + d->lastItem = d->lastItem->m_prev; + if ( d->lastItem ) + d->lastItem->m_next = 0; + else + d->firstItem = d->lastItem = 0; + } + else + { + IconItem *i = item; + if (i) + { + if (i->m_prev ) + i->m_prev->m_next = i->m_next; + if ( i->m_next ) + i->m_next->m_prev = i->m_prev; + } + } +} + +int IconGroupItem::count() const +{ + return d->count; +} + +int IconGroupItem::index(IconItem* item) const +{ + if ( !item ) + return -1; + + if ( item == d->firstItem ) + return 0; + else if ( item == d->lastItem ) + return d->count - 1; + else + { + IconItem *i = d->firstItem; + int j = 0; + while ( i && i != item ) + { + i = i->m_next; + ++j; + } + + return i ? j : -1; + } +} + +void IconGroupItem::clear(bool update) +{ + d->clearing = true; + + IconItem *item = d->firstItem; + while (item) + { + IconItem *tmp = item->m_next; + delete item; + item = tmp; + } + + d->firstItem = 0; + d->lastItem = 0; + d->count = 0; + + if (update) + d->view->triggerRearrangement(); + + d->clearing = false; +} + +void IconGroupItem::sort() +{ + IconGroupItemPriv::SortableItem *items + = new IconGroupItemPriv::SortableItem[ count() ]; + + IconItem *item = d->firstItem; + int i = 0; + for ( ; item; item = item->m_next ) + items[ i++ ].item = item; + + qsort( items, count(), sizeof( IconGroupItemPriv::SortableItem ), cmpItems ); + + IconItem *prev = 0; + item = 0; + for ( i = 0; i < (int)count(); ++i ) { + item = items[ i ].item; + if ( item ) { + item->m_prev = prev; + if ( item->m_prev ) + item->m_prev->m_next = item; + item->m_next = 0; + } + if ( i == 0 ) + d->firstItem = item; + if ( i == (int)count() - 1 ) + d->lastItem = item; + prev = item; + } + + delete [] items; +} + +bool IconGroupItem::move(int y) +{ + if (d->y == y) + return false; + + d->y = y; + return true; +} + +void IconGroupItem::paintBanner() +{ + TQRect r(rect()); + TQPixmap pix(r.width(), r.height()); + pix.fill(d->view->colorGroup().base()); + + r = TQRect(d->view->contentsToViewport(TQPoint(r.x(), r.y())), + TQSize(r.width(), r.height())); + + bitBlt(d->view->viewport(), r.x(), r.y(), &pix, + 0, 0, r.width(), r.height()); +} + +int IconGroupItem::compare(IconGroupItem*) +{ + return 0; +} + +int IconGroupItem::cmpItems(const void *n1, const void *n2) +{ + if ( !n1 || !n2 ) + return 0; + + IconGroupItemPriv::SortableItem *i1 = (IconGroupItemPriv::SortableItem *)n1; + IconGroupItemPriv::SortableItem *i2 = (IconGroupItemPriv::SortableItem *)n2; + + return i1->item->compare( i2->item ); +} + +} // namespace Digikam diff --git a/src/digikam/icongroupitem.h b/src/digikam/icongroupitem.h new file mode 100644 index 00000000..8a960a1f --- /dev/null +++ b/src/digikam/icongroupitem.h @@ -0,0 +1,91 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icons group item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICONGROUPITEM_H +#define ICONGROUPITEM_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class IconView; +class IconItem; +class IconGroupItemPriv; + +class DIGIKAM_EXPORT IconGroupItem +{ + friend class IconView; + +public: + + IconGroupItem(IconView* parent); + virtual ~IconGroupItem(); + + IconView* iconView() const; + + IconGroupItem* nextGroup() const; + IconGroupItem* prevGroup() const; + + TQRect rect() const; + int y() const; + bool move(int y); + + IconItem* firstItem() const; + IconItem* lastItem() const; + + int count() const; + int index(IconItem* item) const; + + void clear(bool update=true); + void sort(); + + void insertItem(IconItem* item); + void takeItem(IconItem* item); + + virtual int compare(IconGroupItem *group); + +protected: + + virtual void paintBanner(); + +private: + + static int cmpItems(const void *n1, const void *n2); + +private: + + IconGroupItemPriv *d; + IconGroupItem *m_next; + IconGroupItem *m_prev; +}; + +} // namespace Digikam + +#endif /* ICONGROUPITEM_H */ diff --git a/src/digikam/iconitem.cpp b/src/digikam/iconitem.cpp new file mode 100644 index 00000000..798b96ae --- /dev/null +++ b/src/digikam/iconitem.cpp @@ -0,0 +1,171 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icon item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// Local includes. + +#include "icongroupitem.h" +#include "iconview.h" +#include "iconitem.h" + +namespace Digikam +{ + +IconItem::IconItem(IconGroupItem* parent) + : m_group(parent) +{ + m_next = 0; + m_prev = 0; + m_x = 0; + m_y = 0; + m_selected = false; + + m_group->insertItem(this); +} + +IconItem::~IconItem() +{ + m_group->takeItem(this); +} + +IconItem* IconItem::nextItem() const +{ + if (m_next) + return m_next; + + if (m_group->nextGroup()) + return m_group->nextGroup()->firstItem(); + + return 0; +} + +IconItem* IconItem::prevItem() const +{ + if (m_prev) + return m_prev; + + if (m_group->prevGroup()) + return m_group->prevGroup()->lastItem(); + + return 0; +} + +int IconItem::x() const +{ + return m_x; +} + +int IconItem::y() const +{ + return m_y; +} + +TQRect IconItem::rect() const +{ + IconView* view = m_group->iconView(); + TQRect r(view->itemRect()); + r.moveTopLeft(TQPoint(m_x, m_y)); + return r; +} + +TQRect IconItem::clickToOpenRect() +{ + return rect(); +} + +bool IconItem::move(int x, int y) +{ + if (m_x == x && m_y == y) + return false; + + m_x = x; m_y = y; + return true; +} + +void IconItem::setSelected(bool val, bool cb) +{ + IconView* view = m_group->iconView(); + + if (cb) + { + view->blockSignals(true); + view->clearSelection(); + view->blockSignals(false); + } + + m_selected = val; + view->selectItem(this, val); + view->updateContents(rect()); +} + +bool IconItem::isSelected() const +{ + return m_selected; +} + +void IconItem::repaint(bool force) +{ + if (force) + m_group->iconView()->repaintContents(rect()); + else + m_group->iconView()->updateContents(rect()); +} + +IconView* IconItem::iconView() const +{ + return m_group->iconView(); +} + +int IconItem::compare(IconItem* /*item*/) +{ + return 0; +} + +void IconItem::paintItem() +{ + IconView* view = m_group->iconView(); + + TQRect r(rect()); + TQPixmap pix(r.width(), r.height()); + pix.fill(m_selected ? TQt::blue : TQt::gray); + + if (this == iconView()->currentItem()) + { + TQPainter p(&pix); + p.setPen(TQPen(m_selected ? TQt::white : TQt::black, 1, TQt::DotLine)); + p.drawRect(2, 2, r.width()-4, r.width()-4); + p.end(); + } + + r = TQRect(view->contentsToViewport(TQPoint(r.x(), r.y())), + TQSize(r.width(), r.height())); + + bitBlt(view->viewport(), r.x(), r.y(), &pix, + 0, 0, r.width(), r.height()); +} + +} // namespace Digikam diff --git a/src/digikam/iconitem.h b/src/digikam/iconitem.h new file mode 100644 index 00000000..aa67e209 --- /dev/null +++ b/src/digikam/iconitem.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icon item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICONITEM_H +#define ICONITEM_H + +// TQt includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class IconGroupItem; +class IconView; + +class DIGIKAM_EXPORT IconItem +{ + friend class IconView; + friend class IconGroupItem; + +public: + + IconItem(IconGroupItem* parent); + virtual ~IconItem(); + + IconItem* nextItem() const; + IconItem* prevItem() const; + + int x() const; + int y() const; + TQRect rect() const; + + bool move(int x, int y); + + void setSelected(bool val, bool cb=true); + bool isSelected() const; + + void repaint(bool force=true); + + IconView* iconView() const; + + virtual int compare(IconItem *item); + virtual TQRect clickToOpenRect(); + +protected: + + virtual void paintItem(); + +private: + + IconGroupItem *m_group; + IconItem *m_next; + IconItem *m_prev; + int m_x; + int m_y; + bool m_selected; +}; + +} // namespace Digikam + +#endif /* ICONITEM_H */ diff --git a/src/digikam/iconview.cpp b/src/digikam/iconview.cpp new file mode 100644 index 00000000..2dac3da0 --- /dev/null +++ b/src/digikam/iconview.cpp @@ -0,0 +1,1969 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icons view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define RECT_EXTENSION 300 + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "iconitem.h" +#include "icongroupitem.h" +#include "iconview.h" +#include "iconview.moc" + +namespace Digikam +{ + +class IconViewPriv +{ +public: + + IconViewPriv() + { + firstGroup = 0; + lastGroup = 0; + currItem = 0; + anchorItem = 0; + clearing = false; + spacing = 10; + + rubber = 0; + dragging = false; + pressedMoved = false; + + firstContainer = 0; + lastContainer = 0; + + showTips = false; + toolTipItem = 0; + toolTipTimer = 0; + rearrangeTimer = 0; + rearrangeTimerInterval = 0; + storedVisibleItem = 0; + needEmitSelectionChanged = false; + } + + bool clearing; + bool showTips; + bool pressedMoved; + bool dragging; + bool needEmitSelectionChanged; // store for slotRearrange + + int spacing; + + TQPtrDict selectedItems; + TQPtrDict prevSelectedItems; + + TQRect* rubber; + + TQPoint dragStartPos; + + TQTimer* rearrangeTimer; + TQTimer* toolTipTimer; + + IconItem* toolTipItem; + IconItem* currItem; + IconItem* anchorItem; + IconItem* storedVisibleItem; // store position for slotRearrange + + IconGroupItem* firstGroup; + IconGroupItem* lastGroup; + + int rearrangeTimerInterval; + + struct ItemContainer + { + ItemContainer(ItemContainer *p, ItemContainer *n, const TQRect &r) + : prev(p), next(n), rect(r) + { + if (prev) + prev->next = this; + if (next) + next->prev = this; + } + + ItemContainer *prev, *next; + TQRect rect; + TQValueList items; + } *firstContainer, *lastContainer; + + struct SortableItem + { + IconGroupItem *group; + }; +}; + +IconView::IconView(TQWidget* parent, const char* name) + : TQScrollView(parent, name, TQt::WStaticContents|TQt::WNoAutoErase) +{ + viewport()->setBackgroundMode(TQt::NoBackground); + viewport()->setFocusProxy(this); + viewport()->setFocusPolicy(TQWidget::WheelFocus); + viewport()->setMouseTracking(true); + + d = new IconViewPriv; + d->rearrangeTimer = new TQTimer(this); + d->toolTipTimer = new TQTimer(this); + + connect(d->rearrangeTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotRearrange())); + + connect(d->toolTipTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotToolTip())); + + setEnableToolTips(true); +} + +IconView::~IconView() +{ + clear(false); + + delete d->rearrangeTimer; + delete d->toolTipTimer; + delete d->rubber; + delete d; +} + +IconGroupItem* IconView::firstGroup() const +{ + return d->firstGroup; +} + +IconGroupItem* IconView::lastGroup() const +{ + return d->lastGroup; +} + +IconItem* IconView::firstItem() const +{ + if (!d->firstGroup) + return 0; + + return d->firstGroup->firstItem(); +} + +IconItem* IconView::lastItem() const +{ + if (!d->lastGroup) + return 0; + + return d->lastGroup->lastItem(); +} + +IconItem* IconView::currentItem() const +{ + return d->currItem; +} + +void IconView::setCurrentItem(IconItem* item) +{ + d->currItem = item; + d->anchorItem = d->currItem; + + if (d->currItem) + { + d->currItem->setSelected(true, true); + ensureItemVisible(d->currItem); + } +} + +IconItem* IconView::findItem(const TQPoint& pos) +{ + IconViewPriv::ItemContainer *c = d->firstContainer; + for (; c; c = c->next) + { + if ( c->rect.contains(pos) ) + { + for (TQValueList::iterator it = c->items.begin(); + it != c->items.end(); ++it) + { + IconItem* item = *it; + if (item->rect().contains(pos)) + return item; + } + } + } + + return 0; +} + +IconGroupItem* IconView::findGroup(const TQPoint& pos) +{ + TQPoint p = viewportToContents(viewport()->mapFromGlobal(pos)); + for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) + { + TQRect rect = group->rect(); + int bottom; + if (group == d->lastGroup) + bottom = contentsHeight(); + else + bottom = group->nextGroup()->rect().top(); + + rect.setBottom(bottom); + + if ( rect.contains(p) ) + { + return group; + } + } + + return 0; +} + +int IconView::count() const +{ + int c = 0; + for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) + { + c += group->count(); + } + + return c; +} + + +int IconView::countSelected() const +{ + int c = 0; + for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) + { + for (IconItem *it = group->firstItem(); it; it = it->nextItem()) + if (it->isSelected()) + c++; + } + + return c; +} + +int IconView::groupCount() const +{ + int c = 0; + for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) + { + c++; + } + + return c; +} + +void IconView::clear(bool update) +{ + d->clearing = true; + + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + deleteContainers(); + + d->selectedItems.clear(); + + IconGroupItem *group = d->firstGroup; + while (group) + { + IconGroupItem *tmp = group->m_next; + delete group; + group = tmp; + } + + d->firstGroup = 0; + d->lastGroup = 0; + d->currItem = 0; + d->anchorItem = 0; + + viewport()->setUpdatesEnabled(false); + resizeContents(0, 0); + setContentsPos(0, 0); + viewport()->setUpdatesEnabled(true); + + if (update) + updateContents(); + + d->clearing = false; + + emit signalSelectionChanged(); +} + +void IconView::clearSelection() +{ + bool wasBlocked = signalsBlocked();; + + if (!wasBlocked) + blockSignals(true); + + TQPtrDict selItems = d->selectedItems; + TQPtrDictIterator it( selItems ); + for ( ; it.current(); ++it ) + it.current()->setSelected(false, false); + + d->selectedItems.clear(); + + if (!wasBlocked) + blockSignals(false); + + emit signalSelectionChanged(); +} + +void IconView::selectAll() +{ + bool wasBlocked = signalsBlocked(); + + if (!wasBlocked) + blockSignals(true); + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (!item->isSelected()) + { + item->setSelected(true, false); + } + } + + if (!wasBlocked) + blockSignals(false); + + emit signalSelectionChanged(); +} + +void IconView::invertSelection() +{ + bool wasBlocked = signalsBlocked(); + + if (!wasBlocked) + blockSignals(true); + + for (IconItem* item = firstItem(); item; item = item->nextItem()) + { + if (!item->isSelected()) + { + item->setSelected(true, false); + } + else + { + item->setSelected(false, false); + } + } + + if (!wasBlocked) + blockSignals(false); + + emit signalSelectionChanged(); +} + +void IconView::selectItem(IconItem* item, bool select) +{ + if (!item) + return; + + if (select) + { + d->selectedItems.replace(item, item); + } + else + { + d->selectedItems.remove(item); + } + + emit signalSelectionChanged(); +} + +void IconView::setStoredVisibleItem(IconItem *item) +{ + d->storedVisibleItem = item; +} + +void IconView::insertGroup(IconGroupItem* group) +{ + if (!group) + return; + + if (!d->firstGroup) + { + d->firstGroup = group; + d->lastGroup = group; + group->m_prev = 0; + group->m_next = 0; + } + else + { + d->lastGroup->m_next = group; + group->m_prev = d->lastGroup; + group->m_next = 0; + d->lastGroup = group; + } + + d->storedVisibleItem = findFirstVisibleItem(); + startRearrangeTimer(); +} + +void IconView::takeGroup(IconGroupItem* group) +{ + if (!group) + return; + + // this is only to find an alternative visible item if all visible items + // are removed + IconGroupItem *alternativeVisibleGroup = 0; + d->storedVisibleItem = 0; + + if (group == d->firstGroup) + { + d->firstGroup = d->firstGroup->m_next; + if (d->firstGroup) + d->firstGroup->m_prev = 0; + else + d->firstGroup = d->lastGroup = 0; + alternativeVisibleGroup = d->firstGroup; + } + else if (group == d->lastGroup) + { + d->lastGroup = d->lastGroup->m_prev; + if ( d->lastGroup ) + d->lastGroup->m_next = 0; + else + d->firstGroup = d->lastGroup = 0; + alternativeVisibleGroup = d->lastGroup->m_prev; + } + else + { + IconGroupItem *i = group; + if (i) + { + if (i->m_prev ) + i->m_prev->m_next = i->m_next; + if ( i->m_next ) + i->m_next->m_prev = i->m_prev; + + if (i->m_prev) + alternativeVisibleGroup = i->m_prev; + else + alternativeVisibleGroup = i->m_next; + } + } + + if (!d->clearing) + { + d->storedVisibleItem = findFirstVisibleItem(); + if (!d->storedVisibleItem && alternativeVisibleGroup) + { + // find an alternative visible item + d->storedVisibleItem = alternativeVisibleGroup->lastItem(); + } + startRearrangeTimer(); + } +} + +void IconView::insertItem(IconItem* item) +{ + if (!item) + return; + + d->storedVisibleItem = findFirstVisibleItem(); + startRearrangeTimer(); +} + +void IconView::takeItem(IconItem* item) +{ + if (!item) + return; + + // First remove item from any containers holding it + IconViewPriv::ItemContainer *tmp = d->firstContainer; + while (tmp) + { + tmp->items.remove(item); + tmp = tmp->next; + } + + // Remove from selected item list + d->selectedItems.remove(item); + // See bug 161084 + if (d->selectedItems.count() || item->isSelected()) + d->needEmitSelectionChanged = true; + + if (d->toolTipItem == item) + { + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + } + + // if it is current item, change the current item + if (d->currItem == item) + { + d->currItem = item->nextItem(); + if (!d->currItem) + d->currItem = item->prevItem(); + // defer calling d->currItem->setSelected (and emitting the signals) to slotRearrange + } + + d->anchorItem = d->currItem; + + if (!d->clearing) + { + d->storedVisibleItem = findFirstVisibleItem(); + if (d->storedVisibleItem == item) + d->storedVisibleItem = d->currItem; + startRearrangeTimer(); + } +} + +void IconView::triggerRearrangement() +{ + d->storedVisibleItem = findFirstVisibleItem(); + startRearrangeTimer(); +} + +void IconView::setDelayedRearrangement(bool delayed) +{ + // if it is known that e.g. several items will be added or deleted in the next time, + // but not from the same event queue thread stack location, it may be desirable to delay + // the rearrangeTimer a bit + if (delayed) + d->rearrangeTimerInterval = 50; + else + d->rearrangeTimerInterval = 0; +} + +void IconView::startRearrangeTimer() +{ + // We want to reduce the number of updates, but not remove all updates + if (!d->rearrangeTimer->isActive()) + d->rearrangeTimer->start(d->rearrangeTimerInterval, true); +} + +void IconView::sort() +{ + // first sort the groups + for (IconGroupItem* group = d->firstGroup; group; + group = group->nextGroup()) + { + group->sort(); + } + + int gcount = groupCount(); + + // then sort the groups themselves + IconViewPriv::SortableItem *groups = new IconViewPriv::SortableItem[ gcount ]; + + IconGroupItem *group = d->firstGroup; + int i = 0; + + for ( ; group; group = group->m_next ) + groups[ i++ ].group = group; + + qsort( groups, gcount, sizeof( IconViewPriv::SortableItem ), cmpItems ); + + IconGroupItem *prev = 0; + group = 0; + + for ( i = 0; i < (int)gcount; ++i ) + { + group = groups[ i ].group; + if ( group ) + { + group->m_prev = prev; + + if ( group->m_prev ) + group->m_prev->m_next = group; + + group->m_next = 0; + } + + if ( i == 0 ) + d->firstGroup = group; + + if ( i == (int)gcount - 1 ) + d->lastGroup = group; + prev = group; + } + + delete [] groups; +} + +void IconView::slotRearrange() +{ + sort(); + arrangeItems(); + + // ensure there is a current item + if (!d->currItem) + { + // set the currItem to first item + if (d->firstGroup) + d->currItem = d->firstGroup->firstItem(); + } + d->anchorItem = d->currItem; + + // ensure there is a selection + if (d->selectedItems.isEmpty() && d->currItem) + { + d->currItem->setSelected(true, true); + } + else if (d->needEmitSelectionChanged) + { + emit signalSelectionChanged(); + } + d->needEmitSelectionChanged = false; + + // set first visible item if they where stored before update was triggered + if (d->storedVisibleItem) + { + ensureItemVisible(d->storedVisibleItem); + // reset to 0 + d->storedVisibleItem = 0; + } + else + { + ensureItemVisible(d->currItem); + } + + viewport()->update(); +} + +bool IconView::arrangeItems() +{ + int y = 0; + int itemW = itemRect().width(); + int itemH = itemRect().height(); + int maxW = 0; + + int numItemsPerRow = visibleWidth()/(itemW + d->spacing); + + bool changed = false; + + IconGroupItem* group = d->firstGroup; + IconItem* item = 0; + while (group) + { + changed = group->move(y) || changed; + y += group->rect().height() + d->spacing; + + item = group->firstItem(); + + int col = 0; + int x = d->spacing; + while (item) + { + changed = item->move(x, y) || changed; + x += itemW + d->spacing; + col++; + + if (col >= numItemsPerRow) + { + x = d->spacing; + y += itemH + d->spacing; + col = 0; + } + + maxW = TQMAX(maxW, x + itemW); + + item = item->m_next; + } + + if (col != 0) + { + y += itemH + d->spacing; + } + + y += d->spacing; + + group = group->m_next; + } + + viewport()->setUpdatesEnabled(false); + resizeContents( maxW, y ); + viewport()->setUpdatesEnabled(true); + + rebuildContainers(); + + return changed; +} + +TQRect IconView::itemRect() const +{ + return TQRect(0, 0, 100, 100); +} + +TQRect IconView::bannerRect() const +{ + return TQRect(0, 0, visibleWidth(), 0); +} + +void IconView::viewportPaintEvent(TQPaintEvent* pe) +{ + TQRect r(pe->rect()); + TQRegion paintRegion(pe->region()); + + TQPainter painter(viewport()); + painter.setClipRegion(paintRegion); + + // paint any group banners which intersect this paintevent rect + for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) + { + TQRect br(contentsRectToViewport(group->rect())); + if (r.intersects(br)) + { + group->paintBanner(); + paintRegion -= TQRegion(br); + } + } + + // now paint any items which intersect + for (IconViewPriv::ItemContainer* c = d->firstContainer; c; + c = c->next) + { + TQRect cr(contentsRectToViewport(c->rect)); + + if (r.intersects(cr)) + { + + for (TQValueList::iterator it = c->items.begin(); + it != c->items.end(); ++it) + { + IconItem* item = *it; + TQRect ir(contentsRectToViewport(item->rect())); + if (r.intersects(ir)) + { + item->paintItem(); + paintRegion -= TQRegion(ir); + } + } + } + } + + painter.setClipRegion(paintRegion); + painter.fillRect(r, colorGroup().base()); + painter.end(); +} + +TQRect IconView::contentsRectToViewport(const TQRect& r) const +{ + TQRect vr = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), r.size()); + return vr; +} + +void IconView::resizeEvent(TQResizeEvent* e) +{ + TQScrollView::resizeEvent(e); + triggerRearrangement(); +} + +void IconView::rebuildContainers() +{ + deleteContainers(); + + IconItem *item = 0; + appendContainer(); + + if (d->firstGroup) + item = d->firstGroup->firstItem(); + + IconViewPriv::ItemContainer* c = d->lastContainer; + while (item) + { + if (c->rect.contains(item->rect())) + { + c->items.append(item); + item = item->nextItem(); + } + else if (c->rect.intersects(item->rect())) + { + c->items.append( item ); + c = c->next; + + if (!c) + { + appendContainer(); + c = d->lastContainer; + } + + c->items.append(item); + item = item->nextItem(); + c = c->prev; + } + else + { + if (item->y() < c->rect.y() && c->prev) + { + c = c->prev; + continue; + } + + c = c->next; + if (!c) + { + appendContainer(); + c = d->lastContainer; + } + } + } +} + +void IconView::appendContainer() +{ + TQSize s( INT_MAX - 1, RECT_EXTENSION ); + + if (!d->firstContainer) + { + d->firstContainer = + new IconViewPriv::ItemContainer(0, 0, TQRect(TQPoint(0, 0), s)); + d->lastContainer = d->firstContainer; + } + else + { + d->lastContainer = new IconViewPriv::ItemContainer( + d->lastContainer, 0, TQRect(d->lastContainer->rect.bottomLeft(), s)); + } +} + +void IconView::deleteContainers() +{ + IconViewPriv::ItemContainer *c = d->firstContainer; + IconViewPriv::ItemContainer *tmp; + + while (c) + { + tmp = c->next; + delete c; + c = tmp; + } + + d->firstContainer = d->lastContainer = 0; +} + +void IconView::leaveEvent(TQEvent *e) +{ + // hide tooltip + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + // if the mouse leaves the widget we are not dragging + // anymore + d->dragging = false; + + TQScrollView::leaveEvent(e); +} + +void IconView::focusOutEvent(TQFocusEvent* e) +{ + // hide tooltip + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + TQScrollView::focusOutEvent(e); +} + +bool IconView::acceptToolTip(IconItem*, const TQPoint&) +{ + return true; +} + +void IconView::contentsMousePressEvent(TQMouseEvent* e) +{ + d->pressedMoved = false; + + // hide tooltip + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + // Delete any existing rubber ------------------------------- + if ( d->rubber ) + { + TQPainter p; + p.begin(viewport()); + p.setRasterOp(NotROP); + p.setPen(TQPen(color0, 1)); + p.setBrush(NoBrush); + + drawRubber(&p); + p.end(); + delete d->rubber; + d->rubber = 0; + } + + if (e->button() == TQt::RightButton) + { + IconItem* item = findItem(e->pos()); + if (item) + { + IconItem* prevCurrItem = d->currItem; + d->currItem = item; + d->anchorItem = item; + if (prevCurrItem) + prevCurrItem->repaint(); + + if (!item->isSelected()) + item->setSelected(true, true); + item->repaint(); + + emit signalRightButtonClicked(item, e->globalPos()); + } + else + { + clearSelection(); + emit signalRightButtonClicked(e->globalPos()); + } + return; + } + + IconItem *item = findItem(e->pos()); + if (item) + { + if (e->state() & TQt::ControlButton) + { + item->setSelected(!item->isSelected(), false); + } + else if (e->state() & TQt::ShiftButton) + { + blockSignals(true); + + if (d->currItem) + { + clearSelection(); + + // select all items from/upto the current item + bool bwdSelect = false; + + // find if the current item is before the clicked item + for (IconItem* it = item->prevItem(); it; it = it->prevItem()) + { + if (it == d->currItem) + { + bwdSelect = true; + break; + } + } + + if (bwdSelect) + { + for (IconItem* it = item; it; it = it->prevItem()) + { + it->setSelected(true, false); + if (it == d->currItem) + break; + } + } + else + { + for (IconItem* it = item; it; it = it->nextItem()) + { + it->setSelected(true, false); + if (it == d->currItem) + break; + } + } + } + else + { + item->setSelected(true, false); + } + + blockSignals(false); + + emit signalSelectionChanged(); + } + else + { + if (!item->isSelected()) + item->setSelected(true, true); + } + + IconItem* prevCurrItem = d->currItem; + d->currItem = item; + d->anchorItem = item; + + if (prevCurrItem) + prevCurrItem->repaint(); + + d->currItem->repaint(); + + d->dragging = true; + d->dragStartPos = e->pos(); + + return; + } + + // Press outside any item. + if (!(e->state() & TQt::ControlButton)) + { + // unselect all if the ctrl button is not pressed + clearSelection(); + } + else + { + // ctrl is pressed. make sure our current selection is not lost + d->prevSelectedItems.clear(); + TQPtrDictIterator it( d->selectedItems ); + + for ( ; it.current(); ++it ) + { + d->prevSelectedItems.insert(it.current(), it.current()); + } + } + + d->rubber = new TQRect( e->x(), e->y(), 0, 0 ); + + TQPainter p; + p.begin( viewport() ); + p.setRasterOp( NotROP ); + p.setPen( TQPen( color0, 1 ) ); + p.setBrush( NoBrush ); + drawRubber( &p ); + p.end(); +} + +void IconView::drawRubber(TQPainter* p) +{ + if ( !p || !d->rubber ) + return; + + TQRect r(d->rubber->normalize()); + + r = contentsRectToViewport(r); + + TQPoint pnt(r.x(), r.y()); + + style().drawPrimitive(TQStyle::PE_FocusRect, p, + TQRect( pnt.x(), pnt.y(), + r.width(), r.height() ), + colorGroup(), TQStyle::Style_Default, + TQStyleOption(colorGroup().base())); +} + +void IconView::contentsMouseMoveEvent(TQMouseEvent* e) +{ + if (e->state() == NoButton) + { + IconItem* item = findItem(e->pos()); + + if(d->showTips) + { + if (!isActiveWindow()) + { + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + return; + } + + if (item != d->toolTipItem) + { + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + if(acceptToolTip(item, e->pos())) + { + d->toolTipItem = item; + d->toolTipTimer->start(500, true); + } + } + + if(item == d->toolTipItem && !acceptToolTip(item, e->pos())) + { + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + } + } + + if (TDEGlobalSettings::changeCursorOverIcon()) + { + if (item && item->clickToOpenRect().contains(e->pos())) + setCursor(KCursor::handCursor()); + else + unsetCursor(); + } + return; + } + + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + + if (d->dragging && (e->state() & TQt::LeftButton)) + { + if ( (d->dragStartPos - e->pos()).manhattanLength() + > TQApplication::startDragDistance() ) + { + startDrag(); + } + return; + } + + if (!d->rubber) + return; + + TQRect oldRubber = TQRect(*d->rubber); + + d->rubber->setRight( e->pos().x() ); + d->rubber->setBottom( e->pos().y() ); + + TQRect nr = d->rubber->normalize(); + TQRect rubberUnion = nr.unite(oldRubber.normalize()); + + bool changed = false; + + TQRegion paintRegion; + viewport()->setUpdatesEnabled(false); + blockSignals(true); + + IconViewPriv::ItemContainer *c = d->firstContainer; + for (; c; c = c->next) + { + if ( rubberUnion.intersects(c->rect) ) + { + for (TQValueList::iterator it = c->items.begin(); + it != c->items.end(); ++it) + { + IconItem* item = *it; + if (nr.intersects(item->rect())) + { + if (!item->isSelected()) + { + item->setSelected(true, false); + changed = true; + paintRegion += TQRect(item->rect()); + } + } + else + { + if (item->isSelected() && !d->prevSelectedItems.find(item)) + { + item->setSelected(false, false); + changed = true; + paintRegion += TQRect(item->rect()); + } + } + } + } + } + + blockSignals(false); + viewport()->setUpdatesEnabled(true); + + TQRect r = *d->rubber; + *d->rubber = oldRubber; + + TQPainter p; + p.begin( viewport() ); + p.setRasterOp( NotROP ); + p.setPen( TQPen( color0, 1 ) ); + p.setBrush( NoBrush ); + drawRubber( &p ); + p.end(); + + if (changed) + { + paintRegion.translate(-contentsX(), -contentsY()); + viewport()->repaint(paintRegion); + } + + ensureVisible(e->pos().x(), e->pos().y()); + + *d->rubber = r; + + p.begin(viewport()); + p.setRasterOp(NotROP); + p.setPen(TQPen(color0, 1)); + p.setBrush(NoBrush); + drawRubber(&p); + p.end(); + + d->pressedMoved = true; + + if (changed) + emit signalSelectionChanged(); +} + +void IconView::contentsMouseReleaseEvent(TQMouseEvent* e) +{ + d->dragging = false; + d->prevSelectedItems.clear(); + + if (d->rubber) + { + TQPainter p; + p.begin( viewport() ); + p.setRasterOp( NotROP ); + p.setPen( TQPen( color0, 1 ) ); + p.setBrush( NoBrush ); + + drawRubber( &p ); + p.end(); + + delete d->rubber; + d->rubber = 0; + } + + if (e->state() == TQt::LeftButton) + { + if (d->pressedMoved) + { + emit signalSelectionChanged(); + d->pressedMoved = false; + return; + } + + // click on item + IconItem *item = findItem(e->pos()); + if (item) + { + IconItem* prevCurrItem = d->currItem; + item->setSelected(true, true); + d->currItem = item; + d->anchorItem = item; + if (prevCurrItem) + prevCurrItem->repaint(); + if (TDEGlobalSettings::singleClick()) + { + if (item->clickToOpenRect().contains(e->pos())) + { + itemClickedToOpen(item); + } + } + } + } +} + +void IconView::contentsWheelEvent(TQWheelEvent* e) +{ + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + viewport()->update(); + + TQScrollView::contentsWheelEvent(e); +} + +void IconView::contentsMouseDoubleClickEvent(TQMouseEvent *e) +{ + if (TDEGlobalSettings::singleClick()) + return; + + IconItem *item = findItem(e->pos()); + if (item) + { + itemClickedToOpen(item); + } +} + +void IconView::keyPressEvent(TQKeyEvent* e) +{ + bool handled = false; + + if (!firstItem()) + return; + + switch ( e->key() ) + { + case Key_Home: + { + IconItem* tmp = d->currItem; + d->currItem = firstItem(); + d->anchorItem = d->currItem; + if (tmp) + tmp->repaint(); + + firstItem()->setSelected(true, true); + ensureItemVisible(firstItem()); + handled = true; + break; + } + + case Key_End: + { + IconItem* tmp = d->currItem; + d->currItem = lastItem(); + d->anchorItem = d->currItem; + if (tmp) + tmp->repaint(); + + lastItem()->setSelected(true, true); + ensureItemVisible(lastItem()); + handled = true; + break; + } + + case Key_Enter: + case Key_Return: + { + if (d->currItem) + { + emit signalReturnPressed(d->currItem); + handled = true; + } + break; + } + + case Key_Right: + { + IconItem *item = 0; + + if (d->currItem) + { + if (d->currItem->nextItem()) + { + if (e->state() & TQt::ControlButton) + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->nextItem(); + d->anchorItem = d->currItem; + tmp->repaint(); + d->currItem->repaint(); + + item = d->currItem; + } + else if (e->state() & TQt::ShiftButton) + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->nextItem(); + tmp->repaint(); + + // if the anchor is behind us, move forward preserving + // the previously selected item. otherwise unselect the + // previously selected item + if (!anchorIsBehind()) + tmp->setSelected(false, false); + + d->currItem->setSelected(true, false); + + item = d->currItem; + } + else + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->nextItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + tmp->repaint(); + + item = d->currItem; + } + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + case Key_Left: + { + IconItem *item = 0; + + if (d->currItem) + { + if (d->currItem->prevItem()) + { + if (e->state() & TQt::ControlButton) + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->prevItem(); + d->anchorItem = d->currItem; + tmp->repaint(); + d->currItem->repaint(); + + item = d->currItem; + } + else if (e->state() & TQt::ShiftButton) + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->prevItem(); + tmp->repaint(); + + // if the anchor is ahead of us, move forward preserving + // the previously selected item. otherwise unselect the + // previously selected item + if (anchorIsBehind()) + tmp->setSelected(false, false); + + d->currItem->setSelected(true, false); + + item = d->currItem; + } + else + { + IconItem* tmp = d->currItem; + d->currItem = d->currItem->prevItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + tmp->repaint(); + + item = d->currItem; + } + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + case Key_Up: + { + IconItem *item = 0; + + if (d->currItem) + { + int x = d->currItem->x() + itemRect().width()/2; + int y = d->currItem->y() - d->spacing*2; + + IconItem *it = 0; + + while (!it && y > 0) + { + it = findItem(TQPoint(x,y)); + y -= d->spacing * 2; + } + + if (it) + { + if (e->state() & TQt::ControlButton) + { + IconItem* tmp = d->currItem; + d->currItem = it; + d->anchorItem = it; + tmp->repaint(); + d->currItem->repaint(); + + item = d->currItem; + } + else if (e->state() & TQt::ShiftButton) + { + IconItem* tmp = d->currItem; + d->currItem = it; + tmp->repaint(); + + clearSelection(); + if (anchorIsBehind()) + { + for (IconItem* i = d->currItem; i; i = i->prevItem()) + { + i->setSelected(true, false); + if (i == d->anchorItem) + break; + } + } + else + { + for (IconItem* i = d->currItem; i; i = i->nextItem()) + { + i->setSelected(true, false); + if (i == d->anchorItem) + break; + } + } + + item = d->currItem; + } + else + { + IconItem* tmp = d->currItem; + d->currItem = it; + d->anchorItem = it; + d->currItem->setSelected(true, true); + tmp->repaint(); + + item = d->currItem; + } + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + case Key_Down: + { + IconItem *item = 0; + + if (d->currItem) + { + int x = d->currItem->x() + itemRect().width()/2; + int y = d->currItem->y() + itemRect().height() + d->spacing*2; + + IconItem *it = 0; + + while (!it && y < contentsHeight()) + { + it = findItem(TQPoint(x,y)); + y += d->spacing * 2; + } + + if (it) + { + if (e->state() & TQt::ControlButton) + { + IconItem* tmp = d->currItem; + d->currItem = it; + d->anchorItem = it; + tmp->repaint(); + d->currItem->repaint(); + + item = d->currItem; + } + else if (e->state() & TQt::ShiftButton) + { + IconItem* tmp = d->currItem; + d->currItem = it; + tmp->repaint(); + + clearSelection(); + if (anchorIsBehind()) + { + for (IconItem* i = d->currItem; i; i = i->prevItem()) + { + i->setSelected(true, false); + if (i == d->anchorItem) + break; + } + } + else + { + for (IconItem* i = d->currItem; i; i = i->nextItem()) + { + i->setSelected(true, false); + if (i == d->anchorItem) + break; + } + } + + item = d->currItem; + } + else + { + IconItem* tmp = d->currItem; + d->currItem = it; + d->anchorItem = it; + d->currItem->setSelected(true, true); + tmp->repaint(); + + item = d->currItem; + } + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + case Key_Next: + { + IconItem *item = 0; + + if (d->currItem) + { + TQRect r( 0, d->currItem->y() + visibleHeight(), + contentsWidth(), visibleHeight() ); + IconItem *ni = findFirstVisibleItem(r, false); + + if (!ni) + { + r = TQRect( 0, d->currItem->y() + itemRect().height(), + contentsWidth(), contentsHeight() ); + ni = findLastVisibleItem(r, false); + } + + if (ni) + { + IconItem* tmp = d->currItem; + d->currItem = ni; + d->anchorItem = ni; + item = ni; + tmp->repaint(); + d->currItem->setSelected(true, true); + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + case Key_Prior: + { + IconItem *item = 0; + + if (d->currItem) + { + TQRect r(0, d->currItem->y() - visibleHeight(), + contentsWidth(), visibleHeight() ); + + IconItem *ni = findFirstVisibleItem(r, false); + + if (!ni) + { + r = TQRect( 0, 0, contentsWidth(), d->currItem->y() ); + ni = findFirstVisibleItem(r, false); + } + + if (ni) + { + IconItem* tmp = d->currItem; + d->currItem = ni; + d->anchorItem = ni; + item = ni; + tmp->repaint(); + d->currItem->setSelected(true, true); + } + } + else + { + d->currItem = firstItem(); + d->anchorItem = d->currItem; + d->currItem->setSelected(true, true); + item = d->currItem; + } + + ensureItemVisible(item); + handled = true; + break; + } + + // Key_Space is used as a global shortcut in DigikamApp. + // Ctrl+Space comes through, Shift+Space is filtered out. + case Key_Space: + { + if (d->currItem) + { + if ( (e->state() & TQt::ControlButton) || (e->state() & TQt::ShiftButton) ) + { + d->currItem->setSelected(!d->currItem->isSelected(), false); + } + else + { + if (!d->currItem->isSelected()) + d->currItem->setSelected(true, true); + } + handled = true; + } + break; + } + + case Key_Menu: + { + if (d->currItem) + { + if (!d->currItem->isSelected()) + d->currItem->setSelected(true, false); + + ensureItemVisible(d->currItem); + + TQRect r(itemRect()); + int w = r.width(); + int h = r.height(); + TQPoint p(d->currItem->x() + w / 2, d->currItem->y() + h / 2); + + emit signalRightButtonClicked(d->currItem, mapToGlobal(contentsToViewport(p))); + } + break; + } + + default: + break; + } + + if (!handled) + { + e->ignore(); + } + else + { + emit signalSelectionChanged(); + viewport()->update(); + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + } +} + +bool IconView::anchorIsBehind() const +{ + if (!d->anchorItem || !d->currItem) + return false; + + for (IconItem* it = d->anchorItem; it; it = it->nextItem()) + { + if (it == d->currItem) + return true; + } + + return false; +} + + +void IconView::startDrag() +{ +} + +void IconView::ensureItemVisible(IconItem *item) +{ + if ( !item ) + return; + + if ( item->y() == firstItem()->y() ) + { + TQRect r(itemRect()); + int w = r.width(); + ensureVisible( item->x() + w / 2, 0, w/2+1, 0 ); + } + else + { + TQRect r(itemRect()); + int w = r.width(); + int h = r.height(); + ensureVisible( item->x() + w / 2, item->y() + h / 2, + w / 2 + 1, h / 2 + 1 ); + } +} + +IconItem* IconView::findFirstVisibleItem(bool useThumbnailRect) const +{ + TQRect r(contentsX(), contentsY(), visibleWidth(), visibleHeight()); + return findFirstVisibleItem(r, useThumbnailRect); +} + +IconItem* IconView::findLastVisibleItem(bool useThumbnailRect) const +{ + TQRect r(contentsX(), contentsY(), visibleWidth(), visibleHeight()); + return findLastVisibleItem(r, useThumbnailRect); +} + +IconItem* IconView::findFirstVisibleItem(const TQRect& r, bool useThumbnailRect) const +{ + IconViewPriv::ItemContainer *c = d->firstContainer; + bool alreadyIntersected = false; + IconItem* i = 0; + for ( ; c; c = c->next ) + { + if ( c->rect.intersects( r ) ) + { + alreadyIntersected = true; + for (TQValueList::iterator it = c->items.begin(); + it != c->items.end(); ++it) + { + IconItem *item = *it; + + // if useThumbnailRect, we only check for the clickToOpenRect, which is the thumbnail, + // otherwise, we take the whole item rect + if ( r.intersects( useThumbnailRect ? item->clickToOpenRect() : item->rect() ) ) + { + if ( !i ) + { + i = item; + } + else + { + TQRect r2 = item->rect(); + TQRect r3 = i->rect(); + if ( r2.y() < r3.y() ) + i = item; + else if ( r2.y() == r3.y() && + r2.x() < r3.x() ) + i = item; + } + } + } + } + else + { + if ( alreadyIntersected ) + break; + } + } + + return i; +} + +IconItem* IconView::findLastVisibleItem(const TQRect& r, bool useThumbnailRect) const +{ + IconViewPriv::ItemContainer *c = d->firstContainer; + IconItem *i = 0; + bool alreadyIntersected = false; + for ( ; c; c = c->next ) + { + if ( c->rect.intersects( r ) ) + { + alreadyIntersected = true; + for (TQValueList::iterator it = c->items.begin(); + it != c->items.end(); ++it) + { + IconItem *item = *it; + + if ( r.intersects( useThumbnailRect ? item->clickToOpenRect() : item->rect() ) ) + { + if ( !i ) + { + i = item; + } + else + { + TQRect r2 = item->rect(); + TQRect r3 = i->rect(); + if ( r2.y() > r3.y() ) + i = item; + else if ( r2.y() == r3.y() && + r2.x() > r3.x() ) + i = item; + } + } + } + } + else { + if ( alreadyIntersected ) + break; + } + } + + return i; +} + +void IconView::drawFrameRaised(TQPainter* p) +{ + TQRect r = frameRect(); + int lwidth = lineWidth(); + + const TQColorGroup & g = colorGroup(); + + qDrawShadeRect( p, r, g, false, lwidth, + midLineWidth() ); +} + +void IconView::drawFrameSunken(TQPainter* p) +{ + TQRect r = frameRect(); + int lwidth = lineWidth(); + + const TQColorGroup & g = colorGroup(); + + qDrawShadeRect( p, r, g, true, lwidth, + midLineWidth() ); +} + +void IconView::setEnableToolTips(bool val) +{ + d->showTips = val; + if (!val) + { + d->toolTipItem = 0; + d->toolTipTimer->stop(); + slotToolTip(); + } +} + +void IconView::slotToolTip() +{ + emit signalShowToolTip(d->toolTipItem); +} + +void IconView::itemClickedToOpen(IconItem* item) +{ + if (!item) + return; + + IconItem* prevCurrItem = d->currItem; + d->currItem = item; + d->anchorItem = item; + + if (prevCurrItem) + prevCurrItem->repaint(); + + item->setSelected(true); + emit signalDoubleClicked(item); +} + +int IconView::cmpItems(const void *n1, const void *n2) +{ + if ( !n1 || !n2 ) + return 0; + + IconViewPriv::SortableItem *i1 = (IconViewPriv::SortableItem *)n1; + IconViewPriv::SortableItem *i2 = (IconViewPriv::SortableItem *)n2; + + return i1->group->compare( i2->group ); +} + +} // namespace Digikam + diff --git a/src/digikam/iconview.h b/src/digikam/iconview.h new file mode 100644 index 00000000..59a4b921 --- /dev/null +++ b/src/digikam/iconview.h @@ -0,0 +1,170 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-24 + * Description : icon view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICONVIEW_H +#define ICONVIEW_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQPainter; +class TQMouseEvent; +class TQPaintEvent; +class TQDropEvent; +class TQPoint; + +namespace Digikam +{ + +class IconItem; +class IconGroupItem; +class IconViewPriv; + +class DIGIKAM_EXPORT IconView : public TQScrollView +{ + TQ_OBJECT + +public: + + IconView(TQWidget* parent=0, const char* name=0); + virtual ~IconView(); + + IconGroupItem* firstGroup() const; + IconGroupItem* lastGroup() const; + IconGroupItem* findGroup(const TQPoint& pos); + + IconItem* firstItem() const; + IconItem* lastItem() const; + IconItem* currentItem() const; + IconItem* findItem(const TQPoint& pos); + + void setCurrentItem(IconItem* item); + + int count() const; + int countSelected() const; + int groupCount() const; + + virtual void clear(bool update=true); + void sort(); + + void clearSelection(); + void selectAll(); + void invertSelection(); + + void selectItem(IconItem* item, bool select); + + /** Define the item which is visible after changing an album + (applies both to physical and virtual albums, like tags and date view). */ + void setStoredVisibleItem(IconItem *item); + + void triggerRearrangement(); + + void insertGroup(IconGroupItem* group); + void takeGroup(IconGroupItem* group); + + void insertItem(IconItem* item); + void takeItem(IconItem* item); + + void ensureItemVisible(IconItem *item); + IconItem* findFirstVisibleItem(const TQRect& r, bool useThumbnailRect = true) const; + IconItem* findLastVisibleItem(const TQRect& r, bool useThumbnailRect = true) const; + IconItem* findFirstVisibleItem(bool useThumbnailRect = true) const; + IconItem* findLastVisibleItem(bool useThumbnailRect = true) const; + + virtual TQRect itemRect() const; + virtual TQRect bannerRect() const; + + TQRect contentsRectToViewport(const TQRect& r) const; + + void setEnableToolTips(bool val); + + void setDelayedRearrangement(bool delayed); + +protected: + + virtual void viewportPaintEvent(TQPaintEvent* pe); + virtual void resizeEvent(TQResizeEvent* e); + virtual void contentsMousePressEvent(TQMouseEvent* e); + virtual void contentsMouseMoveEvent(TQMouseEvent* e); + virtual void contentsMouseReleaseEvent(TQMouseEvent* e); + virtual void contentsMouseDoubleClickEvent(TQMouseEvent *e); + virtual void contentsWheelEvent(TQWheelEvent* e); + virtual void leaveEvent(TQEvent *e); + virtual void focusOutEvent(TQFocusEvent* e); + virtual void keyPressEvent(TQKeyEvent* e); + + virtual void startDrag(); + + void drawFrameRaised(TQPainter* p); + void drawFrameSunken(TQPainter* p); + + virtual bool acceptToolTip(IconItem* , const TQPoint&); + +private: + + bool arrangeItems(); + void rebuildContainers(); + void appendContainer(); + void deleteContainers(); + + void drawRubber(TQPainter* p); + + void itemClickedToOpen(IconItem* item); + + bool anchorIsBehind() const; + + void startRearrangeTimer(); + + static int cmpItems(const void *n1, const void *n2); + +signals: + + void signalSelectionChanged(); + void signalRightButtonClicked(IconItem* item, const TQPoint& pos); + void signalRightButtonClicked(const TQPoint& pos); + void signalDoubleClicked(IconItem* item); + void signalReturnPressed(IconItem* item); + void signalShowToolTip(IconItem* item); + +public slots: + + void slotRearrange(); + +private slots: + + void slotToolTip(); + +private: + + IconViewPriv* d; +}; + +} // namespace Digikam + +#endif /* ICONVIEW_H */ diff --git a/src/digikam/imageattributeswatch.cpp b/src/digikam/imageattributeswatch.cpp new file mode 100644 index 00000000..a91e4dea --- /dev/null +++ b/src/digikam/imageattributeswatch.cpp @@ -0,0 +1,89 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-04 + * Description : Watch image attributes + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "imageattributeswatch.h" +#include "imageattributeswatch.moc" + +namespace Digikam +{ + +ImageAttributesWatch *ImageAttributesWatch::m_instance = 0; + +ImageAttributesWatch::~ImageAttributesWatch() +{ + m_instance = 0; +} + +void ImageAttributesWatch::cleanUp() +{ + delete m_instance; + m_instance = 0; +} + +void ImageAttributesWatch::shutDown() +{ + if (m_instance) + m_instance->disconnect(0, 0, 0); +} + +ImageAttributesWatch *ImageAttributesWatch::instance() +{ + if (!m_instance) + m_instance = new ImageAttributesWatch; + return m_instance; +} + +void ImageAttributesWatch::imageTagsChanged(TQ_LLONG imageId) +{ + emit signalImageTagsChanged(imageId); +} + +void ImageAttributesWatch::imagesChanged(int albumId) +{ + emit signalImagesChanged(albumId); +} + +void ImageAttributesWatch::imageRatingChanged(TQ_LLONG imageId) +{ + emit signalImageRatingChanged(imageId); +} + +void ImageAttributesWatch::imageDateChanged(TQ_LLONG imageId) +{ + emit signalImageDateChanged(imageId); +} + +void ImageAttributesWatch::imageCaptionChanged(TQ_LLONG imageId) +{ + emit signalImageCaptionChanged(imageId); +} + +void ImageAttributesWatch::fileMetadataChanged(const KURL &url) +{ + emit signalFileMetadataChanged(url); +} + +} // namespace Digikam + diff --git a/src/digikam/imageattributeswatch.h b/src/digikam/imageattributeswatch.h new file mode 100644 index 00000000..7d377609 --- /dev/null +++ b/src/digikam/imageattributeswatch.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-05-04 + * Description : Watch image attributes + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEATTRIBUTESWATCH_H +#define IMAGEATTRIBUTESWATCH_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +class ImageAttributesWatch : public TQObject +{ + TQ_OBJECT + +public: + + static ImageAttributesWatch *instance(); + static void cleanUp(); + static void shutDown(); + + void imageTagsChanged(TQ_LLONG imageId); + void imagesChanged(int albumId); + + void imageRatingChanged(TQ_LLONG imageId); + void imageDateChanged(TQ_LLONG imageId); + void imageCaptionChanged(TQ_LLONG imageId); + + void fileMetadataChanged(const KURL &url); + +signals: + + /** Indicates that tags have been assigned or removed + for image with given imageId. + There is no guarantee that the tags were actually changed. + This signal, the signal below, or both may be sent. + */ + void signalImageTagsChanged(TQ_LLONG imageId); + + /** + Indicates that images in the given album id may have changed their tags. + This signal, the signal above, or both may be sent. + */ + void signalImagesChanged(int albumId); + + /** These signals indicated that the rating, data or caption + of the image with given imageId was set. + There is no guarantee that it actually changed. + */ + void signalImageRatingChanged(TQ_LLONG imageId); + void signalImageDateChanged(TQ_LLONG imageId); + void signalImageCaptionChanged(TQ_LLONG imageId); + + /** + Indicates that the metadata if the given file + has been changed (a write operation on the file on disk). + Usually, the database is updated accordingly, so then this + signal is sent in combination with one or more of the above signals. + */ + void signalFileMetadataChanged(const KURL &url); + +protected: + + ~ImageAttributesWatch(); + + static ImageAttributesWatch *m_instance; +}; + +} // namespace Digikam + +#endif // IMAGEATTRIBUTESWATCH_H diff --git a/src/digikam/imageinfo.cpp b/src/digikam/imageinfo.cpp new file mode 100644 index 00000000..f32d3062 --- /dev/null +++ b/src/digikam/imageinfo.cpp @@ -0,0 +1,347 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : image informations container. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file imageinfo.cpp */ + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumdb.h" +#include "albummanager.h" +#include "dio.h" +#include "imageinfo.h" +#include "imageattributeswatch.h" + +namespace Digikam +{ + +AlbumManager* ImageInfo::m_man = 0; + +ImageInfo::ImageInfo() + : m_ID(-1), m_albumID(-1), m_size(0), m_viewitem(0) +{ +} + +ImageInfo::ImageInfo(TQ_LLONG ID, int albumID, const TQString& name, + const TQDateTime& datetime, size_t size, + const TQSize& dims) + : m_ID(ID), m_albumID(albumID), m_name(name), m_datetime(datetime), + m_size(size), m_dims(dims), m_viewitem(0) +{ + if (!m_man) + { + m_man = AlbumManager::instance(); + } +} + +ImageInfo::ImageInfo(TQ_LLONG ID) + : m_ID(ID), m_size(0), m_viewitem(0) +{ + if (!m_man) + { + m_man = AlbumManager::instance(); + } + AlbumDB* db = m_man->albumDB(); + + // retrieve these now, the rest on demand + m_albumID = db->getItemAlbum(m_ID); + m_name = db->getItemName(m_ID); +} + +ImageInfo::~ImageInfo() +{ +} + +bool ImageInfo::isNull() const +{ + return m_ID != -1; +} + +TQString ImageInfo::name() const +{ + return m_name; +} + +void ImageInfo::setName(const TQString& newName) +{ + /* + KURL src = kurlForKIO(); + KURL dst = src.upURL(); + dst.addPath(newName); + + if (!DIO::renameFile(src, dst)) + return false; + + PAlbum* a = album(); + if (!a) + { + DWarning() << "No album found for ID: " << m_albumID << endl; + return false; + } + */ + + m_name = newName; +} + +size_t ImageInfo::fileSize() const +{ + if (m_size == 0) + { + TQFileInfo info(filePath()); + m_size = info.size(); + } + return m_size; +} + +TQDateTime ImageInfo::dateTime() const +{ + if (!m_datetime.isValid()) + { + AlbumDB* db = m_man->albumDB(); + m_datetime = db->getItemDate(m_ID); + } + return m_datetime; +} + +TQDateTime ImageInfo::modDateTime() const +{ + if (!m_modDatetime.isValid()) + { + TQFileInfo fileInfo(filePath()); + m_modDatetime = fileInfo.lastModified(); + } + + return m_modDatetime; +} + +TQSize ImageInfo::dimensions() const +{ + return m_dims; +} + +TQ_LLONG ImageInfo::id() const +{ + return m_ID; +} + +int ImageInfo::albumID() const +{ + return m_albumID; +} + +PAlbum* ImageInfo::album() const +{ + return m_man->findPAlbum(m_albumID); +} + +KURL ImageInfo::kurl() const +{ + PAlbum* a = album(); + if (!a) + { + DWarning() << "No album found for ID: " << m_albumID << endl; + return KURL(); + } + + KURL u(m_man->getLibraryPath()); + u.addPath(a->url()); + u.addPath(m_name); + return u; +} + +TQString ImageInfo::filePath() const +{ + PAlbum* a = album(); + if (!a) + { + DWarning() << "No album found for ID: " << m_albumID << endl; + return TQString(); + } + + TQString path = m_man->getLibraryPath(); + path += a->url() + '/' + m_name; + return path; +} + +KURL ImageInfo::kurlForKIO() const +{ + PAlbum* a = album(); + if (!a) + { + DWarning() << "No album found for ID: " << m_albumID << endl; + return KURL(); + } + + KURL u(a->kurl()); + u.addPath(m_name); + return u; +} + +void ImageInfo::setViewItem(void *d) +{ + m_viewitem = d; +} + +void* ImageInfo::getViewItem() const +{ + return m_viewitem; +} + +void ImageInfo::setDateTime(const TQDateTime& dateTime) +{ + if (dateTime.isValid()) + { + AlbumDB* db = m_man->albumDB(); + db->setItemDate(m_ID, dateTime); + m_datetime = dateTime; + ImageAttributesWatch::instance()->imageDateChanged(m_ID); + } +} + +void ImageInfo::setCaption(const TQString& caption) +{ + AlbumDB* db = m_man->albumDB(); + db->setItemCaption(m_ID, caption); + ImageAttributesWatch::instance()->imageCaptionChanged(m_ID); +} + +TQString ImageInfo::caption() const +{ + AlbumDB* db = m_man->albumDB(); + return db->getItemCaption(m_ID); +} + +TQStringList ImageInfo::tagNames() const +{ + AlbumDB* db = m_man->albumDB(); + return db->getItemTagNames(m_ID); +} + +TQStringList ImageInfo::tagPaths(bool leadingSlash) const +{ + TQStringList tagPaths; + + AlbumDB* db = m_man->albumDB(); + IntList tagIDs = db->getItemTagIDs(m_ID); + for (IntList::iterator it = tagIDs.begin(); it != tagIDs.end(); ++it) + { + TAlbum* ta = m_man->findTAlbum(*it); + if (ta) + { + tagPaths.append(ta->tagPath(leadingSlash)); + } + } + + return tagPaths; +} + +TQValueList ImageInfo::tagIDs() const +{ + AlbumDB* db = m_man->albumDB(); + return db->getItemTagIDs(m_ID); +} + +void ImageInfo::setTag(int tagID) +{ + AlbumDB* db = m_man->albumDB(); + db->addItemTag(m_ID, tagID); + ImageAttributesWatch::instance()->imageTagsChanged(m_ID); +} + +void ImageInfo::removeTag(int tagID) +{ + AlbumDB* db = m_man->albumDB(); + db->removeItemTag(m_ID, tagID); + ImageAttributesWatch::instance()->imageTagsChanged(m_ID); +} + +void ImageInfo::removeAllTags() +{ + AlbumDB *db = m_man->albumDB(); + db->removeItemAllTags(m_ID); + ImageAttributesWatch::instance()->imageTagsChanged(m_ID); +} + +void ImageInfo::addTagPaths(const TQStringList &tagPaths) +{ + AlbumDB *db = m_man->albumDB(); + AlbumList list = m_man->findOrCreateTAlbums(tagPaths); + for (AlbumList::iterator it = list.begin(); it != list.end(); ++it) + db->addItemTag(m_ID, (*it)->id()); + ImageAttributesWatch::instance()->imageTagsChanged(m_ID); +} + + +int ImageInfo::rating() const +{ + AlbumDB* db = m_man->albumDB(); + return db->getItemRating(m_ID); +} + +void ImageInfo::setRating(int value) +{ + AlbumDB* db = m_man->albumDB(); + db->setItemRating(m_ID, value); + ImageAttributesWatch::instance()->imageRatingChanged(m_ID); +} + +ImageInfo ImageInfo::copyItem(PAlbum *dstAlbum, const TQString &dstFileName) +{ + DDebug() << "ImageInfo::copyItem " << m_albumID << " " << m_name << " to " + << dstAlbum->id() << " " << dstFileName << endl; + + if (dstAlbum->id() == m_albumID && dstFileName == m_name) + return (*this); + + AlbumDB* db = m_man->albumDB(); + int id = db->copyItem(m_albumID, m_name, dstAlbum->id(), dstFileName); + + if (id == -1) + return ImageInfo(); + + ImageInfo info; + info.m_ID = id; + info.m_albumID = dstAlbum->id(); + info.m_name = dstFileName; + // set size and datetime + info.refresh(); + // m_dims is not set, m_viewItem is left 0 + return info; +} + +void ImageInfo::refresh() +{ + m_datetime = m_man->albumDB()->getItemDate(m_ID); + + TQFileInfo fileInfo(filePath()); + m_size = fileInfo.size(); + m_modDatetime = fileInfo.lastModified(); +} + +} // namespace Digikam diff --git a/src/digikam/imageinfo.h b/src/digikam/imageinfo.h new file mode 100644 index 00000000..3427b729 --- /dev/null +++ b/src/digikam/imageinfo.h @@ -0,0 +1,280 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : image informations container. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file imageinfo.h */ + +#ifndef IMAGEINFO_H +#define IMAGEINFO_H + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +class PAlbum; +class AlbumManager; + +/** + * This is an abstraction of a file entity on the disk. + * + * This is an abstraction of a file entity on the disk and is uniquely + * identified by an Album identifier (PAlbum in which its located) and its + * name. Additional methods are provided for accessing/modifying: + * - datetime + * - caption + * - filesize + * - tags + * - url (ImageInfo::kurl()) -> Note: accessing this will give you the + * standard KDE file representation. + * (for eg: file:///home/johndoe/Pictures/ItalyPictures/Rome-2004/file001.jpeg) + */ +class ImageInfo +{ +public: + + /** + * Constructor + * Creates a null image info + */ + ImageInfo(); + + /** + * Constructor + * @param ID unique ID for this image + * @param albumID id of the PAlbum to which this item belongs + * @param name name of the image + * @param datetime datetime of the image + * @param size filesize of the image + * @param dims dimensions of the image + */ + ImageInfo(TQ_LLONG ID, int albumID, const TQString& name, + const TQDateTime& datetime, size_t size, + const TQSize& dims=TQSize()); + + /** + * Constructor + * @param ID unique ID for this image + */ + ImageInfo(TQ_LLONG ID); + + /** + * Destructor + */ + ~ImageInfo(); + + /** + * Returns if this objects contains valid data + */ + bool isNull() const; + + /** + * @return the name of the image + */ + TQString name() const; + + /** + * Set a new name for the image. + * @param newName new name for the image + */ + void setName(const TQString& newName); + + /** + * @return the datetime of the image + */ + TQDateTime dateTime() const; + + /** + * @return the modification datetime of the image + */ + TQDateTime modDateTime() const; + + /** + * @return the filesize of the image + */ + size_t fileSize() const; + + /** + * @return the dimensions of the image (valid only if dimensions + * have been requested) + */ + TQSize dimensions() const; + + /** + * @return the standard KDE url with file protocol. The path for + * the url is the absolute path of the image. + * DEPRECATED: don't use this. if you need only the file path, then + * use filePath(). And if you need the digikam "proper" kurl, use + * kurlForKIO(). At some point kurl and kurlForKIO() will be merged + * together + */ + KURL kurl() const; + + /** + * @return the absolute file path of the image + */ + TQString filePath() const; + + /** + * @return the kurl for TDEIO or rather DIO. Use this instead of kurl() + * for metadata preserving file IO operations. Also, this method needs + * to be merged with kurl() + */ + KURL kurlForKIO() const; + + /** + * @return the unique image id for this item + */ + TQ_LLONG id() const; + + /** + * @return the id of the PAlbum to which this item belongs + */ + int albumID() const; + + /** + * @return the PAlbum to which this item belongs + */ + PAlbum* album() const; + + /** + * @return the caption for this item + */ + TQString caption() const; + + /** + * Set the caption (writes it to database) + * @param caption the new caption for this item + */ + void setCaption(const TQString& caption); + + /** + * Set the date and time (write it to database) + * @param dateTime the new date and time. + */ + void setDateTime(const TQDateTime& dateTime); + + /** + * @return a list of names of all tags assigned to this item + * @see tagPaths + */ + TQStringList tagNames() const; + + /** + * @return a list of complete path of all tags assigned to this item. The + * complete path for a tag is a '/' separated string of its hierarchy + * @see tagPaths + */ + TQStringList tagPaths(bool leadingSlash = true) const; + + /** + * @return a list of IDs of tags assigned to this item + * @see tagNames + * @see tagPaths + * @see Album::id() + */ + TQValueList tagIDs() const; + + /** + * Adds a tag to the item (writes it to database) + * @param tagID the ID of the tag to add + */ + void setTag(int tagID); + + /** + * Adds tags in the list to the item. + * Tags are created if they do not yet exist + */ + void addTagPaths(const TQStringList &tagPaths); + + /** + * Remove a tag from the item (removes it from database) + * @param tagID the ID of the tag to remove + */ + void removeTag(int tagID); + + /** + * Remove all tags from the item (removes it from database) + */ + void removeAllTags(); + + int rating() const; + + void setRating(int value); + + /** + * Copy database information of this item to a newly created item + * @param dstAlbum destination album + * @param dstFileName new filename + * @return an ImageInfo object of the new item + */ + ImageInfo copyItem(PAlbum *dstAlbum, const TQString &dstFileName); + + /** + * Assign a viewitem for this item. This is useful when a view has a + * corresponding viewitem for this item and wants to access the viewitem, given + * this item + * @see getViewItem() + */ + void setViewItem(void *d); + + /** + * Returns the viewitem associated with this item a viewitem for this item. + * @see setViewItem() + */ + void* getViewItem() const; + + /** + * refresh the properties of the imageinfo. it reads the database + * again for getting the updated date and stats the file to get + * the updated size + */ + void refresh(); + +private: + + TQ_LLONG m_ID; + int m_albumID; + TQString m_name; + mutable TQDateTime m_datetime; + mutable TQDateTime m_modDatetime; + mutable size_t m_size; + TQSize m_dims; + void* m_viewitem; + static AlbumManager* m_man; +}; + +typedef TQPtrList ImageInfoList; +typedef TQPtrListIterator ImageInfoListIterator; + +} // namespace Digikam + +#endif /* IMAGEINFO_H */ diff --git a/src/digikam/imagepreviewview.cpp b/src/digikam/imagepreviewview.cpp new file mode 100644 index 00000000..f636f685 --- /dev/null +++ b/src/digikam/imagepreviewview.cpp @@ -0,0 +1,686 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-12 + * Description : a embedded view to show the image preview widget. + * + * Copyright (C) 2006-2009 Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKipi includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "albumdb.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumwidgetstack.h" +#include "imageinfo.h" +#include "dmetadata.h" +#include "dpopupmenu.h" +#include "metadatahub.h" +#include "paniconwidget.h" +#include "previewloadthread.h" +#include "loadingdescription.h" +#include "tagspopupmenu.h" +#include "ratingpopupmenu.h" +#include "themeengine.h" +#include "imagepreviewview.h" +#include "imagepreviewview.moc" + +namespace Digikam +{ + +class ImagePreviewViewPriv +{ +public: + + ImagePreviewViewPriv() + { + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + previewThread = 0; + previewPreloadThread = 0; + imageInfo = 0; + parent = 0; + hasPrev = false; + hasNext = false; + loadFullImageSize = false; + currentFitWindowZoom = 0; + previewSize = 1024; + } + + bool hasPrev; + bool hasNext; + bool loadFullImageSize; + + int previewSize; + + double currentFitWindowZoom; + + TQString path; + TQString nextPath; + TQString previousPath; + + TQToolButton *cornerButton; + + TDEPopupFrame *panIconPopup; + + PanIconWidget *panIconWidget; + + DImg preview; + + ImageInfo *imageInfo; + + PreviewLoadThread *previewThread; + PreviewLoadThread *previewPreloadThread; + + AlbumWidgetStack *parent; +}; + +ImagePreviewView::ImagePreviewView(AlbumWidgetStack *parent) + : PreviewWidget(parent) +{ + d = new ImagePreviewViewPriv; + d->parent = parent; + + // get preview size from screen size, but limit from VGA to WQXGA + d->previewSize = TQMAX(TDEApplication::desktop()->height(), + TDEApplication::desktop()->width()); + if (d->previewSize < 640) + d->previewSize = 640; + if (d->previewSize > 2560) + d->previewSize = 2560; + + setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image to a region")); + setCornerWidget(d->cornerButton); + + // ------------------------------------------------------------ + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(this, TQ_SIGNAL(signalShowNextImage()), + this, TQ_SIGNAL(signalNextItem())); + + connect(this, TQ_SIGNAL(signalShowPrevImage()), + this, TQ_SIGNAL(signalPrevItem())); + + connect(this, TQ_SIGNAL(signalRightButtonClicked()), + this, TQ_SLOT(slotContextMenu())); + + connect(this, TQ_SIGNAL(signalLeftButtonClicked()), + this, TQ_SIGNAL(signalBack2Album())); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); + + // ------------------------------------------------------------ + + slotReset(); +} + +ImagePreviewView::~ImagePreviewView() +{ + delete d->previewThread; + delete d->previewPreloadThread; + delete d; +} + +void ImagePreviewView::setLoadFullImageSize(bool b) +{ + d->loadFullImageSize = b; + reload(); +} + +void ImagePreviewView::setImage(const DImg& image) +{ + d->preview = image; + + updateZoomAndSize(true); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); +} + +DImg& ImagePreviewView::getImage() const +{ + return d->preview; +} + +void ImagePreviewView::reload() +{ + // cache is cleaned from AlbumIconView::refreshItems + setImagePath(d->path); +} + +void ImagePreviewView::setPreviousNextPaths(const TQString& previous, const TQString &next) +{ + d->nextPath = next; + d->previousPath = previous; +} + +void ImagePreviewView::setImagePath(const TQString& path) +{ + setCursor( KCursor::waitCursor() ); + + d->path = path; + d->nextPath = TQString(); + d->previousPath = TQString(); + + if (d->path.isEmpty()) + { + slotReset(); + unsetCursor(); + return; + } + + if (!d->previewThread) + { + d->previewThread = new PreviewLoadThread(); + connect(d->previewThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), + this, TQ_SLOT(slotGotImagePreview(const LoadingDescription &, const DImg&))); + } + if (!d->previewPreloadThread) + { + d->previewPreloadThread = new PreviewLoadThread(); + connect(d->previewPreloadThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), + this, TQ_SLOT(slotNextPreload())); + } + + if (d->loadFullImageSize) + d->previewThread->loadHighQuality(LoadingDescription(path, 0, AlbumSettings::instance()->getExifRotate())); + else + d->previewThread->load(LoadingDescription(path, d->previewSize, AlbumSettings::instance()->getExifRotate())); +} + +void ImagePreviewView::slotGotImagePreview(const LoadingDescription &description, const DImg& preview) +{ + if (description.filePath != d->path) + return; + + if (preview.isNull()) + { + d->parent->setPreviewMode(AlbumWidgetStack::PreviewImageMode); + TQPixmap pix(visibleWidth(), visibleHeight()); + pix.fill(ThemeEngine::instance()->baseColor()); + TQPainter p(&pix); + TQFileInfo info(d->path); + p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); + p.drawText(0, 0, pix.width(), pix.height(), + TQt::AlignCenter|TQt::WordBreak, + i18n("Cannot display preview for\n\"%1\"") + .arg(info.fileName())); + p.end(); + // three copies - but the image is small + setImage(DImg(pix.convertToImage())); + d->parent->previewLoaded(); + emit signalPreviewLoaded(false); + } + else + { + DImg img(preview); + if (AlbumSettings::instance()->getExifRotate()) + d->previewThread->exifRotate(img, description.filePath); + d->parent->setPreviewMode(AlbumWidgetStack::PreviewImageMode); + setImage(img); + d->parent->previewLoaded(); + emit signalPreviewLoaded(true); + } + + unsetCursor(); + slotNextPreload(); +} + +void ImagePreviewView::slotNextPreload() +{ + TQString loadPath; + if (!d->nextPath.isNull()) + { + loadPath = d->nextPath; + d->nextPath = TQString(); + } + else if (!d->previousPath.isNull()) + { + loadPath = d->previousPath; + d->previousPath = TQString(); + } + else + return; + + if (d->loadFullImageSize) + d->previewThread->loadHighQuality(LoadingDescription(loadPath, 0, + AlbumSettings::instance()->getExifRotate())); + else + d->previewPreloadThread->load(LoadingDescription(loadPath, d->previewSize, + AlbumSettings::instance()->getExifRotate())); +} + +void ImagePreviewView::setImageInfo(ImageInfo* info, ImageInfo *previous, ImageInfo *next) +{ + d->imageInfo = info; + d->hasPrev = previous; + d->hasNext = next; + + if (d->imageInfo) + setImagePath(info->filePath()); + else + setImagePath(); + + setPreviousNextPaths(previous ? previous->filePath() : TQString(), + next ? next->filePath() : TQString()); +} + +ImageInfo* ImagePreviewView::getImageInfo() const +{ + return d->imageInfo; +} + +void ImagePreviewView::slotContextMenu() +{ + RatingPopupMenu *ratingMenu = 0; + TagsPopupMenu *assignTagsMenu = 0; + TagsPopupMenu *removeTagsMenu = 0; + + if (!d->imageInfo) + return; + + //-- Open With Actions ------------------------------------ + + KURL url(d->imageInfo->kurl().path()); + KMimeType::Ptr mimePtr = KMimeType::findByURL(url, 0, true, true); + + TQValueVector serviceVector; + TDETrader::OfferList offers = TDETrader::self()->query(mimePtr->name(), "Type == 'Application'"); + + TQPopupMenu openWithMenu; + + TDETrader::OfferList::Iterator iter; + KService::Ptr ptr; + int index = 100; + + for( iter = offers.begin(); iter != offers.end(); ++iter ) + { + ptr = *iter; + openWithMenu.insertItem( ptr->pixmap(TDEIcon::Small), ptr->name(), index++); + serviceVector.push_back(ptr); + } + + //-- Navigate actions ------------------------------------------- + + DPopupMenu popmenu(this); + popmenu.insertItem(SmallIcon("back"), i18n("Back"), 10); + if (!d->hasPrev) popmenu.setItemEnabled(10, false); + + popmenu.insertItem(SmallIcon("forward"), i18n("Forward"), 11); + if (!d->hasNext) popmenu.setItemEnabled(11, false); + + popmenu.insertItem(SmallIcon("folder_image"), i18n("Back to Album"), 15); + + //-- Edit actions ----------------------------------------------- + + popmenu.insertSeparator(); + popmenu.insertItem(SmallIcon("slideshow"), i18n("SlideShow"), 16); + popmenu.insertItem(SmallIcon("editimage"), i18n("Edit..."), 12); + popmenu.insertItem(SmallIcon("lighttableadd"), i18n("Add to Light Table"), 17); + popmenu.insertItem(i18n("Open With"), &openWithMenu, 13); + + // Merge in the KIPI plugins actions ---------------------------- + + KIPI::PluginLoader* kipiPluginLoader = KIPI::PluginLoader::instance(); + KIPI::PluginLoader::PluginList pluginList = kipiPluginLoader->pluginList(); + + for (KIPI::PluginLoader::PluginList::const_iterator it = pluginList.begin(); + it != pluginList.end(); ++it) + { + KIPI::Plugin* plugin = (*it)->plugin(); + + if (plugin && (*it)->name() == "JPEGLossless") + { + DDebug() << "Found JPEGLossless plugin" << endl; + + TDEActionPtrList actionList = plugin->actions(); + + for (TDEActionPtrList::const_iterator iter = actionList.begin(); + iter != actionList.end(); ++iter) + { + TDEAction* action = *iter; + + if (TQString::fromLatin1(action->name()) + == TQString::fromLatin1("jpeglossless_rotate")) + { + action->plug(&popmenu); + } + } + } + } + + //-- Trash action ------------------------------------------- + + popmenu.insertSeparator(); + popmenu.insertItem(SmallIcon("edittrash"), i18n("Move to Trash"), 14); + + // Bulk assignment/removal of tags -------------------------- + + TQ_LLONG id = d->imageInfo->id(); + TQValueList idList; + idList.append(id); + + assignTagsMenu = new TagsPopupMenu(idList, 1000, TagsPopupMenu::ASSIGN); + removeTagsMenu = new TagsPopupMenu(idList, 2000, TagsPopupMenu::REMOVE); + + popmenu.insertSeparator(); + + popmenu.insertItem(i18n("Assign Tag"), assignTagsMenu); + int i = popmenu.insertItem(i18n("Remove Tag"), removeTagsMenu); + + connect(assignTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotAssignTag(int))); + + connect(removeTagsMenu, TQ_SIGNAL(signalTagActivated(int)), + this, TQ_SLOT(slotRemoveTag(int))); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + if (!db->hasTags( idList )) + popmenu.setItemEnabled(i, false); + + popmenu.insertSeparator(); + + // Assign Star Rating ------------------------------------------- + + ratingMenu = new RatingPopupMenu(); + + connect(ratingMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotAssignRating(int))); + + popmenu.insertItem(i18n("Assign Rating"), ratingMenu); + + // -------------------------------------------------------- + + int idm = popmenu.exec(TQCursor::pos()); + + switch(idm) + { + case 10: // Back + { + emit signalPrevItem(); + break; + } + + case 11: // Forward + { + emit signalNextItem(); + break; + } + + case 12: // Edit... + { + emit signalEditItem(); + break; + } + + case 14: // Move to trash + { + emit signalDeleteItem(); + break; + } + + case 15: // Back to album + { + emit signalBack2Album(); + break; + } + + case 16: // SlideShow + { + emit signalSlideShow(); + break; + } + + case 17: // Place onto Light Table + { + emit signalInsert2LightTable(); + break; + } + + default: + break; + } + + // Open With... + if (idm >= 100 && idm < 1000) + { + KService::Ptr imageServicePtr = serviceVector[idm-100]; + KRun::run(*imageServicePtr, url); + } + + serviceVector.clear(); + delete assignTagsMenu; + delete removeTagsMenu; + delete ratingMenu; +} + +void ImagePreviewView::slotAssignTag(int tagID) +{ + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setTag(tagID, true); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImagePreviewView::slotRemoveTag(int tagID) +{ + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setTag(tagID, false); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImagePreviewView::slotAssignRating(int rating) +{ + rating = TQMIN(5, TQMAX(0, rating)); + if (d->imageInfo) + { + MetadataHub hub; + hub.load(d->imageInfo); + hub.setRating(rating); + hub.write(d->imageInfo, MetadataHub::PartialWrite); + hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); + } +} + +void ImagePreviewView::slotThemeChanged() +{ + setBackgroundColor(ThemeEngine::instance()->baseColor()); +} + +void ImagePreviewView::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + PanIconWidget *pan = new PanIconWidget(d->panIconPopup); + pan->setImage(180, 120, getImage()); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / zoomFactor()), (int)(contentsY() / zoomFactor()), + (int)(visibleWidth() / zoomFactor()), (int)(visibleHeight() / zoomFactor())); + pan->setRegionSelection(r); + pan->setMouseFocus(); + + connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), + this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); + + connect(pan, TQ_SIGNAL(signalHiden()), + this, TQ_SLOT(slotPanIconHiden())); + + TQPoint g = mapToGlobal(viewport()->pos()); + g.setX(g.x()+ viewport()->size().width()); + g.setY(g.y()+ viewport()->size().height()); + d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(), + g.y() - d->panIconPopup->height())); + + pan->setCursorToLocalRegionSelectionCenter(); +} + +void ImagePreviewView::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void ImagePreviewView::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*zoomFactor()), (int)(r.y()*zoomFactor())); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void ImagePreviewView::zoomFactorChanged(double zoom) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); + + PreviewWidget::zoomFactorChanged(zoom); +} + +void ImagePreviewView::resizeEvent(TQResizeEvent* e) +{ + if (!e) return; + + TQScrollView::resizeEvent(e); + + if (!d->imageInfo) + d->cornerButton->hide(); + + updateZoomAndSize(false); +} + +void ImagePreviewView::updateZoomAndSize(bool alwaysFitToWindow) +{ + // Set zoom for fit-in-window as minimum, but dont scale up images + // that are smaller than the available space, only scale down. + double zoom = calcAutoZoomFactor(ZoomInOnly); + setZoomMin(zoom); + setZoomMax(zoom*12.0); + + // Is currently the zoom factor set to fit to window? Then set it again to fit the new size. + if (zoomFactor() < zoom || alwaysFitToWindow || zoomFactor() == d->currentFitWindowZoom) + { + setZoomFactor(zoom); + } + + // store which zoom factor means it is fit to window + d->currentFitWindowZoom = zoom; + + updateContentsSize(); +} + +int ImagePreviewView::previewWidth() +{ + return d->preview.width(); +} + +int ImagePreviewView::previewHeight() +{ + return d->preview.height(); +} + +bool ImagePreviewView::previewIsNull() +{ + return d->preview.isNull(); +} + +void ImagePreviewView::resetPreview() +{ + d->preview = DImg(); + d->path = TQString(); + d->imageInfo = 0; + + updateZoomAndSize(true); + emit signalPreviewLoaded(false); +} + +void ImagePreviewView::paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh) +{ + DImg img = d->preview.smoothScaleSection(sx, sy, sw, sh, tileSize(), tileSize()); + TQPixmap pix2 = img.convertToPixmap(); + bitBlt(pix, 0, 0, &pix2, 0, 0); +} + +} // NameSpace Digikam diff --git a/src/digikam/imagepreviewview.h b/src/digikam/imagepreviewview.h new file mode 100644 index 00000000..f0058806 --- /dev/null +++ b/src/digikam/imagepreviewview.h @@ -0,0 +1,115 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-12 + * Description : a embedded view to show the image preview widget. + * + * Copyright (C) 2006-2009 Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPREVIEWVIEW_H +#define IMAGEPREVIEWVIEW_H + +// TQt includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "previewwidget.h" +#include "digikam_export.h" + +class TQPixmap; + +namespace Digikam +{ + +class AlbumWidgetStack; +class LoadingDescription; +class ImageInfo; +class ImagePreviewViewPriv; + +class DIGIKAM_EXPORT ImagePreviewView : public PreviewWidget +{ + +TQ_OBJECT + +public: + + ImagePreviewView(AlbumWidgetStack *parent=0); + ~ImagePreviewView(); + + void setLoadFullImageSize(bool b); + + void setImage(const DImg& image); + DImg& getImage() const; + + void setImageInfo(ImageInfo* info=0, ImageInfo *previous=0, ImageInfo *next=0); + ImageInfo* getImageInfo() const; + + void reload(); + void setImagePath(const TQString& path=TQString()); + void setPreviousNextPaths(const TQString& previous, const TQString &next); + +signals: + + void signalNextItem(); + void signalPrevItem(); + void signalDeleteItem(); + void signalEditItem(); + void signalPreviewLoaded(bool success); + void signalBack2Album(); + void signalSlideShow(); + void signalInsert2LightTable(); + +protected: + + void resizeEvent(TQResizeEvent* e); + +private slots: + + void slotGotImagePreview(const LoadingDescription &loadingDescription, const DImg &image); + void slotNextPreload(); + void slotContextMenu(); + void slotAssignTag(int tagID); + void slotRemoveTag(int tagID); + void slotAssignRating(int rating); + void slotThemeChanged(); + void slotCornerButtonPressed(); + void slotPanIconSelectionMoved(const TQRect&, bool); + void slotPanIconHiden(); + +private: + + int previewWidth(); + int previewHeight(); + bool previewIsNull(); + void resetPreview(); + void zoomFactorChanged(double zoom); + void updateZoomAndSize(bool alwaysFitToWindow); + inline void paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh); + +private: + + ImagePreviewViewPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEPREVIEWVIEW_H */ diff --git a/src/digikam/kdateedit.cpp b/src/digikam/kdateedit.cpp new file mode 100644 index 00000000..1cf4ec42 --- /dev/null +++ b/src/digikam/kdateedit.cpp @@ -0,0 +1,378 @@ +/* + This file is part of libtdepim. + + Copyright (c) 2002 Cornelius Schumacher + Copyright (c) 2003-2004 Reinhold Kainhofer + Copyright (c) 2004 Tobias Koenig + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "kdateedit.h" +#include "kdateedit.moc" + +namespace Digikam +{ + +class DateValidator : public TQValidator +{ + public: + DateValidator( const TQStringList &keywords, TQWidget* parent, const char* name = 0 ) + : TQValidator( parent, name ), mKeywords( keywords ) + {} + + virtual State validate( TQString &str, int& ) const + { + int length = str.length(); + + // empty string is intermediate so one can clear the edit line and start from scratch + if ( length <= 0 ) + return Intermediate; + + if ( mKeywords.contains( str.lower() ) ) + return Acceptable; + + bool ok = false; + TDEGlobal::locale()->readDate( str, &ok ); + if ( ok ) + return Acceptable; + else + return Intermediate; + } + + private: + TQStringList mKeywords; +}; + +KDateEdit::KDateEdit( TQWidget *parent, const char *name ) + : TQComboBox( true, parent, name ), + mReadOnly( false ), + mDiscardNextMousePress( false ) +{ + // need at least one entry for popup to work + setMaxCount( 1 ); + + mDate = TQDate::currentDate(); + TQString today = TDEGlobal::locale()->formatDate( mDate, true ); + + insertItem( today ); + setCurrentItem( 0 ); + changeItem( today, 0 ); + setMinimumSize( sizeHint() ); + + connect( lineEdit(), TQ_SIGNAL( returnPressed() ), + this, TQ_SLOT( lineEnterPressed() ) ); + connect( this, TQ_SIGNAL( textChanged( const TQString& ) ), + TQ_SLOT( slotTextChanged( const TQString& ) ) ); + + mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words ); + mPopup->hide(); + mPopup->installEventFilter( this ); + + connect( mPopup, TQ_SIGNAL( dateChanged( TQDate ) ), + TQ_SLOT( dateSelected( TQDate ) ) ); + + // handle keyword entry + setupKeywords(); + lineEdit()->installEventFilter( this ); + + setValidator( new DateValidator( mKeywordMap.keys(), this ) ); + + mTextChanged = false; +} + +KDateEdit::~KDateEdit() +{ + delete mPopup; + mPopup = 0; +} + +void KDateEdit::setDate( const TQDate& date ) +{ + assignDate( date ); + updateView(); +} + +TQDate KDateEdit::date() const +{ + return mDate; +} + +void KDateEdit::setReadOnly( bool readOnly ) +{ + mReadOnly = readOnly; + lineEdit()->setReadOnly( readOnly ); +} + +bool KDateEdit::isReadOnly() const +{ + return mReadOnly; +} + +void KDateEdit::popup() +{ + if ( mReadOnly ) + return; + + TQRect desk = TDEGlobalSettings::desktopGeometry( this ); + + TQPoint popupPoint = mapToGlobal( TQPoint( 0,0 ) ); + + int dateFrameHeight = mPopup->sizeHint().height(); + if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() ) + popupPoint.setY( popupPoint.y() - dateFrameHeight ); + else + popupPoint.setY( popupPoint.y() + height() ); + + int dateFrameWidth = mPopup->sizeHint().width(); + if ( popupPoint.x() + dateFrameWidth > desk.right() ) + popupPoint.setX( desk.right() - dateFrameWidth ); + + if ( popupPoint.x() < desk.left() ) + popupPoint.setX( desk.left() ); + + if ( popupPoint.y() < desk.top() ) + popupPoint.setY( desk.top() ); + + if ( mDate.isValid() ) + mPopup->setDate( mDate ); + else + mPopup->setDate( TQDate::currentDate() ); + + mPopup->popup( popupPoint ); + + // The combo box is now shown pressed. Make it show not pressed again + // by causing its (invisible) list box to emit a 'selected' signal. + // First, ensure that the list box contains the date currently displayed. + TQDate date = parseDate(); + assignDate( date ); + updateView(); + // Now, simulate an Enter to unpress it + TQListBox *lb = listBox(); + if (lb) { + lb->setCurrentItem(0); + TQKeyEvent* keyEvent = new TQKeyEvent(TQEvent::KeyPress, TQt::Key_Enter, 0, 0); + TQApplication::postEvent(lb, keyEvent); + } +} + +void KDateEdit::dateSelected( TQDate date ) +{ + if (assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + + if ( date.isValid() ) { + mPopup->hide(); + } + } +} + +void KDateEdit::dateEntered( TQDate date ) +{ + if (assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + } +} + +void KDateEdit::lineEnterPressed() +{ + bool replaced = false; + + TQDate date = parseDate( &replaced ); + + if (assignDate( date ) ) { + if ( replaced ) + updateView(); + + emit dateChanged( date ); + } +} + +TQDate KDateEdit::parseDate( bool *replaced ) const +{ + TQString text = currentText(); + TQDate result; + + if ( replaced ) + (*replaced) = false; + + if ( text.isEmpty() ) + result = TQDate(); + else if ( mKeywordMap.contains( text.lower() ) ) { + TQDate today = TQDate::currentDate(); + int i = mKeywordMap[ text.lower() ]; + if ( i >= 100 ) { + /* A day name has been entered. Convert to offset from today. + * This uses some math tricks to figure out the offset in days + * to the next date the given day of the week occurs. There + * are two cases, that the new day is >= the current day, which means + * the new day has not occurred yet or that the new day < the current day, + * which means the new day is already passed (so we need to find the + * day in the next week). + */ + i -= 100; + int currentDay = today.dayOfWeek(); + if ( i >= currentDay ) + i -= currentDay; + else + i += 7 - currentDay; + } + + result = today.addDays( i ); + if ( replaced ) + (*replaced) = true; + } else { + result = TDEGlobal::locale()->readDate( text ); + } + + return result; +} + +bool KDateEdit::eventFilter( TQObject *object, TQEvent *event ) +{ + if ( object == lineEdit() ) { + // We only process the focus out event if the text has changed + // since we got focus + if ( (event->type() == TQEvent::FocusOut) && mTextChanged ) { + lineEnterPressed(); + mTextChanged = false; + } else if ( event->type() == TQEvent::KeyPress ) { + // Up and down arrow keys step the date + TQKeyEvent* keyEvent = (TQKeyEvent*)event; + + if ( keyEvent->key() == TQt::Key_Return ) { + lineEnterPressed(); + return true; + } + + int step = 0; + if ( keyEvent->key() == TQt::Key_Up ) + step = 1; + else if ( keyEvent->key() == TQt::Key_Down ) + step = -1; + if ( step && !mReadOnly ) { + TQDate date = parseDate(); + if ( date.isValid() ) { + date = date.addDays( step ); + if ( assignDate( date ) ) { + updateView(); + emit dateChanged( date ); + return true; + } + } + } + } + } else { + // It's a date picker event + switch ( event->type() ) { + case TQEvent::MouseButtonDblClick: + case TQEvent::MouseButtonPress: { + TQMouseEvent *mouseEvent = (TQMouseEvent*)event; + if ( !mPopup->rect().contains( mouseEvent->pos() ) ) { + TQPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() ); + if ( TQApplication::widgetAt( globalPos, true ) == this ) { + // The date picker is being closed by a click on the + // KDateEdit widget. Avoid popping it up again immediately. + mDiscardNextMousePress = true; + } + } + + break; + } + default: + break; + } + } + + return false; +} + +void KDateEdit::mousePressEvent( TQMouseEvent *event ) +{ + if ( event->button() == TQt::LeftButton && mDiscardNextMousePress ) { + mDiscardNextMousePress = false; + return; + } + + TQComboBox::mousePressEvent( event ); +} + +void KDateEdit::slotTextChanged( const TQString& ) +{ + TQDate date = parseDate(); + + if ( assignDate( date ) ) + emit dateChanged( date ); + + mTextChanged = true; +} + +void KDateEdit::setupKeywords() +{ + // Create the keyword list. This will be used to match against when the user + // enters information. + mKeywordMap.insert( i18n( "tomorrow" ), 1 ); + mKeywordMap.insert( i18n( "today" ), 0 ); + mKeywordMap.insert( i18n( "yesterday" ), -1 ); + + TQString dayName; + for ( int i = 1; i <= 7; ++i ) { + dayName = TDEGlobal::locale()->calendar()->weekDayName( i ).lower(); + mKeywordMap.insert( dayName, i + 100 ); + } +} + +bool KDateEdit::assignDate( const TQDate& date ) +{ + mDate = date; + mTextChanged = false; + return true; +} + +void KDateEdit::updateView() +{ + TQString dateString; + if ( mDate.isValid() ) + dateString = TDEGlobal::locale()->formatDate( mDate, true ); + + // We do not want to generate a signal here, + // since we explicitly setting the date + bool blocked = signalsBlocked(); + blockSignals( true ); + changeItem( dateString, 0 ); + blockSignals( blocked ); +} + +} // namespace Digikam + diff --git a/src/digikam/kdateedit.h b/src/digikam/kdateedit.h new file mode 100644 index 00000000..92444823 --- /dev/null +++ b/src/digikam/kdateedit.h @@ -0,0 +1,150 @@ +/* + This file is part of libtdepim. + + Copyright (c) 2002 Cornelius Schumacher + Copyright (c) 2004 Tobias Koenig + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#ifndef KDATEEDIT_H +#define KDATEEDIT_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include "kdatepickerpopup.h" + +namespace Digikam +{ + +/** + A date editing widget that consists of an editable combo box. + The combo box contains the date in text form, and clicking the combo + box arrow will display a 'popup' style date picker. + + This widget also supports advanced features like allowing the user + to type in the day name to get the date. The following keywords + are supported (in the native language): tomorrow, yesturday, today, + monday, tuesday, wednesday, thursday, friday, saturday, sunday. + + @image html kdateedit.png "This is how it looks" + + @author Cornelius Schumacher + @author Mike Pilone + @author David Jarvie + @author Tobias Koenig +*/ +class KDateEdit : public TQComboBox +{ + TQ_OBJECT + + public: + + KDateEdit( TQWidget *parent = 0, const char *name = 0 ); + virtual ~KDateEdit(); + + /** + @return The date entered. This date could be invalid, + you have to check validity yourself. + */ + TQDate date() const; + + /** + Sets whether the widget is read-only for the user. If read-only, + the date picker pop-up is inactive, and the displayed date cannot be edited. + + @param readOnly True to set the widget read-only, false to set it read-write. + */ + void setReadOnly( bool readOnly ); + + /** + @return True if the widget is read-only, false if read-write. + */ + bool isReadOnly() const; + + virtual void popup(); + + signals: + /** + This signal is emitted whenever the user modifies the date. + The passed date can be invalid. + */ + void dateChanged( const TQDate &date ); + + public slots: + /** + Sets the date. + + @param date The new date to display. This date must be valid or + it will not be set + */ + void setDate( const TQDate &date ); + + protected slots: + + void lineEnterPressed(); + void slotTextChanged( const TQString& ); + void dateEntered( TQDate ); + void dateSelected( TQDate ); + + protected: + + virtual bool eventFilter( TQObject*, TQEvent* ); + virtual void mousePressEvent( TQMouseEvent* ); + + /** + Sets the date, without altering the display. + This method is used internally to set the widget's date value. + As a virtual method, it allows derived classes to perform additional validation + on the date value before it is set. Derived classes should return true if + TQDate::isValid(@p date) returns false. + + @param date The new date to set. + @return True if the date was set, false if it was considered invalid and + remains unchanged. + */ + virtual bool assignDate( const TQDate &date ); + + /** + Fills the keyword map. Reimplement it if you want additional + keywords. + */ + void setupKeywords(); + + private: + + TQDate parseDate( bool* = 0 ) const; + void updateView(); + + KDatePickerPopup *mPopup; + + TQDate mDate; + bool mReadOnly; + bool mTextChanged; + bool mDiscardNextMousePress; + + TQMap mKeywordMap; +}; + +} // namespace Digikam + +#endif // KDATEEDIT_H diff --git a/src/digikam/kdatepickerpopup.cpp b/src/digikam/kdatepickerpopup.cpp new file mode 100644 index 00000000..b4cb8bb2 --- /dev/null +++ b/src/digikam/kdatepickerpopup.cpp @@ -0,0 +1,160 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-04-21 + * Description : a menu widget to pick a date. + * this widget come from libtdepim. + * + * Copyright (C) 2004 Bram Schoenmakers + * Copyright (C) 2006 Mikolaj Machowski + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "kdatepickerpopup.h" +#include "kdatepickerpopup.moc" + +namespace Digikam +{ + +KDatePickerPopup::KDatePickerPopup(int items, const TQDate &date, TQWidget *parent, + const char *name) + : TQPopupMenu( parent, name ) +{ + mItems = items; + mDatePicker = new KDatePicker( this ); + mDatePicker->setCloseButton( false ); + + connect( mDatePicker, TQ_SIGNAL( dateEntered( TQDate ) ), + this, TQ_SLOT( slotDateChanged( TQDate ) ) ); + + connect( mDatePicker, TQ_SIGNAL( dateSelected( TQDate ) ), + this, TQ_SLOT( slotDateChanged( TQDate ) ) ); + + mDatePicker->setDate( date ); + buildMenu(); +} + +void KDatePickerPopup::buildMenu() +{ + if ( isVisible() ) return; + clear(); + + if ( mItems & DatePicker ) + { + insertItem( mDatePicker ); + + if ( ( mItems & NoDate ) || ( mItems & Words ) ) + insertSeparator(); + } + + if ( mItems & Words ) + { + insertItem( i18n("&Today"), this, TQ_SLOT( slotToday() ) ); + insertItem( i18n("Y&esterday"), this, TQ_SLOT( slotYesterday() ) ); + insertItem( i18n("Last &Monday"), this, TQ_SLOT( slotPrevMonday() ) ); + insertItem( i18n("Last &Friday"), this, TQ_SLOT( slotPrevFriday() ) ); + insertItem( i18n("Last &Week"), this, TQ_SLOT( slotPrevWeek() ) ); + insertItem( i18n("Last M&onth"), this, TQ_SLOT( slotPrevMonth() ) ); + + if ( mItems & NoDate ) + insertSeparator(); + } + + if ( mItems & NoDate ) + insertItem( i18n("No Date"), this, TQ_SLOT( slotNoDate() ) ); +} + +KDatePicker *KDatePickerPopup::datePicker() const +{ + return mDatePicker; +} + +void KDatePickerPopup::setDate( const TQDate &date ) +{ + mDatePicker->setDate( date ); +} + +#if 0 +void KDatePickerPopup::setItems( int items ) +{ + mItems = items; + buildMenu(); +} +#endif + +void KDatePickerPopup::slotDateChanged( TQDate date ) +{ + emit dateChanged( date ); + hide(); +} + +void KDatePickerPopup::slotToday() +{ + emit dateChanged( TQDate::currentDate() ); +} + +void KDatePickerPopup::slotYesterday() +{ + emit dateChanged( TQDate::currentDate().addDays( -1 ) ); +} + +void KDatePickerPopup::slotPrevFriday() +{ + TQDate date = TQDate::currentDate(); + int day = date.dayOfWeek(); + if ( day < 6 ) + date = date.addDays( 5 - 7 - day ); + else + date = date.addDays( 5 - day ); + + emit dateChanged( date ); +} + +void KDatePickerPopup::slotPrevMonday() +{ + TQDate date = TQDate::currentDate(); + emit dateChanged( date.addDays( 1 - date.dayOfWeek() ) ); +} + +void KDatePickerPopup::slotNoDate() +{ + emit dateChanged( TQDate() ); +} + +void KDatePickerPopup::slotPrevWeek() +{ + emit dateChanged( TQDate::currentDate().addDays( -7 ) ); +} + +void KDatePickerPopup::slotPrevMonth() +{ + emit dateChanged( TQDate::currentDate().addMonths( -1 ) ); +} + +} // namespace Digikam + + diff --git a/src/digikam/kdatepickerpopup.h b/src/digikam/kdatepickerpopup.h new file mode 100644 index 00000000..e343594e --- /dev/null +++ b/src/digikam/kdatepickerpopup.h @@ -0,0 +1,129 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-04-21 + * Description : a menu widget to pick a date. + * this widget come from libtdepim. + * + * Copyright (C) 2004 Bram Schoenmakers + * Copyright (C) 2006 Mikolaj Machowski + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef KDATEPICKERPOPUP_H +#define KDATEPICKERPOPUP_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +namespace Digikam +{ + +/** + @short This menu helps the user to select a past date quickly. + + This menu helps the user to select a past date quickly. It offers various ways of selecting, e.g. with a KDatePicker or with words like "Yesterday". + + The available items are: + + @li NoDate: A menu-item with "No Date". If chosen, the datepicker will emit a null TQDate. + @li DatePicker: Show a KDatePicker-widget. + @li Words: Show items like "Today", "Yesterday" or "Previous Week". + + When supplying multiple items, separate each item with a bitwise OR. + + @author Bram Schoenmakers , Mikolaj Machowski +*/ +class KDatePickerPopup: public TQPopupMenu +{ + TQ_OBJECT + +public: + + enum + { + NoDate = 1, + DatePicker = 2, + Words = 4 + }; + + /** + A constructor for the KDatePickerPopup. + + @param items List of all desirable items, separated with a bitwise OR. + @param date Initial date of datepicker-widget. + @param parent The object's parent. + @param name The object's name. + */ + KDatePickerPopup(int items = 2, const TQDate &date = TQDate::currentDate(), + TQWidget *parent = 0, const char *name = 0); + + /** + @return A pointer to the private variable mDatePicker, an instance of + KDatePicker. + */ + KDatePicker *datePicker() const; + + void setDate( const TQDate &date ); + +#if 0 + /** Set items which should be shown and rebuilds the menu afterwards. Only if the menu is not visible. + @param items List of all desirable items, separated with a bitwise OR. + */ + void setItems( int items = 1 ); +#endif + /** @return Returns the bitwise result of the active items in the popup. */ + int items() const { return mItems; } + + signals: + + /** + This signal emits the new date (selected with datepicker or other + menu-items). + */ + void dateChanged ( TQDate ); + +protected slots: + + void slotDateChanged(TQDate); + void slotToday(); + void slotYesterday(); + void slotPrevMonday(); + void slotPrevFriday(); + void slotPrevWeek(); + void slotPrevMonth(); + void slotNoDate(); + +private: + + void buildMenu(); + +private: + + int mItems; + + KDatePicker *mDatePicker; +}; + +} // namespace Digikam + +#endif // KDATEPICKERPOPUP_H diff --git a/src/digikam/kdatetimeedit.cpp b/src/digikam/kdatetimeedit.cpp new file mode 100644 index 00000000..e78c5cac --- /dev/null +++ b/src/digikam/kdatetimeedit.cpp @@ -0,0 +1,74 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : a widget to edit time stamp. + * + * Copyright (C) 2005 Tom Albers + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include "kdateedit.h" +#include "kdatetimeedit.h" +#include "kdatetimeedit.moc" + +namespace Digikam +{ + +KDateTimeEdit::KDateTimeEdit(TQWidget *parent, const char *name) + : TQHBox(parent, name) +{ + m_datePopUp = new KDateEdit(this, "datepopup"); + m_timePopUp = new TQTimeEdit(TQTime::currentTime(), this); + + connect(m_datePopUp, TQ_SIGNAL(dateChanged(const TQDate& )), + TQ_SLOT(slotDateTimeChanged()) ); + connect(m_timePopUp, TQ_SIGNAL(valueChanged(const TQTime& )), + TQ_SLOT(slotDateTimeChanged()) ); +} + +KDateTimeEdit::~KDateTimeEdit() +{ + delete m_datePopUp; + m_datePopUp = 0; + + delete m_timePopUp; + m_timePopUp = 0; +} + +TQDateTime KDateTimeEdit::dateTime() +{ + return TQDateTime(m_datePopUp->date(), m_timePopUp->time()); +} + +void KDateTimeEdit::setDateTime(const TQDateTime dateTime) +{ + m_datePopUp->setDate(dateTime.date()); + m_timePopUp->setTime(dateTime.time()); +} + +void KDateTimeEdit::slotDateTimeChanged() +{ + emit dateTimeChanged(dateTime()); +} + +} // namespace Digikam diff --git a/src/digikam/kdatetimeedit.h b/src/digikam/kdatetimeedit.h new file mode 100644 index 00000000..ac0575c6 --- /dev/null +++ b/src/digikam/kdatetimeedit.h @@ -0,0 +1,98 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-21 + * Description : a widget to edit time stamp. + * + * Copyright (C) 2005 Tom Albers + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file kdatetimeedit.h **/ + +#ifndef KDATETIMEEDIT_H +#define KDATETIMEEDIT_H + +// TQt includes. + +#include + +class TQTimeEdit; + +namespace Digikam +{ + +class KDateEdit; + +/** + * @class KDateTimeEdit + * This class is basically the same as the KDateTime class + * with the exception that a TQTimeEdit is placed directly + * besides it. + * + * @image html kdatetimeedit.png "This is how it looks" + * @author Tom Albers + */ +class KDateTimeEdit : public TQHBox +{ + TQ_OBJECT + +public: + + /** + * constructor + * @param parent the parent widget + * @param name the name of the widget + */ + KDateTimeEdit(TQWidget *parent, const char *name); + + /** + * destructor + */ + ~KDateTimeEdit(); + + /** + * returns the date and time + * @return a TQDateTime with the currently chosen date and time + */ + TQDateTime dateTime(); + + /** + * Sets the date and the time of this widget. + */ + void setDateTime(const TQDateTime dateTime); + +signals: + + /** + * This signal is emitted whenever the user modifies the date or time. + * The passed date and time can be invalid. + */ + void dateTimeChanged( const TQDateTime &dateTime ); + +private: + + KDateEdit* m_datePopUp; + TQTimeEdit* m_timePopUp; + +private slots: + + void slotDateTimeChanged(); +}; + +} // namespace Digikam + +#endif // KDATETIMEEDIT_H diff --git a/src/digikam/kipiinterface.cpp b/src/digikam/kipiinterface.cpp new file mode 100644 index 00000000..aaf012e3 --- /dev/null +++ b/src/digikam/kipiinterface.cpp @@ -0,0 +1,724 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-02 + * Description : digiKam kipi library interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2005 by Ralf Holzer + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// C Ansi includes + +extern "C" +{ +#include +#include +} + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// libKipi includes. + +#include + +// Local includes. + +#include "constants.h" +#include "ddebug.h" +#include "albummanager.h" +#include "albumitemhandler.h" +#include "album.h" +#include "albumdb.h" +#include "albumsettings.h" +#include "dmetadata.h" +#include "imageattributeswatch.h" +#include "kipiinterface.h" +#include "kipiinterface.moc" + +namespace Digikam +{ + +//-- Image Info ------------------------------------------------------------------ + +DigikamImageInfo::DigikamImageInfo( KIPI::Interface* interface, const KURL& url ) + : KIPI::ImageInfoShared( interface, url ), palbum_(0) +{ +} + +DigikamImageInfo::~DigikamImageInfo() +{ +} + +PAlbum* DigikamImageInfo::parentAlbum() +{ + if (!palbum_) + { + KURL u(_url.directory()); + palbum_ = AlbumManager::instance()->findPAlbum(u); + } + return palbum_; +} + +TQString DigikamImageInfo::title() +{ + return _url.fileName(); +} + +TQString DigikamImageInfo::description() +{ + PAlbum* p = parentAlbum(); + + if (p) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + return db->getItemCaption(p->id(), _url.fileName()); + } + + return TQString(); +} + +void DigikamImageInfo::setTitle( const TQString& newName ) +{ + // Here we get informed that a plugin has renamed the file + + PAlbum* p = parentAlbum(); + + if ( p && !newName.isEmpty() ) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->moveItem(p->id(), _url.fileName(), p->id(), newName); + _url = _url.upURL(); + _url.addPath(newName); + } +} + +void DigikamImageInfo::setDescription( const TQString& description ) +{ + PAlbum* p = parentAlbum(); + + if ( p ) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQ_LLONG imageId = db->getImageId(p->id(), _url.filename()); + db->setItemCaption(imageId, description); + ImageAttributesWatch::instance()->imageCaptionChanged(imageId); + + // See B.K.O #140133. Do not set here metadata comments of picture. + // Only the database comments must be set. + } +} + +TQDateTime DigikamImageInfo::time( KIPI::TimeSpec /*spec*/ ) +{ + PAlbum* p = parentAlbum(); + + if (p) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + return db->getItemDate(p->id(), _url.fileName()); + } + + return TQDateTime(); +} + +void DigikamImageInfo::setTime(const TQDateTime& time, KIPI::TimeSpec) +{ + if ( !time.isValid() ) + { + DWarning() << k_funcinfo << "Invalid datetime specified" << endl; + return; + } + + PAlbum* p = parentAlbum(); + + if ( p ) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQ_LLONG imageId = db->getImageId(p->id(), _url.filename()); + db->setItemDate(imageId, time); + ImageAttributesWatch::instance()->imageDateChanged(imageId); + AlbumManager::instance()->refreshItemHandler( _url ); + } +} + +void DigikamImageInfo::cloneData( ImageInfoShared* other ) +{ + setDescription( other->description() ); + setTime( other->time(KIPI::FromInfo), KIPI::FromInfo ); + addAttributes( other->attributes() ); +} + +TQStringVariantMap DigikamImageInfo::attributes() +{ + TQStringVariantMap res; + + PAlbum* p = parentAlbum(); + if (p) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQ_LLONG imageId = db->getImageId(p->id(), _url.filename()); + + // Get digiKam Tags list of picture. + TQStringList tags = db->getItemTagNames(imageId); + res["tags"] = tags; + + // Get digiKam Rating of picture. + int rating = db->getItemRating(imageId); + res["rating"] = rating; + + // TODO: add here future picture attributes stored by digiKam database + } + return res; +} + +void DigikamImageInfo::addAttributes(const TQStringVariantMap& res) +{ + PAlbum* p = parentAlbum(); + if (p) + { + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQ_LLONG imageId = db->getImageId(p->id(), _url.filename()); + TQStringVariantMap attributes = res; + + // Set digiKam Tags list of picture. + if (attributes.find("tags") != attributes.end()) + { + TQStringList tags = attributes["tags"].asStringList(); + //TODO + } + + // Set digiKam Rating of picture. + if (attributes.find("rating") != attributes.end()) + { + int rating = attributes["rating"].asInt(); + if (rating >= RatingMin && rating <= RatingMax) + db->setItemRating(imageId, rating); + } + + // TODO: add here future picture attributes stored by digiKam database + } + + // To update sidebar content. Some kipi-plugins use this way to refresh sidebar + // using an empty TQMap(). + ImageAttributesWatch::instance()->fileMetadataChanged(_url); +} + +void DigikamImageInfo::clearAttributes() +{ + // TODO ! This will used for the futures tags digiKam features. +} + +int DigikamImageInfo::angle() +{ + AlbumSettings *settings = AlbumSettings::instance(); + if (settings->getExifRotate()) + { + DMetadata metadata(_url.path()); + DMetadata::ImageOrientation orientation = metadata.getImageOrientation(); + + switch (orientation) + { + case DMetadata::ORIENTATION_ROT_180: + return 180; + case DMetadata::ORIENTATION_ROT_90: + case DMetadata::ORIENTATION_ROT_90_HFLIP: + case DMetadata::ORIENTATION_ROT_90_VFLIP: + return 90; + case DMetadata::ORIENTATION_ROT_270: + return 270; + default: + return 0; + } + } + + return 0; +} + +void DigikamImageInfo::setAngle( int ) +{ + // TODO : add here a DMetadata call (thru Exiv2) to set Exif orientation tag. +} + +//-- Image Collection ------------------------------------------------------------ + +DigikamImageCollection::DigikamImageCollection( Type tp, Album* album, const TQString& filter ) + : tp_( tp ), album_(album), imgFilter_(filter) +{ + if (!album) + { + DWarning() << k_funcinfo << "This should not happen. No album specified" << endl; + } +} + +DigikamImageCollection::~DigikamImageCollection() +{ +} + +TQString DigikamImageCollection::name() +{ + if ( album_->type() == Album::TAG ) + { + return i18n("Tag: %1").arg(album_->title()); + } + else + return album_->title(); +} + +TQString DigikamImageCollection::category() +{ + if ( album_->type() == Album::PHYSICAL ) + { + PAlbum *p = dynamic_cast(album_); + return p->collection(); + } + else if ( album_->type() == Album::TAG ) + { + TAlbum *p = dynamic_cast(album_); + return i18n("Tag: %1").arg(p->tagPath()); + } + else + return TQString(); +} + +TQDate DigikamImageCollection::date() +{ + if ( album_->type() == Album::PHYSICAL ) + { + PAlbum *p = dynamic_cast(album_); + return p->date(); + } + else + return TQDate(); +} + +TQString DigikamImageCollection::comment() +{ + if ( album_->type() == Album::PHYSICAL ) + { + PAlbum *p = dynamic_cast(album_); + return p->caption(); + } + else + return TQString(); +} + +static TQValueList makeFilterList( const TQString &filter ) +{ + TQValueList regExps; + if ( filter.isEmpty() ) + return regExps; + + TQChar sep( ';' ); + int i = filter.find( sep, 0 ); + + if ( i == -1 && filter.find( ' ', 0 ) != -1 ) + sep = TQChar( ' ' ); + + TQStringList list = TQStringList::split( sep, filter ); + TQStringList::Iterator it = list.begin(); + + while ( it != list.end() ) + { + regExps << TQRegExp( (*it).stripWhiteSpace(), false, true ); + ++it; + } + + return regExps; +} + +static bool matchFilterList( const TQValueList& filters, const TQString &fileName ) +{ + TQValueList::ConstIterator rit = filters.begin(); + + while ( rit != filters.end() ) + { + if ( (*rit).exactMatch(fileName) ) + return true; + ++rit; + } + + return false; +} + +KURL::List DigikamImageCollection::images() +{ + switch ( tp_ ) + { + case AllItems: + { + if (album_->type() == Album::PHYSICAL) + { + return imagesFromPAlbum(dynamic_cast(album_)); + } + else if (album_->type() == Album::TAG) + { + return imagesFromTAlbum(dynamic_cast(album_)); + } + else if (album_->type() == Album::DATE || + album_->type() == Album::SEARCH) + { + AlbumItemHandler* handler = AlbumManager::instance()->getItemHandler(); + + if (handler) + { + return handler->allItems(); + } + + return KURL::List(); + } + else + { + DWarning() << k_funcinfo << "Unknown album type" << endl; + return KURL::List(); + } + + break; + } + case SelectedItems: + { + AlbumItemHandler* handler = AlbumManager::instance()->getItemHandler(); + + if (handler) + { + return handler->selectedItems(); + } + + return KURL::List(); + + break; + } + default: + break; + } + + // We should never reach here + return KURL::List(); +} + +/** get the images from the Physical album in database and return the items found */ + +KURL::List DigikamImageCollection::imagesFromPAlbum(PAlbum* album) const +{ + // get the images from the database and return the items found + + AlbumDB* db = AlbumManager::instance()->albumDB(); + + db->beginTransaction(); + + TQStringList urls = db->getItemURLsInAlbum(album->id()); + + db->commitTransaction(); + + KURL::List urlList; + + TQValueList regex = makeFilterList(imgFilter_); + + for (TQStringList::iterator it = urls.begin(); it != urls.end(); ++it) + { + if (matchFilterList(regex, *it)) + urlList.append(*it); + } + + return urlList; +} + +/** get the images from the Tags album in database and return the items found */ + +KURL::List DigikamImageCollection::imagesFromTAlbum(TAlbum* album) const +{ + AlbumDB* db = AlbumManager::instance()->albumDB(); + + db->beginTransaction(); + + TQStringList urls = db->getItemURLsInTag(album->id()); + + db->commitTransaction(); + + KURL::List urlList; + + TQValueList regex = makeFilterList(imgFilter_); + + for (TQStringList::iterator it = urls.begin(); it != urls.end(); ++it) + { + if (matchFilterList(regex, *it)) + urlList.append(*it); + } + + return urlList; +} + +KURL DigikamImageCollection::path() +{ + if (album_->type() == Album::PHYSICAL) + { + PAlbum *p = dynamic_cast(album_); + KURL url; + url.setPath(p->folderPath()); + return url; + } + else + { + DWarning() << k_funcinfo << "Requesting kurl from a virtual album" << endl; + return TQString(); + } +} + +KURL DigikamImageCollection::uploadPath() +{ + if (album_->type() == Album::PHYSICAL) + { + PAlbum *p = dynamic_cast(album_); + KURL url; + url.setPath(p->folderPath()); + return url; + } + else + { + DWarning() << k_funcinfo << "Requesting kurl from a virtual album" << endl; + return KURL(); + } +} + +KURL DigikamImageCollection::uploadRoot() +{ + return KURL(AlbumManager::instance()->getLibraryPath() + '/'); +} + +TQString DigikamImageCollection::uploadRootName() +{ + return i18n("My Albums"); +} + +bool DigikamImageCollection::isDirectory() +{ + if (album_->type() == Album::PHYSICAL) + return true; + else + return false; +} + +bool DigikamImageCollection::operator==(ImageCollectionShared& imgCollection) +{ + DigikamImageCollection* thatCollection = static_cast(&imgCollection); + return (album_ == thatCollection->album_); +} + +//-- LibKipi interface ----------------------------------------------------------- + +DigikamKipiInterface::DigikamKipiInterface( TQObject *parent, const char *name) + : KIPI::Interface( parent, name ) +{ + albumManager_ = AlbumManager::instance(); + albumDB_ = albumManager_->albumDB(); + + connect( albumManager_, TQ_SIGNAL( signalAlbumItemsSelected( bool ) ), + this, TQ_SLOT( slotSelectionChanged( bool ) ) ); + + connect( albumManager_, TQ_SIGNAL( signalAlbumCurrentChanged(Album*) ), + this, TQ_SLOT( slotCurrentAlbumChanged(Album*) ) ); +} + +DigikamKipiInterface::~DigikamKipiInterface() +{ +} + +KIPI::ImageCollection DigikamKipiInterface::currentAlbum() +{ + Album* currAlbum = albumManager_->currentAlbum(); + if ( currAlbum ) + { + return KIPI::ImageCollection( + new DigikamImageCollection( DigikamImageCollection::AllItems, + currAlbum, fileExtensions() ) ); + } + else + { + return KIPI::ImageCollection(0); + } +} + +KIPI::ImageCollection DigikamKipiInterface::currentSelection() +{ + Album* currAlbum = albumManager_->currentAlbum(); + if ( currAlbum ) + { + return KIPI::ImageCollection( + new DigikamImageCollection( DigikamImageCollection::SelectedItems, + currAlbum, fileExtensions() ) ); + } + else + { + return KIPI::ImageCollection(0); + } +} + +TQValueList DigikamKipiInterface::allAlbums() +{ + TQValueList result; + + TQString fileFilter(fileExtensions()); + + AlbumList palbumList = albumManager_->allPAlbums(); + for ( AlbumList::Iterator it = palbumList.begin(); + it != palbumList.end(); ++it ) + { + // don't add the root album + if ((*it)->isRoot()) + continue; + + DigikamImageCollection* col = new DigikamImageCollection( DigikamImageCollection::AllItems, + *it, fileFilter ); + result.append( KIPI::ImageCollection( col ) ); + } + + AlbumList talbumList = albumManager_->allTAlbums(); + for ( AlbumList::Iterator it = talbumList.begin(); + it != talbumList.end(); ++it ) + { + // don't add the root album + if ((*it)->isRoot()) + continue; + + DigikamImageCollection* col = new DigikamImageCollection( DigikamImageCollection::AllItems, + *it, fileFilter ); + result.append( KIPI::ImageCollection( col ) ); + } + + return result; +} + +KIPI::ImageInfo DigikamKipiInterface::info( const KURL& url ) +{ + return KIPI::ImageInfo( new DigikamImageInfo( this, url ) ); +} + +void DigikamKipiInterface::refreshImages( const KURL::List& urls ) +{ + KURL::List ulist = urls; + + // Re-scan metadata from pictures. This way will update Metadata sidebar and database. + for ( KURL::List::Iterator it = ulist.begin() ; it != ulist.end() ; ++it ) + ImageAttributesWatch::instance()->fileMetadataChanged(*it); + + // Refresh preview. + albumManager_->refreshItemHandler(urls); +} + +int DigikamKipiInterface::features() const +{ + return ( + +// HostSupportsTags feature require libkipi version > 1.4.0 +#if KIPI_VERSION>0x000104 + KIPI::HostSupportsTags | +#endif + KIPI::ImagesHasComments | KIPI::AcceptNewImages | + KIPI::AlbumsHaveComments | KIPI::ImageTitlesWritable | + KIPI::ImagesHasTime | KIPI::AlbumsHaveCategory | + KIPI::AlbumsHaveCreationDate | KIPI::AlbumsUseFirstImagePreview + ); +} + +bool DigikamKipiInterface::addImage( const KURL& url, TQString& errmsg ) +{ + // Nota : All copy/move operations are processed by the plugins. + + if ( url.isValid() == false ) + { + errmsg = i18n("Target URL %1 is not valid.").arg(url.path()); + return false; + } + + PAlbum *targetAlbum = albumManager_->findPAlbum(url.directory()); + + if ( !targetAlbum ) + { + errmsg = i18n("Target album is not in the album library."); + return false; + } + + albumManager_->refreshItemHandler( url ); + + return true; +} + +void DigikamKipiInterface::delImage( const KURL& url ) +{ + KURL rootURL(albumManager_->getLibraryPath()); + if ( !rootURL.isParentOf(url) ) + { + DWarning() << k_funcinfo << "URL not in the album library" << endl; + } + + // Is there a PAlbum for this url + + PAlbum *palbum = albumManager_->findPAlbum( KURL(url.directory()) ); + + if ( palbum ) + { + // delete the item from the database + albumDB_->deleteItem( palbum->id(), url.fileName() ); + } + else + { + DWarning() << k_funcinfo << "Cannot find Parent album in album library" << endl; + } +} + +void DigikamKipiInterface::slotSelectionChanged( bool b ) +{ + emit selectionChanged( b ); +} + +void DigikamKipiInterface::slotCurrentAlbumChanged( Album *album ) +{ + emit currentAlbumChanged( album != 0 ); +} + +TQString DigikamKipiInterface::fileExtensions() +{ + // do not save this into a local variable, as this + // might change in the main app + + AlbumSettings* s = AlbumSettings::instance(); + return (s->getImageFileFilter() + ' ' + + s->getMovieFileFilter() + ' ' + + s->getAudioFileFilter() + ' ' + + s->getRawFileFilter()); +} + +} // namespace Digikam + diff --git a/src/digikam/kipiinterface.h b/src/digikam/kipiinterface.h new file mode 100644 index 00000000..b2974ece --- /dev/null +++ b/src/digikam/kipiinterface.h @@ -0,0 +1,187 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-02 + * Description : digiKam kipi library interface. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2005 by Ralf Holzer + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIGIKAM_KIPIINTERFACE_H +#define DIGIKAM_KIPIINTERFACE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include + +// libKipi includes. + +#include +#include +#include +#include +#include + +class TQDateTime; + +namespace KIPI +{ +class Interface; +class ImageCollection; +class ImageInfo; +} + +namespace Digikam +{ + +class AlbumManager; +class Album; +class PAlbum; +class TAlbum; +class AlbumDB; +class AlbumSettings; + +/** DigikamImageInfo: class to get/set image information/properties in a digiKam album. */ + +class DigikamImageInfo : public KIPI::ImageInfoShared +{ +public: + + DigikamImageInfo( KIPI::Interface* interface, const KURL& url ); + ~DigikamImageInfo(); + + virtual TQString title(); + virtual void setTitle( const TQString& ); + + virtual TQString description(); + virtual void setDescription( const TQString& ); + + virtual void cloneData( ImageInfoShared* other ); + + virtual TQDateTime time( KIPI::TimeSpec spec ); + virtual void setTime( const TQDateTime& time, KIPI::TimeSpec spec = KIPI::FromInfo ); + + virtual TQStringVariantMap attributes(); + virtual void addAttributes(const TQStringVariantMap& res); + virtual void clearAttributes(); + + virtual int angle(); + virtual void setAngle( int angle ); + +private: + + PAlbum *parentAlbum(); + +private: + + PAlbum *palbum_; +}; + + +/** DigikamImageCollection: class to get/set image collection information/properties in a digiKam + album database. */ + +class DigikamImageCollection : public KIPI::ImageCollectionShared +{ + +public: + + enum Type + { + AllItems, + SelectedItems + }; + +public: + + DigikamImageCollection( Type tp, Album *album, const TQString& filter ); + ~DigikamImageCollection(); + + virtual TQString name(); + virtual TQString comment(); + virtual TQString category(); + virtual TQDate date(); + virtual KURL::List images(); + virtual KURL path(); + virtual KURL uploadPath(); + virtual KURL uploadRoot(); + virtual TQString uploadRootName(); + virtual bool isDirectory(); + virtual bool operator==(ImageCollectionShared&); + +private: + + KURL::List imagesFromPAlbum(PAlbum* album) const; + KURL::List imagesFromTAlbum(TAlbum* album) const; + +private: + + Type tp_; + Album *album_; + TQString imgFilter_; +}; + + +/** DigikamKipiInterface: class to interface digiKam with kipi library. */ + +class DigikamKipiInterface : public KIPI::Interface +{ + TQ_OBJECT + +public: + + DigikamKipiInterface( TQObject *parent, const char *name=0); + ~DigikamKipiInterface(); + + virtual KIPI::ImageCollection currentAlbum(); + virtual KIPI::ImageCollection currentSelection(); + virtual TQValueList allAlbums(); + virtual KIPI::ImageInfo info( const KURL& ); + virtual bool addImage( const KURL&, TQString& errmsg ); + virtual void delImage( const KURL& ); + virtual void refreshImages( const KURL::List& urls ); + virtual int features() const; + virtual TQString fileExtensions(); + +public slots: + + void slotSelectionChanged( bool b ); + void slotCurrentAlbumChanged( Album *palbum ); + +private: + + AlbumManager *albumManager_; + AlbumDB *albumDB_; +}; + +} // namespace Digikam + +#endif // DIGIKAM_KIPIINTERFACE_H + diff --git a/src/digikam/main.cpp b/src/digikam/main.cpp new file mode 100644 index 00000000..77609197 --- /dev/null +++ b/src/digikam/main.cpp @@ -0,0 +1,138 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2002-07-28 + * Description : main program from digiKam + * + * Copyright (C) 2002-2006 by Renchi Raju + * Copyright (C) 2002-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KIPI includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "albumdb.h" +#include "digikamapp.h" +#include "digikamfirstrun.h" + +static TDECmdLineOptions options[] = +{ + { "detect-camera", I18N_NOOP("Automatically detect and open camera"), 0 }, + { "download-from ", I18N_NOOP("Open camera dialog at "), 0 }, + TDECmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + TQString libInfo = Digikam::libraryInfo(); + + TQString description = Digikam::digiKamDescription(); + + TDEAboutData aboutData( "digikam", + I18N_NOOP("digiKam"), + digikam_version, + description.latin1(), + TDEAboutData::License_GPL, + Digikam::copyright(), + 0, + Digikam::webProjectUrl()); + + aboutData.setOtherText(libInfo.latin1()); + + Digikam::authorsRegistration(aboutData); + + TDECmdLineArgs::init( argc, argv, &aboutData ); + TDECmdLineArgs::addCmdLineOptions( options ); + + TDEApplication app; + + TDEConfig* config = TDEGlobal::config(); + config->setGroup("General Settings"); + TQString version = config->readEntry("Version"); + + config->setGroup("Album Settings"); + TQString albumPath = config->readPathEntry("Album Path"); + TQFileInfo dirInfo(albumPath); + + // version 0.6 was the version when the new Albums Library + // storage was implemented + + if (version.startsWith("0.5") || + !dirInfo.exists() || + !dirInfo.isDir()) + { + // Run the first run + Digikam::DigikamFirstRun *firstRun = new Digikam::DigikamFirstRun(config); + app.setMainWidget(firstRun); + firstRun->show(); + return app.exec(); + } + + Digikam::DigikamApp *digikam = new Digikam::DigikamApp(); + + app.setMainWidget(digikam); + digikam->show(); + + TDECmdLineArgs* args = TDECmdLineArgs::parsedArgs(); + if (args && args->isSet("detect-camera")) + digikam->autoDetect(); + else if (args && args->isSet("download-from")) + digikam->downloadFrom(args->getOption("download-from")); + +#if KDE_IS_VERSION(3,2,0) + TQStringList tipsFiles; + tipsFiles.append("digikam/tips"); + tipsFiles.append("kipi/tips"); + + TDEGlobal::locale()->insertCatalogue("kipiplugins"); + TDEGlobal::locale()->insertCatalogue("libkdcraw"); + + KTipDialog::showMultiTip(0, tipsFiles, false); +#else + KTipDialog::showTip(0, "digikam/tips", false); +#endif + + return app.exec(); +} diff --git a/src/digikam/mediaplayerview.cpp b/src/digikam/mediaplayerview.cpp new file mode 100644 index 00000000..97ef26aa --- /dev/null +++ b/src/digikam/mediaplayerview.cpp @@ -0,0 +1,252 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-20-12 + * Description : a view to embed a KPart media player. + * + * Copyright (C) 2006-2007 Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "themeengine.h" +#include "mediaplayerview.h" +#include "mediaplayerview.moc" + +namespace Digikam +{ + +class MediaPlayerViewPriv +{ + +public: + + enum MediaPlayerViewMode + { + ErrorView=0, + PlayerView + }; + +public: + + MediaPlayerViewPriv() + { + mediaPlayerPart = 0; + grid = 0; + errorView = 0; + mediaPlayerView = 0; + } + + TQFrame *errorView; + + TQFrame *mediaPlayerView; + + TQGridLayout *grid; + + KParts::ReadOnlyPart *mediaPlayerPart; +}; + +MediaPlayerView::MediaPlayerView(TQWidget *parent) + : TQWidgetStack(parent, 0, TQt::WDestructiveClose) +{ + d = new MediaPlayerViewPriv; + + // -------------------------------------------------------------------------- + + d->errorView = new TQFrame(this); + TQLabel *errorMsg = new TQLabel(i18n("No media player available..."), d->errorView); + TQGridLayout *grid = new TQGridLayout(d->errorView, 2, 2, + KDialogBase::marginHint(), KDialogBase::spacingHint()); + + errorMsg->setAlignment(TQt::AlignCenter); + d->errorView->setFrameStyle(TQFrame::GroupBoxPanel|TQFrame::Plain); + d->errorView->setMargin(0); + d->errorView->setLineWidth(1); + + grid->addMultiCellWidget(errorMsg, 1, 1, 0, 2); + grid->setColStretch(0, 10), + grid->setColStretch(2, 10), + grid->setRowStretch(0, 10), + grid->setRowStretch(2, 10), + + addWidget(d->errorView, MediaPlayerViewPriv::ErrorView); + + // -------------------------------------------------------------------------- + + d->mediaPlayerView = new TQFrame(this); + d->grid = new TQGridLayout(d->mediaPlayerView, 2, 2, + KDialogBase::marginHint(), KDialogBase::spacingHint()); + + d->mediaPlayerView->setFrameStyle(TQFrame::GroupBoxPanel|TQFrame::Plain); + d->mediaPlayerView->setMargin(0); + d->mediaPlayerView->setLineWidth(1); + + d->grid->setColStretch(0, 10), + d->grid->setColStretch(2, 10), + d->grid->setRowStretch(0, 10), + + addWidget(d->mediaPlayerView, MediaPlayerViewPriv::PlayerView); + setPreviewMode(MediaPlayerViewPriv::PlayerView); + + // -------------------------------------------------------------------------- + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); +} + +MediaPlayerView::~MediaPlayerView() +{ + if (d->mediaPlayerPart) + { + d->mediaPlayerPart->closeURL(); + delete d->mediaPlayerPart; + d->mediaPlayerPart = 0; + } + + delete d; +} + +void MediaPlayerView::setMediaPlayerFromUrl(const KURL& url) +{ + if (url.isEmpty()) + { + if (d->mediaPlayerPart) + { + d->mediaPlayerPart->closeURL(); + delete d->mediaPlayerPart; + d->mediaPlayerPart = 0; + } + return; + } + + KMimeType::Ptr mimePtr = KMimeType::findByURL(url, 0, true, true); + KServiceTypeProfile::OfferList services = KServiceTypeProfile::offers(mimePtr->name(), + TQString::fromLatin1("KParts/ReadOnlyPart")); + + DDebug() << "Search a KPart to preview " << url.fileName() << " (" << mimePtr->name() << ") " << endl; + + if (d->mediaPlayerPart) + { + d->mediaPlayerPart->closeURL(); + delete d->mediaPlayerPart; + d->mediaPlayerPart = 0; + } + + TQWidget *mediaPlayerWidget = 0; + + for( KServiceTypeProfile::OfferList::Iterator it = services.begin(); it != services.end(); ++it ) + { + // Ask for a part for this mime type + KService::Ptr service = (*it).service(); + + if (!service.data()) + { + DWarning() << "Couldn't find a KPart for media" << endl; + continue; + } + + TQString library = service->library(); + if ( library.isNull() ) + { + DWarning() << "The library returned from the service was null, " + << "indicating we could play media." + << endl; + continue; + } + + DDebug() << "Find KPart library " << library << endl; + int error = 0; + d->mediaPlayerPart = KParts::ComponentFactory::createPartInstanceFromService + (service, d->mediaPlayerView, 0, d->mediaPlayerView, + 0, TQStringList(), &error); + if (!d->mediaPlayerPart) + { + DWarning() << "Failed to instantiate KPart from library " << library + << " error=" << error << endl; + continue; + } + + mediaPlayerWidget = d->mediaPlayerPart->widget(); + if ( !mediaPlayerWidget ) + { + DWarning() << "Failed to get KPart widget from library " << library << endl; + continue; + } + + mediaPlayerWidget->show(); + break; + } + + if (!mediaPlayerWidget) + { + setPreviewMode(MediaPlayerViewPriv::ErrorView); + return; + } + + d->grid->addMultiCellWidget(mediaPlayerWidget, 0, 0, 0, 2); + mediaPlayerWidget->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding); + d->mediaPlayerPart->openURL(url); + setPreviewMode(MediaPlayerViewPriv::PlayerView); +} + +void MediaPlayerView::escapePreview() +{ + if (d->mediaPlayerPart) + { + d->mediaPlayerPart->closeURL(); + delete d->mediaPlayerPart; + d->mediaPlayerPart = 0; + } +} + +void MediaPlayerView::slotThemeChanged() +{ + d->errorView->setPaletteBackgroundColor(ThemeEngine::instance()->baseColor()); + d->mediaPlayerView->setPaletteBackgroundColor(ThemeEngine::instance()->baseColor()); +} + +int MediaPlayerView::previewMode(void) +{ + return id(visibleWidget()); +} + +void MediaPlayerView::setPreviewMode(int mode) +{ + if (mode != MediaPlayerViewPriv::ErrorView && mode != MediaPlayerViewPriv::PlayerView) + return; + + raiseWidget(mode); +} + +} // NameSpace Digikam + diff --git a/src/digikam/mediaplayerview.h b/src/digikam/mediaplayerview.h new file mode 100644 index 00000000..08399175 --- /dev/null +++ b/src/digikam/mediaplayerview.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-20-12 + * Description : a view to embed a KPart media player. + * + * Copyright (C) 2006-2007 Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef MEDIAPLAYERVIEW_H +#define MEDIAPLAYERVIEW_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class MediaPlayerViewPriv; + +class DIGIKAM_EXPORT MediaPlayerView : public TQWidgetStack +{ +TQ_OBJECT + +public: + + MediaPlayerView(TQWidget *parent=0); + ~MediaPlayerView(); + + void setMediaPlayerFromUrl(const KURL& url); + void escapePreview(); + +private slots: + + void slotThemeChanged(); + +private: + + int previewMode(void); + void setPreviewMode(int mode); + +private: + + MediaPlayerViewPriv* d; +}; + +} // NameSpace Digikam + +#endif /* MEDIAPLAYERVIEW_H */ diff --git a/src/digikam/metadatahub.cpp b/src/digikam/metadatahub.cpp new file mode 100644 index 00000000..a1ab4d3c --- /dev/null +++ b/src/digikam/metadatahub.cpp @@ -0,0 +1,858 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-05 + * Description : Metadata handling + * + * Copyright (C) 2007-2009 by Marcel Wiesweg + * Copyright (C) 2007-2009 by Gilles Caulier + * + * This program is free software you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +// Local includes. + +#include "ddebug.h" +#include "imageinfo.h" +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "imageattributeswatch.h" +#include "metadatahub.h" + +namespace Digikam +{ + +class MetadataHubPriv +{ +public: + + MetadataHubPriv() + { + dateTimeStatus = MetadataHub::MetadataInvalid; + ratingStatus = MetadataHub::MetadataInvalid; + commentStatus = MetadataHub::MetadataInvalid; + + rating = -1; + highestRating = -1; + + count = 0; + + dbmode = MetadataHub::ManagedTags; + + dateTimeChanged = false; + commentChanged = false; + ratingChanged = false; + tagsChanged = false; + } + + MetadataHub::Status dateTimeStatus; + MetadataHub::Status commentStatus; + MetadataHub::Status ratingStatus; + + TQDateTime dateTime; + TQDateTime lastDateTime; + TQString comment; + int rating; + int highestRating; + + int count; + + TQMap tags; + TQStringList tagList; + + MetadataHub::DatabaseMode dbmode; + + bool dateTimeChanged; + bool commentChanged; + bool ratingChanged; + bool tagsChanged; + + template void loadWithInterval(const T &data, T &storage, T &highestStorage, MetadataHub::Status &status); + template void loadSingleValue(const T &data, T &storage, MetadataHub::Status &status); +}; + +MetadataWriteSettings::MetadataWriteSettings() +{ + saveComments = false; + saveDateTime = false; + saveRating = false; + saveIptcTags = false; + saveIptcPhotographerId = false; + saveIptcCredits = false; +} + +MetadataWriteSettings::MetadataWriteSettings(AlbumSettings *albumSettings) +{ + saveComments = albumSettings->getSaveComments(); + saveDateTime = albumSettings->getSaveDateTime(); + saveRating = albumSettings->getSaveRating(); + saveIptcTags = albumSettings->getSaveIptcTags(); + saveIptcPhotographerId = albumSettings->getSaveIptcPhotographerId(); + saveIptcCredits = albumSettings->getSaveIptcCredits(); + + iptcAuthor = albumSettings->getIptcAuthor(); + iptcAuthorTitle = albumSettings->getIptcAuthorTitle(); + iptcCredit = albumSettings->getIptcCredit(); + iptcSource = albumSettings->getIptcSource(); + iptcCopyright = albumSettings->getIptcCopyright(); +} + +MetadataHub::MetadataHub(DatabaseMode dbmode) +{ + d = new MetadataHubPriv; + d->dbmode = dbmode; +} + +MetadataHub::~MetadataHub() +{ + delete d; +} + +MetadataHub::MetadataHub(const MetadataHub &other) +{ + d = new MetadataHubPriv(*other.d); +} + +MetadataHub &MetadataHub::operator=(const MetadataHub &other) +{ + (*d) = (*other.d); + return *this; +} + +void MetadataHub::reset() +{ + (*d) = MetadataHubPriv(); +} + +// -------------------------------------------------- + +void MetadataHub::load(ImageInfo *info) +{ + d->count++; + + load(info->dateTime(), info->caption(), info->rating()); + + AlbumManager *man = AlbumManager::instance(); + TQValueList tagIDs = info->tagIDs(); + TQValueList loadedTags; + + if (d->dbmode == ManagedTags) + { + TQValueList loadedTags; + for (TQValueList::iterator it = tagIDs.begin(); it != tagIDs.end(); ++it) + { + TAlbum *album = man->findTAlbum(*it); + if (!album) + { + DWarning() << k_funcinfo << "Tag id " << *it << " not found in database." << endl; + continue; + } + loadedTags.append(album); + } + + loadTags(loadedTags); + } + else + { + loadTags(info->tagPaths(false)); + } +} + +void MetadataHub::load(const DMetadata &metadata) +{ + d->count++; + + TQString comment; + TQStringList keywords; + TQDateTime datetime; + int rating; + + // Try to get comments from image : + // In first, from standard JPEG comments, or + // In second, from EXIF comments tag, or + // In third, from IPTC comments tag. + + comment = metadata.getImageComment(); + + // Try to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + // Try to get image rating from IPTC Urgency tag + // else use file system time stamp. + rating = metadata.getImageRating(); + + if ( !datetime.isValid() ) + { + TQFileInfo info( metadata.getFilePath() ); + datetime = info.lastModified(); + } + + load(datetime, comment, rating); + + // Try to get image tags from IPTC keywords tags. + + if (d->dbmode == ManagedTags) + { + AlbumManager *man = AlbumManager::instance(); + TQStringList tagPaths = metadata.getImageKeywords(); + TQValueList loadedTags; + + for (TQStringList::iterator it = tagPaths.begin(); it != tagPaths.end(); ++it) + { + TAlbum *album = man->findTAlbum(*it); + if (!album) + { + DWarning() << k_funcinfo << "Tag id " << *it << " not found in database. Use NewTagsImport mode?" << endl; + continue; + } + loadedTags.append(album); + } + + loadTags(loadedTags); + } + else + { + loadTags(metadata.getImageKeywords()); + } +} + +bool MetadataHub::load(const TQString &filePath) +{ + DMetadata metadata; + bool success = metadata.load(filePath); + load(metadata); // increments count + return success; +} + +// private common code to merge tags +void MetadataHub::loadTags(const TQValueList &loadedTags) +{ + // get copy of tags + TQValueList previousTags = d->tags.keys(); + + // first go through all tags contained in this set + for (TQValueList::const_iterator it = loadedTags.begin(); it != loadedTags.end(); ++it) + { + // that is a reference + TagStatus &status = d->tags[*it]; + // if it was not contained in the list, the default constructor will mark it as invalid + if (status == MetadataInvalid) + { + if (d->count == 1) + // there were no previous sets that could have contained the set + status = TagStatus(MetadataAvailable, true); + else + // previous sets did not contain the tag, we do => disjoint + status = TagStatus(MetadataDisjoint, true); + } + else if (status == TagStatus(MetadataAvailable, false)) + { + // set to explicitly not contained, but we contain it => disjoint + status = TagStatus(MetadataDisjoint, true); + } + // else if mapIt.data() == MetadataAvailable, true: all right, we contain it too + // else if mapIt.data() == MetadataDisjoint: it's already disjoint + + // remove from the list to signal that this tag has been handled + previousTags.remove(*it); + } + + // Those tags which had been set as MetadataAvailable before, + // but are not contained in this set, have to be set to MetadataDisjoint + for (TQValueList::iterator it = previousTags.begin(); it != previousTags.end(); ++it) + { + TQMap::iterator mapIt = d->tags.find(*it); + if (mapIt != d->tags.end() && mapIt.data() == TagStatus(MetadataAvailable, true)) + { + mapIt.data() = TagStatus(MetadataDisjoint, true); + } + } +} + +// private code to merge tags with d->tagList +void MetadataHub::loadTags(const TQStringList &loadedTagPaths) +{ + if (d->count == 1) + { + d->tagList = loadedTagPaths; + } + else + { + // a simple intersection + TQStringList toBeAdded; + for (TQStringList::iterator it = d->tagList.begin(); it != d->tagList.end(); ++it) + { + TQStringList::const_iterator newTagListIt = loadedTagPaths.find(*it); + if (newTagListIt == loadedTagPaths.end()) + { + // it's not in the loadedTagPaths list. Remove it from intersection list. + it = d->tagList.remove(it); + } + // else, it is in both lists, so no need to change d->tagList, it's already added. + } + } +} + +// private common code to load dateTime, comment, rating +void MetadataHub::load(const TQDateTime &dateTime, const TQString &comment, int rating) +{ + if (dateTime.isValid()) + { + d->loadWithInterval(dateTime, d->dateTime, d->lastDateTime, d->dateTimeStatus); + } + + d->loadWithInterval(rating, d->rating, d->highestRating, d->ratingStatus); + + d->loadSingleValue(comment, d->comment, d->commentStatus); +} + +// template method to share code for dateTime and rating +template void MetadataHubPriv::loadWithInterval(const T &data, T &storage, T &highestStorage, MetadataHub::Status &status) +{ + switch (status) + { + case MetadataHub::MetadataInvalid: + storage = data; + status = MetadataHub::MetadataAvailable; + break; + case MetadataHub::MetadataAvailable: + // we have two values. If they are equal, status is unchanged + if (data == storage) + break; + // they are not equal. We need to enter the disjoint state. + status = MetadataHub::MetadataDisjoint; + if (data > storage) + { + highestStorage = data; + } + else + { + highestStorage = storage; + storage = data; + } + break; + case MetadataHub::MetadataDisjoint: + // smaller value is stored in storage + if (data < storage) + storage = data; + else if (highestStorage < data) + highestStorage = data; + break; + } +} + +// template method used by comment +template void MetadataHubPriv::loadSingleValue(const T &data, T &storage, MetadataHub::Status &status) +{ + switch (status) + { + case MetadataHub::MetadataInvalid: + storage = data; + status = MetadataHub::MetadataAvailable; + break; + case MetadataHub::MetadataAvailable: + // we have two values. If they are equal, status is unchanged + if (data == storage) + break; + // they are not equal. We need to enter the disjoint state. + status = MetadataHub::MetadataDisjoint; + break; + case MetadataHub::MetadataDisjoint: + break; + } +} + +// -------------------------------------------------- + +bool MetadataHub::write(ImageInfo *info, WriteMode writeMode) +{ + bool changed = false; + + // find out in advance if we have something to write - needed for FullWriteIfChanged mode + bool saveComment = d->commentStatus == MetadataAvailable; + bool saveDateTime = d->dateTimeStatus == MetadataAvailable; + bool saveRating = d->ratingStatus == MetadataAvailable; + bool saveTags = false; + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + if (it.data() == MetadataAvailable) + { + saveTags = true; + break; + } + } + + bool writeAllFields; + if (writeMode == FullWrite) + writeAllFields = true; + else if (writeMode == FullWriteIfChanged) + writeAllFields = ( + (saveComment && d->commentChanged) || + (saveDateTime && d->dateTimeChanged) || + (saveRating && d->ratingChanged) || + (saveTags && d->tagsChanged) + ); + else // PartialWrite + writeAllFields = false; + + if (saveComment && (writeAllFields || d->commentChanged)) + { + info->setCaption(d->comment); + changed = true; + } + if (saveDateTime && (writeAllFields || d->dateTimeChanged)) + { + info->setDateTime(d->dateTime); + changed = true; + } + if (saveRating && (writeAllFields || d->ratingChanged)) + { + info->setRating(d->rating); + changed = true; + } + + if (writeAllFields || d->tagsChanged) + { + if (d->dbmode == ManagedTags) + { + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + if (it.data() == MetadataAvailable) + { + if (it.data().hasTag) + info->setTag(it.key()->id()); + else + info->removeTag(it.key()->id()); + changed = true; + } + } + } + else + { + // tags not yet contained in database will be created + info->addTagPaths(d->tagList); + changed = changed || !d->tagList.isEmpty(); + } + } + return changed; +} + +bool MetadataHub::write(DMetadata &metadata, WriteMode writeMode, const MetadataWriteSettings &settings) +{ + bool dirty = false; + + // find out in advance if we have something to write - needed for FullWriteIfChanged mode + bool saveComment = (settings.saveComments && d->commentStatus == MetadataAvailable); + bool saveDateTime = (settings.saveDateTime && d->dateTimeStatus == MetadataAvailable); + bool saveRating = (settings.saveRating && d->ratingStatus == MetadataAvailable); + bool saveTags = false; + if (settings.saveIptcTags) + { + saveTags = false; + // find at least one tag to write + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + if (it.data() == MetadataAvailable) + { + saveTags = true; + break; + } + } + } + + bool writeAllFields; + if (writeMode == FullWrite) + writeAllFields = true; + else if (writeMode == FullWriteIfChanged) + writeAllFields = ( + (saveComment && d->commentChanged) || + (saveDateTime && d->dateTimeChanged) || + (saveRating && d->ratingChanged) || + (saveTags && d->tagsChanged) + ); + else // PartialWrite + writeAllFields = false; + + if (saveComment && (writeAllFields || d->commentChanged)) + { + // Store comments in image as JFIF comments, Exif comments, and Iptc Comments. + dirty |= metadata.setImageComment(d->comment); + } + + if (saveDateTime && (writeAllFields || d->dateTimeChanged)) + { + // Store Image Date & Time as Exif and Iptc tags. + dirty |= metadata.setImageDateTime(d->dateTime, false); + } + + if (saveRating && (writeAllFields || d->ratingChanged)) + { + // Store Image rating as Iptc tag. + dirty |= metadata.setImageRating(d->rating); + } + + if (saveTags && (writeAllFields || d->tagsChanged)) + { + // Store tag paths as Iptc keywords tags. + + // DatabaseMode == ManagedTags is assumed. + // To fix this constraint (not needed currently), an oldKeywords parameter is needed + + // create list of keywords to be added and to be removed + TQStringList newKeywords, oldKeywords; + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + // it is important that MetadataDisjoint keywords are not touched + if (it.data() == MetadataAvailable) + { + // This works for single and multiple selection. + // In both situations, tags which had originally been loaded + // have explicitly been removed with setTag. + if (it.data().hasTag) + newKeywords.append(it.key()->tagPath(false)); + else + oldKeywords.append(it.key()->tagPath(false)); + } + } + // NOTE: See B.K.O #175321 : we remove all old keyword from IPTC and XMP before to + // synchronize metadata, else contents is not coherent. + dirty |= metadata.setImageKeywords(metadata.getImageKeywords(), newKeywords); + } + + if (settings.saveIptcPhotographerId && writeAllFields) + { + // Store Photograph identity into the Iptc tags. + dirty |= metadata.setImagePhotographerId(settings.iptcAuthor, + settings.iptcAuthorTitle); + } + + if (settings.saveIptcCredits && writeAllFields) + { + // Store Photograph identity into the Iptc tags. + dirty |= metadata.setImageCredits(settings.iptcCredit, + settings.iptcSource, + settings.iptcCopyright); + } + + return dirty; +} + +bool MetadataHub::write(const TQString &filePath, WriteMode writeMode, const MetadataWriteSettings &settings) +{ + // if no DMetadata object is needed at all, don't construct one - + // important optimization if writing to file is turned off in setup! + if (!needWriteMetadata(writeMode, settings)) + return false; + + DMetadata metadata(filePath); + if (write(metadata, writeMode, settings)) + { + bool success = metadata.applyChanges(); + ImageAttributesWatch::instance()->fileMetadataChanged(filePath); + return success; + } + return false; +} + +bool MetadataHub::write(DImg &image, WriteMode writeMode, const MetadataWriteSettings &settings) +{ + // if no DMetadata object is needed at all, don't construct one + if (!needWriteMetadata(writeMode, settings)) + return false; + + // See DImgLoader::readMetadata() and saveMetadata() + DMetadata metadata; + metadata.setComments(image.getComments()); + metadata.setExif(image.getExif()); + metadata.setIptc(image.getIptc()); + + if (write(metadata, writeMode, settings)) + { + // Do not insert null data into metaData map: + // Even if byte array is null, if there is a key in the map, it will + // be interpreted as "There was data, so write it again to the file". + if (!metadata.getComments().isNull()) + image.setComments(metadata.getComments()); + if (!metadata.getExif().isNull()) + image.setExif(metadata.getExif()); + if (!metadata.getIptc().isNull()) + image.setIptc(metadata.getIptc()); + return true; + } + return false; +} + +bool MetadataHub::needWriteMetadata(WriteMode writeMode, const MetadataWriteSettings &settings) const +{ + // This is the same logic as in write(DMetadata) but without actually writing. + // Adapt if the method above changes + + bool saveComment = (settings.saveComments && d->commentStatus == MetadataAvailable); + bool saveDateTime = (settings.saveDateTime && d->dateTimeStatus == MetadataAvailable); + bool saveRating = (settings.saveRating && d->ratingStatus == MetadataAvailable); + bool saveTags = false; + if (settings.saveIptcTags) + { + saveTags = false; + // find at least one tag to write + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + if (it.data() == MetadataAvailable) + { + saveTags = true; + break; + } + } + } + + bool writeAllFields; + if (writeMode == FullWrite) + writeAllFields = true; + else if (writeMode == FullWriteIfChanged) + writeAllFields = ( + (saveComment && d->commentChanged) || + (saveDateTime && d->dateTimeChanged) || + (saveRating && d->ratingChanged) || + (saveTags && d->tagsChanged) + ); + else // PartialWrite + writeAllFields = false; + + return ( + (saveComment && (writeAllFields || d->commentChanged)) || + (saveDateTime && (writeAllFields || d->dateTimeChanged)) || + (saveRating && (writeAllFields || d->ratingChanged)) || + (saveTags && (writeAllFields || d->tagsChanged)) || + (settings.saveIptcPhotographerId && writeAllFields) || + (settings.saveIptcCredits && writeAllFields) + ); +} + +MetadataWriteSettings MetadataHub::defaultWriteSettings() +{ + if (AlbumSettings::instance()) + return MetadataWriteSettings(AlbumSettings::instance()); + else + // is this check necessary? + return MetadataWriteSettings(); +} + +// -------------------------------------------------- + +MetadataHub::Status MetadataHub::dateTimeStatus() const +{ + return d->dateTimeStatus; +} + +MetadataHub::Status MetadataHub::commentStatus() const +{ + return d->commentStatus; +} + +MetadataHub::Status MetadataHub::ratingStatus() const +{ + return d->ratingStatus; +} + +MetadataHub::TagStatus MetadataHub::tagStatus(int albumId) const +{ + if (d->dbmode == NewTagsImport) + return TagStatus(MetadataInvalid); + return tagStatus(AlbumManager::instance()->findTAlbum(albumId)); +} + +MetadataHub::TagStatus MetadataHub::tagStatus(const TQString &tagPath) const +{ + if (d->dbmode == NewTagsImport) + return TagStatus(MetadataInvalid); + return tagStatus(AlbumManager::instance()->findTAlbum(tagPath)); +} + +MetadataHub::TagStatus MetadataHub::tagStatus(TAlbum *album) const +{ + if (!album) + return TagStatus(MetadataInvalid); + TQMap::iterator mapIt = d->tags.find(album); + if (mapIt == d->tags.end()) + return TagStatus(MetadataInvalid); + return mapIt.data(); +} + + +bool MetadataHub::dateTimeChanged() const +{ + return d->dateTimeChanged; +} + +bool MetadataHub::commentChanged() const +{ + return d->commentChanged; +} + +bool MetadataHub::ratingChanged() const +{ + return d->ratingChanged; +} + +bool MetadataHub::tagsChanged() const +{ + return d->tagsChanged; +} + +TQDateTime MetadataHub::dateTime() const +{ + return d->dateTime; +} + +TQString MetadataHub::comment() const +{ + return d->comment; +} + +int MetadataHub::rating() const +{ + return d->rating; +} + +void MetadataHub::dateTimeInterval(TQDateTime &lowest, TQDateTime &highest) const +{ + switch (d->dateTimeStatus) + { + case MetadataInvalid: + lowest = highest = TQDateTime(); + break; + case MetadataAvailable: + lowest = highest = d->dateTime; + break; + case MetadataDisjoint: + lowest = d->dateTime; + highest = d->lastDateTime; + break; + } +} + +void MetadataHub::ratingInterval(int &lowest, int &highest) const +{ + switch (d->ratingStatus) + { + case MetadataInvalid: + lowest = highest = -1; + break; + case MetadataAvailable: + lowest = highest = d->rating; + break; + case MetadataDisjoint: + lowest = d->rating; + highest = d->highestRating; + break; + } +} + +TQStringList MetadataHub::keywords() const +{ + if (d->dbmode == NewTagsImport) + return d->tagList; + else + { + TQStringList tagList; + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + if (it.data() == TagStatus(MetadataAvailable, true)) + tagList.append(it.key()->tagPath(false)); + } + return tagList; + } +} + +TQMap MetadataHub::tags() const +{ + // DatabaseMode == ManagedTags is assumed + return d->tags; +} + +TQMap MetadataHub::tagIDs() const +{ + // DatabaseMode == ManagedTags is assumed + TQMap intmap; + for (TQMap::iterator it = d->tags.begin(); it != d->tags.end(); ++it) + { + intmap.insert(it.key()->id(), it.data()); + } + return intmap; +} + + +// -------------------------------------------------- + +void MetadataHub::setDateTime(const TQDateTime &dateTime, Status status) +{ + d->dateTimeStatus = status; + d->dateTime = dateTime; + d->dateTimeChanged = true; +} + +void MetadataHub::setComment(const TQString &comment, Status status) +{ + d->commentStatus = status; + d->comment = comment; + d->commentChanged = true; +} + +void MetadataHub::setRating(int rating, Status status) +{ + d->ratingStatus = status; + d->rating = rating; + d->ratingChanged = true; +} + +void MetadataHub::setTag(TAlbum *tag, bool hasTag, Status status) +{ + // DatabaseMode == ManagedTags is assumed + d->tags[tag] = TagStatus(status, hasTag); + d->tagsChanged = true; +} + +void MetadataHub::setTag(int albumID, bool hasTag, Status status) +{ + // DatabaseMode == ManagedTags is assumed + TAlbum *album = AlbumManager::instance()->findTAlbum(albumID); + if (!album) + { + DWarning() << k_funcinfo << "Tag ID " << albumID << " not found in database." << endl; + return; + } + setTag(album, hasTag, status); +} + +void MetadataHub::resetChanged() +{ + d->dateTimeChanged = false; + d->commentChanged = false; + d->ratingChanged = false; + d->tagsChanged = false; +} + +} // namespace Digikam + diff --git a/src/digikam/metadatahub.h b/src/digikam/metadatahub.h new file mode 100644 index 00000000..decb148e --- /dev/null +++ b/src/digikam/metadatahub.h @@ -0,0 +1,362 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-05 + * Description : Metadata handling + * + * Copyright (C) 2007-2009 by Marcel Wiesweg + * Copyright (C) 2007-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef METADATAHUB_H +#define METADATAHUB_H + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "dmetadata.h" +#include "dimg.h" + +namespace Digikam +{ + +class AlbumSettings; +class ImageInfo; +class TAlbum; +class MetadataHubPriv; + +class MetadataWriteSettings +{ + /** + The class MetadataWriteSettings encapsulates all metadata related settings that are available + from the AlbumSettings. + This allows supply changed arguments to MetadataHub without changing the global settings + */ +public: + + /** + Constructs a MetadataWriteSettings object with all boolean values set to false, + all TQString values set to TQString() + */ + MetadataWriteSettings(); + + /** + Constructs a MetadataWriteSettings object from the given AlbumSettings object + */ + MetadataWriteSettings(AlbumSettings *albumsettings); + + bool saveComments; + bool saveDateTime; + bool saveRating; + bool saveIptcTags; + bool saveIptcPhotographerId; + bool saveIptcCredits; + + TQString iptcAuthor; + TQString iptcAuthorTitle; + TQString iptcCredit; + TQString iptcSource; + TQString iptcCopyright; +}; + +class MetadataHub +{ +public: + + /** + The status enum describes the result of joining several metadata sets. + If only one set has been added, the status is always MetadataAvailable. + If no set has been added, the status is always MetadataInvalid + */ + enum Status + { + MetadataInvalid, /// not yet filled with any value + MetadataAvailable, /// only one data set has been added, or a common value is available + MetadataDisjoint /// No common value is available. For rating and dates, the interval is available. + }; + + /** + Describes the complete status of a Tag: The metadata status, and the fact if it has the tag or not. + */ + class TagStatus + { + public: + TagStatus(Status status, bool hasTag = false) : status(status), hasTag(hasTag) {}; + TagStatus() : status(MetadataInvalid), hasTag(false) {}; + + Status status; + bool hasTag; + + bool operator==(TagStatus otherstatus) + { + return otherstatus.status == status && + otherstatus.hasTag == hasTag; + } + bool operator==(Status otherstatus) { return otherstatus == status; } + }; + + enum DatabaseMode + { + /** + Use this mode if + - the album manager is not available and/or + - metadata sets may contain tags that are not available from the AlbumManager + This situation occurs if new tags are imported from IPTC keywords. + This means that the album manager is not accessed, all methods depending on TAlbum* + (tags(), tagIDs(), setTag()) shall not be used. + The method write(ImageInfo*) will create not yet existing tags in the database. + */ + NewTagsImport, + /** + Use this mode if all tags are available from the AlbumManager. + This situation occurs if you load from ImageInfo objects. + All methods can be used. + */ + ManagedTags + }; + + enum WriteMode + { + /** + Write all available information + */ + FullWrite, + /** + Do a full write if and only if + - metadata fields changed + - the changed fields shall be written according to write settings + "Changed" in this context means changed by one of the set... methods, + the load() methods are ignored for this attribute. + This mode allows to avoid write operations when e.g. the user does not want + keywords to be written and only changes keywords. + */ + FullWriteIfChanged, + /** + Write only the changed parts. + Metadata fields which cannot be changed from MetadataHub (photographer ID etc.) + will never be written + */ + PartialWrite + }; + + /** + Constructs a MetadataHub. + @param dbmode Determines if the database may be accessed or not. See the enum description above. + */ + MetadataHub(DatabaseMode dbmode = ManagedTags); + ~MetadataHub(); + MetadataHub &operator=(const MetadataHub &); + MetadataHub(const MetadataHub &); + + void reset(); + + // -------------------------------------------------- + + /** + Add metadata information contained in the ImageInfo object. + This method (or in combination with the other load methods) + can be called multiple times on the same MetadataHub object. + In this case, the metadata will be combined. + */ + void load(ImageInfo *info); + + /** + Add metadata information from the DMetadata object + */ + void load(const DMetadata &metadata); + + /** + Load metadata information from the given file. + (Uses DMetadata, TQFileInfo) + @returns True if the metadata could be loaded + */ + bool load(const TQString &filePath); + + // -------------------------------------------------- + + /** + Applies the set of metadata contained in this MetadataHub + to the given ImageInfo object. + @return Returns true if the info object has been changed + */ + bool write(ImageInfo *info, WriteMode writeMode = FullWrite); + + /** + Applies the set of metadata contained in this MetadataHub + to the given DMetadata object. + The MetadataWriteSettings determine whether data is actually + set or not. + The following metadata fields may be set (depending on settings): + - Comment + - Date + - Rating + - Tags + - Photographer ID (data from settings) + - Credits (data from settings) + + The data fields taken from this MetadataHub object are only set if + their status is MetadataAvailable. + If the status is MetadataInvalid or MetadataDisjoint, the respective + metadata field is not touched. + @return Returns true if the metadata object has been touched + */ + bool write(DMetadata &metadata, WriteMode writeMode = FullWrite, + const MetadataWriteSettings &settings = defaultWriteSettings()); + + /** + Constructs a DMetadata object for given filePath, + calls the above method, writes the changes out to the file, + and notifies the ImageAttributesWatch. + @return Returns if the file has been touched + */ + bool write(const TQString &filePath, WriteMode writeMode = FullWrite, + const MetadataWriteSettings &settings = defaultWriteSettings()); + + /** + Constructs a DMetadata object from the metadata stored in the given DImg object, + calls the above method, and changes the stored metadata in the DImg object. + @return Returns if the DImg object has been touched + */ + bool write(DImg &image, WriteMode writeMode = FullWrite, + const MetadataWriteSettings &settings = defaultWriteSettings()); + + /** + Constructs a MetadataWriteSettings object from the global AlbumSettings object. + */ + static MetadataWriteSettings defaultWriteSettings(); + + // -------------------------------------------------- + + Status dateTimeStatus() const; + Status commentStatus() const; + Status ratingStatus() const; + + TagStatus tagStatus(TAlbum *album) const; + TagStatus tagStatus(int albumId) const; + TagStatus tagStatus(const TQString &tagPath) const; + + /** + Returns if the metadata field has been changed + with the corresponding set... method + */ + bool dateTimeChanged() const; + bool commentChanged() const; + bool ratingChanged() const; + bool tagsChanged() const; + + /** + Returns the dateTime. + If status is MetadataDisjoint, the earliest date is returned. + (see dateTimeInterval()) + If status is MetadataInvalid, an invalid date is returned. + */ + TQDateTime dateTime() const; + /** + Returns the dateTime. + If status is MetadataDisjoint, the first loaded comment is returned. + If status is MetadataInvalid, TQString() is returned. + */ + TQString comment() const; + /** + Returns the rating. + If status is MetadataDisjoint, the lowest rating is returned. + (see ratingInterval()) + If status is MetadataInvalid, -1 is returned. + */ + int rating() const; + + /** + Returns the earliest and latest date. + If status is MetadataAvailable, the values are the same. + If status is MetadataInvalid, invalid dates are returned. + */ + void dateTimeInterval(TQDateTime &lowest, TQDateTime &highest) const; + /** + Returns the lowest and highest rating. + If status is MetadataAvailable, the values are the same. + If status is MetadataInvalid, -1 is returned. + */ + void ratingInterval(int &lowest, int &highest) const; + + /** + Returns a TQStringList with all tags with status MetadataAvailable. + (i.e., the intersection of tags from all loaded metadata sets) + */ + TQStringList keywords() const; + + /** + Returns a map with the status for each tag. + Tags not contained in the list are considered to have the status MetadataInvalid, + that means no loaded metadata set contained this tag. + If a tag in the map has the status MetadataAvailable and it has the tag, + all loaded sets contained the tag. + If a tag in the map has the status MetadataAvailable and it does not have the tag, + no loaded sets contains this tag (has been explicitly set so) + If a tag in the map has the status MetadataDisjoint, some but not all loaded + sets contained the tag. The hasTag value is true then. + If MapMode (set in constructor) is false, returns an empty map. + */ + TQMap tags() const; + /** + Similar to the method above. + This method is less efficient internally. + */ + TQMap tagIDs() const; + + // -------------------------------------------------- + + /** + Set dateTime to the given value, and the dateTime status to MetadataAvailable + */ + void setDateTime(const TQDateTime &dateTime, Status status = MetadataAvailable); + void setComment(const TQString &comment, Status status = MetadataAvailable); + void setRating(int rating, Status status = MetadataAvailable); + void setTag(TAlbum *tag, bool hasTag, Status status = MetadataAvailable); + void setTag(int albumID, bool hasTag, Status status = MetadataAvailable); + + /** + Resets the information that metadata fields have been changed with one of the + set... methods (see commentChanged, dateTimeChanged etc.) + */ + void resetChanged(); + +private: + + void load(const TQDateTime &dateTime, const TQString &comment, int rating); + void loadTags(const TQValueList &loadedTags); + void loadTags(const TQStringList &loadedTagPaths); + bool needWriteMetadata(WriteMode writeMode, const MetadataWriteSettings &settings) const; + +private: + + MetadataHubPriv *d; +}; + +} // namespace Digikam + +#endif // METADATAHUB_H + diff --git a/src/digikam/mimefilter.cpp b/src/digikam/mimefilter.cpp new file mode 100644 index 00000000..80b3b501 --- /dev/null +++ b/src/digikam/mimefilter.cpp @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-10-22 + * Description : a widget to filter album contents by type mime + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "mimefilter.h" +#include "mimefilter.moc" + +namespace Digikam +{ + +class MimeFilterPriv +{ +public: + + MimeFilterPriv() + { + } +}; + +MimeFilter::MimeFilter(TQWidget* parent) + : TQComboBox(parent) +{ + d = new MimeFilterPriv; + insertItem( i18n("All Files"), AllFiles ); + insertItem( i18n("Image Files"), ImageFiles ); + insertItem( i18n("No RAW Files"), NoRAWFiles ); + insertItem( i18n("JPEG Files"), JPGFiles ); + insertItem( i18n("PNG Files"), PNGFiles ); + insertItem( i18n("TIFF Files"), TIFFiles ); + insertItem( i18n("RAW Files"), RAWFiles ); + insertItem( i18n("Movie Files"), MoviesFiles ); + insertItem( i18n("Audio Files"), AudioFiles ); + + TQToolTip::add(this, i18n("Filter for file type")); + TQWhatsThis::add(this, i18n("Select the file types (mime types) you want to show")); + + setMimeFilter(AllFiles); +} + +MimeFilter::~MimeFilter() +{ + delete d; +} + +void MimeFilter::setMimeFilter(int filter) +{ + setCurrentItem(filter); + emit activated(filter); +} + +int MimeFilter::mimeFilter() +{ + return currentItem(); +} + +} // namespace Digikam diff --git a/src/digikam/mimefilter.h b/src/digikam/mimefilter.h new file mode 100644 index 00000000..bddb5609 --- /dev/null +++ b/src/digikam/mimefilter.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-10-22 + * Description : a widget to filter album contents by type mime + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef MIMEFILTER_H +#define MIMEFILTER_H + +// TQt includes. + +#include + +namespace Digikam +{ + +class MimeFilterPriv; + +class MimeFilter : public TQComboBox +{ + TQ_OBJECT + +public: + + enum TypeMimeFilter + { + AllFiles = 0, + ImageFiles, + NoRAWFiles, + JPGFiles, + PNGFiles, + TIFFiles, + RAWFiles, + MoviesFiles, + AudioFiles + }; + +public: + + MimeFilter(TQWidget* parent); + ~MimeFilter(); + + void setMimeFilter(int filter); + int mimeFilter(); + +private: + + MimeFilterPriv* d; +}; + +} // namespace Digikam + +#endif // MIMEFILTER_H diff --git a/src/digikam/monthwidget.cpp b/src/digikam/monthwidget.cpp new file mode 100644 index 00000000..665318fd --- /dev/null +++ b/src/digikam/monthwidget.cpp @@ -0,0 +1,423 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-02 + * Description : a widget to perform month selection. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "imageinfo.h" +#include "albumlister.h" +#include "monthwidget.h" +#include "monthwidget.moc" + +namespace Digikam +{ + +class MonthWidgetPriv +{ +public: + + struct Month + { + bool active; + bool selected; + + int day; + int numImages; + }; + + MonthWidgetPriv() + { + active = true; + } + + bool active; + + int year; + int month; + int width; + int height; + int currw; + int currh; + + struct Month days[42]; +}; + +MonthWidget::MonthWidget(TQWidget* parent) + : TQFrame(parent, 0, TQt::WNoAutoErase) +{ + d = new MonthWidgetPriv; + init(); + + TQDate date = TQDate::currentDate(); + setYearMonth(date.year(), date.month()); + + setActive(false); +} + +MonthWidget::~MonthWidget() +{ + delete d; +} + +void MonthWidget::init() +{ + TQFont fn(font()); + fn.setBold(true); + fn.setPointSize(fn.pointSize()+1); + TQFontMetrics fm(fn); + TQRect r(fm.boundingRect("XX")); + r.setWidth(r.width() + 2); + r.setHeight(r.height() + 4); + d->width = r.width(); + d->height = r.height(); + + setMinimumWidth(d->width * 8); + setMinimumHeight(d->height * 9); +} + +void MonthWidget::setYearMonth(int year, int month) +{ + d->year = year; + d->month = month; + + for (int i=0; i<42; i++) + { + d->days[i].active = false; + d->days[i].selected = false; + d->days[i].day = -1; + d->days[i].numImages = 0; + } + + TQDate date(year, month, 1); + int s = date.dayOfWeek(); + + for (int i=s; i<(s+date.daysInMonth()); i++) + { + d->days[i-1].day = i-s+1; + } + + update(); +} + +TQSize MonthWidget::sizeHint() const +{ + return TQSize(d->width * 8, d->height * 9); +} + +void MonthWidget::resizeEvent(TQResizeEvent *e) +{ + TQWidget::resizeEvent(e); + + d->currw = contentsRect().width()/8; + d->currh = contentsRect().height()/9; +} + +void MonthWidget::drawContents(TQPainter *) +{ + TQRect cr(contentsRect()); + + TQPixmap pix(cr.width(), cr.height()); + + TQColorGroup cg = colorGroup(); + + TQFont fnBold(font()); + TQFont fnOrig(font()); + fnBold.setBold(true); + fnOrig.setBold(false); + + TQPainter p(&pix); + p.fillRect(0, 0, cr.width(), cr.height(), cg.background()); + + TQRect r(0, 0, d->currw, d->currh); + TQRect rsmall; + + int sx, sy; + int index = 0; + bool weekvisible; + for (int j=3; j<9; j++) + { + sy = d->currh * j; + weekvisible = false; + + for (int i=1; i<8; i++) + { + sx = d->currw * i; + r.moveTopLeft(TQPoint(sx,sy)); + rsmall = TQRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + if (d->days[index].day != -1) + { + if (d->days[index].selected) + { + p.fillRect(r, cg.highlight()); + p.setPen(cg.highlightedText()); + + if (d->days[index].active) + { + p.setFont(fnBold); + } + else + { + p.setFont(fnOrig); + } + } + else + { + if (d->days[index].active) + { + p.setPen(cg.text()); + p.setFont(fnBold); + } + else + { + p.setPen(cg.mid()); + p.setFont(fnOrig); + } + } + + p.drawText(rsmall, TQt::AlignVCenter|TQt::AlignHCenter, + TQString::number(d->days[index].day)); + + if(!weekvisible) + { + int weeknr = TDEGlobal::locale()->calendar()->weekNumber(TQDate(d->year, + d->month, d->days[index].day)); + p.setPen(d->active ? TQt::black : TQt::gray); + p.setFont(fnBold); + p.fillRect(1, sy, d->currw-1, d->currh-1, TQColor(210,210,210)); + p.drawText(1, sy, d->currw-1, d->currh-1, TQt::AlignVCenter|TQt::AlignHCenter, + TQString::number(weeknr)); + weekvisible = true; + } + + } + + index++; + } + } + + p.setPen(d->active ? TQt::black : TQt::gray); + + p.setFont(fnBold); + + sy = 2*d->currh; + for (int i=1; i<8; i++) + { + sx = d->currw * i; + r.moveTopLeft(TQPoint(sx+1,sy+1)); + rsmall = r; + rsmall.setWidth(r.width() - 2); + rsmall.setHeight(r.height() - 2); +#if KDE_IS_VERSION(3,2,0) + p.drawText(rsmall, TQt::AlignVCenter|TQt::AlignHCenter, + TDEGlobal::locale()->calendar()->weekDayName(i, true) + .remove(2,1)); +#else + p.drawText(rsmall, TQt::AlignVCenter|TQt::AlignHCenter, + TDEGlobal::locale()->weekDayName(i, true).remove(2,1)); +#endif + index++; + } + + r = TQRect(0, 0, cr.width(), 2*d->currh); + + fnBold.setPointSize(fnBold.pointSize()+2); + p.setFont(fnBold); + +#if KDE_IS_VERSION(3,2,0) + p.drawText(r, TQt::AlignCenter, + TQString("%1 %2") + .arg(TDEGlobal::locale()->calendar()->monthName(d->month, false)) + .arg(TDEGlobal::locale()->calendar()->year(TQDate(d->year,d->month,1)))); +#else + p.drawText(r, TQt::AlignCenter, + TQString("%1 %2") + .arg(TDEGlobal::locale()->monthName(d->month)) + .arg(TQString::number(d->year))); +#endif + + p.end(); + + bitBlt(this, cr.x(), cr.y(), &pix); +} + +void MonthWidget::mousePressEvent(TQMouseEvent *e) +{ + int firstSelected = 0, lastSelected = 0; + if (e->state() != TQt::ControlButton) + { + for (int i=0; i<42; i++) + { + if (d->days[i].selected) + { + if (firstSelected==0) + firstSelected = i; + lastSelected =i; + } + d->days[i].selected = false; + } + } + + TQRect r1(0, d->currh*3, d->currw, d->currh*6); + TQRect r2(d->currw, d->currh*3, d->currw*7, d->currh*6); + TQRect r3(d->currw, d->currh*2, d->currw*7, d->currh); + + // Click on a weekday + if( r3.contains(e->pos())) + { + int j = (e->pos().x() - d->currw)/d->currw; + for (int i=0; i<6; i++) + { + d->days[i*7+j].selected = !d->days[i*7+j].selected; + } + } + // Click on a week + else if (r1.contains(e->pos())) + { + int j = (e->pos().y() - 3*d->currh)/d->currh; + for (int i=0; i<7; i++) + { + d->days[j*7+i].selected = !d->days[j*7+i].selected; + } + } + // Click on a day. + else if (r2.contains(e->pos())) + { + int i, j; + i = (e->pos().x() - d->currw)/d->currw; + j = (e->pos().y() - 3*d->currh)/d->currh; + if (e->state() == TQt::ShiftButton) + { + int endSelection = j*7+i; + if (endSelection > firstSelected) + for (int i2=firstSelected ; i2 <= endSelection; i2++) + d->days[i2].selected = true; + else if (endSelection < firstSelected) + for (int i2=lastSelected ; i2 >= endSelection; i2--) + d->days[i2].selected = true; + } + else + d->days[j*7+i].selected = !d->days[j*7+i].selected; + } + + TQValueList filterDays; + for (int i=0; i<42; i++) + { + if (d->days[i].selected && d->days[i].day != -1) + filterDays.append(TQDateTime(TQDate(d->year, d->month, d->days[i].day), TQTime())); + } + + AlbumLister::instance()->setDayFilter(filterDays); + + update(); +} + +void MonthWidget::setActive(bool val) +{ + if (d->active == val) + return; + + d->active = val; + + if (d->active) + { + connect(AlbumLister::instance(), TQ_SIGNAL(signalNewItems(const ImageInfoList&)), + this, TQ_SLOT(slotAddItems(const ImageInfoList&))); + + connect(AlbumLister::instance(), TQ_SIGNAL(signalDeleteItem(ImageInfo*)), + this, TQ_SLOT(slotDeleteItem(ImageInfo*))); + } + else + { + TQDate date = TQDate::currentDate(); + setYearMonth(date.year(), date.month()); + AlbumLister::instance()->setDayFilter(TQValueList()); + + disconnect(AlbumLister::instance()); + } +} + +void MonthWidget::slotAddItems(const ImageInfoList& items) +{ + if (!d->active) + return; + + ImageInfo* item; + for (ImageInfoListIterator it(items); (item = it.current()); ++it) + { + TQDateTime dt = item->dateTime(); + + for (int i=0; i<42; i++) + { + if (d->days[i].day == dt.date().day()) + { + d->days[i].active = true; + d->days[i].numImages++; + break; + } + } + } + + update(); +} + +void MonthWidget::slotDeleteItem(ImageInfo* item) +{ + if (!d->active || !item) + return; + + TQDateTime dt = item->dateTime(); + + for (int i=0; i<42; i++) + { + if (d->days[i].day == dt.date().day()) + { + d->days[i].numImages--; + if (d->days[i].numImages <= 0) + { + d->days[i].active = false; + d->days[i].numImages = 0; + } + + break; + } + } + + update(); +} + +} // namespace Digikam diff --git a/src/digikam/monthwidget.h b/src/digikam/monthwidget.h new file mode 100644 index 00000000..70988c51 --- /dev/null +++ b/src/digikam/monthwidget.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-02 + * Description : a widget to perform month selection. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef MONTHWIDGET_H +#define MONTHWIDGET_H + +// TQt includes. + +#include + +// Local includes. + +#include "imageinfo.h" + +namespace Digikam +{ +class MonthWidgetPriv; + +class MonthWidget : public TQFrame +{ + TQ_OBJECT + +public: + + MonthWidget(TQWidget* parent); + ~MonthWidget(); + + void setYearMonth(int year, int month); + TQSize sizeHint() const; + + void setActive(bool val); + +protected: + + void resizeEvent(TQResizeEvent *e); + void drawContents(TQPainter *p); + void mousePressEvent(TQMouseEvent *e); + +private: + + void init(); + +private slots: + + void slotAddItems(const ImageInfoList& items); + void slotDeleteItem(ImageInfo* item); + +private: + + MonthWidgetPriv *d; +}; + +} // namespace Digikam + +#endif /* MONTHWIDGET_H */ diff --git a/src/digikam/pixmapmanager.cpp b/src/digikam/pixmapmanager.cpp new file mode 100644 index 00000000..3da11b5a --- /dev/null +++ b/src/digikam/pixmapmanager.cpp @@ -0,0 +1,252 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-14 + * Description : a pixmap manager for album icon view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +} + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "thumbnailjob.h" +#include "albumiconview.h" +#include "albumiconitem.h" +#include "albumsettings.h" +#include "pixmapmanager.h" +#include "pixmapmanager.moc" + +/** @file pixmapmanager.cpp */ + +namespace Digikam +{ + +class PixmapManagerPriv +{ + +public: + + PixmapManagerPriv() + { + size = 0; + cache = 0; + view = 0; + timer = 0; + thumbJob = 0; + } + + int size; + + TQCache *cache; + TQGuardedPtr thumbJob; + TQTimer *timer; + TQString thumbCacheDir; + + AlbumIconView *view; +}; + +PixmapManager::PixmapManager(AlbumIconView* view) +{ + d = new PixmapManagerPriv; + d->view = view; + d->cache = new TQCache(101, 211); + d->cache->setAutoDelete(true); + d->thumbCacheDir = TQDir::homeDirPath() + "/.thumbnails/"; + + d->timer = new TQTimer(this); + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotCompleted())); +} + +PixmapManager::~PixmapManager() +{ + delete d->timer; + + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + } + + delete d->cache; + delete d; +} + +void PixmapManager::setThumbnailSize(int size) +{ + if (d->size == size) + return; + + d->size = size; + d->cache->clear(); + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } +} + +TQPixmap* PixmapManager::find(const KURL& url) +{ + TQPixmap* pix = d->cache->find(url.path()); + if (pix) + return pix; + + if (d->thumbJob.isNull()) + { + d->thumbJob = new ThumbnailJob(url, d->size, true, AlbumSettings::instance()->getExifRotate()); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnail(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotFailedThumbnail(const KURL&))); + + connect(d->thumbJob, TQ_SIGNAL(signalCompleted()), + this, TQ_SLOT(slotCompleted())); + } + + return 0; +} + +void PixmapManager::remove(const KURL& url) +{ + d->cache->remove(url.path()); + + if (!d->thumbJob.isNull()) + d->thumbJob->removeItem(url); + + // remove the items from the thumbnail cache directory as well. + TQString uri = "file://" + TQDir::cleanDirPath(url.path()); + KMD5 md5(TQFile::encodeName(uri).data()); + uri = md5.hexDigest(); + + TQString smallThumbPath = d->thumbCacheDir + "normal/" + uri + ".png"; + TQString bigThumbPath = d->thumbCacheDir + "large/" + uri + ".png"; + + ::unlink(TQFile::encodeName(smallThumbPath)); + ::unlink(TQFile::encodeName(bigThumbPath)); +} + +void PixmapManager::clear() +{ + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + d->cache->clear(); +} + +void PixmapManager::slotGotThumbnail(const KURL& url, const TQPixmap& pix) +{ + d->cache->remove(url.path()); + TQPixmap* thumb = new TQPixmap(pix); + d->cache->insert(url.path(), thumb); + emit signalPixmap(url); +} + +void PixmapManager::slotFailedThumbnail(const KURL& url) +{ + TQImage img; + TQString ext = TQFileInfo(url.path()).extension(false); + + // Wrapper around mime type of item to get the right icon. + + AlbumSettings* settings = AlbumSettings::instance(); + if (settings) + { + if (settings->getImageFileFilter().upper().contains(ext.upper()) || + settings->getRawFileFilter().upper().contains(ext.upper())) + { + img = DesktopIcon("image-x-generic", TDEIcon::SizeEnormous).convertToImage(); + } + else if (settings->getMovieFileFilter().upper().contains(ext.upper())) + { + img = DesktopIcon("video-x-generic", TDEIcon::SizeEnormous).convertToImage(); + } + else if (settings->getAudioFileFilter().upper().contains(ext.upper())) + { + img = DesktopIcon("audio-x-generic", TDEIcon::SizeEnormous).convertToImage(); + } + } + + if (img.isNull()) + img = DesktopIcon("file_broken", TDEIcon::SizeEnormous).convertToImage(); + + // Resize icon to the right size depending of current settings. + + TQSize size(img.size()); + size.scale(d->size, d->size, TQSize::ScaleMin); + if (size.width() < img.width() && size.height() < img.height()) + { + // only scale down + // do not scale up, looks bad + img = img.smoothScale(size); + } + + d->cache->remove(url.path()); + TQPixmap* thumb = new TQPixmap(img); + d->cache->insert(url.path(), thumb); + emit signalPixmap(url); +} + +void PixmapManager::slotCompleted() +{ + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + AlbumIconItem* item = d->view->nextItemToThumbnail(); + if (!item) + return; + + find(item->imageInfo()->kurl()); +} + +int PixmapManager::cacheSize() const +{ + return d->cache->maxCost(); +} + +} // namespace Digikam diff --git a/src/digikam/pixmapmanager.h b/src/digikam/pixmapmanager.h new file mode 100644 index 00000000..62da7fa1 --- /dev/null +++ b/src/digikam/pixmapmanager.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-14 + * Description : a pixmap manager for album icon view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PIXMAPMANAGER_H +#define PIXMAPMANAGER_H + +// TQt includes. + +#include + +/** @file pixmapmanager.h */ + +class TQPixmap; + +class KURL; + +namespace Digikam +{ + +class AlbumIconView; +class PixmapManagerPriv; + +/** + * Since there are date based folders, the number of pixmaps which + * could be kept in memory could potentially become too large. The + * pixmapmanager maintains a fixed size cache of thumbnails and loads + * pixmaps on demand. + */ +class PixmapManager : public TQObject +{ + TQ_OBJECT + +public: + + PixmapManager(AlbumIconView* view); + ~PixmapManager(); + + TQPixmap* find(const KURL& path); + void remove(const KURL& path); + void clear(); + void setThumbnailSize(int size); + int cacheSize() const; + +signals: + + void signalPixmap(const KURL& url); + +private slots: + + void slotGotThumbnail(const KURL& url, const TQPixmap& pix); + void slotFailedThumbnail(const KURL& url); + void slotCompleted(); + +private: + + PixmapManagerPriv *d; +}; + +} // namespace Digikam + +#endif /* PIXMAPMANAGER_H */ diff --git a/src/digikam/ratingfilter.cpp b/src/digikam/ratingfilter.cpp new file mode 100644 index 00000000..1cfa0bbf --- /dev/null +++ b/src/digikam/ratingfilter.cpp @@ -0,0 +1,205 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-10-09 + * Description : a widget to filter album contents by rating + * + * Copyright (C) 2007 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "constants.h" +#include "ddebug.h" +#include "dcursortracker.h" +#include "themeengine.h" +#include "ratingfilter.h" +#include "ratingfilter.moc" + +namespace Digikam +{ + +class RatingFilterPriv +{ +public: + + RatingFilterPriv() + { + dirty = false; + ratingTracker = 0; + filterCond = AlbumLister::GreaterEqualCondition; + } + + bool dirty; + + DTipTracker *ratingTracker; + + AlbumLister::RatingCondition filterCond; +}; + +RatingFilter::RatingFilter(TQWidget* parent) + : RatingWidget(parent) +{ + d = new RatingFilterPriv; + + d->ratingTracker = new DTipTracker("", this); + updateRatingTooltip(); + setMouseTracking(true); + + TQWhatsThis::add(this, i18n("Select the rating value used to filter " + "albums contents. Use contextual pop-up menu to " + "set rating filter condition.")); + + // To dispatch signal from parent widget with filter condition. + connect(this, TQ_SIGNAL(signalRatingChanged(int)), + this, TQ_SLOT(slotRatingChanged())); +} + +RatingFilter::~RatingFilter() +{ + delete d->ratingTracker; + delete d; +} + +void RatingFilter::slotRatingChanged() +{ + emit signalRatingFilterChanged(rating(), d->filterCond); +} + +void RatingFilter::setRatingFilterCondition(AlbumLister::RatingCondition cond) +{ + d->filterCond = cond; + updateRatingTooltip(); + slotRatingChanged(); +} + +AlbumLister::RatingCondition RatingFilter::ratingFilterCondition() +{ + return d->filterCond; +} + +void RatingFilter::mouseMoveEvent(TQMouseEvent* e) +{ + // This method have been re-implemented to display and update the famous TipTracker contents. + + if ( d->dirty ) + { + int pos = e->x() / regPixmapWidth() +1; + + if (rating() != pos) + setRating(pos); + + updateRatingTooltip(); + } +} + +void RatingFilter::mousePressEvent(TQMouseEvent* e) +{ + // This method must be re-implemented to handle witch mousse button is pressed + // and show the rating filter settings pop-up menu with right mouse button. + // NOTE: Left and Middle Mouse buttons continue to change rating filter value. + + d->dirty = false; + + if ( e->button() == TQt::LeftButton || e->button() == TQt::MidButton ) + { + d->dirty = true; + int pos = e->x() / regPixmapWidth() +1; + + if (rating() == pos) + setRating(rating()-1); + else + setRating(pos); + updateRatingTooltip(); + } + else if (e->button() == TQt::RightButton) + { + // Show pop-up menu about Rating Filter condition settings + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("Rating Filter")); + popmenu.setCheckable(true); + popmenu.insertItem(i18n("Greater Equal Condition"), AlbumLister::GreaterEqualCondition); + popmenu.insertItem(i18n("Equal Condition"), AlbumLister::EqualCondition); + popmenu.insertItem(i18n("Less Equal Condition"), AlbumLister::LessEqualCondition); + popmenu.setItemChecked(d->filterCond, true); + + int choice = popmenu.exec((TQCursor::pos())); + switch(choice) + { + case AlbumLister::GreaterEqualCondition: + case AlbumLister::EqualCondition: + case AlbumLister::LessEqualCondition: + { + setRatingFilterCondition((AlbumLister::RatingCondition)choice); + break; + } + default: + break; + } + } +} + +void RatingFilter::mouseReleaseEvent(TQMouseEvent*) +{ + d->dirty = false; +} + +void RatingFilter::updateRatingTooltip() +{ + // Adapt tip message with rating filter condition settings. + + switch(d->filterCond) + { + case AlbumLister::GreaterEqualCondition: + { + d->ratingTracker->setText(i18n("Rating >= %1").arg(rating())); + break; + } + case AlbumLister::EqualCondition: + { + d->ratingTracker->setText(i18n("Rating = %1").arg(rating())); + break; + } + case AlbumLister::LessEqualCondition: + { + d->ratingTracker->setText(i18n("Rating <= %1").arg(rating())); + break; + } + default: + break; + } +} + +} // namespace Digikam diff --git a/src/digikam/ratingfilter.h b/src/digikam/ratingfilter.h new file mode 100644 index 00000000..ac527e49 --- /dev/null +++ b/src/digikam/ratingfilter.h @@ -0,0 +1,75 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-10-09 + * Description : a widget to filter album contents by rating + * + * Copyright (C) 2007 by Gilles Caulier + * Copyright (C) 2007 by Arnd Baecker + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RATINGFILTER_H +#define RATINGFILTER_H + +// Local includes. + +#include "albumlister.h" +#include "ratingwidget.h" + +namespace Digikam +{ + +class RatingFilterPriv; + +class RatingFilter : public RatingWidget +{ + TQ_OBJECT + +public: + + RatingFilter(TQWidget* parent); + ~RatingFilter(); + + void setRatingFilterCondition(AlbumLister::RatingCondition cond); + AlbumLister::RatingCondition ratingFilterCondition(); + +signals: + + void signalRatingFilterChanged(int, AlbumLister::RatingCondition); + +protected: + + void mousePressEvent(TQMouseEvent*); + void mouseMoveEvent(TQMouseEvent*); + void mouseReleaseEvent(TQMouseEvent*); + +private: + + void updateRatingTooltip(); + +private slots: + + void slotRatingChanged(); + +private: + + RatingFilterPriv* d; +}; + +} // namespace Digikam + +#endif // RATINGWIDGET_H diff --git a/src/digikam/ratingpopupmenu.cpp b/src/digikam/ratingpopupmenu.cpp new file mode 100644 index 00000000..a1d17bab --- /dev/null +++ b/src/digikam/ratingpopupmenu.cpp @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-02-02 + * Description : a pop-up menu to show stars rating selection. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "constants.h" +#include "themeengine.h" +#include "ratingpopupmenu.h" +#include "ratingpopupmenu.moc" + +namespace Digikam +{ + +RatingPopupMenu::RatingPopupMenu(TQWidget* parent) + : TQPopupMenu(parent) +{ + TDEGlobal::dirs()->addResourceType("digikam_rating", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString ratingPixPath = TDEGlobal::dirs()->findResourceDir("digikam_rating", "rating.png"); + ratingPixPath += "/rating.png"; + + insertItem(i18n("None"), 0); + + TQBitmap starbm(ratingPixPath); + TQBitmap clearbm(starbm.width(), starbm.height(), true); + + for (int i = 1 ; i <= RatingMax ; i++) + { + TQPixmap pix(starbm.width() * 5, starbm.height()); + pix.fill(ThemeEngine::instance()->textSpecialRegColor()); + TQBitmap mask(starbm.width() * 5, starbm.height()); + TQPainter painter(&mask); + painter.drawTiledPixmap(0, 0, + i*starbm.width(), pix.height(), + starbm); + painter.drawTiledPixmap(i*starbm.width(), 0, + 5*starbm.width()-i*starbm.width(), pix.height(), + clearbm); + painter.end(); + pix.setMask(mask); + insertItem(pix, i); + } +} + +RatingPopupMenu::~RatingPopupMenu() +{ +} + +} // namespace Digikam + diff --git a/src/digikam/ratingpopupmenu.h b/src/digikam/ratingpopupmenu.h new file mode 100644 index 00000000..14b2fef4 --- /dev/null +++ b/src/digikam/ratingpopupmenu.h @@ -0,0 +1,49 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-02-02 + * Description : a pop-up menu to show stars rating selection. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RATING_POPUP_MENU_H +#define RATING_POPUP_MENU_H + +// TQt includes. + +#include + +namespace Digikam +{ + +class RatingPopupMenuPriv; + +class RatingPopupMenu : public TQPopupMenu +{ + TQ_OBJECT + +public: + + RatingPopupMenu(TQWidget* parent=0); + ~RatingPopupMenu(); + +}; + +} // namespace Digikam + +#endif // RATING_POPUP_MENU_H diff --git a/src/digikam/ratingwidget.cpp b/src/digikam/ratingwidget.cpp new file mode 100644 index 00000000..3371a2c6 --- /dev/null +++ b/src/digikam/ratingwidget.cpp @@ -0,0 +1,195 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-08-15 + * Description : a widget to draw stars rating + * + * Copyright (C) 2005 by Owen Hirst + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "constants.h" +#include "themeengine.h" +#include "ratingwidget.h" +#include "ratingwidget.moc" + +namespace Digikam +{ + +class RatingWidgetPriv +{ +public: + + RatingWidgetPriv() + { + rating = 0; + } + + int rating; + + TQString ratingPixPath; + + TQPixmap disPixmap; + TQPixmap selPixmap; + TQPixmap regPixmap; +}; + +RatingWidget::RatingWidget(TQWidget* parent) + : TQWidget(parent) +{ + d = new RatingWidgetPriv; + + TDEGlobal::dirs()->addResourceType("digikam_rating", + TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + d->ratingPixPath = TDEGlobal::dirs()->findResourceDir("digikam_rating", "rating.png"); + d->ratingPixPath.append("/rating.png"); + + slotThemeChanged(); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); +} + +RatingWidget::~RatingWidget() +{ + delete d; +} + +void RatingWidget::setRating(int val) +{ + if (val < RatingMin || val > RatingMax) return; + + d->rating = val; + emit signalRatingChanged(d->rating); + update(); +} + +int RatingWidget::rating() const +{ + return d->rating; +} + +int RatingWidget::regPixmapWidth() const +{ + return d->regPixmap.width(); +} + +void RatingWidget::mouseMoveEvent(TQMouseEvent* e) +{ + int pos = e->x() / d->regPixmap.width() +1; + + if (d->rating != pos) + { + if (pos > RatingMax) // B.K.O.: # 151357 + pos = RatingMax; + if (pos < RatingMin) + pos = RatingMin; + d->rating = pos; + emit signalRatingChanged(d->rating); + update(); + } +} + +void RatingWidget::mousePressEvent(TQMouseEvent* e) +{ + int pos = e->x() / d->regPixmap.width() +1; + + if (d->rating == pos) + { + d->rating--; + } + else + { + d->rating = pos; + } + + emit signalRatingChanged(d->rating); + + update(); +} + +void RatingWidget::paintEvent(TQPaintEvent*) +{ + TQPainter p(this); + int x = 0; + + // Widget is disable : drawing grayed frame. + if (!isEnabled()) + { + for (int i=0; idisPixmap); + x += d->disPixmap.width(); + } + } + else + { + for (int i=0; irating; i++) + { + p.drawPixmap(x, 0, d->selPixmap); + x += d->selPixmap.width(); + } + + for (int i=d->rating; iregPixmap); + x += d->regPixmap.width(); + } + } + + p.end(); +} + +void RatingWidget::slotThemeChanged() +{ + d->regPixmap = TQPixmap(d->ratingPixPath); + d->selPixmap = d->regPixmap; + d->disPixmap = d->regPixmap; + + TQPainter painter(&d->regPixmap); + painter.fillRect(0, 0, d->regPixmap.width(), d->regPixmap.height(), + colorGroup().dark()); + painter.end(); + + TQPainter painter2(&d->selPixmap); + painter2.fillRect(0, 0, d->selPixmap.width(), d->selPixmap.height(), + ThemeEngine::instance()->textSpecialRegColor()); + painter2.end(); + + TQPainter painter3(&d->disPixmap); + painter3.fillRect(0, 0, d->disPixmap.width(), d->disPixmap.height(), + palette().disabled().foreground()); + painter3.end(); + + setFixedSize(TQSize(d->regPixmap.width()*5, d->regPixmap.height())); + update(); +} + +} // namespace Digikam diff --git a/src/digikam/ratingwidget.h b/src/digikam/ratingwidget.h new file mode 100644 index 00000000..a6a6ab57 --- /dev/null +++ b/src/digikam/ratingwidget.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-08-15 + * Description : a widget to draw stars rating + * + * Copyright (C) 2005 by Owen Hirst + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RATINGWIDGET_H +#define RATINGWIDGET_H + +// TQt includes. + +#include + +namespace Digikam +{ + +class RatingWidgetPriv; + +class RatingWidget : public TQWidget +{ + TQ_OBJECT + +public: + + RatingWidget(TQWidget* parent); + virtual ~RatingWidget(); + + void setRating(int val); + int rating() const; + +signals: + + void signalRatingChanged(int); + +protected: + + int regPixmapWidth() const; + + virtual void mousePressEvent(TQMouseEvent*); + virtual void mouseMoveEvent(TQMouseEvent*); + virtual void paintEvent(TQPaintEvent*); + +private slots: + + void slotThemeChanged(); + +private: + + RatingWidgetPriv* d; +}; + +} // namespace Digikam + +#endif // RATINGWIDGET_H diff --git a/src/digikam/scanlib.cpp b/src/digikam/scanlib.cpp new file mode 100644 index 00000000..71ea425c --- /dev/null +++ b/src/digikam/scanlib.cpp @@ -0,0 +1,541 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : scan pictures interface. + * + * Copyright (C) 2005-2006 by Tom Albers + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include +} + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dprogressdlg.h" +#include "dmetadata.h" +#include "albumdb.h" +#include "albummanager.h" +#include "splashscreen.h" +#include "scanlib.h" + +/** @file scanlib.cpp*/ + +namespace Digikam +{ + +ScanLib::ScanLib(SplashScreen *splash) +{ + m_splash = splash; + m_progressBar = new DProgressDlg(0); + m_progressBar->setInitialSize(TQSize(500, 100), true); + m_progressBar->setActionListVSBarVisible(false); + TQWhatsThis::add( m_progressBar, i18n("This shows the progress of the " + "scan. During the scan, all files on disk are registered in a " + "database. This is required for sorting by exif-date, and also speeds up " + "the overall performance of digiKam.") ); + + // these two lines prevent the dialog to be shown in + // findFoldersWhichDoNotExist() method. + m_progressBar->progressBar()->setTotalSteps(1); + m_progressBar->progressBar()->setProgress(1); +} + +ScanLib::~ScanLib() +{ + delete m_progressBar; +} + +void ScanLib::startScan() +{ + struct timeval tv1, tv2; + TQPixmap pix = TDEApplication::kApplication()->iconLoader()->loadIcon( + "system-run", TDEIcon::NoGroup, 32); + + TQString message = i18n("Finding non-existent Albums"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + findFoldersWhichDoNotExist(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + message = i18n("Finding items not in database"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + findMissingItems(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + message = i18n("Updating items without a date"); + if (m_splash) m_splash->message(message); + else m_progressBar->addedAction(pix, message); + gettimeofday(&tv1, 0); + updateItemsWithoutDate(); + gettimeofday(&tv2, 0); + timing(message, tv1, tv2); + + deleteStaleEntries(); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->setSetting("Scanned", TQDateTime::currentDateTime().toString(TQt::ISODate)); +} + +void ScanLib::findFoldersWhichDoNotExist() +{ + TQMap toBeDeleted; + TQString basePath(AlbumManager::instance()->getLibraryPath()); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + AlbumInfo::List aList = db->scanAlbums(); + + for (AlbumInfo::List::iterator it = aList.begin(); it != aList.end(); ++it) + { + AlbumInfo info = *it; + info.url = TQDir::cleanDirPath(info.url); + TQFileInfo fi(basePath + info.url); + if (!fi.exists() || !fi.isDir()) + { + toBeDeleted[info.url] = info.id; + } + } + + kapp->processEvents(); + + if (!toBeDeleted.isEmpty()) + { + int rc = KMessageBox::warningYesNoList(0, + i18n("

There is an album in the database which does not appear to " + "be on disk. This album should be removed from the database, " + "however you may lose information because all images " + "associated with this album will be removed from the database " + "as well.

" + "digiKam cannot continue without removing the items " + "from the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + "

There are %n albums in the database which do not appear to " + "be on disk. These albums should be removed from the database, " + "however you may lose information because all images " + "associated with these albums will be removed from the database " + "as well.

" + "digiKam cannot continue without removing the items " + "from the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + toBeDeleted.count()), + toBeDeleted.keys(), + i18n("Albums are Missing")); + + if (rc != KMessageBox::Yes) + exit(0); + + TQMapIterator it; + for (it = toBeDeleted.begin() ; it != toBeDeleted.end(); ++it) + { + DDebug() << "Removing Album: " << it.key() << endl; + db->deleteAlbum( it.data() ); + } + } +} + +void ScanLib::findMissingItems(const TQString &path) +{ + allFiles(path); +} + +void ScanLib::findMissingItems() +{ + TQString albumPath = AlbumManager::instance()->getLibraryPath(); + albumPath = TQDir::cleanDirPath(albumPath); + + m_progressBar->setAllowCancel( false ); + m_progressBar->showCancelButton (false ); + m_progressBar->progressBar()->setProgress( 0 ); + m_progressBar->setLabel(i18n("Scanning items, please wait...")); + m_progressBar->progressBar()->setTotalSteps( countItemsInFolder( albumPath ) ); + if (!m_splash) m_progressBar->show(); + kapp->processEvents(); + + TQDir dir(albumPath); + TQStringList fileList(dir.entryList(TQDir::Dirs)); + TQPixmap pix = TDEApplication::kApplication()->iconLoader()->loadIcon( + "folder_image", TDEIcon::NoGroup, 32); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->beginTransaction(); + + for (TQStringList::iterator it = fileList.begin(); it != fileList.end(); ++it) + { + if ((*it) == "." || (*it) == "..") + continue; + + TQString path = albumPath + '/' + (*it); + allFiles(path); + m_progressBar->addedAction(pix, path); + } + db->commitTransaction(); + + m_progressBar->hide(); + kapp->processEvents(); +} + +void ScanLib::updateItemsWithoutDate() +{ + AlbumDB* db = AlbumManager::instance()->albumDB(); + TQStringList urls = db->getAllItemURLsWithoutDate(); + + if (urls.isEmpty()) + { + m_progressBar->progressBar()->setTotalSteps(1); + m_progressBar->progressBar()->setProgress(1); + m_progressBar->hide(); + return; + } + + m_progressBar->setAllowCancel( false ); + m_progressBar->showCancelButton (false ); + m_progressBar->progressBar()->setProgress(0); + m_progressBar->progressBar()->setTotalSteps(urls.count()); + m_progressBar->setLabel(i18n("Updating items, please wait...")); + m_progressBar->show(); + kapp->processEvents(); + + TQString basePath = AlbumManager::instance()->getLibraryPath(); + basePath = TQDir::cleanDirPath(basePath); + + db->beginTransaction(); + + int counter=0; + for (TQStringList::iterator it = urls.begin(); it != urls.end(); ++it) + { + m_progressBar->progressBar()->advance(1); + ++counter; + if ( counter % 30 == 0 ) + { + kapp->processEvents(); + } + + TQFileInfo fi(*it); + TQString albumURL = fi.dirPath(); + albumURL = TQDir::cleanDirPath(albumURL.remove(basePath)); + + int albumID = db->getOrCreateAlbumId(albumURL); + + if (albumID <= 0) + { + DWarning() << "Album ID == -1: " << albumURL << endl; + } + + if (fi.exists()) + { + updateItemDate(albumURL, fi.fileName(), albumID); + } + else + { + TQPair fileID = qMakePair(fi.fileName(),albumID); + + if (m_filesToBeDeleted.findIndex(fileID) == -1) + { + m_filesToBeDeleted.append(fileID); + } + } + } + + db->commitTransaction(); + + m_progressBar->hide(); + kapp->processEvents(); +} + +int ScanLib::countItemsInFolder(const TQString& directory) +{ + int items = 0; + + TQDir dir( directory ); + if ( !dir.exists() or !dir.isReadable() ) + return 0; + + const TQFileInfoList *list = dir.entryInfoList(); + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + items += list->count(); + + while ( (fi = it.current()) != 0 ) + { + if ( fi->isDir() && + fi->fileName() != "." && + fi->fileName() != "..") + { + items += countItemsInFolder( fi->filePath() ); + } + + ++it; + } + + return items; +} + +void ScanLib::allFiles(const TQString& directory) +{ + TQDir dir( directory ); + if ( !dir.exists() or !dir.isReadable() ) + { + DWarning() << "Folder does not exist or is not readable: " + << directory << endl; + return; + } + + TQString basePath = AlbumManager::instance()->getLibraryPath(); + basePath = TQDir::cleanDirPath(basePath); + + TQString albumURL = directory; + albumURL = TQDir::cleanDirPath(albumURL.remove(basePath)); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + + int albumID = db->getOrCreateAlbumId(albumURL); + + if (albumID <= 0) + { + DWarning() << "Album ID == -1: " << albumURL << endl; + } + + TQStringList filesInAlbum = db->getItemNamesInAlbum( albumID ); + TQMap filesFoundInDB; + + for (TQStringList::iterator it = filesInAlbum.begin(); + it != filesInAlbum.end(); ++it) + { + filesFoundInDB.insert(*it, true); + } + + const TQFileInfoList *list = dir.entryInfoList(); + if (!list) + return; + + TQFileInfoListIterator it( *list ); + TQFileInfo *fi; + + m_progressBar->progressBar()->advance(list->count()); + kapp->processEvents(); + + while ( (fi = it.current()) != 0 ) + { + if ( fi->isFile()) + { + if (filesFoundInDB.contains(fi->fileName()) ) + { + filesFoundInDB.erase(fi->fileName()); + } + else + { + storeItemInDatabase(albumURL, fi->fileName(), albumID); + } + } + else if ( fi->isDir() && fi->fileName() != "." && fi->fileName() != "..") + { + allFiles( fi->filePath() ); + } + + ++it; + } + + // Removing items from the db which we did not see on disk. + if (!filesFoundInDB.isEmpty()) + { + TQMapIterator it; + for (it = filesFoundInDB.begin(); it != filesFoundInDB.end(); ++it) + { + if (m_filesToBeDeleted.findIndex(qMakePair(it.key(),albumID)) == -1) + { + m_filesToBeDeleted.append(qMakePair(it.key(),albumID)); + } + } + } +} + +void ScanLib::storeItemInDatabase(const TQString& albumURL, + const TQString& filename, + int albumID) +{ + // Do not store items found in the root of the albumdb + if (albumURL.isEmpty()) + return; + + TQString comment; + TQStringList keywords; + TQDateTime datetime; + int rating; + + TQString filePath( AlbumManager::instance()->getLibraryPath()); + filePath += albumURL + '/' + filename; + + DMetadata metadata(filePath); + + // Try to get comments from image : + // In first, from standard JPEG comments, or + // In second, from EXIF comments tag, or + // In third, from IPTC comments tag. + + comment = metadata.getImageComment(); + + // Try to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + // Try to get image rating from IPTC Urgency tag + // else use file system time stamp. + rating = metadata.getImageRating(); + + if ( !datetime.isValid() ) + { + TQFileInfo info( filePath ); + datetime = info.lastModified(); + } + + // Try to get image tags from IPTC keywords tags. + + keywords = metadata.getImageKeywords(); + + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + dbstore->addItem(albumID, filename, datetime, comment, rating, keywords); +} + +void ScanLib::updateItemDate(const TQString& albumURL, + const TQString& filename, + int albumID) +{ + TQDateTime datetime; + + TQString filePath( AlbumManager::instance()->getLibraryPath()); + filePath += albumURL + '/' + filename; + + DMetadata metadata(filePath); + + // Trying to get date and time from image : + // In first, from EXIF date & time tags, or + // In second, from IPTC date & time tags. + + datetime = metadata.getImageDateTime(); + + if ( !datetime.isValid() ) + { + TQFileInfo info( filePath ); + datetime = info.lastModified(); + } + + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + dbstore->setItemDate(albumID, filename, datetime); +} + +void ScanLib::deleteStaleEntries() +{ + TQStringList listToBeDeleted; + TQValueList< TQPair >::iterator it; + + for (it = m_filesToBeDeleted.begin() ; it != m_filesToBeDeleted.end(); ++it) + { + AlbumDB* dbstore = AlbumManager::instance()->albumDB(); + TQString location = " (" + dbstore->getAlbumURL((*it).second) + ')'; + + listToBeDeleted.append((*it).first + location); + } + + if ( !m_filesToBeDeleted.isEmpty() ) + { + int rc = KMessageBox::warningYesNoList(0, + i18n("

There is an item in the database which does not " + "appear to be on disk or is located in the root album of " + "the path. This file should be removed from the " + "database, however you may lose information.

" + "digiKam cannot continue without removing the item from " + "the database because all views depend on the information " + "in the database. Do you want it to be removed from the " + "database?", + "

There are %n items in the database which do not " + "appear to be on disk or are located in the root album of " + "the path. These files should be removed from the " + "database, however you may lose information.

" + "digiKam cannot continue without removing these items from " + "the database because all views depend on the information " + "in the database. Do you want them to be removed from the " + "database?", + listToBeDeleted.count()), + listToBeDeleted, + i18n("Files are Missing")); + + if (rc != KMessageBox::Yes) + exit(0); + + AlbumDB* db = AlbumManager::instance()->albumDB(); + db->beginTransaction(); + for (it = m_filesToBeDeleted.begin() ; it != m_filesToBeDeleted.end(); + ++it) + { + DDebug() << "Removing: " << (*it).first << " in " + << (*it).second << endl; + db->deleteItem( (*it).second, (*it).first ); + } + db->commitTransaction(); + } +} + +void ScanLib::timing(const TQString& text, struct timeval tv1, struct timeval tv2) +{ + DDebug() << "ScanLib: " + << text + ": " + << (((tv2.tv_sec-tv1.tv_sec)*1000000 + + (tv2.tv_usec-tv1.tv_usec))/1000) + << " ms" << endl; +} + +} // namespace Digikam diff --git a/src/digikam/scanlib.h b/src/digikam/scanlib.h new file mode 100644 index 00000000..5e58b29b --- /dev/null +++ b/src/digikam/scanlib.h @@ -0,0 +1,172 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : scan pictures interface. + * + * Copyright (C) 2005-2006 by Tom Albers + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SCANLIB_H +#define SCANLIB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +/** @file scanlib.h */ + +namespace Digikam +{ + +class DProgressDlg; +class SplashScreen; + +/** + * Class which is responsible for keeping the database in sync + * with the disk. Scanlib is a library that takes care of scanning the + * filesystem for new files and adds them in the database and checking + * for missing info in the database so that it can be included: if date + * is empty, it adds the exif or modification date (in that order) and + * the comment to database. If the file is not present in the database, + * make sure to add the file to the database and insert the date and + * comments. + */ +class DIGIKAM_EXPORT ScanLib +{ +public: + /** + * Constructor + */ + ScanLib(SplashScreen *splash=0); + + /** + * Destructor + */ + ~ScanLib(); + + /** + * This will execute findFoldersWhichDoNotExist(), + * findMissingItems() and updateItemsWithoutDate() + * and deletes all items from the database after confirmation. + */ + void startScan(); + + /** + * This checks if all albums in the database still existing + * on the disk + */ + void findFoldersWhichDoNotExist(); + + /** + * This calls allFiles with the albumPath. + */ + void findMissingItems(); + + + /** + * This calls allFiles with a given path. + * @param path the path to scan. + */ + void findMissingItems(const TQString &path); + + /** + * This queries the db for items that have no date + * for each item found, storeItemInDatabase is called. + */ + void updateItemsWithoutDate(); + +private: + /** + * This counts all existing files recursively, starting from + * directory. + * @param directory The path to the start searching from + * @return The amount of items + */ + int countItemsInFolder(const TQString& directory); + + /** + * This checks all existing files recursively, starting from + * directory. Calls storeItemInDatabase to store the found items + * which are not in the database. + * @param directory The path to the start searching from + */ + void allFiles(const TQString& directory); + + /** + * This fetches the exif-date or the modification date when + * the exif-date is not available and retrieves the JFIF-comment + * and calls AlbumDB::setItemDateComment to store the info in + * the db. + * @param albumURL The album path (relative to the + * album library Path) + * @param filename The filename of the item to store + * @param albumID The albumID as used in the database + */ + void storeItemInDatabase(const TQString& albumURL, + const TQString& filename, + int albumID); + + /** + * This fetches the exif-date or the modification date when + * the exif-date is not available and calls AlbumDB::setItemDate + * to store the info in + * the db. + * @param albumURL The album path (relative to the album library Path) + * @param filename The filename of the item to store + * @param albumID The albumID as used in the database + */ + void updateItemDate(const TQString& albumURL, + const TQString& filename, + int albumID); + + /** + * This will delete all items stored in m_filesToBeDeleted + */ + void deleteStaleEntries(); + + /** + * Member variable so we can update the progress bar everywhere + */ + DProgressDlg *m_progressBar; + + SplashScreen *m_splash; + + /** + * Member to store stale filesystem + */ + TQValueList< TQPair > m_filesToBeDeleted; + + /** + * This is used to print out some timings. + */ + void timing(const TQString& text, struct timeval tv1, struct timeval tv2); +}; + +} // namespace Digikam + +#endif /* SCANLIB_H */ diff --git a/src/digikam/searchadvanceddialog.cpp b/src/digikam/searchadvanceddialog.cpp new file mode 100644 index 00000000..cb40b87b --- /dev/null +++ b/src/digikam/searchadvanceddialog.cpp @@ -0,0 +1,664 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : a dialog to perform advanced search in albums + * + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file searchadvanceddialog.cpp */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "searchwidgets.h" +#include "searchresultsview.h" +#include "searchadvanceddialog.h" +#include "searchadvanceddialog.moc" + +namespace Digikam +{ + +class SearchAdvancedDialogPriv +{ +public: + + SearchAdvancedDialogPriv() + { + timer = 0; + title = 0; + optionsCombo = 0; + resultsView = 0; + ungroupButton = 0; + groupButton = 0; + delButton = 0; + addButton = 0; + rulesBox = 0; + } + + TQVGroupBox *rulesBox; + + TQPushButton *addButton; + TQPushButton *delButton; + TQPushButton *groupButton; + TQPushButton *ungroupButton; + + TQComboBox *optionsCombo; + + TQValueList baseList; + + TQTimer *timer; + + KLineEdit *title; + + SearchResultsView *resultsView; +}; + +SearchAdvancedDialog::SearchAdvancedDialog(TQWidget* parent, KURL& url) + : KDialogBase(parent, 0, true, i18n("Advanced Search"), + Help|Ok|Cancel, Ok, true), m_url(url) +{ + d = new SearchAdvancedDialogPriv; + d->timer = new TQTimer(this); + setHelp("advancedsearchtool.anchor", "digikam"); + + TQWidget *page = new TQWidget( this ); + setMainWidget(page); + + resize(configDialogSize("AdvancedSearch Dialog")); + + // ---------------------------------------------------------------- + // two columns, one for the rules, one for the preview. + + TQHBoxLayout* hbox = new TQHBoxLayout( page ); + TQVBoxLayout* leftSide = new TQVBoxLayout(); + d->resultsView = new SearchResultsView(page); + d->resultsView->setMinimumSize(TQSize(200, 200)); + TQWhatsThis::add(d->resultsView, i18n("

Here you can review the images found " + "using the current search settings.")); + hbox->addLayout(leftSide, 10); + hbox->setSpacing(spacingHint()); + hbox->addWidget(d->resultsView, 5); + + // ---------------------------------------------------------------- + // Box for all the rules + + d->rulesBox = new TQVGroupBox(i18n("Search Rules"), page); + TQWhatsThis::add(d->rulesBox, i18n("

Here you can review the search rules used to filter image-" + "searching in album library.")); + d->rulesBox->layout()->setSpacing( spacingHint() ); + d->rulesBox->layout()->setMargin( spacingHint() ); + d->rulesBox->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ); + d->rulesBox->layout()->setAlignment( TQt::AlignTop ); + + // ---------------------------------------------------------------- + // Box for the add/delete + + TQGroupBox *groupbox1 = new TQGroupBox( i18n("Add/Delete Option"), page, "groupbox1" ); + TQWhatsThis::add(groupbox1, i18n("

You can edit the search rules " + "by adding/removing criteria.")); + + groupbox1->setColumnLayout(0, TQt::Vertical ); + groupbox1->layout()->setSpacing( KDialog::spacingHint() ); + groupbox1->layout()->setMargin( KDialog::marginHint() ); + d->optionsCombo = new TQComboBox(groupbox1); + d->optionsCombo->insertItem(i18n("As well as"), 0); + d->optionsCombo->insertItem(i18n("Or"), 1); + d->optionsCombo->setEnabled(false); + + d->addButton = new TQPushButton(i18n("&Add"), groupbox1); + d->delButton = new TQPushButton(i18n("&Del"), groupbox1); + d->addButton->setIconSet(SmallIcon("add")); + d->delButton->setIconSet(SmallIcon("remove")); + + TQHBoxLayout* box1 = new TQHBoxLayout(groupbox1->layout()); + box1->addWidget(d->optionsCombo); + box1->addWidget(d->addButton); + box1->addStretch(10); + box1->addWidget(d->delButton); + + // ---------------------------------------------------------------- + // Box for the group/ungroup + + TQGroupBox *groupbox2 = new TQGroupBox( i18n("Group/Ungroup Options"), page, "groupbox2" ); + TQWhatsThis::add(groupbox1, i18n("

You can group or ungroup any search criteria " + "from the Search Rule set.")); + groupbox2->setColumnLayout(0, TQt::Vertical); + groupbox2->layout()->setSpacing( KDialog::spacingHint() ); + groupbox2->layout()->setMargin( KDialog::marginHint() ); + d->groupButton = new TQPushButton(i18n("&Group"), groupbox2); + d->ungroupButton = new TQPushButton(i18n("&Ungroup"), groupbox2); + + TQHBoxLayout* box2 = new TQHBoxLayout(groupbox2->layout()); + box2->addWidget(d->groupButton); + box2->addStretch(10); + box2->addWidget(d->ungroupButton); + + // ---------------------------------------------------------------- + // box for saving the search. + + TQGroupBox *groupbox3 = new TQGroupBox( page, "groupbox3"); + groupbox3->setColumnLayout(0, TQt::Vertical ); + groupbox3->layout()->setSpacing( KDialog::spacingHint() ); + groupbox3->setFrameStyle( TQFrame::NoFrame ); + TQLabel* label = new TQLabel(i18n("&Save search as: "), groupbox3); + d->title = new KLineEdit(groupbox3, "searchTitle"); + TQWhatsThis::add(d->title, i18n("

Enter the name used to save the current search in " + "\"My Searches\" view")); + + TQHBoxLayout* box3 = new TQHBoxLayout(groupbox3->layout()); + box3->addWidget(label); + box3->addWidget(d->title); + label->setBuddy(d->title); + + // ---------------------------------------------------------------- + + leftSide->addWidget( d->rulesBox ); + leftSide->addStretch(10); // Push the rulesbox to top and the buttons down. + leftSide->addWidget(groupbox1); + leftSide->addWidget(groupbox2); + leftSide->addWidget(groupbox3); + + // ---------------------------------------------------------------- + + if ( url.isEmpty() ) + { + d->title->setText(i18n("Last Search")); + slotAddRule(); + } + else + { + d->title->setText(url.queryItem("name")); + fillWidgets( url ); + } + + slotChangeButtonStates(); + d->timer->start(0, true); + + // ---------------------------------------------------------------- + + connect(d->addButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAddRule())); + + connect(d->delButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotDelRules())); + + connect(d->groupButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotGroupRules())); + + connect(d->ungroupButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotUnGroupRules())); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeOut())); + + connect(d->title, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotChangeButtonStates())); +} + +SearchAdvancedDialog::~SearchAdvancedDialog() +{ + saveDialogSize("AdvancedSearch Dialog"); + delete d->timer; + delete d; +} + +void SearchAdvancedDialog::slotAddRule() +{ + SearchAdvancedBase::Option type = SearchAdvancedBase::NONE; + if ( !d->baseList.isEmpty() ) + { + if (d->optionsCombo->currentItem() == 0 ) + type = SearchAdvancedBase::AND; + else + type = SearchAdvancedBase::OR; + } + + SearchAdvancedRule* rule = new SearchAdvancedRule( d->rulesBox, type ); + d->baseList.append(rule); + + connect( rule, TQ_SIGNAL( signalBaseItemToggled() ), + this, TQ_SLOT( slotChangeButtonStates() ) ); + + connect( rule, TQ_SIGNAL( signalPropertyChanged() ), + this, TQ_SLOT(slotPropertyChanged())); + + slotChangeButtonStates(); + slotPropertyChanged(); +} + +void SearchAdvancedDialog::slotDelRules() +{ + if (d->baseList.isEmpty()) + return; + + typedef TQValueList BaseList; + + BaseList itemsToRemove; + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + SearchAdvancedBase* base = *it; + if (base->isChecked()) + { + itemsToRemove.append(base); + } + } + + for (BaseList::iterator it = itemsToRemove.begin(); + it != itemsToRemove.end(); ++it) + { + SearchAdvancedBase* base = (SearchAdvancedBase*) *it; + d->baseList.remove(base); + delete base; + } + + BaseList::iterator it = d->baseList.begin(); + if (it != d->baseList.end()) + (*it)->removeOption(); + + slotChangeButtonStates(); + slotPropertyChanged(); + if (d->baseList.isEmpty()) + { + d->optionsCombo->setEnabled(false); + d->addButton->setEnabled(true); + enableButtonOK( false ); + } +} + +void SearchAdvancedDialog::slotGroupRules() +{ + typedef TQValueList BaseList; + + BaseList itemsToGroup; + BaseList groupsToUnGroupAndGroup; + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + SearchAdvancedBase* base = *it; + if ( base->isChecked() ) + { + itemsToGroup.append( base ); + if ( base->type() == SearchAdvancedBase::GROUP) + groupsToUnGroupAndGroup.append( base ); + } + } + + // ungroup every found group so it can be regrouped later on. + for (BaseList::iterator it = groupsToUnGroupAndGroup.begin(); + it != groupsToUnGroupAndGroup.end(); ++it) + { + SearchAdvancedGroup* group = (SearchAdvancedGroup*)*it; + BaseList::iterator itemsToGroupPos = itemsToGroup.find(group); + BaseList::iterator itPos = d->baseList.find(group); + TQValueList childRules = group->childRules(); + for (TQValueList::iterator iter = childRules.begin(); + iter != childRules.end(); ++iter) + { + d->baseList.insert(itPos, *iter); + itemsToGroup.insert(itemsToGroupPos, *iter); + } + group->removeRules(); + d->baseList.remove(group); + itemsToGroup.remove(group); + delete group; + } + + // if there is only one or no item return + if (itemsToGroup.size() < 2) + return; + + BaseList::iterator it = itemsToGroup.begin(); + SearchAdvancedRule* rule = (SearchAdvancedRule*)(*it); + + SearchAdvancedGroup* group = new SearchAdvancedGroup(d->rulesBox); + BaseList::iterator itPos = d->baseList.find(rule); + d->baseList.insert(itPos, group); + + for (BaseList::iterator it = itemsToGroup.begin(); + it != itemsToGroup.end(); ++it) + { + SearchAdvancedBase* base = *it; + if (base->type() == SearchAdvancedBase::RULE) + { + SearchAdvancedRule* rule = (SearchAdvancedRule*)base; + group->addRule(rule); + d->baseList.remove(rule); + } + } + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + d->rulesBox->layout()->remove((*it)->widget()); + d->rulesBox->layout()->add((*it)->widget()); + } + + connect( group, TQ_SIGNAL( signalBaseItemToggled() ), + this, TQ_SLOT( slotChangeButtonStates() ) ); + + slotChangeButtonStates(); + slotPropertyChanged(); +} + +void SearchAdvancedDialog::slotUnGroupRules() +{ + typedef TQValueList BaseList; + typedef TQValueList GroupList; + + GroupList itemsToUnGroup; + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + SearchAdvancedBase* base = *it; + if (base->type() == SearchAdvancedBase::GROUP && + base->isChecked()) + { + itemsToUnGroup.append((SearchAdvancedGroup*)base); + } + } + + if (itemsToUnGroup.isEmpty()) + return; + + for (GroupList::iterator it = itemsToUnGroup.begin(); + it != itemsToUnGroup.end(); ++it) + { + SearchAdvancedGroup *group = *it; + TQValueList childRules = group->childRules(); + + BaseList::iterator itPos = d->baseList.find(group); + + for (TQValueList::iterator iter = childRules.begin(); + iter != childRules.end(); ++iter) + { + d->baseList.insert(itPos, *iter); + } + + group->removeRules(); + d->baseList.remove(group); + delete group; + } + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + d->rulesBox->layout()->remove((*it)->widget()); + d->rulesBox->layout()->add((*it)->widget()); + } + + slotChangeButtonStates(); + slotPropertyChanged(); +} + +void SearchAdvancedDialog::slotPropertyChanged() +{ + d->timer->start(500, true); +} + +void SearchAdvancedDialog::slotOk() +{ + // calculate the latest url and name. + slotTimeOut(); + + // Since it's not possible to check the state of the ok button, + // check the state of the add button. + if ( d->addButton->isEnabled() ) + KDialogBase::slotOk(); +} + +void SearchAdvancedDialog::slotTimeOut() +{ + if (d->baseList.isEmpty()) + return; + + typedef TQValueList BaseList; + + TQString grouping; + int count = 0; + bool emptyVal = false; + + KURL url; + url.setProtocol("digikamsearch"); + + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + SearchAdvancedBase* base = *it; + if (base->type() == SearchAdvancedBase::RULE) + { + SearchAdvancedRule* rule = (SearchAdvancedRule*)base; + TQString val = rule->urlValue(); + if ( !val.isEmpty() ) + { + if (rule->option() != SearchAdvancedBase::NONE && + !count == 0 ) + grouping += (rule->option() == SearchAdvancedBase::AND) ? + " AND " : " OR "; + grouping += TQString::number(++count); + url.addQueryItem( TQString::number(count) + ".key", rule->urlKey()); + url.addQueryItem( TQString::number(count) + ".op", rule->urlOperator()); + url.addQueryItem( TQString::number(count) + ".val", val); + } + else + emptyVal = true; + } + else + { + SearchAdvancedGroup* group = (SearchAdvancedGroup*)base; + + TQString tempGrouping; + int curCount = count; + + TQValueList childRules = group->childRules(); + for (TQValueList::iterator iter = + childRules.begin(); + iter != childRules.end(); ++iter) + { + SearchAdvancedRule* rule = (SearchAdvancedRule*)(*iter); + TQString val = rule->urlValue(); + if ( !val.isEmpty() ) + { + if (rule->option() != SearchAdvancedBase::NONE && + !count == 0 ) + tempGrouping += (rule->option() == SearchAdvancedBase::AND) ? + " AND " : " OR "; + tempGrouping += TQString::number(++count); + url.addQueryItem( TQString::number(count) + ".key", rule->urlKey()); + url.addQueryItem( TQString::number(count) + ".op", rule->urlOperator()); + url.addQueryItem( TQString::number(count) + ".val", val); + } + else + emptyVal = true; + } + + if (!tempGrouping.isEmpty()) + { + if (group->option() != SearchAdvancedBase::NONE && + !curCount == 0 ) + grouping += (group->option() == SearchAdvancedBase::AND) ? + " AND " : " OR "; + grouping += " ( " + tempGrouping + " ) "; + } + } + } + + url.setPath(grouping); + url.addQueryItem("name", d->title->text()); + url.addQueryItem("count", TQString::number(count)); + m_url = url; + if (!count == 0) + d->resultsView->openURL( url ); + DDebug() << url << endl; + + if (!d->baseList.isEmpty()) + { + if (!d->title->text().isEmpty()) + enableButtonOK( !emptyVal ); + d->addButton->setEnabled( !emptyVal ); + d->optionsCombo->setEnabled( !emptyVal ); + } +} + +void SearchAdvancedDialog::slotChangeButtonStates() +{ + bool group = false; + int counter = 0; + + typedef TQValueList BaseList; + for (BaseList::iterator it = d->baseList.begin(); + it != d->baseList.end(); ++it) + { + SearchAdvancedBase* base = *it; + if (base->isChecked()) + { + ++counter; + if (base->type() == SearchAdvancedBase::GROUP) + group = true; + } + } + + d->ungroupButton->setEnabled( group ); + + if ( counter == 0) + { + d->delButton->setEnabled(false); + d->groupButton->setEnabled(false); + } + else if ( counter == 1) + { + if (d->baseList.count() > 1) + d->delButton->setEnabled(true); + d->groupButton->setEnabled(false); + } + else if ( counter > 1 ) + { + d->delButton->setEnabled(true); + d->groupButton->setEnabled(true); + } + + enableButtonOK( !d->title->text().isEmpty() ); +} + +void SearchAdvancedDialog::fillWidgets( const KURL& url ) +{ + int count = url.queryItem("count").toInt(); + if (count <= 0) + return; + + TQMap rulesMap; + + for (int i=1; i<=count; i++) + { + KURL newRule; + + TQString key = url.queryItem(TQString::number(i) + ".key"); + TQString op = url.queryItem(TQString::number(i) + ".op"); + TQString val = url.queryItem(TQString::number(i) + ".val"); + + newRule.setPath("1"); + newRule.addQueryItem("1.key",key); + newRule.addQueryItem("1.op",op); + newRule.addQueryItem("1.val",val); + + rulesMap.insert(i, newRule); + } + + TQStringList strList = TQStringList::split(" ", url.path()); + + SearchAdvancedGroup* group = 0; + bool groupingActive = false; + SearchAdvancedBase::Option type = SearchAdvancedBase::NONE; + + for ( TQStringList::Iterator it = strList.begin(); it != strList.end(); ++it ) + { + bool ok; + int num = (*it).toInt(&ok); + if (ok) + { + SearchAdvancedRule* rule = new SearchAdvancedRule( d->rulesBox, type ); + rule->setValues( rulesMap[num] ); + + connect( rule, TQ_SIGNAL( signalBaseItemToggled() ), + this, TQ_SLOT( slotChangeButtonStates() ) ); + + connect( rule, TQ_SIGNAL( signalPropertyChanged() ), + this, TQ_SLOT(slotPropertyChanged())); + + if (groupingActive) + group->addRule(rule); + else + d->baseList.append(rule); + } + else if (*it == "OR") + { + type = SearchAdvancedRule::OR; + } + else if (*it == "AND") + { + type = SearchAdvancedRule::AND; + } + else if (*it == "(") + { + group = new SearchAdvancedGroup(d->rulesBox); + d->baseList.append(group); + + connect( group, TQ_SIGNAL( signalBaseItemToggled() ), + this, TQ_SLOT( slotChangeButtonStates() ) ); + + groupingActive = true; + } + else if (*it == ")") + { + groupingActive = false; + } + else + { + DDebug() << "IGNORED:" << *it << endl; + } + } + + enableButtonOK( true ); +} + +} // namespace Digikam diff --git a/src/digikam/searchadvanceddialog.h b/src/digikam/searchadvanceddialog.h new file mode 100644 index 00000000..e4f233bd --- /dev/null +++ b/src/digikam/searchadvanceddialog.h @@ -0,0 +1,90 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : a dialog to perform advanced search in albums + * + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +/** @file searchadvanceddialog.h */ + +#ifndef SEARCHADVANCEDDIALOG_H +#define SEARCHADVANCEDDIALOG_H + +// KDE includes. + +#include + +class KURL; + +namespace Digikam +{ + +class SearchAdvancedDialogPriv; + +/** @class SearchAdvancedDialog + * + * This is the dialog for the advanced search + * @author Tom Albers + * @author Renchi Raju + * @author Gilles Caulier + * + */ +class SearchAdvancedDialog : public KDialogBase +{ + TQ_OBJECT + +public: + + /** + * Constructor + * @param parent parent window + * @param url holds the url for the search + */ + SearchAdvancedDialog(TQWidget* parent, KURL& url); + + /** + * Destructor + */ + ~SearchAdvancedDialog(); + +private slots: + + void fillWidgets(const KURL& url); + void slotAddRule(); + void slotDelRules(); + void slotGroupRules(); + void slotUnGroupRules(); + void slotChangeButtonStates(); + void slotTimeOut(); + void slotPropertyChanged(); + void slotOk(); + +private: + + KURL& m_url; + + SearchAdvancedDialogPriv* d; +}; + +} // namespace Digikam + +#endif /* SEARCHADVANCEDDIALOG_H */ diff --git a/src/digikam/searchfolderview.cpp b/src/digikam/searchfolderview.cpp new file mode 100644 index 00000000..c302d700 --- /dev/null +++ b/src/digikam/searchfolderview.cpp @@ -0,0 +1,488 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-21 + * Description : Searches folder view + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +#if KDE_IS_VERSION(3,2,0) +#include +#else +#include +#endif + +// Local includes. + +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "searchquickdialog.h" +#include "searchadvanceddialog.h" +#include "folderitem.h" +#include "searchfolderview.h" +#include "searchfolderview.moc" + +namespace Digikam +{ + +class SearchFolderItem : public FolderItem +{ + +public: + + SearchFolderItem(TQListView* parent, SAlbum* album) + : FolderItem(parent, album->title()), + m_album(album) + { + m_album->setExtraData(parent, this); + } + + ~SearchFolderItem() + { + m_album->removeExtraData(listView()); + } + + int compare(TQListViewItem* i, int , bool ) const + { + if (!i) + return 0; + + if (text(0) == i18n("Last Search")) + return -1; + + return text(0).localeAwareCompare(i->text(0)); + } + + int id() const + { + return m_album ? m_album->id() : 0; + } + + SAlbum* album() const + { + return m_album; + } + +private: + + SAlbum *m_album; +}; + +SearchFolderView::SearchFolderView(TQWidget* parent) + : FolderView(parent, "SearchFolderView") +{ + addColumn(i18n("My Searches")); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(false); + + m_lastAddedItem = 0; + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(clear())); + + connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(this, TQ_SIGNAL(doubleClicked(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotDoubleClicked(TQListViewItem*, const TQPoint&, int))); + + connect(this, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); +} + +SearchFolderView::~SearchFolderView() +{ + saveViewState(); +} + +void SearchFolderView::slotTextSearchFilterChanged(const TQString& filter) +{ + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList sList = AlbumManager::instance()->allSAlbums(); + for (AlbumList::iterator it = sList.begin(); it != sList.end(); ++it) + { + SAlbum* salbum = (SAlbum*)(*it); + SearchFolderItem* viewItem = (SearchFolderItem*) salbum->extraData(this); + + // Check if a special url query exist to identify a SAlbum dedicaced to Date Search + // used with TimeLine. + KURL url = salbum->kurl(); + TQString type = url.queryItem("type"); + + if (salbum->title().lower().contains(search) && + type != TQString("datesearch")) + { + atleastOneMatch = true; + + if (viewItem) + viewItem->setVisible(true); + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + } + } + } + + emit signalTextSearchFilterMatch(atleastOneMatch); +} + +void SearchFolderView::quickSearchNew() +{ + KURL url; + SearchQuickDialog dlg(this, url); + + if (dlg.exec() != KDialogBase::Accepted) + return; + + // Check if there is not already an album with that namespace + // and return if user aborts the dialog. + if ( ! checkName( url ) ) + return; + + SAlbum* album = AlbumManager::instance()->createSAlbum(url, true); + + if (album) + { + SearchFolderItem* searchItem = (SearchFolderItem*)(album->extraData(this)); + if (searchItem) + { + clearSelection(); + setSelected(searchItem, true); + slotSelectionChanged(); + } + } +} + +void SearchFolderView::extendedSearchNew() +{ + KURL url; + SearchAdvancedDialog dlg(this, url); + + if (dlg.exec() != KDialogBase::Accepted) + return; + + // Check if there is not already an album with that name + // and return if user aborts the dialog. + if ( ! checkName( url ) ) + return; + + SAlbum* album = AlbumManager::instance()->createSAlbum(url, false); + + if (album) + { + SearchFolderItem* searchItem = (SearchFolderItem*)(album->extraData(this)); + if (searchItem) + { + clearSelection(); + setSelected(searchItem, true); + slotSelectionChanged(); + } + } +} + +bool SearchFolderView::checkName( KURL& url ) +{ + TQString albumTitle = url.queryItem("name"); + AlbumManager* aManager = AlbumManager::instance(); + AlbumList aList = aManager->allSAlbums(); + bool checked = checkAlbum( albumTitle ); + + while ( !checked) + { + TQString label = i18n( "Search name already exists." + "\nPlease enter a new name:" ); + bool ok; +#if KDE_IS_VERSION(3,2,0) + TQString newTitle = KInputDialog::getText( i18n("Name exists"), label, + albumTitle, &ok, this ); +#else + TQString newTitle = KLineEditDlg::getText( i18n("Name exists"), label, + albumTitle, ok, this ); +#endif + if (!ok) + return false; + + albumTitle=newTitle; + checked = checkAlbum( albumTitle ); + } + + url.removeQueryItem( "name" ); + url.addQueryItem( "name", albumTitle ); + return true; +} + +bool SearchFolderView::checkAlbum( const TQString& name ) const +{ + + AlbumManager* aManager = AlbumManager::instance(); + AlbumList aList = aManager->allSAlbums(); + + for ( AlbumList::Iterator it = aList.begin(); + it != aList.end(); ++it ) + { + SAlbum *album = (SAlbum*)(*it); + if ( album->title() == name ) + return false; + } + return true; +} + +void SearchFolderView::quickSearchEdit(SAlbum* album) +{ + if (!album) + return; + + KURL url = album->kurl(); + SearchQuickDialog dlg(this, url); + + if (dlg.exec() != KDialogBase::Accepted) + return; + + AlbumManager::instance()->updateSAlbum(album, url); + + ((SearchFolderItem*)album->extraData(this))->setText(0, album->title()); + + clearSelection(); + setSelected((SearchFolderItem*)(album->extraData(this)), true); +} + +void SearchFolderView::extendedSearchEdit(SAlbum* album) +{ + if (!album) + return; + + KURL url = album->kurl(); + SearchAdvancedDialog dlg(this, url); + + if (dlg.exec() != KDialogBase::Accepted) + return; + + AlbumManager::instance()->updateSAlbum(album, url); + + ((SearchFolderItem*)album->extraData(this))->setText(0, album->title()); + + clearSelection(); + setSelected((SearchFolderItem*)(album->extraData(this)), true); +} + +void SearchFolderView::searchDelete(SAlbum* album) +{ + if (!album) + return; + + // Make sure that a complicated search is not deleted accidentally + int result = KMessageBox::warningYesNo(this, i18n("Are you sure you want to " + "delete the selected search " + "\"%1\"?") + .arg(album->title()), + i18n("Delete Search?"), + i18n("Delete"), + KStdGuiItem::cancel()); + + if (result != KMessageBox::Yes) + return; + + AlbumManager::instance()->deleteSAlbum(album); +} + +void SearchFolderView::slotAlbumAdded(Album* a) +{ + if (!a || a->type() != Album::SEARCH) + return; + + SAlbum* album = (SAlbum*)a; + + // Check if a special url query exist to identify a SAlbum dedicaced to Date Search + // used with TimeLine. In this case, SAlbum is not displayed here, but in TimeLineFolderView. + KURL url = album->kurl(); + TQString type = url.queryItem("type"); + if (type == TQString("datesearch")) return; + + SearchFolderItem* item = new SearchFolderItem(this, album); + item->setPixmap(0, SmallIcon("edit-find", AlbumSettings::instance()->getDefaultTreeIconSize())); + m_lastAddedItem = item; +} + +void SearchFolderView::slotAlbumDeleted(Album* a) +{ + if (!a || a->type() != Album::SEARCH) + return; + + SAlbum* album = (SAlbum*)a; + + SearchFolderItem* item = (SearchFolderItem*) album->extraData(this); + if (item) + delete item; +} + +void SearchFolderView::slotSelectionChanged() +{ + if (!active()) + return; + + TQListViewItem* selItem = 0; + + TQListViewItemIterator it( this ); + while (it.current()) + { + if (it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if (!selItem) + { + AlbumManager::instance()->setCurrentAlbum(0); + return; + } + + SearchFolderItem* searchItem = dynamic_cast(selItem); + + if (!searchItem || !searchItem->album()) + AlbumManager::instance()->setCurrentAlbum(0); + else + AlbumManager::instance()->setCurrentAlbum(searchItem->album()); +} + +void SearchFolderView::slotContextMenu(TQListViewItem* item, const TQPoint&, int) +{ + if (!item) + { + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("My Searches")); + popmenu.insertItem(SmallIcon("filefind"), i18n("New Simple Search..."), 10); + popmenu.insertItem(SmallIcon("edit-find"), i18n("New Advanced Search..."), 11); + + switch (popmenu.exec(TQCursor::pos())) + { + case 10: + { + quickSearchNew(); + break; + } + case 11: + { + extendedSearchNew(); + break; + } + default: + break; + } + } + else + { + SearchFolderItem* sItem = dynamic_cast(item); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("My Searches")); + popmenu.insertItem(SmallIcon("filefind"), i18n("Edit Search..."), 10); + + if ( sItem->album()->isSimple() ) + popmenu.insertItem(SmallIcon("edit-find"), i18n("Edit as Advanced Search..."), 11); + + popmenu.insertSeparator(-1); + popmenu.insertItem(SmallIcon("edit-delete"), i18n("Delete Search"), 12); + + switch (popmenu.exec(TQCursor::pos())) + { + case 10: + { + if (sItem->album()->isSimple()) + quickSearchEdit(sItem->album()); + else + extendedSearchEdit(sItem->album()); + break; + } + case 11: + { + extendedSearchEdit(sItem->album()); + break; + } + case 12: + { + searchDelete(sItem->album()); + break; + } + default: + break; + } + } +} + +void SearchFolderView::slotDoubleClicked(TQListViewItem* item, const TQPoint&, int) +{ + if (!item) + return; + + SearchFolderItem* sItem = dynamic_cast(item); + + if (sItem->album()->isSimple()) + quickSearchEdit(sItem->album()); + else + extendedSearchEdit(sItem->album()); +} + +void SearchFolderView::selectItem(int id) +{ + SAlbum *album = AlbumManager::instance()->findSAlbum(id); + if(!album) + return; + + SearchFolderItem *item = (SearchFolderItem*)album->extraData(this); + if(item) + { + setSelected(item, true); + ensureItemVisible(item); + } +} + +} // namespace Digikam diff --git a/src/digikam/searchfolderview.h b/src/digikam/searchfolderview.h new file mode 100644 index 00000000..8dab4ca0 --- /dev/null +++ b/src/digikam/searchfolderview.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-21 + * Description : Searches folder view + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SEARCHFOLDERVIEW_H +#define SEARCHFOLDERVIEW_H + +// Local includes. + +#include "folderview.h" + +namespace Digikam +{ + +class SAlbum; +class SearchFolderItem; + +class SearchFolderView : public FolderView +{ + TQ_OBJECT + +public: + + SearchFolderView(TQWidget* parent); + ~SearchFolderView(); + + void quickSearchNew(); + void extendedSearchNew(); + + void quickSearchEdit(SAlbum* album); + void extendedSearchEdit(SAlbum* album); + + void searchDelete(SAlbum* album); + +signals: + + void signalTextSearchFilterMatch(bool); + +public slots: + + void slotTextSearchFilterChanged(const TQString&); + +private slots: + + void slotAlbumAdded(Album* album); + void slotAlbumDeleted(Album* album); + void slotSelectionChanged(); + void slotContextMenu(TQListViewItem*, const TQPoint&, int); + void slotDoubleClicked(TQListViewItem*, const TQPoint&, int); + +protected: + + void selectItem(int id); + +private: + + bool checkName( KURL& url ); + bool checkAlbum( const TQString& name ) const; + +private: + + SearchFolderItem* m_lastAddedItem; +}; + +} // namespace Digikam + +#endif /* SEARCHFOLDERVIEW_H */ diff --git a/src/digikam/searchquickdialog.cpp b/src/digikam/searchquickdialog.cpp new file mode 100644 index 00000000..9cca5dfc --- /dev/null +++ b/src/digikam/searchquickdialog.cpp @@ -0,0 +1,200 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-19 + * Description : a dialog to perform simple search in albums + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file searchquickdialog.cpp */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "searchtextbar.h" +#include "searchresultsview.h" +#include "searchquickdialog.h" +#include "searchquickdialog.moc" + +namespace Digikam +{ + +class SearchQuickDialogPriv +{ +public: + + SearchQuickDialogPriv() + { + timer = 0; + searchEdit = 0; + nameEdit = 0; + resultsView = 0; + } + + TQTimer *timer; + + KLineEdit *nameEdit; + + SearchTextBar *searchEdit; + + SearchResultsView *resultsView; +}; + +SearchQuickDialog::SearchQuickDialog(TQWidget* parent, KURL& url) + : KDialogBase(Plain, i18n("Quick Search"), Help|Ok|Cancel, Ok, + parent, 0, true, true), m_url(url) +{ + d = new SearchQuickDialogPriv; + d->timer = new TQTimer(this); + setHelp("quicksearchtool.anchor", "digikam"); + + TQGridLayout* grid = new TQGridLayout(plainPage(), 2, 2, 0, spacingHint()); + + TQLabel *label1 = new TQLabel("" + i18n("Search:") + "", plainPage()); + d->searchEdit = new SearchTextBar(plainPage(), "SearchQuickDialogSearchEdit", i18n("Enter here your search criteria")); + TQWhatsThis::add( d->searchEdit, i18n("

Enter your search criteria to find items in the album library")); + + d->resultsView = new SearchResultsView(plainPage()); + d->resultsView->setMinimumSize(320, 200); + TQWhatsThis::add( d->resultsView, i18n("

Here you can see the items found in album library, " + "using the current search criteria")); + + TQLabel *label2 = new TQLabel(i18n("Save search as:"), plainPage()); + d->nameEdit = new KLineEdit(plainPage()); + d->nameEdit->setText(i18n("Last Search")); + TQWhatsThis::add( d->nameEdit, i18n("

Enter the name of the current search to save in the " + "\"My Searches\" view")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(d->searchEdit, 0, 0, 1, 2); + grid->addMultiCellWidget(d->resultsView, 1, 1, 0, 2); + grid->addMultiCellWidget(label2, 2, 2, 0, 1); + grid->addMultiCellWidget(d->nameEdit, 2, 2, 2, 2); + + connect(d->searchEdit, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotSearchChanged(const TQString&))); + + connect(d->resultsView, TQ_SIGNAL(signalSearchResultsMatch(bool)), + d->searchEdit, TQ_SLOT(slotSearchResult(bool))); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeOut())); + + enableButtonOK(false); + resize(configDialogSize("QuickSearch Dialog")); + + // check if we are being passed a valid url + if (m_url.isValid()) + { + int count = m_url.queryItem("count").toInt(); + if (count > 0) + { + TQStringList strList; + + for (int i=1; i<=count; i++) + { + TQString val = m_url.queryItem(TQString::number(i) + ".val"); + if (!strList.contains(val)) + { + strList.append(val); + } + } + + d->searchEdit->setText(strList.join(" ")); + d->nameEdit->setText(url.queryItem("name")); + d->timer->start(0, true); + } + } +} + +SearchQuickDialog::~SearchQuickDialog() +{ + saveDialogSize(("QuickSearch Dialog")); + delete d->timer; + delete d; +} + +void SearchQuickDialog::slotTimeOut() +{ + if (d->searchEdit->text().isEmpty()) + { + d->resultsView->clear(); + enableButtonOK(false); + return; + } + + enableButtonOK(true); + + KURL url; + url.setProtocol("digikamsearch"); + + TQString path, num; + int count = 0; + + TQStringList textList = TQStringList::split(' ', d->searchEdit->text()); + for (TQStringList::iterator it = textList.begin(); it != textList.end(); ++it) + { + if (count != 0) + path += " AND "; + + path += TQString(" %1 ").arg(count + 1); + + num = TQString::number(++count); + url.addQueryItem(num + ".key", "keyword"); + url.addQueryItem(num + ".op", "like"); + url.addQueryItem(num + ".val", *it); + } + + url.setPath(path); + url.addQueryItem("name", "Live Search"); + url.addQueryItem("count", num); + + m_url = url; + d->resultsView->openURL(url); +} + +void SearchQuickDialog::slotSearchChanged(const TQString&) +{ + d->timer->start(500, true); +} + +void SearchQuickDialog::hideEvent(TQHideEvent* e) +{ + m_url.removeQueryItem("name"); + m_url.addQueryItem("name", d->nameEdit->text().isEmpty() ? + i18n("Last Search") : d->nameEdit->text()); + KDialogBase::hideEvent(e); +} + +} // namespace Digikam diff --git a/src/digikam/searchquickdialog.h b/src/digikam/searchquickdialog.h new file mode 100644 index 00000000..3412283e --- /dev/null +++ b/src/digikam/searchquickdialog.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-19 + * Description : a dialog to perform simple search in albums + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file searchquickdialog.h */ + +#ifndef SEARCHQUICKDIALOG_H +#define SEARCHQUICKDIALOG_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +class KURL; + +namespace Digikam +{ + +class SearchQuickDialogPriv; + +/** @class SearchQuickDialog + * + * This is the dialog for the quick search + * @author Renchi Raju + * + */ +class SearchQuickDialog : public KDialogBase +{ + TQ_OBJECT + +public: + + /** + * Constructor + * @param parent parent window + * @param url holds the url for the search + */ + SearchQuickDialog(TQWidget* parent, KURL& url); + /** + * Destructor + */ + ~SearchQuickDialog(); + +protected: + + void hideEvent(TQHideEvent* e); + +private slots: + + void slotTimeOut(); + void slotSearchChanged(const TQString&); + +private: + + KURL& m_url; + + SearchQuickDialogPriv* d; + +}; + +} // namespace Digikam + +#endif /* SEARCHQUICKDIALOG_H */ diff --git a/src/digikam/searchresultsitem.cpp b/src/digikam/searchresultsitem.cpp new file mode 100644 index 00000000..d3f2ab13 --- /dev/null +++ b/src/digikam/searchresultsitem.cpp @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-20 + * Description : search results item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// Local includes. + +#include "searchresultsitem.h" + +namespace Digikam +{ + +TQPixmap* SearchResultsItem::m_basePixmap = 0; + +SearchResultsItem::SearchResultsItem(TQIconView* view, const TQString& path) + : TQIconViewItem(view), m_path(path) +{ + if (!m_basePixmap) + { + m_basePixmap = new TQPixmap(128, 128); + m_basePixmap->fill(view->colorGroup().base()); + + TQPainter p(m_basePixmap); + p.setPen(TQt::lightGray); + p.drawRect(0, 0, 128, 128); + p.end(); + } + + setPixmap(*m_basePixmap); + calcRect(); + m_marked = true; +} + +SearchResultsItem::~SearchResultsItem() +{ + +} + +void SearchResultsItem::calcRect(const TQString&) +{ + TQRect r(0,0,0,0); + setTextRect(r); + setPixmapRect(r); + setItemRect(TQRect(x(),y(),130,130)); +} + +void SearchResultsItem::paintItem(TQPainter* p, const TQColorGroup&) +{ + TQRect r(rect()); + p->drawPixmap(r.x() + (width()-pixmap()->width())/2 , + r.y() + (height()-pixmap()->height())/2, + *pixmap()); +} + +void SearchResultsItem::paintFocus(TQPainter* p, const TQColorGroup&) +{ + TQRect r(rect()); + p->save(); + p->setPen(TQPen(TQt::darkGray, 0, TQt::DotLine)); + p->drawRect(rect()); + p->restore(); +} + +} // namespace Digikam diff --git a/src/digikam/searchresultsitem.h b/src/digikam/searchresultsitem.h new file mode 100644 index 00000000..39d6562d --- /dev/null +++ b/src/digikam/searchresultsitem.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-20 + * Description : search results item. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * ============================================================ */ + +#ifndef SEARCHRESULTSITEM_H +#define SEARCHRESULTSITEM_H + +// TQt includes. + +#include + +namespace Digikam +{ + +class SearchResultsItem : public TQIconViewItem +{ + friend class SearchResultsView; + +public: + + SearchResultsItem(TQIconView* view, const TQString& path); + ~SearchResultsItem(); + +protected: + + void calcRect(const TQString& text = TQString()); + void paintItem (TQPainter * p, const TQColorGroup & cg); + void paintFocus (TQPainter * p, const TQColorGroup & cg); + +private: + + static TQPixmap* m_basePixmap; + bool m_marked; + TQString m_path; +}; + +} // namespace Digikam + +#endif /* SEARCHRESULTSITEM_H */ diff --git a/src/digikam/searchresultsview.cpp b/src/digikam/searchresultsview.cpp new file mode 100644 index 00000000..968b14a8 --- /dev/null +++ b/src/digikam/searchresultsview.cpp @@ -0,0 +1,211 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-20 + * Description : search results view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "thumbnailjob.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "searchresultsitem.h" +#include "searchresultsview.h" +#include "searchresultsview.moc" + +namespace Digikam +{ + +class SearchResultsViewPriv +{ +public: + + SearchResultsViewPriv() + { + listJob = 0; + thumbJob = 0; + } + + TQString libraryPath; + TQString filter; + + TQDict itemDict; + + TQGuardedPtr thumbJob; + + TDEIO::TransferJob* listJob; +}; + +SearchResultsView::SearchResultsView(TQWidget* parent) + : TQIconView(parent) +{ + d = new SearchResultsViewPriv; + d->libraryPath = AlbumManager::instance()->getLibraryPath(); + d->filter = AlbumSettings::instance()->getAllFileFilter(); + + setAutoArrange(true); + setResizeMode(TQIconView::Adjust); +} + +SearchResultsView::~SearchResultsView() +{ + if (!d->thumbJob.isNull()) + d->thumbJob->kill(); + if (d->listJob) + d->listJob->kill(); + + delete d; +} + +void SearchResultsView::openURL(const KURL& url) +{ + if (d->listJob) + d->listJob->kill(); + d->listJob = 0; + + if (!d->thumbJob.isNull()) + d->thumbJob->kill(); + d->thumbJob = 0; + + TQByteArray ba; + TQDataStream ds(ba, IO_WriteOnly); + ds << d->libraryPath; + ds << url; + ds << d->filter; + ds << 0; // getting dimensions (not needed here) + ds << 0; // recursive sub-album (not needed here) + ds << 0; // recursive sub-tags (not needed here) + ds << 2; // miniListing (Use 1 for full listing) + + d->listJob = new TDEIO::TransferJob(url, TDEIO::CMD_SPECIAL, + ba, TQByteArray(), false); + + connect(d->listJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotResult(TDEIO::Job*))); + + connect(d->listJob, TQ_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), + this, TQ_SLOT(slotData(TDEIO::Job*, const TQByteArray&))); +} + +void SearchResultsView::clear() +{ + if (d->listJob) + d->listJob->kill(); + d->listJob = 0; + + if (!d->thumbJob.isNull()) + d->thumbJob->kill(); + d->thumbJob = 0; + + d->itemDict.clear(); + TQIconView::clear(); +} + +void SearchResultsView::slotData(TDEIO::Job*, const TQByteArray &data) +{ + for (TQIconViewItem* item = firstItem(); item; item = item->nextItem()) + ((SearchResultsItem*)item)->m_marked = false; + + KURL::List ulist; + + TQString path; + TQDataStream ds(data, IO_ReadOnly); + while (!ds.atEnd()) + { + ds >> path; + + SearchResultsItem* existingItem = (SearchResultsItem*) d->itemDict.find(path); + if (existingItem) + { + existingItem->m_marked = true; + continue; + } + + SearchResultsItem* item = new SearchResultsItem(this, path); + d->itemDict.insert(path, item); + + ulist.append(KURL(path)); + } + + SearchResultsItem* item = (SearchResultsItem*)firstItem(); + TQIconViewItem* nextItem; + while (item) + { + nextItem = item->nextItem(); + if (!item->m_marked) + { + d->itemDict.remove(item->m_path); + delete item; + } + item = (SearchResultsItem*)nextItem; + } + arrangeItemsInGrid(); + + bool match = !ulist.isEmpty(); + + emit signalSearchResultsMatch(match); + + if (match) + { + d->thumbJob = new ThumbnailJob(ulist, 128, true, true); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnail(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotFailedThumbnail(const KURL&))); + } +} + +void SearchResultsView::slotResult(TDEIO::Job *job) +{ + if (job->error()) + job->showErrorDialog(this); + d->listJob = 0; +} + +void SearchResultsView::slotGotThumbnail(const KURL& url, const TQPixmap& pix) +{ + TQIconViewItem* i = d->itemDict.find(url.path()); + if (i) + i->setPixmap(pix); + + d->thumbJob = 0; +} + +void SearchResultsView::slotFailedThumbnail(const KURL&) +{ + d->thumbJob = 0; +} + +} // namespace Digikam diff --git a/src/digikam/searchresultsview.h b/src/digikam/searchresultsview.h new file mode 100644 index 00000000..a3846a28 --- /dev/null +++ b/src/digikam/searchresultsview.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-20 + * Description : search results view. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SEARCHRESULTSVIEW_H +#define SEARCHRESULTSVIEW_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +class TQPixmap; + +class KFileItem; + +namespace TDEIO +{ +class TransferJob; +class Job; +} + +namespace Digikam +{ + +class SearchResultsViewPriv; + +class SearchResultsView : public TQIconView +{ + TQ_OBJECT + +public: + + SearchResultsView(TQWidget* parent); + ~SearchResultsView(); + + void openURL(const KURL& url); + void clear(); + +signals: + + void signalSearchResultsMatch(bool); + +private slots: + + void slotData(TDEIO::Job *job, const TQByteArray &data); + void slotResult(TDEIO::Job *job); + void slotGotThumbnail(const KURL& url, const TQPixmap& pix); + void slotFailedThumbnail(const KURL& url); + +private: + + SearchResultsViewPriv *d; +}; + +} // namespace Digikam + +#endif /* SEARCHRESULTSVIEW_H */ diff --git a/src/digikam/searchwidgets.cpp b/src/digikam/searchwidgets.cpp new file mode 100644 index 00000000..c930d80c --- /dev/null +++ b/src/digikam/searchwidgets.cpp @@ -0,0 +1,602 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : search widgets collection. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file searchwidgets.cpp */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albuminfo.h" +#include "albummanager.h" +#include "ratingwidget.h" +#include "squeezedcombobox.h" +#include "kdateedit.h" +#include "searchwidgets.h" +#include "searchwidgets.moc" + +namespace Digikam +{ + +static const int RuleKeyTableCount = 11; +static const int RuleOpTableCount = 18; + +static struct +{ + const char *keyText; + TQString key; + SearchAdvancedRule::valueWidgetTypes cat; +} +RuleKeyTable[] = +{ + { I18N_NOOP("Album"), "album", SearchAdvancedRule::ALBUMS }, + { I18N_NOOP("Album Name"), "albumname", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Album Caption"), "albumcaption", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Album Collection"), "albumcollection", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Tag"), "tag", SearchAdvancedRule::TAGS }, + { I18N_NOOP("Tag Name"), "tagname", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Image Name"), "imagename", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Image Date"), "imagedate", SearchAdvancedRule::DATE }, + { I18N_NOOP("Image Caption"), "imagecaption", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Keyword"), "keyword", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Rating"), "rating", SearchAdvancedRule::RATING }, +}; + +static struct +{ + const char *keyText; + TQString key; + SearchAdvancedRule::valueWidgetTypes cat; +} +RuleOpTable[] = +{ + { I18N_NOOP("Contains"), "LIKE", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Does Not Contain"), "NLIKE", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Equals"), "EQ", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Does Not Equal"), "NE", SearchAdvancedRule::LINEEDIT }, + { I18N_NOOP("Equals"), "EQ", SearchAdvancedRule::ALBUMS }, + { I18N_NOOP("Does Not Equal"), "NE", SearchAdvancedRule::ALBUMS }, + { I18N_NOOP("Contains"), "LIKE", SearchAdvancedRule::ALBUMS }, + { I18N_NOOP("Does Not Contain"), "NLIKE", SearchAdvancedRule::ALBUMS }, + { I18N_NOOP("Equals"), "EQ", SearchAdvancedRule::TAGS }, + { I18N_NOOP("Does Not Equal"), "NE", SearchAdvancedRule::TAGS }, + { I18N_NOOP("Contains"), "LIKE", SearchAdvancedRule::TAGS }, + { I18N_NOOP("Does Not Contain"), "NLIKE", SearchAdvancedRule::TAGS }, + { I18N_NOOP("After"), "GT", SearchAdvancedRule::DATE }, + { I18N_NOOP("Before"), "LT", SearchAdvancedRule::DATE }, + { I18N_NOOP("Equals"), "EQ", SearchAdvancedRule::DATE }, + { I18N_NOOP("At least"), "GTE", SearchAdvancedRule::RATING }, + { I18N_NOOP("At most"), "LTE", SearchAdvancedRule::RATING }, + { I18N_NOOP("Equals"), "EQ", SearchAdvancedRule::RATING }, +}; + +SearchRuleLabel::SearchRuleLabel(const TQString& text, TQWidget *parent, + const char *name, WFlags f ) + : TQLabel(text, parent, name, f) +{ +} + +void SearchRuleLabel::mouseDoubleClickEvent( TQMouseEvent * e ) +{ + emit signalDoubleClick( e ); +} + +SearchAdvancedRule::SearchAdvancedRule(TQWidget* parent, SearchAdvancedRule::Option option) + : SearchAdvancedBase(SearchAdvancedBase::RULE) +{ + m_box = new TQVBox(parent); + m_box->layout()->setSpacing( KDialog::spacingHint() ); + m_box->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + + m_optionsBox = 0; + m_option = option; + if (option != NONE) + { + m_optionsBox = new TQHBox( m_box ); + m_label = new SearchRuleLabel( option == AND ? + i18n("As well as") : i18n("Or"), + m_optionsBox); + TQFrame* hline = new TQFrame( m_optionsBox ); + hline->setFrameStyle( TQFrame::HLine|TQFrame::Sunken ); + m_label->setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ); + hline->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + + connect( m_label, TQ_SIGNAL( signalDoubleClick( TQMouseEvent* ) ), + this, TQ_SLOT( slotLabelDoubleClick() )); + } + + m_hbox = new TQWidget( m_box ); + m_hbox->setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ); + + m_key = new TQComboBox( m_hbox, "key" ); + m_key->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Minimum ); + for (int i=0; i< RuleKeyTableCount; i++) + m_key->insertItem( i18n(RuleKeyTable[i].keyText), i ); + + m_operator = new TQComboBox( m_hbox ); + m_operator->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Minimum ); + // prepopulate the operator widget to get optimal size + for (int i=0; i< RuleOpTableCount; i++) + m_operator->insertItem( i18n(RuleOpTable[i].keyText), i ); + m_operator->adjustSize(); + + m_valueBox = new TQHBox( m_hbox ); + m_widgetType = NOWIDGET; + + slotKeyChanged( 0 ); + m_check = new TQCheckBox( m_hbox ); + + m_hboxLayout = new TQHBoxLayout( m_hbox ); + m_hboxLayout->setSpacing( KDialog::spacingHint() ); + m_hboxLayout->addWidget( m_key ); + m_hboxLayout->addWidget( m_operator ); + m_hboxLayout->addWidget( m_valueBox ); + m_hboxLayout->addWidget( m_check, 0, TQt::AlignRight ); + + m_box->show(); + + connect( m_key, TQ_SIGNAL( activated(int) ), + this, TQ_SLOT(slotKeyChanged(int))); + connect( m_key, TQ_SIGNAL( activated(int) ), + this, TQ_SIGNAL( signalPropertyChanged() )); + connect( m_operator, TQ_SIGNAL( activated(int) ), + this, TQ_SIGNAL( signalPropertyChanged() )); + connect( m_check, TQ_SIGNAL( toggled( bool ) ), + this, TQ_SIGNAL( signalBaseItemToggled() )); +} + +void SearchAdvancedRule::setValues(const KURL& url) +{ + if (url.isEmpty()) + return; + + // set the key widget + for (int i=0; i< RuleKeyTableCount; i++) + { + if (RuleKeyTable[i].key == url.queryItem("1.key")) + { + m_key->setCurrentText( i18n(RuleKeyTable[i].keyText) ); + } + } + + // set the operator and the last widget + slotKeyChanged( m_key->currentItem() ); + for (int i=0; i< RuleOpTableCount; i++) + { + if ( RuleOpTable[i].key == url.queryItem("1.op") && + RuleOpTable[i].cat == m_widgetType ) + { + m_operator->setCurrentText( i18n(RuleOpTable[i].keyText) ); + } + } + + // Set the value for the last widget. + TQString value = url.queryItem("1.val"); + if (m_widgetType == LINEEDIT) + m_lineEdit->setText( value ); + + if (m_widgetType == DATE) + m_dateEdit->setDate( TQDate::fromString( value, TQt::ISODate) ); + + if (m_widgetType == RATING) + { + bool ok; + int num = value.toInt(&ok); + if (ok) + m_ratingWidget->setRating( num ); + } + + if (m_widgetType == TAGS || m_widgetType == ALBUMS) + { + bool ok; + int num = value.toInt(&ok); + if (ok) + { + TQMapIterator it; + for (it = m_itemsIndexIDMap.begin() ; it != m_itemsIndexIDMap.end(); ++it) + { + if (it.data() == num) + m_valueCombo->setCurrentItem( it.key() ); + } + } + } +} + +SearchAdvancedRule::~SearchAdvancedRule() +{ + delete m_box; +} + +void SearchAdvancedRule::slotLabelDoubleClick() +{ + if (m_option == AND) + { + m_option=OR; + m_label->setText( i18n("Or") ); + } + else + { + m_option=AND; + m_label->setText( i18n("As well as") ); + } + emit signalPropertyChanged(); +} + +void SearchAdvancedRule::slotKeyChanged(int id) +{ + TQString currentOperator = m_operator->currentText(); + valueWidgetTypes currentType = m_widgetType; + + // we need to save the current size of the operator combobox + // otherise clear() will shrink it + TQSize curSize = m_operator->size(); + m_operator->clear(); + m_widgetType = RuleKeyTable[id].cat; + + for (int i=0; i< RuleOpTableCount; i++) + { + if ( RuleOpTable[i].cat == m_widgetType ) + { + m_operator->insertItem( i18n(RuleOpTable[i].keyText) ); + + if ( currentOperator == RuleOpTable[i].key ) + m_operator->setCurrentText( currentOperator ); + } + } + m_operator->setFixedSize(curSize); + setValueWidget( currentType, m_widgetType ); +} + +void SearchAdvancedRule::setValueWidget(valueWidgetTypes oldType, valueWidgetTypes newType) +{ + // this map is used to sort album and tag list combobox + typedef TQMap SortedList; + + if (oldType == newType) + return; + + if (m_lineEdit && oldType == LINEEDIT) + delete m_lineEdit; + + if (m_dateEdit && oldType == DATE) + delete m_dateEdit; + + if (m_ratingWidget && oldType == RATING) + delete m_ratingWidget; + + if (m_valueCombo && (oldType == ALBUMS || oldType == TAGS)) + delete m_valueCombo; + + if (newType == DATE) + { + m_dateEdit = new KDateEdit( m_valueBox,"datepicker"); + m_dateEdit->setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ); + m_dateEdit->show(); + + connect( m_dateEdit, TQ_SIGNAL( dateChanged(const TQDate& ) ), + this, TQ_SIGNAL(signalPropertyChanged())); + } + else if (newType == LINEEDIT) + { + m_lineEdit = new TQLineEdit( m_valueBox, "lineedit" ); + m_lineEdit->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + m_lineEdit->show(); + + connect( m_lineEdit, TQ_SIGNAL ( textChanged(const TQString&) ), + this, TQ_SIGNAL(signalPropertyChanged())); + + } + else if (newType == ALBUMS) + { + m_valueCombo = new SqueezedComboBox( m_valueBox, "albumscombo" ); + m_valueCombo->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + + AlbumManager* aManager = AlbumManager::instance(); + AlbumList aList = aManager->allPAlbums(); + + m_itemsIndexIDMap.clear(); + + // First we need to sort the album list. + // We create a map with the album url as key, so that it is + // automatically sorted. + SortedList sAList; + + for ( AlbumList::Iterator it = aList.begin(); + it != aList.end(); ++it ) + { + PAlbum *album = (PAlbum*)(*it); + if ( !album->isRoot() ) + { + sAList.insert(album->url().remove(0,1), album->id()); + } + } + + // Now we can iterate over the sorted list and fill the combobox + int index = 0; + for ( SortedList::Iterator it = sAList.begin(); + it != sAList.end(); ++it ) + { + m_valueCombo->insertSqueezedItem( it.key(), index ); + m_itemsIndexIDMap.insert(index, it.data()); + index++; + } + + m_valueCombo->show(); + + connect( m_valueCombo, TQ_SIGNAL( activated(int) ), + this, TQ_SIGNAL( signalPropertyChanged() )); + } + else if (newType == TAGS) + { + m_valueCombo = new SqueezedComboBox( m_valueBox, "tagscombo" ); + m_valueCombo->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ); + + AlbumManager* aManager = AlbumManager::instance(); + AlbumList tList = aManager->allTAlbums(); + + m_itemsIndexIDMap.clear(); + + // First we need to sort the tags. + // We create a map with the album tagPath as key, so that it is + // automatically sorted. + SortedList sTList; + + for ( AlbumList::Iterator it = tList.begin(); + it != tList.end(); ++it ) + { + TAlbum *album = (TAlbum*)(*it); + if ( !album->isRoot() ) + { + sTList.insert(album->tagPath(false), album->id()); + } + } + + // Now we can iterate over the sorted list and fill the combobox + int index = 0; + for (SortedList::Iterator it = sTList.begin(); + it != sTList.end(); ++it) + { + m_valueCombo->insertSqueezedItem( it.key(), index ); + m_itemsIndexIDMap.insert( index, it.data() ); + ++index; + } + + m_valueCombo->show(); + + connect( m_valueCombo, TQ_SIGNAL( activated(int) ), + this, TQ_SIGNAL( signalPropertyChanged() )); + } + else if (newType == RATING) + { + m_ratingWidget = new RatingWidget( m_valueBox ); + m_ratingWidget->show(); + + connect( m_ratingWidget, TQ_SIGNAL( signalRatingChanged(int) ), + this, TQ_SIGNAL( signalPropertyChanged() )); + } +} + +TQString SearchAdvancedRule::urlKey() const +{ + return RuleKeyTable[m_key->currentItem()].key; +} + +TQString SearchAdvancedRule::urlOperator() const +{ + TQString string; + + int countItems = 0; + for (int i=0; i< RuleOpTableCount; i++) + { + if ( RuleOpTable[i].cat == m_widgetType ) + { + if ( countItems == m_operator->currentItem() ) + string = RuleOpTable[i].key; + ++countItems; + } + } + + return string; +} + +TQString SearchAdvancedRule::urlValue() const +{ + TQString string; + + if (m_widgetType == LINEEDIT) + string = m_lineEdit->text() ; + + else if (m_widgetType == DATE) + string = m_dateEdit->date().toString(TQt::ISODate) ; + + else if (m_widgetType == TAGS || m_widgetType == ALBUMS) + string = TQString::number(m_itemsIndexIDMap[ m_valueCombo->currentItem() ]); + + else if (m_widgetType == RATING) + string = TQString::number(m_ratingWidget->rating()) ; + + return string; +} + +TQWidget* SearchAdvancedRule::widget() const +{ + return m_box; +} + +bool SearchAdvancedRule::isChecked() const +{ + return (m_check && m_check->isChecked()); +} + +void SearchAdvancedRule::addOption(Option option) +{ + if (option == NONE) + { + removeOption(); + return; + } + + m_box->layout()->remove(m_hbox); + + m_optionsBox = new TQHBox(m_box); + new TQLabel(option == AND ? i18n("As well as") : i18n("Or"), m_optionsBox); + TQFrame* hline = new TQFrame(m_optionsBox); + hline->setFrameStyle(TQFrame::HLine|TQFrame::Sunken); + hline->setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Minimum); + m_optionsBox->show(); + + m_box->layout()->add(m_hbox); + m_option = option; +} + +void SearchAdvancedRule::removeOption() +{ + m_option = NONE; + delete m_optionsBox; + m_optionsBox = 0; +} + +void SearchAdvancedRule::addCheck() +{ + m_check = new TQCheckBox(m_hbox); + m_check->setSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Minimum); + m_hboxLayout->addWidget( m_check, 0, TQt::AlignRight ); + m_check->show(); + + connect( m_check, TQ_SIGNAL( toggled( bool ) ), + this, TQ_SIGNAL( signalBaseItemToggled() )); +} + +void SearchAdvancedRule::removeCheck() +{ + delete m_check; + m_check = 0; +} + +SearchAdvancedGroup::SearchAdvancedGroup(TQWidget* parent) + : SearchAdvancedBase(SearchAdvancedBase::GROUP) +{ + m_box = new TQHBox(parent); + m_box->layout()->setSpacing(KDialog::spacingHint()); + m_groupbox = new TQVGroupBox(m_box); + m_check = new TQCheckBox(m_box); + m_option = SearchAdvancedRule::NONE; + m_box->show(); + + connect( m_check, TQ_SIGNAL( toggled( bool ) ), + this, TQ_SIGNAL( signalBaseItemToggled() )); +} + +SearchAdvancedGroup::~SearchAdvancedGroup() +{ + delete m_box; +} + +TQWidget* SearchAdvancedGroup::widget() const +{ + return m_box; +} + +bool SearchAdvancedGroup::isChecked() const +{ + return m_check->isChecked(); +} + +void SearchAdvancedGroup::addRule(SearchAdvancedRule* rule) +{ + if (m_childRules.isEmpty() && rule->option() != SearchAdvancedRule::NONE) + { + // this is the first rule being inserted in this group. + // get its option and remove its option + addOption(rule->option()); + rule->removeOption(); + } + + rule->removeCheck(); + + m_childRules.append(rule); + rule->widget()->reparent(m_groupbox, TQPoint(0,0)); + rule->widget()->show(); +} + +void SearchAdvancedGroup::removeRules() +{ + typedef TQValueList RuleList; + + for (RuleList::iterator it = m_childRules.begin(); + it != m_childRules.end(); ++it) + { + SearchAdvancedRule* rule = (SearchAdvancedRule*)(*it); + if (it == m_childRules.begin()) + { + rule->addOption(m_option); + } + rule->addCheck(); + + rule->widget()->reparent((TQWidget*)m_box->parent(), TQPoint(0,0)); + rule->widget()->show(); + } + + m_childRules.clear(); + removeOption(); +} + +TQValueList SearchAdvancedGroup::childRules() const +{ + return m_childRules; +} + +void SearchAdvancedGroup::addOption(Option option) +{ + m_option = option; + m_groupbox->setTitle(m_option == SearchAdvancedRule::AND ? i18n("As well as") : i18n("Or")); +} + +void SearchAdvancedGroup::removeOption() +{ + m_option = NONE; + m_groupbox->setTitle(""); +} + +} // namespace Digikam diff --git a/src/digikam/searchwidgets.h b/src/digikam/searchwidgets.h new file mode 100644 index 00000000..75ca51ce --- /dev/null +++ b/src/digikam/searchwidgets.h @@ -0,0 +1,391 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-01 + * Description : + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005 by Tom Albers + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file searchwidgets.h */ + +#ifndef SEARCHWIDGETS_H +#define SEARCHWIDGETS_H + +class TQHBox; +class TQVBox; +class TQCheckBox; +class TQComboBox; +class TQLineEdit; +class TQLabel; +class TQVGroupBox; +class TQLabel; + +class KURL; +class KDateEdit; + +namespace Digikam +{ + +class RatingWidget; +class SqueezedComboBox; + +/** @class SearchRuleLabel + * + * This class inherits everything from TQLabel, and adds one + * signal to it, when double clicked on it. + */ +class SearchRuleLabel: public TQLabel +{ + TQ_OBJECT + +public: + + /** Constructor. See for more info the TQLabel clas. + * @param text Text of the label + * @param parent The parent widget + * @param name The name + * @param f WFlags + */ + SearchRuleLabel(const TQString & text, + TQWidget * parent, + const char * name=0, + WFlags f=0 ); + +signals: + + /** + * Signal which gets emitted when a double clicked + * event occurs + * @param e the mouse event received + */ + void signalDoubleClick( TQMouseEvent * e ); + +private: + + void mouseDoubleClickEvent( TQMouseEvent * e ); +}; + +/** @class SearchAdvancedBase + * + * This class is a common class for SearchAdvancedRule and + * SearchAdvancedGroup. It contains the basic functionality + * for the advanced search dialog. + * @author Renchi Raju + * @author Tom Albers + * + */ +class SearchAdvancedBase : public TQObject +{ + TQ_OBJECT + +public: + + /** @enum Type + * Possible types. A Base item can either be a Rule on its own + * or hold a group of rules. + */ + enum Type + { + RULE = 0, + GROUP + }; + + /** @enum Option + * Possible Options. A Rule or a group of rules can have a relation + * to the previous rule or group of rules. None can be used for the + * first rule or group. + */ + enum Option + { + NONE = 0, + AND, + OR + }; + + /** + * Constructor + * @param type Determines which type of base item to be created. + */ + SearchAdvancedBase(Type type): m_type(type) {} + + /** + * Destructor + */ + virtual ~SearchAdvancedBase() {} + + /** + * Returns the type of the base item + * @return The type which is a enum, see above + */ + Type type() const { return m_type; } + + /** + * Returns a pointer to the widget holding the base item + * @return Pointer to the widget + */ + virtual TQWidget* widget() const = 0; + + /** + * Determines if the base item is checked or not + * @return true if the base item is checked and false if not + */ + virtual bool isChecked() const = 0; + + /** + * Returns the option which is accociated with the base item + * @return The Option which is a enum, see above + */ + Option option() const { return m_option; } + + /** + * Sets the option of the base item + * @param option The enum of the option to be set + */ + virtual void addOption(Option option) = 0; + + /** + * Removes the option of the base item + */ + virtual void removeOption() = 0; + +signals: + /** + * This signal is emitted when a rule or group is checked or unchecked + * This is used to determine the state of the buttons of the dialog + */ + void signalBaseItemToggled(); + + /** + * This signal is emitted when there is a change in the rule. + * This is used in the dialog to recalculate the url to pass to the + * preview widget + */ + void signalPropertyChanged(); + +protected: + + enum Option m_option; + enum Type m_type; +}; + +class SearchAdvancedGroup; + +/** @class SearchAdvancedRule + * This inherits SearchAdvancedBase and is one rule in the search dialog + * it contains all widgets to create a rule + */ +class SearchAdvancedRule : public SearchAdvancedBase +{ + TQ_OBJECT + +public: + /** + * Constructor + * @param parent The parent + * @param option Holds the Option of the rule, see the enum. + * + */ + SearchAdvancedRule(TQWidget* parent, Option option); + + /** + * destructor + */ + ~SearchAdvancedRule(); + + /** + * Returns a pointer to the widget holding the rule + * @return Pointer to the widget + */ + TQWidget* widget() const; + + /** + * Determines if the rule is checked or not + * @return True if the rule is checked, false if not + */ + bool isChecked() const; + + /** + * Sets the values of the rule. + * @param url The url which sets defaults for the rule. + */ + void setValues(const KURL& url); + + /** + * Sets the option of the rule, so this holds the + * relation ship to the previous rule or group + * @param option the enum of the option to be set + */ + void addOption(Option option); + + /** + * Removes the option of the rule + */ + void removeOption(); + + /** + * This adds the checkbox at the end of the rule. This is needed + * for example when a group of rules get ungrouped. + */ + void addCheck(); + + /** + * This removes the checkbox at the end of the rule, this is + * used if the rule is a part of a group. In a group the group + * has the checkbox, not the individual rules. + */ + void removeCheck(); + + /** + * Gives back the value of the key part of the rule, which can + * be used to build the correct url for the tdeio + * @return The value of the key part of the rule + */ + TQString urlKey() const; + + /** + * Gives back the value of the operator part of the rule, which can + * be used to build the correct url for the tdeio + * @return The value of the operator part of the rule + */ + TQString urlOperator() const; + + /** + * Gives back the value of the value part of the rule, which can + * be used to build the correct url for the tdeio + * @return The value of the value part of the rule + */ + TQString urlValue() const; + + enum valueWidgetTypes + { + NOWIDGET = 0, + LINEEDIT, + DATE, + ALBUMS, + TAGS, + RATING + }; + +private: + + void setValueWidget(valueWidgetTypes oldType, valueWidgetTypes newType); + +private slots: + + void slotKeyChanged(int); + void slotLabelDoubleClick(); + +private: + + TQLabel* m_label; + + TQVBox* m_box; + TQWidget* m_hbox; + TQHBoxLayout* m_hboxLayout; + + TQHBox* m_valueBox; + + TQCheckBox* m_check; + + TQComboBox* m_key; + TQComboBox* m_operator; + + TQLineEdit* m_lineEdit; + KDateEdit* m_dateEdit; + SqueezedComboBox* m_valueCombo; + RatingWidget* m_ratingWidget; + + TQMap m_itemsIndexIDMap; + + TQHBox* m_optionsBox; + + enum valueWidgetTypes m_widgetType; +}; + +/** @class SearchAdvancedGroup + * This inherits SearchAdvancedBase and is a group of rules + * in the search dialog + */ +class SearchAdvancedGroup : public SearchAdvancedBase +{ + +public: + /** + * Constructor + * @param parent the parent + */ + SearchAdvancedGroup(TQWidget* parent); + + /** + * Destructor + */ + ~SearchAdvancedGroup(); + + /** + * Returns a pointer to the widget holding the group + * @return pointer to the widget + */ + TQWidget* widget() const; + + /** + * determines if the rule is checked or not + * @return bool, which indicated if the base item is checked + */ + bool isChecked() const; + + /** + * sets the option of the group + * @param option the enum of the option to be set + */ + void addOption(Option option); + + /** + * removes the option + */ + void removeOption(); + + /** + * adds a rule to group + * @param rule the pointer to the rule to be added to the group + */ + void addRule(SearchAdvancedRule* rule); + + /** + * removes all rules from the group + */ + void removeRules(); + + /** + * gives back a list of pointers to the rules inside the group + * @return a list of pointers to the childeren in the group + */ + TQValueList childRules() const; + +private: + + TQHBox* m_box; + TQVGroupBox* m_groupbox; + TQCheckBox* m_check; + TQValueList m_childRules; +}; + +} // namespace Digikam + +#endif /* SEARCHWIDGETS_H */ diff --git a/src/digikam/syncjob.cpp b/src/digikam/syncjob.cpp new file mode 100644 index 00000000..b350aabc --- /dev/null +++ b/src/digikam/syncjob.cpp @@ -0,0 +1,288 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-04 + * Description : sync IO jobs. + * + * Copyright (C) 2004 by Renchi Raju + * + * Concept copied from tdelibs/tdeio/tdeio/netaccess.h/cpp + * This file is part of the KDE libraries + * Copyright (C) 1997 Torben Weis (weis@kde.org) + * Copyright (C) 1998 Matthias Ettrich (ettrich@kde.org) + * Copyright (C) 1999 David Faure (faure@kde.org) + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albumsettings.h" +#include "thumbnailjob.h" +#include "thumbnailsize.h" +#include "albumthumbnailloader.h" +#include "album.h" +#include "syncjob.h" +#include "syncjob.moc" + +void tqt_enter_modal( TQWidget *widget ); +void tqt_leave_modal( TQWidget *widget ); + +namespace Digikam +{ + +TQString* SyncJob::lastErrorMsg_ = 0; +int SyncJob::lastErrorCode_ = 0; + +bool SyncJob::del(const KURL::List& urls, bool useTrash) +{ + SyncJob sj; + + if (useTrash) + return sj.trashPriv(urls); + else + return sj.delPriv(urls); +} + +bool SyncJob::file_move(const KURL &src, const KURL &dest) +{ + SyncJob sj; + return sj.fileMovePriv(src, dest); +} + +TQPixmap SyncJob::getTagThumbnail(TAlbum *album) +{ + SyncJob sj; + return sj.getTagThumbnailPriv(album); +} + +TQPixmap SyncJob::getTagThumbnail(const TQString &name, int size) +{ + SyncJob sj; + return sj.getTagThumbnailPriv(name, size); +} + +SyncJob::SyncJob() +{ + thumbnail_ = 0; + album_ = 0; +} + +SyncJob::~SyncJob() +{ + if (thumbnail_) + delete thumbnail_; +} + +bool SyncJob::delPriv(const KURL::List& urls) +{ + success_ = true; + + TDEIO::Job* job = TDEIO::del( urls ); + connect( job, TQ_SIGNAL(result( TDEIO::Job* )), + TQ_SLOT(slotResult( TDEIO::Job*)) ); + + enter_loop(); + return success_; +} + +bool SyncJob::trashPriv(const KURL::List& urls) +{ + success_ = true; + KURL dest("trash:/"); + + if (!KProtocolInfo::isKnownProtocol(dest)) + { + dest = TDEGlobalSettings::trashPath(); + } + + TDEIO::Job* job = TDEIO::move( urls, dest ); + connect( job, TQ_SIGNAL(result( TDEIO::Job* )), + TQ_SLOT(slotResult( TDEIO::Job*)) ); + + enter_loop(); + return success_; +} + +bool SyncJob::fileMovePriv(const KURL &src, const KURL &dest) +{ + success_ = true; + + TDEIO::FileCopyJob* job = TDEIO::file_move(src, dest, -1, + true, false, false); + connect( job, TQ_SIGNAL(result( TDEIO::Job* )), + TQ_SLOT(slotResult( TDEIO::Job*)) ); + + enter_loop(); + return success_; +} + +void SyncJob::enter_loop() +{ + TQWidget dummy(0,0,WType_Dialog | WShowModal); + dummy.setFocusPolicy( TQWidget::NoFocus ); + tqt_enter_modal(&dummy); + tqApp->enter_loop(); + tqt_leave_modal(&dummy); +} + +void SyncJob::slotResult( TDEIO::Job * job ) +{ + lastErrorCode_ = job->error(); + success_ = !(lastErrorCode_); + if ( !success_ ) + { + if ( !lastErrorMsg_ ) + lastErrorMsg_ = new TQString; + *lastErrorMsg_ = job->errorString(); + } + tqApp->exit_loop(); +} + +TQPixmap SyncJob::getTagThumbnailPriv(TAlbum *album) +{ + if (thumbnail_) + delete thumbnail_; + thumbnail_ = new TQPixmap; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + + if (!loader->getTagThumbnail(album, *thumbnail_)) + { + if (thumbnail_->isNull()) + { + return loader->getStandardTagIcon(album); + } + else + { + return loader->blendIcons(loader->getStandardTagIcon(), *thumbnail_); + } + } + else + { + connect(loader, TQ_SIGNAL(signalThumbnail(Album *, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&))); + + connect(loader, TQ_SIGNAL(signalFailed(Album *)), + this, TQ_SLOT(slotLoadThumbnailFailed(Album *))); + + album_ = album; + enter_loop(); + } + return *thumbnail_; +} + +void SyncJob::slotLoadThumbnailFailed(Album *album) +{ + // TODO: setting _lastError* + if (album == album_) + { + tqApp->exit_loop(); + } +} + +void SyncJob::slotGotThumbnailFromIcon(Album *album, const TQPixmap& pix) +{ + if (album == album_) + { + *thumbnail_ = pix; + tqApp->exit_loop(); + } +} + +TQPixmap SyncJob::getTagThumbnailPriv(const TQString &name, int size) +{ + thumbnailSize_ = size; + if (thumbnail_) + delete thumbnail_; + thumbnail_ = new TQPixmap; + + if(name.startsWith("/")) + { + ThumbnailJob *job = new ThumbnailJob(name, + ThumbnailSize::Tiny, + false, + AlbumSettings::instance()->getExifRotate()); + connect(job, + TQ_SIGNAL(signalThumbnail(const KURL&, + const TQPixmap&)), + TQ_SLOT(slotGotThumbnailFromIcon(const KURL&, + const TQPixmap&))); + connect(job, + TQ_SIGNAL(signalFailed(const KURL&)), + TQ_SLOT(slotLoadThumbnailFailed())); + + enter_loop(); + job->kill(); + } + else + { + TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); + *thumbnail_ = iconLoader->loadIcon(name, TDEIcon::NoGroup, thumbnailSize_, + TDEIcon::DefaultState, 0, true); + } + return *thumbnail_; +} + +void SyncJob::slotLoadThumbnailFailed() +{ + // TODO: setting _lastError* + tqApp->exit_loop(); +} + +void SyncJob::slotGotThumbnailFromIcon(const KURL&, const TQPixmap& pix) +{ + if(!pix.isNull() && (thumbnailSize_ < ThumbnailSize::Tiny)) + { + int w1 = pix.width(); + int w2 = thumbnailSize_; + int h1 = pix.height(); + int h2 = thumbnailSize_; + thumbnail_->resize(w2,h2); + bitBlt(thumbnail_, 0, 0, &pix, (w1-w2)/2, (h1-h2)/2, w2, h2); + } + else + { + *thumbnail_ = pix; + } + tqApp->exit_loop(); +} + +TQString SyncJob::lastErrorMsg() +{ + return (lastErrorMsg_ ? *lastErrorMsg_ : TQString()); +} + +int SyncJob::lastErrorCode() +{ + return lastErrorCode_; +} + +} // namespace Digikam + diff --git a/src/digikam/syncjob.h b/src/digikam/syncjob.h new file mode 100644 index 00000000..fca82613 --- /dev/null +++ b/src/digikam/syncjob.h @@ -0,0 +1,108 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-04 + * Description : sync IO jobs. + * + * Copyright (C) 2004 by Renchi Raju + * + * Concept copied from tdelibs/tdeio/tdeio/netaccess.h/cpp + * This file is part of the KDE libraries + * Copyright (C) 1997 Torben Weis (weis@kde.org) + * Copyright (C) 1998 Matthias Ettrich (ettrich@kde.org) + * Copyright (C) 1999 David Faure (faure@kde.org) + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SYNCJOB_H +#define SYNCJOB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +class TQString; + +namespace TDEIO +{ +class Job; +} + +namespace Digikam +{ + +class Album; +class TAlbum; + +class SyncJob : public TQObject +{ + TQ_OBJECT + +public: + + /* this will delete the urls. */ + static bool del(const KURL::List& urls, bool useTrash); + + /* remove this when we move dependency upto kde 3.2 */ + static bool file_move(const KURL &src, const KURL &dest); + + /* Load the image or icon for the tag thumbnail */ + static TQPixmap getTagThumbnail(TAlbum *album); + static TQPixmap getTagThumbnail(const TQString &name, int size); + + static TQString lastErrorMsg(); + static int lastErrorCode(); + +private: + + SyncJob(); + ~SyncJob(); + + bool delPriv(const KURL::List& urls); + bool trashPriv(const KURL::List& urls); + + bool fileMovePriv(const KURL &src, const KURL &dest); + + TQPixmap getTagThumbnailPriv(TAlbum *album); + TQPixmap getTagThumbnailPriv(const TQString &name, int size); + + void enter_loop(); + + static int lastErrorCode_; + static TQString* lastErrorMsg_; + bool success_; + + TQPixmap *thumbnail_; + Album *album_; + int thumbnailSize_; + +private slots: + + void slotResult( TDEIO::Job * job ); + void slotGotThumbnailFromIcon(Album *album, const TQPixmap& pix); + void slotLoadThumbnailFailed(Album *album); + void slotGotThumbnailFromIcon(const KURL& url, const TQPixmap& pix); + void slotLoadThumbnailFailed(); +}; + +} // namespace Digikam + +#endif /* SYNCJOB_H */ diff --git a/src/digikam/tageditdlg.cpp b/src/digikam/tageditdlg.cpp new file mode 100644 index 00000000..d77a0169 --- /dev/null +++ b/src/digikam/tageditdlg.cpp @@ -0,0 +1,406 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-01 + * Description : dialog to edit and create digiKam Tags + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "searchtextbar.h" +#include "syncjob.h" +#include "tageditdlg.h" +#include "tageditdlg.moc" + +namespace Digikam +{ + +class TagsListCreationErrorDialog : public KDialogBase +{ + +public: + + TagsListCreationErrorDialog(TQWidget* parent, const TQMap& errMap); + ~TagsListCreationErrorDialog(){}; +}; + +// ------------------------------------------------------------------------------ + +class TagEditDlgPriv +{ +public: + + TagEditDlgPriv() + { + titleEdit = 0; + iconButton = 0; + resetIconButton = 0; + mainRootAlbum = 0; + topLabel = 0; + create = false; + } + + bool create; + + TQLabel *topLabel; + + TQString icon; + + TQPushButton *iconButton; + TQPushButton *resetIconButton; + + TAlbum *mainRootAlbum; + SearchTextBar *titleEdit; +}; + +TagEditDlg::TagEditDlg(TQWidget *parent, TAlbum* album, bool create) + : KDialogBase(parent, 0, true, 0, Help|Ok|Cancel, Ok, true) +{ + d = new TagEditDlgPriv; + d->mainRootAlbum = album; + d->create = create; + + setHelp("tagscreation.anchor", "digikam"); + if (d->create) setCaption(i18n("New Tag")); + else setCaption(i18n("Edit Tag")); + + TQWidget *page = makeMainWidget(); + TQGridLayout* grid = new TQGridLayout(page, 5, 4, 0, spacingHint()); + + // -------------------------------------------------------- + + TQLabel *logo = new TQLabel(page); + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 96, TDEIcon::DefaultState, 0, true)); + + d->topLabel = new TQLabel(page); + d->topLabel->setAlignment(TQt::AlignAuto | TQt::AlignVCenter | TQt::SingleLine); + + KSeparator *line = new KSeparator (Horizontal, page); + + // -------------------------------------------------------- + + TQLabel *titleLabel = new TQLabel(page); + titleLabel->setText(i18n("&Title:")); + + d->titleEdit = new SearchTextBar(page, "TagEditDlgTitleEdit", i18n("Enter tag name here...")); + titleLabel->setBuddy(d->titleEdit); + + TQLabel *tipLabel = new TQLabel(page); + tipLabel->setTextFormat(TQt::RichText); + tipLabel->setText(i18n("

To create new tags, you can use the following rules:

" + "

  • '/' can be used to create a tags hierarchy.
    " + "Ex.: \"Country/City/Paris\"
  • " + "
  • ',' can be used to create more than one tags hierarchy at the same time.
    " + "Ex.: \"City/Paris, Monument/Notre-Dame\"
  • " + "
  • If a tag hierarchy starts with '/', root tag album is used as parent.

" + )); + + if (d->create) + { + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum *tag = dynamic_cast(*it); + d->titleEdit->lineEdit()->completionObject()->addItem(tag->tagPath()); + } + } + else + { + d->titleEdit->setText(d->mainRootAlbum->title()); + tipLabel->hide(); + } + + TQLabel *iconTextLabel = new TQLabel(page); + iconTextLabel->setText(i18n("&Icon:")); + + d->iconButton = new TQPushButton(page); + d->iconButton->setFixedSize(40, 40); + iconTextLabel->setBuddy(d->iconButton); + + // In create mode, by default assign the icon of the parent (if not root) to this new tag. + if (d->create && !d->mainRootAlbum->isRoot()) + d->icon = d->mainRootAlbum->icon(); + else + d->icon = d->mainRootAlbum->icon(); + + d->iconButton->setIconSet(SyncJob::getTagThumbnail(d->icon, 20)); + + d->resetIconButton = new TQPushButton(SmallIcon("reload_page"), i18n("Reset"), page); + if (d->create) d->resetIconButton->hide(); + + // -------------------------------------------------------- + + grid->addMultiCellWidget(logo, 0, 3, 0, 0); + grid->addMultiCellWidget(d->topLabel, 0, 0, 1, 4); + grid->addMultiCellWidget(line, 1, 1, 1, 4); + grid->addMultiCellWidget(tipLabel, 2, 2, 1, 4); + grid->addMultiCellWidget(titleLabel, 3, 3, 1, 1); + grid->addMultiCellWidget(d->titleEdit, 3, 3, 2, 4); + grid->addMultiCellWidget(iconTextLabel, 4, 4, 1, 1); + grid->addMultiCellWidget(d->iconButton, 4, 4, 2, 2); + grid->addMultiCellWidget(d->resetIconButton, 4, 4, 3, 3); + grid->setColStretch(4, 10); + grid->setRowStretch(5, 10); + + // -------------------------------------------------------- + + connect(d->iconButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotIconChanged())); + + connect(d->resetIconButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotIconResetClicked())); + + connect(d->titleEdit->lineEdit(), TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotTitleChanged(const TQString&))); + + // -------------------------------------------------------- + + slotTitleChanged(d->titleEdit->text()); + d->titleEdit->lineEdit()->setFocus(); + adjustSize(); +} + +TagEditDlg::~TagEditDlg() +{ + delete d; +} + +TQString TagEditDlg::title() const +{ + return d->titleEdit->text(); +} + +TQString TagEditDlg::icon() const +{ + return d->icon; +} + +void TagEditDlg::slotIconResetClicked() +{ + d->icon = TQString("tag"); + d->iconButton->setIconSet(SyncJob::getTagThumbnail(d->icon, 20)); +} + +void TagEditDlg::slotIconChanged() +{ +#if KDE_IS_VERSION(3,3,0) + TDEIconDialog dlg(this); + dlg.setup(TDEIcon::NoGroup, TDEIcon::Application, false, 20, false, false, false); + TQString icon = dlg.openDialog(); +#else + TQString icon = TDEIconDialog::getIcon(TDEIcon::NoGroup, TDEIcon::Application, false, 20); + if (icon.startsWith("/")) + return; +#endif + + if (icon.isEmpty() || icon == d->icon) + return; + + d->icon = icon; + d->iconButton->setIconSet(SyncJob::getTagThumbnail(d->icon, 20)); +} + +void TagEditDlg::slotTitleChanged(const TQString& newtitle) +{ + TQString tagName = d->mainRootAlbum->tagPath(); + if (tagName.endsWith("/") && !d->mainRootAlbum->isRoot()) + tagName.truncate(tagName.length()-1); + + if (d->create) + { + if (d->titleEdit->text().startsWith("/")) + { + d->topLabel->setText(i18n("Create New Tag")); + } + else + { + d->topLabel->setText(i18n("Create New Tag in
" + "\"%1\"
").arg(tagName)); + } + } + else + { + d->topLabel->setText(i18n("Properties of Tag
" + "\"%1\"
").arg(tagName)); + } + + enableButtonOK(!newtitle.isEmpty()); +} + +bool TagEditDlg::tagEdit(TQWidget *parent, TAlbum* album, TQString& title, TQString& icon) +{ + TagEditDlg dlg(parent, album); + + bool valRet = dlg.exec(); + if (valRet == TQDialog::Accepted) + { + title = dlg.title(); + icon = dlg.icon(); + } + + return valRet; +} + +bool TagEditDlg::tagCreate(TQWidget *parent, TAlbum* album, TQString& title, TQString& icon) +{ + TagEditDlg dlg(parent, album, true); + + bool valRet = dlg.exec(); + if (valRet == TQDialog::Accepted) + { + title = dlg.title(); + icon = dlg.icon(); + } + + return valRet; +} + +AlbumList TagEditDlg::createTAlbum(TAlbum *mainRootAlbum, const TQString& tagStr, const TQString& icon, + TQMap& errMap) +{ + errMap.clear(); + AlbumList createdTagsList; + + // Check if new tags are include in a list of tags hierarchy separated by ','. + // Ex: /Country/France/people,/City/France/Paris + + TQStringList tagsHierarchies = TQStringList::split(",", tagStr); + if (tagsHierarchies.isEmpty()) + return createdTagsList; + + for (TQStringList::const_iterator it = tagsHierarchies.begin(); it != tagsHierarchies.end(); ++it) + { + TQString hierarchy = (*it).stripWhiteSpace(); + if (!hierarchy.isEmpty()) + { + // Check if new tags is a hierarchy of tags separated by '/'. + + TAlbum *root = 0; + + if (hierarchy.startsWith("/") || !mainRootAlbum) + root = AlbumManager::instance()->findTAlbum(0); + else + root = mainRootAlbum; + + TQStringList tagsList = TQStringList::split("/", hierarchy); + DDebug() << tagsList << endl; + + if (!tagsList.isEmpty()) + { + for (TQStringList::iterator it2 = tagsList.begin(); it2 != tagsList.end(); ++it2) + { + TQString tagPath, errMsg; + TQString tag = (*it2).stripWhiteSpace(); + if (root->isRoot()) + tagPath = TQString("/%1").arg(tag); + else + tagPath = TQString("%1/%2").arg(root->tagPath()).arg(tag); + + DDebug() << tag << " :: " << tagPath << endl; + + if (!tag.isEmpty()) + { + // Tag already exist ? + TAlbum* album = AlbumManager::instance()->findTAlbum(tagPath); + if (!album) + { + root = AlbumManager::instance()->createTAlbum(root, tag, icon, errMsg); + } + else + { + root = album; + if (*it2 == tagsList.last()) + errMap.insert(tagPath, i18n("Tag name already exists")); + } + + if (root) + createdTagsList.append(root); + } + + // Sanity check if tag creation failed. + if (!root) + { + errMap.insert(tagPath, errMsg); + break; + } + } + } + } + } + + return createdTagsList; +} + +void TagEditDlg::showtagsListCreationError(TQWidget* parent, const TQMap& errMap) +{ + if (!errMap.isEmpty()) + { + TagsListCreationErrorDialog dlg(parent, errMap); + dlg.exec(); + } +} + +// ------------------------------------------------------------------------------ + +TagsListCreationErrorDialog::TagsListCreationErrorDialog(TQWidget* parent, const TQMap& errMap) + : KDialogBase(parent, 0, true, 0, Help|Ok, Ok, false) +{ + setHelp("tagscreation.anchor", "digikam"); + setCaption(i18n("Tag creation Error")); + + TQWidget* box = makeMainWidget(); + TQVBoxLayout* vLay = new TQVBoxLayout(box); + + TQLabel *label = new TQLabel(i18n("Error been occured during Tag creation:"), box); + TDEListView *listView = new TDEListView(box); + listView->addColumn(i18n("Tag Path")); + listView->addColumn(i18n("Error")); + listView->setResizeMode(TQListView::LastColumn); + + vLay->addWidget(label); + vLay->addWidget(listView); + vLay->setMargin(0); + vLay->setSpacing(0); + + for (TQMap::const_iterator it = errMap.begin() ; it != errMap.end() ; ++it) + new TDEListViewItem(listView, it.key(), it.data()); + + adjustSize(); +} + +} // namespace Digikam diff --git a/src/digikam/tageditdlg.h b/src/digikam/tageditdlg.h new file mode 100644 index 00000000..a1bfcfa2 --- /dev/null +++ b/src/digikam/tageditdlg.h @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-01 + * Description : dialog to edit and create digiKam Tags + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TAGEDITDLG_H +#define TAGEDITDLG_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "albummanager.h" + +namespace Digikam +{ +class TagEditDlgPriv; + +class TagEditDlg : public KDialogBase +{ + TQ_OBJECT + +public: + + TagEditDlg(TQWidget *parent, TAlbum* album, bool create=false); + ~TagEditDlg(); + + TQString title() const; + TQString icon() const; + + static bool tagEdit(TQWidget *parent, TAlbum* album, TQString& title, TQString& icon); + static bool tagCreate(TQWidget *parent, TAlbum* album, TQString& title, TQString& icon); + + /** Create a list of new Tag album using a list of tags hierarchies separated by ",". + A hierarchy of tags is a string path of tags name separated by "/". + If a hierarchy start by "/" or if mainRootAlbum is null, it will be created from + root tag album, else it will be created from mainRootAlbum as parent album. + 'errMap' is Map of TAlbum path and error message if tag creation failed. + Return the list of created Albums. + */ + static AlbumList createTAlbum(TAlbum *mainRootAlbum, const TQString& tagStr, const TQString& icon, + TQMap& errMap); + + static void showtagsListCreationError(TQWidget* parent, const TQMap& errMap); + +private slots: + + void slotIconChanged(); + void slotIconResetClicked(); + void slotTitleChanged(const TQString& newtitle); + +private: + + TagEditDlgPriv *d; +}; + +} // namespace Digikam + +#endif /* TAGEDITDLG_H */ diff --git a/src/digikam/tagfilterview.cpp b/src/digikam/tagfilterview.cpp new file mode 100644 index 00000000..2bebc2b8 --- /dev/null +++ b/src/digikam/tagfilterview.cpp @@ -0,0 +1,1429 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-05 + * Description : tags filter view + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumdb.h" +#include "album.h" +#include "albumlister.h" +#include "albumthumbnailloader.h" +#include "syncjob.h" +#include "dragobjects.h" +#include "folderitem.h" +#include "imageattributeswatch.h" +#include "imageinfo.h" +#include "metadatahub.h" +#include "tageditdlg.h" +#include "statusprogressbar.h" +#include "tagfilterview.h" +#include "tagfilterview.moc" + +// X11 includes. + +extern "C" +{ +#include +} + +namespace Digikam +{ + +class TagFilterViewItem : public FolderCheckListItem +{ + +public: + + TagFilterViewItem(TQListView* parent, TAlbum* tag, bool untagged=false); + TagFilterViewItem(TQListViewItem* parent, TAlbum* tag); + + TAlbum* album() const; + int id() const; + bool untagged() const; + void refresh(); + void setOpen(bool o); + void setCount(int count); + int count(); + int compare(TQListViewItem* i, int column, bool ascending) const; + +private: + + void stateChange(bool val); + void paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align); + +private: + + bool m_untagged; + + int m_count; + + TAlbum *m_album; +}; + +TagFilterViewItem::TagFilterViewItem(TQListView* parent, TAlbum* album, bool untagged) + : FolderCheckListItem(parent, album ? album->title() : i18n("Not Tagged"), + TQCheckListItem::CheckBox/*Controller*/) +{ + m_album = album; + m_untagged = untagged; + m_count = 0; + setDragEnabled(!untagged); + + if (m_album) + m_album->setExtraData(listView(), this); +} + +TagFilterViewItem::TagFilterViewItem(TQListViewItem* parent, TAlbum* album) + : FolderCheckListItem(parent, album->title(), + TQCheckListItem::CheckBox/*Controller*/) +{ + m_album = album; + m_untagged = false; + m_count = 0; + setDragEnabled(true); + + if (m_album) + m_album->setExtraData(listView(), this); +} + +void TagFilterViewItem::refresh() +{ + if (!m_album) return; + + if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount()) + { + if (isOpen()) + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count)); + else + { + int countRecursive = m_count; + AlbumIterator it(m_album); + while ( it.current() ) + { + TagFilterViewItem *item = (TagFilterViewItem*)it.current()->extraData(listView()); + if (item) + countRecursive += item->count(); + ++it; + } + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive)); + } + } + else + { + setText(0, m_album->title()); + } +} + +void TagFilterViewItem::stateChange(bool val) +{ + TQCheckListItem::stateChange(val); + + /* NOTE G.Caulier 2007/01/08: this code is now disable because TagFilterViewItem + have been changed from TQCheckListItem::CheckBoxController + to TQCheckListItem::CheckBox. + + // All TagFilterViewItems are CheckBoxControllers. If they have no children, + // they should be of type CheckBox, but that is not possible with our way of adding items. + // When clicked, children-less items first change to the NoChange state, and a second + // click is necessary to set them to On and make the filter take effect. + // So set them to On if the condition is met. + if (!firstChild() && state() == NoChange) + { + setState(On); + } + */ + + ((TagFilterView*)listView())->stateChanged(this); +} + +int TagFilterViewItem::compare(TQListViewItem* i, int column, bool ascending) const +{ + if (m_untagged) + return 1; + + TagFilterViewItem* dItem = dynamic_cast(i); + if (!dItem) + return 0; + + if (dItem && dItem->m_untagged) + return -1; + + return TQListViewItem::compare(i, column, ascending); +} + +void TagFilterViewItem::paintCell(TQPainter* p, const TQColorGroup & cg, int column, int width, int align) +{ + if (!m_untagged) + { + FolderCheckListItem::paintCell(p, cg, column, width, align); + return; + } + + TQFont f(listView()->font()); + f.setBold(true); + f.setItalic(true); + p->setFont(f); + + TQColorGroup mcg(cg); + mcg.setColor(TQColorGroup::Text, TQt::darkRed); + + FolderCheckListItem::paintCell(p, mcg, column, width, align); +} + +void TagFilterViewItem::setOpen(bool o) +{ + TQListViewItem::setOpen(o); + refresh(); +} + +TAlbum* TagFilterViewItem::album() const +{ + return m_album; +} + +int TagFilterViewItem::id() const +{ + return m_album ? m_album->id() : 0; +} + +void TagFilterViewItem::setCount(int count) +{ + m_count = count; + refresh(); +} + +int TagFilterViewItem::count() +{ + return m_count; +} + +bool TagFilterViewItem::untagged() const +{ + return m_untagged; +} + +// --------------------------------------------------------------------- + +class TagFilterViewPrivate +{ + +public: + + TagFilterViewPrivate() + { + ABCMenu = 0; + timer = 0; + toggleAutoTags = TagFilterView::NoToggleAuto; + matchingCond = AlbumLister::OrCondition; + } + + TQTimer *timer; + + TQPopupMenu *ABCMenu; + + TagFilterView::ToggleAutoTags toggleAutoTags; + + AlbumLister::MatchingCondition matchingCond; +}; + +TagFilterView::TagFilterView(TQWidget* parent) + : FolderView(parent, "TagFilterView") +{ + d = new TagFilterViewPrivate; + d->timer = new TQTimer(this); + + addColumn(i18n("Tag Filters")); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(true); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + TagFilterViewItem* notTaggedItem = new TagFilterViewItem(this, 0, true); + notTaggedItem->setPixmap(0, AlbumThumbnailLoader::instance()->getStandardTagIcon()); + + // ------------------------------------------------------------------------ + + connect(AlbumManager::instance(), TQ_SIGNAL(signalTAlbumsDirty(const TQMap&)), + this, TQ_SLOT(slotRefresh(const TQMap&))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotTagAdded(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotTagDeleted(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotTagRenamed(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotClear())); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumIconChanged(Album*)), + this, TQ_SLOT(slotAlbumIconChanged(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalTAlbumMoved(TAlbum*, TAlbum*)), + this, TQ_SLOT(slotTagMoved(TAlbum*, TAlbum*))); + + // ------------------------------------------------------------------------ + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + + connect(loader, TQ_SIGNAL(signalThumbnail(Album *, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&))); + + connect(loader, TQ_SIGNAL(signalFailed(Album *)), + this, TQ_SLOT(slotThumbnailLost(Album *))); + + connect(loader, TQ_SIGNAL(signalReloadThumbnails()), + this, TQ_SLOT(slotReloadThumbnails())); + + connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotTimeOut())); + + // ------------------------------------------------------------------------ + + TDEConfig* config = kapp->config(); + config->setGroup("Tag Filters View"); + d->matchingCond = (AlbumLister::MatchingCondition)(config->readNumEntry("Matching Condition", + AlbumLister::OrCondition)); + + d->toggleAutoTags = (ToggleAutoTags)(config->readNumEntry("Toggle Auto Tags", NoToggleAuto)); +} + +TagFilterView::~TagFilterView() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Tag Filters View"); + config->writeEntry("Matching Condition", (int)(d->matchingCond)); + config->writeEntry("Toggle Auto Tags", (int)(d->toggleAutoTags)); + config->sync(); + + saveViewState(); + + delete d->timer; + delete d; +} + +void TagFilterView::slotTextTagFilterChanged(const TQString& filter) +{ + if (filter.isEmpty()) + { + collapseView(); + return; + } + + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* talbum = (TAlbum*)(*it); + + // don't touch the root Album + if (talbum->isRoot()) + continue; + + bool match = talbum->title().lower().contains(search); + bool doesExpand = false; + if (!match) + { + // check if any of the parents match the search + Album* parent = talbum->parent(); + while (parent && !parent->isRoot()) + { + if (parent->title().lower().contains(search)) + { + match = true; + break; + } + + parent = parent->parent(); + } + } + + if (!match) + { + // check if any of the children match the search + AlbumIterator it(talbum); + while (it.current()) + { + if ((*it)->title().lower().contains(search)) + { + match = true; + doesExpand = true; + break; + } + ++it; + } + } + + TagFilterViewItem* viewItem = (TagFilterViewItem*) talbum->extraData(this); + + if (match) + { + atleastOneMatch = true; + + if (viewItem) + { + viewItem->setVisible(true); + viewItem->setOpen(doesExpand); + } + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + viewItem->setOpen(false); + } + } + } + + emit signalTextTagFilterMatch(atleastOneMatch); +} + +void TagFilterView::stateChanged(TagFilterViewItem* item) +{ + ToggleAutoTags oldAutoTags = d->toggleAutoTags; + + switch(d->toggleAutoTags) + { + case Children: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item, item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + case Parents: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(item, item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + case ChildrenAndParents: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item, item->isOn()); + toggleParentTags(item, item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + default: + break; + } + + triggerChange(); +} + +void TagFilterView::triggerChange() +{ + d->timer->start(50, true); +} + +TQDragObject* TagFilterView::dragObject() +{ + TagFilterViewItem *item = dynamic_cast(dragItem()); + if(!item) + return 0; + + TagDrag *t = new TagDrag(item->id(), this); + t->setPixmap(*item->pixmap(0)); + + return t; +} + +bool TagFilterView::acceptDrop(const TQDropEvent *e) const +{ + TQPoint vp = contentsToViewport(e->pos()); + TagFilterViewItem *itemDrop = dynamic_cast(itemAt(vp)); + TagFilterViewItem *itemDrag = dynamic_cast(dragItem()); + + if(TagDrag::canDecode(e) || TagListDrag::canDecode(e)) + { + // Allow dragging at the root, to move the tag to the root + if(!itemDrop) + return true; + + // Do not allow dragging at the "Not Tagged" item. + if (itemDrop->untagged()) + return false; + + // Dragging an item on itself makes no sense + if(itemDrag == itemDrop) + return false; + + // Dragging a parent on its child makes no sense + if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album())) + return false; + + return true; + } + + if (ItemDrag::canDecode(e) && itemDrop && !itemDrop->untagged()) + { + TAlbum *tag = itemDrop->album(); + + if (tag) + { + if (tag->parent()) + { + // Only other possibility is image items being dropped + // And allow this only if there is a Tag to be dropped + // on and also the Tag is not root or "Not Tagged" item. + return true; + } + } + } + + return false; +} + +void TagFilterView::contentsDropEvent(TQDropEvent *e) +{ + FolderView::contentsDropEvent(e); + + if (!acceptDrop(e)) + return; + + TQPoint vp = contentsToViewport(e->pos()); + TagFilterViewItem *itemDrop = dynamic_cast(itemAt(vp)); + + if (!itemDrop || itemDrop->untagged()) + return; + + if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TAlbum* talbum = man->findTAlbum(tagID); + + if(!talbum) + return; + + if (talbum == itemDrop->album()) + return; + + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters")); + popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + + if(id == 10) + { + TAlbum *newParentTag = 0; + + if (!itemDrop) + { + // move dragItem to the root + newParentTag = AlbumManager::instance()->findTAlbum(0); + } + else + { + // move dragItem as child of dropItem + newParentTag = itemDrop->album(); + } + + TQString errMsg; + if (!AlbumManager::instance()->moveTAlbum(talbum, newParentTag, errMsg)) + { + KMessageBox::error(this, errMsg); + } + + if(itemDrop && !itemDrop->isOpen()) + itemDrop->setOpen(true); + } + + return; + } + + if (ItemDrag::canDecode(e)) + { + TAlbum *destAlbum = itemDrop->album(); + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + return; + + if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) + return; + + int id = 0; + char keys_return[32]; + XQueryKeymap(x11Display(), keys_return); + int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3); + int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4); + + // If a ctrl key is pressed while dropping the drag object, + // the tag is assigned to the images without showing a + // popup menu. + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 10; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters")); + popMenu.insertItem(SmallIcon("tag"), i18n("Assign Tag '%1' to Items") + .arg(destAlbum->prettyURL()), 10) ; + popMenu.insertItem(i18n("Set as Tag Thumbnail"), 11); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel")); + + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if (id == 10) + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + AlbumLister::instance()->blockSignals(true); + AlbumManager::instance()->albumDB()->beginTransaction(); + int i=0; + for (TQValueList::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + // create temporary ImageInfo object + ImageInfo info(*it); + + MetadataHub hub; + hub.load(&info); + hub.setTag(destAlbum, true); + hub.write(&info, MetadataHub::PartialWrite); + hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); + + emit signalProgressValue((int)((i++/(float)imageIDs.count())*100.0)); + kapp->processEvents(); + } + AlbumLister::instance()->blockSignals(false); + AlbumManager::instance()->albumDB()->commitTransaction(); + + ImageAttributesWatch::instance()->imagesChanged(destAlbum->id()); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + } + else if(id == 11) + { + TQString errMsg; + AlbumManager::instance()->updateTAlbumIcon(destAlbum, TQString(), + imageIDs.first(), errMsg); + } + } +} + +void TagFilterView::slotTagAdded(Album* album) +{ + if (!album || album->isRoot()) + return; + + TAlbum* tag = dynamic_cast(album); + if (!tag) + return; + + if (tag->parent()->isRoot()) + { + new TagFilterViewItem(this, tag); + } + else + { + TagFilterViewItem* parent = (TagFilterViewItem*)(tag->parent()->extraData(this)); + if (!parent) + { + DWarning() << k_funcinfo << " Failed to find parent for Tag " + << tag->tagPath() << endl; + return; + } + + new TagFilterViewItem(parent, tag); + } + + setTagThumbnail(tag); +} + +void TagFilterView::slotTagRenamed(Album* album) +{ + if (!album) + return; + + TAlbum* tag = dynamic_cast(album); + if (!tag) + return; + + TagFilterViewItem* item = (TagFilterViewItem*)(tag->extraData(this)); + if (item) + item->refresh(); +} + +void TagFilterView::slotTagMoved(TAlbum* tag, TAlbum* newParent) +{ + if (!tag || !newParent) + return; + + TagFilterViewItem* item = (TagFilterViewItem*)(tag->extraData(this)); + if (!item) + return; + + if (item->parent()) + { + TQListViewItem* oldPItem = item->parent(); + oldPItem->takeItem(item); + + TagFilterViewItem* newPItem = (TagFilterViewItem*)(newParent->extraData(this)); + if (newPItem) + newPItem->insertItem(item); + else + insertItem(item); + } + else + { + takeItem(item); + + TagFilterViewItem* newPItem = (TagFilterViewItem*)(newParent->extraData(this)); + + if (newPItem) + newPItem->insertItem(item); + else + insertItem(item); + } +} + +void TagFilterView::slotTagDeleted(Album* album) +{ + if (!album || album->isRoot()) + return; + + TAlbum* tag = dynamic_cast(album); + if (!tag) + return; + + TagFilterViewItem* item = (TagFilterViewItem*)(album->extraData(this)); + if (!item) + return; + + // NOTE: see B.K.O #158558: unselected tag filter and all childrens before to delete it. + toggleChildTags(item, false); + item->setOn(false); + + album->removeExtraData(this); + delete item; +} + +void TagFilterView::setTagThumbnail(TAlbum *album) +{ + if(!album) + return; + + TagFilterViewItem* item = (TagFilterViewItem*) album->extraData(this); + + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap icon; + if (!loader->getTagThumbnail(album, icon)) + { + if (icon.isNull()) + { + item->setPixmap(0, loader->getStandardTagIcon(album)); + } + else + { + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), icon); + item->setPixmap(0, blendedIcon); + } + } + else + { + // for the time being, set standard icon + item->setPixmap(0, loader->getStandardTagIcon(album)); + } +} + +void TagFilterView::slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail) +{ + if(!album || album->type() != Album::TAG) + return; + + TagFilterViewItem* item = (TagFilterViewItem*)album->extraData(this); + + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), thumbnail); + item->setPixmap(0, blendedIcon); +} + +void TagFilterView::slotThumbnailLost(Album *) +{ + // we already set the standard icon before loading +} + +void TagFilterView::slotReloadThumbnails() +{ + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* tag = (TAlbum*)(*it); + setTagThumbnail(tag); + } +} + +void TagFilterView::slotAlbumIconChanged(Album* album) +{ + if(!album || album->type() != Album::TAG) + return; + + setTagThumbnail((TAlbum *)album); +} + +void TagFilterView::slotClear() +{ + clear(); + + TagFilterViewItem* notTaggedItem = new TagFilterViewItem(this, 0, true); + notTaggedItem->setPixmap(0, AlbumThumbnailLoader::instance()->getStandardTagIcon()); +} + +void TagFilterView::slotTimeOut() +{ + TQValueList filterTags; + + bool showUnTagged = false; + + TQListViewItemIterator it(this, TQListViewItemIterator::Checked); + while (it.current()) + { + TagFilterViewItem* item = (TagFilterViewItem*)it.current(); + if (item->album()) + filterTags.append(item->album()->id()); + else if (item->untagged()) + showUnTagged = true; + ++it; + } + + AlbumLister::instance()->setTagFilter(filterTags, d->matchingCond, showUnTagged); +} + +void TagFilterView::slotContextMenu(TQListViewItem* it, const TQPoint&, int) +{ + TagFilterViewItem *item = dynamic_cast(it); + if (item && item->untagged()) + return; + + d->ABCMenu = new TQPopupMenu; + + connect(d->ABCMenu, TQ_SIGNAL( aboutToShow() ), + this, TQ_SLOT( slotABCContextMenu() )); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("Tag Filters")); + popmenu.insertItem(SmallIcon("tag-new"), i18n("New Tag..."), 10); + popmenu.insertItem(SmallIcon("tag-addressbook"), i18n("Create Tag From AddressBook"), d->ABCMenu); + + if (item) + { + popmenu.insertItem(SmallIcon("tag-properties"), i18n("Edit Tag Properties..."), 11); + popmenu.insertItem(SmallIcon("tag-reset"), i18n("Reset Tag Icon"), 13); + popmenu.insertSeparator(-1); + popmenu.insertItem(SmallIcon("tag-delete"), i18n("Delete Tag"), 12); + } + + popmenu.insertSeparator(-1); + + TQPopupMenu selectTagsMenu; + selectTagsMenu.insertItem(i18n("All Tags"), 14); + if (item) + { + selectTagsMenu.insertSeparator(-1); + selectTagsMenu.insertItem(i18n("Children"), 17); + selectTagsMenu.insertItem(i18n("Parents"), 19); + } + popmenu.insertItem(i18n("Select"), &selectTagsMenu); + + TQPopupMenu deselectTagsMenu; + deselectTagsMenu.insertItem(i18n("All Tags"), 15); + if (item) + { + deselectTagsMenu.insertSeparator(-1); + deselectTagsMenu.insertItem(i18n("Children"), 18); + deselectTagsMenu.insertItem(i18n("Parents"), 20); + } + popmenu.insertItem(i18n("Deselect"), &deselectTagsMenu); + + popmenu.insertItem(i18n("Invert Selection"), 16); + popmenu.insertSeparator(-1); + + TQPopupMenu toggleAutoMenu; + toggleAutoMenu.setCheckable(true); + toggleAutoMenu.insertItem(i18n("None"), 21); + toggleAutoMenu.insertSeparator(-1); + toggleAutoMenu.insertItem(i18n("Children"), 22); + toggleAutoMenu.insertItem(i18n("Parents"), 23); + toggleAutoMenu.insertItem(i18n("Both"), 24); + toggleAutoMenu.setItemChecked(21 + d->toggleAutoTags, true); + popmenu.insertItem(i18n("Toggle Auto"), &toggleAutoMenu); + + TQPopupMenu matchingCongMenu; + matchingCongMenu.setCheckable(true); + matchingCongMenu.insertItem(i18n("Or Between Tags"), 25); + matchingCongMenu.insertItem(i18n("And Between Tags"), 26); + matchingCongMenu.setItemChecked((d->matchingCond == AlbumLister::OrCondition) ? 25 : 26, true); + popmenu.insertItem(i18n("Matching Condition"), &matchingCongMenu); + + ToggleAutoTags oldAutoTags = d->toggleAutoTags; + + int choice = popmenu.exec((TQCursor::pos())); + switch( choice ) + { + case 10: // New Tag. + { + tagNew(item); + break; + } + case 11: // Edit Tag Properties. + { + tagEdit(item); + break; + } + case 12: // Delete Tag. + { + tagDelete(item); + break; + } + case 13: // Reset Tag Icon. + { + TQString errMsg; + AlbumManager::instance()->updateTAlbumIcon(item->album(), TQString("tag"), 0, errMsg); + break; + } + case 14: // Select All Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(this, TQListViewItemIterator::NotChecked); + while (it.current()) + { + TagFilterViewItem* item = (TagFilterViewItem*)it.current(); + + // Ignore "Not Tagged" tag filter. + if (!item->untagged()) + item->setOn(true); + + ++it; + } + triggerChange(); + d->toggleAutoTags = oldAutoTags; + break; + } + case 15: // Deselect All Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(this, TQListViewItemIterator::Checked); + while (it.current()) + { + TagFilterViewItem* item = (TagFilterViewItem*)it.current(); + + // Ignore "Not Tagged" tag filter. + if (!item->untagged()) + item->setOn(false); + + ++it; + } + triggerChange(); + d->toggleAutoTags = oldAutoTags; + break; + } + case 16: // Invert All Tags Selection. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(this); + while (it.current()) + { + TagFilterViewItem* item = (TagFilterViewItem*)it.current(); + + // Ignore "Not Tagged" tag filter. + if (!item->untagged()) + item->setOn(!item->isOn()); + + ++it; + } + triggerChange(); + d->toggleAutoTags = oldAutoTags; + break; + } + case 17: // Select Child Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item, true); + TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this); + tItem->setOn(true); + d->toggleAutoTags = oldAutoTags; + break; + } + case 18: // Deselect Child Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item, false); + TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this); + tItem->setOn(false); + d->toggleAutoTags = oldAutoTags; + break; + } + case 19: // Select Parent Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(item, true); + TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this); + tItem->setOn(true); + d->toggleAutoTags = oldAutoTags; + break; + } + case 20: // Deselect Parent Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(item, false); + TagFilterViewItem *tItem = (TagFilterViewItem*)item->album()->extraData(this); + tItem->setOn(false); + d->toggleAutoTags = oldAutoTags; + break; + } + case 21: // No toggle auto tags. + { + d->toggleAutoTags = NoToggleAuto; + break; + } + case 22: // Toggle auto Children tags. + { + d->toggleAutoTags = Children; + break; + } + case 23: // Toggle auto Parents tags. + { + d->toggleAutoTags = Parents; + break; + } + case 24: // Toggle auto Children and Parents tags. + { + d->toggleAutoTags = ChildrenAndParents; + break; + } + case 25: // Or Between Tags. + { + d->matchingCond = AlbumLister::OrCondition; + triggerChange(); + break; + } + case 26: // And Between Tags. + { + d->matchingCond = AlbumLister::AndCondition; + triggerChange(); + break; + } + default: + break; + } + + if ( choice > 100 ) + { + tagNew(item, d->ABCMenu->text( choice ), "tag-people" ); + } + + delete d->ABCMenu; + d->ABCMenu = 0; +} + +void TagFilterView::slotABCContextMenu() +{ + d->ABCMenu->clear(); + + int counter = 100; + TDEABC::AddressBook* ab = TDEABC::StdAddressBook::self(); + TQStringList names; + for ( TDEABC::AddressBook::Iterator it = ab->begin(); it != ab->end(); ++it ) + { + names.push_back(it->formattedName()); + } + + qHeapSort(names); + + for ( TQStringList::Iterator it = names.begin(); it != names.end(); ++it ) + { + TQString name = *it; + if ( !name.isNull() ) + d->ABCMenu->insertItem( name, ++counter ); + } + + if (counter == 100) + { + d->ABCMenu->insertItem( i18n("No AddressBook entries found"), ++counter ); + d->ABCMenu->setItemEnabled( counter, false ); + } +} + +void TagFilterView::tagNew(TagFilterViewItem* item, const TQString& _title, const TQString& _icon) +{ + TAlbum *parent; + TQString title = _title; + TQString icon = _icon; + AlbumManager *man = AlbumManager::instance(); + + if (!item) + parent = man->findTAlbum(0); + else + parent = item->album(); + + if (title.isNull()) + { + if (!TagEditDlg::tagCreate(kapp->activeWindow(), parent, title, icon)) + return; + } + + TQMap errMap; + AlbumList tList = TagEditDlg::createTAlbum(parent, title, icon, errMap); + TagEditDlg::showtagsListCreationError(kapp->activeWindow(), errMap); + + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TagFilterViewItem* item = (TagFilterViewItem*)(*it)->extraData(this); + if (item) + { + clearSelection(); + setSelected(item, true); + setCurrentItem(item); + ensureItemVisible(item); + } + } +} + +void TagFilterView::tagEdit(TagFilterViewItem* item) +{ + if (!item) + return; + + TAlbum *tag = item->album(); + if (!tag) + return; + + TQString title, icon; + if (!TagEditDlg::tagEdit(kapp->activeWindow(), tag, title, icon)) + { + return; + } + + AlbumManager* man = AlbumManager::instance(); + + if (tag->title() != title) + { + TQString errMsg; + if(!man->renameTAlbum(tag, title, errMsg)) + KMessageBox::error(0, errMsg); + else + item->refresh(); + } + + if (tag->icon() != icon) + { + TQString errMsg; + if (!man->updateTAlbumIcon(tag, icon, 0, errMsg)) + KMessageBox::error(0, errMsg); + else + setTagThumbnail(tag); + } +} + +void TagFilterView::tagDelete(TagFilterViewItem* item) +{ + if (!item) + return; + + TAlbum *tag = item->album(); + if (!tag || tag->isRoot()) + return; + + // find number of subtags + int children = 0; + AlbumIterator iter(tag); + while(iter.current()) + { + children++; + ++iter; + } + + AlbumManager* man = AlbumManager::instance(); + + if (children) + { + int result = KMessageBox::warningContinueCancel(this, + i18n("Tag '%1' has one subtag. " + "Deleting this will also delete " + "the subtag. " + "Do you want to continue?", + "Tag '%1' has %n subtags. " + "Deleting this will also delete " + "the subtags. " + "Do you want to continue?", + children).arg(tag->title())); + + if(result != KMessageBox::Continue) + return; + } + + TQString message; + LLongList assignedItems = man->albumDB()->getItemIDsInTag(tag->id()); + if (!assignedItems.isEmpty()) + { + message = i18n("Tag '%1' is assigned to one item. " + "Do you want to continue?", + "Tag '%1' is assigned to %n items. " + "Do you want to continue?", + assignedItems.count()).arg(tag->title()); + } + else + { + message = i18n("Delete '%1' tag?").arg(tag->title()); + } + + int result = KMessageBox::warningContinueCancel(0, message, + i18n("Delete Tag"), + KGuiItem(i18n("Delete"), + "edit-delete")); + + if (result == KMessageBox::Continue) + { + TQString errMsg; + if (!man->deleteTAlbum(tag, errMsg)) + KMessageBox::error(0, errMsg); + } +} + +void TagFilterView::toggleChildTags(TagFilterViewItem* tItem, bool b) +{ + if (!tItem) + return; + + TAlbum *album = tItem->album(); + if (!album) + return; + + AlbumIterator it(album); + while ( it.current() ) + { + TAlbum *ta = (TAlbum*)it.current(); + TagFilterViewItem *item = (TagFilterViewItem*)ta->extraData(this); + if (item) + { + if (item->isVisible()) + item->setOn(b); + } + ++it; + } +} + +void TagFilterView::toggleParentTags(TagFilterViewItem* tItem, bool b) +{ + if (!tItem) + return; + + TAlbum *album = tItem->album(); + if (!album) + return; + + TQListViewItemIterator it(this); + while (it.current()) + { + TagFilterViewItem* item = dynamic_cast(it.current()); + if (item->isVisible()) + { + Album *a = dynamic_cast(item->album()); + if (a) + { + if (a == album->parent()) + { + item->setOn(b); + toggleParentTags(item, b); + } + } + } + ++it; + } +} + +void TagFilterView::refresh() +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TagFilterViewItem* item = dynamic_cast(*it); + if (item) + item->refresh(); + ++it; + } +} + +void TagFilterView::slotRefresh(const TQMap& tagsStatMap) +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TagFilterViewItem* item = dynamic_cast(*it); + if (item) + { + if (item->album()) + { + int id = item->id(); + TQMap::const_iterator it2 = tagsStatMap.find(id); + if ( it2 != tagsStatMap.end() ) + item->setCount(it2.data()); + } + } + ++it; + } + + refresh(); +} + +void TagFilterView::slotResetTagFilters() +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TagFilterViewItem* item = dynamic_cast(*it); + if (item && item->isOn()) + item->setOn(false); + ++it; + } +} + +void TagFilterView::loadViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + int selectedItem = config->readNumEntry("LastSelectedItem", 0); + + TQValueList openFolders; + if(config->hasKey("OpenFolders")) + { + openFolders = config->readIntListEntry("OpenFolders"); + } + + TagFilterViewItem *item = 0; + TagFilterViewItem *foundItem = 0; + TQListViewItemIterator it(this->lastItem()); + + for( ; it.current(); --it) + { + item = dynamic_cast(it.current()); + if(!item) + continue; + + // Start the album root always open + if(openFolders.contains(item->id()) || item->id() == 0) + setOpen(item, true); + else + setOpen(item, false); + + if(item->id() == selectedItem) + { + // Save the found selected item so that it can be made visible. + foundItem = item; + } + } + + // Important note: this cannot be done inside the previous loop + // because opening folders prevents the visibility. + // Fixes bug #144815. + // (Looks a bit like a bug in TQt to me ...) + if (foundItem) + { + setSelected(foundItem, true); + ensureItemVisible(foundItem); + } +} + +void TagFilterView::saveViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + TagFilterViewItem *item = dynamic_cast(selectedItem()); + if(item) + config->writeEntry("LastSelectedItem", item->id()); + else + config->writeEntry("LastSelectedItem", 0); + + TQValueList openFolders; + TQListViewItemIterator it(this); + for( ; it.current(); ++it) + { + item = dynamic_cast(it.current()); + if(item && isOpen(item)) + openFolders.push_back(item->id()); + } + config->writeEntry("OpenFolders", openFolders); +} + +} // namespace Digikam diff --git a/src/digikam/tagfilterview.h b/src/digikam/tagfilterview.h new file mode 100644 index 00000000..0cd977e1 --- /dev/null +++ b/src/digikam/tagfilterview.h @@ -0,0 +1,118 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-05 + * Description : tags filter view + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TAGFILTERVIEW_H +#define TAGFILTERVIEW_H + +// Local includes. + +#include "folderview.h" + +namespace Digikam +{ + +class Album; +class TagFilterViewItem; +class TagFilterViewPrivate; + +class TagFilterView : public FolderView +{ + TQ_OBJECT + +public: + + enum ToggleAutoTags + { + NoToggleAuto = 0, + Children, + Parents, + ChildrenAndParents + }; + +public: + + TagFilterView(TQWidget* parent); + ~TagFilterView(); + + void stateChanged(TagFilterViewItem*); + void refresh(); + +signals: + + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalTextTagFilterMatch(bool); + +public slots: + + void slotTextTagFilterChanged(const TQString&); + + /** Reset all active tag filters */ + void slotResetTagFilters(); + +protected: + + bool acceptDrop(const TQDropEvent *e) const; + void contentsDropEvent(TQDropEvent *e); + TQDragObject* dragObject(); + +private slots: + + void slotTagAdded(Album* album); + void slotTagMoved(TAlbum* tag, TAlbum* newParent); + void slotTagRenamed(Album* album); + void slotTagDeleted(Album* album); + void slotClear(); + void slotAlbumIconChanged(Album* album); + void slotTimeOut(); + void slotContextMenu(TQListViewItem*, const TQPoint&, int); + void slotABCContextMenu(); + void slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail); + void slotThumbnailLost(Album *album); + void slotReloadThumbnails(); + void slotRefresh(const TQMap&); + +private: + + void triggerChange(); + void tagNew(TagFilterViewItem* item, const TQString& _title=TQString(), + const TQString& _icon=TQString()); + void tagEdit(TagFilterViewItem* item); + void tagDelete(TagFilterViewItem* item); + void setTagThumbnail(TAlbum *album); + void toggleChildTags(TagFilterViewItem* tItem, bool b); + void toggleParentTags(TagFilterViewItem* tItem, bool b); + + void loadViewState(); + void saveViewState(); + +private: + + TagFilterViewPrivate *d; +}; + +} // namespace Digikam + +#endif /* TAGFILTERVIEW_H */ diff --git a/src/digikam/tagfolderview.cpp b/src/digikam/tagfolderview.cpp new file mode 100644 index 00000000..c5d54331 --- /dev/null +++ b/src/digikam/tagfolderview.cpp @@ -0,0 +1,1041 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-22 + * Descritpion : tags folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "album.h" +#include "albumdb.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "albumlister.h" +#include "syncjob.h" +#include "tageditdlg.h" +#include "dragobjects.h" +#include "folderitem.h" +#include "dio.h" +#include "imageattributeswatch.h" +#include "imageinfo.h" +#include "metadatahub.h" +#include "albumthumbnailloader.h" +#include "statusprogressbar.h" +#include "tagfolderview.h" +#include "tagfolderview.moc" + +// X11 includes. + +extern "C" +{ +#include +} + +namespace Digikam +{ + +class TagFolderViewItem : public FolderItem +{ + +public: + + TagFolderViewItem(TQListView *parent, TAlbum *album); + TagFolderViewItem(TQListViewItem *parent, TAlbum *album); + + TAlbum* album() const; + int id() const; + void refresh(); + void setOpen(bool o); + void setCount(int count); + int count(); + +private: + + int m_count; + + TAlbum *m_album; +}; + +TagFolderViewItem::TagFolderViewItem(TQListView *parent, TAlbum *album) + : FolderItem(parent, album->title()) +{ + setDragEnabled(true); + m_album = album; + m_count = 0; +} + +TagFolderViewItem::TagFolderViewItem(TQListViewItem *parent, TAlbum *album) + : FolderItem(parent, album->title()) +{ + setDragEnabled(true); + m_album = album; + m_count = 0; +} + +void TagFolderViewItem::refresh() +{ + if (!m_album) return; + + if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount() && + dynamic_cast(parent())) + { + if (isOpen()) + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count)); + else + { + int countRecursive = m_count; + AlbumIterator it(m_album); + while ( it.current() ) + { + TagFolderViewItem *item = (TagFolderViewItem*)it.current()->extraData(listView()); + if (item) + countRecursive += item->count(); + ++it; + } + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive)); + } + } + else + { + setText(0, m_album->title()); + } +} + +void TagFolderViewItem::setOpen(bool o) +{ + TQListViewItem::setOpen(o); + refresh(); +} + +TAlbum* TagFolderViewItem::album() const +{ + return m_album; +} + +int TagFolderViewItem::id() const +{ + return m_album ? m_album->id() : 0; +} + +void TagFolderViewItem::setCount(int count) +{ + m_count = count; + refresh(); +} + +int TagFolderViewItem::count() +{ + return m_count; +} + +//----------------------------------------------------------------------------- + +class TagFolderViewPriv +{ + +public: + + TagFolderViewPriv() + { + ABCMenu = 0; + albumMan = 0; + } + + TQPopupMenu *ABCMenu; + + AlbumManager *albumMan; +}; + +TagFolderView::TagFolderView(TQWidget *parent) + : FolderView(parent, "TagFolderView") +{ + d = new TagFolderViewPriv(); + d->albumMan = AlbumManager::instance(); + + addColumn(i18n("My Tags")); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(false); + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + // ------------------------------------------------------------------------ + + connect(d->albumMan, TQ_SIGNAL(signalTAlbumsDirty(const TQMap&)), + this, TQ_SLOT(slotRefresh(const TQMap&))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotAlbumRenamed(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotAlbumsCleared())); + + connect(d->albumMan, TQ_SIGNAL(signalAlbumIconChanged(Album*)), + this, TQ_SLOT(slotAlbumIconChanged(Album*))); + + connect(d->albumMan, TQ_SIGNAL(signalTAlbumMoved(TAlbum*, TAlbum*)), + this, TQ_SLOT(slotAlbumMoved(TAlbum*, TAlbum*))); + + // ------------------------------------------------------------------------ + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + + connect(loader, TQ_SIGNAL(signalThumbnail(Album *, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&))); + + connect(loader, TQ_SIGNAL(signalFailed(Album *)), + this, TQ_SLOT(slotThumbnailLost(Album *))); + + connect(loader, TQ_SIGNAL(signalReloadThumbnails()), + this, TQ_SLOT(slotReloadThumbnails())); + + connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(this, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); +} + +TagFolderView::~TagFolderView() +{ + saveViewState(); + delete d; +} + +void TagFolderView::slotTextTagFilterChanged(const TQString& filter) +{ + if (filter.isEmpty()) + { + collapseView(); + return; + } + + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList tList = d->albumMan->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* talbum = (TAlbum*)(*it); + + // don't touch the root Album + if (talbum->isRoot()) + continue; + + bool match = talbum->title().lower().contains(search); + bool doesExpand = false; + if (!match) + { + // check if any of the parents match the search + Album* parent = talbum->parent(); + while (parent && !parent->isRoot()) + { + if (parent->title().lower().contains(search)) + { + match = true; + break; + } + + parent = parent->parent(); + } + } + + if (!match) + { + // check if any of the children match the search + AlbumIterator it(talbum); + while (it.current()) + { + if ((*it)->title().lower().contains(search)) + { + match = true; + doesExpand = true; + break; + } + ++it; + } + } + + TagFolderViewItem* viewItem = (TagFolderViewItem*) talbum->extraData(this); + + if (match) + { + atleastOneMatch = true; + + if (viewItem) + { + viewItem->setVisible(true); + viewItem->setOpen(doesExpand); + } + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + viewItem->setOpen(false); + } + } + } + + emit signalTextTagFilterMatch(atleastOneMatch); +} + +void TagFolderView::slotAlbumAdded(Album *album) +{ + if(!album) + return; + + TAlbum *tag = dynamic_cast(album); + if(!tag) + return; + + TagFolderViewItem *item; + + if(tag->isRoot()) + { + item = new TagFolderViewItem(this, tag); + tag->setExtraData(this, item); + // Toplevel tags are all children of root, and should always be visible - set root to open + item->setOpen(true); + } + else + { + TagFolderViewItem *parent = + (TagFolderViewItem*)tag->parent()->extraData(this); + + if (!parent) + { + DWarning() << k_funcinfo << " Failed to find parent for Tag " + << tag->title() << endl; + return; + } + + item = new TagFolderViewItem(parent, tag); + tag->setExtraData(this, item); + } + + setTagThumbnail(tag); +} + +void TagFolderView::slotAlbumDeleted(Album *album) +{ + if(!album) + return; + + TAlbum *tag = dynamic_cast(album); + if(!tag) + return; + + TagFolderViewItem *item = (TagFolderViewItem*)album->extraData(this); + if(item) + { + TagFolderViewItem *itemParent = dynamic_cast(item->parent()); + + if(itemParent) + itemParent->takeItem(item); + else + takeItem(item); + + delete item; + } +} + +void TagFolderView::slotAlbumsCleared() +{ + clear(); +} + +void TagFolderView::slotAlbumMoved(TAlbum* tag, TAlbum* newParent) +{ + if (!tag || !newParent) + return; + + TagFolderViewItem* item = (TagFolderViewItem*)tag->extraData(this); + if (!item) + return; + + if (item->parent()) + { + TQListViewItem* oldPItem = item->parent(); + oldPItem->takeItem(item); + } + else + { + takeItem(item); + } + + TagFolderViewItem* newPItem = (TagFolderViewItem*)newParent->extraData(this); + if (newPItem) + newPItem->insertItem(item); + else + insertItem(item); +} + +void TagFolderView::slotAlbumRenamed(Album* album) +{ + if (!album) + return; + + TAlbum* tag = dynamic_cast(album); + if (!tag) + return; + + TagFolderViewItem* item = (TagFolderViewItem*)(tag->extraData(this)); + if (item) + item->refresh(); +} + +void TagFolderView::setTagThumbnail(TAlbum *album) +{ + if(!album) + return; + + TagFolderViewItem* item = (TagFolderViewItem*) album->extraData(this); + + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap icon; + if (!loader->getTagThumbnail(album, icon)) + { + if (icon.isNull()) + { + item->setPixmap(0, loader->getStandardTagIcon(album)); + } + else + { + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), icon); + item->setPixmap(0, blendedIcon); + } + } + else + { + // for the time being, set standard icon + item->setPixmap(0, loader->getStandardTagIcon(album)); + } +} + +void TagFolderView::slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail) +{ + if(!album || album->type() != Album::TAG) + return; + + TagFolderViewItem* item = (TagFolderViewItem*)album->extraData(this); + + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), thumbnail); + item->setPixmap(0, blendedIcon); +} + +void TagFolderView::slotThumbnailLost(Album *) +{ + // we already set the standard icon before loading +} + +void TagFolderView::slotReloadThumbnails() +{ + AlbumList tList = d->albumMan->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* tag = (TAlbum*)(*it); + setTagThumbnail(tag); + } +} + +void TagFolderView::slotAlbumIconChanged(Album* album) +{ + if(!album || album->type() != Album::TAG) + return; + + setTagThumbnail((TAlbum *)album); +} + +void TagFolderView::slotSelectionChanged() +{ + if (!active()) + return; + + TQListViewItem* selItem = 0; + TQListViewItemIterator it(this); + while (it.current()) + { + if (it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if (!selItem) + { + d->albumMan->setCurrentAlbum(0); + return; + } + + TagFolderViewItem *tagitem = dynamic_cast(selItem); + if(!tagitem) + { + d->albumMan->setCurrentAlbum(0); + return; + } + + d->albumMan->setCurrentAlbum(tagitem->album()); +} + +void TagFolderView::slotContextMenu(TQListViewItem *item, const TQPoint &, int) +{ + d->ABCMenu = new TQPopupMenu; + + connect( d->ABCMenu, TQ_SIGNAL( aboutToShow() ), + this, TQ_SLOT( slotABCContextMenu() ) ); + + TagFolderViewItem *tag = dynamic_cast(item); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("My Tags")); + popmenu.insertItem(SmallIcon("tag-new"), i18n("New Tag..."), 10); + popmenu.insertItem(SmallIcon("tag-addressbook"), i18n("Create Tag From AddressBook"), d->ABCMenu); + + if(tag && tag->parent()) + { + popmenu.insertItem(SmallIcon("tag-properties"), i18n("Edit Tag Properties..."), 11); + popmenu.insertItem(SmallIcon("tag-reset"), i18n("Reset Tag Icon"), 13); + popmenu.insertSeparator(-1); + popmenu.insertItem(SmallIcon("tag-delete"), i18n("Delete Tag"), 12); + } + + int choice = popmenu.exec((TQCursor::pos())); + switch( choice ) + { + case 10: + { + tagNew(tag); + break; + } + case 11: + { + tagEdit(tag); + break; + } + case 12: + { + tagDelete(tag); + break; + } + case 13: + { + TQString errMsg; + d->albumMan->updateTAlbumIcon(tag->album(), TQString("tag"), 0, errMsg); + break; + } + default: + break; + } + + if ( choice > 100 ) + { + tagNew( tag, d->ABCMenu->text( choice ), "tag-people" ); + } + + delete d->ABCMenu; + d->ABCMenu = 0; +} + +void TagFolderView::slotABCContextMenu() +{ + d->ABCMenu->clear(); + + int counter = 100; + TDEABC::AddressBook* ab = TDEABC::StdAddressBook::self(); + TQStringList names; + for ( TDEABC::AddressBook::Iterator it = ab->begin(); it != ab->end(); ++it ) + { + names.push_back(it->formattedName()); + } + + qHeapSort(names); + + for ( TQStringList::Iterator it = names.begin(); it != names.end(); ++it ) + { + TQString name = *it; + if ( !name.isNull() ) + d->ABCMenu->insertItem( name, ++counter ); + } + + if (counter == 100) + { + d->ABCMenu->insertItem( i18n("No AddressBook entries found"), ++counter ); + d->ABCMenu->setItemEnabled( counter, false ); + } +} + +void TagFolderView::tagNew() +{ + TagFolderViewItem *item = dynamic_cast(selectedItem()); + tagNew(item); +} + +void TagFolderView::tagNew( TagFolderViewItem *item, const TQString& _title, const TQString& _icon ) +{ + TQString title = _title; + TQString icon = _icon; + TAlbum *parent; + + if(!item) + parent = d->albumMan->findTAlbum(0); + else + parent = item->album(); + + if (title.isNull()) + { + if(!TagEditDlg::tagCreate(kapp->activeWindow(), parent, title, icon)) + return; + } + + TQMap errMap; + AlbumList tList = TagEditDlg::createTAlbum(parent, title, icon, errMap); + TagEditDlg::showtagsListCreationError(kapp->activeWindow(), errMap); + + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TagFolderViewItem* item = (TagFolderViewItem*)(*it)->extraData(this); + if (item) + ensureItemVisible(item); + } +} + +void TagFolderView::tagEdit() +{ + TagFolderViewItem *item = dynamic_cast(selectedItem()); + tagEdit(item); +} + +void TagFolderView::tagEdit(TagFolderViewItem *item) +{ + if(!item) + return; + + TAlbum *tag = item->album(); + if(!tag) + return; + + TQString title, icon; + if(!TagEditDlg::tagEdit(kapp->activeWindow(), tag, title, icon)) + return; + + if(tag->title() != title) + { + TQString errMsg; + if(!d->albumMan->renameTAlbum(tag, title, errMsg)) + KMessageBox::error(0, errMsg); + else + item->refresh(); + } + + if(tag->icon() != icon) + { + TQString errMsg; + if (!d->albumMan->updateTAlbumIcon(tag, icon, 0, errMsg)) + KMessageBox::error(0, errMsg); + else + setTagThumbnail(tag); + } +} + +void TagFolderView::tagDelete() +{ + TagFolderViewItem *item = dynamic_cast(selectedItem()); + tagDelete(item); +} + +void TagFolderView::tagDelete(TagFolderViewItem *item) +{ + if(!item) + return; + + TAlbum *tag = item->album(); + if (!tag || tag->isRoot()) + return; + + // find number of subtags + int children = 0; + AlbumIterator iter(tag); + while(iter.current()) + { + children++; + ++iter; + } + + if(children) + { + int result = KMessageBox::warningContinueCancel(this, + i18n("Tag '%1' has one subtag. " + "Deleting this will also delete " + "the subtag. " + "Do you want to continue?", + "Tag '%1' has %n subtags. " + "Deleting this will also delete " + "the subtags. " + "Do you want to continue?", + children).arg(tag->title())); + + if(result != KMessageBox::Continue) + return; + } + + TQString message; + LLongList assignedItems = d->albumMan->albumDB()->getItemIDsInTag(tag->id()); + if (!assignedItems.isEmpty()) + { + message = i18n("Tag '%1' is assigned to one item. " + "Do you want to continue?", + "Tag '%1' is assigned to %n items. " + "Do you want to continue?", + assignedItems.count()).arg(tag->title()); + } + else + { + message = i18n("Delete '%1' tag?").arg(tag->title()); + } + + int result = KMessageBox::warningContinueCancel(0, message, + i18n("Delete Tag"), + KGuiItem(i18n("Delete"), + "edit-delete")); + + if(result == KMessageBox::Continue) + { + TQString errMsg; + if (!d->albumMan->deleteTAlbum(tag, errMsg)) + KMessageBox::error(0, errMsg); + } +} + +TQDragObject* TagFolderView::dragObject() +{ + TagFolderViewItem *item = dynamic_cast(dragItem()); + if(!item) + return 0; + + if(!item->parent()) + return 0; + + TagDrag *t = new TagDrag(item->album()->id(), this); + t->setPixmap(*item->pixmap(0)); + + return t; +} + +bool TagFolderView::acceptDrop(const TQDropEvent *e) const +{ + TQPoint vp = contentsToViewport(e->pos()); + TagFolderViewItem *itemDrop = dynamic_cast(itemAt(vp)); + TagFolderViewItem *itemDrag = dynamic_cast(dragItem()); + + if(TagDrag::canDecode(e) || TagListDrag::canDecode(e)) + { + // Allow dragging at the root, to move the tag to the root + if(!itemDrop) + return true; + + // Dragging an item on itself makes no sense + if(itemDrag == itemDrop) + return false; + + // Dragging a parent on its child makes no sense + if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album())) + return false; + + return true; + } + + if (ItemDrag::canDecode(e) && itemDrop && itemDrop->parent()) + { + // Only other possibility is image items being dropped + // And allow this only if there is a Tag to be dropped + // on and also the Tag is not root. + return true; + } + + return false; +} + +void TagFolderView::contentsDropEvent(TQDropEvent *e) +{ + FolderView::contentsDropEvent(e); + + if(!acceptDrop(e)) + return; + + TQPoint vp = contentsToViewport(e->pos()); + TagFolderViewItem *itemDrop = dynamic_cast(itemAt(vp)); + + if (!itemDrop) + return; + + if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + TAlbum* talbum = d->albumMan->findTAlbum(tagID); + + if(!talbum) + return; + + if (talbum == itemDrop->album()) + return; + + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Tags")); + popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + + if(id == 10) + { + TAlbum *newParentTag = 0; + + if (!itemDrop) + { + // move dragItem to the root + newParentTag = d->albumMan->findTAlbum(0); + } + else + { + // move dragItem as child of dropItem + newParentTag = itemDrop->album(); + } + + TQString errMsg; + if (!d->albumMan->moveTAlbum(talbum, newParentTag, errMsg)) + { + KMessageBox::error(this, errMsg); + } + + if(itemDrop && !itemDrop->isOpen()) + itemDrop->setOpen(true); + } + + return; + } + + if (ItemDrag::canDecode(e)) + { + TAlbum *destAlbum = itemDrop->album(); + TAlbum *srcAlbum; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + return; + + if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) + return; + + // all the albumids will be the same + int albumID = albumIDs.first(); + srcAlbum = d->albumMan->findTAlbum(albumID); + if (!srcAlbum) + { + DWarning() << "Could not find source album of drag" + << endl; + return; + } + + int id = 0; + char keys_return[32]; + XQueryKeymap(x11Display(), keys_return); + int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3); + int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4); + + if(srcAlbum == destAlbum) + { + // Setting the dropped image as the album thumbnail + // If the ctrl key is pressed, when dropping the image, the + // thumbnail is set without a popup menu + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 12; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Tags")); + popMenu.insertItem(i18n("Set as Tag Thumbnail"), 12); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if(id == 12) + { + TQString errMsg; + d->albumMan->updateTAlbumIcon(destAlbum, TQString(), imageIDs.first(), errMsg); + } + return; + } + + // If a ctrl key is pressed while dropping the drag object, + // the tag is assigned to the images without showing a + // popup menu. + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 10; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("My Tags")); + popMenu.insertItem( SmallIcon("tag"), i18n("Assign Tag '%1' to Items") + .arg(destAlbum->prettyURL()), 10) ; + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if (id == 10) + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assigning image tags. Please wait...")); + + AlbumLister::instance()->blockSignals(true); + d->albumMan->albumDB()->beginTransaction(); + int i=0; + for (TQValueList::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + // create temporary ImageInfo object + ImageInfo info(*it); + + MetadataHub hub; + hub.load(&info); + hub.setTag(destAlbum, true); + hub.write(&info, MetadataHub::PartialWrite); + hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); + + emit signalProgressValue((int)((i++/(float)imageIDs.count())*100.0)); + kapp->processEvents(); + } + AlbumLister::instance()->blockSignals(false); + d->albumMan->albumDB()->commitTransaction(); + + ImageAttributesWatch::instance()->imagesChanged(destAlbum->id()); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + } + } +} + +void TagFolderView::selectItem(int id) +{ + TAlbum* tag = d->albumMan->findTAlbum(id); + if(!tag) + return; + + TagFolderViewItem *item = + (TagFolderViewItem*)tag->extraData(this); + if(item) + { + setSelected(item, true); + ensureItemVisible(item); + } +} + +void TagFolderView::refresh() +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TagFolderViewItem* item = dynamic_cast(*it); + if (item) + item->refresh(); + ++it; + } +} + +void TagFolderView::slotRefresh(const TQMap& tagsStatMap) +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TagFolderViewItem* item = dynamic_cast(*it); + if (item) + { + if (item->album()) + { + int id = item->id(); + TQMap::const_iterator it2 = tagsStatMap.find(id); + if ( it2 != tagsStatMap.end() ) + item->setCount(it2.data()); + } + } + ++it; + } + + refresh(); +} + +} // namespace Digikam diff --git a/src/digikam/tagfolderview.h b/src/digikam/tagfolderview.h new file mode 100644 index 00000000..27523e9d --- /dev/null +++ b/src/digikam/tagfolderview.h @@ -0,0 +1,107 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-22 + * Description : tags folder view. + * + * Copyright (C) 2005-2006 by Joern Ahrens + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** @file tagfoldeview.h */ + +#ifndef _TAGFOLDERVIEW_H_ +#define _TAGFOLDERVIEW_H_ + +// Local includes. + +#include "folderview.h" + +class TQDropEvent; + +namespace Digikam +{ + +class Album; +class TAlbum; +class TagFolderViewItem; +class TagFolderViewPriv; + +class TagFolderView : public FolderView +{ + TQ_OBJECT + +public: + + TagFolderView(TQWidget *parent); + ~TagFolderView(); + + void tagNew(); + void tagEdit(); + void tagDelete(); + + void selectItem(int id); + void refresh(); + + signals: + + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalTextTagFilterMatch(bool); + +public slots: + + void slotTextTagFilterChanged(const TQString&); + +protected: + + void contentsDropEvent(TQDropEvent *e); + TQDragObject* dragObject(); + bool acceptDrop(const TQDropEvent *e) const; + +private slots: + + void slotAlbumAdded(Album*); + void slotSelectionChanged(); + void slotAlbumDeleted(Album*); + void slotAlbumRenamed(Album*); + void slotAlbumsCleared(); + void slotAlbumIconChanged(Album* album); + void slotAlbumMoved(TAlbum* tag, TAlbum* newParent); + void slotContextMenu(TQListViewItem*, const TQPoint&, int); + void slotABCContextMenu(); + void slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail); + void slotThumbnailLost(Album *album); + void slotReloadThumbnails(); + void slotRefresh(const TQMap&); + +private: + + void tagNew(TagFolderViewItem *item, const TQString& _title=TQString(), + const TQString& _icon=TQString() ); + void tagEdit(TagFolderViewItem *item); + void tagDelete(TagFolderViewItem *item); + void setTagThumbnail(TAlbum *album); + +private: + + TagFolderViewPriv *d; +}; + +} // namespace Digikam + +#endif // _TAGFOLDEVIEW_H_ diff --git a/src/digikam/tagspopupmenu.cpp b/src/digikam/tagspopupmenu.cpp new file mode 100644 index 00000000..8b9e4644 --- /dev/null +++ b/src/digikam/tagspopupmenu.cpp @@ -0,0 +1,366 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-07 + * Description : a pop-up menu implementation to display a + * hierarchical view of digiKam tags. + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define ADDTAGID 10000 + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albumiconview.h" +#include "albumiconitem.h" +#include "albummanager.h" +#include "albumdb.h" +#include "album.h" +#include "syncjob.h" +#include "tageditdlg.h" +#include "tagspopupmenu.h" +#include "tagspopupmenu.moc" + +namespace Digikam +{ + +class TagsPopupCheckedMenuItem : public TQCustomMenuItem +{ +public: + + TagsPopupCheckedMenuItem(TQPopupMenu* popup, const TQString& txt, const TQPixmap& pix) + : TQCustomMenuItem(), m_popup(popup), m_txt(txt), m_pix(pix) + { + } + + virtual TQSize sizeHint() + { + TQFont fn = m_popup->font(); + TQFontMetrics fm(fn); + int w = fm.width(m_txt) + 5 + kapp->style().pixelMetric(TQStyle::PM_IndicatorWidth, 0); + int h = TQMAX(fm.height(), m_pix.height()); + return TQSize( w, h ); + } + + virtual void paint(TQPainter* p, const TQColorGroup& cg, bool act, bool enabled, + int x, int y, int w, int h ) + { + p->save(); + p->setPen(act ? cg.highlightedText() : cg.highlight()); + p->drawText(x, y, w, h, TQt::AlignLeft|TQt::AlignVCenter, m_txt); + p->restore(); + + if (!m_pix.isNull()) + { + TQRect pixRect(x/2 - m_pix.width()/2, y, m_pix.width(), m_pix.height()); + p->drawPixmap( pixRect.topLeft(), m_pix ); + } + + int checkWidth = kapp->style().pixelMetric(TQStyle::PM_IndicatorWidth, 0); + int checkHeight = kapp->style().pixelMetric(TQStyle::PM_IndicatorHeight, 0); + + TQStyle::SFlags flags = TQStyle::Style_Default; + flags |= TQStyle::Style_On; + if (enabled) + flags |= TQStyle::Style_Enabled; + if (act) + flags |= TQStyle::Style_Active; + + TQFont fn = m_popup->font(); + TQFontMetrics fm(fn); + TQRect r(x + 5 + fm.width(m_txt), y + (h/2-checkHeight/2), checkWidth, checkHeight); + kapp->style().drawPrimitive(TQStyle::PE_CheckMark, p, r, cg, flags); + } + +private: + + TQPopupMenu *m_popup; + + TQString m_txt; + + TQPixmap m_pix; +}; + +// ------------------------------------------------------------------------ + +class TagsPopupMenuPriv +{ +public: + + TagsPopupMenuPriv(){} + + int addToID; + + TQPixmap addTagPix; + + TQValueList assignedTags; + TQValueList selectedImageIDs; + + TagsPopupMenu::Mode mode; +}; + +TagsPopupMenu::TagsPopupMenu(const TQValueList& selectedImageIDs, int addToID, Mode mode) + : TQPopupMenu(0) +{ + d = new TagsPopupMenuPriv; + d->selectedImageIDs = selectedImageIDs; + d->addToID = addToID; + d->mode = mode; + + TDEIconLoader *iconLoader = TDEApplication::kApplication()->iconLoader(); + d->addTagPix = iconLoader->loadIcon("tag", + TDEIcon::NoGroup, + TDEIcon::SizeSmall, + TDEIcon::DefaultState, + 0, true); + + connect(this, TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotAboutToShow())); + + connect(this, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotActivated(int))); +} + +TagsPopupMenu::~TagsPopupMenu() +{ + delete d; +} + +void TagsPopupMenu::clearPopup() +{ + d->assignedTags.clear(); + clear(); +} + +TQPopupMenu* TagsPopupMenu::buildSubMenu(int tagid) +{ + AlbumManager* man = AlbumManager::instance(); + TAlbum* album = man->findTAlbum(tagid); + if (!album) + return 0; + + TQPopupMenu* popup = new TQPopupMenu(this); + + connect(popup, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotActivated(int))); + + if (d->mode == ASSIGN) + { + popup->insertItem(d->addTagPix, i18n("Add New Tag..."), ADDTAGID + album->id()); + popup->insertSeparator(); + + TQPixmap pix = SyncJob::getTagThumbnail(album); + if ((d->mode == ASSIGN) && (d->assignedTags.contains(album->id()))) + { + popup->insertItem(new TagsPopupCheckedMenuItem(popup, album->title(), pix), + d->addToID + album->id()); + } + else + { + popup->insertItem(pix, album->title(), d->addToID + album->id()); + } + + if (album->firstChild()) + { + popup->insertSeparator(); + } + } + else + { + if (!album->isRoot()) + { + TQPixmap pix = SyncJob::getTagThumbnail(album); + popup->insertItem(pix, album->title(), d->addToID + album->id()); + popup->insertSeparator(); + } + } + + iterateAndBuildMenu(popup, album); + + return popup; +} + +void TagsPopupMenu::slotAboutToShow() +{ + clearPopup(); + + AlbumManager* man = AlbumManager::instance(); + + if (d->mode == REMOVE || d->mode == DISPLAY) + { + if (d->selectedImageIDs.isEmpty()) + return; + + d->assignedTags = man->albumDB()->getItemCommonTagIDs(d->selectedImageIDs); + + if (d->assignedTags.isEmpty()) + return; + + // also add the parents of the assigned tags + IntList tList; + for (IntList::iterator it = d->assignedTags.begin(); + it != d->assignedTags.end(); ++it) + { + TAlbum* album = man->findTAlbum(*it); + if (album) + { + Album* a = album->parent(); + while (a) + { + tList.append(a->id()); + a = a->parent(); + } + } + } + + for (IntList::iterator it = tList.begin(); + it != tList.end(); ++it) + { + d->assignedTags.append(*it); + } + } + else if (d->mode == ASSIGN) + { + if (d->selectedImageIDs.count() == 1) + { + d->assignedTags = man->albumDB()->getItemCommonTagIDs(d->selectedImageIDs); + } + } + + TAlbum* album = man->findTAlbum(0); + if (!album) + return; + + if (d->mode == ASSIGN) + { + insertItem(d->addTagPix, i18n("Add New Tag..."), ADDTAGID); + if (album->firstChild()) + { + insertSeparator(); + } + } + + iterateAndBuildMenu(this, album); +} + +// for qHeapSort +typedef TQPair TagsMenuSortType; +bool operator<(const TagsMenuSortType &lhs, const TagsMenuSortType &rhs) +{ + return lhs.first < rhs.first; +} + +void TagsPopupMenu::iterateAndBuildMenu(TQPopupMenu *menu, TAlbum *album) +{ + TQValueVector sortedTags; + + for (Album* a = album->firstChild(); a; a = a->next()) + { + sortedTags.push_back(qMakePair(a->title(), a)); + } + + qHeapSort(sortedTags); + + for (TQValueVector::Iterator i = sortedTags.begin(); i != sortedTags.end(); ++i) + { + Album *a = i->second; + + if (d->mode == REMOVE || d->mode == DISPLAY) + { + IntList::iterator it = tqFind(d->assignedTags.begin(), + d->assignedTags.end(), a->id()); + if (it == d->assignedTags.end()) + continue; + } + + TQPixmap pix = SyncJob::getTagThumbnail((TAlbum*)a); + TQString t = a->title(); + t.replace('&',"&&"); + + if (a->firstChild()) + { + menu->insertItem(pix, t, buildSubMenu(a->id())); + } + else + { + if ((d->mode == ASSIGN) && (d->assignedTags.contains(a->id()))) + { + menu->insertItem(new TagsPopupCheckedMenuItem(this, a->title(), pix), + d->addToID + a->id()); + } + else + { + menu->insertItem(pix, t, d->addToID + a->id()); + } + } + } +} + +void TagsPopupMenu::slotActivated(int id) +{ + if (id >= ADDTAGID) + { + int tagID = id - ADDTAGID; + + AlbumManager* man = AlbumManager::instance(); + TAlbum* parent = man->findTAlbum(tagID); + if (!parent) + { + DWarning() << "Failed to find album with id " + << tagID << endl; + return; + } + + TQString title, icon; + if (!TagEditDlg::tagCreate(kapp->activeWindow(), parent, title, icon)) + return; + + TQMap errMap; + AlbumList tList = TagEditDlg::createTAlbum(parent, title, icon, errMap); + TagEditDlg::showtagsListCreationError(kapp->activeWindow(), errMap); + + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + emit signalTagActivated((*it)->id()); + } + else + { + int tagID = id - d->addToID; + emit signalTagActivated(tagID); + } +} + +} // namespace Digikam diff --git a/src/digikam/tagspopupmenu.h b/src/digikam/tagspopupmenu.h new file mode 100644 index 00000000..5e746410 --- /dev/null +++ b/src/digikam/tagspopupmenu.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-07 + * Description : a pop-up menu implementation to display a + * hierarchical view of digiKam tags. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TAGSPOPUPMENU_H +#define TAGSPOPUPMENU_H + +// TQt includes. + +#include + +namespace Digikam +{ + +class TAlbum; +class TagsPopupMenuPriv; + +class TagsPopupMenu : public TQPopupMenu +{ + TQ_OBJECT + +public: + + enum Mode + { + ASSIGN = 0, + REMOVE, + DISPLAY // Used by "GoTo Tag" feature + }; + + TagsPopupMenu(const TQValueList& selectedImageIDs, int addToID, Mode mode); + ~TagsPopupMenu(); + +signals: + + void signalTagActivated(int id); + +private slots: + + void slotAboutToShow(); + void slotActivated(int id); + +private: + + void clearPopup(); + TQPopupMenu* buildSubMenu(int tagid); + void iterateAndBuildMenu(TQPopupMenu *menu, TAlbum *album); + bool showThisTag(int tagid); + +private: + + TagsPopupMenuPriv* d; +}; + +} // namespace Digikam + +#endif /* TAGSPOPUPMENU_H */ diff --git a/src/digikam/thumbnailsize.h b/src/digikam/thumbnailsize.h new file mode 100644 index 00000000..a665913e --- /dev/null +++ b/src/digikam/thumbnailsize.h @@ -0,0 +1,90 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-07 + * Description : thumbnails size interface + * + * Copyright (C) 2004 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef THUMBNAILSIZE_H +#define THUMBNAILSIZE_H + +namespace Digikam +{ + +class ThumbnailSize +{ + +public: + + enum Size + { + Step = 8, + Tiny = 32, + Small = 64, + Medium = 96, + Large = 160, + Huge = 256 + }; + + ThumbnailSize() + { + m_Size = Medium; + } + + ThumbnailSize(int size) + { + m_Size = size; + } + + ~ThumbnailSize() + { + } + + ThumbnailSize(const ThumbnailSize& thumbsize) + { + if (this != &thumbsize) + m_Size = thumbsize.m_Size; + } + + ThumbnailSize& operator=(const ThumbnailSize& thumbsize) + { + if (this != &thumbsize) + m_Size = thumbsize.m_Size; + return *this; + } + + bool operator!=(const ThumbnailSize& thumbsize) + { + return m_Size != thumbsize.m_Size; + } + + int size() const + { + return m_Size; + } + +private: + + int m_Size; + +}; + +} // namespace Digikam + +#endif // THUMBNAILSIZE_H diff --git a/src/digikam/timelinefolderview.cpp b/src/digikam/timelinefolderview.cpp new file mode 100644 index 00000000..0168b8cb --- /dev/null +++ b/src/digikam/timelinefolderview.cpp @@ -0,0 +1,308 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-01-14 + * Description : Searches dates folder view used by timeline + * + * Copyright (C) 2008-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "album.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "folderitem.h" +#include "timelinefolderview.h" +#include "timelinefolderview.moc" + +namespace Digikam +{ + +class TimeLineFolderItem : public FolderItem +{ + +public: + + TimeLineFolderItem(TQListView* parent, SAlbum* album) + : FolderItem(parent, album->title()), + m_album(album) + { + m_album->setExtraData(parent, this); + } + + ~TimeLineFolderItem() + { + m_album->removeExtraData(listView()); + } + + int compare(TQListViewItem* i, int , bool ) const + { + if (!i) + return 0; + + return text(0).localeAwareCompare(i->text(0)); + } + + int id() const + { + return m_album ? m_album->id() : 0; + } + + SAlbum* album() const + { + return m_album; + } + +private: + + SAlbum *m_album; +}; + +TimeLineFolderView::TimeLineFolderView(TQWidget* parent) + : FolderView(parent, "TimeLineFolderView") +{ + m_currentTimeLineSearchName = TQString("_Current_Time_Line_Search_"); + addColumn(i18n("My Date Searches")); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(false); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(clear())); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotAlbumRenamed(Album*))); + + connect(this, TQ_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)), + this, TQ_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int))); + + connect(this, TQ_SIGNAL(selectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); +} + +TimeLineFolderView::~TimeLineFolderView() +{ + saveViewState(); +} + +TQString TimeLineFolderView::currentTimeLineSearchName() const +{ + return m_currentTimeLineSearchName; +} + +void TimeLineFolderView::slotTextSearchFilterChanged(const TQString& filter) +{ + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList sList = AlbumManager::instance()->allSAlbums(); + for (AlbumList::iterator it = sList.begin(); it != sList.end(); ++it) + { + SAlbum* salbum = (SAlbum*)(*it); + TimeLineFolderItem* viewItem = (TimeLineFolderItem*) salbum->extraData(this); + + // Check if a special url query exist to identify a SAlbum dedicaced to Date Search + // used with TimeLine. + KURL url = salbum->kurl(); + TQString type = url.queryItem("type"); + + if (salbum->title().lower().contains(search) && + type == TQString("datesearch") && + salbum->title() != currentTimeLineSearchName()) + { + atleastOneMatch = true; + + if (viewItem) + viewItem->setVisible(true); + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + } + } + } + + emit signalTextSearchFilterMatch(atleastOneMatch); +} + +void TimeLineFolderView::searchDelete(SAlbum* album) +{ + if (!album) + return; + + // Make sure that a complicated search is not deleted accidentally + int result = KMessageBox::warningYesNo(this, i18n("Are you sure you want to " + "delete the selected Date Search " + "\"%1\"?") + .arg(album->title()), + i18n("Delete Date Search?"), + i18n("Delete"), + KStdGuiItem::cancel()); + + if (result != KMessageBox::Yes) + return; + + AlbumManager::instance()->deleteSAlbum(album); +} + +void TimeLineFolderView::slotAlbumAdded(Album* a) +{ + if (!a || a->type() != Album::SEARCH) + return; + + SAlbum *salbum = dynamic_cast(a); + if (!salbum) return; + + // Check if a special url query exist to identify a SAlbum dedicaced to Date Search + KURL url = salbum->kurl(); + TQMap queries = url.queryItems(); + if (queries.isEmpty()) return; + + TQString type = url.queryItem("type"); + if (type != TQString("datesearch")) return; + + // We will ignore the internal Dates Search Album used to perform selection from timeline. + TQString name = url.queryItem("name"); + if (name == currentTimeLineSearchName()) return; + + TimeLineFolderItem* item = new TimeLineFolderItem(this, salbum); + item->setPixmap(0, SmallIcon("edit-find", AlbumSettings::instance()->getDefaultTreeIconSize())); +} + +void TimeLineFolderView::slotAlbumDeleted(Album* a) +{ + if (!a || a->type() != Album::SEARCH) + return; + + SAlbum* album = (SAlbum*)a; + + TimeLineFolderItem* item = (TimeLineFolderItem*) album->extraData(this); + if (item) + delete item; +} + +void TimeLineFolderView::slotAlbumRenamed(Album* album) +{ + if (!album) + return; + + SAlbum* salbum = dynamic_cast(album); + if (!salbum) + return; + + TimeLineFolderItem* item = (TimeLineFolderItem*)(salbum->extraData(this)); + if (item) + item->setText(0, item->album()->title()); +} + +void TimeLineFolderView::slotSelectionChanged() +{ + TQListViewItem* selItem = 0; + + TQListViewItemIterator it( this ); + while (it.current()) + { + if (it.current()->isSelected()) + { + selItem = it.current(); + break; + } + ++it; + } + + if (!selItem) + { + emit signalAlbumSelected(0); + return; + } + + TimeLineFolderItem* searchItem = dynamic_cast(selItem); + + if (!searchItem || !searchItem->album()) + emit signalAlbumSelected(0); + else + emit signalAlbumSelected(searchItem->album()); +} + +void TimeLineFolderView::slotContextMenu(TQListViewItem* item, const TQPoint&, int) +{ + if (!item) return; + + TimeLineFolderItem* sItem = dynamic_cast(item); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("My Date Searches")); + popmenu.insertItem(SmallIcon("pencil"), i18n("Rename..."), 10); + popmenu.insertItem(SmallIcon("edit-delete"), i18n("Delete"), 11); + + switch (popmenu.exec(TQCursor::pos())) + { + case 10: + { + emit signalRenameAlbum(sItem->album()); + break; + } + case 11: + { + searchDelete(sItem->album()); + break; + } + default: + break; + } +} + +void TimeLineFolderView::selectItem(int id) +{ + SAlbum *album = AlbumManager::instance()->findSAlbum(id); + if(!album) + return; + + TimeLineFolderItem *item = (TimeLineFolderItem*)album->extraData(this); + if(item) + { + setSelected(item, true); + ensureItemVisible(item); + } +} + +} // namespace Digikam diff --git a/src/digikam/timelinefolderview.h b/src/digikam/timelinefolderview.h new file mode 100644 index 00000000..2e8a8a86 --- /dev/null +++ b/src/digikam/timelinefolderview.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-01-14 + * Description : Searches dates folder view used by timeline + * + * Copyright (C) 2008-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TIMELINEFOLDERVIEW_H +#define TIMELINEFOLDERVIEW_H + +// Local includes. + +#include "folderview.h" + +namespace Digikam +{ + +class SAlbum; +class TimeLineFolderItem; + +class TimeLineFolderView : public FolderView +{ + TQ_OBJECT + +public: + + TimeLineFolderView(TQWidget* parent); + ~TimeLineFolderView(); + + void searchDelete(SAlbum* album); + TQString currentTimeLineSearchName() const; + +signals: + + void signalTextSearchFilterMatch(bool); + void signalAlbumSelected(SAlbum*); + void signalRenameAlbum(SAlbum*); + +public slots: + + void slotTextSearchFilterChanged(const TQString&); + +private slots: + + void slotAlbumAdded(Album* album); + void slotAlbumDeleted(Album* album); + void slotAlbumRenamed(Album* album); + void slotSelectionChanged(); + void slotContextMenu(TQListViewItem*, const TQPoint&, int); + +protected: + + void selectItem(int id); + +private: + + // Used to store in database the name of search performed by + // current selection from timeline. + TQString m_currentTimeLineSearchName; +}; + +} // namespace Digikam + +#endif /* TIMELINEFOLDERVIEW_H */ diff --git a/src/digikam/timelineview.cpp b/src/digikam/timelineview.cpp new file mode 100644 index 00000000..ae8a6bc2 --- /dev/null +++ b/src/digikam/timelineview.cpp @@ -0,0 +1,645 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-08 + * Description : Time line sidebar tab contents. + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if KDE_IS_VERSION(3,2,0) +#include +#else +#include +#endif + +// Local includes. + +#include "album.h" +#include "albummanager.h" +#include "ddebug.h" +#include "searchtextbar.h" +#include "timelinefolderview.h" +#include "timelinewidget.h" +#include "timelineview.h" +#include "timelineview.moc" + +namespace Digikam +{ + +class TimeLineViewPriv +{ + +public: + + TimeLineViewPriv() + { + timeUnitCB = 0; + scaleBG = 0; + cursorDateLabel = 0; + cursorCountLabel = 0; + timeLineWidget = 0; + timer = 0; + resetButton = 0; + saveButton = 0; + scrollBar = 0; + timeLineFolderView = 0; + nameEdit = 0; + searchDateBar = 0; + } + + TQScrollBar *scrollBar; + + TQTimer *timer; + + TQComboBox *timeUnitCB; + + TQHButtonGroup *scaleBG; + + TQPushButton *resetButton; + TQPushButton *saveButton; + + TQLabel *cursorCountLabel; + + KLineEdit *nameEdit; + + KSqueezedTextLabel *cursorDateLabel; + + SearchTextBar *searchDateBar; + + TimeLineWidget *timeLineWidget; + + TimeLineFolderView *timeLineFolderView; +}; + +TimeLineView::TimeLineView(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new TimeLineViewPriv; + d->timer = new TQTimer(this); + + TQVBoxLayout *vlay = new TQVBoxLayout(this); + TQFrame *panel = new TQFrame(this); + panel->setFrameStyle(TQFrame::StyledPanel | TQFrame::Sunken); + panel->setLineWidth(1); + + TQGridLayout *grid = new TQGridLayout(panel, 4, 3); + + // --------------------------------------------------------------- + + TQWidget *hbox1 = new TQWidget(panel); + TQHBoxLayout *hlay = new TQHBoxLayout(hbox1); + + TQLabel *label1 = new TQLabel(i18n("Time Unit:"), hbox1); + d->timeUnitCB = new TQComboBox(false, hbox1); + d->timeUnitCB->insertItem(i18n("Day"), TimeLineWidget::Day); + d->timeUnitCB->insertItem(i18n("Week"), TimeLineWidget::Week); + d->timeUnitCB->insertItem(i18n("Month"), TimeLineWidget::Month); + d->timeUnitCB->insertItem(i18n("Year"), TimeLineWidget::Year); + d->timeUnitCB->setCurrentItem((int)TimeLineWidget::Month); + d->timeUnitCB->setFocusPolicy(TQWidget::NoFocus); + TQWhatsThis::add(d->timeUnitCB, i18n("

Select the histogram time unit here.

" + "You can change the graph decade to zoom in or zoom out over time.")); + + d->scaleBG = new TQHButtonGroup(hbox1); + d->scaleBG->setExclusive(true); + d->scaleBG->setFrameShape(TQFrame::NoFrame); + d->scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add(d->scaleBG, i18n("

Select the histogram scale here.

" + "If the date count's maximal values are small, you can use the linear scale.

" + "Logarithmic scale can be used when the maximal values are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

Linear" ) ); + d->scaleBG->insert(linHistoButton, TimeLineWidget::LinScale); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

Logarithmic" ) ); + d->scaleBG->insert(logHistoButton, TimeLineWidget::LogScale); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + hlay->setMargin(0); + hlay->setSpacing(KDialog::spacingHint()); + hlay->addWidget(label1); + hlay->addWidget(d->timeUnitCB); + hlay->addItem(new TQSpacerItem(10, 10, TQSizePolicy::Expanding, TQSizePolicy::Minimum)); + hlay->addWidget(d->scaleBG); + + // --------------------------------------------------------------- + + d->timeLineWidget = new TimeLineWidget(panel); + d->scrollBar = new TQScrollBar(panel); + d->scrollBar->setOrientation(TQt::Horizontal); + d->scrollBar->setMinValue(0); + d->scrollBar->setLineStep(1); + + d->cursorDateLabel = new KSqueezedTextLabel(0, panel); + d->cursorCountLabel = new TQLabel(panel); + d->cursorCountLabel->setAlignment(TQt::AlignRight); + + // --------------------------------------------------------------- + + TQHBox *hbox2 = new TQHBox(panel); + hbox2->setMargin(0); + hbox2->setSpacing(KDialog::spacingHint()); + + d->resetButton = new TQPushButton(hbox2); + d->resetButton->setPixmap(SmallIcon("reload_page")); + TQToolTip::add(d->resetButton, i18n("Clear current selection")); + TQWhatsThis::add(d->resetButton, i18n("

If you press this button, current " + "dates selection from time-line will be " + "clear.")); + d->nameEdit = new KLineEdit(hbox2); + TQWhatsThis::add(d->nameEdit, i18n("

Enter the name of the current dates search to save in the " + "\"My Date Searches\" view")); + + d->saveButton = new TQPushButton(hbox2); + d->saveButton->setPixmap(SmallIcon("document-save")); + d->saveButton->setEnabled(false); + TQToolTip::add(d->saveButton, i18n("Save current selection to a new virtual Album")); + TQWhatsThis::add(d->saveButton, i18n("

If you press this button, current " + "dates selection from time-line will be " + "saved to a new search virtual Album using name " + "set on the left side.")); + + // --------------------------------------------------------------- + + grid->addMultiCellWidget(hbox1, 0, 0, 0, 3); + grid->addMultiCellWidget(d->cursorDateLabel, 1, 1, 0, 2); + grid->addMultiCellWidget(d->cursorCountLabel, 1, 1, 3, 3); + grid->addMultiCellWidget(d->timeLineWidget, 2, 2, 0, 3); + grid->addMultiCellWidget(d->scrollBar, 3, 3, 0, 3); + grid->addMultiCellWidget(hbox2, 4, 4, 0, 3); + grid->setColStretch(2, 10); + grid->setMargin(KDialog::spacingHint()); + grid->setSpacing(KDialog::spacingHint()); + + // --------------------------------------------------------------- + + d->timeLineFolderView = new TimeLineFolderView(this); + d->searchDateBar = new SearchTextBar(this, "TimeLineViewSearchDateBar"); + + vlay->addWidget(panel); + vlay->addWidget(d->timeLineFolderView); + vlay->addItem(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::Minimum)); + vlay->addWidget(d->searchDateBar); + vlay->setMargin(0); + vlay->setSpacing(0); + + // --------------------------------------------------------------- + + connect(AlbumManager::instance(), TQ_SIGNAL(signalDatesMapDirty(const TQMap&)), + d->timeLineWidget, TQ_SLOT(slotDatesMap(const TQMap&))); + + connect(d->timeLineFolderView, TQ_SIGNAL(signalAlbumSelected(SAlbum*)), + this, TQ_SLOT(slotAlbumSelected(SAlbum*))); + + connect(d->timeLineFolderView, TQ_SIGNAL(signalRenameAlbum(SAlbum*)), + this, TQ_SLOT(slotRenameAlbum(SAlbum*))); + + connect(d->timeLineFolderView, TQ_SIGNAL(signalTextSearchFilterMatch(bool)), + d->searchDateBar, TQ_SLOT(slotSearchResult(bool))); + + connect(d->searchDateBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + d->timeLineFolderView, TQ_SLOT(slotTextSearchFilterChanged(const TQString&))); + + connect(d->timeUnitCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotTimeUnitChanged(int))); + + connect(d->scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(d->timeLineWidget, TQ_SIGNAL(signalDateMapChanged()), + this, TQ_SLOT(slotInit())); + + connect(d->timeLineWidget, TQ_SIGNAL(signalCursorPositionChanged()), + this, TQ_SLOT(slotCursorPositionChanged())); + + connect(d->timeLineWidget, TQ_SIGNAL(signalSelectionChanged()), + this, TQ_SLOT(slotSelectionChanged())); + + connect(d->timeLineWidget, TQ_SIGNAL(signalRefDateTimeChanged()), + this, TQ_SLOT(slotRefDateTimeChanged())); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotUpdateCurrentDateSearchAlbum())); + + connect(d->resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetSelection())); + + connect(d->saveButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotSaveSelection())); + + connect(d->scrollBar, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotScrollBarValueChanged(int))); + + connect(d->nameEdit, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SLOT(slotCheckAboutSelection())); + + connect(d->nameEdit, TQ_SIGNAL(returnPressed(const TQString&)), + d->saveButton, TQ_SLOT(animateClick())); +} + +TimeLineView::~TimeLineView() +{ + writeConfig(); + delete d->timer; + delete d; +} + +TimeLineFolderView* TimeLineView::folderView() const +{ + return d->timeLineFolderView; +} + +SearchTextBar* TimeLineView::searchBar() const +{ + return d->searchDateBar; +} + +void TimeLineView::slotInit() +{ + // Date Maps are loaded from AlbumManager to TimeLineWidget after than GUI is initialized. + // AlbumManager query Date TDEIO slave to stats items from database and it can take a while. + // We waiting than TimeLineWidget is ready before to set last config from users. + + readConfig(); + + disconnect(d->timeLineWidget, TQ_SIGNAL(signalDateMapChanged()), + this, TQ_SLOT(slotInit())); + + connect(d->timeLineWidget, TQ_SIGNAL(signalDateMapChanged()), + this, TQ_SLOT(slotCursorPositionChanged())); +} + +void TimeLineView::readConfig() +{ + TDEConfig* config = kapp->config(); + config->setGroup("TimeLine SideBar"); + + d->timeUnitCB->setCurrentItem(config->readNumEntry("Histogram TimeUnit", TimeLineWidget::Month)); + slotTimeUnitChanged(d->timeUnitCB->currentItem()); + + d->scaleBG->setButton(config->readNumEntry("Histogram Scale", TimeLineWidget::LinScale)); + slotScaleChanged(d->scaleBG->selectedId()); + + TQDateTime now = TQDateTime::currentDateTime(); + d->timeLineWidget->setCursorDateTime(config->readDateTimeEntry("Cursor Position", &now)); + d->timeLineWidget->setCurrentIndex(d->timeLineWidget->indexForCursorDateTime()); +} + +void TimeLineView::writeConfig() +{ + TDEConfig* config = kapp->config(); + config->setGroup("TimeLine SideBar"); + config->writeEntry("Histogram TimeUnit", d->timeUnitCB->currentItem()); + config->writeEntry("Histogram Scale", d->scaleBG->selectedId()); + config->writeEntry("Cursor Position", d->timeLineWidget->cursorDateTime()); + config->sync(); +} + +void TimeLineView::setActive(bool val) +{ + if (d->timeLineFolderView->selectedItem()) + { + d->timeLineFolderView->setActive(val); + } + else if (val) + { + int totalCount = 0; + DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); + if (list.isEmpty()) + { + AlbumManager::instance()->setCurrentAlbum(0); + } + else + { + AlbumList sList = AlbumManager::instance()->allSAlbums(); + for (AlbumList::iterator it = sList.begin(); it != sList.end(); ++it) + { + SAlbum* salbum = (SAlbum*)(*it); + if (salbum->title() == d->timeLineFolderView->currentTimeLineSearchName()) + AlbumManager::instance()->setCurrentAlbum(salbum); + } + } + } +} + +void TimeLineView::slotRefDateTimeChanged() +{ + d->scrollBar->blockSignals(true); + d->scrollBar->setMaxValue(d->timeLineWidget->totalIndex()-1); + d->scrollBar->setValue(d->timeLineWidget->indexForRefDateTime()-1); + d->scrollBar->blockSignals(false); +} + +void TimeLineView::slotTimeUnitChanged(int mode) +{ + d->timeLineWidget->setTimeUnit((TimeLineWidget::TimeUnit)mode); +} + +void TimeLineView::slotScrollBarValueChanged(int val) +{ + d->timeLineWidget->setCurrentIndex(val); +} + +void TimeLineView::slotScaleChanged(int mode) +{ + d->timeLineWidget->setScaleMode((TimeLineWidget::ScaleMode)mode); +} + +void TimeLineView::slotCursorPositionChanged() +{ + TQString txt; + int val = d->timeLineWidget->cursorInfo(txt); + d->cursorDateLabel->setText(txt); + d->cursorCountLabel->setText(TQString::number(val)); +} + +void TimeLineView::slotSelectionChanged() +{ + d->timer->start(100, true); +} + +/** Called from d->timer event.*/ +void TimeLineView::slotUpdateCurrentDateSearchAlbum() +{ + slotCheckAboutSelection(); + createNewDateSearchAlbum(d->timeLineFolderView->currentTimeLineSearchName()); +} + +void TimeLineView::slotSaveSelection() +{ + TQString name = d->nameEdit->text(); + if (!checkName(name)) + return; + createNewDateSearchAlbum(name); +} + +void TimeLineView::createNewDateSearchAlbum(const TQString& name) +{ + int totalCount = 0; + TQDateTime start, end; + DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); + + if (list.isEmpty()) + { + AlbumManager::instance()->setCurrentAlbum(0); + return; + } + + d->timeLineFolderView->blockSignals(true); + d->timeLineFolderView->clearSelection(); + d->timeLineFolderView->blockSignals(false); + + // We will make now the Url for digiKam Search TDEIO-Slave + + KURL url; + url.setProtocol("digikamsearch"); + + int grp = list.count(); + TQString path("1 AND 2"); + + if (grp > 1 ) + { + for (int i = 1 ; i < grp; i++) + { + path.append(" OR "); + path.append(TQString("%1 AND %2").arg(i*2+1).arg(i*2+2)); + } + } + url.setPath(path); + + int i = 0; + DateRangeList::iterator it; + for (it = list.begin() ; it != list.end(); ++it) + { + start = (*it).first; + end = (*it).second; + url.addQueryItem(TQString("%1.key").arg(i*2+1), TQString("imagedate")); + url.addQueryItem(TQString("%1.op").arg(i*2+1), TQString("GT")); + url.addQueryItem(TQString("%1.val").arg(i*2+1), start.date().toString(TQt::ISODate)); + url.addQueryItem(TQString("%1.key").arg(i*2+2), TQString("imagedate")); + url.addQueryItem(TQString("%1.op").arg(i*2+2), TQString("LT")); + url.addQueryItem(TQString("%1.val").arg(i*2+2), end.date().toString(TQt::ISODate)); + i++; + } + + url.addQueryItem("name", name); + url.addQueryItem("count", TQString::number(grp*2)); + url.addQueryItem("type", TQString("datesearch")); + + //DDebug() << url << endl; + + SAlbum* album = AlbumManager::instance()->createSAlbum(url, false); + AlbumManager::instance()->setCurrentAlbum(album); +} + +void TimeLineView::slotAlbumSelected(SAlbum* salbum) +{ + if (!salbum) + { + slotResetSelection(); + return; + } + + // Date Search url for TDEIO-Slave is something like that : + // digikamsearch:1 AND 2 OR 3 AND 4 OR 5 AND 6? + // 1.key=imagedate&1.op=GT&1.val=2006-02-06& + // 2.key=imagedate&2.op=LT&2.val=2006-02-07& + // 3.key=imagedate&3.op=GT&3.val=2006-02-10& + // 4.key=imagedate&4.op=LT&4.val=2006-02-11& + // 5.key=imagedate&5.op=GT&5.val=2006-02-12& + // 6.key=imagedate&6.op=LT&6.val=2006-02-13& + // name=TimeLineSelection& + // count=6 + // type=datesearch + + // Check if a special url query exist to identify a SAlbum dedicaced to Date Search + KURL url = salbum->kurl(); + TQMap queries = url.queryItems(); + if (queries.isEmpty()) return; + + TQString type = url.queryItem("type"); + if (type != TQString("datesearch")) return; + + bool ok = false; + int count = url.queryItem("count").toInt(&ok); + if (!ok || count <= 0) return; + + //DDebug() << url << endl; + + TQMap::iterator it2; + TQString key; + TQDateTime start, end; + DateRangeList list; + for (int i = 1 ; i <= count ; i+=2) + { + key = TQString("%1.val").arg(TQString::number(i)); + it2 = queries.find(key); + if (it2 != queries.end()) + start = TQDateTime(TQDate::fromString(it2.data(), TQt::ISODate)); + + //DDebug() << key << " :: " << it2.data() << endl; + + key = TQString("%1.val").arg(TQString::number(i+1)); + it2 = queries.find(key); + if (it2 != queries.end()) + end = TQDateTime(TQDate::fromString(it2.data(), TQt::ISODate)); + + //DDebug() << key << " :: " << it2.data() << endl; + + list.append(DateRange(start, end)); + } + + /* + DateRangeList::iterator it3; + for (it3 = list.begin() ; it3 != list.end(); ++it3) + DDebug() << (*it3).first.date().toString(TQt::ISODate) << " :: " + << (*it3).second.date().toString(TQt::ISODate) << endl; + */ + + d->timeLineWidget->setSelectedDateRange(list); + AlbumManager::instance()->setCurrentAlbum(salbum); +} + +void TimeLineView::slotResetSelection() +{ + d->timeLineWidget->slotResetSelection(); + slotCheckAboutSelection(); + AlbumManager::instance()->setCurrentAlbum(0); +} + +bool TimeLineView::checkName(TQString& name) +{ + bool checked = checkAlbum(name); + + while (!checked) + { + TQString label = i18n( "Search name already exists.\n" + "Please enter a new name:" ); + bool ok; +#if KDE_IS_VERSION(3,2,0) + TQString newTitle = KInputDialog::getText(i18n("Name exists"), label, name, &ok, this); +#else + TQString newTitle = KLineEditDlg::getText(i18n("Name exists"), label, name, ok, this); +#endif + if (!ok) return false; + + name = newTitle; + checked = checkAlbum(name); + } + + return true; +} + +bool TimeLineView::checkAlbum(const TQString& name) const +{ + AlbumList list = AlbumManager::instance()->allSAlbums(); + + for (AlbumList::Iterator it = list.begin() ; it != list.end() ; ++it) + { + SAlbum *album = (SAlbum*)(*it); + if ( album->title() == name ) + return false; + } + return true; +} + +void TimeLineView::slotCheckAboutSelection() +{ + int totalCount = 0; + DateRangeList list = d->timeLineWidget->selectedDateRange(totalCount); + if (!list.isEmpty()) + { + d->nameEdit->setEnabled(true); + + if (!d->nameEdit->text().isEmpty()) + d->saveButton->setEnabled(true); + } + else + { + d->nameEdit->setEnabled(false); + d->saveButton->setEnabled(false); + } +} + +void TimeLineView::slotRenameAlbum(SAlbum* salbum) +{ + if (!salbum) return; + + TQString oldName(salbum->title()); + bool ok; + +#if KDE_IS_VERSION(3,2,0) + TQString name = KInputDialog::getText(i18n("Rename Album (%1)").arg(oldName), + i18n("Enter new album name:"), + oldName, &ok, this); +#else + TQString name = KLineEditDlg::getText(i18n("Rename Album (%1)").arg(oldName), + i18n("Enter new album name:"), + oldName, &ok, this); +#endif + + if (!ok || name == oldName || name.isEmpty()) return; + + if (!checkName(name)) return; + + KURL url = salbum->kurl(); + url.removeQueryItem("name"); + url.addQueryItem("name", name); + AlbumManager::instance()->updateSAlbum(salbum, url); +} + +} // NameSpace Digikam diff --git a/src/digikam/timelineview.h b/src/digikam/timelineview.h new file mode 100644 index 00000000..fd673662 --- /dev/null +++ b/src/digikam/timelineview.h @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-08 + * Description : Time line sidebar tab contents. + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TIMELINEVIEW_H +#define TIMELINEVIEW_H + +// TQt includes. + +#include +#include +#include + +namespace Digikam +{ + +class SAlbum; +class SearchTextBar; +class TimeLineFolderView; +class TimeLineViewPriv; + +class TimeLineView : public TQWidget +{ + TQ_OBJECT + +public: + + TimeLineView(TQWidget *parent=0); + ~TimeLineView(); + + TimeLineFolderView *folderView() const; + SearchTextBar* searchBar() const; + + void setActive(bool val); + +private: + + void readConfig(); + void writeConfig(); + void createNewDateSearchAlbum(const TQString& name); + bool checkName(TQString& name); + bool checkAlbum(const TQString& name) const; + +private slots: + + void slotInit(); + void slotScrollBarValueChanged(int); + void slotRefDateTimeChanged(); + void slotScaleChanged(int); + void slotTimeUnitChanged(int); + void slotCursorPositionChanged(); + void slotSelectionChanged(); + void slotResetSelection(); + void slotSaveSelection(); + void slotUpdateCurrentDateSearchAlbum(); + void slotAlbumSelected(SAlbum*); + void slotCheckAboutSelection(); + void slotRenameAlbum(SAlbum*); + +private: + + TimeLineViewPriv* d; +}; + +} // NameSpace Digikam + +#endif /* TIMELINEVIEW_H */ diff --git a/src/digikam/timelinewidget.cpp b/src/digikam/timelinewidget.cpp new file mode 100644 index 00000000..b9033e41 --- /dev/null +++ b/src/digikam/timelinewidget.cpp @@ -0,0 +1,1718 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-08 + * Description : a widget to display date and time statistics of pictures + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "themeengine.h" +#include "timelinewidget.h" +#include "timelinewidget.moc" + +namespace Digikam +{ + +class TimeLineWidgetPriv +{ + +public : + + typedef TQPair YearRefPair; // Year + a reference association (Month or week or day) + typedef TQPair StatPair; // Statistic value + selection status. + +public: + + TimeLineWidgetPriv() + { + validMouseEvent = false; + selMouseEvent = false; + maxCountByDay = 1; + maxCountByWeek = 1; + maxCountByMonth = 1; + maxCountByYear = 1; + topMargin = 3; + bottomMargin = 20; + barWidth = 20; + startPos = 96; + nbItems = 10; + timeUnit = TimeLineWidget::Month; + scaleMode = TimeLineWidget::LinScale; + calendar = TDEGlobal::locale()->calendar(); + } + + bool validMouseEvent; // Current mouse enter event is valid to set cursor position or selection. + bool selMouseEvent; // Current mouse enter event is about to make a selection. + + int maxCountByDay; + int maxCountByWeek; + int maxCountByMonth; + int maxCountByYear; + int topMargin; + int bottomMargin; + int barWidth; + int nbItems; + int startPos; + + TQDateTime refDateTime; // Reference date-time used to draw histogram from middle of widget. + TQDateTime cursorDateTime; // Current date-time used to draw focus cursor. + TQDateTime minDateTime; // Higher date on histogram. + TQDateTime maxDateTime; // Lower date on histogram. + TQDateTime selStartDateTime; + TQDateTime selMinDateTime; // Lower date available on histogram. + TQDateTime selMaxDateTime; // Higher date available on histogram. + + TQPixmap pixmap; // Used for widget double buffering. + + TQMap dayStatMap; // Store Days count statistics. + TQMap weekStatMap; // Store Weeks count statistics. + TQMap monthStatMap; // Store Month count statistics. + TQMap yearStatMap; // Store Years count statistics. + + const KCalendarSystem *calendar; + + TimeLineWidget::TimeUnit timeUnit; + TimeLineWidget::ScaleMode scaleMode; +}; + +TimeLineWidget::TimeLineWidget(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new TimeLineWidgetPriv; + setBackgroundMode(TQt::NoBackground); + setMouseTracking(true); + setMinimumWidth(256); + setMinimumHeight(192); + + TQDateTime ref = TQDateTime::currentDateTime(); + setCursorDateTime(ref); + setRefDateTime(ref); + + connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), + this, TQ_SLOT(slotThemeChanged())); +} + +TimeLineWidget::~TimeLineWidget() +{ + delete d; +} + +void TimeLineWidget::setTimeUnit(TimeUnit timeUnit) +{ + d->timeUnit = timeUnit; + setCursorDateTime(cursorDateTime()); + setRefDateTime(cursorDateTime()); +} + +TimeLineWidget::TimeUnit TimeLineWidget::timeUnit() const +{ + return d->timeUnit; +} + +void TimeLineWidget::setScaleMode(ScaleMode scaleMode) +{ + d->scaleMode = scaleMode; + updatePixmap(); + update(); +} + +TimeLineWidget::ScaleMode TimeLineWidget::scaleMode() const +{ + return d->scaleMode; +} + +int TimeLineWidget::totalIndex() +{ + if (d->minDateTime.isNull() || d->maxDateTime.isNull()) + return 0; + + int i = 0; + TQDateTime dt = d->minDateTime; + + do + { + dt = nextDateTime(dt); + i++; + } + while(dt < d->maxDateTime); + + return i; +} + +int TimeLineWidget::indexForDateTime(const TQDateTime& date) +{ + if (d->minDateTime.isNull() || d->maxDateTime.isNull() || date.isNull()) + return 0; + + int i = 0; + TQDateTime dt = d->minDateTime; + + do + { + dt = nextDateTime(dt); + i++; + } + while(dt < date); + + return i; +} + +int TimeLineWidget::indexForRefDateTime() +{ + return (indexForDateTime(d->refDateTime)); +} + +int TimeLineWidget::indexForCursorDateTime() +{ + return (indexForDateTime(d->cursorDateTime)); +} + +void TimeLineWidget::setCurrentIndex(int index) +{ + if (d->minDateTime.isNull() || d->maxDateTime.isNull()) + return; + + int i = 0; + TQDateTime dt = d->minDateTime; + + do + { + dt = nextDateTime(dt); + i++; + } + while(i <= index); + + setRefDateTime(dt); +} + +void TimeLineWidget::setCursorDateTime(const TQDateTime& dateTime) +{ + TQDateTime dt = dateTime; + dt.setTime(TQTime(0, 0, 0, 0)); + + switch(d->timeUnit) + { + case Week: + { + // Go to the first day of week. + int weekYear = 0; + int weekNb = d->calendar->weekNumber(dt.date(), &weekYear); + dt = firstDayOfWeek(weekYear, weekNb); + break; + } + case Month: + { + // Go to the first day of month. + dt.setDate(TQDate(dt.date().year(), dt.date().month(), 1)); + break; + } + case Year: + { + // Go to the first day of year. + dt.setDate(TQDate(dt.date().year(), 1, 1)); + break; + } + default: + break; + } + + if (d->cursorDateTime == dt) + return; + + d->cursorDateTime = dt; + + emit signalCursorPositionChanged(); +} + +TQDateTime TimeLineWidget::cursorDateTime() const +{ + return d->cursorDateTime; +} + +int TimeLineWidget::cursorInfo(TQString& infoDate) +{ + SelectionMode selected; + TQDateTime dt = cursorDateTime(); + + switch(d->timeUnit) + { + case Day: + { + infoDate = TDEGlobal::locale()->formatDate(dt.date()); + break; + } + case Week: + { + infoDate = i18n("Week #%1 - %2 %3") + .arg(d->calendar->weekNumber(dt.date())) + .arg(d->calendar->monthName(dt.date())) + .arg(d->calendar->yearString(dt.date(), false)); + break; + } + case Month: + { + infoDate = TQString("%1 %2") + .arg(d->calendar->monthName(dt.date())) + .arg(d->calendar->yearString(dt.date(), false)); + break; + } + case Year: + { + infoDate = d->calendar->yearString(dt.date(), false); + break; + } + } + + return statForDateTime(dt, selected); +} + +void TimeLineWidget::setRefDateTime(const TQDateTime& dateTime) +{ + TQDateTime dt = dateTime; + dt.setTime(TQTime(0, 0, 0, 0)); + + switch(d->timeUnit) + { + case Week: + { + // Go to the first day of week. + int dayWeekOffset = (-1) * (d->calendar->dayOfWeek(dt.date()) - 1); + dt = dt.addDays(dayWeekOffset); + break; + } + case Month: + { + // Go to the first day of month. + dt.setDate(TQDate(dt.date().year(), dt.date().month(), 1)); + break; + } + case Year: + { + // Go to the first day of year. + dt.setDate(TQDate(dt.date().year(), 1, 1)); + break; + } + default: + break; + } + + d->refDateTime = dt; + updatePixmap(); + update(); + emit signalRefDateTimeChanged(); +} + +void TimeLineWidget::slotResetSelection() +{ + resetSelection(); + updatePixmap(); + update(); +} + +void TimeLineWidget::resetSelection() +{ + TQMap::iterator it; + + for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it) + it.data().second = Unselected; + + for (it = d->weekStatMap.begin() ; it != d->weekStatMap.end(); ++it) + it.data().second = Unselected; + + for (it = d->monthStatMap.begin() ; it != d->monthStatMap.end(); ++it) + it.data().second = Unselected; + + TQMap::iterator it2; + + for (it2 = d->yearStatMap.begin() ; it2 != d->yearStatMap.end(); ++it2) + it2.data().second = Unselected; +} + +void TimeLineWidget::setSelectedDateRange(const DateRangeList& list) +{ + if (list.isEmpty()) + return; + + resetSelection(); + + TQDateTime start, end, dt; + DateRangeList::const_iterator it; + + for (it = list.begin() ; it != list.end(); ++it) + { + start = (*it).first; + end = (*it).second; + if (end > start) + { + dt = start; + do + { + setDateTimeSelected(dt, Selected); + dt = dt.addDays(1); + } + while (dt < end); + } + } + + updatePixmap(); + update(); +} + +DateRangeList TimeLineWidget::selectedDateRange(int& totalCount) +{ + // We will parse all selections done on Days stats map. + + DateRangeList list; + totalCount = 0; + TQMap::iterator it3; + TQDateTime sdt, edt; + TQDate date; + + for (it3 = d->dayStatMap.begin() ; it3 != d->dayStatMap.end(); ++it3) + { + if (it3.data().second == Selected) + { + date = TQDate(it3.key().first, 1, 1); + date = date.addDays(it3.key().second-1); + sdt = TQDateTime(date); + edt = sdt.addDays(1); + list.append(DateRange(sdt, edt)); + totalCount += it3.data().first; + } + } + + DateRangeList::iterator it, it2; + + /* + for (it = list.begin() ; it != list.end(); ++it) + DDebug() << (*it).first.date().toString(TQt::ISODate) << " :: " + << (*it).second.date().toString(TQt::ISODate) << endl; + + DDebug() << "Total Count of Items = " << totalCount << endl; + */ + + // Group contiguous date ranges to optimize query on database. + + DateRangeList list2; + TQDateTime first, second, first2, second2; + + for (it = list.begin() ; it != list.end(); ++it) + { + first = (*it).first; + second = (*it).second; + it2 = it; + do + { + ++it2; + if (it2 != list.end()) + { + first2 = (*it2).first; + second2 = (*it2).second; + + if (first2 == second) + { + second = second2; + ++it; + } + else + break; + } + } + while(it2 != list.end()); + + list2.append(DateRange(first, second)); + } + + /* + for (it = list2.begin() ; it != list2.end(); ++it) + DDebug() << (*it).first.date().toString(TQt::ISODate) << " :: " + << (*it).second.date().toString(TQt::ISODate) << endl; + */ + + return list2; +} + +void TimeLineWidget::slotDatesMap(const TQMap& datesStatMap) +{ + // Clear all counts in all stats maps before to update it. Do not clear selections. + + TQMap::iterator it_iP; + for ( it_iP = d->yearStatMap.begin() ; it_iP != d->yearStatMap.end(); ++it_iP ) + it_iP.data().first = 0; + + TQMap::iterator it_YP; + for ( it_YP = d->monthStatMap.begin() ; it_YP != d->monthStatMap.end(); ++it_YP ) + it_YP.data().first = 0; + + for ( it_YP = d->weekStatMap.begin() ; it_YP != d->weekStatMap.end(); ++it_YP ) + it_YP.data().first = 0; + + for ( it_YP = d->dayStatMap.begin() ; it_YP != d->dayStatMap.end(); ++it_YP ) + it_YP.data().first = 0; + + // Parse all new Date stamp and store histogram stats relevant in maps. + + int count; + TQMap::const_iterator it; + if (datesStatMap.isEmpty()) + { + d->minDateTime = TQDateTime(); + d->maxDateTime = TQDateTime(); + } + else + { + d->minDateTime = datesStatMap.begin().key(); + d->maxDateTime = datesStatMap.begin().key(); + } + + for ( it = datesStatMap.begin(); it != datesStatMap.end(); ++it ) + { + if (it.key() > d->maxDateTime) + d->maxDateTime = it.key(); + + if (it.key() < d->minDateTime) + d->minDateTime = it.key(); + + int year = it.key().date().year(); + int month = it.key().date().month(); + int day = d->calendar->dayOfYear(it.key().date()); + int yearForWeek = year; // Used with week shared between 2 years decade (Dec/Jan). + int week = d->calendar->weekNumber(it.key().date(), &yearForWeek); + + // Stats Years values. + + it_iP = d->yearStatMap.find(year); + if ( it_iP == d->yearStatMap.end() ) + { + count = it.data(); + d->yearStatMap.insert( year, TimeLineWidgetPriv::StatPair(count, Unselected) ); + } + else + { + count = it_iP.data().first + it.data(); + d->yearStatMap.replace( year, TimeLineWidgetPriv::StatPair(count, it_iP.data().second) ); + } + + if (d->maxCountByYear < count) + d->maxCountByYear = count; + + // Stats Months values. + + it_YP = d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month)); + if ( it_YP == d->monthStatMap.end() ) + { + count = it.data(); + d->monthStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, month), + TimeLineWidgetPriv::StatPair(count, Unselected) ); + } + else + { + count = it_YP.data().first + it.data(); + d->monthStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, month), + TimeLineWidgetPriv::StatPair(count, it_YP.data().second) ); + } + + if (d->maxCountByMonth < count) + d->maxCountByMonth = count; + + // Stats Weeks values. + + it_YP = d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week)); + if ( it_YP == d->weekStatMap.end() ) + { + count = it.data(); + d->weekStatMap.insert( TimeLineWidgetPriv::YearRefPair(yearForWeek, week), + TimeLineWidgetPriv::StatPair(count, Unselected) ); + } + else + { + count = it_YP.data().first + it.data(); + d->weekStatMap.replace( TimeLineWidgetPriv::YearRefPair(yearForWeek, week), + TimeLineWidgetPriv::StatPair(count, it_YP.data().second) ); + } + + if (d->maxCountByWeek < count) + d->maxCountByWeek = count; + + // Stats Days values. + + it_YP = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day)); + if ( it_YP == d->dayStatMap.end() ) + { + count = it.data(); + d->dayStatMap.insert( TimeLineWidgetPriv::YearRefPair(year, day), + TimeLineWidgetPriv::StatPair(count, Unselected) ); + } + else + { + count = it_YP.data().first + it.data(); + d->dayStatMap.replace( TimeLineWidgetPriv::YearRefPair(year, day), + TimeLineWidgetPriv::StatPair(count, it_YP.data().second) ); + } + + if (d->maxCountByDay < count) + d->maxCountByDay = count; + } + + if (!datesStatMap.isEmpty()) + { + d->maxDateTime.setTime(TQTime(0, 0, 0, 0)); + d->minDateTime.setTime(TQTime(0, 0, 0, 0)); + } + else + { + d->maxDateTime = d->refDateTime; + d->minDateTime = d->refDateTime; + } + + updatePixmap(); + update(); + emit signalDateMapChanged(); +} + +void TimeLineWidget::updatePixmap() +{ + // Drawing background and image. + d->pixmap = TQPixmap(size()); + d->pixmap.fill(palette().active().background()); + + TQPainter p(&d->pixmap); + + d->bottomMargin = (int)(p.fontMetrics().height()*1.5); + d->barWidth = p.fontMetrics().width("00"); + d->nbItems = (int)((width() / 2.0) / (float)d->barWidth); + d->startPos = (int)((width() / 2.0) - ((float)(d->barWidth) / 2.0)); + int dim = height() - d->bottomMargin - d->topMargin; + TQDateTime ref = d->refDateTime; + ref.setTime(TQTime(0, 0, 0, 0)); + double max, logVal; + int val, top; + SelectionMode sel; + TQRect focusRect, selRect, barRect; + TQBrush selBrush; + TQColor dateColor, subDateColor; + + // Date histogram drawing is divided in 2 parts. The current date-time + // is placed on the center of the view and all dates on right are computed, + // and in second time, all dates on the left. + + // Draw all dates on the right of ref. date-time. + + for (int i = 0 ; i < d->nbItems ; i++) + { + val = statForDateTime(ref, sel); + max = (double)maxCount(); + + if (d->scaleMode == TimeLineWidget::LogScale) + { + if (max > 0.0) max = log(max); + else max = 1.0; + + if (val <= 0) logVal = 0; + else logVal = log(val); + + top = lround(dim + d->topMargin - ((logVal * dim) / max)); + + if (top < 0) val = 0; + } + else + { + top = lround(dim + d->topMargin - ((val * dim) / max)); + } + + barRect.setTop(top); + barRect.setLeft(d->startPos + i*d->barWidth); + barRect.setBottom(height() - d->bottomMargin); + barRect.setRight(d->startPos + (i+1)*d->barWidth); + + if (ref == d->cursorDateTime) + focusRect = barRect; + + if (ref > d->maxDateTime) + dateColor = palette().active().mid(); + else + dateColor = palette().active().foreground(); + + p.setPen(palette().active().foreground()); + p.fillRect(barRect, TQBrush(ThemeEngine::instance()->textSpecialRegColor())); + p.drawRect(barRect); + p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3); + p.drawLine(barRect.left(), barRect.bottom(), barRect.left(), barRect.bottom()+3); + + if (val) + { + if (sel) + subDateColor = palette().active().highlightedText(); + else + subDateColor = palette().active().foreground(); + } + else + subDateColor = palette().active().mid(); + + if (sel == Selected || sel == FuzzySelection) + { + selBrush.setColor(ThemeEngine::instance()->thumbSelColor()); + selBrush.setStyle(TQBrush::SolidPattern); + if (sel == FuzzySelection) + selBrush.setStyle(TQBrush::Dense4Pattern); + + selRect.setTop(height() - d->bottomMargin + 1); + selRect.setLeft(d->startPos + i*d->barWidth); + selRect.setBottom(height() - d->bottomMargin/2); + selRect.setRight(d->startPos + (i+1)*d->barWidth); + p.fillRect(selRect, selBrush); + } + + switch(d->timeUnit) + { + case Day: + { + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor); + TQString txt = TQString(d->calendar->weekDayName(ref.date(), true)[0]); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + if (d->calendar->dayOfWeek(ref.date()) == 1) + { + p.setPen(dateColor); + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TDEGlobal::locale()->formatDate(ref.date(), true); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + break; + } + case Week: + { + int week = d->calendar->weekNumber(ref.date()); + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor); + TQString txt = TQString::number(week); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + p.setPen(dateColor); + if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TDEGlobal::locale()->formatDate(ref.date(), true); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + if (week != 50) + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/4); + } + break; + } + case Month: + { + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor) ; + TQString txt = TQString(d->calendar->monthName(ref.date(), true)[0]); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + p.setPen(dateColor); + if (ref.date().month() == 1) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TQString::number(ref.date().year()); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (ref.date().month() == 7) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/4); + } + break; + } + case Year: + { + p.setPen(dateColor); + if (ref.date().year() % 10 == 0) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TQString::number(ref.date().year()); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (ref.date().year() % 5 == 0) + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/4); + break; + } + } + + ref = nextDateTime(ref); + } + + // Draw all dates on the left of ref. date-time. + + ref = d->refDateTime; + ref.setTime(TQTime(0, 0, 0, 0)); + ref = prevDateTime(ref); + + for (int i = 0 ; i < d->nbItems-1 ; i++) + { + val = statForDateTime(ref, sel); + max = (double)maxCount(); + + if (d->scaleMode == TimeLineWidget::LogScale) + { + if (max > 0.0) max = log(max); + else max = 1.0; + + if (val <= 0) logVal = 0; + else logVal = log(val); + + top = lround(dim + d->topMargin - ((logVal * dim) / max)); + + if (top < 0) val = 0; + } + else + { + top = lround(dim + d->topMargin - ((val * dim) / max)); + } + + barRect.setTop(top); + barRect.setRight(d->startPos - i*d->barWidth); + barRect.setBottom(height() - d->bottomMargin); + barRect.setLeft(d->startPos - (i+1)*d->barWidth); + + if (ref == d->cursorDateTime) + focusRect = barRect; + + if (ref < d->minDateTime) + dateColor = palette().active().mid(); + else + dateColor = palette().active().foreground(); + + p.setPen(palette().active().foreground()); + p.fillRect(barRect, TQBrush(ThemeEngine::instance()->textSpecialRegColor())); + p.drawRect(barRect); + p.drawLine(barRect.right(), barRect.bottom(), barRect.right(), barRect.bottom()+3); + p.drawLine(barRect.left(), barRect.bottom(), barRect.left(), barRect.bottom()+3); + + if (val) + { + if (sel) + subDateColor = palette().active().highlightedText(); + else + subDateColor = palette().active().foreground(); + } + else + subDateColor = palette().active().mid(); + + if (sel == Selected || sel == FuzzySelection) + { + selBrush.setColor(ThemeEngine::instance()->thumbSelColor()); + selBrush.setStyle(TQBrush::SolidPattern); + if (sel == FuzzySelection) + selBrush.setStyle(TQBrush::Dense4Pattern); + + selRect.setTop(height() - d->bottomMargin + 1); + selRect.setLeft(d->startPos - (i+1)*d->barWidth); + selRect.setBottom(height() - d->bottomMargin/2); + selRect.setRight(d->startPos - i*d->barWidth); + p.fillRect(selRect, selBrush); + } + + switch(d->timeUnit) + { + case Day: + { + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor) ; + TQString txt = TQString(d->calendar->weekDayName(ref.date(), true)[0]); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + if (d->calendar->dayOfWeek(ref.date()) == 1) + { + p.setPen(dateColor); + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TDEGlobal::locale()->formatDate(ref.date(), true); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + break; + } + case Week: + { + int week = d->calendar->weekNumber(ref.date()); + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor) ; + TQString txt = TQString::number(week); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + p.setPen(dateColor); + if (week == 1 || week == 10 || week == 20 || week == 30 || week == 40 || week == 50) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TDEGlobal::locale()->formatDate(ref.date(), true); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + if (week != 50) + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (week == 6 || week == 16 || week == 26 || week == 36 || week == 46) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/4); + } + break; + } + case Month: + { + { + p.save(); + TQFont fnt = p.font(); + fnt.setPointSize(fnt.pointSize()-4); + p.setFont(fnt); + p.setPen(subDateColor) ; + TQString txt = TQString(d->calendar->monthName(ref.date(), true)[0]); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left() + ((barRect.width()-br.width())/2), + barRect.bottom()+br.height(), txt); + p.restore(); + } + + p.setPen(dateColor); + if (ref.date().month() == 1) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TQString::number(ref.date().year()); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (ref.date().month() == 7) + { + p.drawLine(barRect.right(), barRect.bottom(), + barRect.right(), barRect.bottom()+d->bottomMargin/4); + } + break; + } + case Year: + { + p.setPen(dateColor); + if (ref.date().year() % 10 == 0) + { + p.drawLine(barRect.left(), barRect.bottom(), + barRect.left(), barRect.bottom()+d->bottomMargin/2); + TQString txt = TQString::number(ref.date().year()); + TQRect br = p.fontMetrics().boundingRect(0, 0, width(), height(), 0, txt); + p.drawText(barRect.left()-br.width()/2, barRect.bottom() + d->bottomMargin, txt); + } + else if (ref.date().year() % 5 == 0) + p.drawLine(barRect.right(), barRect.bottom(), + barRect.right(), barRect.bottom()+d->bottomMargin/4); + break; + } + } + + ref = prevDateTime(ref); + } + + // Draw cursor rectangle over current date-time. + if (focusRect.isValid()) + { + focusRect.setTop(d->topMargin); + TQPoint p1(focusRect.left(), height() - d->bottomMargin); + TQPoint p2(focusRect.right(), height() - d->bottomMargin); + focusRect.setBottom(focusRect.bottom() + d->bottomMargin/2); + + p.setPen(palette().active().shadow()); + p.drawLine(p1.x(), p1.y()+1, p2.x(), p2.y()+1); + p.drawRect(focusRect); + + focusRect.addCoords(-1,-1, 1, 1); + p.setPen(ThemeEngine::instance()->textSelColor()); + p.drawRect(focusRect); + p.drawLine(p1.x()-1, p1.y(), p2.x()+1, p2.y()); + + focusRect.addCoords(-1,-1, 1, 1); + p.drawRect(focusRect); + p.drawLine(p1.x()-1, p1.y()-1, p2.x()+1, p2.y()-1); + + focusRect.addCoords(-1,-1, 1, 1); + p.setPen(palette().active().shadow()); + p.drawRect(focusRect); + p.drawLine(p1.x(), p1.y()-2, p2.x(), p2.y()-2); + } + p.end(); +} + +TQDateTime TimeLineWidget::prevDateTime(const TQDateTime& dt) +{ + TQDateTime prev; + switch(d->timeUnit) + { + case Day: + { + prev = dt.addDays(-1); + break; + } + case Week: + { + prev = dt.addDays(-7); + break; + } + case Month: + { + prev = dt.addMonths(-1); + break; + } + case Year: + { + prev = dt.addYears(-1); + break; + } + } + return prev; +} + +TQDateTime TimeLineWidget::nextDateTime(const TQDateTime& dt) +{ + TQDateTime next; + switch(d->timeUnit) + { + case Day: + { + next = dt.addDays(1); + break; + } + case Week: + { + next = dt.addDays(7); + break; + } + case Month: + { + next = dt.addMonths(1); + break; + } + case Year: + { + next = dt.addYears(1); + break; + } + } + return next; +} + +int TimeLineWidget::maxCount() +{ + int max = 1; + switch(d->timeUnit) + { + case Day: + { + max = d->maxCountByDay; + break; + } + case Week: + { + max = d->maxCountByWeek; + break; + } + case Month: + { + max = d->maxCountByMonth; + break; + } + case Year: + { + max = d->maxCountByYear; + break; + } + } + return max; +} + +int TimeLineWidget::statForDateTime(const TQDateTime& dt, SelectionMode& selected) +{ + int count = 0; + int year = dt.date().year(); + int month = dt.date().month(); + int day = d->calendar->dayOfYear(dt.date()); + int yearForWeek; // Used with week shared between 2 years decade (Dec/Jan). + int week = d->calendar->weekNumber(dt.date(), &yearForWeek); + selected = Unselected; + + switch(d->timeUnit) + { + case Day: + { + TQMap::iterator it = + d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day)); + if ( it != d->dayStatMap.end() ) + { + count = it.data().first; + selected = it.data().second; + } + break; + } + case Week: + { + TQMap::iterator it = + d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week)); + if ( it != d->weekStatMap.end() ) + { + count = it.data().first; + selected = it.data().second; + } + break; + } + case Month: + { + TQMap::iterator it = + d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month)); + if ( it != d->monthStatMap.end() ) + { + count = it.data().first; + selected = it.data().second; + } + break; + } + case Year: + { + TQMap::iterator it = d->yearStatMap.find(year); + if ( it != d->yearStatMap.end() ) + { + count = it.data().first; + selected = it.data().second; + } + break; + } + } + + return count; +} + +void TimeLineWidget::setDateTimeSelected(const TQDateTime& dt, SelectionMode selected) +{ + int year = dt.date().year(); + int month = dt.date().month(); + int yearForWeek; // Used with week shared between 2 years decade (Dec/Jan). + int week = d->calendar->weekNumber(dt.date(), &yearForWeek); + + TQDateTime dts, dte; + + switch(d->timeUnit) + { + case Day: + { + dts = dt; + dte = dts.addDays(1); + setDaysRangeSelection(dts, dte, selected); + break; + } + case Week: + { + dts = firstDayOfWeek(yearForWeek, week); + dte = dts.addDays(7); + setDaysRangeSelection(dts, dte, selected); + updateWeekSelection(dts, dte); + break; + } + case Month: + { + dts = TQDateTime(TQDate(year, month, 1)); + dte = dts.addDays(d->calendar->daysInMonth(dts.date())); + setDaysRangeSelection(dts, dte, selected); + updateMonthSelection(dts, dte); + break; + } + case Year: + { + dts = TQDateTime(TQDate(year, 1, 1)); + dte = dts.addDays(d->calendar->daysInYear(dts.date())); + setDaysRangeSelection(dts, dte, selected); + updateYearSelection(dts, dte); + break; + } + } +} + +void TimeLineWidget::updateWeekSelection(const TQDateTime dts, const TQDateTime dte) +{ + TQMap::iterator it; + TQDateTime dtsWeek, dteWeek, dt; + int week; + int yearForWeek; // Used with week shared between 2 years decade (Dec/Jan). + dt = dts; + do + { + yearForWeek = dt.date().year(); + week = d->calendar->weekNumber(dt.date(), &yearForWeek); + + dtsWeek = firstDayOfWeek(yearForWeek, week); + dteWeek = dtsWeek.addDays(7); + it = d->weekStatMap.find(TimeLineWidgetPriv::YearRefPair(yearForWeek, week)); + if ( it != d->weekStatMap.end() ) + it.data().second = checkSelectionForDaysRange(dtsWeek, dteWeek); + + dt = dt.addDays(7); + } + while (dt <= dte); +} + +void TimeLineWidget::updateMonthSelection(const TQDateTime dts, const TQDateTime dte) +{ + TQMap::iterator it; + TQDateTime dtsMonth, dteMonth, dt; + int year, month; + dt = dts; + do + { + year = dt.date().year(); + month = dt.date().month(); + + dtsMonth = TQDateTime(TQDate(year, month, 1)); + dteMonth = dtsMonth.addDays(d->calendar->daysInMonth(dtsMonth.date())); + it = d->monthStatMap.find(TimeLineWidgetPriv::YearRefPair(year, month)); + if ( it != d->monthStatMap.end() ) + it.data().second = checkSelectionForDaysRange(dtsMonth, dteMonth); + + dt = dteMonth; + } + while (dt <= dte); +} + +void TimeLineWidget::updateYearSelection(const TQDateTime dts, const TQDateTime dte) +{ + TQMap::iterator it; + TQDateTime dtsYear, dteYear, dt; + int year; + dt = dts; + do + { + year = dt.date().year(); + + dtsYear = TQDateTime(TQDate(year, 1, 1)); + dteYear = dtsYear.addDays(d->calendar->daysInYear(dtsYear.date())); + it = d->yearStatMap.find(year); + if ( it != d->yearStatMap.end() ) + it.data().second = checkSelectionForDaysRange(dtsYear, dteYear); + + dt = dteYear; + } + while (dt <= dte); +} + +void TimeLineWidget::updateAllSelection() +{ + TQMap::iterator it; + TQDateTime dts, dte; + TQDate date; + + for (it = d->dayStatMap.begin() ; it != d->dayStatMap.end(); ++it) + { + if (it.data().second == Selected) + { + date = TQDate(it.key().first, 1, 1); + date = date.addDays(it.key().second-1); + dts = TQDateTime(date); + dte = dts.addDays(1); + updateWeekSelection(dts, dte); + updateMonthSelection(dts, dte); + updateYearSelection(dts, dte); + } + } +} + +void TimeLineWidget::setDaysRangeSelection(const TQDateTime dts, const TQDateTime dte, SelectionMode selected) +{ + int year, day; + TQDateTime dt = dts; + TQMap::iterator it; + + do + { + year = dt.date().year(); + day = d->calendar->dayOfYear(dt.date()); + it = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day)); + if ( it != d->dayStatMap.end() ) + it.data().second = selected; + + dt = dt.addDays(1); + } + while(dt < dte); +} + +TimeLineWidget::SelectionMode TimeLineWidget::checkSelectionForDaysRange(const TQDateTime dts, const TQDateTime dte) +{ + int year, day; + int items = 0; + int itemsFuz = 0; + int itemsSel = 0; + TQDateTime dt = dts; + TQMap::iterator it; + + do + { + year = dt.date().year(); + day = d->calendar->dayOfYear(dt.date()); + + it = d->dayStatMap.find(TimeLineWidgetPriv::YearRefPair(year, day)); + if ( it != d->dayStatMap.end() ) + { + items++; + + if (it.data().second != Unselected) + { + if (it.data().second == FuzzySelection) + itemsFuz++; + else + itemsSel++; + } + } + dt = dt.addDays(1); + } + while (dt < dte); + + if (items == 0) + return Unselected; + + if (itemsFuz == 0 && itemsSel == 0) + return Unselected; + + if (itemsFuz > 0) + return FuzzySelection; + + if (items > itemsSel) + return FuzzySelection; + + return Selected; +} + +void TimeLineWidget::paintEvent(TQPaintEvent*) +{ + bitBlt(this, 0, 0, &d->pixmap); +} + +void TimeLineWidget::resizeEvent(TQResizeEvent*) +{ + updatePixmap(); +} + +void TimeLineWidget::slotBackward() +{ + TQDateTime ref = d->refDateTime; + + switch(d->timeUnit) + { + case Day: + { + for (int i = 0; i < 7; i++) + ref = prevDateTime(ref); + break; + } + case Week: + { + for (int i = 0; i < 4; i++) + ref = prevDateTime(ref); + break; + } + case Month: + { + for (int i = 0; i < 12; i++) + ref = prevDateTime(ref); + break; + } + case Year: + { + for (int i = 0; i < 5; i++) + ref = prevDateTime(ref); + break; + } + } + + if (ref < d->minDateTime) + ref = d->minDateTime; + + setRefDateTime(ref); +} + +void TimeLineWidget::slotPrevious() +{ + if (d->refDateTime <= d->minDateTime) + return; + TQDateTime ref = prevDateTime(d->refDateTime); + setRefDateTime(ref); +} + +void TimeLineWidget::slotNext() +{ + if (d->refDateTime >= d->maxDateTime) + return; + TQDateTime ref = nextDateTime(d->refDateTime); + setRefDateTime(ref); +} + +void TimeLineWidget::slotForward() +{ + TQDateTime ref = d->refDateTime; + + switch(d->timeUnit) + { + case Day: + { + for (int i = 0; i < 7; i++) + ref = nextDateTime(ref); + break; + } + case Week: + { + for (int i = 0; i < 4; i++) + ref = nextDateTime(ref); + break; + } + case Month: + { + for (int i = 0; i < 12; i++) + ref = nextDateTime(ref); + break; + } + case Year: + { + for (int i = 0; i < 5; i++) + ref = nextDateTime(ref); + break; + } + } + + if (ref > d->maxDateTime) + ref = d->maxDateTime; + + setRefDateTime(ref); +} + +void TimeLineWidget::wheelEvent(TQWheelEvent* e) +{ + if (e->delta() < 0) + { + if (e->state() & TQt::ShiftButton) + slotForward(); + else + slotNext(); + } + + if (e->delta() > 0) + { + if (e->state() & TQt::ShiftButton) + slotBackward(); + else + slotPrevious(); + } +} + +void TimeLineWidget::mousePressEvent(TQMouseEvent *e) +{ + if (e->button() == TQt::LeftButton) + { + TQPoint pt(e->x(), e->y()); + + bool ctrlPressed = e->state() & TQt::ControlButton; + TQDateTime ref = dateTimeForPoint(pt, d->selMouseEvent); + d->selStartDateTime = TQDateTime(); + if (d->selMouseEvent) + { + if (!ctrlPressed) + resetSelection(); + + d->selStartDateTime = ref; + d->selMinDateTime = ref; + d->selMaxDateTime = ref; + setDateTimeSelected(ref, Selected); + } + + if (!ref.isNull()) + setCursorDateTime(ref); + + d->validMouseEvent = true; + updatePixmap(); + update(); + } +} + +void TimeLineWidget::mouseMoveEvent(TQMouseEvent *e) +{ + if (d->validMouseEvent == true) + { + TQPoint pt(e->x(), e->y()); + + bool sel; + TQDateTime selEndDateTime = dateTimeForPoint(pt, sel); + setCursorDateTime(selEndDateTime); + + // Clamp start and end date-time of current contiguous selection. + + if (!selEndDateTime.isNull() && !d->selStartDateTime.isNull()) + { + if (selEndDateTime > d->selStartDateTime && + selEndDateTime > d->selMaxDateTime) + { + d->selMaxDateTime = selEndDateTime; + } + else if (selEndDateTime < d->selStartDateTime && + selEndDateTime < d->selMinDateTime) + { + d->selMinDateTime = selEndDateTime; + } + + TQDateTime dt = d->selMinDateTime; + do + { + setDateTimeSelected(dt, Unselected); + dt = nextDateTime(dt); + } + while(dt <= d->selMaxDateTime); + } + + // Now perform selections on Date Maps. + + if (d->selMouseEvent) + { + if (!d->selStartDateTime.isNull() && !selEndDateTime.isNull()) + { + TQDateTime dt = d->selStartDateTime; + if (selEndDateTime > d->selStartDateTime) + { + do + { + setDateTimeSelected(dt, Selected); + dt = nextDateTime(dt); + } + while(dt <= selEndDateTime); + } + else + { + do + { + setDateTimeSelected(dt, Selected); + dt = prevDateTime(dt); + } + while(dt >= selEndDateTime); + } + } + } + + updatePixmap(); + update(); + } +} + +void TimeLineWidget::mouseReleaseEvent(TQMouseEvent*) +{ + d->validMouseEvent = false; + + // Only dispatch changes about selection when user release mouse selection + // to prevent multiple queries on database. + if (d->selMouseEvent) + { + updateAllSelection(); + emit signalSelectionChanged(); + } + + d->selMouseEvent = false; +} + +TQDateTime TimeLineWidget::dateTimeForPoint(const TQPoint& pt, bool &isOnSelectionArea) +{ + TQRect barRect, selRect; + isOnSelectionArea = false; + + // Check on the right of reference date. + + TQDateTime ref = d->refDateTime; + ref.setTime(TQTime(0, 0, 0, 0)); + + TQRect deskRect = TDEGlobalSettings::desktopGeometry(this); + int items = deskRect.width() / d->barWidth; + + for (int i = 0 ; i < items ; i++) + { + barRect.setTop(0); + barRect.setLeft(d->startPos + i*d->barWidth); + barRect.setBottom(height() - d->bottomMargin + 1); + barRect.setRight(d->startPos + (i+1)*d->barWidth); + + selRect.setTop(height() - d->bottomMargin + 1); + selRect.setLeft(d->startPos + i*d->barWidth); + selRect.setBottom(height()); + selRect.setRight(d->startPos + (i+1)*d->barWidth); + + if (selRect.contains(pt)) + isOnSelectionArea = true; + + if (barRect.contains(pt) || selRect.contains(pt)) + { + if (i >= d->nbItems) + { + // Point is outside visible widget area. We scrolling widget contents. + slotNext(); + } + + return ref; + } + + ref = nextDateTime(ref); + } + + // Check on the left of reference date. + + ref = d->refDateTime; + ref.setTime(TQTime(0, 0, 0, 0)); + ref = prevDateTime(ref); + + for (int i = 0 ; i < items ; i++) + { + barRect.setTop(0); + barRect.setRight(d->startPos - i*d->barWidth); + barRect.setBottom(height() - d->bottomMargin + 1); + barRect.setLeft(d->startPos - (i+1)*d->barWidth); + + selRect.setTop(height() - d->bottomMargin + 1); + selRect.setLeft(d->startPos - (i+1)*d->barWidth); + selRect.setBottom(height()); + selRect.setRight(d->startPos - i*d->barWidth); + + if (selRect.contains(pt)) + isOnSelectionArea = true; + + if (barRect.contains(pt) || selRect.contains(pt)) + { + if (i >= d->nbItems-1) + { + // Point is outside visible widget area. We scrolling widget contents. + slotPrevious(); + } + + return ref; + } + + ref = prevDateTime(ref); + } + + return TQDateTime(); +} + +TQDateTime TimeLineWidget::firstDayOfWeek(int year, int weekNumber) +{ + // Search the first day of first week of year. + // We start to scan from 1st december of year-1 because + // first week of year OR last week of year-1 can be shared + // between year-1 and year. + TQDateTime d1(TQDate(year-1, 12, 1)); + TQDateTime dt = d1; + int weekYear = 0; + int weekNum = 0; + + do + { + dt = dt.addDays(1); + weekNum = d->calendar->weekNumber(dt.date(), &weekYear); + } + while(weekNum != 1 && weekYear != year); + + dt = dt.addDays((weekNumber-1)*7); + +/* + DDebug() << "Year= " << year << " Week= " << weekNumber + << " 1st day= " << dt << endl; +*/ + + return dt; +} + +void TimeLineWidget::slotThemeChanged() +{ + updatePixmap(); + update(); +} + +} // NameSpace Digikam diff --git a/src/digikam/timelinewidget.h b/src/digikam/timelinewidget.h new file mode 100644 index 00000000..33aeb7ea --- /dev/null +++ b/src/digikam/timelinewidget.h @@ -0,0 +1,154 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-08 + * Description : a widget to display date and time statistics of pictures + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TIMELINEWIDGET_H +#define TIMELINEWIDGET_H + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" + +namespace Digikam +{ + +typedef TQPair DateRange; // Range of a contigue dates selection . +typedef TQValueList DateRangeList; // List of dates range selected. + +class TimeLineWidgetPriv; + +class TimeLineWidget : public TQWidget +{ +TQ_OBJECT + +public: + + enum TimeUnit + { + Day = 0, + Week, + Month, + Year + }; + + enum SelectionMode + { + Unselected=0, // No selection. + FuzzySelection, // Partially selected. + Selected // Fully selected. + }; + + enum ScaleMode + { + LinScale=0, // Linear scale. + LogScale // Logarithmic scale. + }; + +public: + + TimeLineWidget(TQWidget *parent=0); + ~TimeLineWidget(); + + void setTimeUnit(TimeUnit timeUnit); + TimeUnit timeUnit() const; + + void setScaleMode(ScaleMode scaleMode); + ScaleMode scaleMode() const; + + void setCursorDateTime(const TQDateTime& dateTime); + TQDateTime cursorDateTime() const; + int cursorInfo(TQString& infoDate); + + /** Return a list of Date-Range based on selection performed on days-map */ + DateRangeList selectedDateRange(int& totalCount); + void setSelectedDateRange(const DateRangeList& list); + + int totalIndex(); + int indexForRefDateTime(); + int indexForCursorDateTime(); + void setCurrentIndex(int index); + +signals: + + void signalCursorPositionChanged(); + void signalSelectionChanged(); + void signalRefDateTimeChanged(); + void signalDateMapChanged(); + +public slots: + + void slotDatesMap(const TQMap&); + void slotPrevious(); + void slotNext(); + void slotBackward(); + void slotForward(); + void slotResetSelection(); + +private slots: + + void slotThemeChanged(); + +private: + + TQDateTime prevDateTime(const TQDateTime& dt); + TQDateTime nextDateTime(const TQDateTime& dt); + + int maxCount(); + int indexForDateTime(const TQDateTime& date); + int statForDateTime(const TQDateTime& dt, SelectionMode& selected); + void setRefDateTime(const TQDateTime& dateTime); + + void updatePixmap(); + void paintEvent(TQPaintEvent*); + void resizeEvent(TQResizeEvent*); + void wheelEvent(TQWheelEvent*); + + void mousePressEvent(TQMouseEvent*); + void mouseMoveEvent(TQMouseEvent*); + void mouseReleaseEvent(TQMouseEvent*); + + TQDateTime dateTimeForPoint(const TQPoint& pt, bool &isOnSelectionArea); + TQDateTime firstDayOfWeek(int year, int weekNumber); + + void resetSelection(); + void setDateTimeSelected(const TQDateTime& dt, SelectionMode selected); + void setDaysRangeSelection(const TQDateTime dts, const TQDateTime dte, SelectionMode selected); + SelectionMode checkSelectionForDaysRange(const TQDateTime dts, const TQDateTime dte); + void updateWeekSelection(const TQDateTime dts, const TQDateTime dte); + void updateMonthSelection(const TQDateTime dts, const TQDateTime dte); + void updateYearSelection(const TQDateTime dts, const TQDateTime dte); + void updateAllSelection(); + +private: + + TimeLineWidgetPriv* d; +}; + +} // NameSpace Digikam + +#endif // TIMELINEWIDGET_H diff --git a/src/digikam/upgradedb_sqlite2tosqlite3.cpp b/src/digikam/upgradedb_sqlite2tosqlite3.cpp new file mode 100644 index 00000000..b9156349 --- /dev/null +++ b/src/digikam/upgradedb_sqlite2tosqlite3.cpp @@ -0,0 +1,609 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-05 + * Description : SQlite 2 to SQlite 3 interface. + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albumdb.h" +#include "albumdb_sqlite2.h" +#include "upgradedb_sqlite2tosqlite3.h" + +namespace Digikam +{ + +struct _Album +{ + int id; + TQString url; + TQString date; + TQString caption; + TQString collection; + TQString icon; +}; + +struct _Tag +{ + int id; + int pid; + TQString name; + TQString icon; +}; + +static TQString escapeString(TQString str) +{ + str.replace( "'", "''" ); + return str; +} + +TQ_LLONG findOrAddImage(AlbumDB* db, int dirid, const TQString& name, + const TQString& caption) +{ + TQStringList values; + + db->execSql(TQString("SELECT id FROM Images WHERE dirid=%1 AND name='%2'") + .arg(dirid) + .arg(escapeString(name)), &values); + + if (!values.isEmpty()) + { + return values.first().toLongLong(); + } + + db->execSql(TQString("INSERT INTO Images (dirid, name, caption) \n " + "VALUES(%1, '%2', '%3');") + .arg(dirid) + .arg(escapeString(name)) + .arg(escapeString(caption)), &values); + + return db->lastInsertedRow(); +} + + +bool upgradeDB_Sqlite2ToSqlite3(const TQString& _libraryPath) +{ + TQString libraryPath = TQDir::cleanDirPath(_libraryPath); + + TQString newDB= libraryPath + "/digikam3.db"; + +#ifdef NFS_HACK + newDB = locateLocal("appdata", TDEIO::encodeFileName(TQDir::cleanDirPath(newDB))); + DDebug() << "NFS: " << newDB << endl; +#endif + + AlbumDB db3; + db3.setDBPath(newDB); + if (!db3.isValid()) + { + DWarning() << "Failed to open new Album Database" << endl; + DWarning() << "The directory <" << libraryPath << "> may not exist or is write-protected" << endl; + return false; + } + + if (db3.getSetting("UpgradedFromSqlite2") == "yes") + return true; + + TQString dbPath = libraryPath + "/digikam.db"; + +#ifdef NFS_HACK + dbPath = locateLocal("appdata", TDEIO::encodeFileName(TQDir::cleanDirPath(dbPath))); + DDebug() << "From NFS: " << dbPath << endl; +#endif + + TQFileInfo fi(dbPath); + + if (!fi.exists()) + { + DDebug() << "No old database present. Not upgrading" << endl; + db3.setSetting("UpgradedFromSqlite2", "yes"); + return true; + } + + AlbumDB_Sqlite2 db2; + db2.setDBPath( dbPath ); + if (!db2.isValid()) + { + DDebug() << "Failed to initialize Old Album Database" << endl; + return false; + } + + // delete entries from sqlite3 database + db3.execSql("DELETE FROM Albums;"); + db3.execSql("DELETE FROM Tags;"); + db3.execSql("DELETE FROM TagsTree;"); + db3.execSql("DELETE FROM Images;"); + db3.execSql("DELETE FROM ImageTags;"); + db3.execSql("DELETE FROM ImageProperties;"); + + TQStringList values; + + // update albums ------------------------------------------------- + + values.clear(); + db2.execSql("SELECT id, url, date, caption, collection, icon FROM Albums;", + &values); + + typedef TQValueList<_Album> AlbumList; + AlbumList albumList; + + typedef TQMap AlbumMap; + AlbumMap albumMap; + + db3.beginTransaction(); + for (TQStringList::iterator it=values.begin(); it!=values.end();) + { + _Album album; + + album.id = (*it).toInt(); + ++it; + album.url = (*it); + ++it; + album.date = (*it); + ++it; + album.caption = (*it); + ++it; + album.collection = (*it); + ++it; + album.icon = (*it); + ++it; + + albumList.append(album); + albumMap.insert(album.url, album.id); + + db3.execSql(TQString("INSERT INTO Albums (id, url, date, caption, collection) " + "VALUES(%1, '%2', '%3', '%4', '%5');") + .arg(album.id) + .arg(escapeString(album.url)) + .arg(escapeString(album.date)) + .arg(escapeString(album.caption)) + .arg(escapeString(album.collection))); + } + db3.commitTransaction(); + + // update tags ------------------------------------------------- + + values.clear(); + db2.execSql("SELECT id, pid, name, icon FROM Tags;", + &values); + + typedef TQValueList<_Tag> TagList; + TagList tagList; + + db3.beginTransaction(); + for (TQStringList::iterator it=values.begin(); it!=values.end();) + { + _Tag tag; + + tag.id = (*it).toInt(); + ++it; + tag.pid = (*it).toInt(); + ++it; + tag.name = (*it); + ++it; + tag.icon = (*it); + ++it; + + tagList.append(tag); + + db3.execSql(TQString("INSERT INTO Tags (id, pid, name) " + "VALUES(%1, %2, '%3');") + .arg(tag.id) + .arg(tag.pid) + .arg(escapeString(tag.name))); + } + db3.commitTransaction(); + + // update images ------------------------------------------------- + + values.clear(); + db2.execSql("SELECT dirid, name, caption FROM Images;", + &values); + + db3.beginTransaction(); + for (TQStringList::iterator it=values.begin(); it!=values.end();) + { + int dirid = (*it).toInt(); + ++it; + TQString name = (*it); + ++it; + TQString caption = (*it); + ++it; + + findOrAddImage(&db3, dirid, name, caption); + } + db3.commitTransaction(); + + // update imagetags ----------------------------------------------- + + values.clear(); + db2.execSql("SELECT dirid, name, tagid FROM ImageTags;", + &values); + db3.beginTransaction(); + for (TQStringList::iterator it=values.begin(); it!=values.end();) + { + int dirid = (*it).toInt(); + ++it; + + TQString name = (*it); + ++it; + + int tagid = (*it).toInt(); + ++it; + + TQ_LLONG imageid = findOrAddImage(&db3, dirid, name, TQString()); + + db3.execSql(TQString("INSERT INTO ImageTags VALUES( %1, %2 )") + .arg(imageid).arg(tagid)); + } + db3.commitTransaction(); + + // update album icons ------------------------------------------------- + + db3.beginTransaction(); + for (AlbumList::iterator it = albumList.begin(); it != albumList.end(); + ++it) + { + _Album album = *it; + + if (album.icon.isEmpty()) + continue; + + TQ_LLONG imageid = findOrAddImage(&db3, album.id, album.icon, TQString()); + + db3.execSql(TQString("UPDATE Albums SET icon=%1 WHERE id=%2") + .arg(imageid) + .arg(album.id)); + } + db3.commitTransaction(); + + // -- update tag icons --------------------------------------------------- + + db3.beginTransaction(); + for (TagList::iterator it = tagList.begin(); it != tagList.end(); ++it) + { + _Tag tag = *it; + + if (tag.icon.isEmpty()) + continue; + + TQFileInfo fi(tag.icon); + if (fi.isRelative()) + { + db3.execSql(TQString("UPDATE Tags SET iconkde='%1' WHERE id=%2") + .arg(escapeString(tag.icon)) + .arg(tag.id)); + continue; + } + + tag.icon = TQDir::cleanDirPath(tag.icon); + fi.setFile(tag.icon.remove(libraryPath)); + + TQString url = fi.dirPath(true); + TQString name = fi.fileName(); + + AlbumMap::iterator it1 = albumMap.find(url); + if (it1 == albumMap.end()) + { + DDebug() << "Could not find album with url: " << url << endl; + DDebug() << "Most likely an external directory. Rejecting." << endl; + continue; + } + + int dirid = it1.data(); + + TQ_LLONG imageid = findOrAddImage(&db3, dirid, name, TQString());; + + db3.execSql(TQString("UPDATE Tags SET icon=%1 WHERE id=%2") + .arg(imageid) + .arg(tag.id)); + + } + db3.commitTransaction(); + + // -- Remove invalid entries ---------------------------------------- + db3.execSql("DELETE FROM Images WHERE dirid=-1"); + + // -- update setting entry ------------------------------------------ + db3.setSetting("UpgradedFromSqlite2", "yes"); + + DDebug() << "Successfully upgraded database to sqlite3 " << endl; + + // -- Check for db consistency ---------------------------------------- + + std::cout << "Checking database consistency" << std::endl; + + + std::cout << "Checking Albums.................."; + values.clear(); + db2.execSql("SELECT id, url, date, caption, collection FROM Albums;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + _Album album; + album.id = (*it).toInt(); + ++it; + album.url = (*it); + ++it; + album.date = (*it); + ++it; + album.caption = (*it); + ++it; + album.collection = (*it); + ++it; + + TQStringList list; + db3.execSql(TQString("SELECT id FROM Albums WHERE \n" + " id=%1 AND \n" + " url='%2' AND \n" + " date='%3' AND \n" + " caption='%4' AND \n" + " collection='%5';") + .arg(album.id) + .arg(escapeString(album.url)) + .arg(escapeString(album.date)) + .arg(escapeString(album.caption)) + .arg(escapeString(album.collection)), &list, false); + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Album: " + << album.url << endl; + return false; + } + } + std::cout << "(" << values.count()/5 << " Albums) " << "OK" << std::endl; + + + std::cout << "Checking Tags...................."; + values.clear(); + db2.execSql("SELECT id, pid, name FROM Tags;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + int id = (*it).toInt(); + ++it; + int pid = (*it).toInt(); + ++it; + TQString name = (*it); + ++it; + + TQStringList list; + db3.execSql(TQString("SELECT id FROM Tags WHERE \n" + " id=%1 AND \n" + " pid=%2 AND \n" + " name='%3';") + .arg(id) + .arg(pid) + .arg(escapeString(name)), + &list, false); + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Tag: " + << name << endl; + return false; + } + } + std::cout << "(" << values.count()/3 << " Tags) " << "OK" << std::endl; + + + std::cout << "Checking Images.................."; + values.clear(); + db2.execSql("SELECT Albums.url, Images.name, Images.caption " + "FROM Images, Albums WHERE Albums.id=Images.dirid;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TQString url = (*it); + ++it; + TQString name = (*it); + ++it; + TQString caption = (*it); + ++it; + + TQStringList list; + db3.execSql(TQString("SELECT Images.id FROM Images, Albums WHERE \n " + "Albums.url = '%1' AND \n " + "Images.dirid = Albums.id AND \n " + "Images.name = '%2' AND \n " + "Images.caption = '%3';") + .arg(escapeString(url)) + .arg(escapeString(name)) + .arg(escapeString(caption)), + &list, false); + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Image: " + << url << ", " << name << ", " << caption << endl; + return false; + } + } + std::cout << "(" << values.count()/3 << " Images) " << "OK" << std::endl; + + + std::cout << "Checking ImageTags..............."; + values.clear(); + db2.execSql("SELECT Albums.url, ImageTags.name, ImageTags.tagid " + "FROM ImageTags, Albums WHERE \n " + " Albums.id=ImageTags.dirid;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TQString url = (*it); + ++it; + TQString name = (*it); + ++it; + int tagid = (*it).toInt(); + ++it; + + TQStringList list; + db3.execSql(TQString("SELECT Images.id FROM Albums, Images, ImageTags WHERE \n " + "Albums.url = '%1' AND \n " + "Images.dirid = Albums.id AND \n " + "Images.name = '%2' AND \n " + "ImageTags.imageid = Images.id AND \n " + "ImageTags.tagid = %3;") + .arg(escapeString(url)) + .arg(escapeString(name)) + .arg(tagid), + &list, false); + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for ImageTag: " + << url << ", " << name << ", " << tagid << endl; + return false; + } + } + std::cout << "(" << values.count()/3 << " ImageTags) " << "OK" << std::endl; + + std::cout << "Checking Album icons ..............."; + values.clear(); + db2.execSql("SELECT url, icon FROM Albums;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + TQString url = (*it); + ++it; + TQString icon = (*it); + ++it; + + if (icon.isEmpty()) + continue; + + TQStringList list; + db3.execSql(TQString("SELECT Images.id FROM Images, Albums WHERE \n " + "Albums.url = '%1' AND \n " + "Images.id = Albums.icon AND \n " + "Images.name = '%2';") + .arg(escapeString(url)) + .arg(escapeString(icon)), &list); + + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Album Icon: " + << url << ", " << icon << endl; + + return false; + } + } + std::cout << "(" << values.count()/2 << " Album Icons) " << "OK" << std::endl; + + + std::cout << "Checking Tag icons ..............."; + values.clear(); + db2.execSql("SELECT id, icon FROM Tags;", &values); + for (TQStringList::iterator it = values.begin(); it != values.end();) + { + int id = (*it).toInt(); + ++it; + TQString icon = (*it); + ++it; + + if (icon.isEmpty()) + continue; + + if (!icon.startsWith("/")) + { + TQStringList list; + db3.execSql(TQString("SELECT id FROM Tags WHERE \n " + "id = %1 AND \n " + "iconkde = '%2';") + .arg(id) + .arg(escapeString(icon)), &list); + + if (list.size() != 1) + { + std::cerr << "Failed" << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Tag Icon: " + << id << ", " << icon << endl; + + return false; + } + } + else + { + icon = TQDir::cleanDirPath(icon); + TQFileInfo fi(icon.remove(libraryPath)); + + TQString url = fi.dirPath(true); + TQString name = fi.fileName(); + + TQStringList list; + + list.clear(); + db3.execSql(TQString("SELECT id FROM Albums WHERE url='%1'") + .arg(escapeString(url)), &list); + if (list.isEmpty()) + { + DWarning() << "Tag icon not in Album Library Path, Rejecting " << endl; + DWarning() << "(" << icon << ")" << endl; + continue; + } + + list.clear(); + db3.execSql(TQString("SELECT Images.id FROM Images, Tags WHERE \n " + " Images.dirid=(SELECT id FROM Albums WHERE url='%1') AND \n " + " Images.name='%2' AND \n " + " Tags.id=%3 AND \n " + " Tags.icon=Images.id") + .arg(escapeString(url)) + .arg(escapeString(name)) + .arg(id), &list); + if (list.size() != 1) + { + std::cerr << "Failed." << std::endl; + DWarning() << "" << endl; + DWarning() << "Consistency check failed for Tag Icon: " + << id << ", " << icon << endl; + + return false; + } + + } + } + std::cout << "(" << values.count()/2 << " Tag Icons) " << "OK" << std::endl; + + std::cout << "" << std::endl; + std::cout << "All Tests: A-OK" << std::endl; + + return true; +} + +} // namespace Digikam diff --git a/src/digikam/upgradedb_sqlite2tosqlite3.h b/src/digikam/upgradedb_sqlite2tosqlite3.h new file mode 100644 index 00000000..8d94fa78 --- /dev/null +++ b/src/digikam/upgradedb_sqlite2tosqlite3.h @@ -0,0 +1,38 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-05 + * Description : SQlite 2 to SQlite 3 interface + * + * Copyright (C) 2005 by Renchi Raju + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef UPGRADEDB_SQLITE2TOSQLITE3_H +#define UPGRADEDB_SQLITE2TOSQLITE3_H + +#include "digikam_export.h" + +class TQString; + +namespace Digikam +{ + +extern bool DIGIKAM_EXPORT upgradeDB_Sqlite2ToSqlite3(const TQString& libraryPath); + +} // namespace Digikam + +#endif /* UPGRADEDB_SQLITE2TOSQLITE3_H */ diff --git a/src/digikam/welcomepageview.cpp b/src/digikam/welcomepageview.cpp new file mode 100644 index 00000000..6327c947 --- /dev/null +++ b/src/digikam/welcomepageview.cpp @@ -0,0 +1,193 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-12-20 + * Description : a widget to display a welcome page + * on root album. + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "welcomepageview.h" +#include "welcomepageview.moc" + +namespace Digikam +{ + +WelcomePageView::WelcomePageView(TQWidget* parent) + : TDEHTMLPart(parent) +{ + widget()->setFocusPolicy(TQWidget::WheelFocus); + // Let's better be paranoid and disable plugins (it defaults to enabled): + setPluginsEnabled(false); + setJScriptEnabled(false); // just make this explicit. + setJavaEnabled(false); // just make this explicit. + setMetaRefreshEnabled(false); + setURLCursor(KCursor::handCursor()); + + TQString fontSize = TQString::number(12); + TQString appTitle = i18n("digiKam"); + TQString catchPhrase = TQString(); // Not enough space for a catch phrase at default window size. + TQString quickDescription = TQString(digiKamDescription()); + TQString locationHtml = locate("data", "digikam/about/main.html"); + TQString locationCss = locate("data", "digikam/about/kde_infopage.css"); + TQString locationRtl = locate("data", "digikam/about/kde_infopage_rtl.css" ); + TQString rtl = kapp->reverseLayout() ? TQString("@import \"%1\";" ).arg(locationRtl) + : TQString(); + + begin(KURL(locationHtml)); + + TQString content = fileToString(locationHtml); + content = content.arg(locationCss) // %1 + .arg(rtl) // %2 + .arg(fontSize) // %3 + .arg(appTitle) // %4 + .arg(catchPhrase) // %5 + .arg(quickDescription) // %6 + .arg(infoPage()); // %7 + + write(content); + end(); + show(); + + connect(browserExtension(), TQ_SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)), + this, TQ_SLOT(slotUrlOpen(const KURL &))); +} + +WelcomePageView::~WelcomePageView() +{ +} + +void WelcomePageView::slotUrlOpen(const KURL &url) +{ + TDEApplication::kApplication()->invokeBrowser(url.url()); +} + +TQString WelcomePageView::infoPage() +{ + TQString info = + i18n( + "%1: digiKam version; " + "%2: help:// URL; " + "%3: homepage URL; " + "%4: prior digiKam version; " + "%5: prior KDE version; " + "%6: generated list of new features; " + "%7: First-time user text (only shown on first start); " + "%8: generated list of important changes; " + "--- end of comment ---", + "

Welcome to digiKam

" + "digiKam is a photo management program for the Trinity Desktop Environment. " + "It is designed to import, organize, and export your digital photographs on your computer." + "

You are currently in the Album view mode of digiKam. The Albums are the real " + "containers where your files are stored, they are identical with the folders " + "on disk.

\n" + "digiKam has many powerful features\n" + "%8\n

" // important changes + "Some of the features of digiKam include

\n" + "
    \n%5
\n" + "%6\n" + "

We hope you will enjoy digiKam.

\n" + "

Thank you,

\n" + "

    The digiKam Team

"); + + TQStringList newFeatures; + newFeatures << i18n("16-bit/color/pixel image support"); + newFeatures << i18n("Full color management support"); + newFeatures << i18n("Native JPEG-2000 support"); + newFeatures << i18n("Makernote and IPTC metadata support"); + newFeatures << i18n("Photograph geolocation"); + newFeatures << i18n("Extensive Sidebars"); + newFeatures << i18n("Advanced RAW image decoding settings"); + newFeatures << i18n("Fast RAW preview"); + newFeatures << i18n("RAW Metadata support"); + newFeatures << i18n("Camera Interface used as generic import tool"); + newFeatures << i18n("New advanced camera download options"); + newFeatures << i18n("New advanced tag management"); + newFeatures << i18n("New zooming/panning support in preview mode"); + newFeatures << i18n("New Light Table provides easy comparison for similar images"); + newFeatures << i18n("New text, mime-type, and rating filters to search contents on icon view"); + newFeatures << i18n("New options to easy navigate between albums, tags and collections"); + newFeatures << i18n("New options to recursively show the contents of sub-folders"); + newFeatures << i18n("New text filter to search contents on folder views"); + newFeatures << i18n("New options to count of items on all folder views"); + newFeatures << i18n("New tool to perform dates search around whole albums collection: Time-Line"); + newFeatures << i18n("New tool to import RAW files in editor with customized decoding settings"); + + TQString featureItems; + for ( uint i = 0 ; i < newFeatures.count() ; i++ ) + featureItems += i18n("
  • %1
  • \n").arg( newFeatures[i] ); + + info = info.arg( featureItems ); + + // Add first-time user text (only shown on first start). + info = info.arg( TQString() ); + + // Generated list of important changes + info = info.arg( TQString() ); + + return info; +} + +TQCString WelcomePageView::fileToString(const TQString &aFileName) +{ + TQCString result; + TQFileInfo info(aFileName); + unsigned int readLen; + unsigned int len = info.size(); + TQFile file(aFileName); + + if (aFileName.isEmpty() || len <= 0 || + !info.exists() || info.isDir() || !info.isReadable() || + !file.open(IO_Raw|IO_ReadOnly)) + return TQCString(); + + result.resize(len + 2); + readLen = file.readBlock(result.data(), len); + if (1 && result[len-1]!='\n') + { + result[len++] = '\n'; + readLen++; + } + result[len] = '\0'; + + if (readLen < len) + return TQCString(); + + return result; +} + +} // namespace Digikam diff --git a/src/digikam/welcomepageview.h b/src/digikam/welcomepageview.h new file mode 100644 index 00000000..b69d82a2 --- /dev/null +++ b/src/digikam/welcomepageview.h @@ -0,0 +1,64 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-12-20 + * Description : a view to display a welcome page + * on root album. + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef WELCOMEPAGEVIEW_H +#define WELCOMEPAGEVIEW_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT WelcomePageView : public TDEHTMLPart +{ + TQ_OBJECT + +public: + + WelcomePageView(TQWidget* parent); + ~WelcomePageView(); + +private: + + TQCString fileToString(const TQString &aFileName); + TQString infoPage(); + +private slots: + + void slotUrlOpen(const KURL &); +}; + +} // namespace Digikam + +#endif /* WELCOMEPAGEVIEW_H */ diff --git a/src/imageplugins/Makefile.am b/src/imageplugins/Makefile.am new file mode 100644 index 00000000..3e04b062 --- /dev/null +++ b/src/imageplugins/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = coreplugin adjustcurves adjustlevels antivignetting blurfx border \ + channelmixer charcoal colorfx distortionfx emboss filmgrain \ + freerotation hotpixels infrared inpainting inserttext lensdistortion \ + noisereduction oilpaint perspective raindrop restoration sheartool \ + superimpose texture whitebalance diff --git a/src/imageplugins/adjustcurves/Makefile.am b/src/imageplugins/adjustcurves/Makefile.am new file mode 100644 index 00000000..f798a7af --- /dev/null +++ b/src/imageplugins/adjustcurves/Makefile.am @@ -0,0 +1,33 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_adjustcurves_la_SOURCES = imageplugin_adjustcurves.cpp \ + adjustcurvestool.cpp + +digikamimageplugin_adjustcurves_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_adjustcurves_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_adjustcurves.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_adjustcurves.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_adjustcurves_ui.rc diff --git a/src/imageplugins/adjustcurves/adjustcurves.cpp b/src/imageplugins/adjustcurves/adjustcurves.cpp new file mode 100644 index 00000000..ace9c9b3 --- /dev/null +++ b/src/imageplugins/adjustcurves/adjustcurves.cpp @@ -0,0 +1,677 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "histogramwidget.h" +#include "curveswidget.h" +#include "colorgradientwidget.h" +#include "dimgimagefilters.h" +#include "adjustcurves.h" +#include "adjustcurves.moc" + +namespace DigikamAdjustCurvesImagesPlugin +{ + +AdjustCurveDialog::AdjustCurveDialog(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Adjust Color Curves"), "adjustcurves", true, false) +{ + m_destinationPreviewData = 0L; + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + m_originalImage = Digikam::DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + + m_histoSegments = m_originalImage.sixteenBit() ? 65535 : 255; + m_curves = new Digikam::ImageCurves(m_originalImage.sixteenBit()); + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Adjust Color Curves"), + digikam_version, + I18N_NOOP("An image-histogram-curves adjustment plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("adjustcurves Tool Dialog", plainPage(), + i18n("

    This is the image's curve-adjustments preview. " + "You can pick a spot on the image " + "to see the corresponding level in the histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* grid = new TQGridLayout( gboxSettings, 5, 5, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->insertItem( i18n("Alpha") ); + m_channelCB->setCurrentText( i18n("Luminosity") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    " + "Alpha: display the alpha image-channel values. " + "This channel corresponds to the transparency value and " + "is supported by some image formats, such as PNG or TIF.")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::CurvesWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::CurvesWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + m_scaleBG->setExclusive(true); + m_scaleBG->setButton(Digikam::CurvesWidget::LogScaleHistogram); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + grid->addMultiCellWidget(label1, 0, 0, 1, 1); + grid->addMultiCellWidget(m_channelCB, 0, 0, 2, 2); + grid->addMultiCellLayout(l1, 0, 0, 4, 5); + + // ------------------------------------------------------------- + + TQWidget *curveBox = new TQWidget(gboxSettings); + TQGridLayout* gl = new TQGridLayout(curveBox, 4, 2, 0); + + m_histogramWidget = new Digikam::HistogramWidget(256, 140, curveBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "curves settings changes.")); + + m_vGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Vertical, 10, curveBox ); + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + + TQLabel *spacev = new TQLabel(curveBox); + spacev->setFixedWidth(1); + + m_curvesWidget = new Digikam::CurvesWidget(256, 256, m_originalImage.bits(), m_originalImage.width(), + m_originalImage.height(), m_originalImage.sixteenBit(), + m_curves, curveBox); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve drawing of the selected channel from " + "original image")); + + TQLabel *spaceh = new TQLabel(curveBox); + spaceh->setFixedHeight(1); + + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, curveBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gl->addMultiCellWidget(m_histogramWidget, 0, 0, 2, 2); + gl->addMultiCellWidget(m_vGradient, 2, 2, 0, 0); + gl->addMultiCellWidget(spacev, 2, 2, 1, 1); + gl->addMultiCellWidget(m_curvesWidget, 2, 2, 2, 2); + gl->addMultiCellWidget(spaceh, 3, 3, 2, 2); + gl->addMultiCellWidget(m_hGradient, 4, 4, 2, 2); + gl->setRowSpacing(1, spacingHint()); + + grid->addMultiCellWidget(curveBox, 2, 3, 0, 5); + + // ------------------------------------------------------------- + + m_curveType = new TQHButtonGroup(gboxSettings); + m_curveFree = new TQPushButton(m_curveType); + m_curveType->insert(m_curveFree, FreeDrawing); + TDEGlobal::dirs()->addResourceType("curvefree", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("curvefree", "curvefree.png"); + m_curveFree->setPixmap( TQPixmap( directory + "curvefree.png" ) ); + m_curveFree->setToggleButton(true); + TQToolTip::add( m_curveFree, i18n( "Curve free mode" ) ); + TQWhatsThis::add( m_curveFree, i18n("

    With this button, you can draw your curve free-hand with the mouse.")); + m_curveSmooth = new TQPushButton(m_curveType); + m_curveType->insert(m_curveSmooth, SmoothDrawing); + TDEGlobal::dirs()->addResourceType("curvemooth", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("curvemooth", "curvemooth.png"); + m_curveSmooth->setPixmap( TQPixmap( directory + "curvemooth.png" ) ); + m_curveSmooth->setToggleButton(true); + TQToolTip::add( m_curveSmooth, i18n( "Curve smooth mode" ) ); + TQWhatsThis::add( m_curveSmooth, i18n("

    With this button, you constrains the curve type to a smooth line with tension.")); + m_curveType->setExclusive(true); + m_curveType->setButton(SmoothDrawing); + m_curveType->setFrameShape(TQFrame::NoFrame); + + // ------------------------------------------------------------- + + m_pickerColorButtonGroup = new TQHButtonGroup(gboxSettings); + m_pickBlack = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickBlack, BlackTonal); + TDEGlobal::dirs()->addResourceType("color-picker-black", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-black", "color-picker-black.png"); + m_pickBlack->setPixmap( TQPixmap( directory + "color-picker-black.png" ) ); + m_pickBlack->setToggleButton(true); + TQToolTip::add( m_pickBlack, i18n( "All channels shadow tone color picker" ) ); + TQWhatsThis::add( m_pickBlack, i18n("

    With this button, you can pick the color from original image used to set Shadow Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickGray = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickGray, GrayTonal); + TDEGlobal::dirs()->addResourceType("color-picker-grey", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-grey", "color-picker-grey.png"); + m_pickGray->setPixmap( TQPixmap( directory + "color-picker-grey.png" ) ); + m_pickGray->setToggleButton(true); + TQToolTip::add( m_pickGray, i18n( "All channels middle tone color picker" ) ); + TQWhatsThis::add( m_pickGray, i18n("

    With this button, you can pick the color from original image used to set Middle Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickWhite = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickWhite, WhiteTonal); + TDEGlobal::dirs()->addResourceType("color-picker-white", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-white", "color-picker-white.png"); + m_pickWhite->setPixmap( TQPixmap( directory + "color-picker-white.png" ) ); + m_pickWhite->setToggleButton(true); + TQToolTip::add( m_pickWhite, i18n( "All channels highlight tone color picker" ) ); + TQWhatsThis::add( m_pickWhite, i18n("

    With this button, you can pick the color from original image used to set Highlight Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickerColorButtonGroup->setExclusive(true); + m_pickerColorButtonGroup->setFrameShape(TQFrame::NoFrame); + + // ------------------------------------------------------------- + + m_resetButton = new TQPushButton(i18n("&Reset"), gboxSettings); + m_resetButton->setPixmap( SmallIcon("reload_page", 18) ); + TQToolTip::add( m_resetButton, i18n( "Reset current channel curves' values." ) ); + TQWhatsThis::add( m_resetButton, i18n("

    If you press this button, all curves' values " + "from the current selected channel " + "will be reset to the default values.")); + + TQHBoxLayout* l3 = new TQHBoxLayout(); + l3->addWidget(m_curveType); + l3->addWidget(m_pickerColorButtonGroup); + l3->addWidget(m_resetButton); + l3->addStretch(10); + + grid->addMultiCellLayout(l3, 4, 4, 1, 5); + + // ------------------------------------------------------------- + + + grid->setRowStretch(5, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotSpotColorChanged( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // ComboBox slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_curveType, TQ_SIGNAL(clicked(int)), + this, TQ_SLOT(slotCurveTypeChanged(int))); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); + + connect(m_pickerColorButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotPickerColorButtonActived())); +} + +AdjustCurveDialog::~AdjustCurveDialog() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_curvesWidget; + delete m_previewWidget; + delete m_curves; +} + +void AdjustCurveDialog::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(Digikam::ImageGuideWidget::PreviewOriginalImage); +} + +void AdjustCurveDialog::slotSpotColorChanged(const Digikam::DColor &color) +{ + Digikam::DColor sc = color; + + if ( m_pickBlack->isOn() ) + { + // Black tonal curves point. + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 1, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 42*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::RedChannel, 1, TQPoint(sc.red(), 42*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::GreenChannel, 1, TQPoint(sc.green(), 42*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::BlueChannel, 1, TQPoint(sc.blue(), 42*m_histoSegments/256)); + m_pickBlack->setOn(false); + } + else if ( m_pickGray->isOn() ) + { + // Gray tonal curves point. + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 8, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 128*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::RedChannel, 8, TQPoint(sc.red(), 128*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::GreenChannel, 8, TQPoint(sc.green(), 128*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::BlueChannel, 8, TQPoint(sc.blue(), 128*m_histoSegments/256)); + m_pickGray->setOn(false); + } + else if ( m_pickWhite->isOn() ) + { + // White tonal curves point. + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 15, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 213*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::RedChannel, 15, TQPoint(sc.red(), 213*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::GreenChannel, 15, TQPoint(sc.green(), 213*m_histoSegments/256)); + m_curves->setCurvePoint(Digikam::ImageHistogram::BlueChannel, 15, TQPoint(sc.blue(), 213*m_histoSegments/256)); + m_pickWhite->setOn(false); + } + else + { + m_curvesWidget->setCurveGuide(color); + return; + } + + // Calculate Red, green, blue curves. + + for (int i = Digikam::ImageHistogram::ValueChannel ; i <= Digikam::ImageHistogram::BlueChannel ; i++) + m_curves->curvesCalculateCurve(i); + + m_curvesWidget->repaint(false); + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void AdjustCurveDialog::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void AdjustCurveDialog::slotResetCurrentChannel() +{ + m_curves->curvesChannelReset(m_channelCB->currentItem()); + + m_curvesWidget->reset(); + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustCurveDialog::slotEffect() +{ + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_curves->curvesLutProcess(orgData, m_destinationPreviewData, w, h); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] orgData; +} + +void AdjustCurveDialog::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + // Create the new empty destination image data space. + uchar* desData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_curves->curvesLutProcess(orgData, desData, w, h); + + iface->putOriginalImage(i18n("Adjust Curve"), desData); + kapp->restoreOverrideCursor(); + + delete [] orgData; + delete [] desData; + accept(); +} + +void AdjustCurveDialog::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_curvesWidget->m_channelType = Digikam::CurvesWidget::ValueHistogram; + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_curvesWidget->m_channelType = Digikam::CurvesWidget::RedChannelHistogram; + m_vGradient->setColors( TQColor( "red" ), TQColor( "black" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_curvesWidget->m_channelType = Digikam::CurvesWidget::GreenChannelHistogram; + m_vGradient->setColors( TQColor( "green" ), TQColor( "black" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_curvesWidget->m_channelType = Digikam::CurvesWidget::BlueChannelHistogram; + m_vGradient->setColors( TQColor( "blue" ), TQColor( "black" ) ); + break; + + case AlphaChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::AlphaChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_curvesWidget->m_channelType = Digikam::CurvesWidget::AlphaChannelHistogram; + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + break; + } + + m_curveType->setButton(m_curves->getCurveType(channel)); + + m_curvesWidget->repaint(false); + m_histogramWidget->repaint(false); +} + +void AdjustCurveDialog::slotScaleChanged(int scale) +{ + m_curvesWidget->m_scaleType = scale; + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_curvesWidget->repaint(false); +} + +void AdjustCurveDialog::slotCurveTypeChanged(int type) +{ + switch(type) + { + case SmoothDrawing: + { + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_SMOOTH); + m_pickerColorButtonGroup->setEnabled(true); + break; + } + + case FreeDrawing: + { + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_FREE); + m_pickerColorButtonGroup->setEnabled(false); + break; + } + } + + m_curvesWidget->curveTypeChanged(); +} + +void AdjustCurveDialog::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustcurves Tool Dialog"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + + m_curvesWidget->reset(); + + for (int i = 0 ; i < 5 ; i++) + { + m_curves->curvesChannelReset(i); + m_curves->setCurveType(i, (Digikam::ImageCurves::CurveType)config->readNumEntry(TQString("CurveTypeChannel%1").arg(i), + Digikam::ImageCurves::CURVE_SMOOTH)); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentChannel%1Point%2").arg(i).arg(j), &disable); + + if (m_originalImage.sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curves->setCurvePoint(i, j, p); + } + + m_curves->curvesCalculateCurve(i); + } + + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void AdjustCurveDialog::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustcurves Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + for (int i = 0 ; i < 5 ; i++) + { + config->writeEntry(TQString("CurveTypeChannel%1").arg(i), m_curves->getCurveType(i)); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curves->getCurvePoint(i, j); + + if (m_originalImage.sixteenBit() && p.x() != -1) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + + config->writeEntry(TQString("CurveAjustmentChannel%1Point%2").arg(i).arg(j), p); + } + } + + config->sync(); +} + +void AdjustCurveDialog::resetValues() +{ + for (int channel = 0 ; channel < 5 ; channel++) + m_curves->curvesChannelReset(channel); + + m_curvesWidget->reset(); + m_histogramWidget->reset(); +} + +// Load all settings. +void AdjustCurveDialog::slotUser3() +{ + KURL loadCurvesFile; + + loadCurvesFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Select Gimp Curves File to Load")) ); + if( loadCurvesFile.isEmpty() ) + return; + + if ( m_curves->loadCurvesFromGimpCurvesFile( loadCurvesFile ) == false ) + { + KMessageBox::error(this, i18n("Cannot load from the Gimp curves text file.")); + return; + } + + // Refresh the current curves config. + slotChannelChanged(m_channelCB->currentItem()); + slotEffect(); +} + +// Save all settings. +void AdjustCurveDialog::slotUser2() +{ + KURL saveCurvesFile; + + saveCurvesFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Gimp Curves File to Save")) ); + if( saveCurvesFile.isEmpty() ) + return; + + if ( m_curves->saveCurvesToGimpCurvesFile( saveCurvesFile ) == false ) + { + KMessageBox::error(this, i18n("Cannot save to the Gimp curves text file.")); + return; + } + + // Refresh the current curves config. + slotChannelChanged(m_channelCB->currentItem()); +} + +} // NameSpace DigikamAdjustCurvesImagesPlugin + diff --git a/src/imageplugins/adjustcurves/adjustcurves.h b/src/imageplugins/adjustcurves/adjustcurves.h new file mode 100644 index 00000000..f4cad2f2 --- /dev/null +++ b/src/imageplugins/adjustcurves/adjustcurves.h @@ -0,0 +1,143 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ADJUSTCURVES_H +#define ADJUSTCURVES_H + +// Digikam includes. + +#include "imagedlgbase.h" +#include "dimg.h" + +// Local includes. + +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +namespace Digikam +{ +class CurvesWidget; +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class ImageCurves; +} + +namespace DigikamAdjustCurvesImagesPlugin +{ + +class AdjustCurveDialog : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + AdjustCurveDialog(TQWidget *parent); + ~AdjustCurveDialog(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotEffect(); + void slotResetCurrentChannel(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotCurveTypeChanged(int type); + void slotSpotColorChanged(const Digikam::DColor &color); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotPickerColorButtonActived(); + +private: + + enum ColorPicker + { + BlackTonal=0, + GrayTonal, + WhiteTonal + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel + }; + + enum CurvesDrawingType + { + SmoothDrawing=0, + FreeDrawing + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + uchar *m_destinationPreviewData; + + int m_histoSegments; + int m_currentPreviewMode; + + TQComboBox *m_channelCB; + + TQPushButton *m_resetButton; + TQPushButton *m_pickBlack; + TQPushButton *m_pickGray; + TQPushButton *m_pickWhite; + TQPushButton *m_curveFree; + TQPushButton *m_curveSmooth; + + TQHButtonGroup *m_pickerColorButtonGroup; + TQHButtonGroup *m_scaleBG; + TQHButtonGroup *m_curveType; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ColorGradientWidget *m_hGradient; + Digikam::ColorGradientWidget *m_vGradient; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ImageCurves *m_curves; + Digikam::DImg m_originalImage; +}; + +} // NameSpace DigikamAdjustCurvesImagesPlugin + +#endif /* ADJUSTCURVES_H */ diff --git a/src/imageplugins/adjustcurves/adjustcurvestool.cpp b/src/imageplugins/adjustcurves/adjustcurvestool.cpp new file mode 100644 index 00000000..7bfd00f7 --- /dev/null +++ b/src/imageplugins/adjustcurves/adjustcurvestool.cpp @@ -0,0 +1,659 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "curveswidget.h" +#include "colorgradientwidget.h" +#include "dimgimagefilters.h" +#include "adjustcurvestool.h" +#include "adjustcurvestool.moc" + +using namespace Digikam; + +namespace DigikamAdjustCurvesImagesPlugin +{ + +AdjustCurvesTool::AdjustCurvesTool(TQObject* parent) + : EditorTool(parent) +{ + m_destinationPreviewData = 0; + + ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + + m_histoSegments = m_originalImage->sixteenBit() ? 65535 : 255; + + setName("adjustcurves"); + setToolName(i18n("Adjust Curves")); + setToolIcon(SmallIcon("adjustcurves")); + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("adjustcurves Tool", 0, + i18n("

    This is the image's curve-adjustments preview. " + "You can pick a spot on the image " + "to see the corresponding level in the histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 5, 5); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, m_gboxSettings->plainPage() ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->insertItem( i18n("Alpha") ); + m_channelCB->setCurrentText( i18n("Luminosity") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    " + "Alpha: display the alpha image-channel values. " + "This channel corresponds to the transparency value and " + "is supported by some image formats, such as PNG or TIF.")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, CurvesWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, CurvesWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + m_scaleBG->setExclusive(true); + m_scaleBG->setButton(CurvesWidget::LogScaleHistogram); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQWidget *curveBox = new TQWidget(m_gboxSettings->plainPage()); + TQGridLayout* gl = new TQGridLayout(curveBox, 4, 2, 0); + + m_histogramWidget = new HistogramWidget(256, 140, curveBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "curves settings changes.")); + + m_vGradient = new ColorGradientWidget( ColorGradientWidget::Vertical, 10, curveBox ); + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + + TQLabel *spacev = new TQLabel(curveBox); + spacev->setFixedWidth(1); + + m_curvesWidget = new CurvesWidget(256, 256, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), + curveBox); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve drawing of the selected channel from " + "original image")); + + TQLabel *spaceh = new TQLabel(curveBox); + spaceh->setFixedHeight(1); + + m_hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, curveBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gl->addMultiCellWidget(m_histogramWidget, 0, 0, 2, 2); + gl->addMultiCellWidget(m_vGradient, 2, 2, 0, 0); + gl->addMultiCellWidget(spacev, 2, 2, 1, 1); + gl->addMultiCellWidget(m_curvesWidget, 2, 2, 2, 2); + gl->addMultiCellWidget(spaceh, 3, 3, 2, 2); + gl->addMultiCellWidget(m_hGradient, 4, 4, 2, 2); + gl->setRowSpacing(1, m_gboxSettings->spacingHint()); + + // ------------------------------------------------------------- + + m_curveType = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_curveFree = new TQPushButton(m_curveType); + m_curveType->insert(m_curveFree, FreeDrawing); + TDEGlobal::dirs()->addResourceType("curvefree", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("curvefree", "curvefree.png"); + m_curveFree->setPixmap( TQPixmap( directory + "curvefree.png" ) ); + m_curveFree->setToggleButton(true); + TQToolTip::add( m_curveFree, i18n( "Curve free mode" ) ); + TQWhatsThis::add( m_curveFree, i18n("

    With this button, you can draw your curve free-hand with the mouse.")); + m_curveSmooth = new TQPushButton(m_curveType); + m_curveType->insert(m_curveSmooth, SmoothDrawing); + TDEGlobal::dirs()->addResourceType("curvemooth", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("curvemooth", "curvemooth.png"); + m_curveSmooth->setPixmap( TQPixmap( directory + "curvemooth.png" ) ); + m_curveSmooth->setToggleButton(true); + TQToolTip::add( m_curveSmooth, i18n( "Curve smooth mode" ) ); + TQWhatsThis::add( m_curveSmooth, i18n("

    With this button, you constrains the curve type to a smooth line with tension.")); + m_curveType->setExclusive(true); + m_curveType->setButton(SmoothDrawing); + m_curveType->setFrameShape(TQFrame::NoFrame); + + // ------------------------------------------------------------- + + m_pickerColorButtonGroup = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_pickBlack = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickBlack, BlackTonal); + TDEGlobal::dirs()->addResourceType("color-picker-black", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-black", "color-picker-black.png"); + m_pickBlack->setPixmap( TQPixmap( directory + "color-picker-black.png" ) ); + m_pickBlack->setToggleButton(true); + TQToolTip::add( m_pickBlack, i18n( "All channels shadow tone color picker" ) ); + TQWhatsThis::add( m_pickBlack, i18n("

    With this button, you can pick the color from original image used to set Shadow Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickGray = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickGray, GrayTonal); + TDEGlobal::dirs()->addResourceType("color-picker-grey", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-grey", "color-picker-grey.png"); + m_pickGray->setPixmap( TQPixmap( directory + "color-picker-grey.png" ) ); + m_pickGray->setToggleButton(true); + TQToolTip::add( m_pickGray, i18n( "All channels middle tone color picker" ) ); + TQWhatsThis::add( m_pickGray, i18n("

    With this button, you can pick the color from original image used to set Middle Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickWhite = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickWhite, WhiteTonal); + TDEGlobal::dirs()->addResourceType("color-picker-white", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-white", "color-picker-white.png"); + m_pickWhite->setPixmap( TQPixmap( directory + "color-picker-white.png" ) ); + m_pickWhite->setToggleButton(true); + TQToolTip::add( m_pickWhite, i18n( "All channels highlight tone color picker" ) ); + TQWhatsThis::add( m_pickWhite, i18n("

    With this button, you can pick the color from original image used to set Highlight Tone " + "smooth curves point on Red, Green, Blue, and Luminosity channels.")); + m_pickerColorButtonGroup->setExclusive(true); + m_pickerColorButtonGroup->setFrameShape(TQFrame::NoFrame); + + // ------------------------------------------------------------- + + m_resetButton = new TQPushButton(i18n("&Reset"), m_gboxSettings->plainPage()); + m_resetButton->setPixmap( SmallIcon("reload_page", 18) ); + TQToolTip::add( m_resetButton, i18n( "Reset current channel curves' values." ) ); + TQWhatsThis::add( m_resetButton, i18n("

    If you press this button, all curves' values " + "from the current selected channel " + "will be reset to the default values.")); + + TQHBoxLayout* l3 = new TQHBoxLayout(); + l3->addWidget(m_curveType); + l3->addWidget(m_pickerColorButtonGroup); + l3->addWidget(m_resetButton); + l3->addStretch(10); + + grid->addMultiCellLayout(l1, 0, 0, 1, 5); + grid->addMultiCellWidget(curveBox, 1, 3, 0, 5); + grid->addMultiCellLayout(l3, 4, 4, 1, 5); + grid->setMargin(0); + grid->setSpacing(m_gboxSettings->spacingHint()); + grid->setRowStretch(5, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotSpotColorChanged(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // ComboBox slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_curveType, TQ_SIGNAL(clicked(int)), + this, TQ_SLOT(slotCurveTypeChanged(int))); + + // ------------------------------------------------------------- + // Buttons slots. + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); + + connect(m_pickerColorButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotPickerColorButtonActived())); +} + +AdjustCurvesTool::~AdjustCurvesTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void AdjustCurvesTool::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(ImageGuideWidget::PreviewOriginalImage); +} + +void AdjustCurvesTool::slotSpotColorChanged(const DColor &color) +{ + DColor sc = color; + + if ( m_pickBlack->isOn() ) + { + // Black tonal curves point. + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, 1, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 42*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::RedChannel, 1, TQPoint(sc.red(), 42*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::GreenChannel, 1, TQPoint(sc.green(), 42*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::BlueChannel, 1, TQPoint(sc.blue(), 42*m_histoSegments/256)); + m_pickBlack->setOn(false); + } + else if ( m_pickGray->isOn() ) + { + // Gray tonal curves point. + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, 8, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 128*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::RedChannel, 8, TQPoint(sc.red(), 128*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::GreenChannel, 8, TQPoint(sc.green(), 128*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::BlueChannel, 8, TQPoint(sc.blue(), 128*m_histoSegments/256)); + m_pickGray->setOn(false); + } + else if ( m_pickWhite->isOn() ) + { + // White tonal curves point. + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, 15, + TQPoint(TQMAX(TQMAX(sc.red(), sc.green()), sc.blue()), 213*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::RedChannel, 15, TQPoint(sc.red(), 213*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::GreenChannel, 15, TQPoint(sc.green(), 213*m_histoSegments/256)); + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::BlueChannel, 15, TQPoint(sc.blue(), 213*m_histoSegments/256)); + m_pickWhite->setOn(false); + } + else + { + m_curvesWidget->setCurveGuide(color); + return; + } + + // Calculate Red, green, blue curves. + + for (int i = ImageHistogram::ValueChannel ; i <= ImageHistogram::BlueChannel ; i++) + m_curvesWidget->curves()->curvesCalculateCurve(i); + + m_curvesWidget->repaint(false); + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void AdjustCurvesTool::slotColorSelectedFromTarget( const DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void AdjustCurvesTool::slotResetCurrentChannel() +{ + m_curvesWidget->curves()->curvesChannelReset(m_channelCB->currentItem()); + + m_curvesWidget->repaint(); + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustCurvesTool::slotEffect() +{ + ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_curvesWidget->curves()->curvesLutProcess(orgData, m_destinationPreviewData, w, h); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] orgData; +} + +void AdjustCurvesTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + // Create the new empty destination image data space. + uchar* desData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_curvesWidget->curves()->curvesLutProcess(orgData, desData, w, h); + + iface->putOriginalImage(i18n("Adjust Curves"), desData); + kapp->restoreOverrideCursor(); + + delete [] orgData; + delete [] desData; +} + +void AdjustCurvesTool::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_curvesWidget->m_channelType = CurvesWidget::ValueHistogram; + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_curvesWidget->m_channelType = CurvesWidget::RedChannelHistogram; + m_vGradient->setColors( TQColor( "red" ), TQColor( "black" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_curvesWidget->m_channelType = CurvesWidget::GreenChannelHistogram; + m_vGradient->setColors( TQColor( "green" ), TQColor( "black" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_curvesWidget->m_channelType = CurvesWidget::BlueChannelHistogram; + m_vGradient->setColors( TQColor( "blue" ), TQColor( "black" ) ); + break; + + case AlphaChannel: + m_histogramWidget->m_channelType = HistogramWidget::AlphaChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_curvesWidget->m_channelType = CurvesWidget::AlphaChannelHistogram; + m_vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + break; + } + + m_curveType->setButton(m_curvesWidget->curves()->getCurveType(channel)); + + m_curvesWidget->repaint(false); + m_histogramWidget->repaint(false); +} + +void AdjustCurvesTool::slotScaleChanged(int scale) +{ + m_curvesWidget->m_scaleType = scale; + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_curvesWidget->repaint(false); +} + +void AdjustCurvesTool::slotCurveTypeChanged(int type) +{ + switch(type) + { + case SmoothDrawing: + { + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_SMOOTH); + m_pickerColorButtonGroup->setEnabled(true); + break; + } + + case FreeDrawing: + { + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_FREE); + m_pickerColorButtonGroup->setEnabled(false); + break; + } + } + + m_curvesWidget->curveTypeChanged(); +} + +void AdjustCurvesTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustcurves Tool"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + + m_curvesWidget->reset(); + + for (int i = 0 ; i < 5 ; i++) + { + m_curvesWidget->curves()->curvesChannelReset(i); + m_curvesWidget->curves()->setCurveType(i, (ImageCurves::CurveType)config->readNumEntry(TQString("CurveTypeChannel%1").arg(i), + ImageCurves::CURVE_SMOOTH)); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentChannel%1Point%2").arg(i).arg(j), &disable); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curvesWidget->curves()->setCurvePoint(i, j, p); + } + + m_curvesWidget->curves()->curvesCalculateCurve(i); + } + + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); + slotEffect(); +} + +void AdjustCurvesTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustcurves Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + for (int i = 0 ; i < 5 ; i++) + { + config->writeEntry(TQString("CurveTypeChannel%1").arg(i), m_curvesWidget->curves()->getCurveType(i)); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curvesWidget->curves()->getCurvePoint(i, j); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + + config->writeEntry(TQString("CurveAjustmentChannel%1Point%2").arg(i).arg(j), p); + } + } + + m_previewWidget->writeSettings(); + config->sync(); +} + +void AdjustCurvesTool::slotResetSettings() +{ + for (int channel = 0 ; channel < 5 ; channel++) + m_curvesWidget->curves()->curvesChannelReset(channel); + + m_curvesWidget->reset(); + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustCurvesTool::slotLoadSettings() +{ + KURL loadCurvesFile; + + loadCurvesFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Select Gimp Curves File to Load")) ); + if( loadCurvesFile.isEmpty() ) + return; + + if ( m_curvesWidget->curves()->loadCurvesFromGimpCurvesFile( loadCurvesFile ) == false ) + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load from the Gimp curves text file.")); + return; + } + + // Refresh the current curves config. + slotChannelChanged(m_channelCB->currentItem()); + slotEffect(); +} + +void AdjustCurvesTool::slotSaveAsSettings() +{ + KURL saveCurvesFile; + + saveCurvesFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Gimp Curves File to Save")) ); + if( saveCurvesFile.isEmpty() ) + return; + + if ( m_curvesWidget->curves()->saveCurvesToGimpCurvesFile( saveCurvesFile ) == false ) + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save to the Gimp curves text file.")); + return; + } + + // Refresh the current curves config. + slotChannelChanged(m_channelCB->currentItem()); +} + +} // NameSpace DigikamAdjustCurvesImagesPlugin diff --git a/src/imageplugins/adjustcurves/adjustcurvestool.h b/src/imageplugins/adjustcurves/adjustcurvestool.h new file mode 100644 index 00000000..4a380530 --- /dev/null +++ b/src/imageplugins/adjustcurves/adjustcurvestool.h @@ -0,0 +1,145 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ADJUSTCURVESTOOL_H +#define ADJUSTCURVESTOOL_H + +// Digikam includes. + +#include "editortool.h" + +// Local includes. + +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +namespace Digikam +{ +class CurvesWidget; +class HistogramWidget; +class ColorGradientWidget; +class EditorToolSettings; +class ImageWidget; +class DImg; +class DColor; +} + +namespace DigikamAdjustCurvesImagesPlugin +{ + +class AdjustCurvesTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + AdjustCurvesTool(TQObject *parent); + ~AdjustCurvesTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotEffect(); + void slotResetSettings(); + void slotResetCurrentChannel(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotCurveTypeChanged(int type); + void slotSpotColorChanged(const Digikam::DColor& color); + void slotColorSelectedFromTarget(const Digikam::DColor& color); + void slotPickerColorButtonActived(); + +private: + + enum ColorPicker + { + BlackTonal=0, + GrayTonal, + WhiteTonal + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel + }; + + enum CurvesDrawingType + { + SmoothDrawing=0, + FreeDrawing + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + uchar *m_destinationPreviewData; + + int m_histoSegments; + int m_currentPreviewMode; + + TQComboBox *m_channelCB; + + TQPushButton *m_resetButton; + TQPushButton *m_pickBlack; + TQPushButton *m_pickGray; + TQPushButton *m_pickWhite; + TQPushButton *m_curveFree; + TQPushButton *m_curveSmooth; + + TQHButtonGroup *m_pickerColorButtonGroup; + TQHButtonGroup *m_scaleBG; + TQHButtonGroup *m_curveType; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ColorGradientWidget *m_hGradient; + Digikam::ColorGradientWidget *m_vGradient; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; + + Digikam::DImg *m_originalImage; +}; + +} // NameSpace DigikamAdjustCurvesImagesPlugin + +#endif /* ADJUSTCURVESTOOL_H */ diff --git a/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves.desktop b/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves.desktop new file mode 100644 index 00000000..377c2a33 --- /dev/null +++ b/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_AdjustCurves +Name[bg]=ПриÑтавка за Ñнимки - ÐаÑтройка на кривите +Name[da]=Billedplugin_Kurvejustering +Name[el]=ΠÏόσθετοΕικόνας_ΠÏοσαÏμογήΚαμπÏλων +Name[fi]=TasonsäätöKäyrä +Name[hr]=PodeÅ¡avanje krivulja +Name[it]=PluginImmagini_RegolaCurve +Name[nl]=Afbeeldingsplugin_CurvesAanpassen +Name[sr]=Подешавање кривих +Name[sr@Latn]=PodeÅ¡avanje krivih +Name[sv]=Insticksprogram för justering av kurvor +Name[tr]=ResimEklentisi_EÄŸriAyarla +Name[xx]=xxImagePlugin_AdjustCurvesxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 + +Comment=Image histogram adjust curves plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наÑтройка кривите на хиÑтограмите на Ñнимки +Comment[ca]=Connector pel digiKam d'ajust de les corbes de l'histograma d'imatges +Comment[da]=Plugin til histogramkurvejustering i Digikam +Comment[de]=digiKam-Modul zur Justierung der Farbkurven +Comment[el]=ΠÏόσθετο Ï€ÏοσαÏμογής των καμπÏλων του ιστογÏάμματος εικόνας για το digiKam +Comment[es]=Histograma de imágenes, plugin de ajuste de curvas para digiKam +Comment[et]=DigiKami pildi histogrammi kõverate kohendamise plugin +Comment[fa]=وصلۀ منحنیهای تنظیم سابقه‌نمای تصویر برای digiKam +Comment[fi]=Muokkaa värikanavien raja-arvoja +Comment[fr]=Module externe pour ajuster les courbes de l'histogramme dans digiKam +Comment[gl]=Un plugin de digiKam para o axuste das curvas do histograma da imaxe +Comment[hr]=digiKam dodatak za histogramsko podeÅ¡avanje krivulja +Comment[is]=Ãforrit fyrir digiKam sem breytir ferlum (curves) í stuðlariti myndar +Comment[it]=Plugin di regolazione delle curve degli istogrammi delle immagini per digiKam +Comment[ja]=digiKam カーブ補正プラグイン +Comment[nds]=digiKam-Moduul för't Topassen vun Histogramm-Klöörbagens +Comment[nl]=Digikam-plugin voor curvesaanpassing van afbeeldingshistogram +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਿੱਤਰ ਹਿਸਟੋਗਰਾਮ ਅਨà©à¨•à©‚ਲ ਚਾਪ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam dostosowujÄ…ca krzywe i histogram dla obrazu +Comment[pt]=Um 'plugin' do digiKam de ajuste de curvas do histograma da imagem +Comment[pt_BR]=Plugin de ajuste de curvas do histograma da imagem +Comment[ru]=Модуль digiKam подÑтройки кривых гиÑтограммы Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +Comment[sk]=digiKam plugin histogramu kriviek úprav obrázku +Comment[sr]=digiKam-ов прикључак за подешавање кривих хиÑтограма Ñлике +Comment[sr@Latn]=digiKam-ov prikljuÄak za podeÅ¡avanje krivih histograma slike +Comment[sv]=Digikam insticksprogram för justering av kurvor i bildhistogram +Comment[tr]=digiKam için resim histogram eÄŸrileri ayarlama eklentisi +Comment[uk]=Втулок ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÑ€Ð¸Ð²Ð¸Ñ… гіÑтограми зображень Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung biểu đồ tần xuất Ä‘iá»u chỉnh Ä‘Æ°á»ng cong ảnh cho digiKam +Comment[xx]=xxImage histogram adjust curves plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_adjustcurves +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves_ui.rc b/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves_ui.rc new file mode 100644 index 00000000..c87c2c1e --- /dev/null +++ b/src/imageplugins/adjustcurves/digikamimageplugin_adjustcurves_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Color + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/adjustcurves/imageplugin_adjustcurves.cpp b/src/imageplugins/adjustcurves/imageplugin_adjustcurves.cpp new file mode 100644 index 00000000..11b6e53f --- /dev/null +++ b/src/imageplugins/adjustcurves/imageplugin_adjustcurves.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "adjustcurvestool.h" +#include "imageplugin_adjustcurves.h" +#include "imageplugin_adjustcurves.moc" + +using namespace DigikamAdjustCurvesImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY( digikamimageplugin_adjustcurves, + KGenericFactory("digikamimageplugin_adjustcurves")); + +ImagePlugin_AdjustCurves::ImagePlugin_AdjustCurves(TQObject *parent, const char*, const TQStringList&) + : Digikam::ImagePlugin(parent, "ImagePlugin_AdjustCurves") +{ + m_curvesAction = new TDEAction(i18n("Curves Adjust..."), "adjustcurves", + CTRL+SHIFT+Key_M, // NOTE: Photoshop 7 use CTRL+M (but it's used in KDE to toogle menu bar). + this, TQ_SLOT(slotCurvesAdjust()), + actionCollection(), "imageplugin_adjustcurves"); + + setXMLFile("digikamimageplugin_adjustcurves_ui.rc"); + + DDebug() << "ImagePlugin_AdjustCurves plugin loaded" << endl; +} + +ImagePlugin_AdjustCurves::~ImagePlugin_AdjustCurves() +{ +} + +void ImagePlugin_AdjustCurves::setEnabledActions(bool enable) +{ + m_curvesAction->setEnabled(enable); +} + +void ImagePlugin_AdjustCurves::slotCurvesAdjust() +{ + AdjustCurvesTool *curves = new AdjustCurvesTool(this); + loadTool(curves); +} diff --git a/src/imageplugins/adjustcurves/imageplugin_adjustcurves.h b/src/imageplugins/adjustcurves/imageplugin_adjustcurves.h new file mode 100644 index 00000000..f8e43578 --- /dev/null +++ b/src/imageplugins/adjustcurves/imageplugin_adjustcurves.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image histogram adjust curves. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_ADJUSTCURVES_H +#define IMAGEPLUGIN_ADJUSTCURVES_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_AdjustCurves : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_AdjustCurves(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_AdjustCurves(); + + void setEnabledActions(bool enable); + +private slots: + + void slotCurvesAdjust(); + +private: + + TDEAction *m_curvesAction; +}; + +#endif /* IMAGEPLUGIN_ADJUSTCURVES_H */ diff --git a/src/imageplugins/adjustlevels/Makefile.am b/src/imageplugins/adjustlevels/Makefile.am new file mode 100644 index 00000000..2b04f4cc --- /dev/null +++ b/src/imageplugins/adjustlevels/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_adjustlevels_la_SOURCES = imageplugin_adjustlevels.cpp \ + adjustlevelstool.cpp + +digikamimageplugin_adjustlevels_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_adjustlevels_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_adjustlevels.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_adjustlevels.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_adjustlevels_ui.rc + diff --git a/src/imageplugins/adjustlevels/adjustlevels.cpp b/src/imageplugins/adjustlevels/adjustlevels.cpp new file mode 100644 index 00000000..72d5e46b --- /dev/null +++ b/src/imageplugins/adjustlevels/adjustlevels.cpp @@ -0,0 +1,913 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-20 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "imagelevels.h" +#include "histogramwidget.h" +#include "dimgimagefilters.h" +#include "adjustlevels.h" +#include "adjustlevels.moc" + +namespace DigikamAdjustLevelsImagesPlugin +{ + +AdjustLevelDialog::AdjustLevelDialog(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Adjust Color Levels"), + "adjustlevels", true, false) +{ + m_destinationPreviewData = 0L; + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + m_originalImage = Digikam::DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + + m_histoSegments = m_originalImage.sixteenBit() ? 65535 : 255; + m_levels = new Digikam::ImageLevels(m_originalImage.sixteenBit()); + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Adjust Color Levels"), + digikam_version, + I18N_NOOP("An image-histogram-levels adjustment plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("adjustlevels Tool Dialog", plainPage(), + i18n("

    Here you can see the image's " + "level-adjustments preview. You can pick a spot on the image " + "to see the corresponding level in the histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* grid = new TQGridLayout(gboxSettings, 16, 8, spacingHint(), 0); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->insertItem( i18n("Alpha") ); + m_channelCB->setCurrentText( i18n("Luminosity") ); + TQWhatsThis::add( m_channelCB, i18n("

    Here select the histogram channel to display:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    " + "Alpha: display the alpha image-channel values. " + "This channel corresponds to the transparency value and " + "is supported by some image formats, such as PNG or TIF.")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Here select the histogram scale.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "The Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + m_histogramWidget = new Digikam::HistogramWidget(256, 140, gboxSettings, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing of the " + "selected image channel. This one is re-computed at any levels " + "settings changes.")); + + m_levelsHistogramWidget = new Digikam::HistogramWidget(256, 140, m_originalImage.bits(), m_originalImage.width(), + m_originalImage.height(), m_originalImage.sixteenBit(), gboxSettings, false); + TQWhatsThis::add( m_levelsHistogramWidget, i18n("

    This is the histogram drawing of the selected channel " + "from original image")); + + // ------------------------------------------------------------- + + m_hGradientMinInput = new KGradientSelector( TDESelector::Horizontal, gboxSettings ); + m_hGradientMinInput->setFixedHeight( 20 ); + m_hGradientMinInput->setMinValue(0); + m_hGradientMinInput->setMaxValue(m_histoSegments); + TQWhatsThis::add( m_hGradientMinInput, i18n("

    Select the minimal intensity input value of the histogram.")); + TQToolTip::add( m_hGradientMinInput, i18n( "Minimal intensity input." ) ); + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinInput->installEventFilter(this); + + m_hGradientMaxInput = new KGradientSelector( TDESelector::Horizontal, gboxSettings ); + m_hGradientMaxInput->setFixedHeight( 20 ); + m_hGradientMaxInput->setMinValue(0); + m_hGradientMaxInput->setMaxValue(m_histoSegments); + TQWhatsThis::add( m_hGradientMaxInput, i18n("

    Select the maximal intensity input value of the histogram.")); + TQToolTip::add( m_hGradientMaxInput, i18n( "Maximal intensity input." ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->installEventFilter(this); + + m_minInput = new TQSpinBox(0, m_histoSegments, 1, gboxSettings); + m_minInput->setValue(0); + TQWhatsThis::add( m_minInput, i18n("

    Select the minimal intensity input value of the histogram.")); + TQToolTip::add( m_minInput, i18n( "Minimal intensity input." ) ); + + m_gammaInput = new KDoubleNumInput(gboxSettings); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01); + m_gammaInput->setValue(1.0); + TQToolTip::add( m_gammaInput, i18n( "Gamma input value." ) ); + TQWhatsThis::add( m_gammaInput, i18n("

    Select the gamma input value.")); + + m_maxInput = new TQSpinBox(0, m_histoSegments, 1, gboxSettings); + m_maxInput->setValue(m_histoSegments); + TQToolTip::add( m_maxInput, i18n( "Maximal intensity input." ) ); + TQWhatsThis::add( m_maxInput, i18n("

    Select the maximal intensity input value of the histogram.")); + + m_hGradientMinOutput = new KGradientSelector( TDESelector::Horizontal, gboxSettings ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + TQWhatsThis::add( m_hGradientMinOutput, i18n("

    Select the minimal intensity output value of the histogram.")); + TQToolTip::add( m_hGradientMinOutput, i18n( "Minimal intensity output." ) ); + m_hGradientMinOutput->setFixedHeight( 20 ); + m_hGradientMinOutput->setMinValue(0); + m_hGradientMinOutput->setMaxValue(m_histoSegments); + m_hGradientMinOutput->installEventFilter(this); + + m_hGradientMaxOutput = new KGradientSelector( TDESelector::Horizontal, gboxSettings ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + TQWhatsThis::add( m_hGradientMaxOutput, i18n("

    Select the maximal intensity output value of the histogram.")); + TQToolTip::add( m_hGradientMaxOutput, i18n( "Maximal intensity output." ) ); + m_hGradientMaxOutput->setFixedHeight( 20 ); + m_hGradientMaxOutput->setMinValue(0); + m_hGradientMaxOutput->setMaxValue(m_histoSegments); + m_hGradientMaxOutput->installEventFilter(this); + + m_minOutput = new TQSpinBox(0, m_histoSegments, 1, gboxSettings); + m_minOutput->setValue(0); + TQToolTip::add( m_minOutput, i18n( "Minimal intensity output." ) ); + TQWhatsThis::add( m_minOutput, i18n("

    Select the minimal intensity output value of the histogram.")); + + m_maxOutput = new TQSpinBox(0, m_histoSegments, 1, gboxSettings); + m_maxOutput->setValue(m_histoSegments); + TQToolTip::add( m_maxOutput, i18n( "Maximal intensity output." ) ); + TQWhatsThis::add( m_maxOutput, i18n("

    Select the maximal intensity output value of the histogram.")); + + // ------------------------------------------------------------- + + m_pickerColorButtonGroup = new TQHButtonGroup(gboxSettings); + m_pickBlack = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickBlack, BlackTonal); + TDEGlobal::dirs()->addResourceType("color-picker-black", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-black", "color-picker-black.png"); + m_pickBlack->setPixmap( TQPixmap( directory + "color-picker-black.png" ) ); + m_pickBlack->setToggleButton(true); + TQToolTip::add( m_pickBlack, i18n( "All channels shadow tone color picker" ) ); + TQWhatsThis::add( m_pickBlack, i18n("

    With this button, you can pick the color from original image used to set Shadow Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickGray = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickGray, GrayTonal); + TDEGlobal::dirs()->addResourceType("color-picker-gray", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-gray", "color-picker-gray.png"); + m_pickGray->setPixmap( TQPixmap( directory + "color-picker-gray.png" ) ); + m_pickGray->setToggleButton(true); + TQToolTip::add( m_pickGray, i18n( "All channels middle tone color picker" ) ); + TQWhatsThis::add( m_pickGray, i18n("

    With this button, you can pick the color from original image used to set Middle Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickWhite = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickWhite, WhiteTonal); + TDEGlobal::dirs()->addResourceType("color-picker-white", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-white", "color-picker-white.png"); + m_pickWhite->setPixmap( TQPixmap( directory + "color-picker-white.png" ) ); + m_pickWhite->setToggleButton(true); + TQToolTip::add( m_pickWhite, i18n( "All channels highlight tone color picker" ) ); + TQWhatsThis::add( m_pickWhite, i18n("

    With this button, you can pick the color from original image used to set Highlight Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickerColorButtonGroup->setExclusive(true); + m_pickerColorButtonGroup->setFrameShape(TQFrame::NoFrame); + + m_autoButton = new TQPushButton(gboxSettings); + m_autoButton->setPixmap(kapp->iconLoader()->loadIcon("system-run", (TDEIcon::Group)TDEIcon::Toolbar)); TQToolTip::add( m_autoButton, i18n( "Adjust all levels automatically." ) ); + TQWhatsThis::add( m_autoButton, i18n("

    If you press this button, all channel levels will be adjusted " + "automatically.")); + + m_resetButton = new TQPushButton(i18n("&Reset"), gboxSettings); + m_resetButton->setPixmap(kapp->iconLoader()->loadIcon("reload_page", (TDEIcon::Group)TDEIcon::Toolbar)); + TQToolTip::add( m_resetButton, i18n( "Reset current channel levels' values." ) ); + TQWhatsThis::add( m_resetButton, i18n("

    If you press this button, all levels' values " + "from the current selected channel " + "will be reset to the default values.")); + + TQLabel *space = new TQLabel(gboxSettings); + space->setFixedWidth(spacingHint()); + + TQHBoxLayout* l3 = new TQHBoxLayout(); + l3->addWidget(m_pickerColorButtonGroup); + l3->addWidget(m_autoButton); + l3->addWidget(space); + l3->addWidget(m_resetButton); + l3->addStretch(10); + + // ------------------------------------------------------------- + + grid->addMultiCellLayout(l1, 0, 0, 0, 6); + grid->setRowSpacing(1, spacingHint()); + grid->addMultiCellWidget(m_histogramWidget, 2, 2, 1, 5); + grid->setRowSpacing(3, spacingHint()); + grid->addMultiCellWidget(m_levelsHistogramWidget, 4, 4, 1, 5); + grid->addMultiCellWidget(m_hGradientMinInput, 5, 5, 0, 6); + grid->addMultiCellWidget(m_minInput, 5, 5, 8, 8); + grid->setRowSpacing(6, spacingHint()); + grid->addMultiCellWidget(m_hGradientMaxInput, 7, 7, 0, 6); + grid->addMultiCellWidget(m_maxInput, 7, 7, 8, 8); + grid->setRowSpacing(8, spacingHint()); + grid->addMultiCellWidget(m_gammaInput, 9, 9, 0, 8); + grid->setRowSpacing(10, spacingHint()); + grid->addMultiCellWidget(m_hGradientMinOutput, 11, 11, 0, 6); + grid->addMultiCellWidget(m_minOutput, 11, 11, 8, 8); + grid->setRowSpacing(12, spacingHint()); + grid->addMultiCellWidget(m_hGradientMaxOutput, 13, 13, 0, 6); + grid->addMultiCellWidget(m_maxOutput, 13, 13, 8, 8); + grid->setRowSpacing(14, spacingHint()); + grid->addMultiCellLayout(l3, 15, 15, 0, 8); + grid->setRowStretch(16, 10); + grid->setColStretch(2, 10); + grid->setColSpacing(0, 5); + grid->setColSpacing(6, 5); + grid->setColSpacing(7, spacingHint()); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + // Channels and scale selection slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotSpotColorChanged( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Color sliders and spinbox slots. + + connect(m_hGradientMinInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMinInputSpinBox(int))); + + connect(m_minInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotGammaInputchanged(double))); + + connect(m_hGradientMaxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMaxInputSpinBox(int))); + + connect(m_maxInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_hGradientMinOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMinOutputSpinBox(int))); + + connect(m_minOutput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_hGradientMaxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMaxOutputSpinBox(int))); + + connect(m_maxOutput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotAdjustSliders())); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_autoButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAutoLevels())); + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); + + connect(m_pickerColorButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotPickerColorButtonActived())); + +} + +AdjustLevelDialog::~AdjustLevelDialog() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_levelsHistogramWidget; + delete m_levels; +} + +void AdjustLevelDialog::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(Digikam::ImageGuideWidget::PreviewOriginalImage); +} + +void AdjustLevelDialog::slotSpotColorChanged(const Digikam::DColor &color) +{ + if ( m_pickBlack->isOn() ) + { + // Black tonal levels point. + m_levels->levelsBlackToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickBlack->setOn(false); + } + else if ( m_pickGray->isOn() ) + { + // Gray tonal levels point. + m_levels->levelsGrayToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickGray->setOn(false); + } + else if ( m_pickWhite->isOn() ) + { + // White tonal levels point. + m_levels->levelsWhiteToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickWhite->setOn(false); + } + else + { + m_levelsHistogramWidget->setHistogramGuideByColor(color); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void AdjustLevelDialog::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void AdjustLevelDialog::slotGammaInputchanged(double val) +{ + blockSignals(true); + m_levels->setLevelGammaValue(m_channelCB->currentItem(), val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelDialog::slotAdjustMinInputSpinBox(int val) +{ + blockSignals(true); + + if ( val < m_hGradientMaxInput->value() ) + val = m_hGradientMaxInput->value(); + + m_minInput->setValue(m_histoSegments - val); + m_hGradientMinInput->setValue( val ); + m_levels->setLevelLowInputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelDialog::slotAdjustMaxInputSpinBox(int val) +{ + blockSignals(true); + + if ( val > m_hGradientMinInput->value() ) + val = m_hGradientMinInput->value(); + + m_maxInput->setValue(m_histoSegments - val); + m_hGradientMaxInput->setValue( val ); + m_levels->setLevelHighInputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelDialog::slotAdjustMinOutputSpinBox(int val) +{ + blockSignals(true); + + if ( val < m_hGradientMaxOutput->value() ) + val = m_hGradientMaxOutput->value(); + + m_minOutput->setValue(m_histoSegments - val); + m_hGradientMinOutput->setValue( val ); + m_levels->setLevelLowOutputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelDialog::slotAdjustMaxOutputSpinBox(int val) +{ + blockSignals(true); + + if ( val > m_hGradientMinOutput->value() ) + val = m_hGradientMinOutput->value(); + + m_maxOutput->setValue(m_histoSegments - val); + m_hGradientMaxOutput->setValue( val ); + m_levels->setLevelHighOutputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelDialog::slotAdjustSliders() +{ + adjustSliders(m_minInput->value(), m_gammaInput->value(), + m_maxInput->value(), m_minOutput->value(), + m_maxOutput->value()); +} + +void AdjustLevelDialog::adjustSliders(int minIn, double gamIn, int maxIn, int minOut, int maxOut) +{ + m_hGradientMinInput->setValue(m_histoSegments - minIn); + m_hGradientMaxInput->setValue(m_histoSegments - maxIn); + m_gammaInput->setValue(gamIn); + m_hGradientMinOutput->setValue(m_histoSegments - minOut); + m_hGradientMaxOutput->setValue(m_histoSegments - maxOut); +} + +void AdjustLevelDialog::slotResetCurrentChannel() +{ + m_levels->levelsChannelReset(m_channelCB->currentItem()); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + m_levelsHistogramWidget->reset(); + + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustLevelDialog::slotAutoLevels() +{ + // Calculate Auto levels. + m_levels->levelsAuto(m_levelsHistogramWidget->m_imageHistogram); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + + slotEffect(); +} + +void AdjustLevelDialog::slotEffect() +{ + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_levels->levelsLutSetup(Digikam::ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_levels->levelsLutProcess(orgData, m_destinationPreviewData, w, h); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + delete [] orgData; +} + +void AdjustLevelDialog::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + // Create the new empty destination image data space. + uchar* desData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_levels->levelsLutSetup(Digikam::ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_levels->levelsLutProcess(orgData, desData, w, h); + + iface->putOriginalImage(i18n("Adjust Level"), desData); + kapp->restoreOverrideCursor(); + + delete [] orgData; + delete [] desData; + accept(); +} + +void AdjustLevelDialog::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_levelsHistogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_levelsHistogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_levelsHistogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_levelsHistogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + + case AlphaChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::AlphaChannelHistogram; + m_levelsHistogramWidget->m_channelType = Digikam::HistogramWidget::AlphaChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + } + + adjustSliders(m_levels->getLevelLowInputValue(channel), + m_levels->getLevelGammaValue(channel), + m_levels->getLevelHighInputValue(channel), + m_levels->getLevelLowOutputValue(channel), + m_levels->getLevelHighOutputValue(channel)); + + m_levelsHistogramWidget->repaint(false); + m_histogramWidget->repaint(false); +} + +void AdjustLevelDialog::slotScaleChanged(int scale) +{ + m_levelsHistogramWidget->m_scaleType = scale; + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_levelsHistogramWidget->repaint(false); +} + +void AdjustLevelDialog::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustlevels Tool Dialog"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + + for (int i = 0 ; i < 5 ; i++) + { + bool sb = m_originalImage.sixteenBit(); + int max = sb ? 65535 : 255; + double gamma = config->readDoubleNumEntry(TQString("GammaChannel%1").arg(i), 1.0); + int lowInput = config->readNumEntry(TQString("LowInputChannel%1").arg(i), 0); + int lowOutput = config->readNumEntry(TQString("LowOutputChannel%1").arg(i), 0); + int highInput = config->readNumEntry(TQString("HighInputChannel%1").arg(i), max); + int highOutput = config->readNumEntry(TQString("HighOutputChannel%1").arg(i), max); + + m_levels->setLevelGammaValue(i, gamma); + m_levels->setLevelLowInputValue(i, sb ? lowInput*255 : lowInput); + m_levels->setLevelHighInputValue(i, sb ? highInput*255 : highInput); + m_levels->setLevelLowOutputValue(i, sb ? lowOutput*255 : lowOutput); + m_levels->setLevelHighOutputValue(i, sb ? highOutput*255 : highOutput); + } + + m_levelsHistogramWidget->reset(); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); + + // This is mandatory here to set spinbox values because slot connections + // can be not set completely at plugin startup. + m_minInput->setValue(m_levels->getLevelLowInputValue(m_channelCB->currentItem())); + m_minOutput->setValue(m_levels->getLevelLowOutputValue(m_channelCB->currentItem())); + m_maxInput->setValue(m_levels->getLevelHighInputValue(m_channelCB->currentItem())); + m_maxOutput->setValue(m_levels->getLevelHighOutputValue(m_channelCB->currentItem())); +} + +void AdjustLevelDialog::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustlevels Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + for (int i = 0 ; i < 5 ; i++) + { + bool sb = m_originalImage.sixteenBit(); + double gamma = m_levels->getLevelGammaValue(i); + int lowInput = m_levels->getLevelLowInputValue(i); + int lowOutput = m_levels->getLevelLowOutputValue(i); + int highInput = m_levels->getLevelHighInputValue(i); + int highOutput = m_levels->getLevelHighOutputValue(i); + + config->writeEntry(TQString("GammaChannel%1").arg(i), gamma); + config->writeEntry(TQString("LowInputChannel%1").arg(i), sb ? lowInput/255 : lowInput); + config->writeEntry(TQString("LowOutputChannel%1").arg(i), sb ? lowOutput/255 : lowOutput); + config->writeEntry(TQString("HighInputChannel%1").arg(i), sb ? highInput/255 : highInput); + config->writeEntry(TQString("HighOutputChannel%1").arg(i), sb ? highOutput/255 : highOutput); + } + + config->sync(); +} + +void AdjustLevelDialog::resetValues() +{ + for (int channel = 0 ; channel < 5 ; ++channel) + m_levels->levelsChannelReset(channel); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + m_levelsHistogramWidget->reset(); + m_histogramWidget->reset(); +} + +// Load all settings. +void AdjustLevelDialog::slotUser3() +{ + KURL loadLevelsFile; + + loadLevelsFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Select Gimp Levels File to Load")) ); + if( loadLevelsFile.isEmpty() ) + return; + + if ( m_levels->loadLevelsFromGimpLevelsFile( loadLevelsFile ) == false ) + { + KMessageBox::error(this, i18n("Cannot load from the Gimp levels text file.")); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); +} + +// Save all settings. +void AdjustLevelDialog::slotUser2() +{ + KURL saveLevelsFile; + + saveLevelsFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Gimp Levels File to Save")) ); + if( saveLevelsFile.isEmpty() ) + return; + + if ( m_levels->saveLevelsToGimpLevelsFile( saveLevelsFile ) == false ) + { + KMessageBox::error(this, i18n("Cannot save to the Gimp levels text file.")); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); +} + +// See B.K.O #146636: use event filter with all level slider to display a +// guide over level histogram. +bool AdjustLevelDialog::eventFilter(TQObject *obj, TQEvent *ev) +{ + if ( obj == m_hGradientMinInput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_minInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_minInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMaxInput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_maxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_maxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMinOutput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_minOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_minOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMaxOutput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_maxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_maxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + else + { + // pass the event on to the parent class + return KDialogBase::eventFilter(obj, ev); + } +} + +void AdjustLevelDialog::slotShowHistogramGuide(int v) +{ + Digikam::DColor color(v, v, v, v, m_originalImage.sixteenBit()); + m_levelsHistogramWidget->setHistogramGuideByColor(color); +} + +} // NameSpace DigikamAdjustLevelsImagesPlugin diff --git a/src/imageplugins/adjustlevels/adjustlevels.h b/src/imageplugins/adjustlevels/adjustlevels.h new file mode 100644 index 00000000..1b1ad9be --- /dev/null +++ b/src/imageplugins/adjustlevels/adjustlevels.h @@ -0,0 +1,153 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-20 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ADJUSTLEVELS_H +#define ADJUSTLEVELS_H + +// Digikam includes. + +#include "imagedlgbase.h" +#include "dimg.h" + +class TQComboBox; +class TQSpinBox; +class TQPushButton; +class TQHButtonGroup; + +class KDoubleSpinBox; +class KGradientSelector; +class KDoubleNumInput; + +namespace Digikam +{ +class HistogramWidget; +class ImageWidget; +class ImageLevels; +} + +namespace DigikamAdjustLevelsImagesPlugin +{ + +class AdjustLevelDialog : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + AdjustLevelDialog(TQWidget *parent); + ~AdjustLevelDialog(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + void adjustSliders(int minIn, double gamIn, int maxIn, int minOut, int maxOut); + bool eventFilter(TQObject *o, TQEvent *e); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotEffect(); + void slotResetCurrentChannel(); + void slotAutoLevels(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotAdjustSliders(); + void slotGammaInputchanged(double val); + void slotAdjustMinInputSpinBox(int val); + void slotAdjustMaxInputSpinBox(int val); + void slotAdjustMinOutputSpinBox(int val); + void slotAdjustMaxOutputSpinBox(int val); + void slotSpotColorChanged(const Digikam::DColor &color); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotPickerColorButtonActived(); + void slotShowHistogramGuide(int v); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel + }; + + enum ColorPicker + { + BlackTonal=0, + GrayTonal, + WhiteTonal + }; + + uchar *m_destinationPreviewData; + + int m_histoSegments; + int m_currentPreviewMode; + + TQComboBox *m_channelCB; + + TQSpinBox *m_minInput; + TQSpinBox *m_maxInput; + TQSpinBox *m_minOutput; + TQSpinBox *m_maxOutput; + + TQPushButton *m_autoButton; + TQPushButton *m_resetButton; + TQPushButton *m_pickBlack; + TQPushButton *m_pickGray; + TQPushButton *m_pickWhite; + + TQHButtonGroup *m_pickerColorButtonGroup; + TQHButtonGroup *m_scaleBG; + + KDoubleNumInput *m_gammaInput; + + KGradientSelector *m_hGradientMinInput; + KGradientSelector *m_hGradientMaxInput; + KGradientSelector *m_hGradientMinOutput; + KGradientSelector *m_hGradientMaxOutput; + + Digikam::HistogramWidget *m_levelsHistogramWidget; + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ImageLevels *m_levels; + Digikam::DImg m_originalImage; +}; + +} // NameSpace DigikamAdjustLevelsImagesPlugin + +#endif /* ADJUSTLEVELS_H */ diff --git a/src/imageplugins/adjustlevels/adjustlevelstool.cpp b/src/imageplugins/adjustlevels/adjustlevelstool.cpp new file mode 100644 index 00000000..28066774 --- /dev/null +++ b/src/imageplugins/adjustlevels/adjustlevelstool.cpp @@ -0,0 +1,901 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-20 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "imagelevels.h" +#include "histogramwidget.h" +#include "dimgimagefilters.h" +#include "adjustlevelstool.h" +#include "adjustlevelstool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamAdjustLevelsImagesPlugin +{ + +AdjustLevelsTool::AdjustLevelsTool(TQObject* parent) + : EditorTool(parent) +{ + m_destinationPreviewData = 0; + + ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + + m_histoSegments = m_originalImage->sixteenBit() ? 65535 : 255; + m_levels = new ImageLevels(m_originalImage->sixteenBit()); + + setName("adjustlevels"); + setToolName(i18n("Adjust Levels")); + setToolIcon(SmallIcon("adjustlevels")); + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("adjustlevels Tool", 0, + i18n("

    Here you can see the image's " + "level-adjustments preview. You can pick a spot on the image " + "to see the corresponding level in the histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 20, 6); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, m_gboxSettings->plainPage() ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->insertItem( i18n("Alpha") ); + m_channelCB->setCurrentText( i18n("Luminosity") ); + TQWhatsThis::add( m_channelCB, i18n("

    Here select the histogram channel to display:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    " + "Alpha: display the alpha image-channel values. " + "This channel corresponds to the transparency value and " + "is supported by some image formats, such as PNG or TIF.")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Here select the histogram scale.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "The Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + m_histogramWidget = new HistogramWidget(256, 140, m_gboxSettings->plainPage(), false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing of the " + "selected image channel. This one is re-computed at any levels " + "settings changes.")); + + m_levelsHistogramWidget = new HistogramWidget(256, 140, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), m_gboxSettings, false); + TQWhatsThis::add( m_levelsHistogramWidget, i18n("

    This is the histogram drawing of the selected channel " + "from original image")); + + // ------------------------------------------------------------- + + m_hGradientMinInput = new KGradientSelector( TDESelector::Horizontal, m_gboxSettings->plainPage() ); + m_hGradientMinInput->setFixedHeight( 20 ); + m_hGradientMinInput->setMinValue(0); + m_hGradientMinInput->setMaxValue(m_histoSegments); + TQWhatsThis::add( m_hGradientMinInput, i18n("

    Select the minimal intensity input value of the histogram.")); + TQToolTip::add( m_hGradientMinInput, i18n( "Minimal intensity input." ) ); + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinInput->installEventFilter(this); + + m_hGradientMaxInput = new KGradientSelector( TDESelector::Horizontal, m_gboxSettings->plainPage() ); + m_hGradientMaxInput->setFixedHeight( 20 ); + m_hGradientMaxInput->setMinValue(0); + m_hGradientMaxInput->setMaxValue(m_histoSegments); + TQWhatsThis::add( m_hGradientMaxInput, i18n("

    Select the maximal intensity input value of the histogram.")); + TQToolTip::add( m_hGradientMaxInput, i18n( "Maximal intensity input." ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->installEventFilter(this); + + m_minInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_minInput->input()->setRange(0, m_histoSegments, 1, false); + m_minInput->setDefaultValue(0); + TQWhatsThis::add( m_minInput, i18n("

    Select the minimal intensity input value of the histogram.")); + TQToolTip::add( m_minInput, i18n( "Minimal intensity input." ) ); + + m_gammaInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01, true); + m_gammaInput->setDefaultValue(1.0); + TQToolTip::add( m_gammaInput, i18n( "Gamma input value." ) ); + TQWhatsThis::add( m_gammaInput, i18n("

    Select the gamma input value.")); + + m_maxInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_maxInput->input()->setRange(0, m_histoSegments, 1, false); + m_maxInput->setDefaultValue(m_histoSegments); + TQToolTip::add( m_maxInput, i18n( "Maximal intensity input." ) ); + TQWhatsThis::add( m_maxInput, i18n("

    Select the maximal intensity input value of the histogram.")); + + m_hGradientMinOutput = new KGradientSelector( TDESelector::Horizontal, m_gboxSettings->plainPage() ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + TQWhatsThis::add( m_hGradientMinOutput, i18n("

    Select the minimal intensity output value of the histogram.")); + TQToolTip::add( m_hGradientMinOutput, i18n( "Minimal intensity output." ) ); + m_hGradientMinOutput->setFixedHeight( 20 ); + m_hGradientMinOutput->setMinValue(0); + m_hGradientMinOutput->setMaxValue(m_histoSegments); + m_hGradientMinOutput->installEventFilter(this); + + m_hGradientMaxOutput = new KGradientSelector( TDESelector::Horizontal, m_gboxSettings->plainPage() ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + TQWhatsThis::add( m_hGradientMaxOutput, i18n("

    Select the maximal intensity output value of the histogram.")); + TQToolTip::add( m_hGradientMaxOutput, i18n( "Maximal intensity output." ) ); + m_hGradientMaxOutput->setFixedHeight( 20 ); + m_hGradientMaxOutput->setMinValue(0); + m_hGradientMaxOutput->setMaxValue(m_histoSegments); + m_hGradientMaxOutput->installEventFilter(this); + + m_minOutput = new RIntNumInput(m_gboxSettings->plainPage()); + m_minOutput->input()->setRange(0, m_histoSegments, 1, false); + m_minOutput->setDefaultValue(0); + TQToolTip::add( m_minOutput, i18n( "Minimal intensity output." ) ); + TQWhatsThis::add( m_minOutput, i18n("

    Select the minimal intensity output value of the histogram.")); + + m_maxOutput = new RIntNumInput(m_gboxSettings->plainPage()); + m_maxOutput->input()->setRange(0, m_histoSegments, 1, false); + m_maxOutput->setDefaultValue(m_histoSegments); + TQToolTip::add( m_maxOutput, i18n( "Maximal intensity output." ) ); + TQWhatsThis::add( m_maxOutput, i18n("

    Select the maximal intensity output value of the histogram.")); + + // ------------------------------------------------------------- + + m_pickerColorButtonGroup = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_pickBlack = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickBlack, BlackTonal); + TDEGlobal::dirs()->addResourceType("color-picker-black", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-black", "color-picker-black.png"); + m_pickBlack->setPixmap( TQPixmap( directory + "color-picker-black.png" ) ); + m_pickBlack->setToggleButton(true); + TQToolTip::add( m_pickBlack, i18n( "All channels shadow tone color picker" ) ); + TQWhatsThis::add( m_pickBlack, i18n("

    With this button, you can pick the color from original image used to set Shadow Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickGray = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickGray, GrayTonal); + TDEGlobal::dirs()->addResourceType("color-picker-gray", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-grey", "color-picker-grey.png"); + m_pickGray->setPixmap( TQPixmap( directory + "color-picker-grey.png" ) ); + m_pickGray->setToggleButton(true); + TQToolTip::add( m_pickGray, i18n( "All channels middle tone color picker" ) ); + TQWhatsThis::add( m_pickGray, i18n("

    With this button, you can pick the color from original image used to set Middle Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickWhite = new TQPushButton(m_pickerColorButtonGroup); + m_pickerColorButtonGroup->insert(m_pickWhite, WhiteTonal); + TDEGlobal::dirs()->addResourceType("color-picker-white", TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-white", "color-picker-white.png"); + m_pickWhite->setPixmap( TQPixmap( directory + "color-picker-white.png" ) ); + m_pickWhite->setToggleButton(true); + TQToolTip::add( m_pickWhite, i18n( "All channels highlight tone color picker" ) ); + TQWhatsThis::add( m_pickWhite, i18n("

    With this button, you can pick the color from original image used to set Highlight Tone " + "levels input on Red, Green, Blue, and Luminosity channels.")); + m_pickerColorButtonGroup->setExclusive(true); + m_pickerColorButtonGroup->setFrameShape(TQFrame::NoFrame); + + m_autoButton = new TQPushButton(m_gboxSettings->plainPage()); + m_autoButton->setPixmap(kapp->iconLoader()->loadIcon("system-run", (TDEIcon::Group)TDEIcon::Toolbar)); TQToolTip::add( m_autoButton, i18n( "Adjust all levels automatically." ) ); + TQWhatsThis::add( m_autoButton, i18n("

    If you press this button, all channel levels will be adjusted " + "automatically.")); + + m_resetButton = new TQPushButton(i18n("&Reset"), m_gboxSettings->plainPage()); + m_resetButton->setPixmap(kapp->iconLoader()->loadIcon("reload_page", (TDEIcon::Group)TDEIcon::Toolbar)); + TQToolTip::add( m_resetButton, i18n( "Reset current channel levels' values." ) ); + TQWhatsThis::add( m_resetButton, i18n("

    If you press this button, all levels' values " + "from the current selected channel " + "will be reset to the default values.")); + + TQLabel *space = new TQLabel(m_gboxSettings->plainPage()); + space->setFixedWidth(m_gboxSettings->spacingHint()); + + TQHBoxLayout* l3 = new TQHBoxLayout(); + l3->addWidget(m_pickerColorButtonGroup); + l3->addWidget(m_autoButton); + l3->addWidget(space); + l3->addWidget(m_resetButton); + l3->addStretch(10); + + // ------------------------------------------------------------- + + grid->addMultiCellLayout(l1, 0, 0, 0, 6); + grid->addMultiCellWidget(m_histogramWidget, 2, 2, 1, 5); + grid->addMultiCellWidget(m_levelsHistogramWidget, 4, 4, 1, 5); + grid->addMultiCellWidget(m_hGradientMinInput, 5, 5, 0, 6); + grid->addMultiCellWidget(m_hGradientMaxInput, 7, 7, 0, 6); + grid->addMultiCellWidget(m_minInput, 9, 9, 1, 1); + grid->addMultiCellWidget(m_maxInput, 9, 9, 5, 5); + grid->addMultiCellWidget(m_gammaInput, 11, 11, 0, 6); + grid->addMultiCellWidget(m_hGradientMinOutput, 13, 13, 0, 6); + grid->addMultiCellWidget(m_hGradientMaxOutput, 15, 15, 0, 6); + grid->addMultiCellWidget(m_minOutput, 17, 17, 1, 1); + grid->addMultiCellWidget(m_maxOutput, 17, 17, 5, 5); + grid->addMultiCellLayout(l3, 19, 19, 0, 6); + grid->setRowStretch(20, 10); + grid->setColStretch(3, 10); + grid->setMargin(0); + grid->setSpacing(5); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + // Channels and scale selection slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotSpotColorChanged(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Color sliders and spinbox slots. + + connect(m_hGradientMinInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMinInputSpinBox(int))); + + connect(m_minInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGammaInputchanged(double))); + + connect(m_hGradientMaxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMaxInputSpinBox(int))); + + connect(m_maxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_hGradientMinOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMinOutputSpinBox(int))); + + connect(m_minOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustSliders())); + + connect(m_hGradientMaxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustMaxOutputSpinBox(int))); + + connect(m_maxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotAdjustSliders())); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_autoButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAutoLevels())); + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); + + connect(m_pickerColorButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotPickerColorButtonActived())); +} + +AdjustLevelsTool::~AdjustLevelsTool() +{ + delete [] m_destinationPreviewData; + delete m_levels; +} + +void AdjustLevelsTool::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(ImageGuideWidget::PreviewOriginalImage); +} + +void AdjustLevelsTool::slotSpotColorChanged(const DColor& color) +{ + if ( m_pickBlack->isOn() ) + { + // Black tonal levels point. + m_levels->levelsBlackToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickBlack->setOn(false); + } + else if ( m_pickGray->isOn() ) + { + // Gray tonal levels point. + m_levels->levelsGrayToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickGray->setOn(false); + } + else if ( m_pickWhite->isOn() ) + { + // White tonal levels point. + m_levels->levelsWhiteToneAdjustByColors(m_channelCB->currentItem(), color); + m_pickWhite->setOn(false); + } + else + { + m_levelsHistogramWidget->setHistogramGuideByColor(color); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void AdjustLevelsTool::slotColorSelectedFromTarget(const DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void AdjustLevelsTool::slotGammaInputchanged(double val) +{ + blockSignals(true); + m_levels->setLevelGammaValue(m_channelCB->currentItem(), val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelsTool::slotAdjustMinInputSpinBox(int val) +{ + blockSignals(true); + + if ( val < m_hGradientMaxInput->value() ) + val = m_hGradientMaxInput->value(); + + m_minInput->setValue(m_histoSegments - val); + m_hGradientMinInput->setValue( val ); + m_levels->setLevelLowInputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelsTool::slotAdjustMaxInputSpinBox(int val) +{ + blockSignals(true); + + if ( val > m_hGradientMinInput->value() ) + val = m_hGradientMinInput->value(); + + m_maxInput->setValue(m_histoSegments - val); + m_hGradientMaxInput->setValue( val ); + m_levels->setLevelHighInputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelsTool::slotAdjustMinOutputSpinBox(int val) +{ + blockSignals(true); + + if ( val < m_hGradientMaxOutput->value() ) + val = m_hGradientMaxOutput->value(); + + m_minOutput->setValue(m_histoSegments - val); + m_hGradientMinOutput->setValue( val ); + m_levels->setLevelLowOutputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelsTool::slotAdjustMaxOutputSpinBox(int val) +{ + blockSignals(true); + + if ( val > m_hGradientMinOutput->value() ) + val = m_hGradientMinOutput->value(); + + m_maxOutput->setValue(m_histoSegments - val); + m_hGradientMaxOutput->setValue( val ); + m_levels->setLevelHighOutputValue(m_channelCB->currentItem(), m_histoSegments - val); + blockSignals(false); + slotTimer(); +} + +void AdjustLevelsTool::slotAdjustSliders() +{ + adjustSliders(m_minInput->value(), m_gammaInput->value(), + m_maxInput->value(), m_minOutput->value(), + m_maxOutput->value()); +} + +void AdjustLevelsTool::adjustSliders(int minIn, double gamIn, int maxIn, int minOut, int maxOut) +{ + m_hGradientMinInput->setValue(m_histoSegments - minIn); + m_hGradientMaxInput->setValue(m_histoSegments - maxIn); + m_gammaInput->setValue(gamIn); + m_hGradientMinOutput->setValue(m_histoSegments - minOut); + m_hGradientMaxOutput->setValue(m_histoSegments - maxOut); +} + +void AdjustLevelsTool::slotResetCurrentChannel() +{ + m_levels->levelsChannelReset(m_channelCB->currentItem()); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + m_levelsHistogramWidget->reset(); + + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustLevelsTool::slotAutoLevels() +{ + // Calculate Auto levels. + m_levels->levelsAuto(m_levelsHistogramWidget->m_imageHistogram); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + + slotEffect(); +} + +void AdjustLevelsTool::slotEffect() +{ + ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_levels->levelsLutSetup(ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_levels->levelsLutProcess(orgData, m_destinationPreviewData, w, h); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + delete [] orgData; +} + +void AdjustLevelsTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *orgData = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + // Create the new empty destination image data space. + uchar* desData = new uchar[w*h*(sb ? 8 : 4)]; + + // Calculate the LUT to apply on the image. + m_levels->levelsLutSetup(ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + m_levels->levelsLutProcess(orgData, desData, w, h); + + iface->putOriginalImage(i18n("Adjust Level"), desData); + kapp->restoreOverrideCursor(); + + delete [] orgData; + delete [] desData; +} + +void AdjustLevelsTool::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_levelsHistogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_levelsHistogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "red" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_levelsHistogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "green" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_levelsHistogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + + case AlphaChannel: + m_histogramWidget->m_channelType = HistogramWidget::AlphaChannelHistogram; + m_levelsHistogramWidget->m_channelType = HistogramWidget::AlphaChannelHistogram; + m_hGradientMinInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxInput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMinOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + m_hGradientMaxOutput->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + } + + adjustSliders(m_levels->getLevelLowInputValue(channel), + m_levels->getLevelGammaValue(channel), + m_levels->getLevelHighInputValue(channel), + m_levels->getLevelLowOutputValue(channel), + m_levels->getLevelHighOutputValue(channel)); + + m_levelsHistogramWidget->repaint(false); + m_histogramWidget->repaint(false); +} + +void AdjustLevelsTool::slotScaleChanged(int scale) +{ + m_levelsHistogramWidget->m_scaleType = scale; + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_levelsHistogramWidget->repaint(false); +} + +void AdjustLevelsTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustlevels Tool"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + + for (int i = 0 ; i < 5 ; i++) + { + bool sb = m_originalImage->sixteenBit(); + int max = sb ? 65535 : 255; + double gamma = config->readDoubleNumEntry(TQString("GammaChannel%1").arg(i), 1.0); + int lowInput = config->readNumEntry(TQString("LowInputChannel%1").arg(i), 0); + int lowOutput = config->readNumEntry(TQString("LowOutputChannel%1").arg(i), 0); + int highInput = config->readNumEntry(TQString("HighInputChannel%1").arg(i), max); + int highOutput = config->readNumEntry(TQString("HighOutputChannel%1").arg(i), max); + + m_levels->setLevelGammaValue(i, gamma); + m_levels->setLevelLowInputValue(i, sb ? lowInput*255 : lowInput); + m_levels->setLevelHighInputValue(i, sb ? highInput*255 : highInput); + m_levels->setLevelLowOutputValue(i, sb ? lowOutput*255 : lowOutput); + m_levels->setLevelHighOutputValue(i, sb ? highOutput*255 : highOutput); + } + + m_levelsHistogramWidget->reset(); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); + + // This is mandatory here to set spinbox values because slot connections + // can be not set completely at plugin startup. + m_minInput->setValue(m_levels->getLevelLowInputValue(m_channelCB->currentItem())); + m_minOutput->setValue(m_levels->getLevelLowOutputValue(m_channelCB->currentItem())); + m_maxInput->setValue(m_levels->getLevelHighInputValue(m_channelCB->currentItem())); + m_maxOutput->setValue(m_levels->getLevelHighOutputValue(m_channelCB->currentItem())); +} + +void AdjustLevelsTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("adjustlevels Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + for (int i = 0 ; i < 5 ; i++) + { + bool sb = m_originalImage->sixteenBit(); + double gamma = m_levels->getLevelGammaValue(i); + int lowInput = m_levels->getLevelLowInputValue(i); + int lowOutput = m_levels->getLevelLowOutputValue(i); + int highInput = m_levels->getLevelHighInputValue(i); + int highOutput = m_levels->getLevelHighOutputValue(i); + + config->writeEntry(TQString("GammaChannel%1").arg(i), gamma); + config->writeEntry(TQString("LowInputChannel%1").arg(i), sb ? lowInput/255 : lowInput); + config->writeEntry(TQString("LowOutputChannel%1").arg(i), sb ? lowOutput/255 : lowOutput); + config->writeEntry(TQString("HighInputChannel%1").arg(i), sb ? highInput/255 : highInput); + config->writeEntry(TQString("HighOutputChannel%1").arg(i), sb ? highOutput/255 : highOutput); + } + + m_previewWidget->writeSettings(); + config->sync(); +} + +void AdjustLevelsTool::slotResetSettings() +{ + for (int channel = 0 ; channel < 5 ; ++channel) + m_levels->levelsChannelReset(channel); + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); + m_levelsHistogramWidget->reset(); + slotEffect(); + m_histogramWidget->reset(); +} + +void AdjustLevelsTool::slotLoadSettings() +{ + KURL loadLevelsFile; + + loadLevelsFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Select Gimp Levels File to Load")) ); + if( loadLevelsFile.isEmpty() ) + return; + + if ( m_levels->loadLevelsFromGimpLevelsFile( loadLevelsFile ) == false ) + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load from the Gimp levels text file.")); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); +} + +void AdjustLevelsTool::slotSaveAsSettings() +{ + KURL saveLevelsFile; + + saveLevelsFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Gimp Levels File to Save")) ); + if( saveLevelsFile.isEmpty() ) + return; + + if ( m_levels->saveLevelsToGimpLevelsFile( saveLevelsFile ) == false ) + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save to the Gimp levels text file.")); + return; + } + + // Refresh the current levels config. + slotChannelChanged(m_channelCB->currentItem()); +} + +// See B.K.O #146636: use event filter with all level slider to display a +// guide over level histogram. +bool AdjustLevelsTool::eventFilter(TQObject *obj, TQEvent *ev) +{ + if ( obj == m_hGradientMinInput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_minInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowInputHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_minInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowInputHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMaxInput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_maxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowInputHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_maxInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowInputHistogramGuide(int))); + + m_levelsHistogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMinOutput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_minOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowOutputHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_minOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowOutputHistogramGuide(int))); + + m_histogramWidget->reset(); + return false; + } + else + { + return false; + } + } + if ( obj == m_hGradientMaxOutput ) + { + if ( ev->type() == TQEvent::MouseButtonPress) + { + connect(m_maxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowOutputHistogramGuide(int))); + + return false; + } + if ( ev->type() == TQEvent::MouseButtonRelease) + { + disconnect(m_maxOutput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotShowOutputHistogramGuide(int))); + + m_histogramWidget->reset(); + return false; + } + else + { + return false; + } + } + else + { + // pass the event on to the parent class + return EditorTool::eventFilter(obj, ev); + } +} + +void AdjustLevelsTool::slotShowInputHistogramGuide(int v) +{ + DColor color(v, v, v, v, m_originalImage->sixteenBit()); + m_levelsHistogramWidget->setHistogramGuideByColor(color); +} + +void AdjustLevelsTool::slotShowOutputHistogramGuide(int v) +{ + DColor color(v, v, v, v, m_originalImage->sixteenBit()); + m_histogramWidget->setHistogramGuideByColor(color); +} + +} // NameSpace DigikamAdjustLevelsImagesPlugin diff --git a/src/imageplugins/adjustlevels/adjustlevelstool.h b/src/imageplugins/adjustlevels/adjustlevelstool.h new file mode 100644 index 00000000..9fdadf91 --- /dev/null +++ b/src/imageplugins/adjustlevels/adjustlevelstool.h @@ -0,0 +1,160 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-20 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ADJUSTLEVELSTOOL_H +#define ADJUSTLEVELSTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +class KGradientSelector; + +namespace KDcrawIface +{ +class RDoubleNumInput; +class RIntNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ImageWidget; +class ImageLevels; +class DImg; +class DColor; +} + +namespace DigikamAdjustLevelsImagesPlugin +{ + +class AdjustLevelsTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + AdjustLevelsTool(TQObject *parent); + ~AdjustLevelsTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + void adjustSliders(int minIn, double gamIn, int maxIn, int minOut, int maxOut); + bool eventFilter(TQObject *o, TQEvent *e); + +private slots: + + void slotLoadSettings(); + void slotSaveAsSettings(); + void slotEffect(); + void slotResetSettings(); + void slotResetCurrentChannel(); + void slotAutoLevels(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotAdjustSliders(); + void slotGammaInputchanged(double val); + void slotAdjustMinInputSpinBox(int val); + void slotAdjustMaxInputSpinBox(int val); + void slotAdjustMinOutputSpinBox(int val); + void slotAdjustMaxOutputSpinBox(int val); + void slotSpotColorChanged(const Digikam::DColor& color); + void slotColorSelectedFromTarget(const Digikam::DColor& color); + void slotPickerColorButtonActived(); + void slotShowInputHistogramGuide(int v); + void slotShowOutputHistogramGuide(int v); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel + }; + + enum ColorPicker + { + BlackTonal=0, + GrayTonal, + WhiteTonal + }; + + uchar *m_destinationPreviewData; + + int m_histoSegments; + int m_currentPreviewMode; + + TQComboBox *m_channelCB; + + TQPushButton *m_autoButton; + TQPushButton *m_resetButton; + TQPushButton *m_pickBlack; + TQPushButton *m_pickGray; + TQPushButton *m_pickWhite; + + TQHButtonGroup *m_pickerColorButtonGroup; + TQHButtonGroup *m_scaleBG; + + KGradientSelector *m_hGradientMinInput; + KGradientSelector *m_hGradientMaxInput; + KGradientSelector *m_hGradientMinOutput; + KGradientSelector *m_hGradientMaxOutput; + + KDcrawIface::RDoubleNumInput *m_gammaInput; + + KDcrawIface::RIntNumInput *m_minInput; + KDcrawIface::RIntNumInput *m_maxInput; + KDcrawIface::RIntNumInput *m_minOutput; + KDcrawIface::RIntNumInput *m_maxOutput; + + Digikam::HistogramWidget *m_levelsHistogramWidget; + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; + + Digikam::ImageLevels *m_levels; + Digikam::DImg *m_originalImage; +}; + +} // NameSpace DigikamAdjustLevelsImagesPlugin + +#endif /* ADJUSTLEVELSTOOL_H */ diff --git a/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels.desktop b/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels.desktop new file mode 100644 index 00000000..b601ebbf --- /dev/null +++ b/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_AdjustLevels +Name[bg]=ПриÑтавка за Ñнимки - ÐаÑтройка на нивата +Name[da]=Billedplugin_Niveaujustering +Name[el]=ΠÏόσθετοΕικόνας_ΠÏοσαÏμογήΕπιπέδων +Name[fi]=TasonsäätöHistogrammi +Name[hr]=PodeÅ¡avanje razina +Name[it]=PluginImmagini_RegolaLivelli +Name[nl]=Afbeeldingsplugin_NiveausAanpassen +Name[sr]=Подешавање нивоа +Name[sr@Latn]=PodeÅ¡avanje nivoa +Name[sv]=Insticksprogram för nivÃ¥justering +Name[tr]=ResimEklentisi_DüzeyAyarla +Name[xx]=xxImagePlugin_AdjustLevelsxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 + +Comment=Image histogram adjust levels plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наÑтройка нивата на хиÑтограмите на Ñнимки +Comment[ca]=Connector pel digiKam d'ajust dels nivells de l'histograma d'imatges +Comment[da]=Plugin til niveaujustering i Digikam +Comment[de]=digiKam-Modul zur Justierung der Bildhistogrammwerte +Comment[el]=ΠÏόσθετο Ï€ÏοσαÏμογής των επιπέδων του ιστογÏάμματος εικόνας για το digiKam +Comment[es]=Plugin de digiKam para ajustar los niveles de color de una imagen +Comment[et]=DigiKami pildi histogrammi tasemete kohendamise plugin +Comment[fa]=وصلۀ سطوح تنظیم سابقه‌نمای تصویر برای digiKam +Comment[fi]=Muokkaa värikanavien raja-arvoja +Comment[fr]=Module externe pour ajuster les niveaux de l'histogramme dans digiKam +Comment[gl]=Un plugin de digiKam para axustar os níveis do histograma +Comment[hr]=digiKam dodatak za histogramsko podeÅ¡avanje razina +Comment[is]=Ãforrit fyrir digiKam sem breytir tíðnidreifingu (levels) í stuðlariti myndar +Comment[it]=Plugin di regolazione dei livelli degli istogrammi delle immagini per digiKam +Comment[ja]=digiKam レベル補正プラグイン +Comment[nds]=digiKam-Moduul för't Topassen vun Klöörkanaal-Toonrebeden +Comment[nl]=Digikam-plugin voor niveauaanpassing van afbeeldingshistogram +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਿੱਤਰ ਹਿਸਟੋਗਰਾਮ ਅਨà©à¨•à©‚ਲ ਪੱਧਰ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam dostosowujÄ…ca poziomy histogramu +Comment[pt]=Um 'plugin' do digiKam para ajustar os níveis do histograma do digiKam +Comment[pt_BR]=Plugin de ajuste de níveis do histograma da imagem +Comment[ru]=Модуль digiKam подÑтройки уровней гиÑтограммы Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +Comment[sk]=digiKam plugin histogramu úrovní obrázku +Comment[sr]=digiKam-ов прикључак за подешавање нивоа хиÑтограма Ñлика +Comment[sr@Latn]=digiKam-ov prikljuÄak za podeÅ¡avanje nivoa histograma slika +Comment[sv]=Digikam insticksprogram för justering av nivÃ¥er i bildhistogram +Comment[tr]=digiKam için resim histogram düzeyleri ayarlama eklentisi +Comment[uk]=Втулок ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ñ–Ð²Ð½Ñ–Ð² гіÑтограми зображень Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung biểu đồ tần xuất Ä‘iá»u chỉnh lá»›p ảnh cho digiKam +Comment[xx]=xxImage histogram adjust levels plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_adjustlevels +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels_ui.rc b/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels_ui.rc new file mode 100644 index 00000000..8036e124 --- /dev/null +++ b/src/imageplugins/adjustlevels/digikamimageplugin_adjustlevels_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Color + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/adjustlevels/imageplugin_adjustlevels.cpp b/src/imageplugins/adjustlevels/imageplugin_adjustlevels.cpp new file mode 100644 index 00000000..0f9dcb9e --- /dev/null +++ b/src/imageplugins/adjustlevels/imageplugin_adjustlevels.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "adjustlevelstool.h" +#include "imageplugin_adjustlevels.h" +#include "imageplugin_adjustlevels.moc" + +using namespace DigikamAdjustLevelsImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_adjustlevels, + KGenericFactory("digikamimageplugin_adjustlevels")) + +ImagePlugin_AdjustLevels::ImagePlugin_AdjustLevels(TQObject *parent, const char*, + const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_AdjustLevels") +{ + m_levelsAction = new TDEAction(i18n("Levels Adjust..."), "adjustlevels", + CTRL+Key_L, // NOTE: Photoshop 7 use CTRL+L. + this, TQ_SLOT(slotLevelsAdjust()), + actionCollection(), "imageplugin_adjustlevels"); + + setXMLFile("digikamimageplugin_adjustlevels_ui.rc"); + + DDebug() << "ImagePlugin_AdjustLevels plugin loaded" << endl; +} + +ImagePlugin_AdjustLevels::~ImagePlugin_AdjustLevels() +{ +} + +void ImagePlugin_AdjustLevels::setEnabledActions(bool enable) +{ + m_levelsAction->setEnabled(enable); +} + +void ImagePlugin_AdjustLevels::slotLevelsAdjust() +{ + AdjustLevelsTool *levels = new AdjustLevelsTool(this); + loadTool(levels); +} diff --git a/src/imageplugins/adjustlevels/imageplugin_adjustlevels.h b/src/imageplugins/adjustlevels/imageplugin_adjustlevels.h new file mode 100644 index 00000000..16af4f32 --- /dev/null +++ b/src/imageplugins/adjustlevels/imageplugin_adjustlevels.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : image histogram adjust levels. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_ADJUSTLEVELS_H +#define IMAGEPLUGIN_ADJUSTLEVELS_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_AdjustLevels : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_AdjustLevels(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_AdjustLevels(); + + void setEnabledActions(bool enable); + +private slots: + + void slotLevelsAdjust(); + +private: + + TDEAction *m_levelsAction; +}; + +#endif /* IMAGEPLUGIN_ADJUSTLEVELS_H */ diff --git a/src/imageplugins/antivignetting/Makefile.am b/src/imageplugins/antivignetting/Makefile.am new file mode 100644 index 00000000..54539672 --- /dev/null +++ b/src/imageplugins/antivignetting/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_antivignetting_la_SOURCES = imageplugin_antivignetting.cpp \ + antivignettingtool.cpp antivignetting.cpp + +digikamimageplugin_antivignetting_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_antivignetting_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_antivignetting.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_antivignetting.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_antivignetting_ui.rc + diff --git a/src/imageplugins/antivignetting/antivignetting.cpp b/src/imageplugins/antivignetting/antivignetting.cpp new file mode 100644 index 00000000..dec0a9ea --- /dev/null +++ b/src/imageplugins/antivignetting/antivignetting.cpp @@ -0,0 +1,149 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Antivignetting threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original AntiVignetting algorithm copyrighted 2003 by + * John Walker from 'pnmctrfilt' implementation. See + * http://www.fourmilab.ch/netpbm/pnmctrfilt for more + * information. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "antivignetting.h" + +namespace DigikamAntiVignettingImagesPlugin +{ + +AntiVignetting::AntiVignetting(Digikam::DImg *orgImage, TQObject *parent, double density, + double power, double radius, int xshift, int yshift, bool normalize) + : Digikam::DImgThreadedFilter(orgImage, parent, "AntiVignetting") +{ + m_density = density; + m_power = power; + m_radius = radius; + m_xshift = xshift; + m_yshift = yshift; + m_normalize = normalize; + + initFilter(); +} + +// This method is inspired from John Walker 'pnmctrfilt' algorithm code. + +void AntiVignetting::filterImage(void) +{ + int progress; + int col, row, xd, td, yd, p; + int i, xsize, ysize, diagonal, erad, xctr, yctr; + double *ldens; + + uchar* NewBits = m_destImage.bits(); + uchar* data = m_orgImage.bits(); + + unsigned short* NewBits16 = (unsigned short*)m_destImage.bits(); + unsigned short* data16 = (unsigned short*)m_orgImage.bits(); + + int Width = m_orgImage.width(); + int Height = m_orgImage.height(); + + // Determine the radius of the filter. This is the half diagonal + // measure of the image multiplied by the command line radius factor. + + xsize = (Height + 1) / 2; + ysize = (Width + 1) / 2; + erad = (int)((sqrt((xsize * xsize) + (ysize * ysize)) + 0.5) * m_radius); + + // Build the in-memory table which maps distance from the + // center of the image (as adjusted by the X and Y offset, + // if any) to the density of the filter at this remove. This + // table needs to be as large as the diagonal from the + // (possibly offset) center to the most distant corner + // of the image. + + xsize = ((Height + 1) / 2) + abs(m_xshift); + ysize = ((Width + 1) / 2) + abs(m_yshift); + diagonal = ((int) (sqrt((xsize * xsize) + (ysize * ysize)) + 0.5)) + 1; + + ldens = new double[diagonal]; + + for (i = 0 ; !m_cancel && (i < diagonal) ; i++) + { + if ( i >= erad ) + ldens[i] = 1; + else + ldens[i] = (1.0 + (m_density - 1) * pow(1.0 - (((double) i) / (erad - 1)), m_power)); + } + + xctr = ((Height + 1) / 2) + m_xshift; + yctr = ((Width + 1) / 2) + m_yshift; + + for (row = 0 ; !m_cancel && (row < Width) ; row++) + { + yd = abs(yctr - row); + + for (col = 0 ; !m_cancel && (col < Height) ; col++) + { + p = (col * Width + row)*4; + + xd = abs(xctr - col); + td = (int) (sqrt((xd * xd) + (yd * yd)) + 0.5); + + if (!m_orgImage.sixteenBit()) // 8 bits image + { + NewBits[ p ] = (uchar)(data[ p ] / ldens[td]); + NewBits[p+1] = (uchar)(data[p+1] / ldens[td]); + NewBits[p+2] = (uchar)(data[p+2] / ldens[td]); + NewBits[p+3] = data[p+3]; + } + else // 16 bits image. + { + NewBits16[ p ] = (unsigned short)(data16[ p ] / ldens[td]); + NewBits16[p+1] = (unsigned short)(data16[p+1] / ldens[td]); + NewBits16[p+2] = (unsigned short)(data16[p+2] / ldens[td]); + NewBits16[p+3] = data16[p+3]; + } + } + + // Update the progress bar in dialog. + progress = (int)(((double)row * 100.0) / Width); + if (progress%5 == 0) + postProgress( progress ); + } + + // Normalize colors for a best rendering. + if (m_normalize) + { + Digikam::DImgImageFilters filters; + filters.normalizeImage(m_destImage.bits(), Width, Height, m_destImage.sixteenBit()); + } + + delete [] ldens; +} + +} // NameSpace DigikamAntiVignettingImagesPlugin diff --git a/src/imageplugins/antivignetting/antivignetting.h b/src/imageplugins/antivignetting/antivignetting.h new file mode 100644 index 00000000..f5311da0 --- /dev/null +++ b/src/imageplugins/antivignetting/antivignetting.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Antivignetting threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ANTIVIGNETTING_H +#define ANTIVIGNETTING_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamAntiVignettingImagesPlugin +{ + +class AntiVignetting : public Digikam::DImgThreadedFilter +{ + +public: + + AntiVignetting(Digikam::DImg *orgImage, TQObject *parent=0, double density=2.0, + double power=1.0, double radius=1.0, int xshift=0, int yshift=0, bool normalize=true); + + ~AntiVignetting(){}; + +private: + + virtual void filterImage(void); + +private: + + bool m_normalize; + + int m_xshift; + int m_yshift; + + double m_density; + double m_power; + double m_radius; +}; + +} // NameSpace DigikamAntiVignettingImagesPlugin + +#endif /* ANTIVIGNETTING_H */ diff --git a/src/imageplugins/antivignetting/antivignettingtool.cpp b/src/imageplugins/antivignetting/antivignettingtool.cpp new file mode 100644 index 00000000..d8f211c7 --- /dev/null +++ b/src/imageplugins/antivignetting/antivignettingtool.cpp @@ -0,0 +1,378 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "bcgmodifier.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "dimgimagefilters.h" +#include "antivignetting.h" +#include "antivignettingtool.h" +#include "antivignettingtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamAntiVignettingImagesPlugin +{ + +AntiVignettingTool::AntiVignettingTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("antivignettings"); + setToolName(i18n("Vignetting Correction")); + setToolIcon(SmallIcon("antivignetting")); + + m_previewWidget = new ImageWidget("antivignetting Tool", 0, TQString(), + false, ImageGuideWidget::HVGuideMode, false); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 14, 2); + + m_maskPreviewLabel = new TQLabel( m_gboxSettings->plainPage() ); + m_maskPreviewLabel->setAlignment ( TQt::AlignHCenter | TQt::AlignVCenter ); + TQWhatsThis::add( m_maskPreviewLabel, i18n("

    You can see here a thumbnail preview of the anti-vignetting " + "mask applied to the image.") ); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Density:"), m_gboxSettings->plainPage()); + + m_densityInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_densityInput->setPrecision(1); + m_densityInput->setRange(1.0, 20.0, 0.1); + m_densityInput->setDefaultValue(2.0); + TQWhatsThis::add( m_densityInput, i18n("

    This value controls the degree of intensity attenuation by the filter " + "at its point of maximum density.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Power:"), m_gboxSettings->plainPage()); + + m_powerInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_powerInput->setPrecision(1); + m_powerInput->setRange(0.1, 2.0, 0.1); + m_powerInput->setDefaultValue(1.0); + TQWhatsThis::add( m_powerInput, i18n("

    This value is used as the exponent controlling the fall-off in density " + "from the center of the filter to the periphery.")); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Radius:"), m_gboxSettings->plainPage()); + + m_radiusInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_radiusInput->setPrecision(1); + m_radiusInput->setRange(-100.0, 100.0, 0.1); + m_radiusInput->setDefaultValue(1.0); + TQWhatsThis::add( m_radiusInput, i18n("

    This value is the radius of the center filter. It is a multiple of the " + "half-diagonal measure of the image, at which the density of the filter falls " + "to zero.")); + + KSeparator *line = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Brightness:"), m_gboxSettings->plainPage()); + + m_brightnessInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_brightnessInput->setRange(0, 100, 1); + m_brightnessInput->setDefaultValue(0); + TQWhatsThis::add( m_brightnessInput, i18n("

    Set here the brightness re-adjustment of the target image.")); + + // ------------------------------------------------------------- + + TQLabel *label5 = new TQLabel(i18n("Contrast:"), m_gboxSettings->plainPage()); + + m_contrastInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_contrastInput->setRange(0, 100, 1); + m_contrastInput->setDefaultValue(0); + TQWhatsThis::add( m_contrastInput, i18n("

    Set here the contrast re-adjustment of the target image.")); + + // ------------------------------------------------------------- + + TQLabel *label6 = new TQLabel(i18n("Gamma:"), m_gboxSettings->plainPage()); + + m_gammaInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01); + m_gammaInput->setDefaultValue(1.0); + TQWhatsThis::add( m_gammaInput, i18n("

    Set here the gamma re-adjustment of the target image.")); + + grid->addMultiCellWidget(m_maskPreviewLabel, 0, 0, 0, 2); + grid->addMultiCellWidget(label1, 1, 1, 0, 2); + grid->addMultiCellWidget(m_densityInput, 2, 2, 0, 2); + grid->addMultiCellWidget(label2, 3, 3, 0, 2); + grid->addMultiCellWidget(m_powerInput, 4, 4, 0, 2); + grid->addMultiCellWidget(label3, 5, 5, 0, 2); + grid->addMultiCellWidget(m_radiusInput, 6, 6, 0, 2); + grid->addMultiCellWidget(line, 7, 7, 0, 2); + grid->addMultiCellWidget(label4, 8, 8, 0, 2); + grid->addMultiCellWidget(m_brightnessInput, 9, 9, 0, 2); + grid->addMultiCellWidget(label5, 10, 10, 0, 2); + grid->addMultiCellWidget(m_contrastInput, 11, 11, 0, 2); + grid->addMultiCellWidget(label6, 12, 12, 0, 2); + grid->addMultiCellWidget(m_gammaInput, 13, 13, 0, 2); + grid->setRowStretch(14, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_densityInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_powerInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_radiusInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_brightnessInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_contrastInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); +} + +AntiVignettingTool::~AntiVignettingTool() +{ +} + +void AntiVignettingTool::renderingFinished() +{ + m_densityInput->setEnabled(true); + m_powerInput->setEnabled(true); + m_radiusInput->setEnabled(true); + m_brightnessInput->setEnabled(true); + m_contrastInput->setEnabled(true); + m_gammaInput->setEnabled(true); +} + +void AntiVignettingTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("antivignettings Tool"); + + m_densityInput->blockSignals(true); + m_powerInput->blockSignals(true); + m_radiusInput->blockSignals(true); + m_brightnessInput->blockSignals(true); + m_contrastInput->blockSignals(true); + m_gammaInput->blockSignals(true); + + m_densityInput->setValue(config->readDoubleNumEntry("DensityAjustment", m_densityInput->defaultValue())); + m_powerInput->setValue(config->readDoubleNumEntry("PowerAjustment", m_powerInput->defaultValue())); + m_radiusInput->setValue(config->readDoubleNumEntry("RadiusAjustment", m_radiusInput->defaultValue())); + m_brightnessInput->setValue(config->readNumEntry("BrightnessAjustment", m_brightnessInput->defaultValue())); + m_contrastInput->setValue(config->readNumEntry("ContrastAjustment", m_contrastInput->defaultValue())); + m_gammaInput->setValue(config->readDoubleNumEntry("GammaAjustment", m_gammaInput->defaultValue())); + + m_densityInput->blockSignals(false); + m_powerInput->blockSignals(false); + m_radiusInput->blockSignals(false); + m_brightnessInput->blockSignals(false); + m_contrastInput->blockSignals(false); + m_gammaInput->blockSignals(false); + + slotEffect(); +} + +void AntiVignettingTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("antivignettings Tool"); + config->writeEntry("DensityAjustment", m_densityInput->value()); + config->writeEntry("PowerAjustment", m_powerInput->value()); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->writeEntry("BrightnessAjustment", m_brightnessInput->value()); + config->writeEntry("ContrastAjustment", m_contrastInput->value()); + config->writeEntry("GammaAjustment", m_gammaInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void AntiVignettingTool::slotResetSettings() +{ + m_densityInput->blockSignals(true); + m_powerInput->blockSignals(true); + m_radiusInput->blockSignals(true); + m_brightnessInput->blockSignals(true); + m_contrastInput->blockSignals(true); + m_gammaInput->blockSignals(true); + + m_densityInput->slotReset(); + m_powerInput->slotReset(); + m_radiusInput->slotReset(); + m_brightnessInput->slotReset(); + m_contrastInput->slotReset(); + m_gammaInput->slotReset(); + + m_densityInput->blockSignals(false); + m_powerInput->blockSignals(false); + m_radiusInput->blockSignals(false); + m_brightnessInput->blockSignals(false); + m_contrastInput->blockSignals(false); + m_gammaInput->blockSignals(false); +} + +void AntiVignettingTool::prepareEffect() +{ + m_densityInput->setEnabled(false); + m_powerInput->setEnabled(false); + m_radiusInput->setEnabled(false); + m_brightnessInput->setEnabled(false); + m_contrastInput->setEnabled(false); + m_gammaInput->setEnabled(false); + + double d = m_densityInput->value(); + double p = m_powerInput->value(); + double r = m_radiusInput->value(); + + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int orgWidth = iface->originalWidth(); + int orgHeight = iface->originalHeight(); + TQSize ps(orgWidth, orgHeight); + ps.scale( TQSize(120, 120), TQSize::ScaleMin ); + + // Calc mask preview. + DImg preview(ps.width(), ps.height(), false); + memset(preview.bits(), 255, preview.numBytes()); + AntiVignetting maskPreview(&preview, 0L, d, p, r, 0, 0, false); + TQPixmap pix = maskPreview.getTargetImage().convertToPixmap(); + TQPainter pt(&pix); + pt.setPen( TQPen(TQt::black, 1) ); + pt.drawRect( 0, 0, pix.width(), pix.height() ); + pt.end(); + m_maskPreviewLabel->setPixmap( pix ); + + DImg orgImage(orgWidth, orgHeight, iface->originalSixteenBit(), + iface->originalHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new AntiVignetting(&orgImage, this, d, p, r, 0, 0, true))); +} + +void AntiVignettingTool::prepareFinal() +{ + m_densityInput->setEnabled(false); + m_powerInput->setEnabled(false); + m_radiusInput->setEnabled(false); + m_brightnessInput->setEnabled(false); + m_contrastInput->setEnabled(false); + m_gammaInput->setEnabled(false); + + double d = m_densityInput->value(); + double p = m_powerInput->value(); + double r = m_radiusInput->value(); + + ImageIface iface(0, 0); + + uchar *data = iface.getOriginalImage(); + DImg orgImage(iface.originalWidth(), iface.originalHeight(), iface.originalSixteenBit(), + iface.originalHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new AntiVignetting(&orgImage, this, d, p, r, 0, 0, true))); +} + +void AntiVignettingTool::putPreviewData(void) +{ + ImageIface* iface = m_previewWidget->imageIface(); + DImg imDest = filter()->getTargetImage(); + + // Adjust Image BCG. + + double b = (double)(m_brightnessInput->value() / 100.0); + double c = (double)(m_contrastInput->value() / 100.0) + (double)(1.00); + double g = m_gammaInput->value(); + + BCGModifier cmod; + cmod.setGamma(g); + cmod.setBrightness(b); + cmod.setContrast(c); + cmod.applyBCG(imDest); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + m_previewWidget->updatePreview(); +} + +void AntiVignettingTool::putFinalData(void) +{ + ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Vignetting Correction"), + filter()->getTargetImage().bits()); + + double b = (double)(m_brightnessInput->value() / 100.0); + double c = (double)(m_contrastInput->value() / 100.0) + (double)(1.00); + double g = m_gammaInput->value(); + + // Adjust Image BCG. + iface.setOriginalBCG(b, c, g); +} + +} // NameSpace DigikamAntiVignettingImagesPlugin diff --git a/src/imageplugins/antivignetting/antivignettingtool.h b/src/imageplugins/antivignetting/antivignettingtool.h new file mode 100644 index 00000000..467a19da --- /dev/null +++ b/src/imageplugins/antivignetting/antivignettingtool.h @@ -0,0 +1,92 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ANTIVIGNETTINGTOOL_H +#define ANTIVIGNETTINGTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; + +namespace KDcrawIface +{ +class RIntNumInput; +class RDoubleNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImageWidget; +} + +namespace DigikamAntiVignettingImagesPlugin +{ + +class AntiVignettingTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + AntiVignettingTool(TQObject *parent); + ~AntiVignettingTool(); + +private slots: + + void slotResetSettings(); + +private: + + void writeSettings(); + void readSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_maskPreviewLabel; + + KDcrawIface::RIntNumInput *m_brightnessInput; + KDcrawIface::RIntNumInput *m_contrastInput; + + KDcrawIface::RDoubleNumInput *m_gammaInput; + KDcrawIface::RDoubleNumInput *m_densityInput; + KDcrawIface::RDoubleNumInput *m_powerInput; + KDcrawIface::RDoubleNumInput *m_radiusInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamAntiVignettingImagesPlugin + +#endif /* ANTIVIGNETTINGTOOL_H */ diff --git a/src/imageplugins/antivignetting/digikamimageplugin_antivignetting.desktop b/src/imageplugins/antivignetting/digikamimageplugin_antivignetting.desktop new file mode 100644 index 00000000..f70a4752 --- /dev/null +++ b/src/imageplugins/antivignetting/digikamimageplugin_antivignetting.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_AntiVignetting +Name[bg]=ПриÑтавка за Ñнимки - Премахване на винетиране +Name[da]=Billedplugin_Anti-vignettering +Name[el]=ΠÏόσθετοΕικόνας_Αντισκίασης +Name[fi]=ReunatummentumienPoisto +Name[hr]=Uklanjanje vinjeta +Name[it]=PluginImmagini_Antivignettatura +Name[nl]=Afbeeldingsplugin_Antivignetting +Name[sr]=Девињетизација +Name[sr@Latn]=Devinjetizacija +Name[sv]=Insticksprogram för antivinjettering +Name[tr]=ResimEklentisi_KenarKararmalarınıDüzelt +Name[xx]=xxImagePlugin_AntiVignettingxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Anti Vignetting image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за намалÑване на винетирането на Ñнимки +Comment[ca]=Connector pel digiKam d'efecte anti-vinyetatge +Comment[da]=Anti-vignetteringsplugin for Digikam +Comment[de]=digiKam-Modul zur Reduzierung von Vignettierung im Bild +Comment[el]=ΠÏόσθετο εφέ αντισκίασης εικόνας για το digiKam +Comment[es]=Un plugin para digiKam para eliminar efectos tipo "Vignetting" +Comment[et]=DigiKami pildi vinjeti kohendamise plugin +Comment[fa]=وصلۀ جلوۀ تصویر ضد عکس برای digiKam +Comment[fi]=Keskialueen portaaton tummennus +Comment[gl]=Un plugin de digiKam para efeitos antiviñeta de imaxe +Comment[hr]=digiKam dodatak za efekt protiv vinjeta +Comment[is]=Ãforrit fyrir digiKam sem minnkar linsuskyggingu í hornum +Comment[it]=Plugin per l'effetto delle immagini di antivignettatura per digiKam +Comment[ja]=digiKam å£å¾„食補正プラグイン +Comment[nds]=digiKam-Bildeffektmoduul gegen Randschaddens +Comment[nl]=Digikam-plugin voor anti-vignetting +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ à¨à¨‚ਟੀ ਵੀਜਨਿੰਟ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam usuwajÄ…ca efekt winietowania +Comment[pt]=Um 'plugin' do digiKam para efeitos anti-vinheta de imagem +Comment[pt_BR]=Plugin de efeito anti-Vignetting para o digiKam +Comment[ru]=Модуль Ñффекта анти-Ð²Ð¸Ð½ÑŒÐµÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ digiKam +Comment[sk]=Plugin korekcie vignetácie obrázku pre digiKam +Comment[sr]=digiKam-ов прикључак за ефекат девињетизације +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekat devinjetizacije +Comment[sv]=Digikam insticksprogram för antivinjetteringsbildeffekt +Comment[tr]=digiKam için kenar kararmalarını düzeltme eklentisi +Comment[uk]=Втулок противіньєткового ефекту зображень Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng chống làm má» nét ảnh cho digiKam +Comment[xx]=xxAnti Vignetting image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_antivignetting +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/antivignetting/digikamimageplugin_antivignetting_ui.rc b/src/imageplugins/antivignetting/digikamimageplugin_antivignetting_ui.rc new file mode 100644 index 00000000..7a54a1f3 --- /dev/null +++ b/src/imageplugins/antivignetting/digikamimageplugin_antivignetting_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/antivignetting/imageeffect_antivignetting.cpp b/src/imageplugins/antivignetting/imageeffect_antivignetting.cpp new file mode 100644 index 00000000..7d7a00c6 --- /dev/null +++ b/src/imageplugins/antivignetting/imageeffect_antivignetting.cpp @@ -0,0 +1,380 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "bcgmodifier.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "dimgimagefilters.h" +#include "antivignetting.h" +#include "imageeffect_antivignetting.h" +#include "imageeffect_antivignetting.moc" + +namespace DigikamAntiVignettingImagesPlugin +{ + +ImageEffect_AntiVignetting::ImageEffect_AntiVignetting(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Vignetting Correction"), + "antivignettings", false, true, false, + Digikam::ImageGuideWidget::HVGuideMode, 0, true) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Vignetting Correction"), + digikam_version, + I18N_NOOP("A digiKam image plugin to reduce image vignetting."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("John Walker", I18N_NOOP("Anti Vignetting algorithm"), 0, + "http://www.fourmilab.ch/netpbm/pnmctrfilt"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 13, 2, spacingHint()); + + m_maskPreviewLabel = new TQLabel( gboxSettings ); + m_maskPreviewLabel->setAlignment ( TQt::AlignHCenter | TQt::AlignVCenter ); + TQWhatsThis::add( m_maskPreviewLabel, i18n("

    You can see here a thumbnail preview of the anti-vignetting " + "mask applied to the image.") ); + gridSettings->addMultiCellWidget(m_maskPreviewLabel, 0, 0, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Density:"), gboxSettings); + + m_densityInput = new KDoubleNumInput(gboxSettings); + m_densityInput->setPrecision(1); + m_densityInput->setRange(1.0, 20.0, 0.1, true); + TQWhatsThis::add( m_densityInput, i18n("

    This value controls the degree of intensity attenuation by the filter " + "at its point of maximum density.")); + + gridSettings->addMultiCellWidget(label1, 1, 1, 0, 2); + gridSettings->addMultiCellWidget(m_densityInput, 2, 2, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Power:"), gboxSettings); + + m_powerInput = new KDoubleNumInput(gboxSettings); + m_powerInput->setPrecision(1); + m_powerInput->setRange(0.1, 2.0, 0.1, true); + TQWhatsThis::add( m_powerInput, i18n("

    This value is used as the exponent controlling the fall-off in density " + "from the center of the filter to the periphery.")); + + gridSettings->addMultiCellWidget(label2, 3, 3, 0, 2); + gridSettings->addMultiCellWidget(m_powerInput, 4, 4, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Radius:"), gboxSettings); + + m_radiusInput = new KDoubleNumInput(gboxSettings); + m_radiusInput->setPrecision(1); + m_radiusInput->setRange(-100.0, 100.0, 0.1, true); + TQWhatsThis::add( m_radiusInput, i18n("

    This value is the radius of the center filter. It is a multiple of the " + "half-diagonal measure of the image, at which the density of the filter falls " + "to zero.")); + + gridSettings->addMultiCellWidget(label3, 5, 5, 0, 2); + gridSettings->addMultiCellWidget(m_radiusInput, 6, 6, 0, 2); + + KSeparator *line = new KSeparator(Horizontal, gboxSettings); + gridSettings->addMultiCellWidget(line, 7, 7, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Brightness:"), gboxSettings); + + m_brightnessInput = new KIntNumInput(gboxSettings); + m_brightnessInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_brightnessInput, i18n("

    Set here the brightness re-adjustment of the target image.")); + + gridSettings->addMultiCellWidget(label4, 8, 8, 0, 2); + gridSettings->addMultiCellWidget(m_brightnessInput, 9, 9, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label5 = new TQLabel(i18n("Contrast:"), gboxSettings); + + m_contrastInput = new KIntNumInput(gboxSettings); + m_contrastInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_contrastInput, i18n("

    Set here the contrast re-adjustment of the target image.")); + + gridSettings->addMultiCellWidget(label5, 10, 10, 0, 2); + gridSettings->addMultiCellWidget(m_contrastInput, 11, 11, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label6 = new TQLabel(i18n("Gamma:"), gboxSettings); + + m_gammaInput = new KDoubleNumInput(gboxSettings); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01, true); + m_gammaInput->setValue(1.0); + TQWhatsThis::add( m_gammaInput, i18n("

    Set here the gamma re-adjustment of the target image.")); + + gridSettings->addMultiCellWidget(label6, 12, 12, 0, 2); + gridSettings->addMultiCellWidget(m_gammaInput, 13, 13, 0, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_densityInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_powerInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_radiusInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_brightnessInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_contrastInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_AntiVignetting::~ImageEffect_AntiVignetting() +{ +} + +void ImageEffect_AntiVignetting::renderingFinished() +{ + m_densityInput->setEnabled(true); + m_powerInput->setEnabled(true); + m_radiusInput->setEnabled(true); + m_brightnessInput->setEnabled(true); + m_contrastInput->setEnabled(true); + m_gammaInput->setEnabled(true); +} + +void ImageEffect_AntiVignetting::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("antivignettings Tool Dialog"); + + m_densityInput->blockSignals(true); + m_powerInput->blockSignals(true); + m_radiusInput->blockSignals(true); + m_brightnessInput->blockSignals(true); + m_contrastInput->blockSignals(true); + m_gammaInput->blockSignals(true); + + m_densityInput->setValue(config->readDoubleNumEntry("DensityAjustment", 2.0)); + m_powerInput->setValue(config->readDoubleNumEntry("PowerAjustment", 1.0)); + m_radiusInput->setValue(config->readDoubleNumEntry("RadiusAjustment", 1.0)); + m_brightnessInput->setValue(config->readNumEntry("BrightnessAjustment", 0)); + m_contrastInput->setValue(config->readNumEntry("ContrastAjustment", 0)); + m_gammaInput->setValue(config->readDoubleNumEntry("GammaAjustment", 1.0)); + + m_densityInput->blockSignals(false); + m_powerInput->blockSignals(false); + m_radiusInput->blockSignals(false); + m_brightnessInput->blockSignals(false); + m_contrastInput->blockSignals(false); + m_gammaInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_AntiVignetting::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("antivignettings Tool Dialog"); + config->writeEntry("DensityAjustment", m_densityInput->value()); + config->writeEntry("PowerAjustment", m_powerInput->value()); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->writeEntry("BrightnessAjustment", m_brightnessInput->value()); + config->writeEntry("ContrastAjustment", m_contrastInput->value()); + config->writeEntry("GammaAjustment", m_gammaInput->value()); + config->sync(); +} + +void ImageEffect_AntiVignetting::resetValues() +{ + m_densityInput->blockSignals(true); + m_powerInput->blockSignals(true); + m_radiusInput->blockSignals(true); + m_brightnessInput->blockSignals(true); + m_contrastInput->blockSignals(true); + m_gammaInput->blockSignals(true); + + m_densityInput->setValue(2.0); + m_powerInput->setValue(1.0); + m_radiusInput->setValue(1.0); + m_brightnessInput->setValue(0); + m_contrastInput->setValue(0); + m_gammaInput->setValue(1.0); + + m_densityInput->blockSignals(false); + m_powerInput->blockSignals(false); + m_radiusInput->blockSignals(false); + m_brightnessInput->blockSignals(false); + m_contrastInput->blockSignals(false); + m_gammaInput->blockSignals(false); +} + +void ImageEffect_AntiVignetting::prepareEffect() +{ + m_densityInput->setEnabled(false); + m_powerInput->setEnabled(false); + m_radiusInput->setEnabled(false); + m_brightnessInput->setEnabled(false); + m_contrastInput->setEnabled(false); + m_gammaInput->setEnabled(false); + + double d = m_densityInput->value(); + double p = m_powerInput->value(); + double r = m_radiusInput->value(); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int orgWidth = iface->originalWidth(); + int orgHeight = iface->originalHeight(); + TQSize ps(orgWidth, orgHeight); + ps.scale( TQSize(120, 120), TQSize::ScaleMin ); + + // Calc mask preview. + Digikam::DImg preview(ps.width(), ps.height(), false); + memset(preview.bits(), 255, preview.numBytes()); + AntiVignetting maskPreview(&preview, 0L, d, p, r, 0, 0, false); + TQPixmap pix = maskPreview.getTargetImage().convertToPixmap(); + TQPainter pt(&pix); + pt.setPen( TQPen(TQt::black, 1) ); + pt.drawRect( 0, 0, pix.width(), pix.height() ); + pt.end(); + m_maskPreviewLabel->setPixmap( pix ); + + Digikam::DImg orgImage(orgWidth, orgHeight, iface->originalSixteenBit(), + iface->originalHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new AntiVignetting(&orgImage, this, d, p, r, 0, 0, true)); +} + +void ImageEffect_AntiVignetting::prepareFinal() +{ + m_densityInput->setEnabled(false); + m_powerInput->setEnabled(false); + m_radiusInput->setEnabled(false); + m_brightnessInput->setEnabled(false); + m_contrastInput->setEnabled(false); + m_gammaInput->setEnabled(false); + + double d = m_densityInput->value(); + double p = m_powerInput->value(); + double r = m_radiusInput->value(); + + Digikam::ImageIface iface(0, 0); + + uchar *data = iface.getOriginalImage(); + Digikam::DImg orgImage(iface.originalWidth(), iface.originalHeight(), iface.originalSixteenBit(), + iface.originalHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new AntiVignetting(&orgImage, this, d, p, r, 0, 0, true)); +} + +void ImageEffect_AntiVignetting::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + + // Adjust Image BCG. + + double b = (double)(m_brightnessInput->value() / 100.0); + double c = (double)(m_contrastInput->value() / 100.0) + (double)(1.00); + double g = m_gammaInput->value(); + + Digikam::BCGModifier cmod; + cmod.setGamma(g); + cmod.setBrightness(b); + cmod.setContrast(c); + cmod.applyBCG(imDest); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + m_imagePreviewWidget->updatePreview(); +} + +void ImageEffect_AntiVignetting::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Vignetting Correction"), + m_threadedFilter->getTargetImage().bits()); + + double b = (double)(m_brightnessInput->value() / 100.0); + double c = (double)(m_contrastInput->value() / 100.0) + (double)(1.00); + double g = m_gammaInput->value(); + + // Adjust Image BCG. + iface.setOriginalBCG(b, c, g); +} + +} // NameSpace DigikamAntiVignettingImagesPlugin + diff --git a/src/imageplugins/antivignetting/imageeffect_antivignetting.h b/src/imageplugins/antivignetting/imageeffect_antivignetting.h new file mode 100644 index 00000000..3ee6f158 --- /dev/null +++ b/src/imageplugins/antivignetting/imageeffect_antivignetting.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_ANTIVIGNETTING_H +#define IMAGEEFFECT_ANTIVIGNETTING_H + +// Digikam includes. + +#include "imageguidedlg.h" + +class TQLabel; + +class KIntNumInput; +class KDoubleNumInput; + +namespace DigikamAntiVignettingImagesPlugin +{ + +class ImageEffect_AntiVignetting : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_AntiVignetting(TQWidget *parent); + ~ImageEffect_AntiVignetting(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_maskPreviewLabel; + + KIntNumInput *m_brightnessInput; + KIntNumInput *m_contrastInput; + + KDoubleNumInput *m_gammaInput; + KDoubleNumInput *m_densityInput; + KDoubleNumInput *m_powerInput; + KDoubleNumInput *m_radiusInput; +}; + +} // NameSpace DigikamAntiVignettingImagesPlugin + +#endif /* IMAGEEFFECT_ANTIVIGNETTING_H */ diff --git a/src/imageplugins/antivignetting/imageplugin_antivignetting.cpp b/src/imageplugins/antivignetting/imageplugin_antivignetting.cpp new file mode 100644 index 00000000..239d9842 --- /dev/null +++ b/src/imageplugins/antivignetting/imageplugin_antivignetting.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "antivignettingtool.h" +#include "imageplugin_antivignetting.h" +#include "imageplugin_antivignetting.moc" + +using namespace DigikamAntiVignettingImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_antivignetting, + KGenericFactory("digikamimageplugin_antivignetting")); + +ImagePlugin_AntiVignetting::ImagePlugin_AntiVignetting(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_AntiVignetting") +{ + m_antivignettingAction = new TDEAction(i18n("Vignetting Correction..."), "antivignetting", 0, + this, TQ_SLOT(slotAntiVignetting()), + actionCollection(), "imageplugin_antivignetting"); + + setXMLFile("digikamimageplugin_antivignetting_ui.rc"); + + DDebug() << "ImagePlugin_AntiVignetting plugin loaded" << endl; +} + +ImagePlugin_AntiVignetting::~ImagePlugin_AntiVignetting() +{ +} + +void ImagePlugin_AntiVignetting::setEnabledActions(bool enable) +{ + m_antivignettingAction->setEnabled(enable); +} + +void ImagePlugin_AntiVignetting::slotAntiVignetting() +{ + AntiVignettingTool *tool = new AntiVignettingTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/antivignetting/imageplugin_antivignetting.h b/src/imageplugins/antivignetting/imageplugin_antivignetting.h new file mode 100644 index 00000000..d8d4eedd --- /dev/null +++ b/src/imageplugins/antivignetting/imageplugin_antivignetting.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-25 + * Description : a digiKam image plugin to reduce + * vignetting on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_ANTIVIGNETTING_H +#define IMAGEPLUGIN_ANTIVIGNETTING_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_AntiVignetting : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_AntiVignetting(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_AntiVignetting(); + + void setEnabledActions(bool enable); + +private slots: + + void slotAntiVignetting(); + +private: + + TDEAction *m_antivignettingAction; +}; + +#endif /* IMAGEPLUGIN_ANTIVIGNETTING_H */ diff --git a/src/imageplugins/blurfx/Makefile.am b/src/imageplugins/blurfx/Makefile.am new file mode 100644 index 00000000..51a78977 --- /dev/null +++ b/src/imageplugins/blurfx/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_blurfx_la_SOURCES = imageplugin_blurfx.cpp \ + blurfxtool.cpp blurfx.cpp + +digikamimageplugin_blurfx_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_blurfx_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_blurfx.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_blurfx.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_blurfx_ui.rc + diff --git a/src/imageplugins/blurfx/blurfx.cpp b/src/imageplugins/blurfx/blurfx.cpp new file mode 100644 index 00000000..ec5ff30e --- /dev/null +++ b/src/imageplugins/blurfx/blurfx.cpp @@ -0,0 +1,1445 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Blur FX threaded image filter. + * + * Copyright 2005-2007 by Gilles Caulier + * Copyright 2006-2007 by Marcel Wiesweg + * + * Original Blur algorithms copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Represents 1 +#define ANGLE_RATIO 0.017453292519943295769236907685 + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "dimggaussianblur.h" +#include "blurfx.h" + +namespace DigikamBlurFXImagesPlugin +{ + +BlurFX::BlurFX(Digikam::DImg *orgImage, TQObject *parent, int blurFXType, int distance, int level) + : Digikam::DImgThreadedFilter(orgImage, parent, "BlurFX") +{ + m_blurFXType = blurFXType; + m_distance = distance; + m_level = level; + + initFilter(); +} + +void BlurFX::filterImage(void) +{ + int w = m_orgImage.width(); + int h = m_orgImage.height(); + + switch (m_blurFXType) + { + case ZoomBlur: + zoomBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance); + break; + + case RadialBlur: + radialBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance); + break; + + case FarBlur: + farBlur(&m_orgImage, &m_destImage, m_distance); + break; + + case MotionBlur: + motionBlur(&m_orgImage, &m_destImage, m_distance, (double)m_level); + break; + + case SoftenerBlur: + softenerBlur(&m_orgImage, &m_destImage); + break; + + case ShakeBlur: + shakeBlur(&m_orgImage, &m_destImage, m_distance); + break; + + case FocusBlur: + focusBlur(&m_orgImage, &m_destImage, w/2, h/2, m_distance, m_level*10); + break; + + case SmartBlur: + smartBlur(&m_orgImage, &m_destImage, m_distance, m_level); + break; + + case FrostGlass: + frostGlass(&m_orgImage, &m_destImage, m_distance); + break; + + case Mosaic: + mosaic(&m_orgImage, &m_destImage, m_distance, m_distance); + break; + } +} + +/* Function to apply the ZoomBlur effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * X, Y => Center of zoom in the image + * Distance => Distance value + * pArea => Preview area. + * + * Theory => Here we have a effect similar to RadialBlur mode Zoom from + * Photoshop. The theory is very similar to RadialBlur, but has one + * difference. Instead we use pixels with the same radius and + * near angles, we take pixels with the same angle but near radius + * This radius is always from the center to out of the image, we + * calc a proportional radius from the center. + */ +void BlurFX::zoomBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, int Distance, TQRect pArea) +{ + if (Distance <= 1) return; + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + // We working on full image. + int xMin = 0; + int xMax = Width; + int yMin = 0; + int yMax = Height; + + // If we working in preview mode, else we using the preview area. + if ( pArea.isValid() ) + { + xMin = pArea.x(); + xMax = pArea.x() + pArea.width(); + yMin = pArea.y(); + yMax = pArea.y() + pArea.height(); + } + + int h, w, nh, nw, r; + int sumR, sumG, sumB, nCount; + double lfRadius, lfNewRadius, lfRadMax, lfAngle; + + Digikam::DColor color; + int offset; + + lfRadMax = sqrt (Height * Height + Width * Width); + + // number of added pixels + nCount = 0; + + // we have reached the main loop + for (h = yMin; !m_cancel && (h < yMax); h++) + { + for (w = xMin; !m_cancel && (w < xMax); w++) + { + // ...we enter this loop to sum the bits + + // we initialize the variables + sumR = sumG = sumB = nCount = 0; + + nw = X - w; + nh = Y - h; + + lfRadius = sqrt (nw * nw + nh * nh); + lfAngle = atan2 ((double)nh, (double)nw); + lfNewRadius = (lfRadius * Distance) / lfRadMax; + + for (r = 0; !m_cancel && (r <= lfNewRadius); r++) + { + // we need to calc the positions + nw = (int)(X - (lfRadius - r) * cos (lfAngle)); + nh = (int)(Y - (lfRadius - r) * sin (lfAngle)); + + if (IsInside(Width, Height, nw, nh)) + { + // read color + offset = GetOffset(Width, nw, nh, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // we sum the bits + sumR += color.red(); + sumG += color.green(); + sumB += color.blue(); + nCount++; + } + } + + if (nCount == 0) nCount = 1; + + // calculate pointer + offset = GetOffset(Width, w, h, bytesDepth); + // read color to preserve alpha + color.setColor(data + offset, sixteenBit); + + // now, we have to calc the arithmetic average + color.setRed (sumR / nCount); + color.setGreen(sumG / nCount); + color.setBlue (sumB / nCount); + + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)(h - yMin) * 100.0) / (yMax - yMin)); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the radialBlur effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * X, Y => Center of radial in the image + * Distance => Distance value + * pArea => Preview area. + * + * Theory => Similar to RadialBlur from Photoshop, its an amazing effect + * Very easy to understand but a little hard to implement. + * We have all the image and find the center pixel. Now, we analize + * all the pixels and calc the radius from the center and find the + * angle. After this, we sum this pixel with others with the same + * radius, but different angles. Here I'm using degrees angles. + */ +void BlurFX::radialBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, int Distance, TQRect pArea) +{ + if (Distance <= 1) return; + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + // We working on full image. + int xMin = 0; + int xMax = Width; + int yMin = 0; + int yMax = Height; + + // If we working in preview mode, else we using the preview area. + if ( pArea.isValid() ) + { + xMin = pArea.x(); + xMax = pArea.x() + pArea.width(); + yMin = pArea.y(); + yMax = pArea.y() + pArea.height(); + } + + int sumR, sumG, sumB, nw, nh; + double Radius, Angle, AngleRad; + + Digikam::DColor color; + int offset; + + double *nMultArray = new double[Distance * 2 + 1]; + + for (int i = -Distance; i <= Distance; i++) + nMultArray[i + Distance] = i * ANGLE_RATIO; + + // number of added pixels + int nCount = 0; + + // we have reached the main loop + + for (int h = yMin; !m_cancel && (h < yMax); h++) + { + for (int w = xMin; !m_cancel && (w < xMax); w++) + { + // ...we enter this loop to sum the bits + + // we initialize the variables + sumR = sumG = sumB = nCount = 0; + + nw = X - w; + nh = Y - h; + + Radius = sqrt (nw * nw + nh * nh); + AngleRad = atan2 ((double)nh, (double)nw); + + for (int a = -Distance; !m_cancel && (a <= Distance); a++) + { + Angle = AngleRad + nMultArray[a + Distance]; + // we need to calc the positions + nw = (int)(X - Radius * cos (Angle)); + nh = (int)(Y - Radius * sin (Angle)); + + if (IsInside(Width, Height, nw, nh)) + { + // read color + offset = GetOffset(Width, nw, nh, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // we sum the bits + sumR += color.red(); + sumG += color.green(); + sumB += color.blue(); + nCount++; + } + } + + if (nCount == 0) nCount = 1; + + // calculate pointer + offset = GetOffset(Width, w, h, bytesDepth); + // read color to preserve alpha + color.setColor(data + offset, sixteenBit); + + // now, we have to calc the arithmetic average + color.setRed (sumR / nCount); + color.setGreen(sumG / nCount); + color.setBlue (sumB / nCount); + + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)(h - yMin) * 100.0) / (yMax - yMin)); + + if (progress%5 == 0) + postProgress(progress); + } + + delete [] nMultArray; +} + +/* Function to apply the focusBlur effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * BlurRadius => Radius of blurred image. + * BlendRadius => Radius of blending effect. + * bInversed => If true, invert focus effect. + * pArea => Preview area. + * + */ +void BlurFX::focusBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int X, int Y, int BlurRadius, int BlendRadius, + bool bInversed, TQRect pArea) +{ + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + // We working on full image. + int xMin = 0; + int xMax = Width; + int yMin = 0; + int yMax = Height; + + // If we working in preview mode, else we using the preview area. + if ( pArea.isValid() ) + { + xMin = pArea.x(); + xMax = pArea.x() + pArea.width(); + yMin = pArea.y(); + yMax = pArea.y() + pArea.height(); + } + + if (pArea.isValid()) + { + //UNTESTED (unused) + + // We do not have access to the loop of the Gaussian blur, + // so we have to cut the image that we run the effect on. + int xMinBlur = xMin - BlurRadius; + int xMaxBlur = xMax + BlurRadius; + int yMinBlur = yMin - BlurRadius; + int yMaxBlur = yMax + BlurRadius; + Digikam::DImg areaImage = orgImage->copy(xMinBlur, yMaxBlur, xMaxBlur - xMinBlur, yMaxBlur - yMinBlur); + + Digikam::DImgGaussianBlur(this, *orgImage, *destImage, 10, 75, BlurRadius); + + // I am unsure about differences of 1 pixel + destImage->bitBltImage(&areaImage, xMinBlur, yMinBlur); + destImage->bitBltImage(orgImage, 0, 0, Width, yMinBlur, 0, 0); + destImage->bitBltImage(orgImage, 0, yMinBlur, xMinBlur, yMaxBlur - yMinBlur, 0, yMinBlur); + destImage->bitBltImage(orgImage, xMaxBlur + 1, yMinBlur, Width - xMaxBlur - 1, yMaxBlur - yMinBlur, yMaxBlur, yMinBlur); + destImage->bitBltImage(orgImage, 0, yMaxBlur + 1, Width, Height - yMaxBlur - 1, 0, yMaxBlur); + + postProgress(80); + } + else + { + // copy bits for blurring + memcpy(pResBits, data, orgImage->numBytes()); + + // Gaussian blur using the BlurRadius parameter. + Digikam::DImgGaussianBlur(this, *orgImage, *destImage, 10, 80, BlurRadius); + } + + // Blending results. + + int nBlendFactor; + double lfRadius; + int offset; + + Digikam::DColor colorOrgImage, colorBlurredImage; + int alpha; + uchar *ptr; + + // get composer for default blending + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffNone); + + int nh = 0, nw = 0; + + for (int h = yMin; !m_cancel && (h < yMax); h++) + { + nh = Y - h; + + for (int w = xMin; !m_cancel && (w < xMax); w++) + { + nw = X - w; + + lfRadius = sqrt (nh * nh + nw * nw); + + if (sixteenBit) + nBlendFactor = LimitValues16 ((int)(65535.0 * lfRadius / (double)BlendRadius)); + else + nBlendFactor = LimitValues8 ((int)(255.0 * lfRadius / (double)BlendRadius)); + + // Read color values + offset = GetOffset(Width, w, h, bytesDepth); + ptr = pResBits + offset; + colorOrgImage.setColor(data + offset, sixteenBit); + colorBlurredImage.setColor(ptr, sixteenBit); + + // Preserve alpha + alpha = colorOrgImage.alpha(); + + // In normal mode, the image is focused in the middle + // and less focused towards the border. + // In inversed mode, the image is more focused towards the edge + // and less focused in the middle. + // This is achieved by swapping src and dest while blending. + if (bInversed) + { + // set blending alpha value as src alpha. Original value is stored above. + colorOrgImage.setAlpha(nBlendFactor); + // compose colors, writing to dest - colorBlurredImage + composer->compose(colorBlurredImage, colorOrgImage); + // restore alpha + colorBlurredImage.setAlpha(alpha); + // write color to destination + colorBlurredImage.setPixel(ptr); + } + else + { + // set blending alpha value as src alpha. Original value is stored above. + colorBlurredImage.setAlpha(nBlendFactor); + // compose colors, writing to dest - colorOrgImage + composer->compose(colorOrgImage, colorBlurredImage); + // restore alpha + colorOrgImage.setAlpha(alpha); + // write color to destination + colorOrgImage.setPixel(ptr); + } + } + + // Update the progress bar in dialog. + progress = (int) (80.0 + ((double)(h - yMin) * 20.0) / (yMax - yMin)); + + if (progress%5 == 0) + postProgress(progress); + } + + delete composer; +} + +/* Function to apply the farBlur effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Distance => Distance value + * + * Theory => This is an interesting effect, the blur is applied in that + * way: (the value "1" means pixel to be used in a blur calc, ok?) + * e.g. With distance = 2 + * |1|1|1|1|1| + * |1|0|0|0|1| + * |1|0|C|0|1| + * |1|0|0|0|1| + * |1|1|1|1|1| + * We sum all the pixels with value = 1 and apply at the pixel with* + * the position "C". + */ +void BlurFX::farBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance) +{ + if (Distance < 1) return; + + // we need to create our kernel + // e.g. distance = 3, so kernel={3 1 1 2 1 1 3} + + int *nKern = new int[Distance * 2 + 1]; + + for (int i = 0; i < Distance * 2 + 1; i++) + { + // the first element is 3 + if (i == 0) + nKern[i] = 2; + // the center element is 2 + else if (i == Distance) + nKern[i] = 3; + // the last element is 3 + else if (i == Distance * 2) + nKern[i] = 3; + // all other elements will be 1 + else + nKern[i] = 1; + } + + // now, we apply a convolution with kernel + MakeConvolution(orgImage, destImage, Distance, nKern); + + // we must delete to free memory + delete [] nKern; +} + +/* Function to apply the SmartBlur effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Radius => blur matrix radius. + * Strenght => Color strenght. + * + * Theory => Similar to SmartBlur from Photoshop, this function has the + * same engine as Blur function, but, in a matrix with n + * dimentions, we take only colors that pass by sensibility filter + * The result is a clean image, not totally blurred, but a image + * with correction between pixels. + */ + +void BlurFX::smartBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Strength) +{ + if (Radius <= 0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int progress; + int sumR, sumG, sumB, nCount, w, h, a; + + int StrengthRange = Strength; + if (sixteenBit) + StrengthRange = (StrengthRange + 1) * 256 - 1; + + Digikam::DColor color, radiusColor, radiusColorBlur; + int offset, loopOffset; + + uchar* pBlur = new uchar[orgImage->numBytes()]; + + // We need to copy our bits to blur bits + + memcpy (pBlur, data, orgImage->numBytes()); + + // we have reached the main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + // we initialize the variables + sumR = sumG = sumB = nCount = 0; + + // read color + offset = GetOffset(Width, w, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // ...we enter this loop to sum the bits + for (a = -Radius; !m_cancel && (a <= Radius); a++) + { + // verify if is inside the rect + if (IsInside( Width, Height, w + a, h)) + { + // read color + loopOffset = GetOffset(Width, w+a, h, bytesDepth); + radiusColor.setColor(data + loopOffset, sixteenBit); + + // now, we have to check if is inside the sensibility filter + if (IsColorInsideTheRange (color.red(), color.green(), color.blue(), + radiusColor.red(), radiusColor.green(), radiusColor.blue(), + StrengthRange)) + { + // finally we sum the bits + sumR += radiusColor.red(); + sumG += radiusColor.green(); + sumB += radiusColor.blue(); + } + else + { + // finally we sum the bits + sumR += color.red(); + sumG += color.green(); + sumB += color.blue(); + } + + // increment counter + nCount++; + } + } + + // now, we have to calc the arithmetic average + color.setRed (sumR / nCount); + color.setGreen(sumG / nCount); + color.setBlue (sumB / nCount); + + // write color to destination + color.setPixel(pBlur + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 50.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + // we have reached the second part of main loop + + for (w = 0; !m_cancel && (w < Width); w++) + { + for (h = 0;!m_cancel && ( h < Height); h++) + { + // we initialize the variables + sumR = sumG = sumB = nCount = 0; + + // read color + offset = GetOffset(Width, w, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // ...we enter this loop to sum the bits + for (a = -Radius; !m_cancel && (a <= Radius); a++) + { + // verify if is inside the rect + if (IsInside( Width, Height, w, h + a)) + { + // read color + loopOffset = GetOffset(Width, w, h+a, bytesDepth); + radiusColor.setColor(data + loopOffset, sixteenBit); + + // now, we have to check if is inside the sensibility filter + if (IsColorInsideTheRange (color.red(), color.green(), color.blue(), + radiusColor.red(), radiusColor.green(), radiusColor.blue(), + StrengthRange)) + { + radiusColorBlur.setColor(pBlur + loopOffset, sixteenBit); + // finally we sum the bits + sumR += radiusColorBlur.red(); + sumG += radiusColorBlur.green(); + sumB += radiusColorBlur.blue(); + } + else + { + // finally we sum the bits + sumR += color.red(); + sumG += color.green(); + sumB += color.blue(); + } + + // increment counter + nCount++; + } + } + + // now, we have to calc the arithmetic average + color.setRed (sumR / nCount); + color.setGreen(sumG / nCount); + color.setBlue (sumB / nCount); + + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (50.0 + ((double)w * 50.0) / Width); + + if (progress%5 == 0) + postProgress(progress); + } + + // now, we must free memory + delete [] pBlur; +} + +/* Function to apply the motionBlur effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Distance => Distance value + * Angle => Angle direction (degrees) + * + * Theory => Similar to MotionBlur from Photoshop, the engine is very + * simple to undertand, we take a pixel (duh!), with the angle we + * will taking near pixels. After this we blur (add and do a + * division). + */ +void BlurFX::motionBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance, double Angle) +{ + if (Distance == 0) return; + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + Digikam::DColor color; + int offset; + + // we try to avoid division by 0 (zero) + if (Angle == 0.0) Angle = 360.0; + + int sumR, sumG, sumB, nCount, nw, nh; + double nAngX, nAngY; + + // we initialize cos and sin for a best performance + nAngX = cos ((2.0 * M_PI) / (360.0 / Angle)); + nAngY = sin ((2.0 * M_PI) / (360.0 / Angle)); + + // total of bits to be taken is given by this formula + nCount = Distance * 2 + 1; + + // we will alloc size and calc the possible results + int *lpXArray = new int[nCount]; + int *lpYArray = new int[nCount]; + + for (int i = 0; i < nCount; i++) + { + lpXArray[i] = lround( (double)(i - Distance) * nAngX); + lpYArray[i] = lround( (double)(i - Distance) * nAngY); + } + + // we have reached the main loop + + for (int h = 0; !m_cancel && (h < Height); h++) + { + for (int w = 0; !m_cancel && (w < Width); w++) + { + // we initialize the variables + sumR = sumG = sumB = 0; + + // ...we enter this loop to sum the bits + for (int a = -Distance; !m_cancel && (a <= Distance); a++) + { + // we need to calc the positions + nw = w + lpXArray[a + Distance]; + nh = h + lpYArray[a + Distance]; + + offset = GetOffsetAdjusted(Width, Height, nw, nh, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // we sum the bits + sumR += color.red(); + sumG += color.green(); + sumB += color.blue(); + } + + if (nCount == 0) nCount = 1; + + // calculate pointer + offset = GetOffset(Width, w, h, bytesDepth); + // read color to preserve alpha + color.setColor(data + offset, sixteenBit); + + // now, we have to calc the arithmetic average + color.setRed (sumR / nCount); + color.setGreen(sumG / nCount); + color.setBlue (sumB / nCount); + + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + delete [] lpXArray; + delete [] lpYArray; +} + +/* Function to apply the softenerBlur effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * + * Theory => An interesting blur-like function. In dark tones we apply a + * blur with 3x3 dimentions, in light tones, we apply a blur with + * 5x5 dimentions. Easy, hun? + */ +void BlurFX::softenerBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage) +{ + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int SomaR = 0, SomaG = 0, SomaB = 0; + int Gray; + + Digikam::DColor color, colorSoma; + int offset, offsetSoma; + + int grayLimit = sixteenBit ? 32767 : 127; + + for (int h = 0; !m_cancel && (h < Height); h++) + { + for (int w = 0; !m_cancel && (w < Width); w++) + { + SomaR = SomaG = SomaB = 0; + + offset = GetOffset(Width, w, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + + Gray = (color.red() + color.green() + color.blue()) / 3; + + if (Gray > grayLimit) + { + // 7x7 + for (int a = -3; !m_cancel && (a <= 3); a++) + { + for (int b = -3; !m_cancel && (b <= 3); b++) + { + if ((h + a < 0) || (w + b < 0)) + offsetSoma = offset; + else + offsetSoma = GetOffset(Width, (w + Lim_Max (w, b, Width)), + (h + Lim_Max (h, a, Height)), bytesDepth); + colorSoma.setColor(data + offsetSoma, sixteenBit); + + SomaR += colorSoma.red(); + SomaG += colorSoma.green(); + SomaB += colorSoma.blue(); + } + } + + // 7*7 = 49 + color.setRed (SomaR / 49); + color.setGreen(SomaG / 49); + color.setBlue (SomaB / 49); + color.setPixel(pResBits + offset); + } + else + { + // 3x3 + for (int a = -1; !m_cancel && (a <= 1); a++) + { + for (int b = -1; !m_cancel && (b <= 1); b++) + { + if ((h + a < 0) || (w + b < 0)) + offsetSoma = offset; + else + offsetSoma = GetOffset(Width, (w + Lim_Max (w, b, Width)), + (h + Lim_Max (h, a, Height)), bytesDepth); + colorSoma.setColor(data + offsetSoma, sixteenBit); + + SomaR += colorSoma.red(); + SomaG += colorSoma.green(); + SomaB += colorSoma.blue(); + } + } + + // 3*3 = 9 + color.setRed (SomaR / 9); + color.setGreen(SomaG / 9); + color.setBlue (SomaB / 9); + color.setPixel(pResBits + offset); + } + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the shake blur effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Distance => Distance between layers (from origin) + * + * Theory => Similar to Fragment effect from Photoshop. We create 4 layers + * each one has the same distance from the origin, but have + * different positions (top, button, left and right), with these 4 + * layers, we join all the pixels. + */ +void BlurFX::shakeBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance) +{ + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + Digikam::DColor color, colorLayer, color1, color2, color3, color4; + int offset, offsetLayer; + + int numBytes = orgImage->numBytes(); + uchar* Layer1 = new uchar[numBytes]; + uchar* Layer2 = new uchar[numBytes]; + uchar* Layer3 = new uchar[numBytes]; + uchar* Layer4 = new uchar[numBytes]; + + int h, w, nw, nh; + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + offsetLayer = GetOffset(Width, w, h, bytesDepth); + + nh = (h + Distance >= Height) ? Height - 1 : h + Distance; + offset = GetOffset(Width, w, nh, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(Layer1 + offsetLayer); + + nh = (h - Distance < 0) ? 0 : h - Distance; + offset = GetOffset(Width, w, nh, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(Layer2 + offsetLayer); + + nw = (w + Distance >= Width) ? Width - 1 : w + Distance; + offset = GetOffset(Width, nw, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(Layer3 + offsetLayer); + + nw = (w - Distance < 0) ? 0 : w - Distance; + offset = GetOffset(Width, nw, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(Layer4 + offsetLayer); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 50.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + for (int h = 0; !m_cancel && (h < Height); h++) + { + for (int w = 0; !m_cancel && (w < Width); w++) + { + offset = GetOffset(Width, w, h, bytesDepth); + // read original data to preserve alpha + color.setColor(data + offset, sixteenBit); + // read colors from all four layers + color1.setColor(Layer1 + offset, sixteenBit); + color2.setColor(Layer2 + offset, sixteenBit); + color3.setColor(Layer3 + offset, sixteenBit); + color4.setColor(Layer4 + offset, sixteenBit); + + // set color components of resulting color + color.setRed ( (color1.red() + color2.red() + color3.red() + color4.red()) / 4 ); + color.setGreen( (color1.green() + color2.green() + color3.green() + color4.green()) / 4 ); + color.setBlue ( (color1.blue() + color2.blue() + color3.blue() + color4.blue()) / 4 ); + + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (50.0 + ((double)h * 50.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + delete [] Layer1; + delete [] Layer2; + delete [] Layer3; + delete [] Layer4; +} + +/* Function to apply the frostGlass effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Frost => Frost value + * + * Theory => Similar to Diffuse effect, but the random byte is defined + * in a matrix. Diffuse uses a random diagonal byte. + */ +void BlurFX::frostGlass(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Frost) +{ + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + Frost = (Frost < 1) ? 1 : (Frost > 10) ? 10 : Frost; + + int h, w; + + Digikam::DColor color; + int offset; + + // Randomize. + + TQDateTime dt = TQDateTime::currentDateTime(); + TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); + uint seed = dt.secsTo(Y2000); + + int range = sixteenBit ? 65535 : 255; + + // it is a huge optimizsation to allocate these here once + uchar *IntensityCount = new uchar[range + 1]; + uint *AverageColorR = new uint[range + 1]; + uint *AverageColorG = new uint[range + 1]; + uint *AverageColorB = new uint[range + 1]; + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + offset = GetOffset(Width, w, h, bytesDepth); + // read color to preserve alpha + color.setColor(data + offset, sixteenBit); + + // get random color from surrounding of w|h + color = RandomColor (data, Width, Height, sixteenBit, bytesDepth, + w, h, Frost, color.alpha(), &seed, range, IntensityCount, + AverageColorR, AverageColorG, AverageColorB); + + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + delete [] IntensityCount; + delete [] AverageColorR; + delete [] AverageColorG; + delete [] AverageColorB; +} + +/* Function to apply the mosaic effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Size => Size of mosaic . + * + * Theory => Ok, you can find some mosaic effects on PSC, but this one + * has a great feature, if you see a mosaic in other code you will + * see that the corner pixel doesn't change. The explanation is + * simple, the color of the mosaic is the same as the first pixel + * get. Here, the color of the mosaic is the same as the mosaic + * center pixel. + * Now the function scan the rows from the top (like photoshop). + */ +void BlurFX::mosaic(Digikam::DImg *orgImage, Digikam::DImg *destImage, int SizeW, int SizeH) +{ + int progress; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + // we need to check for valid values + if (SizeW < 1) SizeW = 1; + if (SizeH < 1) SizeH = 1; + if ((SizeW == 1) && (SizeH == 1)) return; + + Digikam::DColor color; + int offsetCenter, offset; + + // this loop will never look for transparent colors + + for (int h = 0; !m_cancel && (h < Height); h += SizeH) + { + for (int w = 0; !m_cancel && (w < Width); w += SizeW) + { + // we have to find the center pixel for mosaic's rectangle + + offsetCenter = GetOffsetAdjusted(Width, Height, w + (SizeW / 2), h + (SizeH / 2), bytesDepth); + color.setColor(data + offsetCenter, sixteenBit); + + // now, we fill the mosaic's rectangle with the center pixel color + + for (int subw = w; !m_cancel && (subw <= w + SizeW); subw++) + { + for (int subh = h; !m_cancel && (subh <= h + SizeH); subh++) + { + // if is inside... + if (IsInside(Width, Height, subw, subh)) + { + // set color + offset = GetOffset(Width, subw, subh, bytesDepth); + color.setPixel(pResBits + offset); + } + } + } + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to get a color in a matriz with a determined size + * + * Bits => Bits array + * Width => Image width + * Height => Image height + * X => Position horizontal + * Y => Position vertical + * Radius => The radius of the matrix to be created + * + * Theory => This function takes from a distinct matrix a random color + */ +Digikam::DColor BlurFX::RandomColor(uchar *Bits, int Width, int Height, bool sixteenBit, int bytesDepth, + int X, int Y, int Radius, + int alpha, uint *randomSeed, int range, uchar *IntensityCount, + uint *AverageColorR, uint *AverageColorG, uint *AverageColorB) +{ + Digikam::DColor color; + int offset; + + int w, h, counter = 0; + + int I; + + // For 16 bit we have a problem here because this takes 255 times longer, + // and the algorithm is really slow for 16 bit, but I think this cannot be avoided. + memset(IntensityCount, 0, range ); + memset(AverageColorR, 0, range ); + memset(AverageColorG, 0, range ); + memset(AverageColorB, 0, range ); + + for (w = X - Radius; !m_cancel && (w <= X + Radius); w++) + { + for (h = Y - Radius; !m_cancel && (h <= Y + Radius); h++) + { + if ((w >= 0) && (w < Width) && (h >= 0) && (h < Height)) + { + offset = GetOffset(Width, w, h, bytesDepth); + color.setColor(Bits + offset, sixteenBit); + I = GetIntensity (color.red(), color.green(), color.blue()); + IntensityCount[I]++; + counter++; + + if (IntensityCount[I] == 1) + { + AverageColorR[I] = color.red(); + AverageColorG[I] = color.green(); + AverageColorB[I] = color.blue(); + } + else + { + AverageColorR[I] += color.red(); + AverageColorG[I] += color.green(); + AverageColorB[I] += color.blue(); + } + } + } + } + + // check for m_cancel here before entering the do loop (will crash with SIGFPE otherwise) + if (m_cancel) + return Digikam::DColor(0, 0, 0, 0, sixteenBit); + + int RandNumber, count, Index, ErrorCount = 0; + int J; + + do + { + RandNumber = abs( (int)((rand_r(randomSeed) + 1) * ((double)counter / (1 + (double) RAND_MAX))) ); + count = 0; + Index = 0; + + do + { + count += IntensityCount[Index]; + Index++; + } + while (count < RandNumber && !m_cancel); + + J = Index - 1; + ErrorCount++; + } + while ((IntensityCount[J] == 0) && (ErrorCount <= counter) && !m_cancel); + + if (m_cancel) + return Digikam::DColor(0, 0, 0, 0, sixteenBit); + + + color.setSixteenBit(sixteenBit); + color.setAlpha(alpha); + + if (ErrorCount >= counter) + { + color.setRed (AverageColorR[J] / counter); + color.setGreen(AverageColorG[J] / counter); + color.setBlue (AverageColorB[J] / counter); + } + else + { + color.setRed (AverageColorR[J] / IntensityCount[J]); + color.setGreen(AverageColorG[J] / IntensityCount[J]); + color.setBlue (AverageColorB[J] / IntensityCount[J]); + } + + return color; +} + +/* Function to simple convolve a unique pixel with a determined radius + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Radius => kernel radius, e.g. rad=1, so array will be 3X3 + * Kernel => kernel array to apply. + * + * Theory => I've worked hard here, but I think this is a very smart + * way to convolve an array, its very hard to explain how I reach + * this, but the trick here its to store the sum used by the + * previous pixel, so we sum with the other pixels that wasn't get + */ +void BlurFX::MakeConvolution (Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Kernel[]) +{ + if (Radius <= 0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pOutBits = destImage->bits(); + + int progress; + int n, h, w; + + int nSumR, nSumG, nSumB, nCount; + int nKernelWidth = Radius * 2 + 1; + int range = sixteenBit ? 65536 : 256; + Digikam::DColor color; + int offset; + + uchar* pBlur = new uchar[orgImage->numBytes()]; + + // We need to copy our bits to blur bits + + memcpy (pBlur, data, orgImage->numBytes()); + + // We need to alloc a 2d array to help us to store the values + + int** arrMult = Alloc2DArray (nKernelWidth, range); + + for (int i = 0; i < nKernelWidth; i++) + for (int j = 0; j < range; j++) + arrMult[i][j] = j * Kernel[i]; + + // Now, we enter in the main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + // initialize the variables + nSumR = nSumG = nSumB = nCount = 0; + + // first of all, we need to blur the horizontal lines + + for (n = -Radius; !m_cancel && (n <= Radius); n++) + { + // if is inside... + if (IsInside (Width, Height, w + n, h)) + { + // read color from orgImage + offset = GetOffset(Width, w+n, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + + // finally, we sum the pixels using a method similar to assigntables + nSumR += arrMult[n + Radius][color.red()]; + nSumG += arrMult[n + Radius][color.green()]; + nSumB += arrMult[n + Radius][color.blue()]; + + // we need to add the kernel value to the counter + nCount += Kernel[n + Radius]; + } + } + + if (nCount == 0) nCount = 1; + + // calculate pointer + offset = GetOffset(Width, w, h, bytesDepth); + // read color from orgImage to preserve alpha + color.setColor(data + offset, sixteenBit); + + // now, we have to calc the arithmetic average + if (sixteenBit) + { + color.setRed (LimitValues16(nSumR / nCount)); + color.setGreen(LimitValues16(nSumG / nCount)); + color.setBlue (LimitValues16(nSumB / nCount)); + } + else + { + color.setRed (LimitValues8(nSumR / nCount)); + color.setGreen(LimitValues8(nSumG / nCount)); + color.setBlue (LimitValues8(nSumB / nCount)); + } + + // write color to blur bits + color.setPixel(pBlur + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 50.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + + // We enter in the second main loop + for (w = 0; !m_cancel && (w < Width); w++) + { + for (h = 0; !m_cancel && (h < Height); h++) + { + // initialize the variables + nSumR = nSumG = nSumB = nCount = 0; + + // first of all, we need to blur the vertical lines + for (n = -Radius; !m_cancel && (n <= Radius); n++) + { + // if is inside... + if (IsInside(Width, Height, w, h + n)) + { + // read color from blur bits + offset = GetOffset(Width, w, h+n, bytesDepth); + color.setColor(pBlur + offset, sixteenBit); + + // finally, we sum the pixels using a method similar to assigntables + nSumR += arrMult[n + Radius][color.red()]; + nSumG += arrMult[n + Radius][color.green()]; + nSumB += arrMult[n + Radius][color.blue()]; + + // we need to add the kernel value to the counter + nCount += Kernel[n + Radius]; + } + } + + if (nCount == 0) nCount = 1; + + // calculate pointer + offset = GetOffset(Width, w, h, bytesDepth); + // read color from orgImage to preserve alpha + color.setColor(data + offset, sixteenBit); + + // now, we have to calc the arithmetic average + if (sixteenBit) + { + color.setRed (LimitValues16(nSumR / nCount)); + color.setGreen(LimitValues16(nSumG / nCount)); + color.setBlue (LimitValues16(nSumB / nCount)); + } + else + { + color.setRed (LimitValues8(nSumR / nCount)); + color.setGreen(LimitValues8(nSumG / nCount)); + color.setBlue (LimitValues8(nSumB / nCount)); + } + + // write color to destination + color.setPixel(pOutBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (50.0 + ((double)w * 50.0) / Width); + + if (progress%5 == 0) + postProgress(progress); + } + + // now, we must free memory + Free2DArray (arrMult, nKernelWidth); + delete [] pBlur; +} + +} // NameSpace DigikamBlurFXImagesPlugin diff --git a/src/imageplugins/blurfx/blurfx.h b/src/imageplugins/blurfx/blurfx.h new file mode 100644 index 00000000..4a4397b4 --- /dev/null +++ b/src/imageplugins/blurfx/blurfx.h @@ -0,0 +1,191 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Blur FX threaded image filter. + * + * Copyright 2005-2007 by Gilles Caulier + * Copyright 2006-2007 by Marcel Wiesweg + * + * Original Blur algorithms copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BLURFX_H +#define BLURFX_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamBlurFXImagesPlugin +{ + +class BlurFX : public Digikam::DImgThreadedFilter +{ + +public: + + BlurFX(Digikam::DImg *orgImage, TQObject *parent=0, int blurFXType=ZoomBlur, + int distance=100, int level=45); + + ~BlurFX(){}; + +public: + + enum BlurFXTypes + { + ZoomBlur=0, + RadialBlur, + FarBlur, + MotionBlur, + SoftenerBlur, + ShakeBlur, + FocusBlur, + SmartBlur, + FrostGlass, + Mosaic + }; + +private: // BlurFX filter data. + + int m_blurFXType; + int m_distance; + int m_level; + +private: // BlurFX filter methods. + + virtual void filterImage(void); + + // Backported from ImageProcessing version 1 + void softenerBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage); + void shakeBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance); + void frostGlass(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Frost); + + // Backported from ImageProcessing version 2 + void zoomBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int X, int Y, int Distance, TQRect pArea=TQRect()); + void radialBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int X, int Y, int Distance, TQRect pArea=TQRect()); + void focusBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int X, int Y, int BlurRadius, int BlendRadius, + bool bInversed=false, TQRect pArea=TQRect()); + void farBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance); + void motionBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Distance, double Angle=0.0); + void smartBlur(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Strenght); + void mosaic(Digikam::DImg *orgImage, Digikam::DImg *destImage, int SizeW, int SizeH); + +private: // Internal filter methods. + + void MakeConvolution(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Radius, int Kernel[]); + + Digikam::DColor RandomColor(uchar *Bits, int Width, int Height, bool sixteenBit, int bytesDepth, + int X, int Y, int Radius, + int alpha, uint *randomSeed, int range, uchar *IntensityCount, + uint *AverageColorR, uint *AverageColorG, uint *AverageColorB); + + // Return the limit defined the max and min values. + inline int Lim_Max(int Now, int Up, int Max) + { + --Max; + while (Now > Max - Up) --Up; + return (Up); + }; + + // Return the luminance (Y) component of YIQ color model. + inline int GetIntensity (int R, int G, int B) + { + return (int)(R * 0.3 + G * 0.59 + B * 0.11); + }; + + // function to allocate a 2d array + inline int** Alloc2DArray (int Columns, int Rows) + { + // First, we declare our future 2d array to be returned + int** lpcArray = NULL; + + // Now, we alloc the main pointer with Columns + lpcArray = new int*[Columns]; + + for (int i = 0; i < Columns; i++) + lpcArray[i] = new int[Rows]; + + return (lpcArray); + } + + // Function to deallocates the 2d array previously created + inline void Free2DArray (int** lpcArray, int Columns) + { + // loop to dealocate the columns + for (int i = 0; i < Columns; i++) + delete [] lpcArray[i]; + + // now, we delete the main pointer + delete [] lpcArray; + } + + inline bool IsInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; + + inline uchar LimitValues8(int ColorValue) + { + if (ColorValue > 255) ColorValue = 255; + if (ColorValue < 0) ColorValue = 0; + return ((uchar) ColorValue); + }; + + + inline int LimitValues16(int ColorValue) + { + if (ColorValue > 65535) ColorValue = 65535; + if (ColorValue < 0) ColorValue = 0; + return ColorValue; + }; + + inline int GetOffset(int Width, int X, int Y, int bytesDepth) + { + return (Y * Width * bytesDepth) + (X * bytesDepth); + }; + + inline int GetOffsetAdjusted(int Width, int Height, int X, int Y, int bytesDepth) + { + X = (X < 0) ? 0 : ((X >= Width ) ? (Width - 1) : X); + Y = (Y < 0) ? 0 : ((Y >= Height) ? (Height - 1) : Y); + return GetOffset(Width, X, Y, bytesDepth); + }; + + inline bool IsColorInsideTheRange (int cR, int cG, int cB, + int nR, int nG, int nB, + int Range) + { + if ((nR >= cR - Range) && (nR <= cR + Range)) + if ((nG >= cG - Range) && (nG <= cG + Range)) + if ((nB >= cB - Range) && (nB <= cB + Range)) + return (true); + + return (false); + }; + +}; + +} // NameSpace DigikamBlurFXImagesPlugin + +#endif /* BLURFX_H */ diff --git a/src/imageplugins/blurfx/blurfxtool.cpp b/src/imageplugins/blurfx/blurfxtool.cpp new file mode 100644 index 00000000..2998dbde --- /dev/null +++ b/src/imageplugins/blurfx/blurfxtool.cpp @@ -0,0 +1,402 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "blurfx.h" +#include "blurfxtool.h" +#include "blurfxtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamBlurFXImagesPlugin +{ + +BlurFXTool::BlurFXTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("blurfx"); + setToolName(i18n("Blur FX")); + setToolIcon(SmallIcon("blurfx")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 6, 1); + + m_effectTypeLabel = new TQLabel(i18n("Type:"), m_gboxSettings->plainPage()); + + m_effectType = new RComboBox(m_gboxSettings->plainPage()); + m_effectType->insertItem(i18n("Zoom Blur")); + m_effectType->insertItem(i18n("Radial Blur")); + m_effectType->insertItem(i18n("Far Blur")); + m_effectType->insertItem(i18n("Motion Blur")); + m_effectType->insertItem(i18n("Softener Blur")); + m_effectType->insertItem(i18n("Skake Blur")); + m_effectType->insertItem(i18n("Focus Blur")); + m_effectType->insertItem(i18n("Smart Blur")); + m_effectType->insertItem(i18n("Frost Glass")); + m_effectType->insertItem(i18n("Mosaic")); + m_effectType->setDefaultItem(BlurFX::ZoomBlur); + TQWhatsThis::add( m_effectType, i18n("

    Select the blurring effect to apply to the image.

    " + "Zoom Blur: blurs the image along radial lines starting from " + "a specified center point. This simulates the blur of a zooming camera.

    " + "Radial Blur: blurs the image by rotating the pixels around " + "the specified center point. This simulates the blur of a rotating camera.

    " + "Far Blur: blurs the image by using far pixels. This simulates the blur " + "of an unfocalized camera lens.

    " + "Motion Blur: blurs the image by moving the pixels horizontally. " + "This simulates the blur of a linear moving camera.

    " + "Softener Blur: blurs the image softly in dark tones and hardly in light " + "tones. This gives images a dreamy and glossy soft focus effect. It's ideal " + "for creating romantic portraits, glamour photographs, or giving images a warm " + "and subtle glow.

    " + "Skake Blur: blurs the image by skaking randomly the pixels. " + "This simulates the blur of a random moving camera.

    " + "Focus Blur: blurs the image corners to reproduce the astigmatism distortion " + "of a lens.

    " + "Smart Blur: finds the edges of color in your image and blurs them without " + "muddying the rest of the image.

    " + "Frost Glass: blurs the image by randomly disperse light coming through " + "a frosted glass.

    " + "Mosaic: divides the photograph into rectangular cells and then " + "recreates it by filling those cells with average pixel value.")); + + m_distanceLabel = new TQLabel(i18n("Distance:"), m_gboxSettings->plainPage()); + m_distanceInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_distanceInput->setRange(0, 100, 1); + m_distanceInput->setDefaultValue(3); + TQWhatsThis::add( m_distanceInput, i18n("

    Set here the blur distance in pixels.")); + + m_levelLabel = new TQLabel(i18n("Level:"), m_gboxSettings->plainPage()); + m_levelInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_levelInput->setRange(0, 360, 1); + m_levelInput->setDefaultValue(128); + TQWhatsThis::add( m_levelInput, i18n("

    This value controls the level to use with the current effect.")); + + grid->addMultiCellWidget(m_effectTypeLabel, 0, 0, 0, 1); + grid->addMultiCellWidget(m_effectType, 1, 1, 0, 1); + grid->addMultiCellWidget(m_distanceLabel, 2, 2, 0, 1); + grid->addMultiCellWidget(m_distanceInput, 3, 3, 0, 1); + grid->addMultiCellWidget(m_levelLabel, 4, 4, 0, 1); + grid->addMultiCellWidget(m_levelInput, 5, 5, 0, 1); + grid->setRowStretch(6, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "blurfx Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); + + connect(m_distanceInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +BlurFXTool::~BlurFXTool() +{ +} + +void BlurFXTool::renderingFinished(void) +{ + + m_effectTypeLabel->setEnabled(true); + m_effectType->setEnabled(true); + m_distanceInput->setEnabled(true); + m_distanceLabel->setEnabled(true); + + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FarBlur: + case BlurFX::ShakeBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + break; + + case BlurFX::MotionBlur: + case BlurFX::FocusBlur: + case BlurFX::SmartBlur: + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + break; + + case BlurFX::SoftenerBlur: + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + break; + } +} + +void BlurFXTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("blurfx Tool"); + m_effectType->blockSignals(true); + m_distanceInput->blockSignals(true); + m_levelInput->blockSignals(true); + + m_effectType->setCurrentItem(config->readNumEntry("EffectType", m_effectType->defaultItem())); + m_distanceInput->setValue(config->readNumEntry("DistanceAjustment", m_distanceInput->defaultValue())); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", m_levelInput->defaultValue())); + + m_effectType->blockSignals(false); + m_distanceInput->blockSignals(false); + m_levelInput->blockSignals(false); +} + +void BlurFXTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("blurfx Tool"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("DistanceAjustment", m_distanceInput->value()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void BlurFXTool::slotResetSettings() +{ + m_effectType->blockSignals(true); + m_distanceInput->blockSignals(true); + m_levelInput->blockSignals(true); + + m_effectType->slotReset(); + m_distanceInput->slotReset(); + m_levelInput->slotReset(); + + m_effectType->blockSignals(false); + m_distanceInput->blockSignals(false); + m_levelInput->blockSignals(false); + + slotEffectTypeChanged(m_effectType->defaultItem()); +} + +void BlurFXTool::slotEffectTypeChanged(int type) +{ + m_distanceInput->setEnabled(true); + m_distanceLabel->setEnabled(true); + + m_distanceInput->blockSignals(true); + m_levelInput->blockSignals(true); + + m_distanceInput->setRange(0, 200, 1); + m_distanceInput->setValue(100); + m_levelInput->setRange(0, 360, 1); + m_levelInput->setValue(45); + + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + switch (type) + { + case BlurFX::ZoomBlur: + break; + + case BlurFX::RadialBlur: + case BlurFX::FrostGlass: + m_distanceInput->setRange(0, 10, 1); + m_distanceInput->setValue(3); + break; + + case BlurFX::FarBlur: + m_distanceInput->setRange(0, 20, 1); + m_distanceInput->input()->setMaxValue(20); + m_distanceInput->setValue(10); + break; + + case BlurFX::MotionBlur: + case BlurFX::FocusBlur: + m_distanceInput->setRange(0, 100, 1); + m_distanceInput->setValue(20); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + break; + + case BlurFX::SoftenerBlur: + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + break; + + case BlurFX::ShakeBlur: + m_distanceInput->setRange(0, 100, 1); + m_distanceInput->setValue(20); + break; + + case BlurFX::SmartBlur: + m_distanceInput->setRange(0, 20, 1); + m_distanceInput->setValue(3); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + m_levelInput->setRange(0, 255, 1); + m_levelInput->setValue(128); + break; + + case BlurFX::Mosaic: + m_distanceInput->setRange(0, 50, 1); + m_distanceInput->setValue(3); + break; + } + + m_distanceInput->blockSignals(false); + m_levelInput->blockSignals(false); + + slotEffect(); +} + +void BlurFXTool::prepareEffect() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + DImg image; + + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FocusBlur: + { + ImageIface iface(0, 0); + image = *iface.getOriginalImg(); + break; + } + + case BlurFX::FarBlur: + case BlurFX::MotionBlur: + case BlurFX::SoftenerBlur: + case BlurFX::ShakeBlur: + case BlurFX::SmartBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + image = m_previewWidget->getOriginalRegionImage(); + break; + } + + int t = m_effectType->currentItem(); + int d = m_distanceInput->value(); + int l = m_levelInput->value(); + + setFilter(dynamic_cast(new BlurFX(&image, this, t, d, l))); +} + +void BlurFXTool::prepareFinal() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + int t = m_effectType->currentItem(); + int d = m_distanceInput->value(); + int l = m_levelInput->value(); + + ImageIface iface(0, 0); + setFilter(dynamic_cast(new BlurFX(iface.getOriginalImg(), this, t, d, l))); +} + +void BlurFXTool::putPreviewData() +{ + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FocusBlur: + { + TQRect pRect = m_previewWidget->getOriginalImageRegionToRender(); + DImg destImg = filter()->getTargetImage().copy(pRect); + m_previewWidget->setPreviewImage(destImg); + break; + } + case BlurFX::FarBlur: + case BlurFX::MotionBlur: + case BlurFX::SoftenerBlur: + case BlurFX::ShakeBlur: + case BlurFX::SmartBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + m_previewWidget->setPreviewImage(filter()->getTargetImage()); + break; + } +} + +void BlurFXTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Blur Effects"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamBlurFXImagesPlugin diff --git a/src/imageplugins/blurfx/blurfxtool.h b/src/imageplugins/blurfx/blurfxtool.h new file mode 100644 index 00000000..9726e65d --- /dev/null +++ b/src/imageplugins/blurfx/blurfxtool.h @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BLURFXTOOL_H +#define BLURFXTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamBlurFXImagesPlugin +{ + +class BlurFXTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + BlurFXTool(TQObject *parent); + ~BlurFXTool(); + +private slots: + + void slotEffectTypeChanged(int type); + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_effectTypeLabel; + TQLabel *m_distanceLabel; + TQLabel *m_levelLabel; + + KDcrawIface::RComboBox *m_effectType; + + KDcrawIface::RIntNumInput *m_distanceInput; + KDcrawIface::RIntNumInput *m_levelInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamBlurFXImagesPlugin + +#endif /* BLURFXTOOL_H */ diff --git a/src/imageplugins/blurfx/digikamimageplugin_blurfx.desktop b/src/imageplugins/blurfx/digikamimageplugin_blurfx.desktop new file mode 100644 index 00000000..e7968a80 --- /dev/null +++ b/src/imageplugins/blurfx/digikamimageplugin_blurfx.desktop @@ -0,0 +1,49 @@ +[Desktop Entry] +Name=ImagePlugin_BlurFX +Name[bg]=ПриÑтавка за Ñнимки - Ефекти за замъглÑване +Name[el]=ΠÏόσθετοΕικόνας_ΕφέΘολώματος +Name[fi]=Sumennus +Name[hr]=Zamućenje +Name[it]=PluginImmagini_EffettiDiSfocatura +Name[nl]=Afbeeldingsplugin_Vervaageffect +Name[sr]=Ефекти замућења +Name[sr@Latn]=Efekti zamućenja +Name[sv]=Insticksprogram för oskärpeeffekt +Name[tr]=ResimEklentisi_BulanıklaÅŸtır +Name[xx]=xxImagePlugin_BlurFXxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Blur special effects plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam Ñ ÐµÑ„ÐµÐºÑ‚Ð¸ за замъглÑване на Ñнимки +Comment[ca]=Connector pel digiKam d'efectes especials de difuminat +Comment[da]=Digikam plugin med specialeffekter for udviskning +Comment[de]=digiKam-Modul zum Erzeugen von speziellen Unschärfe-Effekten +Comment[el]=ΠÏόσθετο ειδικών εφέ θολώματος για το digiKam +Comment[es]=Plugin para digiKam con efectos especiales de difusión +Comment[et]=DigiKami spetsiaalsete hägustamisefektide plugin +Comment[fa]=وصلۀ جلوه‌های ویژۀ محو برای digiKam +Comment[fi]=Sumennustehosteita +Comment[gl]=Un plugin de digiKam para efeitos especiais de borrón +Comment[hr]=digiKam dodatak za efekt zamućenja +Comment[is]=Ãforrit fyrir digiKam sem mýkir eða afskerpir myndir +Comment[it]=Plugin degli effetti speciali di sfocatura per digiKam +Comment[ja]=digiKam ã¼ã‹ã—特殊効果プラグイン +Comment[nds]=digiKam-Moduul för't Opstellen vun Weekteek-Effekten +Comment[nl]=Digikam-plugin voor vervaageffect +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਬਲੱਰ (ਧà©à©°à¨§à¨²à¨¾à¨ªà¨¨) ਖਾਸ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka specjalnych efektów rozmycia do programu digiKam +Comment[pt]=Um 'plugin' do digiKam para efeitos especiais de borrão +Comment[pt_BR]=Plugin de efeitos especiais de desfocalização +Comment[ru]=Модуль Ñпециальных Ñффектов Ñ€Ð°Ð·Ð¼Ñ‹Ñ‚Ð¸Ñ Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre Å¡peciálne efekty rozmazania +Comment[sr]=Прикључак ефеката замућења за digiKam +Comment[sr@Latn]=PrikljuÄak efekata zamućenja za digiKam +Comment[sv]=Digikam insticksprogram med specialeffekter för oskärpa +Comment[tr]=digiKam için bulanıklaÅŸtırma eklentisi +Comment[uk]=Втулок Ñпеціальних ефектів Ñ€Ð¾Ð·Ð¼Ð¸Ð²Ð°Ð½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng che má» cho digiKam +Comment[xx]=xxBlur special effects plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_blurfx +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/blurfx/digikamimageplugin_blurfx_ui.rc b/src/imageplugins/blurfx/digikamimageplugin_blurfx_ui.rc new file mode 100644 index 00000000..085b4c68 --- /dev/null +++ b/src/imageplugins/blurfx/digikamimageplugin_blurfx_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/blurfx/imageeffect_blurfx.cpp b/src/imageplugins/blurfx/imageeffect_blurfx.cpp new file mode 100644 index 00000000..62bcd525 --- /dev/null +++ b/src/imageplugins/blurfx/imageeffect_blurfx.cpp @@ -0,0 +1,388 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "blurfx.h" +#include "imageeffect_blurfx.h" +#include "imageeffect_blurfx.moc" + +namespace DigikamBlurFXImagesPlugin +{ + +ImageEffect_BlurFX::ImageEffect_BlurFX(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Apply Blurring Special Effect to Photograph"), + "blurfx", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Blur Effects"), + digikam_version, + I18N_NOOP("A digiKam image plugin to apply blurring special effect " + "to an image."), + TDEAboutData::License_GPL, + "(c) 2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Blurring algorithms"), + "pieter dot voloshyn at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 5, 1, 0, spacingHint()); + + m_effectTypeLabel = new TQLabel(i18n("Type:"), gboxSettings); + + m_effectType = new TQComboBox( false, gboxSettings ); + m_effectType->insertItem( i18n("Zoom Blur") ); + m_effectType->insertItem( i18n("Radial Blur") ); + m_effectType->insertItem( i18n("Far Blur") ); + m_effectType->insertItem( i18n("Motion Blur") ); + m_effectType->insertItem( i18n("Softener Blur") ); + m_effectType->insertItem( i18n("Skake Blur") ); + m_effectType->insertItem( i18n("Focus Blur") ); + m_effectType->insertItem( i18n("Smart Blur") ); + m_effectType->insertItem( i18n("Frost Glass") ); + m_effectType->insertItem( i18n("Mosaic") ); + TQWhatsThis::add( m_effectType, i18n("

    Select the blurring effect to apply to the image.

    " + "Zoom Blur: blurs the image along radial lines starting from " + "a specified center point. This simulates the blur of a zooming camera.

    " + "Radial Blur: blurs the image by rotating the pixels around " + "the specified center point. This simulates the blur of a rotating camera.

    " + "Far Blur: blurs the image by using far pixels. This simulates the blur " + "of an unfocalized camera lens.

    " + "Motion Blur: blurs the image by moving the pixels horizontally. " + "This simulates the blur of a linear moving camera.

    " + "Softener Blur: blurs the image softly in dark tones and hardly in light " + "tones. This gives images a dreamy and glossy soft focus effect. It's ideal " + "for creating romantic portraits, glamour photographs, or giving images a warm " + "and subtle glow.

    " + "Skake Blur: blurs the image by skaking randomly the pixels. " + "This simulates the blur of a random moving camera.

    " + "Focus Blur: blurs the image corners to reproduce the astigmatism distortion " + "of a lens.

    " + "Smart Blur: finds the edges of color in your image and blurs them without " + "muddying the rest of the image.

    " + "Frost Glass: blurs the image by randomly disperse light coming through " + "a frosted glass.

    " + "Mosaic: divides the photograph into rectangular cells and then " + "recreates it by filling those cells with average pixel value.")); + gridSettings->addMultiCellWidget(m_effectTypeLabel, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_effectType, 1, 1, 0, 1); + + m_distanceLabel = new TQLabel(i18n("Distance:"), gboxSettings); + m_distanceInput = new KIntNumInput(gboxSettings); + m_distanceInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_distanceInput, i18n("

    Set here the blur distance in pixels.")); + + gridSettings->addMultiCellWidget(m_distanceLabel, 2, 2, 0, 1); + gridSettings->addMultiCellWidget(m_distanceInput, 3, 3, 0, 1); + + m_levelLabel = new TQLabel(i18n("Level:"), gboxSettings); + m_levelInput = new KIntNumInput(gboxSettings); + m_levelInput->setRange(0, 360, 1, true); + TQWhatsThis::add( m_levelInput, i18n("

    This value controls the level to use with the current effect.")); + + gridSettings->addMultiCellWidget(m_levelLabel, 4, 4, 0, 1); + gridSettings->addMultiCellWidget(m_levelInput, 5, 5, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); + + connect(m_distanceInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_BlurFX::~ImageEffect_BlurFX() +{ +} + +void ImageEffect_BlurFX::renderingFinished(void) +{ + + m_effectTypeLabel->setEnabled(true); + m_effectType->setEnabled(true); + m_distanceInput->setEnabled(true); + m_distanceLabel->setEnabled(true); + + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FarBlur: + case BlurFX::ShakeBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + break; + + case BlurFX::MotionBlur: + case BlurFX::FocusBlur: + case BlurFX::SmartBlur: + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + break; + + case BlurFX::SoftenerBlur: + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + break; + } +} + +void ImageEffect_BlurFX::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("blurfx Tool Dialog"); + m_effectType->blockSignals(true); + m_distanceInput->blockSignals(true); + m_levelInput->blockSignals(true); + m_effectType->setCurrentItem(config->readNumEntry("EffectType", BlurFX::ZoomBlur)); + m_distanceInput->setValue(config->readNumEntry("DistanceAjustment", 3)); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", 128)); + m_effectType->blockSignals(false); + m_distanceInput->blockSignals(false); + m_levelInput->blockSignals(false); +} + +void ImageEffect_BlurFX::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("blurfx Tool Dialog"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("DistanceAjustment", m_distanceInput->value()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + config->sync(); +} + +void ImageEffect_BlurFX::resetValues() +{ + m_effectType->setCurrentItem(BlurFX::ZoomBlur); + slotEffectTypeChanged(BlurFX::ZoomBlur); +} + +void ImageEffect_BlurFX::slotEffectTypeChanged(int type) +{ + m_distanceInput->setEnabled(true); + m_distanceLabel->setEnabled(true); + + m_distanceInput->blockSignals(true); + m_levelInput->blockSignals(true); + m_distanceInput->setRange(0, 200, 1, true); + m_distanceInput->setValue(100); + m_levelInput->setRange(0, 360, 1, true); + m_levelInput->setValue(45); + + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + switch (type) + { + case BlurFX::ZoomBlur: + break; + + case BlurFX::RadialBlur: + case BlurFX::FrostGlass: + m_distanceInput->setRange(0, 10, 1, true); + m_distanceInput->setValue(3); + break; + + case BlurFX::FarBlur: + m_distanceInput->setRange(0, 20, 1, true); + m_distanceInput->setMaxValue(20); + m_distanceInput->setValue(10); + break; + + case BlurFX::MotionBlur: + case BlurFX::FocusBlur: + m_distanceInput->setRange(0, 100, 1, true); + m_distanceInput->setValue(20); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + break; + + case BlurFX::SoftenerBlur: + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + break; + + case BlurFX::ShakeBlur: + m_distanceInput->setRange(0, 100, 1, true); + m_distanceInput->setValue(20); + break; + + case BlurFX::SmartBlur: + m_distanceInput->setRange(0, 20, 1, true); + m_distanceInput->setValue(3); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + m_levelInput->setRange(0, 255, 1, true); + m_levelInput->setValue(128); + break; + + case BlurFX::Mosaic: + m_distanceInput->setRange(0, 50, 1, true); + m_distanceInput->setValue(3); + break; + } + + m_distanceInput->blockSignals(false); + m_levelInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_BlurFX::prepareEffect() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + Digikam::DImg image; + + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FocusBlur: + { + Digikam::ImageIface iface(0, 0); + image = *iface.getOriginalImg(); + break; + } + + case BlurFX::FarBlur: + case BlurFX::MotionBlur: + case BlurFX::SoftenerBlur: + case BlurFX::ShakeBlur: + case BlurFX::SmartBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + image = m_imagePreviewWidget->getOriginalRegionImage(); + break; + } + + int t = m_effectType->currentItem(); + int d = m_distanceInput->value(); + int l = m_levelInput->value(); + + m_threadedFilter = dynamic_cast(new BlurFX(&image, this, t, d, l)); +} + +void ImageEffect_BlurFX::prepareFinal() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_distanceInput->setEnabled(false); + m_distanceLabel->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + + int t = m_effectType->currentItem(); + int d = m_distanceInput->value(); + int l = m_levelInput->value(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast(new BlurFX(iface.getOriginalImg(), this, t, d, l)); +} + +void ImageEffect_BlurFX::putPreviewData(void) +{ + switch (m_effectType->currentItem()) + { + case BlurFX::ZoomBlur: + case BlurFX::RadialBlur: + case BlurFX::FocusBlur: + { + TQRect pRect = m_imagePreviewWidget->getOriginalImageRegionToRender(); + Digikam::DImg destImg = m_threadedFilter->getTargetImage().copy(pRect); + m_imagePreviewWidget->setPreviewImage(destImg); + break; + } + case BlurFX::FarBlur: + case BlurFX::MotionBlur: + case BlurFX::SoftenerBlur: + case BlurFX::ShakeBlur: + case BlurFX::SmartBlur: + case BlurFX::FrostGlass: + case BlurFX::Mosaic: + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); + break; + } +} + +void ImageEffect_BlurFX::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Blur Effects"), + m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamBlurFXImagesPlugin + diff --git a/src/imageplugins/blurfx/imageeffect_blurfx.h b/src/imageplugins/blurfx/imageeffect_blurfx.h new file mode 100644 index 00000000..e2517d4f --- /dev/null +++ b/src/imageplugins/blurfx/imageeffect_blurfx.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_BLURFX_H +#define IMAGEEFFECT_BLURFX_H + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class TQComboBox; +class TQLabel; + +class KIntNumInput; + +namespace DigikamBlurFXImagesPlugin +{ + +class ImageEffect_BlurFX : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_BlurFX(TQWidget *parent); + ~ImageEffect_BlurFX(); + +private slots: + + void slotEffectTypeChanged(int type); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQComboBox *m_effectType; + + TQLabel *m_effectTypeLabel; + TQLabel *m_distanceLabel; + TQLabel *m_levelLabel; + + KIntNumInput *m_distanceInput; + KIntNumInput *m_levelInput; +}; + +} // NameSpace DigikamBlurFXImagesPlugin + +#endif /* IMAGEEFFECT_BLURFX_H */ diff --git a/src/imageplugins/blurfx/imageplugin_blurfx.cpp b/src/imageplugins/blurfx/imageplugin_blurfx.cpp new file mode 100644 index 00000000..3bbd05e0 --- /dev/null +++ b/src/imageplugins/blurfx/imageplugin_blurfx.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "blurfxtool.h" +#include "imageplugin_blurfx.h" +#include "imageplugin_blurfx.moc" + +using namespace DigikamBlurFXImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_blurfx, + KGenericFactory("digikamimageplugin_blurfx")); + +ImagePlugin_BlurFX::ImagePlugin_BlurFX(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_BlurFX") +{ + m_blurfxAction = new TDEAction(i18n("Blur Effects..."), "blurfx", 0, + this, TQ_SLOT(slotBlurFX()), + actionCollection(), "imageplugin_blurfx"); + + setXMLFile( "digikamimageplugin_blurfx_ui.rc" ); + + DDebug() << "ImagePlugin_BlurFX plugin loaded" << endl; +} + +ImagePlugin_BlurFX::~ImagePlugin_BlurFX() +{ +} + +void ImagePlugin_BlurFX::setEnabledActions(bool enable) +{ + m_blurfxAction->setEnabled(enable); +} + +void ImagePlugin_BlurFX::slotBlurFX() +{ + BlurFXTool *tool = new BlurFXTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/blurfx/imageplugin_blurfx.h b/src/imageplugins/blurfx/imageplugin_blurfx.h new file mode 100644 index 00000000..13df6534 --- /dev/null +++ b/src/imageplugins/blurfx/imageplugin_blurfx.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-09 + * Description : a plugin to apply Blur FX to images + * + * Copyright 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_BLURFX_H +#define IMAGEPLUGIN_BLURFX_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_BlurFX : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_BlurFX(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_BlurFX(); + + void setEnabledActions(bool enable); + +private slots: + + void slotBlurFX(); + +private: + + TDEAction *m_blurfxAction; +}; + +#endif /* IMAGEPLUGIN_BLURFX_H */ diff --git a/src/imageplugins/border/Makefile.am b/src/imageplugins/border/Makefile.am new file mode 100644 index 00000000..385acfab --- /dev/null +++ b/src/imageplugins/border/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO +SUBDIRS = patterns + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_border_la_SOURCES = imageplugin_border.cpp \ + bordertool.cpp border.cpp + +digikamimageplugin_border_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_border_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_border.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_border.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_border_ui.rc diff --git a/src/imageplugins/border/border.cpp b/src/imageplugins/border/border.cpp new file mode 100644 index 00000000..00726f58 --- /dev/null +++ b/src/imageplugins/border/border.cpp @@ -0,0 +1,393 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : border threaded image filter. + * + * Copyright 2005-2007 by Gilles Caulier + * Copyright 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "border.h" + +namespace DigikamBorderImagesPlugin +{ + +Border::Border(Digikam::DImg *image, TQObject *parent, int orgWidth, int orgHeight, + TQString borderPath, int borderType, float borderPercent, + Digikam::DColor solidColor, + Digikam::DColor niepceBorderColor, + Digikam::DColor niepceLineColor, + Digikam::DColor bevelUpperLeftColor, + Digikam::DColor bevelLowerRightColor, + Digikam::DColor decorativeFirstColor, + Digikam::DColor decorativeSecondColor) + : Digikam::DImgThreadedFilter(image, parent, "Border") +{ + m_orgWidth = orgWidth; + m_orgHeight = orgHeight; + m_orgRatio = (float)m_orgWidth / (float)m_orgHeight; + m_borderType = borderType; + m_borderPath = borderPath; + int size = (image->width() > image->height()) ? image->height() : image->width(); + m_borderMainWidth = (int)(size * borderPercent); + m_border2ndWidth = (int)(size * 0.005); + + // Clamp internal border with to 1 pixel to be visible with small image. + if (m_border2ndWidth < 1) m_border2ndWidth = 1; + + m_solidColor = solidColor; + m_niepceBorderColor = niepceBorderColor; + m_niepceLineColor = niepceLineColor; + m_bevelUpperLeftColor = bevelUpperLeftColor; + m_bevelLowerRightColor = bevelLowerRightColor; + m_decorativeFirstColor = decorativeFirstColor; + m_decorativeSecondColor = decorativeSecondColor; + + m_preserveAspectRatio = true; + + initFilter(); +} + +Border::Border(Digikam::DImg *orgImage, TQObject *parent, int orgWidth, int orgHeight, + TQString borderPath, int borderType, + int borderWidth1, int borderWidth2, int borderWidth3, int borderWidth4, + Digikam::DColor solidColor, + Digikam::DColor niepceBorderColor, + Digikam::DColor niepceLineColor, + Digikam::DColor bevelUpperLeftColor, + Digikam::DColor bevelLowerRightColor, + Digikam::DColor decorativeFirstColor, + Digikam::DColor decorativeSecondColor) + : Digikam::DImgThreadedFilter(orgImage, parent, "Border") +{ + m_orgWidth = orgWidth; + m_orgHeight = orgHeight; + + m_borderType = borderType; + m_borderWidth1 = borderWidth1; + m_borderWidth2 = borderWidth2; + m_borderWidth3 = borderWidth3; + m_borderWidth4 = borderWidth4; + + m_solidColor = solidColor; + m_niepceBorderColor = niepceBorderColor; + m_niepceLineColor = niepceLineColor; + m_bevelUpperLeftColor = bevelUpperLeftColor; + m_bevelLowerRightColor = bevelLowerRightColor; + m_decorativeFirstColor = decorativeFirstColor; + m_decorativeSecondColor = decorativeSecondColor; + + m_borderPath = borderPath; + + m_preserveAspectRatio = false; + + initFilter(); +} + +void Border::filterImage(void) +{ + switch (m_borderType) + { + case SolidBorder: + if (m_preserveAspectRatio) + solid(m_orgImage, m_destImage, m_solidColor, m_borderMainWidth); + else + solid2(m_orgImage, m_destImage, m_solidColor, m_borderWidth1); + break; + + case NiepceBorder: + if (m_preserveAspectRatio) + niepce(m_orgImage, m_destImage, m_niepceBorderColor, m_borderMainWidth, + m_niepceLineColor, m_border2ndWidth); + else + niepce2(m_orgImage, m_destImage, m_niepceBorderColor, m_borderWidth1, + m_niepceLineColor, m_borderWidth4); + break; + + case BeveledBorder: + if (m_preserveAspectRatio) + bevel(m_orgImage, m_destImage, m_bevelUpperLeftColor, + m_bevelLowerRightColor, m_borderMainWidth); + else + bevel2(m_orgImage, m_destImage, m_bevelUpperLeftColor, + m_bevelLowerRightColor, m_borderWidth1); + break; + + case PineBorder: + case WoodBorder: + case PaperBorder: + case ParqueBorder: + case IceBorder: + case LeafBorder: + case MarbleBorder: + case RainBorder: + case CratersBorder: + case DriedBorder: + case PinkBorder: + case StoneBorder: + case ChalkBorder: + case GraniteBorder: + case RockBorder: + case WallBorder: + if (m_preserveAspectRatio) + pattern(m_orgImage, m_destImage, m_borderMainWidth, + m_decorativeFirstColor, m_decorativeSecondColor, + m_border2ndWidth, m_border2ndWidth); + else + pattern2(m_orgImage, m_destImage, m_borderWidth1, + m_decorativeFirstColor, m_decorativeSecondColor, + m_borderWidth2, m_borderWidth2); + break; + } +} + +// -- Methods to preserve aspect ratio of image ------------------------------------------ + +void Border::solid(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth) +{ + if (m_orgWidth > m_orgHeight) + { + int height = src.height() + borderWidth*2; + dest = Digikam::DImg((int)(height*m_orgRatio), height, src.sixteenBit(), src.hasAlpha()); + dest.fill(fg); + dest.bitBltImage(&src, (dest.width()-src.width())/2, borderWidth); + } + else + { + int width = src.width() + borderWidth*2; + dest = Digikam::DImg(width, (int)(width/m_orgRatio), src.sixteenBit(), src.hasAlpha()); + dest.fill(fg); + dest.bitBltImage(&src, borderWidth, (dest.height()-src.height())/2); + } +} + +void Border::niepce(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, + int borderWidth, const Digikam::DColor &bg, int lineWidth) +{ + Digikam::DImg tmp; + solid(src, tmp, bg, lineWidth); + solid(tmp, dest, fg, borderWidth); +} + +void Border::bevel(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &topColor, + const Digikam::DColor &btmColor, int borderWidth) +{ + int width, height; + + if (m_orgWidth > m_orgHeight) + { + height = src.height() + borderWidth*2; + width = (int)(height*m_orgRatio); + } + else + { + width = src.width() + borderWidth*2; + height = (int)(width/m_orgRatio); + } + + dest = Digikam::DImg(width, height, src.sixteenBit(), src.hasAlpha()); + dest.fill(topColor); + + TQPointArray btTriangle(3); + btTriangle.setPoint(0, width, 0); + btTriangle.setPoint(1, 0, height); + btTriangle.setPoint(2, width, height); + TQRegion btRegion(btTriangle); + + for(int x=0 ; x < width ; x++) + { + for(int y=0 ; y < height ; y++) + { + if (btRegion.contains(TQPoint(x, y))) + dest.setPixelColor(x, y, btmColor); + } + } + + if (m_orgWidth > m_orgHeight) + { + dest.bitBltImage(&src, (dest.width()-src.width())/2, borderWidth); + } + else + { + dest.bitBltImage(&src, borderWidth, (dest.height()-src.height())/2); + } +} + +void Border::pattern(Digikam::DImg &src, Digikam::DImg &dest, int borderWidth, + const Digikam::DColor &firstColor, const Digikam::DColor &secondColor, + int firstWidth, int secondWidth) +{ + // Original image with the first solid border around. + Digikam::DImg tmp; + solid(src, tmp, firstColor, firstWidth); + + // Border tiled image using pattern with second solid border around. + int width, height; + + if (m_orgWidth > m_orgHeight) + { + height = tmp.height() + borderWidth*2; + width = (int)(height*m_orgRatio); + } + else + { + width = tmp.width() + borderWidth*2; + height = (int)(width/m_orgRatio); + } + + Digikam::DImg tmp2(width, height, tmp.sixteenBit(), tmp.hasAlpha()); + DDebug() << "Border File:" << m_borderPath << endl; + Digikam::DImg border(m_borderPath); + if ( border.isNull() ) + return; + + border.convertToDepthOfImage(&tmp2); + + for (int x = 0 ; x < width ; x+=border.width()) + for (int y = 0 ; y < height ; y+=border.height()) + tmp2.bitBltImage(&border, x, y); + + solid(tmp2, dest, secondColor, secondWidth); + + // Merge both images to one. + if (m_orgWidth > m_orgHeight) + { + dest.bitBltImage(&tmp, (dest.width()-tmp.width())/2, borderWidth); + } + else + { + dest.bitBltImage(&tmp, borderWidth, (dest.height()-tmp.height())/2); + } +} + +// -- Methods to not-preserve aspect ratio of image ------------------------------------------ + + +void Border::solid2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth) +{ + dest = Digikam::DImg(src.width() + borderWidth*2, src.height() + borderWidth*2, + src.sixteenBit(), src.hasAlpha()); + dest.fill(fg); + dest.bitBltImage(&src, borderWidth, borderWidth); +} + +void Border::niepce2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth, + const Digikam::DColor &bg, int lineWidth) +{ + Digikam::DImg tmp; + solid2(src, tmp, bg, lineWidth); + solid2(tmp, dest, fg, borderWidth); +} + +void Border::bevel2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &topColor, + const Digikam::DColor &btmColor, int borderWidth) +{ + int x, y; + int wc; + + dest = Digikam::DImg(src.width() + borderWidth*2, + src.height() + borderWidth*2, + src.sixteenBit(), src.hasAlpha()); + + // top + + for(y=0, wc = (int)dest.width()-1; y < borderWidth; ++y, --wc) + { + for(x=0; x < wc; ++x) + dest.setPixelColor(x, y, topColor); + + for(;x < (int)dest.width(); ++x) + dest.setPixelColor(x, y, btmColor); + } + + // left and right + + for(; y < (int)dest.height()-borderWidth; ++y) + { + for(x=0; x < borderWidth; ++x) + dest.setPixelColor(x, y, topColor); + + for(x = (int)dest.width()-1; x > (int)dest.width()-borderWidth-1; --x) + dest.setPixelColor(x, y, btmColor); + } + + // bottom + + for(wc = borderWidth; y < (int)dest.height(); ++y, --wc) + { + for(x=0; x < wc; ++x) + dest.setPixelColor(x, y, topColor); + + for(; x < (int)dest.width(); ++x) + dest.setPixelColor(x, y, btmColor); + } + + dest.bitBltImage(&src, borderWidth, borderWidth); +} + +void Border::pattern2(Digikam::DImg &src, Digikam::DImg &dest, int borderWidth, + const Digikam::DColor &firstColor, const Digikam::DColor &secondColor, + int firstWidth, int secondWidth) +{ + // Border tile. + + int w = m_orgWidth + borderWidth*2; + int h = m_orgHeight + borderWidth*2; + + DDebug() << "Border File:" << m_borderPath << endl; + Digikam::DImg border(m_borderPath); + if ( border.isNull() ) + return; + + Digikam::DImg borderImg(w, h, src.sixteenBit(), src.hasAlpha()); + border.convertToDepthOfImage(&borderImg); + + for (int x = 0 ; x < w ; x+=border.width()) + for (int y = 0 ; y < h ; y+=border.height()) + borderImg.bitBltImage(&border, x, y); + + // First line around the pattern tile. + Digikam::DImg tmp = borderImg.smoothScale(src.width() + borderWidth*2, + src.height() + borderWidth*2 ); + + solid2(tmp, dest, firstColor, firstWidth); + + // Second line around original image. + tmp.reset(); + solid2(src, tmp, secondColor, secondWidth); + + // Copy original image. + dest.bitBltImage(&tmp, borderWidth, borderWidth); +} + +} // NameSpace DigikamBorderImagesPlugin diff --git a/src/imageplugins/border/border.h b/src/imageplugins/border/border.h new file mode 100644 index 00000000..b213e27a --- /dev/null +++ b/src/imageplugins/border/border.h @@ -0,0 +1,151 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : border threaded image filter. + * + * Copyright 2005-2007 by Gilles Caulier + * Copyright 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BORDER_H +#define BORDER_H + +// TQt includes. + +#include +#include +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamBorderImagesPlugin +{ + +class Border : public Digikam::DImgThreadedFilter +{ + +public: + + enum BorderTypes + { + SolidBorder=0, + NiepceBorder, + BeveledBorder, + PineBorder, + WoodBorder, + PaperBorder, + ParqueBorder, + IceBorder, + LeafBorder, + MarbleBorder, + RainBorder, + CratersBorder, + DriedBorder, + PinkBorder, + StoneBorder, + ChalkBorder, + GraniteBorder, + RockBorder, + WallBorder + }; + +public: + + /** Constructor using settings to preserve aspect ratio of image. */ + Border(Digikam::DImg *orgImage, TQObject *parent=0, int orgWidth=0, int orgHeight=0, + TQString borderPath=TQString(), int borderType=SolidBorder, float borderPercent=0.1, + Digikam::DColor solidColor = Digikam::DColor(), + Digikam::DColor niepceBorderColor = Digikam::DColor(), + Digikam::DColor niepceLineColor = Digikam::DColor(), + Digikam::DColor bevelUpperLeftColor = Digikam::DColor(), + Digikam::DColor bevelLowerRightColor = Digikam::DColor(), + Digikam::DColor decorativeFirstColor = Digikam::DColor(), + Digikam::DColor decorativeSecondColor = Digikam::DColor()); + + /** Constructor using settings to not-preserve aspect ratio of image. */ + Border(Digikam::DImg *orgImage, TQObject *parent=0, int orgWidth=0, int orgHeight=0, + TQString borderPath=TQString(), int borderType=SolidBorder, + int borderWidth1=100, int borderWidth2=20, int borderWidth3=20, int borderWidth4=10, + Digikam::DColor solidColor = Digikam::DColor(), + Digikam::DColor niepceBorderColor = Digikam::DColor(), + Digikam::DColor niepceLineColor = Digikam::DColor(), + Digikam::DColor bevelUpperLeftColor = Digikam::DColor(), + Digikam::DColor bevelLowerRightColor = Digikam::DColor(), + Digikam::DColor decorativeFirstColor = Digikam::DColor(), + Digikam::DColor decorativeSecondColor = Digikam::DColor()); + + ~Border(){}; + +private: + + virtual void filterImage(void); + + + /** Methods to preserve aspect ratio of image. */ + void solid(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth); + void niepce(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth, + const Digikam::DColor &bg, int lineWidth); + void bevel(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &topColor, + const Digikam::DColor &btmColor, int borderWidth); + void pattern(Digikam::DImg &src, Digikam::DImg &dest, int borderWidth, const Digikam::DColor &firstColor, + const Digikam::DColor &secondColor, int firstWidth, int secondWidth); + + /** Methods to not-preserve aspect ratio of image. */ + void solid2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth); + void niepce2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &fg, int borderWidth, + const Digikam::DColor &bg, int lineWidth); + void bevel2(Digikam::DImg &src, Digikam::DImg &dest, const Digikam::DColor &topColor, + const Digikam::DColor &btmColor, int borderWidth); + void pattern2(Digikam::DImg &src, Digikam::DImg &dest, int borderWidth, const Digikam::DColor &firstColor, + const Digikam::DColor &secondColor, int firstWidth, int secondWidth); + +private: + + bool m_preserveAspectRatio; + + int m_orgWidth; + int m_orgHeight; + + int m_borderType; + + int m_borderWidth1; + int m_borderWidth2; + int m_borderWidth3; + int m_borderWidth4; + + int m_borderMainWidth; + int m_border2ndWidth; + + float m_orgRatio; + + TQString m_borderPath; + + Digikam::DColor m_solidColor; + Digikam::DColor m_niepceBorderColor; + Digikam::DColor m_niepceLineColor; + Digikam::DColor m_bevelUpperLeftColor; + Digikam::DColor m_bevelLowerRightColor; + Digikam::DColor m_decorativeFirstColor; + Digikam::DColor m_decorativeSecondColor; +}; + +} // NameSpace DigikamBorderImagesPlugin + +#endif /* BORDER_H */ diff --git a/src/imageplugins/border/bordertool.cpp b/src/imageplugins/border/bordertool.cpp new file mode 100644 index 00000000..160f5b0a --- /dev/null +++ b/src/imageplugins/border/bordertool.cpp @@ -0,0 +1,668 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "border.h" +#include "bordertool.h" +#include "bordertool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamBorderImagesPlugin +{ + +BorderTool::BorderTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("border"); + setToolName(i18n("Add Border")); + setToolIcon(SmallIcon("bordertool")); + + m_previewWidget = new ImageWidget("bordertool Tool", 0, TQString(), + false, ImageGuideWidget::HVGuideMode, false); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 11, 2); + + TQLabel *label1 = new TQLabel(i18n("Type:"), m_gboxSettings->plainPage()); + + m_borderType = new RComboBox(m_gboxSettings->plainPage()); + m_borderType->insertItem( i18n("Solid") ); + // Niepce is Real name. This is the first guy in the world to have built a camera. + m_borderType->insertItem( "Niepce" ); + m_borderType->insertItem( i18n("Beveled") ); + m_borderType->insertItem( i18n("Decorative Pine") ); + m_borderType->insertItem( i18n("Decorative Wood") ); + m_borderType->insertItem( i18n("Decorative Paper") ); + m_borderType->insertItem( i18n("Decorative Parquet") ); + m_borderType->insertItem( i18n("Decorative Ice") ); + m_borderType->insertItem( i18n("Decorative Leaf") ); + m_borderType->insertItem( i18n("Decorative Marble") ); + m_borderType->insertItem( i18n("Decorative Rain") ); + m_borderType->insertItem( i18n("Decorative Craters") ); + m_borderType->insertItem( i18n("Decorative Dried") ); + m_borderType->insertItem( i18n("Decorative Pink") ); + m_borderType->insertItem( i18n("Decorative Stone") ); + m_borderType->insertItem( i18n("Decorative Chalk") ); + m_borderType->insertItem( i18n("Decorative Granite") ); + m_borderType->insertItem( i18n("Decorative Rock") ); + m_borderType->insertItem( i18n("Decorative Wall") ); + m_borderType->setDefaultItem(Border::SolidBorder); + TQWhatsThis::add( m_borderType, i18n("

    Select the border type to add around the image.")); + + KSeparator *line1 = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + // ------------------------------------------------------------------- + + m_preserveAspectRatio = new TQCheckBox(m_gboxSettings->plainPage()); + m_preserveAspectRatio->setText(i18n("Preserve Aspect Ratio")); + TQWhatsThis::add(m_preserveAspectRatio, i18n("Enable this option if you want to preserve the aspect " + "ratio of the image. If enabled, the border width will be " + "in percent of the image size, else the border width will " + "in pixels.")); + + m_labelBorderPercent = new TQLabel(i18n("Width (%):"), m_gboxSettings->plainPage()); + m_borderPercent = new RIntNumInput(m_gboxSettings->plainPage()); + m_borderPercent->setRange(1, 50, 1); + m_borderPercent->setDefaultValue(10); + TQWhatsThis::add(m_borderPercent, i18n("

    Set here the border width in percent of the image size.")); + + m_labelBorderWidth = new TQLabel(i18n("Width (pixels):"), m_gboxSettings->plainPage()); + m_borderWidth = new RIntNumInput(m_gboxSettings->plainPage()); + m_borderWidth->setDefaultValue(100); + TQWhatsThis::add(m_borderWidth, i18n("

    Set here the border width in pixels to add around the image.")); + + ImageIface iface(0, 0); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + + if (w > h) + m_borderWidth->setRange(1, h/2, 1); + else + m_borderWidth->setRange(1, w/2, 1); + + KSeparator *line2 = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + // ------------------------------------------------------------------- + + m_labelForeground = new TQLabel(m_gboxSettings->plainPage()); + m_firstColorButton = new KColorButton( TQColor( 192, 192, 192 ), m_gboxSettings->plainPage() ); + m_labelBackground = new TQLabel(m_gboxSettings->plainPage()); + m_secondColorButton = new KColorButton( TQColor( 128, 128, 128 ), m_gboxSettings->plainPage() ); + + // ------------------------------------------------------------------- + + grid->addMultiCellWidget(label1, 0, 0, 0, 2); + grid->addMultiCellWidget(m_borderType, 1, 1, 0, 2); + grid->addMultiCellWidget(line1, 2, 2, 0, 2); + grid->addMultiCellWidget(m_preserveAspectRatio, 3, 3, 0, 2); + grid->addMultiCellWidget(m_labelBorderPercent, 4, 4, 0, 2); + grid->addMultiCellWidget(m_borderPercent, 5, 5, 0, 2); + grid->addMultiCellWidget(m_labelBorderWidth, 6, 6, 0, 2); + grid->addMultiCellWidget(m_borderWidth, 7, 7, 0, 2); + grid->addMultiCellWidget(line2, 8, 8, 0, 2); + grid->addMultiCellWidget(m_labelForeground, 9, 9, 0, 0); + grid->addMultiCellWidget(m_firstColorButton, 9, 9, 1, 2); + grid->addMultiCellWidget(m_labelBackground, 10, 10, 0, 0); + grid->addMultiCellWidget(m_secondColorButton, 10, 10, 1, 2); + grid->setRowStretch(11, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_preserveAspectRatio, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotPreserveAspectRatioToggled(bool))); + + connect(m_borderType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotBorderTypeChanged(int))); + + connect(m_borderPercent, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_borderWidth, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_firstColorButton, TQ_SIGNAL(changed(const TQColor &)), + this, TQ_SLOT(slotColorForegroundChanged(const TQColor &))); + + connect(m_secondColorButton, TQ_SIGNAL(changed(const TQColor &)), + this, TQ_SLOT(slotColorBackgroundChanged(const TQColor &))); +} + +BorderTool::~BorderTool() +{ +} + +void BorderTool::readSettings() +{ + m_borderType->blockSignals(true); + m_borderPercent->blockSignals(true); + m_borderWidth->blockSignals(true); + m_firstColorButton->blockSignals(true); + m_secondColorButton->blockSignals(true); + m_preserveAspectRatio->blockSignals(true); + + TDEConfig *config = kapp->config(); + config->setGroup("border Tool"); + + m_borderType->setCurrentItem(config->readNumEntry("Border Type", m_borderType->defaultItem())); + m_borderPercent->setValue(config->readNumEntry("Border Percent", m_borderPercent->defaultValue())); + m_borderWidth->setValue(config->readNumEntry("Border Width", m_borderWidth->defaultValue())); + m_preserveAspectRatio->setChecked(config->readBoolEntry("Preserve Aspect Ratio", true)); + + TQColor black(0, 0, 0); + TQColor white(255, 255, 255); + TQColor gray1(192, 192, 192); + TQColor gray2(128, 128, 128); + + m_solidColor = config->readColorEntry("Solid Color", &black); + m_niepceBorderColor = config->readColorEntry("Niepce Border Color", &white); + m_niepceLineColor = config->readColorEntry("Niepce Line Color", &black); + m_bevelUpperLeftColor = config->readColorEntry("Bevel Upper Left Color", &gray1); + m_bevelLowerRightColor = config->readColorEntry("Bevel Lower Right Color", &gray2); + m_decorativeFirstColor = config->readColorEntry("Decorative First Color", &black); + m_decorativeSecondColor = config->readColorEntry("Decorative Second Color", &black); + + m_borderType->blockSignals(false); + m_borderPercent->blockSignals(false); + m_borderWidth->blockSignals(false); + m_firstColorButton->blockSignals(false); + m_secondColorButton->blockSignals(false); + m_preserveAspectRatio->blockSignals(false); + + slotBorderTypeChanged(m_borderType->currentItem()); +} + +void BorderTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("border Tool"); + + config->writeEntry("Border Type", m_borderType->currentItem()); + config->writeEntry("Border Percent", m_borderPercent->value()); + config->writeEntry("Border Width", m_borderWidth->value()); + config->writeEntry("Preserve Aspect Ratio", m_preserveAspectRatio->isChecked()); + + config->writeEntry("Solid Color", m_solidColor); + config->writeEntry("Niepce Border Color", m_niepceBorderColor); + config->writeEntry("Niepce Line Color", m_niepceLineColor); + config->writeEntry("Bevel Upper Left Color", m_bevelUpperLeftColor); + config->writeEntry("Bevel Lower Right Color", m_bevelLowerRightColor); + config->writeEntry("Decorative First Color", m_decorativeFirstColor); + config->writeEntry("Decorative Second Color", m_decorativeSecondColor); + + m_previewWidget->writeSettings(); + + config->sync(); +} + +void BorderTool::slotResetSettings() +{ + m_borderType->blockSignals(true); + m_borderPercent->blockSignals(true); + m_borderWidth->blockSignals(true); + m_firstColorButton->blockSignals(true); + m_secondColorButton->blockSignals(true); + m_preserveAspectRatio->blockSignals(true); + + m_borderType->slotReset(); + m_borderPercent->slotReset(); + m_borderWidth->slotReset(); + m_preserveAspectRatio->setChecked(true); + + m_solidColor = TQColor(0, 0, 0); + m_niepceBorderColor = TQColor(255, 255, 255); + m_niepceLineColor = TQColor(0, 0, 0); + m_bevelUpperLeftColor = TQColor(192, 192, 192); + m_bevelLowerRightColor = TQColor(128, 128, 128); + m_decorativeFirstColor = TQColor(0, 0, 0); + m_decorativeSecondColor = TQColor(0, 0, 0); + + m_borderType->blockSignals(false); + m_borderPercent->blockSignals(false); + m_borderWidth->blockSignals(false); + m_firstColorButton->blockSignals(false); + m_secondColorButton->blockSignals(false); + m_preserveAspectRatio->blockSignals(false); + + slotBorderTypeChanged(Border::SolidBorder); +} + +void BorderTool::renderingFinished() +{ + m_preserveAspectRatio->setEnabled(true); + m_borderType->setEnabled(true); + m_borderPercent->setEnabled(true); + m_borderWidth->setEnabled(true); + m_firstColorButton->setEnabled(true); + m_secondColorButton->setEnabled(true); + toggleBorderSlider(m_preserveAspectRatio->isChecked()); +} + +void BorderTool::slotColorForegroundChanged(const TQColor &color) +{ + switch (m_borderType->currentItem()) + { + case Border::SolidBorder: + m_solidColor = color; + break; + + case Border::NiepceBorder: + m_niepceBorderColor = color; + break; + + case Border::BeveledBorder: + m_bevelUpperLeftColor = color; + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + m_decorativeFirstColor = color; + break; + } + + slotEffect(); +} + +void BorderTool::slotColorBackgroundChanged(const TQColor &color) +{ + switch (m_borderType->currentItem()) + { + case Border::SolidBorder: + m_solidColor = color; + break; + + case Border::NiepceBorder: + m_niepceLineColor = color; + break; + + case Border::BeveledBorder: + m_bevelLowerRightColor = color; + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + m_decorativeSecondColor = color; + break; + } + + slotEffect(); +} + +void BorderTool::slotBorderTypeChanged(int borderType) +{ + m_labelForeground->setText(i18n("First:")); + m_labelBackground->setText(i18n("Second:")); + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the foreground color of the border.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the Background color of the border.")); + m_firstColorButton->setEnabled(true); + m_secondColorButton->setEnabled(true); + m_labelForeground->setEnabled(true); + m_labelBackground->setEnabled(true); + m_borderPercent->setEnabled(true); + + switch (borderType) + { + case Border::SolidBorder: + m_firstColorButton->setColor( m_solidColor ); + m_secondColorButton->setEnabled(false); + m_labelBackground->setEnabled(false); + break; + + case Border::NiepceBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the main border.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the line.")); + m_firstColorButton->setColor( m_niepceBorderColor ); + m_secondColorButton->setColor( m_niepceLineColor ); + break; + + case Border::BeveledBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the upper left area.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the lower right area.")); + m_firstColorButton->setColor( m_bevelUpperLeftColor ); + m_secondColorButton->setColor( m_bevelLowerRightColor ); + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the first line.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the second line.")); + m_firstColorButton->setColor( m_decorativeFirstColor ); + m_secondColorButton->setColor( m_decorativeSecondColor ); + break; + } + + slotEffect(); +} + +void BorderTool::prepareEffect() +{ + m_borderType->setEnabled(false); + m_borderPercent->setEnabled(false); + m_borderWidth->setEnabled(false); + m_firstColorButton->setEnabled(false); + m_secondColorButton->setEnabled(false); + m_preserveAspectRatio->setEnabled(false); + + ImageIface* iface = m_previewWidget->imageIface(); + int orgWidth = iface->originalWidth(); + int orgHeight = iface->originalHeight(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sixteenBit = iface->previewSixteenBit(); + uchar *data = iface->getPreviewImage(); + DImg previewImage(w, h, sixteenBit, + iface->previewHasAlpha(), data); + delete [] data; + + int borderType = m_borderType->currentItem(); + float ratio = (float)w/(float)orgWidth; + int borderWidth = (int)((float)m_borderWidth->value()*ratio); + TQString border = getBorderPath( m_borderType->currentItem() ); + + if (m_preserveAspectRatio->isChecked()) + { + setFilter(dynamic_cast( + new Border(&previewImage, this, orgWidth, orgHeight, + border, borderType, m_borderPercent->value()/100.0, + DColor(m_solidColor, sixteenBit), + DColor(m_niepceBorderColor, sixteenBit), + DColor(m_niepceLineColor, sixteenBit), + DColor(m_bevelUpperLeftColor, sixteenBit), + DColor(m_bevelLowerRightColor, sixteenBit), + DColor(m_decorativeFirstColor, sixteenBit), + DColor(m_decorativeSecondColor, sixteenBit)))); + } + else + { + setFilter(dynamic_cast( + new Border(&previewImage, this, orgWidth, orgHeight, + border, borderType, borderWidth, + (int)(20.0*ratio), (int)(20.0*ratio), 3, + DColor(m_solidColor, sixteenBit), + DColor(m_niepceBorderColor, sixteenBit), + DColor(m_niepceLineColor, sixteenBit), + DColor(m_bevelUpperLeftColor, sixteenBit), + DColor(m_bevelLowerRightColor, sixteenBit), + DColor(m_decorativeFirstColor, sixteenBit), + DColor(m_decorativeSecondColor, sixteenBit)))); + } +} + +void BorderTool::prepareFinal() +{ + m_borderType->setEnabled(false); + m_borderPercent->setEnabled(false); + m_borderWidth->setEnabled(false); + m_firstColorButton->setEnabled(false); + m_secondColorButton->setEnabled(false); + + int borderType = m_borderType->currentItem(); + int borderWidth = m_borderWidth->value(); + float borderRatio = m_borderPercent->value()/100.0; + TQString border = getBorderPath( m_borderType->currentItem() ); + + ImageIface iface(0, 0); + int orgWidth = iface.originalWidth(); + int orgHeight = iface.originalHeight(); + bool sixteenBit = iface.previewSixteenBit(); + uchar *data = iface.getOriginalImage(); + DImg orgImage(orgWidth, orgHeight, sixteenBit, + iface.originalHasAlpha(), data); + delete [] data; + + if (m_preserveAspectRatio->isChecked()) + { + setFilter(dynamic_cast( + new Border(&orgImage, this, orgWidth, orgHeight, + border, borderType, borderRatio, + DColor(m_solidColor, sixteenBit), + DColor(m_niepceBorderColor, sixteenBit), + DColor(m_niepceLineColor, sixteenBit), + DColor(m_bevelUpperLeftColor, sixteenBit), + DColor(m_bevelLowerRightColor, sixteenBit), + DColor(m_decorativeFirstColor, sixteenBit), + DColor(m_decorativeSecondColor, sixteenBit)))); + } + else + { + setFilter(dynamic_cast( + new Border(&orgImage, this, orgWidth, orgHeight, + border, borderType, borderWidth, 15, 15, 10, + DColor(m_solidColor, sixteenBit), + DColor(m_niepceBorderColor, sixteenBit), + DColor(m_niepceLineColor, sixteenBit), + DColor(m_bevelUpperLeftColor, sixteenBit), + DColor(m_bevelLowerRightColor, sixteenBit), + DColor(m_decorativeFirstColor, sixteenBit), + DColor(m_decorativeSecondColor, sixteenBit)))); + } +} + +void BorderTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + + DImg imTemp = filter()->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + DImg imDest( w, h, filter()->getTargetImage().sixteenBit(), + filter()->getTargetImage().hasAlpha() ); + + imDest.fill( DColor(m_previewWidget->paletteBackgroundColor().rgb(), + filter()->getTargetImage().sixteenBit()) ); + + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage(imDest.bits()); + m_previewWidget->updatePreview(); +} + +void BorderTool::putFinalData() +{ + ImageIface iface(0, 0); + + DImg targetImage = filter()->getTargetImage(); + iface.putOriginalImage(i18n("Add Border"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +TQString BorderTool::getBorderPath(int border) +{ + TQString pattern; + + switch (border) + { + case Border::PineBorder: + pattern = "pine-pattern"; + break; + + case Border::WoodBorder: + pattern = "wood-pattern"; + break; + + case Border::PaperBorder: + pattern = "paper-pattern"; + break; + + case Border::ParqueBorder: + pattern = "parque-pattern"; + break; + + case Border::IceBorder: + pattern = "ice-pattern"; + break; + + case Border::LeafBorder: + pattern = "leaf-pattern"; + break; + + case Border::MarbleBorder: + pattern = "marble-pattern"; + break; + + case Border::RainBorder: + pattern = "rain-pattern"; + break; + + case Border::CratersBorder: + pattern = "craters-pattern"; + break; + + case Border::DriedBorder: + pattern = "dried-pattern"; + break; + + case Border::PinkBorder: + pattern = "pink-pattern"; + break; + + case Border::StoneBorder: + pattern = "stone-pattern"; + break; + + case Border::ChalkBorder: + pattern = "chalk-pattern"; + break; + + case Border::GraniteBorder: + pattern = "granit-pattern"; + break; + + case Border::RockBorder: + pattern = "rock-pattern"; + break; + + case Border::WallBorder: + pattern = "wall-pattern"; + break; + + default: + return TQString(); + } + + TDEGlobal::dirs()->addResourceType(pattern.ascii(), TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + return (TDEGlobal::dirs()->findResourceDir(pattern.ascii(), pattern + ".png") + pattern + ".png" ); +} + +void BorderTool::slotPreserveAspectRatioToggled(bool b) +{ + toggleBorderSlider(b); + slotTimer(); +} + +void BorderTool::toggleBorderSlider(bool b) +{ + m_borderPercent->setEnabled(b); + m_borderWidth->setEnabled(!b); + m_labelBorderPercent->setEnabled(b); + m_labelBorderWidth->setEnabled(!b); +} + +} // NameSpace DigikamBorderImagesPlugin diff --git a/src/imageplugins/border/bordertool.h b/src/imageplugins/border/bordertool.h new file mode 100644 index 00000000..a8ec5ccf --- /dev/null +++ b/src/imageplugins/border/bordertool.h @@ -0,0 +1,123 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BORDERTOOL_H +#define BORDERTOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; +class TQCheckBox; +class TQColor; + +class KColorButton; + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImageWidget; +} + +namespace DigikamBorderImagesPlugin +{ + +class BorderTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + BorderTool(TQObject *parent); + ~BorderTool(); + +private: + + TQString getBorderPath(int border); + +private slots: + + void slotPreserveAspectRatioToggled(bool); + void slotBorderTypeChanged(int borderType); + void slotColorForegroundChanged(const TQColor &color); + void slotColorBackgroundChanged(const TQColor &color); + void slotResetSettings(); + +private: + + void writeSettings(); + void readSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + void toggleBorderSlider(bool b); + +private: + + TQLabel *m_labelBorderPercent; + TQLabel *m_labelBorderWidth; + TQLabel *m_labelForeground; + TQLabel *m_labelBackground; + + TQCheckBox *m_preserveAspectRatio; + + TQColor m_solidColor; + TQColor m_niepceBorderColor; + TQColor m_niepceLineColor; + TQColor m_bevelUpperLeftColor; + TQColor m_bevelLowerRightColor; + TQColor m_decorativeFirstColor; + TQColor m_decorativeSecondColor; + + KDcrawIface::RComboBox *m_borderType; + + KDcrawIface::RIntNumInput *m_borderPercent; + KDcrawIface::RIntNumInput *m_borderWidth; + + KColorButton *m_firstColorButton; + KColorButton *m_secondColorButton; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamBorderImagesPlugin + +#endif /* BORDERTOOL_H */ diff --git a/src/imageplugins/border/digikamimageplugin_border.desktop b/src/imageplugins/border/digikamimageplugin_border.desktop new file mode 100644 index 00000000..45f32a7e --- /dev/null +++ b/src/imageplugins/border/digikamimageplugin_border.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_Border +Name[bg]=ПриÑтавка за Ñнимки - �амки +Name[da]=Billedplugin_Indramning +Name[el]=ΠÏόσθετοΕικόνας_ΠεÏίγÏαμμα +Name[fi]=Reunus +Name[hr]=Obrub +Name[it]=PluginImmagini_Bordo +Name[nl]=Afbeeldingsplugin_Rand +Name[sr]=Оквир +Name[sr@Latn]=Okvir +Name[sv]=Insticksprogram för bildkanter +Name[tr]=ResimEklentisi_Çerçeve +Name[xx]=xxImagePlugin_Borderxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Add border to image plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за добавÑне на рамки на Ñнимки +Comment[ca]=Connector pel digiKam per afegir una vora a la imatge +Comment[da]=Plugin til indramning af billeder i Digikam +Comment[de]=digiKam-Modul zum Hinzufügen eines Rahmens zu einem Bild +Comment[el]=ΠÏόσθετο Ï€Ïοσθήκης πεÏιγÏάμματος σε εικόνα για το digiKam +Comment[es]=Plugin de digiKam para añadir bordes a la imagen +Comment[et]=DigiKami pildile raami lisamise plugin +Comment[fa]=اÙزودن لبه به وصلۀ تصویر برای digiKam +Comment[fi]=Lisää kuvaan reunuksen +Comment[gl]=Un plugin de digiKam para engadir un contorno á imaxe +Comment[hr]=digiKam dodatak za dodavanje obruba +Comment[is]=Ãforrit fyrir digiKam sem gerir ramma á myndir +Comment[it]=Plugin per l'aggiunta di bordi a un'immagire per digiKam +Comment[ja]=digikam ç”»åƒè£…飾プラグイン +Comment[nds]=digiKam-Moduul för't Tofögen vun Ränners rund en Bild +Comment[nl]=Digikam-plugin voor het toevoegen van randen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਹਾਸ਼ੀਆ ਜੋੜਨ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam dodajÄ…ca ramkÄ™ do obrazu +Comment[pt]=Um 'plugin' do digiKam para adicionar um contorno à imagem +Comment[pt_BR]=Plugin de adicionar borda na imagem +Comment[ru]=Модуль digiKam Ð½Ð°Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ€Ð°Ð¼ÐºÐ¸ на изображение +Comment[sk]=digiKam plugin pridávajúci okraj obrázku +Comment[sr]=Прикључак за digiKam који додаје оквир на Ñлику +Comment[sr@Latn]=PrikljuÄak za digiKam koji dodaje okvir na sliku +Comment[sv]=Digikam insticksprogram för tillägg av kanter +Comment[tr]=digiKam için resme çerçeve eklenme eklentisi +Comment[uk]=Втулок digiKam Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ€Ð°Ð¼ÐºÐ¸ навколо Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ +Comment[vi]=Phần bổ sung thêm viá»n trên ảnh cho digiKam +Comment[xx]=xxAdd border to image plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_border +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/border/digikamimageplugin_border_ui.rc b/src/imageplugins/border/digikamimageplugin_border_ui.rc new file mode 100644 index 00000000..8cce0581 --- /dev/null +++ b/src/imageplugins/border/digikamimageplugin_border_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Decorate + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/border/imageeffect_border.cpp b/src/imageplugins/border/imageeffect_border.cpp new file mode 100644 index 00000000..9f3dd6c3 --- /dev/null +++ b/src/imageplugins/border/imageeffect_border.cpp @@ -0,0 +1,667 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "border.h" +#include "imageeffect_border.h" +#include "imageeffect_border.moc" + +namespace DigikamBorderImagesPlugin +{ + +ImageEffect_Border::ImageEffect_Border(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Add Border Around Photograph"), + "border", false, false, false, + Digikam::ImageGuideWidget::HVGuideMode) +{ + // No need Abort button action. + showButton(User1, false); + + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Add Border"), + digikam_version, + I18N_NOOP("A digiKam image plugin to add a border around an image."), + TDEAboutData::License_GPL, + "(c) 2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings, 10, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Type:"), gboxSettings); + + m_borderType = new TQComboBox( false, gboxSettings ); + m_borderType->insertItem( i18n("Solid") ); + // Niepce is Real name. This is the first guy in the world to have built a camera. + m_borderType->insertItem( "Niepce" ); + m_borderType->insertItem( i18n("Beveled") ); + m_borderType->insertItem( i18n("Decorative Pine") ); + m_borderType->insertItem( i18n("Decorative Wood") ); + m_borderType->insertItem( i18n("Decorative Paper") ); + m_borderType->insertItem( i18n("Decorative Parquet") ); + m_borderType->insertItem( i18n("Decorative Ice") ); + m_borderType->insertItem( i18n("Decorative Leaf") ); + m_borderType->insertItem( i18n("Decorative Marble") ); + m_borderType->insertItem( i18n("Decorative Rain") ); + m_borderType->insertItem( i18n("Decorative Craters") ); + m_borderType->insertItem( i18n("Decorative Dried") ); + m_borderType->insertItem( i18n("Decorative Pink") ); + m_borderType->insertItem( i18n("Decorative Stone") ); + m_borderType->insertItem( i18n("Decorative Chalk") ); + m_borderType->insertItem( i18n("Decorative Granite") ); + m_borderType->insertItem( i18n("Decorative Rock") ); + m_borderType->insertItem( i18n("Decorative Wall") ); + TQWhatsThis::add( m_borderType, i18n("

    Select the border type to add around the image.")); + + KSeparator *line1 = new KSeparator(Horizontal, gboxSettings); + + // ------------------------------------------------------------------- + + m_preserveAspectRatio = new TQCheckBox(gboxSettings); + m_preserveAspectRatio->setText(i18n("Preserve Aspect Ratio")); + TQWhatsThis::add(m_preserveAspectRatio, i18n("Enable this option if you want to preserve the aspect " + "ratio of the image. If enabled, the border width will be " + "in percent of the image size, else the border width will " + "in pixels.")); + + m_labelBorderPercent = new TQLabel(i18n("Width (%):"), gboxSettings); + m_borderPercent = new KIntNumInput(gboxSettings); + m_borderPercent->setRange(1, 50, 1, true); + TQWhatsThis::add(m_borderPercent, i18n("

    Set here the border width in percent of the image size.")); + + m_labelBorderWidth = new TQLabel(i18n("Width (pixels):"), gboxSettings); + m_borderWidth = new KIntNumInput(gboxSettings); + TQWhatsThis::add(m_borderWidth, i18n("

    Set here the border width in pixels to add around the image.")); + + Digikam::ImageIface iface(0, 0); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + + if (w > h) + m_borderWidth->setRange(1, h/2, 1, true); + else + m_borderWidth->setRange(1, w/2, 1, true); + + KSeparator *line2 = new KSeparator(Horizontal, gboxSettings); + + // ------------------------------------------------------------------- + + m_labelForeground = new TQLabel(gboxSettings); + m_firstColorButton = new KColorButton( TQColor( 192, 192, 192 ), gboxSettings ); + m_labelBackground = new TQLabel(gboxSettings); + m_secondColorButton = new KColorButton( TQColor( 128, 128, 128 ), gboxSettings ); + + // ------------------------------------------------------------------- + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 2); + gridSettings->addMultiCellWidget(m_borderType, 1, 1, 0, 2); + gridSettings->addMultiCellWidget(line1, 2, 2, 0, 2); + gridSettings->addMultiCellWidget(m_preserveAspectRatio, 3, 3, 0, 2); + gridSettings->addMultiCellWidget(m_labelBorderPercent, 4, 4, 0, 2); + gridSettings->addMultiCellWidget(m_borderPercent, 5, 5, 0, 2); + gridSettings->addMultiCellWidget(m_labelBorderWidth, 6, 6, 0, 2); + gridSettings->addMultiCellWidget(m_borderWidth, 7, 7, 0, 2); + gridSettings->addMultiCellWidget(line2, 8, 8, 0, 2); + gridSettings->addMultiCellWidget(m_labelForeground, 9, 9, 0, 0); + gridSettings->addMultiCellWidget(m_firstColorButton, 9, 9, 1, 2); + gridSettings->addMultiCellWidget(m_labelBackground, 10, 10, 0, 0); + gridSettings->addMultiCellWidget(m_secondColorButton, 10, 10, 1, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_preserveAspectRatio, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotPreserveAspectRatioToggled(bool))); + + connect(m_borderType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotBorderTypeChanged(int))); + + connect(m_borderPercent, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_borderWidth, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_firstColorButton, TQ_SIGNAL(changed(const TQColor &)), + this, TQ_SLOT(slotColorForegroundChanged(const TQColor &))); + + connect(m_secondColorButton, TQ_SIGNAL(changed(const TQColor &)), + this, TQ_SLOT(slotColorBackgroundChanged(const TQColor &))); +} + +ImageEffect_Border::~ImageEffect_Border() +{ +} + +void ImageEffect_Border::readUserSettings(void) +{ + m_borderType->blockSignals(true); + m_borderPercent->blockSignals(true); + m_borderWidth->blockSignals(true); + m_firstColorButton->blockSignals(true); + m_secondColorButton->blockSignals(true); + m_preserveAspectRatio->blockSignals(true); + + TDEConfig *config = kapp->config(); + config->setGroup("border Tool Dialog"); + + m_borderType->setCurrentItem(config->readNumEntry("Border Type", Border::SolidBorder)); + m_borderPercent->setValue(config->readNumEntry("Border Percent", 10) ); + m_borderWidth->setValue(config->readNumEntry("Border Width", 100) ); + m_preserveAspectRatio->setChecked(config->readBoolEntry("Preserve Aspect Ratio", true) ); + + TQColor black(0, 0, 0); + TQColor white(255, 255, 255); + TQColor gray1(192, 192, 192); + TQColor gray2(128, 128, 128); + + m_solidColor = config->readColorEntry("Solid Color", &black); + m_niepceBorderColor = config->readColorEntry("Niepce Border Color", &white); + m_niepceLineColor = config->readColorEntry("Niepce Line Color", &black); + m_bevelUpperLeftColor = config->readColorEntry("Bevel Upper Left Color", &gray1); + m_bevelLowerRightColor = config->readColorEntry("Bevel Lower Right Color", &gray2); + m_decorativeFirstColor = config->readColorEntry("Decorative First Color", &black); + m_decorativeSecondColor = config->readColorEntry("Decorative Second Color", &black); + + m_borderType->blockSignals(false); + m_borderPercent->blockSignals(false); + m_borderWidth->blockSignals(false); + m_firstColorButton->blockSignals(false); + m_secondColorButton->blockSignals(false); + m_preserveAspectRatio->blockSignals(false); + + slotBorderTypeChanged(m_borderType->currentItem()); +} + +void ImageEffect_Border::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("border Tool Dialog"); + + config->writeEntry("Border Type", m_borderType->currentItem()); + config->writeEntry("Border Percent", m_borderPercent->value()); + config->writeEntry("Border Width", m_borderWidth->value()); + config->writeEntry("Preserve Aspect Ratio", m_preserveAspectRatio->isChecked()); + + config->writeEntry("Solid Color", m_solidColor); + config->writeEntry("Niepce Border Color", m_niepceBorderColor); + config->writeEntry("Niepce Line Color", m_niepceLineColor); + config->writeEntry("Bevel Upper Left Color", m_bevelUpperLeftColor); + config->writeEntry("Bevel Lower Right Color", m_bevelLowerRightColor); + config->writeEntry("Decorative First Color", m_decorativeFirstColor); + config->writeEntry("Decorative Second Color", m_decorativeSecondColor); + + config->sync(); +} + +void ImageEffect_Border::resetValues() +{ + m_borderType->blockSignals(true); + m_borderPercent->blockSignals(true); + m_borderWidth->blockSignals(true); + m_firstColorButton->blockSignals(true); + m_secondColorButton->blockSignals(true); + m_preserveAspectRatio->blockSignals(true); + + m_borderType->setCurrentItem(Border::SolidBorder); + m_borderPercent->setValue(10); + m_borderWidth->setValue(100); + m_preserveAspectRatio->setChecked(true); + + m_solidColor = TQColor(0, 0, 0); + m_niepceBorderColor = TQColor(255, 255, 255); + m_niepceLineColor = TQColor(0, 0, 0); + m_bevelUpperLeftColor = TQColor(192, 192, 192); + m_bevelLowerRightColor = TQColor(128, 128, 128); + m_decorativeFirstColor = TQColor(0, 0, 0); + m_decorativeSecondColor = TQColor(0, 0, 0); + + m_borderType->blockSignals(false); + m_borderPercent->blockSignals(false); + m_borderWidth->blockSignals(false); + m_firstColorButton->blockSignals(false); + m_secondColorButton->blockSignals(false); + m_preserveAspectRatio->blockSignals(false); + + slotBorderTypeChanged(Border::SolidBorder); +} + +void ImageEffect_Border::renderingFinished() +{ + m_preserveAspectRatio->setEnabled(true); + m_borderType->setEnabled(true); + m_borderPercent->setEnabled(true); + m_borderWidth->setEnabled(true); + m_firstColorButton->setEnabled(true); + m_secondColorButton->setEnabled(true); + toggleBorderSlider(m_preserveAspectRatio->isChecked()); +} + +void ImageEffect_Border::slotColorForegroundChanged(const TQColor &color) +{ + switch (m_borderType->currentItem()) + { + case Border::SolidBorder: + m_solidColor = color; + break; + + case Border::NiepceBorder: + m_niepceBorderColor = color; + break; + + case Border::BeveledBorder: + m_bevelUpperLeftColor = color; + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + m_decorativeFirstColor = color; + break; + } + + slotEffect(); +} + +void ImageEffect_Border::slotColorBackgroundChanged(const TQColor &color) +{ + switch (m_borderType->currentItem()) + { + case Border::SolidBorder: + m_solidColor = color; + break; + + case Border::NiepceBorder: + m_niepceLineColor = color; + break; + + case Border::BeveledBorder: + m_bevelLowerRightColor = color; + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + m_decorativeSecondColor = color; + break; + } + + slotEffect(); +} + +void ImageEffect_Border::slotBorderTypeChanged(int borderType) +{ + m_labelForeground->setText(i18n("First:")); + m_labelBackground->setText(i18n("Second:")); + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the foreground color of the border.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the Background color of the border.")); + m_firstColorButton->setEnabled(true); + m_secondColorButton->setEnabled(true); + m_labelForeground->setEnabled(true); + m_labelBackground->setEnabled(true); + m_borderPercent->setEnabled(true); + + switch (borderType) + { + case Border::SolidBorder: + m_firstColorButton->setColor( m_solidColor ); + m_secondColorButton->setEnabled(false); + m_labelBackground->setEnabled(false); + break; + + case Border::NiepceBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the main border.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the line.")); + m_firstColorButton->setColor( m_niepceBorderColor ); + m_secondColorButton->setColor( m_niepceLineColor ); + break; + + case Border::BeveledBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the upper left area.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the lower right area.")); + m_firstColorButton->setColor( m_bevelUpperLeftColor ); + m_secondColorButton->setColor( m_bevelLowerRightColor ); + break; + + case Border::PineBorder: + case Border::WoodBorder: + case Border::PaperBorder: + case Border::ParqueBorder: + case Border::IceBorder: + case Border::LeafBorder: + case Border::MarbleBorder: + case Border::RainBorder: + case Border::CratersBorder: + case Border::DriedBorder: + case Border::PinkBorder: + case Border::StoneBorder: + case Border::ChalkBorder: + case Border::GraniteBorder: + case Border::RockBorder: + case Border::WallBorder: + TQWhatsThis::add( m_firstColorButton, i18n("

    Set here the color of the first line.")); + TQWhatsThis::add( m_secondColorButton, i18n("

    Set here the color of the second line.")); + m_firstColorButton->setColor( m_decorativeFirstColor ); + m_secondColorButton->setColor( m_decorativeSecondColor ); + break; + } + + slotEffect(); +} + +void ImageEffect_Border::prepareEffect() +{ + m_borderType->setEnabled(false); + m_borderPercent->setEnabled(false); + m_borderWidth->setEnabled(false); + m_firstColorButton->setEnabled(false); + m_secondColorButton->setEnabled(false); + m_preserveAspectRatio->setEnabled(false); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int orgWidth = iface->originalWidth(); + int orgHeight = iface->originalHeight(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sixteenBit = iface->previewSixteenBit(); + uchar *data = iface->getPreviewImage(); + Digikam::DImg previewImage(w, h, sixteenBit, + iface->previewHasAlpha(), data); + delete [] data; + + int borderType = m_borderType->currentItem(); + float ratio = (float)w/(float)orgWidth; + int borderWidth = (int)((float)m_borderWidth->value()*ratio); + TQString border = getBorderPath( m_borderType->currentItem() ); + + if (m_preserveAspectRatio->isChecked()) + { + m_threadedFilter = dynamic_cast( + new Border(&previewImage, this, orgWidth, orgHeight, + border, borderType, m_borderPercent->value()/100.0, + Digikam::DColor(m_solidColor, sixteenBit), + Digikam::DColor(m_niepceBorderColor, sixteenBit), + Digikam::DColor(m_niepceLineColor, sixteenBit), + Digikam::DColor(m_bevelUpperLeftColor, sixteenBit), + Digikam::DColor(m_bevelLowerRightColor, sixteenBit), + Digikam::DColor(m_decorativeFirstColor, sixteenBit), + Digikam::DColor(m_decorativeSecondColor, sixteenBit)) ); + } + else + { + m_threadedFilter = dynamic_cast( + new Border(&previewImage, this, orgWidth, orgHeight, + border, borderType, borderWidth, + (int)(20.0*ratio), (int)(20.0*ratio), 3, + Digikam::DColor(m_solidColor, sixteenBit), + Digikam::DColor(m_niepceBorderColor, sixteenBit), + Digikam::DColor(m_niepceLineColor, sixteenBit), + Digikam::DColor(m_bevelUpperLeftColor, sixteenBit), + Digikam::DColor(m_bevelLowerRightColor, sixteenBit), + Digikam::DColor(m_decorativeFirstColor, sixteenBit), + Digikam::DColor(m_decorativeSecondColor, sixteenBit)) ); + } +} + +void ImageEffect_Border::prepareFinal() +{ + m_borderType->setEnabled(false); + m_borderPercent->setEnabled(false); + m_borderWidth->setEnabled(false); + m_firstColorButton->setEnabled(false); + m_secondColorButton->setEnabled(false); + + int borderType = m_borderType->currentItem(); + int borderWidth = m_borderWidth->value(); + float borderRatio = m_borderPercent->value()/100.0; + TQString border = getBorderPath( m_borderType->currentItem() ); + + Digikam::ImageIface iface(0, 0); + int orgWidth = iface.originalWidth(); + int orgHeight = iface.originalHeight(); + bool sixteenBit = iface.previewSixteenBit(); + uchar *data = iface.getOriginalImage(); + Digikam::DImg orgImage(orgWidth, orgHeight, sixteenBit, + iface.originalHasAlpha(), data); + delete [] data; + + if (m_preserveAspectRatio->isChecked()) + { + m_threadedFilter = dynamic_cast( + new Border(&orgImage, this, orgWidth, orgHeight, + border, borderType, borderRatio, + Digikam::DColor(m_solidColor, sixteenBit), + Digikam::DColor(m_niepceBorderColor, sixteenBit), + Digikam::DColor(m_niepceLineColor, sixteenBit), + Digikam::DColor(m_bevelUpperLeftColor, sixteenBit), + Digikam::DColor(m_bevelLowerRightColor, sixteenBit), + Digikam::DColor(m_decorativeFirstColor, sixteenBit), + Digikam::DColor(m_decorativeSecondColor, sixteenBit)) ); + } + else + { + m_threadedFilter = dynamic_cast( + new Border(&orgImage, this, orgWidth, orgHeight, + border, borderType, borderWidth, 15, 15, 10, + Digikam::DColor(m_solidColor, sixteenBit), + Digikam::DColor(m_niepceBorderColor, sixteenBit), + Digikam::DColor(m_niepceLineColor, sixteenBit), + Digikam::DColor(m_bevelUpperLeftColor, sixteenBit), + Digikam::DColor(m_bevelLowerRightColor, sixteenBit), + Digikam::DColor(m_decorativeFirstColor, sixteenBit), + Digikam::DColor(m_decorativeSecondColor, sixteenBit)) ); + } +} + +void ImageEffect_Border::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + + Digikam::DImg imTemp = m_threadedFilter->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + Digikam::DImg imDest( w, h, m_threadedFilter->getTargetImage().sixteenBit(), + m_threadedFilter->getTargetImage().hasAlpha() ); + + imDest.fill( Digikam::DColor(paletteBackgroundColor().rgb(), + m_threadedFilter->getTargetImage().sixteenBit()) ); + + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage(imDest.bits()); + m_imagePreviewWidget->updatePreview(); +} + +void ImageEffect_Border::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + Digikam::DImg targetImage = m_threadedFilter->getTargetImage(); + iface.putOriginalImage(i18n("Add Border"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +TQString ImageEffect_Border::getBorderPath(int border) +{ + TQString pattern; + + switch (border) + { + case Border::PineBorder: + pattern = "pine-pattern"; + break; + + case Border::WoodBorder: + pattern = "wood-pattern"; + break; + + case Border::PaperBorder: + pattern = "paper-pattern"; + break; + + case Border::ParqueBorder: + pattern = "parque-pattern"; + break; + + case Border::IceBorder: + pattern = "ice-pattern"; + break; + + case Border::LeafBorder: + pattern = "leaf-pattern"; + break; + + case Border::MarbleBorder: + pattern = "marble-pattern"; + break; + + case Border::RainBorder: + pattern = "rain-pattern"; + break; + + case Border::CratersBorder: + pattern = "craters-pattern"; + break; + + case Border::DriedBorder: + pattern = "dried-pattern"; + break; + + case Border::PinkBorder: + pattern = "pink-pattern"; + break; + + case Border::StoneBorder: + pattern = "stone-pattern"; + break; + + case Border::ChalkBorder: + pattern = "chalk-pattern"; + break; + + case Border::GraniteBorder: + pattern = "granit-pattern"; + break; + + case Border::RockBorder: + pattern = "rock-pattern"; + break; + + case Border::WallBorder: + pattern = "wall-pattern"; + break; + + default: + return TQString(); + } + + TDEGlobal::dirs()->addResourceType(pattern.ascii(), TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + return (TDEGlobal::dirs()->findResourceDir(pattern.ascii(), pattern + ".png") + pattern + ".png" ); +} + +void ImageEffect_Border::slotPreserveAspectRatioToggled(bool b) +{ + toggleBorderSlider(b); + slotTimer(); +} + +void ImageEffect_Border::toggleBorderSlider(bool b) +{ + m_borderPercent->setEnabled(b); + m_borderWidth->setEnabled(!b); + m_labelBorderPercent->setEnabled(b); + m_labelBorderWidth->setEnabled(!b); +} + +} // NameSpace DigikamBorderImagesPlugin + diff --git a/src/imageplugins/border/imageeffect_border.h b/src/imageplugins/border/imageeffect_border.h new file mode 100644 index 00000000..787a76a0 --- /dev/null +++ b/src/imageplugins/border/imageeffect_border.h @@ -0,0 +1,109 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * Copyright 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_BORDER_H +#define IMAGEEFFECT_BORDER_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "imageguidedlg.h" + +class TQComboBox; +class TQLabel; +class TQCheckBox; +class TQColor; + +class KIntNumInput; +class KColorButton; + +namespace DigikamBorderImagesPlugin +{ + +class ImageEffect_Border : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Border(TQWidget *parent); + ~ImageEffect_Border(); + +private: + + TQString getBorderPath(int border); + +private slots: + + void slotPreserveAspectRatioToggled(bool); + void slotBorderTypeChanged(int borderType); + void slotColorForegroundChanged(const TQColor &color); + void slotColorBackgroundChanged(const TQColor &color); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + void toggleBorderSlider(bool b); + +private: + + TQLabel *m_labelBorderPercent; + TQLabel *m_labelBorderWidth; + TQLabel *m_labelForeground; + TQLabel *m_labelBackground; + + TQComboBox *m_borderType; + + TQCheckBox *m_preserveAspectRatio; + + TQColor m_solidColor; + TQColor m_niepceBorderColor; + TQColor m_niepceLineColor; + TQColor m_bevelUpperLeftColor; + TQColor m_bevelLowerRightColor; + TQColor m_decorativeFirstColor; + TQColor m_decorativeSecondColor; + + KIntNumInput *m_borderPercent; + KIntNumInput *m_borderWidth; + + KColorButton *m_firstColorButton; + KColorButton *m_secondColorButton; +}; + +} // NameSpace DigikamBorderImagesPlugin + +#endif /* IMAGEEFFECT_BORDER_H */ diff --git a/src/imageplugins/border/imageplugin_border.cpp b/src/imageplugins/border/imageplugin_border.cpp new file mode 100644 index 00000000..f0c5a236 --- /dev/null +++ b/src/imageplugins/border/imageplugin_border.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "bordertool.h" +#include "imageplugin_border.h" +#include "imageplugin_border.moc" + +using namespace DigikamBorderImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_border, + KGenericFactory("digikamimageplugin_border")); + +ImagePlugin_Border::ImagePlugin_Border(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Border") +{ + m_borderAction = new TDEAction(i18n("Add Border..."), "bordertool", + 0, + this, TQ_SLOT(slotBorder()), + actionCollection(), "imageplugin_border"); + + setXMLFile("digikamimageplugin_border_ui.rc"); + + DDebug() << "ImagePlugin_Border plugin loaded" << endl; +} + +ImagePlugin_Border::~ImagePlugin_Border() +{ +} + +void ImagePlugin_Border::setEnabledActions(bool enable) +{ + m_borderAction->setEnabled(enable); +} + +void ImagePlugin_Border::slotBorder() +{ + BorderTool *tool = new BorderTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/border/imageplugin_border.h b/src/imageplugins/border/imageplugin_border.h new file mode 100644 index 00000000..e5b3d959 --- /dev/null +++ b/src/imageplugins/border/imageplugin_border.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-20 + * Description : a digiKam image plugin to add a border + * around an image. + * + * Copyright 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_BORDER_H +#define IMAGEPLUGIN_BORDER_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Border : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Border(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Border(); + + void setEnabledActions(bool enable); + +private slots: + + void slotBorder(); + +private: + + TDEAction *m_borderAction; +}; + +#endif /* IMAGEPLUGIN_BORDER_H */ diff --git a/src/imageplugins/border/patterns/Makefile.am b/src/imageplugins/border/patterns/Makefile.am new file mode 100644 index 00000000..31bb4b2d --- /dev/null +++ b/src/imageplugins/border/patterns/Makefile.am @@ -0,0 +1,5 @@ +borderpicsdir = $(kde_datadir)/digikam/data +borderpics_DATA = pine-pattern.png wood-pattern.png paper-pattern.png parque-pattern.png \ + ice-pattern.png leaf-pattern.png marble-pattern.png rain-pattern.png \ + craters-pattern.png dried-pattern.png pink-pattern.png stone-pattern.png \ + chalk-pattern.png granit-pattern.png rock-pattern.png wall-pattern.png diff --git a/src/imageplugins/border/patterns/chalk-pattern.png b/src/imageplugins/border/patterns/chalk-pattern.png new file mode 100644 index 00000000..31c34f62 Binary files /dev/null and b/src/imageplugins/border/patterns/chalk-pattern.png differ diff --git a/src/imageplugins/border/patterns/craters-pattern.png b/src/imageplugins/border/patterns/craters-pattern.png new file mode 100644 index 00000000..fe8fe792 Binary files /dev/null and b/src/imageplugins/border/patterns/craters-pattern.png differ diff --git a/src/imageplugins/border/patterns/dried-pattern.png b/src/imageplugins/border/patterns/dried-pattern.png new file mode 100644 index 00000000..fca948ff Binary files /dev/null and b/src/imageplugins/border/patterns/dried-pattern.png differ diff --git a/src/imageplugins/border/patterns/granit-pattern.png b/src/imageplugins/border/patterns/granit-pattern.png new file mode 100644 index 00000000..eccbb354 Binary files /dev/null and b/src/imageplugins/border/patterns/granit-pattern.png differ diff --git a/src/imageplugins/border/patterns/ice-pattern.png b/src/imageplugins/border/patterns/ice-pattern.png new file mode 100644 index 00000000..30a67265 Binary files /dev/null and b/src/imageplugins/border/patterns/ice-pattern.png differ diff --git a/src/imageplugins/border/patterns/leaf-pattern.png b/src/imageplugins/border/patterns/leaf-pattern.png new file mode 100644 index 00000000..b602fceb Binary files /dev/null and b/src/imageplugins/border/patterns/leaf-pattern.png differ diff --git a/src/imageplugins/border/patterns/marble-pattern.png b/src/imageplugins/border/patterns/marble-pattern.png new file mode 100644 index 00000000..6346bf1f Binary files /dev/null and b/src/imageplugins/border/patterns/marble-pattern.png differ diff --git a/src/imageplugins/border/patterns/paper-pattern.png b/src/imageplugins/border/patterns/paper-pattern.png new file mode 100644 index 00000000..43da3b2f Binary files /dev/null and b/src/imageplugins/border/patterns/paper-pattern.png differ diff --git a/src/imageplugins/border/patterns/parque-pattern.png b/src/imageplugins/border/patterns/parque-pattern.png new file mode 100644 index 00000000..2aa5e96f Binary files /dev/null and b/src/imageplugins/border/patterns/parque-pattern.png differ diff --git a/src/imageplugins/border/patterns/pine-pattern.png b/src/imageplugins/border/patterns/pine-pattern.png new file mode 100644 index 00000000..4ae7a687 Binary files /dev/null and b/src/imageplugins/border/patterns/pine-pattern.png differ diff --git a/src/imageplugins/border/patterns/pink-pattern.png b/src/imageplugins/border/patterns/pink-pattern.png new file mode 100644 index 00000000..d0649c85 Binary files /dev/null and b/src/imageplugins/border/patterns/pink-pattern.png differ diff --git a/src/imageplugins/border/patterns/rain-pattern.png b/src/imageplugins/border/patterns/rain-pattern.png new file mode 100644 index 00000000..a282c442 Binary files /dev/null and b/src/imageplugins/border/patterns/rain-pattern.png differ diff --git a/src/imageplugins/border/patterns/rock-pattern.png b/src/imageplugins/border/patterns/rock-pattern.png new file mode 100644 index 00000000..04dc8b0e Binary files /dev/null and b/src/imageplugins/border/patterns/rock-pattern.png differ diff --git a/src/imageplugins/border/patterns/stone-pattern.png b/src/imageplugins/border/patterns/stone-pattern.png new file mode 100644 index 00000000..98da7c2c Binary files /dev/null and b/src/imageplugins/border/patterns/stone-pattern.png differ diff --git a/src/imageplugins/border/patterns/wall-pattern.png b/src/imageplugins/border/patterns/wall-pattern.png new file mode 100644 index 00000000..3c101597 Binary files /dev/null and b/src/imageplugins/border/patterns/wall-pattern.png differ diff --git a/src/imageplugins/border/patterns/wood-pattern.png b/src/imageplugins/border/patterns/wood-pattern.png new file mode 100644 index 00000000..bfe4c724 Binary files /dev/null and b/src/imageplugins/border/patterns/wood-pattern.png differ diff --git a/src/imageplugins/channelmixer/Makefile.am b/src/imageplugins/channelmixer/Makefile.am new file mode 100644 index 00000000..61eaab34 --- /dev/null +++ b/src/imageplugins/channelmixer/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_channelmixer_la_SOURCES = imageplugin_channelmixer.cpp \ + channelmixertool.cpp + +digikamimageplugin_channelmixer_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_channelmixer_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_channelmixer.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_channelmixer.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_channelmixer_ui.rc + diff --git a/src/imageplugins/channelmixer/channelmixer.cpp b/src/imageplugins/channelmixer/channelmixer.cpp new file mode 100644 index 00000000..849cc1e8 --- /dev/null +++ b/src/imageplugins/channelmixer/channelmixer.cpp @@ -0,0 +1,784 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * Load and save mixer gains methods inspired from + * Gimp 2.2.3 and copyrighted 2002 by Martin Guldahl + * . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "dimg.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "dimgimagefilters.h" +#include "channelmixer.h" +#include "channelmixer.moc" + +namespace DigikamChannelMixerImagesPlugin +{ + +ChannelMixerDialog::ChannelMixerDialog(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Color Channel Mixer"), + "channelmixer", true, false) +{ + m_destinationPreviewData = 0L; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Color Channel Mixer"), + digikam_version, + I18N_NOOP("An image color channel mixer plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2005-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("channelmixer Tool Dialog", plainPage(), + i18n("

    You can see here the image's color channels' " + "gains adjustments preview. You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* grid = new TQGridLayout( gboxSettings, 9, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->setCurrentText( i18n("Red") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the color channel to mix here:

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + grid->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "mixer settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + + grid->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + TQLabel *redLabel = new TQLabel(i18n("Red:"), gboxSettings); + m_redGain = new KDoubleNumInput(gboxSettings); + m_redGain->setPrecision(0); + m_redGain->setRange(-200.0, 200.0, 1, true); + TQWhatsThis::add( m_redGain, i18n("

    Select the red color gain in percent for the current channel here.")); + + TQLabel *blueLabel = new TQLabel(i18n("Blue:"), gboxSettings); + m_greenGain = new KDoubleNumInput(gboxSettings); + m_greenGain->setPrecision(0); + m_greenGain->setRange(-200.0, 200.0, 1, true); + TQWhatsThis::add( m_greenGain, i18n("

    Select the green color gain in percent for the current channel here.")); + + TQLabel *greenLabel = new TQLabel(i18n("Green:"), gboxSettings); + m_blueGain = new KDoubleNumInput(gboxSettings); + m_blueGain->setPrecision(0); + m_blueGain->setRange(-200.0, 200.0, 1, true); + TQWhatsThis::add( m_blueGain, i18n("

    Select the blue color gain in percent for the current channel here.")); + + m_resetButton = new TQPushButton(i18n("&Reset"), gboxSettings); + TQWhatsThis::add( m_resetButton, i18n("Reset color channels' gains settings from the currently selected channel.")); + + grid->addMultiCellWidget(redLabel, 3, 3, 0, 0); + grid->addMultiCellWidget(greenLabel, 4, 4, 0, 0); + grid->addMultiCellWidget(blueLabel, 5, 5, 0, 0); + grid->addMultiCellWidget(m_redGain, 3, 3, 1, 4); + grid->addMultiCellWidget(m_greenGain, 4, 4, 1, 4); + grid->addMultiCellWidget(m_blueGain, 5, 5, 1, 4); + grid->addMultiCellWidget(m_resetButton, 6, 6, 0, 1); + + // ------------------------------------------------------------- + + m_monochrome = new TQCheckBox( i18n("Monochrome"), gboxSettings); + TQWhatsThis::add( m_monochrome, i18n("

    Enable this option if you want the image rendered in monochrome mode. " + "In this mode, the histogram will display only luminosity values.")); + + m_preserveLuminosity = new TQCheckBox( i18n("Preserve luminosity"), gboxSettings); + TQWhatsThis::add( m_preserveLuminosity, i18n("

    Enable this option is you want preserve the image luminosity.")); + + grid->addMultiCellWidget(m_monochrome, 7, 7, 0, 4); + grid->addMultiCellWidget(m_preserveLuminosity, 8, 8, 0, 4); + grid->setRowStretch(9, 10); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + // Channels and scale selection slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Gains settings slots. + + connect(m_redGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_greenGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_blueGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_preserveLuminosity, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_monochrome, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotMonochromeActived(bool))); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); +} + +ChannelMixerDialog::~ChannelMixerDialog() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; +} + +void ChannelMixerDialog::slotResetCurrentChannel() +{ + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_greenRedGain = 0.0; + m_greenGreenGain = 1.0; + m_greenBlueGain = 0.0; + break; + + case BlueChannelGains: // Blue. + m_blueRedGain = 0.0; + m_blueGreenGain = 0.0; + m_blueBlueGain = 1.0; + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_blackRedGain = 1.0; + m_blackGreenGain = 0.0; + m_blackBlueGain = 0.0; + } + else + { + m_redRedGain = 1.0; + m_redGreenGain = 0.0; + m_redBlueGain = 0.0; + } + break; + } + + adjustSliders(); + slotEffect(); + m_histogramWidget->reset(); +} + +void ChannelMixerDialog::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ChannelMixerDialog::slotGainsChanged() +{ + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_greenRedGain = m_redGain->value() / 100.0; + m_greenGreenGain = m_greenGain->value() / 100.0; + m_greenBlueGain = m_blueGain->value() / 100.0; + break; + + case BlueChannelGains: // Blue. + m_blueRedGain = m_redGain->value() / 100.0; + m_blueGreenGain = m_greenGain->value() / 100.0; + m_blueBlueGain = m_blueGain->value() / 100.0; + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_blackRedGain = m_redGain->value() / 100.0; + m_blackGreenGain = m_greenGain->value() / 100.0; + m_blackBlueGain = m_blueGain->value() / 100.0; + } + else + { + m_redRedGain = m_redGain->value() / 100.0; + m_redGreenGain = m_greenGain->value() / 100.0; + m_redBlueGain = m_blueGain->value() / 100.0; + } + break; + } + + slotTimer(); +} + +void ChannelMixerDialog::adjustSliders(void) +{ + m_redGain->blockSignals(true); + m_greenGain->blockSignals(true); + m_blueGain->blockSignals(true); + + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_redGain->setValue(m_greenRedGain * 100.0); + m_greenGain->setValue(m_greenGreenGain * 100.0); + m_blueGain->setValue(m_greenBlueGain * 100.0); + break; + + case BlueChannelGains: // Blue. + m_redGain->setValue(m_blueRedGain * 100.0); + m_greenGain->setValue(m_blueGreenGain * 100.0); + m_blueGain->setValue(m_blueBlueGain * 100.0); + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_redGain->setValue(m_blackRedGain * 100.0); + m_greenGain->setValue(m_blackGreenGain * 100.0); + m_blueGain->setValue(m_blackBlueGain * 100.0); + } + else + { + m_redGain->setValue(m_redRedGain * 100.0); + m_greenGain->setValue(m_redGreenGain * 100.0); + m_blueGain->setValue(m_redBlueGain * 100.0); + } + break; + } + + m_redGain->blockSignals(false); + m_greenGain->blockSignals(false); + m_blueGain->blockSignals(false); +} + +void ChannelMixerDialog::slotMonochromeActived(bool mono) +{ + m_channelCB->setEnabled(!mono); + m_channelCB->setCurrentItem(RedChannelGains); // Red for monochrome. + slotChannelChanged(RedChannelGains); // Monochrome => display luminosity histogram value. +} + +void ChannelMixerDialog::slotEffect() +{ + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + Digikam::DImgImageFilters filter; + + if (m_monochrome->isChecked()) + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_blackRedGain, m_blackGreenGain, m_blackBlueGain, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + } + else + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_redRedGain, m_redGreenGain, m_redBlueGain, // Red channel gains. + m_greenRedGain, m_greenGreenGain, m_greenBlueGain, // Green channel gains. + m_blueRedGain, m_blueGreenGain, m_blueBlueGain); // Blue channel gains. + } + + iface->putPreviewImage(data); + m_previewWidget->updatePreview(); + + // Update histogram. + memcpy (m_destinationPreviewData, data, w*h*(sb ? 8 : 4)); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] data; +} + +void ChannelMixerDialog::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + Digikam::DImgImageFilters filter; + + if (m_monochrome->isChecked()) + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_blackRedGain, m_blackGreenGain, m_blackBlueGain, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + } + else + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_redRedGain, m_redGreenGain, m_redBlueGain, // Red channel gains. + m_greenRedGain, m_greenGreenGain, m_greenBlueGain, // Green channel gains. + m_blueRedGain, m_blueGreenGain, m_blueBlueGain); // Blue channel gains. + } + + iface->putOriginalImage(i18n("Channel Mixer"), data); + delete [] data; + kapp->restoreOverrideCursor(); + accept(); +} + +void ChannelMixerDialog::slotChannelChanged(int channel) +{ + switch(channel) + { + case GreenChannelGains: // Green. + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannelGains: // Blue. + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + } + else + { + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + } + break; + } + + m_histogramWidget->repaint(false); + adjustSliders(); + slotEffect(); +} + +void ChannelMixerDialog::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ChannelMixerDialog::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("channelmixer Tool Dialog"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + + m_monochrome->setChecked(config->readBoolEntry("Monochrome", false)); + m_preserveLuminosity->setChecked(config->readNumEntry("PreserveLuminosity", false)); + + m_redRedGain = config->readDoubleNumEntry("RedRedGain", 1.0); + m_redGreenGain = config->readDoubleNumEntry("RedGreenGain", 0.0); + m_redBlueGain = config->readDoubleNumEntry("RedBlueGain", 0.0); + + m_greenRedGain = config->readDoubleNumEntry("GreenRedGain", 0.0); + m_greenGreenGain = config->readDoubleNumEntry("GreenGreenGain", 1.0); + m_greenBlueGain = config->readDoubleNumEntry("GreenBlueGain", 0.0); + + m_blueRedGain = config->readDoubleNumEntry("BlueRedGain", 0.0); + m_blueGreenGain = config->readDoubleNumEntry("BlueGreenGain", 0.0); + m_blueBlueGain = config->readDoubleNumEntry("BlueBlueGain", 1.0); + + m_blackRedGain = config->readDoubleNumEntry("BlackRedGain", 1.0); + m_blackGreenGain = config->readDoubleNumEntry("BlackGreenGain", 0.0); + m_blackBlueGain = config->readDoubleNumEntry("BlackBlueGain", 0.0); + + adjustSliders(); + m_histogramWidget->reset(); + + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ChannelMixerDialog::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("channelmixer Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + config->writeEntry("Monochrome", m_monochrome->isChecked()); + config->writeEntry("PreserveLuminosity", m_preserveLuminosity->isChecked()); + + config->writeEntry("RedRedGain", m_redRedGain); + config->writeEntry("RedGreenGain", m_redGreenGain); + config->writeEntry("RedBlueGain", m_redBlueGain); + + config->writeEntry("GreenRedGain", m_greenRedGain); + config->writeEntry("GreenGreenGain", m_greenGreenGain); + config->writeEntry("GreenBlueGain", m_greenBlueGain); + + config->writeEntry("BlueRedGain", m_blueRedGain); + config->writeEntry("BlueGreenGain", m_blueGreenGain); + config->writeEntry("BlueBlueGain", m_blueBlueGain); + + config->writeEntry("BlackRedGain", m_blackRedGain); + config->writeEntry("BlackGreenGain", m_blackGreenGain); + config->writeEntry("BlackBlueGain", m_blackBlueGain); + + config->sync(); +} + +void ChannelMixerDialog::resetValues() +{ + m_monochrome->blockSignals(true); + m_preserveLuminosity->blockSignals(true); + + m_redRedGain = 1.0; + m_redGreenGain = 0.0; + m_redBlueGain = 0.0; + + m_greenRedGain = 0.0; + m_greenGreenGain = 1.0; + m_greenBlueGain = 0.0; + + m_blueRedGain = 0.0; + m_blueGreenGain = 0.0; + m_blueBlueGain = 1.0; + + m_blackRedGain = 1.0; + m_blackGreenGain = 0.0; + m_blackBlueGain = 0.0; + + adjustSliders(); + + m_monochrome->blockSignals(false); + m_preserveLuminosity->blockSignals(false); + m_channelCB->setEnabled(true); + + m_histogramWidget->reset(); + + slotChannelChanged(RedChannelGains); +} + +// Load all gains. +void ChannelMixerDialog::slotUser3() +{ + KURL loadGainsFileUrl; + FILE *fp = 0L; + int currentOutputChannel; + bool monochrome; + + loadGainsFileUrl = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Select Gimp Gains Mixer File to Load")) ); + if( loadGainsFileUrl.isEmpty() ) + return; + + fp = fopen(TQFile::encodeName(loadGainsFileUrl.path()), "r"); + + if ( fp ) + { + char buf1[1024]; + char buf2[1024]; + char buf3[1024]; + + buf1[0] = '\0'; + + fgets(buf1, 1023, fp); + + fscanf (fp, "%*s %s", buf1); + + // Get the current output channel in dialog. + + if (strcmp (buf1, "RED") == 0) + currentOutputChannel = RedChannelGains; + else if (strcmp (buf1, "GREEN") == 0) + currentOutputChannel = GreenChannelGains; + else if (strcmp (buf1, "BLUE") == 0) + currentOutputChannel = BlueChannelGains; + + fscanf (fp, "%*s %s", buf1); // preview flag, preserved for compatibility + + fscanf (fp, "%*s %s", buf1); + + if (strcmp (buf1, "true") == 0) + monochrome = true; + else + monochrome = false; + + fscanf (fp, "%*s %s", buf1); + + if (strcmp (buf1, "true") == 0) + m_preserveLuminosity->setChecked(true); + else + m_preserveLuminosity->setChecked(false); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_redRedGain = atof(buf1); + m_redGreenGain = atof(buf2); + m_redBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_greenRedGain = atof(buf1); + m_greenGreenGain = atof(buf2); + m_greenBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_blueRedGain = atof(buf1); + m_blueGreenGain = atof(buf2); + m_blueBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_blackRedGain = atof(buf1); + m_blackGreenGain = atof(buf2); + m_blackBlueGain = atof(buf3); + + fclose(fp); + + // Refresh settings. + m_monochrome->setChecked(monochrome); + m_channelCB->setCurrentItem(currentOutputChannel); + slotChannelChanged(currentOutputChannel); + } + else + { + KMessageBox::error(this, i18n("Cannot load settings from the Gains Mixer text file.")); + return; + } +} + +// Save all gains. +void ChannelMixerDialog::slotUser2() +{ + KURL saveGainsFileUrl; + FILE *fp = 0L; + + saveGainsFileUrl = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Gimp Gains Mixer File to Save")) ); + if( saveGainsFileUrl.isEmpty() ) + return; + + fp = fopen(TQFile::encodeName(saveGainsFileUrl.path()), "w"); + + if ( fp ) + { + const char *str = 0L; + char buf1[256]; + char buf2[256]; + char buf3[256]; + + switch ( m_channelCB->currentItem() ) + { + case RedChannelGains: + str = "RED"; + break; + case GreenChannelGains: + str = "GREEN"; + break; + case BlueChannelGains: + str = "BLUE"; + break; + default: + DWarning() << "Unknown Color channel gains" << endl; + break; + } + + fprintf (fp, "# Channel Mixer Configuration File\n"); + + fprintf (fp, "CHANNEL: %s\n", str); + fprintf (fp, "PREVIEW: %s\n", "true"); // preserved for compatibility + fprintf (fp, "MONOCHROME: %s\n", + m_monochrome->isChecked() ? "true" : "false"); + fprintf (fp, "PRESERVE_LUMINOSITY: %s\n", + m_preserveLuminosity->isChecked() ? "true" : "false"); + + sprintf (buf1, "%5.3f", m_redRedGain); + sprintf (buf2, "%5.3f", m_redGreenGain); + sprintf (buf3, "%5.3f", m_redBlueGain); + fprintf (fp, "RED: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_greenRedGain); + sprintf (buf2, "%5.3f", m_greenGreenGain); + sprintf (buf3, "%5.3f", m_greenBlueGain); + fprintf (fp, "GREEN: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_blueRedGain); + sprintf (buf2, "%5.3f", m_blueGreenGain); + sprintf (buf3, "%5.3f", m_blueBlueGain); + fprintf (fp, "BLUE: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_blackRedGain); + sprintf (buf2, "%5.3f", m_blackGreenGain); + sprintf (buf3, "%5.3f", m_blackBlueGain); + fprintf (fp, "BLACK: %s %s %s\n", buf1, buf2,buf3); + + fclose (fp); + } + else + { + KMessageBox::error(this, i18n("Cannot save settings to the Gains Mixer text file.")); + return; + } +} + +} // NameSpace DigikamChannelMixerImagesPlugin + diff --git a/src/imageplugins/channelmixer/channelmixer.h b/src/imageplugins/channelmixer/channelmixer.h new file mode 100644 index 00000000..9b4396d0 --- /dev/null +++ b/src/imageplugins/channelmixer/channelmixer.h @@ -0,0 +1,133 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CHANNELMIXER_H +#define CHANNELMIXER_H + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQCheckBox; +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +class KDoubleNumInput; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamChannelMixerImagesPlugin +{ + +class ChannelMixerDialog : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ChannelMixerDialog(TQWidget *parent); + ~ChannelMixerDialog(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + void adjustSliders(); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotResetCurrentChannel(); + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotGainsChanged(); + void slotMonochromeActived(bool mono); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum ColorChannelGains + { + RedChannelGains=0, + GreenChannelGains, + BlueChannelGains + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + +private: + + uchar *m_destinationPreviewData; + + double m_redRedGain; + double m_redGreenGain; + double m_redBlueGain; + double m_greenRedGain; + double m_greenGreenGain; + double m_greenBlueGain; + double m_blueRedGain; + double m_blueGreenGain; + double m_blueBlueGain; + double m_blackRedGain; + double m_blackGreenGain; + double m_blackBlueGain; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDoubleNumInput *m_redGain; + KDoubleNumInput *m_greenGain; + KDoubleNumInput *m_blueGain; + + TQPushButton *m_resetButton; + + TQCheckBox *m_preserveLuminosity; + TQCheckBox *m_monochrome; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ImageWidget *m_previewWidget; +}; + +} // NameSpace DigikamChannelMixerImagesPlugin + +#endif /* CHANNELMIXER_H */ diff --git a/src/imageplugins/channelmixer/channelmixertool.cpp b/src/imageplugins/channelmixer/channelmixertool.cpp new file mode 100644 index 00000000..c8215178 --- /dev/null +++ b/src/imageplugins/channelmixer/channelmixertool.cpp @@ -0,0 +1,774 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "dimg.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "channelmixertool.h" +#include "channelmixertool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamChannelMixerImagesPlugin +{ + +ChannelMixerTool::ChannelMixerTool(TQObject* parent) + : EditorTool(parent) +{ + m_destinationPreviewData = 0; + + setName("channelmixer"); + setToolName(i18n("Channel Mixer")); + setToolIcon(SmallIcon("channelmixer")); + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("channelmixer Tool", 0, + i18n("

    You can see here the image's color channels' " + "gains adjustments preview. You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 9, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, m_gboxSettings->plainPage() ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + m_channelCB->setCurrentText( i18n("Red") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the color channel to mix here:

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "mixer settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + + // ------------------------------------------------------------- + + TQLabel *redLabel = new TQLabel(i18n("Red:"), m_gboxSettings->plainPage()); + m_redGain = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_redGain->setPrecision(0); + m_redGain->setRange(-200.0, 200.0, 1); + m_redGain->setDefaultValue(0); + TQWhatsThis::add( m_redGain, i18n("

    Select the red color gain in percent for the current channel here.")); + + TQLabel *blueLabel = new TQLabel(i18n("Blue:"), m_gboxSettings->plainPage()); + m_greenGain = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_greenGain->setPrecision(0); + m_greenGain->setRange(-200.0, 200.0, 1); + m_greenGain->setDefaultValue(0); + TQWhatsThis::add( m_greenGain, i18n("

    Select the green color gain in percent for the current channel here.")); + + TQLabel *greenLabel = new TQLabel(i18n("Green:"), m_gboxSettings->plainPage()); + m_blueGain = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_blueGain->setPrecision(0); + m_blueGain->setRange(-200.0, 200.0, 1); + m_blueGain->setDefaultValue(0); + TQWhatsThis::add( m_blueGain, i18n("

    Select the blue color gain in percent for the current channel here.")); + + m_resetButton = new TQPushButton(i18n("&Reset"), m_gboxSettings->plainPage()); + TQWhatsThis::add( m_resetButton, i18n("Reset color channels' gains settings from the currently selected channel.")); + + // ------------------------------------------------------------- + + m_monochrome = new TQCheckBox( i18n("Monochrome"), m_gboxSettings->plainPage()); + TQWhatsThis::add( m_monochrome, i18n("

    Enable this option if you want the image rendered in monochrome mode. " + "In this mode, the histogram will display only luminosity values.")); + + m_preserveLuminosity = new TQCheckBox( i18n("Preserve luminosity"), m_gboxSettings->plainPage()); + TQWhatsThis::add( m_preserveLuminosity, i18n("

    Enable this option is you want preserve the image luminosity.")); + + grid->addMultiCellLayout(l1, 0, 0, 0, 4); + grid->addMultiCellWidget(histoBox, 1, 2, 0, 4); + grid->addMultiCellWidget(redLabel, 3, 3, 0, 0); + grid->addMultiCellWidget(greenLabel, 4, 4, 0, 0); + grid->addMultiCellWidget(blueLabel, 5, 5, 0, 0); + grid->addMultiCellWidget(m_redGain, 3, 3, 1, 4); + grid->addMultiCellWidget(m_greenGain, 4, 4, 1, 4); + grid->addMultiCellWidget(m_blueGain, 5, 5, 1, 4); + grid->addMultiCellWidget(m_resetButton, 6, 6, 0, 1); + grid->addMultiCellWidget(m_monochrome, 7, 7, 0, 4); + grid->addMultiCellWidget(m_preserveLuminosity, 8, 8, 0, 4); + grid->setRowStretch(9, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + // Channels and scale selection slots. + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Gains settings slots. + + connect(m_redGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_greenGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_blueGain, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotGainsChanged())); + + connect(m_preserveLuminosity, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_monochrome, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotMonochromeActived(bool))); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_resetButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotResetCurrentChannel())); +} + +ChannelMixerTool::~ChannelMixerTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void ChannelMixerTool::slotResetCurrentChannel() +{ + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_greenRedGain = 0.0; + m_greenGreenGain = 1.0; + m_greenBlueGain = 0.0; + break; + + case BlueChannelGains: // Blue. + m_blueRedGain = 0.0; + m_blueGreenGain = 0.0; + m_blueBlueGain = 1.0; + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_blackRedGain = 1.0; + m_blackGreenGain = 0.0; + m_blackBlueGain = 0.0; + } + else + { + m_redRedGain = 1.0; + m_redGreenGain = 0.0; + m_redBlueGain = 0.0; + } + break; + } + + adjustSliders(); + slotEffect(); + m_histogramWidget->reset(); +} + +void ChannelMixerTool::slotColorSelectedFromTarget(const DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ChannelMixerTool::slotGainsChanged() +{ + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_greenRedGain = m_redGain->value() / 100.0; + m_greenGreenGain = m_greenGain->value() / 100.0; + m_greenBlueGain = m_blueGain->value() / 100.0; + break; + + case BlueChannelGains: // Blue. + m_blueRedGain = m_redGain->value() / 100.0; + m_blueGreenGain = m_greenGain->value() / 100.0; + m_blueBlueGain = m_blueGain->value() / 100.0; + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_blackRedGain = m_redGain->value() / 100.0; + m_blackGreenGain = m_greenGain->value() / 100.0; + m_blackBlueGain = m_blueGain->value() / 100.0; + } + else + { + m_redRedGain = m_redGain->value() / 100.0; + m_redGreenGain = m_greenGain->value() / 100.0; + m_redBlueGain = m_blueGain->value() / 100.0; + } + break; + } + + slotTimer(); +} + +void ChannelMixerTool::adjustSliders() +{ + m_redGain->blockSignals(true); + m_greenGain->blockSignals(true); + m_blueGain->blockSignals(true); + + switch( m_channelCB->currentItem() ) + { + case GreenChannelGains: // Green. + m_redGain->setValue(m_greenRedGain * 100.0); + m_greenGain->setValue(m_greenGreenGain * 100.0); + m_blueGain->setValue(m_greenBlueGain * 100.0); + break; + + case BlueChannelGains: // Blue. + m_redGain->setValue(m_blueRedGain * 100.0); + m_greenGain->setValue(m_blueGreenGain * 100.0); + m_blueGain->setValue(m_blueBlueGain * 100.0); + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_redGain->setValue(m_blackRedGain * 100.0); + m_greenGain->setValue(m_blackGreenGain * 100.0); + m_blueGain->setValue(m_blackBlueGain * 100.0); + } + else + { + m_redGain->setValue(m_redRedGain * 100.0); + m_greenGain->setValue(m_redGreenGain * 100.0); + m_blueGain->setValue(m_redBlueGain * 100.0); + } + break; + } + + m_redGain->blockSignals(false); + m_greenGain->blockSignals(false); + m_blueGain->blockSignals(false); +} + +void ChannelMixerTool::slotMonochromeActived(bool mono) +{ + m_channelCB->setEnabled(!mono); + m_channelCB->setCurrentItem(RedChannelGains); // Red for monochrome. + slotChannelChanged(RedChannelGains); // Monochrome => display luminosity histogram value. +} + +void ChannelMixerTool::slotEffect() +{ + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + DImgImageFilters filter; + + if (m_monochrome->isChecked()) + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_blackRedGain, m_blackGreenGain, m_blackBlueGain, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + } + else + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_redRedGain, m_redGreenGain, m_redBlueGain, // Red channel gains. + m_greenRedGain, m_greenGreenGain, m_greenBlueGain, // Green channel gains. + m_blueRedGain, m_blueGreenGain, m_blueBlueGain); // Blue channel gains. + } + + iface->putPreviewImage(data); + m_previewWidget->updatePreview(); + + // Update histogram. + memcpy (m_destinationPreviewData, data, w*h*(sb ? 8 : 4)); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] data; +} + +void ChannelMixerTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + DImgImageFilters filter; + + if (m_monochrome->isChecked()) + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_blackRedGain, m_blackGreenGain, m_blackBlueGain, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + } + else + { + filter.channelMixerImage(data, w, h, sb, // Image data. + m_preserveLuminosity->isChecked(), // Preserve luminosity. + m_monochrome->isChecked(), // Monochrome. + m_redRedGain, m_redGreenGain, m_redBlueGain, // Red channel gains. + m_greenRedGain, m_greenGreenGain, m_greenBlueGain, // Green channel gains. + m_blueRedGain, m_blueGreenGain, m_blueBlueGain); // Blue channel gains. + } + + iface->putOriginalImage(i18n("Channel Mixer"), data); + delete [] data; + kapp->restoreOverrideCursor(); +} + +void ChannelMixerTool::slotChannelChanged(int channel) +{ + switch(channel) + { + case GreenChannelGains: // Green. + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannelGains: // Blue. + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + + default: // Red or monochrome. + if ( m_monochrome->isChecked() ) + { + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + } + else + { + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + } + break; + } + + m_histogramWidget->repaint(false); + adjustSliders(); + slotEffect(); +} + +void ChannelMixerTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ChannelMixerTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("channelmixer Tool"); + + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + + m_monochrome->setChecked(config->readBoolEntry("Monochrome", false)); + m_preserveLuminosity->setChecked(config->readNumEntry("PreserveLuminosity", false)); + + m_redRedGain = config->readDoubleNumEntry("RedRedGain", 1.0); + m_redGreenGain = config->readDoubleNumEntry("RedGreenGain", 0.0); + m_redBlueGain = config->readDoubleNumEntry("RedBlueGain", 0.0); + + m_greenRedGain = config->readDoubleNumEntry("GreenRedGain", 0.0); + m_greenGreenGain = config->readDoubleNumEntry("GreenGreenGain", 1.0); + m_greenBlueGain = config->readDoubleNumEntry("GreenBlueGain", 0.0); + + m_blueRedGain = config->readDoubleNumEntry("BlueRedGain", 0.0); + m_blueGreenGain = config->readDoubleNumEntry("BlueGreenGain", 0.0); + m_blueBlueGain = config->readDoubleNumEntry("BlueBlueGain", 1.0); + + m_blackRedGain = config->readDoubleNumEntry("BlackRedGain", 1.0); + m_blackGreenGain = config->readDoubleNumEntry("BlackGreenGain", 0.0); + m_blackBlueGain = config->readDoubleNumEntry("BlackBlueGain", 0.0); + + adjustSliders(); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ChannelMixerTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("channelmixer Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + config->writeEntry("Monochrome", m_monochrome->isChecked()); + config->writeEntry("PreserveLuminosity", m_preserveLuminosity->isChecked()); + + config->writeEntry("RedRedGain", m_redRedGain); + config->writeEntry("RedGreenGain", m_redGreenGain); + config->writeEntry("RedBlueGain", m_redBlueGain); + + config->writeEntry("GreenRedGain", m_greenRedGain); + config->writeEntry("GreenGreenGain", m_greenGreenGain); + config->writeEntry("GreenBlueGain", m_greenBlueGain); + + config->writeEntry("BlueRedGain", m_blueRedGain); + config->writeEntry("BlueGreenGain", m_blueGreenGain); + config->writeEntry("BlueBlueGain", m_blueBlueGain); + + config->writeEntry("BlackRedGain", m_blackRedGain); + config->writeEntry("BlackGreenGain", m_blackGreenGain); + config->writeEntry("BlackBlueGain", m_blackBlueGain); + + m_previewWidget->writeSettings(); + + config->sync(); +} + +void ChannelMixerTool::slotResetSettings() +{ + m_monochrome->blockSignals(true); + m_preserveLuminosity->blockSignals(true); + + m_redRedGain = 1.0; + m_redGreenGain = 0.0; + m_redBlueGain = 0.0; + + m_greenRedGain = 0.0; + m_greenGreenGain = 1.0; + m_greenBlueGain = 0.0; + + m_blueRedGain = 0.0; + m_blueGreenGain = 0.0; + m_blueBlueGain = 1.0; + + m_blackRedGain = 1.0; + m_blackGreenGain = 0.0; + m_blackBlueGain = 0.0; + + adjustSliders(); + + m_monochrome->blockSignals(false); + m_preserveLuminosity->blockSignals(false); + m_channelCB->setEnabled(true); + + m_histogramWidget->reset(); + + slotChannelChanged(RedChannelGains); +} + +void ChannelMixerTool::slotLoadSettings() +{ + KURL loadGainsFileUrl; + FILE *fp = 0L; + int currentOutputChannel; + bool monochrome; + + loadGainsFileUrl = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Select Gimp Gains Mixer File to Load")) ); + if( loadGainsFileUrl.isEmpty() ) + return; + + fp = fopen(TQFile::encodeName(loadGainsFileUrl.path()), "r"); + + if ( fp ) + { + char buf1[1024]; + char buf2[1024]; + char buf3[1024]; + + buf1[0] = '\0'; + + fgets(buf1, 1023, fp); + + fscanf (fp, "%*s %s", buf1); + + // Get the current output channel in dialog. + + if (strcmp (buf1, "RED") == 0) + currentOutputChannel = RedChannelGains; + else if (strcmp (buf1, "GREEN") == 0) + currentOutputChannel = GreenChannelGains; + else if (strcmp (buf1, "BLUE") == 0) + currentOutputChannel = BlueChannelGains; + + fscanf (fp, "%*s %s", buf1); // preview flag, preserved for compatibility + + fscanf (fp, "%*s %s", buf1); + + if (strcmp (buf1, "true") == 0) + monochrome = true; + else + monochrome = false; + + fscanf (fp, "%*s %s", buf1); + + if (strcmp (buf1, "true") == 0) + m_preserveLuminosity->setChecked(true); + else + m_preserveLuminosity->setChecked(false); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_redRedGain = atof(buf1); + m_redGreenGain = atof(buf2); + m_redBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_greenRedGain = atof(buf1); + m_greenGreenGain = atof(buf2); + m_greenBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_blueRedGain = atof(buf1); + m_blueGreenGain = atof(buf2); + m_blueBlueGain = atof(buf3); + + fscanf (fp, "%*s %s %s %s", buf1, buf2, buf3); + m_blackRedGain = atof(buf1); + m_blackGreenGain = atof(buf2); + m_blackBlueGain = atof(buf3); + + fclose(fp); + + // Refresh settings. + m_monochrome->setChecked(monochrome); + m_channelCB->setCurrentItem(currentOutputChannel); + slotChannelChanged(currentOutputChannel); + } + else + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Gains Mixer text file.")); + return; + } +} + +void ChannelMixerTool::slotSaveAsSettings() +{ + KURL saveGainsFileUrl; + FILE *fp = 0L; + + saveGainsFileUrl = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Gimp Gains Mixer File to Save")) ); + if( saveGainsFileUrl.isEmpty() ) + return; + + fp = fopen(TQFile::encodeName(saveGainsFileUrl.path()), "w"); + + if ( fp ) + { + const char *str = 0L; + char buf1[256]; + char buf2[256]; + char buf3[256]; + + switch ( m_channelCB->currentItem() ) + { + case RedChannelGains: + str = "RED"; + break; + case GreenChannelGains: + str = "GREEN"; + break; + case BlueChannelGains: + str = "BLUE"; + break; + default: + DWarning() << "Unknown Color channel gains" << endl; + break; + } + + fprintf (fp, "# Channel Mixer Configuration File\n"); + + fprintf (fp, "CHANNEL: %s\n", str); + fprintf (fp, "PREVIEW: %s\n", "true"); // preserved for compatibility + fprintf (fp, "MONOCHROME: %s\n", + m_monochrome->isChecked() ? "true" : "false"); + fprintf (fp, "PRESERVE_LUMINOSITY: %s\n", + m_preserveLuminosity->isChecked() ? "true" : "false"); + + sprintf (buf1, "%5.3f", m_redRedGain); + sprintf (buf2, "%5.3f", m_redGreenGain); + sprintf (buf3, "%5.3f", m_redBlueGain); + fprintf (fp, "RED: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_greenRedGain); + sprintf (buf2, "%5.3f", m_greenGreenGain); + sprintf (buf3, "%5.3f", m_greenBlueGain); + fprintf (fp, "GREEN: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_blueRedGain); + sprintf (buf2, "%5.3f", m_blueGreenGain); + sprintf (buf3, "%5.3f", m_blueBlueGain); + fprintf (fp, "BLUE: %s %s %s\n", buf1, buf2,buf3); + + sprintf (buf1, "%5.3f", m_blackRedGain); + sprintf (buf2, "%5.3f", m_blackGreenGain); + sprintf (buf3, "%5.3f", m_blackBlueGain); + fprintf (fp, "BLACK: %s %s %s\n", buf1, buf2,buf3); + + fclose (fp); + } + else + { + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Gains Mixer text file.")); + return; + } +} + +} // NameSpace DigikamChannelMixerImagesPlugin diff --git a/src/imageplugins/channelmixer/channelmixertool.h b/src/imageplugins/channelmixer/channelmixertool.h new file mode 100644 index 00000000..c15a30cd --- /dev/null +++ b/src/imageplugins/channelmixer/channelmixertool.h @@ -0,0 +1,138 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CHANNELMIXERTOOL_H +#define CHANNELMIXERTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQCheckBox; +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +namespace KDcrawIface +{ +class RDoubleNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamChannelMixerImagesPlugin +{ + +class ChannelMixerTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + ChannelMixerTool(TQObject *parent); + ~ChannelMixerTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + void adjustSliders(); + +private slots: + + void slotLoadSettings(); + void slotSaveAsSettings(); + void slotResetCurrentChannel(); + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotGainsChanged(); + void slotMonochromeActived(bool mono); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum ColorChannelGains + { + RedChannelGains=0, + GreenChannelGains, + BlueChannelGains + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + +private: + + uchar *m_destinationPreviewData; + + double m_redRedGain; + double m_redGreenGain; + double m_redBlueGain; + double m_greenRedGain; + double m_greenGreenGain; + double m_greenBlueGain; + double m_blueRedGain; + double m_blueGreenGain; + double m_blueBlueGain; + double m_blackRedGain; + double m_blackGreenGain; + double m_blackBlueGain; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDcrawIface::RDoubleNumInput *m_redGain; + KDcrawIface::RDoubleNumInput *m_greenGain; + KDcrawIface::RDoubleNumInput *m_blueGain; + + TQPushButton *m_resetButton; + + TQCheckBox *m_preserveLuminosity; + TQCheckBox *m_monochrome; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamChannelMixerImagesPlugin + +#endif /* CHANNELMIXERTOOL_H */ diff --git a/src/imageplugins/channelmixer/digikamimageplugin_channelmixer.desktop b/src/imageplugins/channelmixer/digikamimageplugin_channelmixer.desktop new file mode 100644 index 00000000..a855fffe --- /dev/null +++ b/src/imageplugins/channelmixer/digikamimageplugin_channelmixer.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=ImagePlugin_ChannelMixer +Name[bg]=ПриÑтавка за Ñнимки - СмеÑител на канали +Name[el]=ΠÏόσθετοΕικόνας_ΑνάμειξηΚαναλιών +Name[fi]=KanavatasapainonSäätö +Name[hr]=MijeÅ¡anje kanala +Name[it]=PluginImmagini_MixerDeiCanali +Name[nl]=Afbeeldingsplugin_Kanaalmixer +Name[sr]=МикÑета канала +Name[sr@Latn]=Mikseta kanala +Name[sv]=Insticksprogram för kanalblandning +Name[tr]=ResimEklentisi_KanalKarıştırıcı +Name[xx]=xxImagePlugin_ChannelMixerxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 + +Comment=Image color channels mixer plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam ÑÑŠÑ ÑмеÑител на цветови канали +Comment[ca]=Connector pel digiKam per mesclar els canals de color de la imatge +Comment[da]=Digikam plugin til at blande billedfarvekanaler +Comment[de]=digiKam-Modul zum Mischen von Farbkanälen eines Bildes +Comment[el]=ΠÏόσθετο μείκτη καναλιών χÏώματος εικόνας για το digiKam +Comment[en_GB]=Image colour channels mixer plugin for digiKam +Comment[es]=Plugin para digiKam para la mezcla de los canales de color de imagen +Comment[et]=DigiKami pildi värvikanalite mikseri plugin +Comment[fa]=وصلۀ مخلوط‌کن مجراهای رنگ تصویر برای digiKam +Comment[fi]=Värikanavien sekoitussuhteiden muokkain +Comment[gl]=Un plugin de digiKam para misturar os canais de cores +Comment[hr]=digiKam dodatak za mijeÅ¡anje kanala boja +Comment[is]=Ãforrit fyrir digiKam sem blandar litrásum mynda +Comment[it]=Plugin di mixer dei canali dei colori per digiKam +Comment[ja]=digiKam カラーãƒãƒ£ãƒ³ãƒãƒ«ãƒŸã‚­ã‚µãƒ¼ãƒ—ラグイン +Comment[nds]=digiKam-Mischermoduul för de Bild-Klöörkanaals +Comment[nl]=Digikam-plugin voor kanaalmixer +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਿੱਤਰ ਰੰਗ ਚੈਨਲ ਮਿਕਸਰ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam mieszajÄ…ca kanaÅ‚y kolorów +Comment[pt]=Um 'plugin' do digiKam para misturar os canais de cores +Comment[pt_BR]=Plugin do mixer de canais de cor da imagem +Comment[ru]=Модуль Ð¼Ð¸ÐºÑˆÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ†Ð²ÐµÑ‚Ð¾Ð²Ñ‹Ñ… каналов Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre mieÅ¡anie farebných kanálov obrázku +Comment[sr]=Прикључак микÑете канала боја за digiKam +Comment[sr@Latn]=PrikljuÄak miksete kanala boja za digiKam +Comment[sv]=Digikam insticksprogram för att blanda bildfärgkanaler +Comment[tr]=digiKam için resim kanalları karıştıcı eklentisi +Comment[uk]=Втулок Ð·Ð¼Ñ–ÑˆÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ°Ð½Ð°Ð»Ñ–Ð² кольорів Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hoà kênh màu ảnh cho digiKam +Comment[xx]=xxImage color channels mixer plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_channelmixer +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/channelmixer/digikamimageplugin_channelmixer_ui.rc b/src/imageplugins/channelmixer/digikamimageplugin_channelmixer_ui.rc new file mode 100644 index 00000000..1a004c96 --- /dev/null +++ b/src/imageplugins/channelmixer/digikamimageplugin_channelmixer_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Color + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/channelmixer/imageplugin_channelmixer.cpp b/src/imageplugins/channelmixer/imageplugin_channelmixer.cpp new file mode 100644 index 00000000..19fe21a9 --- /dev/null +++ b/src/imageplugins/channelmixer/imageplugin_channelmixer.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "channelmixertool.h" +#include "imageplugin_channelmixer.h" +#include "imageplugin_channelmixer.moc" + +using namespace DigikamChannelMixerImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_channelmixer, + KGenericFactory("digikamimageplugin_channelmixer")) + +ImagePlugin_ChannelMixer::ImagePlugin_ChannelMixer(TQObject *parent, const char*, + const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_ChannelMixer") +{ + m_channelMixerAction = new TDEAction(i18n("Channel Mixer..."), "channelmixer", + CTRL+Key_H, + this, TQ_SLOT(slotChannelMixer()), + actionCollection(), "imageplugin_channelmixer"); + + setXMLFile("digikamimageplugin_channelmixer_ui.rc"); + + DDebug() << "ImagePlugin_ChannelMixer plugin loaded" << endl; +} + +ImagePlugin_ChannelMixer::~ImagePlugin_ChannelMixer() +{ +} + +void ImagePlugin_ChannelMixer::setEnabledActions(bool enable) +{ + m_channelMixerAction->setEnabled(enable); +} + +void ImagePlugin_ChannelMixer::slotChannelMixer() +{ + ChannelMixerTool *cm = new ChannelMixerTool(this); + loadTool(cm); +} diff --git a/src/imageplugins/channelmixer/imageplugin_channelmixer.h b/src/imageplugins/channelmixer/imageplugin_channelmixer.h new file mode 100644 index 00000000..444006ab --- /dev/null +++ b/src/imageplugins/channelmixer/imageplugin_channelmixer.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-26 + * Description : image channels mixer. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_CHANNELMIXER_H +#define IMAGEPLUGIN_CHANNELMIXER_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_ChannelMixer : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_ChannelMixer(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_ChannelMixer(); + + void setEnabledActions(bool enable); + +private slots: + + void slotChannelMixer(); + +private: + + TDEAction *m_channelMixerAction; +}; + +#endif /* IMAGEPLUGIN_CHANNELMIXER_H */ diff --git a/src/imageplugins/charcoal/Makefile.am b/src/imageplugins/charcoal/Makefile.am new file mode 100644 index 00000000..3d851deb --- /dev/null +++ b/src/imageplugins/charcoal/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_charcoal_la_SOURCES = imageplugin_charcoal.cpp \ + charcoaltool.cpp charcoal.cpp + +digikamimageplugin_charcoal_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_charcoal_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_charcoal.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_charcoal.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_charcoal_ui.rc + diff --git a/src/imageplugins/charcoal/charcoal.cpp b/src/imageplugins/charcoal/charcoal.cpp new file mode 100644 index 00000000..a4c54a3c --- /dev/null +++ b/src/imageplugins/charcoal/charcoal.cpp @@ -0,0 +1,249 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Charcoal threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Charcoal algorithm copyright 2002 + * by Daniel M. Duley from KImageEffect API. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define SQ2PI 2.50662827463100024161235523934010416269302368164062 +#define Epsilon 1.0e-12 + +// C++ includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimggaussianblur.h" +#include "dimgimagefilters.h" +#include "charcoal.h" + +namespace DigikamCharcoalImagesPlugin +{ + +Charcoal::Charcoal(Digikam::DImg *orgImage, TQObject *parent, double pencil, double smooth) + : Digikam::DImgThreadedFilter(orgImage, parent, "Charcoal") +{ + m_pencil = pencil; + m_smooth = smooth; + + initFilter(); +} + +void Charcoal::filterImage(void) +{ + if (m_orgImage.isNull()) + { + DWarning() << k_funcinfo << "No image data available!" + << endl; + return; + } + + if (m_pencil <= 0.0) + { + m_destImage = m_orgImage; + return; + } + + // -- Applying Edge effect ----------------------------------------------- + + long i=0; + int kernelWidth = getOptimalKernelWidth(m_pencil, m_smooth); + + if((int)m_orgImage.width() < kernelWidth) + { + DWarning() << k_funcinfo << "Image is smaller than radius!" + << endl; + return; + } + + double *kernel = new double[kernelWidth*kernelWidth]; + + if(!kernel) + { + DWarning() << k_funcinfo << "Unable to allocate memory!" + << endl; + return; + } + + for(i = 0 ; i < (kernelWidth*kernelWidth) ; i++) + kernel[i]=(-1.0); + + kernel[i/2]=kernelWidth*kernelWidth-1.0; + convolveImage(kernelWidth, kernel); + delete [] kernel; + + // -- Applying Gaussian blur effect --------------------------------------- + + Digikam::DImgGaussianBlur(this, m_destImage, m_destImage, 50, 60, (int)(m_smooth/10.0)); + + if (m_cancel) + return; + + // -- Applying strech contrast color effect ------------------------------- + + Digikam::DImgImageFilters().stretchContrastImage(m_destImage.bits(), m_destImage.width(), + m_destImage.height(), m_destImage.sixteenBit()); + postProgress( 70 ); + if (m_cancel) + return; + + // -- Inverting image color ----------------------------------------------- + + Digikam::DImgImageFilters().invertImage(m_destImage.bits(), m_destImage.width(), + m_destImage.height(), m_destImage.sixteenBit()); + postProgress( 80 ); + if (m_cancel) + return; + + // -- Convert to neutral black & white ------------------------------------ + + Digikam::DImgImageFilters().channelMixerImage( + m_destImage.bits(), m_destImage.width(), + m_destImage.height(), m_destImage.sixteenBit(), // Image data. + true, // Preserve luminosity. + true, // Monochrome. + 0.3, 0.59 , 0.11, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + postProgress( 90 ); + if (m_cancel) + return; +} + +bool Charcoal::convolveImage(const unsigned int order, const double *kernel) +{ + uint x, y; + int mx, my, sx, sy, mcx, mcy, progress; + long kernelWidth, i; + double red, green, blue, alpha, normalize=0.0; + double *k=0; + Digikam::DColor color; + + kernelWidth = order; + + if((kernelWidth % 2) == 0) + { + DWarning() << k_funcinfo << "Kernel width must be an odd number!" + << endl; + return(false); + } + + double *normal_kernel = new double[kernelWidth*kernelWidth]; + + if(!normal_kernel) + { + DWarning() << k_funcinfo << "Unable to allocate memory!" + << endl; + return(false); + } + + for(i=0 ; i < (kernelWidth*kernelWidth) ; i++) + normalize += kernel[i]; + + if(fabs(normalize) <= Epsilon) + normalize=1.0; + + normalize = 1.0/normalize; + + for(i=0 ; i < (kernelWidth*kernelWidth) ; i++) + normal_kernel[i] = normalize*kernel[i]; + + double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0; + + for(y=0 ; !m_cancel && (y < m_destImage.height()) ; y++) + { + sy = y-(kernelWidth/2); + + for(x=0 ; !m_cancel && (x < m_destImage.width()) ; x++) + { + k = normal_kernel; + red = green = blue = alpha = 0; + sy = y-(kernelWidth/2); + + for(mcy=0 ; !m_cancel && (mcy < kernelWidth) ; mcy++, sy++) + { + my = sy < 0 ? 0 : sy > (int)m_destImage.height()-1 ? m_destImage.height()-1 : sy; + sx = x+(-kernelWidth/2); + + for(mcx=0 ; !m_cancel && (mcx < kernelWidth) ; mcx++, sx++) + { + mx = sx < 0 ? 0 : sx > (int)m_destImage.width()-1 ? m_destImage.width()-1 : sx; + color = m_orgImage.getPixelColor(mx, my); + red += (*k)*(color.red() * 257.0); + green += (*k)*(color.green() * 257.0); + blue += (*k)*(color.blue() * 257.0); + alpha += (*k)*(color.alpha() * 257.0); + k++; + } + } + + red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red+0.5; + green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green+0.5; + blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue+0.5; + alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha+0.5; + + m_destImage.setPixelColor(x, y, Digikam::DColor((int)(red / 257UL), (int)(green / 257UL), + (int)(blue / 257UL), (int)(alpha / 257UL), + m_destImage.sixteenBit())); + } + + progress = (int)(((double)y * 50.0) / m_destImage.height()); + if ( progress%5 == 0 ) + postProgress( progress ); + } + + delete [] normal_kernel; + return(true); +} + +int Charcoal::getOptimalKernelWidth(double radius, double sigma) +{ + double normalize, value; + long kernelWidth; + long u; + + if(radius > 0.0) + return((int)(2.0*ceil(radius)+1.0)); + + for(kernelWidth=5; ;) + { + normalize=0.0; + + for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++) + normalize += exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma); + + u = kernelWidth/2; + value = exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma)/normalize; + + if((long)(65535*value) <= 0) + break; + + kernelWidth+=2; + } + + return((int)kernelWidth-2); +} + +} // NameSpace DigikamCharcoalImagesPlugin diff --git a/src/imageplugins/charcoal/charcoal.h b/src/imageplugins/charcoal/charcoal.h new file mode 100644 index 00000000..2088ccc4 --- /dev/null +++ b/src/imageplugins/charcoal/charcoal.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Charcoal threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CHARCOAL_H +#define CHARCOAL_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamCharcoalImagesPlugin +{ + +class Charcoal : public Digikam::DImgThreadedFilter +{ + +public: + + Charcoal(Digikam::DImg *orgImage, TQObject *parent=0, double pencil=5.0, double smooth=10.0); + ~Charcoal(){}; + +private: + + void filterImage(void); + bool convolveImage(const unsigned int order, const double *kernel); + int getOptimalKernelWidth(double radius, double sigma); + +private: + + double m_pencil; + double m_smooth; +}; + +} // NameSpace DigikamCharcoalImagesPlugin + +#endif /* CHARCOAL_H */ diff --git a/src/imageplugins/charcoal/charcoaltool.cpp b/src/imageplugins/charcoal/charcoaltool.cpp new file mode 100644 index 00000000..75ee22df --- /dev/null +++ b/src/imageplugins/charcoal/charcoaltool.cpp @@ -0,0 +1,202 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "charcoal.h" +#include "charcoaltool.h" +#include "charcoaltool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamCharcoalImagesPlugin +{ + +CharcoalTool::CharcoalTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("charcoal"); + setToolName(i18n("Charcoal")); + setToolIcon(SmallIcon("charcoaltool")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 4, 1); + TQLabel *label1 = new TQLabel(i18n("Pencil size:"), m_gboxSettings->plainPage()); + + m_pencilInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_pencilInput->setRange(1, 100, 1); + m_pencilInput->setDefaultValue(5); + TQWhatsThis::add( m_pencilInput, i18n("

    Set here the charcoal pencil size used to simulate the drawing.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Smooth:"), m_gboxSettings->plainPage()); + + m_smoothInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_smoothInput->setRange(1, 100, 1); + m_smoothInput->setDefaultValue(10); + TQWhatsThis::add( m_smoothInput, i18n("

    This value controls the smoothing effect of the pencil " + "under the canvas.")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 1); + grid->addMultiCellWidget(m_pencilInput, 1, 1, 0, 1); + grid->addMultiCellWidget(label2, 2, 2, 0, 1); + grid->addMultiCellWidget(m_smoothInput, 3, 3, 0, 1); + grid->setRowStretch(4, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "charcoal Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_pencilInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_smoothInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +CharcoalTool::~CharcoalTool() +{ +} + +void CharcoalTool::renderingFinished() +{ + m_pencilInput->setEnabled(true); + m_smoothInput->setEnabled(true); +} + +void CharcoalTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("charcoal Tool"); + m_pencilInput->blockSignals(true); + m_smoothInput->blockSignals(true); + + m_pencilInput->setValue(config->readNumEntry("PencilAjustment", m_pencilInput->defaultValue())); + m_smoothInput->setValue(config->readNumEntry("SmoothAjustment", m_smoothInput->defaultValue())); + + m_pencilInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void CharcoalTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("charcoal Tool"); + config->writeEntry("PencilAjustment", m_pencilInput->value()); + config->writeEntry("SmoothAjustment", m_smoothInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void CharcoalTool::slotResetSettings() +{ + m_pencilInput->blockSignals(true); + m_smoothInput->blockSignals(true); + + m_pencilInput->slotReset(); + m_smoothInput->slotReset(); + + m_pencilInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void CharcoalTool::prepareEffect() +{ + m_pencilInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + double pencil = (double)m_pencilInput->value()/10.0; + double smooth = (double)m_smoothInput->value(); + + DImg image = m_previewWidget->getOriginalRegionImage(); + + setFilter(dynamic_cast(new Charcoal(&image, this, pencil, smooth))); +} + +void CharcoalTool::prepareFinal() +{ + m_pencilInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + double pencil = (double)m_pencilInput->value()/10.0; + double smooth = (double)m_smoothInput->value(); + + ImageIface iface(0, 0); + setFilter(dynamic_cast(new Charcoal(iface.getOriginalImg(), this, pencil, smooth))); +} + +void CharcoalTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void CharcoalTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Charcoal"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamCharcoalImagesPlugin diff --git a/src/imageplugins/charcoal/charcoaltool.h b/src/imageplugins/charcoal/charcoaltool.h new file mode 100644 index 00000000..5833dd06 --- /dev/null +++ b/src/imageplugins/charcoal/charcoaltool.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CHARCOALTOOL_H +#define CHARCOALTOOL_H + +// Local includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamCharcoalImagesPlugin +{ + +class CharcoalTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + CharcoalTool(TQObject* parent); + ~CharcoalTool(); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KDcrawIface::RIntNumInput *m_pencilInput; + KDcrawIface::RIntNumInput *m_smoothInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamCharcoalImagesPlugin + +#endif /* CHARCOALTOOL_H */ diff --git a/src/imageplugins/charcoal/digikamimageplugin_charcoal.desktop b/src/imageplugins/charcoal/digikamimageplugin_charcoal.desktop new file mode 100644 index 00000000..e8957bbf --- /dev/null +++ b/src/imageplugins/charcoal/digikamimageplugin_charcoal.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_Charcoal +Name[bg]=ПриÑтавка за Ñнимки - Въглен +Name[da]=Billedplugin_Kultegning +Name[el]=ΠÏόσθετοΕικόνας_ΚάÏβουνο +Name[fi]=Hiilipiirros +Name[hr]=Crtež ugljenom +Name[it]=PluginImmagini_Carboncino +Name[nl]=Afbeeldingsplugin_Houtskool +Name[sr]=Угљен +Name[sr@Latn]=Ugljen +Name[sv]=Insticksprogram för kolteckning +Name[tr]=ResimEklentisi_Karakalem +Name[xx]=xxImagePlugin_Charcoalxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Charcoal drawing image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наподобÑване на риÑуване Ñ Ð²ÑŠÐ³Ð»ÐµÐ½ върху Ñнимки +Comment[ca]=Connector pel digiKam d'efecte d'imatge de dibuix al carbonet +Comment[da]=Plugin til kultegningseffekt pÃ¥ billeder i Digikam +Comment[de]=digiKam-Modul zum Erzeugen eines Kohlezeichnung-Effekts +Comment[el]=ΠÏόσθετο εφέ σχεδίασης με κάÏβουνο για το digiKam +Comment[es]=Plugin para digiKam con efectos de dibujo a carboncillo +Comment[et]=DigiKami söejoonistuse pildiefektiplugin +Comment[fa]=وصلۀ جلوۀ تصویر ترسیم Charcoal برای digiKam +Comment[fi]=Jäljittelee hiiliviivapiirrosta +Comment[gl]=Un plugin de digiKam para o efeito de imaxe debuxada ao carbón +Comment[hr]=digiKam dodatak za efekt crtanja ugljenom +Comment[is]=Ãforrit fyrir digiKam sem líkir eftir viðarkolateikningu +Comment[it]=Plugin di effetto di disegno dell'immagine con carboncino per digiKam +Comment[ja]=digiKam 木炭画効果プラグイン +Comment[nds]=digiKam-Moduul för Kahlteken-Effekten +Comment[nl]=Digikam-plugin voor houtskooltekeningen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਾਰਕੋਲ ਡਰਾਇੰਗ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam imitujÄ…ca szkice wÄ™glem +Comment[pt]=Um 'plugin' do digiKam para o efeito de imagem de desenho a carvão +Comment[pt_BR]=Plugin de efeito de carvão para o digiKam +Comment[ru]=Модуль Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚Ð¸Ð½ÐºÐ¸ углем Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre efekt kreslenia kriedou +Comment[sr]=Прикључак ефекта цртежа угљеном за digiKam +Comment[sr@Latn]=PrikljuÄak efekta crteža ugljenom za digiKam +Comment[sv]=Digikam insticksprogram för kolteckningsbildeffekt +Comment[tr]=digiKam için karakalem resim etkisi eklentisi +Comment[uk]=Втулок ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ Ð¼Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð²ÑƒÐ³Ñ–Ð»ÑŒÐ½Ð¸Ð¼ олівцем Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng vẽ ảnh than gá»— cho digiKam +Comment[xx]=xxCharcoal drawing image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_charcoal +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/charcoal/digikamimageplugin_charcoal_ui.rc b/src/imageplugins/charcoal/digikamimageplugin_charcoal_ui.rc new file mode 100644 index 00000000..92ab4752 --- /dev/null +++ b/src/imageplugins/charcoal/digikamimageplugin_charcoal_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/charcoal/imageeffect_charcoal.cpp b/src/imageplugins/charcoal/imageeffect_charcoal.cpp new file mode 100644 index 00000000..67a0269d --- /dev/null +++ b/src/imageplugins/charcoal/imageeffect_charcoal.cpp @@ -0,0 +1,193 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "charcoal.h" +#include "imageeffect_charcoal.h" +#include "imageeffect_charcoal.moc" + +namespace DigikamCharcoalImagesPlugin +{ + +ImageEffect_Charcoal::ImageEffect_Charcoal(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Charcoal Drawing"), + "charcoal", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Charcoal Drawing"), + digikam_version, + I18N_NOOP("A digiKam charcoal drawing image effect plugin."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 3, 1, 0, spacingHint()); + TQLabel *label1 = new TQLabel(i18n("Pencil size:"), gboxSettings); + + m_pencilInput = new KIntNumInput(gboxSettings); + m_pencilInput->setRange(1, 100, 1, true); + m_pencilInput->setValue(5); + TQWhatsThis::add( m_pencilInput, i18n("

    Set here the charcoal pencil size used to simulate the drawing.")); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_pencilInput, 1, 1, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Smooth:"), gboxSettings); + + m_smoothInput = new KIntNumInput(gboxSettings); + m_smoothInput->setRange(1, 100, 1, true); + m_smoothInput->setValue(10); + TQWhatsThis::add( m_smoothInput, i18n("

    This value controls the smoothing effect of the pencil " + "under the canvas.")); + + gridSettings->addMultiCellWidget(label2, 2, 2, 0, 1); + gridSettings->addMultiCellWidget(m_smoothInput, 3, 3, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_pencilInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_smoothInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_Charcoal::~ImageEffect_Charcoal() +{ +} + +void ImageEffect_Charcoal::renderingFinished() +{ + m_pencilInput->setEnabled(true); + m_smoothInput->setEnabled(true); +} + +void ImageEffect_Charcoal::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("charcoal Tool Dialog"); + m_pencilInput->blockSignals(true); + m_smoothInput->blockSignals(true); + m_pencilInput->setValue(config->readNumEntry("PencilAjustment", 5)); + m_smoothInput->setValue(config->readNumEntry("SmoothAjustment", 10)); + m_pencilInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void ImageEffect_Charcoal::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("charcoal Tool Dialog"); + config->writeEntry("PencilAjustment", m_pencilInput->value()); + config->writeEntry("SmoothAjustment", m_smoothInput->value()); + config->sync(); +} + +void ImageEffect_Charcoal::resetValues() +{ + m_pencilInput->blockSignals(true); + m_smoothInput->blockSignals(true); + m_pencilInput->setValue(5); + m_smoothInput->setValue(10); + m_pencilInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void ImageEffect_Charcoal::prepareEffect() +{ + m_pencilInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + double pencil = (double)m_pencilInput->value()/10.0; + double smooth = (double)m_smoothInput->value(); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + + m_threadedFilter = dynamic_cast(new Charcoal(&image, this, pencil, smooth)); +} + +void ImageEffect_Charcoal::prepareFinal() +{ + m_pencilInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + double pencil = (double)m_pencilInput->value()/10.0; + double smooth = (double)m_smoothInput->value(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast(new Charcoal(iface.getOriginalImg(), + this, pencil, smooth)); +} + +void ImageEffect_Charcoal::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_Charcoal::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Charcoal"), m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamCharcoalImagesPlugin + diff --git a/src/imageplugins/charcoal/imageeffect_charcoal.h b/src/imageplugins/charcoal/imageeffect_charcoal.h new file mode 100644 index 00000000..31916ecc --- /dev/null +++ b/src/imageplugins/charcoal/imageeffect_charcoal.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_CHARCOAL_H +#define IMAGEEFFECT_CHARCOAL_H + +// Local includes. + +#include "ctrlpaneldlg.h" + +class KIntNumInput; + +namespace DigikamCharcoalImagesPlugin +{ + +class ImageEffect_Charcoal : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Charcoal(TQWidget* parent); + ~ImageEffect_Charcoal(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KIntNumInput *m_pencilInput; + KIntNumInput *m_smoothInput; +}; + +} // NameSpace DigikamCharcoalImagesPlugin + +#endif /* IMAGEEFFECT_CHARCOAL_H */ diff --git a/src/imageplugins/charcoal/imageplugin_charcoal.cpp b/src/imageplugins/charcoal/imageplugin_charcoal.cpp new file mode 100644 index 00000000..ae28e7ca --- /dev/null +++ b/src/imageplugins/charcoal/imageplugin_charcoal.cpp @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "charcoaltool.h" +#include "imageplugin_charcoal.h" +#include "imageplugin_charcoal.moc" + +using namespace DigikamCharcoalImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_charcoal, + KGenericFactory("digikamimageplugin_charcoal")); + +ImagePlugin_Charcoal::ImagePlugin_Charcoal(TQObject *parent, const char*, + const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Charcoal") +{ + m_charcoalAction = new TDEAction(i18n("Charcoal Drawing..."), "charcoaltool", 0, + this, TQ_SLOT(slotCharcoal()), + actionCollection(), "imageplugin_charcoal"); + + setXMLFile( "digikamimageplugin_charcoal_ui.rc" ); + + DDebug() << "ImagePlugin_Charcoal plugin loaded" << endl; +} + +ImagePlugin_Charcoal::~ImagePlugin_Charcoal() +{ +} + +void ImagePlugin_Charcoal::setEnabledActions(bool enable) +{ + m_charcoalAction->setEnabled(enable); +} + +void ImagePlugin_Charcoal::slotCharcoal() +{ + CharcoalTool *tool = new CharcoalTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/charcoal/imageplugin_charcoal.h b/src/imageplugins/charcoal/imageplugin_charcoal.h new file mode 100644 index 00000000..c642087a --- /dev/null +++ b/src/imageplugins/charcoal/imageplugin_charcoal.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digikam image editor plugin to + * simulate charcoal drawing. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_CHARCOAL_H +#define IMAGEPLUGIN_CHARCOAL_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Charcoal : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Charcoal(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Charcoal(); + + void setEnabledActions(bool enable); + +private slots: + + void slotCharcoal(); + +private: + + TDEAction *m_charcoalAction; +}; + +#endif /* IMAGEPLUGIN_CHARCOAL_H */ diff --git a/src/imageplugins/colorfx/Makefile.am b/src/imageplugins/colorfx/Makefile.am new file mode 100644 index 00000000..a0d2e4fa --- /dev/null +++ b/src/imageplugins/colorfx/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_colorfx_la_SOURCES = imageplugin_colorfx.cpp \ + colorfxtool.cpp + +digikamimageplugin_colorfx_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_colorfx_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_colorfx.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_colorfx.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_colorfx_ui.rc + diff --git a/src/imageplugins/colorfx/colorfxtool.cpp b/src/imageplugins/colorfx/colorfxtool.cpp new file mode 100644 index 00000000..dd16e7cf --- /dev/null +++ b/src/imageplugins/colorfx/colorfxtool.cpp @@ -0,0 +1,699 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "colorgradientwidget.h" +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "colorfxtool.h" +#include "colorfxtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamColorFXImagesPlugin +{ + +ColorFXTool::ColorFXTool(TQObject* parent) + : EditorTool(parent) +{ + setName("coloreffects"); + setToolName(i18n("Color Effects")); + setToolIcon(SmallIcon("colorfx")); + + m_destinationPreviewData = 0; + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("coloreffects Tool", 0, + i18n("

    This is the color effects preview")); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + EditorToolSettings *gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings->plainPage(), 9, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + // ------------------------------------------------------------- + + m_effectTypeLabel = new TQLabel(i18n("Type:"), gboxSettings->plainPage()); + + m_effectType = new RComboBox(gboxSettings->plainPage()); + m_effectType->insertItem(i18n("Solarize")); + m_effectType->insertItem(i18n("Vivid")); + m_effectType->insertItem(i18n("Neon")); + m_effectType->insertItem(i18n("Find Edges")); + m_effectType->setDefaultItem(Solarize); + TQWhatsThis::add( m_effectType, i18n("

    Select the effect type to apply to the image here.

    " + "Solarize: simulates solarization of photograph.

    " + "Vivid: simulates the Velvia(tm) slide film colors.

    " + "Neon: coloring the edges in a photograph to " + "reproduce a fluorescent light effect.

    " + "Find Edges: detects the edges in a photograph " + "and their strength." + )); + + m_levelLabel = new TQLabel(i18n("Level:"), gboxSettings->plainPage()); + m_levelInput = new RIntNumInput(gboxSettings->plainPage()); + m_levelInput->setRange(0, 100, 1); + m_levelInput->setDefaultValue(0); + TQWhatsThis::add( m_levelInput, i18n("

    Set here the level of the effect.")); + + m_iterationLabel = new TQLabel(i18n("Iteration:"), gboxSettings->plainPage()); + m_iterationInput = new RIntNumInput(gboxSettings->plainPage()); + m_iterationInput->setRange(0, 100, 1); + m_iterationInput->setDefaultValue(0); + TQWhatsThis::add( m_iterationInput, i18n("

    This value controls the number of iterations " + "to use with the Neon and Find Edges effects.")); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + gridSettings->addMultiCellWidget(m_effectTypeLabel, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_effectType, 4, 4, 0, 4); + gridSettings->addMultiCellWidget(m_levelLabel, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_levelInput, 6, 6, 0, 4); + gridSettings->addMultiCellWidget(m_iterationLabel, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_iterationInput, 8, 8, 0, 4); + gridSettings->setRowStretch(9, 10); + + setToolSettings(gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const DColor & ))); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_iterationInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); +} + +ColorFXTool::~ColorFXTool() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void ColorFXTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("coloreffect Tool"); + m_effectType->setCurrentItem(config->readNumEntry("EffectType", m_effectType->defaultItem())); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", m_levelInput->defaultValue())); + m_iterationInput->setValue(config->readNumEntry("IterationAjustment", m_iterationInput->defaultValue())); + slotEffectTypeChanged(m_effectType->currentItem()); //check for enable/disable of iteration + + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ColorFXTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("coloreffect Tool"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + config->writeEntry("IterationAjustment", m_iterationInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void ColorFXTool::slotResetSettings() +{ + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + m_effectType->blockSignals(true); + + m_levelInput->slotReset(); + m_iterationInput->slotReset(); + m_effectType->slotReset(); + + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); + m_effectType->blockSignals(false); + + slotEffect(); +} + +void ColorFXTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void ColorFXTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ColorFXTool::slotColorSelectedFromTarget(const DColor &color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ColorFXTool::slotEffectTypeChanged(int type) +{ + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->setRange(0, 100, 1); + m_levelInput->setValue(25); + + switch (type) + { + case Solarize: + m_levelInput->setRange(0, 100, 1); + m_levelInput->setValue(0); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + break; + + case Vivid: + m_levelInput->setRange(0, 50, 1); + m_levelInput->setValue(5); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + break; + + case Neon: + case FindEdges: + m_levelInput->setRange(0, 5, 1); + m_levelInput->setValue(3); + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + m_iterationInput->setRange(0, 5, 1); + m_iterationInput->setValue(2); + break; + } + + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); + + slotEffect(); +} + +void ColorFXTool::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + colorEffect(m_destinationPreviewData, w, h, sb); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ColorFXTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + colorEffect(data, w, h, sb); + TQString name; + + switch (m_effectType->currentItem()) + { + case Solarize: + name = i18n("ColorFX"); + break; + + case Vivid: + name = i18n("Vivid"); + break; + + case Neon: + name = i18n("Neon"); + break; + + case FindEdges: + name = i18n("Find Edges"); + break; + } + + iface->putOriginalImage(name, data); + delete[] data; + } + + kapp->restoreOverrideCursor(); +} + +void ColorFXTool::colorEffect(uchar *data, int w, int h, bool sb) +{ + switch (m_effectType->currentItem()) + { + case Solarize: + solarize(m_levelInput->value(), data, w, h, sb); + break; + + case Vivid: + vivid(m_levelInput->value(), data, w, h, sb); + break; + + case Neon: + neon(data, w, h, sb, m_levelInput->value(), m_iterationInput->value()); + break; + + case FindEdges: + findEdges(data, w, h, sb, m_levelInput->value(), m_iterationInput->value()); + break; + } +} + +void ColorFXTool::solarize(int factor, uchar *data, int w, int h, bool sb) +{ + bool stretch = true; + + if (!sb) // 8 bits image. + { + uint threshold = (uint)((100-factor)*(255+1)/100); + threshold = TQMAX(1, threshold); + uchar *ptr = data; + uchar a, r, g, b; + + for (int x=0 ; x < w*h ; x++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + a = ptr[3]; + + if (stretch) + { + r = (r > threshold) ? (255-r)*255/(255-threshold) : r*255/threshold; + g = (g > threshold) ? (255-g)*255/(255-threshold) : g*255/threshold; + b = (b > threshold) ? (255-b)*255/(255-threshold) : b*255/threshold; + } + else + { + if (r > threshold) + r = (255-r); + if (g > threshold) + g = (255-g); + if (b > threshold) + b = (255-b); + } + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + ptr[3] = a; + + ptr += 4; + } + } + else // 16 bits image. + { + uint threshold = (uint)((100-factor)*(65535+1)/100); + threshold = TQMAX(1, threshold); + unsigned short *ptr = (unsigned short *)data; + unsigned short a, r, g, b; + + for (int x=0 ; x < w*h ; x++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + a = ptr[3]; + + if (stretch) + { + r = (r > threshold) ? (65535-r)*65535/(65535-threshold) : r*65535/threshold; + g = (g > threshold) ? (65535-g)*65535/(65535-threshold) : g*65535/threshold; + b = (b > threshold) ? (65535-b)*65535/(65535-threshold) : b*65535/threshold; + } + else + { + if (r > threshold) + r = (65535-r); + if (g > threshold) + g = (65535-g); + if (b > threshold) + b = (65535-b); + } + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + ptr[3] = a; + + ptr += 4; + } + } +} + +void ColorFXTool::vivid(int factor, uchar *data, int w, int h, bool sb) +{ + float amount = factor/100.0; + + DImgImageFilters filter; + + // Apply Channel Mixer adjustments. + + filter.channelMixerImage( + data, w, h, sb, // Image data. + true, // Preserve Luminosity + false, // Disable Black & White mode. + 1.0 + amount + amount, (-1.0)*amount, (-1.0)*amount, // Red Gains. + (-1.0)*amount, 1.0 + amount + amount, (-1.0)*amount, // Green Gains. + (-1.0)*amount, (-1.0)*amount, 1.0 + amount + amount // Blue Gains. + ); + + // Allocate the destination image data. + + uchar *dest = new uchar[w*h*(sb ? 8 : 4)]; + + // And now apply the curve correction. + + ImageCurves Curves(sb); + + if (!sb) // 8 bits image. + { + Curves.setCurvePoint(ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 5, TQPoint(63, 60)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 10, TQPoint(191, 194)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 16, TQPoint(255, 255)); + } + else // 16 bits image. + { + Curves.setCurvePoint(ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 5, TQPoint(16128, 15360)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 10, TQPoint(48896, 49664)); + Curves.setCurvePoint(ImageHistogram::ValueChannel, 16, TQPoint(65535, 65535)); + } + + Curves.curvesCalculateCurve(ImageHistogram::AlphaChannel); // Calculate cure on all channels. + Curves.curvesLutSetup(ImageHistogram::AlphaChannel); // ... and apply it on all channels + Curves.curvesLutProcess(data, dest, w, h); + + memcpy(data, dest, w*h*(sb ? 8 : 4)); + delete [] dest; +} + +/* Function to apply the Neon effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Intensity => Intensity value + * BW => Border Width + * + * Theory => Wow, this is a great effect, you've never seen a Neon effect + * like this on PSC. Is very similar to Growing Edges (photoshop) + * Some pictures will be very interesting + */ +void ColorFXTool::neon(uchar *data, int w, int h, bool sb, int Intensity, int BW) +{ + neonFindEdges(data, w, h, sb, true, Intensity, BW); +} + +/* Function to apply the Find Edges effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Intensity => Intensity value + * BW => Border Width + * + * Theory => Wow, another Photoshop filter (FindEdges). Do you understand + * Neon effect ? This is the same engine, but is inversed with + * 255 - color. + */ +void ColorFXTool::findEdges(uchar *data, int w, int h, bool sb, int Intensity, int BW) +{ + neonFindEdges(data, w, h, sb, false, Intensity, BW); +} + +// Implementation of neon and FindEdges. They share 99% of their code. +void ColorFXTool::neonFindEdges(uchar *data, int w, int h, bool sb, bool neon, int Intensity, int BW) +{ + int Width = w; + int Height = h; + bool sixteenBit = sb; + int bytesDepth = sb ? 8 : 4; + uchar* pResBits = new uchar[Width*Height*bytesDepth]; + + Intensity = (Intensity < 0) ? 0 : (Intensity > 5) ? 5 : Intensity; + BW = (BW < 1) ? 1 : (BW > 5) ? 5 : BW; + + uchar *ptr, *ptr1, *ptr2; + + // these must be uint, we need full 2^32 range for 16 bit + uint color_1, color_2, colorPoint, colorOther1, colorOther2; + + // initial copy + memcpy (pResBits, data, Width*Height*bytesDepth); + + double intensityFactor = sqrt( 1 << Intensity ); + + for (int h = 0; h < Height; h++) + { + for (int w = 0; w < Width; w++) + { + ptr = pResBits + getOffset(Width, w, h, bytesDepth); + ptr1 = pResBits + getOffset(Width, w + Lim_Max (w, BW, Width), h, bytesDepth); + ptr2 = pResBits + getOffset(Width, w, h + Lim_Max (h, BW, Height), bytesDepth); + + if (sixteenBit) + { + for (int k = 0; k <= 2; k++) + { + colorPoint = ((unsigned short *)ptr)[k]; + colorOther1 = ((unsigned short *)ptr1)[k]; + colorOther2 = ((unsigned short *)ptr2)[k]; + color_1 = (colorPoint - colorOther1) * (colorPoint - colorOther1); + color_2 = (colorPoint - colorOther2) * (colorPoint - colorOther2); + + // old algorithm was + // sqrt ((color_1 + color_2) << Intensity) + // As (a << I) = a * (1 << I) = a * (2^I), and we can split the square root + + if (neon) + ((unsigned short *)ptr)[k] = CLAMP065535 ((int)( sqrt((double)color_1 + color_2) * intensityFactor )); + else + ((unsigned short *)ptr)[k] = 65535 - CLAMP065535 ((int)( sqrt((double)color_1 + color_2) * intensityFactor )); + } + } + else + { + for (int k = 0; k <= 2; k++) + { + colorPoint = ptr[k]; + colorOther1 = ptr1[k]; + colorOther2 = ptr2[k]; + color_1 = (colorPoint - colorOther1) * (colorPoint - colorOther1); + color_2 = (colorPoint - colorOther2) * (colorPoint - colorOther2); + + if (neon) + ptr[k] = CLAMP0255 ((int)( sqrt((double)color_1 + color_2) * intensityFactor )); + else + ptr[k] = 255 - CLAMP0255 ((int)( sqrt((double)color_1 + color_2) * intensityFactor )); + } + } + } + } + + memcpy (data, pResBits, Width*Height*bytesDepth); + delete [] pResBits; +} + +int ColorFXTool::getOffset(int Width, int X, int Y, int bytesDepth) +{ + return (Y * Width * bytesDepth) + (X * bytesDepth); +} + +inline int ColorFXTool::Lim_Max(int Now, int Up, int Max) +{ + --Max; + while (Now > Max - Up) --Up; + return (Up); +} + +} // NameSpace DigikamColorFXImagesPlugin + diff --git a/src/imageplugins/colorfx/colorfxtool.h b/src/imageplugins/colorfx/colorfxtool.h new file mode 100644 index 00000000..6040f53e --- /dev/null +++ b/src/imageplugins/colorfx/colorfxtool.h @@ -0,0 +1,136 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef COLORFXTOOL_H +#define COLORFXTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQHButtonGroup; +class TQComboBox; +class TQLabel; + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class ImageWidget; +class ColorGradientWidget; +class HistogramWidget; +class DColor; +} + +namespace DigikamColorFXImagesPlugin +{ + +class ColorFXTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + ColorFXTool(TQObject *parent); + ~ColorFXTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + void colorEffect(uchar *data, int w, int h, bool sb); + void solarize(int factor, uchar *data, int w, int h, bool sb); + void vivid(int factor, uchar *data, int w, int h, bool sb); + void neon(uchar *data, int w, int h, bool sb, int Intensity, int BW); + void findEdges(uchar *data, int w, int h, bool sb, int Intensity, int BW); + void neonFindEdges(uchar *data, int w, int h, bool sb, bool neon, int Intensity, int BW); + + inline int getOffset(int Width, int X, int Y, int bytesDepth); + inline int Lim_Max(int Now, int Up, int Max); + +private slots: + + void slotEffectTypeChanged(int type); + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum ColorFXTypes + { + Solarize=0, + Vivid, + Neon, + FindEdges + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQLabel *m_effectTypeLabel; + TQLabel *m_levelLabel; + TQLabel *m_iterationLabel; + + KDcrawIface::RIntNumInput *m_levelInput; + KDcrawIface::RIntNumInput *m_iterationInput; + + KDcrawIface::RComboBox *m_effectType; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamColorFXImagesPlugin + +#endif /* COLORFXTOOL_H */ diff --git a/src/imageplugins/colorfx/digikamimageplugin_colorfx.desktop b/src/imageplugins/colorfx/digikamimageplugin_colorfx.desktop new file mode 100644 index 00000000..7350f6d9 --- /dev/null +++ b/src/imageplugins/colorfx/digikamimageplugin_colorfx.desktop @@ -0,0 +1,40 @@ +[Desktop Entry] +Name=ImagePlugin_ColorFx +Name[fi]=Väriefektit +Name[it]=PluginImmagini_EffettiDiColore +Name[nl]=Afbeeldingsplugin_Kleureffecten +Name[sr]=Ефекти боје +Name[sr@Latn]=Efekti boje +Name[sv]=Insticksprogram med färgeffekter +Name[tr]=ResimEklentisi_RenkFx +Name[xx]=xxImagePlugin_ColorFxxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Color special effects plugin for digiKam +Comment[ca]=Connector pel digiKam d'efectes especials de color +Comment[da]=Digikam plugin med specialeffekter for farve +Comment[de]=digiKam-Modul zum Erzeugen von speziellen Farbeffekten +Comment[el]=ΠÏόσθετο ειδικών εφέ χÏώματος για το digiKam +Comment[es]=Plugin para digiKam con efectos especiales de color +Comment[et]=DigiKami värvieriefektide plugin +Comment[fi]=Erikoisia väritehosteita +Comment[is]=Ãforrit fyrir digiKam sem litmeðhöndlar sérstaklega myndir +Comment[it]=Plugin degli effetti speciali dei colori per digiKam +Comment[ja]=digiKam 色特殊効果プラグイン +Comment[nds]=digiKam-Moduul för Klöör-Effekten +Comment[nl]=Digikam-plugin voor kleureffecten +Comment[pl]=Wtyczka specjalnych efektów koloru do programu digiKam +Comment[pt]=Um 'plugin' do digiKam para efeitos especiais de cores +Comment[pt_BR]=Plugin de efeitos especiais de Cor +Comment[sk]=digiKam plugin pre Å¡peciálne farebné efekty +Comment[sr]=Прикључак поÑебних ефеката боје за digiKam +Comment[sr@Latn]=PrikljuÄak posebnih efekata boje za digiKam +Comment[sv]=Digikam insticksprogram med specialeffekter för färg +Comment[tr]=digiKam için özel renk eklentisi +Comment[uk]=Втулок Ñпецефектів кольорів Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng màu sắc cho digiKam +Comment[xx]=xxColor special effects plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_colorfx +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/colorfx/digikamimageplugin_colorfx_ui.rc b/src/imageplugins/colorfx/digikamimageplugin_colorfx_ui.rc new file mode 100644 index 00000000..443a00bd --- /dev/null +++ b/src/imageplugins/colorfx/digikamimageplugin_colorfx_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Color + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/colorfx/imageeffect_colorfx.cpp b/src/imageplugins/colorfx/imageeffect_colorfx.cpp new file mode 100644 index 00000000..a4ab6fe7 --- /dev/null +++ b/src/imageplugins/colorfx/imageeffect_colorfx.cpp @@ -0,0 +1,690 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "imageeffect_colorfx.h" +#include "imageeffect_colorfx.moc" + +namespace DigikamColorFXImagesPlugin +{ + +ImageEffect_ColorFX::ImageEffect_ColorFX(TQWidget* parent) + : Digikam::ImageDlgBase(parent, + i18n("Apply Color Special Effects to Photograph"), + "coloreffect", false, false) +{ + m_destinationPreviewData = 0; + + // About data and help button. + + TDEAboutData *about = new TDEAboutData("digikam", + I18N_NOOP("Color Effects"), + digikam_version, + I18N_NOOP("A digiKam plugin to apply special color effects to an image."), + TDEAboutData::License_GPL, + "(c) 2004-2005, Renchi Raju\n(c) 2006-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Renchi Raju", I18N_NOOP("Original Author"), + "renchi@pooh.tam.uiuc.edu"); + + about->addAuthor("Caulier Gilles", I18N_NOOP("Maintainer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("coloreffect Tool Dialog", plainPage(), + i18n("

    This is the color effect preview")); + + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 9, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_effectTypeLabel = new TQLabel(i18n("Type:"), gboxSettings); + + m_effectType = new TQComboBox( false, gboxSettings ); + m_effectType->insertItem( i18n("Solarize") ); + m_effectType->insertItem( i18n("Vivid") ); + m_effectType->insertItem( i18n("Neon") ); + m_effectType->insertItem( i18n("Find Edges") ); + TQWhatsThis::add( m_effectType, i18n("

    Select the effect type to apply to the image here.

    " + "Solarize: simulates solarization of photograph.

    " + "Vivid: simulates the Velvia(tm) slide film colors.

    " + "Neon: coloring the edges in a photograph to " + "reproduce a fluorescent light effect.

    " + "Find Edges: detects the edges in a photograph " + "and their strength." + )); + gridSettings->addMultiCellWidget(m_effectTypeLabel, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_effectType, 4, 4, 0, 4); + + m_levelLabel = new TQLabel(i18n("Level:"), gboxSettings); + m_levelInput = new KIntNumInput(gboxSettings); + m_levelInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_levelInput, i18n("

    Set here the level of the effect.")); + + gridSettings->addMultiCellWidget(m_levelLabel, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_levelInput, 6, 6, 0, 4); + + m_iterationLabel = new TQLabel(i18n("Iteration:"), gboxSettings); + m_iterationInput = new KIntNumInput(gboxSettings); + m_iterationInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_iterationInput, i18n("

    This value controls the number of iterations " + "to use with the Neon and Find Edges effects.")); + + gridSettings->addMultiCellWidget(m_iterationLabel, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_iterationInput, 8, 8, 0, 4); + + gridSettings->setRowStretch(9, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_iterationInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); +} + +ImageEffect_ColorFX::~ImageEffect_ColorFX() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_previewWidget; +} + +void ImageEffect_ColorFX::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("coloreffect Tool Dialog"); + m_effectType->setCurrentItem(config->readNumEntry("EffectType", ColorFX)); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", 0)); + m_iterationInput->setValue(config->readNumEntry("IterationAjustment", 3)); + slotEffectTypeChanged(m_effectType->currentItem()); //check for enable/disable of iteration +} + +void ImageEffect_ColorFX::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("coloreffect Tool Dialog"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + config->writeEntry("IterationAjustment", m_iterationInput->value()); + config->sync(); +} + +void ImageEffect_ColorFX::resetValues() +{ + m_levelInput->setValue(0); +} + +void ImageEffect_ColorFX::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_ColorFX::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_ColorFX::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_ColorFX::slotEffectTypeChanged(int type) +{ + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->setRange(0, 100, 1, true); + m_levelInput->setValue(25); + + switch (type) + { + case ColorFX: + m_levelInput->setRange(0, 100, 1, true); + m_levelInput->setValue(0); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + break; + + case Vivid: + m_levelInput->setRange(0, 50, 1, true); + m_levelInput->setValue(5); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + break; + + case Neon: + case FindEdges: + m_levelInput->setRange(0, 5, 1, true); + m_levelInput->setValue(3); + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + m_iterationInput->setRange(0, 5, 1, true); + m_iterationInput->setValue(2); + break; + } + + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_ColorFX::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + colorEffect(m_destinationPreviewData, w, h, sb); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_ColorFX::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + colorEffect(data, w, h, sb); + TQString name; + + switch (m_effectType->currentItem()) + { + case ColorFX: + name = i18n("ColorFX"); + break; + + case Vivid: + name = i18n("Vivid"); + break; + + case Neon: + name = i18n("Neon"); + break; + + case FindEdges: + name = i18n("Find Edges"); + break; + } + + iface->putOriginalImage(name, data); + delete [] data; + } + + kapp->restoreOverrideCursor(); + accept(); +} + +void ImageEffect_ColorFX::colorEffect(uchar *data, int w, int h, bool sb) +{ + switch (m_effectType->currentItem()) + { + case ColorFX: + solarize(m_levelInput->value(), data, w, h, sb); + break; + + case Vivid: + vivid(m_levelInput->value(), data, w, h, sb); + break; + + case Neon: + neon(data, w, h, sb, m_levelInput->value(), m_iterationInput->value()); + break; + + case FindEdges: + findEdges(data, w, h, sb, m_levelInput->value(), m_iterationInput->value()); + break; + } +} + +void ImageEffect_ColorFX::solarize(int factor, uchar *data, int w, int h, bool sb) +{ + bool stretch = true; + + if (!sb) // 8 bits image. + { + uint threshold = (uint)((100-factor)*(255+1)/100); + threshold = TQMAX(1, threshold); + uchar *ptr = data; + uchar a, r, g, b; + + for (int x=0 ; x < w*h ; x++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + a = ptr[3]; + + if (stretch) + { + r = (r > threshold) ? (255-r)*255/(255-threshold) : r*255/threshold; + g = (g > threshold) ? (255-g)*255/(255-threshold) : g*255/threshold; + b = (b > threshold) ? (255-b)*255/(255-threshold) : b*255/threshold; + } + else + { + if (r > threshold) + r = (255-r); + if (g > threshold) + g = (255-g); + if (b > threshold) + b = (255-b); + } + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + ptr[3] = a; + + ptr += 4; + } + } + else // 16 bits image. + { + uint threshold = (uint)((100-factor)*(65535+1)/100); + threshold = TQMAX(1, threshold); + unsigned short *ptr = (unsigned short *)data; + unsigned short a, r, g, b; + + for (int x=0 ; x < w*h ; x++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + a = ptr[3]; + + if (stretch) + { + r = (r > threshold) ? (65535-r)*65535/(65535-threshold) : r*65535/threshold; + g = (g > threshold) ? (65535-g)*65535/(65535-threshold) : g*65535/threshold; + b = (b > threshold) ? (65535-b)*65535/(65535-threshold) : b*65535/threshold; + } + else + { + if (r > threshold) + r = (65535-r); + if (g > threshold) + g = (65535-g); + if (b > threshold) + b = (65535-b); + } + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + ptr[3] = a; + + ptr += 4; + } + } +} + +void ImageEffect_ColorFX::vivid(int factor, uchar *data, int w, int h, bool sb) +{ + float amount = factor/100.0; + + Digikam::DImgImageFilters filter; + + // Apply Channel Mixer adjustments. + + filter.channelMixerImage( + data, w, h, sb, // Image data. + true, // Preserve Luminosity + false, // Disable Black & White mode. + 1.0 + amount + amount, (-1.0)*amount, (-1.0)*amount, // Red Gains. + (-1.0)*amount, 1.0 + amount + amount, (-1.0)*amount, // Green Gains. + (-1.0)*amount, (-1.0)*amount, 1.0 + amount + amount // Blue Gains. + ); + + // Allocate the destination image data. + + uchar *dest = new uchar[w*h*(sb ? 8 : 4)]; + + // And now apply the curve correction. + + Digikam::ImageCurves Curves(sb); + + if (!sb) // 8 bits image. + { + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 5, TQPoint(63, 60)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 10, TQPoint(191, 194)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(255, 255)); + } + else // 16 bits image. + { + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 5, TQPoint(16128, 15360)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 10, TQPoint(48896, 49664)); + Curves.setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(65535, 65535)); + } + + Curves.curvesCalculateCurve(Digikam::ImageHistogram::AlphaChannel); // Calculate cure on all channels. + Curves.curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); // ... and apply it on all channels + Curves.curvesLutProcess(data, dest, w, h); + + memcpy(data, dest, w*h*(sb ? 8 : 4)); + delete [] dest; +} + +/* Function to apply the Neon effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Intensity => Intensity value + * BW => Border Width + * + * Theory => Wow, this is a great effect, you've never seen a Neon effect + * like this on PSC. Is very similar to Growing Edges (photoshop) + * Some pictures will be very interesting + */ +void ImageEffect_ColorFX::neon(uchar *data, int w, int h, bool sb, int Intensity, int BW) +{ + neonFindEdges(data, w, h, sb, true, Intensity, BW); +} + +/* Function to apply the Find Edges effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Intensity => Intensity value + * BW => Border Width + * + * Theory => Wow, another Photoshop filter (FindEdges). Do you understand + * Neon effect ? This is the same engine, but is inversed with + * 255 - color. + */ +void ImageEffect_ColorFX::findEdges(uchar *data, int w, int h, bool sb, int Intensity, int BW) +{ + neonFindEdges(data, w, h, sb, false, Intensity, BW); +} + +// Implementation of neon and FindEdges. They share 99% of their code. +void ImageEffect_ColorFX::neonFindEdges(uchar *data, int w, int h, bool sb, bool neon, int Intensity, int BW) +{ + int Width = w; + int Height = h; + bool sixteenBit = sb; + int bytesDepth = sb ? 8 : 4; + uchar* pResBits = new uchar[Width*Height*bytesDepth]; + + Intensity = (Intensity < 0) ? 0 : (Intensity > 5) ? 5 : Intensity; + BW = (BW < 1) ? 1 : (BW > 5) ? 5 : BW; + + uchar *ptr, *ptr1, *ptr2; + + // these must be uint, we need full 2^32 range for 16 bit + uint color_1, color_2, colorPoint, colorOther1, colorOther2; + + // initial copy + memcpy (pResBits, data, Width*Height*bytesDepth); + + double intensityFactor = sqrt( 1 << Intensity ); + + for (int h = 0; h < Height; h++) + { + for (int w = 0; w < Width; w++) + { + ptr = pResBits + getOffset(Width, w, h, bytesDepth); + ptr1 = pResBits + getOffset(Width, w + Lim_Max (w, BW, Width), h, bytesDepth); + ptr2 = pResBits + getOffset(Width, w, h + Lim_Max (h, BW, Height), bytesDepth); + + if (sixteenBit) + { + for (int k = 0; k <= 2; k++) + { + colorPoint = ((unsigned short *)ptr)[k]; + colorOther1 = ((unsigned short *)ptr1)[k]; + colorOther2 = ((unsigned short *)ptr2)[k]; + color_1 = (colorPoint - colorOther1) * (colorPoint - colorOther1); + color_2 = (colorPoint - colorOther2) * (colorPoint - colorOther2); + + // old algorithm was + // sqrt ((color_1 + color_2) << Intensity) + // As (a << I) = a * (1 << I) = a * (2^I), and we can split the square root + + if (neon) + ((unsigned short *)ptr)[k] = CLAMP065535 ((int)( sqrt(color_1 + color_2) * intensityFactor )); + else + ((unsigned short *)ptr)[k] = 65535 - CLAMP065535 ((int)( sqrt(color_1 + color_2) * intensityFactor )); + } + } + else + { + for (int k = 0; k <= 2; k++) + { + colorPoint = ptr[k]; + colorOther1 = ptr1[k]; + colorOther2 = ptr2[k]; + color_1 = (colorPoint - colorOther1) * (colorPoint - colorOther1); + color_2 = (colorPoint - colorOther2) * (colorPoint - colorOther2); + + if (neon) + ptr[k] = CLAMP0255 ((int)( sqrt(color_1 + color_2) * intensityFactor )); + else + ptr[k] = 255 - CLAMP0255 ((int)( sqrt(color_1 + color_2) * intensityFactor )); + } + } + } + } + + memcpy (data, pResBits, Width*Height*bytesDepth); + delete [] pResBits; +} + +int ImageEffect_ColorFX::getOffset(int Width, int X, int Y, int bytesDepth) +{ + return (Y * Width * bytesDepth) + (X * bytesDepth); +} + +inline int ImageEffect_ColorFX::Lim_Max(int Now, int Up, int Max) +{ + --Max; + while (Now > Max - Up) --Up; + return (Up); +} + +} // NameSpace DigikamColorFXImagesPlugin + diff --git a/src/imageplugins/colorfx/imageeffect_colorfx.h b/src/imageplugins/colorfx/imageeffect_colorfx.h new file mode 100644 index 00000000..d6d7d105 --- /dev/null +++ b/src/imageplugins/colorfx/imageeffect_colorfx.h @@ -0,0 +1,131 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_COLORFX_H +#define IMAGEEFFECT_COLORFX_H + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQHButtonGroup; +class TQComboBox; +class TQLabel; + +class KIntNumInput; + +namespace Digikam +{ +class ImageWidget; +class ColorGradientWidget; +class HistogramWidget; +class DColor; +} + +namespace DigikamColorFXImagesPlugin +{ + +class ImageEffect_ColorFX : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_ColorFX(TQWidget *parent); + ~ImageEffect_ColorFX(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + void colorEffect(uchar *data, int w, int h, bool sb); + void solarize(int factor, uchar *data, int w, int h, bool sb); + void vivid(int factor, uchar *data, int w, int h, bool sb); + void neon(uchar *data, int w, int h, bool sb, int Intensity, int BW); + void findEdges(uchar *data, int w, int h, bool sb, int Intensity, int BW); + void neonFindEdges(uchar *data, int w, int h, bool sb, bool neon, int Intensity, int BW); + + inline int getOffset(int Width, int X, int Y, int bytesDepth); + inline int Lim_Max(int Now, int Up, int Max); + +private slots: + + void slotEffectTypeChanged(int type); + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum ColorFXTypes + { + ColorFX=0, + Vivid, + Neon, + FindEdges + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + TQComboBox *m_effectType; + + TQHButtonGroup *m_scaleBG; + + TQLabel *m_effectTypeLabel; + TQLabel *m_levelLabel; + TQLabel *m_iterationLabel; + + KIntNumInput *m_levelInput; + KIntNumInput *m_iterationInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamColorFXImagesPlugin + +#endif /* IMAGEEFFECT_COLORFX_H */ diff --git a/src/imageplugins/colorfx/imageplugin_colorfx.cpp b/src/imageplugins/colorfx/imageplugin_colorfx.cpp new file mode 100644 index 00000000..f4725028 --- /dev/null +++ b/src/imageplugins/colorfx/imageplugin_colorfx.cpp @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "colorfxtool.h" +#include "imageplugin_colorfx.h" +#include "imageplugin_colorfx.moc" + +using namespace DigikamColorFXImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_colorfx, + KGenericFactory("digikamimageplugin_colorfx")); + +ImagePlugin_ColorFX::ImagePlugin_ColorFX(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_ColorFX") +{ + m_solarizeAction = new TDEAction(i18n("Color Effects..."), "colorfx", 0, + this, TQ_SLOT(slotColorFX()), + actionCollection(), "imageplugin_colorfx"); + + setXMLFile( "digikamimageplugin_colorfx_ui.rc" ); + + DDebug() << "ImagePlugin_ColorFX plugin loaded" << endl; +} + +ImagePlugin_ColorFX::~ImagePlugin_ColorFX() +{ +} + +void ImagePlugin_ColorFX::setEnabledActions(bool enable) +{ + m_solarizeAction->setEnabled(enable); +} + +void ImagePlugin_ColorFX::slotColorFX() +{ + ColorFXTool *colorfx = new ColorFXTool(this); + loadTool(colorfx); +} + diff --git a/src/imageplugins/colorfx/imageplugin_colorfx.h b/src/imageplugins/colorfx/imageplugin_colorfx.h new file mode 100644 index 00000000..d7aaa83f --- /dev/null +++ b/src/imageplugins/colorfx/imageplugin_colorfx.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-02-14 + * Description : a digiKam image plugin for to apply a color + * effect to an image. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_COLORFX_H +#define IMAGEPLUGIN_COLORFX_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_ColorFX : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_ColorFX(TQObject *parent, const char* name, const TQStringList &args); + ~ImagePlugin_ColorFX(); + + void setEnabledActions(bool enable); + +private slots: + + void slotColorFX(); + +private: + + TDEAction *m_solarizeAction; +}; + +#endif /* IMAGEPLUGIN_COLORFX_H */ diff --git a/src/imageplugins/coreplugin/Makefile.am b/src/imageplugins/coreplugin/Makefile.am new file mode 100644 index 00000000..b36d6f36 --- /dev/null +++ b/src/imageplugins/coreplugin/Makefile.am @@ -0,0 +1,52 @@ +SUBDIRS = sharpnesseditor hsl ratiocrop +COMPILE_FIRST = sharpnesseditor hsl ratiocrop +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/imageplugins/coreplugin/sharpnesseditor \ + -I$(top_srcdir)/src/imageplugins/coreplugin/hsl \ + -I$(top_srcdir)/src/imageplugins/coreplugin/ratiocrop \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_core_la_SOURCES = imageplugin_core.cpp bwsepiatool.cpp \ + autocorrectiontool.cpp \ + rgbtool.cpp \ + redeyetool.cpp blurtool.cpp \ + iccprooftool.cpp bcgtool.cpp + +noinst_HEADERS = autocorrectiontool.h blurtool.h \ + rgbtool.h bcgtool.h \ + bwsepiatool.h iccprooftool.h redeyetool.h + +digikamimageplugin_core_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/imageplugins/coreplugin/sharpnesseditor/libsharpnesseditor.la \ + $(top_builddir)/src/imageplugins/coreplugin/hsl/libhsl.la \ + $(top_builddir)/src/imageplugins/coreplugin/ratiocrop/libratiocrop.la \ + $(top_builddir)/src/digikam/libdigikam.la \ + $(top_builddir)/src/utilities/imageeditor/editor/libdimgeditor.la \ + $(top_builddir)/src/libs/curves/libcurves.la \ + $(top_builddir)/src/libs/widgets/common/libcommonwidgets.la \ + $(top_builddir)/src/libs/widgets/imageplugins/libimagepluginswidgets.la + +digikamimageplugin_core_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio -lkexiv2 -ltdeutils + +kde_services_DATA = digikamimageplugin_core.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_core.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_core_ui.rc diff --git a/src/imageplugins/coreplugin/autocorrectiontool.cpp b/src/imageplugins/coreplugin/autocorrectiontool.cpp new file mode 100644 index 00000000..32f00b44 --- /dev/null +++ b/src/imageplugins/coreplugin/autocorrectiontool.cpp @@ -0,0 +1,438 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : Auto-Color correction tool. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "colorgradientwidget.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "listboxpreviewitem.h" +#include "whitebalance.h" + +// Local includes. + +#include "autocorrectiontool.h" +#include "autocorrectiontool.moc" + +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +AutoCorrectionTool::AutoCorrectionTool(TQObject* parent) + : EditorTool(parent) +{ + setName("autocorrection"); + setToolName(i18n("Auto-Correction")); + setToolIcon(SmallIcon("autocorrection")); + setToolHelp("autocolorcorrectiontool.anchor"); + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("autocorrection Tool", 0, + i18n("

    Here you can see the auto-color correction tool " + "preview. You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + ImageIface iface(0, 0); + m_thumbnailImage = iface.getOriginalImg()->smoothScale(128, 128, TQSize::ScaleMin); + m_destinationPreviewData = 0; + + EditorToolSettings *gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings->plainPage(), 2, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings->plainPage()); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings->plainPage() ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + // ------------------------------------------------------------- + + m_correctionTools = new TQListBox(gboxSettings->plainPage()); + m_correctionTools->setColumnMode(1); + m_correctionTools->setVariableWidth(false); + m_correctionTools->setVariableHeight(false); + ListBoxWhatsThis* whatsThis = new ListBoxWhatsThis(m_correctionTools); + + TQPixmap pix = getThumbnailForEffect(AutoLevelsCorrection); + ListBoxPreviewItem *item = new ListBoxPreviewItem(pix, i18n("Auto Levels")); + whatsThis->add( item, i18n("Auto Levels:" + "

    This option maximizes the tonal range in the Red, " + "Green, and Blue channels. It searches the image shadow and highlight " + "limit values and adjusts the Red, Green, and Blue channels " + "to a full histogram range.

    ")); + m_correctionTools->insertItem(item, AutoLevelsCorrection); + + pix = getThumbnailForEffect(NormalizeCorrection); + item = new ListBoxPreviewItem(pix, i18n("Normalize")); + whatsThis->add( item, i18n("Normalize:" + "

    This option scales brightness values across the active " + "image so that the darkest point becomes black, and the " + "brightest point becomes as bright as possible without " + "altering its hue. This is often a \"magic fix\" for " + "images that are dim or washed out.

    ")); + m_correctionTools->insertItem(item, NormalizeCorrection); + + pix = getThumbnailForEffect(EqualizeCorrection); + item = new ListBoxPreviewItem(pix, i18n("Equalize")); + whatsThis->add( item, i18n("Equalize:" + "

    This option adjusts the brightness of colors across the " + "active image so that the histogram for the value channel " + "is as nearly as possible flat, that is, so that each possible " + "brightness value appears at about the same number of pixels " + "as each other value. Sometimes Equalize works wonderfully at " + "enhancing the contrasts in an image. Other times it gives " + "garbage. It is a very powerful operation, which can either work " + "miracles on an image or destroy it.

    ")); + m_correctionTools->insertItem(item, EqualizeCorrection); + + pix = getThumbnailForEffect(StretchContrastCorrection); + item = new ListBoxPreviewItem(pix, i18n("Stretch Contrast")); + whatsThis->add( item, i18n("Stretch Contrast:" + "

    This option enhances the contrast and brightness " + "of the RGB values of an image by stretching the lowest " + "and highest values to their fullest range, adjusting " + "everything in between.

    ")); + m_correctionTools->insertItem(item, StretchContrastCorrection); + + pix = getThumbnailForEffect(AutoExposureCorrection); + item = new ListBoxPreviewItem(pix, i18n("Auto Exposure")); + whatsThis->add( item, i18n("Auto Exposure:" + "

    This option enhances the contrast and brightness " + "of the RGB values of an image to calculate optimal " + "exposition and black level using image histogram " + "properties.

    ")); + m_correctionTools->insertItem(item, AutoExposureCorrection); + + // ------------------------------------------------------------- + + m_correctionTools->setFocus(); + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + gridSettings->addMultiCellWidget(histoBox, 1, 1, 0, 4); + gridSettings->addMultiCellWidget(m_correctionTools, 2, 2, 0, 4); + gridSettings->setRowStretch(2, 10); + gridSettings->setSpacing(gboxSettings->spacingHint()); + gridSettings->setMargin(gboxSettings->spacingHint()); + + setToolSettings(gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const DColor&))); + + connect(m_correctionTools, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); +} + +AutoCorrectionTool::~AutoCorrectionTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void AutoCorrectionTool::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void AutoCorrectionTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void AutoCorrectionTool::slotColorSelectedFromTarget(const DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void AutoCorrectionTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("autocorrection Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_correctionTools->setCurrentItem(config->readNumEntry("Auto Correction Filter", AutoLevelsCorrection)); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void AutoCorrectionTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("autocorrection Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("Auto Correction Filter", m_correctionTools->currentItem()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void AutoCorrectionTool::slotResetSettings() +{ + m_correctionTools->blockSignals(true); + m_correctionTools->setCurrentItem(AutoLevelsCorrection); + m_correctionTools->blockSignals(false); + + slotEffect(); +} + +void AutoCorrectionTool::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + autoCorrection(m_destinationPreviewData, w, h, sb, m_correctionTools->currentItem()); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +TQPixmap AutoCorrectionTool::getThumbnailForEffect(AutoCorrectionType type) +{ + DImg thumb = m_thumbnailImage.copy(); + autoCorrection(thumb.bits(), thumb.width(), thumb.height(), thumb.sixteenBit(), type); + return (thumb.convertToPixmap()); +} + + +void AutoCorrectionTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + int type = m_correctionTools->currentItem(); + autoCorrection(data, w, h, sb, type); + TQString name; + + switch (type) + { + case AutoLevelsCorrection: + name = i18n("Auto Levels"); + break; + + case NormalizeCorrection: + name = i18n("Normalize"); + break; + + case EqualizeCorrection: + name = i18n("Equalize"); + break; + + case StretchContrastCorrection: + name = i18n("Stretch Contrast"); + break; + + case AutoExposureCorrection: + name = i18n("Auto Exposure"); + break; + } + + iface->putOriginalImage(name, data); + delete [] data; + } + + kapp->restoreOverrideCursor(); +} + +void AutoCorrectionTool::autoCorrection(uchar *data, int w, int h, bool sb, int type) +{ + DImgImageFilters filter; + + switch (type) + { + case AutoLevelsCorrection: + filter.autoLevelsCorrectionImage(data, w, h, sb); + break; + + case NormalizeCorrection: + filter.normalizeImage(data, w, h, sb); + break; + + case EqualizeCorrection: + filter.equalizeImage(data, w, h, sb); + break; + + case StretchContrastCorrection: + filter.stretchContrastImage(data, w, h, sb); + break; + + case AutoExposureCorrection: + WhiteBalance wbFilter(sb); + double blackLevel; + double exposureLevel; + wbFilter.autoExposureAdjustement(data, w, h, sb, blackLevel, exposureLevel); + wbFilter.whiteBalance(data, w, h, sb, blackLevel, exposureLevel); + break; + } +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/autocorrectiontool.h b/src/imageplugins/coreplugin/autocorrectiontool.h new file mode 100644 index 00000000..73a388f2 --- /dev/null +++ b/src/imageplugins/coreplugin/autocorrectiontool.h @@ -0,0 +1,128 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : Auto-Color correction tool. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef AUTOCORRECTIONTOOL_H +#define AUTOCORRECTIONTOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQHButtonGroup; +class TQComboBox; +class TQListBox; +class TQButtonGroup; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +} + +namespace DigikamImagesPluginCore +{ + +class AutoCorrectionTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + AutoCorrectionTool(TQObject *parent); + ~AutoCorrectionTool(); + +protected: + + void finalRendering(); + +private slots: + + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum AutoCorrectionType + { + AutoLevelsCorrection=0, + NormalizeCorrection, + EqualizeCorrection, + StretchContrastCorrection, + AutoExposureCorrection + }; + +private: + + void readSettings(); + void writeSettings(); + + void autoCorrection(uchar *data, int w, int h, bool sb, int type); + TQPixmap getThumbnailForEffect(AutoCorrectionType type); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQListBox *m_correctionTools; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::DImg m_thumbnailImage; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* AUTOCORRECTIONTOOL_H */ diff --git a/src/imageplugins/coreplugin/bcgtool.cpp b/src/imageplugins/coreplugin/bcgtool.cpp new file mode 100644 index 00000000..17ecf838 --- /dev/null +++ b/src/imageplugins/coreplugin/bcgtool.cpp @@ -0,0 +1,366 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-05 + * Description : digiKam image editor to adjust Brightness, + Contrast, and Gamma of picture. + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Digikam includes. + +#include "bcgmodifier.h" +#include "colorgradientwidget.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imageiface.h" +#include "imagewidget.h" + +// Local includes. + +#include "bcgtool.h" +#include "bcgtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +BCGTool::BCGTool(TQObject* parent) + : EditorTool(parent) +{ + setName("bcgadjust"); + setToolName(i18n("Brightness / Contrast / Gamma")); + setToolIcon(SmallIcon("contrast")); + setToolHelp("bcgadjusttool.anchor"); + + m_destinationPreviewData = 0; + + m_previewWidget = new ImageWidget("bcgadjust Tool", 0, + i18n("

    Here you can see the image " + "brightness-contrast-gamma adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 9, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, m_gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Brightness:"), m_gboxSettings->plainPage()); + m_bInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_bInput->setRange(-100, 100, 1); + m_bInput->setDefaultValue(0); + TQWhatsThis::add( m_bInput, i18n("

    Set here the brightness adjustment of the image.")); + + TQLabel *label3 = new TQLabel(i18n("Contrast:"), m_gboxSettings->plainPage()); + m_cInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_cInput->setRange(-100, 100, 1); + m_cInput->setDefaultValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + + TQLabel *label4 = new TQLabel(i18n("Gamma:"), m_gboxSettings->plainPage()); + m_gInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_gInput->setPrecision(2); + m_gInput->setRange(0.1, 3.0, 0.01); + m_gInput->setDefaultValue(1.0); + TQWhatsThis::add( m_gInput, i18n("

    Set here the gamma adjustment of the image.")); + + // ------------------------------------------------------------- + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + gridSettings->addMultiCellWidget(label2, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_bInput, 4, 4, 0, 4); + gridSettings->addMultiCellWidget(label3, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_cInput, 6, 6, 0, 4); + gridSettings->addMultiCellWidget(label4, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_gInput, 8, 8, 0, 4); + gridSettings->setRowStretch(9, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_bInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + m_gboxSettings->enableButton(EditorToolSettings::Ok, false); +} + +BCGTool::~BCGTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void BCGTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void BCGTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void BCGTool::slotColorSelectedFromTarget(const DColor &color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void BCGTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("bcgadjust Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_bInput->setValue(config->readNumEntry("BrightnessAjustment", m_bInput->defaultValue())); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", m_cInput->defaultValue())); + m_gInput->setValue(config->readDoubleNumEntry("GammaAjustment", m_gInput->defaultValue())); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void BCGTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("bcgadjust Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("BrightnessAjustment", m_bInput->value()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + config->writeEntry("GammaAjustment", m_gInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void BCGTool::slotResetSettings() +{ + m_bInput->blockSignals(true); + m_cInput->blockSignals(true); + m_gInput->blockSignals(true); + + m_bInput->slotReset(); + m_cInput->slotReset(); + m_gInput->slotReset(); + + m_bInput->blockSignals(false); + m_cInput->blockSignals(false); + m_gInput->blockSignals(false); + + slotEffect(); +} + +void BCGTool::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + double b = (double) m_bInput->value() / 250.0; + double c = (double) (m_cInput->value() / 100.0) + 1.00; + double g = m_gInput->value(); + + m_gboxSettings->enableButton(EditorToolSettings::Ok, + ( b != 0.0 || c != 1.0 || g != 1.0 )); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + DImg preview(w, h, sb, a, m_destinationPreviewData); + BCGModifier cmod; + cmod.setGamma(g); + cmod.setBrightness(b); + cmod.setContrast(c); + cmod.applyBCG(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void BCGTool::finalRendering() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + ImageIface* iface = m_previewWidget->imageIface(); + + double b = (double) m_bInput->value() / 250.0; + double c = (double) (m_cInput->value() / 100.0) + 1.00; + double g = m_gInput->value(); + + iface->setOriginalBCG(b, c, g); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/bcgtool.h b/src/imageplugins/coreplugin/bcgtool.h new file mode 100644 index 00000000..4a1e9d21 --- /dev/null +++ b/src/imageplugins/coreplugin/bcgtool.h @@ -0,0 +1,115 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-05 + * Description : digiKam image editor to adjust Brightness, + Contrast, and Gamma of picture. + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BCGTOOL_H +#define BCGTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQCheckBox; +class TQComboBox; +class TQHButtonGroup; + +namespace KDcrawIface +{ +class RIntNumInput; +class RDoubleNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamImagesPluginCore +{ + +class BCGTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + BCGTool(TQObject *parent); + ~BCGTool(); + +private slots: + + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDcrawIface::RIntNumInput *m_bInput; + KDcrawIface::RIntNumInput *m_cInput; + KDcrawIface::RDoubleNumInput *m_gInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* BCGTOOL_H */ diff --git a/src/imageplugins/coreplugin/blurtool.cpp b/src/imageplugins/coreplugin/blurtool.cpp new file mode 100644 index 00000000..85e1dc19 --- /dev/null +++ b/src/imageplugins/coreplugin/blurtool.cpp @@ -0,0 +1,169 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to blur an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "dimggaussianblur.h" + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "blurtool.h" +#include "blurtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +BlurTool::BlurTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("gaussianblur"); + setToolName(i18n("Blur")); + setToolIcon(SmallIcon("blurimage")); + setToolHelp("blursharpentool.anchor"); + + // --------------------------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 2, 1); + TQLabel *label = new TQLabel(i18n("Smoothness:"), m_gboxSettings->plainPage()); + + m_radiusInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_radiusInput->setRange(0, 100, 1); + m_radiusInput->setDefaultValue(0); + TQWhatsThis::add(m_radiusInput, i18n("

    A smoothness of 0 has no effect, " + "1 and above determine the Gaussian blur matrix radius " + "that determines how much to blur the image.")); + + grid->addMultiCellWidget(label, 0, 0, 0, 1); + grid->addMultiCellWidget(m_radiusInput, 1, 1, 0, 1); + grid->setRowStretch(2, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + m_previewWidget = new ImagePanelWidget(470, 350, "gaussianblur Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); +} + +BlurTool::~BlurTool() +{ +} + +void BlurTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("gaussianblur Tool"); + m_radiusInput->setValue(config->readNumEntry("RadiusAjustment", m_radiusInput->defaultValue())); +} + +void BlurTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("gaussianblur Tool"); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->sync(); +} + +void BlurTool::slotResetSettings() +{ + m_radiusInput->blockSignals(true); + m_radiusInput->slotReset(); + m_radiusInput->blockSignals(false); +} + +void BlurTool::prepareEffect() +{ + m_radiusInput->setEnabled(false); + + DImg img = m_previewWidget->getOriginalRegionImage(); + + setFilter(dynamic_cast(new DImgGaussianBlur(&img, this, m_radiusInput->value()))); +} + +void BlurTool::prepareFinal() +{ + m_radiusInput->setEnabled(false); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + DImg orgImage = DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + setFilter(dynamic_cast(new DImgGaussianBlur(&orgImage, this, m_radiusInput->value()))); +} + +void BlurTool::putPreviewData() +{ + DImg imDest = filter()->getTargetImage(); + m_previewWidget->setPreviewImage(imDest); +} + +void BlurTool::putFinalData() +{ + ImageIface iface(0, 0); + DImg imDest = filter()->getTargetImage(); + iface.putOriginalImage(i18n("Gaussian Blur"), imDest.bits()); +} + +void BlurTool::renderingFinished() +{ + m_radiusInput->setEnabled(true); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/blurtool.h b/src/imageplugins/coreplugin/blurtool.h new file mode 100644 index 00000000..545f4aa2 --- /dev/null +++ b/src/imageplugins/coreplugin/blurtool.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to blur an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_BLUR_H +#define IMAGEEFFECT_BLUR_H + +// Digikam includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamImagesPluginCore +{ + +class BlurTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + BlurTool(TQObject *parent); + ~BlurTool(); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KDcrawIface::RIntNumInput *m_radiusInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_BLUR_H */ diff --git a/src/imageplugins/coreplugin/bwsepiatool.cpp b/src/imageplugins/coreplugin/bwsepiatool.cpp new file mode 100644 index 00000000..06c30462 --- /dev/null +++ b/src/imageplugins/coreplugin/bwsepiatool.cpp @@ -0,0 +1,1177 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : Black and White conversion tool. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Digikam includes. + +#include "bcgmodifier.h" +#include "colorgradientwidget.h" +#include "curveswidget.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "listboxpreviewitem.h" + +// Local includes. + +#include "bwsepiatool.h" +#include "bwsepiatool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +class PreviewPixmapFactory : public TQObject +{ +public: + + PreviewPixmapFactory(BWSepiaTool* bwSepia); + + void invalidate() { m_previewPixmapMap.clear(); } + + const TQPixmap* pixmap(int id); + +private: + + TQPixmap makePixmap(int id); + + TQIntDict m_previewPixmapMap; + BWSepiaTool *m_bwSepia; +}; + +PreviewPixmapFactory::PreviewPixmapFactory(BWSepiaTool* bwSepia) + : TQObject(bwSepia), m_bwSepia(bwSepia) +{ + m_previewPixmapMap.setAutoDelete(true); +} + +const TQPixmap* PreviewPixmapFactory::pixmap(int id) +{ + if (m_previewPixmapMap.find(id) == 0) + { + TQPixmap pix = makePixmap(id); + m_previewPixmapMap.insert(id, new TQPixmap(pix)); + } + + TQPixmap* res = m_previewPixmapMap[id]; + + return res; +} + +TQPixmap PreviewPixmapFactory::makePixmap(int id) +{ + return m_bwSepia->getThumbnailForEffect(id); +} + +// ----------------------------------------------------------------------------------- + +class ListBoxBWPreviewItem : public ListBoxPreviewItem +{ + +public: + + ListBoxBWPreviewItem(TQListBox *listbox, const TQString &text, + PreviewPixmapFactory* factory, int id) + : ListBoxPreviewItem(listbox, TQPixmap(), text) + { + m_previewPixmapFactory = factory; + m_id = id; + }; + + virtual const TQPixmap* pixmap() const; + +private: + + int m_id; + PreviewPixmapFactory* m_previewPixmapFactory; +}; + +const TQPixmap* ListBoxBWPreviewItem::pixmap() const +{ + return m_previewPixmapFactory->pixmap(m_id); +} + +// ----------------------------------------------------------------------------------- + +BWSepiaTool::BWSepiaTool(TQObject* parent) + : EditorTool(parent) +{ + setName("convertbw"); + setToolName(i18n("Black && White")); + setToolIcon(SmallIcon("bwtonal")); + setToolHelp("blackandwhitetool.anchor"); + + m_destinationPreviewData = 0; + + ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + m_thumbnailImage = m_originalImage->smoothScale(128, 128, TQSize::ScaleMin); + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("convertbw Tool", 0, + i18n("

    Here you can see the black and white conversion tool preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + EditorToolSettings *gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings->plainPage(), 4, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_tab = new KTabWidget(gboxSettings->plainPage()); + + m_bwFilm = new TQListBox(m_tab); + m_bwFilm->setColumnMode(1); + m_bwFilm->setVariableWidth(false); + m_bwFilm->setVariableHeight(false); + ListBoxWhatsThis* whatsThis2 = new ListBoxWhatsThis(m_bwFilm); + m_previewPixmapFactory = new PreviewPixmapFactory(this); + + int type = BWGeneric; + + ListBoxBWPreviewItem *item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Generic"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Generic:" + "

    Simulate a generic black and white film

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa 200X"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa 200X:" + "

    Simulate the Agfa 200X black and white film at 200 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 25"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 25:" + "

    Simulate the Agfa Pan black and white film at 25 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 100:" + "

    Simulate the Agfa Pan black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 400:" + "

    Simulate the Agfa Pan black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 100:" + "

    Simulate the Ilford Delta black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 400:" + "

    Simulate the Ilford Delta black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 400 Pro 3200"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 400 Pro 3200:" + "

    Simulate the Ilford Delta 400 Pro black and white film at 3200 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford FP4 Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford FP4 Plus:" + "

    Simulate the Ilford FP4 Plus black and white film at 125 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford HP5 Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford HP5 Plus:" + "

    Simulate the Ilford HP5 Plus black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford PanF Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford PanF Plus:" + "

    Simulate the Ilford PanF Plus black and white film at 50 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford XP2 Super"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford XP2 Super:" + "

    Simulate the Ilford XP2 Super black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak Tmax 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak Tmax 100:" + "

    Simulate the Kodak Tmax black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak Tmax 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak Tmax 400:" + "

    Simulate the Kodak Tmax black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak TriX"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak TriX:" + "

    Simulate the Kodak TriX black and white film at 400 ISO

    ")); + + // ------------------------------------------------------------- + + TQVBox *vbox = new TQVBox(m_tab); + + m_bwFilters = new TQListBox(vbox); + m_bwFilters->setColumnMode(1); + m_bwFilters->setVariableWidth(false); + m_bwFilters->setVariableHeight(false); + ListBoxWhatsThis* whatsThis = new ListBoxWhatsThis(m_bwFilters); + + type = BWNoFilter; + + item = new ListBoxBWPreviewItem(m_bwFilters, + i18n("No Lens Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("No Lens Filter:" + "

    Do not apply a lens filter when rendering the image.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Green Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Green Filter:" + "

    Simulate black and white film exposure using a green filter. " + "This is usefule for all scenic shoots, especially portraits " + "photographed against the sky.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Orange Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Orange Filter:" + "

    Simulate black and white film exposure using an orange filter. " + "This will enhance landscapes, marine scenes and aerial " + "photography.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Red Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Red Filter:" + "

    Simulate black and white film exposure using a red filter. " + "This creates dramatic sky effects, and simulates moonlight scenes " + "in the daytime.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Yellow Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Yellow Filter:" + "

    Simulate black and white film exposure using a yellow filter. " + "This has the most natural tonal correction, and improves contrast. Ideal for " + "landscapes.

    ")); + + m_strengthInput = new RIntNumInput(vbox); + m_strengthInput->input()->setLabel(i18n("Strength:"), AlignLeft | AlignVCenter); + m_strengthInput->setRange(1, 5, 1); + m_strengthInput->setDefaultValue(1); + TQWhatsThis::add(m_strengthInput, i18n("

    Here, set the strength adjustment of the lens filter.")); + + // ------------------------------------------------------------- + + m_bwTone = new TQListBox(m_tab); + m_bwTone->setColumnMode(1); + m_bwTone->setVariableWidth(false); + m_bwTone->setVariableHeight(false); + ListBoxWhatsThis* whatsThis3 = new ListBoxWhatsThis(m_bwTone); + + type = BWNoTone; + + item = new ListBoxBWPreviewItem(m_bwTone, i18n("No Tone Filter"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("No Tone Filter:" + "

    Do not apply a tone filter to the image.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Sepia Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Sepia Tone:" + "

    Gives a warm highlight and mid-tone while adding a bit of coolness to " + "the shadows - very similar to the process of bleaching a print and " + "re-developing in a sepia toner.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Brown Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Brown Tone:" + "

    This filter is more neutral than the Sepia Tone " + "filter.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Cold Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Cold Tone:" + "

    Start subtle and replicates printing on a cold tone black and white " + "paper such as a bromide enlarging " + "paper.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Selenium Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Selenium Tone:" + "

    This effect replicates traditional selenium chemical toning done " + "in the darkroom.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Platinum Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Platinum Tone:" + "

    This effect replicates traditional platinum chemical toning done " + "in the darkroom.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Green Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with greenish tint:" + "

    This effect is also known as Verdante.

    ")); + + // ------------------------------------------------------------- + + TQWidget *curveBox = new TQWidget( m_tab ); + TQGridLayout *gridTab2 = new TQGridLayout(curveBox, 5, 2, 0); + + ColorGradientWidget* vGradient = new ColorGradientWidget( + ColorGradientWidget::Vertical, + 10, curveBox); + vGradient->setColors(TQColor("white"), TQColor("black")); + + TQLabel *spacev = new TQLabel(curveBox); + spacev->setFixedWidth(1); + + m_curvesWidget = new CurvesWidget(256, 256, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), + curveBox); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve adjustment of the image luminosity")); + + TQLabel *spaceh = new TQLabel(curveBox); + spaceh->setFixedHeight(1); + + ColorGradientWidget *hGradient = new ColorGradientWidget( + ColorGradientWidget::Horizontal, + 10, curveBox); + hGradient->setColors(TQColor("black"), TQColor("white")); + + m_cInput = new RIntNumInput(curveBox); + m_cInput->input()->setLabel(i18n("Contrast:"), AlignLeft | AlignVCenter); + m_cInput->setRange(-100, 100, 1); + m_cInput->setDefaultValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + + gridTab2->addMultiCellWidget(vGradient, 0, 0, 0, 0); + gridTab2->addMultiCellWidget(spacev, 0, 0, 1, 1); + gridTab2->addMultiCellWidget(m_curvesWidget, 0, 0, 2, 2); + gridTab2->addMultiCellWidget(spaceh, 1, 1, 2, 2); + gridTab2->addMultiCellWidget(hGradient, 2, 2, 2, 2); + gridTab2->addMultiCellWidget(m_cInput, 4, 4, 0, 2); +// gridTab2->setRowSpacing(3); + gridTab2->setRowStretch(5, 10); + + // ------------------------------------------------------------- + + m_tab->insertTab(m_bwFilm, i18n("Film"), FilmTab); + m_tab->insertTab(vbox, i18n("Lens Filters"), BWFiltersTab); + m_tab->insertTab(m_bwTone, i18n("Tone"), ToneTab); + m_tab->insertTab(curveBox, i18n("Lightness"), LuminosityTab); + + gridSettings->addMultiCellWidget(m_tab, 3, 3, 0, 4); + gridSettings->setRowStretch(3, 10); + setToolSettings(gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotSpotColorChanged(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_bwFilters, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotFilterSelected(int))); + + connect(m_strengthInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_bwFilm, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_bwTone, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); +} + +BWSepiaTool::~BWSepiaTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void BWSepiaTool::slotFilterSelected(int filter) +{ + if (filter == BWNoFilter) + m_strengthInput->setEnabled(false); + else + m_strengthInput->setEnabled(true); + + slotEffect(); +} + +TQPixmap BWSepiaTool::getThumbnailForEffect(int type) +{ + DImg thumb = m_thumbnailImage.copy(); + int w = thumb.width(); + int h = thumb.height(); + bool sb = thumb.sixteenBit(); + bool a = thumb.hasAlpha(); + + if (type < BWGeneric) + { + // In Filter view, we will render a preview of the B&W filter with the generic B&W film. + blackAndWhiteConversion(thumb.bits(), w, h, sb, type); + blackAndWhiteConversion(thumb.bits(), w, h, sb, BWGeneric); + } + else + { + // In Film and Tone view, we will render the preview without to use the B&W Filter + blackAndWhiteConversion(thumb.bits(), w, h, sb, type); + } + + if (m_curvesWidget->curves()) // in case we're called before the creator is done + { + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + m_curvesWidget->curves()->curvesLutProcess(thumb.bits(), targetData, w, h); + + DImg preview(w, h, sb, a, targetData); + BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview); + + thumb.putImageData(preview.bits()); + + delete [] targetData; + } + return (thumb.convertToPixmap()); +} + +void BWSepiaTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void BWSepiaTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_curvesWidget->m_scaleType = scale; + m_curvesWidget->repaint(false); +} + +void BWSepiaTool::slotSpotColorChanged(const DColor &color) +{ + m_curvesWidget->setCurveGuide(color); +} + +void BWSepiaTool::slotColorSelectedFromTarget(const DColor &color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void BWSepiaTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("convertbw Tool"); + + m_tab->setCurrentPage(config->readNumEntry("Settings Tab", BWFiltersTab)); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_bwFilters->setCurrentItem(config->readNumEntry("BW Filter", 0)); + m_bwFilm->setCurrentItem(config->readNumEntry("BW Film", 0)); + m_bwTone->setCurrentItem(config->readNumEntry("BW Tone", 0)); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", m_cInput->defaultValue())); + m_strengthInput->setValue(config->readNumEntry("StrengthAjustment", m_strengthInput->defaultValue())); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesChannelReset(i); + + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentPoint%1").arg(j), &disable); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesCalculateCurve(i); + + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); + slotFilterSelected(m_bwFilters->currentItem()); +} + +void BWSepiaTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("convertbw Tool"); + config->writeEntry("Settings Tab", m_tab->currentPageIndex()); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("BW Filter", m_bwFilters->currentItem()); + config->writeEntry("BW Film", m_bwFilm->currentItem()); + config->writeEntry("BW Tone", m_bwTone->currentItem()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + config->writeEntry("StrengthAjustment", m_strengthInput->value()); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curvesWidget->curves()->getCurvePoint(ImageHistogram::ValueChannel, j); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + + config->writeEntry(TQString("CurveAjustmentPoint%1").arg(j), p); + } + + m_previewWidget->writeSettings(); + config->sync(); +} + +void BWSepiaTool::slotResetSettings() +{ + m_bwFilm->blockSignals(true); + m_bwFilters->blockSignals(true); + m_bwTone->blockSignals(true); + m_cInput->blockSignals(true); + m_strengthInput->blockSignals(true); + + m_bwFilm->setCurrentItem(0); + m_bwFilm->setSelected(0, true); + + m_bwFilters->setCurrentItem(0); + m_bwFilters->setSelected(0, true); + + m_bwTone->setCurrentItem(0); + m_bwTone->setSelected(0, true); + + m_cInput->slotReset(); + m_strengthInput->slotReset(); + + for (int channel = 0; channel < 5; channel++) + m_curvesWidget->curves()->curvesChannelReset(channel); + + m_curvesWidget->reset(); + + m_bwFilm->blockSignals(false); + m_bwFilters->blockSignals(false); + m_bwTone->blockSignals(false); + m_cInput->blockSignals(false); + m_strengthInput->blockSignals(false); + + m_histogramWidget->reset(); + m_previewPixmapFactory->invalidate(); + m_bwFilters->triggerUpdate(false); + m_bwTone->triggerUpdate(false); + + slotEffect(); +} + +void BWSepiaTool::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_histogramWidget->stopHistogramComputation(); + + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + // Apply black and white filter. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwFilters->currentItem()); + + // Apply black and white film type. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwFilm->currentItem() + BWGeneric); + + // Apply color tone filter. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwTone->currentItem() + BWNoTone); + + // Calculate and apply the curve on image. + + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + m_curvesWidget->curves()->curvesLutProcess(m_destinationPreviewData, targetData, w, h); + + // Adjust contrast. + + DImg preview(w, h, sb, a, targetData); + BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] targetData; + + kapp->restoreOverrideCursor(); +} + +void BWSepiaTool::finalRendering() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + // Apply black and white filter. + + blackAndWhiteConversion(data, w, h, sb, m_bwFilters->currentItem()); + + // Apply black and white film type. + + blackAndWhiteConversion(data, w, h, sb, m_bwFilm->currentItem() + BWGeneric); + + // Apply color tone filter. + + blackAndWhiteConversion(data, w, h, sb, m_bwTone->currentItem() + BWNoTone); + + // Calculate and apply the curve on image. + + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + m_curvesWidget->curves()->curvesLutProcess(data, targetData, w, h); + + // Adjust contrast. + + DImg img(w, h, sb, a, targetData); + BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(img); + + iface->putOriginalImage(i18n("Convert to Black && White"), img.bits()); + + delete [] data; + delete [] targetData; + } + + kapp->restoreOverrideCursor(); +} + +void BWSepiaTool::blackAndWhiteConversion(uchar *data, int w, int h, bool sb, int type) +{ + // Value to multiply RGB 8 bits component of mask used by changeTonality() method. + int mul = sb ? 255 : 1; + DImgImageFilters filter; + double strength = 1.0 + ((double)m_strengthInput->value() - 1.0) * (1.0 / 3.0); + + switch (type) + { + case BWNoFilter: + m_redAttn = 0.0; + m_greenAttn = 0.0; + m_blueAttn = 0.0; + break; + + case BWGreenFilter: + m_redAttn = -0.20 * strength; + m_greenAttn = +0.11 * strength; + m_blueAttn = +0.09 * strength; + break; + + case BWOrangeFilter: + m_redAttn = +0.48 * strength; + m_greenAttn = -0.37 * strength; + m_blueAttn = -0.11 * strength; + break; + + case BWRedFilter: + m_redAttn = +0.60 * strength; + m_greenAttn = -0.49 * strength; + m_blueAttn = -0.11 * strength; + break; + + case BWYellowFilter: + m_redAttn = +0.30 * strength; + m_greenAttn = -0.31 * strength; + m_blueAttn = +0.01 * strength; + break; + + // -------------------------------------------------------------------------------- + + case BWGeneric: + case BWNoTone: + m_redMult = 0.24; + m_greenMult = 0.68; + m_blueMult = 0.08; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfa200X: + m_redMult = 0.18; + m_greenMult = 0.41; + m_blueMult = 0.41; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan25: + m_redMult = 0.25; + m_greenMult = 0.39; + m_blueMult = 0.36; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan100: + m_redMult = 0.21; + m_greenMult = 0.40; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan400: + m_redMult = 0.20; + m_greenMult = 0.41; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta100: + m_redMult = 0.21; + m_greenMult = 0.42; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta400: + m_redMult = 0.22; + m_greenMult = 0.42; + m_blueMult = 0.36; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta400Pro3200: + m_redMult = 0.31; + m_greenMult = 0.36; + m_blueMult = 0.33; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordFP4: + m_redMult = 0.28; + m_greenMult = 0.41; + m_blueMult = 0.31; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordHP5: + m_redMult = 0.23; + m_greenMult = 0.37; + m_blueMult = 0.40; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordPanF: + m_redMult = 0.33; + m_greenMult = 0.36; + m_blueMult = 0.31; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordXP2Super: + m_redMult = 0.21; + m_greenMult = 0.42; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTmax100: + m_redMult = 0.24; + m_greenMult = 0.37; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTmax400: + m_redMult = 0.27; + m_greenMult = 0.36; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTriX: + m_redMult = 0.25; + m_greenMult = 0.35; + m_blueMult = 0.40; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + // -------------------------------------------------------------------------------- + + case BWSepiaTone: + filter.changeTonality(data, w, h, sb, 162*mul, 132*mul, 101*mul); + break; + + case BWBrownTone: + filter.changeTonality(data, w, h, sb, 129*mul, 115*mul, 104*mul); + break; + + case BWColdTone: + filter.changeTonality(data, w, h, sb, 102*mul, 109*mul, 128*mul); + break; + + case BWSeleniumTone: + filter.changeTonality(data, w, h, sb, 122*mul, 115*mul, 122*mul); + break; + + case BWPlatinumTone: + filter.changeTonality(data, w, h, sb, 115*mul, 110*mul, 106*mul); + break; + + case BWGreenTone: + filter.changeTonality(data, w, h, sb, 108*mul, 116*mul, 100*mul); + break; + + } +} + +//-- Load all settings from file -------------------------------------- + +void BWSepiaTool::slotLoadSettings() +{ + KURL loadFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Black & White Settings File to Load")) ); + if( loadFile.isEmpty() ) + return; + + TQFile file(loadFile.path()); + + if (file.open(IO_ReadOnly)) + { + TQTextStream stream(&file); + + if (stream.readLine() != "# Black & White Configuration File") + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Black & White settings text file.") + .arg(loadFile.fileName())); + file.close(); + return; + } + + m_bwFilters->blockSignals(true); + m_bwTone->blockSignals(true); + m_cInput->blockSignals(true); + + m_bwFilters->setCurrentItem(stream.readLine().toInt()); + m_bwTone->setCurrentItem(stream.readLine().toInt()); + m_cInput->setValue(stream.readLine().toInt()); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesChannelReset(i); + + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0; j < 17; j++) + { + TQPoint disable(-1, -1); + TQPoint p; + p.setX(stream.readLine().toInt()); + p.setY(stream.readLine().toInt()); + + if (m_originalImage->sixteenBit() && p != disable) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesCalculateCurve(i); + + m_bwFilters->blockSignals(false); + m_bwTone->blockSignals(false); + m_cInput->blockSignals(false); + + m_histogramWidget->reset(); + m_previewPixmapFactory->invalidate(); + m_bwFilters->triggerUpdate(false); + m_bwTone->triggerUpdate(false); + + slotEffect(); + } + else + KMessageBox::error(kapp->activeWindow(), + i18n("Cannot load settings from the Black & White text file.")); + + file.close(); +} + +//-- Save all settings to file --------------------------------------- + +void BWSepiaTool::slotSaveAsSettings() +{ + KURL saveFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Black & White Settings File to Save"))); + if( saveFile.isEmpty() ) + return; + + TQFile file(saveFile.path()); + + if (file.open(IO_WriteOnly)) + { + TQTextStream stream(&file); + stream << "# Black & White Configuration File\n"; + stream << m_bwFilters->currentItem() << "\n"; + stream << m_bwTone->currentItem() << "\n"; + stream << m_cInput->value() << "\n"; + + for (int j = 0; j < 17; j++) + { + TQPoint p = m_curvesWidget->curves()->getCurvePoint(ImageHistogram::ValueChannel, j); + if (m_originalImage->sixteenBit()) + { + p.setX(p.x() / 255); + p.setY(p.y() / 255); + } + stream << p.x() << "\n"; + stream << p.y() << "\n"; + } + } + else + KMessageBox::error(kapp->activeWindow(), + i18n("Cannot save settings to the Black & White text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/bwsepiatool.h b/src/imageplugins/coreplugin/bwsepiatool.h new file mode 100644 index 00000000..7145f567 --- /dev/null +++ b/src/imageplugins/coreplugin/bwsepiatool.h @@ -0,0 +1,193 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : Black and White conversion tool. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef BWSEPIATOOL_H +#define BWSEPIATOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQHButtonGroup; +class TQComboBox; +class TQButtonGroup; +class TQListBox; + +class KTabWidget; + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +class CurvesWidget; +} + +namespace DigikamImagesPluginCore +{ + +class PreviewPixmapFactory; + +class BWSepiaTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + BWSepiaTool(TQObject *parent); + ~BWSepiaTool(); + + friend class PreviewPixmapFactory; + +protected: + + TQPixmap getThumbnailForEffect(int type); + void finalRendering(); + +private: + + void readSettings(); + void writeSettings(); + void blackAndWhiteConversion(uchar *data, int w, int h, bool sb, int type); + +private slots: + + void slotResetSettings(); + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotSpotColorChanged(const Digikam::DColor &color); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + void slotFilterSelected(int filter); + +private: + + enum BlackWhiteConversionType + { + BWNoFilter=0, // B&W filter to the front of lens. + BWGreenFilter, + BWOrangeFilter, + BWRedFilter, + BWYellowFilter, + + BWGeneric, // B&W film simulation. + BWAgfa200X, + BWAgfapan25, + BWAgfapan100, + BWAgfapan400, + BWIlfordDelta100, + BWIlfordDelta400, + BWIlfordDelta400Pro3200, + BWIlfordFP4, + BWIlfordHP5, + BWIlfordPanF, + BWIlfordXP2Super, + BWKodakTmax100, + BWKodakTmax400, + BWKodakTriX, + + BWNoTone, // Chemical color tone filter. + BWSepiaTone, + BWBrownTone, + BWColdTone, + BWSeleniumTone, + BWPlatinumTone, + BWGreenTone + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum SettingsTab + { + FilmTab=0, + BWFiltersTab, + ToneTab, + LuminosityTab + }; + + // Color filter attenuation in percents. + double m_redAttn, m_greenAttn, m_blueAttn; + + // Channel mixer color multiplier. + double m_redMult, m_greenMult, m_blueMult; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQListBox *m_bwFilters; + TQListBox *m_bwFilm; + TQListBox *m_bwTone; + + KDcrawIface::RIntNumInput *m_cInput; + KDcrawIface::RIntNumInput *m_strengthInput; + + KTabWidget *m_tab; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::DImg *m_originalImage; + Digikam::DImg m_thumbnailImage; + + PreviewPixmapFactory *m_previewPixmapFactory; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* BWSEPIATOOL_H */ diff --git a/src/imageplugins/coreplugin/digikamimageplugin_core.desktop b/src/imageplugins/coreplugin/digikamimageplugin_core.desktop new file mode 100644 index 00000000..8f8b3d10 --- /dev/null +++ b/src/imageplugins/coreplugin/digikamimageplugin_core.desktop @@ -0,0 +1,47 @@ +[Desktop Entry] +Name=ImagePlugin_Core +Name[el]=ΠÏόσθετοΕικόνας_ΠυÏήνας +Name[fi]=Liitännäisten ydin +Name[hr]=Jezgra +Name[it]=PluginImmagini_Nocciolo +Name[nl]=Afbeeldingsplugin_Kern +Name[sr]=Језгро прикључака +Name[sr@Latn]=Jezgro prikljuÄaka +Name[sv]=Insticksprogram med bildkärna +Name[tr]=ResimEklentisi_Çekirdek +Name[xx]=xxImagePlugin_Corexx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=digiKam Core Image Plugin +Comment[bg]=ОÑновна приÑтавка за Ñнимки на digiKam +Comment[ca]=Connector d'imatges bàsic del digiKam +Comment[da]=Grundlæggende Digikam billed-plugin +Comment[de]=digiKam-Kernmodul +Comment[el]=ΠÏόσθετο εικόνων πυÏήνα digiKam +Comment[es]=Plugin fundamental de digiKam de imágenes +Comment[et]=DigiKami peamine pildiplugin +Comment[fa]=وصلۀ تصویر هستۀ digiKam +Comment[fi]=digiKamin liitännäisten ydin +Comment[gl]=Plugin de Imaxe Básico de digiKam +Comment[hr]=digiKam dodatak za slike +Comment[it]=Plugin delle immagini centrale di digiKam +Comment[ja]=digiKam コア画åƒãƒ—ラグイン +Comment[nds]=Karn-Bildmoduul vun digiKam +Comment[nl]=Digikam kernafbeeldingsplugin +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਕੋਰ ਚਿੱਤਰ ਪਲੱਗਇਨ +Comment[pl]=Podstawowa wtyczka obrazu digiKama +Comment[pt]='Plugin' de Imagem Básico do digiKam +Comment[pt_BR]=Plugin digiKam para Centralização da imagem +Comment[ru]=Модуль digiKam оÑÐ½Ð¾Ð²Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð° Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸ÐµÐ¼ +Comment[sk]=Základný obrázkový plugin digiKamu +Comment[sr]=Језгро digiKam-ових Ñликовних прикључака +Comment[sr@Latn]=Jezgro digiKam-ovih slikovnih prikljuÄaka +Comment[sv]=Insticksprogram med bildkärna för Digikam +Comment[tr]=digiKam Çekirdek Resim Eklentisi +Comment[uk]=Втулок оÑнови Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung ảnh lõi digiKam +Comment[xx]=xxdigiKam Core Image Pluginxx + +X-TDE-Library=digikamimageplugin_core +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/coreplugin/digikamimageplugin_core_ui.rc b/src/imageplugins/coreplugin/digikamimageplugin_core_ui.rc new file mode 100644 index 00000000..cb1148bd --- /dev/null +++ b/src/imageplugins/coreplugin/digikamimageplugin_core_ui.rc @@ -0,0 +1,56 @@ + + + + + +

    &Color + + + + + + + + + + + + &Depth + + + + + + + + Enh&ance + + + + + + + Tra&nsform + + + + F&ilters + + + + + + + + + + + + + + Main Toolbar + + + + + diff --git a/src/imageplugins/coreplugin/hsl/Makefile.am b/src/imageplugins/coreplugin/hsl/Makefile.am new file mode 100644 index 00000000..4465d44c --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libhsl.la +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +libhsl_la_SOURCES = hsltool.cpp hspreviewwidget.cpp + +libhsl_la_LDFLAGS = $(all_libraries) + +noinst_HEADERS = hsltool.h hspreviewwidget.h + diff --git a/src/imageplugins/coreplugin/hsl/hsltool.cpp b/src/imageplugins/coreplugin/hsl/hsltool.cpp new file mode 100644 index 00000000..5fa3d746 --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/hsltool.cpp @@ -0,0 +1,453 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-16 + * Description : digiKam image editor to adjust Hue, Saturation, + * and Lightness of picture. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Digikam includes. + +#include "colorgradientwidget.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "hslmodifier.h" +#include "hspreviewwidget.h" +#include "imageiface.h" +#include "imagewidget.h" + +// Local includes. + +#include "hsltool.h" +#include "hsltool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +HSLTool::HSLTool(TQObject* parent) + : EditorTool(parent) +{ + setName("adjusthsl"); + setToolName(i18n("Hue / Saturation / Lightness")); + setToolIcon(SmallIcon("adjusthsl")); + setToolHelp("hsladjusttool.anchor"); + + m_destinationPreviewData = 0; + + ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + + m_previewWidget = new ImageWidget("hsladjust Tool", 0, + i18n("

    Here you can see the image " + "Hue/Saturation/Lightness adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 11, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, m_gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_HSSelector = new KHSSelector(m_gboxSettings->plainPage()); + TQWhatsThis::add( m_HSSelector, i18n("

    Select the hue and saturation adjustments of the image here.")); + m_HSSelector->setMinimumSize(256, 142); + gridSettings->addMultiCellWidget(m_HSSelector, 3, 3, 0, 4); + + m_HSPreview = new HSPreviewWidget(m_gboxSettings->plainPage()); + TQWhatsThis::add( m_HSPreview, i18n("

    You can see here a color preview of the hue and " + "saturation adjustments.")); + m_HSPreview->setMinimumSize(256, 15); + gridSettings->addMultiCellWidget(m_HSPreview, 4, 4, 0, 4); + + TQLabel *label2 = new TQLabel(i18n("Hue:"), m_gboxSettings->plainPage()); + m_hInput = new RDoubleNumInput(m_gboxSettings); + m_hInput->setPrecision(0); + m_hInput->setRange(-180.0, 180.0, 1.0); + m_hInput->setDefaultValue(0.0); + TQWhatsThis::add( m_hInput, i18n("

    Set here the hue adjustment of the image.")); + gridSettings->addMultiCellWidget(label2, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_hInput, 6, 6, 0, 4); + + TQLabel *label3 = new TQLabel(i18n("Saturation:"), m_gboxSettings->plainPage()); + m_sInput = new RDoubleNumInput(m_gboxSettings); + m_sInput->setPrecision(2); + m_sInput->setRange(-100.0, 100.0, 0.01); + m_sInput->setDefaultValue(0.0); + TQWhatsThis::add( m_sInput, i18n("

    Set here the saturation adjustment of the image.")); + gridSettings->addMultiCellWidget(label3, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_sInput, 8, 8, 0, 4); + + TQLabel *label4 = new TQLabel(i18n("Lightness:"), m_gboxSettings->plainPage()); + m_lInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_lInput->setPrecision(2); + m_lInput->setRange(-100.0, 100.0, 0.01); + m_lInput->setDefaultValue(0.0); + TQWhatsThis::add( m_lInput, i18n("

    Set here the lightness adjustment of the image.")); + gridSettings->addMultiCellWidget(label4, 9, 9, 0, 4); + gridSettings->addMultiCellWidget(m_lInput, 10, 10, 0, 4); + + gridSettings->setRowStretch(11, 10); + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_HSSelector, TQ_SIGNAL(valueChanged(int, int)), + this, TQ_SLOT(slotHSChanged(int, int))); + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_hInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_hInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotHChanged(double))); + + connect(m_sInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_sInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotSChanged(double))); + + connect(m_lInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + m_gboxSettings->enableButton(EditorToolSettings::Ok, false); +} + +HSLTool::~HSLTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void HSLTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void HSLTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void HSLTool::slotColorSelectedFromTarget( const DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void HSLTool::slotHSChanged(int h, int s) +{ + double hue = double(h); + if (h >= 180 && h <= 359) + hue = double(h) - 359.0; + + double sat = ((double) s * (200.0 / 255.0)) - 100.0; + + m_hInput->blockSignals(true); + m_sInput->blockSignals(true); + + m_hInput->setValue(hue); + m_sInput->setValue(sat); + + m_hInput->blockSignals(false); + m_sInput->blockSignals(false); + + slotTimer(); +} + +void HSLTool::slotHChanged(double h) +{ + int hue = int(h); + if (h >= -180 && h < 0) + hue = int(h) + 359; + + m_HSSelector->blockSignals(true); + m_HSSelector->setXValue(hue); + m_HSSelector->blockSignals(false); +} + +void HSLTool::slotSChanged(double s) +{ + int sat = (int) ((s + 100.0) * (255.0 / 200.0)); + + m_HSSelector->blockSignals(true); + m_HSSelector->setYValue(sat); + m_HSSelector->blockSignals(false); +} + +void HSLTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("hsladjust Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_hInput->setValue(config->readDoubleNumEntry("HueAjustment", m_hInput->defaultValue())); + m_sInput->setValue(config->readDoubleNumEntry("SaturationAjustment", m_sInput->defaultValue())); + m_lInput->setValue(config->readDoubleNumEntry("LighnessAjustment", m_lInput->defaultValue())); + slotHChanged(m_hInput->value()); + slotSChanged(m_sInput->value()); + + m_histogramWidget->reset(); + + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void HSLTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("hsladjust Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("HueAjustment", m_hInput->value()); + config->writeEntry("SaturationAjustment", m_sInput->value()); + config->writeEntry("LighnessAjustment", m_lInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void HSLTool::slotResetSettings() +{ + m_hInput->blockSignals(true); + m_sInput->blockSignals(true); + m_lInput->blockSignals(true); + + m_hInput->slotReset(); + m_sInput->slotReset(); + m_lInput->slotReset(); + + slotHChanged(0.0); + slotSChanged(0.0); + + slotEffect(); + + m_hInput->blockSignals(false); + m_sInput->blockSignals(false); + m_lInput->blockSignals(false); +} + +void HSLTool::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double hu = m_hInput->value(); + double sa = m_sInput->value(); + double lu = m_lInput->value(); + + m_gboxSettings->enableButton(EditorToolSettings::Ok, + ( hu != 0.0 || sa != 0.0 || lu != 0.0)); + + m_HSPreview->setHS(hu, sa); + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + DImg preview(w, h, sb, a, m_destinationPreviewData); + HSLModifier cmod; + cmod.setHue(hu); + cmod.setSaturation(sa); + cmod.setLightness(lu); + cmod.applyHSL(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void HSLTool::finalRendering() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + double hu = m_hInput->value(); + double sa = m_sInput->value(); + double lu = m_lInput->value(); + + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + DImg original(w, h, sb, a, data); + delete [] data; + + HSLModifier cmod; + cmod.setHue(hu); + cmod.setSaturation(sa); + cmod.setLightness(lu); + cmod.applyHSL(original); + + iface->putOriginalImage(i18n("HSL Adjustments"), original.bits()); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/hsl/hsltool.h b/src/imageplugins/coreplugin/hsl/hsltool.h new file mode 100644 index 00000000..751bacf3 --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/hsltool.h @@ -0,0 +1,126 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-16 + * Description : digiKam image editor to adjust Hue, Saturation, + * and Lightness of picture. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef HSLTOOL_H +#define HSLTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQComboBox; +class TQHButtonGroup; + +class KHSSelector; + +namespace KDcrawIface +{ +class RDoubleNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +class EditorToolSettings; +} + +namespace DigikamImagesPluginCore +{ +class HSPreviewWidget; + +class HSLTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + HSLTool(TQObject *parent); + ~HSLTool(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + void slotHSChanged(int h, int s); + void slotHChanged(double h); + void slotSChanged(double s); + void slotResetSettings(); + +private: + + void writeSettings(); + void readSettings(); + void finalRendering(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDcrawIface::RDoubleNumInput *m_hInput; + KDcrawIface::RDoubleNumInput *m_sInput; + KDcrawIface::RDoubleNumInput *m_lInput; + + KHSSelector *m_HSSelector; + + HSPreviewWidget *m_HSPreview; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::DImg *m_originalImage; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* HSLTOOL_H */ diff --git a/src/imageplugins/coreplugin/hsl/hspreviewwidget.cpp b/src/imageplugins/coreplugin/hsl/hspreviewwidget.cpp new file mode 100644 index 00000000..3841ca38 --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/hspreviewwidget.cpp @@ -0,0 +1,126 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-08 + * Description : Hue/Saturation preview widget + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "hslmodifier.h" +#include "dimg.h" +#include "hspreviewwidget.h" +#include "hspreviewwidget.moc" + +namespace DigikamImagesPluginCore +{ + +class HSPreviewWidgetPrivate +{ + +public: + + HSPreviewWidgetPrivate() + { + hue = 0.0; + sat = 0.0; + } + + int xBorder; + + double hue; + double sat; + + TQPixmap pixmap; +}; + +HSPreviewWidget::HSPreviewWidget(TQWidget *parent, int xBorder) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new HSPreviewWidgetPrivate; + d->xBorder = xBorder; +} + +HSPreviewWidget::~HSPreviewWidget() +{ + delete d; +} + +void HSPreviewWidget::setHS(double hue, double sat) +{ + d->hue = hue; + d->sat = sat; + updatePixmap(); + update(); +} + +void HSPreviewWidget::resizeEvent( TQResizeEvent * ) +{ + updatePixmap(); +} + +void HSPreviewWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0+d->xBorder, 0, &d->pixmap); +} + +void HSPreviewWidget::updatePixmap() +{ + int xSize = width()-2*d->xBorder; + int ySize = height(); + + Digikam::DImg image(xSize, ySize, false, false, 0, false); + TQColor col; + uint *p; + + for ( int s = ySize-1; s >= 0; s-- ) + { + p = (uint *)image.scanLine(ySize - s - 1); + + for( int h = 0 ; h < xSize ; h++ ) + { + col.setHsv( 359*h/(xSize-1), 255, 192 ); + *p = col.rgb(); + p++; + } + } + + Digikam::HSLModifier cmod; + cmod.setHue(d->hue); + cmod.setSaturation(d->sat); + cmod.setLightness(0.0); + cmod.applyHSL(image); + + d->pixmap = image.convertToPixmap(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/hsl/hspreviewwidget.h b/src/imageplugins/coreplugin/hsl/hspreviewwidget.h new file mode 100644 index 00000000..3744907c --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/hspreviewwidget.h @@ -0,0 +1,64 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-08 + * Description : Hue/Saturation preview widget + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef HSPREVIEWWIDGET_H +#define HSPREVIEWWIDGET_H + +// TQt includes. + +#include + +namespace DigikamImagesPluginCore +{ + +class HSPreviewWidgetPrivate; + +class HSPreviewWidget : public TQWidget +{ + TQ_OBJECT + + +public: + + HSPreviewWidget(TQWidget *parent=0, int xBorder=0); + ~HSPreviewWidget(); + + void setHS(double hue, double sat); + +protected: + + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + +private: + + void updatePixmap(); + +private: + + HSPreviewWidgetPrivate *d; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* HSPREVIEWWIDGET_H */ diff --git a/src/imageplugins/coreplugin/hsl/imageeffect_hsl.cpp b/src/imageplugins/coreplugin/hsl/imageeffect_hsl.cpp new file mode 100644 index 00000000..e179d36f --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/imageeffect_hsl.cpp @@ -0,0 +1,428 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-16 + * Description : digiKam image editor to adjust Hue, Saturation, + * and Lightness of picture. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagewidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "hslmodifier.h" +#include "dimg.h" + +// Local includes. + +#include "hspreviewwidget.h" +#include "imageeffect_hsl.h" +#include "imageeffect_hsl.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_HSL::ImageEffect_HSL(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Hue/Saturation/Lightness"), "hsladjust", false) +{ + m_destinationPreviewData = 0L; + setHelp("hsladjusttool.anchor", "digikam"); + + m_previewWidget = new Digikam::ImageWidget("hsladjust Tool Dialog", plainPage(), + i18n("

    Here you can see the image " + "Hue/Saturation/Lightness adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings, 11, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_HSSelector = new KHSSelector(gboxSettings); + TQWhatsThis::add( m_HSSelector, i18n("

    Select the hue and saturation adjustments of the image here.")); + m_HSSelector->setMinimumSize(256, 142); + gridSettings->addMultiCellWidget(m_HSSelector, 3, 3, 0, 4); + + m_HSPreview = new HSPreviewWidget(gboxSettings, spacingHint()); + TQWhatsThis::add( m_HSPreview, i18n("

    You can see here a color preview of the hue and " + "saturation adjustments.")); + m_HSPreview->setMinimumSize(256, 15); + gridSettings->addMultiCellWidget(m_HSPreview, 4, 4, 0, 4); + + TQLabel *label2 = new TQLabel(i18n("Hue:"), gboxSettings); + m_hInput = new KDoubleNumInput(gboxSettings); + m_hInput->setPrecision(0); + m_hInput->setRange(-180.0, 180.0, 1.0, true); + m_hInput->setValue(0.0); + TQWhatsThis::add( m_hInput, i18n("

    Set here the hue adjustment of the image.")); + gridSettings->addMultiCellWidget(label2, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_hInput, 6, 6, 0, 4); + + TQLabel *label3 = new TQLabel(i18n("Saturation:"), gboxSettings); + m_sInput = new KDoubleNumInput(gboxSettings); + m_sInput->setPrecision(2); + m_sInput->setRange(-100.0, 100.0, 0.01, true); + m_sInput->setValue(0.0); + TQWhatsThis::add( m_sInput, i18n("

    Set here the saturation adjustment of the image.")); + gridSettings->addMultiCellWidget(label3, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_sInput, 8, 8, 0, 4); + + TQLabel *label4 = new TQLabel(i18n("Lightness:"), gboxSettings); + m_lInput = new KDoubleNumInput(gboxSettings); + m_lInput->setPrecision(2); + m_lInput->setRange(-100.0, 100.0, 0.01, true); + m_lInput->setValue(0.0); + TQWhatsThis::add( m_lInput, i18n("

    Set here the lightness adjustment of the image.")); + gridSettings->addMultiCellWidget(label4, 9, 9, 0, 4); + gridSettings->addMultiCellWidget(m_lInput, 10, 10, 0, 4); + + gridSettings->setRowStretch(11, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_HSSelector, TQ_SIGNAL(valueChanged(int, int)), + this, TQ_SLOT(slotHSChanged(int, int))); + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_hInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_hInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotHChanged(double))); + + connect(m_sInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_sInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotSChanged(double))); + + connect(m_lInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + enableButtonOK( false ); +} + +ImageEffect_HSL::~ImageEffect_HSL() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; +} + +void ImageEffect_HSL::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_HSL::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_HSL::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_HSL::slotHSChanged(int h, int s) +{ + double hue = double(h); + if (h >= 180 && h <= 359) + hue = double(h) - 359.0; + + double sat = ((double)s * (200.0/255.0)) - 100.0; + + m_hInput->blockSignals(true); + m_sInput->blockSignals(true); + m_hInput->setValue(hue); + m_sInput->setValue(sat); + m_hInput->blockSignals(false); + m_sInput->blockSignals(false); + slotTimer(); +} + +void ImageEffect_HSL::slotHChanged(double h) +{ + int hue = int(h); + if (h >= -180 && h < 0) + hue = int(h) + 359; + + m_HSSelector->blockSignals(true); + m_HSSelector->setXValue(hue); + m_HSSelector->blockSignals(false); +} + +void ImageEffect_HSL::slotSChanged(double s) +{ + int sat = (int)((s + 100.0) * (255.0/200.0)); + + m_HSSelector->blockSignals(true); + m_HSSelector->setYValue(sat); + m_HSSelector->blockSignals(false); +} + +void ImageEffect_HSL::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("hsladjust Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_hInput->setValue(config->readDoubleNumEntry("HueAjustment", 0.0)); + m_sInput->setValue(config->readDoubleNumEntry("SaturationAjustment", 0.0)); + m_lInput->setValue(config->readDoubleNumEntry("LighnessAjustment", 0.0)); + slotHChanged(m_hInput->value()); + slotSChanged(m_sInput->value()); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_HSL::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("hsladjust Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("HueAjustment", m_hInput->value()); + config->writeEntry("SaturationAjustment", m_sInput->value()); + config->writeEntry("LighnessAjustment", m_lInput->value()); + config->sync(); +} + +void ImageEffect_HSL::resetValues() +{ + m_hInput->blockSignals(true); + m_sInput->blockSignals(true); + m_lInput->blockSignals(true); + m_hInput->setValue(0.0); + m_sInput->setValue(0.0); + m_lInput->setValue(0.0); + slotHChanged(0.0); + slotSChanged(0.0); + m_hInput->blockSignals(false); + m_sInput->blockSignals(false); + m_lInput->blockSignals(false); +} + +void ImageEffect_HSL::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double hu = m_hInput->value(); + double sa = m_sInput->value(); + double lu = m_lInput->value(); + + enableButtonOK( hu != 0.0 || sa != 0.0 || lu != 0.0); + + m_HSPreview->setHS(hu, sa); + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + Digikam::DImg preview(w, h, sb, a, m_destinationPreviewData); + Digikam::HSLModifier cmod; + cmod.setHue(hu); + cmod.setSaturation(sa); + cmod.setLightness(lu); + cmod.applyHSL(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_HSL::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double hu = m_hInput->value(); + double sa = m_sInput->value(); + double lu = m_lInput->value(); + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + Digikam::DImg original(w, h, sb, a, data); + delete [] data; + + Digikam::HSLModifier cmod; + cmod.setHue(hu); + cmod.setSaturation(sa); + cmod.setLightness(lu); + cmod.applyHSL(original); + + iface->putOriginalImage(i18n("HSL Adjustments"), original.bits()); + kapp->restoreOverrideCursor(); + accept(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/hsl/imageeffect_hsl.h b/src/imageplugins/coreplugin/hsl/imageeffect_hsl.h new file mode 100644 index 00000000..24880f4d --- /dev/null +++ b/src/imageplugins/coreplugin/hsl/imageeffect_hsl.h @@ -0,0 +1,116 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-16 + * Description : digiKam image editor to adjust Hue, Saturation, + * and Lightness of picture. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_HSL_H +#define IMAGEEFFECT_HSL_H + +// Digikam include. + +#include "imagedlgbase.h" + +class TQComboBox; +class TQHButtonGroup; + +class KDoubleNumInput; +class KHSSelector; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamImagesPluginCore +{ +class HSPreviewWidget; + +class ImageEffect_HSL : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_HSL(TQWidget *parent); + ~ImageEffect_HSL(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + void slotHSChanged(int h, int s); + void slotHChanged(double h); + void slotSChanged(double s); + +private: + + void writeUserSettings(); + void readUserSettings(); + void resetValues(); + void finalRendering(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDoubleNumInput *m_hInput; + KDoubleNumInput *m_sInput; + KDoubleNumInput *m_lInput; + + KHSSelector *m_HSSelector; + + HSPreviewWidget *m_HSPreview; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_HSL_H */ diff --git a/src/imageplugins/coreplugin/iccprooftool.cpp b/src/imageplugins/coreplugin/iccprooftool.cpp new file mode 100644 index 00000000..b74adf9a --- /dev/null +++ b/src/imageplugins/coreplugin/iccprooftool.cpp @@ -0,0 +1,1310 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-21 + * Description : digiKam image editor tool to correct picture + * colors using an ICC color profile + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Digikam includes. + +#include "bcgmodifier.h" +#include "colorgradientwidget.h" +#include "curveswidget.h" +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "iccpreviewwidget.h" +#include "iccprofileinfodlg.h" +#include "icctransform.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "imageiface.h" +#include "imagewidget.h" + +// Local includes. + +#include "iccprooftool.h" +#include "iccprooftool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +ICCProofTool::ICCProofTool(TQObject* parent) + : EditorTool(parent) +{ + setName("colormanagement"); + setToolName(i18n("Color Management")); + setToolIcon(SmallIcon("colormanagement")); + setToolHelp("colormanagement.anchor"); + + m_destinationPreviewData = 0; + m_cmEnabled = true; + m_hasICC = false; + + ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + m_embeddedICC = iface.getEmbeddedICCFromOriginalImage(); + + m_previewWidget = new ImageWidget("colormanagement Tool",0, + i18n("

    Here you can see the image preview after " + "applying a color profile

    ")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout *gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 3, 2); + + TQLabel *label1 = new TQLabel(i18n("Channel: "), m_gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, m_gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red channel values.

    " + "Green: display the green channel values.

    " + "Blue: display the blue channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add(m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal values are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal values are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 2); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "of the selected image channel. " + "This one is updated after setting changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, + histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 2); + + // ------------------------------------------------------------- + + m_toolBoxWidgets = new TQToolBox(m_gboxSettings->plainPage()); + TQWidget *generalOptions = new TQWidget(m_toolBoxWidgets); + TQWidget *inProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *spaceProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *proofProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *lightnessadjust = new TQWidget(m_toolBoxWidgets); + + //---------- "General" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(GENERALPAGE, generalOptions, + SmallIconSet("misc"), i18n("General Settings")); + TQWhatsThis::add(generalOptions, i18n("

    Here you can set general parameters.

    ")); + + TQGridLayout *zeroPageLayout = new TQGridLayout(generalOptions, 5, 1); + + m_doSoftProofBox = new TQCheckBox(generalOptions); + m_doSoftProofBox->setText(i18n("Soft-proofing")); + TQWhatsThis::add(m_doSoftProofBox, i18n("

    Rendering emulation of the device described " + "by the \"Proofing\" profile. Useful to preview the final " + "result without rendering to physical medium.

    ")); + + m_checkGamutBox = new TQCheckBox(generalOptions); + m_checkGamutBox->setText(i18n("Check gamut")); + TQWhatsThis::add(m_checkGamutBox, i18n("

    You can use this option if you want to show " + "the colors that are outside the printer's gamut

    ")); + + m_embeddProfileBox = new TQCheckBox(generalOptions); + m_embeddProfileBox->setChecked(true); + m_embeddProfileBox->setText(i18n("Assign profile")); + TQWhatsThis::add(m_embeddProfileBox, i18n("

    You can use this option to embed " + "the selected workspace color profile into the image.

    ")); + + m_BPCBox = new TQCheckBox(generalOptions); + m_BPCBox->setText(i18n("Use BPC")); + TQWhatsThis::add(m_BPCBox, i18n("

    The Black Point Compensation (BPC) feature does work in conjunction " + "with Relative Colorimetric Intent. Perceptual intent should make no " + "difference, since BPC is always on, and in Absolute Colorimetric " + "Intent it is always turned off.

    " + "

    BPC does compensate for a lack of ICC profiles in the dark tone rendering. " + "With BPC the dark tones are optimally mapped (no clipping) from original media " + "to the destination rendering media, e.g. the combination of paper and ink.

    ")); + + TQLabel *intent = new TQLabel(i18n("Rendering Intent:"), generalOptions); + m_renderingIntentsCB = new RComboBox(generalOptions); + m_renderingIntentsCB->insertItem("Perceptual"); + m_renderingIntentsCB->insertItem("Absolute Colorimetric"); + m_renderingIntentsCB->insertItem("Relative Colorimetric"); + m_renderingIntentsCB->insertItem("Saturation"); + m_renderingIntentsCB->setDefaultItem(0); + TQWhatsThis::add( m_renderingIntentsCB, i18n("
    • Perceptual intent causes the full gamut " + "of the image to be compressed or expanded to fill the gamut of the destination media, " + "so that gray balance is preserved but colorimetric accuracy may not be preserved.
      " + "In other words, if certain colors in an image fall outside of the range of colors that " + "the output device can render, the image intent will cause all the colors in the image " + "to be adjusted so that every color in the image falls within the range that can be " + "rendered and so that the relationship between colors is preserved as much as possible.
      " + "This intent is most suitable for display of photographs and images, and is the default " + "intent.
    • " + "
    • Absolute Colorimetric intent causes any colors that fall outside the range that the " + "output device can render to be adjusted to the closest color that can be rendered, while all " + "other colors are left unchanged.
      " + "This intent preserves the white point and is most suitable for spot colors (Pantone, " + "TruMatch, logo colors, ...).
    • " + "
    • Relative Colorimetric intent is defined such that any colors that fall outside the " + "range that the output device can render are adjusted to the closest color that can be " + "rendered, while all other colors are left unchanged. Proof intent does not preserve " + "the white point.
    • " + "
    • Saturation intent preserves the saturation of colors in the image at the possible " + "expense of hue and lightness.
      " + "Implementation of this intent remains somewhat problematic, and the ICC is still working " + "on methods to achieve the desired effects.
      " + "This intent is most suitable for business graphics such as charts, where it is more " + "important that the colors be vivid and contrast well with each other rather than a " + "specific color.
    ")); + + KURLLabel *lcmsLogoLabel = new KURLLabel(generalOptions); + lcmsLogoLabel->setAlignment(AlignTop | AlignRight); + lcmsLogoLabel->setText(TQString()); + lcmsLogoLabel->setURL("http://www.littlecms.com"); + TDEGlobal::dirs()->addResourceType("logo-lcms", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("logo-lcms", "logo-lcms.png"); + lcmsLogoLabel->setPixmap(TQPixmap(directory + "logo-lcms.png")); + TQToolTip::add(lcmsLogoLabel, i18n("Visit Little CMS project website")); + + zeroPageLayout->addMultiCellWidget(m_doSoftProofBox, 0, 0, 0, 0); + zeroPageLayout->addMultiCellWidget(m_checkGamutBox, 1, 1, 0, 0); + zeroPageLayout->addMultiCellWidget(m_embeddProfileBox, 2, 2, 0, 0); + zeroPageLayout->addMultiCellWidget(lcmsLogoLabel, 0, 2, 1, 1); + zeroPageLayout->addMultiCellWidget(m_BPCBox, 3, 3, 0, 0); + zeroPageLayout->addMultiCellWidget(intent, 4, 4, 0, 0); + zeroPageLayout->addMultiCellWidget(m_renderingIntentsCB, 4, 4, 1, 1); + zeroPageLayout->setRowStretch(5, 10); + + //---------- "Input" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(INPUTPAGE, inProfiles, SmallIconSet("camera-photo"), i18n("Input Profile")); + TQWhatsThis::add(inProfiles, i18n("

    Set here all parameters relevant of Input Color " + "Profiles.

    ")); + + TQGridLayout *firstPageLayout = new TQGridLayout(inProfiles, 4, 2); + + m_inProfileBG = new TQButtonGroup(4, TQt::Vertical, inProfiles); + m_inProfileBG->setFrameStyle(TQFrame::NoFrame); + m_inProfileBG->setInsideMargin(0); + + m_useEmbeddedProfile = new TQRadioButton(m_inProfileBG); + m_useEmbeddedProfile->setText(i18n("Use embedded profile")); + + m_useSRGBDefaultProfile = new TQRadioButton(m_inProfileBG); + m_useSRGBDefaultProfile->setText(i18n("Use builtin sRGB profile")); + m_useSRGBDefaultProfile->setChecked(true); + + m_useInDefaultProfile = new TQRadioButton(m_inProfileBG); + m_useInDefaultProfile->setText(i18n("Use default profile")); + + m_useInSelectedProfile = new TQRadioButton(m_inProfileBG); + m_useInSelectedProfile->setText(i18n("Use selected profile")); + + m_inProfilesPath = new KURLRequester(inProfiles); + m_inProfilesPath->setMode(KFile::File|KFile::ExistingOnly); + m_inProfilesPath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *inProfiles_dialog = m_inProfilesPath->fileDialog(); + m_iccInPreviewWidget = new ICCPreviewWidget(inProfiles_dialog); + inProfiles_dialog->setPreviewWidget(m_iccInPreviewWidget); + + TQPushButton *inProfilesInfo = new TQPushButton(i18n("Info..."), inProfiles); + + TQGroupBox *pictureInfo = new TQGroupBox(2, TQt::Horizontal, i18n("Camera information"), inProfiles); + new TQLabel(i18n("Make:"), pictureInfo); + KSqueezedTextLabel *make = new KSqueezedTextLabel(0, pictureInfo); + new TQLabel(i18n("Model:"), pictureInfo); + KSqueezedTextLabel *model = new KSqueezedTextLabel(0, pictureInfo); + make->setText(iface.getPhotographInformations().make); + model->setText(iface.getPhotographInformations().model); + + firstPageLayout->addMultiCellWidget(m_inProfileBG, 0, 1, 0, 0); + firstPageLayout->addMultiCellWidget(inProfilesInfo, 0, 0, 2, 2); + firstPageLayout->addMultiCellWidget(m_inProfilesPath, 2, 2, 0, 2); + firstPageLayout->addMultiCellWidget(pictureInfo, 3, 3, 0, 2); + firstPageLayout->setColStretch(1, 10); + firstPageLayout->setRowStretch(4, 10); + + //---------- "Workspace" Page Setup --------------------------------- + + m_toolBoxWidgets->insertItem(WORKSPACEPAGE, spaceProfiles, + SmallIconSet("input-tablet"), i18n("Workspace Profile")); + TQWhatsThis::add(spaceProfiles, i18n("

    Set here all parameters relevant to Color Workspace " + "Profiles.

    ")); + + TQGridLayout *secondPageLayout = new TQGridLayout(spaceProfiles, 3, 2); + + m_spaceProfileBG = new TQButtonGroup(2, TQt::Vertical, spaceProfiles); + m_spaceProfileBG->setFrameStyle(TQFrame::NoFrame); + m_spaceProfileBG->setInsideMargin(0); + + m_useSpaceDefaultProfile = new TQRadioButton(m_spaceProfileBG); + m_useSpaceDefaultProfile->setText(i18n("Use default workspace profile")); + + m_useSpaceSelectedProfile = new TQRadioButton(m_spaceProfileBG); + m_useSpaceSelectedProfile->setText(i18n("Use selected profile")); + + m_spaceProfilePath = new KURLRequester(spaceProfiles); + m_spaceProfilePath->setMode(KFile::File|KFile::ExistingOnly); + m_spaceProfilePath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *spaceProfiles_dialog = m_spaceProfilePath->fileDialog(); + m_iccSpacePreviewWidget = new ICCPreviewWidget(spaceProfiles_dialog); + spaceProfiles_dialog->setPreviewWidget(m_iccSpacePreviewWidget); + + TQPushButton *spaceProfilesInfo = new TQPushButton(i18n("Info..."), spaceProfiles); + + secondPageLayout->addMultiCellWidget(m_spaceProfileBG, 0, 1, 0, 0); + secondPageLayout->addMultiCellWidget(spaceProfilesInfo, 0, 0, 2, 2); + secondPageLayout->addMultiCellWidget(m_spaceProfilePath, 2, 2, 0, 2); + secondPageLayout->setColStretch(1, 10); + secondPageLayout->setRowStretch(3, 10); + + //---------- "Proofing" Page Setup --------------------------------- + + m_toolBoxWidgets->insertItem(PROOFINGPAGE, proofProfiles, + SmallIconSet("printer"), i18n("Proofing Profile")); + TQWhatsThis::add(proofProfiles, i18n("

    Set here all parameters relevant to Proofing Color " + "Profiles.

    ")); + + TQGridLayout *thirdPageLayout = new TQGridLayout(proofProfiles, 3, 2); + + m_proofProfileBG = new TQButtonGroup(2, TQt::Vertical, proofProfiles); + m_proofProfileBG->setFrameStyle(TQFrame::NoFrame); + m_proofProfileBG->setInsideMargin(0); + + m_useProofDefaultProfile = new TQRadioButton(m_proofProfileBG); + m_useProofDefaultProfile->setText(i18n("Use default proof profile")); + + m_useProofSelectedProfile = new TQRadioButton(m_proofProfileBG); + m_useProofSelectedProfile->setText(i18n("Use selected profile")); + + m_proofProfilePath = new KURLRequester(proofProfiles); + m_proofProfilePath->setMode(KFile::File|KFile::ExistingOnly); + m_proofProfilePath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *proofProfiles_dialog = m_proofProfilePath->fileDialog(); + m_iccProofPreviewWidget = new ICCPreviewWidget(proofProfiles_dialog); + proofProfiles_dialog->setPreviewWidget(m_iccProofPreviewWidget); + + TQPushButton *proofProfilesInfo = new TQPushButton(i18n("Info..."), proofProfiles); + + thirdPageLayout->addMultiCellWidget(m_proofProfileBG, 0, 1, 0, 0); + thirdPageLayout->addMultiCellWidget(proofProfilesInfo, 0, 0, 2, 2); + thirdPageLayout->addMultiCellWidget(m_proofProfilePath, 2, 2, 0, 2); + thirdPageLayout->setColStretch(1, 10); + thirdPageLayout->setRowStretch(3, 10); + + //---------- "Lightness" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(LIGHTNESSPAGE, lightnessadjust, + SmallIconSet("blend"), i18n("Lightness Adjustments")); + TQWhatsThis::add(lightnessadjust, i18n("

    Set here all lightness adjustments to the target image.

    ")); + + TQGridLayout *fourPageLayout = new TQGridLayout( lightnessadjust, 5, 2); + + ColorGradientWidget* vGradient = new ColorGradientWidget(ColorGradientWidget::Vertical, + 10, lightnessadjust ); + vGradient->setColors(TQColor("white"), TQColor("black")); + + TQLabel *spacev = new TQLabel(lightnessadjust); + spacev->setFixedWidth(1); + + m_curvesWidget = new CurvesWidget(256, 192, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), + lightnessadjust); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve adjustment of the image luminosity")); + + TQLabel *spaceh = new TQLabel(lightnessadjust); + spaceh->setFixedHeight(1); + + ColorGradientWidget *hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, + 10, + lightnessadjust); + + hGradient->setColors(TQColor("black"), TQColor("white")); + + m_cInput = new RIntNumInput(lightnessadjust); + m_cInput->input()->setLabel(i18n("Contrast:"), AlignLeft | AlignVCenter); + m_cInput->setRange(-100, 100, 1); + m_cInput->setDefaultValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + + fourPageLayout->addMultiCellWidget(vGradient, 0, 0, 0, 0); + fourPageLayout->addMultiCellWidget(spacev, 0, 0, 1, 1); + fourPageLayout->addMultiCellWidget(m_curvesWidget, 0, 0, 2, 2); + fourPageLayout->addMultiCellWidget(spaceh, 1, 1, 2, 2); + fourPageLayout->addMultiCellWidget(hGradient, 2, 2, 2, 2); + fourPageLayout->addMultiCellWidget(m_cInput, 4, 4, 0, 2); +// fourPageLayout->setRowSpacing(3); + fourPageLayout->setRowStretch(5, 10); + + // ------------------------------------------------------------- + + gridSettings->addMultiCellWidget(m_toolBoxWidgets, 3, 3, 0, 2); + + setToolSettings(m_gboxSettings); + m_gboxSettings->enableButton(EditorToolSettings::Ok, false); + init(); + + // ------------------------------------------------------------- + + connect(lcmsLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processLCMSURL(const TQString&))); + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_renderingIntentsCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + //-- Check box options connections ------------------------------------------- + + connect(m_doSoftProofBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_checkGamutBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_BPCBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + //-- Button Group ICC profile options connections ---------------------------- + + connect(m_inProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + connect(m_spaceProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + connect(m_proofProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + //-- url requester ICC profile connections ----------------------------------- + + connect(m_inProfilesPath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + connect(m_spaceProfilePath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + connect(m_proofProfilePath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + //-- Image preview widget connections ---------------------------- + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotSpotColorChanged( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + //-- ICC profile preview connections ----------------------------- + + connect(inProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotInICCInfo())); + + connect(spaceProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotSpaceICCInfo())); + + connect(proofProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotProofICCInfo())); +} + +ICCProofTool::~ICCProofTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void ICCProofTool::readSettings() +{ + TQString defaultICCPath = TDEGlobalSettings::documentPath(); + TDEConfig* config = kapp->config(); + + // General settings of digiKam Color Management + config->setGroup("Color Management"); + + if (!config->readBoolEntry("EnableCM", false)) + { + m_cmEnabled = false; + slotToggledWidgets(false); + } + else + { + m_inPath = config->readPathEntry("InProfileFile"); + m_spacePath = config->readPathEntry("WorkProfileFile"); + m_proofPath = config->readPathEntry("ProofProfileFile"); + + if (TQFile::exists(config->readPathEntry("DefaultPath"))) + { + defaultICCPath = config->readPathEntry("DefaultPath"); + } + else + { + TQString message = i18n("The ICC profiles path seems to be invalid. You won't be able to use the \"Default profile\"\ + options.

    Please fix this in the digiKam ICC setup."); + slotToggledWidgets( false ); + KMessageBox::information(kapp->activeWindow(), message); + } + } + + // Plugin settings. + config->setGroup("colormanagement Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_toolBoxWidgets->setCurrentIndex(config->readNumEntry("Settings Tab", GENERALPAGE)); + m_inProfilesPath->setURL(config->readPathEntry("InputProfilePath", defaultICCPath)); + m_proofProfilePath->setURL(config->readPathEntry("ProofProfilePath", defaultICCPath)); + m_spaceProfilePath->setURL(config->readPathEntry("SpaceProfilePath", defaultICCPath)); + m_renderingIntentsCB->setCurrentItem(config->readNumEntry("RenderingIntent", m_renderingIntentsCB->defaultItem())); + m_doSoftProofBox->setChecked(config->readBoolEntry("DoSoftProof", false)); + m_checkGamutBox->setChecked(config->readBoolEntry("CheckGamut", false)); + m_embeddProfileBox->setChecked(config->readBoolEntry("EmbeddProfile", true)); + m_BPCBox->setChecked(config->readBoolEntry("BPC", true)); + m_inProfileBG->setButton(config->readNumEntry("InputProfileMethod", 0)); + m_spaceProfileBG->setButton(config->readNumEntry("SpaceProfileMethod", 0)); + m_proofProfileBG->setButton(config->readNumEntry("ProofProfileMethod", 0)); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", m_cInput->defaultValue())); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesChannelReset(i); + + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentPoint%1").arg(j), &disable); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesCalculateCurve(i); + + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ICCProofTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colormanagement Tool"); + config->writeEntry("Settings Tab", m_toolBoxWidgets->currentIndex()); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writePathEntry("InputProfilePath", m_inProfilesPath->url()); + config->writePathEntry("ProofProfilePath", m_proofProfilePath->url()); + config->writePathEntry("SpaceProfilePath", m_spaceProfilePath->url()); + config->writeEntry("RenderingIntent", m_renderingIntentsCB->currentItem()); + config->writeEntry("DoSoftProof", m_doSoftProofBox->isChecked()); + config->writeEntry("CheckGamut", m_checkGamutBox->isChecked()); + config->writeEntry("EmbeddProfile", m_embeddProfileBox->isChecked()); + config->writeEntry("BPC", m_BPCBox->isChecked()); + config->writeEntry("InputProfileMethod", m_inProfileBG->selectedId()); + config->writeEntry("SpaceProfileMethod", m_spaceProfileBG->selectedId()); + config->writeEntry("ProofProfileMethod", m_proofProfileBG->selectedId()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + + for (int j = 0; j < 17; j++) + { + TQPoint p = m_curvesWidget->curves()->getCurvePoint(ImageHistogram::ValueChannel, j); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x() / 255); + p.setY(p.y() / 255); + } + + config->writeEntry(TQString("CurveAjustmentPoint%1").arg(j), p); + } + + m_previewWidget->writeSettings(); + config->sync(); +} + +void ICCProofTool::processLCMSURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void ICCProofTool::slotSpotColorChanged(const DColor &color) +{ + m_curvesWidget->setCurveGuide(color); +} + +void ICCProofTool::slotColorSelectedFromTarget( const DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ICCProofTool::slotChannelChanged( int channel ) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void ICCProofTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ICCProofTool::slotResetSettings() +{ + m_cInput->blockSignals(true); + m_renderingIntentsCB->blockSignals(true); + + m_cInput->slotReset(); + m_renderingIntentsCB->slotReset(); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesChannelReset(i); + + m_curvesWidget->reset(); + m_cInput->blockSignals(false); + m_renderingIntentsCB->blockSignals(false); +} + +void ICCProofTool::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + m_gboxSettings->enableButton(EditorToolSettings::Ok, true); + m_histogramWidget->stopHistogramComputation(); + + IccTransform transform; + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface *iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + DImg preview(w, h, sb, a, m_destinationPreviewData); + + TQString tmpInPath = TQString(); + TQString tmpProofPath = TQString(); + TQString tmpSpacePath = TQString(); + + bool proofCondition = false; + bool spaceCondition = false; + + //-- Input profile parameters ------------------ + + if (useDefaultInProfile()) + { + tmpInPath = m_inPath; + } + else if (useSelectedInProfile()) + { + tmpInPath = m_inProfilesPath->url(); + TQFileInfo info(tmpInPath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    The selected ICC input profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + //-- Proof profile parameters ------------------ + + if (useDefaultProofProfile()) + { + tmpProofPath = m_proofPath; + } + else + { + tmpProofPath = m_proofProfilePath->url(); + TQFileInfo info(tmpProofPath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    The selected ICC proof profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + if (m_doSoftProofBox->isChecked()) + proofCondition = tmpProofPath.isEmpty(); + + //-- Workspace profile parameters -------------- + + if (useDefaultSpaceProfile()) + { + tmpSpacePath = m_spacePath; + } + else + { + tmpSpacePath = m_spaceProfilePath->url(); + TQFileInfo info(tmpSpacePath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    Selected ICC workspace profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + spaceCondition = tmpSpacePath.isEmpty(); + + //-- Perform the color transformations ------------------ + + transform.getTransformType(m_doSoftProofBox->isChecked()); + + if (m_doSoftProofBox->isChecked()) + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles(tmpSpacePath, tmpProofPath, true); + } + else + { + transform.setProfiles(tmpInPath, tmpSpacePath, tmpProofPath); + } + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles(tmpSpacePath); + } + else + { + transform.setProfiles(tmpInPath, tmpSpacePath); + } + } + + if ( proofCondition || spaceCondition ) + { + kapp->restoreOverrideCursor(); + TQString error = i18n("

    Your settings are not sufficient.

    " + "

    To apply a color transform, you need at least two ICC profiles:

    " + "
    • An \"Input\" profile.
    • " + "
    • A \"Workspace\" profile.
    " + "

    If you want to do a \"soft-proof\" transform, in addition to these profiles " + "you need a \"Proof\" profile.

    "); + KMessageBox::information(kapp->activeWindow(), error); + m_gboxSettings->enableButton(EditorToolSettings::Ok, false); + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.apply(preview, m_embeddedICC, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + else + { + TQByteArray fakeProfile = TQByteArray(); + transform.apply(preview, fakeProfile, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + + //-- Calculate and apply the curve on image after transformation ------------- + + DImg preview2(w, h, sb, a, 0, false); + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + m_curvesWidget->curves()->curvesLutProcess(preview.bits(), preview2.bits(), w, h); + + //-- Adjust contrast --------------------------------------------------------- + + BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview2); + + iface->putPreviewImage(preview2.bits()); + m_previewWidget->updatePreview(); + + //-- Update histogram -------------------------------------------------------- + + memcpy(m_destinationPreviewData, preview2.bits(), preview2.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + kapp->restoreOverrideCursor(); + } +} + +void ICCProofTool::finalRendering() +{ + if (!m_doSoftProofBox->isChecked()) + { + kapp->setOverrideCursor( KCursor::waitCursor() ); + + ImageIface *iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + IccTransform transform; + + DImg img(w, h, sb, a, data); + + TQString tmpInPath; + TQString tmpProofPath; + TQString tmpSpacePath; + bool proofCondition; + + //-- Input profile parameters ------------------ + + if (useDefaultInProfile()) + { + tmpInPath = m_inPath; + } + else if (useSelectedInProfile()) + { + tmpInPath = m_inProfilesPath->url(); + TQFileInfo info(tmpInPath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    Selected ICC input profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + //-- Proof profile parameters ------------------ + + if (useDefaultProofProfile()) + { + tmpProofPath = m_proofPath; + } + else + { + tmpProofPath = m_proofProfilePath->url(); + TQFileInfo info(tmpProofPath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    The selected ICC proof profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + if (tmpProofPath.isNull()) + proofCondition = false; + + //-- Workspace profile parameters -------------- + + if (useDefaultSpaceProfile()) + { + tmpSpacePath = m_spacePath; + } + else + { + tmpSpacePath = m_spaceProfilePath->url(); + TQFileInfo info(tmpSpacePath); + if (!info.exists() || !info.isReadable() || !info.isFile()) + { + KMessageBox::information(kapp->activeWindow(), + i18n("

    Selected ICC workspace profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + //-- Perform the color transformations ------------------ + + transform.getTransformType(m_doSoftProofBox->isChecked()); + + if (m_doSoftProofBox->isChecked()) + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath, tmpProofPath, true ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath, tmpProofPath); + } + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath ); + } + } + + if (m_useEmbeddedProfile->isChecked()) + { + transform.apply(img, m_embeddedICC, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + else + { + TQByteArray fakeProfile = TQByteArray(); + transform.apply(img, fakeProfile, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + + //-- Embed the workspace profile if necessary -------------------------------- + + if (m_embeddProfileBox->isChecked()) + { + iface->setEmbeddedICCToOriginalImage(tmpSpacePath); + DDebug() << k_funcinfo << TQFile::encodeName(tmpSpacePath) << endl; + } + + //-- Calculate and apply the curve on image after transformation ------------- + + DImg img2(w, h, sb, a, 0, false); + m_curvesWidget->curves()->curvesLutSetup(ImageHistogram::AlphaChannel); + m_curvesWidget->curves()->curvesLutProcess(img.bits(), img2.bits(), w, h); + + //-- Adjust contrast --------------------------------------------------------- + + BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(img2); + + iface->putOriginalImage("Color Management", img2.bits()); + delete [] data; + } + + kapp->restoreOverrideCursor(); + } +} + +void ICCProofTool::slotToggledWidgets( bool t) +{ + m_useInDefaultProfile->setEnabled(t); + m_useProofDefaultProfile->setEnabled(t); + m_useSpaceDefaultProfile->setEnabled(t); +} + +void ICCProofTool::slotInICCInfo() +{ + if (useEmbeddedProfile()) + { + getICCInfo(m_embeddedICC); + } + else if (useBuiltinProfile()) + { + TQString message = i18n("

    You have selected the \"Default builtin sRGB profile\"

    "); + message.append(i18n("

    This profile is built on the fly, so there is no relevant information " + "about it.

    ")); + KMessageBox::information(kapp->activeWindow(), message); + } + else if (useDefaultInProfile()) + { + getICCInfo(m_inPath); + } + else if (useSelectedInProfile()) + { + getICCInfo(m_inProfilesPath->url()); + } +} + +void ICCProofTool::slotProofICCInfo() +{ + if (useDefaultProofProfile()) + { + getICCInfo(m_proofPath); + } + else + { + getICCInfo(m_proofProfilePath->url()); + } +} + +void ICCProofTool::slotSpaceICCInfo() +{ + if (useDefaultSpaceProfile()) + { + getICCInfo(m_spacePath); + } + else + { + getICCInfo(m_spaceProfilePath->url()); + } +} + +void ICCProofTool::getICCInfo(const TQString& profile) +{ + if (profile.isEmpty()) + { + KMessageBox::error(kapp->activeWindow(), + i18n("Sorry, there is no selected profile"), + i18n("Profile Error")); + return; + } + + ICCProfileInfoDlg infoDlg(kapp->activeWindow(), profile); + infoDlg.exec(); +} + +void ICCProofTool::getICCInfo(const TQByteArray& profile) +{ + if (profile.isNull()) + { + KMessageBox::error(kapp->activeWindow(), + i18n("Sorry, it seems there is no embedded profile"), + i18n("Profile Error")); + return; + } + + ICCProfileInfoDlg infoDlg(kapp->activeWindow(), TQString(), profile); + infoDlg.exec(); +} + +void ICCProofTool::slotCMDisabledWarning() +{ + if (!m_cmEnabled) + { + TQString message = i18n("

    You have not enabled Color Management in the digiKam preferences.

    "); + message.append(i18n("

    \"Use of default profile\" options will be disabled now.

    ")); + KMessageBox::information(kapp->activeWindow(), message); + slotToggledWidgets(false); + } +} + +//-- General Tab --------------------------- + +bool ICCProofTool::useBPC() +{ + return m_BPCBox->isChecked(); +} + +bool ICCProofTool::doProof() +{ + return m_doSoftProofBox->isChecked(); +} + +bool ICCProofTool::checkGamut() +{ + return m_checkGamutBox->isChecked(); +} + +bool ICCProofTool::embedProfile() +{ + return m_embeddProfileBox->isChecked(); +} + +//-- Input Tab --------------------------- + +bool ICCProofTool::useEmbeddedProfile() +{ + return m_useEmbeddedProfile->isChecked(); +} + +bool ICCProofTool::useBuiltinProfile() +{ + return m_useSRGBDefaultProfile->isChecked(); +} + +bool ICCProofTool::useDefaultInProfile() +{ + return m_useInDefaultProfile->isChecked(); +} + +bool ICCProofTool::useSelectedInProfile() +{ + return m_useInSelectedProfile->isChecked(); +} + +//-- Workspace Tab --------------------------- + +bool ICCProofTool::useDefaultSpaceProfile() +{ + return m_useSpaceDefaultProfile->isChecked(); +} + +//-- Proofing Tab --------------------------- + +bool ICCProofTool::useDefaultProofProfile() +{ + return m_useProofDefaultProfile->isChecked(); +} + +//-- Load all settings from file -------------------------------------- + +void ICCProofTool::slotLoadSettings() +{ + KURL loadColorManagementFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString("*"), kapp->activeWindow(), + TQString(i18n("Color Management Settings File to Load"))); + if (loadColorManagementFile.isEmpty()) + return; + + TQFile file(loadColorManagementFile.path()); + + if (file.open(IO_ReadOnly)) + { + TQTextStream stream(&file); + + if (stream.readLine() != "# Color Management Configuration File") + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Color Management settings text file.") + .arg(loadColorManagementFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + + m_renderingIntentsCB->setCurrentItem(stream.readLine().toInt()); + m_doSoftProofBox->setChecked((bool) (stream.readLine().toUInt())); + m_checkGamutBox->setChecked((bool) (stream.readLine().toUInt())); + m_embeddProfileBox->setChecked((bool) (stream.readLine().toUInt())); + m_BPCBox->setChecked((bool) (stream.readLine().toUInt())); + m_inProfileBG->setButton(stream.readLine().toInt()); + m_spaceProfileBG->setButton(stream.readLine().toInt()); + m_proofProfileBG->setButton(stream.readLine().toInt()); + m_inProfilesPath->setURL(stream.readLine()); + m_proofProfilePath->setURL(stream.readLine()); + m_spaceProfilePath->setURL(stream.readLine()); + m_cInput->setValue(stream.readLine().toInt()); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesChannelReset(i); + + m_curvesWidget->curves()->setCurveType(m_curvesWidget->m_channelType, ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0; j < 17; j++) + { + TQPoint disable(-1, -1); + TQPoint p; + p.setX(stream.readLine().toInt()); + p.setY(stream.readLine().toInt()); + + if (m_originalImage->sixteenBit() && p != disable) + { + p.setX(p.x() * 255); + p.setY(p.y() * 255); + } + + m_curvesWidget->curves()->setCurvePoint(ImageHistogram::ValueChannel, j, p); + } + + blockSignals(false); + + for (int i = 0 ; i < 5 ; i++) + m_curvesWidget->curves()->curvesCalculateCurve(i); + + m_histogramWidget->reset(); + slotEffect(); + } + else + KMessageBox::error(kapp->activeWindow(), + i18n("Cannot load settings from the Color Management text file.")); + + file.close(); +} + +//-- Save all settings to file --------------------------------------- + +void ICCProofTool::slotSaveAsSettings() +{ + KURL saveColorManagementFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString(i18n("Color Management Settings File to Save"))); + if (saveColorManagementFile.isEmpty()) + return; + + TQFile file(saveColorManagementFile.path()); + + if (file.open(IO_WriteOnly)) + { + TQTextStream stream(&file); + stream << "# Color Management Configuration File\n"; + stream << m_renderingIntentsCB->currentItem() << "\n"; + stream << m_doSoftProofBox->isChecked() << "\n"; + stream << m_checkGamutBox->isChecked() << "\n"; + stream << m_embeddProfileBox->isChecked() << "\n"; + stream << m_BPCBox->isChecked() << "\n"; + stream << m_inProfileBG->selectedId() << "\n"; + stream << m_spaceProfileBG->selectedId() << "\n"; + stream << m_proofProfileBG->selectedId() << "\n"; + stream << m_inProfilesPath->url() << "\n"; + stream << m_proofProfilePath->url() << "\n"; + stream << m_spaceProfilePath->url() << "\n"; + stream << m_cInput->value() << "\n"; + + for (int j = 0; j < 17; j++) + { + TQPoint p = m_curvesWidget->curves()->getCurvePoint(ImageHistogram::ValueChannel, j); + if (m_originalImage->sixteenBit()) + { + p.setX(p.x() / 255); + p.setY(p.y() / 255); + } + stream << p.x() << "\n"; + stream << p.y() << "\n"; + } + } + else + KMessageBox::error(kapp->activeWindow(), + i18n("Cannot save settings to the Color Management text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/iccprooftool.h b/src/imageplugins/coreplugin/iccprooftool.h new file mode 100644 index 00000000..86d93ea9 --- /dev/null +++ b/src/imageplugins/coreplugin/iccprooftool.h @@ -0,0 +1,209 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-21 + * Description : digiKam image editor tool to correct picture + * colors using an ICC color profile + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICCPROOFTOOL_H +#define ICCPROOFTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQCheckBox; +class TQComboBox; +class TQVButtonGroup; +class TQButtonGroup; +class TQHButtonGroup; +class TQRadioButton; +class TQPushButton; +class TQToolBox; + +class KURLRequester; + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class ICCTransform; +class ImageWidget; +class HistogramWidget; +class ColorGradientWidget; +class DColor; +class ICCPreviewWidget; +class CurvesWidget; +} + +namespace DigikamImagesPluginCore +{ + +class ICCProofTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + ICCProofTool(TQObject* parent); + ~ICCProofTool(); + +protected: + + void finalRendering(); + +private: + + void readSettings(); + void writeSettings(); + + void getICCInfo(const TQString&); + void getICCInfo(const TQByteArray&); + + bool useBPC(); + bool doProof(); + bool checkGamut(); + bool embedProfile(); + + bool useEmbeddedProfile(); + bool useBuiltinProfile(); + bool useDefaultInProfile(); + bool useSelectedInProfile(); + + bool useDefaultSpaceProfile(); + bool useSelectedSpaceProfile(); + + bool useDefaultProofProfile(); + bool useSelectedProofProfile(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int); + void slotScaleChanged(int); + void slotSpotColorChanged(const Digikam::DColor &); + void slotColorSelectedFromTarget(const Digikam::DColor &); + void slotToggledWidgets(bool); + void slotInICCInfo(); + void slotProofICCInfo(); + void slotSpaceICCInfo(); + void slotCMDisabledWarning(); + void processLCMSURL(const TQString&); + +private: + + enum HistogramScale + { + Linear = 0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel = 0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum ICCSettingsTab + { + GENERALPAGE=0, + INPUTPAGE, + WORKSPACEPAGE, + PROOFINGPAGE, + LIGHTNESSPAGE + }; + + bool m_cmEnabled; + bool m_hasICC; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQCheckBox *m_doSoftProofBox; + TQCheckBox *m_checkGamutBox; + TQCheckBox *m_embeddProfileBox; + TQCheckBox *m_BPCBox; + + TQRadioButton *m_useEmbeddedProfile; + TQRadioButton *m_useInDefaultProfile; + TQRadioButton *m_useInSelectedProfile; + TQRadioButton *m_useProofDefaultProfile; + TQRadioButton *m_useProofSelectedProfile; + TQRadioButton *m_useSpaceDefaultProfile; + TQRadioButton *m_useSpaceSelectedProfile; + TQRadioButton *m_useSRGBDefaultProfile; + + TQString m_inPath; + TQString m_spacePath; + TQString m_proofPath; + + TQButtonGroup *m_optionsBG; + TQButtonGroup *m_inProfileBG; + TQButtonGroup *m_spaceProfileBG; + TQButtonGroup *m_proofProfileBG; + + TQHButtonGroup *m_scaleBG; + TQVButtonGroup *m_renderingIntentBG; + TQVButtonGroup *m_profilesBG; + + TQByteArray m_embeddedICC; + + TQToolBox *m_toolBoxWidgets; + + KURLRequester *m_inProfilesPath; + KURLRequester *m_spaceProfilePath; + KURLRequester *m_proofProfilePath; + + KDcrawIface::RIntNumInput *m_cInput; + + KDcrawIface::RComboBox *m_renderingIntentsCB; + + Digikam::DImg *m_originalImage; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ICCPreviewWidget *m_iccInPreviewWidget; + Digikam::ICCPreviewWidget *m_iccSpacePreviewWidget; + Digikam::ICCPreviewWidget *m_iccProofPreviewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif // ICCPROOFTOOL_H diff --git a/src/imageplugins/coreplugin/imageeffect_autocorrection.cpp b/src/imageplugins/coreplugin/imageeffect_autocorrection.cpp new file mode 100644 index 00000000..290b93cb --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_autocorrection.cpp @@ -0,0 +1,431 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : Auto-Color correction tool. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + // TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagewidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "dimgimagefilters.h" +#include "whitebalance.h" +#include "dimg.h" +#include "listboxpreviewitem.h" + +// Local includes. + +#include "imageeffect_autocorrection.h" +#include "imageeffect_autocorrection.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_AutoCorrection::ImageEffect_AutoCorrection(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Auto Color Correction"), + "autocorrection", false), m_destinationPreviewData(0L) +{ + setHelp("autocolorcorrectiontool.anchor", "digikam"); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("autocorrection Tool Dialog", plainPage(), + i18n("

    Here you can see the auto-color correction tool " + "preview. You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + Digikam::ImageIface iface(0, 0); + m_thumbnailImage = iface.getOriginalImg()->smoothScale(128, 128, TQSize::ScaleMin); + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 4, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_correctionTools = new TQListBox(gboxSettings); + m_correctionTools->setColumnMode(1); + m_correctionTools->setVariableWidth(false); + m_correctionTools->setVariableHeight(false); + Digikam::ListBoxWhatsThis* whatsThis = new Digikam::ListBoxWhatsThis(m_correctionTools); + + TQPixmap pix = getThumbnailForEffect(AutoLevelsCorrection); + Digikam::ListBoxPreviewItem *item = new Digikam::ListBoxPreviewItem(pix, i18n("Auto Levels")); + whatsThis->add( item, i18n("Auto Levels:" + "

    This option maximizes the tonal range in the Red, " + "Green, and Blue channels. It searches the image shadow and highlight " + "limit values and adjusts the Red, Green, and Blue channels " + "to a full histogram range.

    ")); + m_correctionTools->insertItem(item, AutoLevelsCorrection); + + pix = getThumbnailForEffect(NormalizeCorrection); + item = new Digikam::ListBoxPreviewItem(pix, i18n("Normalize")); + whatsThis->add( item, i18n("Normalize:" + "

    This option scales brightness values across the active " + "image so that the darkest point becomes black, and the " + "brightest point becomes as bright as possible without " + "altering its hue. This is often a \"magic fix\" for " + "images that are dim or washed out.

    ")); + m_correctionTools->insertItem(item, NormalizeCorrection); + + pix = getThumbnailForEffect(EqualizeCorrection); + item = new Digikam::ListBoxPreviewItem(pix, i18n("Equalize")); + whatsThis->add( item, i18n("Equalize:" + "

    This option adjusts the brightness of colors across the " + "active image so that the histogram for the value channel " + "is as nearly as possible flat, that is, so that each possible " + "brightness value appears at about the same number of pixels " + "as each other value. Sometimes Equalize works wonderfully at " + "enhancing the contrasts in an image. Other times it gives " + "garbage. It is a very powerful operation, which can either work " + "miracles on an image or destroy it.

    ")); + m_correctionTools->insertItem(item, EqualizeCorrection); + + pix = getThumbnailForEffect(StretchContrastCorrection); + item = new Digikam::ListBoxPreviewItem(pix, i18n("Stretch Contrast")); + whatsThis->add( item, i18n("Stretch Contrast:" + "

    This option enhances the contrast and brightness " + "of the RGB values of an image by stretching the lowest " + "and highest values to their fullest range, adjusting " + "everything in between.

    ")); + m_correctionTools->insertItem(item, StretchContrastCorrection); + + pix = getThumbnailForEffect(AutoExposureCorrection); + item = new Digikam::ListBoxPreviewItem(pix, i18n("Auto Exposure")); + whatsThis->add( item, i18n("Auto Exposure:" + "

    This option enhances the contrast and brightness " + "of the RGB values of an image to calculate optimal " + "exposition and black level using image histogram " + "properties.

    ")); + m_correctionTools->insertItem(item, AutoExposureCorrection); + + // ------------------------------------------------------------- + + m_correctionTools->setFocus(); + gridSettings->addMultiCellWidget(m_correctionTools, 3, 3, 0, 4); + gridSettings->setRowStretch(3, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_correctionTools, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); +} + +ImageEffect_AutoCorrection::~ImageEffect_AutoCorrection() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; +} + +void ImageEffect_AutoCorrection::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_AutoCorrection::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_AutoCorrection::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_AutoCorrection::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("autocorrection Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_correctionTools->setCurrentItem(config->readNumEntry("Auto Correction Filter", AutoLevelsCorrection)); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_AutoCorrection::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("autocorrection Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("Auto Correction Filter", m_correctionTools->currentItem()); + config->sync(); +} + +void ImageEffect_AutoCorrection::resetValues() +{ + m_correctionTools->blockSignals(true); + m_correctionTools->setCurrentItem(AutoLevelsCorrection); + m_correctionTools->blockSignals(false); +} + +void ImageEffect_AutoCorrection::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + autoCorrection(m_destinationPreviewData, w, h, sb, m_correctionTools->currentItem()); + + iface->putPreviewImage(m_destinationPreviewData); + m_previewWidget->updatePreview(); + + // Update histogram. + + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +TQPixmap ImageEffect_AutoCorrection::getThumbnailForEffect(AutoCorrectionType type) +{ + Digikam::DImg thumb = m_thumbnailImage.copy(); + autoCorrection(thumb.bits(), thumb.width(), thumb.height(), thumb.sixteenBit(), type); + return (thumb.convertToPixmap()); +} + + +void ImageEffect_AutoCorrection::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + int type = m_correctionTools->currentItem(); + autoCorrection(data, w, h, sb, type); + TQString name; + + switch (type) + { + case AutoLevelsCorrection: + name = i18n("Auto Levels"); + break; + + case NormalizeCorrection: + name = i18n("Normalize"); + break; + + case EqualizeCorrection: + name = i18n("Equalize"); + break; + + case StretchContrastCorrection: + name = i18n("Stretch Contrast"); + break; + + case AutoExposureCorrection: + name = i18n("Auto Exposure"); + break; + } + + iface->putOriginalImage(name, data); + delete [] data; + } + + kapp->restoreOverrideCursor(); + accept(); +} + +void ImageEffect_AutoCorrection::autoCorrection(uchar *data, int w, int h, bool sb, int type) +{ + Digikam::DImgImageFilters filter; + + switch (type) + { + case AutoLevelsCorrection: + filter.autoLevelsCorrectionImage(data, w, h, sb); + break; + + case NormalizeCorrection: + filter.normalizeImage(data, w, h, sb); + break; + + case EqualizeCorrection: + filter.equalizeImage(data, w, h, sb); + break; + + case StretchContrastCorrection: + filter.stretchContrastImage(data, w, h, sb); + break; + + case AutoExposureCorrection: + Digikam::WhiteBalance wbFilter(sb); + double blackLevel; + double exposureLevel; + wbFilter.autoExposureAdjustement(data, w, h, sb, blackLevel, exposureLevel); + wbFilter.whiteBalance(data, w, h, sb, blackLevel, exposureLevel); + break; + } +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/imageeffect_autocorrection.h b/src/imageplugins/coreplugin/imageeffect_autocorrection.h new file mode 100644 index 00000000..c707ac1b --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_autocorrection.h @@ -0,0 +1,128 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-31 + * Description : Auto-Color correction tool. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_AUTOCORRECTION_H +#define IMAGEEFFECT_AUTOCORRECTION_H + +// TQt Includes. + +#include + +// Digikam include. + +#include "imagedlgbase.h" + +class TQHButtonGroup; +class TQComboBox; +class TQListBox; +class TQButtonGroup; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +} + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_AutoCorrection : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_AutoCorrection(TQWidget *parent); + ~ImageEffect_AutoCorrection(); + +protected: + + void finalRendering(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + +private: + + enum AutoCorrectionType + { + AutoLevelsCorrection=0, + NormalizeCorrection, + EqualizeCorrection, + StretchContrastCorrection, + AutoExposureCorrection + }; + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + + void autoCorrection(uchar *data, int w, int h, bool sb, int type); + TQPixmap getThumbnailForEffect(AutoCorrectionType type); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQListBox *m_correctionTools; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::DImg m_thumbnailImage; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_AUTOCORRECTION_H */ diff --git a/src/imageplugins/coreplugin/imageeffect_bcg.cpp b/src/imageplugins/coreplugin/imageeffect_bcg.cpp new file mode 100644 index 00000000..9d21115d --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_bcg.cpp @@ -0,0 +1,350 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-05 + * Description : digiKam image editor to adjust Brightness, + Contrast, and Gamma of picture. + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagewidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "bcgmodifier.h" +#include "dimg.h" + +// Local includes. + +#include "imageeffect_bcg.h" +#include "imageeffect_bcg.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_BCG::ImageEffect_BCG(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Brightness Contrast Gamma Adjustments"), + "bcgadjust", false) +{ + m_destinationPreviewData = 0L; + setHelp("bcgadjusttool.anchor", "digikam"); + + m_previewWidget = new Digikam::ImageWidget("bcgadjust Tool Dialog", plainPage(), + i18n("

    Here you can see the image " + "brightness-contrast-gamma adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 9, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Brightness:"), gboxSettings); + m_bInput = new KIntNumInput(gboxSettings); + m_bInput->setRange(-100, 100, 1, true); + m_bInput->setValue(0); + TQWhatsThis::add( m_bInput, i18n("

    Set here the brightness adjustment of the image.")); + gridSettings->addMultiCellWidget(label2, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_bInput, 4, 4, 0, 4); + + TQLabel *label3 = new TQLabel(i18n("Contrast:"), gboxSettings); + m_cInput = new KIntNumInput(gboxSettings); + m_cInput->setRange(-100, 100, 1, true); + m_cInput->setValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + gridSettings->addMultiCellWidget(label3, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_cInput, 6, 6, 0, 4); + + TQLabel *label4 = new TQLabel(i18n("Gamma:"), gboxSettings); + m_gInput = new KDoubleNumInput(gboxSettings); + m_gInput->setPrecision(2); + m_gInput->setRange(0.1, 3.0, 0.01, true); + m_gInput->setValue(1.0); + TQWhatsThis::add( m_gInput, i18n("

    Set here the gamma adjustment of the image.")); + gridSettings->addMultiCellWidget(label4, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_gInput, 8, 8, 0, 4); + + gridSettings->setRowStretch(9, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_bInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + enableButtonOK( false ); +} + +ImageEffect_BCG::~ImageEffect_BCG() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; +} + +void ImageEffect_BCG::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_BCG::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_BCG::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_BCG::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("bcgadjust Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_bInput->setValue(config->readNumEntry("BrightnessAjustment", 0)); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", 0)); + m_gInput->setValue(config->readDoubleNumEntry("GammaAjustment", 1.0)); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_BCG::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("bcgadjust Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("BrightnessAjustment", m_bInput->value()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + config->writeEntry("GammaAjustment", m_gInput->value()); + config->sync(); +} + +void ImageEffect_BCG::resetValues() +{ + m_bInput->blockSignals(true); + m_cInput->blockSignals(true); + m_gInput->blockSignals(true); + m_bInput->setValue(0); + m_cInput->setValue(0); + m_gInput->setValue(1.0); + m_bInput->blockSignals(false); + m_cInput->blockSignals(false); + m_gInput->blockSignals(false); +} + +void ImageEffect_BCG::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double b = (double)m_bInput->value()/250.0; + double c = (double)(m_cInput->value()/100.0) + 1.00; + double g = m_gInput->value(); + + enableButtonOK( b != 0.0 || c != 1.0 || g != 1.0 ); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + Digikam::DImg preview(w, h, sb, a, m_destinationPreviewData); + Digikam::BCGModifier cmod; + cmod.setGamma(g); + cmod.setBrightness(b); + cmod.setContrast(c); + cmod.applyBCG(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_BCG::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + + double b = (double)m_bInput->value()/250.0; + double c = (double)(m_cInput->value()/100.0) + 1.00; + double g = m_gInput->value(); + + iface->setOriginalBCG(b, c, g); + kapp->restoreOverrideCursor(); + accept(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/imageeffect_bcg.h b/src/imageplugins/coreplugin/imageeffect_bcg.h new file mode 100644 index 00000000..a9cd020b --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_bcg.h @@ -0,0 +1,110 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-05 + * Description : digiKam image editor to adjust Brightness, + Contrast, and Gamma of picture. + * + * Copyright (C) 2004 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_BCG_H +#define IMAGEEFFECT_BCG_H + +// Digikam include. + +#include "imagedlgbase.h" + +class TQCheckBox; +class TQComboBox; +class TQHButtonGroup; + +class KIntNumInput; +class KDoubleNumInput; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_BCG : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_BCG(TQWidget *parent); + ~ImageEffect_BCG(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KIntNumInput *m_bInput; + KIntNumInput *m_cInput; + KDoubleNumInput *m_gInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_BCG_H */ diff --git a/src/imageplugins/coreplugin/imageeffect_blur.cpp b/src/imageplugins/coreplugin/imageeffect_blur.cpp new file mode 100644 index 00000000..bd23854b --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_blur.cpp @@ -0,0 +1,147 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to blur an image + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "dimggaussianblur.h" + +// Local includes. + +#include "imageeffect_blur.h" +#include "imageeffect_blur.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_Blur::ImageEffect_Blur(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Apply Gaussian Blur on Photograph"), + "gaussianblur", false, true, true) +{ + setHelp("blursharpentool.anchor", "digikam"); + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 1, 1, 0, spacingHint()); + TQLabel *label = new TQLabel(i18n("Smoothness:"), gboxSettings); + + m_radiusInput = new KIntNumInput(gboxSettings); + m_radiusInput->setRange(0, 100, 1, true); + m_radiusInput->setValue(0); + TQWhatsThis::add( m_radiusInput, i18n("

    A smoothness of 0 has no effect, " + "1 and above determine the Gaussian blur matrix radius " + "that determines how much to blur the image.")); + + gridSettings->addMultiCellWidget(label, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_radiusInput, 1, 1, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); +} + +ImageEffect_Blur::~ImageEffect_Blur() +{ +} + +void ImageEffect_Blur::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("gaussianblur Tool Dialog"); + m_radiusInput->setValue(config->readNumEntry("RadiusAjustment", 0)); +} + +void ImageEffect_Blur::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("gaussianblur Tool Dialog"); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->sync(); +} + +void ImageEffect_Blur::resetValues(void) +{ + m_radiusInput->blockSignals(true); + m_radiusInput->setValue(0); + m_radiusInput->blockSignals(false); +} + +void ImageEffect_Blur::prepareEffect() +{ + m_radiusInput->setEnabled(false); + + Digikam::DImg img = m_imagePreviewWidget->getOriginalRegionImage(); + + m_threadedFilter = dynamic_cast + (new Digikam::DImgGaussianBlur(&img, this, m_radiusInput->value())); +} + +void ImageEffect_Blur::prepareFinal() +{ + m_radiusInput->setEnabled(false); + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + Digikam::DImg orgImage = Digikam::DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + m_threadedFilter = dynamic_cast + (new Digikam::DImgGaussianBlur(&orgImage, this, m_radiusInput->value())); +} + +void ImageEffect_Blur::putPreviewData(void) +{ + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + m_imagePreviewWidget->setPreviewImage(imDest); +} + +void ImageEffect_Blur::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + iface.putOriginalImage(i18n("Gaussian Blur"), imDest.bits()); +} + +void ImageEffect_Blur::renderingFinished(void) +{ + m_radiusInput->setEnabled(true); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/imageeffect_blur.h b/src/imageplugins/coreplugin/imageeffect_blur.h new file mode 100644 index 00000000..eab7694e --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_blur.h @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to blur an image + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_BLUR_H +#define IMAGEEFFECT_BLUR_H + +// Digikam include. + +#include "ctrlpaneldlg.h" + +class KIntNumInput; + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_Blur : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Blur(TQWidget *parent); + ~ImageEffect_Blur(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KIntNumInput *m_radiusInput; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_BLUR_H */ diff --git a/src/imageplugins/coreplugin/imageeffect_bwsepia.cpp b/src/imageplugins/coreplugin/imageeffect_bwsepia.cpp new file mode 100644 index 00000000..7dcdf0da --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_bwsepia.cpp @@ -0,0 +1,1183 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : Black and White conversion tool. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + // TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagehistogram.h" +#include "dimgimagefilters.h" +#include "imagewidget.h" +#include "imagecurves.h" +#include "histogramwidget.h" +#include "curveswidget.h" +#include "colorgradientwidget.h" +#include "dimg.h" +#include "bcgmodifier.h" +#include "listboxpreviewitem.h" + +// Local includes. + +#include "imageeffect_bwsepia.h" +#include "imageeffect_bwsepia.moc" + +namespace DigikamImagesPluginCore +{ + +class PreviewPixmapFactory : public TQObject +{ +public: + + PreviewPixmapFactory(ImageEffect_BWSepia* bwSepia); + + void invalidate() { m_previewPixmapMap.clear(); } + + const TQPixmap* pixmap(int id); + +private: + + TQPixmap makePixmap(int id); + + TQIntDict m_previewPixmapMap; + ImageEffect_BWSepia *m_bwSepia; +}; + +PreviewPixmapFactory::PreviewPixmapFactory(ImageEffect_BWSepia* bwSepia) + : TQObject(bwSepia), m_bwSepia(bwSepia) +{ + m_previewPixmapMap.setAutoDelete(true); +} + +const TQPixmap* PreviewPixmapFactory::pixmap(int id) +{ + if (m_previewPixmapMap.find(id) == 0) + { + TQPixmap pix = makePixmap(id); + m_previewPixmapMap.insert(id, new TQPixmap(pix)); + } + + TQPixmap* res = m_previewPixmapMap[id]; + + return res; +} + +TQPixmap PreviewPixmapFactory::makePixmap(int id) +{ + return m_bwSepia->getThumbnailForEffect(id); +} + +// ----------------------------------------------------------------------------------- + +class ListBoxBWPreviewItem : public Digikam::ListBoxPreviewItem +{ + +public: + + ListBoxBWPreviewItem(TQListBox *listbox, const TQString &text, + PreviewPixmapFactory* factory, int id) + : ListBoxPreviewItem(listbox, TQPixmap(), text) + { + m_previewPixmapFactory = factory; + m_id = id; + }; + + virtual const TQPixmap* pixmap() const; + +private: + + int m_id; + PreviewPixmapFactory* m_previewPixmapFactory; +}; + +const TQPixmap* ListBoxBWPreviewItem::pixmap() const +{ + return m_previewPixmapFactory->pixmap(m_id); +} + +// ----------------------------------------------------------------------------------- + +ImageEffect_BWSepia::ImageEffect_BWSepia(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Convert to Black & White"), + "convertbw", true, false), + m_destinationPreviewData(0L), + m_channelCB(0), + m_scaleBG(0), + m_bwFilters(0), + m_bwTone(0), + m_cInput(0), + m_tab(0), + m_previewWidget(0), + m_histogramWidget(0), + m_curvesWidget(0), + m_curves(0), + m_originalImage(0), + m_previewPixmapFactory(0) +{ + setHelp("blackandwhitetool.anchor", "digikam"); + + Digikam::ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + m_thumbnailImage = m_originalImage->smoothScale(128, 128, TQSize::ScaleMin); + m_curves = new Digikam::ImageCurves(m_originalImage->sixteenBit()); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("convertbw Tool Dialog", plainPage(), + i18n("

    Here you can see the black and white conversion tool preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 4, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + m_tab = new KTabWidget(gboxSettings); + + m_bwFilm = new TQListBox(m_tab); + m_bwFilm->setColumnMode(1); + m_bwFilm->setVariableWidth(false); + m_bwFilm->setVariableHeight(false); + Digikam::ListBoxWhatsThis* whatsThis2 = new Digikam::ListBoxWhatsThis(m_bwFilm); + m_previewPixmapFactory = new PreviewPixmapFactory(this); + + int type = BWGeneric; + + ListBoxBWPreviewItem *item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Generic"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Generic:" + "

    Simulate a generic black and white film

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa 200X"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa 200X:" + "

    Simulate the Agfa 200X black and white film at 200 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 25"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 25:" + "

    Simulate the Agfa Pan black and white film at 25 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 100:" + "

    Simulate the Agfa Pan black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Agfa Pan 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Agfa Pan 400:" + "

    Simulate the Agfa Pan black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 100:" + "

    Simulate the Ilford Delta black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 400:" + "

    Simulate the Ilford Delta black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford Delta 400 Pro 3200"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford Delta 400 Pro 3200:" + "

    Simulate the Ilford Delta 400 Pro black and white film at 3200 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford FP4 Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford FP4 Plus:" + "

    Simulate the Ilford FP4 Plus black and white film at 125 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford HP5 Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford HP5 Plus:" + "

    Simulate the Ilford HP5 Plus black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford PanF Plus"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford PanF Plus:" + "

    Simulate the Ilford PanF Plus black and white film at 50 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Ilford XP2 Super"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Ilford XP2 Super:" + "

    Simulate the Ilford XP2 Super black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak Tmax 100"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak Tmax 100:" + "

    Simulate the Kodak Tmax black and white film at 100 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak Tmax 400"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak Tmax 400:" + "

    Simulate the Kodak Tmax black and white film at 400 ISO

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilm, i18n("Kodak TriX"), m_previewPixmapFactory, type); + whatsThis2->add( item, i18n("Kodak TriX:" + "

    Simulate the Kodak TriX black and white film at 400 ISO

    ")); + + // ------------------------------------------------------------- + + TQVBox *vbox = new TQVBox(m_tab); + vbox->setSpacing(spacingHint()); + + m_bwFilters = new TQListBox(vbox); + m_bwFilters->setColumnMode(1); + m_bwFilters->setVariableWidth(false); + m_bwFilters->setVariableHeight(false); + Digikam::ListBoxWhatsThis* whatsThis = new Digikam::ListBoxWhatsThis(m_bwFilters); + + type = BWNoFilter; + + item = new ListBoxBWPreviewItem(m_bwFilters, + i18n("No Lens Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("No Lens Filter:" + "

    Do not apply a lens filter when rendering the image.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Green Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Green Filter:" + "

    Simulate black and white film exposure using a green filter. " + "This is usefule for all scenic shoots, especially portraits " + "photographed against the sky.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Orange Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Orange Filter:" + "

    Simulate black and white film exposure using an orange filter. " + "This will enhance landscapes, marine scenes and aerial " + "photography.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Red Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Red Filter:" + "

    Simulate black and white film exposure using a red filter. " + "This creates dramatic sky effects, and simulates moonlight scenes " + "in the daytime.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwFilters, i18n("Yellow Filter"), m_previewPixmapFactory, type); + whatsThis->add( item, i18n("Black & White with Yellow Filter:" + "

    Simulate black and white film exposure using a yellow filter. " + "This has the most natural tonal correction, and improves contrast. Ideal for " + "landscapes.

    ")); + + m_strengthInput = new KIntNumInput(vbox); + m_strengthInput->setLabel(i18n("Strength:"), AlignLeft | AlignVCenter); + m_strengthInput->setRange(1, 5, 1, true); + m_strengthInput->setValue(1); + TQWhatsThis::add(m_strengthInput, i18n("

    Here, set the strength adjustment of the lens filter.")); + + // ------------------------------------------------------------- + + m_bwTone = new TQListBox(m_tab); + m_bwTone->setColumnMode(1); + m_bwTone->setVariableWidth(false); + m_bwTone->setVariableHeight(false); + Digikam::ListBoxWhatsThis* whatsThis3 = new Digikam::ListBoxWhatsThis(m_bwTone); + + type = BWNoTone; + + item = new ListBoxBWPreviewItem(m_bwTone, i18n("No Tone Filter"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("No Tone Filter:" + "

    Do not apply a tone filter to the image.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Sepia Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Sepia Tone:" + "

    Gives a warm highlight and mid-tone while adding a bit of coolness to " + "the shadows - very similar to the process of bleaching a print and " + "re-developing in a sepia toner.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Brown Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Brown Tone:" + "

    This filter is more neutral than the Sepia Tone " + "filter.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Cold Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Cold Tone:" + "

    Start subtle and replicates printing on a cold tone black and white " + "paper such as a bromide enlarging " + "paper.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Selenium Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Selenium Tone:" + "

    This effect replicates traditional selenium chemical toning done " + "in the darkroom.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Platinum Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with Platinum Tone:" + "

    This effect replicates traditional platinum chemical toning done " + "in the darkroom.

    ")); + + ++type; + item = new ListBoxBWPreviewItem(m_bwTone, i18n("Green Tone"), m_previewPixmapFactory, type); + whatsThis3->add( item, i18n("Black & White with greenish tint:" + "

    This effect is also known as Verdante.

    ")); + + // ------------------------------------------------------------- + + TQWidget *curveBox = new TQWidget( m_tab ); + TQGridLayout *gridTab2 = new TQGridLayout(curveBox, 5, 2, spacingHint(), 0); + + Digikam::ColorGradientWidget* vGradient = new Digikam::ColorGradientWidget( + Digikam::ColorGradientWidget::Vertical, + 10, curveBox ); + vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + + TQLabel *spacev = new TQLabel(curveBox); + spacev->setFixedWidth(1); + + m_curvesWidget = new Digikam::CurvesWidget(256, 256, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), + m_curves, curveBox); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve adjustment of the image luminosity")); + + TQLabel *spaceh = new TQLabel(curveBox); + spaceh->setFixedHeight(1); + + Digikam::ColorGradientWidget *hGradient = new Digikam::ColorGradientWidget( + Digikam::ColorGradientWidget::Horizontal, + 10, curveBox ); + hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + m_cInput = new KIntNumInput(curveBox); + m_cInput->setLabel(i18n("Contrast:"), AlignLeft | AlignVCenter); + m_cInput->setRange(-100, 100, 1, true); + m_cInput->setValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + + gridTab2->addMultiCellWidget(vGradient, 0, 0, 0, 0); + gridTab2->addMultiCellWidget(spacev, 0, 0, 1, 1); + gridTab2->addMultiCellWidget(m_curvesWidget, 0, 0, 2, 2); + gridTab2->addMultiCellWidget(spaceh, 1, 1, 2, 2); + gridTab2->addMultiCellWidget(hGradient, 2, 2, 2, 2); + gridTab2->addMultiCellWidget(m_cInput, 4, 4, 0, 2); + gridTab2->setRowSpacing(3, spacingHint()); + gridTab2->setRowStretch(5, 10); + + // ------------------------------------------------------------- + + m_tab->insertTab(m_bwFilm, i18n("Film"), FilmTab); + m_tab->insertTab(vbox, i18n("Lens Filters"), BWFiltersTab); + m_tab->insertTab(m_bwTone, i18n("Tone"), ToneTab); + m_tab->insertTab(curveBox, i18n("Lightness"), LuminosityTab); + + gridSettings->addMultiCellWidget(m_tab, 3, 3, 0, 4); + gridSettings->setRowStretch(3, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotSpotColorChanged(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_bwFilters, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotFilterSelected(int))); + + connect(m_strengthInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_bwFilm, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_bwTone, TQ_SIGNAL(highlighted(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); +} + +ImageEffect_BWSepia::~ImageEffect_BWSepia() +{ + m_histogramWidget->stopHistogramComputation(); + + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; + delete m_curvesWidget; + delete m_curves; +} + +void ImageEffect_BWSepia::slotFilterSelected(int filter) +{ + if (filter == BWNoFilter) + m_strengthInput->setEnabled(false); + else + m_strengthInput->setEnabled(true); + + slotEffect(); +} + +TQPixmap ImageEffect_BWSepia::getThumbnailForEffect(int type) +{ + Digikam::DImg thumb = m_thumbnailImage.copy(); + int w = thumb.width(); + int h = thumb.height(); + bool sb = thumb.sixteenBit(); + bool a = thumb.hasAlpha(); + + if (type < BWGeneric) + { + // In Filter view, we will render a preview of the B&W filter with the generic B&W film. + blackAndWhiteConversion(thumb.bits(), w, h, sb, type); + blackAndWhiteConversion(thumb.bits(), w, h, sb, BWGeneric); + } + else + { + // In Film and Tone view, we will render the preview without to use the B&W Filter + blackAndWhiteConversion(thumb.bits(), w, h, sb, type); + } + + if (m_curves) // in case we're called before the creator is done + { + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + m_curves->curvesLutProcess(thumb.bits(), targetData, w, h); + + Digikam::DImg preview(w, h, sb, a, targetData); + Digikam::BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview); + + thumb.putImageData(preview.bits()); + + delete [] targetData; + } + return (thumb.convertToPixmap()); +} + +void ImageEffect_BWSepia::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_BWSepia::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); + m_curvesWidget->m_scaleType = scale; + m_curvesWidget->repaint(false); +} + +void ImageEffect_BWSepia::slotSpotColorChanged(const Digikam::DColor &color) +{ + m_curvesWidget->setCurveGuide(color); +} + +void ImageEffect_BWSepia::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_BWSepia::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("convertbw Tool Dialog"); + + m_tab->setCurrentPage(config->readNumEntry("Settings Tab", BWFiltersTab)); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_bwFilters->setCurrentItem(config->readNumEntry("BW Filter", 0)); + m_bwFilm->setCurrentItem(config->readNumEntry("BW Film", 0)); + m_bwTone->setCurrentItem(config->readNumEntry("BW Tone", 0)); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", 0)); + m_strengthInput->setValue(config->readNumEntry("StrengthAjustment", 1)); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesChannelReset(i); + + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentPoint%1").arg(j), &disable); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesCalculateCurve(i); + + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); + slotFilterSelected(m_bwFilters->currentItem()); +} + +void ImageEffect_BWSepia::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("convertbw Tool Dialog"); + config->writeEntry("Settings Tab", m_tab->currentPageIndex()); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("BW Filter", m_bwFilters->currentItem()); + config->writeEntry("BW Film", m_bwFilm->currentItem()); + config->writeEntry("BW Tone", m_bwTone->currentItem()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + config->writeEntry("StrengthAjustment", m_strengthInput->value()); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curves->getCurvePoint(Digikam::ImageHistogram::ValueChannel, j); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + + config->writeEntry(TQString("CurveAjustmentPoint%1").arg(j), p); + } + + config->sync(); +} + +void ImageEffect_BWSepia::resetValues() +{ + m_bwFilters->blockSignals(true); + m_bwTone->blockSignals(true); + m_cInput->blockSignals(true); + m_strengthInput->blockSignals(true); + + m_bwFilters->setCurrentItem(0); + m_bwFilters->setSelected(0, true); + + m_bwTone->setCurrentItem(0); + m_bwTone->setSelected(0, true); + + m_cInput->setValue(0); + + for (int channel = 0 ; channel < 5 ; channel++) + m_curves->curvesChannelReset(channel); + + m_curvesWidget->reset(); + + m_cInput->blockSignals(false); + m_bwTone->blockSignals(false); + m_bwFilters->blockSignals(false); + m_strengthInput->blockSignals(false); + + m_histogramWidget->reset(); + m_previewPixmapFactory->invalidate(); + m_bwFilters->triggerUpdate(false); + m_bwTone->triggerUpdate(false); +} + +void ImageEffect_BWSepia::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_histogramWidget->stopHistogramComputation(); + + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + // Apply black and white filter. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwFilters->currentItem()); + + // Apply black and white film type. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwFilm->currentItem() + BWGeneric); + + // Apply color tone filter. + + blackAndWhiteConversion(m_destinationPreviewData, w, h, sb, m_bwTone->currentItem() + BWNoTone); + + // Calculate and apply the curve on image. + + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + m_curves->curvesLutProcess(m_destinationPreviewData, targetData, w, h); + + // Adjust contrast. + + Digikam::DImg preview(w, h, sb, a, targetData); + Digikam::BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] targetData; + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_BWSepia::slotTimer() +{ + Digikam::ImageDlgBase::slotTimer(); + if (m_previewPixmapFactory && m_bwFilters && m_bwTone) + { + m_previewPixmapFactory->invalidate(); + m_bwFilters->triggerUpdate(false); + m_bwTone->triggerUpdate(false); + } +} + +void ImageEffect_BWSepia::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + // Apply black and white filter. + + blackAndWhiteConversion(data, w, h, sb, m_bwFilters->currentItem()); + + // Apply black and white film type. + + blackAndWhiteConversion(data, w, h, sb, m_bwFilm->currentItem() + BWGeneric); + + // Apply color tone filter. + + blackAndWhiteConversion(data, w, h, sb, m_bwTone->currentItem() + BWNoTone); + + // Calculate and apply the curve on image. + + uchar *targetData = new uchar[w*h*(sb ? 8 : 4)]; + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + m_curves->curvesLutProcess(data, targetData, w, h); + + // Adjust contrast. + + Digikam::DImg img(w, h, sb, a, targetData); + Digikam::BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(img); + + iface->putOriginalImage(i18n("Convert to Black && White"), img.bits()); + + delete [] data; + delete [] targetData; + } + + kapp->restoreOverrideCursor(); + accept(); +} + +void ImageEffect_BWSepia::blackAndWhiteConversion(uchar *data, int w, int h, bool sb, int type) +{ + // Value to multiply RGB 8 bits component of mask used by changeTonality() method. + int mul = sb ? 255 : 1; + Digikam::DImgImageFilters filter; + double strength = 1.0 + ((double)m_strengthInput->value() - 1.0) * (1.0 / 3.0); + + switch (type) + { + case BWNoFilter: + m_redAttn = 0.0; + m_greenAttn = 0.0; + m_blueAttn = 0.0; + break; + + case BWGreenFilter: + m_redAttn = -0.20 * strength; + m_greenAttn = +0.11 * strength; + m_blueAttn = +0.09 * strength; + break; + + case BWOrangeFilter: + m_redAttn = +0.48 * strength; + m_greenAttn = -0.37 * strength; + m_blueAttn = -0.11 * strength; + break; + + case BWRedFilter: + m_redAttn = +0.60 * strength; + m_greenAttn = -0.49 * strength; + m_blueAttn = -0.11 * strength; + break; + + case BWYellowFilter: + m_redAttn = +0.30 * strength; + m_greenAttn = -0.31 * strength; + m_blueAttn = +0.01 * strength; + break; + + // -------------------------------------------------------------------------------- + + case BWGeneric: + case BWNoTone: + m_redMult = 0.24; + m_greenMult = 0.68; + m_blueMult = 0.08; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfa200X: + m_redMult = 0.18; + m_greenMult = 0.41; + m_blueMult = 0.41; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan25: + m_redMult = 0.25; + m_greenMult = 0.39; + m_blueMult = 0.36; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan100: + m_redMult = 0.21; + m_greenMult = 0.40; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWAgfapan400: + m_redMult = 0.20; + m_greenMult = 0.41; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta100: + m_redMult = 0.21; + m_greenMult = 0.42; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta400: + m_redMult = 0.22; + m_greenMult = 0.42; + m_blueMult = 0.36; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordDelta400Pro3200: + m_redMult = 0.31; + m_greenMult = 0.36; + m_blueMult = 0.33; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordFP4: + m_redMult = 0.28; + m_greenMult = 0.41; + m_blueMult = 0.31; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordHP5: + m_redMult = 0.23; + m_greenMult = 0.37; + m_blueMult = 0.40; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordPanF: + m_redMult = 0.33; + m_greenMult = 0.36; + m_blueMult = 0.31; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWIlfordXP2Super: + m_redMult = 0.21; + m_greenMult = 0.42; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTmax100: + m_redMult = 0.24; + m_greenMult = 0.37; + m_blueMult = 0.39; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTmax400: + m_redMult = 0.27; + m_greenMult = 0.36; + m_blueMult = 0.37; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + case BWKodakTriX: + m_redMult = 0.25; + m_greenMult = 0.35; + m_blueMult = 0.40; + filter.channelMixerImage(data, w, h, sb, true, true, + m_redMult + m_redMult*m_redAttn, m_greenMult + m_greenMult*m_greenAttn, m_blueMult + m_blueMult*m_blueAttn, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); + break; + + // -------------------------------------------------------------------------------- + + case BWSepiaTone: + filter.changeTonality(data, w, h, sb, 162*mul, 132*mul, 101*mul); + break; + + case BWBrownTone: + filter.changeTonality(data, w, h, sb, 129*mul, 115*mul, 104*mul); + break; + + case BWColdTone: + filter.changeTonality(data, w, h, sb, 102*mul, 109*mul, 128*mul); + break; + + case BWSeleniumTone: + filter.changeTonality(data, w, h, sb, 122*mul, 115*mul, 122*mul); + break; + + case BWPlatinumTone: + filter.changeTonality(data, w, h, sb, 115*mul, 110*mul, 106*mul); + break; + + case BWGreenTone: + filter.changeTonality(data, w, h, sb, 108*mul, 116*mul, 100*mul); + break; + + } +} + +//-- Load all settings from file -------------------------------------- + +void ImageEffect_BWSepia::slotUser3() +{ + KURL loadFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Black & White Settings File to Load")) ); + if( loadFile.isEmpty() ) + return; + + TQFile file(loadFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + + if ( stream.readLine() != "# Black & White Configuration File" ) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Black & White settings text file.") + .arg(loadFile.fileName())); + file.close(); + return; + } + + m_bwFilters->blockSignals(true); + m_bwTone->blockSignals(true); + m_cInput->blockSignals(true); + + m_bwFilters->setCurrentItem(stream.readLine().toInt()); + m_bwTone->setCurrentItem(stream.readLine().toInt()); + m_cInput->setValue(stream.readLine().toInt()); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesChannelReset(i); + + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p; + p.setX( stream.readLine().toInt() ); + p.setY( stream.readLine().toInt() ); + + if (m_originalImage->sixteenBit() && p != disable) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesCalculateCurve(i); + + m_bwFilters->blockSignals(false); + m_bwTone->blockSignals(false); + m_cInput->blockSignals(false); + + m_histogramWidget->reset(); + m_previewPixmapFactory->invalidate(); + m_bwFilters->triggerUpdate(false); + m_bwTone->triggerUpdate(false); + + slotEffect(); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Black & White text file.")); + + file.close(); +} + +//-- Save all settings to file --------------------------------------- + +void ImageEffect_BWSepia::slotUser2() +{ + KURL saveFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Black & White Settings File to Save")) ); + if( saveFile.isEmpty() ) + return; + + TQFile file(saveFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Black & White Configuration File\n"; + stream << m_bwFilters->currentItem() << "\n"; + stream << m_bwTone->currentItem() << "\n"; + stream << m_cInput->value() << "\n"; + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curves->getCurvePoint(Digikam::ImageHistogram::ValueChannel, j); + if (m_originalImage->sixteenBit()) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + stream << p.x() << "\n"; + stream << p.y() << "\n"; + } + } + else + KMessageBox::error(this, i18n("Cannot save settings to the Black & White text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore + + diff --git a/src/imageplugins/coreplugin/imageeffect_bwsepia.h b/src/imageplugins/coreplugin/imageeffect_bwsepia.h new file mode 100644 index 00000000..74b4edf3 --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_bwsepia.h @@ -0,0 +1,195 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : Black and White conversion tool. + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef IMAGEEFFECT_BWSEPIA_H +#define IMAGEEFFECT_BWSEPIA_H + +// TQt Includes. + +#include + +// Digikam include. + +#include "imagedlgbase.h" + +class TQHButtonGroup; +class TQComboBox; +class TQButtonGroup; + +class KIntNumInput; +class KTabWidget; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +class ImageCurves; +class CurvesWidget; +} + +namespace DigikamImagesPluginCore +{ + +class PreviewPixmapFactory; + +class ImageEffect_BWSepia : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_BWSepia(TQWidget *parent); + ~ImageEffect_BWSepia(); + + friend class PreviewPixmapFactory; + +protected: + + TQPixmap getThumbnailForEffect(int type); + void finalRendering(); + +protected slots: + + virtual void slotTimer(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void blackAndWhiteConversion(uchar *data, int w, int h, bool sb, int type); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotSpotColorChanged(const Digikam::DColor &color); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + void slotFilterSelected(int filter); + +private: + + enum BlackWhiteConversionType + { + BWNoFilter=0, // B&W filter to the front of lens. + BWGreenFilter, + BWOrangeFilter, + BWRedFilter, + BWYellowFilter, + + BWGeneric, // B&W film simulation. + BWAgfa200X, + BWAgfapan25, + BWAgfapan100, + BWAgfapan400, + BWIlfordDelta100, + BWIlfordDelta400, + BWIlfordDelta400Pro3200, + BWIlfordFP4, + BWIlfordHP5, + BWIlfordPanF, + BWIlfordXP2Super, + BWKodakTmax100, + BWKodakTmax400, + BWKodakTriX, + + BWNoTone, // Chemical color tone filter. + BWSepiaTone, + BWBrownTone, + BWColdTone, + BWSeleniumTone, + BWPlatinumTone, + BWGreenTone + }; + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum SettingsTab + { + FilmTab=0, + BWFiltersTab, + ToneTab, + LuminosityTab + }; + + // Color filter attenuation in percents. + double m_redAttn, m_greenAttn, m_blueAttn; + + // Channel mixer color multiplier. + double m_redMult, m_greenMult, m_blueMult; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQListBox *m_bwFilters; + TQListBox *m_bwFilm; + TQListBox *m_bwTone; + + KIntNumInput *m_cInput; + KIntNumInput *m_strengthInput; + + KTabWidget *m_tab; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::ImageCurves *m_curves; + + Digikam::DImg *m_originalImage; + Digikam::DImg m_thumbnailImage; + + PreviewPixmapFactory *m_previewPixmapFactory; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_BWSEPIA_H */ diff --git a/src/imageplugins/coreplugin/imageeffect_iccproof.cpp b/src/imageplugins/coreplugin/imageeffect_iccproof.cpp new file mode 100644 index 00000000..79a09983 --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_iccproof.cpp @@ -0,0 +1,1284 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-21 + * Description : digiKam image editor tool to correct picture + * colors using an ICC color profile + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "ddebug.h" +#include "bcgmodifier.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "curveswidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "iccpreviewwidget.h" +#include "icctransform.h" +#include "iccprofileinfodlg.h" + +// Local includes. + +#include "imageeffect_iccproof.h" +#include "imageeffect_iccproof.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_ICCProof::ImageEffect_ICCProof(TQWidget* parent) + : Digikam::ImageDlgBase(parent,i18n("Color Management"), + "colormanagement", true, false) +{ + m_destinationPreviewData = 0; + m_cmEnabled = true; + m_hasICC = false; + + setHelp("colormanagement.anchor", "digikam"); + + Digikam::ImageIface iface(0, 0); + m_originalImage = iface.getOriginalImg(); + m_embeddedICC = iface.getEmbeddedICCFromOriginalImage(); + m_curves = new Digikam::ImageCurves(m_originalImage->sixteenBit()); + + m_previewWidget = new Digikam::ImageWidget("colormanagement Tool Dialog", plainPage(), + i18n("

    Here you can see the image preview after " + "applying a color profile

    ")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout *gridSettings = new TQGridLayout( gboxSettings, 3, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel: "), gboxSettings); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, gboxSettings); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red channel values.

    " + "Green: display the green channel values.

    " + "Blue: display the blue channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal values are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal values are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 2); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "of the selected image channel. " + "This one is updated after setting changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, + histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 2); + + // ------------------------------------------------------------- + + m_toolBoxWidgets = new TQToolBox(gboxSettings); + TQWidget *generalOptions = new TQWidget(m_toolBoxWidgets); + TQWidget *inProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *spaceProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *proofProfiles = new TQWidget(m_toolBoxWidgets); + TQWidget *lightnessadjust = new TQWidget(m_toolBoxWidgets); + + //---------- "General" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(GENERALPAGE, generalOptions, + SmallIconSet("misc"), i18n("General Settings")); + TQWhatsThis::add(generalOptions, i18n("

    Here you can set general parameters.

    ")); + + TQGridLayout *zeroPageLayout = new TQGridLayout(generalOptions, 5, 1, spacingHint()); + + m_doSoftProofBox = new TQCheckBox(generalOptions); + m_doSoftProofBox->setText(i18n("Soft-proofing")); + TQWhatsThis::add(m_doSoftProofBox, i18n("

    Rendering emulation of the device described " + "by the \"Proofing\" profile. Useful to preview the final " + "result without rendering to physical medium.

    ")); + + m_checkGamutBox = new TQCheckBox(generalOptions); + m_checkGamutBox->setText(i18n("Check gamut")); + TQWhatsThis::add(m_checkGamutBox, i18n("

    You can use this option if you want to show " + "the colors that are outside the printer's gamut

    ")); + + m_embeddProfileBox = new TQCheckBox(generalOptions); + m_embeddProfileBox->setChecked(true); + m_embeddProfileBox->setText(i18n("Assign profile")); + TQWhatsThis::add(m_embeddProfileBox, i18n("

    You can use this option to embed " + "the selected workspace color profile into the image.

    ")); + + m_BPCBox = new TQCheckBox(generalOptions); + m_BPCBox->setText(i18n("Use BPC")); + TQWhatsThis::add(m_BPCBox, i18n("

    The Black Point Compensation (BPC) feature does work in conjunction " + "with Relative Colorimetric Intent. Perceptual intent should make no " + "difference, since BPC is always on, and in Absolute Colorimetric " + "Intent it is always turned off.

    " + "

    BPC does compensate for a lack of ICC profiles in the dark tone rendering. " + "With BPC the dark tones are optimally mapped (no clipping) from original media " + "to the destination rendering media, e.g. the combination of paper and ink.

    ")); + + TQLabel *intent = new TQLabel(i18n("Rendering Intent:"), generalOptions); + m_renderingIntentsCB = new TQComboBox(false, generalOptions); + m_renderingIntentsCB->insertItem("Perceptual"); + m_renderingIntentsCB->insertItem("Absolute Colorimetric"); + m_renderingIntentsCB->insertItem("Relative Colorimetric"); + m_renderingIntentsCB->insertItem("Saturation"); + TQWhatsThis::add( m_renderingIntentsCB, i18n("
    • Perceptual intent causes the full gamut " + "of the image to be compressed or expanded to fill the gamut of the destination media, " + "so that gray balance is preserved but colorimetric accuracy may not be preserved.
      " + "In other words, if certain colors in an image fall outside of the range of colors that " + "the output device can render, the image intent will cause all the colors in the image " + "to be adjusted so that every color in the image falls within the range that can be " + "rendered and so that the relationship between colors is preserved as much as possible.
      " + "This intent is most suitable for display of photographs and images, and is the default " + "intent.
    • " + "
    • Absolute Colorimetric intent causes any colors that fall outside the range that the " + "output device can render to be adjusted to the closest color that can be rendered, while all " + "other colors are left unchanged.
      " + "This intent preserves the white point and is most suitable for spot colors (Pantone, " + "TruMatch, logo colors, ...).
    • " + "
    • Relative Colorimetric intent is defined such that any colors that fall outside the " + "range that the output device can render are adjusted to the closest color that can be " + "rendered, while all other colors are left unchanged. Proof intent does not preserve " + "the white point.
    • " + "
    • Saturation intent preserves the saturation of colors in the image at the possible " + "expense of hue and lightness.
      " + "Implementation of this intent remains somewhat problematic, and the ICC is still working " + "on methods to achieve the desired effects.
      " + "This intent is most suitable for business graphics such as charts, where it is more " + "important that the colors be vivid and contrast well with each other rather than a " + "specific color.
    ")); + + KURLLabel *lcmsLogoLabel = new KURLLabel(generalOptions); + lcmsLogoLabel->setAlignment( AlignTop | AlignRight ); + lcmsLogoLabel->setText(TQString()); + lcmsLogoLabel->setURL("http://www.littlecms.com"); + TDEGlobal::dirs()->addResourceType("logo-lcms", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("logo-lcms", "logo-lcms.png"); + lcmsLogoLabel->setPixmap( TQPixmap( directory + "logo-lcms.png" ) ); + TQToolTip::add(lcmsLogoLabel, i18n("Visit Little CMS project website")); + + zeroPageLayout->addMultiCellWidget(m_doSoftProofBox, 0, 0, 0, 0); + zeroPageLayout->addMultiCellWidget(m_checkGamutBox, 1, 1, 0, 0); + zeroPageLayout->addMultiCellWidget(m_embeddProfileBox, 2, 2, 0, 0); + zeroPageLayout->addMultiCellWidget(lcmsLogoLabel, 0, 2, 1, 1); + zeroPageLayout->addMultiCellWidget(m_BPCBox, 3, 3, 0, 0); + zeroPageLayout->addMultiCellWidget(intent, 4, 4, 0, 0); + zeroPageLayout->addMultiCellWidget(m_renderingIntentsCB, 4, 4, 1, 1); + zeroPageLayout->setRowStretch(5, 10); + + //---------- "Input" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(INPUTPAGE, inProfiles, SmallIconSet("camera-photo"), i18n("Input Profile")); + TQWhatsThis::add(inProfiles, i18n("

    Set here all parameters relevant of Input Color " + "Profiles.

    ")); + + TQGridLayout *firstPageLayout = new TQGridLayout(inProfiles, 4, 2, spacingHint()); + + m_inProfileBG = new TQButtonGroup(4, TQt::Vertical, inProfiles); + m_inProfileBG->setFrameStyle(TQFrame::NoFrame); + m_inProfileBG->setInsideMargin(0); + + m_useEmbeddedProfile = new TQRadioButton(m_inProfileBG); + m_useEmbeddedProfile->setText(i18n("Use embedded profile")); + + m_useSRGBDefaultProfile = new TQRadioButton(m_inProfileBG); + m_useSRGBDefaultProfile->setText(i18n("Use builtin sRGB profile")); + m_useSRGBDefaultProfile->setChecked(true); + + m_useInDefaultProfile = new TQRadioButton(m_inProfileBG); + m_useInDefaultProfile->setText(i18n("Use default profile")); + + m_useInSelectedProfile = new TQRadioButton(m_inProfileBG); + m_useInSelectedProfile->setText(i18n("Use selected profile")); + + m_inProfilesPath = new KURLRequester(inProfiles); + m_inProfilesPath->setMode(KFile::File|KFile::ExistingOnly); + m_inProfilesPath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *inProfiles_dialog = m_inProfilesPath->fileDialog(); + m_iccInPreviewWidget = new Digikam::ICCPreviewWidget(inProfiles_dialog); + inProfiles_dialog->setPreviewWidget(m_iccInPreviewWidget); + + TQPushButton *inProfilesInfo = new TQPushButton(i18n("Info..."), inProfiles); + + TQGroupBox *pictureInfo = new TQGroupBox(2, TQt::Horizontal, i18n("Camera information"), inProfiles); + new TQLabel(i18n("Make:"), pictureInfo); + KSqueezedTextLabel *make = new KSqueezedTextLabel(0, pictureInfo); + new TQLabel(i18n("Model:"), pictureInfo); + KSqueezedTextLabel *model = new KSqueezedTextLabel(0, pictureInfo); + make->setText(iface.getPhotographInformations().make); + model->setText(iface.getPhotographInformations().model); + + firstPageLayout->addMultiCellWidget(m_inProfileBG, 0, 1, 0, 0); + firstPageLayout->addMultiCellWidget(inProfilesInfo, 0, 0, 2, 2); + firstPageLayout->addMultiCellWidget(m_inProfilesPath, 2, 2, 0, 2); + firstPageLayout->addMultiCellWidget(pictureInfo, 3, 3, 0, 2); + firstPageLayout->setColStretch(1, 10); + firstPageLayout->setRowStretch(4, 10); + + //---------- "Workspace" Page Setup --------------------------------- + + m_toolBoxWidgets->insertItem(WORKSPACEPAGE, spaceProfiles, + SmallIconSet("input-tablet"), i18n("Workspace Profile")); + TQWhatsThis::add(spaceProfiles, i18n("

    Set here all parameters relevant to Color Workspace " + "Profiles.

    ")); + + TQGridLayout *secondPageLayout = new TQGridLayout(spaceProfiles, 3, 2, spacingHint()); + + m_spaceProfileBG = new TQButtonGroup(2, TQt::Vertical, spaceProfiles); + m_spaceProfileBG->setFrameStyle(TQFrame::NoFrame); + m_spaceProfileBG->setInsideMargin(0); + + m_useSpaceDefaultProfile = new TQRadioButton(m_spaceProfileBG); + m_useSpaceDefaultProfile->setText(i18n("Use default workspace profile")); + + m_useSpaceSelectedProfile = new TQRadioButton(m_spaceProfileBG); + m_useSpaceSelectedProfile->setText(i18n("Use selected profile")); + + m_spaceProfilePath = new KURLRequester(spaceProfiles); + m_spaceProfilePath->setMode(KFile::File|KFile::ExistingOnly); + m_spaceProfilePath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *spaceProfiles_dialog = m_spaceProfilePath->fileDialog(); + m_iccSpacePreviewWidget = new Digikam::ICCPreviewWidget(spaceProfiles_dialog); + spaceProfiles_dialog->setPreviewWidget(m_iccSpacePreviewWidget); + + TQPushButton *spaceProfilesInfo = new TQPushButton(i18n("Info..."), spaceProfiles); + + secondPageLayout->addMultiCellWidget(m_spaceProfileBG, 0, 1, 0, 0); + secondPageLayout->addMultiCellWidget(spaceProfilesInfo, 0, 0, 2, 2); + secondPageLayout->addMultiCellWidget(m_spaceProfilePath, 2, 2, 0, 2); + secondPageLayout->setColStretch(1, 10); + secondPageLayout->setRowStretch(3, 10); + + //---------- "Proofing" Page Setup --------------------------------- + + m_toolBoxWidgets->insertItem(PROOFINGPAGE, proofProfiles, + SmallIconSet("printer"), i18n("Proofing Profile")); + TQWhatsThis::add(proofProfiles, i18n("

    Set here all parameters relevant to Proofing Color " + "Profiles.

    ")); + + TQGridLayout *thirdPageLayout = new TQGridLayout(proofProfiles, 3, 2, + spacingHint(), spacingHint()); + + m_proofProfileBG = new TQButtonGroup(2, TQt::Vertical, proofProfiles); + m_proofProfileBG->setFrameStyle(TQFrame::NoFrame); + m_proofProfileBG->setInsideMargin(0); + + m_useProofDefaultProfile = new TQRadioButton(m_proofProfileBG); + m_useProofDefaultProfile->setText(i18n("Use default proof profile")); + + m_useProofSelectedProfile = new TQRadioButton(m_proofProfileBG); + m_useProofSelectedProfile->setText(i18n("Use selected profile")); + + m_proofProfilePath = new KURLRequester(proofProfiles); + m_proofProfilePath->setMode(KFile::File|KFile::ExistingOnly); + m_proofProfilePath->setFilter("*.icc *.icm|"+i18n("ICC Files (*.icc; *.icm)")); + KFileDialog *proofProfiles_dialog = m_proofProfilePath->fileDialog(); + m_iccProofPreviewWidget = new Digikam::ICCPreviewWidget(proofProfiles_dialog); + proofProfiles_dialog->setPreviewWidget(m_iccProofPreviewWidget); + + TQPushButton *proofProfilesInfo = new TQPushButton(i18n("Info..."), proofProfiles); + + thirdPageLayout->addMultiCellWidget(m_proofProfileBG, 0, 1, 0, 0); + thirdPageLayout->addMultiCellWidget(proofProfilesInfo, 0, 0, 2, 2); + thirdPageLayout->addMultiCellWidget(m_proofProfilePath, 2, 2, 0, 2); + thirdPageLayout->setColStretch(1, 10); + thirdPageLayout->setRowStretch(3, 10); + + //---------- "Lightness" Page Setup ---------------------------------- + + m_toolBoxWidgets->insertItem(LIGHTNESSPAGE, lightnessadjust, + SmallIconSet("blend"), i18n("Lightness Adjustments")); + TQWhatsThis::add(lightnessadjust, i18n("

    Set here all lightness adjustments to the target image.

    ")); + + TQGridLayout *fourPageLayout = new TQGridLayout( lightnessadjust, 5, 2, spacingHint(), 0); + + Digikam::ColorGradientWidget* vGradient = new Digikam::ColorGradientWidget( + Digikam::ColorGradientWidget::Vertical, + 10, lightnessadjust ); + vGradient->setColors( TQColor( "white" ), TQColor( "black" ) ); + + TQLabel *spacev = new TQLabel(lightnessadjust); + spacev->setFixedWidth(1); + + m_curvesWidget = new Digikam::CurvesWidget(256, 192, m_originalImage->bits(), m_originalImage->width(), + m_originalImage->height(), m_originalImage->sixteenBit(), + m_curves, lightnessadjust); + TQWhatsThis::add( m_curvesWidget, i18n("

    This is the curve adjustment of the image luminosity")); + + TQLabel *spaceh = new TQLabel(lightnessadjust); + spaceh->setFixedHeight(1); + + Digikam::ColorGradientWidget *hGradient = new Digikam::ColorGradientWidget( + Digikam::ColorGradientWidget::Horizontal, + 10, lightnessadjust ); + hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + m_cInput = new KIntNumInput(lightnessadjust); + m_cInput->setLabel(i18n("Contrast:"), AlignLeft | AlignVCenter); + m_cInput->setRange(-100, 100, 1, true); + m_cInput->setValue(0); + TQWhatsThis::add( m_cInput, i18n("

    Set here the contrast adjustment of the image.")); + + fourPageLayout->addMultiCellWidget(vGradient, 0, 0, 0, 0); + fourPageLayout->addMultiCellWidget(spacev, 0, 0, 1, 1); + fourPageLayout->addMultiCellWidget(m_curvesWidget, 0, 0, 2, 2); + fourPageLayout->addMultiCellWidget(spaceh, 1, 1, 2, 2); + fourPageLayout->addMultiCellWidget(hGradient, 2, 2, 2, 2); + fourPageLayout->addMultiCellWidget(m_cInput, 4, 4, 0, 2); + fourPageLayout->setRowSpacing(3, spacingHint()); + fourPageLayout->setRowStretch(5, 10); + + // ------------------------------------------------------------- + + gridSettings->addMultiCellWidget(m_toolBoxWidgets, 3, 3, 0, 2); + setUserAreaWidget(gboxSettings); + enableButtonOK(false); + + // ------------------------------------------------------------- + + connect(lcmsLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processLCMSURL(const TQString&))); + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_curvesWidget, TQ_SIGNAL(signalCurvesChanged()), + this, TQ_SLOT(slotTimer())); + + connect(m_cInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_renderingIntentsCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + //-- Check box options connections ------------------------------------------- + + connect(m_doSoftProofBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_checkGamutBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_BPCBox, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + //-- Button Group ICC profile options connections ---------------------------- + + connect(m_inProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + connect(m_spaceProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + connect(m_proofProfileBG, TQ_SIGNAL(released (int)), + this, TQ_SLOT(slotEffect())); + + //-- url requester ICC profile connections ----------------------------------- + + connect(m_inProfilesPath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + connect(m_spaceProfilePath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + connect(m_proofProfilePath, TQ_SIGNAL(urlSelected(const TQString&)), + this, TQ_SLOT(slotEffect())); + + //-- Image preview widget connections ---------------------------- + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotSpotColorChanged( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + //-- ICC profile preview connections ----------------------------- + + connect(inProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotInICCInfo())); + + connect(spaceProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotSpaceICCInfo())); + + connect(proofProfilesInfo, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotProofICCInfo())); +} + +ImageEffect_ICCProof::~ImageEffect_ICCProof() +{ + m_histogramWidget->stopHistogramComputation(); + + delete [] m_destinationPreviewData; + delete m_histogramWidget; + delete m_previewWidget; + delete m_curvesWidget; + delete m_curves; +} + +void ImageEffect_ICCProof::readUserSettings() +{ + TQString defaultICCPath = TDEGlobalSettings::documentPath(); + TDEConfig* config = kapp->config(); + + // General settings of digiKam Color Management + config->setGroup("Color Management"); + + if (!config->readBoolEntry("EnableCM", false)) + { + m_cmEnabled = false; + slotToggledWidgets(false); + } + else + { + m_inPath = config->readPathEntry("InProfileFile"); + m_spacePath = config->readPathEntry("WorkProfileFile"); + m_proofPath = config->readPathEntry("ProofProfileFile"); + + if (TQFile::exists(config->readPathEntry("DefaultPath"))) + { + defaultICCPath = config->readPathEntry("DefaultPath"); + } + else + { + TQString message = i18n("The ICC profiles path seems to be invalid. You won't be able to use the \"Default profile\"\ + options.

    Please fix this in the digiKam ICC setup."); + slotToggledWidgets( false ); + KMessageBox::information(this, message); + } + } + + // Plugin settings. + config->setGroup("colormanagement Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_toolBoxWidgets->setCurrentIndex(config->readNumEntry("Settings Tab", GENERALPAGE)); + m_inProfilesPath->setURL(config->readPathEntry("InputProfilePath", defaultICCPath)); + m_proofProfilePath->setURL(config->readPathEntry("ProofProfilePath", defaultICCPath)); + m_spaceProfilePath->setURL(config->readPathEntry("SpaceProfilePath", defaultICCPath)); + m_renderingIntentsCB->setCurrentItem(config->readNumEntry("RenderingIntent", 0)); + m_doSoftProofBox->setChecked(config->readBoolEntry("DoSoftProof", false)); + m_checkGamutBox->setChecked(config->readBoolEntry("CheckGamut", false)); + m_embeddProfileBox->setChecked(config->readBoolEntry("EmbeddProfile", true)); + m_BPCBox->setChecked(config->readBoolEntry("BPC", true)); + m_inProfileBG->setButton(config->readNumEntry("InputProfileMethod", 0)); + m_spaceProfileBG->setButton(config->readNumEntry("SpaceProfileMethod", 0)); + m_proofProfileBG->setButton(config->readNumEntry("ProofProfileMethod", 0)); + m_cInput->setValue(config->readNumEntry("ContrastAjustment", 0)); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesChannelReset(i); + + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p = config->readPointEntry(TQString("CurveAjustmentPoint%1").arg(j), &disable); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, j, p); + } + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesCalculateCurve(i); + + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_ICCProof::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colormanagement Tool Dialog"); + config->writeEntry("Settings Tab", m_toolBoxWidgets->currentIndex()); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writePathEntry("InputProfilePath", m_inProfilesPath->url()); + config->writePathEntry("ProofProfilePath", m_proofProfilePath->url()); + config->writePathEntry("SpaceProfilePath", m_spaceProfilePath->url()); + config->writeEntry("RenderingIntent", m_renderingIntentsCB->currentItem()); + config->writeEntry("DoSoftProof", m_doSoftProofBox->isChecked()); + config->writeEntry("CheckGamut", m_checkGamutBox->isChecked()); + config->writeEntry("EmbeddProfile", m_embeddProfileBox->isChecked()); + config->writeEntry("BPC", m_BPCBox->isChecked()); + config->writeEntry("InputProfileMethod", m_inProfileBG->selectedId()); + config->writeEntry("SpaceProfileMethod", m_spaceProfileBG->selectedId()); + config->writeEntry("ProofProfileMethod", m_proofProfileBG->selectedId()); + config->writeEntry("ContrastAjustment", m_cInput->value()); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curves->getCurvePoint(Digikam::ImageHistogram::ValueChannel, j); + + if (m_originalImage->sixteenBit() && p.x() != -1) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + + config->writeEntry(TQString("CurveAjustmentPoint%1").arg(j), p); + } + + config->sync(); +} + +void ImageEffect_ICCProof::processLCMSURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void ImageEffect_ICCProof::slotSpotColorChanged(const Digikam::DColor &color) +{ + m_curvesWidget->setCurveGuide(color); +} + +void ImageEffect_ICCProof::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_ICCProof::slotChannelChanged( int channel ) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_ICCProof::slotScaleChanged( int scale ) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_ICCProof::resetValues() +{ + m_cInput->blockSignals(true); + m_cInput->setValue(0); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesChannelReset(i); + + m_curvesWidget->reset(); + m_cInput->blockSignals(false); +} + +void ImageEffect_ICCProof::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + enableButtonOK(true); + m_histogramWidget->stopHistogramComputation(); + + Digikam::IccTransform transform; + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface *iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool a = iface->previewHasAlpha(); + bool sb = iface->previewSixteenBit(); + + Digikam::DImg preview(w, h, sb, a, m_destinationPreviewData); + + TQString tmpInPath = TQString(); + TQString tmpProofPath = TQString(); + TQString tmpSpacePath = TQString(); + + bool proofCondition = false; + bool spaceCondition = false; + + //-- Input profile parameters ------------------ + + if (useDefaultInProfile()) + { + tmpInPath = m_inPath; + } + else if (useSelectedInProfile()) + { + tmpInPath = m_inProfilesPath->url(); + TQFileInfo info(tmpInPath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    The selected ICC input profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + //-- Proof profile parameters ------------------ + + if (useDefaultProofProfile()) + { + tmpProofPath = m_proofPath; + } + else + { + tmpProofPath = m_proofProfilePath->url(); + TQFileInfo info(tmpProofPath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    The selected ICC proof profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + if (m_doSoftProofBox->isChecked()) + proofCondition = tmpProofPath.isEmpty(); + + //-- Workspace profile parameters -------------- + + if (useDefaultSpaceProfile()) + { + tmpSpacePath = m_spacePath; + } + else + { + tmpSpacePath = m_spaceProfilePath->url(); + TQFileInfo info(tmpSpacePath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    Selected ICC workspace profile path seems to be invalid.

    " + "Please check it.")); + return; + } + } + + spaceCondition = tmpSpacePath.isEmpty(); + + //-- Perform the color transformations ------------------ + + transform.getTransformType(m_doSoftProofBox->isChecked()); + + if (m_doSoftProofBox->isChecked()) + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath, tmpProofPath, true ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath, tmpProofPath); + } + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath ); + } + } + + if ( proofCondition || spaceCondition ) + { + kapp->restoreOverrideCursor(); + TQString error = i18n("

    Your settings are not sufficient.

    " + "

    To apply a color transform, you need at least two ICC profiles:

    " + "
    • An \"Input\" profile.
    • " + "
    • A \"Workspace\" profile.
    " + "

    If you want to do a \"soft-proof\" transform, in addition to these profiles " + "you need a \"Proof\" profile.

    "); + KMessageBox::information(this, error); + enableButtonOK(false); + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.apply(preview, m_embeddedICC, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + else + { + TQByteArray fakeProfile = TQByteArray(); + transform.apply(preview, fakeProfile, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + + //-- Calculate and apply the curve on image after transformation ------------- + + Digikam::DImg preview2(w, h, sb, a, 0, false); + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + m_curves->curvesLutProcess(preview.bits(), preview2.bits(), w, h); + + //-- Adjust contrast --------------------------------------------------------- + + Digikam::BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(preview2); + + iface->putPreviewImage(preview2.bits()); + m_previewWidget->updatePreview(); + + //-- Update histogram -------------------------------------------------------- + + memcpy(m_destinationPreviewData, preview2.bits(), preview2.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + kapp->restoreOverrideCursor(); + } +} + +void ImageEffect_ICCProof::finalRendering() +{ + if (!m_doSoftProofBox->isChecked()) + { + kapp->setOverrideCursor( KCursor::waitCursor() ); + + Digikam::ImageIface *iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + if (data) + { + Digikam::IccTransform transform; + + Digikam::DImg img(w, h, sb, a, data); + + TQString tmpInPath; + TQString tmpProofPath; + TQString tmpSpacePath; + bool proofCondition; + + //-- Input profile parameters ------------------ + + if (useDefaultInProfile()) + { + tmpInPath = m_inPath; + } + else if (useSelectedInProfile()) + { + tmpInPath = m_inProfilesPath->url(); + TQFileInfo info(tmpInPath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    Selected ICC input profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + //-- Proof profile parameters ------------------ + + if (useDefaultProofProfile()) + { + tmpProofPath = m_proofPath; + } + else + { + tmpProofPath = m_proofProfilePath->url(); + TQFileInfo info(tmpProofPath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    The selected ICC proof profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + if (tmpProofPath.isNull()) + proofCondition = false; + + //-- Workspace profile parameters -------------- + + if (useDefaultSpaceProfile()) + { + tmpSpacePath = m_spacePath; + } + else + { + tmpSpacePath = m_spaceProfilePath->url(); + TQFileInfo info(tmpSpacePath); + if (!info.exists() || !info.isReadable() || !info.isFile() ) + { + KMessageBox::information(this, i18n("

    Selected ICC workspace profile path seems " + "to be invalid.

    Please check it.")); + return; + } + } + + //-- Perform the color transformations ------------------ + + transform.getTransformType(m_doSoftProofBox->isChecked()); + + if (m_doSoftProofBox->isChecked()) + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath, tmpProofPath, true ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath, tmpProofPath); + } + } + else + { + if (m_useEmbeddedProfile->isChecked()) + { + transform.setProfiles( tmpSpacePath ); + } + else + { + transform.setProfiles( tmpInPath, tmpSpacePath ); + } + } + + if (m_useEmbeddedProfile->isChecked()) + { + transform.apply(img, m_embeddedICC, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + else + { + TQByteArray fakeProfile = TQByteArray(); + transform.apply(img, fakeProfile, m_renderingIntentsCB->currentItem(), useBPC(), + m_checkGamutBox->isChecked(), useBuiltinProfile()); + } + + //-- Embed the workspace profile if necessary -------------------------------- + + if (m_embeddProfileBox->isChecked()) + { + iface->setEmbeddedICCToOriginalImage( tmpSpacePath ); + DDebug() << k_funcinfo << TQFile::encodeName(tmpSpacePath) << endl; + } + + //-- Calculate and apply the curve on image after transformation ------------- + + Digikam::DImg img2(w, h, sb, a, 0, false); + m_curves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + m_curves->curvesLutProcess(img.bits(), img2.bits(), w, h); + + //-- Adjust contrast --------------------------------------------------------- + + Digikam::BCGModifier cmod; + cmod.setContrast((double)(m_cInput->value()/100.0) + 1.00); + cmod.applyBCG(img2); + + iface->putOriginalImage("Color Management", img2.bits()); + delete [] data; + } + + kapp->restoreOverrideCursor(); + } + + accept(); +} + +void ImageEffect_ICCProof::slotToggledWidgets( bool t) +{ + m_useInDefaultProfile->setEnabled(t); + m_useProofDefaultProfile->setEnabled(t); + m_useSpaceDefaultProfile->setEnabled(t); +} + +void ImageEffect_ICCProof::slotInICCInfo() +{ + if (useEmbeddedProfile()) + { + getICCInfo(m_embeddedICC); + } + else if(useBuiltinProfile()) + { + TQString message = i18n("

    You have selected the \"Default builtin sRGB profile\"

    "); + message.append(i18n("

    This profile is built on the fly, so there is no relevant information " + "about it.

    ")); + KMessageBox::information(this, message); + } + else if (useDefaultInProfile()) + { + getICCInfo(m_inPath); + } + else if (useSelectedInProfile()) + { + getICCInfo(m_inProfilesPath->url()); + } +} + +void ImageEffect_ICCProof::slotProofICCInfo() +{ + if (useDefaultProofProfile()) + { + getICCInfo(m_proofPath); + } + else + { + getICCInfo(m_proofProfilePath->url()); + } +} + +void ImageEffect_ICCProof::slotSpaceICCInfo() +{ + if (useDefaultSpaceProfile()) + { + getICCInfo(m_spacePath); + } + else + { + getICCInfo(m_spaceProfilePath->url()); + } +} + +void ImageEffect_ICCProof::getICCInfo(const TQString& profile) +{ + if (profile.isEmpty()) + { + KMessageBox::error(this, i18n("Sorry, there is no selected profile"), i18n("Profile Error")); + return; + } + + Digikam::ICCProfileInfoDlg infoDlg(this, profile); + infoDlg.exec(); +} + +void ImageEffect_ICCProof::getICCInfo(const TQByteArray& profile) +{ + if (profile.isNull()) + { + KMessageBox::error(this, i18n("Sorry, it seems there is no embedded profile"), i18n("Profile Error")); + return; + } + + Digikam::ICCProfileInfoDlg infoDlg(this, TQString(), profile); + infoDlg.exec(); +} + +void ImageEffect_ICCProof::slotCMDisabledWarning() +{ + if (!m_cmEnabled) + { + TQString message = i18n("

    You have not enabled Color Management in the digiKam preferences.

    "); + message.append( i18n("

    \"Use of default profile\" options will be disabled now.

    ")); + KMessageBox::information(this, message); + slotToggledWidgets(false); + } +} + +//-- General Tab --------------------------- + +bool ImageEffect_ICCProof::useBPC() +{ + return m_BPCBox->isChecked(); +} + +bool ImageEffect_ICCProof::doProof() +{ + return m_doSoftProofBox->isChecked(); +} + +bool ImageEffect_ICCProof::checkGamut() +{ + return m_checkGamutBox->isChecked(); +} + +bool ImageEffect_ICCProof::embedProfile() +{ + return m_embeddProfileBox->isChecked(); +} + +//-- Input Tab --------------------------- + +bool ImageEffect_ICCProof::useEmbeddedProfile() +{ + return m_useEmbeddedProfile->isChecked(); +} + +bool ImageEffect_ICCProof::useBuiltinProfile() +{ + return m_useSRGBDefaultProfile->isChecked(); +} + +bool ImageEffect_ICCProof::useDefaultInProfile() +{ + return m_useInDefaultProfile->isChecked(); +} + +bool ImageEffect_ICCProof::useSelectedInProfile() +{ + return m_useInSelectedProfile->isChecked(); +} + +//-- Workspace Tab --------------------------- + +bool ImageEffect_ICCProof::useDefaultSpaceProfile() +{ + return m_useSpaceDefaultProfile->isChecked(); +} + +//-- Proofing Tab --------------------------- + +bool ImageEffect_ICCProof::useDefaultProofProfile() +{ + return m_useProofDefaultProfile->isChecked(); +} + +//-- Load all settings from file -------------------------------------- + +void ImageEffect_ICCProof::slotUser3() +{ + KURL loadColorManagementFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Color Management Settings File to Load")) ); + if( loadColorManagementFile.isEmpty() ) + return; + + TQFile file(loadColorManagementFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + + if ( stream.readLine() != "# Color Management Configuration File" ) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Color Management settings text file.") + .arg(loadColorManagementFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + + m_renderingIntentsCB->setCurrentItem( stream.readLine().toInt() ); + m_doSoftProofBox->setChecked( (bool)(stream.readLine().toUInt()) ); + m_checkGamutBox->setChecked( (bool)(stream.readLine().toUInt()) ); + m_embeddProfileBox->setChecked( (bool)(stream.readLine().toUInt()) ); + m_BPCBox->setChecked( (bool)(stream.readLine().toUInt()) ); + m_inProfileBG->setButton( stream.readLine().toInt() ); + m_spaceProfileBG->setButton( stream.readLine().toInt() ); + m_proofProfileBG->setButton( stream.readLine().toInt() ); + m_inProfilesPath->setURL( stream.readLine() ); + m_proofProfilePath->setURL( stream.readLine() ); + m_spaceProfilePath->setURL( stream.readLine() ); + m_cInput->setValue( stream.readLine().toInt() ); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesChannelReset(i); + + m_curves->setCurveType(m_curvesWidget->m_channelType, Digikam::ImageCurves::CURVE_SMOOTH); + m_curvesWidget->reset(); + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint disable(-1, -1); + TQPoint p; + p.setX( stream.readLine().toInt() ); + p.setY( stream.readLine().toInt() ); + + if (m_originalImage->sixteenBit() && p != disable) + { + p.setX(p.x()*255); + p.setY(p.y()*255); + } + + m_curves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, j, p); + } + + blockSignals(false); + + for (int i = 0 ; i < 5 ; i++) + m_curves->curvesCalculateCurve(i); + + m_histogramWidget->reset(); + slotEffect(); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Color Management text file.")); + + file.close(); +} + +//-- Save all settings to file --------------------------------------- + +void ImageEffect_ICCProof::slotUser2() +{ + KURL saveColorManagementFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Color Management Settings File to Save")) ); + if( saveColorManagementFile.isEmpty() ) + return; + + TQFile file(saveColorManagementFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Color Management Configuration File\n"; + stream << m_renderingIntentsCB->currentItem() << "\n"; + stream << m_doSoftProofBox->isChecked() << "\n"; + stream << m_checkGamutBox->isChecked() << "\n"; + stream << m_embeddProfileBox->isChecked() << "\n"; + stream << m_BPCBox->isChecked() << "\n"; + stream << m_inProfileBG->selectedId() << "\n"; + stream << m_spaceProfileBG->selectedId() << "\n"; + stream << m_proofProfileBG->selectedId() << "\n"; + stream << m_inProfilesPath->url() << "\n"; + stream << m_proofProfilePath->url() << "\n"; + stream << m_spaceProfilePath->url() << "\n"; + stream << m_cInput->value() << "\n"; + + for (int j = 0 ; j < 17 ; j++) + { + TQPoint p = m_curves->getCurvePoint(Digikam::ImageHistogram::ValueChannel, j); + if (m_originalImage->sixteenBit()) + { + p.setX(p.x()/255); + p.setY(p.y()/255); + } + stream << p.x() << "\n"; + stream << p.y() << "\n"; + } + } + else + KMessageBox::error(this, i18n("Cannot save settings to the Color Management text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/imageeffect_iccproof.h b/src/imageplugins/coreplugin/imageeffect_iccproof.h new file mode 100644 index 00000000..58112aef --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_iccproof.h @@ -0,0 +1,204 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-21 + * Description : digiKam image editor tool to correct picture + * colors using an ICC color profile + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_ICCPROOF_H +#define IMAGEEFFECT_ICCPROOF_H + +// Digikam include. + +#include "imagedlgbase.h" + +class TQCheckBox; +class TQComboBox; +class TQVButtonGroup; +class TQButtonGroup; +class TQHButtonGroup; +class TQRadioButton; +class TQPushButton; +class TQToolBox; + +class KURLRequester; +class KIntNumInput; + +namespace Digikam +{ +class ICCTransform; +class ImageWidget; +class HistogramWidget; +class ColorGradientWidget; +class DColor; +class ICCPreviewWidget; +class ImageCurves; +class CurvesWidget; +} + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_ICCProof : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_ICCProof(TQWidget* parent); + ~ImageEffect_ICCProof(); + +protected: + + void finalRendering(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + + void getICCInfo(const TQString&); + void getICCInfo(const TQByteArray&); + + bool useBPC(); + bool doProof(); + bool checkGamut(); + bool embedProfile(); + + bool useEmbeddedProfile(); + bool useBuiltinProfile(); + bool useDefaultInProfile(); + bool useSelectedInProfile(); + + bool useDefaultSpaceProfile(); + bool useSelectedSpaceProfile(); + + bool useDefaultProofProfile(); + bool useSelectedProofProfile(); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotEffect(); + void slotChannelChanged(int); + void slotScaleChanged(int); + void slotSpotColorChanged(const Digikam::DColor &); + void slotColorSelectedFromTarget(const Digikam::DColor &); + void slotToggledWidgets(bool); + void slotInICCInfo(); + void slotProofICCInfo(); + void slotSpaceICCInfo(); + void slotCMDisabledWarning(); + void processLCMSURL(const TQString&); + +private: + + enum HistogramScale + { + Linear = 0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel = 0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum ICCSettingsTab + { + GENERALPAGE=0, + INPUTPAGE, + WORKSPACEPAGE, + PROOFINGPAGE, + LIGHTNESSPAGE + }; + + bool m_cmEnabled; + bool m_hasICC; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + TQComboBox *m_renderingIntentsCB; + + TQCheckBox *m_doSoftProofBox; + TQCheckBox *m_checkGamutBox; + TQCheckBox *m_embeddProfileBox; + TQCheckBox *m_BPCBox; + + TQRadioButton *m_useEmbeddedProfile; + TQRadioButton *m_useInDefaultProfile; + TQRadioButton *m_useInSelectedProfile; + TQRadioButton *m_useProofDefaultProfile; + TQRadioButton *m_useProofSelectedProfile; + TQRadioButton *m_useSpaceDefaultProfile; + TQRadioButton *m_useSpaceSelectedProfile; + TQRadioButton *m_useSRGBDefaultProfile; + + TQString m_inPath; + TQString m_spacePath; + TQString m_proofPath; + + TQButtonGroup *m_optionsBG; + TQButtonGroup *m_inProfileBG; + TQButtonGroup *m_spaceProfileBG; + TQButtonGroup *m_proofProfileBG; + + TQHButtonGroup *m_scaleBG; + TQVButtonGroup *m_renderingIntentBG; + TQVButtonGroup *m_profilesBG; + + TQByteArray m_embeddedICC; + + TQToolBox *m_toolBoxWidgets; + + KIntNumInput *m_cInput; + + KURLRequester *m_inProfilesPath; + KURLRequester *m_spaceProfilePath; + KURLRequester *m_proofProfilePath; + + Digikam::DImg *m_originalImage; + + Digikam::CurvesWidget *m_curvesWidget; + + Digikam::ImageCurves *m_curves; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ICCPreviewWidget *m_iccInPreviewWidget; + Digikam::ICCPreviewWidget *m_iccSpacePreviewWidget; + Digikam::ICCPreviewWidget *m_iccProofPreviewWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif // IMAGEEFFECT_ICCPROOF_H diff --git a/src/imageplugins/coreplugin/imageeffect_redeye.cpp b/src/imageplugins/coreplugin/imageeffect_redeye.cpp new file mode 100644 index 00000000..df3ae2e7 --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_redeye.cpp @@ -0,0 +1,574 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-06 + * Description : Red eyes correction tool for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagewidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "bcgmodifier.h" +#include "dimg.h" +#include "dimgimagefilters.h" + +// Local includes. + +#include "imageeffect_redeye.h" +#include "imageeffect_redeye.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_RedEye::ImageEffect_RedEye(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Red Eye Reduction"), "redeye", false) +{ + m_destinationPreviewData = 0; + setHelp("redeyecorrectiontool.anchor", "digikam"); + + m_previewWidget = new Digikam::ImageWidget("redeye Tool Dialog", plainPage(), + i18n("

    Here you can see the image selection preview with " + "red eye reduction applied."), + true, Digikam::ImageGuideWidget::PickColorMode, true, true); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings, 11, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image channel values.

    " + "Green: display the green image channel values.

    " + "Blue: display the blue image channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximum counts are small, you can use the linear scale.

    " + "The logarithmic scale can be used when the maximal counts are big " + "to show all values (small and large) on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "of the selected image channel. It is " + "updated upon setting changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget(Digikam::ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + // ------------------------------------------------------------- + + m_thresholdLabel = new TQLabel(i18n("Sensitivity:"), gboxSettings); + m_redThreshold = new KIntNumInput(gboxSettings); + m_redThreshold->setRange(10, 90, 1, true); + m_redThreshold->setValue(20); + TQWhatsThis::add(m_redThreshold, i18n("

    Sets the red color pixels selection threshold. " + "Low values will select more red color pixels (agressive correction), high " + "values less (mild correction). Use low value if eye have been selected " + "exactly. Use high value if other parts of the face are also selected.")); + + m_smoothLabel = new TQLabel(i18n("Smooth:"), gboxSettings); + m_smoothLevel = new KIntNumInput(gboxSettings); + m_smoothLevel->setRange(0, 5, 1, true); + m_smoothLevel->setValue(1); + TQWhatsThis::add(m_smoothLevel, i18n("

    Sets the smoothness value when blurring the border " + "of the changed pixels. " + "This leads to a more naturally looking pupil.")); + + TQLabel *label3 = new TQLabel(i18n("Coloring Tint:"), gboxSettings); + m_HSSelector = new KHSSelector(gboxSettings); + m_VSelector = new KValueSelector(gboxSettings); + m_HSSelector->setMinimumSize(200, 142); + m_VSelector->setMinimumSize(26, 142); + TQWhatsThis::add(m_HSSelector, i18n("

    Sets a custom color to re-colorize the eyes.")); + + TQLabel *label4 = new TQLabel(i18n("Tint Level:"), gboxSettings); + m_tintLevel = new KIntNumInput(gboxSettings); + m_tintLevel->setRange(1, 200, 1, true); + m_tintLevel->setValue(128); + TQWhatsThis::add(m_tintLevel, i18n("

    Set the tint level to adjust the luminosity of " + "the new color of the pupil.")); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + gridSettings->addMultiCellWidget(m_thresholdLabel, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_redThreshold, 4, 4, 0, 4); + gridSettings->addMultiCellWidget(m_smoothLabel, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_smoothLevel, 6, 6, 0, 4); + gridSettings->addMultiCellWidget(label3, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_HSSelector, 8, 8, 0, 3); + gridSettings->addMultiCellWidget(m_VSelector, 8, 8, 4, 4); + gridSettings->addMultiCellWidget(label4, 9, 9, 0, 4); + gridSettings->addMultiCellWidget(m_tintLevel, 10, 10, 0, 4); + gridSettings->setRowStretch(11, 10); + gridSettings->setColStretch(3, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_redThreshold, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_smoothLevel, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_HSSelector, TQ_SIGNAL(valueChanged(int, int)), + this, TQ_SLOT(slotHSChanged(int, int))); + + connect(m_VSelector, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_tintLevel, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_RedEye::~ImageEffect_RedEye() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; +} + +void ImageEffect_RedEye::slotHSChanged(int h, int s) +{ + m_VSelector->blockSignals(true); + m_VSelector->setHue(h); + m_VSelector->setSaturation(s); + m_VSelector->updateContents(); + m_VSelector->repaint(false); + m_VSelector->blockSignals(false); + slotTimer(); +} + +void ImageEffect_RedEye::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_RedEye::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_RedEye::slotColorSelectedFromTarget(const Digikam::DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_RedEye::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("redeye Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + m_redThreshold->setValue(config->readNumEntry("RedThreshold", 20)); + m_smoothLevel->setValue(config->readNumEntry("SmoothLevel", 1)); + m_HSSelector->setXValue(config->readNumEntry("HueColoringTint", 0)); + m_HSSelector->setYValue(config->readNumEntry("SatColoringTint", 0)); + m_VSelector->setValue(config->readNumEntry("ValColoringTint", 0)); + m_tintLevel->setValue(config->readNumEntry("TintLevel", 128)); + + slotHSChanged(m_HSSelector->xValue(), m_HSSelector->yValue()); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_RedEye::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("redeye Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("RedThreshold", m_redThreshold->value()); + config->writeEntry("SmoothLevel", m_smoothLevel->value()); + config->writeEntry("HueColoringTint", m_HSSelector->xValue()); + config->writeEntry("SatColoringTint", m_HSSelector->yValue()); + config->writeEntry("ValColoringTint", m_VSelector->value()); + config->writeEntry("TintLevel", m_tintLevel->value()); + config->sync(); +} + +void ImageEffect_RedEye::resetValues() +{ + m_redThreshold->blockSignals(true); + m_HSSelector->blockSignals(true); + m_VSelector->blockSignals(true); + m_tintLevel->blockSignals(true); + + m_redThreshold->setValue(20); + m_smoothLevel->setValue(1); + + // Black color by default + m_HSSelector->setXValue(0); + m_HSSelector->setYValue(0); + m_VSelector->setValue(0); + + m_tintLevel->setValue(128); + + m_redThreshold->blockSignals(false); + m_HSSelector->blockSignals(false); + m_VSelector->blockSignals(false); + m_tintLevel->blockSignals(false); +} + +void ImageEffect_RedEye::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + // Here, we need to use the real selection image data because we will apply + // a Gaussian blur filter on pixels and we cannot use directly the preview scaled image + // else the blur radius will not give the same result between preview and final rendering. + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getImageSelection(); + int w = iface->selectedWidth(); + int h = iface->selectedHeight(); + bool sb = iface->originalSixteenBit(); + bool a = iface->originalHasAlpha(); + Digikam::DImg selection(w, h, sb, a, m_destinationPreviewData); + + redEyeFilter(selection); + + Digikam::DImg preview = selection.smoothScale(iface->previewWidth(), iface->previewHeight()); + + iface->putPreviewImage(preview.bits()); + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, selection.bits(), selection.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_RedEye::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getImageSelection(); + int w = iface->selectedWidth(); + int h = iface->selectedHeight(); + bool sixteenBit = iface->originalSixteenBit(); + bool hasAlpha = iface->originalHasAlpha(); + Digikam::DImg selection(w, h, sixteenBit, hasAlpha, data); + delete [] data; + + redEyeFilter(selection); + + iface->putImageSelection(i18n("Red Eyes Correction"), selection.bits()); + + kapp->restoreOverrideCursor(); + accept(); +} + +void ImageEffect_RedEye::redEyeFilter(Digikam::DImg& selection) +{ + Digikam::DImg mask(selection.width(), selection.height(), selection.sixteenBit(), true, + selection.bits(), true); + + selection = mask.copy(); + float redThreshold = m_redThreshold->value()/10.0; + int hue = m_HSSelector->xValue(); + int sat = m_HSSelector->yValue(); + int val = m_VSelector->value(); + KColor coloring; + coloring.setHsv(hue, sat, val); + + struct channel + { + float red_gain; + float green_gain; + float blue_gain; + }; + + channel red_chan, green_chan, blue_chan; + + red_chan.red_gain = 0.1; + red_chan.green_gain = 0.6; + red_chan.blue_gain = 0.3; + + green_chan.red_gain = 0.0; + green_chan.green_gain = 1.0; + green_chan.blue_gain = 0.0; + + blue_chan.red_gain = 0.0; + blue_chan.green_gain = 0.0; + blue_chan.blue_gain = 1.0; + + float red_norm, green_norm, blue_norm; + int level = 201 - m_tintLevel->value(); + + red_norm = 1.0 / (red_chan.red_gain + red_chan.green_gain + red_chan.blue_gain); + green_norm = 1.0 / (green_chan.red_gain + green_chan.green_gain + green_chan.blue_gain); + blue_norm = 1.0 / (blue_chan.red_gain + blue_chan.green_gain + blue_chan.blue_gain); + + red_norm *= coloring.red() / level; + green_norm *= coloring.green() / level; + blue_norm *= coloring.blue() / level; + + // Perform a red color pixels detection in selection image and create a correction mask using an alpha channel. + + if (!selection.sixteenBit()) // 8 bits image. + { + uchar* ptr = selection.bits(); + uchar* mptr = mask.bits(); + uchar r, g, b, r1, g1, b1; + + for (uint i = 0 ; i < selection.width() * selection.height() ; i++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + mptr[3] = 255; + + if (r >= ( redThreshold * g)) + { + r1 = TQMIN(255, (int)(red_norm * (red_chan.red_gain * r + + red_chan.green_gain * g + + red_chan.blue_gain * b))); + + g1 = TQMIN(255, (int)(green_norm * (green_chan.red_gain * r + + green_chan.green_gain * g + + green_chan.blue_gain * b))); + + b1 = TQMIN(255, (int)(blue_norm * (blue_chan.red_gain * r + + blue_chan.green_gain * g + + blue_chan.blue_gain * b))); + + mptr[0] = b1; + mptr[1] = g1; + mptr[2] = r1; + mptr[3] = TQMIN( (int)((r-g) / 150.0 * 255.0), 255); + } + + ptr += 4; + mptr+= 4; + } + } + else // 16 bits image. + { + unsigned short* ptr = (unsigned short*)selection.bits(); + unsigned short* mptr = (unsigned short*)mask.bits(); + unsigned short r, g, b, r1, g1, b1; + + for (uint i = 0 ; i < selection.width() * selection.height() ; i++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + mptr[3] = 65535; + + if (r >= ( redThreshold * g)) + { + r1 = TQMIN(65535, (int)(red_norm * (red_chan.red_gain * r + + red_chan.green_gain * g + + red_chan.blue_gain * b))); + + g1 = TQMIN(65535, (int)(green_norm * (green_chan.red_gain * r + + green_chan.green_gain * g + + green_chan.blue_gain * b))); + + b1 = TQMIN(65535, (int)(blue_norm * (blue_chan.red_gain * r + + blue_chan.green_gain * g + + blue_chan.blue_gain * b))); + + mptr[0] = b1; + mptr[1] = g1; + mptr[2] = r1; + mptr[3] = TQMIN( (int)((r-g) / 38400.0 * 65535.0), 65535);; + } + + ptr += 4; + mptr+= 4; + } + } + + // Now, we will blur only the transparency pixels from the mask. + + Digikam::DImg mask2 = mask.copy(); + Digikam::DImgImageFilters filter; + filter.gaussianBlurImage(mask2.bits(), mask2.width(), mask2.height(), + mask2.sixteenBit(), m_smoothLevel->value()); + + if (!selection.sixteenBit()) // 8 bits image. + { + uchar* mptr = mask.bits(); + uchar* mptr2 = mask2.bits(); + + for (uint i = 0 ; i < mask2.width() * mask2.height() ; i++) + { + if (mptr2[3] < 255) + { + mptr[0] = mptr2[0]; + mptr[1] = mptr2[1]; + mptr[2] = mptr2[2]; + mptr[3] = mptr2[3]; + } + + mptr += 4; + mptr2+= 4; + } + } + else // 16 bits image. + { + unsigned short* mptr = (unsigned short*)mask.bits(); + unsigned short* mptr2 = (unsigned short*)mask2.bits(); + + for (uint i = 0 ; i < mask2.width() * mask2.height() ; i++) + { + if (mptr2[3] < 65535) + { + mptr[0] = mptr2[0]; + mptr[1] = mptr2[1]; + mptr[2] = mptr2[2]; + mptr[3] = mptr2[3]; + } + + mptr += 4; + mptr2+= 4; + } + } + + // - Perform pixels blending using alpha channel between the mask and the selection. + + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffSrcOver); + + // NOTE: 'mask' is the Source image, 'selection' is the Destination image. + + selection.bitBlendImage(composer, &mask, + 0, 0, mask.width(), mask.height(), + 0, 0); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/imageeffect_redeye.h b/src/imageplugins/coreplugin/imageeffect_redeye.h new file mode 100644 index 00000000..79b517ee --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_redeye.h @@ -0,0 +1,153 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-06 + * Description : Red eyes correction tool for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef IMAGEEFFECT_REDEYE_H +#define IMAGEEFFECT_REDEYE_H + +// KDE includes. + +#include + +// Digikam include. + +#include "imagedlgbase.h" + +class TQLabel; +class TQComboBox; +class TQHButtonGroup; + +class KHSSelector; +class KValueSelector; +class KIntNumInput; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +} + +namespace DigikamImagesPluginCore +{ + +class RedEyePassivePopup : public KPassivePopup +{ +public: + + RedEyePassivePopup(TQWidget* parent) + : KPassivePopup(parent), m_parent(parent) + { + } + +protected: + + virtual void positionSelf() + { + move(m_parent->x() + 30, m_parent->y() + 30); + } + +private: + + TQWidget* m_parent; +}; + +// ---------------------------------------------------------------- + +class ImageEffect_RedEye : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_RedEye(TQWidget *parent); + ~ImageEffect_RedEye(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotHSChanged(int h, int s); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + void redEyeFilter(Digikam::DImg& selection); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum RedThresold + { + Mild=0, + Aggressive + }; + + uchar *m_destinationPreviewData; + + TQLabel *m_thresholdLabel; + TQLabel *m_smoothLabel; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KIntNumInput *m_tintLevel; + KIntNumInput *m_redThreshold; + KIntNumInput *m_smoothLevel; + + KHSSelector *m_HSSelector; + KValueSelector *m_VSelector; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_REDEYE_H */ diff --git a/src/imageplugins/coreplugin/imageeffect_rgb.cpp b/src/imageplugins/coreplugin/imageeffect_rgb.cpp new file mode 100644 index 00000000..fcff9c41 --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_rgb.cpp @@ -0,0 +1,419 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-11 + * Description : digiKam image editor Color Balance tool. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imagewidget.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "colormodifier.h" +#include "dimg.h" + +// Local includes. + +#include "imageeffect_rgb.h" +#include "imageeffect_rgb.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_RGB::ImageEffect_RGB(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Color Balance"), "colorbalance", false) +{ + m_destinationPreviewData = 0L; + setHelp("colorbalancetool.anchor", "digikam"); + + m_previewWidget = new Digikam::ImageWidget("colorbalance Tool Dialog", plainPage(), + i18n("

    Here you can see the image " + "color-balance adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 7, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + TQLabel *labelLeft = new TQLabel(i18n("Cyan"), gboxSettings); + labelLeft->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_rSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, gboxSettings, "m_rSlider"); + m_rSlider->setTickmarks(TQSlider::Below); + m_rSlider->setTickInterval(20); + TQWhatsThis::add( m_rSlider, i18n("

    Set here the cyan/red color adjustment of the image.")); + TQLabel *labelRight = new TQLabel(i18n("Red"), gboxSettings); + labelRight->setAlignment ( TQt::AlignLeft | TQt::AlignVCenter ); + m_rInput = new TQSpinBox(-100, 100, 1, gboxSettings, "m_rInput"); + + gridSettings->addMultiCellWidget(labelLeft, 3, 3, 0, 0); + gridSettings->addMultiCellWidget(m_rSlider, 3, 3, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 3, 3, 2, 2); + gridSettings->addMultiCellWidget(m_rInput, 3, 3, 3, 3); + + // ------------------------------------------------------------- + + labelLeft = new TQLabel(i18n("Magenta"), gboxSettings); + labelLeft->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_gSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, gboxSettings, "m_gSlider"); + m_gSlider->setTickmarks(TQSlider::Below); + m_gSlider->setTickInterval(20); + TQWhatsThis::add( m_gSlider, i18n("

    Set here the magenta/green color adjustment of the image.")); + labelRight = new TQLabel(i18n("Green"), gboxSettings); + labelRight->setAlignment ( TQt::AlignLeft | TQt::AlignVCenter ); + m_gInput = new TQSpinBox(-100, 100, 1, gboxSettings, "m_gInput"); + + gridSettings->addMultiCellWidget(labelLeft, 4, 4, 0, 0); + gridSettings->addMultiCellWidget(m_gSlider, 4, 4, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 4, 4, 2, 2); + gridSettings->addMultiCellWidget(m_gInput, 4, 4, 3, 3); + + // ------------------------------------------------------------- + + labelLeft = new TQLabel(i18n("Yellow"), gboxSettings); + labelLeft->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_bSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, gboxSettings, "m_bSlider"); + m_bSlider->setTickmarks(TQSlider::Below); + m_bSlider->setTickInterval(20); + TQWhatsThis::add( m_bSlider, i18n("

    Set here the yellow/blue color adjustment of the image.")); + labelRight = new TQLabel(i18n("Blue"), gboxSettings); + labelRight->setAlignment ( TQt::AlignLeft | TQt::AlignVCenter ); + m_bInput = new TQSpinBox(-100, 100, 1, gboxSettings, "m_bInput"); + + gridSettings->addMultiCellWidget(labelLeft, 5, 5, 0, 0); + gridSettings->addMultiCellWidget(m_bSlider, 5, 5, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 5, 5, 2, 2); + gridSettings->addMultiCellWidget(m_bInput, 5, 5, 3, 3); + + m_rInput->setValue(0); + m_gInput->setValue(0); + m_bInput->setValue(0); + + gridSettings->setRowStretch(6, 10); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_rSlider, TQ_SIGNAL(valueChanged(int)), + m_rInput, TQ_SLOT(setValue(int))); + connect(m_rInput, TQ_SIGNAL(valueChanged (int)), + m_rSlider, TQ_SLOT(setValue(int))); + connect(m_rInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gSlider, TQ_SIGNAL(valueChanged(int)), + m_gInput, TQ_SLOT(setValue(int))); + connect(m_gInput, TQ_SIGNAL(valueChanged (int)), + m_gSlider, TQ_SLOT(setValue(int))); + connect(m_gInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_bSlider, TQ_SIGNAL(valueChanged(int)), + m_bInput, TQ_SLOT(setValue(int))); + connect(m_bInput, TQ_SIGNAL(valueChanged (int)), + m_bSlider, TQ_SLOT(setValue(int))); + connect(m_bInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + enableButtonOK( false ); +} + +ImageEffect_RGB::~ImageEffect_RGB() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; + delete m_previewWidget; +} + +void ImageEffect_RGB::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_RGB::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_RGB::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_RGB::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colorbalance Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + int r = config->readNumEntry("RedAjustment", 0); + int g = config->readNumEntry("GreenAjustment", 0); + int b = config->readNumEntry("BlueAjustment", 0); + adjustSliders(r, g, b); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_RGB::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colorbalance Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("RedAjustment", m_rSlider->value()); + config->writeEntry("GreenAjustment", m_gInput->value()); + config->writeEntry("BlueAjustment", m_bInput->value()); + config->sync(); +} + +void ImageEffect_RGB::resetValues() +{ + adjustSliders(0, 0, 0); +} + +void ImageEffect_RGB::adjustSliders(int r, int g, int b) +{ + m_rSlider->blockSignals(true); + m_gSlider->blockSignals(true); + m_bSlider->blockSignals(true); + m_rInput->blockSignals(true); + m_gInput->blockSignals(true); + m_bInput->blockSignals(true); + + m_rSlider->setValue(r); + m_gSlider->setValue(g); + m_bSlider->setValue(b); + m_rInput->setValue(r); + m_gInput->setValue(g); + m_bInput->setValue(b); + + m_rSlider->blockSignals(false); + m_gSlider->blockSignals(false); + m_bSlider->blockSignals(false); + m_rInput->blockSignals(false); + m_gInput->blockSignals(false); + m_bInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_RGB::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + enableButtonOK(m_rInput->value() != 0 || + m_gInput->value() != 0 || + m_bInput->value() != 0); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool alpha = iface->previewHasAlpha(); + bool sixteenBit = iface->previewSixteenBit(); + + double r = ((double)m_rInput->value() + 100.0)/100.0; + double g = ((double)m_gInput->value() + 100.0)/100.0; + double b = ((double)m_bInput->value() + 100.0)/100.0; + double a = 1.0; + + Digikam::DImg preview(w, h, sixteenBit, alpha, m_destinationPreviewData); + Digikam::ColorModifier cmod; + cmod.applyColorModifier(preview, r, g, b, a); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sixteenBit, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void ImageEffect_RGB::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double r = ((double)m_rInput->value() + 100.0)/100.0; + double g = ((double)m_gInput->value() + 100.0)/100.0; + double b = ((double)m_bInput->value() + 100.0)/100.0; + double a = 1.0; + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool alpha = iface->originalHasAlpha(); + bool sixteenBit = iface->originalSixteenBit(); + Digikam::DImg original(w, h, sixteenBit, alpha, data); + delete [] data; + + Digikam::ColorModifier cmod; + cmod.applyColorModifier(original, r, g, b, a); + + iface->putOriginalImage(i18n("Color Balance"), original.bits()); + kapp->restoreOverrideCursor(); + accept(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/imageeffect_rgb.h b/src/imageplugins/coreplugin/imageeffect_rgb.h new file mode 100644 index 00000000..9f5db52c --- /dev/null +++ b/src/imageplugins/coreplugin/imageeffect_rgb.h @@ -0,0 +1,113 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-11 + * Description : digiKam image editor Color Balance tool. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_RGB_H +#define IMAGEEFFECT_RGB_H + +// Digikam include. + +#include "imagedlgbase.h" + +class TQComboBox; +class TQHButtonGroup; + +class TQSpinBox; +class TQSlider; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_RGB : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_RGB(TQWidget *parent); + ~ImageEffect_RGB(); + +private: + + void writeUserSettings(); + void readUserSettings(); + void resetValues(); + void adjustSliders(int r, int g, int b); + void finalRendering(); + +private slots: + + void slotEffect(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQSpinBox *m_rInput; + TQSpinBox *m_gInput; + TQSpinBox *m_bInput; + + TQSlider *m_rSlider; + TQSlider *m_gSlider; + TQSlider *m_bSlider; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_RGB_H */ diff --git a/src/imageplugins/coreplugin/imageplugin_core.cpp b/src/imageplugins/coreplugin/imageplugin_core.cpp new file mode 100644 index 00000000..22c63b34 --- /dev/null +++ b/src/imageplugins/coreplugin/imageplugin_core.cpp @@ -0,0 +1,295 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : digiKam image editor plugin core + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "imageiface.h" +#include "rgbtool.h" +#include "hsltool.h" +#include "bcgtool.h" +#include "bwsepiatool.h" +#include "redeyetool.h" +#include "blurtool.h" +#include "sharpentool.h" +#include "ratiocroptool.h" +#include "autocorrectiontool.h" +#include "iccprooftool.h" +#include "imageplugin_core.h" +#include "imageplugin_core.moc" + +using namespace DigikamImagesPluginCore; +using namespace Digikam; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_core, + KGenericFactory("digikam")); + +ImagePlugin_Core::ImagePlugin_Core(TQObject *parent, const char*, const TQStringList&) + : ImagePlugin(parent, "ImagePlugin_Core") +{ + //------------------------------- + // Fix and Colors menu actions + + m_blurAction = new TDEAction(i18n("Blur..."), "blurimage", 0, + this, TQ_SLOT(slotBlur()), + actionCollection(), "implugcore_blur"); + + m_sharpenAction = new TDEAction(i18n("Sharpen..."), "sharpenimage", 0, + this, TQ_SLOT(slotSharpen()), + actionCollection(), "implugcore_sharpen"); + + m_redeyeAction = new TDEAction(i18n("Red Eye..."), "redeyes", 0, + this, TQ_SLOT(slotRedEye()), + actionCollection(), "implugcore_redeye"); + m_redeyeAction->setWhatsThis( i18n( "This filter can be used to correct red eyes in a photo. " + "Select a region including the eyes to use this option.") ); + + m_BCGAction = new TDEAction(i18n("Brightness/Contrast/Gamma..."), "contrast", 0, + this, TQ_SLOT(slotBCG()), + actionCollection(), "implugcore_bcg"); + + m_HSLAction = new TDEAction(i18n("Hue/Saturation/Lightness..."), "adjusthsl", + CTRL+Key_U, // NOTE: Photoshop 7 use CTRL+U. + this, TQ_SLOT(slotHSL()), + actionCollection(), "implugcore_hsl"); + + m_RGBAction = new TDEAction(i18n("Color Balance..."), "adjustrgb", + CTRL+Key_B, // NOTE: Photoshop 7 use CTRL+B. + this, TQ_SLOT(slotRGB()), + actionCollection(), "implugcore_rgb"); + + m_autoCorrectionAction = new TDEAction(i18n("Auto-Correction..."), "autocorrection", + CTRL+SHIFT+Key_B, // NOTE: Photoshop 7 use CTRL+SHIFT+B with 'Auto-Color' option. + this, TQ_SLOT(slotAutoCorrection()), + actionCollection(), "implugcore_autocorrection"); + + m_invertAction = new TDEAction(i18n("Invert"), "invertimage", + CTRL+Key_I, // NOTE: Photoshop 7 use CTRL+I. + this, TQ_SLOT(slotInvert()), + actionCollection(), "implugcore_invert"); + + m_convertTo8Bits = new TDEAction(i18n("8 bits"), "depth16to8", 0, + this, TQ_SLOT(slotConvertTo8Bits()), + actionCollection(), "implugcore_convertto8bits"); + + m_convertTo16Bits = new TDEAction(i18n("16 bits"), "depth8to16", 0, + this, TQ_SLOT(slotConvertTo16Bits()), + actionCollection(), "implugcore_convertto16bits"); + + m_colorManagementAction = new TDEAction(i18n("Color Management..."), "colormanagement", 0, + this, TQ_SLOT(slotColorManagement()), + actionCollection(), "implugcore_colormanagement"); + //------------------------------- + // Filters menu actions. + + m_BWAction = new TDEAction(i18n("Black && White..."), "bwtonal", 0, + this, TQ_SLOT(slotBW()), + actionCollection(), "implugcore_blackwhite"); + + //------------------------------- + // Transform menu actions. + + m_aspectRatioCropAction = new TDEAction(i18n("Aspect Ratio Crop..."), "ratiocrop", 0, + this, TQ_SLOT(slotRatioCrop()), + actionCollection(), "implugcore_ratiocrop"); + + //------------------------------- + // Init. menu actions. + + setXMLFile("digikamimageplugin_core_ui.rc"); + + DDebug() << "ImagePlugin_Core plugin loaded" << endl; +} + +ImagePlugin_Core::~ImagePlugin_Core() +{ +} + +void ImagePlugin_Core::setEnabledSelectionActions(bool) +{ +} + +void ImagePlugin_Core::setEnabledActions(bool enable) +{ + m_redeyeAction->setEnabled(enable); + m_BCGAction->setEnabled(enable); + m_HSLAction->setEnabled(enable); + m_RGBAction->setEnabled(enable); + m_autoCorrectionAction->setEnabled(enable); + m_invertAction->setEnabled(enable); + m_BWAction->setEnabled(enable); + m_aspectRatioCropAction->setEnabled(enable); + m_sharpenAction->setEnabled(enable); + m_blurAction->setEnabled(enable); + m_colorManagementAction->setEnabled(enable); + m_convertTo8Bits->setEnabled(enable); + m_convertTo16Bits->setEnabled(enable); +} + +void ImagePlugin_Core::slotBlur() +{ + BlurTool *tool = new BlurTool(this); + loadTool(tool); +} + +void ImagePlugin_Core::slotSharpen() +{ + SharpenTool *tool = new SharpenTool(this); + loadTool(tool); +} + +void ImagePlugin_Core::slotBCG() +{ + BCGTool *bcg = new BCGTool(this); + loadTool(bcg); +} + +void ImagePlugin_Core::slotRGB() +{ + RGBTool *rgb = new RGBTool(this); + loadTool(rgb); +} + +void ImagePlugin_Core::slotHSL() +{ + HSLTool *hsl = new HSLTool(this); + loadTool(hsl); +} + +void ImagePlugin_Core::slotAutoCorrection() +{ + AutoCorrectionTool *autocorrection = new AutoCorrectionTool(this); + loadTool(autocorrection); +} + +void ImagePlugin_Core::slotInvert() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + ImageIface iface(0, 0); + + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + + DImgImageFilters filter; + filter.invertImage(data, w, h, sixteenBit); + iface.putOriginalImage(i18n("Invert"), data); + delete [] data; + + kapp->restoreOverrideCursor(); +} + +void ImagePlugin_Core::slotBW() +{ + BWSepiaTool *bwsepia = new BWSepiaTool(this); + loadTool(bwsepia); +} + +void ImagePlugin_Core::slotRedEye() +{ + ImageIface iface(0, 0); + + if (!iface.selectedWidth() || !iface.selectedHeight()) + { + RedEyePassivePopup* popup = new RedEyePassivePopup(kapp->activeWindow()); + popup->setView(i18n("Red-Eye Correction Tool"), + i18n("You need to select a region including the eyes to use " + "the red-eye correction tool")); + popup->setAutoDelete(true); + popup->setTimeout(2500); + popup->show(); + return; + } + + RedEyeTool *redeye = new RedEyeTool(this); + loadTool(redeye); +} + +void ImagePlugin_Core::slotColorManagement() +{ + ICCProofTool *tool = new ICCProofTool(this); + loadTool(tool); +} + +void ImagePlugin_Core::slotRatioCrop() +{ + RatioCropTool *ratiocrop = new RatioCropTool(this); + loadTool(ratiocrop); +} + +void ImagePlugin_Core::slotConvertTo8Bits() +{ + ImageIface iface(0, 0); + + if (!iface.originalSixteenBit()) + { + KMessageBox::error(kapp->activeWindow(), i18n("This image is already using a depth of 8 bits / color / pixel.")); + return; + } + else + { + if (KMessageBox::warningContinueCancel( + kapp->activeWindow(), + i18n("Performing this operation will reduce image color quality. " + "Do you want to continue?"), TQString(), + KStdGuiItem::cont(), + TQString("ImagePluginCore16To8Bits")) == KMessageBox::Cancel) + return; + } + + kapp->setOverrideCursor( KCursor::waitCursor() ); + iface.convertOriginalColorDepth(32); + kapp->restoreOverrideCursor(); +} + +void ImagePlugin_Core::slotConvertTo16Bits() +{ + ImageIface iface(0, 0); + + if (iface.originalSixteenBit()) + { + KMessageBox::error(kapp->activeWindow(), i18n("This image is already using a depth of 16 bits / color / pixel.")); + return; + } + + kapp->setOverrideCursor( KCursor::waitCursor() ); + iface.convertOriginalColorDepth(64); + kapp->restoreOverrideCursor(); +} diff --git a/src/imageplugins/coreplugin/imageplugin_core.h b/src/imageplugins/coreplugin/imageplugin_core.h new file mode 100644 index 00000000..41168d26 --- /dev/null +++ b/src/imageplugins/coreplugin/imageplugin_core.h @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-04 + * Description : digiKam image editor plugin core + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_CORE_H +#define IMAGEPLUGIN_CORE_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Core : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Core(TQObject *parent, const char* name, const TQStringList &args); + ~ImagePlugin_Core(); + + void setEnabledSelectionActions(bool enable); + void setEnabledActions(bool enable); + +private slots: + + void slotBlur(); + void slotSharpen(); + void slotBCG(); + void slotRGB(); + void slotHSL(); + void slotAutoCorrection(); + void slotInvert(); + + void slotBW(); + + void slotRedEye(); + void slotRatioCrop(); + + void slotConvertTo8Bits(); + void slotConvertTo16Bits(); + + void slotColorManagement(); + +private: + + TDEAction *m_redeyeAction; + TDEAction *m_BCGAction; + TDEAction *m_HSLAction; + TDEAction *m_RGBAction; + TDEAction *m_autoCorrectionAction; + TDEAction *m_invertAction; + TDEAction *m_BWAction; + TDEAction *m_aspectRatioCropAction; + TDEAction *m_sharpenAction; + TDEAction *m_blurAction; + TDEAction *m_colorManagementAction; + TDEAction *m_convertTo8Bits; + TDEAction *m_convertTo16Bits; +}; + +#endif /* IMAGEPLUGIN_CORE_H */ diff --git a/src/imageplugins/coreplugin/ratiocrop/Makefile.am b/src/imageplugins/coreplugin/ratiocrop/Makefile.am new file mode 100644 index 00000000..47ec5b58 --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/Makefile.am @@ -0,0 +1,26 @@ +noinst_LTLIBRARIES = libratiocrop.la +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +libratiocrop_la_SOURCES = ratiocroptool.cpp imageselectionwidget.cpp + +libratiocrop_la_LDFLAGS = $(all_libraries) + +noinst_HEADERS = ratiocroptool.h imageselectionwidget.h + diff --git a/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.cpp b/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.cpp new file mode 100644 index 00000000..64bc7a8e --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.cpp @@ -0,0 +1,799 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : digiKam image editor Ratio Crop tool + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" +#include "imageselectionwidget.h" + +// Local includes. + +#include "imageeffect_ratiocrop.h" +#include "imageeffect_ratiocrop.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_RatioCrop::ImageEffect_RatioCrop(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Aspect Ratio Crop & Composition Guide"), + "aspectratiocrop", false) +{ + setHelp("ratiocroptool.anchor", "digikam"); + setButtonWhatsThis ( User1, i18n("

    Set selection area to the maximum size according " + "to the current ratio.") ); + setButtonText(User1, i18n("&Max. Aspect")); + showButton(User1, true); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(plainPage()); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* l = new TQVBoxLayout(frame, 5, 0); + m_imageSelectionWidget = new ImageSelectionWidget(480, 320, frame); + l->addWidget(m_imageSelectionWidget); + TQWhatsThis::add( m_imageSelectionWidget, i18n("

    Here you can see the aspect ratio selection preview " + "used for cropping. You can use the mouse to move and " + "resize the crop area. " + "Press and hold the CTRL key to move the opposite corner too. " + "Press and hold the SHIFT key to move the closest corner to the " + "mouse pointer.")); + setPreviewAreaWidget(frame); + + m_originalIsLandscape = m_imageSelectionWidget->getOriginalImageWidth() > + m_imageSelectionWidget->getOriginalImageHeight(); + + // ------------------------------------------------------------- + + TQWidget *gbox2 = new TQWidget(plainPage()); + TQGridLayout *gridBox2 = new TQGridLayout( gbox2, 2, 0); + + TQFrame *cropSelection = new TQFrame( gbox2 ); + cropSelection->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQGridLayout* grid = new TQGridLayout( cropSelection, 6, 4, spacingHint()); + + TQLabel *label = new TQLabel(i18n("Aspect ratio:"), cropSelection); + m_ratioCB = new TQComboBox( false, cropSelection ); + setRatioCBText(ImageSelectionWidget::Landscape); + TQWhatsThis::add( m_ratioCB, i18n("

    Select your constrained aspect ratio for cropping. " + "Aspect Ratio Crop tool uses a relative ratio. That means it " + "is the same if you use centimeters or inches and it doesn't " + "specify the physical size.

    " + "You can see below a correspondence list of traditional photographic " + "paper sizes and aspect ratio crop:

    " + "2:3: 10x15cm, 20x30cm, 30x45cm, 4x6\", 8x12\", " + "12x18\", 16x24\", 20x30\"

    " + "3:4: 6x8cm, 15x20cm, 18x24cm, 30x40cm, 3.75x5\", 4.5x6\", " + "6x8\", 7.5x10\", 9x12\"

    " + "4:5: 20x25cm, 40x50cm, 8x10\", 16x20\"

    " + "5:7: 15x21cm, 30x42cm, 5x7\"

    " + "7:10: 21x30cm, 42x60cm, 3.5x5\"

    " + "The Golden Ratio is 1:1.618. A composition following this rule " + "is considered visually harmonious but can be unadapted to print on " + "standard photographic paper.")); + + m_preciseCrop = new TQCheckBox(i18n("Exact"), cropSelection); + TQWhatsThis::add( m_preciseCrop, i18n("

    Enable this option to force exact aspect ratio crop.")); + + m_orientLabel = new TQLabel(i18n("Orientation:"), cropSelection); + m_orientCB = new TQComboBox( false, cropSelection ); + m_orientCB->insertItem( i18n("Landscape") ); + m_orientCB->insertItem( i18n("Portrait") ); + TQWhatsThis::add( m_orientCB, i18n("

    Select constrained aspect ratio orientation.")); + + m_autoOrientation = new TQCheckBox(i18n("Auto"), cropSelection); + TQWhatsThis::add( m_autoOrientation, i18n("

    Enable this option to automatically set the orientation.")); + + grid->addMultiCellWidget(label, 0, 0, 0, 0); + grid->addMultiCellWidget(m_ratioCB, 0, 0, 1, 3); + grid->addMultiCellWidget(m_preciseCrop, 0, 0, 4, 4); + grid->addMultiCellWidget(m_orientLabel, 2, 2, 0, 0); + grid->addMultiCellWidget(m_orientCB, 2, 2, 1, 3); + grid->addMultiCellWidget(m_autoOrientation, 2, 2, 4, 4); + + // ------------------------------------------------------------- + + m_customLabel1 = new TQLabel(i18n("Custom ratio:"), cropSelection); + m_customLabel1->setAlignment(AlignLeft|AlignVCenter); + m_customRatioNInput = new KIntSpinBox(1, 10000, 1, 1, 10, cropSelection); + TQWhatsThis::add( m_customRatioNInput, i18n("

    Set here the desired custom aspect numerator value.")); + m_customLabel2 = new TQLabel(" : ", cropSelection); + m_customLabel2->setAlignment(AlignCenter|AlignVCenter); + m_customRatioDInput = new KIntSpinBox(1, 10000, 1, 1, 10, cropSelection); + TQWhatsThis::add( m_customRatioDInput, i18n("

    Set here the desired custom aspect denominator value.")); + + grid->addMultiCellWidget(m_customLabel1, 1, 1, 0, 0); + grid->addMultiCellWidget(m_customRatioNInput, 1, 1, 1, 1); + grid->addMultiCellWidget(m_customLabel2, 1, 1, 2, 2); + grid->addMultiCellWidget(m_customRatioDInput, 1, 1, 3, 3); + + // ------------------------------------------------------------- + + m_xInput = new KIntNumInput(cropSelection); + TQWhatsThis::add( m_xInput, i18n("

    Set here the top left selection corner position for cropping.")); + m_xInput->setLabel(i18n("X:"), AlignLeft|AlignVCenter); + m_xInput->setRange(0, m_imageSelectionWidget->getOriginalImageWidth(), 1, true); + + m_widthInput = new KIntNumInput(cropSelection); + m_widthInput->setLabel(i18n("Width:"), AlignLeft|AlignVCenter); + TQWhatsThis::add( m_widthInput, i18n("

    Set here the width selection for cropping.")); + m_widthInput->setRange(m_imageSelectionWidget->getMinWidthRange(), + m_imageSelectionWidget->getMaxWidthRange(), + m_imageSelectionWidget->getWidthStep(), true); + + m_centerWidth = new TQPushButton(cropSelection); + TDEGlobal::dirs()->addResourceType("centerwidth", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("centerwidth", "centerwidth.png"); + m_centerWidth->setPixmap( TQPixmap( directory + "centerwidth.png" ) ); + TQWhatsThis::add( m_centerWidth, i18n("

    Set width position to center.")); + + grid->addMultiCellWidget(m_xInput, 3, 3, 0, 3); + grid->addMultiCellWidget(m_widthInput, 4, 4, 0, 3); + grid->addMultiCellWidget(m_centerWidth, 3, 3, 4, 4); + + // ------------------------------------------------------------- + + m_yInput = new KIntNumInput(cropSelection); + m_yInput->setLabel(i18n("Y:"), AlignLeft|AlignVCenter); + TQWhatsThis::add( m_yInput, i18n("

    Set here the top left selection corner position for cropping.")); + m_yInput->setRange(0, m_imageSelectionWidget->getOriginalImageHeight(), 1, true); + + m_heightInput = new KIntNumInput(cropSelection); + m_heightInput->setLabel(i18n("Height:"), AlignLeft|AlignVCenter); + TQWhatsThis::add( m_heightInput, i18n("

    Set here the height selection for cropping.")); + m_heightInput->setRange(m_imageSelectionWidget->getMinHeightRange(), + m_imageSelectionWidget->getMaxHeightRange(), + m_imageSelectionWidget->getHeightStep(), true); + + m_centerHeight = new TQPushButton(cropSelection); + TDEGlobal::dirs()->addResourceType("centerheight", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("centerheight", "centerheight.png"); + m_centerHeight->setPixmap( TQPixmap( directory + "centerheight.png" ) ); + TQWhatsThis::add( m_centerHeight, i18n("

    Set height position to center.")); + + grid->addMultiCellWidget(m_yInput, 5, 5, 0, 3); + grid->addMultiCellWidget(m_heightInput, 6, 6, 0, 3); + grid->addMultiCellWidget(m_centerHeight, 5, 5, 4, 4); + + gridBox2->addMultiCellWidget(cropSelection, 0, 0, 0, 0); + + // ------------------------------------------------------------- + + TQFrame* compositionGuide = new TQFrame( gbox2 ); + TQGridLayout* grid2 = new TQGridLayout( compositionGuide, 7, 2, spacingHint()); + compositionGuide->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + + TQLabel *labelGuideLines = new TQLabel(i18n("Composition guide:"), compositionGuide); + m_guideLinesCB = new TQComboBox( false, compositionGuide ); + m_guideLinesCB->insertItem( i18n("Rules of Thirds") ); + m_guideLinesCB->insertItem( i18n("Harmonious Triangles") ); + m_guideLinesCB->insertItem( i18n("Golden Mean") ); + m_guideLinesCB->insertItem( i18n("None") ); + m_guideLinesCB->setCurrentText( i18n("None") ); + TQWhatsThis::add( m_guideLinesCB, i18n("

    With this option, you can display guide lines " + "which help you to compose your photograph.")); + + m_goldenSectionBox = new TQCheckBox(i18n("Golden sections"), compositionGuide); + TQWhatsThis::add( m_goldenSectionBox, i18n("

    Enable this option to show golden sections.")); + + m_goldenSpiralSectionBox = new TQCheckBox(i18n("Golden spiral sections"), compositionGuide); + TQWhatsThis::add( m_goldenSpiralSectionBox, i18n("

    Enable this option to show golden spiral sections.")); + + m_goldenSpiralBox = new TQCheckBox(i18n("Golden spiral"), compositionGuide); + TQWhatsThis::add( m_goldenSpiralBox, i18n("

    Enable this option to show golden spiral guide.")); + + m_goldenTriangleBox = new TQCheckBox(i18n("Golden triangles"), compositionGuide); + TQWhatsThis::add( m_goldenTriangleBox, i18n("

    Enable this option to show golden triangles.")); + + m_flipHorBox = new TQCheckBox(i18n("Flip horizontally"), compositionGuide); + TQWhatsThis::add( m_flipHorBox, i18n("

    Enable this option to flip horizontally guidelines.")); + + m_flipVerBox = new TQCheckBox(i18n("Flip vertically"), compositionGuide); + TQWhatsThis::add( m_flipVerBox, i18n("

    Enable this option to flip vertically guidelines.")); + + m_colorGuideLabel = new TQLabel(i18n("Color and width:"), compositionGuide); + m_guideColorBt = new KColorButton( TQColor( 250, 250, 255 ), compositionGuide ); + m_guideSize = new TQSpinBox( 1, 5, 1, compositionGuide); + TQWhatsThis::add( m_guideColorBt, i18n("

    Set here the color used to draw composition guides.")); + TQWhatsThis::add( m_guideSize, i18n("

    Set here the width in pixels used to draw composition guides.")); + + grid2->addMultiCellWidget(labelGuideLines, 0, 0, 0, 0); + grid2->addMultiCellWidget(m_guideLinesCB, 0, 0, 1, 2); + grid2->addMultiCellWidget(m_goldenSectionBox, 1, 1, 0, 2); + grid2->addMultiCellWidget(m_goldenSpiralSectionBox, 2, 2, 0, 2); + grid2->addMultiCellWidget(m_goldenSpiralBox, 3, 3, 0, 2); + grid2->addMultiCellWidget(m_goldenTriangleBox, 4, 4, 0, 2); + grid2->addMultiCellWidget(m_flipHorBox, 5, 5, 0, 2); + grid2->addMultiCellWidget(m_flipVerBox, 6, 6, 0, 2); + grid2->addMultiCellWidget(m_colorGuideLabel, 7, 7, 0, 0); + grid2->addMultiCellWidget(m_guideColorBt, 7, 7, 1, 1); + grid2->addMultiCellWidget(m_guideSize, 7, 7, 2, 2); + + gridBox2->addMultiCellWidget(compositionGuide, 1, 1, 0, 0); + gridBox2->setRowStretch(2, 10); + + setUserAreaWidget(gbox2); + + // ------------------------------------------------------------- + + connect(m_ratioCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotRatioChanged(int))); + + connect(m_preciseCrop, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotPreciseCropChanged(bool))); + + connect(m_orientCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotOrientChanged(int))); + + connect(m_autoOrientation, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotAutoOrientChanged(bool))); + + connect(m_xInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotXChanged(int))); + + connect(m_yInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotYChanged(int))); + + connect(m_customRatioNInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotCustomNRatioChanged(int))); + + connect(m_customRatioDInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotCustomDRatioChanged(int))); + + connect(m_guideLinesCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotGuideTypeChanged(int))); + + connect(m_goldenSectionBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenSpiralSectionBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenSpiralBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenTriangleBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_flipHorBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_flipVerBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_guideColorBt, TQ_SIGNAL(changed(const TQColor &)), + m_imageSelectionWidget, TQ_SLOT(slotChangeGuideColor(const TQColor &))); + + connect(m_guideSize, TQ_SIGNAL(valueChanged(int)), + m_imageSelectionWidget, TQ_SLOT(slotChangeGuideSize(int))); + + connect(m_widthInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotWidthChanged(int))); + + connect(m_heightInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotHeightChanged(int))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionChanged(TQRect)), + this, TQ_SLOT(slotSelectionChanged(TQRect))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionMoved(TQRect)), + this, TQ_SLOT(slotSelectionChanged(TQRect))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionOrientationChanged(int)), + this, TQ_SLOT(slotSelectionOrientationChanged(int))); + + connect(m_centerWidth, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCenterWidth())); + + connect(m_centerHeight, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCenterHeight())); + + // ------------------------------------------------------------- + + // Sets current region selection + slotSelectionChanged(m_imageSelectionWidget->getRegionSelection()); + + readSettings(); +} + +ImageEffect_RatioCrop::~ImageEffect_RatioCrop() +{ +} + +void ImageEffect_RatioCrop::readSettings() +{ + TQColor defaultGuideColor(250, 250, 255); + TDEConfig *config = kapp->config(); + config->setGroup("aspectratiocrop Tool Dialog"); + + // No guide lines per default. + m_guideLinesCB->setCurrentItem( config->readNumEntry("Guide Lines Type", + ImageSelectionWidget::GuideNone) ); + m_goldenSectionBox->setChecked( config->readBoolEntry("Golden Section", true) ); + m_goldenSpiralSectionBox->setChecked( config->readBoolEntry("Golden Spiral Section", false) ); + m_goldenSpiralBox->setChecked( config->readBoolEntry("Golden Spiral", false) ); + m_goldenTriangleBox->setChecked( config->readBoolEntry("Golden Triangle", false) ); + m_flipHorBox->setChecked( config->readBoolEntry("Golden Flip Horizontal", false) ); + m_flipVerBox->setChecked( config->readBoolEntry("Golden Flip Vertical", false) ); + m_guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor)); + m_guideSize->setValue(config->readNumEntry("Guide Width", 1)); + m_imageSelectionWidget->slotGuideLines(m_guideLinesCB->currentItem()); + m_imageSelectionWidget->slotChangeGuideColor(m_guideColorBt->color()); + + m_preciseCrop->setChecked( config->readBoolEntry("Precise Aspect Ratio Crop", false) ); + m_imageSelectionWidget->setPreciseCrop( m_preciseCrop->isChecked() ); + + if (m_originalIsLandscape) + { + m_orientCB->setCurrentItem( config->readNumEntry("Hor.Oriented Aspect Ratio Orientation", + ImageSelectionWidget::Landscape) ); + + m_imageSelectionWidget->setSelectionOrientation(m_orientCB->currentItem()); + + m_customRatioNInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Num", 1) ); + m_customRatioDInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Den", 1) ); + m_ratioCB->setCurrentItem( config->readNumEntry("Hor.Oriented Aspect Ratio", + ImageSelectionWidget::RATIO03X04) ); + + applyRatioChanges(m_ratioCB->currentItem()); + + // Empty selection so it can be moved w/out size constraint + m_widthInput->setValue( 0 ); + m_heightInput->setValue( 0 ); + + m_xInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Xpos", 50) ); + m_yInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Ypos", 50) ); + + m_widthInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Width", 800) ); + m_heightInput->setValue( config->readNumEntry("Hor.Oriented Custom Aspect Ratio Height", 600) ); + } + else + { + m_orientCB->setCurrentItem( config->readNumEntry("Ver.Oriented Aspect Ratio Orientation", + ImageSelectionWidget::Portrait) ); + + m_imageSelectionWidget->setSelectionOrientation(m_orientCB->currentItem()); + + m_customRatioNInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Num", 1) ); + m_customRatioDInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Den", 1) ); + m_ratioCB->setCurrentItem( config->readNumEntry("Ver.Oriented Aspect Ratio", + ImageSelectionWidget::RATIO03X04) ); + + applyRatioChanges(m_ratioCB->currentItem()); + + // Empty selection so it can be moved w/out size constraint + m_widthInput->setValue( 0 ); + m_heightInput->setValue( 0 ); + + m_xInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Xpos", 50) ); + m_yInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Ypos", 50) ); + + m_widthInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Width", 800) ); + m_heightInput->setValue( config->readNumEntry("Ver.Oriented Custom Aspect Ratio Height", 600) ); + } + + m_autoOrientation->setChecked( config->readBoolEntry("Auto Orientation", false) ); + slotAutoOrientChanged( m_autoOrientation->isChecked() ); +} + +void ImageEffect_RatioCrop::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("aspectratiocrop Tool Dialog"); + + if (m_originalIsLandscape) + { + config->writeEntry( "Hor.Oriented Aspect Ratio", m_ratioCB->currentItem() ); + config->writeEntry( "Hor.Oriented Aspect Ratio Orientation", m_orientCB->currentItem() ); + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Num", m_customRatioNInput->value() ); + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Den", m_customRatioDInput->value() ); + + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Xpos", m_xInput->value() ); + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Ypos", m_yInput->value() ); + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Width", m_widthInput->value() ); + config->writeEntry( "Hor.Oriented Custom Aspect Ratio Height", m_heightInput->value() ); + } + else + { + config->writeEntry( "Ver.Oriented Aspect Ratio", m_ratioCB->currentItem() ); + config->writeEntry( "Ver.Oriented Aspect Ratio Orientation", m_orientCB->currentItem() ); + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Num", m_customRatioNInput->value() ); + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Den", m_customRatioDInput->value() ); + + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Xpos", m_xInput->value() ); + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Ypos", m_yInput->value() ); + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Width", m_widthInput->value() ); + config->writeEntry( "Ver.Oriented Custom Aspect Ratio Height", m_heightInput->value() ); + } + + config->writeEntry( "Precise Aspect Ratio Crop", m_preciseCrop->isChecked() ); + config->writeEntry( "Auto Orientation", m_autoOrientation->isChecked() ); + config->writeEntry( "Guide Lines Type", m_guideLinesCB->currentItem() ); + config->writeEntry( "Golden Section", m_goldenSectionBox->isChecked() ); + config->writeEntry( "Golden Spiral Section", m_goldenSpiralSectionBox->isChecked() ); + config->writeEntry( "Golden Spiral", m_goldenSpiralBox->isChecked() ); + config->writeEntry( "Golden Triangle", m_goldenTriangleBox->isChecked() ); + config->writeEntry( "Golden Flip Horizontal", m_flipHorBox->isChecked() ); + config->writeEntry( "Golden Flip Vertical", m_flipVerBox->isChecked() ); + config->writeEntry( "Guide Color", m_guideColorBt->color() ); + config->writeEntry( "Guide Width", m_guideSize->value() ); + config->sync(); +} + +void ImageEffect_RatioCrop::slotDefault() +{ + m_imageSelectionWidget->resetSelection(); +} + +void ImageEffect_RatioCrop::slotUser1() +{ + m_imageSelectionWidget->maxAspectSelection(); +} + +void ImageEffect_RatioCrop::slotCenterWidth() +{ + m_imageSelectionWidget->setCenterSelection(ImageSelectionWidget::CenterWidth); +} + +void ImageEffect_RatioCrop::slotCenterHeight() +{ + m_imageSelectionWidget->setCenterSelection(ImageSelectionWidget::CenterHeight); +} + +void ImageEffect_RatioCrop::slotSelectionChanged(TQRect rect) +{ + m_xInput->blockSignals(true); + m_yInput->blockSignals(true); + m_widthInput->blockSignals(true); + m_heightInput->blockSignals(true); + + m_xInput->setRange(0, m_imageSelectionWidget->getOriginalImageWidth() - rect.width(), 1, true); + m_yInput->setRange(0, m_imageSelectionWidget->getOriginalImageHeight() - rect.height(), 1, true); + m_widthInput->setRange(m_imageSelectionWidget->getMinWidthRange(), + m_imageSelectionWidget->getMaxWidthRange(), + m_imageSelectionWidget->getWidthStep(), true); + m_heightInput->setRange(m_imageSelectionWidget->getMinHeightRange(), + m_imageSelectionWidget->getMaxHeightRange(), + m_imageSelectionWidget->getHeightStep(), true); + + m_xInput->setValue(rect.x()); + m_yInput->setValue(rect.y()); + m_widthInput->setValue(rect.width()); + m_heightInput->setValue(rect.height()); + + enableButtonOK( rect.isValid() ); + m_preciseCrop->setEnabled(m_imageSelectionWidget->preciseCropAvailable()); + + m_xInput->blockSignals(false); + m_yInput->blockSignals(false); + m_widthInput->blockSignals(false); + m_heightInput->blockSignals(false); +} + +void ImageEffect_RatioCrop::setRatioCBText(int orientation) +{ + int item = m_ratioCB->currentItem(); + + m_ratioCB->blockSignals(true); + m_ratioCB->clear(); + m_ratioCB->insertItem( i18n("Custom") ); + m_ratioCB->insertItem( "1:1" ); + if ( orientation == ImageSelectionWidget::Landscape ) + { + m_ratioCB->insertItem( "3:2" ); + m_ratioCB->insertItem( "4:3" ); + m_ratioCB->insertItem( "5:4" ); + m_ratioCB->insertItem( "7:5" ); + m_ratioCB->insertItem( "10:7" ); + } + else + { + m_ratioCB->insertItem( "2:3" ); + m_ratioCB->insertItem( "3:4" ); + m_ratioCB->insertItem( "4:5" ); + m_ratioCB->insertItem( "5:7" ); + m_ratioCB->insertItem( "7:10" ); + } + m_ratioCB->insertItem( i18n("Golden Ratio") ); + m_ratioCB->insertItem( i18n("None") ); + m_ratioCB->setCurrentItem( item ); + m_ratioCB->blockSignals(false); +} + +void ImageEffect_RatioCrop::slotSelectionOrientationChanged(int newOrientation) +{ + // Change text for Aspect ratio ComboBox + + setRatioCBText(newOrientation); + + // Change Orientation ComboBox + + m_orientCB->setCurrentItem(newOrientation); + + // Reverse custom values + + if ( ( m_customRatioNInput->value() < m_customRatioDInput->value() && + newOrientation == ImageSelectionWidget::Landscape ) || + ( m_customRatioNInput->value() > m_customRatioDInput->value() && + newOrientation == ImageSelectionWidget::Portrait ) ) + { + m_customRatioNInput->blockSignals(true); + m_customRatioDInput->blockSignals(true); + + int tmp = m_customRatioNInput->value(); + m_customRatioNInput->setValue( m_customRatioDInput->value() ); + m_customRatioDInput->setValue( tmp ); + + m_customRatioNInput->blockSignals(false); + m_customRatioDInput->blockSignals(false); + } +} + +void ImageEffect_RatioCrop::slotXChanged(int x) +{ + m_imageSelectionWidget->setSelectionX(x); +} + +void ImageEffect_RatioCrop::slotYChanged(int y) +{ + m_imageSelectionWidget->setSelectionY(y); +} + +void ImageEffect_RatioCrop::slotWidthChanged(int w) +{ + m_imageSelectionWidget->setSelectionWidth(w); +} + +void ImageEffect_RatioCrop::slotHeightChanged(int h) +{ + m_imageSelectionWidget->setSelectionHeight(h); +} + +void ImageEffect_RatioCrop::slotPreciseCropChanged(bool a) +{ + m_imageSelectionWidget->setPreciseCrop(a); +} + +void ImageEffect_RatioCrop::slotOrientChanged(int o) +{ + m_imageSelectionWidget->setSelectionOrientation(o); + + // Reset selection area. + slotDefault(); +} + +void ImageEffect_RatioCrop::slotAutoOrientChanged(bool a) +{ + m_orientCB->setEnabled(!a /*|| m_ratioCB->currentItem() == ImageSelectionWidget::RATIONONE*/); + m_imageSelectionWidget->setAutoOrientation(a); +} + +void ImageEffect_RatioCrop::slotRatioChanged(int a) +{ + applyRatioChanges(a); + + // Reset selection area. + slotDefault(); +} + +void ImageEffect_RatioCrop::applyRatioChanges(int a) +{ + m_imageSelectionWidget->setSelectionAspectRatioType(a); + + if ( a == ImageSelectionWidget::RATIOCUSTOM ) + { + m_customLabel1->setEnabled(true); + m_customLabel2->setEnabled(true); + m_customRatioNInput->setEnabled(true); + m_customRatioDInput->setEnabled(true); + m_orientLabel->setEnabled(true); + m_orientCB->setEnabled(! m_autoOrientation->isChecked()); + m_autoOrientation->setEnabled(true); + slotCustomRatioChanged(); + } + else if ( a == ImageSelectionWidget::RATIONONE ) + { + m_orientLabel->setEnabled(false); + m_orientCB->setEnabled(false); + m_autoOrientation->setEnabled(false); + m_customLabel1->setEnabled(false); + m_customLabel2->setEnabled(false); + m_customRatioNInput->setEnabled(false); + m_customRatioDInput->setEnabled(false); + } + else // Pre-config ratio selected. + { + m_orientLabel->setEnabled(true); + m_orientCB->setEnabled(! m_autoOrientation->isChecked()); + m_autoOrientation->setEnabled(true); + m_customLabel1->setEnabled(false); + m_customLabel2->setEnabled(false); + m_customRatioNInput->setEnabled(false); + m_customRatioDInput->setEnabled(false); + } +} + +void ImageEffect_RatioCrop::slotGuideTypeChanged(int t) +{ + if ( t == ImageSelectionWidget::GuideNone ) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(false); + m_flipVerBox->setEnabled(false); + m_colorGuideLabel->setEnabled(false); + m_guideColorBt->setEnabled(false); + m_guideSize->setEnabled(false); + } + else if ( t == ImageSelectionWidget::RulesOfThirds ) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(false); + m_flipVerBox->setEnabled(false); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + else if ( t == ImageSelectionWidget::HarmoniousTriangles ) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(true); + m_flipVerBox->setEnabled(true); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + else + { + m_goldenSectionBox->setEnabled(true); + m_goldenSpiralSectionBox->setEnabled(true); + m_goldenSpiralBox->setEnabled(true); + m_goldenTriangleBox->setEnabled(true); + m_flipHorBox->setEnabled(true); + m_flipVerBox->setEnabled(true); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + + m_imageSelectionWidget->setGoldenGuideTypes(m_goldenSectionBox->isChecked(), + m_goldenSpiralSectionBox->isChecked(), + m_goldenSpiralBox->isChecked(), + m_goldenTriangleBox->isChecked(), + m_flipHorBox->isChecked(), + m_flipVerBox->isChecked()); + m_imageSelectionWidget->slotGuideLines(t); +} + +void ImageEffect_RatioCrop::slotGoldenGuideTypeChanged() +{ + slotGuideTypeChanged(m_guideLinesCB->currentItem()); +} + +void ImageEffect_RatioCrop::slotCustomNRatioChanged(int a) +{ + if ( ! m_autoOrientation->isChecked() ) + { + if ( ( m_orientCB->currentItem() == ImageSelectionWidget::Portrait && + m_customRatioDInput->value() < a ) || + ( m_orientCB->currentItem() == ImageSelectionWidget::Landscape && + m_customRatioDInput->value() > a ) ) + { + m_customRatioDInput->blockSignals(true); + m_customRatioDInput->setValue(a); + m_customRatioDInput->blockSignals(false); + } + } + + slotCustomRatioChanged(); +} + +void ImageEffect_RatioCrop::slotCustomDRatioChanged(int a) +{ + if ( ! m_autoOrientation->isChecked() ) + { + if ( ( m_orientCB->currentItem() == ImageSelectionWidget::Landscape && + m_customRatioNInput->value() < a ) || + ( m_orientCB->currentItem() == ImageSelectionWidget::Portrait && + m_customRatioNInput->value() > a ) ) + { + m_customRatioNInput->blockSignals(true); + m_customRatioNInput->setValue(a); + m_customRatioNInput->blockSignals(false); + } + } + + slotCustomRatioChanged(); +} + +void ImageEffect_RatioCrop::slotCustomRatioChanged() +{ + m_imageSelectionWidget->setSelectionAspectRatioValue( + m_customRatioNInput->value(), m_customRatioDInput->value() ); + + // Reset selection area. + slotDefault(); +} + +void ImageEffect_RatioCrop::slotOk() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + TQRect currentRegion = m_imageSelectionWidget->getRegionSelection(); + Digikam::ImageIface* iface = m_imageSelectionWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + TQRect normalizedRegion = currentRegion.normalize(); + if (normalizedRegion.right() > w) normalizedRegion.setRight(w); + if (normalizedRegion.bottom() > h) normalizedRegion.setBottom(h); + + Digikam::DImg imOrg(w, h, sb, a, data); + delete [] data; + imOrg.crop(normalizedRegion); + + iface->putOriginalImage(i18n("Aspect Ratio Crop"), imOrg.bits(), imOrg.width(), imOrg.height()); + + kapp->restoreOverrideCursor(); + writeSettings(); + accept(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.h b/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.h new file mode 100644 index 00000000..ca24eeac --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/imageeffect_ratiocrop.h @@ -0,0 +1,132 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : digiKam image editor Ratio Crop tool + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_RATIOCROP_H +#define IMAGEEFFECT_RATIOCROP_H + +// Digikam include. + +#include "imagedlgbase.h" + +class TQLabel; +class TQComboBox; +class TQPushButton; +class TQCheckBox; +class TQSpinBox; + +class KIntNumInput; +class KIntSpinBox; +class KColorButton; + +namespace DigikamImagesPluginCore +{ + +class ImageSelectionWidget; + +class ImageEffect_RatioCrop : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_RatioCrop(TQWidget *parent); + ~ImageEffect_RatioCrop(); + +private: + + void readSettings(); + void writeSettings(); + + void applyRatioChanges(int a); + void setRatioCBText(int orientation); + +private slots: + + void slotUser1(); + void slotDefault(); + void slotOk(); + + void slotCenterWidth(); + void slotCenterHeight(); + void slotXChanged(int x); + void slotYChanged(int y); + void slotWidthChanged(int w); + void slotHeightChanged(int h); + void slotCustomRatioChanged(); + void slotCustomNRatioChanged(int a); + void slotCustomDRatioChanged(int a); + void slotPreciseCropChanged(bool a); + void slotOrientChanged(int o); + void slotAutoOrientChanged(bool a); + void slotRatioChanged(int a); + void slotSelectionChanged(TQRect rect ); + void slotSelectionOrientationChanged(int); + void slotGuideTypeChanged(int t); + void slotGoldenGuideTypeChanged(); + +private: + + bool m_originalIsLandscape; + + TQLabel *m_customLabel1; + TQLabel *m_customLabel2; + TQLabel *m_orientLabel; + TQLabel *m_colorGuideLabel; + + TQComboBox *m_ratioCB; + TQComboBox *m_orientCB; + TQComboBox *m_guideLinesCB; + + TQPushButton *m_centerWidth; + TQPushButton *m_centerHeight; + + TQCheckBox *m_goldenSectionBox; + TQCheckBox *m_goldenSpiralSectionBox; + TQCheckBox *m_goldenSpiralBox; + TQCheckBox *m_goldenTriangleBox; + TQCheckBox *m_flipHorBox; + TQCheckBox *m_flipVerBox; + TQCheckBox *m_autoOrientation; + TQCheckBox *m_preciseCrop; + + TQSpinBox *m_guideSize; + + KIntNumInput *m_widthInput; + KIntNumInput *m_heightInput; + KIntNumInput *m_xInput; + KIntNumInput *m_yInput; + + KIntSpinBox *m_customRatioNInput; + KIntSpinBox *m_customRatioDInput; + + KColorButton *m_guideColorBt; + + ImageSelectionWidget *m_imageSelectionWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_RATIOCROP_H */ diff --git a/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp new file mode 100644 index 00000000..17eaf415 --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.cpp @@ -0,0 +1,1422 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-09 + * Description : image selection widget used by ratio crop tool. + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define OPACITY 0.7 +#define RCOL 0xAA +#define GCOL 0xAA +#define BCOL 0xAA + +#define MINRANGE 0 + +// Golden number (1+sqrt(5))/2 +#define PHI 1.61803398874989479 +// 1/PHI +#define INVPHI 0.61803398874989479 + +// C++ includes. + +#include +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "dimg.h" +#include "imageselectionwidget.h" +#include "imageselectionwidget.moc" + +namespace DigikamImagesPluginCore +{ + +class ImageSelectionWidgetPriv +{ +public: + + enum ResizingMode + { + ResizingNone = 0, + ResizingTopLeft, + ResizingTopRight, + ResizingBottomLeft, + ResizingBottomRight + }; + + ImageSelectionWidgetPriv() + { + currentResizing = ResizingNone; + iface = 0; + pixmap = 0; + guideSize = 1; + } + + // Golden guide types. + bool drawGoldenSection; + bool drawGoldenSpiralSection; + bool drawGoldenSpiral; + bool drawGoldenTriangle; + + // Golden guide translations. + bool flipHorGoldenGuide; + bool flipVerGoldenGuide; + + bool moving; + bool autoOrientation; + bool preciseCrop; + + int guideLinesType; + int guideSize; + + int currentAspectRatioType; + int currentResizing; + int currentOrientation; + + float currentWidthRatioValue; + float currentHeightRatioValue; + + TQPoint lastPos; + + TQRect rect; + TQRect image; // Real image dimension. + TQRect regionSelection; // Real size image selection. + TQRect localRegionSelection; // Local size selection. + + // Draggable local region selection corners. + TQRect localTopLeftCorner; + TQRect localBottomLeftCorner; + TQRect localTopRightCorner; + TQRect localBottomRightCorner; + + TQPixmap *pixmap; + + TQColor guideColor; + + Digikam::DImg preview; + + Digikam::ImageIface *iface; +}; + +ImageSelectionWidget::ImageSelectionWidget(int w, int h, TQWidget *parent, + int widthRatioValue, int heightRatioValue, + int aspectRatioType, int orient, int guideLinesType) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new ImageSelectionWidgetPriv; + d->currentAspectRatioType = aspectRatioType; + d->currentWidthRatioValue = widthRatioValue; + d->currentHeightRatioValue = heightRatioValue; + d->currentOrientation = orient; + d->guideLinesType = guideLinesType; + d->autoOrientation = false; + d->preciseCrop = false; + d->moving = true; + reverseRatioValues(); + + setBackgroundMode(TQt::NoBackground); + setMinimumSize(w, h); + setMouseTracking(true); + + d->iface = new Digikam::ImageIface(w, h); + uchar *data = d->iface->getPreviewImage(); + int width = d->iface->previewWidth(); + int height = d->iface->previewHeight(); + bool sixteenBit = d->iface->previewSixteenBit(); + bool hasAlpha = d->iface->previewHasAlpha(); + d->preview = Digikam::DImg(width, height, sixteenBit, hasAlpha, data); + delete [] data; + d->preview.convertToEightBit(); + d->pixmap = new TQPixmap(w, h); + + d->image = TQRect(0, 0, d->iface->originalWidth(), d->iface->originalHeight()); + d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2, + d->preview.width(), d->preview.height()); + updatePixmap(); + setGoldenGuideTypes(true, false, false, false, false, false); +} + +ImageSelectionWidget::~ImageSelectionWidget() +{ + delete d->iface; + delete d->pixmap; + delete d; +} + +Digikam::ImageIface* ImageSelectionWidget::imageIface() +{ + return d->iface; +} + +void ImageSelectionWidget::resizeEvent(TQResizeEvent *e) +{ + delete d->pixmap; + + int w = e->size().width(); + int h = e->size().height(); + + uchar *data = d->iface->setPreviewImageSize(w, h); + int width = d->iface->previewWidth(); + int height = d->iface->previewHeight(); + bool sixteenBit = d->iface->previewSixteenBit(); + bool hasAlpha = d->iface->previewHasAlpha(); + d->preview = Digikam::DImg(width, height, sixteenBit, hasAlpha, data); + delete [] data; + d->preview.convertToEightBit(); + + d->pixmap = new TQPixmap(w, h); + + d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2, + d->preview.width(), d->preview.height()); + updatePixmap(); +} + +int ImageSelectionWidget::getOriginalImageWidth() +{ + return d->image.width(); +} + +int ImageSelectionWidget::getOriginalImageHeight() +{ + return d->image.height(); +} + +TQRect ImageSelectionWidget::getRegionSelection() +{ + return d->regionSelection; +} + +int ImageSelectionWidget::getMinWidthRange() +{ + return MINRANGE; +} + +int ImageSelectionWidget::getMinHeightRange() +{ + return MINRANGE; +} + +int ImageSelectionWidget::getMaxWidthRange() +{ + int maxW = d->image.width() - d->regionSelection.left(); + + if (d->currentAspectRatioType != RATIONONE) + { + // Compute max width taking aspect ratio into account + int t = d->currentWidthRatioValue > d->currentHeightRatioValue ? 1 : 0; + int h = d->image.height() - d->regionSelection.top(); + int w = rint( ( h + t ) * d->currentWidthRatioValue / + d->currentHeightRatioValue ) - t; + if ( w < maxW ) + maxW = w; + } + + // Return max width adjusted if a precise crop is wanted + return computePreciseSize(maxW, d->currentWidthRatioValue); +} + +int ImageSelectionWidget::getMaxHeightRange() +{ + int maxH = d->image.height() - d->regionSelection.top(); + + if (d->currentAspectRatioType != RATIONONE) + { + // Compute max height taking aspect ratio into account + int t = d->currentHeightRatioValue > d->currentWidthRatioValue ? 1 : 0; + int w = d->image.width() - d->regionSelection.left(); + int h = rint( ( w + t ) * d->currentHeightRatioValue / + d->currentWidthRatioValue ) - t; + if ( h < maxH ) + maxH = h; + } + + // Return max height adjusted if a precise crop is wanted + return computePreciseSize(maxH, d->currentHeightRatioValue); +} + +int ImageSelectionWidget::getWidthStep() +{ + if ( d->preciseCrop && preciseCropAvailable() ) + return d->currentWidthRatioValue; + else + return 1; +} + +int ImageSelectionWidget::getHeightStep() +{ + if ( d->preciseCrop && preciseCropAvailable() ) + return d->currentHeightRatioValue; + else + return 1; +} + +// Draw a new centered selection with half width (if orientation = Landscape) +// or with half height (if orientation = Portrait) +void ImageSelectionWidget::resetSelection() +{ + d->regionSelection.setWidth(d->image.width()/2); + d->regionSelection.setHeight(d->image.height()/2); + applyAspectRatio(d->currentOrientation == Portrait, false); + + setCenterSelection(CenterImage); +} + +void ImageSelectionWidget::setCenterSelection(int centerType) +{ + // Adjust selection size if bigger than real image + if ( d->regionSelection.height() > d->image.height() ) + { + d->regionSelection.setHeight(d->image.height()); + applyAspectRatio(true, false); + } + if ( d->regionSelection.width() > d->image.width() ) + { + d->regionSelection.setWidth(d->image.width()); + applyAspectRatio(false, false); + } + + // Set center point for selection + TQPoint center = d->image.center(); + switch (centerType) + { + case CenterWidth: + center.setY(d->regionSelection.center().y()); + break; + + case CenterHeight: + center.setX(d->regionSelection.center().x()); + break; + } + d->regionSelection.moveCenter(center); + + // Repaint + updatePixmap(); + repaint(false); + regionSelectionChanged(); +} + +// Draw a new centered selection with max size +void ImageSelectionWidget::maxAspectSelection() +{ + d->regionSelection.setWidth(d->image.width()); + d->regionSelection.setHeight(d->image.height()); + if ( d->currentAspectRatioType != RATIONONE ) + applyAspectRatio(d->currentOrientation == Portrait, false); + + setCenterSelection(CenterImage); +} + +void ImageSelectionWidget::setGoldenGuideTypes(bool drawGoldenSection, bool drawGoldenSpiralSection, + bool drawGoldenSpiral, bool drawGoldenTriangle, + bool flipHorGoldenGuide, bool flipVerGoldenGuide) +{ + d->drawGoldenSection = drawGoldenSection; + d->drawGoldenSpiralSection = drawGoldenSpiralSection; + d->drawGoldenSpiral = drawGoldenSpiral; + d->drawGoldenTriangle = drawGoldenTriangle; + d->flipHorGoldenGuide = flipHorGoldenGuide; + d->flipVerGoldenGuide = flipVerGoldenGuide; +} + +void ImageSelectionWidget::slotGuideLines(int guideLinesType) +{ + d->guideLinesType = guideLinesType; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::slotChangeGuideColor(const TQColor &color) +{ + d->guideColor = color; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::slotChangeGuideSize(int size) +{ + d->guideSize = size; + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::setSelectionOrientation(int orient) +{ + d->currentOrientation = orient; + reverseRatioValues(); + applyAspectRatio(true); + emit signalSelectionOrientationChanged( d->currentOrientation ); +} + +void ImageSelectionWidget::setSelectionAspectRatioType(int aspectRatioType) +{ + d->currentAspectRatioType = aspectRatioType; + + // Set ratio values + switch(aspectRatioType) + { + case RATIO01X01: + d->currentWidthRatioValue = 1.0; + d->currentHeightRatioValue = 1.0; + break; + + case RATIO03X04: + d->currentWidthRatioValue = 4.0; + d->currentHeightRatioValue = 3.0; + break; + + case RATIO02x03: + d->currentWidthRatioValue = 3.0; + d->currentHeightRatioValue = 2.0; + break; + + case RATIO05x07: + d->currentWidthRatioValue = 7.0; + d->currentHeightRatioValue = 5.0; + break; + + case RATIO07x10: + d->currentWidthRatioValue = 10.0; + d->currentHeightRatioValue = 7.0; + break; + + case RATIO04X05: + d->currentWidthRatioValue = 5.0; + d->currentHeightRatioValue = 4.0; + break; + + case RATIOGOLDEN: + d->currentWidthRatioValue = PHI; + d->currentHeightRatioValue = 1.0; + break; + } + + reverseRatioValues(); + applyAspectRatio(false); +} + +void ImageSelectionWidget::setSelectionAspectRatioValue(int widthRatioValue, + int heightRatioValue) +{ + int gdc = widthRatioValue; + + // Compute greatest common divisor using Euclidean algorithm + for (int tmp, mod = heightRatioValue; mod != 0; mod = tmp % mod) + { + tmp = gdc; + gdc = mod; + } + + d->currentWidthRatioValue = widthRatioValue / gdc; + d->currentHeightRatioValue = heightRatioValue / gdc; + d->currentAspectRatioType = RATIOCUSTOM; + + // Fix orientation + if ( d->autoOrientation ) + { + if ( heightRatioValue > widthRatioValue && + d->currentOrientation == Landscape ) + { + d->currentOrientation = Portrait; + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + else if ( widthRatioValue > heightRatioValue && + d->currentOrientation == Portrait ) + { + d->currentOrientation = Landscape; + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + else + reverseRatioValues(); + + applyAspectRatio(false); +} + +void ImageSelectionWidget::reverseRatioValues() +{ + // Reverse ratio values if needed + if ( ( d->currentWidthRatioValue > d->currentHeightRatioValue && + d->currentOrientation == Portrait ) || + ( d->currentHeightRatioValue > d->currentWidthRatioValue && + d->currentOrientation == Landscape ) ) + { + float tmp = d->currentWidthRatioValue; + d->currentWidthRatioValue = d->currentHeightRatioValue; + d->currentHeightRatioValue = tmp; + } +} + +bool ImageSelectionWidget::preciseCropAvailable() +{ + // Define when precise crop feature can be used + // No needed when aspect ratio is 1:1 + switch(d->currentAspectRatioType) + { + case RATIONONE: + case RATIO01X01: + case RATIOGOLDEN: + return false; + + case RATIOCUSTOM: + return ( d->currentWidthRatioValue != d->currentHeightRatioValue ); + + default: + return true; + } +} + +void ImageSelectionWidget::setPreciseCrop(bool precise) +{ + d->preciseCrop = precise; + applyAspectRatio(false, true); + regionSelectionChanged(); +} + +void ImageSelectionWidget::setAutoOrientation(bool orientation) +{ + d->autoOrientation = orientation; +} + +void ImageSelectionWidget::setSelectionX(int x) +{ + d->regionSelection.moveLeft(x); + regionSelectionMoved(); +} + +void ImageSelectionWidget::setSelectionY(int y) +{ + d->regionSelection.moveTop(y); + regionSelectionMoved(); +} + +void ImageSelectionWidget::setSelectionWidth(int w) +{ + d->regionSelection.setWidth(w); + applyAspectRatio(false, true); + + regionSelectionChanged(); +} + +void ImageSelectionWidget::setSelectionHeight(int h) +{ + d->regionSelection.setHeight(h); + applyAspectRatio(true, true); + + regionSelectionChanged(); +} + +TQPoint ImageSelectionWidget::convertPoint(const TQPoint pm, bool localToReal) +{ + return convertPoint(pm.x(), pm.y(), localToReal); +} + +TQPoint ImageSelectionWidget::convertPoint(int x, int y, bool localToReal) +{ + int pmX, pmY; + + if (localToReal) + { + pmX = ( x - d->rect.left() ) * (float)d->image.width() / + (float)d->preview.width(); + + pmY = ( y - d->rect.top() ) * (float)d->image.height() / + (float)d->preview.height(); + } + else + { + pmX = d->rect.left() + ( x * (float)d->preview.width() / + (float)d->image.width() ); + + pmY = d->rect.top() + ( y * (float)d->preview.height() / + (float)d->image.height() ); + } + + return TQPoint(pmX, pmY); +} + +int ImageSelectionWidget::computePreciseSize(int size, int step) +{ + // Adjust size if precise crop is wanted + if ( d->preciseCrop && preciseCropAvailable() ) + size = int(size / step) * step; + + return size; +} + +void ImageSelectionWidget::applyAspectRatio(bool useHeight, bool repaintWidget) +{ + // Save selection area for re-adjustment after changing width and height. + TQRect oldRegionSelection = d->regionSelection; + + if ( !useHeight ) // Width changed. + { + int w = computePreciseSize(d->regionSelection.width(), + d->currentWidthRatioValue); + + d->regionSelection.setWidth(w); + switch(d->currentAspectRatioType) + { + case RATIONONE: + break; + + default: + d->regionSelection.setHeight(rint( w * d->currentHeightRatioValue / + d->currentWidthRatioValue ) ); + break; + } + } + else // Height changed. + { + int h = computePreciseSize(d->regionSelection.height(), + d->currentHeightRatioValue); + + d->regionSelection.setHeight(h); + switch(d->currentAspectRatioType) + { + case RATIONONE: + break; + + default: + d->regionSelection.setWidth(rint( h * d->currentWidthRatioValue / + d->currentHeightRatioValue ) ); + break; + } + } + + // If we change selection size by a corner, re-adjust the oposite corner position. + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + d->regionSelection.moveBottomRight( oldRegionSelection.bottomRight() ); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + d->regionSelection.moveBottomLeft( oldRegionSelection.bottomLeft() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + d->regionSelection.moveTopRight( oldRegionSelection.topRight() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + d->regionSelection.moveTopLeft( oldRegionSelection.topLeft() ); + break; + } + + if (repaintWidget) + { + updatePixmap(); + repaint(false); + } +} + +void ImageSelectionWidget::normalizeRegion() +{ + // Perform normalization of selection area. + + if (d->regionSelection.left() < d->image.left()) + d->regionSelection.moveLeft(d->image.left()); + + if (d->regionSelection.top() < d->image.top()) + d->regionSelection.moveTop(d->image.top()); + + if (d->regionSelection.right() > d->image.right()) + d->regionSelection.moveRight(d->image.right()); + + if (d->regionSelection.bottom() > d->image.bottom()) + d->regionSelection.moveBottom(d->image.bottom()); +} + +void ImageSelectionWidget::regionSelectionMoved() +{ + normalizeRegion(); + + updatePixmap(); + repaint(false); + + emit signalSelectionMoved( d->regionSelection ); +} + +void ImageSelectionWidget::regionSelectionChanged() +{ + // Compute the intersection of selection region and image region + TQRect cut = d->regionSelection & d->image; + + // Adjust selection size if it was cropped + if ( d->regionSelection.width() > cut.width() ) + { + d->regionSelection = cut; + applyAspectRatio(false); + } + if ( d->regionSelection.height() > cut.height() ) + { + d->regionSelection = cut; + applyAspectRatio(true); + } + + emit signalSelectionChanged( d->regionSelection ); +} + +void ImageSelectionWidget::updatePixmap() +{ + // Updated local selection region. + d->localRegionSelection.setTopLeft( + convertPoint(d->regionSelection.topLeft(), false)); + d->localRegionSelection.setBottomRight( + convertPoint(d->regionSelection.bottomRight(), false)); + + // Updated dragging corners region. + d->localTopLeftCorner.setRect(d->localRegionSelection.left(), + d->localRegionSelection.top(), 8, 8); + d->localBottomLeftCorner.setRect(d->localRegionSelection.left(), + d->localRegionSelection.bottom() - 7, 8, 8); + d->localTopRightCorner.setRect(d->localRegionSelection.right() - 7, + d->localRegionSelection.top(), 8, 8); + d->localBottomRightCorner.setRect(d->localRegionSelection.right() - 7, + d->localRegionSelection.bottom() - 7, 8, 8); + + // Drawing background and image. + d->pixmap->fill(colorGroup().background()); + + if (d->preview.isNull()) + return; + + // Drawing region outside selection grayed. + + Digikam::DImg image = d->preview.copy(); + + uchar* ptr = image.bits(); + uchar r, g, b; + + for (int y=d->rect.top() ; y <= d->rect.bottom() ; y++) + { + for (int x=d->rect.left() ; x <= d->rect.right() ; x++) + { + if (! d->localRegionSelection.contains(x, y, true) ) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + + r += (uchar)((RCOL - r) * OPACITY); + g += (uchar)((GCOL - g) * OPACITY); + b += (uchar)((BCOL - b) * OPACITY); + + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + } + + ptr+=4; + } + } + + TQPixmap pix = d->iface->convertToPixmap(image); + bitBlt(d->pixmap, d->rect.x(), d->rect.y(), &pix); + + // Stop here if no selection to draw + if ( d->regionSelection.isEmpty() ) + return; + + TQPainter p(d->pixmap); + + // Drawing selection borders. + + p.setPen(TQPen(TQColor(250, 250, 255), 1, TQt::SolidLine)); + p.drawRect(d->localRegionSelection); + + // Drawing selection corners. + + p.drawRect(d->localTopLeftCorner); + p.drawRect(d->localBottomLeftCorner); + p.drawRect(d->localTopRightCorner); + p.drawRect(d->localBottomRightCorner); + + // Drawing guide lines. + + // Constraint drawing only on local selection region. + // This is needed because arcs and incurved lines can draw + // outside a little of local selection region. + p.setClipping(true); + p.setClipRect(d->localRegionSelection); + + switch (d->guideLinesType) + { + case RulesOfThirds: + { + int xThird = d->localRegionSelection.width() / 3; + int yThird = d->localRegionSelection.height() / 3; + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + p.drawLine( d->localRegionSelection.left() + xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + xThird, d->localRegionSelection.bottom() ); + p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() ); + + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + yThird ); + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + 2*yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird ); + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + p.drawLine( d->localRegionSelection.left() + xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + xThird, d->localRegionSelection.bottom() ); + p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(), + d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() ); + + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + yThird ); + p.drawLine( d->localRegionSelection.left(), d->localRegionSelection.top() + 2*yThird, + d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird ); + break; + } + + case DiagonalMethod: + { + // Move coordinates to top, left + p.translate(d->localRegionSelection.topLeft().x(), d->localRegionSelection.topLeft().y()); + + float w = (float)d->localRegionSelection.width(); + float h = (float)d->localRegionSelection.height(); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + if (w > h) + { + p.drawLine( 0, 0, h, h); + p.drawLine( 0, h, h, 0); + p.drawLine( w-h, 0, w, h); + p.drawLine( w-h, h, w, 0); + + } + else + { + p.drawLine( 0, 0, w, w); + p.drawLine( 0, w, w, 0); + p.drawLine( 0, h-w, w, h); + p.drawLine( 0, h, w, h-w); + } + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + if (w > h) + { + p.drawLine( 0, 0, h, h); + p.drawLine( 0, h, h, 0); + p.drawLine( w-h, 0, w, h); + p.drawLine( w-h, h, w, 0); + + } + else + { + p.drawLine( 0, 0, w, w); + p.drawLine( 0, w, w, 0); + p.drawLine( 0, h-w, w, h); + p.drawLine( 0, h, w, h-w); + } + break; + } + + case HarmoniousTriangles: + { + // Move coordinates to local center selection. + p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y()); + + // Flip horizontal. + if (d->flipHorGoldenGuide) + p.scale(-1, 1); + + // Flip verical. + if (d->flipVerGoldenGuide) + p.scale(1, -1); + + float w = (float)d->localRegionSelection.width(); + float h = (float)d->localRegionSelection.height(); + int dst = (int)((h*cos(atan(w/h)) / (cos(atan(h/w))))); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2, + -d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2 - dst, d->localRegionSelection.height()/2); + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2, + -d->localRegionSelection.width()/2, d->localRegionSelection.height()/2); + + p.drawLine( d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2, + d->localRegionSelection.width()/2 - dst, d->localRegionSelection.height()/2); + break; + } + + case GoldenMean: + { + // Move coordinates to local center selection. + p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y()); + + // Flip horizontal. + if (d->flipHorGoldenGuide) + p.scale(-1, 1); + + // Flip vertical. + if (d->flipVerGoldenGuide) + p.scale(1, -1); + + int w = d->localRegionSelection.width(); + int h = d->localRegionSelection.height(); + + // lengths for the golden mean and half the sizes of the region: + int w_g = (int)(w*INVPHI); + int h_g = (int)(h*INVPHI); + int w_2 = w/2; + int h_2 = h/2; + + TQRect R1(-w_2, -h_2, w_g, h); + // w - 2*w_2 corrects for one-pixel difference + // so that R2.right() is really at the right end of the region + TQRect R2(w_g-w_2, h_2-h_g, w-w_g+1-(w - 2*w_2), h_g); + + TQRect R3((int)(w_2 - R2.width()*INVPHI), -h_2, + (int)(R2.width()*INVPHI), h - R2.height()); + TQRect R4(R2.x(), R1.y(), R3.x() - R2.x(), + (int)(R3.height()*INVPHI)); + TQRect R5(R4.x(), R4.bottom(), (int)(R4.width()*INVPHI), + R3.height() - R4.height()); + TQRect R6(R5.x() + R5.width(), R5.bottom() - (int)(R5.height()*INVPHI), + R3.x() - R5.right(), (int)(R5.height()*INVPHI)); + TQRect R7(R6.right() - (int)(R6.width()*INVPHI), R4.bottom(), + (int)(R6.width()*INVPHI), R5.height() - R6.height()); + + p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine)); + + // Drawing Golden sections. + if (d->drawGoldenSection) + { + // horizontal lines: + p.drawLine( R1.left(), R2.top(), + R2.right(), R2.top()); + + p.drawLine( R1.left(), R1.top() + R2.height(), + R2.right(), R1.top() + R2.height()); + + // vertical lines: + p.drawLine( R1.right(), R1.top(), + R1.right(), R1.bottom() ); + + p.drawLine( R1.left()+R2.width(), R1.top(), + R1.left()+R2.width(), R1.bottom() ); + } + + // Drawing Golden triangle guides. + if (d->drawGoldenTriangle) + { + p.drawLine( R1.left(), R1.bottom(), + R2.right(), R1.top() ); + + p.drawLine( R1.left(), R1.top(), + R2.right() - R1.width(), R1.bottom()); + + p.drawLine( R1.left() + R1.width(), R1.top(), + R2.right(), R1.bottom() ); + } + + // Drawing Golden spiral sections. + if (d->drawGoldenSpiralSection) + { + p.drawLine( R1.topRight(), R1.bottomRight() ); + p.drawLine( R2.topLeft(), R2.topRight() ); + p.drawLine( R3.topLeft(), R3.bottomLeft() ); + p.drawLine( R4.bottomLeft(), R4.bottomRight() ); + p.drawLine( R5.topRight(), R5.bottomRight() ); + p.drawLine( R6.topLeft(), R6.topRight() ); + p.drawLine( R7.topLeft(), R7.bottomLeft() ); + } + + // Drawing Golden Spiral. + if (d->drawGoldenSpiral) + { + p.drawArc ( R1.left(), + R1.top() - R1.height(), + 2*R1.width(), 2*R1.height(), + 180*16, 90*16); + + p.drawArc ( R2.right() - 2*R2.width(), + R1.bottom() - 2*R2.height(), + 2*R2.width(), 2*R2.height(), + 270*16, 90*16); + + p.drawArc ( R2.right() - 2*R3.width(), + R3.top(), + 2*R3.width(), 2*R3.height(), + 0, 90*16); + + p.drawArc ( R4.left(), + R4.top(), + 2*R4.width(), 2*R4.height(), + 90*16, 90*16); + + p.drawArc ( R5.left(), + R5.top()-R5.height(), + 2*R5.width(), 2*R5.height(), + 180*16, 90*16); + + p.drawArc ( R6.left()-R6.width(), + R6.top()-R6.height(), + 2*R6.width(), 2*R6.height(), + 270*16, 90*16); + + p.drawArc ( R7.left()-R7.width(), + R7.top(), + 2*R7.width(), 2*R7.height(), + 0, 90*16); + } + + p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine)); + + // Drawing Golden sections. + if (d->drawGoldenSection) + { + // horizontal lines: + p.drawLine( R1.left(), R2.top(), + R2.right(), R2.top()); + + p.drawLine( R1.left(), R1.top() + R2.height(), + R2.right(), R1.top() + R2.height()); + + // vertical lines: + p.drawLine( R1.right(), R1.top(), + R1.right(), R1.bottom() ); + + p.drawLine( R1.left()+R2.width(), R1.top(), + R1.left()+R2.width(), R1.bottom() ); + } + + // Drawing Golden triangle guides. + if (d->drawGoldenTriangle) + { + p.drawLine( R1.left(), R1.bottom(), + R2.right(), R1.top() ); + + p.drawLine( R1.left(), R1.top(), + R2.right() - R1.width(), R1.bottom()); + + p.drawLine( R1.left() + R1.width(), R1.top(), + R2.right(), R1.bottom() ); + } + + // Drawing Golden spiral sections. + if (d->drawGoldenSpiralSection) + { + p.drawLine( R1.topRight(), R1.bottomRight() ); + p.drawLine( R2.topLeft(), R2.topRight() ); + p.drawLine( R3.topLeft(), R3.bottomLeft() ); + p.drawLine( R4.bottomLeft(), R4.bottomRight() ); + p.drawLine( R5.topRight(), R5.bottomRight() ); + p.drawLine( R6.topLeft(), R6.topRight() ); + p.drawLine( R7.topLeft(), R7.bottomLeft() ); + } + + // Drawing Golden Spiral. + if (d->drawGoldenSpiral) + { + p.drawArc ( R1.left(), + R1.top() - R1.height(), + 2*R1.width(), 2*R1.height(), + 180*16, 90*16); + + p.drawArc ( R2.right() - 2*R2.width(), + R1.bottom() - 2*R2.height(), + 2*R2.width(), 2*R2.height(), + 270*16, 90*16); + + p.drawArc ( R2.right() - 2*R3.width(), + R3.top(), + 2*R3.width(), 2*R3.height(), + 0, 90*16); + + p.drawArc ( R4.left(), + R4.top(), + 2*R4.width(), 2*R4.height(), + 90*16, 90*16); + + p.drawArc ( R5.left(), + R5.top()-R5.height(), + 2*R5.width(), 2*R5.height(), + 180*16, 90*16); + + p.drawArc ( R6.left()-R6.width(), + R6.top()-R6.height(), + 2*R6.width(), 2*R6.height(), + 270*16, 90*16); + + p.drawArc ( R7.left()-R7.width(), + R7.top(), + 2*R7.width(), 2*R7.height(), + 0, 90*16); + } + + break; + } + } + + p.setClipping(false); + + p.end(); +} + +void ImageSelectionWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0, 0, d->pixmap); +} + +TQPoint ImageSelectionWidget::opposite() +{ + TQPoint opp; + + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopRight: + opp = d->regionSelection.bottomLeft(); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + opp = d->regionSelection.topRight(); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + opp = d->regionSelection.topLeft(); + break; + + case ImageSelectionWidgetPriv::ResizingTopLeft: + default: + opp = d->regionSelection.bottomRight(); + break; + } + + return opp; +} + +float ImageSelectionWidget::distance(TQPoint a, TQPoint b) +{ + return sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2)); +} + +void ImageSelectionWidget::setCursorResizing() +{ + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + setCursor( KCursor::sizeFDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + setCursor( KCursor::sizeBDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + setCursor( KCursor::sizeBDiagCursor() ); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + setCursor( KCursor::sizeFDiagCursor() ); + break; + } +} + +void ImageSelectionWidget::placeSelection(TQPoint pm, bool symmetric, TQPoint center) +{ + // Set orientation + if ( d->autoOrientation ) + { + TQPoint rel = pm - opposite(); + + if ( abs(rel.x()) > abs(rel.y()) ) + { + if ( d->currentOrientation == Portrait ) + { + d->currentOrientation = Landscape; + reverseRatioValues(); + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + else + { + if ( d->currentOrientation == Landscape ) + { + d->currentOrientation = Portrait; + reverseRatioValues(); + emit signalSelectionOrientationChanged( d->currentOrientation ); + } + } + } + + // Place the corner at the mouse + // If a symmetric selection is wanted, place opposite corner to + // the center, double selection size and move it to old center after + // computing aspect ratio. + switch(d->currentResizing) + { + case ImageSelectionWidgetPriv::ResizingTopLeft: + // Place corners to the proper position + d->regionSelection.setTopLeft(pm); + if ( symmetric ) + d->regionSelection.setBottomRight(center); + break; + + case ImageSelectionWidgetPriv::ResizingTopRight: + d->regionSelection.setTopRight(pm); + if ( symmetric ) + d->regionSelection.setBottomLeft(center); + break; + + case ImageSelectionWidgetPriv::ResizingBottomLeft: + d->regionSelection.setBottomLeft(pm); + if ( symmetric ) + d->regionSelection.setTopRight(center); + break; + + case ImageSelectionWidgetPriv::ResizingBottomRight: + d->regionSelection.setBottomRight(pm); + if ( symmetric ) + d->regionSelection.setTopLeft(center); + break; + } + + if ( symmetric ) + d->regionSelection.setSize(d->regionSelection.size()*2); + applyAspectRatio(d->currentOrientation == Portrait, false); + if ( symmetric ) + d->regionSelection.moveCenter(center); + + // Repaint + updatePixmap(); + repaint(false); +} + +void ImageSelectionWidget::mousePressEvent ( TQMouseEvent * e ) +{ + if ( e->button() == TQt::LeftButton ) + { + TQPoint pm = TQPoint(e->x(), e->y()); + TQPoint pmVirtual = convertPoint(pm); + d->moving = false; + + if ( (e->state() & TQt::ShiftButton) == TQt::ShiftButton ) + { + bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton; + TQPoint center = d->regionSelection.center(); + + // Find the closest corner + + TQPoint points[] = { d->regionSelection.topLeft(), + d->regionSelection.topRight(), + d->regionSelection.bottomLeft(), + d->regionSelection.bottomRight() }; + int resizings[] = { ImageSelectionWidgetPriv::ResizingTopLeft, + ImageSelectionWidgetPriv::ResizingTopRight, + ImageSelectionWidgetPriv::ResizingBottomLeft, + ImageSelectionWidgetPriv::ResizingBottomRight }; + float dist = -1; + for (int i = 0 ; i < 4 ; i++) + { + TQPoint point = points[i]; + float dist2 = distance(pmVirtual, point); + if (dist2 < dist || d->currentResizing == ImageSelectionWidgetPriv::ResizingNone) + { + dist = dist2; + d->currentResizing = resizings[i]; + } + } + + setCursorResizing(); + + placeSelection(pmVirtual, symmetric, center); + } + else + { + if ( d->localTopLeftCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; + else if ( d->localTopRightCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight; + else if ( d->localBottomLeftCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft; + else if ( d->localBottomRightCorner.contains( pm ) ) + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight; + else + { + d->lastPos = pmVirtual; + setCursor( KCursor::sizeAllCursor() ); + + if (d->regionSelection.contains( pmVirtual ) ) + { + d->moving = true; + } + else + { + d->regionSelection.moveCenter( pmVirtual ); + normalizeRegion(); + updatePixmap(); + repaint(false); + } + } + } + } +} + +void ImageSelectionWidget::mouseReleaseEvent ( TQMouseEvent * ) +{ + if ( d->currentResizing != ImageSelectionWidgetPriv::ResizingNone ) + { + setCursor( KCursor::arrowCursor() ); + regionSelectionChanged(); + d->currentResizing = ImageSelectionWidgetPriv::ResizingNone; + } + else if ( d->regionSelection.contains( d->lastPos ) ) + { + setCursor( KCursor::handCursor() ); + regionSelectionMoved(); + } + else + { + setCursor( KCursor::arrowCursor() ); + regionSelectionMoved(); + } +} + +void ImageSelectionWidget::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( ( e->state() & TQt::LeftButton ) == TQt::LeftButton ) + { + if ( d->moving ) + { + setCursor( KCursor::sizeAllCursor() ); + TQPoint newPos = convertPoint(e->x(), e->y()); + + d->regionSelection.moveBy( newPos.x() - d->lastPos.x(), + newPos.y() - d->lastPos.y() ); + + d->lastPos = newPos; + + normalizeRegion(); + + updatePixmap(); + repaint(false); + } + else + { + TQPoint pmVirtual = convertPoint(e->x(), e->y()); + + if ( d->currentResizing == ImageSelectionWidgetPriv::ResizingNone ) + { + d->regionSelection.setTopLeft( pmVirtual ); + d->regionSelection.setBottomRight( pmVirtual ); + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; // set to anything + } + + TQPoint center = d->regionSelection.center(); + bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton; + + // Change resizing mode + + TQPoint opp = symmetric ? center : opposite(); + TQPoint dir = pmVirtual - opp; + + if ( dir.x() > 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomRight) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight; + d->regionSelection.setTopLeft( opp ); + setCursor( KCursor::sizeFDiagCursor() ); + } + else if ( dir.x() > 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopRight) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight; + d->regionSelection.setBottomLeft( opp ); + setCursor( KCursor::sizeBDiagCursor() ); + } + else if ( dir.x() < 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomLeft) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft; + d->regionSelection.setTopRight( opp ); + setCursor( KCursor::sizeBDiagCursor() ); + } + else if ( dir.x() < 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopLeft) + { + d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; + d->regionSelection.setBottomRight( opp ); + setCursor( KCursor::sizeFDiagCursor() ); + } + else + { + if ( dir.x() == 0 && dir.y() == 0 ) + setCursor( KCursor::sizeAllCursor() ); + else if ( dir.x() == 0 ) + setCursor( KCursor::sizeHorCursor() ); + else if ( dir.y() == 0 ) + setCursor( KCursor::sizeVerCursor() ); + } + + placeSelection(pmVirtual, symmetric, center); + } + } + else + { + if ( d->localTopLeftCorner.contains( e->x(), e->y() ) || + d->localBottomRightCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeFDiagCursor() ); + else if ( d->localTopRightCorner.contains( e->x(), e->y() ) || + d->localBottomLeftCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeBDiagCursor() ); + else if ( d->localRegionSelection.contains( e->x(), e->y() ) ) + setCursor( KCursor::handCursor() ); + else + setCursor( KCursor::arrowCursor() ); + } +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.h b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.h new file mode 100644 index 00000000..0d2bd4fd --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/imageselectionwidget.h @@ -0,0 +1,176 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-09 + * Description : image selection widget used by ratio crop tool. + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGESELECTIONWIDGET_H +#define IMAGESELECTIONWIDGET_H + +// TQt includes. + +#include +#include +#include + +namespace Digikam +{ +class ImageIface; +} + +namespace DigikamImagesPluginCore +{ + +class ImageSelectionWidgetPriv; + +class ImageSelectionWidget : public TQWidget +{ + +TQ_OBJECT + + +public: + + enum RatioAspect // Constrained Aspect Ratio list. + { + RATIOCUSTOM=0, // Custom aspect ratio. + RATIO01X01, // 1:1 + RATIO02x03, // 2:3 + RATIO03X04, // 3:4 + RATIO04X05, // 4:5 + RATIO05x07, // 5:7 + RATIO07x10, // 7:10 + RATIOGOLDEN, // Golden ratio : 1:1.618 + RATIONONE // No aspect ratio. + }; + + enum Orient + { + Landscape = 0, + Portrait + }; + + enum CenterType + { + CenterWidth = 0, // Center selection to the center of image width. + CenterHeight, // Center selection to the center of image height. + CenterImage // Center selection to the center of image. + }; + + // Proportion : Golden Ratio and Rule of Thirds. More information at this url: + // http://photoinf.com/General/Robert_Berdan/Composition_and_the_Elements_of_Visual_Design.htm + + enum GuideLineType + { + RulesOfThirds = 0, // Line guides position to 1/3 width and height. + DiagonalMethod, // Diagonal Method to improve composition. + HarmoniousTriangles, // Harmonious Triangle to improve composition. + GoldenMean, // Guides tools using Phi ratio (1.618). + GuideNone // No guide line. + }; + +public: + + ImageSelectionWidget(int width, int height, TQWidget *parent=0, + int widthRatioValue=1, int heightRatioValue=1, + int aspectRatio=RATIO01X01, int orient=Landscape, + int guideLinesType=GuideNone); + ~ImageSelectionWidget(); + + void setCenterSelection(int centerType=CenterImage); + void setSelectionX(int x); + void setSelectionY(int y); + void setSelectionWidth(int w); + void setSelectionHeight(int h); + void setSelectionOrientation(int orient); + void setPreciseCrop(bool precise); + void setAutoOrientation(bool orientation); + void setSelectionAspectRatioType(int aspectRatioType); + void setSelectionAspectRatioValue(int widthRatioValue, int heightRatioValue); + void setGoldenGuideTypes(bool drawGoldenSection, bool drawGoldenSpiralSection, + bool drawGoldenSpiral, bool drawGoldenTriangle, + bool flipHorGoldenGuide, bool flipVerGoldenGuide); + + int getOriginalImageWidth(); + int getOriginalImageHeight(); + TQRect getRegionSelection(); + + int getMinWidthRange(); + int getMinHeightRange(); + int getMaxWidthRange(); + int getMaxHeightRange(); + int getWidthStep(); + int getHeightStep(); + + bool preciseCropAvailable(); + + void resetSelection(); + void maxAspectSelection(); + + Digikam::ImageIface* imageIface(); + +public slots: + + void slotGuideLines(int guideLinesType); + void slotChangeGuideColor(const TQColor &color); + void slotChangeGuideSize(int size); + +signals: + + void signalSelectionMoved( TQRect rect ); + void signalSelectionChanged( TQRect rect ); + void signalSelectionOrientationChanged( int newOrientation ); + +protected: + + void paintEvent( TQPaintEvent *e ); + void mousePressEvent ( TQMouseEvent * e ); + void mouseReleaseEvent ( TQMouseEvent * e ); + void mouseMoveEvent ( TQMouseEvent * e ); + void resizeEvent(TQResizeEvent * e); + +private: + + // Recalculate the target selection position and emit 'signalSelectionMoved'. + void regionSelectionMoved(); + + void regionSelectionChanged(); + TQPoint convertPoint(const TQPoint pm, bool localToReal=true); + TQPoint convertPoint(int x, int y, bool localToReal=true); + void normalizeRegion(); + void reverseRatioValues(); + int computePreciseSize(int size, int step); + void applyAspectRatio(bool useHeight, bool repaintWidget=true); + void updatePixmap(); + TQPoint opposite(); + float distance(TQPoint a, TQPoint b); + void placeSelection(TQPoint pm, bool symetric, TQPoint center); + void setCursorResizing(); + +private: + + ImageSelectionWidgetPriv* d; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGESELECTIONWIDGET_H */ diff --git a/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.cpp b/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.cpp new file mode 100644 index 00000000..ed0e670e --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.cpp @@ -0,0 +1,853 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : digiKam image editor Ratio Crop tool + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Digikam includes. + +#include "editortoolsettings.h" +#include "imageiface.h" +#include "imageselectionwidget.h" + +// Local includes. + +#include "ratiocroptool.h" +#include "ratiocroptool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +RatioCropTool::RatioCropTool(TQObject* parent) + : EditorTool(parent) +{ + setName("aspectratiocrop"); + setToolName(i18n("Aspect Ratio Crop")); + setToolIcon(SmallIcon("ratiocrop")); + setToolHelp("ratiocroptool.anchor"); + + // ------------------------------------------------------------- + + m_imageSelectionWidget = new ImageSelectionWidget(480, 320); + TQWhatsThis::add(m_imageSelectionWidget, + i18n("

    Here you can see the aspect ratio selection preview " + "used for cropping. You can use the mouse to move and " + "resize the crop area. " + "Press and hold the CTRL key to move the opposite corner too. " + "Press and hold the SHIFT key to move the closest corner to the " + "mouse pointer.")); + + m_originalIsLandscape = ((m_imageSelectionWidget->getOriginalImageWidth()) > + (m_imageSelectionWidget->getOriginalImageHeight())); + + setToolView(m_imageSelectionWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Try| + EditorToolSettings::Cancel); + + // ------------------------------------------------------------- + + // need to set the button to a KStdGuiItem that has no icon + m_gboxSettings->button(EditorToolSettings::Try)->setGuiItem(KStdGuiItem::Test); + // now we can set the correct text for the button + m_gboxSettings->button(EditorToolSettings::Try)->setText(i18n("Max. Aspect")); + + TQToolTip::add(m_gboxSettings->button(EditorToolSettings::Try), + i18n("

    Set selection area to the maximum size according " + "to the current ratio.")); + + // ------------------------------------------------------------- + + TQGridLayout *gboxLayout = new TQGridLayout(m_gboxSettings->plainPage(), 3, 2); + + TQFrame *cropSelection = new TQFrame(m_gboxSettings->plainPage()); + cropSelection->setFrameStyle(TQFrame::Panel | TQFrame::Sunken); + + TQGridLayout* grid = new TQGridLayout(cropSelection, 7, 5); + + TQLabel *label = new TQLabel(i18n("Ratio:"), cropSelection); + m_ratioCB = new RComboBox(cropSelection); + m_ratioCB->setDefaultItem(ImageSelectionWidget::RATIO03X04); + setRatioCBText(ImageSelectionWidget::Landscape); + TQWhatsThis::add( m_ratioCB, i18n("

    Select your constrained aspect ratio for cropping. " + "Aspect Ratio Crop tool uses a relative ratio. That means it " + "is the same if you use centimeters or inches and it doesn't " + "specify the physical size.

    " + "You can see below a correspondence list of traditional photographic " + "paper sizes and aspect ratio crop:

    " + "2:3: 10x15cm, 20x30cm, 30x45cm, 4x6\", 8x12\", " + "12x18\", 16x24\", 20x30\"

    " + "3:4: 6x8cm, 15x20cm, 18x24cm, 30x40cm, 3.75x5\", 4.5x6\", " + "6x8\", 7.5x10\", 9x12\"

    " + "4:5: 20x25cm, 40x50cm, 8x10\", 16x20\"

    " + "5:7: 15x21cm, 30x42cm, 5x7\"

    " + "7:10: 21x30cm, 42x60cm, 3.5x5\"

    " + "The Golden Ratio is 1:1.618. A composition following this rule " + "is considered visually harmonious but can be unadapted to print on " + "standard photographic paper.")); + + m_preciseCrop = new TQCheckBox(i18n("Exact"), cropSelection); + TQWhatsThis::add( m_preciseCrop, i18n("

    Enable this option to force exact aspect ratio crop.")); + + m_orientLabel = new TQLabel(i18n("Orientation:"), cropSelection); + m_orientCB = new RComboBox(cropSelection); + m_orientCB->insertItem( i18n("Landscape")); + m_orientCB->insertItem( i18n("Portrait")); + m_orientCB->setDefaultItem(ImageSelectionWidget::Landscape); + TQWhatsThis::add( m_orientCB, i18n("

    Select constrained aspect ratio orientation.")); + + m_autoOrientation = new TQCheckBox(i18n("Auto"), cropSelection); + TQWhatsThis::add( m_autoOrientation, i18n("

    Enable this option to automatically set the orientation.")); + + // ------------------------------------------------------------- + + m_customLabel1 = new TQLabel(i18n("Custom:"), cropSelection); + m_customLabel1->setAlignment(AlignLeft|AlignVCenter); + m_customRatioNInput = new RIntNumInput(cropSelection); + m_customRatioNInput->input()->setRange(1, 10000, 1, false); + m_customRatioNInput->setDefaultValue(1); + TQWhatsThis::add( m_customRatioNInput, i18n("

    Set here the desired custom aspect numerator value.")); + + m_customLabel2 = new TQLabel(" : ", cropSelection); + m_customLabel2->setAlignment(AlignCenter|AlignVCenter); + m_customRatioDInput = new RIntNumInput(cropSelection); + m_customRatioDInput->input()->setRange(1, 10000, 1, false); + m_customRatioDInput->setDefaultValue(1); + TQWhatsThis::add( m_customRatioDInput, i18n("

    Set here the desired custom aspect denominator value.")); + + // ------------------------------------------------------------- + + m_xInput = new RIntNumInput(cropSelection); + m_xInput->input()->setLabel(i18n("X:"), AlignLeft|AlignVCenter); + m_xInput->setRange(0, m_imageSelectionWidget->getOriginalImageWidth(), 1); + m_xInput->setDefaultValue(50); + TQWhatsThis::add( m_xInput, i18n("

    Set here the top left selection corner position for cropping.")); + + m_widthInput = new RIntNumInput(cropSelection); + m_widthInput->input()->setLabel(i18n("Width:"), AlignLeft|AlignVCenter); + m_widthInput->setRange(m_imageSelectionWidget->getMinWidthRange(), + m_imageSelectionWidget->getMaxWidthRange(), + m_imageSelectionWidget->getWidthStep()); + m_widthInput->setDefaultValue(800); + TQWhatsThis::add( m_widthInput, i18n("

    Set here the width selection for cropping.")); + + m_centerWidth = new TQToolButton(cropSelection); + TDEGlobal::dirs()->addResourceType("centerwidth", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("centerwidth", "centerwidth.png"); + m_centerWidth->setPixmap(TQPixmap(directory + "centerwidth.png")); + TQWhatsThis::add(m_centerWidth, i18n("

    Set width position to center.")); + + // ------------------------------------------------------------- + + m_yInput = new RIntNumInput(cropSelection); + m_yInput->input()->setLabel(i18n("Y:"), AlignLeft | AlignVCenter); + m_yInput->setRange(0, m_imageSelectionWidget->getOriginalImageHeight(), 1); + m_yInput->setDefaultValue(50); + TQWhatsThis::add(m_yInput, i18n("

    Set here the top left selection corner position for cropping.")); + + m_heightInput = new RIntNumInput(cropSelection); + m_heightInput->input()->setLabel(i18n("Height:"), AlignLeft | AlignVCenter); + m_heightInput->setRange(m_imageSelectionWidget->getMinHeightRange(), + m_imageSelectionWidget->getMaxHeightRange(), + m_imageSelectionWidget->getHeightStep()); + m_heightInput->setDefaultValue(600); + TQWhatsThis::add( m_heightInput, i18n("

    Set here the height selection for cropping.")); + + m_centerHeight = new TQToolButton(cropSelection); + TDEGlobal::dirs()->addResourceType("centerheight", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("centerheight", "centerheight.png"); + m_centerHeight->setPixmap(TQPixmap(directory + "centerheight.png")); + TQWhatsThis::add(m_centerHeight, i18n("

    Set height position to center.")); + + grid->addMultiCellWidget(label, 0, 0, 0, 0); + grid->addMultiCellWidget(m_ratioCB, 0, 0, 1, 3); + grid->addMultiCellWidget(m_preciseCrop, 0, 0, 4, 4); + grid->addMultiCellWidget(m_customLabel1, 1, 1, 0, 0); + grid->addMultiCellWidget(m_customRatioNInput, 1, 1, 1, 1); + grid->addMultiCellWidget(m_customLabel2, 1, 1, 2, 2); + grid->addMultiCellWidget(m_customRatioDInput, 1, 1, 3, 3); + grid->addMultiCellWidget(m_orientLabel, 2, 2, 0, 0); + grid->addMultiCellWidget(m_orientCB, 2, 2, 1, 3); + grid->addMultiCellWidget(m_autoOrientation, 2, 2, 4, 4); + grid->addMultiCellWidget(m_xInput, 3, 3, 0, 3); + grid->addMultiCellWidget(m_widthInput, 4, 4, 0, 3); + grid->addMultiCellWidget(m_centerWidth, 4, 4, 4, 4); + grid->addMultiCellWidget(m_yInput, 5, 5, 0, 3); + grid->addMultiCellWidget(m_heightInput, 6, 6, 0, 3); + grid->addMultiCellWidget(m_centerHeight, 6, 6, 4, 4); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + // ------------------------------------------------------------- + + TQFrame* compositionGuide = new TQFrame(m_gboxSettings->plainPage()); + TQGridLayout* grid2 = new TQGridLayout(compositionGuide, 8, 3); + compositionGuide->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + + TQLabel *labelGuideLines = new TQLabel(i18n("Composition guide:"), compositionGuide); + m_guideLinesCB = new RComboBox(compositionGuide); + m_guideLinesCB->insertItem( i18n("Rules of Thirds")); + m_guideLinesCB->insertItem( i18n("Diagonal Method")); + m_guideLinesCB->insertItem( i18n("Harmonious Triangles")); + m_guideLinesCB->insertItem( i18n("Golden Mean")); + m_guideLinesCB->insertItem( i18n("None")); + m_guideLinesCB->setDefaultItem(ImageSelectionWidget::GuideNone); + TQWhatsThis::add( m_guideLinesCB, i18n("

    With this option, you can display guide lines " + "which help you to compose your photograph.")); + + m_goldenSectionBox = new TQCheckBox(i18n("Golden sections"), compositionGuide); + TQWhatsThis::add( m_goldenSectionBox, i18n("

    Enable this option to show golden sections.")); + + m_goldenSpiralSectionBox = new TQCheckBox(i18n("Golden spiral sections"), compositionGuide); + TQWhatsThis::add( m_goldenSpiralSectionBox, i18n("

    Enable this option to show golden spiral sections.")); + + m_goldenSpiralBox = new TQCheckBox(i18n("Golden spiral"), compositionGuide); + TQWhatsThis::add( m_goldenSpiralBox, i18n("

    Enable this option to show golden spiral guide.")); + + m_goldenTriangleBox = new TQCheckBox(i18n("Golden triangles"), compositionGuide); + TQWhatsThis::add( m_goldenTriangleBox, i18n("

    Enable this option to show golden triangles.")); + + m_flipHorBox = new TQCheckBox(i18n("Flip horizontally"), compositionGuide); + TQWhatsThis::add( m_flipHorBox, i18n("

    Enable this option to flip horizontally guidelines.")); + + m_flipVerBox = new TQCheckBox(i18n("Flip vertically"), compositionGuide); + TQWhatsThis::add( m_flipVerBox, i18n("

    Enable this option to flip vertically guidelines.")); + + m_colorGuideLabel = new TQLabel(i18n("Color and width:"), compositionGuide); + m_guideColorBt = new KColorButton(TQColor(250, 250, 255), compositionGuide); + m_guideSize = new RIntNumInput(compositionGuide); + m_guideSize->input()->setRange(1, 5, 1, false); + m_guideSize->setDefaultValue(1); + TQWhatsThis::add( m_guideColorBt, i18n("

    Set here the color used to draw composition guides.")); + TQWhatsThis::add( m_guideSize, i18n("

    Set here the width in pixels used to draw composition guides.")); + + grid2->addMultiCellWidget(labelGuideLines, 0, 0, 0, 0); + grid2->addMultiCellWidget(m_guideLinesCB, 0, 0, 1, 2); + grid2->addMultiCellWidget(m_goldenSectionBox, 1, 1, 0, 2); + grid2->addMultiCellWidget(m_goldenSpiralSectionBox, 2, 2, 0, 2); + grid2->addMultiCellWidget(m_goldenSpiralBox, 3, 3, 0, 2); + grid2->addMultiCellWidget(m_goldenTriangleBox, 4, 4, 0, 2); + grid2->addMultiCellWidget(m_flipHorBox, 5, 5, 0, 2); + grid2->addMultiCellWidget(m_flipVerBox, 6, 6, 0, 2); + grid2->addMultiCellWidget(m_colorGuideLabel, 7, 7, 0, 0); + grid2->addMultiCellWidget(m_guideColorBt, 7, 7, 1, 1); + grid2->addMultiCellWidget(m_guideSize, 7, 7, 2, 2); + grid2->setMargin(m_gboxSettings->spacingHint()); + grid2->setSpacing(m_gboxSettings->spacingHint()); + + + // ------------------------------------------------------------- + + gboxLayout->addMultiCellWidget(cropSelection, 0, 0, 0, 1); + gboxLayout->addMultiCellWidget(compositionGuide, 1, 1, 0, 1); + gboxLayout->setRowStretch(2, 10); + gboxLayout->setMargin(m_gboxSettings->spacingHint()); + gboxLayout->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_ratioCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotRatioChanged(int))); + + connect(m_preciseCrop, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotPreciseCropChanged(bool))); + + connect(m_orientCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotOrientChanged(int))); + + connect(m_autoOrientation, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotAutoOrientChanged(bool))); + + connect(m_xInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotXChanged(int))); + + connect(m_yInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotYChanged(int))); + + connect(m_customRatioNInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotCustomNRatioChanged(int))); + + connect(m_customRatioDInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotCustomDRatioChanged(int))); + + connect(m_guideLinesCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotGuideTypeChanged(int))); + + connect(m_goldenSectionBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenSpiralSectionBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenSpiralBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_goldenTriangleBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_flipHorBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_flipVerBox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotGoldenGuideTypeChanged())); + + connect(m_guideColorBt, TQ_SIGNAL(changed(const TQColor&)), + m_imageSelectionWidget, TQ_SLOT(slotChangeGuideColor(const TQColor&))); + + connect(m_guideSize, TQ_SIGNAL(valueChanged(int)), + m_imageSelectionWidget, TQ_SLOT(slotChangeGuideSize(int))); + + connect(m_widthInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotWidthChanged(int))); + + connect(m_heightInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotHeightChanged(int))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionChanged(TQRect)), + this, TQ_SLOT(slotSelectionChanged(TQRect))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionMoved(TQRect)), + this, TQ_SLOT(slotSelectionChanged(TQRect))); + + connect(m_imageSelectionWidget, TQ_SIGNAL(signalSelectionOrientationChanged(int)), + this, TQ_SLOT(slotSelectionOrientationChanged(int))); + + connect(m_centerWidth, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCenterWidth())); + + connect(m_centerHeight, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotCenterHeight())); + + // we need to disconnect the standard connection of the Try button first + disconnect(m_gboxSettings, TQ_SIGNAL(signalTryClicked()), + this, TQ_SLOT(slotEffect())); + + connect(m_gboxSettings, TQ_SIGNAL(signalTryClicked()), + this, TQ_SLOT(slotMaxAspectRatio())); + + // ------------------------------------------------------------- + + // Sets current region selection + slotSelectionChanged(m_imageSelectionWidget->getRegionSelection()); +} + +RatioCropTool::~RatioCropTool() +{ +} + +void RatioCropTool::readSettings() +{ + TQColor defaultGuideColor(250, 250, 255); + TDEConfig *config = kapp->config(); + config->setGroup("aspectratiocrop Tool"); + + // No guide lines per default. + m_guideLinesCB->setCurrentItem(config->readNumEntry("Guide Lines Type", ImageSelectionWidget::GuideNone)); + m_goldenSectionBox->setChecked(config->readBoolEntry("Golden Section", true)); + m_goldenSpiralSectionBox->setChecked(config->readBoolEntry("Golden Spiral Section", false)); + m_goldenSpiralBox->setChecked(config->readBoolEntry("Golden Spiral", false)); + m_goldenTriangleBox->setChecked(config->readBoolEntry("Golden Triangle", false)); + m_flipHorBox->setChecked(config->readBoolEntry("Golden Flip Horizontal", false)); + m_flipVerBox->setChecked(config->readBoolEntry("Golden Flip Vertical", false)); + m_guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor)); + m_guideSize->setValue(config->readNumEntry("Guide Width", m_guideSize->defaultValue())); + m_imageSelectionWidget->slotGuideLines(m_guideLinesCB->currentItem()); + m_imageSelectionWidget->slotChangeGuideColor(m_guideColorBt->color()); + + m_preciseCrop->setChecked(config->readBoolEntry("Precise Aspect Ratio Crop", false)); + m_imageSelectionWidget->setPreciseCrop(m_preciseCrop->isChecked()); + + // Empty selection so it can be moved w/out size constraint + m_widthInput->setValue(0); + m_heightInput->setValue(0); + + m_xInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Xpos", + m_xInput->defaultValue())); + m_yInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Ypos", + m_yInput->defaultValue())); + + m_widthInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Width", + m_widthInput->defaultValue())); + m_heightInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Height", + m_heightInput->defaultValue())); + + m_imageSelectionWidget->setSelectionOrientation(m_orientCB->currentItem()); + + m_customRatioNInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Num", + m_customRatioNInput->defaultValue())); + m_customRatioDInput->setValue(config->readNumEntry("Hor.Oriented Custom Aspect Ratio Den", + m_customRatioDInput->defaultValue())); + + m_ratioCB->setCurrentItem(config->readNumEntry("Hor.Oriented Aspect Ratio", + m_ratioCB->defaultItem())); + + if (m_originalIsLandscape) + { + m_orientCB->setCurrentItem(config->readNumEntry("Hor.Oriented Aspect Ratio Orientation", + ImageSelectionWidget::Landscape)); + m_orientCB->setDefaultItem(ImageSelectionWidget::Landscape); + } + else + { + m_orientCB->setCurrentItem(config->readNumEntry("Ver.Oriented Aspect Ratio Orientation", + ImageSelectionWidget::Portrait)); + m_orientCB->setDefaultItem(ImageSelectionWidget::Portrait); + } + + applyRatioChanges(m_ratioCB->currentItem()); + + m_autoOrientation->setChecked( config->readBoolEntry("Auto Orientation", false) ); + slotAutoOrientChanged( m_autoOrientation->isChecked() ); +} + +void RatioCropTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("aspectratiocrop Tool"); + + if (m_originalIsLandscape) + { + config->writeEntry("Hor.Oriented Aspect Ratio", m_ratioCB->currentItem()); + config->writeEntry("Hor.Oriented Aspect Ratio Orientation", m_orientCB->currentItem()); + config->writeEntry("Hor.Oriented Custom Aspect Ratio Num", m_customRatioNInput->value()); + config->writeEntry("Hor.Oriented Custom Aspect Ratio Den", m_customRatioDInput->value()); + + config->writeEntry("Hor.Oriented Custom Aspect Ratio Xpos", m_xInput->value()); + config->writeEntry("Hor.Oriented Custom Aspect Ratio Ypos", m_yInput->value()); + config->writeEntry("Hor.Oriented Custom Aspect Ratio Width", m_widthInput->value()); + config->writeEntry("Hor.Oriented Custom Aspect Ratio Height", m_heightInput->value()); + } + else + { + config->writeEntry("Ver.Oriented Aspect Ratio", m_ratioCB->currentItem()); + config->writeEntry("Ver.Oriented Aspect Ratio Orientation", m_orientCB->currentItem()); + config->writeEntry("Ver.Oriented Custom Aspect Ratio Num", m_customRatioNInput->value()); + config->writeEntry("Ver.Oriented Custom Aspect Ratio Den", m_customRatioDInput->value()); + + config->writeEntry("Ver.Oriented Custom Aspect Ratio Xpos", m_xInput->value()); + config->writeEntry("Ver.Oriented Custom Aspect Ratio Ypos", m_yInput->value()); + config->writeEntry("Ver.Oriented Custom Aspect Ratio Width", m_widthInput->value()); + config->writeEntry("Ver.Oriented Custom Aspect Ratio Height", m_heightInput->value()); + } + + config->writeEntry("Precise Aspect Ratio Crop", m_preciseCrop->isChecked()); + config->writeEntry("Auto Orientation", m_autoOrientation->isChecked()); + config->writeEntry("Guide Lines Type", m_guideLinesCB->currentItem()); + config->writeEntry("Golden Section", m_goldenSectionBox->isChecked()); + config->writeEntry("Golden Spiral Section", m_goldenSpiralSectionBox->isChecked()); + config->writeEntry("Golden Spiral", m_goldenSpiralBox->isChecked()); + config->writeEntry("Golden Triangle", m_goldenTriangleBox->isChecked()); + config->writeEntry("Golden Flip Horizontal", m_flipHorBox->isChecked()); + config->writeEntry("Golden Flip Vertical", m_flipVerBox->isChecked()); + config->writeEntry("Guide Color", m_guideColorBt->color()); + config->writeEntry("Guide Width", m_guideSize->value()); + config->sync(); +} + +void RatioCropTool::slotResetSettings() +{ + m_imageSelectionWidget->resetSelection(); +} + +void RatioCropTool::slotMaxAspectRatio() +{ + m_imageSelectionWidget->maxAspectSelection(); +} + +void RatioCropTool::slotCenterWidth() +{ + m_imageSelectionWidget->setCenterSelection(ImageSelectionWidget::CenterWidth); +} + +void RatioCropTool::slotCenterHeight() +{ + m_imageSelectionWidget->setCenterSelection(ImageSelectionWidget::CenterHeight); +} + +void RatioCropTool::slotSelectionChanged(TQRect rect) +{ + m_xInput->blockSignals(true); + m_yInput->blockSignals(true); + m_widthInput->blockSignals(true); + m_heightInput->blockSignals(true); + + m_xInput->setRange(0, m_imageSelectionWidget->getOriginalImageWidth() - rect.width(), 1); + m_yInput->setRange(0, m_imageSelectionWidget->getOriginalImageHeight() - rect.height(), 1); + + m_widthInput->setRange(m_imageSelectionWidget->getMinWidthRange(), + m_imageSelectionWidget->getMaxWidthRange(), + m_imageSelectionWidget->getWidthStep()); + + m_heightInput->setRange(m_imageSelectionWidget->getMinHeightRange(), + m_imageSelectionWidget->getMaxHeightRange(), + m_imageSelectionWidget->getHeightStep()); + + m_xInput->setValue(rect.x()); + m_yInput->setValue(rect.y()); + m_widthInput->setValue(rect.width()); + m_heightInput->setValue(rect.height()); + + m_gboxSettings->enableButton(EditorToolSettings::Ok, rect.isValid()); + m_preciseCrop->setEnabled(m_imageSelectionWidget->preciseCropAvailable()); + + m_xInput->blockSignals(false); + m_yInput->blockSignals(false); + m_widthInput->blockSignals(false); + m_heightInput->blockSignals(false); +} + +void RatioCropTool::setRatioCBText(int orientation) +{ + int item = m_ratioCB->currentItem(); + + m_ratioCB->blockSignals(true); + m_ratioCB->combo()->clear(); + m_ratioCB->insertItem(i18n("Custom")); + m_ratioCB->insertItem("1:1"); + if (orientation == ImageSelectionWidget::Landscape) + { + m_ratioCB->insertItem("3:2"); + m_ratioCB->insertItem("4:3"); + m_ratioCB->insertItem("5:4"); + m_ratioCB->insertItem("7:5"); + m_ratioCB->insertItem("10:7"); + } + else + { + m_ratioCB->insertItem("2:3"); + m_ratioCB->insertItem("3:4"); + m_ratioCB->insertItem("4:5"); + m_ratioCB->insertItem("5:7"); + m_ratioCB->insertItem("7:10"); + } + m_ratioCB->insertItem(i18n("Golden Ratio")); + m_ratioCB->insertItem(i18n("None")); + m_ratioCB->setCurrentItem(item); + m_ratioCB->blockSignals(false); +} + +void RatioCropTool::slotSelectionOrientationChanged(int newOrientation) +{ + // Change text for Aspect ratio ComboBox + + setRatioCBText(newOrientation); + + // Change Orientation ComboBox + + m_orientCB->setCurrentItem(newOrientation); + + // Reverse custom values + + if ( ( m_customRatioNInput->value() < m_customRatioDInput->value() && + newOrientation == ImageSelectionWidget::Landscape) || + ( m_customRatioNInput->value() > m_customRatioDInput->value() && + newOrientation == ImageSelectionWidget::Portrait)) + { + m_customRatioNInput->blockSignals(true); + m_customRatioDInput->blockSignals(true); + + int tmp = m_customRatioNInput->value(); + m_customRatioNInput->setValue(m_customRatioDInput->value()); + m_customRatioDInput->setValue(tmp); + + m_customRatioNInput->blockSignals(false); + m_customRatioDInput->blockSignals(false); + } +} + +void RatioCropTool::slotXChanged(int x) +{ + m_imageSelectionWidget->setSelectionX(x); +} + +void RatioCropTool::slotYChanged(int y) +{ + m_imageSelectionWidget->setSelectionY(y); +} + +void RatioCropTool::slotWidthChanged(int w) +{ + m_imageSelectionWidget->setSelectionWidth(w); +} + +void RatioCropTool::slotHeightChanged(int h) +{ + m_imageSelectionWidget->setSelectionHeight(h); +} + +void RatioCropTool::slotPreciseCropChanged(bool a) +{ + m_imageSelectionWidget->setPreciseCrop(a); +} + +void RatioCropTool::slotOrientChanged(int o) +{ + m_imageSelectionWidget->setSelectionOrientation(o); + + // Reset selection area. + slotResetSettings(); +} + +void RatioCropTool::slotAutoOrientChanged(bool a) +{ + m_orientCB->setEnabled(!a /*|| m_ratioCB->currentItem() == ImageSelectionWidget::RATIONONE*/); + m_imageSelectionWidget->setAutoOrientation(a); +} + +void RatioCropTool::slotRatioChanged(int a) +{ + applyRatioChanges(a); + + // Reset selection area. + slotResetSettings(); +} + +void RatioCropTool::applyRatioChanges(int a) +{ + m_imageSelectionWidget->setSelectionAspectRatioType(a); + + if (a == ImageSelectionWidget::RATIOCUSTOM) + { + m_customLabel1->setEnabled(true); + m_customLabel2->setEnabled(true); + m_customRatioNInput->setEnabled(true); + m_customRatioDInput->setEnabled(true); + m_orientLabel->setEnabled(true); + m_orientCB->setEnabled(!m_autoOrientation->isChecked()); + m_autoOrientation->setEnabled(true); + slotCustomRatioChanged(); + } + else if (a == ImageSelectionWidget::RATIONONE) + { + m_orientLabel->setEnabled(false); + m_orientCB->setEnabled(false); + m_autoOrientation->setEnabled(false); + m_customLabel1->setEnabled(false); + m_customLabel2->setEnabled(false); + m_customRatioNInput->setEnabled(false); + m_customRatioDInput->setEnabled(false); + } + else // Pre-config ratio selected. + { + m_orientLabel->setEnabled(true); + m_orientCB->setEnabled(!m_autoOrientation->isChecked()); + m_autoOrientation->setEnabled(true); + m_customLabel1->setEnabled(false); + m_customLabel2->setEnabled(false); + m_customRatioNInput->setEnabled(false); + m_customRatioDInput->setEnabled(false); + } +} + +void RatioCropTool::slotGuideTypeChanged(int t) +{ + if (t == ImageSelectionWidget::GuideNone) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(false); + m_flipVerBox->setEnabled(false); + m_colorGuideLabel->setEnabled(false); + m_guideColorBt->setEnabled(false); + m_guideSize->setEnabled(false); + } + else if (t == ImageSelectionWidget::RulesOfThirds) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(false); + m_flipVerBox->setEnabled(false); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + else if (t == ImageSelectionWidget::DiagonalMethod) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(false); + m_flipVerBox->setEnabled(false); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + else if (t == ImageSelectionWidget::HarmoniousTriangles) + { + m_goldenSectionBox->setEnabled(false); + m_goldenSpiralSectionBox->setEnabled(false); + m_goldenSpiralBox->setEnabled(false); + m_goldenTriangleBox->setEnabled(false); + m_flipHorBox->setEnabled(true); + m_flipVerBox->setEnabled(true); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + else + { + m_goldenSectionBox->setEnabled(true); + m_goldenSpiralSectionBox->setEnabled(true); + m_goldenSpiralBox->setEnabled(true); + m_goldenTriangleBox->setEnabled(true); + m_flipHorBox->setEnabled(true); + m_flipVerBox->setEnabled(true); + m_colorGuideLabel->setEnabled(true); + m_guideColorBt->setEnabled(true); + m_guideSize->setEnabled(true); + } + + m_imageSelectionWidget->setGoldenGuideTypes(m_goldenSectionBox->isChecked(), + m_goldenSpiralSectionBox->isChecked(), + m_goldenSpiralBox->isChecked(), + m_goldenTriangleBox->isChecked(), + m_flipHorBox->isChecked(), + m_flipVerBox->isChecked()); + m_imageSelectionWidget->slotGuideLines(t); +} + +void RatioCropTool::slotGoldenGuideTypeChanged() +{ + slotGuideTypeChanged(m_guideLinesCB->currentItem()); +} + +void RatioCropTool::slotCustomNRatioChanged(int a) +{ + if ( ! m_autoOrientation->isChecked() ) + { + if ( ( m_orientCB->currentItem() == ImageSelectionWidget::Portrait && + m_customRatioDInput->value() < a) || + ( m_orientCB->currentItem() == ImageSelectionWidget::Landscape && + m_customRatioDInput->value() > a)) + { + m_customRatioDInput->blockSignals(true); + m_customRatioDInput->setValue(a); + m_customRatioDInput->blockSignals(false); + } + } + + slotCustomRatioChanged(); +} + +void RatioCropTool::slotCustomDRatioChanged(int a) +{ + if ( ! m_autoOrientation->isChecked() ) + { + if ( ( m_orientCB->currentItem() == ImageSelectionWidget::Landscape && + m_customRatioNInput->value() < a) || + ( m_orientCB->currentItem() == ImageSelectionWidget::Portrait && + m_customRatioNInput->value() > a)) + { + m_customRatioNInput->blockSignals(true); + m_customRatioNInput->setValue(a); + m_customRatioNInput->blockSignals(false); + } + } + + slotCustomRatioChanged(); +} + +void RatioCropTool::slotCustomRatioChanged() +{ + m_imageSelectionWidget->setSelectionAspectRatioValue(m_customRatioNInput->value(), + m_customRatioDInput->value()); + + // Reset selection area. + slotResetSettings(); +} + +void RatioCropTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + TQRect currentRegion = m_imageSelectionWidget->getRegionSelection(); + ImageIface* iface = m_imageSelectionWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool a = iface->originalHasAlpha(); + bool sb = iface->originalSixteenBit(); + + TQRect normalizedRegion = currentRegion.normalize(); + if (normalizedRegion.right() > w) + normalizedRegion.setRight(w); + + if (normalizedRegion.bottom() > h) + normalizedRegion.setBottom(h); + + DImg imOrg(w, h, sb, a, data); + delete [] data; + imOrg.crop(normalizedRegion); + + iface->putOriginalImage(i18n("Aspect Ratio Crop"), imOrg.bits(), imOrg.width(), imOrg.height()); + + kapp->restoreOverrideCursor(); + writeSettings(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.h b/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.h new file mode 100644 index 00000000..833677da --- /dev/null +++ b/src/imageplugins/coreplugin/ratiocrop/ratiocroptool.h @@ -0,0 +1,135 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-06 + * Description : digiKam image editor Ratio Crop tool + * + * Copyright (C) 2007 by Jaromir Malenko + * Copyright (C) 2008 by Roberto Castagnola + * Copyright (C) 2004-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RATIOCROPTOOL_H +#define RATIOCROPTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQCheckBox; +class TQLabel; +class TQToolButton; + +class KColorButton; + +namespace KDcrawIface +{ +class RComboBox; +class RIntNumInput; +} + +namespace DigikamImagesPluginCore +{ + +class ImageSelectionWidget; + +class RatioCropTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + RatioCropTool(TQObject *parent); + ~RatioCropTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + + void applyRatioChanges(int a); + void setRatioCBText(int orientation); + +private slots: + + void slotMaxAspectRatio(); + void slotResetSettings(); + + void slotCenterWidth(); + void slotCenterHeight(); + void slotXChanged(int x); + void slotYChanged(int y); + void slotWidthChanged(int w); + void slotHeightChanged(int h); + void slotCustomRatioChanged(); + void slotCustomNRatioChanged(int a); + void slotCustomDRatioChanged(int a); + void slotPreciseCropChanged(bool a); + void slotOrientChanged(int o); + void slotAutoOrientChanged(bool a); + void slotRatioChanged(int a); + void slotSelectionChanged(TQRect rect ); + void slotSelectionOrientationChanged(int); + void slotGuideTypeChanged(int t); + void slotGoldenGuideTypeChanged(); + +private: + + bool m_originalIsLandscape; + + TQLabel *m_customLabel1; + TQLabel *m_customLabel2; + TQLabel *m_orientLabel; + TQLabel *m_colorGuideLabel; + + + TQToolButton *m_centerWidth; + TQToolButton *m_centerHeight; + + TQCheckBox *m_goldenSectionBox; + TQCheckBox *m_goldenSpiralSectionBox; + TQCheckBox *m_goldenSpiralBox; + TQCheckBox *m_goldenTriangleBox; + TQCheckBox *m_flipHorBox; + TQCheckBox *m_flipVerBox; + TQCheckBox *m_autoOrientation; + TQCheckBox *m_preciseCrop; + + KDcrawIface::RComboBox *m_guideLinesCB; + KDcrawIface::RComboBox *m_orientCB; + KDcrawIface::RComboBox *m_ratioCB; + + KDcrawIface::RIntNumInput *m_customRatioDInput; + KDcrawIface::RIntNumInput *m_customRatioNInput; + KDcrawIface::RIntNumInput *m_guideSize; + KDcrawIface::RIntNumInput *m_heightInput; + KDcrawIface::RIntNumInput *m_widthInput; + KDcrawIface::RIntNumInput *m_xInput; + KDcrawIface::RIntNumInput *m_yInput; + + KColorButton *m_guideColorBt; + + ImageSelectionWidget *m_imageSelectionWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* RATIOCROPTOOL_H */ diff --git a/src/imageplugins/coreplugin/redeyetool.cpp b/src/imageplugins/coreplugin/redeyetool.cpp new file mode 100644 index 00000000..5b5af7e7 --- /dev/null +++ b/src/imageplugins/coreplugin/redeyetool.cpp @@ -0,0 +1,587 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-06 + * Description : Red eyes correction tool for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "bcgmodifier.h" +#include "colorgradientwidget.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imageiface.h" +#include "imagewidget.h" + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "redeyetool.h" +#include "redeyetool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +RedEyeTool::RedEyeTool(TQObject* parent) + : EditorTool(parent) +{ + setName("redeye"); + setToolName(i18n("Red Eye")); + setToolIcon(SmallIcon("redeyes")); + setToolHelp("redeyecorrectiontool.anchor"); + + m_destinationPreviewData = 0; + + m_previewWidget = new ImageWidget("redeye Tool", 0, + i18n("

    Here you can see the image selection preview with " + "red eye reduction applied."), + true, ImageGuideWidget::PickColorMode, true, true); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + EditorToolSettings *gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings->plainPage(), 11, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image channel values.

    " + "Green: display the green image channel values.

    " + "Blue: display the blue image channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximum counts are small, you can use the linear scale.

    " + "The logarithmic scale can be used when the maximal counts are big " + "to show all values (small and large) on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "of the selected image channel. It is " + "updated upon setting changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + // ------------------------------------------------------------- + + m_thresholdLabel = new TQLabel(i18n("Sensitivity:"), gboxSettings->plainPage()); + m_redThreshold = new RIntNumInput(gboxSettings->plainPage()); + m_redThreshold->setRange(10, 90, 1); + m_redThreshold->setDefaultValue(20); + TQWhatsThis::add(m_redThreshold, i18n("

    Sets the red color pixels selection threshold. " + "Low values will select more red color pixels (agressive correction), high " + "values less (mild correction). Use low value if eye have been selected " + "exactly. Use high value if other parts of the face are also selected.")); + + m_smoothLabel = new TQLabel(i18n("Smooth:"), gboxSettings->plainPage()); + m_smoothLevel = new RIntNumInput(gboxSettings->plainPage()); + m_smoothLevel->setRange(0, 5, 1); + m_smoothLevel->setDefaultValue(1); + TQWhatsThis::add(m_smoothLevel, i18n("

    Sets the smoothness value when blurring the border " + "of the changed pixels. " + "This leads to a more naturally looking pupil.")); + + TQLabel *label3 = new TQLabel(i18n("Coloring Tint:"), gboxSettings->plainPage()); + m_HSSelector = new KHSSelector(gboxSettings->plainPage()); + m_VSelector = new KValueSelector(gboxSettings->plainPage()); + m_HSSelector->setMinimumSize(200, 142); + m_VSelector->setMinimumSize(26, 142); + TQWhatsThis::add(m_HSSelector, i18n("

    Sets a custom color to re-colorize the eyes.")); + + TQLabel *label4 = new TQLabel(i18n("Tint Level:"), gboxSettings->plainPage()); + m_tintLevel = new RIntNumInput(gboxSettings->plainPage()); + m_tintLevel->setRange(1, 200, 1); + m_tintLevel->setDefaultValue(128); + TQWhatsThis::add(m_tintLevel, i18n("

    Set the tint level to adjust the luminosity of " + "the new color of the pupil.")); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + gridSettings->addMultiCellWidget(m_thresholdLabel, 3, 3, 0, 4); + gridSettings->addMultiCellWidget(m_redThreshold, 4, 4, 0, 4); + gridSettings->addMultiCellWidget(m_smoothLabel, 5, 5, 0, 4); + gridSettings->addMultiCellWidget(m_smoothLevel, 6, 6, 0, 4); + gridSettings->addMultiCellWidget(label3, 7, 7, 0, 4); + gridSettings->addMultiCellWidget(m_HSSelector, 8, 8, 0, 3); + gridSettings->addMultiCellWidget(m_VSelector, 8, 8, 4, 4); + gridSettings->addMultiCellWidget(label4, 9, 9, 0, 4); + gridSettings->addMultiCellWidget(m_tintLevel, 10, 10, 0, 4); + gridSettings->setRowStretch(11, 10); + gridSettings->setColStretch(3, 10); + + setToolSettings(gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + connect(m_redThreshold, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_smoothLevel, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_HSSelector, TQ_SIGNAL(valueChanged(int, int)), + this, TQ_SLOT(slotHSChanged(int, int))); + + connect(m_VSelector, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_tintLevel, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +RedEyeTool::~RedEyeTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void RedEyeTool::slotHSChanged(int h, int s) +{ + m_VSelector->blockSignals(true); + m_VSelector->setHue(h); + m_VSelector->setSaturation(s); + m_VSelector->updateContents(); + m_VSelector->repaint(false); + m_VSelector->blockSignals(false); + slotTimer(); +} + +void RedEyeTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void RedEyeTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void RedEyeTool::slotColorSelectedFromTarget(const DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void RedEyeTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("redeye Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + m_redThreshold->setValue(config->readNumEntry("RedThreshold", m_redThreshold->defaultValue())); + m_smoothLevel->setValue(config->readNumEntry("SmoothLevel", m_smoothLevel->defaultValue())); + m_HSSelector->setXValue(config->readNumEntry("HueColoringTint", 0)); + m_HSSelector->setYValue(config->readNumEntry("SatColoringTint", 0)); + m_VSelector->setValue(config->readNumEntry("ValColoringTint", 0)); + m_tintLevel->setValue(config->readNumEntry("TintLevel", m_tintLevel->defaultValue())); + + slotHSChanged(m_HSSelector->xValue(), m_HSSelector->yValue()); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void RedEyeTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("redeye Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("RedThreshold", m_redThreshold->value()); + config->writeEntry("SmoothLevel", m_smoothLevel->value()); + config->writeEntry("HueColoringTint", m_HSSelector->xValue()); + config->writeEntry("SatColoringTint", m_HSSelector->yValue()); + config->writeEntry("ValColoringTint", m_VSelector->value()); + config->writeEntry("TintLevel", m_tintLevel->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void RedEyeTool::slotResetSettings() +{ + m_redThreshold->blockSignals(true); + m_HSSelector->blockSignals(true); + m_VSelector->blockSignals(true); + m_tintLevel->blockSignals(true); + + m_redThreshold->slotReset(); + m_smoothLevel->slotReset(); + + // Black color by default + m_HSSelector->setXValue(0); + m_HSSelector->setYValue(0); + m_VSelector->setValue(0); + + m_tintLevel->slotReset(); + + m_redThreshold->blockSignals(false); + m_HSSelector->blockSignals(false); + m_VSelector->blockSignals(false); + m_tintLevel->blockSignals(false); +} + +void RedEyeTool::slotEffect() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + // Here, we need to use the real selection image data because we will apply + // a Gaussian blur filter on pixels and we cannot use directly the preview scaled image + // else the blur radius will not give the same result between preview and final rendering. + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getImageSelection(); + int w = iface->selectedWidth(); + int h = iface->selectedHeight(); + bool sb = iface->originalSixteenBit(); + bool a = iface->originalHasAlpha(); + DImg selection(w, h, sb, a, m_destinationPreviewData); + + redEyeFilter(selection); + + DImg preview = selection.smoothScale(iface->previewWidth(), iface->previewHeight()); + + iface->putPreviewImage(preview.bits()); + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, selection.bits(), selection.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void RedEyeTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getImageSelection(); + int w = iface->selectedWidth(); + int h = iface->selectedHeight(); + bool sixteenBit = iface->originalSixteenBit(); + bool hasAlpha = iface->originalHasAlpha(); + DImg selection(w, h, sixteenBit, hasAlpha, data); + delete [] data; + + redEyeFilter(selection); + + iface->putImageSelection(i18n("Red Eyes Correction"), selection.bits()); + + kapp->restoreOverrideCursor(); +} + +void RedEyeTool::redEyeFilter(DImg& selection) +{ + DImg mask(selection.width(), selection.height(), selection.sixteenBit(), true, + selection.bits(), true); + + selection = mask.copy(); + float redThreshold = m_redThreshold->value()/10.0; + int hue = m_HSSelector->xValue(); + int sat = m_HSSelector->yValue(); + int val = m_VSelector->value(); + KColor coloring; + coloring.setHsv(hue, sat, val); + + struct channel + { + float red_gain; + float green_gain; + float blue_gain; + }; + + channel red_chan, green_chan, blue_chan; + + red_chan.red_gain = 0.1; + red_chan.green_gain = 0.6; + red_chan.blue_gain = 0.3; + + green_chan.red_gain = 0.0; + green_chan.green_gain = 1.0; + green_chan.blue_gain = 0.0; + + blue_chan.red_gain = 0.0; + blue_chan.green_gain = 0.0; + blue_chan.blue_gain = 1.0; + + float red_norm, green_norm, blue_norm; + int level = 201 - m_tintLevel->value(); + + red_norm = 1.0 / (red_chan.red_gain + red_chan.green_gain + red_chan.blue_gain); + green_norm = 1.0 / (green_chan.red_gain + green_chan.green_gain + green_chan.blue_gain); + blue_norm = 1.0 / (blue_chan.red_gain + blue_chan.green_gain + blue_chan.blue_gain); + + red_norm *= coloring.red() / level; + green_norm *= coloring.green() / level; + blue_norm *= coloring.blue() / level; + + // Perform a red color pixels detection in selection image and create a correction mask using an alpha channel. + + if (!selection.sixteenBit()) // 8 bits image. + { + uchar* ptr = selection.bits(); + uchar* mptr = mask.bits(); + uchar r, g, b, r1, g1, b1; + + for (uint i = 0 ; i < selection.width() * selection.height() ; i++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + mptr[3] = 255; + + if (r >= ( redThreshold * g)) + { + r1 = TQMIN(255, (int)(red_norm * (red_chan.red_gain * r + + red_chan.green_gain * g + + red_chan.blue_gain * b))); + + g1 = TQMIN(255, (int)(green_norm * (green_chan.red_gain * r + + green_chan.green_gain * g + + green_chan.blue_gain * b))); + + b1 = TQMIN(255, (int)(blue_norm * (blue_chan.red_gain * r + + blue_chan.green_gain * g + + blue_chan.blue_gain * b))); + + mptr[0] = b1; + mptr[1] = g1; + mptr[2] = r1; + mptr[3] = TQMIN( (int)((r-g) / 150.0 * 255.0), 255); + } + + ptr += 4; + mptr+= 4; + } + } + else // 16 bits image. + { + unsigned short* ptr = (unsigned short*)selection.bits(); + unsigned short* mptr = (unsigned short*)mask.bits(); + unsigned short r, g, b, r1, g1, b1; + + for (uint i = 0 ; i < selection.width() * selection.height() ; i++) + { + b = ptr[0]; + g = ptr[1]; + r = ptr[2]; + mptr[3] = 65535; + + if (r >= ( redThreshold * g)) + { + r1 = TQMIN(65535, (int)(red_norm * (red_chan.red_gain * r + + red_chan.green_gain * g + + red_chan.blue_gain * b))); + + g1 = TQMIN(65535, (int)(green_norm * (green_chan.red_gain * r + + green_chan.green_gain * g + + green_chan.blue_gain * b))); + + b1 = TQMIN(65535, (int)(blue_norm * (blue_chan.red_gain * r + + blue_chan.green_gain * g + + blue_chan.blue_gain * b))); + + mptr[0] = b1; + mptr[1] = g1; + mptr[2] = r1; + mptr[3] = TQMIN( (int)((r-g) / 38400.0 * 65535.0), 65535);; + } + + ptr += 4; + mptr+= 4; + } + } + + // Now, we will blur only the transparency pixels from the mask. + + DImg mask2 = mask.copy(); + DImgImageFilters filter; + filter.gaussianBlurImage(mask2.bits(), mask2.width(), mask2.height(), + mask2.sixteenBit(), m_smoothLevel->value()); + + if (!selection.sixteenBit()) // 8 bits image. + { + uchar* mptr = mask.bits(); + uchar* mptr2 = mask2.bits(); + + for (uint i = 0 ; i < mask2.width() * mask2.height() ; i++) + { + if (mptr2[3] < 255) + { + mptr[0] = mptr2[0]; + mptr[1] = mptr2[1]; + mptr[2] = mptr2[2]; + mptr[3] = mptr2[3]; + } + + mptr += 4; + mptr2+= 4; + } + } + else // 16 bits image. + { + unsigned short* mptr = (unsigned short*)mask.bits(); + unsigned short* mptr2 = (unsigned short*)mask2.bits(); + + for (uint i = 0 ; i < mask2.width() * mask2.height() ; i++) + { + if (mptr2[3] < 65535) + { + mptr[0] = mptr2[0]; + mptr[1] = mptr2[1]; + mptr[2] = mptr2[2]; + mptr[3] = mptr2[3]; + } + + mptr += 4; + mptr2+= 4; + } + } + + // - Perform pixels blending using alpha channel between the mask and the selection. + + DColorComposer *composer = DColorComposer::getComposer(DColorComposer::PorterDuffSrcOver); + + // NOTE: 'mask' is the Source image, 'selection' is the Destination image. + + selection.bitBlendImage(composer, &mask, + 0, 0, mask.width(), mask.height(), + 0, 0); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/redeyetool.h b/src/imageplugins/coreplugin/redeyetool.h new file mode 100644 index 00000000..eb614fd3 --- /dev/null +++ b/src/imageplugins/coreplugin/redeyetool.h @@ -0,0 +1,157 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-06-06 + * Description : Red eyes correction tool for image editor + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef REDEYETOOL_H +#define REDEYETOOL_H + +// KDE includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; +class TQComboBox; +class TQHButtonGroup; + +class KHSSelector; +class KValueSelector; + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class DImg; +} + +namespace DigikamImagesPluginCore +{ + +class RedEyePassivePopup : public KPassivePopup +{ +public: + + RedEyePassivePopup(TQWidget* parent) + : KPassivePopup(parent), m_parent(parent) + { + } + +protected: + + virtual void positionSelf() + { + move(m_parent->x() + 30, m_parent->y() + 30); + } + +private: + + TQWidget* m_parent; +}; + +// ---------------------------------------------------------------- + +class RedEyeTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + RedEyeTool(TQObject *parent); + ~RedEyeTool(); + +private slots: + + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotHSChanged(int h, int s); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + void redEyeFilter(Digikam::DImg& selection); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum RedThresold + { + Mild=0, + Aggressive + }; + + uchar *m_destinationPreviewData; + + TQLabel *m_thresholdLabel; + TQLabel *m_smoothLabel; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDcrawIface::RIntNumInput *m_tintLevel; + KDcrawIface::RIntNumInput *m_redThreshold; + KDcrawIface::RIntNumInput *m_smoothLevel; + + KHSSelector *m_HSSelector; + KValueSelector *m_VSelector; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* REDEYETOOL_H */ diff --git a/src/imageplugins/coreplugin/rgbtool.cpp b/src/imageplugins/coreplugin/rgbtool.cpp new file mode 100644 index 00000000..e27e396e --- /dev/null +++ b/src/imageplugins/coreplugin/rgbtool.cpp @@ -0,0 +1,440 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-11 + * Description : digiKam image editor Color Balance tool. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Digikam includes. + +#include "colorgradientwidget.h" +#include "colormodifier.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imageiface.h" +#include "imagewidget.h" + +// Local includes. + +#include "rgbtool.h" +#include "rgbtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +RGBTool::RGBTool(TQObject* parent) + : EditorTool(parent) +{ + setName("colorbalance"); + setToolName(i18n("Color Balance")); + setToolIcon(SmallIcon("adjustrgb")); + + m_destinationPreviewData = 0; + + m_previewWidget = new ImageWidget("colorbalance Tool", 0, + i18n("

    Here you can see the image " + "color-balance adjustments preview. " + "You can pick color on image " + "to see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 7, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_channelCB = new TQComboBox(false, m_gboxSettings->plainPage()); + m_channelCB->insertItem(i18n("Luminosity")); + m_channelCB->insertItem(i18n("Red")); + m_channelCB->insertItem(i18n("Green")); + m_channelCB->insertItem(i18n("Blue")); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin(0); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the graph.")); + + TQPushButton *linHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(linHistoButton, i18n("

    Linear")); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap(TQPixmap(directory + "histogram-lin.png")); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton(m_scaleBG); + TQToolTip::add(logHistoButton, i18n("

    Logarithmic")); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap(TQPixmap(directory + "histogram-log.png")); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + gridSettings->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram drawing " + "of the selected image channel. This one is re-computed at any " + "settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + m_hGradient->setColors(TQColor("black"), TQColor("white")); + + gridSettings->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + TQLabel *labelLeft = new TQLabel(i18n("Cyan"), m_gboxSettings->plainPage()); + labelLeft->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_rSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, m_gboxSettings->plainPage(), "m_rSlider"); + m_rSlider->setTickmarks(TQSlider::Below); + m_rSlider->setTickInterval(20); + TQWhatsThis::add( m_rSlider, i18n("

    Set here the cyan/red color adjustment of the image.")); + TQLabel *labelRight = new TQLabel(i18n("Red"), m_gboxSettings->plainPage()); + labelRight->setAlignment ( TQt::AlignLeft | TQt::AlignVCenter ); + m_rInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_rInput->setDefaultValue(0); + m_rInput->input()->setRange(-100, 100, 1, false); + + gridSettings->addMultiCellWidget(labelLeft, 3, 3, 0, 0); + gridSettings->addMultiCellWidget(m_rSlider, 3, 3, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 3, 3, 2, 2); + gridSettings->addMultiCellWidget(m_rInput, 3, 3, 3, 3); + + // ------------------------------------------------------------- + + labelLeft = new TQLabel(i18n("Magenta"), m_gboxSettings->plainPage()); + labelLeft->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + m_gSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, m_gboxSettings->plainPage(), "m_gSlider"); + m_gSlider->setTickmarks(TQSlider::Below); + m_gSlider->setTickInterval(20); + TQWhatsThis::add( m_gSlider, i18n("

    Set here the magenta/green color adjustment of the image.")); + labelRight = new TQLabel(i18n("Green"), m_gboxSettings->plainPage()); + labelRight->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + m_gInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_gInput->setDefaultValue(0); + m_gInput->input()->setRange(-100, 100, 1, false); + + gridSettings->addMultiCellWidget(labelLeft, 4, 4, 0, 0); + gridSettings->addMultiCellWidget(m_gSlider, 4, 4, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 4, 4, 2, 2); + gridSettings->addMultiCellWidget(m_gInput, 4, 4, 3, 3); + + // ------------------------------------------------------------- + + labelLeft = new TQLabel(i18n("Yellow"), m_gboxSettings->plainPage()); + labelLeft->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_bSlider = new TQSlider(-100, 100, 1, 0, TQt::Horizontal, m_gboxSettings->plainPage(), "m_bSlider"); + m_bSlider->setTickmarks(TQSlider::Below); + m_bSlider->setTickInterval(20); + TQWhatsThis::add( m_bSlider, i18n("

    Set here the yellow/blue color adjustment of the image.")); + labelRight = new TQLabel(i18n("Blue"), m_gboxSettings->plainPage()); + labelRight->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + m_bInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_bInput->setDefaultValue(0); + m_bInput->input()->setRange(-100, 100, 1, false); + + gridSettings->addMultiCellWidget(labelLeft, 5, 5, 0, 0); + gridSettings->addMultiCellWidget(m_bSlider, 5, 5, 1, 1); + gridSettings->addMultiCellWidget(labelRight, 5, 5, 2, 2); + gridSettings->addMultiCellWidget(m_bInput, 5, 5, 3, 3); + + m_rInput->setValue(0); + m_gInput->setValue(0); + m_bInput->setValue(0); + + gridSettings->setRowStretch(6, 10); + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_rSlider, TQ_SIGNAL(valueChanged(int)), + m_rInput, TQ_SLOT(setValue(int))); + connect(m_rInput, TQ_SIGNAL(valueChanged (int)), + m_rSlider, TQ_SLOT(setValue(int))); + connect(m_rInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gSlider, TQ_SIGNAL(valueChanged(int)), + m_gInput, TQ_SLOT(setValue(int))); + connect(m_gInput, TQ_SIGNAL(valueChanged (int)), + m_gSlider, TQ_SLOT(setValue(int))); + connect(m_gInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_bSlider, TQ_SIGNAL(valueChanged(int)), + m_bInput, TQ_SLOT(setValue(int))); + connect(m_bInput, TQ_SIGNAL(valueChanged (int)), + m_bSlider, TQ_SLOT(setValue(int))); + connect(m_bInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + + m_gboxSettings->enableButton(EditorToolSettings::Ok, false); +} + +RGBTool::~RGBTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void RGBTool::slotChannelChanged(int channel) +{ + switch (channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("white")); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("red")); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("green")); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors(TQColor("black"), TQColor("blue")); + break; + } + + m_histogramWidget->repaint(false); +} + +void RGBTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void RGBTool::slotColorSelectedFromTarget(const DColor &color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void RGBTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colorbalance Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + int r = config->readNumEntry("RedAjustment", m_rInput->defaultValue()); + int g = config->readNumEntry("GreenAjustment", m_gInput->defaultValue()); + int b = config->readNumEntry("BlueAjustment", m_bInput->defaultValue()); + adjustSliders(r, g, b); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void RGBTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("colorbalance Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + config->writeEntry("RedAjustment", m_rSlider->value()); + config->writeEntry("GreenAjustment", m_gInput->value()); + config->writeEntry("BlueAjustment", m_bInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void RGBTool::slotResetSettings() +{ + int r = m_rInput->defaultValue(); + int g = m_gInput->defaultValue(); + int b = m_bInput->defaultValue(); + + adjustSliders(r, g, b); +} + +void RGBTool::adjustSliders(int r, int g, int b) +{ + m_rSlider->blockSignals(true); + m_gSlider->blockSignals(true); + m_bSlider->blockSignals(true); + m_rInput->blockSignals(true); + m_gInput->blockSignals(true); + m_bInput->blockSignals(true); + + m_rSlider->setValue(r); + m_gSlider->setValue(g); + m_bSlider->setValue(b); + m_rInput->setValue(r); + m_gInput->setValue(g); + m_bInput->setValue(b); + + m_rSlider->blockSignals(false); + m_gSlider->blockSignals(false); + m_bSlider->blockSignals(false); + m_rInput->blockSignals(false); + m_gInput->blockSignals(false); + m_bInput->blockSignals(false); + + slotEffect(); +} + +void RGBTool::slotEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + m_gboxSettings->enableButton(EditorToolSettings::Ok, + (m_rInput->value() != 0 || + m_gInput->value() != 0 || + m_bInput->value() != 0)); + + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + ImageIface* iface = m_previewWidget->imageIface(); + m_destinationPreviewData = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool alpha = iface->previewHasAlpha(); + bool sixteenBit = iface->previewSixteenBit(); + + double r = ((double) m_rInput->value() + 100.0) / 100.0; + double g = ((double) m_gInput->value() + 100.0) / 100.0; + double b = ((double) m_bInput->value() + 100.0) / 100.0; + double a = 1.0; + + DImg preview(w, h, sixteenBit, alpha, m_destinationPreviewData); + ColorModifier cmod; + cmod.applyColorModifier(preview, r, g, b, a); + iface->putPreviewImage(preview.bits()); + + m_previewWidget->updatePreview(); + + // Update histogram. + + memcpy(m_destinationPreviewData, preview.bits(), preview.numBytes()); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sixteenBit, 0, 0, 0, false); + + kapp->restoreOverrideCursor(); +} + +void RGBTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + double r = ((double) m_rInput->value() + 100.0) / 100.0; + double g = ((double) m_gInput->value() + 100.0) / 100.0; + double b = ((double) m_bInput->value() + 100.0) / 100.0; + double a = 1.0; + + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool alpha = iface->originalHasAlpha(); + bool sixteenBit = iface->originalSixteenBit(); + DImg original(w, h, sixteenBit, alpha, data); + delete [] data; + + ColorModifier cmod; + cmod.applyColorModifier(original, r, g, b, a); + + iface->putOriginalImage(i18n("Color Balance"), original.bits()); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/rgbtool.h b/src/imageplugins/coreplugin/rgbtool.h new file mode 100644 index 00000000..dadb6a0e --- /dev/null +++ b/src/imageplugins/coreplugin/rgbtool.h @@ -0,0 +1,119 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-11 + * Description : digiKam image editor Color Balance tool. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RGBTOOL_H +#define RGBTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQComboBox; +class TQHButtonGroup; + +class TQSlider; + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamImagesPluginCore +{ + +class RGBTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + RGBTool(TQObject *parent); + ~RGBTool(); + +private: + + void writeSettings(); + void readSettings(); + void adjustSliders(int r, int g, int b); + void finalRendering(); + +private slots: + + void slotEffect(); + void slotResetSettings(); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorSelectedFromTarget( const Digikam::DColor &color ); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + uchar *m_destinationPreviewData; + + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + KDcrawIface::RIntNumInput *m_rInput; + KDcrawIface::RIntNumInput *m_gInput; + KDcrawIface::RIntNumInput *m_bInput; + + TQSlider *m_rSlider; + TQSlider *m_gSlider; + TQSlider *m_bSlider; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::EditorToolSettings *m_gboxSettings; + +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* RGBTOOL_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/Makefile.am b/src/imageplugins/coreplugin/sharpnesseditor/Makefile.am new file mode 100644 index 00000000..c02087ee --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS = clapack +COMPILE_FIRST = clapack + +noinst_LTLIBRARIES = libsharpnesseditor.la +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/imageplugins/coreplugin/sharpnesseditor/clapack \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +libsharpnesseditor_la_LIBADD = $(top_builddir)/src/imageplugins/coreplugin/sharpnesseditor/clapack/liblapack.la + +libsharpnesseditor_la_SOURCES = sharpentool.cpp unsharp.cpp matrix.cpp refocus.cpp + +libsharpnesseditor_la_LDFLAGS = $(all_libraries) + +noinst_HEADERS = sharpentool.h unsharp.h matrix.h refocus.h + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/LICENCE b/src/imageplugins/coreplugin/sharpnesseditor/clapack/LICENCE new file mode 100644 index 00000000..a338f860 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/LICENCE @@ -0,0 +1,12 @@ +REDISTRIBUTABLE + +LAPACK is a freely-available software package. It is available from netlib via anonymous ftp and the World Wide Web. +Thus, it can be included in commercial software packages (and has been). We only ask that proper credit be given to the authors. + +Like all software, it is copyrighted. It is not trademarked, but we do ask the following: + +If you modify the source for these routines we ask that you change the name of the routine +and comment the changes made to the original. + +We will gladly answer any questions regarding the software. If a modification is done, however, +it is the responsibility of the person who modified the routine to provide support. diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/Makefile.am b/src/imageplugins/coreplugin/sharpnesseditor/clapack/Makefile.am new file mode 100644 index 00000000..bf478556 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/Makefile.am @@ -0,0 +1,7 @@ +noinst_LTLIBRARIES = liblapack.la + +liblapack_la_CFLAGS = -w + +noinst_HEADERS = blaswrap.h clapack.h f2c.h fio.h fmt.h fp.h + +liblapack_la_SOURCES = abort_.c dgesv.c dlaswp.c endfile.c idamax.c open.c sig_die.c wref.c close.c dgetf2.c dscal.c err.c ieeeck.c s_cmp.c s_stop.c wrtfmt.c dgemm.c dgetrf.c dswap.c fmt.c ilaenv.c s_copy.c wsfe.c dger.c dgetrs.c dtrsm.c fmtlib.c lsame.c sfe.c util.c xerbla.c diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/README b/src/imageplugins/coreplugin/sharpnesseditor/clapack/README new file mode 100644 index 00000000..530f73ee --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/README @@ -0,0 +1,2 @@ +The sources in this directory were copied from the CLAPACK +distribution (see http://www.netlib.org/clapack). diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/abort_.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/abort_.c new file mode 100644 index 00000000..67278e93 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/abort_.c @@ -0,0 +1,10 @@ +#include "stdio.h" +#include "f2c.h" + +extern void sig_die(char*,int); + +int abort_(void) +{ +sig_die("Fortran abort routine called", 1); +return 0; /* not reached */ +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/blaswrap.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/blaswrap.h new file mode 100644 index 00000000..84c08d30 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/blaswrap.h @@ -0,0 +1,158 @@ +/* CLAPACK 3.0 BLAS wrapper macros + * Feb 5, 2000 + */ + +#ifndef __BLASWRAP_H +#define __BLASWRAP_H + +#ifndef NO_BLAS_WRAP + +/* BLAS1 routines */ +#define srotg_ f2c_srotg +#define drotg_ f2c_drotg +#define srotmg_ f2c_srotmg +#define drotmg_ f2c_drotmg +#define srot_ f2c_srot +#define drot_ f2c_drot +#define srotm_ f2c_srotm +#define drotm_ f2c_drotm +#define sswap_ f2c_sswap +#define dswap_ f2c_dswap +#define cswap_ f2c_cswap +#define zswap_ f2c_zswap +#define sscal_ f2c_sscal +#define dscal_ f2c_dscal +#define cscal_ f2c_cscal +#define zscal_ f2c_zscal +#define csscal_ f2c_csscal +#define zdscal_ f2c_zdscal +#define scopy_ f2c_scopy +#define dcopy_ f2c_dcopy +#define ccopy_ f2c_ccopy +#define zcopy_ f2c_zcopy +#define saxpy_ f2c_saxpy +#define daxpy_ f2c_daxpy +#define caxpy_ f2c_caxpy +#define zaxpy_ f2c_zaxpy +#define sdot_ f2c_sdot +#define ddot_ f2c_ddot +#define cdotu_ f2c_cdotu +#define zdotu_ f2c_zdotu +#define cdotc_ f2c_cdotc +#define zdotc_ f2c_zdotc +#define snrm2_ f2c_snrm2 +#define dnrm2_ f2c_dnrm2 +#define scnrm2_ f2c_scnrm2 +#define dznrm2_ f2c_dznrm2 +#define sasum_ f2c_sasum +#define dasum_ f2c_dasum +#define scasum_ f2c_scasum +#define dzasum_ f2c_dzasum +#define isamax_ f2c_isamax +#define idamax_ f2c_idamax +#define icamax_ f2c_icamax +#define izamax_ f2c_izamax + +/* BLAS2 routines */ +#define sgemv_ f2c_sgemv +#define dgemv_ f2c_dgemv +#define cgemv_ f2c_cgemv +#define zgemv_ f2c_zgemv +#define sgbmv_ f2c_sgbmv +#define dgbmv_ f2c_dgbmv +#define cgbmv_ f2c_cgbmv +#define zgbmv_ f2c_zgbmv +#define chemv_ f2c_chemv +#define zhemv_ f2c_zhemv +#define chbmv_ f2c_chbmv +#define zhbmv_ f2c_zhbmv +#define chpmv_ f2c_chpmv +#define zhpmv_ f2c_zhpmv +#define ssymv_ f2c_ssymv +#define dsymv_ f2c_dsymv +#define ssbmv_ f2c_ssbmv +#define dsbmv_ f2c_dsbmv +#define sspmv_ f2c_sspmv +#define dspmv_ f2c_dspmv +#define strmv_ f2c_strmv +#define dtrmv_ f2c_dtrmv +#define ctrmv_ f2c_ctrmv +#define ztrmv_ f2c_ztrmv +#define stbmv_ f2c_stbmv +#define dtbmv_ f2c_dtbmv +#define ctbmv_ f2c_ctbmv +#define ztbmv_ f2c_ztbmv +#define stpmv_ f2c_stpmv +#define dtpmv_ f2c_dtpmv +#define ctpmv_ f2c_ctpmv +#define ztpmv_ f2c_ztpmv +#define strsv_ f2c_strsv +#define dtrsv_ f2c_dtrsv +#define ctrsv_ f2c_ctrsv +#define ztrsv_ f2c_ztrsv +#define stbsv_ f2c_stbsv +#define dtbsv_ f2c_dtbsv +#define ctbsv_ f2c_ctbsv +#define ztbsv_ f2c_ztbsv +#define stpsv_ f2c_stpsv +#define dtpsv_ f2c_dtpsv +#define ctpsv_ f2c_ctpsv +#define ztpsv_ f2c_ztpsv +#define sger_ f2c_sger +#define dger_ f2c_dger +#define cgeru_ f2c_cgeru +#define zgeru_ f2c_zgeru +#define cgerc_ f2c_cgerc +#define zgerc_ f2c_zgerc +#define cher_ f2c_cher +#define zher_ f2c_zher +#define chpr_ f2c_chpr +#define zhpr_ f2c_zhpr +#define cher2_ f2c_cher2 +#define zher2_ f2c_zher2 +#define chpr2_ f2c_chpr2 +#define zhpr2_ f2c_zhpr2 +#define ssyr_ f2c_ssyr +#define dsyr_ f2c_dsyr +#define sspr_ f2c_sspr +#define dspr_ f2c_dspr +#define ssyr2_ f2c_ssyr2 +#define dsyr2_ f2c_dsyr2 +#define sspr2_ f2c_sspr2 +#define dspr2_ f2c_dspr2 + +/* BLAS3 routines */ +#define sgemm_ f2c_sgemm +#define dgemm_ f2c_dgemm +#define cgemm_ f2c_cgemm +#define zgemm_ f2c_zgemm +#define ssymm_ f2c_ssymm +#define dsymm_ f2c_dsymm +#define csymm_ f2c_csymm +#define zsymm_ f2c_zsymm +#define chemm_ f2c_chemm +#define zhemm_ f2c_zhemm +#define ssyrk_ f2c_ssyrk +#define dsyrk_ f2c_dsyrk +#define csyrk_ f2c_csyrk +#define zsyrk_ f2c_zsyrk +#define cherk_ f2c_cherk +#define zherk_ f2c_zherk +#define ssyr2k_ f2c_ssyr2k +#define dsyr2k_ f2c_dsyr2k +#define csyr2k_ f2c_csyr2k +#define zsyr2k_ f2c_zsyr2k +#define cher2k_ f2c_cher2k +#define zher2k_ f2c_zher2k +#define strmm_ f2c_strmm +#define dtrmm_ f2c_dtrmm +#define ctrmm_ f2c_ctrmm +#define ztrmm_ f2c_ztrmm +#define strsm_ f2c_strsm +#define dtrsm_ f2c_dtrsm +#define ctrsm_ f2c_ctrsm +#define ztrsm_ f2c_ztrsm + +#endif /* NO_BLAS_WRAP */ + +#endif /* __BLASWRAP_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/clapack.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/clapack.h new file mode 100644 index 00000000..cad9a4c2 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/clapack.h @@ -0,0 +1,5079 @@ +#ifndef __CLAPACK_H +#define __CLAPACK_H + +/* Subroutine */ int cbdsqr_(char *uplo, integer *n, integer *ncvt, integer * + nru, integer *ncc, real *d__, real *e, complex *vt, integer *ldvt, + complex *u, integer *ldu, complex *c__, integer *ldc, real *rwork, + integer *info); + +/* Subroutine */ int cgbbrd_(char *vect, integer *m, integer *n, integer *ncc, + integer *kl, integer *ku, complex *ab, integer *ldab, real *d__, + real *e, complex *q, integer *ldq, complex *pt, integer *ldpt, + complex *c__, integer *ldc, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgbcon_(char *norm, integer *n, integer *kl, integer *ku, + complex *ab, integer *ldab, integer *ipiv, real *anorm, real *rcond, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgbequ_(integer *m, integer *n, integer *kl, integer *ku, + complex *ab, integer *ldab, real *r__, real *c__, real *rowcnd, real + *colcnd, real *amax, integer *info); + +/* Subroutine */ int cgbrfs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, complex *ab, integer *ldab, complex *afb, integer * + ldafb, integer *ipiv, complex *b, integer *ldb, complex *x, integer * + ldx, real *ferr, real *berr, complex *work, real *rwork, integer * + info); + +/* Subroutine */ int cgbsv_(integer *n, integer *kl, integer *ku, integer * + nrhs, complex *ab, integer *ldab, integer *ipiv, complex *b, integer * + ldb, integer *info); + +/* Subroutine */ int cgbsvx_(char *fact, char *trans, integer *n, integer *kl, + integer *ku, integer *nrhs, complex *ab, integer *ldab, complex *afb, + integer *ldafb, integer *ipiv, char *equed, real *r__, real *c__, + complex *b, integer *ldb, complex *x, integer *ldx, real *rcond, real + *ferr, real *berr, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgbtf2_(integer *m, integer *n, integer *kl, integer *ku, + complex *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int cgbtrf_(integer *m, integer *n, integer *kl, integer *ku, + complex *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int cgbtrs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, complex *ab, integer *ldab, integer *ipiv, complex + *b, integer *ldb, integer *info); + +/* Subroutine */ int cgebak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, real *scale, integer *m, complex *v, integer *ldv, + integer *info); + +/* Subroutine */ int cgebal_(char *job, integer *n, complex *a, integer *lda, + integer *ilo, integer *ihi, real *scale, integer *info); + +/* Subroutine */ int cgebd2_(integer *m, integer *n, complex *a, integer *lda, + real *d__, real *e, complex *tauq, complex *taup, complex *work, + integer *info); + +/* Subroutine */ int cgebrd_(integer *m, integer *n, complex *a, integer *lda, + real *d__, real *e, complex *tauq, complex *taup, complex *work, + integer *lwork, integer *info); + +/* Subroutine */ int cgecon_(char *norm, integer *n, complex *a, integer *lda, + real *anorm, real *rcond, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgeequ_(integer *m, integer *n, complex *a, integer *lda, + real *r__, real *c__, real *rowcnd, real *colcnd, real *amax, + integer *info); + +/* Subroutine */ int cgees_(char *jobvs, char *sort, L_fp select, integer *n, + complex *a, integer *lda, integer *sdim, complex *w, complex *vs, + integer *ldvs, complex *work, integer *lwork, real *rwork, logical * + bwork, integer *info); + +/* Subroutine */ int cgeesx_(char *jobvs, char *sort, L_fp select, char * + sense, integer *n, complex *a, integer *lda, integer *sdim, complex * + w, complex *vs, integer *ldvs, real *rconde, real *rcondv, complex * + work, integer *lwork, real *rwork, logical *bwork, integer *info); + +/* Subroutine */ int cgeev_(char *jobvl, char *jobvr, integer *n, complex *a, + integer *lda, complex *w, complex *vl, integer *ldvl, complex *vr, + integer *ldvr, complex *work, integer *lwork, real *rwork, integer * + info); + +/* Subroutine */ int cgeevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, complex *a, integer *lda, complex *w, complex *vl, + integer *ldvl, complex *vr, integer *ldvr, integer *ilo, integer *ihi, + real *scale, real *abnrm, real *rconde, real *rcondv, complex *work, + integer *lwork, real *rwork, integer *info); + +/* Subroutine */ int cgegs_(char *jobvsl, char *jobvsr, integer *n, complex * + a, integer *lda, complex *b, integer *ldb, complex *alpha, complex * + beta, complex *vsl, integer *ldvsl, complex *vsr, integer *ldvsr, + complex *work, integer *lwork, real *rwork, integer *info); + +/* Subroutine */ int cgegv_(char *jobvl, char *jobvr, integer *n, complex *a, + integer *lda, complex *b, integer *ldb, complex *alpha, complex *beta, + complex *vl, integer *ldvl, complex *vr, integer *ldvr, complex * + work, integer *lwork, real *rwork, integer *info); + +/* Subroutine */ int cgehd2_(integer *n, integer *ilo, integer *ihi, complex * + a, integer *lda, complex *tau, complex *work, integer *info); + +/* Subroutine */ int cgehrd_(integer *n, integer *ilo, integer *ihi, complex * + a, integer *lda, complex *tau, complex *work, integer *lwork, integer + *info); + +/* Subroutine */ int cgelq2_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *info); + +/* Subroutine */ int cgelqf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgels_(char *trans, integer *m, integer *n, integer * + nrhs, complex *a, integer *lda, complex *b, integer *ldb, complex * + work, integer *lwork, integer *info); + +/* Subroutine */ int cgelsx_(integer *m, integer *n, integer *nrhs, complex * + a, integer *lda, complex *b, integer *ldb, integer *jpvt, real *rcond, + integer *rank, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgelsy_(integer *m, integer *n, integer *nrhs, complex * + a, integer *lda, complex *b, integer *ldb, integer *jpvt, real *rcond, + integer *rank, complex *work, integer *lwork, real *rwork, integer * + info); + +/* Subroutine */ int cgeql2_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *info); + +/* Subroutine */ int cgeqlf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgeqp3_(integer *m, integer *n, complex *a, integer *lda, + integer *jpvt, complex *tau, complex *work, integer *lwork, real * + rwork, integer *info); + +/* Subroutine */ int cgeqpf_(integer *m, integer *n, complex *a, integer *lda, + integer *jpvt, complex *tau, complex *work, real *rwork, integer * + info); + +/* Subroutine */ int cgeqr2_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *info); + +/* Subroutine */ int cgeqrf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgerfs_(char *trans, integer *n, integer *nrhs, complex * + a, integer *lda, complex *af, integer *ldaf, integer *ipiv, complex * + b, integer *ldb, complex *x, integer *ldx, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgerq2_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *info); + +/* Subroutine */ int cgerqf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgesc2_(integer *n, complex *a, integer *lda, complex * + rhs, integer *ipiv, integer *jpiv, real *scale); + +/* Subroutine */ int cgesv_(integer *n, integer *nrhs, complex *a, integer * + lda, integer *ipiv, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cgesvx_(char *fact, char *trans, integer *n, integer * + nrhs, complex *a, integer *lda, complex *af, integer *ldaf, integer * + ipiv, char *equed, real *r__, real *c__, complex *b, integer *ldb, + complex *x, integer *ldx, real *rcond, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgetc2_(integer *n, complex *a, integer *lda, integer * + ipiv, integer *jpiv, integer *info); + +/* Subroutine */ int cgetf2_(integer *m, integer *n, complex *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int cgetrf_(integer *m, integer *n, complex *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int cgetri_(integer *n, complex *a, integer *lda, integer * + ipiv, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgetrs_(char *trans, integer *n, integer *nrhs, complex * + a, integer *lda, integer *ipiv, complex *b, integer *ldb, integer * + info); + +/* Subroutine */ int cggbak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, real *lscale, real *rscale, integer *m, complex *v, + integer *ldv, integer *info); + +/* Subroutine */ int cggbal_(char *job, integer *n, complex *a, integer *lda, + complex *b, integer *ldb, integer *ilo, integer *ihi, real *lscale, + real *rscale, real *work, integer *info); + +/* Subroutine */ int cgges_(char *jobvsl, char *jobvsr, char *sort, L_fp + selctg, integer *n, complex *a, integer *lda, complex *b, integer * + ldb, integer *sdim, complex *alpha, complex *beta, complex *vsl, + integer *ldvsl, complex *vsr, integer *ldvsr, complex *work, integer * + lwork, real *rwork, logical *bwork, integer *info); + +/* Subroutine */ int cggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp + selctg, char *sense, integer *n, complex *a, integer *lda, complex *b, + integer *ldb, integer *sdim, complex *alpha, complex *beta, complex * + vsl, integer *ldvsl, complex *vsr, integer *ldvsr, real *rconde, real + *rcondv, complex *work, integer *lwork, real *rwork, integer *iwork, + integer *liwork, logical *bwork, integer *info); + +/* Subroutine */ int cggev_(char *jobvl, char *jobvr, integer *n, complex *a, + integer *lda, complex *b, integer *ldb, complex *alpha, complex *beta, + complex *vl, integer *ldvl, complex *vr, integer *ldvr, complex * + work, integer *lwork, real *rwork, integer *info); + +/* Subroutine */ int cggevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, complex *a, integer *lda, complex *b, integer *ldb, + complex *alpha, complex *beta, complex *vl, integer *ldvl, complex * + vr, integer *ldvr, integer *ilo, integer *ihi, real *lscale, real * + rscale, real *abnrm, real *bbnrm, real *rconde, real *rcondv, complex + *work, integer *lwork, real *rwork, integer *iwork, logical *bwork, + integer *info); + +/* Subroutine */ int cggglm_(integer *n, integer *m, integer *p, complex *a, + integer *lda, complex *b, integer *ldb, complex *d__, complex *x, + complex *y, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cgghrd_(char *compq, char *compz, integer *n, integer * + ilo, integer *ihi, complex *a, integer *lda, complex *b, integer *ldb, + complex *q, integer *ldq, complex *z__, integer *ldz, integer *info); + +/* Subroutine */ int cgglse_(integer *m, integer *n, integer *p, complex *a, + integer *lda, complex *b, integer *ldb, complex *c__, complex *d__, + complex *x, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cggqrf_(integer *n, integer *m, integer *p, complex *a, + integer *lda, complex *taua, complex *b, integer *ldb, complex *taub, + complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cggrqf_(integer *m, integer *p, integer *n, complex *a, + integer *lda, complex *taua, complex *b, integer *ldb, complex *taub, + complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cggsvd_(char *jobu, char *jobv, char *jobq, integer *m, + integer *n, integer *p, integer *k, integer *l, complex *a, integer * + lda, complex *b, integer *ldb, real *alpha, real *beta, complex *u, + integer *ldu, complex *v, integer *ldv, complex *q, integer *ldq, + complex *work, real *rwork, integer *iwork, integer *info); + +/* Subroutine */ int cggsvp_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, complex *a, integer *lda, complex *b, integer + *ldb, real *tola, real *tolb, integer *k, integer *l, complex *u, + integer *ldu, complex *v, integer *ldv, complex *q, integer *ldq, + integer *iwork, real *rwork, complex *tau, complex *work, integer * + info); + +/* Subroutine */ int cgtcon_(char *norm, integer *n, complex *dl, complex * + d__, complex *du, complex *du2, integer *ipiv, real *anorm, real * + rcond, complex *work, integer *info); + +/* Subroutine */ int cgtrfs_(char *trans, integer *n, integer *nrhs, complex * + dl, complex *d__, complex *du, complex *dlf, complex *df, complex * + duf, complex *du2, integer *ipiv, complex *b, integer *ldb, complex * + x, integer *ldx, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int cgtsv_(integer *n, integer *nrhs, complex *dl, complex * + d__, complex *du, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cgtsvx_(char *fact, char *trans, integer *n, integer * + nrhs, complex *dl, complex *d__, complex *du, complex *dlf, complex * + df, complex *duf, complex *du2, integer *ipiv, complex *b, integer * + ldb, complex *x, integer *ldx, real *rcond, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int cgttrf_(integer *n, complex *dl, complex *d__, complex * + du, complex *du2, integer *ipiv, integer *info); + +/* Subroutine */ int cgttrs_(char *trans, integer *n, integer *nrhs, complex * + dl, complex *d__, complex *du, complex *du2, integer *ipiv, complex * + b, integer *ldb, integer *info); + +/* Subroutine */ int cgtts2_(integer *itrans, integer *n, integer *nrhs, + complex *dl, complex *d__, complex *du, complex *du2, integer *ipiv, + complex *b, integer *ldb); + +/* Subroutine */ int chbev_(char *jobz, char *uplo, integer *n, integer *kd, + complex *ab, integer *ldab, real *w, complex *z__, integer *ldz, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int chbevd_(char *jobz, char *uplo, integer *n, integer *kd, + complex *ab, integer *ldab, real *w, complex *z__, integer *ldz, + complex *work, integer *lwork, real *rwork, integer *lrwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int chbevx_(char *jobz, char *range, char *uplo, integer *n, + integer *kd, complex *ab, integer *ldab, complex *q, integer *ldq, + real *vl, real *vu, integer *il, integer *iu, real *abstol, integer * + m, real *w, complex *z__, integer *ldz, complex *work, real *rwork, + integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int chbgst_(char *vect, char *uplo, integer *n, integer *ka, + integer *kb, complex *ab, integer *ldab, complex *bb, integer *ldbb, + complex *x, integer *ldx, complex *work, real *rwork, integer *info); + +/* Subroutine */ int chbgv_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, complex *ab, integer *ldab, complex *bb, integer *ldbb, + real *w, complex *z__, integer *ldz, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int chbgvx_(char *jobz, char *range, char *uplo, integer *n, + integer *ka, integer *kb, complex *ab, integer *ldab, complex *bb, + integer *ldbb, complex *q, integer *ldq, real *vl, real *vu, integer * + il, integer *iu, real *abstol, integer *m, real *w, complex *z__, + integer *ldz, complex *work, real *rwork, integer *iwork, integer * + ifail, integer *info); + +/* Subroutine */ int chbtrd_(char *vect, char *uplo, integer *n, integer *kd, + complex *ab, integer *ldab, real *d__, real *e, complex *q, integer * + ldq, complex *work, integer *info); + +/* Subroutine */ int checon_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, real *anorm, real *rcond, complex *work, integer * + info); + +/* Subroutine */ int cheev_(char *jobz, char *uplo, integer *n, complex *a, + integer *lda, real *w, complex *work, integer *lwork, real *rwork, + integer *info); + +/* Subroutine */ int cheevd_(char *jobz, char *uplo, integer *n, complex *a, + integer *lda, real *w, complex *work, integer *lwork, real *rwork, + integer *lrwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int cheevr_(char *jobz, char *range, char *uplo, integer *n, + complex *a, integer *lda, real *vl, real *vu, integer *il, integer * + iu, real *abstol, integer *m, real *w, complex *z__, integer *ldz, + integer *isuppz, complex *work, integer *lwork, real *rwork, integer * + lrwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int cheevx_(char *jobz, char *range, char *uplo, integer *n, + complex *a, integer *lda, real *vl, real *vu, integer *il, integer * + iu, real *abstol, integer *m, real *w, complex *z__, integer *ldz, + complex *work, integer *lwork, real *rwork, integer *iwork, integer * + ifail, integer *info); + +/* Subroutine */ int chegs2_(integer *itype, char *uplo, integer *n, complex * + a, integer *lda, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int chegst_(integer *itype, char *uplo, integer *n, complex * + a, integer *lda, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int chegv_(integer *itype, char *jobz, char *uplo, integer * + n, complex *a, integer *lda, complex *b, integer *ldb, real *w, + complex *work, integer *lwork, real *rwork, integer *info); + +/* Subroutine */ int chegvd_(integer *itype, char *jobz, char *uplo, integer * + n, complex *a, integer *lda, complex *b, integer *ldb, real *w, + complex *work, integer *lwork, real *rwork, integer *lrwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int chegvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, complex *a, integer *lda, complex *b, integer *ldb, + real *vl, real *vu, integer *il, integer *iu, real *abstol, integer * + m, real *w, complex *z__, integer *ldz, complex *work, integer *lwork, + real *rwork, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int cherfs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, complex *af, integer *ldaf, integer *ipiv, complex * + b, integer *ldb, complex *x, integer *ldx, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int chesv_(char *uplo, integer *n, integer *nrhs, complex *a, + integer *lda, integer *ipiv, complex *b, integer *ldb, complex *work, + integer *lwork, integer *info); + +/* Subroutine */ int chesvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *a, integer *lda, complex *af, integer *ldaf, integer * + ipiv, complex *b, integer *ldb, complex *x, integer *ldx, real *rcond, + real *ferr, real *berr, complex *work, integer *lwork, real *rwork, + integer *info); + +/* Subroutine */ int chetf2_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int chetrd_(char *uplo, integer *n, complex *a, integer *lda, + real *d__, real *e, complex *tau, complex *work, integer *lwork, + integer *info); + +/* Subroutine */ int chetrf_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int chetri_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, complex *work, integer *info); + +/* Subroutine */ int chetrs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, integer *ipiv, complex *b, integer *ldb, integer * + info); + +/* Subroutine */ int chgeqz_(char *job, char *compq, char *compz, integer *n, + integer *ilo, integer *ihi, complex *a, integer *lda, complex *b, + integer *ldb, complex *alpha, complex *beta, complex *q, integer *ldq, + complex *z__, integer *ldz, complex *work, integer *lwork, real * + rwork, integer *info); + +/* Subroutine */ int chpcon_(char *uplo, integer *n, complex *ap, integer * + ipiv, real *anorm, real *rcond, complex *work, integer *info); + +/* Subroutine */ int chpev_(char *jobz, char *uplo, integer *n, complex *ap, + real *w, complex *z__, integer *ldz, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int chpevd_(char *jobz, char *uplo, integer *n, complex *ap, + real *w, complex *z__, integer *ldz, complex *work, integer *lwork, + real *rwork, integer *lrwork, integer *iwork, integer *liwork, + integer *info); + +/* Subroutine */ int chpevx_(char *jobz, char *range, char *uplo, integer *n, + complex *ap, real *vl, real *vu, integer *il, integer *iu, real * + abstol, integer *m, real *w, complex *z__, integer *ldz, complex * + work, real *rwork, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int chpgst_(integer *itype, char *uplo, integer *n, complex * + ap, complex *bp, integer *info); + +/* Subroutine */ int chpgv_(integer *itype, char *jobz, char *uplo, integer * + n, complex *ap, complex *bp, real *w, complex *z__, integer *ldz, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int chpgvd_(integer *itype, char *jobz, char *uplo, integer * + n, complex *ap, complex *bp, real *w, complex *z__, integer *ldz, + complex *work, integer *lwork, real *rwork, integer *lrwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int chpgvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, complex *ap, complex *bp, real *vl, real *vu, + integer *il, integer *iu, real *abstol, integer *m, real *w, complex * + z__, integer *ldz, complex *work, real *rwork, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int chprfs_(char *uplo, integer *n, integer *nrhs, complex * + ap, complex *afp, integer *ipiv, complex *b, integer *ldb, complex *x, + integer *ldx, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int chpsv_(char *uplo, integer *n, integer *nrhs, complex * + ap, integer *ipiv, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int chpsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *ap, complex *afp, integer *ipiv, complex *b, integer * + ldb, complex *x, integer *ldx, real *rcond, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int chptrd_(char *uplo, integer *n, complex *ap, real *d__, + real *e, complex *tau, integer *info); + +/* Subroutine */ int chptrf_(char *uplo, integer *n, complex *ap, integer * + ipiv, integer *info); + +/* Subroutine */ int chptri_(char *uplo, integer *n, complex *ap, integer * + ipiv, complex *work, integer *info); + +/* Subroutine */ int chptrs_(char *uplo, integer *n, integer *nrhs, complex * + ap, integer *ipiv, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int chsein_(char *side, char *eigsrc, char *initv, logical * + select, integer *n, complex *h__, integer *ldh, complex *w, complex * + vl, integer *ldvl, complex *vr, integer *ldvr, integer *mm, integer * + m, complex *work, real *rwork, integer *ifaill, integer *ifailr, + integer *info); + +/* Subroutine */ int chseqr_(char *job, char *compz, integer *n, integer *ilo, + integer *ihi, complex *h__, integer *ldh, complex *w, complex *z__, + integer *ldz, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int clabrd_(integer *m, integer *n, integer *nb, complex *a, + integer *lda, real *d__, real *e, complex *tauq, complex *taup, + complex *x, integer *ldx, complex *y, integer *ldy); + +/* Subroutine */ int clacgv_(integer *n, complex *x, integer *incx); + +/* Subroutine */ int clacon_(integer *n, complex *v, complex *x, real *est, + integer *kase); + +/* Subroutine */ int clacp2_(char *uplo, integer *m, integer *n, real *a, + integer *lda, complex *b, integer *ldb); + +/* Subroutine */ int clacpy_(char *uplo, integer *m, integer *n, complex *a, + integer *lda, complex *b, integer *ldb); + +/* Subroutine */ int clacrm_(integer *m, integer *n, complex *a, integer *lda, + real *b, integer *ldb, complex *c__, integer *ldc, real *rwork); + +/* Subroutine */ int clacrt_(integer *n, complex *cx, integer *incx, complex * + cy, integer *incy, complex *c__, complex *s); + +/* Subroutine */ int claed0_(integer *qsiz, integer *n, real *d__, real *e, + complex *q, integer *ldq, complex *qstore, integer *ldqs, real *rwork, + integer *iwork, integer *info); + +/* Subroutine */ int claed7_(integer *n, integer *cutpnt, integer *qsiz, + integer *tlvls, integer *curlvl, integer *curpbm, real *d__, complex * + q, integer *ldq, real *rho, integer *indxq, real *qstore, integer * + qptr, integer *prmptr, integer *perm, integer *givptr, integer * + givcol, real *givnum, complex *work, real *rwork, integer *iwork, + integer *info); + +/* Subroutine */ int claed8_(integer *k, integer *n, integer *qsiz, complex * + q, integer *ldq, real *d__, real *rho, integer *cutpnt, real *z__, + real *dlamda, complex *q2, integer *ldq2, real *w, integer *indxp, + integer *indx, integer *indxq, integer *perm, integer *givptr, + integer *givcol, real *givnum, integer *info); + +/* Subroutine */ int claein_(logical *rightv, logical *noinit, integer *n, + complex *h__, integer *ldh, complex *w, complex *v, complex *b, + integer *ldb, real *rwork, real *eps3, real *smlnum, integer *info); + +/* Subroutine */ int claesy_(complex *a, complex *b, complex *c__, complex * + rt1, complex *rt2, complex *evscal, complex *cs1, complex *sn1); + +/* Subroutine */ int claev2_(complex *a, complex *b, complex *c__, real *rt1, + real *rt2, real *cs1, complex *sn1); + +/* Subroutine */ int clags2_(logical *upper, real *a1, complex *a2, real *a3, + real *b1, complex *b2, real *b3, real *csu, complex *snu, real *csv, + complex *snv, real *csq, complex *snq); + +/* Subroutine */ int clagtm_(char *trans, integer *n, integer *nrhs, real * + alpha, complex *dl, complex *d__, complex *du, complex *x, integer * + ldx, real *beta, complex *b, integer *ldb); + +/* Subroutine */ int clahef_(char *uplo, integer *n, integer *nb, integer *kb, + complex *a, integer *lda, integer *ipiv, complex *w, integer *ldw, + integer *info); + +/* Subroutine */ int clahqr_(logical *wantt, logical *wantz, integer *n, + integer *ilo, integer *ihi, complex *h__, integer *ldh, complex *w, + integer *iloz, integer *ihiz, complex *z__, integer *ldz, integer * + info); + +/* Subroutine */ int clahrd_(integer *n, integer *k, integer *nb, complex *a, + integer *lda, complex *tau, complex *t, integer *ldt, complex *y, + integer *ldy); + +/* Subroutine */ int claic1_(integer *job, integer *j, complex *x, real *sest, + complex *w, complex *gamma, real *sestpr, complex *s, complex *c__); + +/* Subroutine */ int clals0_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *nrhs, complex *b, integer *ldb, complex *bx, + integer *ldbx, integer *perm, integer *givptr, integer *givcol, + integer *ldgcol, real *givnum, integer *ldgnum, real *poles, real * + difl, real *difr, real *z__, integer *k, real *c__, real *s, real * + rwork, integer *info); + +/* Subroutine */ int clalsa_(integer *icompq, integer *smlsiz, integer *n, + integer *nrhs, complex *b, integer *ldb, complex *bx, integer *ldbx, + real *u, integer *ldu, real *vt, integer *k, real *difl, real *difr, + real *z__, real *poles, integer *givptr, integer *givcol, integer * + ldgcol, integer *perm, real *givnum, real *c__, real *s, real *rwork, + integer *iwork, integer *info); + +/* Subroutine */ int clapll_(integer *n, complex *x, integer *incx, complex * + y, integer *incy, real *ssmin); + +/* Subroutine */ int clapmt_(logical *forwrd, integer *m, integer *n, complex + *x, integer *ldx, integer *k); + +/* Subroutine */ int claqgb_(integer *m, integer *n, integer *kl, integer *ku, + complex *ab, integer *ldab, real *r__, real *c__, real *rowcnd, real + *colcnd, real *amax, char *equed); + +/* Subroutine */ int claqge_(integer *m, integer *n, complex *a, integer *lda, + real *r__, real *c__, real *rowcnd, real *colcnd, real *amax, char * + equed); + +/* Subroutine */ int claqhb_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int claqhe_(char *uplo, integer *n, complex *a, integer *lda, + real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int claqhp_(char *uplo, integer *n, complex *ap, real *s, + real *scond, real *amax, char *equed); + +/* Subroutine */ int claqp2_(integer *m, integer *n, integer *offset, complex + *a, integer *lda, integer *jpvt, complex *tau, real *vn1, real *vn2, + complex *work); + +/* Subroutine */ int claqps_(integer *m, integer *n, integer *offset, integer + *nb, integer *kb, complex *a, integer *lda, integer *jpvt, complex * + tau, real *vn1, real *vn2, complex *auxv, complex *f, integer *ldf); + +/* Subroutine */ int claqsb_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int claqsp_(char *uplo, integer *n, complex *ap, real *s, + real *scond, real *amax, char *equed); + +/* Subroutine */ int claqsy_(char *uplo, integer *n, complex *a, integer *lda, + real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int clar1v_(integer *n, integer *b1, integer *bn, real * + sigma, real *d__, real *l, real *ld, real *lld, real *gersch, complex + *z__, real *ztz, real *mingma, integer *r__, integer *isuppz, real * + work); + +/* Subroutine */ int clar2v_(integer *n, complex *x, complex *y, complex *z__, + integer *incx, real *c__, complex *s, integer *incc); + +/* Subroutine */ int clarcm_(integer *m, integer *n, real *a, integer *lda, + complex *b, integer *ldb, complex *c__, integer *ldc, real *rwork); + +/* Subroutine */ int clarf_(char *side, integer *m, integer *n, complex *v, + integer *incv, complex *tau, complex *c__, integer *ldc, complex * + work); + +/* Subroutine */ int clarfb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, complex *v, integer *ldv, + complex *t, integer *ldt, complex *c__, integer *ldc, complex *work, + integer *ldwork); + +/* Subroutine */ int clarfg_(integer *n, complex *alpha, complex *x, integer * + incx, complex *tau); + +/* Subroutine */ int clarft_(char *direct, char *storev, integer *n, integer * + k, complex *v, integer *ldv, complex *tau, complex *t, integer *ldt); + +/* Subroutine */ int clarfx_(char *side, integer *m, integer *n, complex *v, + complex *tau, complex *c__, integer *ldc, complex *work); + +/* Subroutine */ int clargv_(integer *n, complex *x, integer *incx, complex * + y, integer *incy, real *c__, integer *incc); + +/* Subroutine */ int clarnv_(integer *idist, integer *iseed, integer *n, + complex *x); + +/* Subroutine */ int clarrv_(integer *n, real *d__, real *l, integer *isplit, + integer *m, real *w, integer *iblock, real *gersch, real *tol, + complex *z__, integer *ldz, integer *isuppz, real *work, integer * + iwork, integer *info); + +/* Subroutine */ int clartg_(complex *f, complex *g, real *cs, complex *sn, + complex *r__); + +/* Subroutine */ int clartv_(integer *n, complex *x, integer *incx, complex * + y, integer *incy, real *c__, complex *s, integer *incc); + +/* Subroutine */ int clarz_(char *side, integer *m, integer *n, integer *l, + complex *v, integer *incv, complex *tau, complex *c__, integer *ldc, + complex *work); + +/* Subroutine */ int clarzb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, integer *l, complex *v, + integer *ldv, complex *t, integer *ldt, complex *c__, integer *ldc, + complex *work, integer *ldwork); + +/* Subroutine */ int clarzt_(char *direct, char *storev, integer *n, integer * + k, complex *v, integer *ldv, complex *tau, complex *t, integer *ldt); + +/* Subroutine */ int clascl_(char *type__, integer *kl, integer *ku, real * + cfrom, real *cto, integer *m, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int claset_(char *uplo, integer *m, integer *n, complex * + alpha, complex *beta, complex *a, integer *lda); + +/* Subroutine */ int clasr_(char *side, char *pivot, char *direct, integer *m, + integer *n, real *c__, real *s, complex *a, integer *lda); + +/* Subroutine */ int classq_(integer *n, complex *x, integer *incx, real * + scale, real *sumsq); + +/* Subroutine */ int claswp_(integer *n, complex *a, integer *lda, integer * + k1, integer *k2, integer *ipiv, integer *incx); + +/* Subroutine */ int clasyf_(char *uplo, integer *n, integer *nb, integer *kb, + complex *a, integer *lda, integer *ipiv, complex *w, integer *ldw, + integer *info); + +/* Subroutine */ int clatbs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, integer *kd, complex *ab, integer *ldab, complex * + x, real *scale, real *cnorm, integer *info); + +/* Subroutine */ int clatdf_(integer *ijob, integer *n, complex *z__, integer + *ldz, complex *rhs, real *rdsum, real *rdscal, integer *ipiv, integer + *jpiv); + +/* Subroutine */ int clatps_(char *uplo, char *trans, char *diag, char * + normin, integer *n, complex *ap, complex *x, real *scale, real *cnorm, + integer *info); + +/* Subroutine */ int clatrd_(char *uplo, integer *n, integer *nb, complex *a, + integer *lda, real *e, complex *tau, complex *w, integer *ldw); + +/* Subroutine */ int clatrs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, complex *a, integer *lda, complex *x, real *scale, + real *cnorm, integer *info); + +/* Subroutine */ int clatrz_(integer *m, integer *n, integer *l, complex *a, + integer *lda, complex *tau, complex *work); + +/* Subroutine */ int clatzm_(char *side, integer *m, integer *n, complex *v, + integer *incv, complex *tau, complex *c1, complex *c2, integer *ldc, + complex *work); + +/* Subroutine */ int clauu2_(char *uplo, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int clauum_(char *uplo, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int cpbcon_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, real *anorm, real *rcond, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int cpbequ_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, real *s, real *scond, real *amax, integer *info); + +/* Subroutine */ int cpbrfs_(char *uplo, integer *n, integer *kd, integer * + nrhs, complex *ab, integer *ldab, complex *afb, integer *ldafb, + complex *b, integer *ldb, complex *x, integer *ldx, real *ferr, real * + berr, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cpbstf_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, integer *info); + +/* Subroutine */ int cpbsv_(char *uplo, integer *n, integer *kd, integer * + nrhs, complex *ab, integer *ldab, complex *b, integer *ldb, integer * + info); + +/* Subroutine */ int cpbsvx_(char *fact, char *uplo, integer *n, integer *kd, + integer *nrhs, complex *ab, integer *ldab, complex *afb, integer * + ldafb, char *equed, real *s, complex *b, integer *ldb, complex *x, + integer *ldx, real *rcond, real *ferr, real *berr, complex *work, + real *rwork, integer *info); + +/* Subroutine */ int cpbtf2_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, integer *info); + +/* Subroutine */ int cpbtrf_(char *uplo, integer *n, integer *kd, complex *ab, + integer *ldab, integer *info); + +/* Subroutine */ int cpbtrs_(char *uplo, integer *n, integer *kd, integer * + nrhs, complex *ab, integer *ldab, complex *b, integer *ldb, integer * + info); + +/* Subroutine */ int cpocon_(char *uplo, integer *n, complex *a, integer *lda, + real *anorm, real *rcond, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cpoequ_(integer *n, complex *a, integer *lda, real *s, + real *scond, real *amax, integer *info); + +/* Subroutine */ int cporfs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, complex *af, integer *ldaf, complex *b, integer *ldb, + complex *x, integer *ldx, real *ferr, real *berr, complex *work, + real *rwork, integer *info); + +/* Subroutine */ int cposv_(char *uplo, integer *n, integer *nrhs, complex *a, + integer *lda, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cposvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *a, integer *lda, complex *af, integer *ldaf, char * + equed, real *s, complex *b, integer *ldb, complex *x, integer *ldx, + real *rcond, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int cpotf2_(char *uplo, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int cpotrf_(char *uplo, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int cpotri_(char *uplo, integer *n, complex *a, integer *lda, + integer *info); + +/* Subroutine */ int cpotrs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cppcon_(char *uplo, integer *n, complex *ap, real *anorm, + real *rcond, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cppequ_(char *uplo, integer *n, complex *ap, real *s, + real *scond, real *amax, integer *info); + +/* Subroutine */ int cpprfs_(char *uplo, integer *n, integer *nrhs, complex * + ap, complex *afp, complex *b, integer *ldb, complex *x, integer *ldx, + real *ferr, real *berr, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cppsv_(char *uplo, integer *n, integer *nrhs, complex * + ap, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cppsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *ap, complex *afp, char *equed, real *s, complex *b, + integer *ldb, complex *x, integer *ldx, real *rcond, real *ferr, real + *berr, complex *work, real *rwork, integer *info); + +/* Subroutine */ int cpptrf_(char *uplo, integer *n, complex *ap, integer * + info); + +/* Subroutine */ int cpptri_(char *uplo, integer *n, complex *ap, integer * + info); + +/* Subroutine */ int cpptrs_(char *uplo, integer *n, integer *nrhs, complex * + ap, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cptcon_(integer *n, real *d__, complex *e, real *anorm, + real *rcond, real *rwork, integer *info); + +/* Subroutine */ int cptrfs_(char *uplo, integer *n, integer *nrhs, real *d__, + complex *e, real *df, complex *ef, complex *b, integer *ldb, complex + *x, integer *ldx, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int cptsv_(integer *n, integer *nrhs, real *d__, complex *e, + complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cptsvx_(char *fact, integer *n, integer *nrhs, real *d__, + complex *e, real *df, complex *ef, complex *b, integer *ldb, complex + *x, integer *ldx, real *rcond, real *ferr, real *berr, complex *work, + real *rwork, integer *info); + +/* Subroutine */ int cpttrf_(integer *n, real *d__, complex *e, integer *info); + +/* Subroutine */ int cpttrs_(char *uplo, integer *n, integer *nrhs, real *d__, + complex *e, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cptts2_(integer *iuplo, integer *n, integer *nrhs, real * + d__, complex *e, complex *b, integer *ldb); + +/* Subroutine */ int crot_(integer *n, complex *cx, integer *incx, complex * + cy, integer *incy, real *c__, complex *s); + +/* Subroutine */ int cspcon_(char *uplo, integer *n, complex *ap, integer * + ipiv, real *anorm, real *rcond, complex *work, integer *info); + +/* Subroutine */ int cspmv_(char *uplo, integer *n, complex *alpha, complex * + ap, complex *x, integer *incx, complex *beta, complex *y, integer * + incy); + +/* Subroutine */ int cspr_(char *uplo, integer *n, complex *alpha, complex *x, + integer *incx, complex *ap); + +/* Subroutine */ int csprfs_(char *uplo, integer *n, integer *nrhs, complex * + ap, complex *afp, integer *ipiv, complex *b, integer *ldb, complex *x, + integer *ldx, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int cspsv_(char *uplo, integer *n, integer *nrhs, complex * + ap, integer *ipiv, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int cspsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *ap, complex *afp, integer *ipiv, complex *b, integer * + ldb, complex *x, integer *ldx, real *rcond, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int csptrf_(char *uplo, integer *n, complex *ap, integer * + ipiv, integer *info); + +/* Subroutine */ int csptri_(char *uplo, integer *n, complex *ap, integer * + ipiv, complex *work, integer *info); + +/* Subroutine */ int csptrs_(char *uplo, integer *n, integer *nrhs, complex * + ap, integer *ipiv, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int csrot_(integer *n, complex *cx, integer *incx, complex * + cy, integer *incy, real *c__, real *s); + +/* Subroutine */ int csrscl_(integer *n, real *sa, complex *sx, integer *incx); + +/* Subroutine */ int cstedc_(char *compz, integer *n, real *d__, real *e, + complex *z__, integer *ldz, complex *work, integer *lwork, real * + rwork, integer *lrwork, integer *iwork, integer *liwork, integer * + info); + +/* Subroutine */ int cstein_(integer *n, real *d__, real *e, integer *m, real + *w, integer *iblock, integer *isplit, complex *z__, integer *ldz, + real *work, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int csteqr_(char *compz, integer *n, real *d__, real *e, + complex *z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int csycon_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, real *anorm, real *rcond, complex *work, integer * + info); + +/* Subroutine */ int csymv_(char *uplo, integer *n, complex *alpha, complex * + a, integer *lda, complex *x, integer *incx, complex *beta, complex *y, + integer *incy); + +/* Subroutine */ int csyr_(char *uplo, integer *n, complex *alpha, complex *x, + integer *incx, complex *a, integer *lda); + +/* Subroutine */ int csyrfs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, complex *af, integer *ldaf, integer *ipiv, complex * + b, integer *ldb, complex *x, integer *ldx, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int csysv_(char *uplo, integer *n, integer *nrhs, complex *a, + integer *lda, integer *ipiv, complex *b, integer *ldb, complex *work, + integer *lwork, integer *info); + +/* Subroutine */ int csysvx_(char *fact, char *uplo, integer *n, integer * + nrhs, complex *a, integer *lda, complex *af, integer *ldaf, integer * + ipiv, complex *b, integer *ldb, complex *x, integer *ldx, real *rcond, + real *ferr, real *berr, complex *work, integer *lwork, real *rwork, + integer *info); + +/* Subroutine */ int csytf2_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int csytrf_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int csytri_(char *uplo, integer *n, complex *a, integer *lda, + integer *ipiv, complex *work, integer *info); + +/* Subroutine */ int csytrs_(char *uplo, integer *n, integer *nrhs, complex * + a, integer *lda, integer *ipiv, complex *b, integer *ldb, integer * + info); + +/* Subroutine */ int ctbcon_(char *norm, char *uplo, char *diag, integer *n, + integer *kd, complex *ab, integer *ldab, real *rcond, complex *work, + real *rwork, integer *info); + +/* Subroutine */ int ctbrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, complex *ab, integer *ldab, complex *b, + integer *ldb, complex *x, integer *ldx, real *ferr, real *berr, + complex *work, real *rwork, integer *info); + +/* Subroutine */ int ctbtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, complex *ab, integer *ldab, complex *b, + integer *ldb, integer *info); + +/* Subroutine */ int ctgevc_(char *side, char *howmny, logical *select, + integer *n, complex *a, integer *lda, complex *b, integer *ldb, + complex *vl, integer *ldvl, complex *vr, integer *ldvr, integer *mm, + integer *m, complex *work, real *rwork, integer *info); + +/* Subroutine */ int ctgex2_(logical *wantq, logical *wantz, integer *n, + complex *a, integer *lda, complex *b, integer *ldb, complex *q, + integer *ldq, complex *z__, integer *ldz, integer *j1, integer *info); + +/* Subroutine */ int ctgexc_(logical *wantq, logical *wantz, integer *n, + complex *a, integer *lda, complex *b, integer *ldb, complex *q, + integer *ldq, complex *z__, integer *ldz, integer *ifst, integer * + ilst, integer *info); + +/* Subroutine */ int ctgsen_(integer *ijob, logical *wantq, logical *wantz, + logical *select, integer *n, complex *a, integer *lda, complex *b, + integer *ldb, complex *alpha, complex *beta, complex *q, integer *ldq, + complex *z__, integer *ldz, integer *m, real *pl, real *pr, real * + dif, complex *work, integer *lwork, integer *iwork, integer *liwork, + integer *info); + +/* Subroutine */ int ctgsja_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, integer *k, integer *l, complex *a, integer * + lda, complex *b, integer *ldb, real *tola, real *tolb, real *alpha, + real *beta, complex *u, integer *ldu, complex *v, integer *ldv, + complex *q, integer *ldq, complex *work, integer *ncycle, integer * + info); + +/* Subroutine */ int ctgsna_(char *job, char *howmny, logical *select, + integer *n, complex *a, integer *lda, complex *b, integer *ldb, + complex *vl, integer *ldvl, complex *vr, integer *ldvr, real *s, real + *dif, integer *mm, integer *m, complex *work, integer *lwork, integer + *iwork, integer *info); + +/* Subroutine */ int ctgsy2_(char *trans, integer *ijob, integer *m, integer * + n, complex *a, integer *lda, complex *b, integer *ldb, complex *c__, + integer *ldc, complex *d__, integer *ldd, complex *e, integer *lde, + complex *f, integer *ldf, real *scale, real *rdsum, real *rdscal, + integer *info); + +/* Subroutine */ int ctgsyl_(char *trans, integer *ijob, integer *m, integer * + n, complex *a, integer *lda, complex *b, integer *ldb, complex *c__, + integer *ldc, complex *d__, integer *ldd, complex *e, integer *lde, + complex *f, integer *ldf, real *scale, real *dif, complex *work, + integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int ctpcon_(char *norm, char *uplo, char *diag, integer *n, + complex *ap, real *rcond, complex *work, real *rwork, integer *info); + +/* Subroutine */ int ctprfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, complex *ap, complex *b, integer *ldb, complex *x, + integer *ldx, real *ferr, real *berr, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int ctptri_(char *uplo, char *diag, integer *n, complex *ap, + integer *info); + +/* Subroutine */ int ctptrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, complex *ap, complex *b, integer *ldb, integer *info); + +/* Subroutine */ int ctrcon_(char *norm, char *uplo, char *diag, integer *n, + complex *a, integer *lda, real *rcond, complex *work, real *rwork, + integer *info); + +/* Subroutine */ int ctrevc_(char *side, char *howmny, logical *select, + integer *n, complex *t, integer *ldt, complex *vl, integer *ldvl, + complex *vr, integer *ldvr, integer *mm, integer *m, complex *work, + real *rwork, integer *info); + +/* Subroutine */ int ctrexc_(char *compq, integer *n, complex *t, integer * + ldt, complex *q, integer *ldq, integer *ifst, integer *ilst, integer * + info); + +/* Subroutine */ int ctrrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, complex *a, integer *lda, complex *b, integer *ldb, + complex *x, integer *ldx, real *ferr, real *berr, complex *work, real + *rwork, integer *info); + +/* Subroutine */ int ctrsen_(char *job, char *compq, logical *select, integer + *n, complex *t, integer *ldt, complex *q, integer *ldq, complex *w, + integer *m, real *s, real *sep, complex *work, integer *lwork, + integer *info); + +/* Subroutine */ int ctrsna_(char *job, char *howmny, logical *select, + integer *n, complex *t, integer *ldt, complex *vl, integer *ldvl, + complex *vr, integer *ldvr, real *s, real *sep, integer *mm, integer * + m, complex *work, integer *ldwork, real *rwork, integer *info); + +/* Subroutine */ int ctrsyl_(char *trana, char *tranb, integer *isgn, integer + *m, integer *n, complex *a, integer *lda, complex *b, integer *ldb, + complex *c__, integer *ldc, real *scale, integer *info); + +/* Subroutine */ int ctrti2_(char *uplo, char *diag, integer *n, complex *a, + integer *lda, integer *info); + +/* Subroutine */ int ctrtri_(char *uplo, char *diag, integer *n, complex *a, + integer *lda, integer *info); + +/* Subroutine */ int ctrtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, complex *a, integer *lda, complex *b, integer *ldb, + integer *info); + +/* Subroutine */ int ctzrqf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, integer *info); + +/* Subroutine */ int ctzrzf_(integer *m, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cung2l_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *info); + +/* Subroutine */ int cung2r_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *info); + +/* Subroutine */ int cungbr_(char *vect, integer *m, integer *n, integer *k, + complex *a, integer *lda, complex *tau, complex *work, integer *lwork, + integer *info); + +/* Subroutine */ int cunghr_(integer *n, integer *ilo, integer *ihi, complex * + a, integer *lda, complex *tau, complex *work, integer *lwork, integer + *info); + +/* Subroutine */ int cungl2_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *info); + +/* Subroutine */ int cunglq_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cungql_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cungqr_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cungr2_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *info); + +/* Subroutine */ int cungrq_(integer *m, integer *n, integer *k, complex *a, + integer *lda, complex *tau, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cungtr_(char *uplo, integer *n, complex *a, integer *lda, + complex *tau, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cunm2l_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *info); + +/* Subroutine */ int cunm2r_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *info); + +/* Subroutine */ int cunmbr_(char *vect, char *side, char *trans, integer *m, + integer *n, integer *k, complex *a, integer *lda, complex *tau, + complex *c__, integer *ldc, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cunmhr_(char *side, char *trans, integer *m, integer *n, + integer *ilo, integer *ihi, complex *a, integer *lda, complex *tau, + complex *c__, integer *ldc, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cunml2_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *info); + +/* Subroutine */ int cunmlq_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cunmql_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cunmqr_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cunmr2_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *info); + +/* Subroutine */ int cunmr3_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, complex *a, integer *lda, complex *tau, + complex *c__, integer *ldc, complex *work, integer *info); + +/* Subroutine */ int cunmrq_(char *side, char *trans, integer *m, integer *n, + integer *k, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cunmrz_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, complex *a, integer *lda, complex *tau, + complex *c__, integer *ldc, complex *work, integer *lwork, integer * + info); + +/* Subroutine */ int cunmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, complex *a, integer *lda, complex *tau, complex *c__, + integer *ldc, complex *work, integer *lwork, integer *info); + +/* Subroutine */ int cupgtr_(char *uplo, integer *n, complex *ap, complex * + tau, complex *q, integer *ldq, complex *work, integer *info); + +/* Subroutine */ int cupmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, complex *ap, complex *tau, complex *c__, integer *ldc, + complex *work, integer *info); + +/* Subroutine */ int dbdsdc_(char *uplo, char *compq, integer *n, doublereal * + d__, doublereal *e, doublereal *u, integer *ldu, doublereal *vt, + integer *ldvt, doublereal *q, integer *iq, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dbdsqr_(char *uplo, integer *n, integer *ncvt, integer * + nru, integer *ncc, doublereal *d__, doublereal *e, doublereal *vt, + integer *ldvt, doublereal *u, integer *ldu, doublereal *c__, integer * + ldc, doublereal *work, integer *info); + +/* Subroutine */ int ddisna_(char *job, integer *m, integer *n, doublereal * + d__, doublereal *sep, integer *info); + +/* Subroutine */ int dgbbrd_(char *vect, integer *m, integer *n, integer *ncc, + integer *kl, integer *ku, doublereal *ab, integer *ldab, doublereal * + d__, doublereal *e, doublereal *q, integer *ldq, doublereal *pt, + integer *ldpt, doublereal *c__, integer *ldc, doublereal *work, + integer *info); + +/* Subroutine */ int dgbcon_(char *norm, integer *n, integer *kl, integer *ku, + doublereal *ab, integer *ldab, integer *ipiv, doublereal *anorm, + doublereal *rcond, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dgbequ_(integer *m, integer *n, integer *kl, integer *ku, + doublereal *ab, integer *ldab, doublereal *r__, doublereal *c__, + doublereal *rowcnd, doublereal *colcnd, doublereal *amax, integer * + info); + +/* Subroutine */ int dgbrfs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, doublereal *ab, integer *ldab, doublereal *afb, + integer *ldafb, integer *ipiv, doublereal *b, integer *ldb, + doublereal *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dgbsv_(integer *n, integer *kl, integer *ku, integer * + nrhs, doublereal *ab, integer *ldab, integer *ipiv, doublereal *b, + integer *ldb, integer *info); + +/* Subroutine */ int dgbsvx_(char *fact, char *trans, integer *n, integer *kl, + integer *ku, integer *nrhs, doublereal *ab, integer *ldab, + doublereal *afb, integer *ldafb, integer *ipiv, char *equed, + doublereal *r__, doublereal *c__, doublereal *b, integer *ldb, + doublereal *x, integer *ldx, doublereal *rcond, doublereal *ferr, + doublereal *berr, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dgbtf2_(integer *m, integer *n, integer *kl, integer *ku, + doublereal *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int dgbtrf_(integer *m, integer *n, integer *kl, integer *ku, + doublereal *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int dgbtrs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, doublereal *ab, integer *ldab, integer *ipiv, + doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dgebak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, doublereal *scale, integer *m, doublereal *v, integer * + ldv, integer *info); + +/* Subroutine */ int dgebal_(char *job, integer *n, doublereal *a, integer * + lda, integer *ilo, integer *ihi, doublereal *scale, integer *info); + +/* Subroutine */ int dgebd2_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *d__, doublereal *e, doublereal *tauq, doublereal * + taup, doublereal *work, integer *info); + +/* Subroutine */ int dgebrd_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *d__, doublereal *e, doublereal *tauq, doublereal * + taup, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgecon_(char *norm, integer *n, doublereal *a, integer * + lda, doublereal *anorm, doublereal *rcond, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dgeequ_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *r__, doublereal *c__, doublereal *rowcnd, doublereal + *colcnd, doublereal *amax, integer *info); + +/* Subroutine */ int dgees_(char *jobvs, char *sort, L_fp select, integer *n, + doublereal *a, integer *lda, integer *sdim, doublereal *wr, + doublereal *wi, doublereal *vs, integer *ldvs, doublereal *work, + integer *lwork, logical *bwork, integer *info); + +/* Subroutine */ int dgeesx_(char *jobvs, char *sort, L_fp select, char * + sense, integer *n, doublereal *a, integer *lda, integer *sdim, + doublereal *wr, doublereal *wi, doublereal *vs, integer *ldvs, + doublereal *rconde, doublereal *rcondv, doublereal *work, integer * + lwork, integer *iwork, integer *liwork, logical *bwork, integer *info); + +/* Subroutine */ int dgeev_(char *jobvl, char *jobvr, integer *n, doublereal * + a, integer *lda, doublereal *wr, doublereal *wi, doublereal *vl, + integer *ldvl, doublereal *vr, integer *ldvr, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dgeevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, doublereal *a, integer *lda, doublereal *wr, + doublereal *wi, doublereal *vl, integer *ldvl, doublereal *vr, + integer *ldvr, integer *ilo, integer *ihi, doublereal *scale, + doublereal *abnrm, doublereal *rconde, doublereal *rcondv, doublereal + *work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int dgegs_(char *jobvsl, char *jobvsr, integer *n, + doublereal *a, integer *lda, doublereal *b, integer *ldb, doublereal * + alphar, doublereal *alphai, doublereal *beta, doublereal *vsl, + integer *ldvsl, doublereal *vsr, integer *ldvsr, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dgegv_(char *jobvl, char *jobvr, integer *n, doublereal * + a, integer *lda, doublereal *b, integer *ldb, doublereal *alphar, + doublereal *alphai, doublereal *beta, doublereal *vl, integer *ldvl, + doublereal *vr, integer *ldvr, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dgehd2_(integer *n, integer *ilo, integer *ihi, + doublereal *a, integer *lda, doublereal *tau, doublereal *work, + integer *info); + +/* Subroutine */ int dgehrd_(integer *n, integer *ilo, integer *ihi, + doublereal *a, integer *lda, doublereal *tau, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dgelq2_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dgelqf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgels_(char *trans, integer *m, integer *n, integer * + nrhs, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgelsd_(integer *m, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *b, integer *ldb, doublereal * + s, doublereal *rcond, integer *rank, doublereal *work, integer *lwork, + integer *iwork, integer *info); + +/* Subroutine */ int dgelss_(integer *m, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *b, integer *ldb, doublereal * + s, doublereal *rcond, integer *rank, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dgelsx_(integer *m, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *b, integer *ldb, integer * + jpvt, doublereal *rcond, integer *rank, doublereal *work, integer * + info); + +/* Subroutine */ int dgelsy_(integer *m, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *b, integer *ldb, integer * + jpvt, doublereal *rcond, integer *rank, doublereal *work, integer * + lwork, integer *info); + +/* Subroutine */ int dgeql2_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dgeqlf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgeqp3_(integer *m, integer *n, doublereal *a, integer * + lda, integer *jpvt, doublereal *tau, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dgeqpf_(integer *m, integer *n, doublereal *a, integer * + lda, integer *jpvt, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dgeqr2_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dgeqrf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgerfs_(char *trans, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *af, integer *ldaf, integer * + ipiv, doublereal *b, integer *ldb, doublereal *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dgerq2_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dgerqf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgesc2_(integer *n, doublereal *a, integer *lda, + doublereal *rhs, integer *ipiv, integer *jpiv, doublereal *scale); + +/* Subroutine */ int dgesdd_(char *jobz, integer *m, integer *n, doublereal * + a, integer *lda, doublereal *s, doublereal *u, integer *ldu, + doublereal *vt, integer *ldvt, doublereal *work, integer *lwork, + integer *iwork, integer *info); + +/* Subroutine */ int dgesv_(integer *n, integer *nrhs, doublereal *a, integer + *lda, integer *ipiv, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dgesvd_(char *jobu, char *jobvt, integer *m, integer *n, + doublereal *a, integer *lda, doublereal *s, doublereal *u, integer * + ldu, doublereal *vt, integer *ldvt, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dgesvx_(char *fact, char *trans, integer *n, integer * + nrhs, doublereal *a, integer *lda, doublereal *af, integer *ldaf, + integer *ipiv, char *equed, doublereal *r__, doublereal *c__, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + rcond, doublereal *ferr, doublereal *berr, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dgetc2_(integer *n, doublereal *a, integer *lda, integer + *ipiv, integer *jpiv, integer *info); + +/* Subroutine */ int dgetf2_(integer *m, integer *n, doublereal *a, integer * + lda, integer *ipiv, integer *info); + +/* Subroutine */ int dgetrf_(integer *m, integer *n, doublereal *a, integer * + lda, integer *ipiv, integer *info); + +/* Subroutine */ int dgetri_(integer *n, doublereal *a, integer *lda, integer + *ipiv, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dgetrs_(char *trans, integer *n, integer *nrhs, + doublereal *a, integer *lda, integer *ipiv, doublereal *b, integer * + ldb, integer *info); + +/* Subroutine */ int dggbak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, doublereal *lscale, doublereal *rscale, integer *m, + doublereal *v, integer *ldv, integer *info); + +/* Subroutine */ int dggbal_(char *job, integer *n, doublereal *a, integer * + lda, doublereal *b, integer *ldb, integer *ilo, integer *ihi, + doublereal *lscale, doublereal *rscale, doublereal *work, integer * + info); + +/* Subroutine */ int dgges_(char *jobvsl, char *jobvsr, char *sort, L_fp + delctg, integer *n, doublereal *a, integer *lda, doublereal *b, + integer *ldb, integer *sdim, doublereal *alphar, doublereal *alphai, + doublereal *beta, doublereal *vsl, integer *ldvsl, doublereal *vsr, + integer *ldvsr, doublereal *work, integer *lwork, logical *bwork, + integer *info); + +/* Subroutine */ int dggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp + delctg, char *sense, integer *n, doublereal *a, integer *lda, + doublereal *b, integer *ldb, integer *sdim, doublereal *alphar, + doublereal *alphai, doublereal *beta, doublereal *vsl, integer *ldvsl, + doublereal *vsr, integer *ldvsr, doublereal *rconde, doublereal * + rcondv, doublereal *work, integer *lwork, integer *iwork, integer * + liwork, logical *bwork, integer *info); + +/* Subroutine */ int dggev_(char *jobvl, char *jobvr, integer *n, doublereal * + a, integer *lda, doublereal *b, integer *ldb, doublereal *alphar, + doublereal *alphai, doublereal *beta, doublereal *vl, integer *ldvl, + doublereal *vr, integer *ldvr, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dggevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, doublereal *a, integer *lda, doublereal *b, + integer *ldb, doublereal *alphar, doublereal *alphai, doublereal * + beta, doublereal *vl, integer *ldvl, doublereal *vr, integer *ldvr, + integer *ilo, integer *ihi, doublereal *lscale, doublereal *rscale, + doublereal *abnrm, doublereal *bbnrm, doublereal *rconde, doublereal * + rcondv, doublereal *work, integer *lwork, integer *iwork, logical * + bwork, integer *info); + +/* Subroutine */ int dggglm_(integer *n, integer *m, integer *p, doublereal * + a, integer *lda, doublereal *b, integer *ldb, doublereal *d__, + doublereal *x, doublereal *y, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dgghrd_(char *compq, char *compz, integer *n, integer * + ilo, integer *ihi, doublereal *a, integer *lda, doublereal *b, + integer *ldb, doublereal *q, integer *ldq, doublereal *z__, integer * + ldz, integer *info); + +/* Subroutine */ int dgglse_(integer *m, integer *n, integer *p, doublereal * + a, integer *lda, doublereal *b, integer *ldb, doublereal *c__, + doublereal *d__, doublereal *x, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dggqrf_(integer *n, integer *m, integer *p, doublereal * + a, integer *lda, doublereal *taua, doublereal *b, integer *ldb, + doublereal *taub, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dggrqf_(integer *m, integer *p, integer *n, doublereal * + a, integer *lda, doublereal *taua, doublereal *b, integer *ldb, + doublereal *taub, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dggsvd_(char *jobu, char *jobv, char *jobq, integer *m, + integer *n, integer *p, integer *k, integer *l, doublereal *a, + integer *lda, doublereal *b, integer *ldb, doublereal *alpha, + doublereal *beta, doublereal *u, integer *ldu, doublereal *v, integer + *ldv, doublereal *q, integer *ldq, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dggsvp_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, doublereal *a, integer *lda, doublereal *b, + integer *ldb, doublereal *tola, doublereal *tolb, integer *k, integer + *l, doublereal *u, integer *ldu, doublereal *v, integer *ldv, + doublereal *q, integer *ldq, integer *iwork, doublereal *tau, + doublereal *work, integer *info); + +/* Subroutine */ int dgtcon_(char *norm, integer *n, doublereal *dl, + doublereal *d__, doublereal *du, doublereal *du2, integer *ipiv, + doublereal *anorm, doublereal *rcond, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dgtrfs_(char *trans, integer *n, integer *nrhs, + doublereal *dl, doublereal *d__, doublereal *du, doublereal *dlf, + doublereal *df, doublereal *duf, doublereal *du2, integer *ipiv, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + ferr, doublereal *berr, doublereal *work, integer *iwork, integer * + info); + +/* Subroutine */ int dgtsv_(integer *n, integer *nrhs, doublereal *dl, + doublereal *d__, doublereal *du, doublereal *b, integer *ldb, integer + *info); + +/* Subroutine */ int dgtsvx_(char *fact, char *trans, integer *n, integer * + nrhs, doublereal *dl, doublereal *d__, doublereal *du, doublereal * + dlf, doublereal *df, doublereal *duf, doublereal *du2, integer *ipiv, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + rcond, doublereal *ferr, doublereal *berr, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dgttrf_(integer *n, doublereal *dl, doublereal *d__, + doublereal *du, doublereal *du2, integer *ipiv, integer *info); + +/* Subroutine */ int dgttrs_(char *trans, integer *n, integer *nrhs, + doublereal *dl, doublereal *d__, doublereal *du, doublereal *du2, + integer *ipiv, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dgtts2_(integer *itrans, integer *n, integer *nrhs, + doublereal *dl, doublereal *d__, doublereal *du, doublereal *du2, + integer *ipiv, doublereal *b, integer *ldb); + +/* Subroutine */ int dhgeqz_(char *job, char *compq, char *compz, integer *n, + integer *ilo, integer *ihi, doublereal *a, integer *lda, doublereal * + b, integer *ldb, doublereal *alphar, doublereal *alphai, doublereal * + beta, doublereal *q, integer *ldq, doublereal *z__, integer *ldz, + doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dhsein_(char *side, char *eigsrc, char *initv, logical * + select, integer *n, doublereal *h__, integer *ldh, doublereal *wr, + doublereal *wi, doublereal *vl, integer *ldvl, doublereal *vr, + integer *ldvr, integer *mm, integer *m, doublereal *work, integer * + ifaill, integer *ifailr, integer *info); + +/* Subroutine */ int dhseqr_(char *job, char *compz, integer *n, integer *ilo, + integer *ihi, doublereal *h__, integer *ldh, doublereal *wr, + doublereal *wi, doublereal *z__, integer *ldz, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dlabad_(doublereal *small, doublereal *large); + +/* Subroutine */ int dlabrd_(integer *m, integer *n, integer *nb, doublereal * + a, integer *lda, doublereal *d__, doublereal *e, doublereal *tauq, + doublereal *taup, doublereal *x, integer *ldx, doublereal *y, integer + *ldy); + +/* Subroutine */ int dlacon_(integer *n, doublereal *v, doublereal *x, + integer *isgn, doublereal *est, integer *kase); + +/* Subroutine */ int dlacpy_(char *uplo, integer *m, integer *n, doublereal * + a, integer *lda, doublereal *b, integer *ldb); + +/* Subroutine */ int dladiv_(doublereal *a, doublereal *b, doublereal *c__, + doublereal *d__, doublereal *p, doublereal *q); + +/* Subroutine */ int dlae2_(doublereal *a, doublereal *b, doublereal *c__, + doublereal *rt1, doublereal *rt2); + +/* Subroutine */ int dlaebz_(integer *ijob, integer *nitmax, integer *n, + integer *mmax, integer *minp, integer *nbmin, doublereal *abstol, + doublereal *reltol, doublereal *pivmin, doublereal *d__, doublereal * + e, doublereal *e2, integer *nval, doublereal *ab, doublereal *c__, + integer *mout, integer *nab, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dlaed0_(integer *icompq, integer *qsiz, integer *n, + doublereal *d__, doublereal *e, doublereal *q, integer *ldq, + doublereal *qstore, integer *ldqs, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dlaed1_(integer *n, doublereal *d__, doublereal *q, + integer *ldq, integer *indxq, doublereal *rho, integer *cutpnt, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dlaed2_(integer *k, integer *n, integer *n1, doublereal * + d__, doublereal *q, integer *ldq, integer *indxq, doublereal *rho, + doublereal *z__, doublereal *dlamda, doublereal *w, doublereal *q2, + integer *indx, integer *indxc, integer *indxp, integer *coltyp, + integer *info); + +/* Subroutine */ int dlaed3_(integer *k, integer *n, integer *n1, doublereal * + d__, doublereal *q, integer *ldq, doublereal *rho, doublereal *dlamda, + doublereal *q2, integer *indx, integer *ctot, doublereal *w, + doublereal *s, integer *info); + +/* Subroutine */ int dlaed4_(integer *n, integer *i__, doublereal *d__, + doublereal *z__, doublereal *delta, doublereal *rho, doublereal *dlam, + integer *info); + +/* Subroutine */ int dlaed5_(integer *i__, doublereal *d__, doublereal *z__, + doublereal *delta, doublereal *rho, doublereal *dlam); + +/* Subroutine */ int dlaed6_(integer *kniter, logical *orgati, doublereal * + rho, doublereal *d__, doublereal *z__, doublereal *finit, doublereal * + tau, integer *info); + +/* Subroutine */ int dlaed7_(integer *icompq, integer *n, integer *qsiz, + integer *tlvls, integer *curlvl, integer *curpbm, doublereal *d__, + doublereal *q, integer *ldq, integer *indxq, doublereal *rho, integer + *cutpnt, doublereal *qstore, integer *qptr, integer *prmptr, integer * + perm, integer *givptr, integer *givcol, doublereal *givnum, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dlaed8_(integer *icompq, integer *k, integer *n, integer + *qsiz, doublereal *d__, doublereal *q, integer *ldq, integer *indxq, + doublereal *rho, integer *cutpnt, doublereal *z__, doublereal *dlamda, + doublereal *q2, integer *ldq2, doublereal *w, integer *perm, integer + *givptr, integer *givcol, doublereal *givnum, integer *indxp, integer + *indx, integer *info); + +/* Subroutine */ int dlaed9_(integer *k, integer *kstart, integer *kstop, + integer *n, doublereal *d__, doublereal *q, integer *ldq, doublereal * + rho, doublereal *dlamda, doublereal *w, doublereal *s, integer *lds, + integer *info); + +/* Subroutine */ int dlaeda_(integer *n, integer *tlvls, integer *curlvl, + integer *curpbm, integer *prmptr, integer *perm, integer *givptr, + integer *givcol, doublereal *givnum, doublereal *q, integer *qptr, + doublereal *z__, doublereal *ztemp, integer *info); + +/* Subroutine */ int dlaein_(logical *rightv, logical *noinit, integer *n, + doublereal *h__, integer *ldh, doublereal *wr, doublereal *wi, + doublereal *vr, doublereal *vi, doublereal *b, integer *ldb, + doublereal *work, doublereal *eps3, doublereal *smlnum, doublereal * + bignum, integer *info); + +/* Subroutine */ int dlaev2_(doublereal *a, doublereal *b, doublereal *c__, + doublereal *rt1, doublereal *rt2, doublereal *cs1, doublereal *sn1); + +/* Subroutine */ int dlaexc_(logical *wantq, integer *n, doublereal *t, + integer *ldt, doublereal *q, integer *ldq, integer *j1, integer *n1, + integer *n2, doublereal *work, integer *info); + +/* Subroutine */ int dlag2_(doublereal *a, integer *lda, doublereal *b, + integer *ldb, doublereal *safmin, doublereal *scale1, doublereal * + scale2, doublereal *wr1, doublereal *wr2, doublereal *wi); + +/* Subroutine */ int dlags2_(logical *upper, doublereal *a1, doublereal *a2, + doublereal *a3, doublereal *b1, doublereal *b2, doublereal *b3, + doublereal *csu, doublereal *snu, doublereal *csv, doublereal *snv, + doublereal *csq, doublereal *snq); + +/* Subroutine */ int dlagtf_(integer *n, doublereal *a, doublereal *lambda, + doublereal *b, doublereal *c__, doublereal *tol, doublereal *d__, + integer *in, integer *info); + +/* Subroutine */ int dlagtm_(char *trans, integer *n, integer *nrhs, + doublereal *alpha, doublereal *dl, doublereal *d__, doublereal *du, + doublereal *x, integer *ldx, doublereal *beta, doublereal *b, integer + *ldb); + +/* Subroutine */ int dlagts_(integer *job, integer *n, doublereal *a, + doublereal *b, doublereal *c__, doublereal *d__, integer *in, + doublereal *y, doublereal *tol, integer *info); + +/* Subroutine */ int dlagv2_(doublereal *a, integer *lda, doublereal *b, + integer *ldb, doublereal *alphar, doublereal *alphai, doublereal * + beta, doublereal *csl, doublereal *snl, doublereal *csr, doublereal * + snr); + +/* Subroutine */ int dlahqr_(logical *wantt, logical *wantz, integer *n, + integer *ilo, integer *ihi, doublereal *h__, integer *ldh, doublereal + *wr, doublereal *wi, integer *iloz, integer *ihiz, doublereal *z__, + integer *ldz, integer *info); + +/* Subroutine */ int dlahrd_(integer *n, integer *k, integer *nb, doublereal * + a, integer *lda, doublereal *tau, doublereal *t, integer *ldt, + doublereal *y, integer *ldy); + +/* Subroutine */ int dlaic1_(integer *job, integer *j, doublereal *x, + doublereal *sest, doublereal *w, doublereal *gamma, doublereal * + sestpr, doublereal *s, doublereal *c__); + +/* Subroutine */ int dlaln2_(logical *ltrans, integer *na, integer *nw, + doublereal *smin, doublereal *ca, doublereal *a, integer *lda, + doublereal *d1, doublereal *d2, doublereal *b, integer *ldb, + doublereal *wr, doublereal *wi, doublereal *x, integer *ldx, + doublereal *scale, doublereal *xnorm, integer *info); + +/* Subroutine */ int dlals0_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *nrhs, doublereal *b, integer *ldb, doublereal + *bx, integer *ldbx, integer *perm, integer *givptr, integer *givcol, + integer *ldgcol, doublereal *givnum, integer *ldgnum, doublereal * + poles, doublereal *difl, doublereal *difr, doublereal *z__, integer * + k, doublereal *c__, doublereal *s, doublereal *work, integer *info); + +/* Subroutine */ int dlalsa_(integer *icompq, integer *smlsiz, integer *n, + integer *nrhs, doublereal *b, integer *ldb, doublereal *bx, integer * + ldbx, doublereal *u, integer *ldu, doublereal *vt, integer *k, + doublereal *difl, doublereal *difr, doublereal *z__, doublereal * + poles, integer *givptr, integer *givcol, integer *ldgcol, integer * + perm, doublereal *givnum, doublereal *c__, doublereal *s, doublereal * + work, integer *iwork, integer *info); + +/* Subroutine */ int dlalsd_(char *uplo, integer *smlsiz, integer *n, integer + *nrhs, doublereal *d__, doublereal *e, doublereal *b, integer *ldb, + doublereal *rcond, integer *rank, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dlamc1_(integer *beta, integer *t, logical *rnd, logical + *ieee1); + +/* Subroutine */ int dlamc2_(integer *beta, integer *t, logical *rnd, + doublereal *eps, integer *emin, doublereal *rmin, integer *emax, + doublereal *rmax); + +/* Subroutine */ int dlamc4_(integer *emin, doublereal *start, integer *base); + +/* Subroutine */ int dlamc5_(integer *beta, integer *p, integer *emin, + logical *ieee, integer *emax, doublereal *rmax); + +/* Subroutine */ int dlamrg_(integer *n1, integer *n2, doublereal *a, integer + *dtrd1, integer *dtrd2, integer *index); + +/* Subroutine */ int dlanv2_(doublereal *a, doublereal *b, doublereal *c__, + doublereal *d__, doublereal *rt1r, doublereal *rt1i, doublereal *rt2r, + doublereal *rt2i, doublereal *cs, doublereal *sn); + +/* Subroutine */ int dlapll_(integer *n, doublereal *x, integer *incx, + doublereal *y, integer *incy, doublereal *ssmin); + +/* Subroutine */ int dlapmt_(logical *forwrd, integer *m, integer *n, + doublereal *x, integer *ldx, integer *k); + +/* Subroutine */ int dlaqgb_(integer *m, integer *n, integer *kl, integer *ku, + doublereal *ab, integer *ldab, doublereal *r__, doublereal *c__, + doublereal *rowcnd, doublereal *colcnd, doublereal *amax, char *equed); + +/* Subroutine */ int dlaqge_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *r__, doublereal *c__, doublereal *rowcnd, doublereal + *colcnd, doublereal *amax, char *equed); + +/* Subroutine */ int dlaqp2_(integer *m, integer *n, integer *offset, + doublereal *a, integer *lda, integer *jpvt, doublereal *tau, + doublereal *vn1, doublereal *vn2, doublereal *work); + +/* Subroutine */ int dlaqps_(integer *m, integer *n, integer *offset, integer + *nb, integer *kb, doublereal *a, integer *lda, integer *jpvt, + doublereal *tau, doublereal *vn1, doublereal *vn2, doublereal *auxv, + doublereal *f, integer *ldf); + +/* Subroutine */ int dlaqsb_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, doublereal *s, doublereal *scond, doublereal *amax, + char *equed); + +/* Subroutine */ int dlaqsp_(char *uplo, integer *n, doublereal *ap, + doublereal *s, doublereal *scond, doublereal *amax, char *equed); + +/* Subroutine */ int dlaqsy_(char *uplo, integer *n, doublereal *a, integer * + lda, doublereal *s, doublereal *scond, doublereal *amax, char *equed); + +/* Subroutine */ int dlaqtr_(logical *ltran, logical *lreal, integer *n, + doublereal *t, integer *ldt, doublereal *b, doublereal *w, doublereal + *scale, doublereal *x, doublereal *work, integer *info); + +/* Subroutine */ int dlar1v_(integer *n, integer *b1, integer *bn, doublereal + *sigma, doublereal *d__, doublereal *l, doublereal *ld, doublereal * + lld, doublereal *gersch, doublereal *z__, doublereal *ztz, doublereal + *mingma, integer *r__, integer *isuppz, doublereal *work); + +/* Subroutine */ int dlar2v_(integer *n, doublereal *x, doublereal *y, + doublereal *z__, integer *incx, doublereal *c__, doublereal *s, + integer *incc); + +/* Subroutine */ int dlarf_(char *side, integer *m, integer *n, doublereal *v, + integer *incv, doublereal *tau, doublereal *c__, integer *ldc, + doublereal *work); + +/* Subroutine */ int dlarfb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, doublereal *v, integer * + ldv, doublereal *t, integer *ldt, doublereal *c__, integer *ldc, + doublereal *work, integer *ldwork); + +/* Subroutine */ int dlarfg_(integer *n, doublereal *alpha, doublereal *x, + integer *incx, doublereal *tau); + +/* Subroutine */ int dlarft_(char *direct, char *storev, integer *n, integer * + k, doublereal *v, integer *ldv, doublereal *tau, doublereal *t, + integer *ldt); + +/* Subroutine */ int dlarfx_(char *side, integer *m, integer *n, doublereal * + v, doublereal *tau, doublereal *c__, integer *ldc, doublereal *work); + +/* Subroutine */ int dlargv_(integer *n, doublereal *x, integer *incx, + doublereal *y, integer *incy, doublereal *c__, integer *incc); + +/* Subroutine */ int dlarnv_(integer *idist, integer *iseed, integer *n, + doublereal *x); + +/* Subroutine */ int dlarrb_(integer *n, doublereal *d__, doublereal *l, + doublereal *ld, doublereal *lld, integer *ifirst, integer *ilast, + doublereal *sigma, doublereal *reltol, doublereal *w, doublereal * + wgap, doublereal *werr, doublereal *work, integer *iwork, integer * + info); + +/* Subroutine */ int dlarre_(integer *n, doublereal *d__, doublereal *e, + doublereal *tol, integer *nsplit, integer *isplit, integer *m, + doublereal *w, doublereal *woff, doublereal *gersch, doublereal *work, + integer *info); + +/* Subroutine */ int dlarrf_(integer *n, doublereal *d__, doublereal *l, + doublereal *ld, doublereal *lld, integer *ifirst, integer *ilast, + doublereal *w, doublereal *dplus, doublereal *lplus, doublereal *work, + integer *iwork, integer *info); + +/* Subroutine */ int dlarrv_(integer *n, doublereal *d__, doublereal *l, + integer *isplit, integer *m, doublereal *w, integer *iblock, + doublereal *gersch, doublereal *tol, doublereal *z__, integer *ldz, + integer *isuppz, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dlartg_(doublereal *f, doublereal *g, doublereal *cs, + doublereal *sn, doublereal *r__); + +/* Subroutine */ int dlartv_(integer *n, doublereal *x, integer *incx, + doublereal *y, integer *incy, doublereal *c__, doublereal *s, integer + *incc); + +/* Subroutine */ int dlaruv_(integer *iseed, integer *n, doublereal *x); + +/* Subroutine */ int dlarz_(char *side, integer *m, integer *n, integer *l, + doublereal *v, integer *incv, doublereal *tau, doublereal *c__, + integer *ldc, doublereal *work); + +/* Subroutine */ int dlarzb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, integer *l, doublereal *v, + integer *ldv, doublereal *t, integer *ldt, doublereal *c__, integer * + ldc, doublereal *work, integer *ldwork); + +/* Subroutine */ int dlarzt_(char *direct, char *storev, integer *n, integer * + k, doublereal *v, integer *ldv, doublereal *tau, doublereal *t, + integer *ldt); + +/* Subroutine */ int dlas2_(doublereal *f, doublereal *g, doublereal *h__, + doublereal *ssmin, doublereal *ssmax); + +/* Subroutine */ int dlascl_(char *type__, integer *kl, integer *ku, + doublereal *cfrom, doublereal *cto, integer *m, integer *n, + doublereal *a, integer *lda, integer *info); + +/* Subroutine */ int dlasd0_(integer *n, integer *sqre, doublereal *d__, + doublereal *e, doublereal *u, integer *ldu, doublereal *vt, integer * + ldvt, integer *smlsiz, integer *iwork, doublereal *work, integer * + info); + +/* Subroutine */ int dlasd1_(integer *nl, integer *nr, integer *sqre, + doublereal *d__, doublereal *alpha, doublereal *beta, doublereal *u, + integer *ldu, doublereal *vt, integer *ldvt, integer *idxq, integer * + iwork, doublereal *work, integer *info); + +/* Subroutine */ int dlasd2_(integer *nl, integer *nr, integer *sqre, integer + *k, doublereal *d__, doublereal *z__, doublereal *alpha, doublereal * + beta, doublereal *u, integer *ldu, doublereal *vt, integer *ldvt, + doublereal *dsigma, doublereal *u2, integer *ldu2, doublereal *vt2, + integer *ldvt2, integer *idxp, integer *idx, integer *idxc, integer * + idxq, integer *coltyp, integer *info); + +/* Subroutine */ int dlasd3_(integer *nl, integer *nr, integer *sqre, integer + *k, doublereal *d__, doublereal *q, integer *ldq, doublereal *dsigma, + doublereal *u, integer *ldu, doublereal *u2, integer *ldu2, + doublereal *vt, integer *ldvt, doublereal *vt2, integer *ldvt2, + integer *idxc, integer *ctot, doublereal *z__, integer *info); + +/* Subroutine */ int dlasd4_(integer *n, integer *i__, doublereal *d__, + doublereal *z__, doublereal *delta, doublereal *rho, doublereal * + sigma, doublereal *work, integer *info); + +/* Subroutine */ int dlasd5_(integer *i__, doublereal *d__, doublereal *z__, + doublereal *delta, doublereal *rho, doublereal *dsigma, doublereal * + work); + +/* Subroutine */ int dlasd6_(integer *icompq, integer *nl, integer *nr, + integer *sqre, doublereal *d__, doublereal *vf, doublereal *vl, + doublereal *alpha, doublereal *beta, integer *idxq, integer *perm, + integer *givptr, integer *givcol, integer *ldgcol, doublereal *givnum, + integer *ldgnum, doublereal *poles, doublereal *difl, doublereal * + difr, doublereal *z__, integer *k, doublereal *c__, doublereal *s, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dlasd7_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *k, doublereal *d__, doublereal *z__, + doublereal *zw, doublereal *vf, doublereal *vfw, doublereal *vl, + doublereal *vlw, doublereal *alpha, doublereal *beta, doublereal * + dsigma, integer *idx, integer *idxp, integer *idxq, integer *perm, + integer *givptr, integer *givcol, integer *ldgcol, doublereal *givnum, + integer *ldgnum, doublereal *c__, doublereal *s, integer *info); + +/* Subroutine */ int dlasd8_(integer *icompq, integer *k, doublereal *d__, + doublereal *z__, doublereal *vf, doublereal *vl, doublereal *difl, + doublereal *difr, integer *lddifr, doublereal *dsigma, doublereal * + work, integer *info); + +/* Subroutine */ int dlasd9_(integer *icompq, integer *ldu, integer *k, + doublereal *d__, doublereal *z__, doublereal *vf, doublereal *vl, + doublereal *difl, doublereal *difr, doublereal *dsigma, doublereal * + work, integer *info); + +/* Subroutine */ int dlasda_(integer *icompq, integer *smlsiz, integer *n, + integer *sqre, doublereal *d__, doublereal *e, doublereal *u, integer + *ldu, doublereal *vt, integer *k, doublereal *difl, doublereal *difr, + doublereal *z__, doublereal *poles, integer *givptr, integer *givcol, + integer *ldgcol, integer *perm, doublereal *givnum, doublereal *c__, + doublereal *s, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dlasdq_(char *uplo, integer *sqre, integer *n, integer * + ncvt, integer *nru, integer *ncc, doublereal *d__, doublereal *e, + doublereal *vt, integer *ldvt, doublereal *u, integer *ldu, + doublereal *c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dlasdt_(integer *n, integer *lvl, integer *nd, integer * + inode, integer *ndiml, integer *ndimr, integer *msub); + +/* Subroutine */ int dlaset_(char *uplo, integer *m, integer *n, doublereal * + alpha, doublereal *beta, doublereal *a, integer *lda); + +/* Subroutine */ int dlasq1_(integer *n, doublereal *d__, doublereal *e, + doublereal *work, integer *info); + +/* Subroutine */ int dlasq2_(integer *n, doublereal *z__, integer *info); + +/* Subroutine */ int dlasq3_(integer *i0, integer *n0, doublereal *z__, + integer *pp, doublereal *dmin__, doublereal *sigma, doublereal *desig, + doublereal *qmax, integer *nfail, integer *iter, integer *ndiv, + logical *ieee); + +/* Subroutine */ int dlasq4_(integer *i0, integer *n0, doublereal *z__, + integer *pp, integer *n0in, doublereal *dmin__, doublereal *dmin1, + doublereal *dmin2, doublereal *dn, doublereal *dn1, doublereal *dn2, + doublereal *tau, integer *ttype); + +/* Subroutine */ int dlasq5_(integer *i0, integer *n0, doublereal *z__, + integer *pp, doublereal *tau, doublereal *dmin__, doublereal *dmin1, + doublereal *dmin2, doublereal *dn, doublereal *dnm1, doublereal *dnm2, + logical *ieee); + +/* Subroutine */ int dlasq6_(integer *i0, integer *n0, doublereal *z__, + integer *pp, doublereal *dmin__, doublereal *dmin1, doublereal *dmin2, + doublereal *dn, doublereal *dnm1, doublereal *dnm2); + +/* Subroutine */ int dlasr_(char *side, char *pivot, char *direct, integer *m, + integer *n, doublereal *c__, doublereal *s, doublereal *a, integer * + lda); + +/* Subroutine */ int dlasrt_(char *id, integer *n, doublereal *d__, integer * + info); + +/* Subroutine */ int dlassq_(integer *n, doublereal *x, integer *incx, + doublereal *scale, doublereal *sumsq); + +/* Subroutine */ int dlasv2_(doublereal *f, doublereal *g, doublereal *h__, + doublereal *ssmin, doublereal *ssmax, doublereal *snr, doublereal * + csr, doublereal *snl, doublereal *csl); + +/* Subroutine */ int dlaswp_(integer *n, doublereal *a, integer *lda, integer + *k1, integer *k2, integer *ipiv, integer *incx); + +/* Subroutine */ int dlasy2_(logical *ltranl, logical *ltranr, integer *isgn, + integer *n1, integer *n2, doublereal *tl, integer *ldtl, doublereal * + tr, integer *ldtr, doublereal *b, integer *ldb, doublereal *scale, + doublereal *x, integer *ldx, doublereal *xnorm, integer *info); + +/* Subroutine */ int dlasyf_(char *uplo, integer *n, integer *nb, integer *kb, + doublereal *a, integer *lda, integer *ipiv, doublereal *w, integer * + ldw, integer *info); + +/* Subroutine */ int dlatbs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, integer *kd, doublereal *ab, integer *ldab, + doublereal *x, doublereal *scale, doublereal *cnorm, integer *info); + +/* Subroutine */ int dlatdf_(integer *ijob, integer *n, doublereal *z__, + integer *ldz, doublereal *rhs, doublereal *rdsum, doublereal *rdscal, + integer *ipiv, integer *jpiv); + +/* Subroutine */ int dlatps_(char *uplo, char *trans, char *diag, char * + normin, integer *n, doublereal *ap, doublereal *x, doublereal *scale, + doublereal *cnorm, integer *info); + +/* Subroutine */ int dlatrd_(char *uplo, integer *n, integer *nb, doublereal * + a, integer *lda, doublereal *e, doublereal *tau, doublereal *w, + integer *ldw); + +/* Subroutine */ int dlatrs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, doublereal *a, integer *lda, doublereal *x, + doublereal *scale, doublereal *cnorm, integer *info); + +/* Subroutine */ int dlatrz_(integer *m, integer *n, integer *l, doublereal * + a, integer *lda, doublereal *tau, doublereal *work); + +/* Subroutine */ int dlatzm_(char *side, integer *m, integer *n, doublereal * + v, integer *incv, doublereal *tau, doublereal *c1, doublereal *c2, + integer *ldc, doublereal *work); + +/* Subroutine */ int dlauu2_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *info); + +/* Subroutine */ int dlauum_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *info); + +/* Subroutine */ int dopgtr_(char *uplo, integer *n, doublereal *ap, + doublereal *tau, doublereal *q, integer *ldq, doublereal *work, + integer *info); + +/* Subroutine */ int dopmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, doublereal *ap, doublereal *tau, doublereal *c__, integer + *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dorg2l_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dorg2r_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dorgbr_(char *vect, integer *m, integer *n, integer *k, + doublereal *a, integer *lda, doublereal *tau, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dorghr_(integer *n, integer *ilo, integer *ihi, + doublereal *a, integer *lda, doublereal *tau, doublereal *work, + integer *lwork, integer *info); + +/* Subroutine */ int dorgl2_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dorglq_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dorgql_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dorgqr_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dorgr2_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *info); + +/* Subroutine */ int dorgrq_(integer *m, integer *n, integer *k, doublereal * + a, integer *lda, doublereal *tau, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dorgtr_(char *uplo, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dorm2l_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dorm2r_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dormbr_(char *vect, char *side, char *trans, integer *m, + integer *n, integer *k, doublereal *a, integer *lda, doublereal *tau, + doublereal *c__, integer *ldc, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dormhr_(char *side, char *trans, integer *m, integer *n, + integer *ilo, integer *ihi, doublereal *a, integer *lda, doublereal * + tau, doublereal *c__, integer *ldc, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dorml2_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dormlq_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dormql_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dormqr_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dormr2_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dormr3_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, doublereal *a, integer *lda, doublereal *tau, + doublereal *c__, integer *ldc, doublereal *work, integer *info); + +/* Subroutine */ int dormrq_(char *side, char *trans, integer *m, integer *n, + integer *k, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dormrz_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, doublereal *a, integer *lda, doublereal *tau, + doublereal *c__, integer *ldc, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dormtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, doublereal *a, integer *lda, doublereal *tau, doublereal * + c__, integer *ldc, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dpbcon_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, doublereal *anorm, doublereal *rcond, doublereal * + work, integer *iwork, integer *info); + +/* Subroutine */ int dpbequ_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, doublereal *s, doublereal *scond, doublereal *amax, + integer *info); + +/* Subroutine */ int dpbrfs_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublereal *ab, integer *ldab, doublereal *afb, integer *ldafb, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + ferr, doublereal *berr, doublereal *work, integer *iwork, integer * + info); + +/* Subroutine */ int dpbstf_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, integer *info); + +/* Subroutine */ int dpbsv_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublereal *ab, integer *ldab, doublereal *b, integer *ldb, + integer *info); + +/* Subroutine */ int dpbsvx_(char *fact, char *uplo, integer *n, integer *kd, + integer *nrhs, doublereal *ab, integer *ldab, doublereal *afb, + integer *ldafb, char *equed, doublereal *s, doublereal *b, integer * + ldb, doublereal *x, integer *ldx, doublereal *rcond, doublereal *ferr, + doublereal *berr, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dpbtf2_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, integer *info); + +/* Subroutine */ int dpbtrf_(char *uplo, integer *n, integer *kd, doublereal * + ab, integer *ldab, integer *info); + +/* Subroutine */ int dpbtrs_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublereal *ab, integer *ldab, doublereal *b, integer *ldb, + integer *info); + +/* Subroutine */ int dpocon_(char *uplo, integer *n, doublereal *a, integer * + lda, doublereal *anorm, doublereal *rcond, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dpoequ_(integer *n, doublereal *a, integer *lda, + doublereal *s, doublereal *scond, doublereal *amax, integer *info); + +/* Subroutine */ int dporfs_(char *uplo, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *af, integer *ldaf, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + ferr, doublereal *berr, doublereal *work, integer *iwork, integer * + info); + +/* Subroutine */ int dposv_(char *uplo, integer *n, integer *nrhs, doublereal + *a, integer *lda, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dposvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublereal *a, integer *lda, doublereal *af, integer *ldaf, + char *equed, doublereal *s, doublereal *b, integer *ldb, doublereal * + x, integer *ldx, doublereal *rcond, doublereal *ferr, doublereal * + berr, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dpotf2_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *info); + +/* Subroutine */ int dpotrf_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *info); + +/* Subroutine */ int dpotri_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *info); + +/* Subroutine */ int dpotrs_(char *uplo, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *b, integer *ldb, integer * + info); + +/* Subroutine */ int dppcon_(char *uplo, integer *n, doublereal *ap, + doublereal *anorm, doublereal *rcond, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dppequ_(char *uplo, integer *n, doublereal *ap, + doublereal *s, doublereal *scond, doublereal *amax, integer *info); + +/* Subroutine */ int dpprfs_(char *uplo, integer *n, integer *nrhs, + doublereal *ap, doublereal *afp, doublereal *b, integer *ldb, + doublereal *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dppsv_(char *uplo, integer *n, integer *nrhs, doublereal + *ap, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dppsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublereal *ap, doublereal *afp, char *equed, doublereal *s, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + rcond, doublereal *ferr, doublereal *berr, doublereal *work, integer * + iwork, integer *info); + +/* Subroutine */ int dpptrf_(char *uplo, integer *n, doublereal *ap, integer * + info); + +/* Subroutine */ int dpptri_(char *uplo, integer *n, doublereal *ap, integer * + info); + +/* Subroutine */ int dpptrs_(char *uplo, integer *n, integer *nrhs, + doublereal *ap, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dptcon_(integer *n, doublereal *d__, doublereal *e, + doublereal *anorm, doublereal *rcond, doublereal *work, integer *info); + +/* Subroutine */ int dpteqr_(char *compz, integer *n, doublereal *d__, + doublereal *e, doublereal *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int dptrfs_(integer *n, integer *nrhs, doublereal *d__, + doublereal *e, doublereal *df, doublereal *ef, doublereal *b, integer + *ldb, doublereal *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublereal *work, integer *info); + +/* Subroutine */ int dptsv_(integer *n, integer *nrhs, doublereal *d__, + doublereal *e, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dptsvx_(char *fact, integer *n, integer *nrhs, + doublereal *d__, doublereal *e, doublereal *df, doublereal *ef, + doublereal *b, integer *ldb, doublereal *x, integer *ldx, doublereal * + rcond, doublereal *ferr, doublereal *berr, doublereal *work, integer * + info); + +/* Subroutine */ int dpttrf_(integer *n, doublereal *d__, doublereal *e, + integer *info); + +/* Subroutine */ int dpttrs_(integer *n, integer *nrhs, doublereal *d__, + doublereal *e, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dptts2_(integer *n, integer *nrhs, doublereal *d__, + doublereal *e, doublereal *b, integer *ldb); + +/* Subroutine */ int drscl_(integer *n, doublereal *sa, doublereal *sx, + integer *incx); + +/* Subroutine */ int dsbev_(char *jobz, char *uplo, integer *n, integer *kd, + doublereal *ab, integer *ldab, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *info); + +/* Subroutine */ int dsbevd_(char *jobz, char *uplo, integer *n, integer *kd, + doublereal *ab, integer *ldab, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int dsbevx_(char *jobz, char *range, char *uplo, integer *n, + integer *kd, doublereal *ab, integer *ldab, doublereal *q, integer * + ldq, doublereal *vl, doublereal *vu, integer *il, integer *iu, + doublereal *abstol, integer *m, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *iwork, integer *ifail, + integer *info); + +/* Subroutine */ int dsbgst_(char *vect, char *uplo, integer *n, integer *ka, + integer *kb, doublereal *ab, integer *ldab, doublereal *bb, integer * + ldbb, doublereal *x, integer *ldx, doublereal *work, integer *info); + +/* Subroutine */ int dsbgv_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, doublereal *ab, integer *ldab, doublereal *bb, integer * + ldbb, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int dsbgvd_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, doublereal *ab, integer *ldab, doublereal *bb, integer * + ldbb, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dsbgvx_(char *jobz, char *range, char *uplo, integer *n, + integer *ka, integer *kb, doublereal *ab, integer *ldab, doublereal * + bb, integer *ldbb, doublereal *q, integer *ldq, doublereal *vl, + doublereal *vu, integer *il, integer *iu, doublereal *abstol, integer + *m, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int dsbtrd_(char *vect, char *uplo, integer *n, integer *kd, + doublereal *ab, integer *ldab, doublereal *d__, doublereal *e, + doublereal *q, integer *ldq, doublereal *work, integer *info); + +/* Subroutine */ int dspcon_(char *uplo, integer *n, doublereal *ap, integer * + ipiv, doublereal *anorm, doublereal *rcond, doublereal *work, integer + *iwork, integer *info); + +/* Subroutine */ int dspev_(char *jobz, char *uplo, integer *n, doublereal * + ap, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int dspevd_(char *jobz, char *uplo, integer *n, doublereal * + ap, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dspevx_(char *jobz, char *range, char *uplo, integer *n, + doublereal *ap, doublereal *vl, doublereal *vu, integer *il, integer * + iu, doublereal *abstol, integer *m, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *iwork, integer *ifail, + integer *info); + +/* Subroutine */ int dspgst_(integer *itype, char *uplo, integer *n, + doublereal *ap, doublereal *bp, integer *info); + +/* Subroutine */ int dspgv_(integer *itype, char *jobz, char *uplo, integer * + n, doublereal *ap, doublereal *bp, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *info); + +/* Subroutine */ int dspgvd_(integer *itype, char *jobz, char *uplo, integer * + n, doublereal *ap, doublereal *bp, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int dspgvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, doublereal *ap, doublereal *bp, doublereal *vl, + doublereal *vu, integer *il, integer *iu, doublereal *abstol, integer + *m, doublereal *w, doublereal *z__, integer *ldz, doublereal *work, + integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int dsprfs_(char *uplo, integer *n, integer *nrhs, + doublereal *ap, doublereal *afp, integer *ipiv, doublereal *b, + integer *ldb, doublereal *x, integer *ldx, doublereal *ferr, + doublereal *berr, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dspsv_(char *uplo, integer *n, integer *nrhs, doublereal + *ap, integer *ipiv, doublereal *b, integer *ldb, integer *info); + +/* Subroutine */ int dspsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublereal *ap, doublereal *afp, integer *ipiv, doublereal *b, + integer *ldb, doublereal *x, integer *ldx, doublereal *rcond, + doublereal *ferr, doublereal *berr, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dsptrd_(char *uplo, integer *n, doublereal *ap, + doublereal *d__, doublereal *e, doublereal *tau, integer *info); + +/* Subroutine */ int dsptrf_(char *uplo, integer *n, doublereal *ap, integer * + ipiv, integer *info); + +/* Subroutine */ int dsptri_(char *uplo, integer *n, doublereal *ap, integer * + ipiv, doublereal *work, integer *info); + +/* Subroutine */ int dsptrs_(char *uplo, integer *n, integer *nrhs, + doublereal *ap, integer *ipiv, doublereal *b, integer *ldb, integer * + info); + +/* Subroutine */ int dstebz_(char *range, char *order, integer *n, doublereal + *vl, doublereal *vu, integer *il, integer *iu, doublereal *abstol, + doublereal *d__, doublereal *e, integer *m, integer *nsplit, + doublereal *w, integer *iblock, integer *isplit, doublereal *work, + integer *iwork, integer *info); + +/* Subroutine */ int dstedc_(char *compz, integer *n, doublereal *d__, + doublereal *e, doublereal *z__, integer *ldz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dstegr_(char *jobz, char *range, integer *n, doublereal * + d__, doublereal *e, doublereal *vl, doublereal *vu, integer *il, + integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublereal *z__, integer *ldz, integer *isuppz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dstein_(integer *n, doublereal *d__, doublereal *e, + integer *m, doublereal *w, integer *iblock, integer *isplit, + doublereal *z__, integer *ldz, doublereal *work, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int dsteqr_(char *compz, integer *n, doublereal *d__, + doublereal *e, doublereal *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int dsterf_(integer *n, doublereal *d__, doublereal *e, + integer *info); + +/* Subroutine */ int dstev_(char *jobz, integer *n, doublereal *d__, + doublereal *e, doublereal *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int dstevd_(char *jobz, integer *n, doublereal *d__, + doublereal *e, doublereal *z__, integer *ldz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dstevr_(char *jobz, char *range, integer *n, doublereal * + d__, doublereal *e, doublereal *vl, doublereal *vu, integer *il, + integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublereal *z__, integer *ldz, integer *isuppz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dstevx_(char *jobz, char *range, integer *n, doublereal * + d__, doublereal *e, doublereal *vl, doublereal *vu, integer *il, + integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublereal *z__, integer *ldz, doublereal *work, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int dsycon_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *ipiv, doublereal *anorm, doublereal *rcond, doublereal * + work, integer *iwork, integer *info); + +/* Subroutine */ int dsyev_(char *jobz, char *uplo, integer *n, doublereal *a, + integer *lda, doublereal *w, doublereal *work, integer *lwork, + integer *info); + +/* Subroutine */ int dsyevd_(char *jobz, char *uplo, integer *n, doublereal * + a, integer *lda, doublereal *w, doublereal *work, integer *lwork, + integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dsyevr_(char *jobz, char *range, char *uplo, integer *n, + doublereal *a, integer *lda, doublereal *vl, doublereal *vu, integer * + il, integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublereal *z__, integer *ldz, integer *isuppz, doublereal *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int dsyevx_(char *jobz, char *range, char *uplo, integer *n, + doublereal *a, integer *lda, doublereal *vl, doublereal *vu, integer * + il, integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublereal *z__, integer *ldz, doublereal *work, integer *lwork, + integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int dsygs2_(integer *itype, char *uplo, integer *n, + doublereal *a, integer *lda, doublereal *b, integer *ldb, integer * + info); + +/* Subroutine */ int dsygst_(integer *itype, char *uplo, integer *n, + doublereal *a, integer *lda, doublereal *b, integer *ldb, integer * + info); + +/* Subroutine */ int dsygv_(integer *itype, char *jobz, char *uplo, integer * + n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *w, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dsygvd_(integer *itype, char *jobz, char *uplo, integer * + n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *w, doublereal *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int dsygvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, doublereal *a, integer *lda, doublereal *b, integer + *ldb, doublereal *vl, doublereal *vu, integer *il, integer *iu, + doublereal *abstol, integer *m, doublereal *w, doublereal *z__, + integer *ldz, doublereal *work, integer *lwork, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int dsyrfs_(char *uplo, integer *n, integer *nrhs, + doublereal *a, integer *lda, doublereal *af, integer *ldaf, integer * + ipiv, doublereal *b, integer *ldb, doublereal *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dsysv_(char *uplo, integer *n, integer *nrhs, doublereal + *a, integer *lda, integer *ipiv, doublereal *b, integer *ldb, + doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dsysvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublereal *a, integer *lda, doublereal *af, integer *ldaf, + integer *ipiv, doublereal *b, integer *ldb, doublereal *x, integer * + ldx, doublereal *rcond, doublereal *ferr, doublereal *berr, + doublereal *work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int dsytd2_(char *uplo, integer *n, doublereal *a, integer * + lda, doublereal *d__, doublereal *e, doublereal *tau, integer *info); + +/* Subroutine */ int dsytf2_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *ipiv, integer *info); + +/* Subroutine */ int dsytrd_(char *uplo, integer *n, doublereal *a, integer * + lda, doublereal *d__, doublereal *e, doublereal *tau, doublereal * + work, integer *lwork, integer *info); + +/* Subroutine */ int dsytrf_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *ipiv, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dsytri_(char *uplo, integer *n, doublereal *a, integer * + lda, integer *ipiv, doublereal *work, integer *info); + +/* Subroutine */ int dsytrs_(char *uplo, integer *n, integer *nrhs, + doublereal *a, integer *lda, integer *ipiv, doublereal *b, integer * + ldb, integer *info); + +/* Subroutine */ int dtbcon_(char *norm, char *uplo, char *diag, integer *n, + integer *kd, doublereal *ab, integer *ldab, doublereal *rcond, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dtbrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, doublereal *ab, integer *ldab, doublereal + *b, integer *ldb, doublereal *x, integer *ldx, doublereal *ferr, + doublereal *berr, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dtbtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, doublereal *ab, integer *ldab, doublereal + *b, integer *ldb, integer *info); + +/* Subroutine */ int dtgevc_(char *side, char *howmny, logical *select, + integer *n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *vl, integer *ldvl, doublereal *vr, integer *ldvr, integer + *mm, integer *m, doublereal *work, integer *info); + +/* Subroutine */ int dtgex2_(logical *wantq, logical *wantz, integer *n, + doublereal *a, integer *lda, doublereal *b, integer *ldb, doublereal * + q, integer *ldq, doublereal *z__, integer *ldz, integer *j1, integer * + n1, integer *n2, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dtgexc_(logical *wantq, logical *wantz, integer *n, + doublereal *a, integer *lda, doublereal *b, integer *ldb, doublereal * + q, integer *ldq, doublereal *z__, integer *ldz, integer *ifst, + integer *ilst, doublereal *work, integer *lwork, integer *info); + +/* Subroutine */ int dtgsen_(integer *ijob, logical *wantq, logical *wantz, + logical *select, integer *n, doublereal *a, integer *lda, doublereal * + b, integer *ldb, doublereal *alphar, doublereal *alphai, doublereal * + beta, doublereal *q, integer *ldq, doublereal *z__, integer *ldz, + integer *m, doublereal *pl, doublereal *pr, doublereal *dif, + doublereal *work, integer *lwork, integer *iwork, integer *liwork, + integer *info); + +/* Subroutine */ int dtgsja_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, integer *k, integer *l, doublereal *a, + integer *lda, doublereal *b, integer *ldb, doublereal *tola, + doublereal *tolb, doublereal *alpha, doublereal *beta, doublereal *u, + integer *ldu, doublereal *v, integer *ldv, doublereal *q, integer * + ldq, doublereal *work, integer *ncycle, integer *info); + +/* Subroutine */ int dtgsna_(char *job, char *howmny, logical *select, + integer *n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *vl, integer *ldvl, doublereal *vr, integer *ldvr, + doublereal *s, doublereal *dif, integer *mm, integer *m, doublereal * + work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int dtgsy2_(char *trans, integer *ijob, integer *m, integer * + n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *c__, integer *ldc, doublereal *d__, integer *ldd, + doublereal *e, integer *lde, doublereal *f, integer *ldf, doublereal * + scale, doublereal *rdsum, doublereal *rdscal, integer *iwork, integer + *pq, integer *info); + +/* Subroutine */ int dtgsyl_(char *trans, integer *ijob, integer *m, integer * + n, doublereal *a, integer *lda, doublereal *b, integer *ldb, + doublereal *c__, integer *ldc, doublereal *d__, integer *ldd, + doublereal *e, integer *lde, doublereal *f, integer *ldf, doublereal * + scale, doublereal *dif, doublereal *work, integer *lwork, integer * + iwork, integer *info); + +/* Subroutine */ int dtpcon_(char *norm, char *uplo, char *diag, integer *n, + doublereal *ap, doublereal *rcond, doublereal *work, integer *iwork, + integer *info); + +/* Subroutine */ int dtprfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublereal *ap, doublereal *b, integer *ldb, + doublereal *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dtptri_(char *uplo, char *diag, integer *n, doublereal * + ap, integer *info); + +/* Subroutine */ int dtptrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublereal *ap, doublereal *b, integer *ldb, integer * + info); + +/* Subroutine */ int dtrcon_(char *norm, char *uplo, char *diag, integer *n, + doublereal *a, integer *lda, doublereal *rcond, doublereal *work, + integer *iwork, integer *info); + +/* Subroutine */ int dtrevc_(char *side, char *howmny, logical *select, + integer *n, doublereal *t, integer *ldt, doublereal *vl, integer * + ldvl, doublereal *vr, integer *ldvr, integer *mm, integer *m, + doublereal *work, integer *info); + +/* Subroutine */ int dtrexc_(char *compq, integer *n, doublereal *t, integer * + ldt, doublereal *q, integer *ldq, integer *ifst, integer *ilst, + doublereal *work, integer *info); + +/* Subroutine */ int dtrrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublereal *a, integer *lda, doublereal *b, integer * + ldb, doublereal *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int dtrsen_(char *job, char *compq, logical *select, integer + *n, doublereal *t, integer *ldt, doublereal *q, integer *ldq, + doublereal *wr, doublereal *wi, integer *m, doublereal *s, doublereal + *sep, doublereal *work, integer *lwork, integer *iwork, integer * + liwork, integer *info); + +/* Subroutine */ int dtrsna_(char *job, char *howmny, logical *select, + integer *n, doublereal *t, integer *ldt, doublereal *vl, integer * + ldvl, doublereal *vr, integer *ldvr, doublereal *s, doublereal *sep, + integer *mm, integer *m, doublereal *work, integer *ldwork, integer * + iwork, integer *info); + +/* Subroutine */ int dtrsyl_(char *trana, char *tranb, integer *isgn, integer + *m, integer *n, doublereal *a, integer *lda, doublereal *b, integer * + ldb, doublereal *c__, integer *ldc, doublereal *scale, integer *info); + +/* Subroutine */ int dtrti2_(char *uplo, char *diag, integer *n, doublereal * + a, integer *lda, integer *info); + +/* Subroutine */ int dtrtri_(char *uplo, char *diag, integer *n, doublereal * + a, integer *lda, integer *info); + +/* Subroutine */ int dtrtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublereal *a, integer *lda, doublereal *b, integer * + ldb, integer *info); + +/* Subroutine */ int dtzrqf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, integer *info); + +/* Subroutine */ int dtzrzf_(integer *m, integer *n, doublereal *a, integer * + lda, doublereal *tau, doublereal *work, integer *lwork, integer *info); + +integer icmax1_(integer *n, complex *cx, integer *incx); + +integer ieeeck_(integer *ispec, real *zero, real *one); + +integer ilaenv_(integer *ispec, char *name__, char *opts, integer *n1, + integer *n2, integer *n3, integer *n4, ftnlen name_len, ftnlen + opts_len); + +integer izmax1_(integer *n, doublecomplex *cx, integer *incx); + +/* Subroutine */ int sbdsdc_(char *uplo, char *compq, integer *n, real *d__, + real *e, real *u, integer *ldu, real *vt, integer *ldvt, real *q, + integer *iq, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sbdsqr_(char *uplo, integer *n, integer *ncvt, integer * + nru, integer *ncc, real *d__, real *e, real *vt, integer *ldvt, real * + u, integer *ldu, real *c__, integer *ldc, real *work, integer *info); + +/* Subroutine */ int sdisna_(char *job, integer *m, integer *n, real *d__, + real *sep, integer *info); + +/* Subroutine */ int sgbbrd_(char *vect, integer *m, integer *n, integer *ncc, + integer *kl, integer *ku, real *ab, integer *ldab, real *d__, real * + e, real *q, integer *ldq, real *pt, integer *ldpt, real *c__, integer + *ldc, real *work, integer *info); + +/* Subroutine */ int sgbcon_(char *norm, integer *n, integer *kl, integer *ku, + real *ab, integer *ldab, integer *ipiv, real *anorm, real *rcond, + real *work, integer *iwork, integer *info); + +/* Subroutine */ int sgbequ_(integer *m, integer *n, integer *kl, integer *ku, + real *ab, integer *ldab, real *r__, real *c__, real *rowcnd, real * + colcnd, real *amax, integer *info); + +/* Subroutine */ int sgbrfs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, real *ab, integer *ldab, real *afb, integer *ldafb, + integer *ipiv, real *b, integer *ldb, real *x, integer *ldx, real * + ferr, real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sgbsv_(integer *n, integer *kl, integer *ku, integer * + nrhs, real *ab, integer *ldab, integer *ipiv, real *b, integer *ldb, + integer *info); + +/* Subroutine */ int sgbsvx_(char *fact, char *trans, integer *n, integer *kl, + integer *ku, integer *nrhs, real *ab, integer *ldab, real *afb, + integer *ldafb, integer *ipiv, char *equed, real *r__, real *c__, + real *b, integer *ldb, real *x, integer *ldx, real *rcond, real *ferr, + real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sgbtf2_(integer *m, integer *n, integer *kl, integer *ku, + real *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int sgbtrf_(integer *m, integer *n, integer *kl, integer *ku, + real *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int sgbtrs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, real *ab, integer *ldab, integer *ipiv, real *b, + integer *ldb, integer *info); + +/* Subroutine */ int sgebak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, real *scale, integer *m, real *v, integer *ldv, integer + *info); + +/* Subroutine */ int sgebal_(char *job, integer *n, real *a, integer *lda, + integer *ilo, integer *ihi, real *scale, integer *info); + +/* Subroutine */ int sgebd2_(integer *m, integer *n, real *a, integer *lda, + real *d__, real *e, real *tauq, real *taup, real *work, integer *info); + +/* Subroutine */ int sgebrd_(integer *m, integer *n, real *a, integer *lda, + real *d__, real *e, real *tauq, real *taup, real *work, integer * + lwork, integer *info); + +/* Subroutine */ int sgecon_(char *norm, integer *n, real *a, integer *lda, + real *anorm, real *rcond, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sgeequ_(integer *m, integer *n, real *a, integer *lda, + real *r__, real *c__, real *rowcnd, real *colcnd, real *amax, integer + *info); + +/* Subroutine */ int sgees_(char *jobvs, char *sort, L_fp select, integer *n, + real *a, integer *lda, integer *sdim, real *wr, real *wi, real *vs, + integer *ldvs, real *work, integer *lwork, logical *bwork, integer * + info); + +/* Subroutine */ int sgeesx_(char *jobvs, char *sort, L_fp select, char * + sense, integer *n, real *a, integer *lda, integer *sdim, real *wr, + real *wi, real *vs, integer *ldvs, real *rconde, real *rcondv, real * + work, integer *lwork, integer *iwork, integer *liwork, logical *bwork, + integer *info); + +/* Subroutine */ int sgeev_(char *jobvl, char *jobvr, integer *n, real *a, + integer *lda, real *wr, real *wi, real *vl, integer *ldvl, real *vr, + integer *ldvr, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgeevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, real *a, integer *lda, real *wr, real *wi, real * + vl, integer *ldvl, real *vr, integer *ldvr, integer *ilo, integer * + ihi, real *scale, real *abnrm, real *rconde, real *rcondv, real *work, + integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int sgegs_(char *jobvsl, char *jobvsr, integer *n, real *a, + integer *lda, real *b, integer *ldb, real *alphar, real *alphai, real + *beta, real *vsl, integer *ldvsl, real *vsr, integer *ldvsr, real * + work, integer *lwork, integer *info); + +/* Subroutine */ int sgegv_(char *jobvl, char *jobvr, integer *n, real *a, + integer *lda, real *b, integer *ldb, real *alphar, real *alphai, real + *beta, real *vl, integer *ldvl, real *vr, integer *ldvr, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int sgehd2_(integer *n, integer *ilo, integer *ihi, real *a, + integer *lda, real *tau, real *work, integer *info); + +/* Subroutine */ int sgehrd_(integer *n, integer *ilo, integer *ihi, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgelq2_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *info); + +/* Subroutine */ int sgelqf_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgels_(char *trans, integer *m, integer *n, integer * + nrhs, real *a, integer *lda, real *b, integer *ldb, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int sgelsd_(integer *m, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, real *s, real *rcond, integer * + rank, real *work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int sgelss_(integer *m, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, real *s, real *rcond, integer * + rank, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgelsx_(integer *m, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, integer *jpvt, real *rcond, + integer *rank, real *work, integer *info); + +/* Subroutine */ int sgelsy_(integer *m, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, integer *jpvt, real *rcond, + integer *rank, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgeql2_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *info); + +/* Subroutine */ int sgeqlf_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgeqp3_(integer *m, integer *n, real *a, integer *lda, + integer *jpvt, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgeqpf_(integer *m, integer *n, real *a, integer *lda, + integer *jpvt, real *tau, real *work, integer *info); + +/* Subroutine */ int sgeqr2_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *info); + +/* Subroutine */ int sgeqrf_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgerfs_(char *trans, integer *n, integer *nrhs, real *a, + integer *lda, real *af, integer *ldaf, integer *ipiv, real *b, + integer *ldb, real *x, integer *ldx, real *ferr, real *berr, real * + work, integer *iwork, integer *info); + +/* Subroutine */ int sgerq2_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *info); + +/* Subroutine */ int sgerqf_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgesc2_(integer *n, real *a, integer *lda, real *rhs, + integer *ipiv, integer *jpiv, real *scale); + +/* Subroutine */ int sgesdd_(char *jobz, integer *m, integer *n, real *a, + integer *lda, real *s, real *u, integer *ldu, real *vt, integer *ldvt, + real *work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int sgesv_(integer *n, integer *nrhs, real *a, integer *lda, + integer *ipiv, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sgesvd_(char *jobu, char *jobvt, integer *m, integer *n, + real *a, integer *lda, real *s, real *u, integer *ldu, real *vt, + integer *ldvt, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgesvx_(char *fact, char *trans, integer *n, integer * + nrhs, real *a, integer *lda, real *af, integer *ldaf, integer *ipiv, + char *equed, real *r__, real *c__, real *b, integer *ldb, real *x, + integer *ldx, real *rcond, real *ferr, real *berr, real *work, + integer *iwork, integer *info); + +/* Subroutine */ int sgetc2_(integer *n, real *a, integer *lda, integer *ipiv, + integer *jpiv, integer *info); + +/* Subroutine */ int sgetf2_(integer *m, integer *n, real *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int sgetrf_(integer *m, integer *n, real *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int sgetri_(integer *n, real *a, integer *lda, integer *ipiv, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgetrs_(char *trans, integer *n, integer *nrhs, real *a, + integer *lda, integer *ipiv, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sggbak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, real *lscale, real *rscale, integer *m, real *v, + integer *ldv, integer *info); + +/* Subroutine */ int sggbal_(char *job, integer *n, real *a, integer *lda, + real *b, integer *ldb, integer *ilo, integer *ihi, real *lscale, real + *rscale, real *work, integer *info); + +/* Subroutine */ int sgges_(char *jobvsl, char *jobvsr, char *sort, L_fp + selctg, integer *n, real *a, integer *lda, real *b, integer *ldb, + integer *sdim, real *alphar, real *alphai, real *beta, real *vsl, + integer *ldvsl, real *vsr, integer *ldvsr, real *work, integer *lwork, + logical *bwork, integer *info); + +/* Subroutine */ int sggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp + selctg, char *sense, integer *n, real *a, integer *lda, real *b, + integer *ldb, integer *sdim, real *alphar, real *alphai, real *beta, + real *vsl, integer *ldvsl, real *vsr, integer *ldvsr, real *rconde, + real *rcondv, real *work, integer *lwork, integer *iwork, integer * + liwork, logical *bwork, integer *info); + +/* Subroutine */ int sggev_(char *jobvl, char *jobvr, integer *n, real *a, + integer *lda, real *b, integer *ldb, real *alphar, real *alphai, real + *beta, real *vl, integer *ldvl, real *vr, integer *ldvr, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int sggevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, real *a, integer *lda, real *b, integer *ldb, real + *alphar, real *alphai, real *beta, real *vl, integer *ldvl, real *vr, + integer *ldvr, integer *ilo, integer *ihi, real *lscale, real *rscale, + real *abnrm, real *bbnrm, real *rconde, real *rcondv, real *work, + integer *lwork, integer *iwork, logical *bwork, integer *info); + +/* Subroutine */ int sggglm_(integer *n, integer *m, integer *p, real *a, + integer *lda, real *b, integer *ldb, real *d__, real *x, real *y, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sgghrd_(char *compq, char *compz, integer *n, integer * + ilo, integer *ihi, real *a, integer *lda, real *b, integer *ldb, real + *q, integer *ldq, real *z__, integer *ldz, integer *info); + +/* Subroutine */ int sgglse_(integer *m, integer *n, integer *p, real *a, + integer *lda, real *b, integer *ldb, real *c__, real *d__, real *x, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sggqrf_(integer *n, integer *m, integer *p, real *a, + integer *lda, real *taua, real *b, integer *ldb, real *taub, real * + work, integer *lwork, integer *info); + +/* Subroutine */ int sggrqf_(integer *m, integer *p, integer *n, real *a, + integer *lda, real *taua, real *b, integer *ldb, real *taub, real * + work, integer *lwork, integer *info); + +/* Subroutine */ int sggsvd_(char *jobu, char *jobv, char *jobq, integer *m, + integer *n, integer *p, integer *k, integer *l, real *a, integer *lda, + real *b, integer *ldb, real *alpha, real *beta, real *u, integer * + ldu, real *v, integer *ldv, real *q, integer *ldq, real *work, + integer *iwork, integer *info); + +/* Subroutine */ int sggsvp_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, real *a, integer *lda, real *b, integer *ldb, + real *tola, real *tolb, integer *k, integer *l, real *u, integer *ldu, + real *v, integer *ldv, real *q, integer *ldq, integer *iwork, real * + tau, real *work, integer *info); + +/* Subroutine */ int sgtcon_(char *norm, integer *n, real *dl, real *d__, + real *du, real *du2, integer *ipiv, real *anorm, real *rcond, real * + work, integer *iwork, integer *info); + +/* Subroutine */ int sgtrfs_(char *trans, integer *n, integer *nrhs, real *dl, + real *d__, real *du, real *dlf, real *df, real *duf, real *du2, + integer *ipiv, real *b, integer *ldb, real *x, integer *ldx, real * + ferr, real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sgtsv_(integer *n, integer *nrhs, real *dl, real *d__, + real *du, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sgtsvx_(char *fact, char *trans, integer *n, integer * + nrhs, real *dl, real *d__, real *du, real *dlf, real *df, real *duf, + real *du2, integer *ipiv, real *b, integer *ldb, real *x, integer * + ldx, real *rcond, real *ferr, real *berr, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int sgttrf_(integer *n, real *dl, real *d__, real *du, real * + du2, integer *ipiv, integer *info); + +/* Subroutine */ int sgttrs_(char *trans, integer *n, integer *nrhs, real *dl, + real *d__, real *du, real *du2, integer *ipiv, real *b, integer *ldb, + integer *info); + +/* Subroutine */ int sgtts2_(integer *itrans, integer *n, integer *nrhs, real + *dl, real *d__, real *du, real *du2, integer *ipiv, real *b, integer * + ldb); + +/* Subroutine */ int shgeqz_(char *job, char *compq, char *compz, integer *n, + integer *ilo, integer *ihi, real *a, integer *lda, real *b, integer * + ldb, real *alphar, real *alphai, real *beta, real *q, integer *ldq, + real *z__, integer *ldz, real *work, integer *lwork, integer *info); + +/* Subroutine */ int shsein_(char *side, char *eigsrc, char *initv, logical * + select, integer *n, real *h__, integer *ldh, real *wr, real *wi, real + *vl, integer *ldvl, real *vr, integer *ldvr, integer *mm, integer *m, + real *work, integer *ifaill, integer *ifailr, integer *info); + +/* Subroutine */ int shseqr_(char *job, char *compz, integer *n, integer *ilo, + integer *ihi, real *h__, integer *ldh, real *wr, real *wi, real *z__, + integer *ldz, real *work, integer *lwork, integer *info); + +/* Subroutine */ int slabad_(real *small, real *large); + +/* Subroutine */ int slabrd_(integer *m, integer *n, integer *nb, real *a, + integer *lda, real *d__, real *e, real *tauq, real *taup, real *x, + integer *ldx, real *y, integer *ldy); + +/* Subroutine */ int slacon_(integer *n, real *v, real *x, integer *isgn, + real *est, integer *kase); + +/* Subroutine */ int slacpy_(char *uplo, integer *m, integer *n, real *a, + integer *lda, real *b, integer *ldb); + +/* Subroutine */ int sladiv_(real *a, real *b, real *c__, real *d__, real *p, + real *q); + +/* Subroutine */ int slae2_(real *a, real *b, real *c__, real *rt1, real *rt2); + +/* Subroutine */ int slaebz_(integer *ijob, integer *nitmax, integer *n, + integer *mmax, integer *minp, integer *nbmin, real *abstol, real * + reltol, real *pivmin, real *d__, real *e, real *e2, integer *nval, + real *ab, real *c__, integer *mout, integer *nab, real *work, integer + *iwork, integer *info); + +/* Subroutine */ int slaed0_(integer *icompq, integer *qsiz, integer *n, real + *d__, real *e, real *q, integer *ldq, real *qstore, integer *ldqs, + real *work, integer *iwork, integer *info); + +/* Subroutine */ int slaed1_(integer *n, real *d__, real *q, integer *ldq, + integer *indxq, real *rho, integer *cutpnt, real *work, integer * + iwork, integer *info); + +/* Subroutine */ int slaed2_(integer *k, integer *n, integer *n1, real *d__, + real *q, integer *ldq, integer *indxq, real *rho, real *z__, real * + dlamda, real *w, real *q2, integer *indx, integer *indxc, integer * + indxp, integer *coltyp, integer *info); + +/* Subroutine */ int slaed3_(integer *k, integer *n, integer *n1, real *d__, + real *q, integer *ldq, real *rho, real *dlamda, real *q2, integer * + indx, integer *ctot, real *w, real *s, integer *info); + +/* Subroutine */ int slaed4_(integer *n, integer *i__, real *d__, real *z__, + real *delta, real *rho, real *dlam, integer *info); + +/* Subroutine */ int slaed5_(integer *i__, real *d__, real *z__, real *delta, + real *rho, real *dlam); + +/* Subroutine */ int slaed6_(integer *kniter, logical *orgati, real *rho, + real *d__, real *z__, real *finit, real *tau, integer *info); + +/* Subroutine */ int slaed7_(integer *icompq, integer *n, integer *qsiz, + integer *tlvls, integer *curlvl, integer *curpbm, real *d__, real *q, + integer *ldq, integer *indxq, real *rho, integer *cutpnt, real * + qstore, integer *qptr, integer *prmptr, integer *perm, integer * + givptr, integer *givcol, real *givnum, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int slaed8_(integer *icompq, integer *k, integer *n, integer + *qsiz, real *d__, real *q, integer *ldq, integer *indxq, real *rho, + integer *cutpnt, real *z__, real *dlamda, real *q2, integer *ldq2, + real *w, integer *perm, integer *givptr, integer *givcol, real * + givnum, integer *indxp, integer *indx, integer *info); + +/* Subroutine */ int slaed9_(integer *k, integer *kstart, integer *kstop, + integer *n, real *d__, real *q, integer *ldq, real *rho, real *dlamda, + real *w, real *s, integer *lds, integer *info); + +/* Subroutine */ int slaeda_(integer *n, integer *tlvls, integer *curlvl, + integer *curpbm, integer *prmptr, integer *perm, integer *givptr, + integer *givcol, real *givnum, real *q, integer *qptr, real *z__, + real *ztemp, integer *info); + +/* Subroutine */ int slaein_(logical *rightv, logical *noinit, integer *n, + real *h__, integer *ldh, real *wr, real *wi, real *vr, real *vi, real + *b, integer *ldb, real *work, real *eps3, real *smlnum, real *bignum, + integer *info); + +/* Subroutine */ int slaev2_(real *a, real *b, real *c__, real *rt1, real * + rt2, real *cs1, real *sn1); + +/* Subroutine */ int slaexc_(logical *wantq, integer *n, real *t, integer * + ldt, real *q, integer *ldq, integer *j1, integer *n1, integer *n2, + real *work, integer *info); + +/* Subroutine */ int slag2_(real *a, integer *lda, real *b, integer *ldb, + real *safmin, real *scale1, real *scale2, real *wr1, real *wr2, real * + wi); + +/* Subroutine */ int slags2_(logical *upper, real *a1, real *a2, real *a3, + real *b1, real *b2, real *b3, real *csu, real *snu, real *csv, real * + snv, real *csq, real *snq); + +/* Subroutine */ int slagtf_(integer *n, real *a, real *lambda, real *b, real + *c__, real *tol, real *d__, integer *in, integer *info); + +/* Subroutine */ int slagtm_(char *trans, integer *n, integer *nrhs, real * + alpha, real *dl, real *d__, real *du, real *x, integer *ldx, real * + beta, real *b, integer *ldb); + +/* Subroutine */ int slagts_(integer *job, integer *n, real *a, real *b, real + *c__, real *d__, integer *in, real *y, real *tol, integer *info); + +/* Subroutine */ int slagv2_(real *a, integer *lda, real *b, integer *ldb, + real *alphar, real *alphai, real *beta, real *csl, real *snl, real * + csr, real *snr); + +/* Subroutine */ int slahqr_(logical *wantt, logical *wantz, integer *n, + integer *ilo, integer *ihi, real *h__, integer *ldh, real *wr, real * + wi, integer *iloz, integer *ihiz, real *z__, integer *ldz, integer * + info); + +/* Subroutine */ int slahrd_(integer *n, integer *k, integer *nb, real *a, + integer *lda, real *tau, real *t, integer *ldt, real *y, integer *ldy); + +/* Subroutine */ int slaic1_(integer *job, integer *j, real *x, real *sest, + real *w, real *gamma, real *sestpr, real *s, real *c__); + +/* Subroutine */ int slaln2_(logical *ltrans, integer *na, integer *nw, real * + smin, real *ca, real *a, integer *lda, real *d1, real *d2, real *b, + integer *ldb, real *wr, real *wi, real *x, integer *ldx, real *scale, + real *xnorm, integer *info); + +/* Subroutine */ int slals0_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *nrhs, real *b, integer *ldb, real *bx, + integer *ldbx, integer *perm, integer *givptr, integer *givcol, + integer *ldgcol, real *givnum, integer *ldgnum, real *poles, real * + difl, real *difr, real *z__, integer *k, real *c__, real *s, real * + work, integer *info); + +/* Subroutine */ int slalsa_(integer *icompq, integer *smlsiz, integer *n, + integer *nrhs, real *b, integer *ldb, real *bx, integer *ldbx, real * + u, integer *ldu, real *vt, integer *k, real *difl, real *difr, real * + z__, real *poles, integer *givptr, integer *givcol, integer *ldgcol, + integer *perm, real *givnum, real *c__, real *s, real *work, integer * + iwork, integer *info); + +/* Subroutine */ int slalsd_(char *uplo, integer *smlsiz, integer *n, integer + *nrhs, real *d__, real *e, real *b, integer *ldb, real *rcond, + integer *rank, real *work, integer *iwork, integer *info); + +/* Subroutine */ int slamc1_(integer *beta, integer *t, logical *rnd, logical + *ieee1); + +/* Subroutine */ int slamc2_(integer *beta, integer *t, logical *rnd, real * + eps, integer *emin, real *rmin, integer *emax, real *rmax); + +/* Subroutine */ int slamc4_(integer *emin, real *start, integer *base); + +/* Subroutine */ int slamc5_(integer *beta, integer *p, integer *emin, + logical *ieee, integer *emax, real *rmax); + +/* Subroutine */ int slamrg_(integer *n1, integer *n2, real *a, integer * + strd1, integer *strd2, integer *index); + +/* Subroutine */ int slanv2_(real *a, real *b, real *c__, real *d__, real * + rt1r, real *rt1i, real *rt2r, real *rt2i, real *cs, real *sn); + +/* Subroutine */ int slapll_(integer *n, real *x, integer *incx, real *y, + integer *incy, real *ssmin); + +/* Subroutine */ int slapmt_(logical *forwrd, integer *m, integer *n, real *x, + integer *ldx, integer *k); + +/* Subroutine */ int slaqgb_(integer *m, integer *n, integer *kl, integer *ku, + real *ab, integer *ldab, real *r__, real *c__, real *rowcnd, real * + colcnd, real *amax, char *equed); + +/* Subroutine */ int slaqge_(integer *m, integer *n, real *a, integer *lda, + real *r__, real *c__, real *rowcnd, real *colcnd, real *amax, char * + equed); + +/* Subroutine */ int slaqp2_(integer *m, integer *n, integer *offset, real *a, + integer *lda, integer *jpvt, real *tau, real *vn1, real *vn2, real * + work); + +/* Subroutine */ int slaqps_(integer *m, integer *n, integer *offset, integer + *nb, integer *kb, real *a, integer *lda, integer *jpvt, real *tau, + real *vn1, real *vn2, real *auxv, real *f, integer *ldf); + +/* Subroutine */ int slaqsb_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int slaqsp_(char *uplo, integer *n, real *ap, real *s, real * + scond, real *amax, char *equed); + +/* Subroutine */ int slaqsy_(char *uplo, integer *n, real *a, integer *lda, + real *s, real *scond, real *amax, char *equed); + +/* Subroutine */ int slaqtr_(logical *ltran, logical *lreal, integer *n, real + *t, integer *ldt, real *b, real *w, real *scale, real *x, real *work, + integer *info); + +/* Subroutine */ int slar1v_(integer *n, integer *b1, integer *bn, real * + sigma, real *d__, real *l, real *ld, real *lld, real *gersch, real * + z__, real *ztz, real *mingma, integer *r__, integer *isuppz, real * + work); + +/* Subroutine */ int slar2v_(integer *n, real *x, real *y, real *z__, integer + *incx, real *c__, real *s, integer *incc); + +/* Subroutine */ int slarf_(char *side, integer *m, integer *n, real *v, + integer *incv, real *tau, real *c__, integer *ldc, real *work); + +/* Subroutine */ int slarfb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, real *v, integer *ldv, + real *t, integer *ldt, real *c__, integer *ldc, real *work, integer * + ldwork); + +/* Subroutine */ int slarfg_(integer *n, real *alpha, real *x, integer *incx, + real *tau); + +/* Subroutine */ int slarft_(char *direct, char *storev, integer *n, integer * + k, real *v, integer *ldv, real *tau, real *t, integer *ldt); + +/* Subroutine */ int slarfx_(char *side, integer *m, integer *n, real *v, + real *tau, real *c__, integer *ldc, real *work); + +/* Subroutine */ int slargv_(integer *n, real *x, integer *incx, real *y, + integer *incy, real *c__, integer *incc); + +/* Subroutine */ int slarnv_(integer *idist, integer *iseed, integer *n, real + *x); + +/* Subroutine */ int slarrb_(integer *n, real *d__, real *l, real *ld, real * + lld, integer *ifirst, integer *ilast, real *sigma, real *reltol, real + *w, real *wgap, real *werr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int slarre_(integer *n, real *d__, real *e, real *tol, + integer *nsplit, integer *isplit, integer *m, real *w, real *woff, + real *gersch, real *work, integer *info); + +/* Subroutine */ int slarrf_(integer *n, real *d__, real *l, real *ld, real * + lld, integer *ifirst, integer *ilast, real *w, real *dplus, real * + lplus, real *work, integer *iwork, integer *info); + +/* Subroutine */ int slarrv_(integer *n, real *d__, real *l, integer *isplit, + integer *m, real *w, integer *iblock, real *gersch, real *tol, real * + z__, integer *ldz, integer *isuppz, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int slartg_(real *f, real *g, real *cs, real *sn, real *r__); + +/* Subroutine */ int slartv_(integer *n, real *x, integer *incx, real *y, + integer *incy, real *c__, real *s, integer *incc); + +/* Subroutine */ int slaruv_(integer *iseed, integer *n, real *x); + +/* Subroutine */ int slarz_(char *side, integer *m, integer *n, integer *l, + real *v, integer *incv, real *tau, real *c__, integer *ldc, real * + work); + +/* Subroutine */ int slarzb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, integer *l, real *v, + integer *ldv, real *t, integer *ldt, real *c__, integer *ldc, real * + work, integer *ldwork); + +/* Subroutine */ int slarzt_(char *direct, char *storev, integer *n, integer * + k, real *v, integer *ldv, real *tau, real *t, integer *ldt); + +/* Subroutine */ int slas2_(real *f, real *g, real *h__, real *ssmin, real * + ssmax); + +/* Subroutine */ int slascl_(char *type__, integer *kl, integer *ku, real * + cfrom, real *cto, integer *m, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int slasd0_(integer *n, integer *sqre, real *d__, real *e, + real *u, integer *ldu, real *vt, integer *ldvt, integer *smlsiz, + integer *iwork, real *work, integer *info); + +/* Subroutine */ int slasd1_(integer *nl, integer *nr, integer *sqre, real * + d__, real *alpha, real *beta, real *u, integer *ldu, real *vt, + integer *ldvt, integer *idxq, integer *iwork, real *work, integer * + info); + +/* Subroutine */ int slasd2_(integer *nl, integer *nr, integer *sqre, integer + *k, real *d__, real *z__, real *alpha, real *beta, real *u, integer * + ldu, real *vt, integer *ldvt, real *dsigma, real *u2, integer *ldu2, + real *vt2, integer *ldvt2, integer *idxp, integer *idx, integer *idxc, + integer *idxq, integer *coltyp, integer *info); + +/* Subroutine */ int slasd3_(integer *nl, integer *nr, integer *sqre, integer + *k, real *d__, real *q, integer *ldq, real *dsigma, real *u, integer * + ldu, real *u2, integer *ldu2, real *vt, integer *ldvt, real *vt2, + integer *ldvt2, integer *idxc, integer *ctot, real *z__, integer * + info); + +/* Subroutine */ int slasd4_(integer *n, integer *i__, real *d__, real *z__, + real *delta, real *rho, real *sigma, real *work, integer *info); + +/* Subroutine */ int slasd5_(integer *i__, real *d__, real *z__, real *delta, + real *rho, real *dsigma, real *work); + +/* Subroutine */ int slasd6_(integer *icompq, integer *nl, integer *nr, + integer *sqre, real *d__, real *vf, real *vl, real *alpha, real *beta, + integer *idxq, integer *perm, integer *givptr, integer *givcol, + integer *ldgcol, real *givnum, integer *ldgnum, real *poles, real * + difl, real *difr, real *z__, integer *k, real *c__, real *s, real * + work, integer *iwork, integer *info); + +/* Subroutine */ int slasd7_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *k, real *d__, real *z__, real *zw, real *vf, + real *vfw, real *vl, real *vlw, real *alpha, real *beta, real *dsigma, + integer *idx, integer *idxp, integer *idxq, integer *perm, integer * + givptr, integer *givcol, integer *ldgcol, real *givnum, integer * + ldgnum, real *c__, real *s, integer *info); + +/* Subroutine */ int slasd8_(integer *icompq, integer *k, real *d__, real * + z__, real *vf, real *vl, real *difl, real *difr, integer *lddifr, + real *dsigma, real *work, integer *info); + +/* Subroutine */ int slasd9_(integer *icompq, integer *ldu, integer *k, real * + d__, real *z__, real *vf, real *vl, real *difl, real *difr, real * + dsigma, real *work, integer *info); + +/* Subroutine */ int slasda_(integer *icompq, integer *smlsiz, integer *n, + integer *sqre, real *d__, real *e, real *u, integer *ldu, real *vt, + integer *k, real *difl, real *difr, real *z__, real *poles, integer * + givptr, integer *givcol, integer *ldgcol, integer *perm, real *givnum, + real *c__, real *s, real *work, integer *iwork, integer *info); + +/* Subroutine */ int slasdq_(char *uplo, integer *sqre, integer *n, integer * + ncvt, integer *nru, integer *ncc, real *d__, real *e, real *vt, + integer *ldvt, real *u, integer *ldu, real *c__, integer *ldc, real * + work, integer *info); + +/* Subroutine */ int slasdt_(integer *n, integer *lvl, integer *nd, integer * + inode, integer *ndiml, integer *ndimr, integer *msub); + +/* Subroutine */ int slaset_(char *uplo, integer *m, integer *n, real *alpha, + real *beta, real *a, integer *lda); + +/* Subroutine */ int slasq1_(integer *n, real *d__, real *e, real *work, + integer *info); + +/* Subroutine */ int slasq2_(integer *n, real *z__, integer *info); + +/* Subroutine */ int slasq3_(integer *i0, integer *n0, real *z__, integer *pp, + real *dmin__, real *sigma, real *desig, real *qmax, integer *nfail, + integer *iter, integer *ndiv, logical *ieee); + +/* Subroutine */ int slasq4_(integer *i0, integer *n0, real *z__, integer *pp, + integer *n0in, real *dmin__, real *dmin1, real *dmin2, real *dn, + real *dn1, real *dn2, real *tau, integer *ttype); + +/* Subroutine */ int slasq5_(integer *i0, integer *n0, real *z__, integer *pp, + real *tau, real *dmin__, real *dmin1, real *dmin2, real *dn, real * + dnm1, real *dnm2, logical *ieee); + +/* Subroutine */ int slasq6_(integer *i0, integer *n0, real *z__, integer *pp, + real *dmin__, real *dmin1, real *dmin2, real *dn, real *dnm1, real * + dnm2); + +/* Subroutine */ int slasr_(char *side, char *pivot, char *direct, integer *m, + integer *n, real *c__, real *s, real *a, integer *lda); + +/* Subroutine */ int slasrt_(char *id, integer *n, real *d__, integer *info); + +/* Subroutine */ int slassq_(integer *n, real *x, integer *incx, real *scale, + real *sumsq); + +/* Subroutine */ int slasv2_(real *f, real *g, real *h__, real *ssmin, real * + ssmax, real *snr, real *csr, real *snl, real *csl); + +/* Subroutine */ int slaswp_(integer *n, real *a, integer *lda, integer *k1, + integer *k2, integer *ipiv, integer *incx); + +/* Subroutine */ int slasy2_(logical *ltranl, logical *ltranr, integer *isgn, + integer *n1, integer *n2, real *tl, integer *ldtl, real *tr, integer * + ldtr, real *b, integer *ldb, real *scale, real *x, integer *ldx, real + *xnorm, integer *info); + +/* Subroutine */ int slasyf_(char *uplo, integer *n, integer *nb, integer *kb, + real *a, integer *lda, integer *ipiv, real *w, integer *ldw, integer + *info); + +/* Subroutine */ int slatbs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, integer *kd, real *ab, integer *ldab, real *x, + real *scale, real *cnorm, integer *info); + +/* Subroutine */ int slatdf_(integer *ijob, integer *n, real *z__, integer * + ldz, real *rhs, real *rdsum, real *rdscal, integer *ipiv, integer * + jpiv); + +/* Subroutine */ int slatps_(char *uplo, char *trans, char *diag, char * + normin, integer *n, real *ap, real *x, real *scale, real *cnorm, + integer *info); + +/* Subroutine */ int slatrd_(char *uplo, integer *n, integer *nb, real *a, + integer *lda, real *e, real *tau, real *w, integer *ldw); + +/* Subroutine */ int slatrs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, real *a, integer *lda, real *x, real *scale, real + *cnorm, integer *info); + +/* Subroutine */ int slatrz_(integer *m, integer *n, integer *l, real *a, + integer *lda, real *tau, real *work); + +/* Subroutine */ int slatzm_(char *side, integer *m, integer *n, real *v, + integer *incv, real *tau, real *c1, real *c2, integer *ldc, real * + work); + +/* Subroutine */ int slauu2_(char *uplo, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int slauum_(char *uplo, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int sopgtr_(char *uplo, integer *n, real *ap, real *tau, + real *q, integer *ldq, real *work, integer *info); + +/* Subroutine */ int sopmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, real *ap, real *tau, real *c__, integer *ldc, real *work, + integer *info); + +/* Subroutine */ int sorg2l_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *info); + +/* Subroutine */ int sorg2r_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *info); + +/* Subroutine */ int sorgbr_(char *vect, integer *m, integer *n, integer *k, + real *a, integer *lda, real *tau, real *work, integer *lwork, integer + *info); + +/* Subroutine */ int sorghr_(integer *n, integer *ilo, integer *ihi, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorgl2_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *info); + +/* Subroutine */ int sorglq_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorgql_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorgqr_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorgr2_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *info); + +/* Subroutine */ int sorgrq_(integer *m, integer *n, integer *k, real *a, + integer *lda, real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorgtr_(char *uplo, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorm2l_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *info); + +/* Subroutine */ int sorm2r_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *info); + +/* Subroutine */ int sormbr_(char *vect, char *side, char *trans, integer *m, + integer *n, integer *k, real *a, integer *lda, real *tau, real *c__, + integer *ldc, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormhr_(char *side, char *trans, integer *m, integer *n, + integer *ilo, integer *ihi, real *a, integer *lda, real *tau, real * + c__, integer *ldc, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sorml2_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *info); + +/* Subroutine */ int sormlq_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormql_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormqr_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormr2_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *info); + +/* Subroutine */ int sormr3_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, real *a, integer *lda, real *tau, real *c__, + integer *ldc, real *work, integer *info); + +/* Subroutine */ int sormrq_(char *side, char *trans, integer *m, integer *n, + integer *k, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormrz_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, real *a, integer *lda, real *tau, real *c__, + integer *ldc, real *work, integer *lwork, integer *info); + +/* Subroutine */ int sormtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, real *a, integer *lda, real *tau, real *c__, integer *ldc, + real *work, integer *lwork, integer *info); + +/* Subroutine */ int spbcon_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, real *anorm, real *rcond, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int spbequ_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, real *s, real *scond, real *amax, integer *info); + +/* Subroutine */ int spbrfs_(char *uplo, integer *n, integer *kd, integer * + nrhs, real *ab, integer *ldab, real *afb, integer *ldafb, real *b, + integer *ldb, real *x, integer *ldx, real *ferr, real *berr, real * + work, integer *iwork, integer *info); + +/* Subroutine */ int spbstf_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, integer *info); + +/* Subroutine */ int spbsv_(char *uplo, integer *n, integer *kd, integer * + nrhs, real *ab, integer *ldab, real *b, integer *ldb, integer *info); + +/* Subroutine */ int spbsvx_(char *fact, char *uplo, integer *n, integer *kd, + integer *nrhs, real *ab, integer *ldab, real *afb, integer *ldafb, + char *equed, real *s, real *b, integer *ldb, real *x, integer *ldx, + real *rcond, real *ferr, real *berr, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int spbtf2_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, integer *info); + +/* Subroutine */ int spbtrf_(char *uplo, integer *n, integer *kd, real *ab, + integer *ldab, integer *info); + +/* Subroutine */ int spbtrs_(char *uplo, integer *n, integer *kd, integer * + nrhs, real *ab, integer *ldab, real *b, integer *ldb, integer *info); + +/* Subroutine */ int spocon_(char *uplo, integer *n, real *a, integer *lda, + real *anorm, real *rcond, real *work, integer *iwork, integer *info); + +/* Subroutine */ int spoequ_(integer *n, real *a, integer *lda, real *s, real + *scond, real *amax, integer *info); + +/* Subroutine */ int sporfs_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, real *af, integer *ldaf, real *b, integer *ldb, real *x, + integer *ldx, real *ferr, real *berr, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int sposv_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sposvx_(char *fact, char *uplo, integer *n, integer * + nrhs, real *a, integer *lda, real *af, integer *ldaf, char *equed, + real *s, real *b, integer *ldb, real *x, integer *ldx, real *rcond, + real *ferr, real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int spotf2_(char *uplo, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int spotrf_(char *uplo, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int spotri_(char *uplo, integer *n, real *a, integer *lda, + integer *info); + +/* Subroutine */ int spotrs_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sppcon_(char *uplo, integer *n, real *ap, real *anorm, + real *rcond, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sppequ_(char *uplo, integer *n, real *ap, real *s, real * + scond, real *amax, integer *info); + +/* Subroutine */ int spprfs_(char *uplo, integer *n, integer *nrhs, real *ap, + real *afp, real *b, integer *ldb, real *x, integer *ldx, real *ferr, + real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sppsv_(char *uplo, integer *n, integer *nrhs, real *ap, + real *b, integer *ldb, integer *info); + +/* Subroutine */ int sppsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, real *ap, real *afp, char *equed, real *s, real *b, integer * + ldb, real *x, integer *ldx, real *rcond, real *ferr, real *berr, real + *work, integer *iwork, integer *info); + +/* Subroutine */ int spptrf_(char *uplo, integer *n, real *ap, integer *info); + +/* Subroutine */ int spptri_(char *uplo, integer *n, real *ap, integer *info); + +/* Subroutine */ int spptrs_(char *uplo, integer *n, integer *nrhs, real *ap, + real *b, integer *ldb, integer *info); + +/* Subroutine */ int sptcon_(integer *n, real *d__, real *e, real *anorm, + real *rcond, real *work, integer *info); + +/* Subroutine */ int spteqr_(char *compz, integer *n, real *d__, real *e, + real *z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int sptrfs_(integer *n, integer *nrhs, real *d__, real *e, + real *df, real *ef, real *b, integer *ldb, real *x, integer *ldx, + real *ferr, real *berr, real *work, integer *info); + +/* Subroutine */ int sptsv_(integer *n, integer *nrhs, real *d__, real *e, + real *b, integer *ldb, integer *info); + +/* Subroutine */ int sptsvx_(char *fact, integer *n, integer *nrhs, real *d__, + real *e, real *df, real *ef, real *b, integer *ldb, real *x, integer + *ldx, real *rcond, real *ferr, real *berr, real *work, integer *info); + +/* Subroutine */ int spttrf_(integer *n, real *d__, real *e, integer *info); + +/* Subroutine */ int spttrs_(integer *n, integer *nrhs, real *d__, real *e, + real *b, integer *ldb, integer *info); + +/* Subroutine */ int sptts2_(integer *n, integer *nrhs, real *d__, real *e, + real *b, integer *ldb); + +/* Subroutine */ int srscl_(integer *n, real *sa, real *sx, integer *incx); + +/* Subroutine */ int ssbev_(char *jobz, char *uplo, integer *n, integer *kd, + real *ab, integer *ldab, real *w, real *z__, integer *ldz, real *work, + integer *info); + +/* Subroutine */ int ssbevd_(char *jobz, char *uplo, integer *n, integer *kd, + real *ab, integer *ldab, real *w, real *z__, integer *ldz, real *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int ssbevx_(char *jobz, char *range, char *uplo, integer *n, + integer *kd, real *ab, integer *ldab, real *q, integer *ldq, real *vl, + real *vu, integer *il, integer *iu, real *abstol, integer *m, real * + w, real *z__, integer *ldz, real *work, integer *iwork, integer * + ifail, integer *info); + +/* Subroutine */ int ssbgst_(char *vect, char *uplo, integer *n, integer *ka, + integer *kb, real *ab, integer *ldab, real *bb, integer *ldbb, real * + x, integer *ldx, real *work, integer *info); + +/* Subroutine */ int ssbgv_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, real *ab, integer *ldab, real *bb, integer *ldbb, real * + w, real *z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int ssbgvd_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, real *ab, integer *ldab, real *bb, integer *ldbb, real * + w, real *z__, integer *ldz, real *work, integer *lwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int ssbgvx_(char *jobz, char *range, char *uplo, integer *n, + integer *ka, integer *kb, real *ab, integer *ldab, real *bb, integer * + ldbb, real *q, integer *ldq, real *vl, real *vu, integer *il, integer + *iu, real *abstol, integer *m, real *w, real *z__, integer *ldz, real + *work, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssbtrd_(char *vect, char *uplo, integer *n, integer *kd, + real *ab, integer *ldab, real *d__, real *e, real *q, integer *ldq, + real *work, integer *info); + +/* Subroutine */ int sspcon_(char *uplo, integer *n, real *ap, integer *ipiv, + real *anorm, real *rcond, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sspev_(char *jobz, char *uplo, integer *n, real *ap, + real *w, real *z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int sspevd_(char *jobz, char *uplo, integer *n, real *ap, + real *w, real *z__, integer *ldz, real *work, integer *lwork, integer + *iwork, integer *liwork, integer *info); + +/* Subroutine */ int sspevx_(char *jobz, char *range, char *uplo, integer *n, + real *ap, real *vl, real *vu, integer *il, integer *iu, real *abstol, + integer *m, real *w, real *z__, integer *ldz, real *work, integer * + iwork, integer *ifail, integer *info); + +/* Subroutine */ int sspgst_(integer *itype, char *uplo, integer *n, real *ap, + real *bp, integer *info); + +/* Subroutine */ int sspgv_(integer *itype, char *jobz, char *uplo, integer * + n, real *ap, real *bp, real *w, real *z__, integer *ldz, real *work, + integer *info); + +/* Subroutine */ int sspgvd_(integer *itype, char *jobz, char *uplo, integer * + n, real *ap, real *bp, real *w, real *z__, integer *ldz, real *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int sspgvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, real *ap, real *bp, real *vl, real *vu, integer *il, + integer *iu, real *abstol, integer *m, real *w, real *z__, integer * + ldz, real *work, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssprfs_(char *uplo, integer *n, integer *nrhs, real *ap, + real *afp, integer *ipiv, real *b, integer *ldb, real *x, integer * + ldx, real *ferr, real *berr, real *work, integer *iwork, integer * + info); + +/* Subroutine */ int sspsv_(char *uplo, integer *n, integer *nrhs, real *ap, + integer *ipiv, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sspsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, real *ap, real *afp, integer *ipiv, real *b, integer *ldb, real + *x, integer *ldx, real *rcond, real *ferr, real *berr, real *work, + integer *iwork, integer *info); + +/* Subroutine */ int ssptrd_(char *uplo, integer *n, real *ap, real *d__, + real *e, real *tau, integer *info); + +/* Subroutine */ int ssptrf_(char *uplo, integer *n, real *ap, integer *ipiv, + integer *info); + +/* Subroutine */ int ssptri_(char *uplo, integer *n, real *ap, integer *ipiv, + real *work, integer *info); + +/* Subroutine */ int ssptrs_(char *uplo, integer *n, integer *nrhs, real *ap, + integer *ipiv, real *b, integer *ldb, integer *info); + +/* Subroutine */ int sstebz_(char *range, char *order, integer *n, real *vl, + real *vu, integer *il, integer *iu, real *abstol, real *d__, real *e, + integer *m, integer *nsplit, real *w, integer *iblock, integer * + isplit, real *work, integer *iwork, integer *info); + +/* Subroutine */ int sstedc_(char *compz, integer *n, real *d__, real *e, + real *z__, integer *ldz, real *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int sstegr_(char *jobz, char *range, integer *n, real *d__, + real *e, real *vl, real *vu, integer *il, integer *iu, real *abstol, + integer *m, real *w, real *z__, integer *ldz, integer *isuppz, real * + work, integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int sstein_(integer *n, real *d__, real *e, integer *m, real + *w, integer *iblock, integer *isplit, real *z__, integer *ldz, real * + work, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssteqr_(char *compz, integer *n, real *d__, real *e, + real *z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int ssterf_(integer *n, real *d__, real *e, integer *info); + +/* Subroutine */ int sstev_(char *jobz, integer *n, real *d__, real *e, real * + z__, integer *ldz, real *work, integer *info); + +/* Subroutine */ int sstevd_(char *jobz, integer *n, real *d__, real *e, real + *z__, integer *ldz, real *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int sstevr_(char *jobz, char *range, integer *n, real *d__, + real *e, real *vl, real *vu, integer *il, integer *iu, real *abstol, + integer *m, real *w, real *z__, integer *ldz, integer *isuppz, real * + work, integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int sstevx_(char *jobz, char *range, integer *n, real *d__, + real *e, real *vl, real *vu, integer *il, integer *iu, real *abstol, + integer *m, real *w, real *z__, integer *ldz, real *work, integer * + iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssycon_(char *uplo, integer *n, real *a, integer *lda, + integer *ipiv, real *anorm, real *rcond, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int ssyev_(char *jobz, char *uplo, integer *n, real *a, + integer *lda, real *w, real *work, integer *lwork, integer *info); + +/* Subroutine */ int ssyevd_(char *jobz, char *uplo, integer *n, real *a, + integer *lda, real *w, real *work, integer *lwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int ssyevr_(char *jobz, char *range, char *uplo, integer *n, + real *a, integer *lda, real *vl, real *vu, integer *il, integer *iu, + real *abstol, integer *m, real *w, real *z__, integer *ldz, integer * + isuppz, real *work, integer *lwork, integer *iwork, integer *liwork, + integer *info); + +/* Subroutine */ int ssyevx_(char *jobz, char *range, char *uplo, integer *n, + real *a, integer *lda, real *vl, real *vu, integer *il, integer *iu, + real *abstol, integer *m, real *w, real *z__, integer *ldz, real * + work, integer *lwork, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssygs2_(integer *itype, char *uplo, integer *n, real *a, + integer *lda, real *b, integer *ldb, integer *info); + +/* Subroutine */ int ssygst_(integer *itype, char *uplo, integer *n, real *a, + integer *lda, real *b, integer *ldb, integer *info); + +/* Subroutine */ int ssygv_(integer *itype, char *jobz, char *uplo, integer * + n, real *a, integer *lda, real *b, integer *ldb, real *w, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int ssygvd_(integer *itype, char *jobz, char *uplo, integer * + n, real *a, integer *lda, real *b, integer *ldb, real *w, real *work, + integer *lwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int ssygvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, real *a, integer *lda, real *b, integer *ldb, real * + vl, real *vu, integer *il, integer *iu, real *abstol, integer *m, + real *w, real *z__, integer *ldz, real *work, integer *lwork, integer + *iwork, integer *ifail, integer *info); + +/* Subroutine */ int ssyrfs_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, real *af, integer *ldaf, integer *ipiv, real *b, + integer *ldb, real *x, integer *ldx, real *ferr, real *berr, real * + work, integer *iwork, integer *info); + +/* Subroutine */ int ssysv_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, integer *ipiv, real *b, integer *ldb, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int ssysvx_(char *fact, char *uplo, integer *n, integer * + nrhs, real *a, integer *lda, real *af, integer *ldaf, integer *ipiv, + real *b, integer *ldb, real *x, integer *ldx, real *rcond, real *ferr, + real *berr, real *work, integer *lwork, integer *iwork, integer * + info); + +/* Subroutine */ int ssytd2_(char *uplo, integer *n, real *a, integer *lda, + real *d__, real *e, real *tau, integer *info); + +/* Subroutine */ int ssytf2_(char *uplo, integer *n, real *a, integer *lda, + integer *ipiv, integer *info); + +/* Subroutine */ int ssytrd_(char *uplo, integer *n, real *a, integer *lda, + real *d__, real *e, real *tau, real *work, integer *lwork, integer * + info); + +/* Subroutine */ int ssytrf_(char *uplo, integer *n, real *a, integer *lda, + integer *ipiv, real *work, integer *lwork, integer *info); + +/* Subroutine */ int ssytri_(char *uplo, integer *n, real *a, integer *lda, + integer *ipiv, real *work, integer *info); + +/* Subroutine */ int ssytrs_(char *uplo, integer *n, integer *nrhs, real *a, + integer *lda, integer *ipiv, real *b, integer *ldb, integer *info); + +/* Subroutine */ int stbcon_(char *norm, char *uplo, char *diag, integer *n, + integer *kd, real *ab, integer *ldab, real *rcond, real *work, + integer *iwork, integer *info); + +/* Subroutine */ int stbrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, real *ab, integer *ldab, real *b, integer + *ldb, real *x, integer *ldx, real *ferr, real *berr, real *work, + integer *iwork, integer *info); + +/* Subroutine */ int stbtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, real *ab, integer *ldab, real *b, integer + *ldb, integer *info); + +/* Subroutine */ int stgevc_(char *side, char *howmny, logical *select, + integer *n, real *a, integer *lda, real *b, integer *ldb, real *vl, + integer *ldvl, real *vr, integer *ldvr, integer *mm, integer *m, real + *work, integer *info); + +/* Subroutine */ int stgex2_(logical *wantq, logical *wantz, integer *n, real + *a, integer *lda, real *b, integer *ldb, real *q, integer *ldq, real * + z__, integer *ldz, integer *j1, integer *n1, integer *n2, real *work, + integer *lwork, integer *info); + +/* Subroutine */ int stgexc_(logical *wantq, logical *wantz, integer *n, real + *a, integer *lda, real *b, integer *ldb, real *q, integer *ldq, real * + z__, integer *ldz, integer *ifst, integer *ilst, real *work, integer * + lwork, integer *info); + +/* Subroutine */ int stgsen_(integer *ijob, logical *wantq, logical *wantz, + logical *select, integer *n, real *a, integer *lda, real *b, integer * + ldb, real *alphar, real *alphai, real *beta, real *q, integer *ldq, + real *z__, integer *ldz, integer *m, real *pl, real *pr, real *dif, + real *work, integer *lwork, integer *iwork, integer *liwork, integer * + info); + +/* Subroutine */ int stgsja_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, integer *k, integer *l, real *a, integer *lda, + real *b, integer *ldb, real *tola, real *tolb, real *alpha, real * + beta, real *u, integer *ldu, real *v, integer *ldv, real *q, integer * + ldq, real *work, integer *ncycle, integer *info); + +/* Subroutine */ int stgsna_(char *job, char *howmny, logical *select, + integer *n, real *a, integer *lda, real *b, integer *ldb, real *vl, + integer *ldvl, real *vr, integer *ldvr, real *s, real *dif, integer * + mm, integer *m, real *work, integer *lwork, integer *iwork, integer * + info); + +/* Subroutine */ int stgsy2_(char *trans, integer *ijob, integer *m, integer * + n, real *a, integer *lda, real *b, integer *ldb, real *c__, integer * + ldc, real *d__, integer *ldd, real *e, integer *lde, real *f, integer + *ldf, real *scale, real *rdsum, real *rdscal, integer *iwork, integer + *pq, integer *info); + +/* Subroutine */ int stgsyl_(char *trans, integer *ijob, integer *m, integer * + n, real *a, integer *lda, real *b, integer *ldb, real *c__, integer * + ldc, real *d__, integer *ldd, real *e, integer *lde, real *f, integer + *ldf, real *scale, real *dif, real *work, integer *lwork, integer * + iwork, integer *info); + +/* Subroutine */ int stpcon_(char *norm, char *uplo, char *diag, integer *n, + real *ap, real *rcond, real *work, integer *iwork, integer *info); + +/* Subroutine */ int stprfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, real *ap, real *b, integer *ldb, real *x, integer *ldx, + real *ferr, real *berr, real *work, integer *iwork, integer *info); + +/* Subroutine */ int stptri_(char *uplo, char *diag, integer *n, real *ap, + integer *info); + +/* Subroutine */ int stptrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, real *ap, real *b, integer *ldb, integer *info); + +/* Subroutine */ int strcon_(char *norm, char *uplo, char *diag, integer *n, + real *a, integer *lda, real *rcond, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int strevc_(char *side, char *howmny, logical *select, + integer *n, real *t, integer *ldt, real *vl, integer *ldvl, real *vr, + integer *ldvr, integer *mm, integer *m, real *work, integer *info); + +/* Subroutine */ int strexc_(char *compq, integer *n, real *t, integer *ldt, + real *q, integer *ldq, integer *ifst, integer *ilst, real *work, + integer *info); + +/* Subroutine */ int strrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, real *a, integer *lda, real *b, integer *ldb, real *x, + integer *ldx, real *ferr, real *berr, real *work, integer *iwork, + integer *info); + +/* Subroutine */ int strsen_(char *job, char *compq, logical *select, integer + *n, real *t, integer *ldt, real *q, integer *ldq, real *wr, real *wi, + integer *m, real *s, real *sep, real *work, integer *lwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int strsna_(char *job, char *howmny, logical *select, + integer *n, real *t, integer *ldt, real *vl, integer *ldvl, real *vr, + integer *ldvr, real *s, real *sep, integer *mm, integer *m, real * + work, integer *ldwork, integer *iwork, integer *info); + +/* Subroutine */ int strsyl_(char *trana, char *tranb, integer *isgn, integer + *m, integer *n, real *a, integer *lda, real *b, integer *ldb, real * + c__, integer *ldc, real *scale, integer *info); + +/* Subroutine */ int strti2_(char *uplo, char *diag, integer *n, real *a, + integer *lda, integer *info); + +/* Subroutine */ int strtri_(char *uplo, char *diag, integer *n, real *a, + integer *lda, integer *info); + +/* Subroutine */ int strtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, real *a, integer *lda, real *b, integer *ldb, integer * + info); + +/* Subroutine */ int stzrqf_(integer *m, integer *n, real *a, integer *lda, + real *tau, integer *info); + +/* Subroutine */ int stzrzf_(integer *m, integer *n, real *a, integer *lda, + real *tau, real *work, integer *lwork, integer *info); + +/* Subroutine */ int xerbla_(char *srname, integer *info); + +/* Subroutine */ int zbdsqr_(char *uplo, integer *n, integer *ncvt, integer * + nru, integer *ncc, doublereal *d__, doublereal *e, doublecomplex *vt, + integer *ldvt, doublecomplex *u, integer *ldu, doublecomplex *c__, + integer *ldc, doublereal *rwork, integer *info); + +/* Subroutine */ int zdrot_(integer *n, doublecomplex *cx, integer *incx, + doublecomplex *cy, integer *incy, doublereal *c__, doublereal *s); + +/* Subroutine */ int zdrscl_(integer *n, doublereal *sa, doublecomplex *sx, + integer *incx); + +/* Subroutine */ int zgbbrd_(char *vect, integer *m, integer *n, integer *ncc, + integer *kl, integer *ku, doublecomplex *ab, integer *ldab, + doublereal *d__, doublereal *e, doublecomplex *q, integer *ldq, + doublecomplex *pt, integer *ldpt, doublecomplex *c__, integer *ldc, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zgbcon_(char *norm, integer *n, integer *kl, integer *ku, + doublecomplex *ab, integer *ldab, integer *ipiv, doublereal *anorm, + doublereal *rcond, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zgbequ_(integer *m, integer *n, integer *kl, integer *ku, + doublecomplex *ab, integer *ldab, doublereal *r__, doublereal *c__, + doublereal *rowcnd, doublereal *colcnd, doublereal *amax, integer * + info); + +/* Subroutine */ int zgbrfs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, doublecomplex *ab, integer *ldab, doublecomplex * + afb, integer *ldafb, integer *ipiv, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zgbsv_(integer *n, integer *kl, integer *ku, integer * + nrhs, doublecomplex *ab, integer *ldab, integer *ipiv, doublecomplex * + b, integer *ldb, integer *info); + +/* Subroutine */ int zgbsvx_(char *fact, char *trans, integer *n, integer *kl, + integer *ku, integer *nrhs, doublecomplex *ab, integer *ldab, + doublecomplex *afb, integer *ldafb, integer *ipiv, char *equed, + doublereal *r__, doublereal *c__, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *rcond, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zgbtf2_(integer *m, integer *n, integer *kl, integer *ku, + doublecomplex *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int zgbtrf_(integer *m, integer *n, integer *kl, integer *ku, + doublecomplex *ab, integer *ldab, integer *ipiv, integer *info); + +/* Subroutine */ int zgbtrs_(char *trans, integer *n, integer *kl, integer * + ku, integer *nrhs, doublecomplex *ab, integer *ldab, integer *ipiv, + doublecomplex *b, integer *ldb, integer *info); + +/* Subroutine */ int zgebak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, doublereal *scale, integer *m, doublecomplex *v, + integer *ldv, integer *info); + +/* Subroutine */ int zgebal_(char *job, integer *n, doublecomplex *a, integer + *lda, integer *ilo, integer *ihi, doublereal *scale, integer *info); + +/* Subroutine */ int zgebd2_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublereal *d__, doublereal *e, doublecomplex *tauq, + doublecomplex *taup, doublecomplex *work, integer *info); + +/* Subroutine */ int zgebrd_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublereal *d__, doublereal *e, doublecomplex *tauq, + doublecomplex *taup, doublecomplex *work, integer *lwork, integer * + info); + +/* Subroutine */ int zgecon_(char *norm, integer *n, doublecomplex *a, + integer *lda, doublereal *anorm, doublereal *rcond, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zgeequ_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublereal *r__, doublereal *c__, doublereal *rowcnd, + doublereal *colcnd, doublereal *amax, integer *info); + +/* Subroutine */ int zgees_(char *jobvs, char *sort, L_fp select, integer *n, + doublecomplex *a, integer *lda, integer *sdim, doublecomplex *w, + doublecomplex *vs, integer *ldvs, doublecomplex *work, integer *lwork, + doublereal *rwork, logical *bwork, integer *info); + +/* Subroutine */ int zgeesx_(char *jobvs, char *sort, L_fp select, char * + sense, integer *n, doublecomplex *a, integer *lda, integer *sdim, + doublecomplex *w, doublecomplex *vs, integer *ldvs, doublereal * + rconde, doublereal *rcondv, doublecomplex *work, integer *lwork, + doublereal *rwork, logical *bwork, integer *info); + +/* Subroutine */ int zgeev_(char *jobvl, char *jobvr, integer *n, + doublecomplex *a, integer *lda, doublecomplex *w, doublecomplex *vl, + integer *ldvl, doublecomplex *vr, integer *ldvr, doublecomplex *work, + integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgeevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, doublecomplex *a, integer *lda, doublecomplex *w, + doublecomplex *vl, integer *ldvl, doublecomplex *vr, integer *ldvr, + integer *ilo, integer *ihi, doublereal *scale, doublereal *abnrm, + doublereal *rconde, doublereal *rcondv, doublecomplex *work, integer * + lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgegs_(char *jobvsl, char *jobvsr, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *alpha, doublecomplex *beta, doublecomplex *vsl, + integer *ldvsl, doublecomplex *vsr, integer *ldvsr, doublecomplex * + work, integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgegv_(char *jobvl, char *jobvr, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *alpha, doublecomplex *beta, doublecomplex *vl, integer + *ldvl, doublecomplex *vr, integer *ldvr, doublecomplex *work, integer + *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgehd2_(integer *n, integer *ilo, integer *ihi, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *info); + +/* Subroutine */ int zgehrd_(integer *n, integer *ilo, integer *ihi, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zgelq2_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *info); + +/* Subroutine */ int zgelqf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zgels_(char *trans, integer *m, integer *n, integer * + nrhs, doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zgelsx_(integer *m, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *jpvt, doublereal *rcond, integer *rank, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zgelsy_(integer *m, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *jpvt, doublereal *rcond, integer *rank, doublecomplex *work, + integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgeql2_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *info); + +/* Subroutine */ int zgeqlf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zgeqp3_(integer *m, integer *n, doublecomplex *a, + integer *lda, integer *jpvt, doublecomplex *tau, doublecomplex *work, + integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zgeqpf_(integer *m, integer *n, doublecomplex *a, + integer *lda, integer *jpvt, doublecomplex *tau, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zgeqr2_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *info); + +/* Subroutine */ int zgeqrf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zgerfs_(char *trans, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *af, integer *ldaf, + integer *ipiv, doublecomplex *b, integer *ldb, doublecomplex *x, + integer *ldx, doublereal *ferr, doublereal *berr, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zgerq2_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *info); + +/* Subroutine */ int zgerqf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zgesc2_(integer *n, doublecomplex *a, integer *lda, + doublecomplex *rhs, integer *ipiv, integer *jpiv, doublereal *scale); + +/* Subroutine */ int zgesv_(integer *n, integer *nrhs, doublecomplex *a, + integer *lda, integer *ipiv, doublecomplex *b, integer *ldb, integer * + info); + +/* Subroutine */ int zgesvx_(char *fact, char *trans, integer *n, integer * + nrhs, doublecomplex *a, integer *lda, doublecomplex *af, integer * + ldaf, integer *ipiv, char *equed, doublereal *r__, doublereal *c__, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *rcond, doublereal *ferr, doublereal *berr, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zgetc2_(integer *n, doublecomplex *a, integer *lda, + integer *ipiv, integer *jpiv, integer *info); + +/* Subroutine */ int zgetf2_(integer *m, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, integer *info); + +/* Subroutine */ int zgetrf_(integer *m, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, integer *info); + +/* Subroutine */ int zgetri_(integer *n, doublecomplex *a, integer *lda, + integer *ipiv, doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zgetrs_(char *trans, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *b, + integer *ldb, integer *info); + +/* Subroutine */ int zggbak_(char *job, char *side, integer *n, integer *ilo, + integer *ihi, doublereal *lscale, doublereal *rscale, integer *m, + doublecomplex *v, integer *ldv, integer *info); + +/* Subroutine */ int zggbal_(char *job, integer *n, doublecomplex *a, integer + *lda, doublecomplex *b, integer *ldb, integer *ilo, integer *ihi, + doublereal *lscale, doublereal *rscale, doublereal *work, integer * + info); + +/* Subroutine */ int zgges_(char *jobvsl, char *jobvsr, char *sort, L_fp + delctg, integer *n, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, integer *sdim, doublecomplex *alpha, doublecomplex * + beta, doublecomplex *vsl, integer *ldvsl, doublecomplex *vsr, integer + *ldvsr, doublecomplex *work, integer *lwork, doublereal *rwork, + logical *bwork, integer *info); + +/* Subroutine */ int zggesx_(char *jobvsl, char *jobvsr, char *sort, L_fp + delctg, char *sense, integer *n, doublecomplex *a, integer *lda, + doublecomplex *b, integer *ldb, integer *sdim, doublecomplex *alpha, + doublecomplex *beta, doublecomplex *vsl, integer *ldvsl, + doublecomplex *vsr, integer *ldvsr, doublereal *rconde, doublereal * + rcondv, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *iwork, integer *liwork, logical *bwork, integer *info); + +/* Subroutine */ int zggev_(char *jobvl, char *jobvr, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *alpha, doublecomplex *beta, doublecomplex *vl, integer + *ldvl, doublecomplex *vr, integer *ldvr, doublecomplex *work, integer + *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zggevx_(char *balanc, char *jobvl, char *jobvr, char * + sense, integer *n, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, doublecomplex *alpha, doublecomplex *beta, + doublecomplex *vl, integer *ldvl, doublecomplex *vr, integer *ldvr, + integer *ilo, integer *ihi, doublereal *lscale, doublereal *rscale, + doublereal *abnrm, doublereal *bbnrm, doublereal *rconde, doublereal * + rcondv, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *iwork, logical *bwork, integer *info); + +/* Subroutine */ int zggglm_(integer *n, integer *m, integer *p, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *d__, doublecomplex *x, doublecomplex *y, doublecomplex + *work, integer *lwork, integer *info); + +/* Subroutine */ int zgghrd_(char *compq, char *compz, integer *n, integer * + ilo, integer *ihi, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, doublecomplex *q, integer *ldq, doublecomplex *z__, + integer *ldz, integer *info); + +/* Subroutine */ int zgglse_(integer *m, integer *n, integer *p, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *c__, doublecomplex *d__, doublecomplex *x, + doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zggqrf_(integer *n, integer *m, integer *p, + doublecomplex *a, integer *lda, doublecomplex *taua, doublecomplex *b, + integer *ldb, doublecomplex *taub, doublecomplex *work, integer * + lwork, integer *info); + +/* Subroutine */ int zggrqf_(integer *m, integer *p, integer *n, + doublecomplex *a, integer *lda, doublecomplex *taua, doublecomplex *b, + integer *ldb, doublecomplex *taub, doublecomplex *work, integer * + lwork, integer *info); + +/* Subroutine */ int zggsvd_(char *jobu, char *jobv, char *jobq, integer *m, + integer *n, integer *p, integer *k, integer *l, doublecomplex *a, + integer *lda, doublecomplex *b, integer *ldb, doublereal *alpha, + doublereal *beta, doublecomplex *u, integer *ldu, doublecomplex *v, + integer *ldv, doublecomplex *q, integer *ldq, doublecomplex *work, + doublereal *rwork, integer *iwork, integer *info); + +/* Subroutine */ int zggsvp_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, doublecomplex *a, integer *lda, doublecomplex + *b, integer *ldb, doublereal *tola, doublereal *tolb, integer *k, + integer *l, doublecomplex *u, integer *ldu, doublecomplex *v, integer + *ldv, doublecomplex *q, integer *ldq, integer *iwork, doublereal * + rwork, doublecomplex *tau, doublecomplex *work, integer *info); + +/* Subroutine */ int zgtcon_(char *norm, integer *n, doublecomplex *dl, + doublecomplex *d__, doublecomplex *du, doublecomplex *du2, integer * + ipiv, doublereal *anorm, doublereal *rcond, doublecomplex *work, + integer *info); + +/* Subroutine */ int zgtrfs_(char *trans, integer *n, integer *nrhs, + doublecomplex *dl, doublecomplex *d__, doublecomplex *du, + doublecomplex *dlf, doublecomplex *df, doublecomplex *duf, + doublecomplex *du2, integer *ipiv, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zgtsv_(integer *n, integer *nrhs, doublecomplex *dl, + doublecomplex *d__, doublecomplex *du, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zgtsvx_(char *fact, char *trans, integer *n, integer * + nrhs, doublecomplex *dl, doublecomplex *d__, doublecomplex *du, + doublecomplex *dlf, doublecomplex *df, doublecomplex *duf, + doublecomplex *du2, integer *ipiv, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *rcond, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zgttrf_(integer *n, doublecomplex *dl, doublecomplex * + d__, doublecomplex *du, doublecomplex *du2, integer *ipiv, integer * + info); + +/* Subroutine */ int zgttrs_(char *trans, integer *n, integer *nrhs, + doublecomplex *dl, doublecomplex *d__, doublecomplex *du, + doublecomplex *du2, integer *ipiv, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zgtts2_(integer *itrans, integer *n, integer *nrhs, + doublecomplex *dl, doublecomplex *d__, doublecomplex *du, + doublecomplex *du2, integer *ipiv, doublecomplex *b, integer *ldb); + +/* Subroutine */ int zhbev_(char *jobz, char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *w, doublecomplex *z__, + integer *ldz, doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zhbevd_(char *jobz, char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *w, doublecomplex *z__, + integer *ldz, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *lrwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int zhbevx_(char *jobz, char *range, char *uplo, integer *n, + integer *kd, doublecomplex *ab, integer *ldab, doublecomplex *q, + integer *ldq, doublereal *vl, doublereal *vu, integer *il, integer * + iu, doublereal *abstol, integer *m, doublereal *w, doublecomplex *z__, + integer *ldz, doublecomplex *work, doublereal *rwork, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int zhbgst_(char *vect, char *uplo, integer *n, integer *ka, + integer *kb, doublecomplex *ab, integer *ldab, doublecomplex *bb, + integer *ldbb, doublecomplex *x, integer *ldx, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zhbgv_(char *jobz, char *uplo, integer *n, integer *ka, + integer *kb, doublecomplex *ab, integer *ldab, doublecomplex *bb, + integer *ldbb, doublereal *w, doublecomplex *z__, integer *ldz, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zhbgvx_(char *jobz, char *range, char *uplo, integer *n, + integer *ka, integer *kb, doublecomplex *ab, integer *ldab, + doublecomplex *bb, integer *ldbb, doublecomplex *q, integer *ldq, + doublereal *vl, doublereal *vu, integer *il, integer *iu, doublereal * + abstol, integer *m, doublereal *w, doublecomplex *z__, integer *ldz, + doublecomplex *work, doublereal *rwork, integer *iwork, integer * + ifail, integer *info); + +/* Subroutine */ int zhbtrd_(char *vect, char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *d__, doublereal *e, + doublecomplex *q, integer *ldq, doublecomplex *work, integer *info); + +/* Subroutine */ int zhecon_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublereal *anorm, doublereal *rcond, + doublecomplex *work, integer *info); + +/* Subroutine */ int zheev_(char *jobz, char *uplo, integer *n, doublecomplex + *a, integer *lda, doublereal *w, doublecomplex *work, integer *lwork, + doublereal *rwork, integer *info); + +/* Subroutine */ int zheevd_(char *jobz, char *uplo, integer *n, + doublecomplex *a, integer *lda, doublereal *w, doublecomplex *work, + integer *lwork, doublereal *rwork, integer *lrwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int zheevr_(char *jobz, char *range, char *uplo, integer *n, + doublecomplex *a, integer *lda, doublereal *vl, doublereal *vu, + integer *il, integer *iu, doublereal *abstol, integer *m, doublereal * + w, doublecomplex *z__, integer *ldz, integer *isuppz, doublecomplex * + work, integer *lwork, doublereal *rwork, integer *lrwork, integer * + iwork, integer *liwork, integer *info); + +/* Subroutine */ int zheevx_(char *jobz, char *range, char *uplo, integer *n, + doublecomplex *a, integer *lda, doublereal *vl, doublereal *vu, + integer *il, integer *iu, doublereal *abstol, integer *m, doublereal * + w, doublecomplex *z__, integer *ldz, doublecomplex *work, integer * + lwork, doublereal *rwork, integer *iwork, integer *ifail, integer * + info); + +/* Subroutine */ int zhegs2_(integer *itype, char *uplo, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zhegst_(integer *itype, char *uplo, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zhegv_(integer *itype, char *jobz, char *uplo, integer * + n, doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublereal *w, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *info); + +/* Subroutine */ int zhegvd_(integer *itype, char *jobz, char *uplo, integer * + n, doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublereal *w, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *lrwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int zhegvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, doublereal *vl, doublereal *vu, integer *il, integer * + iu, doublereal *abstol, integer *m, doublereal *w, doublecomplex *z__, + integer *ldz, doublecomplex *work, integer *lwork, doublereal *rwork, + integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int zherfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *af, integer *ldaf, + integer *ipiv, doublecomplex *b, integer *ldb, doublecomplex *x, + integer *ldx, doublereal *ferr, doublereal *berr, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zhesv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *b, + integer *ldb, doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zhesvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *a, integer *lda, doublecomplex *af, integer * + ldaf, integer *ipiv, doublecomplex *b, integer *ldb, doublecomplex *x, + integer *ldx, doublereal *rcond, doublereal *ferr, doublereal *berr, + doublecomplex *work, integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zhetf2_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, integer *info); + +/* Subroutine */ int zhetrd_(char *uplo, integer *n, doublecomplex *a, + integer *lda, doublereal *d__, doublereal *e, doublecomplex *tau, + doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zhetrf_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zhetri_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublecomplex *work, integer *info); + +/* Subroutine */ int zhetrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *b, + integer *ldb, integer *info); + +/* Subroutine */ int zhgeqz_(char *job, char *compq, char *compz, integer *n, + integer *ilo, integer *ihi, doublecomplex *a, integer *lda, + doublecomplex *b, integer *ldb, doublecomplex *alpha, doublecomplex * + beta, doublecomplex *q, integer *ldq, doublecomplex *z__, integer * + ldz, doublecomplex *work, integer *lwork, doublereal *rwork, integer * + info); + +/* Subroutine */ int zhpcon_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, doublereal *anorm, doublereal *rcond, doublecomplex * + work, integer *info); + +/* Subroutine */ int zhpev_(char *jobz, char *uplo, integer *n, doublecomplex + *ap, doublereal *w, doublecomplex *z__, integer *ldz, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zhpevd_(char *jobz, char *uplo, integer *n, + doublecomplex *ap, doublereal *w, doublecomplex *z__, integer *ldz, + doublecomplex *work, integer *lwork, doublereal *rwork, integer * + lrwork, integer *iwork, integer *liwork, integer *info); + +/* Subroutine */ int zhpevx_(char *jobz, char *range, char *uplo, integer *n, + doublecomplex *ap, doublereal *vl, doublereal *vu, integer *il, + integer *iu, doublereal *abstol, integer *m, doublereal *w, + doublecomplex *z__, integer *ldz, doublecomplex *work, doublereal * + rwork, integer *iwork, integer *ifail, integer *info); + +/* Subroutine */ int zhpgst_(integer *itype, char *uplo, integer *n, + doublecomplex *ap, doublecomplex *bp, integer *info); + +/* Subroutine */ int zhpgv_(integer *itype, char *jobz, char *uplo, integer * + n, doublecomplex *ap, doublecomplex *bp, doublereal *w, doublecomplex + *z__, integer *ldz, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zhpgvd_(integer *itype, char *jobz, char *uplo, integer * + n, doublecomplex *ap, doublecomplex *bp, doublereal *w, doublecomplex + *z__, integer *ldz, doublecomplex *work, integer *lwork, doublereal * + rwork, integer *lrwork, integer *iwork, integer *liwork, integer * + info); + +/* Subroutine */ int zhpgvx_(integer *itype, char *jobz, char *range, char * + uplo, integer *n, doublecomplex *ap, doublecomplex *bp, doublereal * + vl, doublereal *vu, integer *il, integer *iu, doublereal *abstol, + integer *m, doublereal *w, doublecomplex *z__, integer *ldz, + doublecomplex *work, doublereal *rwork, integer *iwork, integer * + ifail, integer *info); + +/* Subroutine */ int zhprfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, doublecomplex *afp, integer *ipiv, doublecomplex * + b, integer *ldb, doublecomplex *x, integer *ldx, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zhpsv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, integer *ipiv, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zhpsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *ap, doublecomplex *afp, integer *ipiv, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *rcond, doublereal *ferr, doublereal *berr, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zhptrd_(char *uplo, integer *n, doublecomplex *ap, + doublereal *d__, doublereal *e, doublecomplex *tau, integer *info); + +/* Subroutine */ int zhptrf_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, integer *info); + +/* Subroutine */ int zhptri_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, doublecomplex *work, integer *info); + +/* Subroutine */ int zhptrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, integer *ipiv, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zhsein_(char *side, char *eigsrc, char *initv, logical * + select, integer *n, doublecomplex *h__, integer *ldh, doublecomplex * + w, doublecomplex *vl, integer *ldvl, doublecomplex *vr, integer *ldvr, + integer *mm, integer *m, doublecomplex *work, doublereal *rwork, + integer *ifaill, integer *ifailr, integer *info); + +/* Subroutine */ int zhseqr_(char *job, char *compz, integer *n, integer *ilo, + integer *ihi, doublecomplex *h__, integer *ldh, doublecomplex *w, + doublecomplex *z__, integer *ldz, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zlabrd_(integer *m, integer *n, integer *nb, + doublecomplex *a, integer *lda, doublereal *d__, doublereal *e, + doublecomplex *tauq, doublecomplex *taup, doublecomplex *x, integer * + ldx, doublecomplex *y, integer *ldy); + +/* Subroutine */ int zlacgv_(integer *n, doublecomplex *x, integer *incx); + +/* Subroutine */ int zlacon_(integer *n, doublecomplex *v, doublecomplex *x, + doublereal *est, integer *kase); + +/* Subroutine */ int zlacp2_(char *uplo, integer *m, integer *n, doublereal * + a, integer *lda, doublecomplex *b, integer *ldb); + +/* Subroutine */ int zlacpy_(char *uplo, integer *m, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb); + +/* Subroutine */ int zlacrm_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublereal *b, integer *ldb, doublecomplex *c__, + integer *ldc, doublereal *rwork); + +/* Subroutine */ int zlacrt_(integer *n, doublecomplex *cx, integer *incx, + doublecomplex *cy, integer *incy, doublecomplex *c__, doublecomplex * + s); + +/* Subroutine */ int zlaed0_(integer *qsiz, integer *n, doublereal *d__, + doublereal *e, doublecomplex *q, integer *ldq, doublecomplex *qstore, + integer *ldqs, doublereal *rwork, integer *iwork, integer *info); + +/* Subroutine */ int zlaed7_(integer *n, integer *cutpnt, integer *qsiz, + integer *tlvls, integer *curlvl, integer *curpbm, doublereal *d__, + doublecomplex *q, integer *ldq, doublereal *rho, integer *indxq, + doublereal *qstore, integer *qptr, integer *prmptr, integer *perm, + integer *givptr, integer *givcol, doublereal *givnum, doublecomplex * + work, doublereal *rwork, integer *iwork, integer *info); + +/* Subroutine */ int zlaed8_(integer *k, integer *n, integer *qsiz, + doublecomplex *q, integer *ldq, doublereal *d__, doublereal *rho, + integer *cutpnt, doublereal *z__, doublereal *dlamda, doublecomplex * + q2, integer *ldq2, doublereal *w, integer *indxp, integer *indx, + integer *indxq, integer *perm, integer *givptr, integer *givcol, + doublereal *givnum, integer *info); + +/* Subroutine */ int zlaein_(logical *rightv, logical *noinit, integer *n, + doublecomplex *h__, integer *ldh, doublecomplex *w, doublecomplex *v, + doublecomplex *b, integer *ldb, doublereal *rwork, doublereal *eps3, + doublereal *smlnum, integer *info); + +/* Subroutine */ int zlaesy_(doublecomplex *a, doublecomplex *b, + doublecomplex *c__, doublecomplex *rt1, doublecomplex *rt2, + doublecomplex *evscal, doublecomplex *cs1, doublecomplex *sn1); + +/* Subroutine */ int zlaev2_(doublecomplex *a, doublecomplex *b, + doublecomplex *c__, doublereal *rt1, doublereal *rt2, doublereal *cs1, + doublecomplex *sn1); + +/* Subroutine */ int zlags2_(logical *upper, doublereal *a1, doublecomplex * + a2, doublereal *a3, doublereal *b1, doublecomplex *b2, doublereal *b3, + doublereal *csu, doublecomplex *snu, doublereal *csv, doublecomplex * + snv, doublereal *csq, doublecomplex *snq); + +/* Subroutine */ int zlagtm_(char *trans, integer *n, integer *nrhs, + doublereal *alpha, doublecomplex *dl, doublecomplex *d__, + doublecomplex *du, doublecomplex *x, integer *ldx, doublereal *beta, + doublecomplex *b, integer *ldb); + +/* Subroutine */ int zlahef_(char *uplo, integer *n, integer *nb, integer *kb, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *w, + integer *ldw, integer *info); + +/* Subroutine */ int zlahqr_(logical *wantt, logical *wantz, integer *n, + integer *ilo, integer *ihi, doublecomplex *h__, integer *ldh, + doublecomplex *w, integer *iloz, integer *ihiz, doublecomplex *z__, + integer *ldz, integer *info); + +/* Subroutine */ int zlahrd_(integer *n, integer *k, integer *nb, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex *t, + integer *ldt, doublecomplex *y, integer *ldy); + +/* Subroutine */ int zlaic1_(integer *job, integer *j, doublecomplex *x, + doublereal *sest, doublecomplex *w, doublecomplex *gamma, doublereal * + sestpr, doublecomplex *s, doublecomplex *c__); + +/* Subroutine */ int zlals0_(integer *icompq, integer *nl, integer *nr, + integer *sqre, integer *nrhs, doublecomplex *b, integer *ldb, + doublecomplex *bx, integer *ldbx, integer *perm, integer *givptr, + integer *givcol, integer *ldgcol, doublereal *givnum, integer *ldgnum, + doublereal *poles, doublereal *difl, doublereal *difr, doublereal * + z__, integer *k, doublereal *c__, doublereal *s, doublereal *rwork, + integer *info); + +/* Subroutine */ int zlalsa_(integer *icompq, integer *smlsiz, integer *n, + integer *nrhs, doublecomplex *b, integer *ldb, doublecomplex *bx, + integer *ldbx, doublereal *u, integer *ldu, doublereal *vt, integer * + k, doublereal *difl, doublereal *difr, doublereal *z__, doublereal * + poles, integer *givptr, integer *givcol, integer *ldgcol, integer * + perm, doublereal *givnum, doublereal *c__, doublereal *s, doublereal * + rwork, integer *iwork, integer *info); + +/* Subroutine */ int zlapll_(integer *n, doublecomplex *x, integer *incx, + doublecomplex *y, integer *incy, doublereal *ssmin); + +/* Subroutine */ int zlapmt_(logical *forwrd, integer *m, integer *n, + doublecomplex *x, integer *ldx, integer *k); + +/* Subroutine */ int zlaqgb_(integer *m, integer *n, integer *kl, integer *ku, + doublecomplex *ab, integer *ldab, doublereal *r__, doublereal *c__, + doublereal *rowcnd, doublereal *colcnd, doublereal *amax, char *equed); + +/* Subroutine */ int zlaqge_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublereal *r__, doublereal *c__, doublereal *rowcnd, + doublereal *colcnd, doublereal *amax, char *equed); + +/* Subroutine */ int zlaqhb_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *s, doublereal *scond, + doublereal *amax, char *equed); + +/* Subroutine */ int zlaqhe_(char *uplo, integer *n, doublecomplex *a, + integer *lda, doublereal *s, doublereal *scond, doublereal *amax, + char *equed); + +/* Subroutine */ int zlaqhp_(char *uplo, integer *n, doublecomplex *ap, + doublereal *s, doublereal *scond, doublereal *amax, char *equed); + +/* Subroutine */ int zlaqp2_(integer *m, integer *n, integer *offset, + doublecomplex *a, integer *lda, integer *jpvt, doublecomplex *tau, + doublereal *vn1, doublereal *vn2, doublecomplex *work); + +/* Subroutine */ int zlaqps_(integer *m, integer *n, integer *offset, integer + *nb, integer *kb, doublecomplex *a, integer *lda, integer *jpvt, + doublecomplex *tau, doublereal *vn1, doublereal *vn2, doublecomplex * + auxv, doublecomplex *f, integer *ldf); + +/* Subroutine */ int zlaqsb_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *s, doublereal *scond, + doublereal *amax, char *equed); + +/* Subroutine */ int zlaqsp_(char *uplo, integer *n, doublecomplex *ap, + doublereal *s, doublereal *scond, doublereal *amax, char *equed); + +/* Subroutine */ int zlaqsy_(char *uplo, integer *n, doublecomplex *a, + integer *lda, doublereal *s, doublereal *scond, doublereal *amax, + char *equed); + +/* Subroutine */ int zlar1v_(integer *n, integer *b1, integer *bn, doublereal + *sigma, doublereal *d__, doublereal *l, doublereal *ld, doublereal * + lld, doublereal *gersch, doublecomplex *z__, doublereal *ztz, + doublereal *mingma, integer *r__, integer *isuppz, doublereal *work); + +/* Subroutine */ int zlar2v_(integer *n, doublecomplex *x, doublecomplex *y, + doublecomplex *z__, integer *incx, doublereal *c__, doublecomplex *s, + integer *incc); + +/* Subroutine */ int zlarcm_(integer *m, integer *n, doublereal *a, integer * + lda, doublecomplex *b, integer *ldb, doublecomplex *c__, integer *ldc, + doublereal *rwork); + +/* Subroutine */ int zlarf_(char *side, integer *m, integer *n, doublecomplex + *v, integer *incv, doublecomplex *tau, doublecomplex *c__, integer * + ldc, doublecomplex *work); + +/* Subroutine */ int zlarfb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, doublecomplex *v, integer + *ldv, doublecomplex *t, integer *ldt, doublecomplex *c__, integer * + ldc, doublecomplex *work, integer *ldwork); + +/* Subroutine */ int zlarfg_(integer *n, doublecomplex *alpha, doublecomplex * + x, integer *incx, doublecomplex *tau); + +/* Subroutine */ int zlarft_(char *direct, char *storev, integer *n, integer * + k, doublecomplex *v, integer *ldv, doublecomplex *tau, doublecomplex * + t, integer *ldt); + +/* Subroutine */ int zlarfx_(char *side, integer *m, integer *n, + doublecomplex *v, doublecomplex *tau, doublecomplex *c__, integer * + ldc, doublecomplex *work); + +/* Subroutine */ int zlargv_(integer *n, doublecomplex *x, integer *incx, + doublecomplex *y, integer *incy, doublereal *c__, integer *incc); + +/* Subroutine */ int zlarnv_(integer *idist, integer *iseed, integer *n, + doublecomplex *x); + +/* Subroutine */ int zlarrv_(integer *n, doublereal *d__, doublereal *l, + integer *isplit, integer *m, doublereal *w, integer *iblock, + doublereal *gersch, doublereal *tol, doublecomplex *z__, integer *ldz, + integer *isuppz, doublereal *work, integer *iwork, integer *info); + +/* Subroutine */ int zlartg_(doublecomplex *f, doublecomplex *g, doublereal * + cs, doublecomplex *sn, doublecomplex *r__); + +/* Subroutine */ int zlartv_(integer *n, doublecomplex *x, integer *incx, + doublecomplex *y, integer *incy, doublereal *c__, doublecomplex *s, + integer *incc); + +/* Subroutine */ int zlarz_(char *side, integer *m, integer *n, integer *l, + doublecomplex *v, integer *incv, doublecomplex *tau, doublecomplex * + c__, integer *ldc, doublecomplex *work); + +/* Subroutine */ int zlarzb_(char *side, char *trans, char *direct, char * + storev, integer *m, integer *n, integer *k, integer *l, doublecomplex + *v, integer *ldv, doublecomplex *t, integer *ldt, doublecomplex *c__, + integer *ldc, doublecomplex *work, integer *ldwork); + +/* Subroutine */ int zlarzt_(char *direct, char *storev, integer *n, integer * + k, doublecomplex *v, integer *ldv, doublecomplex *tau, doublecomplex * + t, integer *ldt); + +/* Subroutine */ int zlascl_(char *type__, integer *kl, integer *ku, + doublereal *cfrom, doublereal *cto, integer *m, integer *n, + doublecomplex *a, integer *lda, integer *info); + +/* Subroutine */ int zlaset_(char *uplo, integer *m, integer *n, + doublecomplex *alpha, doublecomplex *beta, doublecomplex *a, integer * + lda); + +/* Subroutine */ int zlasr_(char *side, char *pivot, char *direct, integer *m, + integer *n, doublereal *c__, doublereal *s, doublecomplex *a, + integer *lda); + +/* Subroutine */ int zlassq_(integer *n, doublecomplex *x, integer *incx, + doublereal *scale, doublereal *sumsq); + +/* Subroutine */ int zlaswp_(integer *n, doublecomplex *a, integer *lda, + integer *k1, integer *k2, integer *ipiv, integer *incx); + +/* Subroutine */ int zlasyf_(char *uplo, integer *n, integer *nb, integer *kb, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *w, + integer *ldw, integer *info); + +/* Subroutine */ int zlatbs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, integer *kd, doublecomplex *ab, integer *ldab, + doublecomplex *x, doublereal *scale, doublereal *cnorm, integer *info); + +/* Subroutine */ int zlatdf_(integer *ijob, integer *n, doublecomplex *z__, + integer *ldz, doublecomplex *rhs, doublereal *rdsum, doublereal * + rdscal, integer *ipiv, integer *jpiv); + +/* Subroutine */ int zlatps_(char *uplo, char *trans, char *diag, char * + normin, integer *n, doublecomplex *ap, doublecomplex *x, doublereal * + scale, doublereal *cnorm, integer *info); + +/* Subroutine */ int zlatrd_(char *uplo, integer *n, integer *nb, + doublecomplex *a, integer *lda, doublereal *e, doublecomplex *tau, + doublecomplex *w, integer *ldw); + +/* Subroutine */ int zlatrs_(char *uplo, char *trans, char *diag, char * + normin, integer *n, doublecomplex *a, integer *lda, doublecomplex *x, + doublereal *scale, doublereal *cnorm, integer *info); + +/* Subroutine */ int zlatrz_(integer *m, integer *n, integer *l, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work); + +/* Subroutine */ int zlatzm_(char *side, integer *m, integer *n, + doublecomplex *v, integer *incv, doublecomplex *tau, doublecomplex * + c1, doublecomplex *c2, integer *ldc, doublecomplex *work); + +/* Subroutine */ int zlauu2_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *info); + +/* Subroutine */ int zlauum_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *info); + +/* Subroutine */ int zpbcon_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *anorm, doublereal * + rcond, doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zpbequ_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, doublereal *s, doublereal *scond, + doublereal *amax, integer *info); + +/* Subroutine */ int zpbrfs_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublecomplex *ab, integer *ldab, doublecomplex *afb, integer * + ldafb, doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublecomplex *work, doublereal * + rwork, integer *info); + +/* Subroutine */ int zpbstf_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, integer *info); + +/* Subroutine */ int zpbsv_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublecomplex *ab, integer *ldab, doublecomplex *b, integer * + ldb, integer *info); + +/* Subroutine */ int zpbsvx_(char *fact, char *uplo, integer *n, integer *kd, + integer *nrhs, doublecomplex *ab, integer *ldab, doublecomplex *afb, + integer *ldafb, char *equed, doublereal *s, doublecomplex *b, integer + *ldb, doublecomplex *x, integer *ldx, doublereal *rcond, doublereal * + ferr, doublereal *berr, doublecomplex *work, doublereal *rwork, + integer *info); + +/* Subroutine */ int zpbtf2_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, integer *info); + +/* Subroutine */ int zpbtrf_(char *uplo, integer *n, integer *kd, + doublecomplex *ab, integer *ldab, integer *info); + +/* Subroutine */ int zpbtrs_(char *uplo, integer *n, integer *kd, integer * + nrhs, doublecomplex *ab, integer *ldab, doublecomplex *b, integer * + ldb, integer *info); + +/* Subroutine */ int zpocon_(char *uplo, integer *n, doublecomplex *a, + integer *lda, doublereal *anorm, doublereal *rcond, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zpoequ_(integer *n, doublecomplex *a, integer *lda, + doublereal *s, doublereal *scond, doublereal *amax, integer *info); + +/* Subroutine */ int zporfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *af, integer *ldaf, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublecomplex *work, doublereal * + rwork, integer *info); + +/* Subroutine */ int zposv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zposvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *a, integer *lda, doublecomplex *af, integer * + ldaf, char *equed, doublereal *s, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *rcond, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zpotf2_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *info); + +/* Subroutine */ int zpotrf_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *info); + +/* Subroutine */ int zpotri_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *info); + +/* Subroutine */ int zpotrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zppcon_(char *uplo, integer *n, doublecomplex *ap, + doublereal *anorm, doublereal *rcond, doublecomplex *work, doublereal + *rwork, integer *info); + +/* Subroutine */ int zppequ_(char *uplo, integer *n, doublecomplex *ap, + doublereal *s, doublereal *scond, doublereal *amax, integer *info); + +/* Subroutine */ int zpprfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, doublecomplex *afp, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int zppsv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, doublecomplex *b, integer *ldb, integer *info); + +/* Subroutine */ int zppsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *ap, doublecomplex *afp, char *equed, doublereal * + s, doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *rcond, doublereal *ferr, doublereal *berr, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zpptrf_(char *uplo, integer *n, doublecomplex *ap, + integer *info); + +/* Subroutine */ int zpptri_(char *uplo, integer *n, doublecomplex *ap, + integer *info); + +/* Subroutine */ int zpptrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, doublecomplex *b, integer *ldb, integer *info); + +/* Subroutine */ int zptcon_(integer *n, doublereal *d__, doublecomplex *e, + doublereal *anorm, doublereal *rcond, doublereal *rwork, integer * + info); + +/* Subroutine */ int zptrfs_(char *uplo, integer *n, integer *nrhs, + doublereal *d__, doublecomplex *e, doublereal *df, doublecomplex *ef, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublecomplex *work, doublereal * + rwork, integer *info); + +/* Subroutine */ int zptsv_(integer *n, integer *nrhs, doublereal *d__, + doublecomplex *e, doublecomplex *b, integer *ldb, integer *info); + +/* Subroutine */ int zptsvx_(char *fact, integer *n, integer *nrhs, + doublereal *d__, doublecomplex *e, doublereal *df, doublecomplex *ef, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *rcond, doublereal *ferr, doublereal *berr, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zpttrf_(integer *n, doublereal *d__, doublecomplex *e, + integer *info); + +/* Subroutine */ int zpttrs_(char *uplo, integer *n, integer *nrhs, + doublereal *d__, doublecomplex *e, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zptts2_(integer *iuplo, integer *n, integer *nrhs, + doublereal *d__, doublecomplex *e, doublecomplex *b, integer *ldb); + +/* Subroutine */ int zrot_(integer *n, doublecomplex *cx, integer *incx, + doublecomplex *cy, integer *incy, doublereal *c__, doublecomplex *s); + +/* Subroutine */ int zspcon_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, doublereal *anorm, doublereal *rcond, doublecomplex * + work, integer *info); + +/* Subroutine */ int zspmv_(char *uplo, integer *n, doublecomplex *alpha, + doublecomplex *ap, doublecomplex *x, integer *incx, doublecomplex * + beta, doublecomplex *y, integer *incy); + +/* Subroutine */ int zspr_(char *uplo, integer *n, doublecomplex *alpha, + doublecomplex *x, integer *incx, doublecomplex *ap); + +/* Subroutine */ int zsprfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, doublecomplex *afp, integer *ipiv, doublecomplex * + b, integer *ldb, doublecomplex *x, integer *ldx, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int zspsv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, integer *ipiv, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zspsvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *ap, doublecomplex *afp, integer *ipiv, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *rcond, doublereal *ferr, doublereal *berr, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int zsptrf_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, integer *info); + +/* Subroutine */ int zsptri_(char *uplo, integer *n, doublecomplex *ap, + integer *ipiv, doublecomplex *work, integer *info); + +/* Subroutine */ int zsptrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *ap, integer *ipiv, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int zstedc_(char *compz, integer *n, doublereal *d__, + doublereal *e, doublecomplex *z__, integer *ldz, doublecomplex *work, + integer *lwork, doublereal *rwork, integer *lrwork, integer *iwork, + integer *liwork, integer *info); + +/* Subroutine */ int zstein_(integer *n, doublereal *d__, doublereal *e, + integer *m, doublereal *w, integer *iblock, integer *isplit, + doublecomplex *z__, integer *ldz, doublereal *work, integer *iwork, + integer *ifail, integer *info); + +/* Subroutine */ int zsteqr_(char *compz, integer *n, doublereal *d__, + doublereal *e, doublecomplex *z__, integer *ldz, doublereal *work, + integer *info); + +/* Subroutine */ int zsycon_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublereal *anorm, doublereal *rcond, + doublecomplex *work, integer *info); + +/* Subroutine */ int zsymv_(char *uplo, integer *n, doublecomplex *alpha, + doublecomplex *a, integer *lda, doublecomplex *x, integer *incx, + doublecomplex *beta, doublecomplex *y, integer *incy); + +/* Subroutine */ int zsyr_(char *uplo, integer *n, doublecomplex *alpha, + doublecomplex *x, integer *incx, doublecomplex *a, integer *lda); + +/* Subroutine */ int zsyrfs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, doublecomplex *af, integer *ldaf, + integer *ipiv, doublecomplex *b, integer *ldb, doublecomplex *x, + integer *ldx, doublereal *ferr, doublereal *berr, doublecomplex *work, + doublereal *rwork, integer *info); + +/* Subroutine */ int zsysv_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *b, + integer *ldb, doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int zsysvx_(char *fact, char *uplo, integer *n, integer * + nrhs, doublecomplex *a, integer *lda, doublecomplex *af, integer * + ldaf, integer *ipiv, doublecomplex *b, integer *ldb, doublecomplex *x, + integer *ldx, doublereal *rcond, doublereal *ferr, doublereal *berr, + doublecomplex *work, integer *lwork, doublereal *rwork, integer *info); + +/* Subroutine */ int zsytf2_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, integer *info); + +/* Subroutine */ int zsytrf_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zsytri_(char *uplo, integer *n, doublecomplex *a, + integer *lda, integer *ipiv, doublecomplex *work, integer *info); + +/* Subroutine */ int zsytrs_(char *uplo, integer *n, integer *nrhs, + doublecomplex *a, integer *lda, integer *ipiv, doublecomplex *b, + integer *ldb, integer *info); + +/* Subroutine */ int ztbcon_(char *norm, char *uplo, char *diag, integer *n, + integer *kd, doublecomplex *ab, integer *ldab, doublereal *rcond, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int ztbrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, doublecomplex *ab, integer *ldab, + doublecomplex *b, integer *ldb, doublecomplex *x, integer *ldx, + doublereal *ferr, doublereal *berr, doublecomplex *work, doublereal * + rwork, integer *info); + +/* Subroutine */ int ztbtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *kd, integer *nrhs, doublecomplex *ab, integer *ldab, + doublecomplex *b, integer *ldb, integer *info); + +/* Subroutine */ int ztgevc_(char *side, char *howmny, logical *select, + integer *n, doublecomplex *a, integer *lda, doublecomplex *b, integer + *ldb, doublecomplex *vl, integer *ldvl, doublecomplex *vr, integer * + ldvr, integer *mm, integer *m, doublecomplex *work, doublereal *rwork, + integer *info); + +/* Subroutine */ int ztgex2_(logical *wantq, logical *wantz, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *q, integer *ldq, doublecomplex *z__, integer *ldz, + integer *j1, integer *info); + +/* Subroutine */ int ztgexc_(logical *wantq, logical *wantz, integer *n, + doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *q, integer *ldq, doublecomplex *z__, integer *ldz, + integer *ifst, integer *ilst, integer *info); + +/* Subroutine */ int ztgsen_(integer *ijob, logical *wantq, logical *wantz, + logical *select, integer *n, doublecomplex *a, integer *lda, + doublecomplex *b, integer *ldb, doublecomplex *alpha, doublecomplex * + beta, doublecomplex *q, integer *ldq, doublecomplex *z__, integer * + ldz, integer *m, doublereal *pl, doublereal *pr, doublereal *dif, + doublecomplex *work, integer *lwork, integer *iwork, integer *liwork, + integer *info); + +/* Subroutine */ int ztgsja_(char *jobu, char *jobv, char *jobq, integer *m, + integer *p, integer *n, integer *k, integer *l, doublecomplex *a, + integer *lda, doublecomplex *b, integer *ldb, doublereal *tola, + doublereal *tolb, doublereal *alpha, doublereal *beta, doublecomplex * + u, integer *ldu, doublecomplex *v, integer *ldv, doublecomplex *q, + integer *ldq, doublecomplex *work, integer *ncycle, integer *info); + +/* Subroutine */ int ztgsna_(char *job, char *howmny, logical *select, + integer *n, doublecomplex *a, integer *lda, doublecomplex *b, integer + *ldb, doublecomplex *vl, integer *ldvl, doublecomplex *vr, integer * + ldvr, doublereal *s, doublereal *dif, integer *mm, integer *m, + doublecomplex *work, integer *lwork, integer *iwork, integer *info); + +/* Subroutine */ int ztgsy2_(char *trans, integer *ijob, integer *m, integer * + n, doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *c__, integer *ldc, doublecomplex *d__, integer *ldd, + doublecomplex *e, integer *lde, doublecomplex *f, integer *ldf, + doublereal *scale, doublereal *rdsum, doublereal *rdscal, integer * + info); + +/* Subroutine */ int ztgsyl_(char *trans, integer *ijob, integer *m, integer * + n, doublecomplex *a, integer *lda, doublecomplex *b, integer *ldb, + doublecomplex *c__, integer *ldc, doublecomplex *d__, integer *ldd, + doublecomplex *e, integer *lde, doublecomplex *f, integer *ldf, + doublereal *scale, doublereal *dif, doublecomplex *work, integer * + lwork, integer *iwork, integer *info); + +/* Subroutine */ int ztpcon_(char *norm, char *uplo, char *diag, integer *n, + doublecomplex *ap, doublereal *rcond, doublecomplex *work, doublereal + *rwork, integer *info); + +/* Subroutine */ int ztprfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublecomplex *ap, doublecomplex *b, integer *ldb, + doublecomplex *x, integer *ldx, doublereal *ferr, doublereal *berr, + doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int ztptri_(char *uplo, char *diag, integer *n, + doublecomplex *ap, integer *info); + +/* Subroutine */ int ztptrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublecomplex *ap, doublecomplex *b, integer *ldb, + integer *info); + +/* Subroutine */ int ztrcon_(char *norm, char *uplo, char *diag, integer *n, + doublecomplex *a, integer *lda, doublereal *rcond, doublecomplex * + work, doublereal *rwork, integer *info); + +/* Subroutine */ int ztrevc_(char *side, char *howmny, logical *select, + integer *n, doublecomplex *t, integer *ldt, doublecomplex *vl, + integer *ldvl, doublecomplex *vr, integer *ldvr, integer *mm, integer + *m, doublecomplex *work, doublereal *rwork, integer *info); + +/* Subroutine */ int ztrexc_(char *compq, integer *n, doublecomplex *t, + integer *ldt, doublecomplex *q, integer *ldq, integer *ifst, integer * + ilst, integer *info); + +/* Subroutine */ int ztrrfs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, doublecomplex *x, integer *ldx, doublereal *ferr, + doublereal *berr, doublecomplex *work, doublereal *rwork, integer * + info); + +/* Subroutine */ int ztrsen_(char *job, char *compq, logical *select, integer + *n, doublecomplex *t, integer *ldt, doublecomplex *q, integer *ldq, + doublecomplex *w, integer *m, doublereal *s, doublereal *sep, + doublecomplex *work, integer *lwork, integer *info); + +/* Subroutine */ int ztrsna_(char *job, char *howmny, logical *select, + integer *n, doublecomplex *t, integer *ldt, doublecomplex *vl, + integer *ldvl, doublecomplex *vr, integer *ldvr, doublereal *s, + doublereal *sep, integer *mm, integer *m, doublecomplex *work, + integer *ldwork, doublereal *rwork, integer *info); + +/* Subroutine */ int ztrsyl_(char *trana, char *tranb, integer *isgn, integer + *m, integer *n, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, doublecomplex *c__, integer *ldc, doublereal *scale, + integer *info); + +/* Subroutine */ int ztrti2_(char *uplo, char *diag, integer *n, + doublecomplex *a, integer *lda, integer *info); + +/* Subroutine */ int ztrtri_(char *uplo, char *diag, integer *n, + doublecomplex *a, integer *lda, integer *info); + +/* Subroutine */ int ztrtrs_(char *uplo, char *trans, char *diag, integer *n, + integer *nrhs, doublecomplex *a, integer *lda, doublecomplex *b, + integer *ldb, integer *info); + +/* Subroutine */ int ztzrqf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, integer *info); + +/* Subroutine */ int ztzrzf_(integer *m, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zung2l_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *info); + +/* Subroutine */ int zung2r_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *info); + +/* Subroutine */ int zungbr_(char *vect, integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zunghr_(integer *n, integer *ilo, integer *ihi, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zungl2_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *info); + +/* Subroutine */ int zunglq_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zungql_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zungqr_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zungr2_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *info); + +/* Subroutine */ int zungrq_(integer *m, integer *n, integer *k, + doublecomplex *a, integer *lda, doublecomplex *tau, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zungtr_(char *uplo, integer *n, doublecomplex *a, + integer *lda, doublecomplex *tau, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zunm2l_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *info); + +/* Subroutine */ int zunm2r_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *info); + +/* Subroutine */ int zunmbr_(char *vect, char *side, char *trans, integer *m, + integer *n, integer *k, doublecomplex *a, integer *lda, doublecomplex + *tau, doublecomplex *c__, integer *ldc, doublecomplex *work, integer * + lwork, integer *info); + +/* Subroutine */ int zunmhr_(char *side, char *trans, integer *m, integer *n, + integer *ilo, integer *ihi, doublecomplex *a, integer *lda, + doublecomplex *tau, doublecomplex *c__, integer *ldc, doublecomplex * + work, integer *lwork, integer *info); + +/* Subroutine */ int zunml2_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *info); + +/* Subroutine */ int zunmlq_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zunmql_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zunmqr_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zunmr2_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *info); + +/* Subroutine */ int zunmr3_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, doublecomplex *a, integer *lda, doublecomplex + *tau, doublecomplex *c__, integer *ldc, doublecomplex *work, integer * + info); + +/* Subroutine */ int zunmrq_(char *side, char *trans, integer *m, integer *n, + integer *k, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zunmrz_(char *side, char *trans, integer *m, integer *n, + integer *k, integer *l, doublecomplex *a, integer *lda, doublecomplex + *tau, doublecomplex *c__, integer *ldc, doublecomplex *work, integer * + lwork, integer *info); + +/* Subroutine */ int zunmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, doublecomplex *a, integer *lda, doublecomplex *tau, + doublecomplex *c__, integer *ldc, doublecomplex *work, integer *lwork, + integer *info); + +/* Subroutine */ int zupgtr_(char *uplo, integer *n, doublecomplex *ap, + doublecomplex *tau, doublecomplex *q, integer *ldq, doublecomplex * + work, integer *info); + +/* Subroutine */ int zupmtr_(char *side, char *uplo, char *trans, integer *m, + integer *n, doublecomplex *ap, doublecomplex *tau, doublecomplex *c__, + integer *ldc, doublecomplex *work, integer *info); + +#endif /* __CLAPACK_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/close.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/close.c new file mode 100644 index 00000000..d305ccf8 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/close.c @@ -0,0 +1,80 @@ +#include "f2c.h" +#include "fio.h" +#undef abs +#undef min +#undef max +#include "stdlib.h" +#ifdef NON_UNIX_STDIO +#ifndef unlink +#define unlink remove +#endif +#else +#ifdef MSDOS +#include "io.h" +#else +#ifdef __cplusplus +extern "C" int unlink(const char*); +#else +extern int unlink(const char*); +#endif +#endif +#endif + +integer f_clos(cllist *a) +{ unit *b; + + if(a->cunit >= MXUNIT) return(0); + b= &f__units[a->cunit]; + if(b->ufd==NULL) + goto done; + if (b->uscrtch == 1) + goto Delete; + if (!a->csta) + goto Keep; + switch(*a->csta) { + default: + Keep: + case 'k': + case 'K': + if(b->uwrt == 1) + t_runc((alist *)a); + if(b->ufnm) { + fclose(b->ufd); + free(b->ufnm); + } + break; + case 'd': + case 'D': + Delete: + fclose(b->ufd); + if(b->ufnm) { + unlink(b->ufnm); /*SYSDEP*/ + free(b->ufnm); + } + } + b->ufd=NULL; + done: + b->uend=0; + b->ufnm=NULL; + return(0); + } +void f_exit(void) +{ int i; + static cllist xx; + if (!xx.cerr) { + xx.cerr=1; + xx.csta=NULL; + for(i=0;i 0) { + jy = 1; + } else { + jy = 1 - (*n - 1) * *incy; + } + if (*incx == 1) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (y[jy] != 0.) { + temp = *alpha * y[jy]; + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + a_ref(i__, j) = a_ref(i__, j) + x[i__] * temp; +/* L10: */ + } + } + jy += *incy; +/* L20: */ + } + } else { + if (*incx > 0) { + kx = 1; + } else { + kx = 1 - (*m - 1) * *incx; + } + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (y[jy] != 0.) { + temp = *alpha * y[jy]; + ix = kx; + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + a_ref(i__, j) = a_ref(i__, j) + x[ix] * temp; + ix += *incx; +/* L30: */ + } + } + jy += *incy; +/* L40: */ + } + } + return 0; +/* End of DGER . */ +} /* dger_ */ +#undef a_ref + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgesv.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgesv.c new file mode 100644 index 00000000..5c0dc52b --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgesv.c @@ -0,0 +1,117 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dgesv_(integer *n, integer *nrhs, doublereal *a, integer + *lda, integer *ipiv, doublereal *b, integer *ldb, integer *info) +{ +/* -- LAPACK driver routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + March 31, 1993 + + + Purpose + ======= + + DGESV computes the solution to a real system of linear equations + A * X = B, + where A is an N-by-N matrix and X and B are N-by-NRHS matrices. + + The LU decomposition with partial pivoting and row interchanges is + used to factor A as + A = P * L * U, + where P is a permutation matrix, L is unit lower triangular, and U is + upper triangular. The factored form of A is then used to solve the + system of equations A * X = B. + + Arguments + ========= + + N (input) INTEGER + The number of linear equations, i.e., the order of the + matrix A. N >= 0. + + NRHS (input) INTEGER + The number of right hand sides, i.e., the number of columns + of the matrix B. NRHS >= 0. + + A (input/output) DOUBLE PRECISION array, dimension (LDA,N) + On entry, the N-by-N coefficient matrix A. + On exit, the factors L and U from the factorization + A = P*L*U; the unit diagonal elements of L are not stored. + + LDA (input) INTEGER + The leading dimension of the array A. LDA >= max(1,N). + + IPIV (output) INTEGER array, dimension (N) + The pivot indices that define the permutation matrix P; + row i of the matrix was interchanged with row IPIV(i). + + B (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) + On entry, the N-by-NRHS matrix of right hand side matrix B. + On exit, if INFO = 0, the N-by-NRHS solution matrix X. + + LDB (input) INTEGER + The leading dimension of the array B. LDB >= max(1,N). + + INFO (output) INTEGER + = 0: successful exit + < 0: if INFO = -i, the i-th argument had an illegal value + > 0: if INFO = i, U(i,i) is exactly zero. The factorization + has been completed, but the factor U is exactly + singular, so the solution could not be computed. + + ===================================================================== + + + Test the input parameters. + + Parameter adjustments */ + /* System generated locals */ + integer a_dim1, a_offset, b_dim1, b_offset, i__1; + /* Local variables */ + extern /* Subroutine */ int dgetrf_(integer *, integer *, doublereal *, + integer *, integer *, integer *), xerbla_(char *, integer *), dgetrs_(char *, integer *, integer *, doublereal *, + integer *, integer *, doublereal *, integer *, integer *); + + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + --ipiv; + b_dim1 = *ldb; + b_offset = 1 + b_dim1 * 1; + b -= b_offset; + + /* Function Body */ + *info = 0; + if (*n < 0) { + *info = -1; + } else if (*nrhs < 0) { + *info = -2; + } else if (*lda < max(1,*n)) { + *info = -4; + } else if (*ldb < max(1,*n)) { + *info = -7; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_("DGESV ", &i__1); + return 0; + } + +/* Compute the LU factorization of A. */ + + dgetrf_(n, n, &a[a_offset], lda, &ipiv[1], info); + if (*info == 0) { + +/* Solve the system A*X = B, overwriting B with X. */ + + dgetrs_("No transpose", n, nrhs, &a[a_offset], lda, &ipiv[1], &b[ + b_offset], ldb, info); + } + return 0; + +/* End of DGESV */ + +} /* dgesv_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetf2.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetf2.c new file mode 100644 index 00000000..83bf1872 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetf2.c @@ -0,0 +1,157 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dgetf2_(integer *m, integer *n, doublereal *a, integer * + lda, integer *ipiv, integer *info) +{ +/* -- LAPACK routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + June 30, 1992 + + + Purpose + ======= + + DGETF2 computes an LU factorization of a general m-by-n matrix A + using partial pivoting with row interchanges. + + The factorization has the form + A = P * L * U + where P is a permutation matrix, L is lower triangular with unit + diagonal elements (lower trapezoidal if m > n), and U is upper + triangular (upper trapezoidal if m < n). + + This is the right-looking Level 2 BLAS version of the algorithm. + + Arguments + ========= + + M (input) INTEGER + The number of rows of the matrix A. M >= 0. + + N (input) INTEGER + The number of columns of the matrix A. N >= 0. + + A (input/output) DOUBLE PRECISION array, dimension (LDA,N) + On entry, the m by n matrix to be factored. + On exit, the factors L and U from the factorization + A = P*L*U; the unit diagonal elements of L are not stored. + + LDA (input) INTEGER + The leading dimension of the array A. LDA >= max(1,M). + + IPIV (output) INTEGER array, dimension (min(M,N)) + The pivot indices; for 1 <= i <= min(M,N), row i of the + matrix was interchanged with row IPIV(i). + + INFO (output) INTEGER + = 0: successful exit + < 0: if INFO = -k, the k-th argument had an illegal value + > 0: if INFO = k, U(k,k) is exactly zero. The factorization + has been completed, but the factor U is exactly + singular, and division by zero will occur if it is used + to solve a system of equations. + + ===================================================================== + + + Test the input parameters. + + Parameter adjustments */ + /* Table of constant values */ + static integer c__1 = 1; + static doublereal c_b6 = -1.; + + /* System generated locals */ + integer a_dim1, a_offset, i__1, i__2, i__3; + doublereal d__1; + /* Local variables */ + extern /* Subroutine */ int dger_(integer *, integer *, doublereal *, + doublereal *, integer *, doublereal *, integer *, doublereal *, + integer *); + static integer j; + extern /* Subroutine */ int dscal_(integer *, doublereal *, doublereal *, + integer *), dswap_(integer *, doublereal *, integer *, doublereal + *, integer *); + static integer jp; + extern integer idamax_(integer *, doublereal *, integer *); + extern /* Subroutine */ int xerbla_(char *, integer *); +#define a_ref(a_1,a_2) a[(a_2)*a_dim1 + a_1] + + + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + --ipiv; + + /* Function Body */ + *info = 0; + if (*m < 0) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*lda < max(1,*m)) { + *info = -4; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_("DGETF2", &i__1); + return 0; + } + +/* Quick return if possible */ + + if (*m == 0 || *n == 0) { + return 0; + } + + i__1 = min(*m,*n); + for (j = 1; j <= i__1; ++j) { + +/* Find pivot and test for singularity. */ + + i__2 = *m - j + 1; + jp = j - 1 + idamax_(&i__2, &a_ref(j, j), &c__1); + ipiv[j] = jp; + if (a_ref(jp, j) != 0.) { + +/* Apply the interchange to columns 1:N. */ + + if (jp != j) { + dswap_(n, &a_ref(j, 1), lda, &a_ref(jp, 1), lda); + } + +/* Compute elements J+1:M of J-th column. */ + + if (j < *m) { + i__2 = *m - j; + d__1 = 1. / a_ref(j, j); + dscal_(&i__2, &d__1, &a_ref(j + 1, j), &c__1); + } + + } else if (*info == 0) { + + *info = j; + } + + if (j < min(*m,*n)) { + +/* Update trailing submatrix. */ + + i__2 = *m - j; + i__3 = *n - j; + dger_(&i__2, &i__3, &c_b6, &a_ref(j + 1, j), &c__1, &a_ref(j, j + + 1), lda, &a_ref(j + 1, j + 1), lda); + } +/* L10: */ + } + return 0; + +/* End of DGETF2 */ + +} /* dgetf2_ */ + +#undef a_ref + + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrf.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrf.c new file mode 100644 index 00000000..13175f01 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrf.c @@ -0,0 +1,197 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dgetrf_(integer *m, integer *n, doublereal *a, integer * + lda, integer *ipiv, integer *info) +{ +/* -- LAPACK routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + March 31, 1993 + + + Purpose + ======= + + DGETRF computes an LU factorization of a general M-by-N matrix A + using partial pivoting with row interchanges. + + The factorization has the form + A = P * L * U + where P is a permutation matrix, L is lower triangular with unit + diagonal elements (lower trapezoidal if m > n), and U is upper + triangular (upper trapezoidal if m < n). + + This is the right-looking Level 3 BLAS version of the algorithm. + + Arguments + ========= + + M (input) INTEGER + The number of rows of the matrix A. M >= 0. + + N (input) INTEGER + The number of columns of the matrix A. N >= 0. + + A (input/output) DOUBLE PRECISION array, dimension (LDA,N) + On entry, the M-by-N matrix to be factored. + On exit, the factors L and U from the factorization + A = P*L*U; the unit diagonal elements of L are not stored. + + LDA (input) INTEGER + The leading dimension of the array A. LDA >= max(1,M). + + IPIV (output) INTEGER array, dimension (min(M,N)) + The pivot indices; for 1 <= i <= min(M,N), row i of the + matrix was interchanged with row IPIV(i). + + INFO (output) INTEGER + = 0: successful exit + < 0: if INFO = -i, the i-th argument had an illegal value + > 0: if INFO = i, U(i,i) is exactly zero. The factorization + has been completed, but the factor U is exactly + singular, and division by zero will occur if it is used + to solve a system of equations. + + ===================================================================== + + + Test the input parameters. + + Parameter adjustments */ + /* Table of constant values */ + static integer c__1 = 1; + static integer c_n1 = -1; + static doublereal c_b16 = 1.; + static doublereal c_b19 = -1.; + + /* System generated locals */ + integer a_dim1, a_offset, i__1, i__2, i__3, i__4, i__5; + /* Local variables */ + static integer i__, j; + extern /* Subroutine */ int dgemm_(char *, char *, integer *, integer *, + integer *, doublereal *, doublereal *, integer *, doublereal *, + integer *, doublereal *, doublereal *, integer *); + static integer iinfo; + extern /* Subroutine */ int dtrsm_(char *, char *, char *, char *, + integer *, integer *, doublereal *, doublereal *, integer *, + doublereal *, integer *), dgetf2_( + integer *, integer *, doublereal *, integer *, integer *, integer + *); + static integer jb, nb; + extern /* Subroutine */ int xerbla_(char *, integer *); + extern integer ilaenv_(integer *, char *, char *, integer *, integer *, + integer *, integer *, ftnlen, ftnlen); + extern /* Subroutine */ int dlaswp_(integer *, doublereal *, integer *, + integer *, integer *, integer *, integer *); +#define a_ref(a_1,a_2) a[(a_2)*a_dim1 + a_1] + + + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + --ipiv; + + /* Function Body */ + *info = 0; + if (*m < 0) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*lda < max(1,*m)) { + *info = -4; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_("DGETRF", &i__1); + return 0; + } + +/* Quick return if possible */ + + if (*m == 0 || *n == 0) { + return 0; + } + +/* Determine the block size for this environment. */ + + nb = ilaenv_(&c__1, "DGETRF", " ", m, n, &c_n1, &c_n1, (ftnlen)6, (ftnlen) + 1); + if (nb <= 1 || nb >= min(*m,*n)) { + +/* Use unblocked code. */ + + dgetf2_(m, n, &a[a_offset], lda, &ipiv[1], info); + } else { + +/* Use blocked code. */ + + i__1 = min(*m,*n); + i__2 = nb; + for (j = 1; i__2 < 0 ? j >= i__1 : j <= i__1; j += i__2) { +/* Computing MIN */ + i__3 = min(*m,*n) - j + 1; + jb = min(i__3,nb); + +/* Factor diagonal and subdiagonal blocks and test for exact + singularity. */ + + i__3 = *m - j + 1; + dgetf2_(&i__3, &jb, &a_ref(j, j), lda, &ipiv[j], &iinfo); + +/* Adjust INFO and the pivot indices. */ + + if (*info == 0 && iinfo > 0) { + *info = iinfo + j - 1; + } +/* Computing MIN */ + i__4 = *m, i__5 = j + jb - 1; + i__3 = min(i__4,i__5); + for (i__ = j; i__ <= i__3; ++i__) { + ipiv[i__] = j - 1 + ipiv[i__]; +/* L10: */ + } + +/* Apply interchanges to columns 1:J-1. */ + + i__3 = j - 1; + i__4 = j + jb - 1; + dlaswp_(&i__3, &a[a_offset], lda, &j, &i__4, &ipiv[1], &c__1); + + if (j + jb <= *n) { + +/* Apply interchanges to columns J+JB:N. */ + + i__3 = *n - j - jb + 1; + i__4 = j + jb - 1; + dlaswp_(&i__3, &a_ref(1, j + jb), lda, &j, &i__4, &ipiv[1], & + c__1); + +/* Compute block row of U. */ + + i__3 = *n - j - jb + 1; + dtrsm_("Left", "Lower", "No transpose", "Unit", &jb, &i__3, & + c_b16, &a_ref(j, j), lda, &a_ref(j, j + jb), lda); + if (j + jb <= *m) { + +/* Update trailing submatrix. */ + + i__3 = *m - j - jb + 1; + i__4 = *n - j - jb + 1; + dgemm_("No transpose", "No transpose", &i__3, &i__4, &jb, + &c_b19, &a_ref(j + jb, j), lda, &a_ref(j, j + jb), + lda, &c_b16, &a_ref(j + jb, j + jb), lda); + } + } +/* L20: */ + } + } + return 0; + +/* End of DGETRF */ + +} /* dgetrf_ */ + +#undef a_ref + + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrs.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrs.c new file mode 100644 index 00000000..c4dd0b38 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dgetrs.c @@ -0,0 +1,159 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dgetrs_(char *trans, integer *n, integer *nrhs, + doublereal *a, integer *lda, integer *ipiv, doublereal *b, integer * + ldb, integer *info) +{ +/* -- LAPACK routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + March 31, 1993 + + + Purpose + ======= + + DGETRS solves a system of linear equations + A * X = B or A' * X = B + with a general N-by-N matrix A using the LU factorization computed + by DGETRF. + + Arguments + ========= + + TRANS (input) CHARACTER*1 + Specifies the form of the system of equations: + = 'N': A * X = B (No transpose) + = 'T': A'* X = B (Transpose) + = 'C': A'* X = B (Conjugate transpose = Transpose) + + N (input) INTEGER + The order of the matrix A. N >= 0. + + NRHS (input) INTEGER + The number of right hand sides, i.e., the number of columns + of the matrix B. NRHS >= 0. + + A (input) DOUBLE PRECISION array, dimension (LDA,N) + The factors L and U from the factorization A = P*L*U + as computed by DGETRF. + + LDA (input) INTEGER + The leading dimension of the array A. LDA >= max(1,N). + + IPIV (input) INTEGER array, dimension (N) + The pivot indices from DGETRF; for 1<=i<=N, row i of the + matrix was interchanged with row IPIV(i). + + B (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) + On entry, the right hand side matrix B. + On exit, the solution matrix X. + + LDB (input) INTEGER + The leading dimension of the array B. LDB >= max(1,N). + + INFO (output) INTEGER + = 0: successful exit + < 0: if INFO = -i, the i-th argument had an illegal value + + ===================================================================== + + + Test the input parameters. + + Parameter adjustments */ + /* Table of constant values */ + static integer c__1 = 1; + static doublereal c_b12 = 1.; + static integer c_n1 = -1; + + /* System generated locals */ + integer a_dim1, a_offset, b_dim1, b_offset, i__1; + /* Local variables */ + extern logical lsame_(char *, char *); + extern /* Subroutine */ int dtrsm_(char *, char *, char *, char *, + integer *, integer *, doublereal *, doublereal *, integer *, + doublereal *, integer *), xerbla_( + char *, integer *), dlaswp_(integer *, doublereal *, + integer *, integer *, integer *, integer *, integer *); + static logical notran; + + + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + --ipiv; + b_dim1 = *ldb; + b_offset = 1 + b_dim1 * 1; + b -= b_offset; + + /* Function Body */ + *info = 0; + notran = lsame_(trans, "N"); + if (! notran && ! lsame_(trans, "T") && ! lsame_( + trans, "C")) { + *info = -1; + } else if (*n < 0) { + *info = -2; + } else if (*nrhs < 0) { + *info = -3; + } else if (*lda < max(1,*n)) { + *info = -5; + } else if (*ldb < max(1,*n)) { + *info = -8; + } + if (*info != 0) { + i__1 = -(*info); + xerbla_("DGETRS", &i__1); + return 0; + } + +/* Quick return if possible */ + + if (*n == 0 || *nrhs == 0) { + return 0; + } + + if (notran) { + +/* Solve A * X = B. + + Apply row interchanges to the right hand sides. */ + + dlaswp_(nrhs, &b[b_offset], ldb, &c__1, n, &ipiv[1], &c__1); + +/* Solve L*X = B, overwriting B with X. */ + + dtrsm_("Left", "Lower", "No transpose", "Unit", n, nrhs, &c_b12, &a[ + a_offset], lda, &b[b_offset], ldb); + +/* Solve U*X = B, overwriting B with X. */ + + dtrsm_("Left", "Upper", "No transpose", "Non-unit", n, nrhs, &c_b12, & + a[a_offset], lda, &b[b_offset], ldb); + } else { + +/* Solve A' * X = B. + + Solve U'*X = B, overwriting B with X. */ + + dtrsm_("Left", "Upper", "Transpose", "Non-unit", n, nrhs, &c_b12, &a[ + a_offset], lda, &b[b_offset], ldb); + +/* Solve L'*X = B, overwriting B with X. */ + + dtrsm_("Left", "Lower", "Transpose", "Unit", n, nrhs, &c_b12, &a[ + a_offset], lda, &b[b_offset], ldb); + +/* Apply row interchanges to the solution vectors. */ + + dlaswp_(nrhs, &b[b_offset], ldb, &c__1, n, &ipiv[1], &c_n1); + } + + return 0; + +/* End of DGETRS */ + +} /* dgetrs_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dlaswp.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dlaswp.c new file mode 100644 index 00000000..4dd6fd7b --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dlaswp.c @@ -0,0 +1,143 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dlaswp_(integer *n, doublereal *a, integer *lda, integer + *k1, integer *k2, integer *ipiv, integer *incx) +{ +/* -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + June 30, 1999 + + + Purpose + ======= + + DLASWP performs a series of row interchanges on the matrix A. + One row interchange is initiated for each of rows K1 through K2 of A. + + Arguments + ========= + + N (input) INTEGER + The number of columns of the matrix A. + + A (input/output) DOUBLE PRECISION array, dimension (LDA,N) + On entry, the matrix of column dimension N to which the row + interchanges will be applied. + On exit, the permuted matrix. + + LDA (input) INTEGER + The leading dimension of the array A. + + K1 (input) INTEGER + The first element of IPIV for which a row interchange will + be done. + + K2 (input) INTEGER + The last element of IPIV for which a row interchange will + be done. + + IPIV (input) INTEGER array, dimension (M*abs(INCX)) + The vector of pivot indices. Only the elements in positions + K1 through K2 of IPIV are accessed. + IPIV(K) = L implies rows K and L are to be interchanged. + + INCX (input) INTEGER + The increment between successive values of IPIV. If IPIV + is negative, the pivots are applied in reverse order. + + Further Details + =============== + + Modified by + R. C. Whaley, Computer Science Dept., Univ. of Tenn., Knoxville, USA + + ===================================================================== + + + Interchange row I with row IPIV(I) for each of rows K1 through K2. + + Parameter adjustments */ + /* System generated locals */ + integer a_dim1, a_offset, i__1, i__2, i__3, i__4; + /* Local variables */ + static doublereal temp; + static integer i__, j, k, i1, i2, n32, ip, ix, ix0, inc; +#define a_ref(a_1,a_2) a[(a_2)*a_dim1 + a_1] + + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + --ipiv; + + /* Function Body */ + if (*incx > 0) { + ix0 = *k1; + i1 = *k1; + i2 = *k2; + inc = 1; + } else if (*incx < 0) { + ix0 = (1 - *k2) * *incx + 1; + i1 = *k2; + i2 = *k1; + inc = -1; + } else { + return 0; + } + + n32 = *n / 32 << 5; + if (n32 != 0) { + i__1 = n32; + for (j = 1; j <= i__1; j += 32) { + ix = ix0; + i__2 = i2; + i__3 = inc; + for (i__ = i1; i__3 < 0 ? i__ >= i__2 : i__ <= i__2; i__ += i__3) + { + ip = ipiv[ix]; + if (ip != i__) { + i__4 = j + 31; + for (k = j; k <= i__4; ++k) { + temp = a_ref(i__, k); + a_ref(i__, k) = a_ref(ip, k); + a_ref(ip, k) = temp; +/* L10: */ + } + } + ix += *incx; +/* L20: */ + } +/* L30: */ + } + } + if (n32 != *n) { + ++n32; + ix = ix0; + i__1 = i2; + i__3 = inc; + for (i__ = i1; i__3 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__3) { + ip = ipiv[ix]; + if (ip != i__) { + i__2 = *n; + for (k = n32; k <= i__2; ++k) { + temp = a_ref(i__, k); + a_ref(i__, k) = a_ref(ip, k); + a_ref(ip, k) = temp; +/* L40: */ + } + } + ix += *incx; +/* L50: */ + } + } + + return 0; + +/* End of DLASWP */ + +} /* dlaswp_ */ + +#undef a_ref + + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dscal.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dscal.c new file mode 100644 index 00000000..e0e6ffb9 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dscal.c @@ -0,0 +1,62 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dscal_(integer *n, doublereal *da, doublereal *dx, + integer *incx) +{ + /* System generated locals */ + integer i__1, i__2; + /* Local variables */ + static integer i__, m, nincx, mp1; +/* scales a vector by a constant. + uses unrolled loops for increment equal to one. + jack dongarra, linpack, 3/11/78. + modified 3/93 to return if incx .le. 0. + modified 12/3/93, array(1) declarations changed to array(*) + Parameter adjustments */ + --dx; + /* Function Body */ + if (*n <= 0 || *incx <= 0) { + return 0; + } + if (*incx == 1) { + goto L20; + } +/* code for increment not equal to 1 */ + nincx = *n * *incx; + i__1 = nincx; + i__2 = *incx; + for (i__ = 1; i__2 < 0 ? i__ >= i__1 : i__ <= i__1; i__ += i__2) { + dx[i__] = *da * dx[i__]; +/* L10: */ + } + return 0; +/* code for increment equal to 1 + clean-up loop */ +L20: + m = *n % 5; + if (m == 0) { + goto L40; + } + i__2 = m; + for (i__ = 1; i__ <= i__2; ++i__) { + dx[i__] = *da * dx[i__]; +/* L30: */ + } + if (*n < 5) { + return 0; + } +L40: + mp1 = m + 1; + i__2 = *n; + for (i__ = mp1; i__ <= i__2; i__ += 5) { + dx[i__] = *da * dx[i__]; + dx[i__ + 1] = *da * dx[i__ + 1]; + dx[i__ + 2] = *da * dx[i__ + 2]; + dx[i__ + 3] = *da * dx[i__ + 3]; + dx[i__ + 4] = *da * dx[i__ + 4]; +/* L50: */ + } + return 0; +} /* dscal_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dswap.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dswap.c new file mode 100644 index 00000000..5aacf29b --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dswap.c @@ -0,0 +1,81 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dswap_(integer *n, doublereal *dx, integer *incx, + doublereal *dy, integer *incy) +{ + /* System generated locals */ + integer i__1; + /* Local variables */ + static integer i__, m; + static doublereal dtemp; + static integer ix, iy, mp1; +/* interchanges two vectors. + uses unrolled loops for increments equal one. + jack dongarra, linpack, 3/11/78. + modified 12/3/93, array(1) declarations changed to array(*) + Parameter adjustments */ + --dy; + --dx; + /* Function Body */ + if (*n <= 0) { + return 0; + } + if (*incx == 1 && *incy == 1) { + goto L20; + } +/* code for unequal increments or equal increments not equal + to 1 */ + ix = 1; + iy = 1; + if (*incx < 0) { + ix = (-(*n) + 1) * *incx + 1; + } + if (*incy < 0) { + iy = (-(*n) + 1) * *incy + 1; + } + i__1 = *n; + for (i__ = 1; i__ <= i__1; ++i__) { + dtemp = dx[ix]; + dx[ix] = dy[iy]; + dy[iy] = dtemp; + ix += *incx; + iy += *incy; +/* L10: */ + } + return 0; +/* code for both increments equal to 1 + clean-up loop */ +L20: + m = *n % 3; + if (m == 0) { + goto L40; + } + i__1 = m; + for (i__ = 1; i__ <= i__1; ++i__) { + dtemp = dx[i__]; + dx[i__] = dy[i__]; + dy[i__] = dtemp; +/* L30: */ + } + if (*n < 3) { + return 0; + } +L40: + mp1 = m + 1; + i__1 = *n; + for (i__ = mp1; i__ <= i__1; i__ += 3) { + dtemp = dx[i__]; + dx[i__] = dy[i__]; + dy[i__] = dtemp; + dtemp = dx[i__ + 1]; + dx[i__ + 1] = dy[i__ + 1]; + dy[i__ + 1] = dtemp; + dtemp = dx[i__ + 2]; + dx[i__ + 2] = dy[i__ + 2]; + dy[i__ + 2] = dtemp; +/* L50: */ + } + return 0; +} /* dswap_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/dtrsm.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dtrsm.c new file mode 100644 index 00000000..d178c1ed --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/dtrsm.c @@ -0,0 +1,404 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int dtrsm_(char *side, char *uplo, char *transa, char *diag, + integer *m, integer *n, doublereal *alpha, doublereal *a, integer * + lda, doublereal *b, integer *ldb) +{ + /* System generated locals */ + integer a_dim1, a_offset, b_dim1, b_offset, i__1, i__2, i__3; + /* Local variables */ + static integer info; + static doublereal temp; + static integer i__, j, k; + static logical lside; + extern logical lsame_(char *, char *); + static integer nrowa; + static logical upper; + extern /* Subroutine */ int xerbla_(char *, integer *); + static logical nounit; +#define a_ref(a_1,a_2) a[(a_2)*a_dim1 + a_1] +#define b_ref(a_1,a_2) b[(a_2)*b_dim1 + a_1] +/* Purpose + ======= + DTRSM solves one of the matrix equations + op( A )*X = alpha*B, or X*op( A ) = alpha*B, + where alpha is a scalar, X and B are m by n matrices, A is a unit, or + non-unit, upper or lower triangular matrix and op( A ) is one of + op( A ) = A or op( A ) = A'. + The matrix X is overwritten on B. + Parameters + ========== + SIDE - CHARACTER*1. + On entry, SIDE specifies whether op( A ) appears on the left + or right of X as follows: + SIDE = 'L' or 'l' op( A )*X = alpha*B. + SIDE = 'R' or 'r' X*op( A ) = alpha*B. + Unchanged on exit. + UPLO - CHARACTER*1. + On entry, UPLO specifies whether the matrix A is an upper or + lower triangular matrix as follows: + UPLO = 'U' or 'u' A is an upper triangular matrix. + UPLO = 'L' or 'l' A is a lower triangular matrix. + Unchanged on exit. + TRANSA - CHARACTER*1. + On entry, TRANSA specifies the form of op( A ) to be used in + the matrix multiplication as follows: + TRANSA = 'N' or 'n' op( A ) = A. + TRANSA = 'T' or 't' op( A ) = A'. + TRANSA = 'C' or 'c' op( A ) = A'. + Unchanged on exit. + DIAG - CHARACTER*1. + On entry, DIAG specifies whether or not A is unit triangular + as follows: + DIAG = 'U' or 'u' A is assumed to be unit triangular. + DIAG = 'N' or 'n' A is not assumed to be unit + triangular. + Unchanged on exit. + M - INTEGER. + On entry, M specifies the number of rows of B. M must be at + least zero. + Unchanged on exit. + N - INTEGER. + On entry, N specifies the number of columns of B. N must be + at least zero. + Unchanged on exit. + ALPHA - DOUBLE PRECISION. + On entry, ALPHA specifies the scalar alpha. When alpha is + zero then A is not referenced and B need not be set before + entry. + Unchanged on exit. + A - DOUBLE PRECISION array of DIMENSION ( LDA, k ), where k is m + when SIDE = 'L' or 'l' and is n when SIDE = 'R' or 'r'. + Before entry with UPLO = 'U' or 'u', the leading k by k + upper triangular part of the array A must contain the upper + triangular matrix and the strictly lower triangular part of + A is not referenced. + Before entry with UPLO = 'L' or 'l', the leading k by k + lower triangular part of the array A must contain the lower + triangular matrix and the strictly upper triangular part of + A is not referenced. + Note that when DIAG = 'U' or 'u', the diagonal elements of + A are not referenced either, but are assumed to be unity. + Unchanged on exit. + LDA - INTEGER. + On entry, LDA specifies the first dimension of A as declared + in the calling (sub) program. When SIDE = 'L' or 'l' then + LDA must be at least max( 1, m ), when SIDE = 'R' or 'r' + then LDA must be at least max( 1, n ). + Unchanged on exit. + B - DOUBLE PRECISION array of DIMENSION ( LDB, n ). + Before entry, the leading m by n part of the array B must + contain the right-hand side matrix B, and on exit is + overwritten by the solution matrix X. + LDB - INTEGER. + On entry, LDB specifies the first dimension of B as declared + in the calling (sub) program. LDB must be at least + max( 1, m ). + Unchanged on exit. + Level 3 Blas routine. + -- Written on 8-February-1989. + Jack Dongarra, Argonne National Laboratory. + Iain Duff, AERE Harwell. + Jeremy Du Croz, Numerical Algorithms Group Ltd. + Sven Hammarling, Numerical Algorithms Group Ltd. + Test the input parameters. + Parameter adjustments */ + a_dim1 = *lda; + a_offset = 1 + a_dim1 * 1; + a -= a_offset; + b_dim1 = *ldb; + b_offset = 1 + b_dim1 * 1; + b -= b_offset; + /* Function Body */ + lside = lsame_(side, "L"); + if (lside) { + nrowa = *m; + } else { + nrowa = *n; + } + nounit = lsame_(diag, "N"); + upper = lsame_(uplo, "U"); + info = 0; + if (! lside && ! lsame_(side, "R")) { + info = 1; + } else if (! upper && ! lsame_(uplo, "L")) { + info = 2; + } else if (! lsame_(transa, "N") && ! lsame_(transa, + "T") && ! lsame_(transa, "C")) { + info = 3; + } else if (! lsame_(diag, "U") && ! lsame_(diag, + "N")) { + info = 4; + } else if (*m < 0) { + info = 5; + } else if (*n < 0) { + info = 6; + } else if (*lda < max(1,nrowa)) { + info = 9; + } else if (*ldb < max(1,*m)) { + info = 11; + } + if (info != 0) { + xerbla_("DTRSM ", &info); + return 0; + } +/* Quick return if possible. */ + if (*n == 0) { + return 0; + } +/* And when alpha.eq.zero. */ + if (*alpha == 0.) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = 0.; +/* L10: */ + } +/* L20: */ + } + return 0; + } +/* Start the operations. */ + if (lside) { + if (lsame_(transa, "N")) { +/* Form B := alpha*inv( A )*B. */ + if (upper) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (*alpha != 1.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = *alpha * b_ref(i__, j); +/* L30: */ + } + } + for (k = *m; k >= 1; --k) { + if (b_ref(k, j) != 0.) { + if (nounit) { + b_ref(k, j) = b_ref(k, j) / a_ref(k, k); + } + i__2 = k - 1; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - b_ref(k, j) * + a_ref(i__, k); +/* L40: */ + } + } +/* L50: */ + } +/* L60: */ + } + } else { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (*alpha != 1.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = *alpha * b_ref(i__, j); +/* L70: */ + } + } + i__2 = *m; + for (k = 1; k <= i__2; ++k) { + if (b_ref(k, j) != 0.) { + if (nounit) { + b_ref(k, j) = b_ref(k, j) / a_ref(k, k); + } + i__3 = *m; + for (i__ = k + 1; i__ <= i__3; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - b_ref(k, j) * + a_ref(i__, k); +/* L80: */ + } + } +/* L90: */ + } +/* L100: */ + } + } + } else { +/* Form B := alpha*inv( A' )*B. */ + if (upper) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp = *alpha * b_ref(i__, j); + i__3 = i__ - 1; + for (k = 1; k <= i__3; ++k) { + temp -= a_ref(k, i__) * b_ref(k, j); +/* L110: */ + } + if (nounit) { + temp /= a_ref(i__, i__); + } + b_ref(i__, j) = temp; +/* L120: */ + } +/* L130: */ + } + } else { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + for (i__ = *m; i__ >= 1; --i__) { + temp = *alpha * b_ref(i__, j); + i__2 = *m; + for (k = i__ + 1; k <= i__2; ++k) { + temp -= a_ref(k, i__) * b_ref(k, j); +/* L140: */ + } + if (nounit) { + temp /= a_ref(i__, i__); + } + b_ref(i__, j) = temp; +/* L150: */ + } +/* L160: */ + } + } + } + } else { + if (lsame_(transa, "N")) { +/* Form B := alpha*B*inv( A ). */ + if (upper) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (*alpha != 1.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = *alpha * b_ref(i__, j); +/* L170: */ + } + } + i__2 = j - 1; + for (k = 1; k <= i__2; ++k) { + if (a_ref(k, j) != 0.) { + i__3 = *m; + for (i__ = 1; i__ <= i__3; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - a_ref(k, j) * + b_ref(i__, k); +/* L180: */ + } + } +/* L190: */ + } + if (nounit) { + temp = 1. / a_ref(j, j); + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = temp * b_ref(i__, j); +/* L200: */ + } + } +/* L210: */ + } + } else { + for (j = *n; j >= 1; --j) { + if (*alpha != 1.) { + i__1 = *m; + for (i__ = 1; i__ <= i__1; ++i__) { + b_ref(i__, j) = *alpha * b_ref(i__, j); +/* L220: */ + } + } + i__1 = *n; + for (k = j + 1; k <= i__1; ++k) { + if (a_ref(k, j) != 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - a_ref(k, j) * + b_ref(i__, k); +/* L230: */ + } + } +/* L240: */ + } + if (nounit) { + temp = 1. / a_ref(j, j); + i__1 = *m; + for (i__ = 1; i__ <= i__1; ++i__) { + b_ref(i__, j) = temp * b_ref(i__, j); +/* L250: */ + } + } +/* L260: */ + } + } + } else { +/* Form B := alpha*B*inv( A' ). */ + if (upper) { + for (k = *n; k >= 1; --k) { + if (nounit) { + temp = 1. / a_ref(k, k); + i__1 = *m; + for (i__ = 1; i__ <= i__1; ++i__) { + b_ref(i__, k) = temp * b_ref(i__, k); +/* L270: */ + } + } + i__1 = k - 1; + for (j = 1; j <= i__1; ++j) { + if (a_ref(j, k) != 0.) { + temp = a_ref(j, k); + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - temp * b_ref( + i__, k); +/* L280: */ + } + } +/* L290: */ + } + if (*alpha != 1.) { + i__1 = *m; + for (i__ = 1; i__ <= i__1; ++i__) { + b_ref(i__, k) = *alpha * b_ref(i__, k); +/* L300: */ + } + } +/* L310: */ + } + } else { + i__1 = *n; + for (k = 1; k <= i__1; ++k) { + if (nounit) { + temp = 1. / a_ref(k, k); + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, k) = temp * b_ref(i__, k); +/* L320: */ + } + } + i__2 = *n; + for (j = k + 1; j <= i__2; ++j) { + if (a_ref(j, k) != 0.) { + temp = a_ref(j, k); + i__3 = *m; + for (i__ = 1; i__ <= i__3; ++i__) { + b_ref(i__, j) = b_ref(i__, j) - temp * b_ref( + i__, k); +/* L330: */ + } + } +/* L340: */ + } + if (*alpha != 1.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + b_ref(i__, k) = *alpha * b_ref(i__, k); +/* L350: */ + } + } +/* L360: */ + } + } + } + } + return 0; +/* End of DTRSM . */ +} /* dtrsm_ */ +#undef b_ref +#undef a_ref + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/endfile.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/endfile.c new file mode 100644 index 00000000..d309fc26 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/endfile.c @@ -0,0 +1,102 @@ +#include "f2c.h" +#include "fio.h" + +#undef abs +#undef min +#undef max +#include "stdlib.h" +#include "string.h" + +extern char *f__r_mode[], *f__w_mode[]; + +integer f_end(alist *a) +{ + unit *b; + FILE *tf; + + if(a->aunit>=MXUNIT || a->aunit<0) err(a->aerr,101,"endfile"); + b = &f__units[a->aunit]; + if(b->ufd==NULL) { + char nbuf[10]; + sprintf(nbuf,"fort.%ld",a->aunit); + if (tf = fopen(nbuf, f__w_mode[0])) + fclose(tf); + return(0); + } + b->uend=1; + return(b->useek ? t_runc(a) : 0); +} + +static int copy(FILE *from, long len, FILE *to) +{ + int len1; + char buf[BUFSIZ]; + + while(fread(buf, len1 = len > BUFSIZ ? BUFSIZ : (int)len, 1, from)) { + if (!fwrite(buf, len1, 1, to)) + return 1; + if ((len -= len1) <= 0) + break; + } + return 0; + } + +int t_runc(alist *a) +{ + long loc, len; + unit *b; + FILE *bf, *tf; + int rc = 0; + + b = &f__units[a->aunit]; + if(b->url) + return(0); /*don't truncate direct files*/ + loc=ftell(bf = b->ufd); + fseek(bf,0L,SEEK_END); + len=ftell(bf); + if (loc >= len || b->useek == 0 || b->ufnm == NULL) + return(0); + fclose(b->ufd); + if (!loc) { + if (!(bf = fopen(b->ufnm, f__w_mode[b->ufmt]))) + rc = 1; + if (b->uwrt) + b->uwrt = 1; + goto done; + } + if (!(bf = fopen(b->ufnm, f__r_mode[0])) + || !(tf = tmpfile())) { +#ifdef NON_UNIX_STDIO + bad: +#endif + rc = 1; + goto done; + } + if (copy(bf, loc, tf)) { + bad1: + rc = 1; + goto done1; + } + if (!(bf = freopen(b->ufnm, f__w_mode[0], bf))) + goto bad1; + rewind(tf); + if (copy(tf, loc, bf)) + goto bad1; + b->urw = 2; +#ifdef NON_UNIX_STDIO + if (b->ufmt) { + fclose(bf); + if (!(bf = fopen(b->ufnm, f__w_mode[3]))) + goto bad; + fseek(bf,0L,SEEK_END); + b->urw = 3; + } +#endif +done1: + fclose(tf); +done: + f__cf = b->ufd = bf; + if (rc) + err(a->aerr,111,"endfile"); + return 0; + } diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/err.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/err.c new file mode 100644 index 00000000..90af7cae --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/err.c @@ -0,0 +1,240 @@ +#ifndef NON_UNIX_STDIO +#define _INCLUDE_POSIX_SOURCE /* for HP-UX */ +#define _INCLUDE_XOPEN_SOURCE /* for HP-UX */ +#include "sys/types.h" +#include "sys/stat.h" +#endif +#include "f2c.h" +#undef abs +#undef min +#undef max +#include "stdlib.h" +#include "fio.h" +#include "fmt.h" /* for struct syl */ + +/*global definitions*/ +unit f__units[MXUNIT]; /*unit table*/ +flag f__init; /*0 on entry, 1 after initializations*/ +cilist *f__elist; /*active external io list*/ +icilist *f__svic; /*active internal io list*/ +flag f__reading; /*1 if reading, 0 if writing*/ +flag f__cplus,f__cblank; +char *f__fmtbuf; +flag f__external; /*1 if external io, 0 if internal */ +int (*f__getn)(void); /* for formatted input */ +void (*f__putn)(int); /* for formatted output */ +int (*f__doed)(struct syl*, char*, ftnlen),(*f__doned)(struct syl*); +int (*f__dorevert)(void),(*f__donewrec)(void),(*f__doend)(void); +flag f__sequential; /*1 if sequential io, 0 if direct*/ +flag f__formatted; /*1 if formatted io, 0 if unformatted*/ +FILE *f__cf; /*current file*/ +unit *f__curunit; /*current unit*/ +int f__recpos; /*place in current record*/ +int f__cursor, f__hiwater, f__scale; +char *f__icptr; + +/*error messages*/ +char *F_err[] = +{ + "error in format", /* 100 */ + "illegal unit number", /* 101 */ + "formatted io not allowed", /* 102 */ + "unformatted io not allowed", /* 103 */ + "direct io not allowed", /* 104 */ + "sequential io not allowed", /* 105 */ + "can't backspace file", /* 106 */ + "null file name", /* 107 */ + "can't stat file", /* 108 */ + "unit not connected", /* 109 */ + "off end of record", /* 110 */ + "truncation failed in endfile", /* 111 */ + "incomprehensible list input", /* 112 */ + "out of free space", /* 113 */ + "unit not connected", /* 114 */ + "read unexpected character", /* 115 */ + "bad logical input field", /* 116 */ + "bad variable type", /* 117 */ + "bad namelist name", /* 118 */ + "variable not in namelist", /* 119 */ + "no end record", /* 120 */ + "variable count incorrect", /* 121 */ + "subscript for scalar variable", /* 122 */ + "invalid array section", /* 123 */ + "substring out of bounds", /* 124 */ + "subscript out of bounds", /* 125 */ + "can't read file", /* 126 */ + "can't write file", /* 127 */ + "'new' file exists", /* 128 */ + "can't append to file", /* 129 */ + "non-positive record number" /* 130 */ +}; +#define MAXERR (sizeof(F_err)/sizeof(char *)+100) + +int f__canseek(FILE *f) /*SYSDEP*/ +{ +#ifdef NON_UNIX_STDIO + return !isatty(fileno(f)); +#else + struct stat x; + + if (fstat(fileno(f),&x) < 0) + return(0); +#ifdef S_IFMT + switch(x.st_mode & S_IFMT) { + case S_IFDIR: + case S_IFREG: + if(x.st_nlink > 0) /* !pipe */ + return(1); + else + return(0); + case S_IFCHR: + if(isatty(fileno(f))) + return(0); + return(1); +#ifdef S_IFBLK + case S_IFBLK: + return(1); +#endif + } +#else +#ifdef S_ISDIR + /* POSIX version */ + if (S_ISREG(x.st_mode) || S_ISDIR(x.st_mode)) { + if(x.st_nlink > 0) /* !pipe */ + return(1); + else + return(0); + } + if (S_ISCHR(x.st_mode)) { + if(isatty(fileno(f))) + return(0); + return(1); + } + if (S_ISBLK(x.st_mode)) + return(1); +#else + Help! How does fstat work on this system? +#endif +#endif + return(0); /* who knows what it is? */ +#endif +} + +void f__fatal(int n, char *s) +{ + if(n<100 && n>=0) perror(s); /*SYSDEP*/ + else if(n >= (int)MAXERR || n < -1) + { fprintf(stderr,"%s: illegal error number %d\n",s,n); + } + else if(n == -1) fprintf(stderr,"%s: end of file\n",s); + else + fprintf(stderr,"%s: %s\n",s,F_err[n-100]); + if (f__curunit) { + fprintf(stderr,"apparent state: unit %d ", + (int)(f__curunit-f__units)); + fprintf(stderr, f__curunit->ufnm ? "named %s\n" : "(unnamed)\n", + f__curunit->ufnm); + } + else + fprintf(stderr,"apparent state: internal I/O\n"); + if (f__fmtbuf) + fprintf(stderr,"last format: %s\n",f__fmtbuf); + fprintf(stderr,"lately %s %s %s %s",f__reading?"reading":"writing", + f__sequential?"sequential":"direct",f__formatted?"formatted":"unformatted", + f__external?"external":"internal"); + sig_die(" IO", 1); +} +/*initialization routine*/ + VOID +f_init(Void) +{ unit *p; + + f__init=1; + p= &f__units[0]; + p->ufd=stderr; + p->useek=f__canseek(stderr); + p->ufmt=1; + p->uwrt=1; + p = &f__units[5]; + p->ufd=stdin; + p->useek=f__canseek(stdin); + p->ufmt=1; + p->uwrt=0; + p= &f__units[6]; + p->ufd=stdout; + p->useek=f__canseek(stdout); + p->ufmt=1; + p->uwrt=1; +} + +int f__nowreading(unit *x) +{ + long loc; + int ufmt, urw; + extern char *f__r_mode[], *f__w_mode[]; + + if (x->urw & 1) + goto done; + if (!x->ufnm) + goto cantread; + ufmt = x->url ? 0 : x->ufmt; + loc = ftell(x->ufd); + urw = 3; + if (!freopen(x->ufnm, f__w_mode[ufmt|2], x->ufd)) { + urw = 1; + if(!freopen(x->ufnm, f__r_mode[ufmt], x->ufd)) { + cantread: + errno = 126; + return 1; + } + } + fseek(x->ufd,loc,SEEK_SET); + x->urw = urw; + done: + x->uwrt = 0; + return 0; +} + +int f__nowwriting(unit *x) +{ + long loc; + int ufmt; + extern char *f__w_mode[]; + + if (x->urw & 2) + goto done; + if (!x->ufnm) + goto cantwrite; + ufmt = x->url ? 0 : x->ufmt; + if (x->uwrt == 3) { /* just did write, rewind */ + if (!(f__cf = x->ufd = + freopen(x->ufnm,f__w_mode[ufmt],x->ufd))) + goto cantwrite; + x->urw = 2; + } + else { + loc=ftell(x->ufd); + if (!(f__cf = x->ufd = + freopen(x->ufnm, f__w_mode[ufmt |= 2], x->ufd))) + { + x->ufd = NULL; + cantwrite: + errno = 127; + return(1); + } + x->urw = 3; + fseek(x->ufd,loc,SEEK_SET); + } + done: + x->uwrt = 1; + return 0; +} + +int err__fl(int f, int m, char *s) +{ + if (!f) + f__fatal(m, s); + if (f__doend) + (*f__doend)(); + return errno = m; + } diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/f2c.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/f2c.h new file mode 100644 index 00000000..6514cd91 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/f2c.h @@ -0,0 +1,223 @@ +/* f2c.h -- Standard Fortran to C header file */ + +/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." + + - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ + +#ifndef F2C_INCLUDE +#define F2C_INCLUDE + +typedef long int integer; +typedef unsigned long uinteger; +typedef char *address; +typedef short int shortint; +typedef float real; +typedef double doublereal; +typedef struct { real r, i; } complex; +typedef struct { doublereal r, i; } doublecomplex; +typedef long int logical; +typedef short int shortlogical; +typedef char logical1; +typedef char integer1; +#if 0 /* Adjust for integer*8. */ +typedef long long longint; /* system-dependent */ +typedef unsigned long long ulongint; /* system-dependent */ +#define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b))) +#define qbit_set(a,b) ((a) | ((ulongint)1 << (b))) +#endif + +#define TRUE_ (1) +#define FALSE_ (0) + +/* Extern is for use with -E */ +#ifndef Extern +#define Extern extern +#endif + +/* I/O stuff */ + +#ifdef f2c_i2 +/* for -i2 */ +typedef short flag; +typedef short ftnlen; +typedef short ftnint; +#else +typedef long int flag; +typedef long int ftnlen; +typedef long int ftnint; +#endif + +/*external read, write*/ +typedef struct +{ flag cierr; + ftnint ciunit; + flag ciend; + char *cifmt; + ftnint cirec; +} cilist; + +/*internal read, write*/ +typedef struct +{ flag icierr; + char *iciunit; + flag iciend; + char *icifmt; + ftnint icirlen; + ftnint icirnum; +} icilist; + +/*open*/ +typedef struct +{ flag oerr; + ftnint ounit; + char *ofnm; + ftnlen ofnmlen; + char *osta; + char *oacc; + char *ofm; + ftnint orl; + char *oblnk; +} olist; + +/*close*/ +typedef struct +{ flag cerr; + ftnint cunit; + char *csta; +} cllist; + +/*rewind, backspace, endfile*/ +typedef struct +{ flag aerr; + ftnint aunit; +} alist; + +/* inquire */ +typedef struct +{ flag inerr; + ftnint inunit; + char *infile; + ftnlen infilen; + ftnint *inex; /*parameters in standard's order*/ + ftnint *inopen; + ftnint *innum; + ftnint *innamed; + char *inname; + ftnlen innamlen; + char *inacc; + ftnlen inacclen; + char *inseq; + ftnlen inseqlen; + char *indir; + ftnlen indirlen; + char *infmt; + ftnlen infmtlen; + char *inform; + ftnint informlen; + char *inunf; + ftnlen inunflen; + ftnint *inrecl; + ftnint *innrec; + char *inblank; + ftnlen inblanklen; +} inlist; + +#define VOID void + +union Multitype { /* for multiple entry points */ + integer1 g; + shortint h; + integer i; + /* longint j; */ + real r; + doublereal d; + complex c; + doublecomplex z; + }; + +typedef union Multitype Multitype; + +/*typedef long int Long;*/ /* No longer used; formerly in Namelist */ + +struct Vardesc { /* for Namelist */ + char *name; + char *addr; + ftnlen *dims; + int type; + }; +typedef struct Vardesc Vardesc; + +struct Namelist { + char *name; + Vardesc **vars; + int nvars; + }; +typedef struct Namelist Namelist; + +#define abs(x) ((x) >= 0 ? (x) : -(x)) +#define dabs(x) (doublereal)abs(x) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#define dmin(a,b) (doublereal)min(a,b) +#define dmax(a,b) (doublereal)max(a,b) +#define bit_test(a,b) ((a) >> (b) & 1) +#define bit_clear(a,b) ((a) & ~((uinteger)1 << (b))) +#define bit_set(a,b) ((a) | ((uinteger)1 << (b))) + +/* procedure parameter types for -A and -C++ */ + +#define F2C_proc_par_types 1 +#ifdef __cplusplus +typedef int /* Unknown procedure type */ (*U_fp)(...); +typedef shortint (*J_fp)(...); +typedef integer (*I_fp)(...); +typedef real (*R_fp)(...); +typedef doublereal (*D_fp)(...), (*E_fp)(...); +typedef /* Complex */ VOID (*C_fp)(...); +typedef /* Double Complex */ VOID (*Z_fp)(...); +typedef logical (*L_fp)(...); +typedef shortlogical (*K_fp)(...); +typedef /* Character */ VOID (*H_fp)(...); +typedef /* Subroutine */ int (*S_fp)(...); +#else +typedef int /* Unknown procedure type */ (*U_fp)(); +typedef shortint (*J_fp)(); +typedef integer (*I_fp)(); +typedef real (*R_fp)(); +typedef doublereal (*D_fp)(), (*E_fp)(); +typedef /* Complex */ VOID (*C_fp)(); +typedef /* Double Complex */ VOID (*Z_fp)(); +typedef logical (*L_fp)(); +typedef shortlogical (*K_fp)(); +typedef /* Character */ VOID (*H_fp)(); +typedef /* Subroutine */ int (*S_fp)(); +#endif +/* E_fp is for real functions when -R is not specified */ +typedef VOID C_f; /* complex function */ +typedef VOID H_f; /* character function */ +typedef VOID Z_f; /* double complex function */ +typedef doublereal E_f; /* real function with -R not specified */ + +/* undef any lower-case symbols that your C compiler predefines, e.g.: */ + +#ifndef Skip_f2c_Undefs +#undef cray +#undef gcos +#undef mc68010 +#undef mc68020 +#undef mips +#undef pdp11 +#undef sgi +#undef sparc +#undef sun +#undef sun2 +#undef sun3 +#undef sun4 +#undef u370 +#undef u3b +#undef u3b2 +#undef u3b5 +#undef unix +#undef vax +#endif +#endif diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/fio.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fio.h new file mode 100644 index 00000000..b1632d50 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fio.h @@ -0,0 +1,96 @@ +#include "stdio.h" +#include "errno.h" +#ifndef NULL +/* ANSI C */ +#include "stddef.h" +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifdef MSDOS +#ifndef NON_UNIX_STDIO +#define NON_UNIX_STDIO +#endif +#endif + +#ifdef UIOLEN_int +typedef int uiolen; +#else +typedef long uiolen; +#endif + +/*units*/ +typedef struct +{ FILE *ufd; /*0=unconnected*/ + char *ufnm; +#ifndef MSDOS + long uinode; + int udev; +#endif + int url; /*0=sequential*/ + flag useek; /*true=can backspace, use dir, ...*/ + flag ufmt; + flag urw; /* (1 for can read) | (2 for can write) */ + flag ublnk; + flag uend; + flag uwrt; /*last io was write*/ + flag uscrtch; +} unit; + +extern flag f__init; +extern cilist *f__elist; /*active external io list*/ +extern flag f__reading,f__external,f__sequential,f__formatted; +#undef Void +#define Void void +#ifdef __cplusplus +extern "C" { +#endif +extern int (*f__getn)(void); /* for formatted input */ +extern void (*f__putn)(int); /* for formatted output */ +extern void x_putc(int); +extern long f__inode(char*,int*); +extern void sig_die(char*,int); +extern void f__fatal(int,char*); +extern int t_runc(alist*); +extern int f__nowreading(unit*), f__nowwriting(unit*); +extern int fk_open(int,int,ftnint); +extern int en_fio(void); +extern void f_init(void); +extern int (*f__donewrec)(void), t_putc(int), x_wSL(void); +extern void b_char(char*,char*,ftnlen), g_char(char*,ftnlen,char*); +extern int c_sfe(cilist*), z_rnew(void); +extern int isatty(int); +extern int err__fl(int,int,char*); +extern int xrd_SL(void); +extern int f__putbuf(int); +#ifdef __cplusplus + } +#endif +extern int (*f__doend)(Void); +extern FILE *f__cf; /*current file*/ +extern unit *f__curunit; /*current unit*/ +extern unit f__units[]; +#define err(f,m,s) {if(f) errno= m; else f__fatal(m,s); return(m);} +#define errfl(f,m,s) return err__fl((int)f,m,s) + +/*Table sizes*/ +#define MXUNIT 100 + +extern int f__recpos; /*position in current record*/ +extern int f__cursor; /* offset to move to */ +extern int f__hiwater; /* so TL doesn't confuse us */ + +#define WRITE 1 +#define READ 2 +#define SEQ 3 +#define DIR 4 +#define FMT 5 +#define UNF 6 +#define EXT 7 +#define INT 8 + +#define buf_end(x) (x->_flag & _IONBF ? x->_ptr : x->_base + BUFSIZ) diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.c new file mode 100644 index 00000000..a4fcbc38 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.c @@ -0,0 +1,470 @@ +#include "f2c.h" +#include "fio.h" +#include "fmt.h" +#define skip(s) while(*s==' ') s++ +#ifdef interdata +#define SYLMX 300 +#endif +#ifdef pdp11 +#define SYLMX 300 +#endif +#ifdef vax +#define SYLMX 300 +#endif +#ifndef SYLMX +#define SYLMX 300 +#endif +#define GLITCH '\2' + /* special quote character for stu */ +extern int f__cursor,f__scale; +extern flag f__cblank,f__cplus; /*blanks in I and compulsory plus*/ +static struct syl f__syl[SYLMX]; +int f__parenlvl,f__pc,f__revloc; + +static char *ap_end(char *s) +{ char quote; + quote= *s++; + for(;*s;s++) + { if(*s!=quote) continue; + if(*++s!=quote) return(s); + } + if(f__elist->cierr) { + errno = 100; + return(NULL); + } + f__fatal(100, "bad string"); + /*NOTREACHED*/ return 0; +} + +static int op_gen(int a, int b, int c, int d) +{ struct syl *p= &f__syl[f__pc]; + if(f__pc>=SYLMX) + { fprintf(stderr,"format too complicated:\n"); + sig_die(f__fmtbuf, 1); + } + p->op=a; + p->p1=b; + p->p2.i[0]=c; + p->p2.i[1]=d; + return(f__pc++); +} + +static char *f_list(char*); +static char *gt_num(char *s, int *n, int n1) +{ int m=0,f__cnt=0; + char c; + for(c= *s;;c = *s) + { if(c==' ') + { s++; + continue; + } + if(c>'9' || c<'0') break; + m=10*m+c-'0'; + f__cnt++; + s++; + } + if(f__cnt==0) { + if (!n1) + s = 0; + *n=n1; + } + else *n=m; + return(s); +} + +static char *f_s(char *s, int curloc) +{ + skip(s); + if(*s++!='(') + { + return(NULL); + } + if(f__parenlvl++ ==1) f__revloc=curloc; + if(op_gen(RET1,curloc,0,0)<0 || + (s=f_list(s))==NULL) + { + return(NULL); + } + skip(s); + return(s); +} + +static int ne_d(char *s, char **p) +{ int n,x,sign=0; + struct syl *sp; + switch(*s) + { + default: + return(0); + case ':': (void) op_gen(COLON,0,0,0); break; + case '$': + (void) op_gen(NONL, 0, 0, 0); break; + case 'B': + case 'b': + if(*++s=='z' || *s == 'Z') (void) op_gen(BZ,0,0,0); + else (void) op_gen(BN,0,0,0); + break; + case 'S': + case 's': + if(*(s+1)=='s' || *(s+1) == 'S') + { x=SS; + s++; + } + else if(*(s+1)=='p' || *(s+1) == 'P') + { x=SP; + s++; + } + else x=S; + (void) op_gen(x,0,0,0); + break; + case '/': (void) op_gen(SLASH,0,0,0); break; + case '-': sign=1; + case '+': s++; /*OUTRAGEOUS CODING TRICK*/ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (!(s=gt_num(s,&n,0))) { + bad: *p = 0; + return 1; + } + switch(*s) + { + default: + return(0); + case 'P': + case 'p': if(sign) n= -n; (void) op_gen(P,n,0,0); break; + case 'X': + case 'x': (void) op_gen(X,n,0,0); break; + case 'H': + case 'h': + sp = &f__syl[op_gen(H,n,0,0)]; + sp->p2.s = s + 1; + s+=n; + break; + } + break; + case GLITCH: + case '"': + case '\'': + sp = &f__syl[op_gen(APOS,0,0,0)]; + sp->p2.s = s; + if((*p = ap_end(s)) == NULL) + return(0); + return(1); + case 'T': + case 't': + if(*(s+1)=='l' || *(s+1) == 'L') + { x=TL; + s++; + } + else if(*(s+1)=='r'|| *(s+1) == 'R') + { x=TR; + s++; + } + else x=T; + if (!(s=gt_num(s+1,&n,0))) + goto bad; + s--; + (void) op_gen(x,n,0,0); + break; + case 'X': + case 'x': (void) op_gen(X,1,0,0); break; + case 'P': + case 'p': (void) op_gen(P,1,0,0); break; + } + s++; + *p=s; + return(1); +} + +static int e_d(char *s, char **p) +{ int i,im,n,w,d,e,found=0,x=0; + char *sv=s; + s=gt_num(s,&n,1); + (void) op_gen(STACK,n,0,0); + switch(*s++) + { + default: break; + case 'E': + case 'e': x=1; + case 'G': + case 'g': + found=1; + if (!(s=gt_num(s,&w,0))) { + bad: + *p = 0; + return 1; + } + if(w==0) break; + if(*s=='.') { + if (!(s=gt_num(s+1,&d,0))) + goto bad; + } + else d=0; + if(*s!='E' && *s != 'e') + (void) op_gen(x==1?E:G,w,d,0); /* default is Ew.dE2 */ + else { + if (!(s=gt_num(s+1,&e,0))) + goto bad; + (void) op_gen(x==1?EE:GE,w,d,e); + } + break; + case 'O': + case 'o': + i = O; + im = OM; + goto finish_I; + case 'Z': + case 'z': + i = Z; + im = ZM; + goto finish_I; + case 'L': + case 'l': + found=1; + if (!(s=gt_num(s,&w,0))) + goto bad; + if(w==0) break; + (void) op_gen(L,w,0,0); + break; + case 'A': + case 'a': + found=1; + skip(s); + if(*s>='0' && *s<='9') + { s=gt_num(s,&w,1); + if(w==0) break; + (void) op_gen(AW,w,0,0); + break; + } + (void) op_gen(A,0,0,0); + break; + case 'F': + case 'f': + if (!(s=gt_num(s,&w,0))) + goto bad; + found=1; + if(w==0) break; + if(*s=='.') { + if (!(s=gt_num(s+1,&d,0))) + goto bad; + } + else d=0; + (void) op_gen(F,w,d,0); + break; + case 'D': + case 'd': + found=1; + if (!(s=gt_num(s,&w,0))) + goto bad; + if(w==0) break; + if(*s=='.') { + if (!(s=gt_num(s+1,&d,0))) + goto bad; + } + else d=0; + (void) op_gen(D,w,d,0); + break; + case 'I': + case 'i': + i = I; + im = IM; + finish_I: + if (!(s=gt_num(s,&w,0))) + goto bad; + found=1; + if(w==0) break; + if(*s!='.') + { (void) op_gen(i,w,0,0); + break; + } + if (!(s=gt_num(s+1,&d,0))) + goto bad; + (void) op_gen(im,w,d,0); + break; + } + if(found==0) + { f__pc--; /*unSTACK*/ + *p=sv; + return(0); + } + *p=s; + return(1); +} + +static char *i_tem(char *s) +{ char *t; + int n,curloc; + if(*s==')') return(s); + if(ne_d(s,&t)) return(t); + if(e_d(s,&t)) return(t); + s=gt_num(s,&n,1); + if((curloc=op_gen(STACK,n,0,0))<0) return(NULL); + return(f_s(s,curloc)); +} + +static char *f_list(char *s) +{ + for(;*s!=0;) + { skip(s); + if((s=i_tem(s))==NULL) return(NULL); + skip(s); + if(*s==',') s++; + else if(*s==')') + { if(--f__parenlvl==0) + { + (void) op_gen(REVERT,f__revloc,0,0); + return(++s); + } + (void) op_gen(GOTO,0,0,0); + return(++s); + } + } + return(NULL); +} + +int pars_f(char *s) +{ + f__parenlvl=f__revloc=f__pc=0; + if(f_s(s,0) == NULL) + { + return(-1); + } + return(0); +} + +#define STKSZ 10 +int f__cnt[STKSZ],f__ret[STKSZ],f__cp,f__rp; +flag f__workdone, f__nonl; + +static int type_f(int n) +{ + switch(n) + { + default: + return(n); + case RET1: + return(RET1); + case REVERT: return(REVERT); + case GOTO: return(GOTO); + case STACK: return(STACK); + case X: + case SLASH: + case APOS: case H: + case T: case TL: case TR: + return(NED); + case F: + case I: + case IM: + case A: case AW: + case O: case OM: + case L: + case E: case EE: case D: + case G: case GE: + case Z: case ZM: + return(ED); + } +} + +integer do_fio(ftnint *number, char *ptr, ftnlen len) +{ struct syl *p; + int n,i; + for(i=0;i<*number;i++,ptr+=len) + { +loop: switch(type_f((p= &f__syl[f__pc])->op)) + { + default: + fprintf(stderr,"unknown code in do_fio: %d\n%s\n", + p->op,f__fmtbuf); + err(f__elist->cierr,100,"do_fio"); + case NED: + if((*f__doned)(p)) + { f__pc++; + goto loop; + } + f__pc++; + continue; + case ED: + if(f__cnt[f__cp]<=0) + { f__cp--; + f__pc++; + goto loop; + } + if(ptr==NULL) + return((*f__doend)()); + f__cnt[f__cp]--; + f__workdone=1; + if((n=(*f__doed)(p,ptr,len))>0) + errfl(f__elist->cierr,errno,"fmt"); + if(n<0) + err(f__elist->ciend,(EOF),"fmt"); + continue; + case STACK: + f__cnt[++f__cp]=p->p1; + f__pc++; + goto loop; + case RET1: + f__ret[++f__rp]=p->p1; + f__pc++; + goto loop; + case GOTO: + if(--f__cnt[f__cp]<=0) + { f__cp--; + f__rp--; + f__pc++; + goto loop; + } + f__pc=1+f__ret[f__rp--]; + goto loop; + case REVERT: + f__rp=f__cp=0; + f__pc = p->p1; + if(ptr==NULL) + return((*f__doend)()); + if(!f__workdone) return(0); + if((n=(*f__dorevert)()) != 0) return(n); + goto loop; + case COLON: + if(ptr==NULL) + return((*f__doend)()); + f__pc++; + goto loop; + case NONL: + f__nonl = 1; + f__pc++; + goto loop; + case S: + case SS: + f__cplus=0; + f__pc++; + goto loop; + case SP: + f__cplus = 1; + f__pc++; + goto loop; + case P: f__scale=p->p1; + f__pc++; + goto loop; + case BN: + f__cblank=0; + f__pc++; + goto loop; + case BZ: + f__cblank=1; + f__pc++; + goto loop; + } + } + return(0); +} + +int en_fio(Void) +{ ftnint one=1; + return(do_fio(&one,(char *)NULL,(ftnint)0)); +} + + VOID +fmt_bg(Void) +{ + f__workdone=f__cp=f__rp=f__pc=f__cursor=0; + f__cnt[0]=f__ret[0]=0; +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.h new file mode 100644 index 00000000..b46bc9c5 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmt.h @@ -0,0 +1,86 @@ +struct syl +{ int op; + int p1; + union { int i[2]; char *s;} p2; + }; +#define RET1 1 +#define REVERT 2 +#define GOTO 3 +#define X 4 +#define SLASH 5 +#define STACK 6 +#define I 7 +#define ED 8 +#define NED 9 +#define IM 10 +#define APOS 11 +#define H 12 +#define TL 13 +#define TR 14 +#define T 15 +#define COLON 16 +#define S 17 +#define SP 18 +#define SS 19 +#define P 20 +#define BN 21 +#define BZ 22 +#define F 23 +#define E 24 +#define EE 25 +#define D 26 +#define G 27 +#define GE 28 +#define L 29 +#define A 30 +#define AW 31 +#define O 32 +#define NONL 33 +#define OM 34 +#define Z 35 +#define ZM 36 +extern int f__pc,f__parenlvl,f__revloc; +typedef union +{ real pf; + doublereal pd; +} ufloat; +typedef union +{ short is; + signed char ic; + integer il; +#ifdef Allow_TYQUAD + longint ili; +#endif +} Uint; +#ifdef __cplusplus +extern "C" { +#endif +extern int (*f__doed)(struct syl*, char*, ftnlen),(*f__doned)(struct syl*); +extern int (*f__dorevert)(void); +extern void fmt_bg(void); +extern int pars_f(char*); +extern int rd_ed(struct syl*, char*, ftnlen),rd_ned(struct syl*); +extern int w_ed(struct syl*, char*, ftnlen),w_ned(struct syl*); +extern int wrt_E(ufloat*, int, int, int, ftnlen); +extern int wrt_F(ufloat*, int, int, ftnlen); +extern int wrt_L(Uint*, int, ftnlen); +#ifdef __cplusplus + } +#endif +extern flag f__cblank,f__cplus,f__workdone, f__nonl; +extern char *f__fmtbuf; +extern int f__scale; +#define GET(x) if((x=(*f__getn)())<0) return(x) +#define VAL(x) (x!='\n'?x:' ') +#define PUT(x) (*f__putn)(x) +extern int f__cursor; + +#undef TYQUAD +#ifndef Allow_TYQUAD +#undef longint +#define longint long +#else +#define TYQUAD 14 +#endif + +extern char *f__icvt(longint, int*, int*, int); diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmtlib.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmtlib.c new file mode 100644 index 00000000..8343337b --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fmtlib.c @@ -0,0 +1,40 @@ +/* @(#)fmtlib.c 1.2 */ +#define MAXINTLENGTH 23 + +#include "f2c.h" +#ifndef Allow_TYQUAD +#undef longint +#define longint long +#undef ulongint +#define ulongint unsigned long +#endif + +char *f__icvt(longint value, int *ndigit, int *sign, int base) +{ + static char buf[MAXINTLENGTH+1]; + int i; + ulongint uvalue; + + if(value > 0) { + uvalue = value; + *sign = 0; + } + else if (value < 0) { + uvalue = -value; + *sign = 1; + } + else { + *sign = 0; + *ndigit = 1; + buf[MAXINTLENGTH-1] = '0'; + return &buf[MAXINTLENGTH-1]; + } + i = MAXINTLENGTH; + do { + buf[--i] = (uvalue%base) + '0'; + uvalue /= base; + } + while(uvalue > 0); + *ndigit = MAXINTLENGTH - i; + return &buf[i]; + } diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/fp.h b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fp.h new file mode 100644 index 00000000..40743d79 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/fp.h @@ -0,0 +1,28 @@ +#define FMAX 40 +#define EXPMAXDIGS 8 +#define EXPMAX 99999999 +/* FMAX = max number of nonzero digits passed to atof() */ +/* EXPMAX = 10^EXPMAXDIGS - 1 = largest allowed exponent absolute value */ + +#ifdef V10 /* Research Tenth-Edition Unix */ +#include "local.h" +#endif + +/* MAXFRACDIGS and MAXINTDIGS are for wrt_F -- bounds (not necessarily + tight) on the maximum number of digits to the right and left of + * the decimal point. + */ + +#ifdef VAX +#define MAXFRACDIGS 56 +#define MAXINTDIGS 38 +#else +#ifdef CRAY +#define MAXFRACDIGS 9880 +#define MAXINTDIGS 9864 +#else +/* values that suffice for IEEE double */ +#define MAXFRACDIGS 344 +#define MAXINTDIGS 308 +#endif +#endif diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/idamax.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/idamax.c new file mode 100644 index 00000000..2cc54813 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/idamax.c @@ -0,0 +1,61 @@ +#include "blaswrap.h" +#include "f2c.h" + +integer idamax_(integer *n, doublereal *dx, integer *incx) +{ + /* System generated locals */ + integer ret_val, i__1; + doublereal d__1; + /* Local variables */ + static doublereal dmax__; + static integer i__, ix; +/* finds the index of element having max. absolute value. + jack dongarra, linpack, 3/11/78. + modified 3/93 to return if incx .le. 0. + modified 12/3/93, array(1) declarations changed to array(*) + Parameter adjustments */ + --dx; + /* Function Body */ + ret_val = 0; + if (*n < 1 || *incx <= 0) { + return ret_val; + } + ret_val = 1; + if (*n == 1) { + return ret_val; + } + if (*incx == 1) { + goto L20; + } +/* code for increment not equal to 1 */ + ix = 1; + dmax__ = abs(dx[1]); + ix += *incx; + i__1 = *n; + for (i__ = 2; i__ <= i__1; ++i__) { + if ((d__1 = dx[ix], abs(d__1)) <= dmax__) { + goto L5; + } + ret_val = i__; + dmax__ = (d__1 = dx[ix], abs(d__1)); +L5: + ix += *incx; +/* L10: */ + } + return ret_val; +/* code for increment equal to 1 */ +L20: + dmax__ = abs(dx[1]); + i__1 = *n; + for (i__ = 2; i__ <= i__1; ++i__) { + if ((d__1 = dx[i__], abs(d__1)) <= dmax__) { + goto L30; + } + ret_val = i__; + dmax__ = (d__1 = dx[i__], abs(d__1)); +L30: + ; + } + return ret_val; +} /* idamax_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/ieeeck.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/ieeeck.c new file mode 100644 index 00000000..39256a48 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/ieeeck.c @@ -0,0 +1,150 @@ +#include "blaswrap.h" +#include "f2c.h" + +integer ieeeck_(integer *ispec, real *zero, real *one) +{ +/* -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + June 30, 1998 + + + Purpose + ======= + + IEEECK is called from the ILAENV to verify that Infinity and + possibly NaN arithmetic is safe (i.e. will not trap). + + Arguments + ========= + + ISPEC (input) INTEGER + Specifies whether to test just for inifinity arithmetic + or whether to test for infinity and NaN arithmetic. + = 0: Verify infinity arithmetic only. + = 1: Verify infinity and NaN arithmetic. + + ZERO (input) REAL + Must contain the value 0.0 + This is passed to prevent the compiler from optimizing + away this code. + + ONE (input) REAL + Must contain the value 1.0 + This is passed to prevent the compiler from optimizing + away this code. + + RETURN VALUE: INTEGER + = 0: Arithmetic failed to produce the correct answers + = 1: Arithmetic produced the correct answers */ + /* System generated locals */ + integer ret_val; + /* Local variables */ + static real neginf, posinf, negzro, newzro, nan1, nan2, nan3, nan4, nan5, + nan6; + + + ret_val = 1; + + posinf = *one / *zero; + if (posinf <= *one) { + ret_val = 0; + return ret_val; + } + + neginf = -(*one) / *zero; + if (neginf >= *zero) { + ret_val = 0; + return ret_val; + } + + negzro = *one / (neginf + *one); + if (negzro != *zero) { + ret_val = 0; + return ret_val; + } + + neginf = *one / negzro; + if (neginf >= *zero) { + ret_val = 0; + return ret_val; + } + + newzro = negzro + *zero; + if (newzro != *zero) { + ret_val = 0; + return ret_val; + } + + posinf = *one / newzro; + if (posinf <= *one) { + ret_val = 0; + return ret_val; + } + + neginf *= posinf; + if (neginf >= *zero) { + ret_val = 0; + return ret_val; + } + + posinf *= posinf; + if (posinf <= *one) { + ret_val = 0; + return ret_val; + } + + + + +/* Return if we were only asked to check infinity arithmetic */ + + if (*ispec == 0) { + return ret_val; + } + + nan1 = posinf + neginf; + + nan2 = posinf / neginf; + + nan3 = posinf / posinf; + + nan4 = posinf * *zero; + + nan5 = neginf * negzro; + + nan6 = nan5 * 0.f; + + if (nan1 == nan1) { + ret_val = 0; + return ret_val; + } + + if (nan2 == nan2) { + ret_val = 0; + return ret_val; + } + + if (nan3 == nan3) { + ret_val = 0; + return ret_val; + } + + if (nan4 == nan4) { + ret_val = 0; + return ret_val; + } + + if (nan5 == nan5) { + ret_val = 0; + return ret_val; + } + + if (nan6 == nan6) { + ret_val = 0; + return ret_val; + } + + return ret_val; +} /* ieeeck_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/ilaenv.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/ilaenv.c new file mode 100644 index 00000000..58299fff --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/ilaenv.c @@ -0,0 +1,610 @@ +#include "blaswrap.h" +#include "f2c.h" + +integer ilaenv_(integer *ispec, char *name__, char *opts, integer *n1, + integer *n2, integer *n3, integer *n4, ftnlen name_len, ftnlen + opts_len) +{ +/* -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + June 30, 1999 + + + Purpose + ======= + + ILAENV is called from the LAPACK routines to choose problem-dependent + parameters for the local environment. See ISPEC for a description of + the parameters. + + This version provides a set of parameters which should give good, + but not optimal, performance on many of the currently available + computers. Users are encouraged to modify this subroutine to set + the tuning parameters for their particular machine using the option + and problem size information in the arguments. + + This routine will not function correctly if it is converted to all + lower case. Converting it to all upper case is allowed. + + Arguments + ========= + + ISPEC (input) INTEGER + Specifies the parameter to be returned as the value of + ILAENV. + = 1: the optimal blocksize; if this value is 1, an unblocked + algorithm will give the best performance. + = 2: the minimum block size for which the block routine + should be used; if the usable block size is less than + this value, an unblocked routine should be used. + = 3: the crossover point (in a block routine, for N less + than this value, an unblocked routine should be used) + = 4: the number of shifts, used in the nonsymmetric + eigenvalue routines + = 5: the minimum column dimension for blocking to be used; + rectangular blocks must have dimension at least k by m, + where k is given by ILAENV(2,...) and m by ILAENV(5,...) + = 6: the crossover point for the SVD (when reducing an m by n + matrix to bidiagonal form, if max(m,n)/min(m,n) exceeds + this value, a QR factorization is used first to reduce + the matrix to a triangular form.) + = 7: the number of processors + = 8: the crossover point for the multishift QR and QZ methods + for nonsymmetric eigenvalue problems. + = 9: maximum size of the subproblems at the bottom of the + computation tree in the divide-and-conquer algorithm + (used by xGELSD and xGESDD) + =10: ieee NaN arithmetic can be trusted not to trap + =11: infinity arithmetic can be trusted not to trap + + NAME (input) CHARACTER*(*) + The name of the calling subroutine, in either upper case or + lower case. + + OPTS (input) CHARACTER*(*) + The character options to the subroutine NAME, concatenated + into a single character string. For example, UPLO = 'U', + TRANS = 'T', and DIAG = 'N' for a triangular routine would + be specified as OPTS = 'UTN'. + + N1 (input) INTEGER + N2 (input) INTEGER + N3 (input) INTEGER + N4 (input) INTEGER + Problem dimensions for the subroutine NAME; these may not all + be required. + + (ILAENV) (output) INTEGER + >= 0: the value of the parameter specified by ISPEC + < 0: if ILAENV = -k, the k-th argument had an illegal value. + + Further Details + =============== + + The following conventions have been used when calling ILAENV from the + LAPACK routines: + 1) OPTS is a concatenation of all of the character options to + subroutine NAME, in the same order that they appear in the + argument list for NAME, even if they are not used in determining + the value of the parameter specified by ISPEC. + 2) The problem dimensions N1, N2, N3, N4 are specified in the order + that they appear in the argument list for NAME. N1 is used + first, N2 second, and so on, and unused problem dimensions are + passed a value of -1. + 3) The parameter value returned by ILAENV is checked for validity in + the calling subroutine. For example, ILAENV is used to retrieve + the optimal blocksize for STRTRI as follows: + + NB = ILAENV( 1, 'STRTRI', UPLO // DIAG, N, -1, -1, -1 ) + IF( NB.LE.1 ) NB = MAX( 1, N ) + + ===================================================================== */ + /* Table of constant values */ + static integer c__0 = 0; + static real c_b162 = 0.f; + static real c_b163 = 1.f; + static integer c__1 = 1; + + /* System generated locals */ + integer ret_val; + /* Builtin functions + Subroutine */ int s_copy(char *, char *, ftnlen, ftnlen); + integer s_cmp(char *, char *, ftnlen, ftnlen); + /* Local variables */ + static integer i__; + static logical cname, sname; + static integer nbmin; + static char c1[1], c2[2], c3[3], c4[2]; + static integer ic, nb; + extern integer ieeeck_(integer *, real *, real *); + static integer iz, nx; + static char subnam[6]; + + + + + switch (*ispec) { + case 1: goto L100; + case 2: goto L100; + case 3: goto L100; + case 4: goto L400; + case 5: goto L500; + case 6: goto L600; + case 7: goto L700; + case 8: goto L800; + case 9: goto L900; + case 10: goto L1000; + case 11: goto L1100; + } + +/* Invalid value for ISPEC */ + + ret_val = -1; + return ret_val; + +L100: + +/* Convert NAME to upper case if the first character is lower case. */ + + ret_val = 1; + s_copy(subnam, name__, (ftnlen)6, name_len); + ic = *(unsigned char *)subnam; + iz = 'Z'; + if (iz == 90 || iz == 122) { + +/* ASCII character set */ + + if (ic >= 97 && ic <= 122) { + *(unsigned char *)subnam = (char) (ic - 32); + for (i__ = 2; i__ <= 6; ++i__) { + ic = *(unsigned char *)&subnam[i__ - 1]; + if (ic >= 97 && ic <= 122) { + *(unsigned char *)&subnam[i__ - 1] = (char) (ic - 32); + } +/* L10: */ + } + } + + } else if (iz == 233 || iz == 169) { + +/* EBCDIC character set */ + + if (ic >= 129 && ic <= 137 || ic >= 145 && ic <= 153 || ic >= 162 && + ic <= 169) { + *(unsigned char *)subnam = (char) (ic + 64); + for (i__ = 2; i__ <= 6; ++i__) { + ic = *(unsigned char *)&subnam[i__ - 1]; + if (ic >= 129 && ic <= 137 || ic >= 145 && ic <= 153 || ic >= + 162 && ic <= 169) { + *(unsigned char *)&subnam[i__ - 1] = (char) (ic + 64); + } +/* L20: */ + } + } + + } else if (iz == 218 || iz == 250) { + +/* Prime machines: ASCII+128 */ + + if (ic >= 225 && ic <= 250) { + *(unsigned char *)subnam = (char) (ic - 32); + for (i__ = 2; i__ <= 6; ++i__) { + ic = *(unsigned char *)&subnam[i__ - 1]; + if (ic >= 225 && ic <= 250) { + *(unsigned char *)&subnam[i__ - 1] = (char) (ic - 32); + } +/* L30: */ + } + } + } + + *(unsigned char *)c1 = *(unsigned char *)subnam; + sname = *(unsigned char *)c1 == 'S' || *(unsigned char *)c1 == 'D'; + cname = *(unsigned char *)c1 == 'C' || *(unsigned char *)c1 == 'Z'; + if (! (cname || sname)) { + return ret_val; + } + s_copy(c2, subnam + 1, (ftnlen)2, (ftnlen)2); + s_copy(c3, subnam + 3, (ftnlen)3, (ftnlen)3); + s_copy(c4, c3 + 1, (ftnlen)2, (ftnlen)2); + + switch (*ispec) { + case 1: goto L110; + case 2: goto L200; + case 3: goto L300; + } + +L110: + +/* ISPEC = 1: block size + + In these examples, separate code is provided for setting NB for + real and complex. We assume that NB will take the same value in + single or double precision. */ + + nb = 1; + + if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } else if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, + "RQF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen) + 3, (ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) + == 0) { + if (sname) { + nb = 32; + } else { + nb = 32; + } + } else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 32; + } else { + nb = 32; + } + } else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 32; + } else { + nb = 32; + } + } else if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } + } else if (s_cmp(c2, "PO", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } + } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } else if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nb = 32; + } else if (sname && s_cmp(c3, "GST", (ftnlen)3, (ftnlen)3) == 0) { + nb = 64; + } + } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + nb = 64; + } else if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nb = 32; + } else if (s_cmp(c3, "GST", (ftnlen)3, (ftnlen)3) == 0) { + nb = 64; + } + } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nb = 32; + } + } else if (*(unsigned char *)c3 == 'M') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nb = 32; + } + } + } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nb = 32; + } + } else if (*(unsigned char *)c3 == 'M') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nb = 32; + } + } + } else if (s_cmp(c2, "GB", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + if (*n4 <= 64) { + nb = 1; + } else { + nb = 32; + } + } else { + if (*n4 <= 64) { + nb = 1; + } else { + nb = 32; + } + } + } + } else if (s_cmp(c2, "PB", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + if (*n2 <= 64) { + nb = 1; + } else { + nb = 32; + } + } else { + if (*n2 <= 64) { + nb = 1; + } else { + nb = 32; + } + } + } + } else if (s_cmp(c2, "TR", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } + } else if (s_cmp(c2, "LA", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "UUM", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nb = 64; + } else { + nb = 64; + } + } + } else if (sname && s_cmp(c2, "ST", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "EBZ", (ftnlen)3, (ftnlen)3) == 0) { + nb = 1; + } + } + ret_val = nb; + return ret_val; + +L200: + +/* ISPEC = 2: minimum block size */ + + nbmin = 2; + if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "RQF", ( + ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen)3, ( + ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) == 0) + { + if (sname) { + nbmin = 2; + } else { + nbmin = 2; + } + } else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nbmin = 2; + } else { + nbmin = 2; + } + } else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nbmin = 2; + } else { + nbmin = 2; + } + } else if (s_cmp(c3, "TRI", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nbmin = 2; + } else { + nbmin = 2; + } + } + } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRF", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nbmin = 8; + } else { + nbmin = 8; + } + } else if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nbmin = 2; + } + } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nbmin = 2; + } + } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nbmin = 2; + } + } else if (*(unsigned char *)c3 == 'M') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nbmin = 2; + } + } + } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nbmin = 2; + } + } else if (*(unsigned char *)c3 == 'M') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nbmin = 2; + } + } + } + ret_val = nbmin; + return ret_val; + +L300: + +/* ISPEC = 3: crossover point */ + + nx = 0; + if (s_cmp(c2, "GE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "QRF", (ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "RQF", ( + ftnlen)3, (ftnlen)3) == 0 || s_cmp(c3, "LQF", (ftnlen)3, ( + ftnlen)3) == 0 || s_cmp(c3, "QLF", (ftnlen)3, (ftnlen)3) == 0) + { + if (sname) { + nx = 128; + } else { + nx = 128; + } + } else if (s_cmp(c3, "HRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nx = 128; + } else { + nx = 128; + } + } else if (s_cmp(c3, "BRD", (ftnlen)3, (ftnlen)3) == 0) { + if (sname) { + nx = 128; + } else { + nx = 128; + } + } + } else if (s_cmp(c2, "SY", (ftnlen)2, (ftnlen)2) == 0) { + if (sname && s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nx = 32; + } + } else if (cname && s_cmp(c2, "HE", (ftnlen)2, (ftnlen)2) == 0) { + if (s_cmp(c3, "TRD", (ftnlen)3, (ftnlen)3) == 0) { + nx = 32; + } + } else if (sname && s_cmp(c2, "OR", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nx = 128; + } + } + } else if (cname && s_cmp(c2, "UN", (ftnlen)2, (ftnlen)2) == 0) { + if (*(unsigned char *)c3 == 'G') { + if (s_cmp(c4, "QR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "RQ", + (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "LQ", (ftnlen)2, ( + ftnlen)2) == 0 || s_cmp(c4, "QL", (ftnlen)2, (ftnlen)2) == + 0 || s_cmp(c4, "HR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp( + c4, "TR", (ftnlen)2, (ftnlen)2) == 0 || s_cmp(c4, "BR", ( + ftnlen)2, (ftnlen)2) == 0) { + nx = 128; + } + } + } + ret_val = nx; + return ret_val; + +L400: + +/* ISPEC = 4: number of shifts (used by xHSEQR) */ + + ret_val = 6; + return ret_val; + +L500: + +/* ISPEC = 5: minimum column dimension (not used) */ + + ret_val = 2; + return ret_val; + +L600: + +/* ISPEC = 6: crossover point for SVD (used by xGELSS and xGESVD) */ + + ret_val = (integer) ((real) min(*n1,*n2) * 1.6f); + return ret_val; + +L700: + +/* ISPEC = 7: number of processors (not used) */ + + ret_val = 1; + return ret_val; + +L800: + +/* ISPEC = 8: crossover point for multishift (used by xHSEQR) */ + + ret_val = 50; + return ret_val; + +L900: + +/* ISPEC = 9: maximum size of the subproblems at the bottom of the + computation tree in the divide-and-conquer algorithm + (used by xGELSD and xGESDD) */ + + ret_val = 25; + return ret_val; + +L1000: + +/* ISPEC = 10: ieee NaN arithmetic can be trusted not to trap + + ILAENV = 0 */ + ret_val = 1; + if (ret_val == 1) { + ret_val = ieeeck_(&c__0, &c_b162, &c_b163); + } + return ret_val; + +L1100: + +/* ISPEC = 11: infinity arithmetic can be trusted not to trap + + ILAENV = 0 */ + ret_val = 1; + if (ret_val == 1) { + ret_val = ieeeck_(&c__1, &c_b162, &c_b163); + } + return ret_val; + +/* End of ILAENV */ + +} /* ilaenv_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/lsame.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/lsame.c new file mode 100644 index 00000000..d9baaa3f --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/lsame.c @@ -0,0 +1,101 @@ +#include "f2c.h" + +logical lsame_(char *ca, char *cb) +{ +/* -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + September 30, 1994 + + + Purpose + ======= + + LSAME returns .TRUE. if CA is the same letter as CB regardless of + case. + + Arguments + ========= + + CA (input) CHARACTER*1 + CB (input) CHARACTER*1 + CA and CB specify the single characters to be compared. + + ===================================================================== + + + + Test if the characters are equal */ + /* System generated locals */ + logical ret_val; + /* Local variables */ + static integer inta, intb, zcode; + + + ret_val = *(unsigned char *)ca == *(unsigned char *)cb; + if (ret_val) { + return ret_val; + } + +/* Now test for equivalence if both characters are alphabetic. */ + + zcode = 'Z'; + +/* Use 'Z' rather than 'A' so that ASCII can be detected on Prime + machines, on which ICHAR returns a value with bit 8 set. + ICHAR('A') on Prime machines returns 193 which is the same as + ICHAR('A') on an EBCDIC machine. */ + + inta = *(unsigned char *)ca; + intb = *(unsigned char *)cb; + + if (zcode == 90 || zcode == 122) { + +/* ASCII is assumed - ZCODE is the ASCII code of either lower o +r + upper case 'Z'. */ + + if (inta >= 97 && inta <= 122) { + inta += -32; + } + if (intb >= 97 && intb <= 122) { + intb += -32; + } + + } else if (zcode == 233 || zcode == 169) { + +/* EBCDIC is assumed - ZCODE is the EBCDIC code of either lower + or + upper case 'Z'. */ + + if (inta >= 129 && inta <= 137 || inta >= 145 && inta <= 153 || inta + >= 162 && inta <= 169) { + inta += 64; + } + if (intb >= 129 && intb <= 137 || intb >= 145 && intb <= 153 || intb + >= 162 && intb <= 169) { + intb += 64; + } + + } else if (zcode == 218 || zcode == 250) { + +/* ASCII is assumed, on Prime machines - ZCODE is the ASCII cod +e + plus 128 of either lower or upper case 'Z'. */ + + if (inta >= 225 && inta <= 250) { + inta += -32; + } + if (intb >= 225 && intb <= 250) { + intb += -32; + } + } + ret_val = inta == intb; + +/* RETURN + + End of LSAME */ + + return ret_val; +} /* lsame_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/open.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/open.c new file mode 100644 index 00000000..e7810757 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/open.c @@ -0,0 +1,256 @@ +#include "f2c.h" +#include "fio.h" +#include "string.h" +#ifndef NON_POSIX_STDIO +#ifdef MSDOS +#include "io.h" +#else +#include "unistd.h" /* for access */ +#endif +#endif + +#undef abs +#undef min +#undef max +#include "stdlib.h" +extern int f__canseek(FILE*); +extern integer f_clos(cllist*); + +#ifdef NON_ANSI_RW_MODES +char *f__r_mode[2] = {"r", "r"}; +char *f__w_mode[4] = {"w", "w", "r+w", "r+w"}; +#else +char *f__r_mode[2] = {"rb", "r"}; +char *f__w_mode[4] = {"wb", "w", "r+b", "r+"}; +#endif + +static char f__buf0[400], *f__buf = f__buf0; +int f__buflen = (int)sizeof(f__buf0); + +static void f__bufadj(int n, int c) +{ + unsigned int len; + char *nbuf, *s, *t, *te; + + if (f__buf == f__buf0) + f__buflen = 1024; + while(f__buflen <= n) + f__buflen <<= 1; + len = (unsigned int)f__buflen; + if (len != f__buflen || !(nbuf = (char*)malloc(len))) + f__fatal(113, "malloc failure"); + s = nbuf; + t = f__buf; + te = t + c; + while(t < te) + *s++ = *t++; + if (f__buf != f__buf0) + free(f__buf); + f__buf = nbuf; + } + +int f__putbuf(int c) +{ + char *s, *se; + int n; + + if (f__hiwater > f__recpos) + f__recpos = f__hiwater; + n = f__recpos + 1; + if (n >= f__buflen) + f__bufadj(n, f__recpos); + s = f__buf; + se = s + f__recpos; + if (c) + *se++ = c; + *se = 0; + for(;;) { + fputs(s, f__cf); + s += strlen(s); + if (s >= se) + break; /* normally happens the first time */ + putc(*s++, f__cf); + } + return 0; + } + +void x_putc(int c) +{ + if (f__recpos >= f__buflen) + f__bufadj(f__recpos, f__buflen); + f__buf[f__recpos++] = c; + } + +#define opnerr(f,m,s) {if(f) errno= m; else opn_err(m,s,a); return(m);} + +static void opn_err(int m, char *s, olist *a) +{ + if (a->ofnm) { + /* supply file name to error message */ + if (a->ofnmlen >= f__buflen) + f__bufadj((int)a->ofnmlen, 0); + g_char(a->ofnm, a->ofnmlen, f__curunit->ufnm = f__buf); + } + f__fatal(m, s); + } + +integer f_open(olist *a) +{ unit *b; + integer rv; + char buf[256], *s; + cllist x; + int ufmt; + FILE *tf; +#ifndef NON_UNIX_STDIO + int n; +#endif + f__external = 1; + if(a->ounit>=MXUNIT || a->ounit<0) + err(a->oerr,101,"open") + if (!f__init) + f_init(); + f__curunit = b = &f__units[a->ounit]; + if(b->ufd) { + if(a->ofnm==0) + { + same: if (a->oblnk) + b->ublnk = *a->oblnk == 'z' || *a->oblnk == 'Z'; + return(0); + } +#ifdef NON_UNIX_STDIO + if (b->ufnm + && strlen(b->ufnm) == a->ofnmlen + && !strncmp(b->ufnm, a->ofnm, (unsigned)a->ofnmlen)) + goto same; +#else + g_char(a->ofnm,a->ofnmlen,buf); + if (f__inode(buf,&n) == b->uinode && n == b->udev) + goto same; +#endif + x.cunit=a->ounit; + x.csta=0; + x.cerr=a->oerr; + if ((rv = f_clos(&x)) != 0) + return rv; + } + b->url = (int)a->orl; + b->ublnk = a->oblnk && (*a->oblnk == 'z' || *a->oblnk == 'Z'); + if(a->ofm==0) + { if(b->url>0) b->ufmt=0; + else b->ufmt=1; + } + else if(*a->ofm=='f' || *a->ofm == 'F') b->ufmt=1; + else b->ufmt=0; + ufmt = b->ufmt; +#ifdef url_Adjust + if (b->url && !ufmt) + url_Adjust(b->url); +#endif + if (a->ofnm) { + g_char(a->ofnm,a->ofnmlen,buf); + if (!buf[0]) + opnerr(a->oerr,107,"open") + } + else + sprintf(buf, "fort.%ld", (long)a->ounit); + b->uscrtch = 0; + b->uend=0; + b->uwrt = 0; + b->ufd = 0; + b->urw = 3; + switch(a->osta ? *a->osta : 'u') + { + case 'o': + case 'O': +#ifdef NON_POSIX_STDIO + if (!(tf = fopen(buf,"r"))) + opnerr(a->oerr,errno,"open") + fclose(tf); +#else + if (access(buf,0)) + opnerr(a->oerr,errno,"open") +#endif + break; + case 's': + case 'S': + b->uscrtch=1; +#ifdef NON_ANSI_STDIO + (void) strcpy(buf,"tmp.FXXXXXX"); + (void) mktemp(buf); + goto replace; +#else + if (!(b->ufd = tmpfile())) + opnerr(a->oerr,errno,"open") + b->ufnm = 0; +#ifndef NON_UNIX_STDIO + b->uinode = b->udev = -1; +#endif + b->useek = 1; + return 0; +#endif + + case 'n': + case 'N': +#ifdef NON_POSIX_STDIO + if ((tf = fopen(buf,"r")) || (tf = fopen(buf,"a"))) { + fclose(tf); + opnerr(a->oerr,128,"open") + } +#else + if (!access(buf,0)) + opnerr(a->oerr,128,"open") +#endif + /* no break */ + case 'r': /* Fortran 90 replace option */ + case 'R': +#ifdef NON_ANSI_STDIO + replace: +#endif + if (tf = fopen(buf,f__w_mode[0])) + fclose(tf); + } + + b->ufnm=(char *) malloc((unsigned int)(strlen(buf)+1)); + if(b->ufnm==NULL) opnerr(a->oerr,113,"no space"); + (void) strcpy(b->ufnm,buf); + if ((s = a->oacc) && b->url) + ufmt = 0; + if(!(tf = fopen(buf, f__w_mode[ufmt|2]))) { + if (tf = fopen(buf, f__r_mode[ufmt])) + b->urw = 1; + else if (tf = fopen(buf, f__w_mode[ufmt])) { + b->uwrt = 1; + b->urw = 2; + } + else + err(a->oerr, errno, "open"); + } + b->useek = f__canseek(b->ufd = tf); +#ifndef NON_UNIX_STDIO + if((b->uinode = f__inode(buf,&b->udev)) == -1) + opnerr(a->oerr,108,"open") +#endif + if(b->useek) + if (a->orl) + rewind(b->ufd); + else if ((s = a->oacc) && (*s == 'a' || *s == 'A') + && fseek(b->ufd, 0L, SEEK_END)) + opnerr(a->oerr,129,"open"); + return(0); +} + +int fk_open(int seq, int fmt, ftnint n) +{ char nbuf[10]; + olist a; + (void) sprintf(nbuf,"fort.%ld",(long)n); + a.oerr=1; + a.ounit=n; + a.ofnm=nbuf; + a.ofnmlen=strlen(nbuf); + a.osta=NULL; + a.oacc= seq==SEQ?"s":"d"; + a.ofm = fmt==FMT?"f":"u"; + a.orl = seq==DIR?1:0; + a.oblnk=NULL; + return(f_open(&a)); +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_cmp.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_cmp.c new file mode 100644 index 00000000..cd6a7dc4 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_cmp.c @@ -0,0 +1,40 @@ +#include "f2c.h" + +/* compare two strings */ + +integer s_cmp(char *a0, char *b0, ftnlen la, ftnlen lb) +{ +unsigned char *a, *aend, *b, *bend; +a = (unsigned char *)a0; +b = (unsigned char *)b0; +aend = a + la; +bend = b + lb; + +if(la <= lb) + { + while(a < aend) + if(*a != *b) + return( *a - *b ); + else + { ++a; ++b; } + + while(b < bend) + if(*b != ' ') + return( ' ' - *b ); + else ++b; + } + +else + { + while(b < bend) + if(*a == *b) + { ++a; ++b; } + else + return( *a - *b ); + while(a < aend) + if(*a != ' ') + return(*a - ' '); + else ++a; + } +return(0); +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_copy.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_copy.c new file mode 100644 index 00000000..289c67dd --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_copy.c @@ -0,0 +1,47 @@ +/* Unless compiled with -DNO_OVERWRITE, this variant of s_copy allows the + * target of an assignment to appear on its right-hand side (contrary + * to the Fortran 77 Standard, but in accordance with Fortran 90), + * as in a(2:5) = a(4:7) . + */ + +#include "f2c.h" + +/* assign strings: a = b */ + +void s_copy(char *a, char *b, ftnlen la, ftnlen lb) +{ + char *aend, *bend; + + aend = a + la; + + if(la <= lb) +#ifndef NO_OVERWRITE + if (a <= b || a >= b + la) +#endif + while(a < aend) + *a++ = *b++; +#ifndef NO_OVERWRITE + else + for(b += la; a < aend; ) + *--aend = *--b; +#endif + + else { + bend = b + lb; +#ifndef NO_OVERWRITE + if (a <= b || a >= bend) +#endif + while(b < bend) + *a++ = *b++; +#ifndef NO_OVERWRITE + else { + a += lb; + while(b < bend) + *--a = *--bend; + a += lb; + } +#endif + while(a < aend) + *a++ = ' '; + } + } diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_stop.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_stop.c new file mode 100644 index 00000000..049f71b6 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/s_stop.c @@ -0,0 +1,37 @@ +#include "stdio.h" +#include "f2c.h" + +#undef abs +#undef min +#undef max +#include "stdlib.h" +#ifdef __cplusplus +extern "C" { +#endif +void f_exit(void); + +int s_stop(char *s, ftnlen n) +{ +int i; + +if(n > 0) + { + fprintf(stderr, "STOP "); + for(i = 0; iciunit]; + if(a->ciunit >= MXUNIT || a->ciunit<0) + err(a->cierr,101,"startio"); + if(p->ufd==NULL && fk_open(SEQ,FMT,a->ciunit)) err(a->cierr,114,"sfe") + if(!p->ufmt) err(a->cierr,102,"sfe") + return(0); +} + +integer e_wsfe(Void) +{ + int n = en_fio(); + f__fmtbuf = NULL; + return n; +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/sig_die.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/sig_die.c new file mode 100644 index 00000000..78335200 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/sig_die.c @@ -0,0 +1,41 @@ +#include "stdio.h" +#include "signal.h" + +#ifndef SIGIOT +#ifdef SIGABRT +#define SIGIOT SIGABRT +#endif +#endif + +#include "stdlib.h" +#ifdef __cplusplus +extern "C" { +#endif + extern void f_exit(void); + +void sig_die(char *s, int kill) +{ + /* print error message, then clear buffers */ + fprintf(stderr, "%s\n", s); + + if(kill) + { + fflush(stderr); + f_exit(); + fflush(stderr); + /* now get a core */ +#ifdef SIGIOT + signal(SIGIOT, SIG_DFL); +#endif + abort(); + } + else { +#ifdef NO_ONEXIT + f_exit(); +#endif + exit(1); + } + } +#ifdef __cplusplus +} +#endif diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/util.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/util.c new file mode 100644 index 00000000..684cdfc4 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/util.c @@ -0,0 +1,39 @@ +#ifndef NON_UNIX_STDIO +#define _INCLUDE_POSIX_SOURCE /* for HP-UX */ +#define _INCLUDE_XOPEN_SOURCE /* for HP-UX */ +#include "sys/types.h" +#include "sys/stat.h" +#endif +#include "f2c.h" +#include "fio.h" + +void g_char(char *a, ftnlen alen, char *b) +{ + char *x = a + alen, *y = b + alen; + + for(;; y--) { + if (x <= a) { + *b = 0; + return; + } + if (*--x != ' ') + break; + } + *y-- = 0; + do *y-- = *x; + while(x-- > a); + } + +void b_char(char *a, char *b, ftnlen blen) +{ int i; + for(i=0;i= d + 2 || f__scale <= -d) + goto nogood; + } + if(f__scale <= 0) + --d; + if (len == sizeof(real)) + dd = p->pf; + else + dd = p->pd; + if (dd < 0.) { + signspace = sign = 1; + dd = -dd; + } + else { + sign = 0; + signspace = (int)f__cplus; +#ifndef VAX + if (!dd) + dd = 0.; /* avoid -0 */ +#endif + } + delta = w - (2 /* for the . and the d adjustment above */ + + 2 /* for the E+ */ + signspace + d + e); +#ifdef WANT_LEAD_0 + if (f__scale <= 0 && delta > 0) { + delta--; + insert0 = 1; + } + else +#endif + if (delta < 0) { +nogood: + while(--w >= 0) + PUT('*'); + return(0); + } + if (f__scale < 0) + d += f__scale; + if (d > FMAX) { + d1 = d - FMAX; + d = FMAX; + } + else + d1 = 0; + sprintf(buf,"%#.*E", d, dd); +#ifndef VAX + /* check for NaN, Infinity */ + if (!isdigit(buf[0])) { + switch(buf[0]) { + case 'n': + case 'N': + signspace = 0; /* no sign for NaNs */ + } + delta = w - strlen(buf) - signspace; + if (delta < 0) + goto nogood; + while(--delta >= 0) + PUT(' '); + if (signspace) + PUT(sign ? '-' : '+'); + for(s = buf; *s; s++) + PUT(*s); + return 0; + } +#endif + se = buf + d + 3; +#ifdef GOOD_SPRINTF_EXPONENT /* When possible, exponent has 2 digits. */ + if (f__scale != 1 && dd) + sprintf(se, "%+.2d", atoi(se) + 1 - f__scale); +#else + if (dd) + sprintf(se, "%+.2d", atoi(se) + 1 - f__scale); + else + strcpy(se, "+00"); +#endif + s = ++se; + if (e < 2) { + if (*s != '0') + goto nogood; + } +#ifndef VAX + /* accommodate 3 significant digits in exponent */ + if (s[2]) { +#ifdef Pedantic + if (!e0 && !s[3]) + for(s -= 2, e1 = 2; s[0] = s[1]; s++); + + /* Pedantic gives the behavior that Fortran 77 specifies, */ + /* i.e., requires that E be specified for exponent fields */ + /* of more than 3 digits. With Pedantic undefined, we get */ + /* the behavior that Cray displays -- you get a bigger */ + /* exponent field if it fits. */ +#else + if (!e0) { + for(s -= 2, e1 = 2; s[0] = s[1]; s++) +#ifdef CRAY + delta--; + if ((delta += 4) < 0) + goto nogood +#endif + ; + } +#endif + else if (e0 >= 0) + goto shift; + else + e1 = e; + } + else + shift: +#endif + for(s += 2, e1 = 2; *s; ++e1, ++s) + if (e1 >= e) + goto nogood; + while(--delta >= 0) + PUT(' '); + if (signspace) + PUT(sign ? '-' : '+'); + s = buf; + i = f__scale; + if (f__scale <= 0) { +#ifdef WANT_LEAD_0 + if (insert0) + PUT('0'); +#endif + PUT('.'); + for(; i < 0; ++i) + PUT('0'); + PUT(*s); + s += 2; + } + else if (f__scale > 1) { + PUT(*s); + s += 2; + while(--i > 0) + PUT(*s++); + PUT('.'); + } + if (d1) { + se -= 2; + while(s < se) PUT(*s++); + se += 2; + do PUT('0'); while(--d1 > 0); + } + while(s < se) + PUT(*s++); + if (e < 2) + PUT(s[1]); + else { + while(++e1 <= e) + PUT('0'); + while(*s) + PUT(*s++); + } + return 0; + } + +int wrt_F(ufloat *p, int w, int d, ftnlen len) +{ + int d1, sign, n; + double x; + char *b, buf[MAXINTDIGS+MAXFRACDIGS+4], *s; + + x= (len==sizeof(real)?p->pf:p->pd); + if (d < MAXFRACDIGS) + d1 = 0; + else { + d1 = d - MAXFRACDIGS; + d = MAXFRACDIGS; + } + if (x < 0.) + { x = -x; sign = 1; } + else { + sign = 0; +#ifndef VAX + if (!x) + x = 0.; +#endif + } + + if (n = f__scale) + if (n > 0) + do x *= 10.; while(--n > 0); + else + do x *= 0.1; while(++n < 0); + +#ifdef USE_STRLEN + sprintf(b = buf, "%#.*f", d, x); + n = strlen(b) + d1; +#else + n = sprintf(b = buf, "%#.*f", d, x) + d1; +#endif + +#ifndef WANT_LEAD_0 + if (buf[0] == '0' && d) + { ++b; --n; } +#endif + if (sign) { + /* check for all zeros */ + for(s = b;;) { + while(*s == '0') s++; + switch(*s) { + case '.': + s++; continue; + case 0: + sign = 0; + } + break; + } + } + if (sign || f__cplus) + ++n; + if (n > w) { +#ifdef WANT_LEAD_0 + if (buf[0] == '0' && --n == w) + ++b; + else +#endif + { + while(--w >= 0) + PUT('*'); + return 0; + } + } + for(w -= n; --w >= 0; ) + PUT(' '); + if (sign) + PUT('-'); + else if (f__cplus) + PUT('+'); + while(n = *b++) + PUT(n); + while(--d1 >= 0) + PUT('0'); + return 0; + } diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/wrtfmt.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/wrtfmt.c new file mode 100644 index 00000000..74d1131a --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/wrtfmt.c @@ -0,0 +1,321 @@ +#include "f2c.h" +#include "fio.h" +#include "fmt.h" + +extern icilist *f__svic; +extern char *f__icptr; + + static int +mv_cur(Void) /* shouldn't use fseek because it insists on calling fflush */ + /* instead we know too much about stdio */ +{ + int cursor = f__cursor; + f__cursor = 0; + if(f__external == 0) { + if(cursor < 0) { + if(f__hiwater < f__recpos) + f__hiwater = f__recpos; + f__recpos += cursor; + f__icptr += cursor; + if(f__recpos < 0) + err(f__elist->cierr, 110, "left off"); + } + else if(cursor > 0) { + if(f__recpos + cursor >= f__svic->icirlen) + err(f__elist->cierr, 110, "recend"); + if(f__hiwater <= f__recpos) + for(; cursor > 0; cursor--) + (*f__putn)(' '); + else if(f__hiwater <= f__recpos + cursor) { + cursor -= f__hiwater - f__recpos; + f__icptr += f__hiwater - f__recpos; + f__recpos = f__hiwater; + for(; cursor > 0; cursor--) + (*f__putn)(' '); + } + else { + f__icptr += cursor; + f__recpos += cursor; + } + } + return(0); + } + if (cursor > 0) { + if(f__hiwater <= f__recpos) + for(;cursor>0;cursor--) (*f__putn)(' '); + else if(f__hiwater <= f__recpos + cursor) { + cursor -= f__hiwater - f__recpos; + f__recpos = f__hiwater; + for(; cursor > 0; cursor--) + (*f__putn)(' '); + } + else { + f__recpos += cursor; + } + } + else if (cursor < 0) + { + if(cursor + f__recpos < 0) + err(f__elist->cierr,110,"left off"); + if(f__hiwater < f__recpos) + f__hiwater = f__recpos; + f__recpos += cursor; + } + return(0); +} + +static int wrt_Z(Uint *n, int w, int minlen, ftnlen len) +{ + char *s, *se; + int i, w1; + static int one = 1; + static char hex[] = "0123456789ABCDEF"; + s = (char *)n; + --len; + if (*(char *)&one) { + /* little endian */ + se = s; + s += len; + i = -1; + } + else { + se = s + len; + i = 1; + } + for(;; s += i) + if (s == se || *s) + break; + w1 = (i*(se-s) << 1) + 1; + if (*s & 0xf0) + w1++; + if (w1 > w) + for(i = 0; i < w; i++) + (*f__putn)('*'); + else { + if ((minlen -= w1) > 0) + w1 += minlen; + while(--w >= w1) + (*f__putn)(' '); + while(--minlen >= 0) + (*f__putn)('0'); + if (!(*s & 0xf0)) { + (*f__putn)(hex[*s & 0xf]); + if (s == se) + return 0; + s += i; + } + for(;; s += i) { + (*f__putn)(hex[*s >> 4 & 0xf]); + (*f__putn)(hex[*s & 0xf]); + if (s == se) + break; + } + } + return 0; + } + +static int wrt_I(Uint *n, int w, ftnlen len, int base) +{ int ndigit,sign,spare,i; + longint x; + char *ans; + if(len==sizeof(integer)) x=n->il; + else if(len == sizeof(char)) x = n->ic; +#ifdef Allow_TYQUAD + else if (len == sizeof(longint)) x = n->ili; +#endif + else x=n->is; + ans=f__icvt(x,&ndigit,&sign, base); + spare=w-ndigit; + if(sign || f__cplus) spare--; + if(spare<0) + for(i=0;iil; + else if(len == sizeof(char)) x = n->ic; +#ifdef Allow_TYQUAD + else if (len == sizeof(longint)) x = n->ili; +#endif + else x=n->is; + ans=f__icvt(x,&ndigit,&sign, base); + if(sign || f__cplus) xsign=1; + else xsign=0; + if(ndigit+xsign>w || m+xsign>w) + { for(i=0;i=m) + spare=w-ndigit-xsign; + else + spare=w-m-xsign; + for(i=0;iil; + else if(sz == sizeof(char)) x = n->ic; + else x=n->is; + for(i=0;i 0) (*f__putn)(*p++); + return(0); +} + +static int wrt_AW(char * p, int w, ftnlen len) +{ + while(w>len) + { w--; + (*f__putn)(' '); + } + while(w-- > 0) + (*f__putn)(*p++); + return(0); +} + +static int wrt_G(ufloat *p, int w, int d, int e, ftnlen len) +{ double up = 1,x; + int i=0,oldscale,n,j; + x = len==sizeof(real)?p->pf:p->pd; + if(x < 0 ) x = -x; + if(x<.1) { + if (x != 0.) + return(wrt_E(p,w,d,e,len)); + i = 1; + goto have_i; + } + for(;i<=d;i++,up*=10) + { if(x>=up) continue; + have_i: + oldscale = f__scale; + f__scale = 0; + if(e==0) n=4; + else n=e+2; + i=wrt_F(p,w-n,d-i,len); + for(j=0;jop) + { + default: + fprintf(stderr,"w_ed, unexpected code: %d\n", p->op); + sig_die(f__fmtbuf, 1); + case I: return(wrt_I((Uint *)ptr,p->p1,len, 10)); + case IM: + return(wrt_IM((Uint *)ptr,p->p1,p->p2.i[0],len,10)); + + /* O and OM don't work right for character, double, complex, */ + /* or doublecomplex, and they differ from Fortran 90 in */ + /* showing a minus sign for negative values. */ + + case O: return(wrt_I((Uint *)ptr, p->p1, len, 8)); + case OM: + return(wrt_IM((Uint *)ptr,p->p1,p->p2.i[0],len,8)); + case L: return(wrt_L((Uint *)ptr,p->p1, len)); + case A: return(wrt_A(ptr,len)); + case AW: + return(wrt_AW(ptr,p->p1,len)); + case D: + case E: + case EE: + return(wrt_E((ufloat *)ptr,p->p1,p->p2.i[0],p->p2.i[1],len)); + case G: + case GE: + return(wrt_G((ufloat *)ptr,p->p1,p->p2.i[0],p->p2.i[1],len)); + case F: return(wrt_F((ufloat *)ptr,p->p1,p->p2.i[0],len)); + + /* Z and ZM assume 8-bit bytes. */ + + case Z: return(wrt_Z((Uint *)ptr,p->p1,0,len)); + case ZM: + return(wrt_Z((Uint *)ptr,p->p1,p->p2.i[0],len)); + } +} + +int w_ned(struct syl *p) +{ + switch(p->op) + { + default: fprintf(stderr,"w_ned, unexpected code: %d\n", p->op); + sig_die(f__fmtbuf, 1); + case SLASH: + return((*f__donewrec)()); + case T: f__cursor = p->p1-f__recpos - 1; + return(1); + case TL: f__cursor -= p->p1; + if(f__cursor < -f__recpos) /* TL1000, 1X */ + f__cursor = -f__recpos; + return(1); + case TR: + case X: + f__cursor += p->p1; + return(1); + case APOS: + return(wrt_AP(p->p2.s)); + case H: + return(wrt_H(p->p1,p->p2.s)); + } +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/wsfe.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/wsfe.c new file mode 100644 index 00000000..b772df32 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/wsfe.c @@ -0,0 +1,69 @@ +/*write sequential formatted external*/ +#include "f2c.h" +#include "fio.h" +#include "fmt.h" +extern int f__hiwater; + + int +x_wSL(Void) +{ + int n = f__putbuf('\n'); + f__hiwater = f__recpos = f__cursor = 0; + return(n == 0); +} + + static int +xw_end(Void) +{ + int n; + + if(f__nonl) { + f__putbuf(n = 0); + fflush(f__cf); + } + else + n = f__putbuf('\n'); + f__hiwater = f__recpos = f__cursor = 0; + return n; +} + + static int +xw_rev(Void) +{ + int n = 0; + if(f__workdone) { + n = f__putbuf('\n'); + f__workdone = 0; + } + f__hiwater = f__recpos = f__cursor = 0; + return n; +} + +integer s_wsfe(cilist *a) /*start*/ +{ int n; + if(!f__init) f_init(); + f__reading=0; + f__sequential=1; + f__formatted=1; + f__external=1; + if(n=c_sfe(a)) return(n); + f__elist=a; + f__hiwater = f__cursor=f__recpos=0; + f__nonl = 0; + f__scale=0; + f__fmtbuf=a->cifmt; + f__cf=f__curunit->ufd; + if(pars_f(f__fmtbuf)<0) err(a->cierr,100,"startio"); + f__putn= x_putc; + f__doed= w_ed; + f__doned= w_ned; + f__doend=xw_end; + f__dorevert=xw_rev; + f__donewrec=x_wSL; + fmt_bg(); + f__cplus=0; + f__cblank=f__curunit->ublnk; + if(f__curunit->uwrt != 1 && f__nowwriting(f__curunit)) + err(a->cierr,errno,"write start"); + return(0); +} diff --git a/src/imageplugins/coreplugin/sharpnesseditor/clapack/xerbla.c b/src/imageplugins/coreplugin/sharpnesseditor/clapack/xerbla.c new file mode 100644 index 00000000..d8ef512b --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/clapack/xerbla.c @@ -0,0 +1,58 @@ +#include "blaswrap.h" +#include "f2c.h" + +/* Subroutine */ int xerbla_(char *srname, integer *info) +{ +/* -- LAPACK auxiliary routine (preliminary version) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + February 29, 1992 + + + Purpose + ======= + + XERBLA is an error handler for the LAPACK routines. + It is called by an LAPACK routine if an input parameter has an + invalid value. A message is printed and execution stops. + + Installers may consider modifying the STOP statement in order to + call system-specific exception-handling facilities. + + Arguments + ========= + + SRNAME (input) CHARACTER*6 + The name of the routine which called XERBLA. + + INFO (input) INTEGER + The position of the invalid parameter in the parameter list + of the calling routine. */ + /* Table of constant values */ + static integer c__1 = 1; + + /* Format strings */ + static char fmt_9999[] = "(\002 ** On entry to \002,a6,\002 parameter nu" + "mber \002,i2,\002 had \002,\002an illegal value\002)"; + /* Builtin functions */ + integer s_wsfe(cilist *), do_fio(integer *, char *, ftnlen), e_wsfe(void); + /* Subroutine */ int s_stop(char *, ftnlen); + /* Fortran I/O blocks */ + static cilist io___1 = { 0, 6, 0, fmt_9999, 0 }; + + + + + s_wsfe(&io___1); + do_fio(&c__1, srname, (ftnlen)6); + do_fio(&c__1, (char *)&(*info), (ftnlen)sizeof(integer)); + e_wsfe(); + + s_stop("", (ftnlen)0); + + +/* End of XERBLA */ + + return 0; +} /* xerbla_ */ + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.cpp b/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.cpp new file mode 100644 index 00000000..b2ae9a62 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.cpp @@ -0,0 +1,696 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to sharp an image + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define MAX_MATRIX_SIZE 25 + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "dimgsharpen.h" +#include "unsharp.h" +#include "refocus.h" +#include "imageeffect_sharpen.h" +#include "imageeffect_sharpen.moc" + +namespace DigikamImagesPluginCore +{ + +ImageEffect_Sharpen::ImageEffect_Sharpen(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Sharpening Photograph"), "sharpen", + true, true, true) +{ + setHelp("blursharpentool.anchor", "digikam"); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 2, 1, 0, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Method:"), gboxSettings); + + m_sharpMethod = new TQComboBox( false, gboxSettings ); + m_sharpMethod->insertItem( i18n("Simple sharp") ); + m_sharpMethod->insertItem( i18n("Unsharp mask") ); + m_sharpMethod->insertItem( i18n("Refocus") ); + TQWhatsThis::add( m_sharpMethod, i18n("

    Select the sharpening method to apply to the image.")); + + m_stack = new TQWidgetStack(gboxSettings); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_sharpMethod, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(new KSeparator(gboxSettings), 1, 1, 0, 1); + gridSettings->addMultiCellWidget(m_stack, 2, 2, 0, 1); + + // ------------------------------------------------------------- + + TQWidget *simpleSharpSettings = new TQWidget(m_stack); + TQGridLayout* grid1 = new TQGridLayout( simpleSharpSettings, 2, 1, 0, spacingHint()); + + TQLabel *label = new TQLabel(i18n("Sharpness:"), simpleSharpSettings); + m_radiusInput = new KIntNumInput(simpleSharpSettings); + m_radiusInput->setRange(0, 100, 1, true); + m_radiusInput->setValue(0); + TQWhatsThis::add( m_radiusInput, i18n("

    A sharpness of 0 has no effect, " + "1 and above determine the sharpen matrix radius " + "that determines how much to sharpen the image.")); + + grid1->addMultiCellWidget(label, 0, 0, 0, 1); + grid1->addMultiCellWidget(m_radiusInput, 1, 1, 0, 1); + grid1->setRowStretch(2, 10); + m_stack->addWidget(simpleSharpSettings, SimpleSharp); + + // ------------------------------------------------------------- + + TQWidget *unsharpMaskSettings = new TQWidget(m_stack); + TQGridLayout* grid2 = new TQGridLayout( unsharpMaskSettings, 6, 1, 0, spacingHint()); + + TQLabel *label2 = new TQLabel(i18n("Radius:"), unsharpMaskSettings); + m_radiusInput2 = new KIntNumInput(unsharpMaskSettings); + m_radiusInput2->setRange(1, 120, 1, true); + TQWhatsThis::add( m_radiusInput2, i18n("

    Radius value is the gaussian blur matrix radius value " + "used to determines how much to blur the image.") ); + + TQLabel *label3 = new TQLabel(i18n("Amount:"), unsharpMaskSettings); + m_amountInput = new KDoubleNumInput(unsharpMaskSettings); + m_amountInput->setPrecision(1); + m_amountInput->setRange(0.0, 5.0, 0.1, true); + TQWhatsThis::add( m_amountInput, i18n("

    The value of the difference between the " + "original and the blur image that is added back into the original.") ); + + TQLabel *label4 = new TQLabel(i18n("Threshold:"), unsharpMaskSettings); + m_thresholdInput = new KDoubleNumInput(unsharpMaskSettings); + m_thresholdInput->setPrecision(2); + m_thresholdInput->setRange(0.0, 1.0, 0.01, true); + TQWhatsThis::add( m_thresholdInput, i18n("

    The threshold, as a fraction of the maximum " + "luminosity value, needed to apply the difference amount.") ); + + grid2->addMultiCellWidget(label2, 0, 0, 0, 1); + grid2->addMultiCellWidget(m_radiusInput2, 1, 1, 0, 1); + grid2->addMultiCellWidget(label3, 2, 2, 0, 1); + grid2->addMultiCellWidget(m_amountInput, 3, 3, 0, 1); + grid2->addMultiCellWidget(label4, 4, 4, 0, 1); + grid2->addMultiCellWidget(m_thresholdInput, 5, 5, 0, 1); + grid2->setRowStretch(6, 10); + m_stack->addWidget(unsharpMaskSettings, UnsharpMask); + + // ------------------------------------------------------------- + + TQWidget *refocusSettings = new TQWidget(m_stack); + TQGridLayout* grid3 = new TQGridLayout(refocusSettings, 10, 1, 0, spacingHint()); + + TQLabel *label5 = new TQLabel(i18n("Circular sharpness:"), refocusSettings); + m_radius = new KDoubleNumInput(refocusSettings); + m_radius->setPrecision(2); + m_radius->setRange(0.0, 5.0, 0.01, true); + TQWhatsThis::add( m_radius, i18n("

    This is the radius of the circular convolution. It is the most important " + "parameter for using this plugin. For most images the default value of 1.0 " + "should give good results. Select a higher value when your image is very blurred.")); + + TQLabel *label6 = new TQLabel(i18n("Correlation:"), refocusSettings); + m_correlation = new KDoubleNumInput(refocusSettings); + m_correlation->setPrecision(2); + m_correlation->setRange(0.0, 1.0, 0.01, true); + TQWhatsThis::add( m_correlation, i18n("

    Increasing the correlation may help to reduce artifacts. The correlation can " + "range from 0-1. Useful values are 0.5 and values close to 1, e.g. 0.95 and 0.99. " + "Using a high value for the correlation will reduce the sharpening effect of the " + "plugin.")); + + TQLabel *label7 = new TQLabel(i18n("Noise filter:"), refocusSettings); + m_noise = new KDoubleNumInput(refocusSettings); + m_noise->setPrecision(3); + m_noise->setRange(0.0, 1.0, 0.001, true); + TQWhatsThis::add( m_noise, i18n("

    Increasing the noise filter parameter may help to reduce artifacts. The noise filter " + "can range from 0-1 but values higher than 0.1 are rarely helpful. When the noise filter " + "value is too low, e.g. 0.0 the image quality will be very poor. A useful value is 0.01. " + "Using a high value for the noise filter will reduce the sharpening " + "effect of the plugin.")); + + TQLabel *label8 = new TQLabel(i18n("Gaussian sharpness:"), refocusSettings); + m_gauss = new KDoubleNumInput(refocusSettings); + m_gauss->setPrecision(2); + m_gauss->setRange(0.0, 1.0, 0.01, true); + TQWhatsThis::add( m_gauss, i18n("

    This is the sharpness for the gaussian convolution. Use this parameter when your " + "blurring is of a Gaussian type. In most cases you should set this parameter to 0, because " + "it causes nasty artifacts. When you use non-zero values, you will probably have to " + "increase the correlation and/or noise filter parameters too.")); + + TQLabel *label9 = new TQLabel(i18n("Matrix size:"), refocusSettings); + m_matrixSize = new KIntNumInput(refocusSettings); + m_matrixSize->setRange(0, MAX_MATRIX_SIZE, 1, true); + TQWhatsThis::add( m_matrixSize, i18n("

    This parameter determines the size of the transformation matrix. " + "Increasing the matrix width may give better results, especially when you have " + "chosen large values for circular or gaussian sharpness.")); + + grid3->addMultiCellWidget(label5, 0, 0, 0, 1); + grid3->addMultiCellWidget(m_radius, 1, 1, 0, 1); + grid3->addMultiCellWidget(label6, 2, 2, 0, 1); + grid3->addMultiCellWidget(m_correlation, 3, 3, 0, 1); + grid3->addMultiCellWidget(label7, 4, 4, 0, 1); + grid3->addMultiCellWidget(m_noise, 5, 5, 0, 1); + grid3->addMultiCellWidget(label8, 6, 6, 0, 1); + grid3->addMultiCellWidget(m_gauss, 7, 7, 0, 1); + grid3->addMultiCellWidget(label9, 8, 8, 0, 1); + grid3->addMultiCellWidget(m_matrixSize, 9, 9, 0, 1); + grid3->setRowStretch(10, 10); + m_stack->addWidget(refocusSettings, Refocus); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_sharpMethod, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotSharpMethodActived(int))); + + // ------------------------------------------------------------- + + // Image creation with dummy borders (mosaic mode) used by Refocus method. It needs to do + // it before to apply deconvolution filter on original image border pixels including + // on matrix size area. This way limit artifacts on image border. + + Digikam::ImageIface iface(0, 0); + + uchar* data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sb = iface.originalSixteenBit(); + bool a = iface.originalHasAlpha(); + + m_img = Digikam::DImg( w + 4*MAX_MATRIX_SIZE, h + 4*MAX_MATRIX_SIZE, sb, a); + + Digikam::DImg tmp; + Digikam::DImg org(w, h, sb, a, data); + + // Copy original. + m_img.bitBltImage(&org, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + + // Create dummy top border + tmp = org.copy(0, 0, w, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, 2*MAX_MATRIX_SIZE, 0); + + // Create dummy bottom border + tmp = org.copy(0, h-2*MAX_MATRIX_SIZE, w, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE+h); + + // Create dummy left border + tmp = org.copy(0, 0, 2*MAX_MATRIX_SIZE, h); + tmp.flip(Digikam::DImg::HORIZONTAL); + m_img.bitBltImage(&tmp, 0, 2*MAX_MATRIX_SIZE); + + // Create dummy right border + tmp = org.copy(w-2*MAX_MATRIX_SIZE, 0, 2*MAX_MATRIX_SIZE, h); + tmp.flip(Digikam::DImg::HORIZONTAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + + // Create dummy top/left corner + tmp = org.copy(0, 0, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::HORIZONTAL); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, 0, 0); + + // Create dummy top/right corner + tmp = org.copy(w-2*MAX_MATRIX_SIZE, 0, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::HORIZONTAL); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, 0); + + // Create dummy bottom/left corner + tmp = org.copy(0, h-2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::HORIZONTAL); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, 0, h+2*MAX_MATRIX_SIZE); + + // Create dummy bottom/right corner + tmp = org.copy(w-2*MAX_MATRIX_SIZE, h-2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(Digikam::DImg::HORIZONTAL); + tmp.flip(Digikam::DImg::VERTICAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, h+2*MAX_MATRIX_SIZE); + + delete [] data; +} + +ImageEffect_Sharpen::~ImageEffect_Sharpen() +{ +} + +void ImageEffect_Sharpen::renderingFinished(void) +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(true); + enableButton(User2, false); + enableButton(User3, false); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(true); + m_amountInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + enableButton(User2, false); + enableButton(User3, false); + break; + } + + case Refocus: + { + m_matrixSize->setEnabled(true); + m_radius->setEnabled(true); + m_gauss->setEnabled(true); + m_correlation->setEnabled(true); + m_noise->setEnabled(true); + break; + } + } +} + +void ImageEffect_Sharpen::slotSharpMethodActived(int w) +{ + m_stack->raiseWidget(w); + if (w == Refocus) + { + enableButton(User2, true); + enableButton(User3, true); + } + else + { + enableButton(User2, false); + enableButton(User3, false); + } +} + +void ImageEffect_Sharpen::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("sharpen Tool Dialog"); + m_radiusInput->blockSignals(true); + m_radiusInput2->blockSignals(true); + m_amountInput->blockSignals(true); + m_thresholdInput->blockSignals(true); + m_matrixSize->blockSignals(true); + m_radius->blockSignals(true); + m_gauss->blockSignals(true); + m_correlation->blockSignals(true); + m_noise->blockSignals(true); + m_sharpMethod->blockSignals(true); + m_radiusInput->setValue(config->readNumEntry("SimpleSharpRadiusAjustment", 0)); + m_radiusInput2->setValue(config->readNumEntry("UnsharpMaskRadiusAjustment", 1)); + m_amountInput->setValue(config->readDoubleNumEntry("UnsharpMaskAmountAjustment", 1.0)); + m_thresholdInput->setValue(config->readDoubleNumEntry("UnsharpMaskThresholdAjustment", 0.05)); + m_matrixSize->setValue(config->readNumEntry("RefocusMatrixSize", 5)); + m_radius->setValue(config->readDoubleNumEntry("RefocusRadiusAjustment", 1.0)); + m_gauss->setValue(config->readDoubleNumEntry("RefocusGaussAjustment", 0.0)); + m_correlation->setValue(config->readDoubleNumEntry("RefocusCorrelationAjustment", 0.5)); + m_noise->setValue(config->readDoubleNumEntry("RefocusNoiseAjustment", 0.03)); + m_sharpMethod->setCurrentItem(config->readNumEntry("SharpenMethod", SimpleSharp)); + m_radiusInput->blockSignals(false); + m_radiusInput2->blockSignals(false); + m_amountInput->blockSignals(false); + m_thresholdInput->blockSignals(false); + m_matrixSize->blockSignals(false); + m_radius->blockSignals(false); + m_gauss->blockSignals(false); + m_correlation->blockSignals(false); + m_noise->blockSignals(false); + m_sharpMethod->blockSignals(false); + slotSharpMethodActived(m_sharpMethod->currentItem()); +} + +void ImageEffect_Sharpen::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("sharpen Tool Dialog"); + config->writeEntry("SimpleSharpRadiusAjustment", m_radiusInput->value()); + config->writeEntry("UnsharpMaskRadiusAjustment", m_radiusInput2->value()); + config->writeEntry("UnsharpMaskAmountAjustment", m_amountInput->value()); + config->writeEntry("UnsharpMaskThresholdAjustment", m_thresholdInput->value()); + config->writeEntry("RefocusMatrixSize", m_matrixSize->value()); + config->writeEntry("RefocusRadiusAjustment", m_radius->value()); + config->writeEntry("RefocusGaussAjustment", m_gauss->value()); + config->writeEntry("RefocusCorrelationAjustment", m_correlation->value()); + config->writeEntry("RefocusNoiseAjustment", m_noise->value()); + config->writeEntry("SharpenMethod", m_sharpMethod->currentItem()); + config->sync(); +} + +void ImageEffect_Sharpen::resetValues(void) +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->blockSignals(true); + m_radiusInput->setValue(0); + m_radiusInput->blockSignals(false); + break; + } + + case UnsharpMask: + { + m_radiusInput2->blockSignals(true); + m_amountInput->blockSignals(true); + m_thresholdInput->blockSignals(true); + m_radiusInput2->setValue(1); + m_amountInput->setValue(1.0); + m_thresholdInput->setValue(0.05); + m_radiusInput2->blockSignals(false); + m_amountInput->blockSignals(false); + m_thresholdInput->blockSignals(false); + break; + } + + case Refocus: + { + m_matrixSize->blockSignals(true); + m_radius->blockSignals(true); + m_gauss->blockSignals(true); + m_correlation->blockSignals(true); + m_noise->blockSignals(true); + m_matrixSize->setValue(5); + m_radius->setValue(1.0); + m_gauss->setValue(0.0); + m_correlation->setValue(0.5); + m_noise->setValue(0.03); + m_matrixSize->blockSignals(false); + m_radius->blockSignals(false); + m_gauss->blockSignals(false); + m_correlation->blockSignals(false); + m_noise->blockSignals(false); + break; + } + } +} + +void ImageEffect_Sharpen::prepareEffect() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(false); + + Digikam::DImg img = m_imagePreviewWidget->getOriginalRegionImage(); + + double radius = m_radiusInput->value()/10.0; + double sigma; + + if (radius < 1.0) sigma = radius; + else sigma = sqrt(radius); + + m_threadedFilter = dynamic_cast + (new Digikam::DImgSharpen(&img, this, radius, sigma )); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(false); + m_amountInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + + Digikam::DImg img = m_imagePreviewWidget->getOriginalRegionImage(); + + int r = m_radiusInput2->value(); + double a = m_amountInput->value(); + double th = m_thresholdInput->value(); + + m_threadedFilter = dynamic_cast + (new DigikamImagesPluginCore::UnsharpMask(&img, this, r, a, th)); + break; + } + + case Refocus: + { + m_matrixSize->setEnabled(false); + m_radius->setEnabled(false); + m_gauss->setEnabled(false); + m_correlation->setEnabled(false); + m_noise->setEnabled(false); + + int ms = m_matrixSize->value(); + double r = m_radius->value(); + double g = m_gauss->value(); + double c = m_correlation->value(); + double n = m_noise->value(); + + TQRect area = m_imagePreviewWidget->getOriginalImageRegionToRender(); + TQRect tmpRect; + tmpRect.setLeft(area.left()-2*ms); + tmpRect.setRight(area.right()+2*ms); + tmpRect.setTop(area.top()-2*ms); + tmpRect.setBottom(area.bottom()+2*ms); + tmpRect.moveBy(2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + Digikam::DImg imTemp = m_img.copy(tmpRect); + + m_threadedFilter = dynamic_cast + (new DigikamImagesPluginCore::Refocus(&imTemp, this, ms, r, g, c, n)); + break; + } + } +} + +void ImageEffect_Sharpen::prepareFinal() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(false); + + double radius = m_radiusInput->value()/10.0; + double sigma; + + if (radius < 1.0) sigma = radius; + else sigma = sqrt(radius); + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + Digikam::DImg orgImage = Digikam::DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + m_threadedFilter = dynamic_cast + (new Digikam::DImgSharpen(&orgImage, this, radius, sigma )); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(false); + m_amountInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + + int r = m_radiusInput2->value(); + double a = m_amountInput->value(); + double th = m_thresholdInput->value(); + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + Digikam::DImg orgImage = Digikam::DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + m_threadedFilter = dynamic_cast + (new DigikamImagesPluginCore::UnsharpMask(&orgImage, this, r, a, th)); + break; + } + + case Refocus: + { + + m_matrixSize->setEnabled(false); + m_radius->setEnabled(false); + m_gauss->setEnabled(false); + m_correlation->setEnabled(false); + m_noise->setEnabled(false); + + int ms = m_matrixSize->value(); + double r = m_radius->value(); + double g = m_gauss->value(); + double c = m_correlation->value(); + double n = m_noise->value(); + + m_threadedFilter = dynamic_cast + (new DigikamImagesPluginCore::Refocus(&m_img, this, ms, r, g, c, n)); + break; + } + } +} + +void ImageEffect_Sharpen::putPreviewData(void) +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + case UnsharpMask: + { + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + m_imagePreviewWidget->setPreviewImage(imDest); + break; + } + + case Refocus: + { + int ms = m_matrixSize->value(); + TQRect area = m_imagePreviewWidget->getOriginalImageRegionToRender(); + + Digikam::DImg imDest = m_threadedFilter->getTargetImage() + .copy(2*ms, 2*ms, area.width(), area.height()); + m_imagePreviewWidget->setPreviewImage(imDest); + break; + } + } +} + +void ImageEffect_Sharpen::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + iface.putOriginalImage(i18n("Sharpen"), imDest.bits()); + break; + } + + case UnsharpMask: + { + iface.putOriginalImage(i18n("Unsharp Mask"), imDest.bits()); + break; + } + + case Refocus: + { + TQRect area = m_imagePreviewWidget->getOriginalImageRegionToRender(); + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Refocus"), m_threadedFilter->getTargetImage() + .copy(2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, + iface.originalWidth(), + iface.originalHeight()) + .bits()); + break; + } + } +} + +void ImageEffect_Sharpen::slotUser3() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Refocus Settings File to Load")) ); + if ( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + if ( stream.readLine() != "# Photograph Refocus Configuration File" ) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Photograph Refocus settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_matrixSize->setValue( stream.readLine().toInt() ); + m_radius->setValue( stream.readLine().toDouble() ); + m_gauss->setValue( stream.readLine().toDouble() ); + m_correlation->setValue( stream.readLine().toDouble() ); + m_noise->setValue( stream.readLine().toDouble() ); + blockSignals(false); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Photograph Refocus text file.")); + + file.close(); +} + +void ImageEffect_Sharpen::slotUser2() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Refocus Settings File to Save")) ); + if ( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Photograph Refocus Configuration File\n"; + stream << m_matrixSize->value() << "\n"; + stream << m_radius->value() << "\n"; + stream << m_gauss->value() << "\n"; + stream << m_correlation->value() << "\n"; + stream << m_noise->value() << "\n"; + } + else + KMessageBox::error(this, i18n("Cannot save settings to the Photograph Refocus text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.h b/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.h new file mode 100644 index 00000000..fa926383 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/imageeffect_sharpen.h @@ -0,0 +1,102 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to sharp an image + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_SHARPEN_H +#define IMAGEEFFECT_SHARPEN_H + +// Digikam include. + +#include "ctrlpaneldlg.h" + +class TQComboBox; +class TQWidgetStack; + +class KIntNumInput; +class KDoubleNumInput; + +namespace Digikam +{ + class DImg; +} + +namespace DigikamImagesPluginCore +{ + +class ImageEffect_Sharpen : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Sharpen(TQWidget *parent); + ~ImageEffect_Sharpen(); + +private slots: + + void slotUser2(); + void slotUser3(); + void readUserSettings(); + void slotSharpMethodActived(int); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum SharpingMethods + { + SimpleSharp=0, + UnsharpMask, + Refocus + }; + + TQWidgetStack *m_stack; + + TQComboBox *m_sharpMethod; + + KIntNumInput *m_matrixSize; + KIntNumInput *m_radiusInput; + KIntNumInput *m_radiusInput2; + + KDoubleNumInput *m_radius; + KDoubleNumInput *m_gauss; + KDoubleNumInput *m_correlation; + KDoubleNumInput *m_noise; + KDoubleNumInput *m_amountInput; + KDoubleNumInput *m_thresholdInput; + + Digikam::DImg m_img; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* IMAGEEFFECT_SHARPEN_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/matrix.cpp b/src/imageplugins/coreplugin/sharpnesseditor/matrix.cpp new file mode 100644 index 00000000..cfb8afe4 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/matrix.cpp @@ -0,0 +1,663 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-29 + * Description : refocus deconvolution matrix implementation. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original implementation from Refocus Gimp plug-in + * Copyright (C) 1999-2003 Ernst Lippe + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Uncomment this line to debug matrix computation. +//#define RF_DEBUG 1 + +// Square +#define SQR(x) ((x) * (x)) + +// C++ includes. + +#include + +extern "C" +{ +#include "f2c.h" +#include "clapack.h" +} + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "matrix.h" + +namespace DigikamImagesPluginCore +{ + +Mat *RefocusMatrix::allocate_matrix (int nrows, int ncols) +{ + Mat *result = new Mat; + memset (result, 0, sizeof(result)); + + result->cols = ncols; + result->rows = nrows; + result->data = new double [nrows * ncols]; + memset (result->data, 0, nrows * ncols * sizeof(double)); + + return (result); +} + +void RefocusMatrix::finish_matrix (Mat * mat) +{ + delete [] mat->data; +} + +void RefocusMatrix::finish_and_free_matrix (Mat * mat) +{ + delete [] mat->data; + delete mat; +} + +double *RefocusMatrix::mat_eltptr (Mat * mat, const int r, const int c) +{ + Q_ASSERT ((r >= 0) && (r < mat->rows)); + Q_ASSERT ((c >= 0) && (c < mat->rows)); + return (&(mat->data[mat->rows * c + r])); +} + +double RefocusMatrix::mat_elt (const Mat * mat, const int r, const int c) +{ + Q_ASSERT ((r >= 0) && (r < mat->rows)); + Q_ASSERT ((c >= 0) && (c < mat->rows)); + return (mat->data[mat->rows * c + r]); +} + +void RefocusMatrix::init_c_mat (CMat * mat, const int radius) +{ + mat->radius = radius; + mat->row_stride = 2 * radius + 1; + mat->data = new double [SQR (mat->row_stride)]; + memset (mat->data, 0, SQR (mat->row_stride) * sizeof(double)); + mat->center = mat->data + mat->row_stride * mat->radius + mat->radius; +} + +CMat *RefocusMatrix::allocate_c_mat (const int radius) +{ + CMat *result = new CMat; + memset(result, 0, sizeof(result)); + init_c_mat (result, radius); + return (result); +} + +void RefocusMatrix::finish_c_mat (CMat * mat) +{ + delete [] mat->data; + mat->data = NULL; +} + +inline double *RefocusMatrix::c_mat_eltptr (CMat * mat, const int col, const int row) +{ + Q_ASSERT ((TQABS (row) <= mat->radius) && (TQABS (col) <= mat->radius)); + return (mat->center + mat->row_stride * row + col); +} + +inline double RefocusMatrix::c_mat_elt (const CMat * const mat, const int col, const int row) +{ + Q_ASSERT ((TQABS (row) <= mat->radius) && (TQABS (col) <= mat->radius)); + return (mat->center[mat->row_stride * row + col]); +} + +void RefocusMatrix::convolve_mat (CMat * result, const CMat * const mata, const CMat * const matb) +{ + int xr, yr, xa, ya; + + for (yr = -result->radius; yr <= result->radius; yr++) + { + for (xr = -result->radius; xr <= result->radius; xr++) + { + const int ya_low = TQMAX (-mata->radius, yr - matb->radius); + const int ya_high = TQMIN (mata->radius, yr + matb->radius); + const int xa_low = TQMAX (-mata->radius, xr - matb->radius); + const int xa_high = TQMIN (mata->radius, xr + matb->radius); + double val = 0.0; + + for (ya = ya_low; ya <= ya_high; ya++) + { + for (xa = xa_low; xa <= xa_high; xa++) + { + val += c_mat_elt (mata, xa, ya) * + c_mat_elt (matb, xr - xa, yr - ya); + } + } + + *c_mat_eltptr (result, xr, yr) = val; + } + } +} + +void RefocusMatrix::convolve_star_mat (CMat * result, const CMat * const mata, const CMat * const matb) +{ + int xr, yr, xa, ya; + + for (yr = -result->radius; yr <= result->radius; yr++) + { + for (xr = -result->radius; xr <= result->radius; xr++) + { + const int ya_low = TQMAX (-mata->radius, -matb->radius - yr); + const int ya_high = TQMIN (mata->radius, matb->radius - yr); + const int xa_low = TQMAX (-mata->radius, -matb->radius - xr); + const int xa_high = TQMIN (mata->radius, matb->radius - xr); + double val = 0.0; + + for (ya = ya_low; ya <= ya_high; ya++) + { + for (xa = xa_low; xa <= xa_high; xa++) + { + val += c_mat_elt (mata, xa, ya) * + c_mat_elt (matb, xr + xa, yr + ya); + } + } + + *c_mat_eltptr (result, xr, yr) = val; + } + } +} + +void RefocusMatrix::convolve_mat_fun (CMat * result, const CMat * const mata, double (f) (int, int)) +{ + int xr, yr, xa, ya; + + for (yr = -result->radius; yr <= result->radius; yr++) + { + for (xr = -result->radius; xr <= result->radius; xr++) + { + double val = 0.0; + + for (ya = -mata->radius; ya <= mata->radius; ya++) + { + for (xa = -mata->radius; xa <= mata->radius; xa++) + { + val += c_mat_elt (mata, xa, ya) * f (xr - xa, yr - ya); + } + } + + *c_mat_eltptr (result, xr, yr) = val; + } + } +} + +int RefocusMatrix::as_idx (const int k, const int l, const int m) +{ + return ((k + m) * (2 * m + 1) + (l + m)); +} + +int RefocusMatrix::as_cidx (const int k, const int l) +{ + const int a = TQMAX (TQABS (k), TQABS (l)); + const int b = TQMIN (TQABS (k), TQABS (l)); + return ((a * (a + 1)) / 2 + b); +} + +void RefocusMatrix::print_c_mat (const CMat * const mat) +{ + int x, y; + + for (y = -mat->radius; y <= mat->radius; y++) + { + TQString output, num; + + for (x = -mat->radius; x <= mat->radius; x++) + { + output.append( num.setNum( c_mat_elt (mat, x, y) ) ); + } + + DDebug() << output << endl; + } +} + +void RefocusMatrix::print_matrix (Mat * matrix) +{ + int col_idx, row_idx; + + for (row_idx = 0; row_idx < matrix->rows; row_idx++) + { + TQString output, num; + + for (col_idx = 0; col_idx < matrix->cols; col_idx++) + { + output.append( num.setNum( mat_elt (matrix, row_idx, col_idx) ) ); + } + + DDebug() << output << endl; + } +} + +Mat *RefocusMatrix::make_s_matrix (CMat * mat, int m, double noise_factor) +{ + const int mat_size = SQR (2 * m + 1); + Mat *result = allocate_matrix (mat_size, mat_size); + int yr, yc, xr, xc; + + for (yr = -m; yr <= m; yr++) + { + for (xr = -m; xr <= m; xr++) + { + for (yc = -m; yc <= m; yc++) + { + for (xc = -m; xc <= m; xc++) + { + *mat_eltptr (result, as_idx (xr, yr, m), + as_idx (xc, yc, m)) = + c_mat_elt (mat, xr - xc, yr - yc); + if ((xr == xc) && (yr == yc)) + { + *mat_eltptr (result, as_idx (xr, yr, m), + as_idx (xc, yc, m)) += noise_factor; + } + } + } + } + } + + return (result); +} + +Mat *RefocusMatrix::make_s_cmatrix (CMat * mat, int m, double noise_factor) +{ + const int mat_size = as_cidx (m + 1, 0); + Mat *result = allocate_matrix (mat_size, mat_size); + int yr, yc, xr, xc; + + for (yr = 0; yr <= m; yr++) + { + for (xr = 0; xr <= yr; xr++) + { + for (yc = -m; yc <= m; yc++) + { + for (xc = -m; xc <= m; xc++) + { + *mat_eltptr (result, as_cidx (xr, yr), as_cidx (xc, yc)) += + c_mat_elt (mat, xr - xc, yr - yc); + if ((xr == xc) && (yr == yc)) + { + *mat_eltptr (result, as_cidx (xr, yr), + as_cidx (xc, yc)) += noise_factor; + } + } + } + } + } + + return (result); +} + +double RefocusMatrix::correlation (const int x, const int y, const double gamma, const double musq) +{ + return (musq + pow (gamma, sqrt (SQR (x) + SQR (y)))); +} + +Mat *RefocusMatrix::copy_vec (const CMat * const mat, const int m) +{ + Mat *result = allocate_matrix (SQR (2 * m + 1), 1); + int x, y, index = 0; + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *mat_eltptr (result, index, 0) = c_mat_elt (mat, x, y); + index++; + } + } + + Q_ASSERT (index == SQR (2 * m + 1)); + return (result); +} + +Mat *RefocusMatrix::copy_cvec (const CMat * const mat, const int m) +{ + Mat *result = allocate_matrix (as_cidx (m + 1, 0), 1); + int x, y, index = 0; + + for (y = 0; y <= m; y++) + { + for (x = 0; x <= y; x++) + { + *mat_eltptr (result, index, 0) = c_mat_elt (mat, x, y); + index++; + } + } + + Q_ASSERT (index == as_cidx (m + 1, 0)); + return (result); +} + +CMat *RefocusMatrix::copy_cvec2mat (const Mat * const cvec, const int m) +{ + CMat *result = allocate_c_mat (m); + int x, y; + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (result, x, y) = mat_elt (cvec, as_cidx (x, y), 0); + } + } + + return (result); +} + +CMat *RefocusMatrix::copy_vec2mat (const Mat * const cvec, const int m) +{ + CMat *result = allocate_c_mat (m); + int x, y; + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (result, x, y) = mat_elt (cvec, as_idx (x, y, m), 0); + } + } + + return (result); +} + +CMat *RefocusMatrix::compute_g (const CMat * const convolution, const int m, const double gamma, + const double noise_factor, const double musq, const bool symmetric) +{ + CMat h_conv_ruv, a, corr; + CMat *result; + Mat *b; + Mat *s; + int status; + + init_c_mat (&h_conv_ruv, 3 * m); + fill_matrix2 (&corr, 4 * m, correlation, gamma, musq); + convolve_mat (&h_conv_ruv, convolution, &corr); + init_c_mat (&a, 2 * m); + convolve_star_mat (&a, convolution, &h_conv_ruv); + + if (symmetric) + { + s = make_s_cmatrix (&a, m, noise_factor); + b = copy_cvec (&h_conv_ruv, m); + } + else + { + s = make_s_matrix (&a, m, noise_factor); + b = copy_vec (&h_conv_ruv, m); + } + +#ifdef RF_DEBUG + DDebug() << "Convolution:" << endl; + print_c_mat (convolution); + DDebug() << "h_conv_ruv:" << endl; + print_c_mat (&h_conv_ruv); + DDebug() << "Value of s:" << endl; + print_matrix (s); +#endif + + Q_ASSERT (s->cols == s->rows); + Q_ASSERT (s->rows == b->rows); + status = dgesv (s->rows, 1, s->data, s->rows, b->data, b->rows); + + if (symmetric) + { + result = copy_cvec2mat (b, m); + } + else + { + result = copy_vec2mat (b, m); + } + +#ifdef RF_DEBUG + DDebug() << "Deconvolution:" << endl; + print_c_mat (result); +#endif + + finish_c_mat (&a); + finish_c_mat (&h_conv_ruv); + finish_c_mat (&corr); + finish_and_free_matrix (s); + finish_and_free_matrix (b); + return (result); +} + +CMat *RefocusMatrix::compute_g_matrix (const CMat * const convolution, const int m, + const double gamma, const double noise_factor, + const double musq, const bool symmetric) +{ +#ifdef RF_DEBUG + DDebug() << "matrix size: " << m << endl; + DDebug() << "correlation: " << gamma << endl; + DDebug() << "noise: " << noise_factor << endl; +#endif + + CMat *g = compute_g (convolution, m, gamma, noise_factor, musq, symmetric); + int r, c; + double sum = 0.0; + + /* Determine sum of array */ + for (r = -g->radius; r <= g->radius; r++) + { + for (c = -g->radius; c <= g->radius; c++) + { + sum += c_mat_elt (g, r, c); + } + } + + for (r = -g->radius; r <= g->radius; r++) + { + for (c = -g->radius; c <= g->radius; c++) + { + *c_mat_eltptr (g, r, c) /= sum; + } + } + + return (g); +} + +void RefocusMatrix::fill_matrix (CMat * matrix, const int m, + double f (const int, const int, const double), + const double fun_arg) +{ + int x, y; + init_c_mat (matrix, m); + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (matrix, x, y) = f (x, y, fun_arg); + } + } +} + +void RefocusMatrix::fill_matrix2 (CMat * matrix, const int m, + double f (const int, const int, const double, const double), + const double fun_arg1, const double fun_arg2) +{ + int x, y; + init_c_mat (matrix, m); + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (matrix, x, y) = f (x, y, fun_arg1, fun_arg2); + } + } +} + +void RefocusMatrix::make_gaussian_convolution (const double gradius, CMat * convolution, const int m) +{ + int x, y; + +#ifdef RF_DEBUG + DDebug() << "gauss: " << gradius << endl; +#endif + + init_c_mat (convolution, m); + + if (SQR (gradius) <= 1 / 3.40282347e38F) + { + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (convolution, x, y) = 0; + } + } + + *c_mat_eltptr (convolution, 0, 0) = 1; + } + else + { + const double alpha = log (2.0) / SQR (gradius); + + for (y = -m; y <= m; y++) + { + for (x = -m; x <= m; x++) + { + *c_mat_eltptr (convolution, x, y) = + exp (-alpha * (SQR (x) + SQR (y))); + } + } + } +} + +/** Return the integral of sqrt(radius^2 - z^2) for z = 0 to x. */ + +double RefocusMatrix::circle_integral (const double x, const double radius) +{ + if (radius == 0) + { + // Perhaps some epsilon must be added here. + return (0); + } + else + { + const double sin = x / radius; + const double sq_diff = SQR (radius) - SQR (x); + // From a mathematical point of view the following is redundant. + // Numerically they are not equivalent! + + if ((sq_diff < 0.0) || (sin < -1.0) || (sin > 1.0)) + { + if (sin < 0) + { + return (-0.25 * SQR (radius) * M_PI); + } + else + { + return (0.25 * SQR (radius) * M_PI); + } + } + else + { + return (0.5 * x * sqrt (sq_diff) + 0.5 * SQR (radius) * asin (sin)); + } + } +} + +double RefocusMatrix::circle_intensity (const int x, const int y, const double radius) +{ + if (radius == 0) + { + return (((x == 0) && (y == 0)) ? 1 : 0); + } + else + { + double xlo = TQABS (x) - 0.5, xhi = TQABS (x) + 0.5, + ylo = TQABS (y) - 0.5, yhi = TQABS (y) + 0.5; + double symmetry_factor = 1, xc1, xc2; + + if (xlo < 0) + { + xlo = 0; + symmetry_factor *= 2; + } + + if (ylo < 0) + { + ylo = 0; + symmetry_factor *= 2; + } + + if (SQR (xlo) + SQR (yhi) > SQR (radius)) + { + xc1 = xlo; + } + else if (SQR (xhi) + SQR (yhi) > SQR (radius)) + { + xc1 = sqrt (SQR (radius) - SQR (yhi)); + } + else + { + xc1 = xhi; + } + + if (SQR (xlo) + SQR (ylo) > SQR (radius)) + { + xc2 = xlo; + } + else if (SQR (xhi) + SQR (ylo) > SQR (radius)) + { + xc2 = sqrt (SQR (radius) - SQR (ylo)); + } + else + { + xc2 = xhi; + } + + return (((yhi - ylo) * (xc1 - xlo) + + circle_integral (xc2, radius) - circle_integral (xc1, radius) - + (xc2 - xc1) * ylo) * symmetry_factor / (M_PI * SQR (radius))); + } +} + +void RefocusMatrix::make_circle_convolution (const double radius, CMat * convolution, const int m) +{ +#ifdef RF_DEBUG + DDebug() << "radius: " << radius << endl; +#endif + + fill_matrix (convolution, m, circle_intensity, radius); +} + +int RefocusMatrix::dgesv (const int N, const int NRHS, double *A, const int lda, double *B, const int ldb) +{ + int result = 0; + integer i_N = N, i_NHRS = NRHS, i_lda = lda, i_ldb = ldb, info; + integer *ipiv = new integer[N]; + + // Clapack call. + dgesv_ (&i_N, &i_NHRS, A, &i_lda, ipiv, B, &i_ldb, &info); + + delete [] ipiv; + result = info; + return (result); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/sharpnesseditor/matrix.h b/src/imageplugins/coreplugin/sharpnesseditor/matrix.h new file mode 100644 index 00000000..6b2f65cb --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/matrix.h @@ -0,0 +1,129 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-04-29 + * Description : refocus deconvolution matrix implementation. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef MATRIX_H_INCLUDED +#define MATRIX_H_INCLUDED + +// C ++ includes. + +#include + +namespace DigikamImagesPluginCore +{ + +/** +* CMat: +* @radius: Radius of the matrix. +* +* Centered matrix. This is a square matrix where +* the indices range from [-radius, radius]. +* The matrix contains (2 * radius + 1) ** 2 elements. +* +**/ +typedef struct +{ + int radius; // Radius of the matrix + int row_stride; // Size of one row = 2 * radius + 1 + double *data; // Contents of matrix + double *center; // Points to element with index (0, 0) +} +CMat; + +/** +* Mat: +* @rows: Number of rows in the matrix. +* +* Normal matrix type. Indices range from +* [0, rows -1 ] and [0, cols - 1]. +* +**/ +typedef struct +{ + int rows; // Number of rows in the matrix + int cols; // Number of columns in the matrix + double *data; // Content of the matrix +} +Mat; + +class RefocusMatrix +{ + +public: + + static void fill_matrix (CMat * matrix, const int m, double f (int, int, double), const double fun_arg); + + static void fill_matrix2 (CMat * matrix, const int m, + double f (const int, const int, const double, const double), + const double fun_arg1, const double fun_arg2); + + static void make_circle_convolution (const double radius, CMat *convolution, const int m); + + static void make_gaussian_convolution (const double alpha, CMat *convolution, const int m); + + static void convolve_star_mat (CMat *result, const CMat *const mata, const CMat* const matb); + + static CMat *compute_g_matrix (const CMat * const convolution, const int m, + const double gamma, const double noise_factor, + const double musq, const bool symmetric); + + static void finish_matrix (Mat * mat); + static void finish_and_free_matrix (Mat * mat); + static void init_c_mat (CMat * mat, const int radius); + static void finish_c_mat (CMat * mat); + +private: + + // Debug methods. + static void print_c_mat (const CMat * const mat); + static void print_matrix (Mat * matrix); + + static Mat *allocate_matrix (int nrows, int ncols); + static double *mat_eltptr (Mat * mat, const int r, const int c); + static double mat_elt (const Mat * mat, const int r, const int c); + static CMat *allocate_c_mat (const int radius); + static inline double *c_mat_eltptr (CMat * mat, const int col, const int row); + static inline double c_mat_elt (const CMat * const mat, const int col, const int row); + static void convolve_mat (CMat * result, const CMat * const mata, const CMat * const matb); + static void convolve_mat_fun (CMat * result, const CMat * const mata, double (f) (int, int)); + static int as_idx (const int k, const int l, const int m); + static int as_cidx (const int k, const int l); + static Mat *make_s_matrix (CMat * mat, int m, double noise_factor); + static Mat *make_s_cmatrix (CMat * mat, int m, double noise_factor); + static double correlation (const int x, const int y, const double gamma, const double musq); + static Mat *copy_vec (const CMat * const mat, const int m); + static Mat *copy_cvec (const CMat * const mat, const int m); + static CMat *copy_cvec2mat (const Mat * const cvec, const int m); + static CMat *copy_vec2mat (const Mat * const cvec, const int m); + static CMat *compute_g (const CMat * const convolution, const int m, const double gamma, + const double noise_factor, const double musq, const bool symmetric); + static double circle_integral (const double x, const double radius); + static double circle_intensity (const int x, const int y, const double radius); + + // CLapack interface. + static int dgesv (const int N, const int NRHS, double *A, const int lda, double *B, const int ldb); + +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* MATRIX_H_INCLUDED */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/refocus.cpp b/src/imageplugins/coreplugin/sharpnesseditor/refocus.cpp new file mode 100644 index 00000000..7e99d663 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/refocus.cpp @@ -0,0 +1,199 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Refocus threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dcolor.h" +#include "dimgimagefilters.h" +#include "matrix.h" +#include "refocus.h" + +namespace DigikamImagesPluginCore +{ + +Refocus::Refocus(Digikam::DImg *orgImage, TQObject *parent, int matrixSize, double radius, + double gauss, double correlation, double noise) + : Digikam::DImgThreadedFilter(orgImage, parent, "Refocus") +{ + m_matrixSize = matrixSize; + m_radius = radius; + m_gauss = gauss; + m_correlation = correlation; + m_noise = noise; + initFilter(); +} + +void Refocus::filterImage(void) +{ + refocusImage(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_matrixSize, m_radius, m_gauss, + m_correlation, m_noise); +} + +void Refocus::refocusImage(uchar* data, int width, int height, bool sixteenBit, + int matrixSize, double radius, double gauss, + double correlation, double noise) +{ + CMat *matrix=0; + + // Compute matrix + DDebug() << "Refocus::Compute matrix..." << endl; + + CMat circle, gaussian, convolution; + + RefocusMatrix::make_gaussian_convolution (gauss, &gaussian, matrixSize); + RefocusMatrix::make_circle_convolution (radius, &circle, matrixSize); + RefocusMatrix::init_c_mat (&convolution, matrixSize); + RefocusMatrix::convolve_star_mat (&convolution, &gaussian, &circle); + + matrix = RefocusMatrix::compute_g_matrix (&convolution, matrixSize, correlation, noise, 0.0, true); + + RefocusMatrix::finish_c_mat (&convolution); + RefocusMatrix::finish_c_mat (&gaussian); + RefocusMatrix::finish_c_mat (&circle); + + // Apply deconvolution kernel to image. + DDebug() << "Refocus::Apply Matrix to image..." << endl; + convolveImage(data, m_destImage.bits(), width, height, sixteenBit, + matrix->data, 2 * matrixSize + 1); + + // Clean up memory + delete matrix; +} + +void Refocus::convolveImage(uchar *orgData, uchar *destData, int width, int height, + bool sixteenBit, const double *const matrix, int mat_size) +{ + int progress; + unsigned short *orgData16 = (unsigned short *)orgData; + unsigned short *destData16 = (unsigned short *)destData; + + double valRed, valGreen, valBlue; + int x1, y1, x2, y2, index1, index2; + + const int imageSize = width*height; + const int mat_offset = mat_size / 2; + + for (y1 = 0; !m_cancel && (y1 < height); y1++) + { + for (x1 = 0; !m_cancel && (x1 < width); x1++) + { + valRed = valGreen = valBlue = 0.0; + + if (!sixteenBit) // 8 bits image. + { + uchar red, green, blue; + uchar *ptr; + + for (y2 = 0; !m_cancel && (y2 < mat_size); y2++) + { + for (x2 = 0; !m_cancel && (x2 < mat_size); x2++) + { + index1 = width * (y1 + y2 - mat_offset) + + x1 + x2 - mat_offset; + + if ( index1 >= 0 && index1 < imageSize ) + { + ptr = &orgData[index1*4]; + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + const double matrixValue = matrix[y2 * mat_size + x2]; + valRed += matrixValue * red; + valGreen += matrixValue * green; + valBlue += matrixValue * blue; + } + } + } + + index2 = y1 * width + x1; + + if (index2 >= 0 && index2 < imageSize) + { + // To get Alpha channel value from original (unchanged) + memcpy (&destData[index2*4], &orgData[index2*4], 4); + ptr = &destData[index2*4]; + + // Overwrite RGB values to destination. + ptr[0] = (uchar) CLAMP (valBlue, 0, 255); + ptr[1] = (uchar) CLAMP (valGreen, 0, 255); + ptr[2] = (uchar) CLAMP (valRed, 0, 255); + } + } + else // 16 bits image. + { + unsigned short red, green, blue; + unsigned short *ptr; + + for (y2 = 0; !m_cancel && (y2 < mat_size); y2++) + { + for (x2 = 0; !m_cancel && (x2 < mat_size); x2++) + { + index1 = width * (y1 + y2 - mat_offset) + + x1 + x2 - mat_offset; + + if ( index1 >= 0 && index1 < imageSize ) + { + ptr = &orgData16[index1*4]; + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + const double matrixValue = matrix[y2 * mat_size + x2]; + valRed += matrixValue * red; + valGreen += matrixValue * green; + valBlue += matrixValue * blue; + } + } + } + + index2 = y1 * width + x1; + + if (index2 >= 0 && index2 < imageSize) + { + // To get Alpha channel value from original (unchanged) + memcpy (&destData16[index2*4], &orgData16[index2*4], 8); + ptr = &destData16[index2*4]; + + // Overwrite RGB values to destination. + ptr[0] = (unsigned short) CLAMP (valBlue, 0, 65535); + ptr[1] = (unsigned short) CLAMP (valGreen, 0, 65535); + ptr[2] = (unsigned short) CLAMP (valRed, 0, 65535); + } + } + } + + // Update the progress bar in dialog. + progress = (int)(((double)y1 * 100.0) / height); + if (progress%5 == 0) + postProgress( progress ); + } +} + +} // NameSpace DigikamImagesPluginCore + diff --git a/src/imageplugins/coreplugin/sharpnesseditor/refocus.h b/src/imageplugins/coreplugin/sharpnesseditor/refocus.h new file mode 100644 index 00000000..323b24b9 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/refocus.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Refocus threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef REFOCUS_H +#define REFOCUS_H + +// Local includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamImagesPluginCore +{ + +class Refocus : public Digikam::DImgThreadedFilter +{ + +public: + + Refocus(Digikam::DImg *orgImage, TQObject *parent=0, int matrixSize=5, double radius=0.9, + double gauss=0.0, double correlation=0.5, double noise=0.01); + + ~Refocus(){}; + +private: // Refocus filter methods. + + virtual void filterImage(void); + + void refocusImage(uchar* data, int width, int height, bool sixteenBit, + int matrixSize, double radius, double gauss, + double correlation, double noise); + + void convolveImage(uchar *orgData, uchar *destData, int width, int height, + bool sixteenBit, const double *const matrix, int mat_size); + +private: // Refocus filter data. + + int m_matrixSize; + + double m_radius; + double m_gauss; + double m_correlation; + double m_noise; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* REFOCUS_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.cpp b/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.cpp new file mode 100644 index 00000000..aee5d841 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.cpp @@ -0,0 +1,741 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to sharp an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define MAX_MATRIX_SIZE 25 + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "dimgsharpen.h" +#include "unsharp.h" +#include "refocus.h" +#include "sharpentool.h" +#include "sharpentool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamImagesPluginCore +{ + +SharpenTool::SharpenTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("sharpen"); + setToolName(i18n("Sharpen")); + setToolIcon(SmallIcon("sharpenimage")); + setToolHelp("blursharpentool.anchor"); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 3, 1); + + TQLabel *label1 = new TQLabel(i18n("Method:"), m_gboxSettings->plainPage()); + + m_sharpMethod = new RComboBox(m_gboxSettings->plainPage()); + m_sharpMethod->insertItem( i18n("Simple sharp") ); + m_sharpMethod->insertItem( i18n("Unsharp mask") ); + m_sharpMethod->insertItem( i18n("Refocus") ); + m_sharpMethod->setDefaultItem(SimpleSharp); + TQWhatsThis::add( m_sharpMethod, i18n("

    Select the sharpening method to apply to the image.")); + + m_stack = new TQWidgetStack(m_gboxSettings->plainPage()); + + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(m_sharpMethod, 0, 0, 1, 1); + grid->addMultiCellWidget(new KSeparator(m_gboxSettings->plainPage()), 1, 1, 0, 1); + grid->addMultiCellWidget(m_stack, 2, 2, 0, 1); + grid->setRowStretch(3, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + // ------------------------------------------------------------- + + TQWidget *simpleSharpSettings = new TQWidget(m_stack); + TQGridLayout* grid1 = new TQGridLayout( simpleSharpSettings, 2, 1); + + TQLabel *label = new TQLabel(i18n("Sharpness:"), simpleSharpSettings); + m_radiusInput = new RIntNumInput(simpleSharpSettings); + m_radiusInput->setRange(0, 100, 1); + m_radiusInput->setDefaultValue(0); + TQWhatsThis::add( m_radiusInput, i18n("

    A sharpness of 0 has no effect, " + "1 and above determine the sharpen matrix radius " + "that determines how much to sharpen the image.")); + + grid1->addMultiCellWidget(label, 0, 0, 0, 1); + grid1->addMultiCellWidget(m_radiusInput, 1, 1, 0, 1); + grid1->setRowStretch(2, 10); + grid1->setMargin(0); + grid1->setSpacing(0); + + m_stack->addWidget(simpleSharpSettings, SimpleSharp); + + // ------------------------------------------------------------- + + TQWidget *unsharpMaskSettings = new TQWidget(m_stack); + TQGridLayout* grid2 = new TQGridLayout( unsharpMaskSettings, 6, 1); + + TQLabel *label2 = new TQLabel(i18n("Radius:"), unsharpMaskSettings); + m_radiusInput2 = new RIntNumInput(unsharpMaskSettings); + m_radiusInput2->setRange(1, 120, 1); + m_radiusInput2->setDefaultValue(1); + TQWhatsThis::add( m_radiusInput2, i18n("

    Radius value is the gaussian blur matrix radius value " + "used to determines how much to blur the image.") ); + + TQLabel *label3 = new TQLabel(i18n("Amount:"), unsharpMaskSettings); + m_amountInput = new RDoubleNumInput(unsharpMaskSettings); + m_amountInput->setPrecision(1); + m_amountInput->setRange(0.0, 5.0, 0.1); + m_amountInput->setDefaultValue(1.0); + TQWhatsThis::add( m_amountInput, i18n("

    The value of the difference between the " + "original and the blur image that is added back into the original.") ); + + TQLabel *label4 = new TQLabel(i18n("Threshold:"), unsharpMaskSettings); + m_thresholdInput = new RDoubleNumInput(unsharpMaskSettings); + m_thresholdInput->setPrecision(2); + m_thresholdInput->setRange(0.0, 1.0, 0.01); + m_thresholdInput->setDefaultValue(0.05); + TQWhatsThis::add( m_thresholdInput, i18n("

    The threshold, as a fraction of the maximum " + "luminosity value, needed to apply the difference amount.") ); + + grid2->addMultiCellWidget(label2, 0, 0, 0, 1); + grid2->addMultiCellWidget(m_radiusInput2, 1, 1, 0, 1); + grid2->addMultiCellWidget(label3, 2, 2, 0, 1); + grid2->addMultiCellWidget(m_amountInput, 3, 3, 0, 1); + grid2->addMultiCellWidget(label4, 4, 4, 0, 1); + grid2->addMultiCellWidget(m_thresholdInput, 5, 5, 0, 1); + grid2->setRowStretch(6, 10); + grid2->setMargin(0); + grid2->setSpacing(0); + + m_stack->addWidget(unsharpMaskSettings, UnsharpMask); + + // ------------------------------------------------------------- + + TQWidget *refocusSettings = new TQWidget(m_stack); + TQGridLayout* grid3 = new TQGridLayout(refocusSettings, 10, 1); + + TQLabel *label5 = new TQLabel(i18n("Circular sharpness:"), refocusSettings); + m_radius = new RDoubleNumInput(refocusSettings); + m_radius->setPrecision(2); + m_radius->setRange(0.0, 5.0, 0.01); + m_radius->setDefaultValue(1.0); + TQWhatsThis::add( m_radius, i18n("

    This is the radius of the circular convolution. It is the most important " + "parameter for using this plugin. For most images the default value of 1.0 " + "should give good results. Select a higher value when your image is very blurred.")); + + TQLabel *label6 = new TQLabel(i18n("Correlation:"), refocusSettings); + m_correlation = new RDoubleNumInput(refocusSettings); + m_correlation->setPrecision(2); + m_correlation->setRange(0.0, 1.0, 0.01); + m_correlation->setDefaultValue(0.5); + TQWhatsThis::add( m_correlation, i18n("

    Increasing the correlation may help to reduce artifacts. The correlation can " + "range from 0-1. Useful values are 0.5 and values close to 1, e.g. 0.95 and 0.99. " + "Using a high value for the correlation will reduce the sharpening effect of the " + "plugin.")); + + TQLabel *label7 = new TQLabel(i18n("Noise filter:"), refocusSettings); + m_noise = new RDoubleNumInput(refocusSettings); + m_noise->setPrecision(3); + m_noise->setRange(0.0, 1.0, 0.001); + m_noise->setDefaultValue(0.03); + TQWhatsThis::add( m_noise, i18n("

    Increasing the noise filter parameter may help to reduce artifacts. The noise filter " + "can range from 0-1 but values higher than 0.1 are rarely helpful. When the noise filter " + "value is too low, e.g. 0.0 the image quality will be very poor. A useful value is 0.01. " + "Using a high value for the noise filter will reduce the sharpening " + "effect of the plugin.")); + + TQLabel *label8 = new TQLabel(i18n("Gaussian sharpness:"), refocusSettings); + m_gauss = new RDoubleNumInput(refocusSettings); + m_gauss->setPrecision(2); + m_gauss->setRange(0.0, 1.0, 0.01); + m_gauss->setDefaultValue(0.0); + TQWhatsThis::add( m_gauss, i18n("

    This is the sharpness for the gaussian convolution. Use this parameter when your " + "blurring is of a Gaussian type. In most cases you should set this parameter to 0, because " + "it causes nasty artifacts. When you use non-zero values, you will probably have to " + "increase the correlation and/or noise filter parameters too.")); + + TQLabel *label9 = new TQLabel(i18n("Matrix size:"), refocusSettings); + m_matrixSize = new RIntNumInput(refocusSettings); + m_matrixSize->setRange(0, MAX_MATRIX_SIZE, 1); + m_matrixSize->setDefaultValue(5); + TQWhatsThis::add( m_matrixSize, i18n("

    This parameter determines the size of the transformation matrix. " + "Increasing the matrix width may give better results, especially when you have " + "chosen large values for circular or gaussian sharpness.")); + + grid3->addMultiCellWidget(label5, 0, 0, 0, 1); + grid3->addMultiCellWidget(m_radius, 1, 1, 0, 1); + grid3->addMultiCellWidget(label6, 2, 2, 0, 1); + grid3->addMultiCellWidget(m_correlation, 3, 3, 0, 1); + grid3->addMultiCellWidget(label7, 4, 4, 0, 1); + grid3->addMultiCellWidget(m_noise, 5, 5, 0, 1); + grid3->addMultiCellWidget(label8, 6, 6, 0, 1); + grid3->addMultiCellWidget(m_gauss, 7, 7, 0, 1); + grid3->addMultiCellWidget(label9, 8, 8, 0, 1); + grid3->addMultiCellWidget(m_matrixSize, 9, 9, 0, 1); + grid3->setRowStretch(10, 10); + grid3->setMargin(0); + grid3->setSpacing(0); + + m_stack->addWidget(refocusSettings, Refocus); + + setToolSettings(m_gboxSettings); + + m_previewWidget = new ImagePanelWidget(470, 350, "sharpen Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_sharpMethod, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotSharpMethodActived(int))); + + // ------------------------------------------------------------- + + // Image creation with dummy borders (mosaic mode) used by Refocus method. It needs to do + // it before to apply deconvolution filter on original image border pixels including + // on matrix size area. This way limit artifacts on image border. + + ImageIface iface(0, 0); + + uchar* data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sb = iface.originalSixteenBit(); + bool a = iface.originalHasAlpha(); + + m_img = DImg( w + 4*MAX_MATRIX_SIZE, h + 4*MAX_MATRIX_SIZE, sb, a); + + DImg tmp; + DImg org(w, h, sb, a, data); + + // Copy original. + m_img.bitBltImage(&org, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + + // Create dummy top border + tmp = org.copy(0, 0, w, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, 2*MAX_MATRIX_SIZE, 0); + + // Create dummy bottom border + tmp = org.copy(0, h-2*MAX_MATRIX_SIZE, w, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE+h); + + // Create dummy left border + tmp = org.copy(0, 0, 2*MAX_MATRIX_SIZE, h); + tmp.flip(DImg::HORIZONTAL); + m_img.bitBltImage(&tmp, 0, 2*MAX_MATRIX_SIZE); + + // Create dummy right border + tmp = org.copy(w-2*MAX_MATRIX_SIZE, 0, 2*MAX_MATRIX_SIZE, h); + tmp.flip(DImg::HORIZONTAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + + // Create dummy top/left corner + tmp = org.copy(0, 0, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::HORIZONTAL); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, 0, 0); + + // Create dummy top/right corner + tmp = org.copy(w-2*MAX_MATRIX_SIZE, 0, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::HORIZONTAL); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, 0); + + // Create dummy bottom/left corner + tmp = org.copy(0, h-2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::HORIZONTAL); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, 0, h+2*MAX_MATRIX_SIZE); + + // Create dummy bottom/right corner + tmp = org.copy(w-2*MAX_MATRIX_SIZE, h-2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + tmp.flip(DImg::HORIZONTAL); + tmp.flip(DImg::VERTICAL); + m_img.bitBltImage(&tmp, w+2*MAX_MATRIX_SIZE, h+2*MAX_MATRIX_SIZE); + + delete [] data; +} + +SharpenTool::~SharpenTool() +{ +} + +void SharpenTool::renderingFinished() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(true); + m_gboxSettings->enableButton(EditorToolSettings::Load, false); + m_gboxSettings->enableButton(EditorToolSettings::SaveAs, false); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(true); + m_amountInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_gboxSettings->enableButton(EditorToolSettings::Load, false); + m_gboxSettings->enableButton(EditorToolSettings::SaveAs, false); + break; + } + + case Refocus: + { + m_matrixSize->setEnabled(true); + m_radius->setEnabled(true); + m_gauss->setEnabled(true); + m_correlation->setEnabled(true); + m_noise->setEnabled(true); + break; + } + } +} + +void SharpenTool::slotSharpMethodActived(int w) +{ + m_stack->raiseWidget(w); + if (w == Refocus) + { + m_gboxSettings->enableButton(EditorToolSettings::Load, true); + m_gboxSettings->enableButton(EditorToolSettings::SaveAs, true); + } + else + { + m_gboxSettings->enableButton(EditorToolSettings::Load, false); + m_gboxSettings->enableButton(EditorToolSettings::SaveAs, false); + } +} + +void SharpenTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("sharpen Tool"); + m_radiusInput->blockSignals(true); + m_radiusInput2->blockSignals(true); + m_amountInput->blockSignals(true); + m_thresholdInput->blockSignals(true); + m_matrixSize->blockSignals(true); + m_radius->blockSignals(true); + m_gauss->blockSignals(true); + m_correlation->blockSignals(true); + m_noise->blockSignals(true); + m_sharpMethod->blockSignals(true); + + m_radiusInput->setValue(config->readNumEntry("SimpleSharpRadiusAjustment", m_radiusInput->defaultValue())); + m_radiusInput2->setValue(config->readNumEntry("UnsharpMaskRadiusAjustment", m_radiusInput2->defaultValue())); + m_amountInput->setValue(config->readDoubleNumEntry("UnsharpMaskAmountAjustment", m_amountInput->defaultValue())); + m_thresholdInput->setValue(config->readDoubleNumEntry("UnsharpMaskThresholdAjustment", m_thresholdInput->defaultValue())); + m_matrixSize->setValue(config->readNumEntry("RefocusMatrixSize", m_matrixSize->defaultValue())); + m_radius->setValue(config->readDoubleNumEntry("RefocusRadiusAjustment", m_radius->defaultValue())); + m_gauss->setValue(config->readDoubleNumEntry("RefocusGaussAjustment", m_gauss->defaultValue())); + m_correlation->setValue(config->readDoubleNumEntry("RefocusCorrelationAjustment", m_correlation->defaultValue())); + m_noise->setValue(config->readDoubleNumEntry("RefocusNoiseAjustment", m_noise->defaultValue())); + m_sharpMethod->setCurrentItem(config->readNumEntry("SharpenMethod", SimpleSharp)); + + m_radiusInput->blockSignals(false); + m_radiusInput2->blockSignals(false); + m_amountInput->blockSignals(false); + m_thresholdInput->blockSignals(false); + m_matrixSize->blockSignals(false); + m_radius->blockSignals(false); + m_gauss->blockSignals(false); + m_correlation->blockSignals(false); + m_noise->blockSignals(false); + m_sharpMethod->blockSignals(false); + + slotSharpMethodActived(m_sharpMethod->currentItem()); +} + +void SharpenTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("sharpen Tool"); + config->writeEntry("SimpleSharpRadiusAjustment", m_radiusInput->value()); + config->writeEntry("UnsharpMaskRadiusAjustment", m_radiusInput2->value()); + config->writeEntry("UnsharpMaskAmountAjustment", m_amountInput->value()); + config->writeEntry("UnsharpMaskThresholdAjustment", m_thresholdInput->value()); + config->writeEntry("RefocusMatrixSize", m_matrixSize->value()); + config->writeEntry("RefocusRadiusAjustment", m_radius->value()); + config->writeEntry("RefocusGaussAjustment", m_gauss->value()); + config->writeEntry("RefocusCorrelationAjustment", m_correlation->value()); + config->writeEntry("RefocusNoiseAjustment", m_noise->value()); + config->writeEntry("SharpenMethod", m_sharpMethod->currentItem()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void SharpenTool::slotResetSettings() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->blockSignals(true); + m_radiusInput->slotReset(); + m_radiusInput->blockSignals(false); + break; + } + + case UnsharpMask: + { + m_radiusInput2->blockSignals(true); + m_amountInput->blockSignals(true); + m_thresholdInput->blockSignals(true); + + m_radiusInput2->slotReset(); + m_amountInput->slotReset(); + m_thresholdInput->slotReset(); + + m_radiusInput2->blockSignals(false); + m_amountInput->blockSignals(false); + m_thresholdInput->blockSignals(false); + break; + } + + case Refocus: + { + m_matrixSize->blockSignals(true); + m_radius->blockSignals(true); + m_gauss->blockSignals(true); + m_correlation->blockSignals(true); + m_noise->blockSignals(true); + + m_matrixSize->slotReset(); + m_radius->slotReset(); + m_gauss->slotReset(); + m_correlation->slotReset(); + m_noise->slotReset(); + + m_matrixSize->blockSignals(false); + m_radius->blockSignals(false); + m_gauss->blockSignals(false); + m_correlation->blockSignals(false); + m_noise->blockSignals(false); + break; + } + } +} + +void SharpenTool::prepareEffect() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(false); + + DImg img = m_previewWidget->getOriginalRegionImage(); + + double radius = m_radiusInput->value()/10.0; + double sigma; + + if (radius < 1.0) sigma = radius; + else sigma = sqrt(radius); + + setFilter(dynamic_cast(new DImgSharpen(&img, this, radius, sigma ))); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(false); + m_amountInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + + DImg img = m_previewWidget->getOriginalRegionImage(); + + int r = m_radiusInput2->value(); + double a = m_amountInput->value(); + double th = m_thresholdInput->value(); + + setFilter(dynamic_cast(new DigikamImagesPluginCore::UnsharpMask(&img, this, r, a, th))); + break; + } + + case Refocus: + { + m_matrixSize->setEnabled(false); + m_radius->setEnabled(false); + m_gauss->setEnabled(false); + m_correlation->setEnabled(false); + m_noise->setEnabled(false); + + int ms = m_matrixSize->value(); + double r = m_radius->value(); + double g = m_gauss->value(); + double c = m_correlation->value(); + double n = m_noise->value(); + + TQRect area = m_previewWidget->getOriginalImageRegionToRender(); + TQRect tmpRect; + tmpRect.setLeft(area.left()-2*ms); + tmpRect.setRight(area.right()+2*ms); + tmpRect.setTop(area.top()-2*ms); + tmpRect.setBottom(area.bottom()+2*ms); + tmpRect.moveBy(2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE); + DImg imTemp = m_img.copy(tmpRect); + + setFilter(dynamic_cast(new DigikamImagesPluginCore::Refocus(&imTemp, this, ms, r, g, c, n))); + break; + } + } +} + +void SharpenTool::prepareFinal() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + m_radiusInput->setEnabled(false); + + double radius = m_radiusInput->value()/10.0; + double sigma; + + if (radius < 1.0) sigma = radius; + else sigma = sqrt(radius); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + DImg orgImage = DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + setFilter(dynamic_cast(new DImgSharpen(&orgImage, this, radius, sigma ))); + break; + } + + case UnsharpMask: + { + m_radiusInput2->setEnabled(false); + m_amountInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + + int r = m_radiusInput2->value(); + double a = m_amountInput->value(); + double th = m_thresholdInput->value(); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + int w = iface.originalWidth(); + int h = iface.originalHeight(); + bool sixteenBit = iface.originalSixteenBit(); + bool hasAlpha = iface.originalHasAlpha(); + DImg orgImage = DImg(w, h, sixteenBit, hasAlpha ,data); + delete [] data; + setFilter(dynamic_cast(new DigikamImagesPluginCore::UnsharpMask(&orgImage, this, r, a, th))); + break; + } + + case Refocus: + { + + m_matrixSize->setEnabled(false); + m_radius->setEnabled(false); + m_gauss->setEnabled(false); + m_correlation->setEnabled(false); + m_noise->setEnabled(false); + + int ms = m_matrixSize->value(); + double r = m_radius->value(); + double g = m_gauss->value(); + double c = m_correlation->value(); + double n = m_noise->value(); + + setFilter(dynamic_cast(new DigikamImagesPluginCore::Refocus(&m_img, this, ms, r, g, c, n))); + break; + } + } +} + +void SharpenTool::putPreviewData() +{ + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + case UnsharpMask: + { + DImg imDest = filter()->getTargetImage(); + m_previewWidget->setPreviewImage(imDest); + break; + } + + case Refocus: + { + int ms = m_matrixSize->value(); + TQRect area = m_previewWidget->getOriginalImageRegionToRender(); + + DImg imDest = filter()->getTargetImage() + .copy(2*ms, 2*ms, area.width(), area.height()); + m_previewWidget->setPreviewImage(imDest); + break; + } + } +} + +void SharpenTool::putFinalData() +{ + ImageIface iface(0, 0); + DImg imDest = filter()->getTargetImage(); + + switch (m_stack->id(m_stack->visibleWidget())) + { + case SimpleSharp: + { + iface.putOriginalImage(i18n("Sharpen"), imDest.bits()); + break; + } + + case UnsharpMask: + { + iface.putOriginalImage(i18n("Unsharp Mask"), imDest.bits()); + break; + } + + case Refocus: + { + TQRect area = m_previewWidget->getOriginalImageRegionToRender(); + ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Refocus"), filter()->getTargetImage() + .copy(2*MAX_MATRIX_SIZE, 2*MAX_MATRIX_SIZE, + iface.originalWidth(), + iface.originalHeight()) + .bits()); + break; + } + } +} + +void SharpenTool::slotLoadSettings() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Refocus Settings File to Load")) ); + if ( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + if ( stream.readLine() != "# Photograph Refocus Configuration File" ) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Photograph Refocus settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_matrixSize->setValue( stream.readLine().toInt() ); + m_radius->setValue( stream.readLine().toDouble() ); + m_gauss->setValue( stream.readLine().toDouble() ); + m_correlation->setValue( stream.readLine().toDouble() ); + m_noise->setValue( stream.readLine().toDouble() ); + blockSignals(false); + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Photograph Refocus text file.")); + + file.close(); +} + +void SharpenTool::slotSaveAsSettings() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Refocus Settings File to Save")) ); + if ( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Photograph Refocus Configuration File\n"; + stream << m_matrixSize->value() << "\n"; + stream << m_radius->value() << "\n"; + stream << m_gauss->value() << "\n"; + stream << m_correlation->value() << "\n"; + stream << m_noise->value() << "\n"; + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Refocus text file.")); + + file.close(); +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.h b/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.h new file mode 100644 index 00000000..8dbca1c5 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/sharpentool.h @@ -0,0 +1,111 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-09 + * Description : a tool to sharp an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SHARPENTOOL_H +#define SHARPENTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQWidgetStack; + +namespace KDcrawIface +{ +class RIntNumInput; +class RDoubleNumInput; +class RComboBox; +} + +namespace Digikam +{ +class DImg; +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamImagesPluginCore +{ + +class SharpenTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + SharpenTool(TQObject *parent); + ~SharpenTool(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotResetSettings(); + void slotSharpMethodActived(int); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum SharpingMethods + { + SimpleSharp=0, + UnsharpMask, + Refocus + }; + + TQWidgetStack *m_stack; + + KDcrawIface::RComboBox *m_sharpMethod; + + KDcrawIface::RIntNumInput *m_matrixSize; + KDcrawIface::RIntNumInput *m_radiusInput; + KDcrawIface::RIntNumInput *m_radiusInput2; + + KDcrawIface::RDoubleNumInput *m_radius; + KDcrawIface::RDoubleNumInput *m_gauss; + KDcrawIface::RDoubleNumInput *m_correlation; + KDcrawIface::RDoubleNumInput *m_noise; + KDcrawIface::RDoubleNumInput *m_amountInput; + KDcrawIface::RDoubleNumInput *m_thresholdInput; + + Digikam::DImg m_img; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* SHARPENTOOL_H */ diff --git a/src/imageplugins/coreplugin/sharpnesseditor/unsharp.cpp b/src/imageplugins/coreplugin/sharpnesseditor/unsharp.cpp new file mode 100644 index 00000000..fb1b9d21 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/unsharp.cpp @@ -0,0 +1,127 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Unsharp Mask threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dcolor.h" +#include "dimgimagefilters.h" +#include "dimggaussianblur.h" +#include "unsharp.h" + +namespace DigikamImagesPluginCore +{ + +UnsharpMask::UnsharpMask(Digikam::DImg *orgImage, TQObject *parent, int radius, + double amount, double threshold) + : DImgThreadedFilter(orgImage, parent, "UnsharpMask") +{ + m_radius = radius; + m_amount = amount; + m_threshold = threshold; + initFilter(); +} + +void UnsharpMask::filterImage(void) +{ + int progress; + int quantum; + double quantumThreshold; + double value; + Digikam::DColor p; + Digikam::DColor q; + + if (m_orgImage.isNull()) + { + DWarning() << k_funcinfo << "No image data available!" << endl; + return; + } + + Digikam::DImgGaussianBlur(this, m_orgImage, m_destImage, 0, 10, (int)(m_radius)); + + quantum = m_destImage.sixteenBit() ? 65535 : 255; + quantumThreshold = quantum*m_threshold; + + for (uint y = 0 ; !m_cancel && (y < m_destImage.height()) ; y++) + { + for (uint x = 0 ; !m_cancel && (x < m_destImage.width()) ; x++) + { + p = m_orgImage.getPixelColor(x, y); + q = m_destImage.getPixelColor(x, y); + + // Red channel. + value = (double)(p.red())-(double)(q.red()); + + if (fabs(2.0*value) < quantumThreshold) + value = (double)(p.red()); + else + value = (double)(p.red()) + value*m_amount; + + q.setRed(CLAMP(ROUND(value), 0, quantum)); + + // Green Channel. + value = (double)(p.green())-(double)(q.green()); + + if (fabs(2.0*value) < quantumThreshold) + value = (double)(p.green()); + else + value = (double)(p.green()) + value*m_amount; + + q.setGreen(CLAMP(ROUND(value), 0, quantum)); + + // Blue Channel. + value = (double)(p.blue())-(double)(q.blue()); + + if (fabs(2.0*value) < quantumThreshold) + value = (double)(p.blue()); + else + value = (double)(p.blue()) + value*m_amount; + + q.setBlue(CLAMP(ROUND(value), 0, quantum)); + + // Alpha Channel. + value = (double)(p.alpha())-(double)(q.alpha()); + + if (fabs(2.0*value) < quantumThreshold) + value = (double)(p.alpha()); + else + value = (double)(p.alpha()) + value*m_amount; + + q.setAlpha(CLAMP(ROUND(value), 0, quantum)); + + m_destImage.setPixelColor(x, y, q); + } + + progress = (int)(10.0 + ((double)y * 90.0) / m_destImage.height()); + if ( progress%5 == 0 ) + postProgress( progress ); + } +} + +} // NameSpace DigikamImagesPluginCore diff --git a/src/imageplugins/coreplugin/sharpnesseditor/unsharp.h b/src/imageplugins/coreplugin/sharpnesseditor/unsharp.h new file mode 100644 index 00000000..a1780ab4 --- /dev/null +++ b/src/imageplugins/coreplugin/sharpnesseditor/unsharp.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Unsharp Mask threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef UNSHARP_MASK_H +#define UNSHARP_MASK_H + +// Local includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamImagesPluginCore +{ + +class UnsharpMask : public Digikam::DImgThreadedFilter +{ + +public: + + UnsharpMask(Digikam::DImg *orgImage, TQObject *parent=0, int radius=1, + double amount=1.0, double threshold=0.05); + + ~UnsharpMask(){}; + +private: + + virtual void filterImage(void); + +private: + + int m_radius; + + double m_amount; + double m_threshold; +}; + +} // NameSpace DigikamImagesPluginCore + +#endif /* UNSHARP_MASK_H */ diff --git a/src/imageplugins/distortionfx/Makefile.am b/src/imageplugins/distortionfx/Makefile.am new file mode 100644 index 00000000..fcb7b74a --- /dev/null +++ b/src/imageplugins/distortionfx/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_distortionfx_la_SOURCES = imageplugin_distortionfx.cpp \ + distortionfxtool.cpp distortionfx.cpp + +digikamimageplugin_distortionfx_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_distortionfx_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_distortionfx.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_distortionfx.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_distortionfx_ui.rc + diff --git a/src/imageplugins/distortionfx/digikamimageplugin_distortionfx.desktop b/src/imageplugins/distortionfx/digikamimageplugin_distortionfx.desktop new file mode 100644 index 00000000..8baca4eb --- /dev/null +++ b/src/imageplugins/distortionfx/digikamimageplugin_distortionfx.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_DistortionFX +Name[bg]=ПриÑтавка за Ñнимки - ИзкривÑващи ефекти +Name[da]=Plugin for forvrængningseffekt +Name[el]=ΠÏόσθετοΕικόνας_ΕφέΠαÏαμόÏφωσης +Name[fi]=Vääristymä +Name[hr]=IzobliÄenje +Name[it]=PluginImmagini_EffettiDiDistorsione +Name[nl]=Afbeeldingsplugin_Vervormingseffect +Name[sr]=Ефекти изобличења +Name[sr@Latn]=Efekti izobliÄenja +Name[sv]=Insticksprogram för förvrängningseffekt +Name[tr]=ResimEklentisi_Bozma +Name[xx]=xxImagePlugin_DistortionFXxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Distortion special effects plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam Ñ Ð¸Ð·ÐºÑ€Ð¸Ð²Ñващи Ñнимките ефекти +Comment[ca]=Connector pel digiKam d'efectes especials de distorsió +Comment[da]=Digikam plugin for forvrængningsspecialeffekt +Comment[de]=digiKam-Modul zum Erzeugen von speziellen Verzerrungseffekten +Comment[el]=ΠÏόσθετο ειδικών εφέ παÏαμόÏφωσης για το digiKam +Comment[es]=Plugin para digiKam con efectos de distorsión especiales +Comment[et]=DigiKami spetsiaalsete moonutusefektide plugin +Comment[fa]=وصلۀ جلوه‌های ویژۀ اعواج برای digiKam +Comment[fi]=Vääristymäerikoistehosteita +Comment[gl]=Un plugin de digiKam para efeitos especiais de distorsión +Comment[hr]=digiKam dodatak za efekt izobliÄavanja +Comment[is]=Ãforrit fyrir digiKam sem afbakar sérstaklega myndir +Comment[it]=Plugin degli effetti speciali di distorsione per digiKam +Comment[ja]=digiKam ゆãŒã‚特殊効果プラグイン +Comment[nds]=digiKam-Moduul för Vertarren-Effekten +Comment[nl]=Digikam-plugin voor vervormingseffect +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਖਿੰਡਾਉਣ ਖਾਸ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam oferujÄ…ca efekty znieksztaÅ‚ceÅ„ +Comment[pt]=Um 'plugin' do digiKam para efeitos especiais de distorção +Comment[pt_BR]=Plugin de efeito especial de distorção +Comment[ru]=Модуль Ñпециальных шумовых Ñффектов Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre Å¡peciálne efekty skreslenia +Comment[sr]=digiKam-ов прикључак за ефекте изобличења +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekte izobliÄenja +Comment[sv]=Digikam insticksprogram för förvrängningsspecialeffekt +Comment[tr]=Bozma etkileri uygulamak için bir digiKam eklentisi +Comment[uk]=Втулок Ñпеціальних ефектів ÑÐ¿Ð¾Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng méo mó ảnh cho digiKam +Comment[xx]=xxDistortion special effects plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_distortionfx +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/distortionfx/digikamimageplugin_distortionfx_ui.rc b/src/imageplugins/distortionfx/digikamimageplugin_distortionfx_ui.rc new file mode 100644 index 00000000..99b905ad --- /dev/null +++ b/src/imageplugins/distortionfx/digikamimageplugin_distortionfx_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/distortionfx/distortionfx.cpp b/src/imageplugins/distortionfx/distortionfx.cpp new file mode 100644 index 00000000..d17abd4f --- /dev/null +++ b/src/imageplugins/distortionfx/distortionfx.cpp @@ -0,0 +1,869 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Distortion FX threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Represents 1 +#define ANGLE_RATIO 0.017453292519943295769236907685 + +// C++ includes. + +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "distortionfx.h" + +namespace DigikamDistortionFXImagesPlugin +{ + +DistortionFX::DistortionFX(Digikam::DImg *orgImage, TQObject *parent, int effectType, + int level, int iteration, bool antialiasing) + : Digikam::DImgThreadedFilter(orgImage, parent, "DistortionFX") +{ + m_effectType = effectType; + m_level = level; + m_iteration = iteration; + m_antiAlias = antialiasing; + + initFilter(); +} + +void DistortionFX::filterImage(void) +{ + int w = m_orgImage.width(); + int h = m_orgImage.height(); + int l = m_level; + int f = m_iteration; + + switch (m_effectType) + { + case FishEye: + fisheye(&m_orgImage, &m_destImage, (double)(l/5.0), m_antiAlias); + break; + + case Twirl: + twirl(&m_orgImage, &m_destImage, l, m_antiAlias); + break; + + case CilindricalHor: + cilindrical(&m_orgImage, &m_destImage, (double)l, true, false, m_antiAlias); + break; + + case CilindricalVert: + cilindrical(&m_orgImage, &m_destImage, (double)l, false, true, m_antiAlias); + break; + + case CilindricalHV: + cilindrical(&m_orgImage, &m_destImage, (double)l, true, true, m_antiAlias); + break; + + case Caricature: + fisheye(&m_orgImage, &m_destImage, (double)(-l/5.0), m_antiAlias); + break; + + case MultipleCorners: + multipleCorners(&m_orgImage, &m_destImage, l, m_antiAlias); + break; + + case WavesHorizontal: + waves(&m_orgImage, &m_destImage, l, f, true, true); + break; + + case WavesVertical: + waves(&m_orgImage, &m_destImage, l, f, true, false); + break; + + case BlockWaves1: + blockWaves(&m_orgImage, &m_destImage, l, f, false); + break; + + case BlockWaves2: + blockWaves(&m_orgImage, &m_destImage, l, f, true); + break; + + case CircularWaves1: + circularWaves(&m_orgImage, &m_destImage, w/2, h/2, (double)l, (double)f, 0.0, false, m_antiAlias); + break; + + case CircularWaves2: + circularWaves(&m_orgImage, &m_destImage, w/2, h/2, (double)l, (double)f, 25.0, true, m_antiAlias); + break; + + case PolarCoordinates: + polarCoordinates(&m_orgImage, &m_destImage, true, m_antiAlias); + break; + + case UnpolarCoordinates: + polarCoordinates(&m_orgImage, &m_destImage, false, m_antiAlias); + break; + + case Tile: + tile(&m_orgImage, &m_destImage, 200-f, 200-f, l); + break; + } +} + +/* + This code is shared by six methods. + Write value of pixel w|h in data to pixel nw|nh in pResBits. + Antialias if requested. +*/ +void DistortionFX::setPixelFromOther(int Width, int Height, bool sixteenBit, int bytesDepth, + uchar *data, uchar *pResBits, + int w, int h, double nw, double nh, bool AntiAlias) +{ + Digikam::DColor color; + int offset, offsetOther; + + offset = getOffset(Width, w, h, bytesDepth); + + if (AntiAlias) + { + uchar *ptr = pResBits + offset; + if (sixteenBit) + { + unsigned short *ptr16 = (unsigned short *)ptr; + Digikam::DImgImageFilters().pixelAntiAliasing16((unsigned short *)data, Width, Height, nw, nh, + ptr16+3, ptr16+2, ptr16+1, ptr16); + } + else + { + Digikam::DImgImageFilters().pixelAntiAliasing(data, Width, Height, nw, nh, + ptr+3, ptr+2, ptr+1, ptr); + } + } + else + { + // we get the position adjusted + offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth); + // read color + color.setColor(data + offsetOther, sixteenBit); + // write color to destination + color.setPixel(pResBits + offset); + } +} + +/* Function to apply the fisheye effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Coeff => Distortion effect coeff. Positive value render 'Fish Eyes' effect, + * and negative values render 'Caricature' effect. + * Antialias => Smart bluring result. + * + * Theory => This is a great effect if you take employee photos + * Its pure trigonometry. I think if you study hard the code you + * understand very well. + */ +void DistortionFX::fisheye(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff, bool AntiAlias) +{ + if (Coeff == 0.0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int h, w; + double nh, nw, th, tw; + + int progress; + int nHalfW = Width / 2, nHalfH = Height / 2; + + Digikam::DColor color; + int offset; + + double lfXScale = 1.0, lfYScale = 1.0; + double lfRadius, lfRadMax, lfAngle, lfCoeff, lfCoeffStep = Coeff / 1000.0; + + if (Width > Height) + lfYScale = (double)Width / (double)Height; + else if (Height > Width) + lfXScale = (double)Height / (double)Width; + + lfRadMax = (double)TQMAX(Height, Width) / 2.0; + lfCoeff = lfRadMax / log (fabs (lfCoeffStep) * lfRadMax + 1.0); + + // main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + th = lfYScale * (double)(h - nHalfH); + + for (w = 0; !m_cancel && (w < Width); w++) + { + tw = lfXScale * (double)(w - nHalfW); + + // we find the distance from the center + lfRadius = sqrt (th * th + tw * tw); + + if (lfRadius < lfRadMax) + { + lfAngle = atan2 (th, tw); + + if (Coeff > 0.0) + lfRadius = (exp (lfRadius / lfCoeff) - 1.0) / lfCoeffStep; + else + lfRadius = lfCoeff * log (1.0 + (-1.0 * lfCoeffStep) * lfRadius); + + nw = (double)nHalfW + (lfRadius / lfXScale) * cos (lfAngle); + nh = (double)nHalfH + (lfRadius / lfYScale) * sin (lfAngle); + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + else + { + // copy pixel + offset = getOffset(Width, w, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(pResBits + offset); + } + } + + // Update the progress bar in dialog. + progress = (int) (((double)(h) * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the twirl effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Twirl => Distance value. + * Antialias => Smart bluring result. + * + * Theory => Take spiral studies, you will understand better, I'm studying + * hard on this effect, because it is not too fast. + */ +void DistortionFX::twirl(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Twirl, bool AntiAlias) +{ + // if twirl value is zero, we do nothing + + if (Twirl == 0) + return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int h, w; + double tw, th, nh, nw; + + Digikam::DColor color; + int offset; + + int progress; + int nHalfW = Width / 2, nHalfH = Height / 2; + + double lfXScale = 1.0, lfYScale = 1.0; + double lfAngle, lfNewAngle, lfAngleStep, lfAngleSum, lfCurrentRadius, lfRadMax; + + if (Width > Height) + lfYScale = (double)Width / (double)Height; + else if (Height > Width) + lfXScale = (double)Height / (double)Width; + + // the angle step is twirl divided by 10000 + lfAngleStep = Twirl / 10000.0; + // now, we get the minimum radius + lfRadMax = (double)TQMAX(Width, Height) / 2.0; + + // main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + th = lfYScale * (double)(h - nHalfH); + + for (w = 0; !m_cancel && (w < Width); w++) + { + tw = lfXScale * (double)(w - nHalfW); + + // now, we get the distance + lfCurrentRadius = sqrt (th * th + tw * tw); + + // if distance is less than maximum radius... + if (lfCurrentRadius < lfRadMax) + { + // we find the angle from the center + lfAngle = atan2 (th, tw); + // we get the accumuled angle + lfAngleSum = lfAngleStep * (-1.0 * (lfCurrentRadius - lfRadMax)); + // ok, we sum angle with accumuled to find a new angle + lfNewAngle = lfAngle + lfAngleSum; + + // now we find the exact position's x and y + nw = (double)nHalfW + cos (lfNewAngle) * (lfCurrentRadius / lfXScale); + nh = (double)nHalfH + sin (lfNewAngle) * (lfCurrentRadius / lfYScale); + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + else + { + // copy pixel + offset = getOffset(Width, w, h, bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(pResBits + offset); + } + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the Cilindrical effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Coeff => Cilindrical value. + * Horizontal => Apply horizontally. + * Vertical => Apply vertically. + * Antialias => Smart bluring result. + * + * Theory => This is a great effect, similar to Spherize (Photoshop). + * If you understand FishEye, you will understand Cilindrical + * FishEye apply a logarithm function using a sphere radius, + * Spherize use the same function but in a rectangular + * environment. + */ +void DistortionFX::cilindrical(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff, + bool Horizontal, bool Vertical, bool AntiAlias) + +{ + if ((Coeff == 0.0) || (! (Horizontal || Vertical))) + return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int progress; + + int h, w; + double nh, nw; + + int nHalfW = Width / 2, nHalfH = Height / 2; + double lfCoeffX = 1.0, lfCoeffY = 1.0, lfCoeffStep = Coeff / 1000.0; + + if (Horizontal) + lfCoeffX = (double)nHalfW / log (fabs (lfCoeffStep) * nHalfW + 1.0); + if (Vertical) + lfCoeffY = (double)nHalfH / log (fabs (lfCoeffStep) * nHalfH + 1.0); + + // initial copy + memcpy (pResBits, data, orgImage->numBytes()); + + // main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + // we find the distance from the center + nh = fabs ((double)(h - nHalfH)); + nw = fabs ((double)(w - nHalfW)); + + if (Horizontal) + { + if (Coeff > 0.0) + nw = (exp (nw / lfCoeffX) - 1.0) / lfCoeffStep; + else + nw = lfCoeffX * log (1.0 + (-1.0 * lfCoeffStep) * nw); + } + + if (Vertical) + { + if (Coeff > 0.0) + nh = (exp (nh / lfCoeffY) - 1.0) / lfCoeffStep; + else + nh = lfCoeffY * log (1.0 + (-1.0 * lfCoeffStep) * nh); + } + + nw = (double)nHalfW + ((w >= nHalfW) ? nw : -nw); + nh = (double)nHalfH + ((h >= nHalfH) ? nh : -nh); + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the Multiple Corners effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Factor => nb corners. + * Antialias => Smart bluring result. + * + * Theory => This is an amazing function, you've never seen this before. + * I was testing some trigonometric functions, and I saw that if + * I multiply the angle by 2, the result is an image like this + * If we multiply by 3, we can create the SixCorners effect. + */ +void DistortionFX::multipleCorners(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Factor, bool AntiAlias) +{ + if (Factor == 0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int h, w; + double nh, nw; + int progress; + + int nHalfW = Width / 2, nHalfH = Height / 2; + double lfAngle, lfNewRadius, lfCurrentRadius, lfRadMax; + + lfRadMax = sqrt (Height * Height + Width * Width) / 2.0; + + // main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + // we find the distance from the center + nh = nHalfH - h; + nw = nHalfW - w; + + // now, we get the distance + lfCurrentRadius = sqrt (nh * nh + nw * nw); + // we find the angle from the center + lfAngle = atan2 (nh, nw) * (double)Factor; + + // ok, we sum angle with accumuled to find a new angle + lfNewRadius = lfCurrentRadius * lfCurrentRadius / lfRadMax; + + // now we find the exact position's x and y + nw = (double)nHalfW - (cos (lfAngle) * lfNewRadius); + nh = (double)nHalfH - (sin (lfAngle) * lfNewRadius); + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the Polar Coordinates effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Type => if true Polar Coordinate to Polar else inverse. + * Antialias => Smart bluring result. + * + * Theory => Similar to PolarCoordinates from Photoshop. We apply the polar + * transformation in a proportional (Height and Width) radius. + */ +void DistortionFX::polarCoordinates(Digikam::DImg *orgImage, Digikam::DImg *destImage, bool Type, bool AntiAlias) +{ + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int h, w; + double nh, nw, th, tw; + int progress; + + int nHalfW = Width / 2, nHalfH = Height / 2; + double lfXScale = 1.0, lfYScale = 1.0; + double lfAngle, lfRadius, lfRadMax; + + if (Width > Height) + lfYScale = (double)Width / (double)Height; + else if (Height > Width) + lfXScale = (double)Height / (double)Width; + + lfRadMax = (double)TQMAX(Height, Width) / 2.0; + + // main loop + + for (h = 0; !m_cancel && (h < Height); h++) + { + th = lfYScale * (double)(h - nHalfH); + + for (w = 0; !m_cancel && (w < Width); w++) + { + tw = lfXScale * (double)(w - nHalfW); + + if (Type) + { + // now, we get the distance + lfRadius = sqrt (th * th + tw * tw); + // we find the angle from the center + lfAngle = atan2 (tw, th); + + // now we find the exact position's x and y + nh = lfRadius * (double) Height / lfRadMax; + nw = lfAngle * (double) Width / (2 * M_PI); + + nw = (double)nHalfW + nw; + } + else + { + lfRadius = (double)(h) * lfRadMax / (double)Height; + lfAngle = (double)(w) * (2 * M_PI) / (double) Width; + + nw = (double)nHalfW - (lfRadius / lfXScale) * sin (lfAngle); + nh = (double)nHalfH - (lfRadius / lfYScale) * cos (lfAngle); + } + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the circular waves effect backported from ImageProcessing version 2 + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * X, Y => Position of circle center on the image. + * Amplitude => Sinoidal maximum height + * Frequency => Frequency value. + * Phase => Phase value. + * WavesType => If true the amplitude is proportional to radius. + * Antialias => Smart bluring result. + * + * Theory => Similar to Waves effect, but here I apply a senoidal function + * with the angle point. + */ +void DistortionFX::circularWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, double Amplitude, + double Frequency, double Phase, bool WavesType, bool AntiAlias) +{ + if (Amplitude < 0.0) Amplitude = 0.0; + if (Frequency < 0.0) Frequency = 0.0; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int h, w; + double nh, nw; + int progress; + + double lfRadius, lfRadMax, lfNewAmp = Amplitude; + double lfFreqAngle = Frequency * ANGLE_RATIO; + + Phase *= ANGLE_RATIO; + + lfRadMax = sqrt (Height * Height + Width * Width); + + for (h = 0; !m_cancel && (h < Height); h++) + { + for (w = 0; !m_cancel && (w < Width); w++) + { + nw = X - w; + nh = Y - h; + + lfRadius = sqrt (nw * nw + nh * nh); + + if (WavesType) + lfNewAmp = Amplitude * lfRadius / lfRadMax; + + nw = (double)w + lfNewAmp * sin(lfFreqAngle * lfRadius + Phase); + nh = (double)h + lfNewAmp * cos(lfFreqAngle * lfRadius + Phase); + + setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the waves effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Amplitude => Sinoidal maximum height. + * Frequency => Frequency value. + * FillSides => Like a boolean variable. + * Direction => Vertical or horizontal flag. + * + * Theory => This is an amazing effect, very funny, and very simple to + * understand. You just need understand how sin and cos works. + */ +void DistortionFX::waves(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int Amplitude, int Frequency, + bool FillSides, bool Direction) +{ + if (Amplitude < 0) Amplitude = 0; + if (Frequency < 0) Frequency = 0; + + int Width = orgImage->width(); + int Height = orgImage->height(); + + int progress; + int h, w; + + if (Direction) // Horizontal + { + int tx; + + for (h = 0; !m_cancel && (h < Height); h++) + { + tx = lround(Amplitude * sin ((Frequency * 2) * h * (M_PI / 180))); + destImage->bitBltImage(orgImage, 0, h, Width, 1, tx, h); + + if (FillSides) + { + destImage->bitBltImage(orgImage, Width - tx, h, tx, 1, 0, h); + destImage->bitBltImage(orgImage, 0, h, Width - (Width - 2 * Amplitude + tx), 1, Width + tx, h); + } + + // Update the progress bar in dialog. + progress = (int) (((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } + } + else + { + int ty; + + for (w = 0; !m_cancel && (w < Width); w++) + { + ty = lround(Amplitude * sin ((Frequency * 2) * w * (M_PI / 180))); + destImage->bitBltImage(orgImage, w, 0, 1, Height, w, ty); + + if (FillSides) + { + destImage->bitBltImage(orgImage, w, Height - ty, 1, ty, w, 0); + destImage->bitBltImage(orgImage, w, 0, 1, Height - (Height - 2 * Amplitude + ty), w, Height + ty); + } + + // Update the progress bar in dialog. + progress = (int) (((double)w * 100.0) / Width); + + if (progress%5 == 0) + postProgress(progress); + } + } +} + +/* Function to apply the block waves effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * Amplitude => Sinoidal maximum height + * Frequency => Frequency value + * Mode => The mode to be applied. + * + * Theory => This is an amazing effect, very funny when amplitude and + * frequency are small values. + */ +void DistortionFX::blockWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int Amplitude, int Frequency, bool Mode) +{ + if (Amplitude < 0) Amplitude = 0; + if (Frequency < 0) Frequency = 0; + + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* pResBits = destImage->bits(); + + int nw, nh, progress; + double Radius; + + Digikam::DColor color; + int offset, offsetOther; + + int nHalfW = Width / 2, nHalfH = Height / 2; + + for (int w = 0; !m_cancel && (w < Width); w++) + { + for (int h = 0; !m_cancel && (h < Height); h++) + { + nw = nHalfW - w; + nh = nHalfH - h; + + Radius = sqrt (nw * nw + nh * nh); + + if (Mode) + { + nw = (int)(w + Amplitude * sin (Frequency * nw * (M_PI / 180))); + nh = (int)(h + Amplitude * cos (Frequency * nh * (M_PI / 180))); + } + else + { + nw = (int)(w + Amplitude * sin (Frequency * w * (M_PI / 180))); + nh = (int)(h + Amplitude * cos (Frequency * h * (M_PI / 180))); + } + + offset = getOffset(Width, w, h, bytesDepth); + offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth); + + // read color + color.setColor(data + offsetOther, sixteenBit); + // write color to destination + color.setPixel(pResBits + offset); + } + + // Update the progress bar in dialog. + progress = (int) (((double)w * 100.0) / Width); + + if (progress%5 == 0) + postProgress(progress); + } +} + +/* Function to apply the tile effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * WSize => Tile Width + * HSize => Tile Height + * Random => Maximum random value + * + * Theory => Similar to Tile effect from Photoshop and very easy to + * understand. We get a rectangular area using WSize and HSize and + * replace in a position with a random distance from the original + * position. + */ +void DistortionFX::tile(Digikam::DImg *orgImage, Digikam::DImg *destImage, + int WSize, int HSize, int Random) +{ + if (WSize < 1) WSize = 1; + if (HSize < 1) HSize = 1; + if (Random < 1) Random = 1; + + int Width = orgImage->width(); + int Height = orgImage->height(); + + TQDateTime dt = TQDateTime::currentDateTime(); + TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); + uint seed = dt.secsTo(Y2000); + + int tx, ty, h, w, progress; + + for (h = 0; !m_cancel && (h < Height); h += HSize) + { + for (w = 0; !m_cancel && (w < Width); w += WSize) + { + tx = (int)(rand_r(&seed) % Random) - (Random / 2); + ty = (int)(rand_r(&seed) % Random) - (Random / 2); + destImage->bitBltImage(orgImage, w, h, WSize, HSize, w + tx, h + ty); + } + + // Update the progress bar in dialog. + progress = (int)(((double)h * 100.0) / Height); + + if (progress%5 == 0) + postProgress(progress); + } +} + +// UNUSED +/* Function to return the maximum radius with a determined angle + * + * Height => Height of the image + * Width => Width of the image + * Angle => Angle to analize the maximum radius + * + * Theory => This function calcule the maximum radius to that angle + * so, we can build an oval circunference + */ + /* +double DistortionFX::maximumRadius(int Height, int Width, double Angle) +{ + double MaxRad, MinRad; + double Radius, DegAngle = fabs (Angle * 57.295); // Rads -> Degrees + + MinRad = TQMIN (Height, Width) / 2.0; // Gets the minor radius + MaxRad = TQMAX (Height, Width) / 2.0; // Gets the major radius + + // Find the quadrant between -PI/2 and PI/2 + if (DegAngle > 90.0) + Radius = proportionalValue (MinRad, MaxRad, (DegAngle * (255.0 / 90.0))); + else + Radius = proportionalValue (MaxRad, MinRad, ((DegAngle - 90.0) * (255.0 / 90.0))); + return (Radius); +} + */ + +} // NameSpace DigikamDistortionFXImagesPlugin diff --git a/src/imageplugins/distortionfx/distortionfx.h b/src/imageplugins/distortionfx/distortionfx.h new file mode 100644 index 00000000..073f4df2 --- /dev/null +++ b/src/imageplugins/distortionfx/distortionfx.h @@ -0,0 +1,149 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Distortion FX threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DISTORTION_FX_H +#define DISTORTION_FX_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamDistortionFXImagesPlugin +{ + +class DistortionFX : public Digikam::DImgThreadedFilter +{ + +public: + + DistortionFX(Digikam::DImg *orgImage, TQObject *parent=0, int effectType=0, + int level=0, int iteration=0, bool antialiasing=true); + + ~DistortionFX(){}; + +public: + + enum DistortionFXTypes + { + FishEye=0, + Twirl, + CilindricalHor, + CilindricalVert, + CilindricalHV, + Caricature, + MultipleCorners, + WavesHorizontal, + WavesVertical, + BlockWaves1, + BlockWaves2, + CircularWaves1, + CircularWaves2, + PolarCoordinates, + UnpolarCoordinates, + Tile + }; + +private: + + virtual void filterImage(void); + + // Backported from ImageProcessing version 2 + void fisheye(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff, bool AntiAlias=true); + void twirl(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Twirl, bool AntiAlias=true); + void cilindrical(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff, + bool Horizontal, bool Vertical, bool AntiAlias=true); + void multipleCorners(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Factor, bool AntiAlias=true); + void polarCoordinates(Digikam::DImg *orgImage, Digikam::DImg *destImage, bool Type, bool AntiAlias=true); + void circularWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, double Amplitude, + double Frequency, double Phase, bool WavesType, bool AntiAlias=true); + + // Backported from ImageProcessing version 1 + void waves(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Amplitude, int Frequency, + bool FillSides, bool Direction); + void blockWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Amplitude, int Frequency, bool Mode); + void tile(Digikam::DImg *orgImage, Digikam::DImg *destImage, int WSize, int HSize, int Random); + + void setPixelFromOther(int Width, int Height, bool sixteenBit, int bytesDepth, + uchar *data, uchar *pResBits, + int w, int h, double nw, double nh, bool AntiAlias); + /* + //UNUSED + + inline double maximumRadius(int Height, int Width, double Angle); + + // This function does the same thing that ShadeColors function but using double variables. + inline double proportionalValue (double DestValue, double SrcValue, double Shade) + { + if (Shade == 0.0) return DestValue; + if (Shade == 255.0) return SrcValue; + return ((DestValue * (255.0 - Shade) + SrcValue * Shade) / 256.0); + }; + */ + + // Return the limit defined the max and min values. + inline int Lim_Max(int Now, int Up, int Max) + { + --Max; + while (Now > Max - Up) --Up; + return (Up); + }; + + inline bool isInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; + + inline int getOffset(int Width, int X, int Y, int bytesDepth) + { + return (Y * Width * bytesDepth) + (X * bytesDepth); + }; + + inline int getOffsetAdjusted(int Width, int Height, int X, int Y, int bytesDepth) + { + X = (X < 0) ? 0 : ((X >= Width ) ? (Width - 1) : X); + Y = (Y < 0) ? 0 : ((Y >= Height) ? (Height - 1) : Y); + return getOffset(Width, X, Y, bytesDepth); + }; + +private: + + bool m_antiAlias; + + int m_level; + int m_iteration; + int m_effectType; +}; + +} // NameSpace DigikamDistortionFXImagesPlugin + +#endif /* DISTORTION_FX_H */ diff --git a/src/imageplugins/distortionfx/distortionfxtool.cpp b/src/imageplugins/distortionfx/distortionfxtool.cpp new file mode 100644 index 00000000..45ff0e45 --- /dev/null +++ b/src/imageplugins/distortionfx/distortionfxtool.cpp @@ -0,0 +1,398 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "distortionfx.h" +#include "distortionfxtool.h" +#include "distortionfxtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamDistortionFXImagesPlugin +{ + +DistortionFXTool::DistortionFXTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("distortionfx"); + setToolName(i18n("Distortion Effects")); + setToolIcon(SmallIcon("distortionfx")); + + m_previewWidget = new ImageWidget("distortionfx Tool", 0, + i18n("

    This is the preview of the distortion effect " + "applied to the photograph."), + false, ImageGuideWidget::HVGuideMode); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel, + EditorToolSettings::ColorGuide); + + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 7, 2); + + m_effectTypeLabel = new TQLabel(i18n("Type:"), m_gboxSettings->plainPage()); + + m_effectType = new RComboBox(m_gboxSettings->plainPage()); + m_effectType->insertItem(i18n("Fish Eyes")); + m_effectType->insertItem(i18n("Twirl")); + m_effectType->insertItem(i18n("Cylindrical Hor.")); + m_effectType->insertItem(i18n("Cylindrical Vert.")); + m_effectType->insertItem(i18n("Cylindrical H/V.")); + m_effectType->insertItem(i18n("Caricature")); + m_effectType->insertItem(i18n("Multiple Corners")); + m_effectType->insertItem(i18n("Waves Hor.")); + m_effectType->insertItem(i18n("Waves Vert.")); + m_effectType->insertItem(i18n("Block Waves 1")); + m_effectType->insertItem(i18n("Block Waves 2")); + m_effectType->insertItem(i18n("Circular Waves 1")); + m_effectType->insertItem(i18n("Circular Waves 2")); + m_effectType->insertItem(i18n("Polar Coordinates")); + m_effectType->insertItem(i18n("Unpolar Coordinates")); + m_effectType->insertItem(i18n("Tile")); + m_effectType->setDefaultItem(DistortionFX::FishEye); + TQWhatsThis::add( m_effectType, i18n("

    Here, select the type of effect to apply to the image.

    " + "Fish Eyes: warps the photograph around a 3D spherical shape to " + "reproduce the common photograph 'Fish Eyes' effect.

    " + "Twirl: spins the photograph to produce a Twirl pattern.

    " + "Cylinder Hor.: warps the photograph around a horizontal cylinder.

    " + "Cylinder Vert.: warps the photograph around a vertical cylinder.

    " + "Cylinder H/V.: warps the photograph around 2 cylinders, vertical " + "and horizontal.

    " + "Caricature: distorts the photograph with the 'Fish Eyes' effect inverted.

    " + "Multiple Corners: splits the photograph like a multiple corners pattern.

    " + "Waves Horizontal: distorts the photograph with horizontal waves.

    " + "Waves Vertical: distorts the photograph with vertical waves.

    " + "Block Waves 1: divides the image into cells and makes it look as " + "if it is being viewed through glass blocks.

    " + "Block Waves 2: like Block Waves 1 but with another version " + "of glass blocks distortion.

    " + "Circular Waves 1: distorts the photograph with circular waves.

    " + "Circular Waves 2: another variation of the Circular Waves effect.

    " + "Polar Coordinates: converts the photograph from rectangular " + "to polar coordinates.

    " + "Unpolar Coordinates: the Polar Coordinates effect inverted.

    " + "Tile: splits the photograph into square blocks and moves " + "them randomly inside the image.

    " + )); + + m_levelLabel = new TQLabel(i18n("Level:"), m_gboxSettings->plainPage()); + m_levelInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_levelInput->setRange(0, 100, 1); + m_levelInput->setDefaultValue(50); + TQWhatsThis::add( m_levelInput, i18n("

    Set here the level of the effect.")); + + + m_iterationLabel = new TQLabel(i18n("Iteration:"), m_gboxSettings->plainPage()); + m_iterationInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_iterationInput->setRange(0, 100, 1); + m_iterationInput->setDefaultValue(10); + TQWhatsThis::add( m_iterationInput, i18n("

    This value controls the iterations to use for Waves, " + "Tile, and Neon effects.")); + + gridSettings->addMultiCellWidget(m_effectTypeLabel, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_effectType, 1, 1, 0, 1); + gridSettings->addMultiCellWidget(m_levelLabel, 2, 2, 0, 1); + gridSettings->addMultiCellWidget(m_levelInput, 3, 3, 0, 1); + gridSettings->addMultiCellWidget(m_iterationLabel, 4, 4, 0, 1); + gridSettings->addMultiCellWidget(m_iterationInput, 5, 5, 0, 1); + gridSettings->setRowStretch(6, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_iterationInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_gboxSettings, TQ_SIGNAL(signalColorGuideChanged()), + this, TQ_SLOT(slotColorGuideChanged())); +} + +DistortionFXTool::~DistortionFXTool() +{ +} + +void DistortionFXTool::slotColorGuideChanged() +{ + m_previewWidget->slotChangeGuideColor(m_gboxSettings->guideColor()); + m_previewWidget->slotChangeGuideSize(m_gboxSettings->guideSize()); +} + +void DistortionFXTool::renderingFinished() +{ + m_effectTypeLabel->setEnabled(true); + m_effectType->setEnabled(true); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + + switch (m_effectType->currentItem()) + { + case DistortionFX::FishEye: + case DistortionFX::Twirl: + case DistortionFX::CilindricalHor: + case DistortionFX::CilindricalVert: + case DistortionFX::CilindricalHV: + case DistortionFX::Caricature: + case DistortionFX::MultipleCorners: + break; + + case DistortionFX::PolarCoordinates: + case DistortionFX::UnpolarCoordinates: + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + break; + + case DistortionFX::WavesHorizontal: + case DistortionFX::WavesVertical: + case DistortionFX::BlockWaves1: + case DistortionFX::BlockWaves2: + case DistortionFX::CircularWaves1: + case DistortionFX::CircularWaves2: + case DistortionFX::Tile: + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + break; + } +} + +void DistortionFXTool::readSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("distortionfx Tool"); + + m_effectType->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->blockSignals(true); + + m_effectType->setCurrentItem(config->readNumEntry("EffectType", + m_effectType->defaultItem())); + m_iterationInput->setValue(config->readNumEntry("IterationAjustment", + m_iterationInput->defaultValue())); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", + m_levelInput->defaultValue())); + + m_effectType->blockSignals(false); + m_iterationInput->blockSignals(false); + m_levelInput->blockSignals(false); + + slotEffect(); +} + +void DistortionFXTool::writeSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("distortionfx Tool"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("IterationAjustment", m_iterationInput->value()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void DistortionFXTool::slotResetSettings() +{ + m_effectType->blockSignals(true); + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + + m_levelInput->slotReset(); + m_iterationInput->slotReset(); + m_effectType->slotReset(); + slotEffectTypeChanged(m_effectType->defaultItem()); + + m_effectType->blockSignals(false); + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); +} + +void DistortionFXTool::slotEffectTypeChanged(int type) +{ + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->setRange(0, 100, 1); + m_levelInput->setValue(25); + + switch (type) + { + case DistortionFX::Twirl: + m_levelInput->setRange(-50, 50, 1); + m_levelInput->setValue(10); + break; + + case DistortionFX::FishEye: + case DistortionFX::CilindricalHor: + case DistortionFX::CilindricalVert: + case DistortionFX::CilindricalHV: + case DistortionFX::Caricature: + m_levelInput->setRange(0, 200, 1); + m_levelInput->setValue(50); + break; + + case DistortionFX::MultipleCorners: + m_levelInput->setRange(1, 10, 1); + m_levelInput->setValue(4); + break; + + case DistortionFX::WavesHorizontal: + case DistortionFX::WavesVertical: + case DistortionFX::BlockWaves1: + case DistortionFX::BlockWaves2: + case DistortionFX::CircularWaves1: + case DistortionFX::CircularWaves2: + case DistortionFX::Tile: + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + m_iterationInput->setRange(0, 200, 1); + m_iterationInput->setValue(10); + break; + + case DistortionFX::PolarCoordinates: + case DistortionFX::UnpolarCoordinates: + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + break; + } + + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); + + slotEffect(); +} + +void DistortionFXTool::prepareEffect() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + + int l = m_levelInput->value(); + int f = m_iterationInput->value(); + int e = m_effectType->currentItem(); + + ImageIface* iface = m_previewWidget->imageIface(); + + uchar *data = iface->getPreviewImage(); + DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast (new DistortionFX(&image, this, e, l, f))); +} + +void DistortionFXTool::prepareFinal() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + + int l = m_levelInput->value(); + int f = m_iterationInput->value(); + int e = m_effectType->currentItem(); + + ImageIface iface(0, 0); + + setFilter(dynamic_cast (new DistortionFX(iface.getOriginalImg(), this, e, l, f))); +} + +void DistortionFXTool::putPreviewData(void) +{ + ImageIface* iface = m_previewWidget->imageIface(); + + DImg imDest = filter()->getTargetImage() + .smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_previewWidget->updatePreview(); +} + +void DistortionFXTool::putFinalData(void) +{ + ImageIface iface(0, 0); + DImg targetImage = filter()->getTargetImage(); + iface.putOriginalImage(i18n("Distortion Effects"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +} // NameSpace DigikamDistortionFXImagesPlugin + diff --git a/src/imageplugins/distortionfx/distortionfxtool.h b/src/imageplugins/distortionfx/distortionfxtool.h new file mode 100644 index 00000000..bcdd6088 --- /dev/null +++ b/src/imageplugins/distortionfx/distortionfxtool.h @@ -0,0 +1,96 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DISTORTIONFXTOOL_H +#define DISTORTIONFXTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class ImageWidget; +} + +namespace DigikamDistortionFXImagesPlugin +{ + +class DistortionFXTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + DistortionFXTool(TQObject *parent); + ~DistortionFXTool(); + +private slots: + + void slotEffectTypeChanged(int type); + void slotResetSettings(); + void slotColorGuideChanged(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + + TQLabel *m_effectTypeLabel; + TQLabel *m_levelLabel; + TQLabel *m_iterationLabel; + + KDcrawIface::RComboBox *m_effectType; + + KDcrawIface::RIntNumInput *m_levelInput; + KDcrawIface::RIntNumInput *m_iterationInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamDistortionFXImagesPlugin + +#endif /* DISTORTIONFXTOOL_H */ diff --git a/src/imageplugins/distortionfx/imageeffect_distortionfx.cpp b/src/imageplugins/distortionfx/imageeffect_distortionfx.cpp new file mode 100644 index 00000000..695e7749 --- /dev/null +++ b/src/imageplugins/distortionfx/imageeffect_distortionfx.cpp @@ -0,0 +1,378 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "distortionfx.h" +#include "imageeffect_distortionfx.h" +#include "imageeffect_distortionfx.moc" + +namespace DigikamDistortionFXImagesPlugin +{ + +ImageEffect_DistortionFX::ImageEffect_DistortionFX(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Distortion Effects"), + "distortionfx", false, true, false, + Digikam::ImageGuideWidget::HVGuideMode) +{ + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Distortion Effects"), + digikam_version, + I18N_NOOP("A digiKam image plugin to apply distortion effects to an image."), + TDEAboutData::License_GPL, + "(c) 2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Distortion algorithms"), + "pieter dot voloshyn at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + TQWhatsThis::add( m_imagePreviewWidget, i18n("

    This is the preview of the distortion effect " + "applied to the photograph.") ); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 5, 2, spacingHint()); + + m_effectTypeLabel = new TQLabel(i18n("Type:"), gboxSettings); + + m_effectType = new TQComboBox( false, gboxSettings ); + m_effectType->insertItem( i18n("Fish Eyes") ); + m_effectType->insertItem( i18n("Twirl") ); + m_effectType->insertItem( i18n("Cylindrical Hor.") ); + m_effectType->insertItem( i18n("Cylindrical Vert.") ); + m_effectType->insertItem( i18n("Cylindrical H/V.") ); + m_effectType->insertItem( i18n("Caricature") ); + m_effectType->insertItem( i18n("Multiple Corners") ); + m_effectType->insertItem( i18n("Waves Hor.") ); + m_effectType->insertItem( i18n("Waves Vert.") ); + m_effectType->insertItem( i18n("Block Waves 1") ); + m_effectType->insertItem( i18n("Block Waves 2") ); + m_effectType->insertItem( i18n("Circular Waves 1") ); + m_effectType->insertItem( i18n("Circular Waves 2") ); + m_effectType->insertItem( i18n("Polar Coordinates") ); + m_effectType->insertItem( i18n("Unpolar Coordinates") ); + m_effectType->insertItem( i18n("Tile") ); + TQWhatsThis::add( m_effectType, i18n("

    Here, select the type of effect to apply to the image.

    " + "Fish Eyes: warps the photograph around a 3D spherical shape to " + "reproduce the common photograph 'Fish Eyes' effect.

    " + "Twirl: spins the photograph to produce a Twirl pattern.

    " + "Cylinder Hor.: warps the photograph around a horizontal cylinder.

    " + "Cylinder Vert.: warps the photograph around a vertical cylinder.

    " + "Cylinder H/V.: warps the photograph around 2 cylinders, vertical " + "and horizontal.

    " + "Caricature: distorts the photograph with the 'Fish Eyes' effect inverted.

    " + "Multiple Corners: splits the photograph like a multiple corners pattern.

    " + "WavesQt Horizontal: distorts the photograph with horizontal waves.

    " + "Waves Vertical: distorts the photograph with vertical waves.

    " + "Block Waves 1: divides the image into cells and makes it look as " + "if it is being viewed through glass blocks.

    " + "Block Waves 2: like Block Waves 1 but with another version " + "of glass blocks distortion.

    " + "Circular Waves 1: distorts the photograph with circular waves.

    " + "Circular Waves 2: another variation of the Circular Waves effect.

    " + "Polar Coordinates: converts the photograph from rectangular " + "to polar coordinates.

    " + "Unpolar Coordinates: the Polar Coordinates effect inverted.

    " + "Tile: splits the photograph into square blocks and moves " + "them randomly inside the image.

    " + )); + gridSettings->addMultiCellWidget(m_effectTypeLabel, 0, 0, 0, 2); + gridSettings->addMultiCellWidget(m_effectType, 1, 1, 0, 2); + + m_levelLabel = new TQLabel(i18n("Level:"), gboxSettings); + m_levelInput = new KIntNumInput(gboxSettings); + m_levelInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_levelInput, i18n("

    Set here the level of the effect.")); + + gridSettings->addMultiCellWidget(m_levelLabel, 2, 2, 0, 2); + gridSettings->addMultiCellWidget(m_levelInput, 3, 3, 0, 2); + + m_iterationLabel = new TQLabel(i18n("Iteration:"), gboxSettings); + m_iterationInput = new KIntNumInput(gboxSettings); + m_iterationInput->setRange(0, 100, 1, true); + TQWhatsThis::add( m_iterationInput, i18n("

    This value controls the iterations to use for Waves, " + "Tile, and Neon effects.")); + + gridSettings->addMultiCellWidget(m_iterationLabel, 4, 4, 0, 2); + gridSettings->addMultiCellWidget(m_iterationInput, 5, 5, 0, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_effectType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffectTypeChanged(int))); + + connect(m_levelInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_iterationInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_DistortionFX::~ImageEffect_DistortionFX() +{ +} + +void ImageEffect_DistortionFX::renderingFinished() +{ + m_effectTypeLabel->setEnabled(true); + m_effectType->setEnabled(true); + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + + switch (m_effectType->currentItem()) + { + case DistortionFX::FishEye: + case DistortionFX::Twirl: + case DistortionFX::CilindricalHor: + case DistortionFX::CilindricalVert: + case DistortionFX::CilindricalHV: + case DistortionFX::Caricature: + case DistortionFX::MultipleCorners: + break; + + case DistortionFX::PolarCoordinates: + case DistortionFX::UnpolarCoordinates: + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + break; + + case DistortionFX::WavesHorizontal: + case DistortionFX::WavesVertical: + case DistortionFX::BlockWaves1: + case DistortionFX::BlockWaves2: + case DistortionFX::CircularWaves1: + case DistortionFX::CircularWaves2: + case DistortionFX::Tile: + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + break; + } +} + +void ImageEffect_DistortionFX::readUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("distortionfx Tool Dialog"); + + m_effectType->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->blockSignals(true); + + m_effectType->setCurrentItem(config->readNumEntry("EffectType", DistortionFX::FishEye)); + m_iterationInput->setValue(config->readNumEntry("IterationAjustment", 10)); + m_levelInput->setValue(config->readNumEntry("LevelAjustment", 50)); + + m_effectType->blockSignals(false); + m_iterationInput->blockSignals(false); + m_levelInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_DistortionFX::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("distortionfx Tool Dialog"); + config->writeEntry("EffectType", m_effectType->currentItem()); + config->writeEntry("IterationAjustment", m_iterationInput->value()); + config->writeEntry("LevelAjustment", m_levelInput->value()); + config->sync(); +} + +void ImageEffect_DistortionFX::resetValues() +{ + m_effectType->blockSignals(true); + m_effectType->setCurrentItem(DistortionFX::FishEye); + slotEffectTypeChanged(DistortionFX::FishEye); + m_effectType->blockSignals(false); +} + +void ImageEffect_DistortionFX::slotEffectTypeChanged(int type) +{ + m_levelInput->setEnabled(true); + m_levelLabel->setEnabled(true); + + m_levelInput->blockSignals(true); + m_iterationInput->blockSignals(true); + m_levelInput->setRange(0, 100, 1, true); + m_levelInput->setValue(25); + + switch (type) + { + case DistortionFX::Twirl: + m_levelInput->setRange(-50, 50, 1, true); + m_levelInput->setValue(10); + break; + + case DistortionFX::FishEye: + case DistortionFX::CilindricalHor: + case DistortionFX::CilindricalVert: + case DistortionFX::CilindricalHV: + case DistortionFX::Caricature: + m_levelInput->setRange(0, 200, 1, true); + m_levelInput->setValue(50); + break; + + case DistortionFX::MultipleCorners: + m_levelInput->setRange(1, 10, 1, true); + m_levelInput->setValue(4); + break; + + case DistortionFX::WavesHorizontal: + case DistortionFX::WavesVertical: + case DistortionFX::BlockWaves1: + case DistortionFX::BlockWaves2: + case DistortionFX::CircularWaves1: + case DistortionFX::CircularWaves2: + case DistortionFX::Tile: + m_iterationInput->setEnabled(true); + m_iterationLabel->setEnabled(true); + m_iterationInput->setRange(0, 200, 1, true); + m_iterationInput->setValue(10); + break; + + case DistortionFX::PolarCoordinates: + case DistortionFX::UnpolarCoordinates: + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + break; + } + + m_levelInput->blockSignals(false); + m_iterationInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_DistortionFX::prepareEffect() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + + int l = m_levelInput->value(); + int f = m_iterationInput->value(); + int e = m_effectType->currentItem(); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + uchar *data = iface->getPreviewImage(); + Digikam::DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new DistortionFX(&image, this, e, l, f)); +} + +void ImageEffect_DistortionFX::prepareFinal() +{ + m_effectTypeLabel->setEnabled(false); + m_effectType->setEnabled(false); + m_levelInput->setEnabled(false); + m_levelLabel->setEnabled(false); + m_iterationInput->setEnabled(false); + m_iterationLabel->setEnabled(false); + + int l = m_levelInput->value(); + int f = m_iterationInput->value(); + int e = m_effectType->currentItem(); + + Digikam::ImageIface iface(0, 0); + + m_threadedFilter = dynamic_cast( + new DistortionFX(iface.getOriginalImg(), this, e, l, f)); +} + +void ImageEffect_DistortionFX::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + Digikam::DImg imDest = m_threadedFilter->getTargetImage() + .smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_imagePreviewWidget->updatePreview(); +} + +void ImageEffect_DistortionFX::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Distortion Effects"), + m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamDistortionFXImagesPlugin + diff --git a/src/imageplugins/distortionfx/imageeffect_distortionfx.h b/src/imageplugins/distortionfx/imageeffect_distortionfx.h new file mode 100644 index 00000000..652b372b --- /dev/null +++ b/src/imageplugins/distortionfx/imageeffect_distortionfx.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_DISTORTIONFX_H +#define IMAGEEFFECT_DISTORTIONFX_H + +// Digikam includes. + +#include "imageguidedlg.h" + +class TQComboBox; +class TQLabel; + +class KIntNumInput; + +namespace DigikamDistortionFXImagesPlugin +{ + +class ImageEffect_DistortionFX : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_DistortionFX(TQWidget *parent); + ~ImageEffect_DistortionFX(); + +private slots: + + void slotEffectTypeChanged(int type); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQComboBox *m_effectType; + + TQLabel *m_effectTypeLabel; + TQLabel *m_levelLabel; + TQLabel *m_iterationLabel; + + KIntNumInput *m_levelInput; + KIntNumInput *m_iterationInput; +}; + +} // NameSpace DigikamDistortionFXImagesPlugin + +#endif /* IMAGEEFFECT_DISTORTIONFX_H */ diff --git a/src/imageplugins/distortionfx/imageplugin_distortionfx.cpp b/src/imageplugins/distortionfx/imageplugin_distortionfx.cpp new file mode 100644 index 00000000..582e1b99 --- /dev/null +++ b/src/imageplugins/distortionfx/imageplugin_distortionfx.cpp @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "distortionfxtool.h" +#include "imageplugin_distortionfx.h" +#include "imageplugin_distortionfx.moc" + +using namespace DigikamDistortionFXImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_distortionfx, + KGenericFactory("digikamimageplugin_distortionfx")); + +ImagePlugin_DistortionFX::ImagePlugin_DistortionFX(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_DistortionFX") +{ + m_distortionfxAction = new TDEAction(i18n("Distortion Effects..."), "distortionfx", 0, + this, TQ_SLOT(slotDistortionFX()), + actionCollection(), "imageplugin_distortionfx"); + + setXMLFile( "digikamimageplugin_distortionfx_ui.rc" ); + + DDebug() << "ImagePlugin_DistortionFX plugin loaded" << endl; +} + +ImagePlugin_DistortionFX::~ImagePlugin_DistortionFX() +{ +} + +void ImagePlugin_DistortionFX::setEnabledActions(bool enable) +{ + m_distortionfxAction->setEnabled(enable); +} + +void ImagePlugin_DistortionFX::slotDistortionFX() +{ + DistortionFXTool *distortionfx = new DistortionFXTool(this); + loadTool(distortionfx); +} diff --git a/src/imageplugins/distortionfx/imageplugin_distortionfx.h b/src/imageplugins/distortionfx/imageplugin_distortionfx.h new file mode 100644 index 00000000..2af33a14 --- /dev/null +++ b/src/imageplugins/distortionfx/imageplugin_distortionfx.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-11 + * Description : a plugin to apply Distortion FX to an image. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Distortion algorithms copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_DISTORTIONFX_H +#define IMAGEPLUGIN_DISTORTIONFX_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_DistortionFX : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_DistortionFX(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_DistortionFX(); + + void setEnabledActions(bool enable); + +private slots: + + void slotDistortionFX(); + +private: + + TDEAction *m_distortionfxAction; +}; + +#endif /* IMAGEPLUGIN_DISTORTIONFX_H */ diff --git a/src/imageplugins/emboss/Makefile.am b/src/imageplugins/emboss/Makefile.am new file mode 100644 index 00000000..c595aa30 --- /dev/null +++ b/src/imageplugins/emboss/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_emboss_la_SOURCES = imageplugin_emboss.cpp \ + embosstool.cpp emboss.cpp + +digikamimageplugin_emboss_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_emboss_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_emboss.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_emboss.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_emboss_ui.rc + diff --git a/src/imageplugins/emboss/digikamimageplugin_emboss.desktop b/src/imageplugins/emboss/digikamimageplugin_emboss.desktop new file mode 100644 index 00000000..414f235b --- /dev/null +++ b/src/imageplugins/emboss/digikamimageplugin_emboss.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=ImagePlugin_Emboss +Name[bg]=ПриÑтавка за Ñнимки - Релеф +Name[da]=Billedplugin_Præg i relief +Name[el]=ΠÏόσθετοΕικόνας_Εμβάθυνση +Name[fi]=Kohokuvio +Name[hr]=Reljef +Name[it]=PluginImmagini_Rilievo +Name[nl]=Afbeeldingsplugin_Reliëf +Name[sr]=Рељеф +Name[sr@Latn]=Reljef +Name[sv]=Insticksprogram för reliefeffekt +Name[tr]=ResimEklentisi_Kabart +Name[xx]=xxImagePlugin_Embossxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Emboss image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за добавÑне на релеф към Ñнимки +Comment[ca]=Connector pel digiKam d'efecte d'imatge de relleu +Comment[cs]=Efektový modul pro tvorbu reliéfu pro digiKam +Comment[da]=Reliefprægnings-plugin for Digikam +Comment[de]=digiKam-Modul zum Erzeugen eines Gravureffektes +Comment[el]=ΠÏόσθετο εφέ εμβάθυνσης για το digiKam +Comment[es]=Plugin para digiKam con efectos de bajo relieve +Comment[et]=DigiKami kohrutuse pildiefektiplugin +Comment[fa]=برجسته کردن وصلۀ جلوۀ تصویر برای digiKam +Comment[fi]=Kohokuvioefekti kohteiden ääriviivoista +Comment[gl]=Un plugin de digiKam para dar-lle relevo á imaxe +Comment[hr]=digiKam dodatak za efekt reljefa +Comment[is]=Ãforrit fyrir digiKam sem framkallar upphleypingaráhrif +Comment[it]=Plugin di effetto di rilievo delle immagini per digiKam +Comment[ja]=digiKam エンボス効果プラグイン +Comment[nds]=digiKam-Moduul för Ingraav-Effekten +Comment[nl]=Digikam-plugin voor reliëf-afbeeldingen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਈਮਬੋਸ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam oferujÄ…ca efekty znieksztaÅ‚ceÅ„ +Comment[pt]=Um 'plugin' do digiKam para gravar em relevo a imagem +Comment[pt_BR]=Plugin de efeito de estampa +Comment[ru]=Модуль рельефного риÑунка Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre efekt reliéfu +Comment[sr]=digiKam-ов прикључак за ефекат рељефа на Ñликама +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekat reljefa na slikama +Comment[sv]=Digikam insticksprogram för reliefbildeffekt +Comment[tr]=digiKam için resim kabartma etkisi eklentisi +Comment[uk]=Втулок ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ барельєфу Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng chạm nổi ảnh cho digiKam +Comment[xx]=xxEmboss image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_emboss +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/emboss/digikamimageplugin_emboss_ui.rc b/src/imageplugins/emboss/digikamimageplugin_emboss_ui.rc new file mode 100644 index 00000000..1a6943c6 --- /dev/null +++ b/src/imageplugins/emboss/digikamimageplugin_emboss_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/emboss/emboss.cpp b/src/imageplugins/emboss/emboss.cpp new file mode 100644 index 00000000..d4225148 --- /dev/null +++ b/src/imageplugins/emboss/emboss.cpp @@ -0,0 +1,127 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Emboss threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original Emboss algorithm copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "emboss.h" + +namespace DigikamEmbossImagesPlugin +{ + +Emboss::Emboss(Digikam::DImg *orgImage, TQObject *parent, int depth) + : Digikam::DImgThreadedFilter(orgImage, parent, "Emboss") +{ + m_depth = depth; + initFilter(); +} + +void Emboss::filterImage(void) +{ + embossImage(&m_orgImage, &m_destImage, m_depth); +} + +// This method have been ported from Pieter Z. Voloshyn algorithm code. + +/* Function to apply the Emboss effect + * + * data => The image data in RGBA mode. + * Width => Width of image. + * Height => Height of image. + * d => Emboss value + * + * Theory => This is an amazing effect. And the theory is very simple to + * understand. You get the diference between the colors and + * increase it. After this, get the gray tone + */ + +void Emboss::embossImage(Digikam::DImg *orgImage, Digikam::DImg *destImage, int d) +{ + int Width = orgImage->width(); + int Height = orgImage->height(); + uchar* data = orgImage->bits(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar* Bits = destImage->bits(); + + // Initial copy + memcpy (Bits, data, destImage->numBytes()); + + double Depth = d / 10.0; + + int progress; + int red, green, blue, gray; + Digikam::DColor color, colorOther; + int offset, offsetOther; + + for (int h = 0 ; !m_cancel && (h < Height) ; h++) + { + for (int w = 0 ; !m_cancel && (w < Width) ; w++) + { + offset = getOffset(Width, w, h, bytesDepth); + offsetOther = getOffset(Width, w + Lim_Max (w, 1, Width), h + Lim_Max (h, 1, Height), bytesDepth); + + color.setColor(Bits + offset, sixteenBit); + colorOther.setColor(Bits + offsetOther, sixteenBit); + + if (sixteenBit) + { + red = abs ((int)((color.red() - colorOther.red()) * Depth + 32768)); + green = abs ((int)((color.green() - colorOther.green()) * Depth + 32768)); + blue = abs ((int)((color.blue() - colorOther.blue()) * Depth + 32768)); + + gray = CLAMP065535 ((red + green + blue) / 3); + } + else + { + red = abs ((int)((color.red() - colorOther.red()) * Depth + 128)); + green = abs ((int)((color.green() - colorOther.green()) * Depth + 128)); + blue = abs ((int)((color.blue() - colorOther.blue()) * Depth + 128)); + + gray = CLAMP0255 ((red + green + blue) / 3); + } + + // Overwrite RGB values to destination. Alpha remains unchanged. + color.setRed(gray); + color.setGreen(gray); + color.setBlue(gray); + color.setPixel(Bits + offset); + } + + progress = (int) (((double)h * 100.0) / Height); + if ( progress%5 == 0 ) + postProgress( progress ); + } +} + +} // NameSpace DigikamEmbossImagesPlugin diff --git a/src/imageplugins/emboss/emboss.h b/src/imageplugins/emboss/emboss.h new file mode 100644 index 00000000..470755a6 --- /dev/null +++ b/src/imageplugins/emboss/emboss.h @@ -0,0 +1,75 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Emboss threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original Emboss algorithm copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef EMBOSS_H +#define EMBOSS_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamEmbossImagesPlugin +{ + +class Emboss : public Digikam::DImgThreadedFilter +{ + +public: + + Emboss(Digikam::DImg *orgImage, TQObject *parent=0, int depth=30); + ~Emboss(){}; + +private: + + virtual void filterImage(void); + + + void embossImage(Digikam::DImg *orgImage, Digikam::DImg *destImage, int d); + + // Function to limit the max and min values defined by the developer. + + inline int Lim_Max (int Now, int Up, int Max) + { + --Max; + while (Now > Max - Up) + --Up; + return (Up); + }; + + inline int getOffset(int Width, int X, int Y, int bytesDepth) + { + return (Y * Width * bytesDepth) + (X * bytesDepth); + }; + +private: + + int m_depth; +}; + +} // NameSpace DigikamEmbossImagesPlugin + +#endif /* EMBOSS_H */ diff --git a/src/imageplugins/emboss/embosstool.cpp b/src/imageplugins/emboss/embosstool.cpp new file mode 100644 index 00000000..f16ef4de --- /dev/null +++ b/src/imageplugins/emboss/embosstool.cpp @@ -0,0 +1,167 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "emboss.h" +#include "embosstool.h" +#include "embosstool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamEmbossImagesPlugin +{ + +EmbossTool::EmbossTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("emboss"); + setToolName(i18n("Emboss")); + setToolIcon(SmallIcon("embosstool")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 2, 1); + TQLabel *label1 = new TQLabel(i18n("Depth:"), m_gboxSettings->plainPage()); + + m_depthInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_depthInput->setRange(10, 300, 1); + m_depthInput->setDefaultValue(30); + TQWhatsThis::add( m_depthInput, i18n("

    Set here the depth of the embossing image effect.") ); + + grid->addMultiCellWidget(label1, 0, 0, 0, 1); + grid->addMultiCellWidget(m_depthInput, 1, 1, 0, 1); + grid->setRowStretch(2, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "emboss Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_depthInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); +} + +EmbossTool::~EmbossTool() +{ +} + +void EmbossTool::renderingFinished() +{ + m_depthInput->setEnabled(true); +} + +void EmbossTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("emboss Tool"); + m_depthInput->blockSignals(true); + m_depthInput->setValue(config->readNumEntry("DepthAjustment", m_depthInput->defaultValue())); + m_depthInput->blockSignals(false); +} + +void EmbossTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("emboss Tool"); + config->writeEntry("DepthAjustment", m_depthInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void EmbossTool::slotResetSettings() +{ + m_depthInput->blockSignals(true); + m_depthInput->slotReset(); + m_depthInput->blockSignals(false); +} + +void EmbossTool::prepareEffect() +{ + m_depthInput->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + int depth = m_depthInput->value(); + + setFilter(dynamic_cast(new Emboss(&image, this, depth))); +} + +void EmbossTool::prepareFinal() +{ + m_depthInput->setEnabled(false); + + int depth = m_depthInput->value(); + ImageIface iface(0, 0); + setFilter(dynamic_cast(new Emboss(iface.getOriginalImg(), this, depth))); +} + +void EmbossTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void EmbossTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Emboss"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamEmbossImagesPlugin diff --git a/src/imageplugins/emboss/embosstool.h b/src/imageplugins/emboss/embosstool.h new file mode 100644 index 00000000..224858f7 --- /dev/null +++ b/src/imageplugins/emboss/embosstool.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef EMBOSSTOOL_H +#define EMBOSSTOOL_H + +// Digikam includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamEmbossImagesPlugin +{ + +class EmbossTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + EmbossTool(TQObject* parent); + ~EmbossTool(); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KDcrawIface::RIntNumInput *m_depthInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamEmbossImagesPlugin + +#endif /* EMBOSSTOOL_H */ diff --git a/src/imageplugins/emboss/imageeffect_emboss.cpp b/src/imageplugins/emboss/imageeffect_emboss.cpp new file mode 100644 index 00000000..b4557c80 --- /dev/null +++ b/src/imageplugins/emboss/imageeffect_emboss.cpp @@ -0,0 +1,170 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "emboss.h" +#include "imageeffect_emboss.h" +#include "imageeffect_emboss.moc" + +namespace DigikamEmbossImagesPlugin +{ + +ImageEffect_Emboss::ImageEffect_Emboss(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Emboss Image"), "emboss", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Emboss Image"), + digikam_version, + I18N_NOOP("Emboss image effect plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2004-2006, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Emboss algorithm"), + "pieter dot voloshyn at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 1, 1, 0, spacingHint()); + TQLabel *label1 = new TQLabel(i18n("Depth:"), gboxSettings); + + m_depthInput = new KIntNumInput(gboxSettings); + m_depthInput->setRange(10, 300, 1, true); + TQWhatsThis::add( m_depthInput, i18n("

    Set here the depth of the embossing image effect.") ); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_depthInput, 1, 1, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_depthInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_Emboss::~ImageEffect_Emboss() +{ +} + +void ImageEffect_Emboss::renderingFinished() +{ + m_depthInput->setEnabled(true); +} + +void ImageEffect_Emboss::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("emboss Tool Dialog"); + m_depthInput->blockSignals(true); + m_depthInput->setValue(config->readNumEntry("DepthAjustment", 30)); + m_depthInput->blockSignals(false); +} + +void ImageEffect_Emboss::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("emboss Tool Dialog"); + config->writeEntry("DepthAjustment", m_depthInput->value()); + config->sync(); +} + +void ImageEffect_Emboss::resetValues() +{ + m_depthInput->blockSignals(true); + m_depthInput->setValue(30); + m_depthInput->blockSignals(false); +} + +void ImageEffect_Emboss::prepareEffect() +{ + m_depthInput->setEnabled(false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + + int depth = m_depthInput->value(); + + m_threadedFilter = dynamic_cast(new Emboss(&image, this, depth)); +} + +void ImageEffect_Emboss::prepareFinal() +{ + m_depthInput->setEnabled(false); + + int depth = m_depthInput->value(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast(new Emboss(iface.getOriginalImg(), this, depth)); +} + +void ImageEffect_Emboss::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_Emboss::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Emboss"), + m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamEmbossImagesPlugin + diff --git a/src/imageplugins/emboss/imageeffect_emboss.h b/src/imageplugins/emboss/imageeffect_emboss.h new file mode 100644 index 00000000..19cf4f77 --- /dev/null +++ b/src/imageplugins/emboss/imageeffect_emboss.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_EMBOSS_H +#define IMAGEEFFECT_EMBOSS_H + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class KIntNumInput; + +namespace DigikamEmbossImagesPlugin +{ + +class ImageEffect_Emboss : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Emboss(TQWidget* parent); + ~ImageEffect_Emboss(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KIntNumInput *m_depthInput; +}; + +} // NameSpace DigikamEmbossImagesPlugin + +#endif /* IMAGEEFFECT_EMBOSS_H */ diff --git a/src/imageplugins/emboss/imageplugin_emboss.cpp b/src/imageplugins/emboss/imageplugin_emboss.cpp new file mode 100644 index 00000000..6bd616d6 --- /dev/null +++ b/src/imageplugins/emboss/imageplugin_emboss.cpp @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "embosstool.h" +#include "imageplugin_emboss.h" +#include "imageplugin_emboss.moc" + +using namespace DigikamEmbossImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_emboss, + KGenericFactory("digikamimageplugin_emboss")); + +ImagePlugin_Emboss::ImagePlugin_Emboss(TQObject *parent, const char*, + const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Emboss") +{ + m_embossAction = new TDEAction(i18n("Emboss..."), "embosstool", 0, + this, TQ_SLOT(slotEmboss()), + actionCollection(), "imageplugin_emboss"); + + setXMLFile( "digikamimageplugin_emboss_ui.rc" ); + + DDebug() << "ImagePlugin_Emboss plugin loaded" << endl; +} + +ImagePlugin_Emboss::~ImagePlugin_Emboss() +{ +} + +void ImagePlugin_Emboss::setEnabledActions(bool enable) +{ + m_embossAction->setEnabled(enable); +} + +void ImagePlugin_Emboss::slotEmboss() +{ + EmbossTool *tool = new EmbossTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/emboss/imageplugin_emboss.h b/src/imageplugins/emboss/imageplugin_emboss.h new file mode 100644 index 00000000..1ccac064 --- /dev/null +++ b/src/imageplugins/emboss/imageplugin_emboss.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin to emboss + * an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_EMBOSS_H +#define IMAGEPLUGIN_EMBOSS_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Emboss : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Emboss(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Emboss(); + + void setEnabledActions(bool enable); + +private slots: + + void slotEmboss(); + +private: + + TDEAction *m_embossAction; +}; + +#endif /* IMAGEPLUGIN_EMBOSS_H */ diff --git a/src/imageplugins/filmgrain/Makefile.am b/src/imageplugins/filmgrain/Makefile.am new file mode 100644 index 00000000..c23c9d8e --- /dev/null +++ b/src/imageplugins/filmgrain/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_filmgrain_la_SOURCES = imageplugin_filmgrain.cpp \ + filmgraintool.cpp filmgrain.cpp + +digikamimageplugin_filmgrain_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_filmgrain_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_filmgrain.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_filmgrain.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_filmgrain_ui.rc + diff --git a/src/imageplugins/filmgrain/digikamimageplugin_filmgrain.desktop b/src/imageplugins/filmgrain/digikamimageplugin_filmgrain.desktop new file mode 100644 index 00000000..5ad6582b --- /dev/null +++ b/src/imageplugins/filmgrain/digikamimageplugin_filmgrain.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_FilmGrain +Name[bg]=ПриÑтавка за Ñнимки - Филмово зърно +Name[da]=Billedplugin_Filmkorn +Name[el]=ΠÏόσθετοΕικόνας_ΚόκκοςΦιλμ +Name[fi]=Filmirakeet +Name[hr]=Zrnatost +Name[it]=PluginImmagini_GranaPellicola +Name[nl]=Afbeeldingsplugin_Filmkorrel +Name[sr]=ЗрнаÑÑ‚ филм +Name[sr@Latn]=Zrnast film +Name[sv]=Insticksprogram för filmkorn +Name[tr]=ResimEklentisi_FilmTanecikleri +Name[xx]=xxImagePlugin_FilmGrainxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Film grain image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наподобÑване на филмово зърно +Comment[ca]=Connector pel digiKam d'efecte d'imatge de gra de pel·lícula +Comment[cs]=Efektový modul pro tvorbu filmového zrna pro digiKam +Comment[da]=Plugin til filmkorn-effekt pÃ¥ billeder i Digikam +Comment[de]=digiKam-Modul zum Erzeugen eines Effektes von körnigem Film +Comment[el]=ΠÏόσθετο εφέ κόκκου φιλμ για το digiKam +Comment[es]=Plugin para digiKam de efectos de imagen tipo grano fino de película +Comment[et]=DigiKami teralisuse pildiefektiplugin +Comment[fa]=وصلۀ جلوۀ تصویر خرده Ùیلم برای digiKam +Comment[fi]=Filmikuvien rakeisuuden jäljittelijä +Comment[gl]=Un plugin de digiKam para o efeito de grao de filme +Comment[hr]=digiKam dodatak za efekt zrnatosti filma +Comment[is]=Ãforrit fyrir digiKam sem líkir eftir filmukornum +Comment[it]=Plugin per l'effetto di grana della pellicola delle immagini per digiKam +Comment[ja]=digiKam フィルム粒å­åŠ¹æžœãƒ—ラグイン +Comment[nds]=digiKam-Moduul för Effekten vun körnig Film +Comment[nl]=Digikam-plugin voor filmkorrel +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਫਿਲਮ ਗਰੇਨ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam oferujÄ…ca efekty znieksztaÅ‚ceÅ„ +Comment[pt]=Um 'plugin' do digiKam para o efeito de grão de filme +Comment[pt_BR]=Um 'plugin' do digiKam para o efeito de grão de filme +Comment[ru]=Модуль зерниÑтоÑти Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ digiKam +Comment[sk]=digiKam obrázkový plugin pre efekt filmového zrna +Comment[sr]=digiKam-ов прикључак за ефекат зрнаÑтоÑти филма +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekat zrnastosti filma +Comment[sv]=Digikam insticksprogram för filmkornsbildeffekt +Comment[tr]=digiKam için film tanecikleri etkisi eklentisi +Comment[uk]=Втулок digiKam Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ фільму на зображеннÑÑ… +Comment[vi]=Phần bổ sung hiệu ứng chạm mịn mặt màng ảnh cho digiKam +Comment[xx]=xxFilm grain image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_filmgrain +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/filmgrain/digikamimageplugin_filmgrain_ui.rc b/src/imageplugins/filmgrain/digikamimageplugin_filmgrain_ui.rc new file mode 100644 index 00000000..770c2dbd --- /dev/null +++ b/src/imageplugins/filmgrain/digikamimageplugin_filmgrain_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/filmgrain/filmgrain.cpp b/src/imageplugins/filmgrain/filmgrain.cpp new file mode 100644 index 00000000..57616eb9 --- /dev/null +++ b/src/imageplugins/filmgrain/filmgrain.cpp @@ -0,0 +1,202 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : FilmGrain threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2005-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimggaussianblur.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "dimgimagefilters.h" +#include "filmgrain.h" + +namespace DigikamFilmGrainImagesPlugin +{ + +FilmGrain::FilmGrain(Digikam::DImg *orgImage, TQObject *parent, int sensibility) + : Digikam::DImgThreadedFilter(orgImage, parent, "FilmGrain") +{ + m_sensibility = sensibility; + initFilter(); +} + +void FilmGrain::filterImage(void) +{ + filmgrainImage(&m_orgImage, m_sensibility); +} + +// This method is based on the Simulate Film grain tutorial from GimpGuru.org web site +// available at this url : http://www.gimpguru.org/Tutorials/FilmGrain + +void FilmGrain::filmgrainImage(Digikam::DImg *orgImage, int Sensibility) +{ + // Sensibility: 800..6400 + + if (Sensibility <= 0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + int bytesDepth = orgImage->bytesDepth(); + bool sixteenBit = orgImage->sixteenBit(); + uchar* data = orgImage->bits(); + + Digikam::DImg grain(Width, Height, sixteenBit); // Grain blured without curves adjustment. + Digikam::DImg mask(Width, Height, sixteenBit); // Grain mask with curves adjustment. + uchar* pGrainBits = grain.bits(); + uchar* pMaskBits = mask.bits(); + uchar* pOutBits = m_destImage.bits(); // Destination image with merged grain mask and original. + + int Noise, Shade, nRand, component, progress; + uchar *ptr; + Digikam::DColor blendData, grainData, maskData, outData; + + if (sixteenBit) + Noise = (Sensibility / 10 + 1) * 256 - 1; + else + Noise = Sensibility / 10; + + // This value controls the shading pixel effect between original image and grain mask. + if (sixteenBit) + Shade = (52 + 1) * 256 - 1; + else + Shade = 52; + + TQDateTime dt = TQDateTime::currentDateTime(); + TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); + uint seed = (uint) dt.secsTo(Y2000); + + // Make gray grain mask. + + grainData.setSixteenBit(sixteenBit); + + for (int x = 0; !m_cancel && x < Width; x++) + { + for (int y = 0; !m_cancel && y < Height; y++) + { + ptr = pGrainBits + x*bytesDepth + (y*Width*bytesDepth); + + nRand = (rand_r(&seed) % Noise) - (Noise / 2); + if (sixteenBit) + component = CLAMP(32768 + nRand, 0, 65535); + else + component = CLAMP(128 + nRand, 0, 255); + + grainData.setRed (component); + grainData.setGreen(component); + grainData.setBlue (component); + grainData.setAlpha(0); + + grainData.setPixel(ptr); + } + + // Update progress bar in dialog. + progress = (int) (((double)x * 25.0) / Width); + + if (progress%5 == 0) + postProgress( progress ); + } + + // Smooth grain mask using gaussian blur with radius 1. + Digikam::DImgGaussianBlur(this, grain, grain, 25, 30, 1); + + // Normally, film grain tends to be most noticeable in the midtones, and much less + // so in the shadows and highlights. Adjust histogram curve to adjust grain like this. + + Digikam::ImageCurves *grainCurves = new Digikam::ImageCurves(sixteenBit); + + // We modify only global luminosity of the grain. + if (sixteenBit) + { + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 8, TQPoint(32768, 32768)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(65535, 0)); + } + else + { + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 8, TQPoint(128, 128)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(255, 0)); + } + + // Calculate curves and lut to apply on grain. + grainCurves->curvesCalculateCurve(Digikam::ImageHistogram::ValueChannel); + grainCurves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + grainCurves->curvesLutProcess(pGrainBits, pMaskBits, Width, Height); + + grain.reset(); + delete grainCurves; + + // Update progress bar in dialog. + postProgress( 40 ); + + // Merge src image with grain using shade coefficient. + + int alpha; + // get composer for default blending + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffNone); + + for (int x = 0; !m_cancel && x < Width; x++) + { + for (int y = 0; !m_cancel && y < Height; y++) + { + int offset = x*bytesDepth + (y*Width*bytesDepth); + + // read color from orig image + blendData.setColor(data + offset, sixteenBit); + // read color from mask + maskData.setColor(pMaskBits + offset, sixteenBit); + // set shade as alpha value - it will be used as source alpha when blending + maskData.setAlpha(Shade); + + // compose, write result to blendData. + // Preserve alpha, do not blend it (taken from old algorithm - correct?) + alpha = blendData.alpha(); + composer->compose(blendData, maskData); + blendData.setAlpha(alpha); + + // write to destination + blendData.setPixel(pOutBits + offset); + } + + // Update progress bar in dialog. + progress = (int) (50.0 + ((double)x * 50.0) / Width); + + if (progress%5 == 0) + postProgress( progress ); + } + + delete composer; +} + +} // NameSpace DigikamFilmGrainImagesPlugin diff --git a/src/imageplugins/filmgrain/filmgrain.h b/src/imageplugins/filmgrain/filmgrain.h new file mode 100644 index 00000000..42bcf03c --- /dev/null +++ b/src/imageplugins/filmgrain/filmgrain.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : FilmGrain threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2005-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef FILMGRAIN_H +#define FILMGRAIN_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamFilmGrainImagesPlugin +{ + +class FilmGrain : public Digikam::DImgThreadedFilter +{ + +public: + + FilmGrain(Digikam::DImg *orgImage, TQObject *parent=0, int sensibility=12); + + ~FilmGrain(){}; + +private: + + virtual void filterImage(void); + + void filmgrainImage(Digikam::DImg *orgImage, int Sensibility); + +private: + + int m_sensibility; +}; + +} // NameSpace DigikamFilmGrainImagesPlugin + +#endif /* FILMGRAIN_H */ diff --git a/src/imageplugins/filmgrain/filmgraintool.cpp b/src/imageplugins/filmgrain/filmgraintool.cpp new file mode 100644 index 00000000..5799d814 --- /dev/null +++ b/src/imageplugins/filmgrain/filmgraintool.cpp @@ -0,0 +1,195 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "filmgrain.h" +#include "filmgraintool.h" +#include "filmgraintool.moc" + +using namespace Digikam; + +namespace DigikamFilmGrainImagesPlugin +{ + +FilmGrainTool::FilmGrainTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("filmgrain"); + setToolName(i18n("Film Grain")); + setToolIcon(SmallIcon("filmgrain")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 2, 1); + TQLabel *label1 = new TQLabel(i18n("Sensitivity (ISO):"), m_gboxSettings->plainPage()); + + m_sensibilitySlider = new TQSlider(2, 30, 1, 12, TQt::Horizontal, m_gboxSettings->plainPage()); + m_sensibilitySlider->setTracking(false); + m_sensibilitySlider->setTickInterval(1); + m_sensibilitySlider->setTickmarks(TQSlider::Below); + + m_sensibilityLCDValue = new TQLCDNumber(4, m_gboxSettings->plainPage()); + m_sensibilityLCDValue->setSegmentStyle(TQLCDNumber::Flat); + m_sensibilityLCDValue->display(TQString::number(2400)); + TQString whatsThis = i18n("

    Set here the film ISO-sensitivity to " + "use for simulating the film graininess."); + + TQWhatsThis::add(m_sensibilityLCDValue, whatsThis); + TQWhatsThis::add(m_sensibilitySlider, whatsThis); + + grid->addMultiCellWidget(label1, 0, 0, 0, 1); + grid->addMultiCellWidget(m_sensibilitySlider, 1, 1, 0, 0); + grid->addMultiCellWidget(m_sensibilityLCDValue, 1, 1, 1, 1); + grid->setRowStretch(2, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "filmgrain Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer()) ); + + // this connection is necessary to change the LCD display when + // the value is changed by single clicking on the slider + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_sensibilitySlider, TQ_SIGNAL(sliderMoved(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); +} + +FilmGrainTool::~FilmGrainTool() +{ +} + +void FilmGrainTool::renderingFinished() +{ + m_sensibilitySlider->setEnabled(true); +} + +void FilmGrainTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("filmgrain Tool"); + m_sensibilitySlider->blockSignals(true); + m_sensibilitySlider->setValue(config->readNumEntry("SensitivityAjustment", 12)); + m_sensibilitySlider->blockSignals(false); + slotSliderMoved(m_sensibilitySlider->value()); +} + +void FilmGrainTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("filmgrain Tool"); + config->writeEntry("SensitivityAjustment", m_sensibilitySlider->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void FilmGrainTool::slotResetSettings() +{ + m_sensibilitySlider->blockSignals(true); + m_sensibilitySlider->setValue(12); + m_sensibilitySlider->blockSignals(false); +} + +void FilmGrainTool::slotSliderMoved(int v) +{ + m_sensibilityLCDValue->display( TQString::number(400+200*v) ); +} + +void FilmGrainTool::prepareEffect() +{ + m_sensibilitySlider->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + int s = 400 + 200 * m_sensibilitySlider->value(); + + setFilter(dynamic_cast(new FilmGrain(&image, this, s))); +} + +void FilmGrainTool::prepareFinal() +{ + m_sensibilitySlider->setEnabled(false); + + int s = 400 + 200 * m_sensibilitySlider->value(); + + ImageIface iface(0, 0); + + setFilter(dynamic_cast(new FilmGrain(iface.getOriginalImg(), this, s))); +} + +void FilmGrainTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void FilmGrainTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Film Grain"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamFilmGrainImagesPlugin diff --git a/src/imageplugins/filmgrain/filmgraintool.h b/src/imageplugins/filmgrain/filmgraintool.h new file mode 100644 index 00000000..1a179b53 --- /dev/null +++ b/src/imageplugins/filmgrain/filmgraintool.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef FILMGRAINTOOL_H +#define FILMGRAINTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQSlider; +class TQLCDNumber; + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamFilmGrainImagesPlugin +{ + +class FilmGrainTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + FilmGrainTool(TQObject* parent); + ~FilmGrainTool(); + +private slots: + + void slotSliderMoved(int); + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQSlider *m_sensibilitySlider; + + TQLCDNumber *m_sensibilityLCDValue; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamFilmGrainImagesPlugin + +#endif /* FILMGRAINTOOL_H */ diff --git a/src/imageplugins/filmgrain/imageeffect_filmgrain.cpp b/src/imageplugins/filmgrain/imageeffect_filmgrain.cpp new file mode 100644 index 00000000..e008095a --- /dev/null +++ b/src/imageplugins/filmgrain/imageeffect_filmgrain.cpp @@ -0,0 +1,195 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "filmgrain.h" +#include "imageeffect_filmgrain.h" +#include "imageeffect_filmgrain.moc" + +namespace DigikamFilmGrainImagesPlugin +{ + +ImageEffect_FilmGrain::ImageEffect_FilmGrain(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Add Film Grain to Photograph"), + "filmgrain", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Film Grain"), + digikam_version, + I18N_NOOP("A digiKam image plugin to apply a film grain " + "effect to an image."), + TDEAboutData::License_GPL, + "(c) 2004-2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 1, 1, 0, spacingHint()); + TQLabel *label1 = new TQLabel(i18n("Sensitivity (ISO):"), gboxSettings); + + m_sensibilitySlider = new TQSlider(2, 30, 1, 12, TQt::Horizontal, gboxSettings); + m_sensibilitySlider->setTracking ( false ); + m_sensibilitySlider->setTickInterval(1); + m_sensibilitySlider->setTickmarks(TQSlider::Below); + + m_sensibilityLCDValue = new TQLCDNumber (4, gboxSettings); + m_sensibilityLCDValue->setSegmentStyle ( TQLCDNumber::Flat ); + m_sensibilityLCDValue->display( TQString::number(2400) ); + whatsThis = i18n("

    Set here the film ISO-sensitivity to use for simulating the film graininess."); + + TQWhatsThis::add( m_sensibilityLCDValue, whatsThis); + TQWhatsThis::add( m_sensibilitySlider, whatsThis); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_sensibilitySlider, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(m_sensibilityLCDValue, 1, 1, 1, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer()) ); + + // this connection is necessary to change the LCD display when + // the value is changed by single clicking on the slider + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_sensibilitySlider, TQ_SIGNAL(sliderMoved(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); +} + +ImageEffect_FilmGrain::~ImageEffect_FilmGrain() +{ +} + +void ImageEffect_FilmGrain::renderingFinished() +{ + m_sensibilitySlider->setEnabled(true); +} + +void ImageEffect_FilmGrain::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("filmgrain Tool Dialog"); + m_sensibilitySlider->blockSignals(true); + m_sensibilitySlider->setValue(config->readNumEntry("SensitivityAjustment", 12)); + m_sensibilitySlider->blockSignals(false); + slotSliderMoved(m_sensibilitySlider->value()); +} + +void ImageEffect_FilmGrain::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("filmgrain Tool Dialog"); + config->writeEntry("SensitivityAjustment", m_sensibilitySlider->value()); + config->sync(); +} + +void ImageEffect_FilmGrain::resetValues() +{ + m_sensibilitySlider->blockSignals(true); + m_sensibilitySlider->setValue(12); + m_sensibilitySlider->blockSignals(false); +} + +void ImageEffect_FilmGrain::slotSliderMoved(int v) +{ + m_sensibilityLCDValue->display( TQString::number(400+200*v) ); +} + +void ImageEffect_FilmGrain::prepareEffect() +{ + m_sensibilitySlider->setEnabled(false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + int s = 400 + 200 * m_sensibilitySlider->value(); + + m_threadedFilter = dynamic_cast(new FilmGrain(&image, this, s)); +} + +void ImageEffect_FilmGrain::prepareFinal() +{ + m_sensibilitySlider->setEnabled(false); + + int s = 400 + 200 * m_sensibilitySlider->value(); + + Digikam::ImageIface iface(0, 0); + + m_threadedFilter = dynamic_cast(new FilmGrain(iface.getOriginalImg(), this, s)); +} + +void ImageEffect_FilmGrain::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_FilmGrain::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Film Grain"), m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamFilmGrainImagesPlugin + diff --git a/src/imageplugins/filmgrain/imageeffect_filmgrain.h b/src/imageplugins/filmgrain/imageeffect_filmgrain.h new file mode 100644 index 00000000..ba66e0a2 --- /dev/null +++ b/src/imageplugins/filmgrain/imageeffect_filmgrain.h @@ -0,0 +1,74 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-26 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef IMAGEEFFECT_FILMGRAIN_H +#define IMAGEEFFECT_FILMGRAIN_H + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class TQSlider; +class TQLCDNumber; + +namespace DigikamFilmGrainImagesPlugin +{ + +class ImageEffect_FilmGrain : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_FilmGrain(TQWidget* parent); + ~ImageEffect_FilmGrain(); + +private slots: + + void slotSliderMoved(int); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQSlider *m_sensibilitySlider; + + TQLCDNumber *m_sensibilityLCDValue; +}; + +} // NameSpace DigikamFilmGrainImagesPlugin + +#endif /* IMAGEEFFECT_FILMGRAIN_H */ diff --git a/src/imageplugins/filmgrain/imageplugin_filmgrain.cpp b/src/imageplugins/filmgrain/imageplugin_filmgrain.cpp new file mode 100644 index 00000000..efa2c35d --- /dev/null +++ b/src/imageplugins/filmgrain/imageplugin_filmgrain.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-01 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "filmgraintool.h" +#include "imageplugin_filmgrain.h" +#include "imageplugin_filmgrain.moc" + +using namespace DigikamFilmGrainImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_filmgrain, + KGenericFactory("digikamimageplugin_filmgrain")); + +ImagePlugin_FilmGrain::ImagePlugin_FilmGrain(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_FilmGrain") +{ + m_filmgrainAction = new TDEAction(i18n("Add Film Grain..."), "filmgrain", 0, + this, TQ_SLOT(slotFilmGrain()), + actionCollection(), "imageplugin_filmgrain"); + + setXMLFile( "digikamimageplugin_filmgrain_ui.rc" ); + + DDebug() << "ImagePlugin_FilmGrain plugin loaded" << endl; +} + +ImagePlugin_FilmGrain::~ImagePlugin_FilmGrain() +{ +} + +void ImagePlugin_FilmGrain::setEnabledActions(bool enable) +{ + m_filmgrainAction->setEnabled(enable); +} + +void ImagePlugin_FilmGrain::slotFilmGrain() +{ + FilmGrainTool *tool = new FilmGrainTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/filmgrain/imageplugin_filmgrain.h b/src/imageplugins/filmgrain/imageplugin_filmgrain.h new file mode 100644 index 00000000..495da122 --- /dev/null +++ b/src/imageplugins/filmgrain/imageplugin_filmgrain.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-10-01 + * Description : a digiKam image editor plugin for add film + * grain on an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_FILMGRAIN_H +#define IMAGEPLUGIN_FILMGRAIN_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_FilmGrain : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_FilmGrain(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_FilmGrain(); + + void setEnabledActions(bool enable); + +private slots: + + void slotFilmGrain(); + +private: + + TDEAction *m_filmgrainAction; +}; + +#endif /* IMAGEPLUGIN_FILMGRAIN_H */ diff --git a/src/imageplugins/freerotation/Makefile.am b/src/imageplugins/freerotation/Makefile.am new file mode 100644 index 00000000..e2b67283 --- /dev/null +++ b/src/imageplugins/freerotation/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_freerotation_la_SOURCES = imageplugin_freerotation.cpp \ + freerotationtool.cpp freerotation.cpp + +digikamimageplugin_freerotation_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_freerotation_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_freerotation.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_freerotation.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_freerotation_ui.rc + diff --git a/src/imageplugins/freerotation/digikamimageplugin_freerotation.desktop b/src/imageplugins/freerotation/digikamimageplugin_freerotation.desktop new file mode 100644 index 00000000..47659a0d --- /dev/null +++ b/src/imageplugins/freerotation/digikamimageplugin_freerotation.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=ImagePlugin_FreeRotation +Name[bg]=ПриÑтавка за Ñнимки - Свободно завъртане +Name[da]=Billedplugin_Fri rotation +Name[el]=ΠÏόσθετοΕικόνας_ΕλεÏθεÏηΠεÏιστÏοφή +Name[fi]=Kierto +Name[hr]=Slobodno obrtanje +Name[it]=PluginImmagini_RotazioneLibera +Name[nl]=Afbeeldingsplugin_VrijeRotatie +Name[sr]=Слободно окретање +Name[sr@Latn]=Slobodno okretanje +Name[sv]=Insticksprogram för bildrotation +Name[tr]=ResimEklentisi_SerbestDöndürme +Name[xx]=xxImagePlugin_FreeRotationxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Free rotation plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за Ñвободно завъртане на Ñнимки +Comment[ca]=Connector pel digiKam de gir lliure +Comment[da]=Plugin til fri rotation af billeder til Digikam +Comment[de]=digiKam-Modul zur freien Drehung von Bildern +Comment[el]=ΠÏόσθετο ελεÏθεÏης πεÏιστÏοφής για το digiKam +Comment[es]=Plugin para digiKam de rotación +Comment[et]=DigiKami pildi vaba pööramise plugin +Comment[fa]=وصلۀ چرخش آزاد برای digiKam +Comment[fi]=Kiertää kuvaa vapaasti määritettävien asteiden verran +Comment[gl]=Un plugin de digiKam para a rotazón libre da imaxe +Comment[hr]=digiKam dodatak za slobodno obrtanje +Comment[is]=Ãforrit fyrir digiKam sem leyfir frjálsan snúning mynda +Comment[it]=Plugin per la rotazione libera per digiKam +Comment[ja]=digiKam ç”»åƒè‡ªç”±å›žè»¢ãƒ—ラグイン +Comment[nds]=digiKam-Moduul för't fre'e Dreihen +Comment[nl]=Digikam-plugin voor vrije rotatie +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਮà©à¨•à¨¤ ਘà©à©°à¨®à¨¾à¨‰à¨£ ਵਾਲੀ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca swobodny obrót obrazu +Comment[pt]=Um 'plugin' do digiKam para a rotação livre da imagem +Comment[pt_BR]=Plugin de Rotação Livre +Comment[ru]=Модуль Ñвободного Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin voľného otáÄania +Comment[sr]=digiKam-ов прикључак за Ñлободно окретање +Comment[sr@Latn]=digiKam-ov prikljuÄak za slobodno okretanje +Comment[sv]=Digikam insticksprogram för bildrotation +Comment[tr]=digiKam için serbest döndürme eklentisi +Comment[uk]=Втулок вільного Ð¾Ð±ÐµÑ€Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung xoay tá»± do cho digiKam +Comment[xx]=xxFree rotation plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_freerotation +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/freerotation/digikamimageplugin_freerotation_ui.rc b/src/imageplugins/freerotation/digikamimageplugin_freerotation_ui.rc new file mode 100644 index 00000000..fbbf9605 --- /dev/null +++ b/src/imageplugins/freerotation/digikamimageplugin_freerotation_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Tra&nsform + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/freerotation/freerotation.cpp b/src/imageplugins/freerotation/freerotation.cpp new file mode 100644 index 00000000..3f8803d5 --- /dev/null +++ b/src/imageplugins/freerotation/freerotation.cpp @@ -0,0 +1,263 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Free rotation threaded image filter. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Degrees to radian convertion coeff (PI/180). To optimize computation. +#define DEG2RAD 0.017453292519943 + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "freerotation.h" + +namespace DigikamFreeRotationImagesPlugin +{ + +FreeRotation::FreeRotation(Digikam::DImg *orgImage, TQObject *parent, double angle, bool antialiasing, + int autoCrop, TQColor backgroundColor, int orgW, int orgH) + : Digikam::DImgThreadedFilter(orgImage, parent, "FreeRotation") +{ + m_angle = angle; + m_orgW = orgW; + m_orgH = orgH; + m_antiAlias = antialiasing; + m_autoCrop = autoCrop; + m_backgroundColor = backgroundColor; + + initFilter(); +} + +void FreeRotation::filterImage(void) +{ + int progress; + int w, h, nw, nh, j, i = 0; + int nNewHeight, nNewWidth; + int nhdx, nhdy, nhsx, nhsy; + double lfSin, lfCos, lfx, lfy; + + int nWidth = m_orgImage.width(); + int nHeight = m_orgImage.height(); + + uchar *pBits = m_orgImage.bits(); + unsigned short *pBits16 = (unsigned short*)m_orgImage.bits(); + + // first of all, we need to calcule the sin and cos of the given angle + + lfSin = sin (m_angle * -DEG2RAD); + lfCos = cos (m_angle * -DEG2RAD); + + // now, we have to calc the new size for the destination image + + if ((lfSin * lfCos) < 0) + { + nNewWidth = ROUND (fabs (nWidth * lfCos - nHeight * lfSin)); + nNewHeight = ROUND (fabs (nWidth * lfSin - nHeight * lfCos)); + } + else + { + nNewWidth = ROUND (fabs (nWidth * lfCos + nHeight * lfSin)); + nNewHeight = ROUND (fabs (nWidth * lfSin + nHeight * lfCos)); + } + + // getting the destination's center position + + nhdx = nNewWidth / 2; + nhdy = nNewHeight / 2; + + // getting the source's center position + + nhsx = nWidth / 2; + nhsy = nHeight / 2; + + // now, we have to alloc a new image + + bool sixteenBit = m_orgImage.sixteenBit(); + + m_destImage = Digikam::DImg(nNewWidth, nNewHeight, sixteenBit, m_orgImage.hasAlpha()); + m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) ); + + uchar *pResBits = m_destImage.bits(); + unsigned short *pResBits16 = (unsigned short *)m_destImage.bits(); + + Digikam::DImgImageFilters filters; + + // main loop + + for (h = 0; !m_cancel && (h < nNewHeight); h++) + { + nh = h - nhdy; + + for (w = 0; !m_cancel && (w < nNewWidth); w++) + { + nw = w - nhdx; + + i = setPosition (nNewWidth, w, h); + + lfx = (double)nw * lfCos - (double)nh * lfSin + nhsx; + lfy = (double)nw * lfSin + (double)nh * lfCos + nhsy; + + if (isInside (nWidth, nHeight, (int)lfx, (int)lfy)) + { + if (m_antiAlias) + { + if (!sixteenBit) + filters.pixelAntiAliasing(pBits, nWidth, nHeight, lfx, lfy, + &pResBits[i+3], &pResBits[i+2], + &pResBits[i+1], &pResBits[i]); + else + filters.pixelAntiAliasing16(pBits16, nWidth, nHeight, lfx, lfy, + &pResBits16[i+3], &pResBits16[i+2], + &pResBits16[i+1], &pResBits16[i]); + } + else + { + j = setPosition (nWidth, (int)lfx, (int)lfy); + + for (int p = 0 ; p < 4 ; p++) + { + if (!sixteenBit) + pResBits[i] = pBits[j]; + else + pResBits16[i] = pBits16[j]; + + i++; + j++; + } + } + } + } + + // Update the progress bar in dialog. + progress = (int)(((double)h * 100.0) / nNewHeight); + if (progress%5 == 0) + postProgress( progress ); + } + + // Compute the rotated destination image size using original image dimensions. + int W, H; + double absAngle = fabs(m_angle); + + if (absAngle < 90.0) + { + W = (int)(m_orgW * cos(absAngle * DEG2RAD) + m_orgH * sin(absAngle * DEG2RAD)); + H = (int)(m_orgH * cos(absAngle * DEG2RAD) + m_orgW * sin(absAngle * DEG2RAD)); + } + else + { + H = (int)(m_orgW * cos((absAngle-90.0) * DEG2RAD) + m_orgH * sin((absAngle-90.0) * DEG2RAD)); + W = (int)(m_orgH * cos((absAngle-90.0) * DEG2RAD) + m_orgW * sin((absAngle-90.0) * DEG2RAD)); + } + + // Auto-cropping destination image without black holes around. + TQRect autoCrop; + + switch(m_autoCrop) + { + case WidestArea: + { + // 'Widest Area' method (by Renchi Raju). + + autoCrop.setX( (int)(nHeight * sin(absAngle * DEG2RAD)) ); + autoCrop.setY( (int)(nWidth * sin(absAngle * DEG2RAD)) ); + autoCrop.setWidth( (int)(nNewWidth - 2*nHeight * sin(absAngle * DEG2RAD)) ); + autoCrop.setHeight( (int)(nNewHeight - 2*nWidth * sin(absAngle * DEG2RAD)) ); + + if (!autoCrop.isValid()) + { + m_destImage = Digikam::DImg(m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) ); + m_newSize = TQSize(); + } + else + { + m_destImage = m_destImage.copy(autoCrop); + m_newSize.setWidth( (int)(W - 2*m_orgH * sin(absAngle * DEG2RAD)) ); + m_newSize.setHeight( (int)(H - 2*m_orgW * sin(absAngle * DEG2RAD)) ); + } + break; + } + + case LargestArea: + { + // 'Largest Area' method (by Gerhard Kulzer). + + float gamma = atan((float)nHeight / (float)nWidth); + + if (absAngle < 90.0) + { + autoCrop.setWidth( (int)((float)nHeight / cos(absAngle*DEG2RAD) / + ( tan(gamma) + tan(absAngle*DEG2RAD) )) ); + autoCrop.setHeight( (int)((float)autoCrop.width() * tan(gamma)) ); + } + else + { + autoCrop.setHeight( (int)((float)nHeight / cos((absAngle-90.0)*DEG2RAD) / + ( tan(gamma) + tan((absAngle-90.0)*DEG2RAD) )) ); + autoCrop.setWidth( (int)((float)autoCrop.height() * tan(gamma)) ); + } + + autoCrop.moveCenter( TQPoint(nNewWidth/2, nNewHeight/2)); + + if (!autoCrop.isValid()) + { + m_destImage = Digikam::DImg(m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) ); + m_newSize = TQSize(); + } + else + { + m_destImage = m_destImage.copy(autoCrop); + gamma = atan((float)m_orgH / (float)m_orgW); + + if (absAngle < 90.0) + { + m_newSize.setWidth( (int)((float)m_orgH / cos(absAngle*DEG2RAD) / + ( tan(gamma) + tan(absAngle*DEG2RAD) )) ); + m_newSize.setHeight( (int)((float)m_newSize.width() * tan(gamma)) ); + } + else + { + m_newSize.setHeight( (int)((float)m_orgH / cos((absAngle-90.0)*DEG2RAD) / + ( tan(gamma) + tan((absAngle-90.0)*DEG2RAD) )) ); + m_newSize.setWidth( (int)((float)m_newSize.height() * tan(gamma)) ); + } + } + break; + } + default: // No auto croping. + { + m_newSize.setWidth( W ); + m_newSize.setHeight( H ); + break; + } + } +} + +} // NameSpace DigikamFreeRotationImagesPlugin diff --git a/src/imageplugins/freerotation/freerotation.h b/src/imageplugins/freerotation/freerotation.h new file mode 100644 index 00000000..7dc4cc8c --- /dev/null +++ b/src/imageplugins/freerotation/freerotation.h @@ -0,0 +1,94 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Free rotation threaded image filter. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef FREE_ROTATION_H +#define FREE_ROTATION_H + +// TQt includes. + +#include +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamFreeRotationImagesPlugin +{ + +class FreeRotation : public Digikam::DImgThreadedFilter +{ + +public: + + FreeRotation(Digikam::DImg *orgImage, TQObject *parent=0, double angle=0.0, + bool antialiasing=true, int autoCrop=NoAutoCrop, TQColor backgroundColor=TQt::black, + int orgW=0, int orgH=0); + + ~FreeRotation(){}; + + TQSize getNewSize(void){ return m_newSize; }; + +public: + + enum AutoCropTypes + { + NoAutoCrop=0, + WidestArea, + LargestArea + }; + +private: + + virtual void filterImage(void); + + inline int setPosition (int Width, int X, int Y) + { + return (Y *Width*4 + 4*X); + }; + + inline bool isInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; + +private: + + bool m_antiAlias; + + int m_autoCrop; + int m_orgW; + int m_orgH; + + double m_angle; + + TQSize m_newSize; + + TQColor m_backgroundColor; +}; + +} // NameSpace DigikamFreeRotationImagesPlugin + +#endif /* FREE_ROTATION_H */ diff --git a/src/imageplugins/freerotation/freerotationtool.cpp b/src/imageplugins/freerotation/freerotationtool.cpp new file mode 100644 index 00000000..b25f2196 --- /dev/null +++ b/src/imageplugins/freerotation/freerotationtool.cpp @@ -0,0 +1,318 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "freerotation.h" +#include "freerotationtool.h" +#include "freerotationtool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamFreeRotationImagesPlugin +{ + +FreeRotationTool::FreeRotationTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("freerotation"); + setToolName(i18n("Free Rotation")); + setToolIcon(SmallIcon("freerotation")); + + m_previewWidget = new ImageWidget("freerotation Tool", 0, + i18n("

    This is the free rotation operation preview. " + "If you move the mouse cursor on this preview, " + "a vertical and horizontal dashed line will be drawn " + "to guide you in adjusting the free rotation correction. " + "Release the left mouse button to freeze the dashed " + "line's position."), + false, ImageGuideWidget::HVGuideMode); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel, + EditorToolSettings::ColorGuide); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 9, 2); + + TQLabel *label1 = new TQLabel(i18n("New width:"), m_gboxSettings->plainPage()); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), m_gboxSettings->plainPage()); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + KSeparator *line = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + TQLabel *label3 = new TQLabel(i18n("Main angle:"), m_gboxSettings->plainPage()); + m_angleInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_angleInput->setRange(-180, 180, 1); + m_angleInput->setDefaultValue(0); + TQWhatsThis::add( m_angleInput, i18n("

    An angle in degrees by which to rotate the image. " + "A positive angle rotates the image clockwise; " + "a negative angle rotates it counter-clockwise.")); + + TQLabel *label4 = new TQLabel(i18n("Fine angle:"), m_gboxSettings->plainPage()); + m_fineAngleInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_fineAngleInput->setRange(-5.0, 5.0, 0.01); + m_fineAngleInput->setDefaultValue(0); + TQWhatsThis::add( m_fineAngleInput, i18n("

    This value in degrees will be added to main angle value " + "to set fine target angle.")); + + m_antialiasInput = new TQCheckBox(i18n("Anti-Aliasing"), m_gboxSettings->plainPage()); + TQWhatsThis::add( m_antialiasInput, i18n("

    Enable this option to apply the anti-aliasing filter " + "to the rotated image. " + "In order to smooth the target image, it will be blurred a little.")); + + TQLabel *label5 = new TQLabel(i18n("Auto-crop:"), m_gboxSettings->plainPage()); + m_autoCropCB = new RComboBox(m_gboxSettings->plainPage()); + m_autoCropCB->insertItem( i18n("None") ); + m_autoCropCB->insertItem( i18n("Widest Area") ); + m_autoCropCB->insertItem( i18n("Largest Area") ); + m_autoCropCB->setDefaultItem(FreeRotation::NoAutoCrop); + TQWhatsThis::add( m_autoCropCB, i18n("

    Select the method to process image auto-cropping " + "to remove black frames around a rotated image.")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + grid->addMultiCellWidget(label2, 1, 1, 0, 0); + grid->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + grid->addMultiCellWidget(line, 2, 2, 0, 2); + grid->addMultiCellWidget(label3, 3, 3, 0, 2); + grid->addMultiCellWidget(m_angleInput, 4, 4, 0, 2); + grid->addMultiCellWidget(label4, 5, 5, 0, 2); + grid->addMultiCellWidget(m_fineAngleInput, 6, 6, 0, 2); + grid->addMultiCellWidget(m_antialiasInput, 7, 7, 0, 2); + grid->addMultiCellWidget(label5, 8, 8, 0, 0); + grid->addMultiCellWidget(m_autoCropCB, 8, 8, 1, 2); + grid->setRowStretch(9, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_angleInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineAngleInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_antialiasInput, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_autoCropCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_gboxSettings, TQ_SIGNAL(signalColorGuideChanged()), + this, TQ_SLOT(slotColorGuideChanged())); +} + +FreeRotationTool::~FreeRotationTool() +{ +} + +void FreeRotationTool::slotColorGuideChanged() +{ + m_previewWidget->slotChangeGuideColor(m_gboxSettings->guideColor()); + m_previewWidget->slotChangeGuideSize(m_gboxSettings->guideSize()); +} + +void FreeRotationTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("freerotation Tool"); + m_angleInput->setValue(config->readNumEntry("Main Angle", m_angleInput->defaultValue())); + m_fineAngleInput->setValue(config->readDoubleNumEntry("Fine Angle", m_fineAngleInput->defaultValue())); + m_autoCropCB->setCurrentItem(config->readNumEntry("Auto Crop Type", m_autoCropCB->defaultItem())); + m_antialiasInput->setChecked(config->readBoolEntry("Anti Aliasing", true)); + m_gboxSettings->setGuideColor(config->readColorEntry("Guide Color", &TQt::red)); + m_gboxSettings->setGuideSize(config->readNumEntry("Guide Width", 1)); + + slotColorGuideChanged(); + slotEffect(); +} + +void FreeRotationTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("freerotation Tool"); + config->writeEntry("Main Angle", m_angleInput->value()); + config->writeEntry("Fine Angle", m_fineAngleInput->value()); + config->writeEntry("Auto Crop Type", m_autoCropCB->currentItem()); + config->writeEntry("Anti Aliasing", m_antialiasInput->isChecked()); + config->writeEntry("Guide Color", m_gboxSettings->guideColor()); + config->writeEntry("Guide Width", m_gboxSettings->guideSize()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void FreeRotationTool::slotResetSettings() +{ + m_angleInput->blockSignals(true); + m_antialiasInput->blockSignals(true); + m_autoCropCB->blockSignals(true); + + m_angleInput->slotReset(); + m_fineAngleInput->slotReset(); + m_antialiasInput->setChecked(true); + m_autoCropCB->slotReset(); + + m_angleInput->blockSignals(false); + m_antialiasInput->blockSignals(false); + m_autoCropCB->blockSignals(false); +} + +void FreeRotationTool::prepareEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_angleInput->setEnabled(false); + m_fineAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + m_autoCropCB->setEnabled(false); + + double angle = m_angleInput->value() + m_fineAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + int autocrop = m_autoCropCB->currentItem(); + TQColor background = toolView()->paletteBackgroundColor().rgb(); + ImageIface* iface = m_previewWidget->imageIface(); + int orgW = iface->originalWidth(); + int orgH = iface->originalHeight(); + + uchar *data = iface->getPreviewImage(); + DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new FreeRotation(&image, this, angle, antialiasing, + autocrop, background, orgW, orgH))); +} + +void FreeRotationTool::prepareFinal() +{ + m_angleInput->setEnabled(false); + m_fineAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + m_autoCropCB->setEnabled(false); + + double angle = m_angleInput->value() + m_fineAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + int autocrop = m_autoCropCB->currentItem(); + TQColor background = TQt::black; + + ImageIface iface(0, 0); + int orgW = iface.originalWidth(); + int orgH = iface.originalHeight(); + + uchar *data = iface.getOriginalImage(); + DImg orgImage(orgW, orgH, iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new FreeRotation(&orgImage, this, angle, antialiasing, + autocrop, background, orgW, orgH))); +} + +void FreeRotationTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + + DImg imTemp = filter()->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + DImg imDest( w, h, filter()->getTargetImage().sixteenBit(), + filter()->getTargetImage().hasAlpha() ); + + imDest.fill(DColor(toolView()->paletteBackgroundColor().rgb(), + filter()->getTargetImage().sixteenBit()) ); + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + + m_previewWidget->updatePreview(); + TQSize newSize = dynamic_cast(filter())->getNewSize(); + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); +} + +void FreeRotationTool::putFinalData() +{ + ImageIface iface(0, 0); + DImg targetImage = filter()->getTargetImage(); + iface.putOriginalImage(i18n("Free Rotation"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +void FreeRotationTool::renderingFinished() +{ + m_angleInput->setEnabled(true); + m_fineAngleInput->setEnabled(true); + m_antialiasInput->setEnabled(true); + m_autoCropCB->setEnabled(true); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamFreeRotationImagesPlugin diff --git a/src/imageplugins/freerotation/freerotationtool.h b/src/imageplugins/freerotation/freerotationtool.h new file mode 100644 index 00000000..fd17f4ff --- /dev/null +++ b/src/imageplugins/freerotation/freerotationtool.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef FREEROTATIONTOOL_H +#define FREEROTATIONTOOL_H + +// Local includes. + +#include "editortool.h" + +class TQFrame; +class TQLabel; +class TQCheckBox; + +namespace KDcrawIface +{ +class RIntNumInput; +class RDoubleNumInput; +class RComboBox; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImageWidget; +} + +namespace DigikamFreeRotationImagesPlugin +{ + +class FreeRotationTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + FreeRotationTool(TQObject *parent); + ~FreeRotationTool(); + +private slots: + + void slotResetSettings(); + void slotColorGuideChanged(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + + TQCheckBox *m_antialiasInput; + + KDcrawIface::RComboBox *m_autoCropCB; + + KDcrawIface::RIntNumInput *m_angleInput; + + KDcrawIface::RDoubleNumInput *m_fineAngleInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamFreeRotationImagesPlugin + +#endif /* FREEROTATIONTOOL_H */ diff --git a/src/imageplugins/freerotation/imageeffect_freerotation.cpp b/src/imageplugins/freerotation/imageeffect_freerotation.cpp new file mode 100644 index 00000000..bce50f07 --- /dev/null +++ b/src/imageplugins/freerotation/imageeffect_freerotation.cpp @@ -0,0 +1,308 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "freerotation.h" +#include "imageeffect_freerotation.h" +#include "imageeffect_freerotation.moc" + +namespace DigikamFreeRotationImagesPlugin +{ + +ImageEffect_FreeRotation::ImageEffect_FreeRotation(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Free Rotation"), "freerotation", + false, true, true, Digikam::ImageGuideWidget::HVGuideMode) +{ + // No need Abort button action. + showButton(User1, false); + + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Free Rotation"), + digikam_version, + I18N_NOOP("A digiKam image plugin to process free image " + "rotation."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Free Rotation algorithm"), + "pieter dot voloshyn at gmail dot com"); + + setAboutData(about); + + TQWhatsThis::add( m_imagePreviewWidget, i18n("

    This is the free image operation preview. " + "If you move the mouse cursor on this preview, " + "a vertical and horizontal dashed line will be drawn " + "to guide you in adjusting the free rotation correction. " + "Release the left mouse button to freeze the dashed " + "line's position.")); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 9, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("New width:"), gboxSettings); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), gboxSettings); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), gboxSettings); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), gboxSettings); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + gridSettings->addMultiCellWidget(label2, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + + KSeparator *line = new KSeparator(Horizontal, gboxSettings); + gridSettings->addMultiCellWidget(line, 2, 2, 0, 2); + + TQLabel *label3 = new TQLabel(i18n("Main angle:"), gboxSettings); + m_angleInput = new KIntNumInput(gboxSettings); + m_angleInput->setRange(-180, 180, 1, true); + m_angleInput->setValue(0); + TQWhatsThis::add( m_angleInput, i18n("

    An angle in degrees by which to rotate the image. " + "A positive angle rotates the image clockwise; " + "a negative angle rotates it counter-clockwise.")); + + gridSettings->addMultiCellWidget(label3, 3, 3, 0, 2); + gridSettings->addMultiCellWidget(m_angleInput, 4, 4, 0, 2); + + TQLabel *label4 = new TQLabel(i18n("Fine angle:"), gboxSettings); + m_fineAngleInput = new KDoubleNumInput(gboxSettings); + m_fineAngleInput->setRange(-5.0, 5.0, 0.01, true); + m_fineAngleInput->setValue(0); + TQWhatsThis::add( m_fineAngleInput, i18n("

    This value in degrees will be added to main angle value " + "to set fine target angle.")); + + gridSettings->addMultiCellWidget(label4, 5, 5, 0, 2); + gridSettings->addMultiCellWidget(m_fineAngleInput, 6, 6, 0, 2); + + m_antialiasInput = new TQCheckBox(i18n("Anti-Aliasing"), gboxSettings); + TQWhatsThis::add( m_antialiasInput, i18n("

    Enable this option to apply the anti-aliasing filter " + "to the rotated image. " + "In order to smooth the target image, it will be blurred a little.")); + gridSettings->addMultiCellWidget(m_antialiasInput, 7, 7, 0, 2); + + TQLabel *label5 = new TQLabel(i18n("Auto-crop:"), gboxSettings); + m_autoCropCB = new TQComboBox(false, gboxSettings); + m_autoCropCB->insertItem( i18n("None") ); + m_autoCropCB->insertItem( i18n("Widest Area") ); + m_autoCropCB->insertItem( i18n("Largest Area") ); + TQWhatsThis::add( m_autoCropCB, i18n("

    Select the method to process image auto-cropping " + "to remove black frames around a rotated image.")); + gridSettings->addMultiCellWidget(label5, 8, 8, 0, 0); + gridSettings->addMultiCellWidget(m_autoCropCB, 8, 8, 1, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_angleInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineAngleInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_antialiasInput, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_autoCropCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); +} + +ImageEffect_FreeRotation::~ImageEffect_FreeRotation() +{ +} + +void ImageEffect_FreeRotation::readUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("freerotation Tool Dialog"); + m_angleInput->setValue(config->readNumEntry("Main Angle", 0)); + m_fineAngleInput->setValue(config->readDoubleNumEntry("Fine Angle", 0.0)); + m_autoCropCB->setCurrentItem(config->readNumEntry("Auto Crop Type", FreeRotation::NoAutoCrop)); + m_antialiasInput->setChecked(config->readBoolEntry("Anti Aliasing", true)); + slotEffect(); +} + +void ImageEffect_FreeRotation::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("freerotation Tool Dialog"); + config->writeEntry("Main Angle", m_angleInput->value()); + config->writeEntry("Fine Angle", m_fineAngleInput->value()); + config->writeEntry( "Auto Crop Type", m_autoCropCB->currentItem() ); + config->writeEntry( "Anti Aliasing", m_antialiasInput->isChecked() ); + config->sync(); +} + +void ImageEffect_FreeRotation::resetValues() +{ + m_angleInput->blockSignals(true); + m_antialiasInput->blockSignals(true); + m_autoCropCB->blockSignals(true); + m_angleInput->setValue(0); + m_fineAngleInput->setValue(0.0); + m_antialiasInput->setChecked(true); + m_autoCropCB->setCurrentItem(FreeRotation::NoAutoCrop); + m_angleInput->blockSignals(false); + m_antialiasInput->blockSignals(false); + m_autoCropCB->blockSignals(false); +} + +void ImageEffect_FreeRotation::prepareEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_angleInput->setEnabled(false); + m_fineAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + m_autoCropCB->setEnabled(false); + + double angle = m_angleInput->value() + m_fineAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + int autocrop = m_autoCropCB->currentItem(); + TQColor background = paletteBackgroundColor().rgb(); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int orgW = iface->originalWidth(); + int orgH = iface->originalHeight(); + + uchar *data = iface->getPreviewImage(); + Digikam::DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new FreeRotation(&image, this, angle, antialiasing, autocrop, + background, orgW, orgH)); +} + +void ImageEffect_FreeRotation::prepareFinal() +{ + m_angleInput->setEnabled(false); + m_fineAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + m_autoCropCB->setEnabled(false); + + double angle = m_angleInput->value() + m_fineAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + int autocrop = m_autoCropCB->currentItem(); + TQColor background = TQt::black; + + Digikam::ImageIface iface(0, 0); + int orgW = iface.originalWidth(); + int orgH = iface.originalHeight(); + + uchar *data = iface.getOriginalImage(); + Digikam::DImg orgImage(orgW, orgH, iface.originalSixteenBit(), + iface.originalHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new FreeRotation(&orgImage, this, angle, antialiasing, autocrop, + background, orgW, orgH)); +} + +void ImageEffect_FreeRotation::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + + Digikam::DImg imTemp = m_threadedFilter->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + Digikam::DImg imDest( w, h, m_threadedFilter->getTargetImage().sixteenBit(), + m_threadedFilter->getTargetImage().hasAlpha() ); + + imDest.fill( Digikam::DColor(paletteBackgroundColor().rgb(), + m_threadedFilter->getTargetImage().sixteenBit()) ); + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + + m_imagePreviewWidget->updatePreview(); + TQSize newSize = dynamic_cast(m_threadedFilter)->getNewSize(); + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); +} + +void ImageEffect_FreeRotation::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + Digikam::DImg targetImage = m_threadedFilter->getTargetImage(); + iface.putOriginalImage(i18n("Free Rotation"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +void ImageEffect_FreeRotation::renderingFinished() +{ + m_angleInput->setEnabled(true); + m_fineAngleInput->setEnabled(true); + m_antialiasInput->setEnabled(true); + m_autoCropCB->setEnabled(true); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamFreeRotationImagesPlugin + diff --git a/src/imageplugins/freerotation/imageeffect_freerotation.h b/src/imageplugins/freerotation/imageeffect_freerotation.h new file mode 100644 index 00000000..a90be09d --- /dev/null +++ b/src/imageplugins/freerotation/imageeffect_freerotation.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_FREEROTATION_H +#define IMAGEEFFECT_FREEROTATION_H + +// Local includes. + +#include "imageguidedlg.h" + +class TQFrame; +class TQLabel; +class TQCheckBox; +class TQComboBox; + +class KIntNumInput; +class KDoubleNumInput; + +namespace DigikamFreeRotationImagesPlugin +{ + +class ImageEffect_FreeRotation : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_FreeRotation(TQWidget *parent); + ~ImageEffect_FreeRotation(); + +private slots: + + void readUserSettings(void); + +protected: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + + TQCheckBox *m_antialiasInput; + + TQComboBox *m_autoCropCB; + + KIntNumInput *m_angleInput; + + KDoubleNumInput *m_fineAngleInput; +}; + +} // NameSpace DigikamFreeRotationImagesPlugin + +#endif /* IMAGEEFFECT_FREEROTATION_H */ diff --git a/src/imageplugins/freerotation/imageplugin_freerotation.cpp b/src/imageplugins/freerotation/imageplugin_freerotation.cpp new file mode 100644 index 00000000..0c04a2ca --- /dev/null +++ b/src/imageplugins/freerotation/imageplugin_freerotation.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "freerotationtool.h" +#include "imageplugin_freerotation.h" +#include "imageplugin_freerotation.moc" + +using namespace DigikamFreeRotationImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_freerotation, + KGenericFactory("digikamimageplugin_freerotation")); + +ImagePlugin_FreeRotation::ImagePlugin_FreeRotation(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_FreeRotation") +{ + m_freerotationAction = new TDEAction(i18n("Free Rotation..."), "freerotation", 0, + this, TQ_SLOT(slotFreeRotation()), + actionCollection(), "imageplugin_freerotation"); + + setXMLFile("digikamimageplugin_freerotation_ui.rc"); + + DDebug() << "ImagePlugin_FreeRotation plugin loaded" << endl; +} + +ImagePlugin_FreeRotation::~ImagePlugin_FreeRotation() +{ +} + +void ImagePlugin_FreeRotation::setEnabledActions(bool enable) +{ + m_freerotationAction->setEnabled(enable); +} + +void ImagePlugin_FreeRotation::slotFreeRotation() +{ + FreeRotationTool *tool = new FreeRotationTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/freerotation/imageplugin_freerotation.h b/src/imageplugins/freerotation/imageplugin_freerotation.h new file mode 100644 index 00000000..2a0c2627 --- /dev/null +++ b/src/imageplugins/freerotation/imageplugin_freerotation.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-28 + * Description : a digiKam image editor plugin to process image + * free rotation. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_FREEROTATION_H +#define IMAGEPLUGIN_FREEROTATION_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_FreeRotation : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_FreeRotation(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_FreeRotation(); + + void setEnabledActions(bool enable); + +private slots: + + void slotFreeRotation(); + +private: + + TDEAction *m_freerotationAction; +}; + +#endif /* IMAGEPLUGIN_FREEROTATION_H */ diff --git a/src/imageplugins/hotpixels/Makefile.am b/src/imageplugins/hotpixels/Makefile.am new file mode 100644 index 00000000..711fe84e --- /dev/null +++ b/src/imageplugins/hotpixels/Makefile.am @@ -0,0 +1,36 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_hotpixels_la_SOURCES = blackframeparser.cpp weights.cpp \ + hotpixelfixer.cpp imageplugin_hotpixels.cpp \ + blackframelistview.cpp hotpixelstool.cpp + +digikamimageplugin_hotpixels_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_hotpixels_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_hotpixels.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_hotpixels.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_hotpixels_ui.rc + diff --git a/src/imageplugins/hotpixels/TODO b/src/imageplugins/hotpixels/TODO new file mode 100644 index 00000000..880e1a36 --- /dev/null +++ b/src/imageplugins/hotpixels/TODO @@ -0,0 +1,4 @@ +- Store black frames. Include the fullsize image to be able to reedit it if necessary. +- Add a hand hot-pixel editor for the hot pixels on the black frame +- Use the same hot-pixel editor from the image view, to edit a new black frame with the added data + diff --git a/src/imageplugins/hotpixels/blackframelistview.cpp b/src/imageplugins/hotpixels/blackframelistview.cpp new file mode 100644 index 00000000..1202b094 --- /dev/null +++ b/src/imageplugins/hotpixels/blackframelistview.cpp @@ -0,0 +1,176 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-05 + * Description : a ListView to display black frames + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define THUMB_WIDTH 150 + +// TQt includes. + +#include +#include + +// Local includes. + +#include "blackframelistview.h" +#include "blackframelistview.moc" + +namespace DigikamHotPixelsImagesPlugin +{ + +BlackFrameListView::BlackFrameListView(TQWidget* parent) + : TQListView(parent) +{ + addColumn(i18n("Preview")); + addColumn(i18n("Size")); + addColumn(i18n("This is a column which will contain the amount of HotPixels " + "found in the black frame file", "HP")); + setAllColumnsShowFocus(true); + setResizeMode(TQListView::LastColumn); + setSelectionMode(TQListView::Single); +} + +// -------------------------------------------------------------------------- + +BlackFrameListViewItem::BlackFrameListViewItem(BlackFrameListView* parent, const KURL &url) + : TQObject(parent), TQListViewItem(parent) +{ + m_parent = parent; + m_blackFrameURL = url; + m_parser = new BlackFrameParser(parent); + m_parser->parseBlackFrame(url); + + connect(m_parser, TQ_SIGNAL(parsed(TQValueList)), + this, TQ_SLOT(slotParsed(TQValueList))); + + connect(this, TQ_SIGNAL(parsed(TQValueList, const KURL&)), + parent, TQ_SLOT(slotParsed(TQValueList, const KURL&))); + + connect(m_parser, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SIGNAL(signalLoadingProgress(float))); + + connect(m_parser, TQ_SIGNAL(signalLoadingComplete()), + this, TQ_SIGNAL(signalLoadingComplete())); +} + +void BlackFrameListViewItem::activate() +{ + TQToolTip::add( m_parent, m_blackFrameDesc); + emit parsed(m_hotPixels, m_blackFrameURL); +} + +TQString BlackFrameListViewItem::text(int column)const +{ + switch (column) + { + case 0: + { + // First column includes the pixmap + break; + } + case 1: + { + // The image size. + if (!m_imageSize.isEmpty()) + return (TQString("%1x%2").arg(m_imageSize.width()).arg(m_imageSize.height())); + break; + } + case 2: + { + // The amount of hot pixels found in the black frame. + return (TQString::number(m_hotPixels.count())); + break; + } + } + + return TQString(); +} + +void BlackFrameListViewItem::paintCell(TQPainter* p, const TQColorGroup& cg, int column, int width, int align) +{ + //Let the normal listview item draw it all for now + TQListViewItem::paintCell(p, cg, column, width, align); +} + +void BlackFrameListViewItem::slotParsed(TQValueList hotPixels) +{ + m_hotPixels = hotPixels; + m_image = m_parser->image(); + m_imageSize = m_image.size(); + m_thumb = thumb(TQSize(THUMB_WIDTH, THUMB_WIDTH/3*2)); + setPixmap(0, m_thumb); + + m_blackFrameDesc = TQString("

    " + m_blackFrameURL.fileName() + ":

    "); + TQValueList ::Iterator end(m_hotPixels.end()); + for (TQValueList ::Iterator it = m_hotPixels.begin() ; it != end ; ++it) + m_blackFrameDesc.append( TQString("[%1,%2] ").arg((*it).x()).arg((*it).y()) ); + + emit parsed(m_hotPixels, m_blackFrameURL); +} + +TQPixmap BlackFrameListViewItem::thumb(const TQSize& size) +{ + TQPixmap thumb; + + //First scale it down to the size + thumb = m_image.smoothScale(size, TQImage::ScaleMin); + + //And draw the hot pixel positions on the thumb + TQPainter p(&thumb); + + //Take scaling into account + float xRatio, yRatio; + float hpThumbX, hpThumbY; + TQRect hpRect; + + xRatio = (float)size.width()/(float)m_image.width(); + yRatio = (float)size.height()/(float)m_image.height(); + + //Draw hot pixels one by one + TQValueList ::Iterator it; + TQValueList ::Iterator end(m_hotPixels.end()); + for (it=m_hotPixels.begin() ; it!=end ; ++it) + { + hpRect = (*it).rect; + hpThumbX = (hpRect.x()+hpRect.width()/2)*xRatio; + hpThumbY = (hpRect.y()+hpRect.height()/2)*yRatio; + + p.setPen(TQPen(TQt::black)); + p.drawLine((int)hpThumbX, (int)hpThumbY-1, (int)hpThumbX, (int)hpThumbY+1); + p.drawLine((int)hpThumbX-1, (int)hpThumbY, (int)hpThumbX+1, (int)hpThumbY); + p.setPen(TQPen(TQt::white)); + p.drawPoint((int)hpThumbX-1, (int)hpThumbY-1); + p.drawPoint((int)hpThumbX+1, (int)hpThumbY+1); + p.drawPoint((int)hpThumbX-1, (int)hpThumbY+1); + p.drawPoint((int)hpThumbX+1, (int)hpThumbY-1); + } + + return thumb; +} + +int BlackFrameListViewItem::width(const TQFontMetrics& fm,const TQListView* lv,int c)const +{ + if (c==0) return THUMB_WIDTH; + else return TQListViewItem::width(fm,lv,c); +} + +} // NameSpace DigikamHotPixelsImagesPlugin diff --git a/src/imageplugins/hotpixels/blackframelistview.h b/src/imageplugins/hotpixels/blackframelistview.h new file mode 100644 index 00000000..977d226d --- /dev/null +++ b/src/imageplugins/hotpixels/blackframelistview.h @@ -0,0 +1,127 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-05 + * Description : a ListView to display black frames + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BLACKFRAMELISTVIEW_H +#define BLACKFRAMELISTVIEW_H + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "blackframeparser.h" +#include "hotpixel.h" + +namespace DigikamHotPixelsImagesPlugin +{ + +class BlackFrameListView : public TQListView +{ + TQ_OBJECT + + +public: + + BlackFrameListView(TQWidget* parent=0); + ~BlackFrameListView(){}; + +signals: + + void blackFrameSelected(TQValueList, const KURL&); + +private slots: + + void slotParsed(TQValueList hotPixels, const KURL& blackFrameURL) + { + emit blackFrameSelected(hotPixels, blackFrameURL); + }; +}; + +// -------------------------------------------------------------------------- + +class BlackFrameListViewItem : public TQObject, TQListViewItem +{ +TQ_OBJECT + + +public: + + BlackFrameListViewItem(BlackFrameListView* parent, const KURL &url); + ~BlackFrameListViewItem(){}; + + virtual TQString text(int column)const; + virtual void paintCell(TQPainter* p, const TQColorGroup& cg, int column, int width, int align); + virtual int width(const TQFontMetrics& fm, const TQListView* lv, int c)const; + +signals: + + void parsed(TQValueList, const KURL&); + void signalLoadingProgress(float); + void signalLoadingComplete(); + +protected: + + void activate(); + +private: + + TQPixmap thumb(const TQSize& size); + +private slots: + + void slotParsed(TQValueList); + +private: + + // Data contained within each listview item + TQImage m_thumb; + TQImage m_image; + + TQSize m_imageSize; + + TQValueList m_hotPixels; + + TQString m_blackFrameDesc; + + KURL m_blackFrameURL; + + BlackFrameParser *m_parser; + + BlackFrameListView *m_parent; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif // BLACKFRAMELISTVIEW_H diff --git a/src/imageplugins/hotpixels/blackframeparser.cpp b/src/imageplugins/hotpixels/blackframeparser.cpp new file mode 100644 index 00000000..7306f4e6 --- /dev/null +++ b/src/imageplugins/hotpixels/blackframeparser.cpp @@ -0,0 +1,211 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : black frames parser + * + * Copyright (C) 2005-2006 by Unai Garro + * Copyright (C) 2005-2008 by Gilles Caulier + * + * Part of the algorithm for finding the hot pixels was based on + * the code of jpegpixi, which was released under the GPL license, + * and is Copyright (C) 2003, 2004 Martin Dickopp + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Denominator for relative quantities. +#define DENOM (DENOM_STQRT * DENOM_STQRT) + +// Square root of denominator for relative quantities. +#define DENOM_STQRT 10000 + +// Convert relative to absolute numbers. Care must be taken not to overflow integers. +#define REL_TO_ABS(n,m) \ + ((((n) / DENOM_STQRT) * (m) + ((n) % DENOM_STQRT) * (m) / DENOM_STQRT) / DENOM_STQRT) + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "blackframeparser.h" +#include "blackframeparser.moc" + +namespace DigikamHotPixelsImagesPlugin +{ + +BlackFrameParser::BlackFrameParser(TQObject *parent) + : TQObject(parent) +{ + m_imageLoaderThread = 0; +} + +BlackFrameParser::~BlackFrameParser() +{ + delete m_imageLoaderThread; +} + +void BlackFrameParser::parseHotPixels(const TQString &file) +{ + parseBlackFrame(KURL(file)); +} + +void BlackFrameParser::parseBlackFrame(const KURL &url) +{ +#if KDE_IS_VERSION(3,2,0) + TDEIO::NetAccess::download(url, m_localFile, kapp->activeWindow()); +#else + TDEIO::NetAccess::download(url, m_localFile); +#endif + + if (!m_imageLoaderThread) + { + m_imageLoaderThread = new LoadSaveThread(); + + connect(m_imageLoaderThread, TQ_SIGNAL(signalLoadingProgress(const LoadingDescription&, float)), + this, TQ_SLOT(slotLoadingProgress(const LoadingDescription&, float))); + + connect(m_imageLoaderThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription&, const DImg&)), + this, TQ_SLOT(slotLoadImageFromUrlComplete(const LoadingDescription&, const DImg&))); + } + + LoadingDescription desc = LoadingDescription(m_localFile, DRawDecoding()); + m_imageLoaderThread->load(desc); +} + +void BlackFrameParser::slotLoadingProgress(const LoadingDescription&, float v) +{ + emit signalLoadingProgress(v); +} + +void BlackFrameParser::slotLoadImageFromUrlComplete(const LoadingDescription&, const DImg& img) +{ + DImg image(img); + m_Image = image.copyTQImage(); + blackFrameParsing(); + emit signalLoadingComplete(); +} + +void BlackFrameParser::parseBlackFrame(TQImage& img) +{ + m_Image = img; + blackFrameParsing(); +} + +// Parses black frames + +void BlackFrameParser::blackFrameParsing() +{ + // Now find the hot pixels and store them in a list + TQValueList hpList; + + for (int y=0 ; y < m_Image.height() ; ++y) + { + for (int x=0 ; x < m_Image.width() ; ++x) + { + //Get each point in the image + TQRgb pixrgb = m_Image.pixel(x,y); + TQColor color; color.setRgb(pixrgb); + + // Find maximum component value. + int maxValue; + int threshold = DENOM/10; + const int threshold_value = REL_TO_ABS(threshold, 255); + maxValue = (color.red()>color.blue()) ? color.red() : color.blue(); + if (color.green() > maxValue) maxValue = color.green(); + + // If the component is bigger than the threshold, add the point + if (maxValue > threshold_value) + { + HotPixel point; + point.rect = TQRect (x, y, 1, 1); + //TODO:check this + point.luminosity = ((2 * DENOM) / 255 ) * maxValue / 2; + + hpList.append(point); + } + } + } + + //Now join points together into groups + consolidatePixels (hpList); + + //And notify + emit parsed(hpList); +} + +// Consolidate adjacent points into larger points. + +void BlackFrameParser::consolidatePixels (TQValueList& list) +{ + if (list.isEmpty()) + return; + + /* Consolidate horizontally. */ + + TQValueList::iterator it, prevPointIt; + + prevPointIt = list.begin(); + it = list.begin(); + ++it; + + HotPixel tmp; + HotPixel point; + HotPixel point_below; + TQValueList::iterator end(list.end()); + for (; it != end; ++it ) + { + while (1) + { + point = (*it); + tmp = point; + + TQValueList::Iterator point_below_it; + point_below_it = list.find (tmp); //find any intersecting hotp below tmp + if (point_below_it != list.end()) + { + point_below =* point_below_it; + validateAndConsolidate (&point, &point_below); + + point.rect.setX(MIN(point.x(), point_below.x())); + point.rect.setWidth(MAX(point.x() + point.width(), + point_below.x() + point_below.width()) - point.x()); + point.rect.setHeight(MAX(point.y() + point.height(), + point_below.y() + point_below.height()) - point.y()); + *it = point; + list.remove (point_below_it); //TODO: Check! this could remove it++? + } + else + break; + } + } +} + +void BlackFrameParser::validateAndConsolidate (HotPixel *a, HotPixel *b) +{ + a->luminosity = MAX (a->luminosity, b->luminosity); +} + +} // NameSpace DigikamHotPixelsImagesPlugin diff --git a/src/imageplugins/hotpixels/blackframeparser.h b/src/imageplugins/hotpixels/blackframeparser.h new file mode 100644 index 00000000..f13ebdc4 --- /dev/null +++ b/src/imageplugins/hotpixels/blackframeparser.h @@ -0,0 +1,102 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : black frames parser + * + * Copyright (C) 2005-2006 by Unai Garro + * Copyright (C) 2005-2008 by Gilles Caulier + * + * Part of the algorithm for finding the hot pixels was based on + * the code of jpegpixi, which was released under the GPL license, + * and is Copyright (C) 2003, 2004 Martin Dickopp + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BLACKFRAMEPARSER_H +#define BLACKFRAMEPARSER_H + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "loadsavethread.h" +#include "hotpixel.h" + +using namespace Digikam; + +namespace DigikamHotPixelsImagesPlugin +{ + +class BlackFrameParser: public TQObject +{ + TQ_OBJECT + + +public: + + BlackFrameParser(TQObject *parent); + ~BlackFrameParser(); + + void parseHotPixels(const TQString &file); + void parseBlackFrame(const KURL &url); + void parseBlackFrame(TQImage& img); + TQImage image(){return m_Image;} + +signals: + + void parsed(TQValueList); + void signalLoadingProgress(float); + void signalLoadingComplete(); + +private slots: + + void slotLoadingProgress(const LoadingDescription&, float); + void slotLoadImageFromUrlComplete(const LoadingDescription&, const DImg&); + +private: + + void blackFrameParsing(); + void consolidatePixels(TQValueList& list); + void validateAndConsolidate(HotPixel *a, HotPixel *b); + +private: + + TQString m_OutputString; + TQString m_localFile; + + TQImage m_Image; + + LoadSaveThread *m_imageLoaderThread; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif // BLACKFRAMEPARSER_H diff --git a/src/imageplugins/hotpixels/digikamimageplugin_hotpixels.desktop b/src/imageplugins/hotpixels/digikamimageplugin_hotpixels.desktop new file mode 100644 index 00000000..5b12fe2c --- /dev/null +++ b/src/imageplugins/hotpixels/digikamimageplugin_hotpixels.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_HotPixels +Name[bg]=ПриÑтавка за Ñнимки - Горещи пикÑели +Name[el]=ΠÏόσθετοΕικόνας_ΈντοναΕικονοστοιχεία +Name[fi]=KuumatPikselit +Name[hr]=Vrući pikseli +Name[it]=PluginImmagini_PixelBruciati +Name[ms]=ImagePlugin_PikselPanas +Name[nl]=Afbeeldingsplugin_HotPixels +Name[sr]=Врући пикÑели +Name[sr@Latn]=Vrući pikseli +Name[sv]=Insticksprogram för heta bildpunkter +Name[tr]=ResimEklentisi_Çekirdek +Name[xx]=xxImagePlugin_HotPixelsxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Hot pixel correction plugin for digiKam +Comment[ca]=Connector per al digiKam de correcció de píxels cremats +Comment[da]=Hot pixel rettelsesplugin for Digikam +Comment[de]=digiKam-Modul zur Korrektur von heißen (defekten) Pixeln +Comment[el]=ΠÏόσθετο διόÏθωσης έντονων εικονοστοιχείων για το digiKam +Comment[es]=Plugin para digiKampara corregir los píxeles quemados de la imagen +Comment[et]=DigiKami kuumade pikslite korrigeerimise plugin +Comment[fa]=وصلۀ اصلاح تصویردانۀ Hot برای digiKam +Comment[fi]=Digitaalisen rakeisuushäiriön korjaus +Comment[gl]=Un plugin de digiKam para corrixir os pixels queimados da imaxe +Comment[hr]=digiKam dodatak za ispravljanje vrućih piksela +Comment[is]=Ãforrit fyrir digiKam sem fjarlægir skemmda díla (hot pixels) +Comment[it]=Plugin di correzione dei pixel bruciati per digiKam +Comment[ja]=digiKam ホットピクセル除去プラグイン +Comment[ms]=Plugin pembetulan piksel panas untuk digiKam +Comment[nds]=digiKam-Moduul för't Richten vun hitte Pixels +Comment[nl]=Digikam-plugin voor het corrigeren van de hotpixels +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਹਾਟ ਪਿਕਸਲ ਸੋਧ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam usuwajÄ…ca "gorÄ…ce piksele" +Comment[pt]=Um 'plugin' do digiKam para corrigir os pixels queimados da imagem +Comment[pt_BR]=Plugin de Correção de hot pixel para o digiKam +Comment[ru]=Модуль коррекции Ñрких пикÑелей Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre korekciu vypálených pixelov +Comment[sr]=digiKam-ов прикључак иÑправку врућих пикÑела +Comment[sr@Latn]=digiKam-ov prikljuÄak ispravku vrućih piksela +Comment[sv]=Digikam insticksprogram för korrigering av heta bildpunkter +Comment[tr]=digiKam için beyaz dengesini düzeltme eklentisi +Comment[uk]=Втулок Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð³Ð°Ñ€Ñчих пікÑелів Ð´Ð»Ñ Digikam +Comment[vi]=Phần bổ sung sá»­a Ä‘iểm ảnh nóng cho digiKam +Comment[xx]=xxHot pixel correction plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_hotpixels +author=Unai Garro, ugarro at sourceforge dot net diff --git a/src/imageplugins/hotpixels/digikamimageplugin_hotpixels_ui.rc b/src/imageplugins/hotpixels/digikamimageplugin_hotpixels_ui.rc new file mode 100644 index 00000000..b80d7f5a --- /dev/null +++ b/src/imageplugins/hotpixels/digikamimageplugin_hotpixels_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/hotpixels/hotpixel.h b/src/imageplugins/hotpixels/hotpixel.h new file mode 100644 index 00000000..bceb539f --- /dev/null +++ b/src/imageplugins/hotpixels/hotpixel.h @@ -0,0 +1,74 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : Threaded image filter to fix hot pixels + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef HOTPIXEL_H +#define HOTPIXEL_H + +// TQt includes. + +#include + +namespace DigikamHotPixelsImagesPlugin +{ + +class HotPixel +{ + +public: + + TQRect rect; + int luminosity; + int y() const {return rect.y(); }; + int x() const {return rect.x(); }; + int width()const {return rect.width(); }; + int height()const {return rect.height();}; + + bool operator==(const HotPixel p) const + { + //we can say they're same hotpixel spot if they + //touch(next to) each other horizontally or vertically, not diagonal corners + //return (rect.intersects(p.rect)); + return (rect != p.rect) && (x() + width() >= p.x() && x() <= p.x() + p.width() + && y() + height() >= p.y() && y() <= p.y() + p.height()) + && !diagonal(rect, p.rect); + } + +private: + + bool diagonal(TQRect r1,TQRect r2) const + { + //locate next-to positions + + bool top = r1.y() + height()-1 == r2.y()-1; //r1 is on the top of r2 + bool left = r1.x() + width()-1 == r2.x()-1; //r1 is on the left of r2 + bool right = r1.x() == r2.x() + r2.width(); + bool bottom = r1.y() == r2.y() + r2.height(); + + return ((top && left) || (top && right) || (bottom && left) || (bottom && right)); + } +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif // HOTPIXEL_H diff --git a/src/imageplugins/hotpixels/hotpixelfixer.cpp b/src/imageplugins/hotpixels/hotpixelfixer.cpp new file mode 100644 index 00000000..d29b8525 --- /dev/null +++ b/src/imageplugins/hotpixels/hotpixelfixer.cpp @@ -0,0 +1,302 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : Threaded image filter to fix hot pixels + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "hotpixelfixer.h" + +#ifdef HAVE_FLOAT_H +#if HAVE_FLOAT_H +# include +#endif +#endif + +#ifndef DBL_MIN +# define DBL_MIN 1e-37 +#endif +#ifndef DBL_MAX +# define DBL_MAX 1e37 +#endif + +namespace DigikamHotPixelsImagesPlugin +{ + +HotPixelFixer::HotPixelFixer(Digikam::DImg *orgImage, TQObject *parent, const TQValueList& hpList, + int interpolationMethod) + : Digikam::DImgThreadedFilter(orgImage, parent, "HotPixels") +{ + m_hpList = hpList; + m_interpolationMethod = interpolationMethod; + mWeightList.clear(); + + initFilter(); +} + +HotPixelFixer::~HotPixelFixer() +{ +} + +void HotPixelFixer::filterImage(void) +{ + TQValueList ::ConstIterator it; + TQValueList ::ConstIterator end(m_hpList.end()); + for (it = m_hpList.begin() ; it != end ; ++it) + { + HotPixel hp = *it; + interpolate(m_orgImage, hp, m_interpolationMethod); + } + + m_destImage = m_orgImage; +} + +// Interpolates a pixel block +void HotPixelFixer::interpolate (Digikam::DImg &img, HotPixel &hp, int method) +{ + const int xPos = hp.x(); + const int yPos = hp.y(); + bool sixtBits = img.sixteenBit(); + + // Interpolate pixel. + switch (method) + { + case AVERAGE_INTERPOLATION: + { + // We implement the bidimendional one first. + // TODO: implement the rest of directions (V & H) here + + //case twodim: + // { + int sum_weight = 0; + double vr=0.0,vg=0.0,vb=0.0; + int x, y; + Digikam::DColor col; + + for (x = xPos; x < xPos+hp.width(); ++x) + { + if (validPoint(img,TQPoint(x,yPos-1))) + { + col=img.getPixelColor(x,yPos-1); + vr += col.red(); + vg += col.green(); + vb += col.blue(); + ++sum_weight; + } + if (validPoint(img,TQPoint(x,yPos+hp.height()))) + { + col=img.getPixelColor(x,yPos+hp.height()); + vr += col.red(); + vg += col.green(); + vb += col.blue(); + ++sum_weight; + } + } + + for (y = yPos; y < hp.height(); ++y) + { + if (validPoint(img,TQPoint(xPos-1,y))) + { + col=img.getPixelColor(xPos,y); + vr += col.red(); + vg += col.green(); + vb += col.blue(); + ++sum_weight; + } + if (validPoint(img,TQPoint(xPos+hp.width(),y))) + { + col=img.getPixelColor(xPos+hp.width(),y); + vr += col.red(); + vg += col.green(); + vb += col.blue(); + ++sum_weight; + } + } + + if (sum_weight > 0) + { + vr /= (double)sum_weight; + vg /= (double)sum_weight; + vb /= (double)sum_weight; + + + for (x = 0; x < hp.width(); ++x) + for (y = 0; y < hp.height(); ++y) + if (validPoint(img,TQPoint(xPos+x,yPos+y))) + { + int alpha=sixtBits ? 65535 : 255; + int ir=(int )round(vr),ig=(int) round(vg),ib=(int) round(vb); + img.setPixelColor(xPos+x,yPos+y,Digikam::DColor(ir,ig,ib,alpha,sixtBits)); + } + } + break; + } //Case average + + case LINEAR_INTERPOLATION: + //(Bi)linear interpolation. + weightPixels (img,hp,LINEAR_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535: 255); + break; + + case QUADRATIC_INTERPOLATION: + // (Bi)quadratic interpolation. + weightPixels (img,hp,QUADRATIC_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535 : 255); + break; + + case CUBIC_INTERPOLATION: + // (Bi)cubic interpolation. + weightPixels (img,hp,CUBIC_INTERPOLATION,TWODIM_DIRECTION,sixtBits ? 65535 : 255); + break; + } //switch +} + +void HotPixelFixer::weightPixels (Digikam::DImg &img, HotPixel &px, int method, Direction dir,int maxComponent) +{ + //TODO: implement direction here too + + for (int iComp = 0; iComp < 3; ++iComp) + { + // Obtain weight data block. + + Weights w; + int polynomeOrder=-1; + + switch (method) + { + case AVERAGE_INTERPOLATION: // Gilles: to prevent warnings from compiler. + break; + case LINEAR_INTERPOLATION: + polynomeOrder=1; + break; + case QUADRATIC_INTERPOLATION: + polynomeOrder=2; + break; + case CUBIC_INTERPOLATION: + polynomeOrder=3; + break; + } + if (polynomeOrder<0) return; + + // In the one-dimensional case, the width must be 1, + // and the size must be stored in height + + w.setWidth(dir == TWODIM_DIRECTION ? px.width() : 1); + w.setHeight(dir == HORIZONTAL_DIRECTION ? px.width() : px.height()); + w.setPolynomeOrder(polynomeOrder); + w.setTwoDim(dir == TWODIM_DIRECTION); + + //TODO: check this, it must not recalculate existing calculated weights + //for now I don't think it is finding the duplicates fine, so it uses + //the previous one always... + + //if (mWeightList.find(w)==mWeightList.end()) + //{ + w.calculateWeights(); + + // mWeightList.append(w); + + //} + + // Calculate weighted pixel sum. + for (int y = 0; y= DBL_MIN) + { + component=(int) (v/sum_weight); + //Clamp value + if (component<0) component=0; + if (component>maxComponent) component=maxComponent; + } + else if (v >= 0.0) + component=maxComponent; + else + component=0; + + if (iComp==0) color.setRed(component); + else if (iComp==1) color.setGreen(component); + else color.setBlue(component); + + + img.setPixelColor(px.x()+x,px.y()+y,color); + } + } + } + } +} + +} // NameSpace DigikamHotPixelsImagesPlugin diff --git a/src/imageplugins/hotpixels/hotpixelfixer.h b/src/imageplugins/hotpixels/hotpixelfixer.h new file mode 100644 index 00000000..2bf8131c --- /dev/null +++ b/src/imageplugins/hotpixels/hotpixelfixer.h @@ -0,0 +1,98 @@ +/* ============================================================ + * Authors: Unai Garro + * Gilles Caulier + * Date : 2005-03-27 + * Description : Threaded image filter to fix hot pixels + * + * Copyright 2005-2007 by Unai Garro and Gilles Caulier + * + * The algorithm for fixing the hot pixels was based on + * the code of jpegpixi, which was released under the GPL license, + * and is Copyright (C) 2003, 2004 Martin Dickopp + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================*/ + +#ifndef HOTPIXELFIXER_H +#define HOTPIXELFIXER_H + +// TQt includes. + +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +// Local includes. + +#include "hotpixel.h" +#include "weights.h" + +namespace DigikamHotPixelsImagesPlugin +{ + +class HotPixelFixer : public Digikam::DImgThreadedFilter +{ + +public: + + enum InterpolationMethod + { + AVERAGE_INTERPOLATION = 0, + LINEAR_INTERPOLATION = 1, + QUADRATIC_INTERPOLATION = 2, + CUBIC_INTERPOLATION = 3 + }; + + enum Direction + { + TWODIM_DIRECTION = 0, + VERTICAL_DIRECTION = 1, + HORIZONTAL_DIRECTION = 2 + }; + +public: + + HotPixelFixer(Digikam::DImg *orgImage, TQObject *parent, + const TQValueList& hpList, int interpolationMethod); + ~HotPixelFixer(); + +private: + + virtual void filterImage(void); + + void interpolate (Digikam::DImg &img,HotPixel &hp, int method); + void weightPixels (Digikam::DImg &img, HotPixel &px, int method, Direction dir, int maxComponent); + + inline bool validPoint(Digikam::DImg &img, TQPoint p) + { + return (p.x()>=0 && p.y()>=0 && p.x()<(long) img.width() && p.y()<(long) img.height()); + }; + + TQValueList mWeightList; + +private: + + int m_interpolationMethod; + + TQValueList m_hpList; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif // HOTPIXELFIXER_H diff --git a/src/imageplugins/hotpixels/hotpixelstool.cpp b/src/imageplugins/hotpixels/hotpixelstool.cpp new file mode 100644 index 00000000..fbcc6c9f --- /dev/null +++ b/src/imageplugins/hotpixels/hotpixelstool.cpp @@ -0,0 +1,276 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortooliface.h" +#include "editortoolsettings.h" +#include "imagedialog.h" +#include "blackframelistview.h" +#include "hotpixelstool.h" +#include "hotpixelstool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamHotPixelsImagesPlugin +{ + +HotPixelsTool::HotPixelsTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("hotpixels"); + setToolName(i18n("Hot Pixels")); + setToolIcon(SmallIcon("hotpixels")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Ok| + EditorToolSettings::Try| + EditorToolSettings::Cancel, + EditorToolSettings::PanIcon); + + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 3, 2); + + TQLabel *filterMethodLabel = new TQLabel(i18n("Filter:"), m_gboxSettings->plainPage()); + m_filterMethodCombo = new RComboBox(m_gboxSettings->plainPage()); + m_filterMethodCombo->insertItem(i18n("Average")); + m_filterMethodCombo->insertItem(i18n("Linear")); + m_filterMethodCombo->insertItem(i18n("Quadratic")); + m_filterMethodCombo->insertItem(i18n("Cubic")); + m_filterMethodCombo->setDefaultItem(HotPixelFixer::QUADRATIC_INTERPOLATION); + + m_blackFrameButton = new TQPushButton(i18n("Black Frame..."), m_gboxSettings->plainPage()); + TQWhatsThis::add(m_blackFrameButton, i18n("

    Use this button to " + "add a new black frame file which will be used by the hot pixels removal filter.")); + + m_blackFrameListView = new BlackFrameListView(m_gboxSettings->plainPage()); + + grid->addMultiCellWidget(filterMethodLabel, 0, 0, 0, 0); + grid->addMultiCellWidget(m_filterMethodCombo, 0, 0, 1, 1); + grid->addMultiCellWidget(m_blackFrameButton, 0, 0, 2, 2); + grid->addMultiCellWidget(m_blackFrameListView, 1, 2, 0, 2); + grid->setRowStretch(3, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "hotpixels Tool", m_gboxSettings->panIconView(), + 0, ImagePanelWidget::SeparateViewDuplicate); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_filterMethodCombo, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_blackFrameButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAddBlackFrame())); + + connect(m_blackFrameListView, TQ_SIGNAL(blackFrameSelected(TQValueList, const KURL&)), + this, TQ_SLOT(slotBlackFrame(TQValueList, const KURL&))); +} + +HotPixelsTool::~HotPixelsTool() +{ +} + +void HotPixelsTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("hotpixels Tool"); + m_blackFrameURL = KURL(config->readEntry("Last Black Frame File", TQString())); + m_filterMethodCombo->setCurrentItem(config->readNumEntry("Filter Method", + m_filterMethodCombo->defaultItem())); + + if (m_blackFrameURL.isValid()) + { + EditorToolIface::editorToolIface()->setToolStartProgress(i18n("Loading: ")); + BlackFrameListViewItem *item = new BlackFrameListViewItem(m_blackFrameListView, m_blackFrameURL); + + connect(item, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SLOT(slotLoadingProgress(float))); + + connect(item, TQ_SIGNAL(signalLoadingComplete()), + this, TQ_SLOT(slotLoadingComplete())); + } +} + +void HotPixelsTool::slotLoadingProgress(float v) +{ + EditorToolIface::editorToolIface()->setToolProgress((int)(v*100)); +} + +void HotPixelsTool::slotLoadingComplete() +{ + EditorToolIface::editorToolIface()->setToolStopProgress(); +} + +void HotPixelsTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("hotpixels Tool"); + config->writeEntry("Last Black Frame File", m_blackFrameURL.url()); + config->writeEntry("Filter Method", m_filterMethodCombo->currentItem()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void HotPixelsTool::slotResetSettings() +{ + m_filterMethodCombo->blockSignals(true); + m_filterMethodCombo->slotReset(); + m_filterMethodCombo->blockSignals(false); +} + +void HotPixelsTool::slotAddBlackFrame() +{ + KURL url = ImageDialog::getImageURL(kapp->activeWindow(), m_blackFrameURL, i18n("Select Black Frame Image")); + + if (!url.isEmpty()) + { + // Load the selected file and insert into the list. + + m_blackFrameURL = url; + m_blackFrameListView->clear(); + BlackFrameListViewItem *item = new BlackFrameListViewItem(m_blackFrameListView, m_blackFrameURL); + + connect(item, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SLOT(slotLoadingProgress(float))); + + connect(item, TQ_SIGNAL(signalLoadingComplete()), + this, TQ_SLOT(slotLoadingComplete())); + } +} + +void HotPixelsTool::renderingFinished() +{ + m_filterMethodCombo->setEnabled(true); + m_blackFrameListView->setEnabled(true); +} + +void HotPixelsTool::prepareEffect() +{ + m_filterMethodCombo->setEnabled(false); + m_blackFrameListView->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + int interpolationMethod = m_filterMethodCombo->currentItem(); + + TQValueList hotPixelsRegion; + TQRect area = m_previewWidget->getOriginalImageRegionToRender(); + TQValueList::Iterator end(m_hotPixelsList.end()); + + for (TQValueList::Iterator it = m_hotPixelsList.begin() ; it != end ; ++it ) + { + HotPixel hp = (*it); + + if ( area.contains( hp.rect ) ) + { + hp.rect.moveTopLeft(TQPoint( hp.rect.x()-area.x(), hp.rect.y()-area.y() )); + hotPixelsRegion.append(hp); + } + } + + setFilter(dynamic_cast(new HotPixelFixer(&image, this, hotPixelsRegion, interpolationMethod))); +} + +void HotPixelsTool::prepareFinal() +{ + m_filterMethodCombo->setEnabled(false); + m_blackFrameListView->setEnabled(false); + + int interpolationMethod = m_filterMethodCombo->currentItem(); + + ImageIface iface(0, 0); + setFilter(dynamic_cast(new HotPixelFixer(iface.getOriginalImg(), this,m_hotPixelsList,interpolationMethod))); +} + +void HotPixelsTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void HotPixelsTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Hot Pixels Correction"), filter()->getTargetImage().bits()); +} + +void HotPixelsTool::slotBlackFrame(TQValueList hpList, const KURL& blackFrameURL) +{ + m_blackFrameURL = blackFrameURL; + m_hotPixelsList = hpList; + + TQPointArray pointList(m_hotPixelsList.size()); + TQValueList ::Iterator it; + int i = 0; + TQValueList ::Iterator end(m_hotPixelsList.end()); + + for (it = m_hotPixelsList.begin() ; it != end ; ++it, i++) + pointList.setPoint(i, (*it).rect.center()); + + m_previewWidget->setPanIconHighLightPoints(pointList); + + slotEffect(); +} + +} // NameSpace DigikamHotPixelsImagesPlugin diff --git a/src/imageplugins/hotpixels/hotpixelstool.h b/src/imageplugins/hotpixels/hotpixelstool.h new file mode 100644 index 00000000..727c6387 --- /dev/null +++ b/src/imageplugins/hotpixels/hotpixelstool.h @@ -0,0 +1,113 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_HOTPIXELS_H +#define IMAGEEFFECT_HOTPIXELS_H + +#define MAX_PIXEL_DEPTH 4 + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +// Local includes. + +#include "hotpixelfixer.h" + +class TQPushButton; + +namespace KDcrawIface +{ +class RComboBox; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamHotPixelsImagesPlugin +{ + +class BlackFrameListView; + +class HotPixelsTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + HotPixelsTool(TQObject *parent); + ~HotPixelsTool(); + +private slots: + + void slotBlackFrame(TQValueList hpList, const KURL& blackFrameURL); + void slotResetSettings(); + void slotAddBlackFrame(); + void slotLoadingProgress(float); + void slotLoadingComplete(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQPushButton *m_blackFrameButton; + + TQValueList m_hotPixelsList; + + KURL m_blackFrameURL; + + BlackFrameListView *m_blackFrameListView; + + KDcrawIface::RComboBox *m_filterMethodCombo; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif /* IMAGEEFFECT_HOTPIXELS_H */ diff --git a/src/imageplugins/hotpixels/imageeffect_hotpixels.cpp b/src/imageplugins/hotpixels/imageeffect_hotpixels.cpp new file mode 100644 index 00000000..67e9a1b2 --- /dev/null +++ b/src/imageplugins/hotpixels/imageeffect_hotpixels.cpp @@ -0,0 +1,279 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagedialog.h" +#include "blackframelistview.h" +#include "imageeffect_hotpixels.h" +#include "imageeffect_hotpixels.moc" + +namespace DigikamHotPixelsImagesPlugin +{ + +ImageEffect_HotPixels::ImageEffect_HotPixels(TQWidget* parent) + : CtrlPanelDlg(parent, i18n("Hot Pixels Correction"), + "hotpixels", false, false, false, + Digikam::ImagePannelWidget::SeparateViewDuplicate) +{ + // No need Abort button action. + showButton(User1, false); + + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Hot Pixels Correction"), + digikam_version, + I18N_NOOP("A digiKam image plugin for fixing dots produced by " + "hot/stuck/dead pixels from a CCD."), + TDEAboutData::License_GPL, + "(c) 2005-2006, Unai Garro\n(c) 2005-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Unai Garro", I18N_NOOP("Author and maintainer"), + "ugarro at sourceforge dot net"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Developer"), + "caulier dot gilles at gmail dot com"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings, 3, 2, 0, spacingHint()); + + TQLabel *filterMethodLabel = new TQLabel(i18n("Filter:"), gboxSettings); + m_filterMethodCombo = new TQComboBox(gboxSettings); + m_filterMethodCombo->insertItem(i18n("Average")); + m_filterMethodCombo->insertItem(i18n("Linear")); + m_filterMethodCombo->insertItem(i18n("Quadratic")); + m_filterMethodCombo->insertItem(i18n("Cubic")); + + m_blackFrameButton = new TQPushButton(i18n("Black Frame..."), gboxSettings); + setButtonWhatsThis( Apply, i18n("

    Use this button to add a new black frame file which will " + "be used by the hot pixels removal filter.") ); + + m_blackFrameListView = new BlackFrameListView(gboxSettings); + m_progressBar = new KProgress(100, gboxSettings); + m_progressBar->setValue(0); + m_progressBar->hide(); + + gridSettings->addMultiCellWidget(filterMethodLabel, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_filterMethodCombo, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(m_blackFrameButton, 0, 0, 2, 2); + gridSettings->addMultiCellWidget(m_blackFrameListView, 1, 2, 0, 2); + gridSettings->addMultiCellWidget(m_progressBar, 3, 3, 0, 2); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_filterMethodCombo, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_blackFrameButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAddBlackFrame())); + + connect(m_blackFrameListView, TQ_SIGNAL(blackFrameSelected(TQValueList, const KURL&)), + this, TQ_SLOT(slotBlackFrame(TQValueList, const KURL&))); +} + +ImageEffect_HotPixels::~ImageEffect_HotPixels() +{ +} + +void ImageEffect_HotPixels::readUserSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("hotpixels Tool Dialog"); + m_blackFrameURL = KURL(config->readEntry("Last Black Frame File", TQString())); + m_filterMethodCombo->setCurrentItem(config->readNumEntry("Filter Method", + HotPixelFixer::QUADRATIC_INTERPOLATION)); + + if (m_blackFrameURL.isValid()) + { + BlackFrameListViewItem *item = new BlackFrameListViewItem(m_blackFrameListView, m_blackFrameURL); + + connect(item, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SLOT(slotLoadingProgress(float))); + + connect(item, TQ_SIGNAL(signalLoadingComplete()), + this, TQ_SLOT(slotLoadingComplete())); + } +} + +void ImageEffect_HotPixels::writeUserSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("hotpixels Tool Dialog"); + config->writeEntry("Last Black Frame File", m_blackFrameURL.url()); + config->writeEntry("Filter Method", m_filterMethodCombo->currentItem()); + config->sync(); +} + +void ImageEffect_HotPixels::resetValues() +{ + m_filterMethodCombo->blockSignals(true); + m_filterMethodCombo->setCurrentItem(HotPixelFixer::QUADRATIC_INTERPOLATION); + m_filterMethodCombo->blockSignals(false); +} + +void ImageEffect_HotPixels::slotAddBlackFrame() +{ + KURL url = Digikam::ImageDialog::getImageURL(this, m_blackFrameURL, + i18n("Select Black Frame Image")); + + if (!url.isEmpty()) + { + // Load the selected file and insert into the list. + + m_blackFrameURL = url; + m_blackFrameListView->clear(); + BlackFrameListViewItem *item = new BlackFrameListViewItem(m_blackFrameListView, m_blackFrameURL); + + connect(item, TQ_SIGNAL(signalLoadingProgress(float)), + this, TQ_SLOT(slotLoadingProgress(float))); + + connect(item, TQ_SIGNAL(signalLoadingComplete()), + this, TQ_SLOT(slotLoadingComplete())); + } +} + +void ImageEffect_HotPixels::slotLoadingProgress(float v) +{ + m_progressBar->show(); + m_progressBar->setValue((int)(v*100)); +} + +void ImageEffect_HotPixels::slotLoadingComplete() +{ + m_progressBar->hide(); +} + +void ImageEffect_HotPixels::renderingFinished() +{ + m_filterMethodCombo->setEnabled(true); + m_blackFrameListView->setEnabled(true); + enableButton(Apply, true); +} + +void ImageEffect_HotPixels::prepareEffect() +{ + m_filterMethodCombo->setEnabled(false); + m_blackFrameListView->setEnabled(false); + enableButton(Apply, false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + int interpolationMethod = m_filterMethodCombo->currentItem(); + + TQValueList hotPixelsRegion; + TQRect area = m_imagePreviewWidget->getOriginalImageRegionToRender(); + TQValueList::Iterator end(m_hotPixelsList.end()); + + for (TQValueList::Iterator it = m_hotPixelsList.begin() ; it != end ; ++it ) + { + HotPixel hp = (*it); + + if ( area.contains( hp.rect ) ) + { + hp.rect.moveTopLeft(TQPoint( hp.rect.x()-area.x(), hp.rect.y()-area.y() )); + hotPixelsRegion.append(hp); + } + } + + m_threadedFilter = dynamic_cast( + new HotPixelFixer(&image, this, hotPixelsRegion, interpolationMethod)); +} + +void ImageEffect_HotPixels::prepareFinal() +{ + m_filterMethodCombo->setEnabled(false); + m_blackFrameListView->setEnabled(false); + enableButton(Apply, false); + + int interpolationMethod = m_filterMethodCombo->currentItem(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast( + new HotPixelFixer(iface.getOriginalImg(), this,m_hotPixelsList,interpolationMethod)); +} + +void ImageEffect_HotPixels::putPreviewData() +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_HotPixels::putFinalData() +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Hot Pixels Correction"), m_threadedFilter->getTargetImage().bits()); +} + +void ImageEffect_HotPixels::slotBlackFrame(TQValueList hpList, const KURL& blackFrameURL) +{ + m_blackFrameURL = blackFrameURL; + m_hotPixelsList = hpList; + + TQPointArray pointList(m_hotPixelsList.size()); + TQValueList ::Iterator it; + int i = 0; + TQValueList ::Iterator end(m_hotPixelsList.end()); + + for (it = m_hotPixelsList.begin() ; it != end ; ++it, i++) + pointList.setPoint(i, (*it).rect.center()); + + m_imagePreviewWidget->setPanIconHighLightPoints(pointList); + + slotEffect(); +} + +} // NameSpace DigikamHotPixelsImagesPlugin diff --git a/src/imageplugins/hotpixels/imageeffect_hotpixels.h b/src/imageplugins/hotpixels/imageeffect_hotpixels.h new file mode 100644 index 00000000..4a8b0868 --- /dev/null +++ b/src/imageplugins/hotpixels/imageeffect_hotpixels.h @@ -0,0 +1,104 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_HOTPIXELS_H +#define IMAGEEFFECT_HOTPIXELS_H + +#define MAX_PIXEL_DEPTH 4 + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +// Local includes. + +#include "hotpixelfixer.h" + +class TQComboBox; +class TQPushButton; + +class KProgress; + +namespace DigikamHotPixelsImagesPlugin +{ + +class BlackFrameListView; + +class ImageEffect_HotPixels : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_HotPixels(TQWidget *parent); + ~ImageEffect_HotPixels(); + +private slots: + + void slotLoadingProgress(float v); + void slotLoadingComplete(); + + void slotBlackFrame(TQValueList hpList, const KURL& blackFrameURL); + void slotAddBlackFrame(); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void abortPreview(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQComboBox *m_filterMethodCombo; + + TQPushButton *m_blackFrameButton; + + TQValueList m_hotPixelsList; + + KURL m_blackFrameURL; + + KProgress *m_progressBar; + + BlackFrameListView *m_blackFrameListView; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif /* IMAGEEFFECT_HOTPIXELS_H */ diff --git a/src/imageplugins/hotpixels/imageplugin_hotpixels.cpp b/src/imageplugins/hotpixels/imageplugin_hotpixels.cpp new file mode 100644 index 00000000..864b59dc --- /dev/null +++ b/src/imageplugins/hotpixels/imageplugin_hotpixels.cpp @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "hotpixelstool.h" +#include "imageplugin_hotpixels.h" +#include "imageplugin_hotpixels.moc" + +using namespace DigikamHotPixelsImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_hotpixels, + KGenericFactory("digikamimageplugin_hotpixels")); + +ImagePlugin_HotPixels::ImagePlugin_HotPixels(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_HotPixels") +{ + m_hotpixelsAction = new TDEAction(i18n("Hot Pixels..."), "hotpixels", 0, + this, TQ_SLOT(slotHotPixels()), + actionCollection(), "imageplugin_hotpixels"); + + setXMLFile("digikamimageplugin_hotpixels_ui.rc"); + + DDebug() << "ImagePlugin_HotPixels plugin loaded" << endl; +} + +ImagePlugin_HotPixels::~ImagePlugin_HotPixels() +{ +} + +void ImagePlugin_HotPixels::setEnabledActions(bool enable) +{ + m_hotpixelsAction->setEnabled(enable); +} + +void ImagePlugin_HotPixels::slotHotPixels() +{ + HotPixelsTool *tool = new HotPixelsTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/hotpixels/imageplugin_hotpixels.h b/src/imageplugins/hotpixels/imageplugin_hotpixels.h new file mode 100644 index 00000000..c639b830 --- /dev/null +++ b/src/imageplugins/hotpixels/imageplugin_hotpixels.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a digiKam image plugin for fixing dots produced by + * hot/stuck/dead pixels from a CCD. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_HOTPIXELS_H +#define IMAGEPLUGIN_HOTPIXELS_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_HotPixels : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_HotPixels(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_HotPixels(); + + void setEnabledActions(bool enable); + +private slots: + + void slotHotPixels(); + +private: + + TDEAction *m_hotpixelsAction; +}; + +#endif /* IMAGEPLUGIN_HOTPIXELS_H */ diff --git a/src/imageplugins/hotpixels/weights.cpp b/src/imageplugins/hotpixels/weights.cpp new file mode 100644 index 00000000..e0d3f246 --- /dev/null +++ b/src/imageplugins/hotpixels/weights.cpp @@ -0,0 +1,283 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a class to calculate filter weights + * + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// Local includes. + +#include "weights.h" + +namespace DigikamHotPixelsImagesPlugin +{ + +Weights::Weights(const Weights &w) +{ + (*this) = w; +} + +void Weights::operator=(const Weights &w) +{ + mHeight=w.height(); + mWidth=w.width(); + mPositions=(w.positions()); + mCoefficientNumber=w.coefficientNumber(); + mTwoDim=w.twoDim(); + mPolynomeOrder=w.polynomeOrder(); + + // Allocate memory and copy weights + // if the original one was calculated + + if (!w.weightMatrices()) return; + else + { + double*** origMatrices=w.weightMatrices(); + mWeightMatrices = new double**[mPositions.count()]; //allocate mPositions.count() matrices + + for (uint i=0; i= iHeight && -x + y - iHeight < iPolynomeOrder + 1) + || (x >= iWidth && y < 0 && x - y - iWidth < iPolynomeOrder + 1) + || (x >= iWidth && y >= iHeight && x + y - iWidth - iHeight < iPolynomeOrder) + || (x < 0 && y >= 0 && y < iHeight) || (x >= iWidth && y >= 0 && y < iHeight) + || (y < 0 && x >= 0 && x < iWidth ) || (y >= iHeight && x >= 0 && x < iWidth)) + { + TQPoint position(x,y); + mPositions.append(position); + } + } + } + } + else + { + // In the one-dimensional case, only the y coordinate and y size is used. */ + + for (y = -mPolynomeOrder; y < 0; ++y) + { + TQPoint position(0,y); + mPositions.append(position); + } + + for (y = (int) height(); y < (int) height() + (int) mPolynomeOrder; ++y) + { + TQPoint position(0,y); + mPositions.append(position); + } + } + + // Allocate memory. + + matrix = new double[mCoefficientNumber*mCoefficientNumber]; + vector0 = new double[mPositions.count() * mCoefficientNumber]; + vector1 = new double[mPositions.count() * mCoefficientNumber]; + + // Calculate coefficient matrix and vectors + + for (iy = 0; iy < mCoefficientNumber; ++iy) + { + for (ix = 0; ix < mCoefficientNumber; ++ix) + matrix [iy*mCoefficientNumber+ix] = 0.0; + + for (j = 0; j < mPositions.count(); ++j) + { + vector0 [iy * mPositions.count() + j] = polyTerm (iy, mPositions [j].x(), + mPositions [j].y(), mPolynomeOrder); + + for (ix = 0; ix < mCoefficientNumber; ++ix) + matrix [iy * mCoefficientNumber + ix] += (vector0 [iy * mPositions.count() + j] + * polyTerm (ix, mPositions [j].x(), mPositions[j].y(), mPolynomeOrder)); + } + } + + // Invert matrix. + + matrixInv (matrix, mCoefficientNumber); + + // Multiply inverse matrix with vector. + + for (iy = 0; iy < mCoefficientNumber; ++iy) + for (j = 0; j < mPositions.count(); ++j) + { + vector1 [iy * mPositions.count() + j] = 0.0; + + for (ix = 0; ix < mCoefficientNumber; ++ix) + vector1 [iy * mPositions.count() + j] += matrix [iy * mCoefficientNumber + ix] + * vector0 [ix * mPositions.count() + j]; + } + + // Store weights + + mWeightMatrices = new double**[mPositions.count()]; //allocate mPositions.count() matrices + + for (i=0; i 0; --iy) + { + for (j = 0; j < iy; ++j) + { + const double factor = b [j * size + iy] / b [iy * size + iy]; + + for (ix = 0; ix < size; ++ix) + a [j * size + ix] -= factor * a [iy * size + ix]; + } + } + + // Convert matrix to unit matrix. + + for (iy = 0; iy < size; ++iy) + for (ix = 0; ix < size; ++ix) + a [iy * size + ix] /= b [iy * size + iy]; + + delete [] b; +} + +// Calculates one term of the polynomial +double Weights::polyTerm (const size_t i_coeff, const int x, const int y, const int poly_order) +{ + const size_t x_power = i_coeff / ((size_t)poly_order + 1); + const size_t y_power = i_coeff % ((size_t)poly_order + 1); + int result; + size_t i; + + result = 1; + + for (i = 0; i < x_power; ++i) + result *= x; + + for (i = 0; i < y_power; ++i) + result *= y; + + return (double)result; +} + +} // NameSpace DigikamHotPixelsImagesPlugin + diff --git a/src/imageplugins/hotpixels/weights.h b/src/imageplugins/hotpixels/weights.h new file mode 100644 index 00000000..b82fde89 --- /dev/null +++ b/src/imageplugins/hotpixels/weights.h @@ -0,0 +1,90 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-27 + * Description : a class to calculate filter weights + * + * Copyright (C) 2005-2006 by Unai Garro + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef WEIGHTS_H +#define WEIGHTS_H + +// TQt includes. + +#include +#include + +namespace DigikamHotPixelsImagesPlugin +{ + +class Weights +{ +public: + + Weights(){}; + Weights(const Weights &w); + void operator=(const Weights &w); + + ~Weights() + { + if (!mWeightMatrices) return; + for (unsigned int i=0; i positions() const { return mPositions; }; + +protected: + + int coefficientNumber() const { return mCoefficientNumber; }; + + double*** weightMatrices() const { return mWeightMatrices; }; + +private: + + double polyTerm (const size_t i_coeff, const int x, const int y, const int poly_order); + void matrixInv (double *const a, const size_t size); + +private: + + unsigned int mHeight,mWidth; + unsigned int mCoefficientNumber; + bool mTwoDim; + unsigned int mPolynomeOrder; + double *** mWeightMatrices; //Stores a list of weight matrices + TQValueList mPositions; +}; + +} // NameSpace DigikamHotPixelsImagesPlugin + +#endif // WEIGHTS_H diff --git a/src/imageplugins/infrared/Makefile.am b/src/imageplugins/infrared/Makefile.am new file mode 100644 index 00000000..1e9cd294 --- /dev/null +++ b/src/imageplugins/infrared/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_infrared_la_SOURCES = imageplugin_infrared.cpp \ + infraredtool.cpp infrared.cpp + +digikamimageplugin_infrared_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_infrared_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_infrared.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_infrared.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_infrared_ui.rc + diff --git a/src/imageplugins/infrared/digikamimageplugin_infrared.desktop b/src/imageplugins/infrared/digikamimageplugin_infrared.desktop new file mode 100644 index 00000000..716de474 --- /dev/null +++ b/src/imageplugins/infrared/digikamimageplugin_infrared.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_Infrared +Name[bg]=ПриÑтавка за Ñнимки - Инфрачервен филм +Name[da]=Plugin for infrarødt +Name[el]=ΠÏόσθετοΕικόνας_ΥπέÏυθÏο +Name[fi]=Infrapuna +Name[hr]=Infracrveno +Name[it]=PluginImmagini_Infrarosso +Name[ms]=ImagePlugin_Inframerah +Name[nl]=Afbeeldingsplugin_Infrarood +Name[sr]=Инфрацрвено +Name[sr@Latn]=Infracrveno +Name[sv]=Insticksprogram för infrarött +Name[tr]=ResimEklentisi_Kızılötesi +Name[xx]=xxImagePlugin_Infraredxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Simulate infrared film plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наподобÑване на Ñнимка Ñ Ð¸Ð½Ñ„Ñ€Ð°Ñ‡ÐµÑ€Ð²ÐµÐ½ филм +Comment[ca]=Connector pel digiKam de simulació de pel·lícula d'infraroigs +Comment[da]=Digikam plugin til at simulere infrarød film +Comment[de]=digiKam-Modul für die Simulation eines Infrarot-Filmes +Comment[el]=ΠÏόσθετο εξομοίωσης υπέÏυθÏου φιλμ για το digiKam +Comment[es]=Plugin para digiKam para simular la película infrarroja +Comment[et]=DigiKami infrapunafilmi matkimise plugin +Comment[fa]=شبیه‌سازی وصلۀ Ùیلم مادون قرمز برای digiKam +Comment[fi]=Jäljittelee infrapunafilmiä +Comment[gl]=Un plugin de digiKam para simulazón de infravermellos +Comment[hr]=digiKam dodatak za oponaÅ¡anje IC filma +Comment[is]=Ãforrit fyrir digiKam sem líkir eftir innrauðri filmu +Comment[it]=Plugin di simulazione di pellicola infrarossa per digiKam +Comment[ja]=digiKam 赤外線フィルム効果プラグイン +Comment[nds]=digiKam-Moduul för't Simuleren vun Infraroot-Filmen +Comment[nl]=Digikam-plugin voor infraroodfilm-effect +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਸਿਮੂਲੇਸ਼ਨ ਇੰਫਰਾਰੈੱਡ ਫਿਲਮ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam symulujÄ…ca efekt podczerwonej kliszy +Comment[pt]=Um 'plugin' do digiKam para simulação de infravermelhos +Comment[pt_BR]=Um 'plugin' do digiKam para simulação de infravermelhos +Comment[ru]=Модуль имитирующий инфракраÑное фильм Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre napodobenie infraÄerveného filmu +Comment[sr]=digiKam-ов прикључак који Ñимулира инфрацрвени филм +Comment[sr@Latn]=digiKam-ov prikljuÄak koji simulira infracrveni film +Comment[sv]=Digikam insticksprogram för att simulera infraröd film +Comment[tr]=digiKam için kızılötesi film benzetme eklentisi +Comment[uk]=Втулок ÑимулÑції інфрачервоного фільму Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung mô phá»ng phim ảnh hồng ngoại cho digiKam +Comment[xx]=xxSimulate infrared film plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_infrared +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/infrared/digikamimageplugin_infrared_ui.rc b/src/imageplugins/infrared/digikamimageplugin_infrared_ui.rc new file mode 100644 index 00000000..1df83fa6 --- /dev/null +++ b/src/imageplugins/infrared/digikamimageplugin_infrared_ui.rc @@ -0,0 +1,19 @@ + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/infrared/imageeffect_infrared.cpp b/src/imageplugins/infrared/imageeffect_infrared.cpp new file mode 100644 index 00000000..f8069255 --- /dev/null +++ b/src/imageplugins/infrared/imageeffect_infrared.cpp @@ -0,0 +1,227 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "infrared.h" +#include "imageeffect_infrared.h" +#include "imageeffect_infrared.moc" + +namespace DigikamInfraredImagesPlugin +{ + +ImageEffect_Infrared::ImageEffect_Infrared(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Simulate Infrared Film to Photograph"), + "infrared", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Infrared Film"), + digikam_version, + I18N_NOOP("A digiKam image plugin to simulate infrared film."), + TDEAboutData::License_GPL, + "(c) 2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 2, 1, 0, spacingHint()); + TQLabel *label1 = new TQLabel(i18n("Sensitivity (ISO):"), gboxSettings); + + m_sensibilitySlider = new TQSlider(1, 25, 1, 1, TQt::Horizontal, gboxSettings); + m_sensibilitySlider->setTracking ( false ); + m_sensibilitySlider->setTickInterval(1); + m_sensibilitySlider->setTickmarks(TQSlider::Below); + + m_sensibilityLCDValue = new TQLCDNumber (4, gboxSettings); + m_sensibilityLCDValue->setSegmentStyle ( TQLCDNumber::Flat ); + m_sensibilityLCDValue->display( TQString::number(200) ); + whatsThis = i18n("

    Set here the ISO-sensitivity of the simulated infrared film. " + "Increasing this value will increase the proportion of green color in the mix. " + "It will also increase the halo effect on the hightlights, and the film " + "graininess (if that box is checked).

    " + "

    Note: to simulate an Ilford SFX200 infrared film, use a sensitivity excursion of 200 to 800. " + "A sensitivity over 800 simulates Kodak HIE high-speed infrared film. This last one creates a more " + "dramatic photographic style.

    "); + + TQWhatsThis::add( m_sensibilityLCDValue, whatsThis); + TQWhatsThis::add( m_sensibilitySlider, whatsThis); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_sensibilitySlider, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(m_sensibilityLCDValue, 1, 1, 1, 1); + + // ------------------------------------------------------------- + + m_addFilmGrain = new TQCheckBox( i18n("Add film grain"), gboxSettings); + m_addFilmGrain->setChecked( true ); + TQWhatsThis::add( m_addFilmGrain, i18n("

    This option adds infrared film grain to " + "the image depending on ISO-sensitivity.")); + gridSettings->addMultiCellWidget(m_addFilmGrain, 2, 2, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer()) ); + + // this connection is necessary to change the LCD display when + // the value is changed by single clicking on the slider + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_sensibilitySlider, TQ_SIGNAL(sliderMoved(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_addFilmGrain, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect()) ); +} + +ImageEffect_Infrared::~ImageEffect_Infrared() +{ +} + +void ImageEffect_Infrared::renderingFinished() +{ + m_sensibilitySlider->setEnabled(true); + m_addFilmGrain->setEnabled(true); +} + +void ImageEffect_Infrared::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("infrared Tool Dialog"); + m_sensibilitySlider->blockSignals(true); + m_addFilmGrain->blockSignals(true); + m_sensibilitySlider->setValue(config->readNumEntry("SensitivityAjustment", 1)); + m_addFilmGrain->setChecked(config->readBoolEntry("AddFilmGrain", false)); + m_sensibilitySlider->blockSignals(false); + m_addFilmGrain->blockSignals(false); + slotSliderMoved(m_sensibilitySlider->value()); +} + +void ImageEffect_Infrared::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("infrared Tool Dialog"); + config->writeEntry("SensitivityAjustment", m_sensibilitySlider->value()); + config->writeEntry("AddFilmGrain", m_addFilmGrain->isChecked()); + config->sync(); +} + +void ImageEffect_Infrared::resetValues() +{ + m_sensibilitySlider->blockSignals(true); + m_addFilmGrain->blockSignals(true); + m_sensibilitySlider->setValue(1); + m_addFilmGrain->setChecked(false); + m_sensibilitySlider->blockSignals(false); + m_addFilmGrain->blockSignals(false); +} + +void ImageEffect_Infrared::slotSliderMoved(int v) +{ + m_sensibilityLCDValue->display( TQString::number(100 + 100 * v) ); +} + +void ImageEffect_Infrared::prepareEffect() +{ + m_addFilmGrain->setEnabled(false); + m_sensibilitySlider->setEnabled(false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + int s = 100 + 100 * m_sensibilitySlider->value(); + bool g = m_addFilmGrain->isChecked(); + + m_threadedFilter = dynamic_cast( + new Infrared(&image, this, s, g)); +} + +void ImageEffect_Infrared::prepareFinal() +{ + m_addFilmGrain->setEnabled(false); + m_sensibilitySlider->setEnabled(false); + + int s = 100 + 100 * m_sensibilitySlider->value(); + bool g = m_addFilmGrain->isChecked(); + + Digikam::ImageIface iface(0, 0); + + m_threadedFilter = dynamic_cast( + new Infrared(iface.getOriginalImg(), this, s, g)); +} + +void ImageEffect_Infrared::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_Infrared::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Infrared"), m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamInfraredImagesPlugin + diff --git a/src/imageplugins/infrared/imageeffect_infrared.h b/src/imageplugins/infrared/imageeffect_infrared.h new file mode 100644 index 00000000..d6c28237 --- /dev/null +++ b/src/imageplugins/infrared/imageeffect_infrared.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_INFRARED_H +#define IMAGEEFFECT_INFRARED_H + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class TQSlider; +class TQLCDNumber; +class TQCheckBox; + +namespace DigikamInfraredImagesPlugin +{ + +class ImageEffect_Infrared : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Infrared(TQWidget* parent); + ~ImageEffect_Infrared(); + +private slots: + + void slotSliderMoved(int); + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQCheckBox *m_addFilmGrain; + + TQSlider *m_sensibilitySlider; + + TQLCDNumber *m_sensibilityLCDValue; +}; + +} // NameSpace DigikamInfraredImagesPlugin + +#endif /* IMAGEEFFECT_INFRARED_H */ diff --git a/src/imageplugins/infrared/imageplugin_infrared.cpp b/src/imageplugins/infrared/imageplugin_infrared.cpp new file mode 100644 index 00000000..4b845908 --- /dev/null +++ b/src/imageplugins/infrared/imageplugin_infrared.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "infraredtool.h" +#include "imageplugin_infrared.h" +#include "imageplugin_infrared.moc" + +using namespace DigikamInfraredImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_infrared, + KGenericFactory("digikamimageplugin_infrared")); + +ImagePlugin_Infrared::ImagePlugin_Infrared(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Infrared") +{ + m_infraredAction = new TDEAction(i18n("Infrared Film..."), "infrared", 0, + this, TQ_SLOT(slotInfrared()), + actionCollection(), "imageplugin_infrared"); + + setXMLFile( "digikamimageplugin_infrared_ui.rc" ); + + DDebug() << "ImagePlugin_Infrared plugin loaded" << endl; +} + +ImagePlugin_Infrared::~ImagePlugin_Infrared() +{ +} + +void ImagePlugin_Infrared::setEnabledActions(bool enable) +{ + m_infraredAction->setEnabled(enable); +} + +void ImagePlugin_Infrared::slotInfrared() +{ + InfraredTool *tool = new InfraredTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/infrared/imageplugin_infrared.h b/src/imageplugins/infrared/imageplugin_infrared.h new file mode 100644 index 00000000..7b6fa12a --- /dev/null +++ b/src/imageplugins/infrared/imageplugin_infrared.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_INFRARED_H +#define IMAGEPLUGIN_INFRARED_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Infrared : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Infrared(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Infrared(); + + void setEnabledActions(bool enable); + +private slots: + + void slotInfrared(); + +private: + + TDEAction *m_infraredAction; +}; + +#endif /* IMAGEPLUGIN_INFRARED_H */ diff --git a/src/imageplugins/infrared/infrared.cpp b/src/imageplugins/infrared/infrared.cpp new file mode 100644 index 00000000..fa2983d5 --- /dev/null +++ b/src/imageplugins/infrared/infrared.cpp @@ -0,0 +1,367 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Infrared threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimggaussianblur.h" +#include "imagecurves.h" +#include "imagehistogram.h" +#include "dimgimagefilters.h" +#include "infrared.h" + +namespace DigikamInfraredImagesPlugin +{ + +Infrared::Infrared(Digikam::DImg *orgImage, TQObject *parent, int sensibility, bool grain) + : Digikam::DImgThreadedFilter(orgImage, parent, "Infrared") +{ + m_sensibility = sensibility; + m_grain = grain; + initFilter(); +} + +void Infrared::filterImage(void) +{ + infraredImage(&m_orgImage, m_sensibility, m_grain); +} + +// This method is based on the Simulate Infrared Film tutorial from GimpGuru.org web site +// available at this url : http://www.gimpguru.org/Tutorials/SimulatedInfrared/ + +inline static int intMult8(uint a, uint b) +{ + uint t = a * b + 0x80; + return ((t >> 8) + t) >> 8; +} + +inline static int intMult16(uint a, uint b) +{ + uint t = a * b + 0x8000; + return ((t >> 16) + t) >> 16; +} + +/* More info about IR film can be seen at this url : + +http://www.pauck.de/marco/photo/infrared/comparison_of_films/comparison_of_films.html +*/ + +void Infrared::infraredImage(Digikam::DImg *orgImage, int Sensibility, bool Grain) +{ + // Sensibility: 200..2600 + + if (Sensibility <= 0) return; + + int Width = orgImage->width(); + int Height = orgImage->height(); + int bytesDepth = orgImage->bytesDepth(); + uint numBytes = orgImage->numBytes(); + bool sixteenBit = orgImage->sixteenBit(); + uchar* data = orgImage->bits(); + + // Infrared film variables depending on Sensibility. + // We can reproduce famous Ilford SFX200 infrared film + // http://www.ilford.com/html/us_english/prod_html/sfx200/sfx200.html + // This film have a sensibility escursion from 200 to 800 ISO. + // Over 800 ISO, we reproduce The Kodak HIE hight speed infrared film. + + // Infrared film grain. + int Noise = (Sensibility + 3000) / 10; + if (sixteenBit) + Noise = (Noise + 1) * 256 - 1; + + int blurRadius = (int)((Sensibility / 200.0) + 1.0); // Gaussian blur infrared hightlight effect + // [2 to 5]. + float greenBoost = 2.1 - (Sensibility / 2000.0); // Infrared green color boost [1.7 to 2.0]. + + int nRand, offset, progress; + + uchar* pBWBits = 0; // Black and White conversion. + uchar* pBWBlurBits = 0; // Black and White with blur. + uchar* pGrainBits = 0; // Grain blured without curves adjustment. + uchar* pMaskBits = 0; // Grain mask with curves adjustment. + uchar* pOverlayBits = 0; // Overlay to merge with original converted in gray scale. + uchar* pOutBits = m_destImage.bits(); // Destination image with merged grain mask and original. + + Digikam::DColor bwData, bwBlurData, grainData, maskData, overData, outData; + + //------------------------------------------ + // 1 - Create GrayScale green boosted image. + //------------------------------------------ + + // Convert to gray scale with boosting Green channel. + // Infrared film increase green color. + + Digikam::DImg BWImage(Width, Height, sixteenBit); // Black and White conversion. + pBWBits = BWImage.bits(); + memcpy (pBWBits, data, numBytes); + + Digikam::DImgImageFilters().channelMixerImage(pBWBits, Width, Height, sixteenBit, // Image data. + true, // Preserve luminosity. + true, // Monochrome. + 0.4, greenBoost, -0.8, // Red channel gains. + 0.0, 1.0, 0.0, // Green channel gains (not used). + 0.0, 0.0, 1.0); // Blue channel gains (not used). + postProgress( 10 ); + if (m_cancel) + { + return; + } + + // Apply a Gaussian blur to the black and white image. + // This way simulate Infrared film dispersion for the highlights. + + Digikam::DImg BWBlurImage(Width, Height, sixteenBit); + pBWBlurBits = BWBlurImage.bits(); + + Digikam::DImgGaussianBlur(this, BWImage, BWBlurImage, 10, 20, blurRadius); + + if (m_cancel) + { + return; + } + + //----------------------------------------------------------------- + // 2 - Create Gaussian blured averlay mask with grain if necessary. + //----------------------------------------------------------------- + + + if (Grain) + { + + // Create gray grain mask. + + TQDateTime dt = TQDateTime::currentDateTime(); + TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); + uint seed = ((uint) dt.secsTo(Y2000)); + + pGrainBits = new uchar[numBytes]; // Grain blured without curves adjustment. + uchar *ptr; + int component; + grainData.setSixteenBit(sixteenBit); + + for (int x = 0; !m_cancel && x < Width; x++) + { + for (int y = 0; !m_cancel && y < Height; y++) + { + ptr = pGrainBits + x*bytesDepth + (y*Width*bytesDepth); + + nRand = (rand_r(&seed) % Noise) - (Noise / 2); + if (sixteenBit) + component = CLAMP(32768 + nRand, 0, 65535); + else + component = CLAMP(128 + nRand, 0, 255); + + grainData.setRed (component); + grainData.setGreen(component); + grainData.setBlue (component); + grainData.setAlpha(0); + + grainData.setPixel(ptr); + } + + // Update progress bar in dialog. + progress = (int) (30.0 + ((double)x * 10.0) / Width); + + if (progress%5 == 0) + postProgress( progress ); + } + + // Smooth grain mask using gaussian blur. + + Digikam::DImgImageFilters().gaussianBlurImage(pGrainBits, Width, Height, sixteenBit, 1); + + postProgress( 40 ); + if (m_cancel) + { + delete [] pGrainBits; + return; + } + } + + postProgress( 50 ); + if (m_cancel) + { + delete [] pGrainBits; + return; + } + + // Normally, film grain tends to be most noticeable in the midtones, and much less + // so in the shadows and highlights. Adjust histogram curve to adjust grain like this. + + if (Grain) + { + Digikam::ImageCurves *grainCurves = new Digikam::ImageCurves(sixteenBit); + pMaskBits = new uchar[numBytes]; // Grain mask with curves adjustment. + + // We modify only global luminosity of the grain. + if (sixteenBit) + { + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 8, TQPoint(32768, 32768)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(65535, 0)); + } + else + { + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 0, TQPoint(0, 0)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 8, TQPoint(128, 128)); + grainCurves->setCurvePoint(Digikam::ImageHistogram::ValueChannel, 16, TQPoint(255, 0)); + } + + // Calculate curves and lut to apply on grain. + grainCurves->curvesCalculateCurve(Digikam::ImageHistogram::ValueChannel); + grainCurves->curvesLutSetup(Digikam::ImageHistogram::AlphaChannel); + grainCurves->curvesLutProcess(pGrainBits, pMaskBits, Width, Height); + delete grainCurves; + + // delete it here, not used any more + delete [] pGrainBits; + pGrainBits = 0; + } + + postProgress( 60 ); + if (m_cancel) + { + delete [] pGrainBits; + delete [] pMaskBits; + return; + } + + // Merge gray scale image with grain using shade coefficient. + + if (Grain) + { + pOverlayBits = new uchar[numBytes]; // Overlay to merge with original converted in gray scale. + + // get composer for default blending + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffNone); + int alpha; + + int Shade = 52; // This value control the shading pixel effect between original image and grain mask. + if (sixteenBit) + Shade = (Shade + 1) * 256 - 1; + + for (int x = 0; !m_cancel && x < Width; x++) + { + for (int y = 0; !m_cancel && y < Height; y++) + { + int offset = x*bytesDepth + (y*Width*bytesDepth); + + // read color from orig image + bwBlurData.setColor(pBWBlurBits + offset, sixteenBit); + // read color from mask + maskData.setColor(pMaskBits + offset, sixteenBit); + // set shade as alpha value - it will be used as source alpha when blending + maskData.setAlpha(Shade); + + // compose, write result to blendData. + // Preserve alpha, do not blend it (taken from old algorithm - correct?) + alpha = bwBlurData.alpha(); + composer->compose(bwBlurData, maskData); + bwBlurData.setAlpha(alpha); + + // write to destination + bwBlurData.setPixel(pOverlayBits + offset); + } + + // Update progress bar in dialog. + progress = (int) (70.0 + ((double)x * 10.0) / Width); + + if (progress%5 == 0) + postProgress( progress ); + } + + delete composer; + + // delete it here, not used any more + BWBlurImage.reset(); + delete [] pMaskBits; + pMaskBits = 0; + } + else + { + // save a memcpy + pOverlayBits = pBWBlurBits; + pBWBlurBits = 0; + } + + //------------------------------------------ + // 3 - Merge Grayscale image & overlay mask. + //------------------------------------------ + + // Merge overlay and gray scale image using 'Overlay' Gimp method for increase the highlight. + // The result is usually a brighter picture. + // Overlay mode composite value computation is D = A * (B + (2 * B) * (255 - A)). + + outData.setSixteenBit(sixteenBit); + for (int x = 0; !m_cancel && x < Width; x++) + { + for (int y = 0; !m_cancel && y < Height; y++) + { + offset = x*bytesDepth + (y*Width*bytesDepth); + + bwData.setColor (pBWBits + offset, sixteenBit); + overData.setColor(pOverlayBits + offset, sixteenBit); + + if (sixteenBit) + { + outData.setRed ( intMult16 (bwData.red(), bwData.red() + intMult16(2 * overData.red(), 65535 - bwData.red()) ) ); + outData.setGreen( intMult16 (bwData.green(), bwData.green() + intMult16(2 * overData.green(), 65535 - bwData.green()) ) ); + outData.setBlue ( intMult16 (bwData.blue(), bwData.blue() + intMult16(2 * overData.blue(), 65535 - bwData.blue()) ) ); + } + else + { + outData.setRed ( intMult8 (bwData.red(), bwData.red() + intMult8(2 * overData.red(), 255 - bwData.red()) ) ); + outData.setGreen( intMult8 (bwData.green(), bwData.green() + intMult8(2 * overData.green(), 255 - bwData.green()) ) ); + outData.setBlue ( intMult8 (bwData.blue(), bwData.blue() + intMult8(2 * overData.blue(), 255 - bwData.blue()) ) ); + } + outData.setAlpha( bwData.alpha() ); + outData.setPixel( pOutBits + offset ); + } + + // Update progress bar in dialog. + progress = (int) (80.0 + ((double)x * 20.0) / Width); + + if (progress%5 == 0) + postProgress(progress); + } + + delete [] pGrainBits; + delete [] pMaskBits; + + if (Grain) + delete [] pOverlayBits; +} + +} // NameSpace DigikamInfraredImagesPlugin diff --git a/src/imageplugins/infrared/infrared.h b/src/imageplugins/infrared/infrared.h new file mode 100644 index 00000000..fc2f89ee --- /dev/null +++ b/src/imageplugins/infrared/infrared.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Infrared threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef INFRARED_H +#define INFRARED_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamInfraredImagesPlugin +{ + +class Infrared : public Digikam::DImgThreadedFilter +{ + +public: + + Infrared(Digikam::DImg *orgImage, TQObject *parent=0, int sensibility=1, bool grain=true); + + ~Infrared(){}; + +private: // Infrared filter data. + + bool m_grain; + + int m_sensibility; + +private: // Infrared filter methods. + + virtual void filterImage(void); + + void infraredImage(Digikam::DImg *orgImage, int Sensibility, bool Grain); + +}; + +} // NameSpace DigikamInfraredImagesPlugin + +#endif /* INFRARED_H */ diff --git a/src/imageplugins/infrared/infraredtool.cpp b/src/imageplugins/infrared/infraredtool.cpp new file mode 100644 index 00000000..f34d1c8a --- /dev/null +++ b/src/imageplugins/infrared/infraredtool.cpp @@ -0,0 +1,225 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "infrared.h" +#include "infraredtool.h" +#include "infraredtool.moc" + +using namespace Digikam; + +namespace DigikamInfraredImagesPlugin +{ + +InfraredTool::InfraredTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("infrared"); + setToolName(i18n("Infrared")); + setToolIcon(SmallIcon("infrared")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 3, 1); + TQLabel *label1 = new TQLabel(i18n("Sensitivity (ISO):"), m_gboxSettings->plainPage()); + + m_sensibilitySlider = new TQSlider(1, 25, 1, 1, TQt::Horizontal, m_gboxSettings->plainPage()); + m_sensibilitySlider->setTracking(false); + m_sensibilitySlider->setTickInterval(1); + m_sensibilitySlider->setTickmarks(TQSlider::Below); + + m_sensibilityLCDValue = new TQLCDNumber(4, m_gboxSettings->plainPage()); + m_sensibilityLCDValue->setSegmentStyle(TQLCDNumber::Flat); + m_sensibilityLCDValue->display(TQString::number(200)); + TQString whatsThis = i18n("

    Set here the ISO-sensitivity of the simulated infrared film. " + "Increasing this value will increase the proportion of green color in the mix. " + "It will also increase the halo effect on the hightlights, and the film " + "graininess (if that box is checked).

    " + "

    Note: to simulate an Ilford SFX200 infrared film, use a sensitivity excursion of 200 to 800. " + "A sensitivity over 800 simulates Kodak HIE high-speed infrared film. This last one creates a more " + "dramatic photographic style.

    "); + + TQWhatsThis::add(m_sensibilityLCDValue, whatsThis); + TQWhatsThis::add(m_sensibilitySlider, whatsThis); + + // ------------------------------------------------------------- + + m_addFilmGrain = new TQCheckBox(i18n("Add film grain"), m_gboxSettings->plainPage()); + m_addFilmGrain->setChecked(true); + TQWhatsThis::add( m_addFilmGrain, i18n("

    This option adds infrared film grain to " + "the image depending on ISO-sensitivity.")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 1); + grid->addMultiCellWidget(m_sensibilitySlider, 1, 1, 0, 0); + grid->addMultiCellWidget(m_sensibilityLCDValue, 1, 1, 1, 1); + grid->addMultiCellWidget(m_addFilmGrain, 2, 2, 0, 1); + grid->setRowStretch(3, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "infrared Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer()) ); + + // this connection is necessary to change the LCD display when + // the value is changed by single clicking on the slider + connect( m_sensibilitySlider, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_sensibilitySlider, TQ_SIGNAL(sliderMoved(int)), + this, TQ_SLOT(slotSliderMoved(int)) ); + + connect( m_addFilmGrain, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect()) ); +} + +InfraredTool::~InfraredTool() +{ +} + +void InfraredTool::renderingFinished() +{ + m_sensibilitySlider->setEnabled(true); + m_addFilmGrain->setEnabled(true); +} + +void InfraredTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("infrared Tool"); + m_sensibilitySlider->blockSignals(true); + m_addFilmGrain->blockSignals(true); + m_sensibilitySlider->setValue(config->readNumEntry("SensitivityAjustment", 1)); + m_addFilmGrain->setChecked(config->readBoolEntry("AddFilmGrain", false)); + m_sensibilitySlider->blockSignals(false); + m_addFilmGrain->blockSignals(false); + slotSliderMoved(m_sensibilitySlider->value()); +} + +void InfraredTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("infrared Tool"); + config->writeEntry("SensitivityAjustment", m_sensibilitySlider->value()); + config->writeEntry("AddFilmGrain", m_addFilmGrain->isChecked()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void InfraredTool::slotResetSettings() +{ + m_sensibilitySlider->blockSignals(true); + m_addFilmGrain->blockSignals(true); + m_sensibilitySlider->setValue(1); + m_addFilmGrain->setChecked(false); + m_sensibilitySlider->blockSignals(false); + m_addFilmGrain->blockSignals(false); +} + +void InfraredTool::slotSliderMoved(int v) +{ + m_sensibilityLCDValue->display( TQString::number(100 + 100 * v) ); +} + +void InfraredTool::prepareEffect() +{ + m_addFilmGrain->setEnabled(false); + m_sensibilitySlider->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + int s = 100 + 100 * m_sensibilitySlider->value(); + bool g = m_addFilmGrain->isChecked(); + + setFilter(dynamic_cast(new Infrared(&image, this, s, g))); +} + +void InfraredTool::prepareFinal() +{ + m_addFilmGrain->setEnabled(false); + m_sensibilitySlider->setEnabled(false); + + int s = 100 + 100 * m_sensibilitySlider->value(); + bool g = m_addFilmGrain->isChecked(); + + ImageIface iface(0, 0); + + setFilter(dynamic_cast(new Infrared(iface.getOriginalImg(), this, s, g))); +} + +void InfraredTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void InfraredTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Infrared"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamInfraredImagesPlugin diff --git a/src/imageplugins/infrared/infraredtool.h b/src/imageplugins/infrared/infraredtool.h new file mode 100644 index 00000000..ddb5144b --- /dev/null +++ b/src/imageplugins/infrared/infraredtool.h @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-22 + * Description : a digiKam image editor plugin for simulate + * infrared film. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef INFRAREDTOOL_H +#define INFRAREDTOOL_H + +// Digikam includes. + +#include "editortool.h" + +class TQSlider; +class TQLCDNumber; +class TQCheckBox; + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamInfraredImagesPlugin +{ + +class InfraredTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + InfraredTool(TQObject* parent); + ~InfraredTool(); + +private slots: + + void slotSliderMoved(int); + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQCheckBox *m_addFilmGrain; + + TQSlider *m_sensibilitySlider; + + TQLCDNumber *m_sensibilityLCDValue; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamInfraredImagesPlugin + +#endif /* INFRAREDTOOL_H */ diff --git a/src/imageplugins/inpainting/Makefile.am b/src/imageplugins/inpainting/Makefile.am new file mode 100644 index 00000000..243253a8 --- /dev/null +++ b/src/imageplugins/inpainting/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/greycstoration \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_inpainting_la_SOURCES = imageplugin_inpainting.cpp \ + inpaintingtool.cpp + +digikamimageplugin_inpainting_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_inpainting_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -no-undefined -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_inpainting.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_inpainting.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_inpainting_ui.rc diff --git a/src/imageplugins/inpainting/digikamimageplugin_inpainting.desktop b/src/imageplugins/inpainting/digikamimageplugin_inpainting.desktop new file mode 100644 index 00000000..09f891a7 --- /dev/null +++ b/src/imageplugins/inpainting/digikamimageplugin_inpainting.desktop @@ -0,0 +1,49 @@ +[Desktop Entry] +Name=ImagePlugin_InPainting +Name[bg]=ПриÑтавка за Ñнимка - ДориÑуване +Name[da]=Plugin til indfyldning +Name[el]=ΠÏόσθετοΕικόνας_ΑποζωγÏαφισμός +Name[hr]=Bojanje +Name[it]=PluginImmagini_Reintegrazione +Name[nl]=Afbeeldingsplugin_Inkleuren +Name[sr]=Пребојавање +Name[sr@Latn]=Prebojavanje +Name[sv]=Insticksprogram för ifyllnad +Name[tr]=ResimEklentisi_InPainting +Name[xx]=xxImagePlugin_InPaintingxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=digiKam plugin to inpaint a photograph +Comment[bg]=ПриÑтавка на digiKam за дориÑуване на повредени или липÑващи облаÑти от Ñнимки +Comment[ca]=Connector pel digiKam per repintar una fotografia +Comment[da]=Digikam plugin til at indfylde i et fotografi +Comment[de]=digiKam-Modul zur Korrektur von Bildfehlern durch Inpainting +Comment[el]=ΠÏόσθετο αποζωγÏÎ±Ï†Î¹ÏƒÎ¼Î¿Ï Î¼Î¹Î±Ï‚ φωτογÏαφίας μέσα για το digiKam +Comment[es]=Plugin para digiKam repintar una parte de una fotografía +Comment[et]=DigiKami foto objektide kaotamise plugin +Comment[fa]=وصلۀ digiKam برای کشیدن یک عکس +Comment[gl]=Un plugin de digiKam para pintar por dentro unha fotografia +Comment[hr]=digiKam dodatak za bojanje u fotografiji +Comment[is]=Ãforrit fyrir digiKam til að fjarlægja óæskilega hluti úr mynd +Comment[it]=Plugin di digiKam per reintegrare una fotografia +Comment[ja]=digiKam 写真修復プラグイン +Comment[nds]=digiKam-Moduul för de Bildkorrektuur dör Övermalen +Comment[nl]=Digikam-plugin voor het inkleuren van een foto +Comment[pa]=ਇੱਕ ਫੋਟੋ-ਗਰਾਫ਼ ਇਨ-ਪੇਂਟ ਲਈ ਡਿਜ਼ੀਕੈਮ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca zamalowanie fragmentów zdjÄ™cia +Comment[pt]=Um 'plugin' do digiKam para pintar por dentro uma fotografia +Comment[pt_BR]=Um 'plugin' do digiKam para pintar por dentro uma fotografia +Comment[ru]=Модуль подриÑовки фотографий Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin na odstránenie artefaktov +Comment[sr]=digiKam-ов прикључак за пребојавање фотографије +Comment[sr@Latn]=digiKam-ov prikljuÄak za prebojavanje fotografije +Comment[sv]=Digikam insticksprogram för att fylla i ett fotografi +Comment[tr]=TDE FotoÄŸraf Yöneticisi +Comment[uk]=Втулок Ð¼Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð½Ð° фотографіÑÑ… Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung sÆ¡n vào ảnh chụp cho digiKam +Comment[xx]=xxdigiKam plugin to inpaint a photographxx + +X-TDE-Library=digikamimageplugin_inpainting +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/inpainting/digikamimageplugin_inpainting_ui.rc b/src/imageplugins/inpainting/digikamimageplugin_inpainting_ui.rc new file mode 100644 index 00000000..a4c13901 --- /dev/null +++ b/src/imageplugins/inpainting/digikamimageplugin_inpainting_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/inpainting/imageeffect_inpainting.cpp b/src/imageplugins/inpainting/imageeffect_inpainting.cpp new file mode 100644 index 00000000..d7f2f5fe --- /dev/null +++ b/src/imageplugins/inpainting/imageeffect_inpainting.cpp @@ -0,0 +1,466 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ include. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "greycstorationsettings.h" +#include "greycstorationwidget.h" +#include "greycstorationiface.h" +#include "imageeffect_inpainting.h" +#include "imageeffect_inpainting.moc" + +namespace DigikamInPaintingImagesPlugin +{ + +class InPaintingPassivePopup : public KPassivePopup +{ +public: + + InPaintingPassivePopup(TQWidget* parent) : KPassivePopup(parent), m_parent(parent) {} + +protected: + + virtual void positionSelf() { move(m_parent->x() + 30, m_parent->y() + 30); } + +private: + + TQWidget* m_parent; +}; + +//------------------------------------------------------------------------------------------ + +void ImageEffect_InPainting::inPainting(TQWidget* parent) +{ + // -- check if we actually have a selection -------------------- + + Digikam::ImageIface iface(0, 0); + + int w = iface.selectedWidth(); + int h = iface.selectedHeight(); + + if (!w || !h) + { + InPaintingPassivePopup* popup = new InPaintingPassivePopup(parent); + popup->setView(i18n("Inpainting Photograph Tool"), + i18n("You need to select a region to inpaint to use " + "this tool")); + popup->setAutoDelete(true); + popup->setTimeout(2500); + popup->show(); + return; + } + + // -- run the dlg ---------------------------------------------- + + ImageEffect_InPainting_Dialog dlg(parent); + dlg.exec(); +} + +//------------------------------------------------------------------------------------------ + +ImageEffect_InPainting_Dialog::ImageEffect_InPainting_Dialog(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Photograph Inpainting"), + "inpainting", true, true, false, + Digikam::ImageGuideWidget::HVGuideMode, + 0, true, true, true) +{ + m_isComputed = false; + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Photograph Inpainting"), + digikam_version, + I18N_NOOP("A digiKam image plugin to inpaint a photograph."), + TDEAboutData::License_GPL, + "(c) 2005-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("David Tschumperle", I18N_NOOP("CImg library"), 0, + "http://cimg.sourceforge.net"); + + about->addAuthor("Gerhard Kulzer", I18N_NOOP("Feedback and plugin polishing"), + "gerhard at kulzer.net"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout(gboxSettings, 2, 1, spacingHint()); + m_mainTab = new TQTabWidget( gboxSettings ); + + TQWidget* firstPage = new TQWidget( m_mainTab ); + TQGridLayout* grid = new TQGridLayout( firstPage, 2, 2, marginHint(), spacingHint()); + m_mainTab->addTab( firstPage, i18n("Preset") ); + + KURLLabel *cimgLogoLabel = new KURLLabel(firstPage); + cimgLogoLabel->setText(TQString()); + cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + TDEGlobal::dirs()->addResourceType("logo-cimg", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + cimgLogoLabel->setPixmap( TQPixmap( directory + "logo-cimg.png" ) ); + TQToolTip::add(cimgLogoLabel, i18n("Visit CImg library website")); + + TQLabel *typeLabel = new TQLabel(i18n("Filtering type:"), firstPage); + typeLabel->setAlignment ( TQt::AlignRight | TQt::AlignVCenter); + m_inpaintingTypeCB = new TQComboBox( false, firstPage ); + m_inpaintingTypeCB->insertItem( i18n("None") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Small Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Medium Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Large Artefact") ); + TQWhatsThis::add( m_inpaintingTypeCB, i18n("

    Select the filter preset to use for photograph restoration:

    " + "None: Most common values. Puts settings to default.

    " + "Remove Small Artefact: inpaint small image artefact like image glitch.

    " + "Remove Medium Artefact: inpaint medium image artefact.

    " + "Remove Large Artefact: inpaint image artefact like unwanted object.

    ")); + + grid->addMultiCellWidget(cimgLogoLabel, 0, 0, 1, 1); + grid->addMultiCellWidget(typeLabel, 1, 1, 0, 0); + grid->addMultiCellWidget(m_inpaintingTypeCB, 1, 1, 1, 1); + grid->setRowStretch(1, 10); + + // ------------------------------------------------------------- + + m_settingsWidget = new Digikam::GreycstorationWidget(m_mainTab); + + gridSettings->addMultiCellWidget(m_mainTab, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(new TQLabel(gboxSettings), 1, 1, 1, 1); + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(cimgLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processCImgURL(const TQString&))); + + connect(m_inpaintingTypeCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotResetValues(int))); +} + +ImageEffect_InPainting_Dialog::~ImageEffect_InPainting_Dialog() +{ +} + +void ImageEffect_InPainting_Dialog::renderingFinished() +{ + m_mainTab->setEnabled(true); +} + +void ImageEffect_InPainting_Dialog::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("inpainting Tool Dialog"); + + Digikam::GreycstorationSettings settings; + settings.fastApprox = config->readBoolEntry("FastApprox", true); + settings.interp = config->readNumEntry("Interpolation", + Digikam::GreycstorationSettings::NearestNeighbor); + settings.amplitude = config->readDoubleNumEntry("Amplitude", 20.0); + settings.sharpness = config->readDoubleNumEntry("Sharpness", 0.3); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", 1.0); + settings.alpha = config->readDoubleNumEntry("Alpha", 0.8); + settings.sigma = config->readDoubleNumEntry("Sigma", 2.0); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", 2.0); + settings.dl = config->readDoubleNumEntry("Dl", 0.8); + settings.da = config->readDoubleNumEntry("Da", 30.0); + settings.nbIter = config->readNumEntry("Iteration", 30); + settings.tile = config->readNumEntry("Tile", 512); + settings.btile = config->readNumEntry("BTile", 4); + m_settingsWidget->setSettings(settings); + + int p = config->readNumEntry("Preset", NoPreset); + m_inpaintingTypeCB->setCurrentItem(p); + if (p == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); +} + +void ImageEffect_InPainting_Dialog::writeUserSettings() +{ + Digikam::GreycstorationSettings settings = m_settingsWidget->getSettings(); + TDEConfig* config = kapp->config(); + config->setGroup("inpainting Tool Dialog"); + config->writeEntry("Preset", m_inpaintingTypeCB->currentItem()); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + config->sync(); +} + +void ImageEffect_InPainting_Dialog::slotResetValues(int i) +{ + if (i == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); + + resetValues(); +} + +void ImageEffect_InPainting_Dialog::resetValues() +{ + Digikam::GreycstorationSettings settings; + settings.setInpaintingDefaultSettings(); + + switch(m_inpaintingTypeCB->currentItem()) + { + case RemoveSmallArtefact: + // We use default settings here. + break; + + case RemoveMediumArtefact: + { + settings.amplitude = 50.0; + settings.nbIter = 50; + break; + } + + case RemoveLargeArtefact: + { + settings.amplitude = 100.0; + settings.nbIter = 100; + break; + } + } + + m_settingsWidget->setSettings(settings); +} + +void ImageEffect_InPainting_Dialog::processCImgURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void ImageEffect_InPainting_Dialog::prepareEffect() +{ + m_mainTab->setEnabled(false); + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + m_originalImage = Digikam::DImg(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + // Selected area from the image and mask creation: + // + // We optimize the computation time to use the current selected area in image editor + // and to create an inpainting mask with it. Because inpainting is done by interpolation + // neighboor pixels which can be located far from the selected area, we need to ajust the + // mask size in according with the parameter algorithms, especially 'amplitude'. + // Mask size is computed like this : + // + // (image_size_x + 2*amplitude , image_size_y + 2*amplitude) + + + TQRect selectionRect = TQRect(iface.selectedXOrg(), iface.selectedYOrg(), + iface.selectedWidth(), iface.selectedHeight()); + + TQPixmap inPaintingMask(iface.originalWidth(), iface.originalHeight()); + inPaintingMask.fill(TQt::black); + TQPainter p(&inPaintingMask); + p.fillRect( selectionRect, TQBrush(TQt::white) ); + p.end(); + + Digikam::GreycstorationSettings settings = m_settingsWidget->getSettings(); + + int x1 = (int)(selectionRect.left() - 2*settings.amplitude); + int y1 = (int)(selectionRect.top() - 2*settings.amplitude); + int x2 = (int)(selectionRect.right() + 2*settings.amplitude); + int y2 = (int)(selectionRect.bottom() + 2*settings.amplitude); + m_maskRect = TQRect(x1, y1, x2-x1, y2-y1); + + // Mask area normalization. + // We need to check if mask area is out of image size else inpainting give strange results. + + if (m_maskRect.left() < 0) m_maskRect.setLeft(0); + if (m_maskRect.top() < 0) m_maskRect.setTop(0); + if (m_maskRect.right() > iface.originalWidth()) m_maskRect.setRight(iface.originalWidth()); + if (m_maskRect.bottom() > iface.originalHeight()) m_maskRect.setBottom(iface.originalHeight()); + + m_maskImage = inPaintingMask.convertToImage().copy(m_maskRect); + m_cropImage = m_originalImage.copy(m_maskRect); + + m_threadedFilter = dynamic_cast( + new Digikam::GreycstorationIface( + &m_cropImage, + settings, + Digikam::GreycstorationIface::InPainting, + 0, 0, + m_maskImage, this)); +} + +void ImageEffect_InPainting_Dialog::prepareFinal() +{ + if (!m_isComputed) + { + setProgressVisible(true); + prepareEffect(); + } + else + { + putFinalData(); + kapp->restoreOverrideCursor(); + accept(); + } +} + +void ImageEffect_InPainting_Dialog::putPreviewData() +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + Digikam::GreycstorationSettings settings = m_settingsWidget->getSettings(); + + m_cropImage = m_threadedFilter->getTargetImage(); + TQRect cropSel((int)(2*settings.amplitude), (int)(2*settings.amplitude), + iface->selectedWidth(), iface->selectedHeight()); + Digikam::DImg imDest = m_cropImage.copy(cropSel); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + m_imagePreviewWidget->updatePreview(); + m_isComputed = true; +} + +void ImageEffect_InPainting_Dialog::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + if (!m_isComputed) + m_cropImage = m_threadedFilter->getTargetImage(); + + m_originalImage.bitBltImage(&m_cropImage, m_maskRect.left(), m_maskRect.top()); + + iface.putOriginalImage(i18n("InPainting"), m_originalImage.bits()); +} + +void ImageEffect_InPainting_Dialog::slotUser3() +{ + KURL loadInpaintingFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Inpainting Settings File to Load")) ); + if( loadInpaintingFile.isEmpty() ) + return; + + TQFile file(loadInpaintingFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!m_settingsWidget->loadSettings(file, TQString("# Photograph Inpainting Configuration File V2"))) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Photograph Inpainting settings text file.") + .arg(loadInpaintingFile.fileName())); + file.close(); + return; + } + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Photograph Inpainting text file.")); + + file.close(); + m_inpaintingTypeCB->blockSignals(true); + m_inpaintingTypeCB->setCurrentItem(NoPreset); + m_inpaintingTypeCB->blockSignals(false); + m_settingsWidget->setEnabled(true); +} + +void ImageEffect_InPainting_Dialog::slotUser2() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Inpainting Settings File to Save")) ); + if( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + m_settingsWidget->saveSettings(file, TQString("# Photograph Inpainting Configuration File V2")); + else + KMessageBox::error(this, i18n("Cannot save settings to the Photograph Inpainting text file.")); + + file.close(); +} + +} // NameSpace DigikamInPaintingImagesPlugin + diff --git a/src/imageplugins/inpainting/imageeffect_inpainting.h b/src/imageplugins/inpainting/imageeffect_inpainting.h new file mode 100644 index 00000000..542e9504 --- /dev/null +++ b/src/imageplugins/inpainting/imageeffect_inpainting.h @@ -0,0 +1,116 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef IMAGEEFFECT_INPAINTING_H +#define IMAGEEFFECT_INPAINTING_H + +// TQt include. + +#include +#include +#include + +// Digikam includes. + +#include "dimg.h" +#include "imageguidedlg.h" + +class TQTabWidget; +class TQComboBox; + +namespace Digikam +{ +class GreycstorationWidget; +} + +namespace DigikamInPaintingImagesPlugin +{ + +class ImageEffect_InPainting +{ +public: + + static void inPainting(TQWidget *parent); +}; + +//----------------------------------------------------------- + +class ImageEffect_InPainting_Dialog : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_InPainting_Dialog(TQWidget* parent); + ~ImageEffect_InPainting_Dialog(); + +private slots: + + void slotUser2(); + void slotUser3(); + void readUserSettings(); + void processCImgURL(const TQString&); + void slotResetValues(int); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum InPaintingFilteringPreset + { + NoPreset=0, + RemoveSmallArtefact, + RemoveMediumArtefact, + RemoveLargeArtefact + }; + + bool m_isComputed; + + TQRect m_maskRect; + + TQImage m_maskImage; + + TQComboBox *m_inpaintingTypeCB; + + TQTabWidget *m_mainTab; + + Digikam::DImg m_originalImage; + Digikam::DImg m_cropImage; + + Digikam::GreycstorationWidget *m_settingsWidget; +}; + +} // NameSpace DigikamInPaintingImagesPlugin + +#endif /* IMAGEEFFECT_INPAINTING_H */ diff --git a/src/imageplugins/inpainting/imageplugin_inpainting.cpp b/src/imageplugins/inpainting/imageplugin_inpainting.cpp new file mode 100644 index 00000000..2c1e30f3 --- /dev/null +++ b/src/imageplugins/inpainting/imageplugin_inpainting.cpp @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "imageiface.h" +#include "inpaintingtool.h" +#include "imageplugin_inpainting.h" +#include "imageplugin_inpainting.moc" + +using namespace DigikamInPaintingImagesPlugin; +using namespace Digikam; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_inpainting, + KGenericFactory("digikamimageplugin_inpainting")); + +ImagePlugin_InPainting::ImagePlugin_InPainting(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_InPainting") +{ + m_inPaintingAction = new TDEAction(i18n("Inpainting..."), "inpainting", + CTRL+Key_E, + this, TQ_SLOT(slotInPainting()), + actionCollection(), "imageplugin_inpainting"); + + m_inPaintingAction->setWhatsThis( i18n( "This filter can be used to inpaint a part in a photo. " + "Select a region to inpaint to use this option.") ); + + setXMLFile( "digikamimageplugin_inpainting_ui.rc" ); + + DDebug() << "ImagePlugin_InPainting plugin loaded" << endl; +} + +ImagePlugin_InPainting::~ImagePlugin_InPainting() +{ +} + +void ImagePlugin_InPainting::setEnabledActions(bool enable) +{ + m_inPaintingAction->setEnabled(enable); +} + +void ImagePlugin_InPainting::slotInPainting() +{ + + ImageIface iface(0, 0); + + int w = iface.selectedWidth(); + int h = iface.selectedHeight(); + + if (!w || !h) + { + InPaintingPassivePopup* popup = new InPaintingPassivePopup(kapp->activeWindow()); + popup->setView(i18n("Inpainting Photograph Tool"), + i18n("You need to select a region to inpaint to use " + "this tool")); + popup->setAutoDelete(true); + popup->setTimeout(2500); + popup->show(); + return; + } + + // -- run the dlg ---------------------------------------------- + + InPaintingTool *tool = new InPaintingTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/inpainting/imageplugin_inpainting.h b/src/imageplugins/inpainting/imageplugin_inpainting.h new file mode 100644 index 00000000..fb2b1f5c --- /dev/null +++ b/src/imageplugins/inpainting/imageplugin_inpainting.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_INPAINTING_H +#define IMAGEPLUGIN_INPAINTING_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_InPainting : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_InPainting(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_InPainting(); + + void setEnabledActions(bool enable); + +private slots: + + void slotInPainting(); + +private: + + TDEAction *m_inPaintingAction; +}; + +#endif /* IMAGEPLUGIN_INPAINTING_H */ diff --git a/src/imageplugins/inpainting/inpaintingtool.cpp b/src/imageplugins/inpainting/inpaintingtool.cpp new file mode 100644 index 00000000..2ada52bf --- /dev/null +++ b/src/imageplugins/inpainting/inpaintingtool.cpp @@ -0,0 +1,430 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimgthreadedfilter.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "greycstorationsettings.h" +#include "greycstorationwidget.h" +#include "greycstorationiface.h" +#include "inpaintingtool.h" +#include "inpaintingtool.moc" + +using namespace Digikam; + +namespace DigikamInPaintingImagesPlugin +{ + +InPaintingTool::InPaintingTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("inpainting"); + setToolName(i18n("Inpainting")); + setToolIcon(SmallIcon("inpainting")); + + m_isComputed = false; + + m_previewWidget = new ImageWidget("inpainting Tool", 0, + i18n("

    Here you can see the image selection preview with " + "inpainting applied."), + true, ImageGuideWidget::HVGuideMode, false, true); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Try| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 2, 1); + m_mainTab = new TQTabWidget( m_gboxSettings->plainPage() ); + + TQWidget* firstPage = new TQWidget( m_mainTab ); + TQGridLayout* grid = new TQGridLayout( firstPage, 2, 2); + m_mainTab->addTab( firstPage, i18n("Preset") ); + + KURLLabel *cimgLogoLabel = new KURLLabel(firstPage); + cimgLogoLabel->setText(TQString()); + cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + TDEGlobal::dirs()->addResourceType("logo-cimg", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + cimgLogoLabel->setPixmap( TQPixmap( directory + "logo-cimg.png" ) ); + TQToolTip::add(cimgLogoLabel, i18n("Visit CImg library website")); + + TQLabel *typeLabel = new TQLabel(i18n("Filtering type:"), firstPage); + typeLabel->setAlignment ( TQt::AlignRight | TQt::AlignVCenter); + m_inpaintingTypeCB = new TQComboBox( false, firstPage ); + m_inpaintingTypeCB->insertItem( i18n("None") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Small Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Medium Artefact") ); + m_inpaintingTypeCB->insertItem( i18n("Remove Large Artefact") ); + TQWhatsThis::add( m_inpaintingTypeCB, i18n("

    Select the filter preset to use for photograph restoration:

    " + "None: Most common values. Puts settings to default.

    " + "Remove Small Artefact: inpaint small image artefact like image glitch.

    " + "Remove Medium Artefact: inpaint medium image artefact.

    " + "Remove Large Artefact: inpaint image artefact like unwanted object.

    ")); + + grid->addMultiCellWidget(cimgLogoLabel, 0, 0, 1, 1); + grid->addMultiCellWidget(typeLabel, 1, 1, 0, 0); + grid->addMultiCellWidget(m_inpaintingTypeCB, 1, 1, 1, 1); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + grid->setRowStretch(1, 10); + + // ------------------------------------------------------------- + + m_settingsWidget = new GreycstorationWidget(m_mainTab); + + gridSettings->addMultiCellWidget(m_mainTab, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(new TQLabel(m_gboxSettings->plainPage()), 1, 1, 1, 1); + gridSettings->setMargin(m_gboxSettings->spacingHint()); + gridSettings->setSpacing(m_gboxSettings->spacingHint()); + gridSettings->setRowStretch(1, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(cimgLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processCImgURL(const TQString&))); + + connect(m_inpaintingTypeCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotResetValues(int))); + + // ------------------------------------------------------------- + + GreycstorationSettings defaults; + defaults.setInpaintingDefaultSettings(); + m_settingsWidget->setDefaultSettings(defaults); +} + +InPaintingTool::~InPaintingTool() +{ +} + +void InPaintingTool::renderingFinished() +{ + m_mainTab->setEnabled(true); +} + +void InPaintingTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("inpainting Tool"); + + GreycstorationSettings settings; + GreycstorationSettings defaults; + defaults.setInpaintingDefaultSettings(); + + settings.fastApprox = config->readBoolEntry("FastApprox", defaults.fastApprox); + settings.interp = config->readNumEntry("Interpolation", defaults.interp); + settings.amplitude = config->readDoubleNumEntry("Amplitude", defaults.amplitude); + settings.sharpness = config->readDoubleNumEntry("Sharpness", defaults.sharpness); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", defaults.anisotropy); + settings.alpha = config->readDoubleNumEntry("Alpha", defaults.alpha); + settings.sigma = config->readDoubleNumEntry("Sigma", defaults.sigma); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", defaults.gaussPrec); + settings.dl = config->readDoubleNumEntry("Dl", defaults.dl); + settings.da = config->readDoubleNumEntry("Da", defaults.da); + settings.nbIter = config->readNumEntry("Iteration", defaults.nbIter); + settings.tile = config->readNumEntry("Tile", defaults.tile); + settings.btile = config->readNumEntry("BTile", defaults.btile); + m_settingsWidget->setSettings(settings); + + int p = config->readNumEntry("Preset", NoPreset); + m_inpaintingTypeCB->setCurrentItem(p); + if (p == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); +} + +void InPaintingTool::writeSettings() +{ + GreycstorationSettings settings = m_settingsWidget->getSettings(); + TDEConfig* config = kapp->config(); + config->setGroup("inpainting Tool"); + config->writeEntry("Preset", m_inpaintingTypeCB->currentItem()); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + m_previewWidget->writeSettings(); + config->sync(); +} + +void InPaintingTool::slotResetValues(int i) +{ + if (i == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); + + slotResetSettings(); +} + +void InPaintingTool::slotResetSettings() +{ + GreycstorationSettings settings; + settings.setInpaintingDefaultSettings(); + + switch(m_inpaintingTypeCB->currentItem()) + { + case RemoveSmallArtefact: + // We use default settings here. + break; + + case RemoveMediumArtefact: + { + settings.amplitude = 50.0; + settings.nbIter = 50; + break; + } + + case RemoveLargeArtefact: + { + settings.amplitude = 100.0; + settings.nbIter = 100; + break; + } + } + + m_settingsWidget->setSettings(settings); +} + +void InPaintingTool::processCImgURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void InPaintingTool::prepareEffect() +{ + m_mainTab->setEnabled(false); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + m_originalImage = DImg(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + // Selected area from the image and mask creation: + // + // We optimize the computation time to use the current selected area in image editor + // and to create an inpainting mask with it. Because inpainting is done by interpolation + // neighboor pixels which can be located far from the selected area, we need to ajust the + // mask size in according with the parameter algorithms, especially 'amplitude'. + // Mask size is computed like this : + // + // (image_size_x + 2*amplitude , image_size_y + 2*amplitude) + + + TQRect selectionRect = TQRect(iface.selectedXOrg(), iface.selectedYOrg(), + iface.selectedWidth(), iface.selectedHeight()); + + TQPixmap inPaintingMask(iface.originalWidth(), iface.originalHeight()); + inPaintingMask.fill(TQt::black); + TQPainter p(&inPaintingMask); + p.fillRect( selectionRect, TQBrush(TQt::white) ); + p.end(); + + GreycstorationSettings settings = m_settingsWidget->getSettings(); + + int x1 = (int)(selectionRect.left() - 2*settings.amplitude); + int y1 = (int)(selectionRect.top() - 2*settings.amplitude); + int x2 = (int)(selectionRect.right() + 2*settings.amplitude); + int y2 = (int)(selectionRect.bottom() + 2*settings.amplitude); + m_maskRect = TQRect(x1, y1, x2-x1, y2-y1); + + // Mask area normalization. + // We need to check if mask area is out of image size else inpainting give strange results. + + if (m_maskRect.left() < 0) m_maskRect.setLeft(0); + if (m_maskRect.top() < 0) m_maskRect.setTop(0); + if (m_maskRect.right() > iface.originalWidth()) m_maskRect.setRight(iface.originalWidth()); + if (m_maskRect.bottom() > iface.originalHeight()) m_maskRect.setBottom(iface.originalHeight()); + + m_maskImage = inPaintingMask.convertToImage().copy(m_maskRect); + m_cropImage = m_originalImage.copy(m_maskRect); + + setFilter(dynamic_cast( + new GreycstorationIface( + &m_cropImage, + settings, + GreycstorationIface::InPainting, + 0, 0, + m_maskImage, this))); +} + +void InPaintingTool::prepareFinal() +{ + if (!m_isComputed) + { + prepareEffect(); + } + else + { + Digikam::DImgThreadedFilter::EventData *eventData = new Digikam::DImgThreadedFilter::EventData(); + eventData->progress = 100; + eventData->starting = false; + eventData->success = true; + TQApplication::postEvent(this, new TQCustomEvent(TQEvent::User, eventData)); + } +} + +void InPaintingTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + GreycstorationSettings settings = m_settingsWidget->getSettings(); + + m_cropImage = filter()->getTargetImage(); + TQRect cropSel((int)(2*settings.amplitude), (int)(2*settings.amplitude), + iface->selectedWidth(), iface->selectedHeight()); + DImg imDest = m_cropImage.copy(cropSel); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + m_previewWidget->updatePreview(); + m_isComputed = true; +} + +void InPaintingTool::putFinalData() +{ + ImageIface iface(0, 0); + + if (!m_isComputed) + m_cropImage = filter()->getTargetImage(); + + m_originalImage.bitBltImage(&m_cropImage, m_maskRect.left(), m_maskRect.top()); + + iface.putOriginalImage(i18n("InPainting"), m_originalImage.bits()); +} + +void InPaintingTool::slotLoadSettings() +{ + KURL loadInpaintingFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Inpainting Settings File to Load")) ); + if( loadInpaintingFile.isEmpty() ) + return; + + TQFile file(loadInpaintingFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!m_settingsWidget->loadSettings(file, TQString("# Photograph Inpainting Configuration File V2"))) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Photograph Inpainting settings text file.") + .arg(loadInpaintingFile.fileName())); + file.close(); + return; + } + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Photograph Inpainting text file.")); + + file.close(); + m_inpaintingTypeCB->blockSignals(true); + m_inpaintingTypeCB->setCurrentItem(NoPreset); + m_inpaintingTypeCB->blockSignals(false); + m_settingsWidget->setEnabled(true); +} + +void InPaintingTool::slotSaveAsSettings() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Inpainting Settings File to Save")) ); + if( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + m_settingsWidget->saveSettings(file, TQString("# Photograph Inpainting Configuration File V2")); + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Inpainting text file.")); + + file.close(); +} + +} // NameSpace DigikamInPaintingImagesPlugin diff --git a/src/imageplugins/inpainting/inpaintingtool.h b/src/imageplugins/inpainting/inpaintingtool.h new file mode 100644 index 00000000..44202c5d --- /dev/null +++ b/src/imageplugins/inpainting/inpaintingtool.h @@ -0,0 +1,133 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-30 + * Description : a digiKam image editor plugin to inpaint + * a photograph + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef INPAINTINGTOOL_H +#define INPAINTINGTOOL_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "dimg.h" +#include "editortool.h" + +class TQTabWidget; +class TQComboBox; + +namespace Digikam +{ +class GreycstorationWidget; +class ImageWidget; +class EditorToolSettings; +} + +namespace DigikamInPaintingImagesPlugin +{ + +class InPaintingPassivePopup : public KPassivePopup +{ +public: + + InPaintingPassivePopup(TQWidget* parent) : KPassivePopup(parent), m_parent(parent) {} + +protected: + + virtual void positionSelf() { move(m_parent->x() + 30, m_parent->y() + 30); } + +private: + + TQWidget* m_parent; +}; + +//----------------------------------------------------------- + +class InPaintingTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + InPaintingTool(TQObject* parent); + ~InPaintingTool(); + +private slots: + + void processCImgURL(const TQString&); + void slotResetValues(int); + void slotResetSettings(); + void slotSaveAsSettings(); + void slotLoadSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum InPaintingFilteringPreset + { + NoPreset=0, + RemoveSmallArtefact, + RemoveMediumArtefact, + RemoveLargeArtefact + }; + + bool m_isComputed; + + TQRect m_maskRect; + + TQImage m_maskImage; + + TQComboBox *m_inpaintingTypeCB; + + TQTabWidget *m_mainTab; + + Digikam::DImg m_originalImage; + Digikam::DImg m_cropImage; + + Digikam::GreycstorationWidget *m_settingsWidget; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamInPaintingImagesPlugin + +#endif /* INPAINTINGTOOL_H */ diff --git a/src/imageplugins/inserttext/Makefile.am b/src/imageplugins/inserttext/Makefile.am new file mode 100644 index 00000000..2753def7 --- /dev/null +++ b/src/imageplugins/inserttext/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_inserttext_la_SOURCES = imageplugin_inserttext.cpp inserttextwidget.cpp \ + inserttexttool.cpp fontchooserwidget.cpp + +digikamimageplugin_inserttext_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_inserttext_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_inserttext.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_inserttext.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_inserttext_ui.rc + diff --git a/src/imageplugins/inserttext/digikamimageplugin_inserttext.desktop b/src/imageplugins/inserttext/digikamimageplugin_inserttext.desktop new file mode 100644 index 00000000..e9ca6cfd --- /dev/null +++ b/src/imageplugins/inserttext/digikamimageplugin_inserttext.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_InsertText +Name[bg]=ПриÑтавка за Ñнимки - ДобавÑне на текÑÑ‚ +Name[da]=Plugin til at indsætte tekst +Name[el]=ΠÏόσθετοΕικόνας_ΕισαγωγήΚειμένου +Name[fi]=Tekstinlisäys +Name[hr]=Tekst +Name[it]=PluginImmagini_InserisciTesto +Name[nl]=Afbeeldingsplugin_TekstInvoegen +Name[sr]=Убаци текÑÑ‚ +Name[sr@Latn]=Ubaci tekst +Name[sv]=Insticksprogram för infoga text +Name[tr]=ResimEklentisi_MetinEkle +Name[xx]=xxImagePlugin_InsertTextxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Insert text to image plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за добавÑне на текÑÑ‚ към Ñнимки +Comment[ca]=Connector pel digiKam per inserir text en una imatge +Comment[da]=Digikam plugin til at indsætte tekst i billeder +Comment[de]=digiKam-Modul zum Einfügen von Text in ein Bild +Comment[el]=ΠÏόσθετο εισαγωγής κειμένου σε εικόνα για το digiKam +Comment[es]=Plugin de digiKampara insertar texto a una imagen +Comment[et]=DigiKami pildile teksti lisamise plugin +Comment[fa]=درج متن در وصلۀ تصویر برای digiKam +Comment[fi]=Lisää tekstiä kuvaan +Comment[gl]=Un plugin de digiKam para inserir texto na imaxe +Comment[hr]=digiKam dodatak za umetanje teksta +Comment[is]=Ãforrit fyrir digiKam sem setur texta inn á mynd +Comment[it]=Plugin per l'aggiunta di testo nell'immagire per digiKam +Comment[ja]=digiKam テキスト挿入プラグイン +Comment[nds]=digiKam-Moduul för't Infögen vun Text in en Bild +Comment[nl]=Digikam-plugin voor het invoegen van tekst +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਿੱਤਰ ਉੱਤੇ ਪਾਠ ਲਿਖਣ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam dodajÄ…ca tekst do zdjÄ™cia +Comment[pt]=Um 'plugin' do digiKam para inserir texto na imagem +Comment[pt_BR]=Plugin de inserção de texto na imagem +Comment[ru]=Модуль digiKam Ð½Ð°Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑта на Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ +Comment[sk]=digiKam plugin pre vkladanie textu do obrázku +Comment[sr]=digiKam-ов прикључак за убацивање текÑта у Ñлику +Comment[sr@Latn]=digiKam-ov prikljuÄak za ubacivanje teksta u sliku +Comment[sv]=Digikam insticksprogram för att infoga text i bilder +Comment[tr]=digiKam için resme metin ekleme eklentisi +Comment[uk]=Втулок Ð´Ð»Ñ Ð²ÑтавлÑÐ½Ð½Ñ Ñ‚ÐµÐºÑту в Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung chèn chữ vào ảnh cho digiKam +Comment[xx]=xxInsert text to image plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_inserttext +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/inserttext/digikamimageplugin_inserttext_ui.rc b/src/imageplugins/inserttext/digikamimageplugin_inserttext_ui.rc new file mode 100644 index 00000000..4fa71fcc --- /dev/null +++ b/src/imageplugins/inserttext/digikamimageplugin_inserttext_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Decorate + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/inserttext/fontchooserwidget.cpp b/src/imageplugins/inserttext/fontchooserwidget.cpp new file mode 100644 index 00000000..aa76ea8e --- /dev/null +++ b/src/imageplugins/inserttext/fontchooserwidget.cpp @@ -0,0 +1,738 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a simple widget to choose a font based on + * KDE FontChooserWidget implementation. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 1999 by Preston Brown + * Copyright (C) 1997 by Bernd Johannes Wuebben + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "fontchooserwidget.h" +#include "fontchooserwidget.moc" + +namespace DigikamInsertTextImagesPlugin +{ + +class FontChooserWidget::FontChooserWidgetPrivate +{ +public: + + FontChooserWidgetPrivate() + { + m_palette.setColor(TQPalette::Active, TQColorGroup::Text, TQt::black); + m_palette.setColor(TQPalette::Active, TQColorGroup::Base, TQt::white); + } + + TQPalette m_palette; +}; + +FontChooserWidget::FontChooserWidget(TQWidget *parent, const char *name, + bool onlyFixed, const TQStringList &fontList, + int visibleListSize, bool diff, + TQButton::ToggleState *sizeIsRelativeState ) + : TQWidget(parent, name), usingFixed(onlyFixed) +{ + charsetsCombo = 0; + + TQString mainWhatsThisText = i18n( "Here you can choose the font to be used." ); + TQWhatsThis::add( this, mainWhatsThisText ); + + d = new FontChooserWidgetPrivate; + TQVBoxLayout *topLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() ); + int checkBoxGap = KDialog::spacingHint() / 2; + + int row = 0; + TQWidget *page = new TQWidget( this ); + topLayout->addWidget(page); + TQGridLayout *gridLayout = new TQGridLayout( page, 4, 3, 0, KDialog::spacingHint() ); + + // First, create the labels across the top + + TQHBoxLayout *familyLayout = new TQHBoxLayout(); + familyLayout->addSpacing( checkBoxGap ); + + if (diff) + { + familyCheckbox = new TQCheckBox(i18n("Font"), page); + + connect(familyCheckbox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(toggled_checkbox())); + + familyLayout->addWidget(familyCheckbox, 0, TQt::AlignLeft); + TQString familyCBToolTipText = + i18n("Change font family?"); + TQString familyCBWhatsThisText = + i18n("Enable this checkbox to change the font family settings."); + TQWhatsThis::add( familyCheckbox, familyCBWhatsThisText ); + TQToolTip::add( familyCheckbox, familyCBToolTipText ); + familyLabel = 0; + } + else + { + familyCheckbox = 0; + familyLabel = new TQLabel( i18n("Font:"), page, "familyLabel" ); + familyLayout->addWidget(familyLabel, 1, TQt::AlignLeft); + } + + gridLayout->addLayout(familyLayout, row, 0 ); + + TQHBoxLayout *styleLayout = new TQHBoxLayout(); + + if (diff) + { + styleCheckbox = new TQCheckBox(i18n("Style:"), page); + + connect(styleCheckbox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(toggled_checkbox())); + + styleLayout->addWidget(styleCheckbox, 0, TQt::AlignLeft); + TQString styleCBToolTipText = + i18n("Change font style?"); + TQString styleCBWhatsThisText = + i18n("Enable this checkbox to change the font style settings."); + TQWhatsThis::add( styleCheckbox, styleCBWhatsThisText ); + TQToolTip::add( styleCheckbox, styleCBToolTipText ); + styleLabel = 0; + } + else + { + styleCheckbox = 0; + styleLabel = new TQLabel( i18n("Style:"), page, "styleLabel"); + styleLayout->addWidget(styleLabel, 1, TQt::AlignLeft); + } + + styleLayout->addSpacing( checkBoxGap ); + gridLayout->addLayout(styleLayout, row, 1 ); + + TQHBoxLayout *sizeLayout = new TQHBoxLayout(); + + if (diff) + { + sizeCheckbox = new TQCheckBox(i18n("Size"),page); + + connect(sizeCheckbox, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(toggled_checkbox())); + + sizeLayout->addWidget(sizeCheckbox, 0, TQt::AlignLeft); + TQString sizeCBToolTipText = + i18n("Change font size?"); + TQString sizeCBWhatsThisText = + i18n("Enable this checkbox to change the font size settings."); + TQWhatsThis::add( sizeCheckbox, sizeCBWhatsThisText ); + TQToolTip::add( sizeCheckbox, sizeCBToolTipText ); + sizeLabel = 0; + } + else + { + sizeCheckbox = 0; + sizeLabel = new TQLabel( i18n("Size:"), page, "sizeLabel"); + sizeLayout->addWidget(sizeLabel, 1, TQt::AlignLeft); + } + + sizeLayout->addSpacing( checkBoxGap ); + sizeLayout->addSpacing( checkBoxGap ); // prevent label from eating border + gridLayout->addLayout(sizeLayout, row, 2 ); + + row ++; + + // Now create the actual boxes that hold the info + + familyListBox = new TDEListBox( page, "familyListBox"); + familyListBox->setEnabled( !diff ); + gridLayout->addWidget( familyListBox, row, 0 ); + TQString fontFamilyWhatsThisText = i18n("Here you can choose the font family to be used." ); + TQWhatsThis::add( familyListBox, fontFamilyWhatsThisText ); + TQWhatsThis::add(diff?(TQWidget *) familyCheckbox:(TQWidget *) familyLabel, fontFamilyWhatsThisText ); + + connect(familyListBox, TQ_SIGNAL(highlighted(const TQString &)), + this, TQ_SLOT(family_chosen_slot(const TQString &))); + + if(!fontList.isEmpty()) + { + familyListBox->insertStringList(fontList); + } + else + { + fillFamilyListBox(onlyFixed); + } + + familyListBox->setMinimumHeight( minimumListHeight( familyListBox, visibleListSize ) ); + + styleListBox = new TDEListBox( page, "styleListBox"); + styleListBox->setEnabled( !diff ); + gridLayout->addWidget(styleListBox, row, 1); + TQString fontStyleWhatsThisText = i18n("Here you can choose the font style to be used." ); + TQWhatsThis::add( styleListBox, fontStyleWhatsThisText ); + TQWhatsThis::add(diff?(TQWidget *)styleCheckbox:(TQWidget *)styleLabel, fontFamilyWhatsThisText ); + styleListBox->insertItem(i18n("Regular")); + styleListBox->insertItem(i18n("Italic")); + styleListBox->insertItem(i18n("Bold")); + styleListBox->insertItem(i18n("Bold Italic")); + styleListBox->setMinimumWidth( minimumListWidth( styleListBox ) ); + styleListBox->setMinimumHeight( minimumListHeight( styleListBox, visibleListSize ) ); + + connect(styleListBox, TQ_SIGNAL(highlighted(const TQString &)), + this, TQ_SLOT(style_chosen_slot(const TQString &))); + + sizeListBox = new TDEListBox( page, "sizeListBox"); + sizeOfFont = new KIntNumInput( page, "sizeOfFont"); + sizeOfFont->setMinValue(4); + + sizeListBox->setEnabled( !diff ); + sizeOfFont->setEnabled( !diff ); + + if( sizeIsRelativeState ) + { + TQString sizeIsRelativeCBText = + i18n("Relative"); + TQString sizeIsRelativeCBToolTipText = + i18n("Font size
    fixed or relative
    to environment"); + TQString sizeIsRelativeCBWhatsThisText = + i18n("Here you can switch between a fixed font size and a font size " + "to be calculated dynamically and adjusted to any changing " + "environment (e.g. widget dimensions, paper size)." ); + sizeIsRelativeCheckBox = new TQCheckBox( sizeIsRelativeCBText, + page, + "sizeIsRelativeCheckBox" ); + sizeIsRelativeCheckBox->setTristate( diff ); + TQGridLayout *sizeLayout2 = new TQGridLayout( 3,2, KDialog::spacingHint()/2, "sizeLayout2" ); + gridLayout->addLayout(sizeLayout2, row, 2); + sizeLayout2->setColStretch( 1, 1 ); // to prevent text from eating the right border + sizeLayout2->addMultiCellWidget( sizeOfFont, 0, 0, 0, 1); + sizeLayout2->addMultiCellWidget(sizeListBox, 1,1, 0,1); + sizeLayout2->addWidget(sizeIsRelativeCheckBox, 2, 0, TQt::AlignLeft); + TQWhatsThis::add( sizeIsRelativeCheckBox, sizeIsRelativeCBWhatsThisText ); + TQToolTip::add( sizeIsRelativeCheckBox, sizeIsRelativeCBToolTipText ); + } + else + { + sizeIsRelativeCheckBox = 0L; + TQGridLayout *sizeLayout2 = new TQGridLayout( 2,1, KDialog::spacingHint()/2, "sizeLayout2" ); + gridLayout->addLayout(sizeLayout2, row, 2); + sizeLayout2->addWidget( sizeOfFont, 0, 0); + sizeLayout2->addMultiCellWidget(sizeListBox, 1,1, 0,0); + } + + TQString fontSizeWhatsThisText = i18n("Here you can choose the font size to be used." ); + TQWhatsThis::add( sizeListBox, fontSizeWhatsThisText ); + TQWhatsThis::add( diff?(TQWidget *)sizeCheckbox:(TQWidget *)sizeLabel, fontSizeWhatsThisText ); + + fillSizeList(); + sizeListBox->setMinimumWidth( minimumListWidth(sizeListBox) + + sizeListBox->fontMetrics().maxWidth() ); + sizeListBox->setMinimumHeight( minimumListHeight( sizeListBox, visibleListSize ) ); + + connect( sizeOfFont, TQ_SIGNAL( valueChanged(int) ), + this, TQ_SLOT(size_value_slot(int))); + + connect( sizeListBox, TQ_SIGNAL(highlighted(const TQString&)), + this, TQ_SLOT(size_chosen_slot(const TQString&)) ); + + sizeListBox->setSelected(sizeListBox->findItem(TQString::number(10)), true); // default to 10pt. + + row ++; + + row ++; + + TQVBoxLayout *vbox; + page = new TQWidget( this ); + topLayout->addWidget(page); + vbox = new TQVBoxLayout( page, 0, KDialog::spacingHint() ); + TQLabel *label = new TQLabel( i18n("Actual Font"), page ); + vbox->addWidget( label ); + + xlfdEdit = new TQLineEdit( page, "xlfdEdit" ); + vbox->addWidget( xlfdEdit ); + + // lets initialize the display if possible + setFont( TDEGlobalSettings::generalFont(), usingFixed ); + + // check or uncheck or gray out the "relative" checkbox + if( sizeIsRelativeState && sizeIsRelativeCheckBox ) + setSizeIsRelative( *sizeIsRelativeState ); + + TDEConfig *config = TDEGlobal::config(); + TDEConfigGroupSaver saver(config, TQString::fromLatin1("General")); + showXLFDArea(config->readBoolEntry(TQString::fromLatin1("fontSelectorShowXLFD"), false)); +} + +FontChooserWidget::~FontChooserWidget() +{ + delete d; +} + +int FontChooserWidget::minimumListWidth( const TQListBox *list ) +{ + int w=0; + + for( uint i=0; icount(); i++ ) + { + int itemWidth = list->item(i)->width(list); + w = TQMAX(w,itemWidth); + } + + if( w == 0 ) + { + w = 40; + } + + w += list->frameWidth() * 2; + w += list->verticalScrollBar()->sizeHint().width(); + return w; +} + +int FontChooserWidget::minimumListHeight( const TQListBox *list, int numVisibleEntry ) +{ + int w = list->count() > 0 ? list->item(0)->height(list) : + list->fontMetrics().lineSpacing(); + + if( w < 0 ) { w = 10; } + if( numVisibleEntry <= 0 ) { numVisibleEntry = 4; } + return ( w * numVisibleEntry + 2 * list->frameWidth() ); +} + +void FontChooserWidget::fillSizeList() +{ + if(! sizeListBox) return; //assertion. + + static const int c[] = + { + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + 16, 17, 18, 19, + 20, 22, 24, 26, + 28, 32, 48, 64, + 72, 80, 94, 102, + 116, 128, 132, 148, + 156, 164, 172, 188, + 192, 202, 212, 224, + 232, 240, 248, 256, + 0 + }; + + for(int i = 0; c[i]; ++i) + { + sizeListBox->insertItem(TQString::number(c[i])); + } +} + +void FontChooserWidget::setColor( const TQColor & col ) +{ + d->m_palette.setColor( TQPalette::Active, TQColorGroup::Text, col ); +} + +TQColor FontChooserWidget::color() const +{ + return d->m_palette.color( TQPalette::Active, TQColorGroup::Text ); +} + +void FontChooserWidget::setBackgroundColor( const TQColor & col ) +{ + d->m_palette.setColor( TQPalette::Active, TQColorGroup::Base, col ); +} + +TQColor FontChooserWidget::backgroundColor() const +{ + return d->m_palette.color( TQPalette::Active, TQColorGroup::Base ); +} + +void FontChooserWidget::setSizeIsRelative( TQButton::ToggleState relative ) +{ + // check or uncheck or gray out the "relative" checkbox + if( sizeIsRelativeCheckBox ) + { + if( TQButton::NoChange == relative ) + sizeIsRelativeCheckBox->setNoChange(); + else + sizeIsRelativeCheckBox->setChecked( TQButton::On == relative ); + } +} + +TQButton::ToggleState FontChooserWidget::sizeIsRelative() const +{ + return sizeIsRelativeCheckBox + ? sizeIsRelativeCheckBox->state() + : TQButton::NoChange; +} + +TQSize FontChooserWidget::sizeHint( void ) const +{ + return minimumSizeHint(); +} + +void FontChooserWidget::enableColumn( int column, bool state ) +{ + if( column & FamilyList ) + { + familyListBox->setEnabled(state); + } + if( column & StyleList ) + { + styleListBox->setEnabled(state); + } + if( column & SizeList ) + { + sizeListBox->setEnabled(state); + } +} + +void FontChooserWidget::setFont( const TQFont& aFont, bool onlyFixed ) +{ + selFont = aFont; + selectedSize=aFont.pointSize(); + if (selectedSize == -1) + selectedSize = TQFontInfo(aFont).pointSize(); + + if( onlyFixed != usingFixed) + { + usingFixed = onlyFixed; + fillFamilyListBox(usingFixed); + } + setupDisplay(); + displaySample(selFont); +} + +int FontChooserWidget::fontDiffFlags() { + int diffFlags = 0; + if (familyCheckbox && styleCheckbox && sizeCheckbox) + { + diffFlags = (int)(familyCheckbox->isChecked() ? FontDiffFamily : 0) + | (int)( styleCheckbox->isChecked() ? FontDiffStyle : 0) + | (int)( sizeCheckbox->isChecked() ? FontDiffSize : 0); + } + return diffFlags; +} + +void FontChooserWidget::toggled_checkbox() +{ + familyListBox->setEnabled( familyCheckbox->isChecked() ); + styleListBox->setEnabled( styleCheckbox->isChecked() ); + sizeListBox->setEnabled( sizeCheckbox->isChecked() ); + sizeOfFont->setEnabled( sizeCheckbox->isChecked() ); +} + +void FontChooserWidget::family_chosen_slot(const TQString& family) +{ + TQFontDatabase dbase; + TQStringList styles = TQStringList(dbase.styles(family)); + styleListBox->clear(); + currentStyles.clear(); + TQStringList::Iterator end(styles.end()); + for ( TQStringList::Iterator it = styles.begin(); it != end; ++it ) + { + TQString style = *it; + int pos = style.find("Plain"); + if(pos >=0) style = style.replace(pos,5,i18n("Regular")); + pos = style.find("Normal"); + if(pos >=0) style = style.replace(pos,6,i18n("Regular")); + pos = style.find("Oblique"); + if(pos >=0) style = style.replace(pos,7,i18n("Italic")); + if(!styleListBox->findItem(style)) + { + styleListBox->insertItem(i18n(style.utf8())); + currentStyles.insert(i18n(style.utf8()), *it); + } + } + if(styleListBox->count()==0) + { + styleListBox->insertItem(i18n("Regular")); + currentStyles.insert(i18n("Regular"), "Normal"); + } + + styleListBox->blockSignals(true); + TQListBoxItem *item = styleListBox->findItem(selectedStyle); + if (item) + styleListBox->setSelected(styleListBox->findItem(selectedStyle), true); + else + styleListBox->setSelected(0, true); + styleListBox->blockSignals(false); + + style_chosen_slot(TQString()); +} + +void FontChooserWidget::size_chosen_slot(const TQString& size) +{ + selectedSize=size.toInt(); + sizeOfFont->setValue(selectedSize); + selFont.setPointSize(selectedSize); + emit fontSelected(selFont); +} + +void FontChooserWidget::size_value_slot(int val) +{ + selFont.setPointSize(val); + emit fontSelected(selFont); +} + +void FontChooserWidget::style_chosen_slot(const TQString& style) +{ + TQString currentStyle; + if (style.isEmpty()) + currentStyle = styleListBox->currentText(); + else + currentStyle = style; + + int diff=0; // the difference between the font size requested and what we can show. + + sizeListBox->clear(); + TQFontDatabase dbase; + if(dbase.isSmoothlyScalable(familyListBox->currentText(), currentStyles[currentStyle])) + { // is vector font + //sampleEdit->setPaletteBackgroundPixmap( VectorPixmap ); // TODO + fillSizeList(); + } + else + { // is bitmap font. + //sampleEdit->setPaletteBackgroundPixmap( BitmapPixmap ); // TODO + TQValueList sizes = dbase.smoothSizes(familyListBox->currentText(), currentStyles[currentStyle]); + if(sizes.count() > 0) + { + TQValueList::iterator it; + diff=1000; + TQValueList::iterator end(sizes.end()); + for ( it = sizes.begin(); it != end; ++it ) + { + if(*it <= selectedSize || diff > *it - selectedSize) diff = selectedSize - *it; + sizeListBox->insertItem(TQString::number(*it)); + } + } + else // there are times TQT does not provide the list.. + fillSizeList(); + } + sizeListBox->blockSignals(true); + sizeListBox->setSelected(sizeListBox->findItem(TQString::number(selectedSize)), true); + sizeListBox->blockSignals(false); + sizeListBox->ensureCurrentVisible(); + + selFont = dbase.font(familyListBox->currentText(), currentStyles[currentStyle], selectedSize-diff); + emit fontSelected(selFont); + if (!style.isEmpty()) + selectedStyle = style; +} + +void FontChooserWidget::displaySample(const TQFont& font) +{ + xlfdEdit->setText(font.rawName()); + xlfdEdit->setCursorPosition(0); +} + +void FontChooserWidget::setupDisplay() +{ + // Calling familyListBox->setCurrentItem() causes the value of selFont + // to change, so we save the family, style and size beforehand. + TQString family = selFont.family().lower(); + int style = (selFont.bold() ? 2 : 0) + (selFont.italic() ? 1 : 0); + int size = selFont.pointSize(); + if (size == -1) + size = TQFontInfo(selFont).pointSize(); + TQString sizeStr = TQString::number(size); + + int numEntries, i; + + numEntries = familyListBox->count(); + for (i = 0; i < numEntries; i++) + { + if (family == familyListBox->text(i).lower()) + { + familyListBox->setCurrentItem(i); + break; + } + } + + // 1st Fallback + if ( (i == numEntries) ) + { + if (family.contains('[')) + { + family = family.left(family.find('[')).stripWhiteSpace(); + for (i = 0; i < numEntries; i++) + { + if (family == familyListBox->text(i).lower()) + { + familyListBox->setCurrentItem(i); + break; + } + } + } + } + + // 2nd Fallback + if ( (i == numEntries) ) + { + TQString fallback = family+" ["; + for (i = 0; i < numEntries; i++) + { + if (familyListBox->text(i).lower().startsWith(fallback)) + { + familyListBox->setCurrentItem(i); + break; + } + } + } + + // 3rd Fallback + if ( (i == numEntries) ) + { + for (i = 0; i < numEntries; i++) + { + if (familyListBox->text(i).lower().startsWith(family)) + { + familyListBox->setCurrentItem(i); + break; + } + } + } + + // Fall back in case nothing matched. Otherwise, diff doesn't work + if ( i == numEntries ) + familyListBox->setCurrentItem( 0 ); + + styleListBox->setCurrentItem(style); + + numEntries = sizeListBox->count(); + for (i = 0; i < numEntries; i++) + { + if (sizeStr == sizeListBox->text(i)) + { + sizeListBox->setCurrentItem(i); + break; + } + } + + sizeOfFont->setValue(size); +} + +void FontChooserWidget::getFontList( TQStringList &list, uint fontListCriteria) +{ + TQFontDatabase dbase; + TQStringList lstSys(dbase.families()); + + // if we have criteria; then check fonts before adding + if (fontListCriteria) + { + TQStringList lstFonts; + TQStringList::Iterator end(lstSys.end()); + for (TQStringList::Iterator it = lstSys.begin(); it != end; ++it) + { + if ((fontListCriteria & FixedWidthFonts) > 0 && !dbase.isFixedPitch(*it)) continue; + if (((fontListCriteria & (SmoothScalableFonts | ScalableFonts)) == ScalableFonts) && + !dbase.isBitmapScalable(*it)) continue; + if ((fontListCriteria & SmoothScalableFonts) > 0 && !dbase.isSmoothlyScalable(*it)) continue; + lstFonts.append(*it); + } + + if((fontListCriteria & FixedWidthFonts) > 0) { + // Fallback.. if there are no fixed fonts found, it is probably a + // bug in the font server or TQt. In this case, just use 'fixed' + if (lstFonts.count() == 0) + lstFonts.append("fixed"); + } + + lstSys = lstFonts; + } + + lstSys.sort(); + + list = lstSys; +} + +void FontChooserWidget::addFont( TQStringList &list, const char *xfont ) +{ + const char *ptr = strchr( xfont, '-' ); + if ( !ptr ) + return; + + ptr = strchr( ptr + 1, '-' ); + if ( !ptr ) + return; + + TQString font = TQString::fromLatin1(ptr + 1); + + int pos; + if ( ( pos = font.find( '-' ) ) > 0 ) { + font.truncate( pos ); + + if ( font.find( TQString::fromLatin1("open look"), 0, false ) >= 0 ) + return; + + TQStringList::Iterator it = list.begin(); + TQStringList::Iterator end(list.end()); + for ( ; it != end; ++it ) + if ( *it == font ) + return; + list.append( font ); + } +} + +void FontChooserWidget::fillFamilyListBox(bool onlyFixedFonts) +{ + TQStringList fontList; + getFontList(fontList, onlyFixedFonts?FixedWidthFonts:0); + familyListBox->clear(); + familyListBox->insertStringList(fontList); +} + +void FontChooserWidget::showXLFDArea(bool show) +{ + if( show ) + { + xlfdEdit->parentWidget()->show(); + } + else + { + xlfdEdit->parentWidget()->hide(); + } +} + +} // NameSpace DigikamInsertTextImagesPlugin + diff --git a/src/imageplugins/inserttext/fontchooserwidget.h b/src/imageplugins/inserttext/fontchooserwidget.h new file mode 100644 index 00000000..4585c231 --- /dev/null +++ b/src/imageplugins/inserttext/fontchooserwidget.h @@ -0,0 +1,172 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a simple widget to choose a font based on + * KDE FontChooserWidget implementation. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 1999 by Preston Brown + * Copyright (C) 1997 by Bernd Johannes Wuebben + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef FONT_CHOOSER_WIDGET_H +#define FONT_CHOOSER_WIDGET_H + +#include +#include + +class TQComboBox; +class TQCheckBox; +class TQFont; +class TQGroupBox; +class TQLabel; +class TQStringList; + +class TDEListBox; +class KIntNumInput; + +namespace DigikamInsertTextImagesPlugin +{ + +class FontChooserWidget : public TQWidget +{ + TQ_OBJECT + + TQ_PROPERTY( TQFont font READ font WRITE setFont ) + +public: + + enum FontColumn + { + FamilyList=0x01, + StyleList=0x02, + SizeList=0x04 + }; + + enum FontDiff + { + FontDiffFamily=0x01, + FontDiffStyle=0x02, + FontDiffSize=0x04 + }; + + enum FontListCriteria + { + FixedWidthFonts=0x01, + ScalableFonts=0x02, + SmoothScalableFonts=0x04 + }; + +public: + + FontChooserWidget(TQWidget *parent = 0L, const char *name = 0L, + bool onlyFixed = false, + const TQStringList &fontList = TQStringList(), + int visibleListSize=8, + bool diff = false, + TQButton::ToggleState *sizeIsRelativeState = 0L ); + + ~FontChooserWidget(); + + void setFont( const TQFont &font, bool onlyFixed = false ); + void setColor( const TQColor & col ); + void setBackgroundColor( const TQColor & col ); + void setSizeIsRelative( TQButton::ToggleState relative ); + + TQFont font() const { return selFont; }; + TQColor color() const; + TQColor backgroundColor() const; + static void getFontList( TQStringList &list, uint fontListCriteria); + TQButton::ToggleState sizeIsRelative() const; + static TQString getXLFD( const TQFont &theFont ) { return theFont.rawName(); }; + + int fontDiffFlags(); + void enableColumn( int column, bool state ); + virtual TQSize sizeHint( void ) const; + +signals: + + void fontSelected( const TQFont &font ); + +private slots: + + void toggled_checkbox(); + void family_chosen_slot(const TQString&); + void size_chosen_slot(const TQString&); + void style_chosen_slot(const TQString&); + void displaySample(const TQFont &font); + void showXLFDArea(bool); + void size_value_slot(int); + +private: + + void fillFamilyListBox(bool onlyFixedFonts = false); + void fillSizeList(); + + void addFont( TQStringList &list, const char *xfont ); + + void setupDisplay(); + + int minimumListWidth( const TQListBox *list ); + int minimumListHeight( const TQListBox *list, int numVisibleEntry ); + +private: + + bool usingFixed; + int selectedSize; + + TQMap currentStyles; + + // pointer to an optinally supplied list of fonts to + // inserted into the fontdialog font-family combo-box + TQStringList fontList; + + TQLineEdit *xlfdEdit; + + TQLabel *familyLabel; + TQLabel *styleLabel; + + TQCheckBox *familyCheckbox; + TQCheckBox *styleCheckbox; + TQCheckBox *sizeCheckbox; + TQCheckBox *sizeIsRelativeCheckBox; + + TQComboBox *charsetsCombo; + + TQFont selFont; + + TQString selectedStyle; + + TQLabel *sizeLabel; + + KIntNumInput *sizeOfFont; + + TDEListBox *familyListBox; + TDEListBox *styleListBox; + TDEListBox *sizeListBox; + +private: + + class FontChooserWidgetPrivate; + FontChooserWidgetPrivate *d; + +}; + +} // NameSpace DigikamInsertTextImagesPlugin + +#endif // FONT_CHOOSER_WIDGET_H diff --git a/src/imageplugins/inserttext/imageeffect_inserttext.cpp b/src/imageplugins/inserttext/imageeffect_inserttext.cpp new file mode 100644 index 00000000..3f88644b --- /dev/null +++ b/src/imageplugins/inserttext/imageeffect_inserttext.cpp @@ -0,0 +1,348 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "inserttextwidget.h" +#include "fontchooserwidget.h" +#include "imageeffect_inserttext.h" +#include "imageeffect_inserttext.moc" + +namespace DigikamInsertTextImagesPlugin +{ + +ImageEffect_InsertText::ImageEffect_InsertText(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Insert Text on Photograph"), + "inserttext", false, false) +{ + TQString whatsThis; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Insert Text"), + digikam_version, + I18N_NOOP("A digiKam image plugin for inserting text on a photograph."), + TDEAboutData::License_GPL, + "(c) 2005-2006, Gilles Caulier\n" + "(c) 2006-2007, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(plainPage()); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* l = new TQVBoxLayout(frame, 5, 0); + m_previewWidget = new InsertTextWidget(480, 320, frame); + l->addWidget(m_previewWidget); + TQWhatsThis::add( m_previewWidget, i18n("

    This previews the text inserted in the image. " + "You can use the mouse to move the text to the right location.")); + setPreviewAreaWidget(frame); + + // ------------------------------------------------------------- + + TQWidget *gbox2 = new TQWidget(plainPage()); + TQGridLayout *gridBox2 = new TQGridLayout( gbox2, 9, 1, spacingHint()); + + m_textEdit = new KTextEdit(gbox2); + m_textEdit->setCheckSpellingEnabled(true); + m_textEdit->setWordWrap(TQTextEdit::NoWrap); + TQWhatsThis::add( m_textEdit, i18n("

    Here, enter the text you want to insert in your image.")); + gridBox2->addMultiCellWidget(m_textEdit, 0, 2, 0, 1); + + // ------------------------------------------------------------- + + m_fontChooserWidget = new FontChooserWidget(gbox2); + TQWhatsThis::add( m_textEdit, i18n("

    Here you can choose the font to be used.")); + gridBox2->addMultiCellWidget(m_fontChooserWidget, 3, 3, 0, 1); + + // ------------------------------------------------------------- + + TDEIconLoader icon; + m_alignButtonGroup = new TQHButtonGroup(gbox2); + + TQPushButton *alignLeft = new TQPushButton( m_alignButtonGroup ); + m_alignButtonGroup->insert(alignLeft, ALIGN_LEFT); + alignLeft->setPixmap( icon.loadIcon( "format-text-direction-ltr", (TDEIcon::Group)TDEIcon::Small ) ); + alignLeft->setToggleButton(true); + TQToolTip::add( alignLeft, i18n( "Align text to the left" ) ); + + TQPushButton *alignRight = new TQPushButton( m_alignButtonGroup ); + m_alignButtonGroup->insert(alignRight, ALIGN_RIGHT); + alignRight->setPixmap( icon.loadIcon( "format-text-direction-rtl", (TDEIcon::Group)TDEIcon::Small ) ); + alignRight->setToggleButton(true); + TQToolTip::add( alignRight, i18n( "Align text to the right" ) ); + + TQPushButton *alignCenter = new TQPushButton( m_alignButtonGroup ); + m_alignButtonGroup->insert(alignCenter, ALIGN_CENTER); + alignCenter->setPixmap( icon.loadIcon( "text_center", (TDEIcon::Group)TDEIcon::Small ) ); + alignCenter->setToggleButton(true); + TQToolTip::add( alignCenter, i18n( "Align text to center" ) ); + + TQPushButton *alignBlock = new TQPushButton( m_alignButtonGroup ); + m_alignButtonGroup->insert(alignBlock, ALIGN_BLOCK); + alignBlock->setPixmap( icon.loadIcon( "text_block", (TDEIcon::Group)TDEIcon::Small ) ); + alignBlock->setToggleButton(true); + TQToolTip::add( alignBlock, i18n( "Align text to a block" ) ); + + m_alignButtonGroup->setExclusive(true); + m_alignButtonGroup->setFrameShape(TQFrame::NoFrame); + gridBox2->addMultiCellWidget(m_alignButtonGroup, 4, 4, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Rotation:"), gbox2); + m_textRotation = new TQComboBox( false, gbox2 ); + m_textRotation->insertItem( i18n("None") ); + m_textRotation->insertItem( i18n("90 Degrees") ); + m_textRotation->insertItem( i18n("180 Degrees") ); + m_textRotation->insertItem( i18n("270 Degrees") ); + TQWhatsThis::add( m_textRotation, i18n("

    Select the text rotation to use.")); + gridBox2->addMultiCellWidget(label1, 5, 5, 0, 0); + gridBox2->addMultiCellWidget(m_textRotation, 5, 5, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Color:"), gbox2); + m_fontColorButton = new KColorButton( TQt::black, gbox2 ); + TQWhatsThis::add( m_fontColorButton, i18n("

    Select the font color to use.")); + gridBox2->addMultiCellWidget(label2, 6, 6, 0, 0); + gridBox2->addMultiCellWidget(m_fontColorButton, 6, 6, 1, 1); + + // ------------------------------------------------------------- + + m_borderText = new TQCheckBox( i18n( "Add border"), gbox2 ); + TQToolTip::add( m_borderText, i18n( "Add a solid border around text using current text color" ) ); + + m_transparentText = new TQCheckBox( i18n( "Semi-transparent"), gbox2 ); + TQToolTip::add( m_transparentText, i18n( "Use semi-transparent text background under image" ) ); + + gridBox2->addMultiCellWidget(m_borderText, 7, 7, 0, 1); + gridBox2->addMultiCellWidget(m_transparentText, 8, 8, 0, 1); + gridBox2->setRowStretch(9, 10); + + setUserAreaWidget(gbox2); + + // ------------------------------------------------------------- + + connect(m_fontChooserWidget, TQ_SIGNAL(fontSelected (const TQFont &)), + this, TQ_SLOT(slotFontPropertiesChanged(const TQFont &))); + + connect(m_fontColorButton, TQ_SIGNAL(changed(const TQColor &)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_textEdit, TQ_SIGNAL(textChanged()), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_alignButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotAlignModeChanged(int))); + + connect(m_borderText, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_transparentText, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_textRotation, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(this, TQ_SIGNAL(signalUpdatePreview()), TQ_SLOT(slotUpdatePreview())); + // ------------------------------------------------------------- + + slotUpdatePreview(); +} + +ImageEffect_InsertText::~ImageEffect_InsertText() +{ +} + +void ImageEffect_InsertText::readUserSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("inserttext Tool Dialog"); + TQColor black(0, 0, 0); + TQFont defaultFont; + + int orgW = m_previewWidget->imageIface()->originalWidth(); + int orgH = m_previewWidget->imageIface()->originalHeight(); + + if ( orgW > orgH ) m_defaultSizeFont = (int)(orgH / 8.0); + else m_defaultSizeFont = (int)(orgW / 8.0); + + defaultFont.setPointSize(m_defaultSizeFont); + m_textRotation->setCurrentItem( config->readNumEntry("Text Rotation", 0) ); + m_fontColorButton->setColor(config->readColorEntry("Font Color", &black)); + m_textEdit->setText(config->readEntry("Text String", i18n("Enter your text here!"))); + m_textFont = config->readFontEntry("Font Properties", &defaultFont); + m_fontChooserWidget->setFont(m_textFont); + m_alignTextMode = config->readNumEntry("Text Alignment", ALIGN_LEFT); + m_borderText->setChecked( config->readBoolEntry("Border Text", false) ); + m_transparentText->setChecked( config->readBoolEntry("Transparent Text", false) ); + m_previewWidget->setPositionHint( config->readRectEntry("Position Hint") ); + + static_cast(m_alignButtonGroup->find(m_alignTextMode))->setOn(true); + slotAlignModeChanged(m_alignTextMode); +} + +void ImageEffect_InsertText::writeUserSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("inserttext Tool Dialog"); + + config->writeEntry( "Text Rotation", m_textRotation->currentItem() ); + config->writeEntry( "Font Color", m_fontColorButton->color() ); + config->writeEntry( "Text String", m_textEdit->text() ); + config->writeEntry( "Font Properties", m_textFont ); + config->writeEntry( "Text Alignment", m_alignTextMode ); + config->writeEntry( "Border Text", m_borderText->isChecked() ); + config->writeEntry( "Transparent Text", m_transparentText->isChecked() ); + config->writeEntry( "Position Hint", m_previewWidget->getPositionHint() ); + + config->sync(); +} + +void ImageEffect_InsertText::resetValues() +{ + m_fontColorButton->blockSignals(true); + m_alignButtonGroup->blockSignals(true); + m_fontChooserWidget->blockSignals(true); + + m_textRotation->setCurrentItem(0); // No rotation. + m_fontColorButton->setColor(TQt::black); + TQFont defaultFont; + m_textFont = defaultFont; // Reset to default TDE font. + m_textFont.setPointSize( m_defaultSizeFont ); + m_fontChooserWidget->setFont(m_textFont); + m_borderText->setChecked( false ); + m_transparentText->setChecked( false ); + m_previewWidget->resetEdit(); + static_cast(m_alignButtonGroup->find(ALIGN_LEFT))->setOn(true); + + m_fontChooserWidget->blockSignals(false); + m_fontColorButton->blockSignals(false); + m_alignButtonGroup->blockSignals(false); + slotAlignModeChanged(ALIGN_LEFT); +} + +void ImageEffect_InsertText::slotAlignModeChanged(int mode) +{ + m_alignTextMode = mode; + m_textEdit->selectAll(true); + + switch (m_alignTextMode) + { + case ALIGN_LEFT: + m_textEdit->setAlignment( TQt::AlignLeft ); + break; + + case ALIGN_RIGHT: + m_textEdit->setAlignment( TQt::AlignRight ); + break; + + case ALIGN_CENTER: + m_textEdit->setAlignment( TQt::AlignHCenter ); + break; + + case ALIGN_BLOCK: + m_textEdit->setAlignment( TQt::AlignJustify ); + break; + } + + m_textEdit->selectAll(false); + emit signalUpdatePreview(); +} + +void ImageEffect_InsertText::slotFontPropertiesChanged(const TQFont &font) +{ + m_textFont = font; + emit signalUpdatePreview(); +} + +void ImageEffect_InsertText::slotUpdatePreview() +{ + m_previewWidget->setText(m_textEdit->text(), m_textFont, m_fontColorButton->color(), m_alignTextMode, + m_borderText->isChecked(), m_transparentText->isChecked(), + m_textRotation->currentItem()); +} + +void ImageEffect_InsertText::finalRendering() +{ + accept(); + kapp->setOverrideCursor( KCursor::waitCursor() ); + + Digikam::ImageIface iface(0, 0); + Digikam::DImg dest = m_previewWidget->makeInsertText(); + iface.putOriginalImage(i18n("Insert Text"), dest.bits(), dest.width(), dest.height()); + + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamInsertTextImagesPlugin + diff --git a/src/imageplugins/inserttext/imageeffect_inserttext.h b/src/imageplugins/inserttext/imageeffect_inserttext.h new file mode 100644 index 00000000..2b7c204c --- /dev/null +++ b/src/imageplugins/inserttext/imageeffect_inserttext.h @@ -0,0 +1,103 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_INSERTEXT_H +#define IMAGEEFFECT_INSERTEXT_H + +// TQt includes. + +#include +#include + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQLabel; +class TQFont; +class TQHButtonGroup; +class TQComboBox; +class TQCheckBox; + +class KTextEdit; +class KColorButton; + +namespace DigikamInsertTextImagesPlugin +{ + +class InsertTextWidget; +class FontChooserWidget; + +class ImageEffect_InsertText : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_InsertText(TQWidget *parent); + ~ImageEffect_InsertText(); + +signals: + + void signalUpdatePreview(); + +private slots: + + void slotFontPropertiesChanged(const TQFont &font); + void slotUpdatePreview(); + void slotAlignModeChanged(int mode); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + +private: + + int m_alignTextMode; + int m_defaultSizeFont; + + TQComboBox *m_textRotation; + + TQCheckBox *m_borderText; + TQCheckBox *m_transparentText; + + TQHButtonGroup *m_alignButtonGroup; + + TQFont m_textFont; + + KColorButton *m_fontColorButton; + + FontChooserWidget *m_fontChooserWidget; + + KTextEdit *m_textEdit; + + InsertTextWidget *m_previewWidget; +}; + +} // NameSpace DigikamInsertTextImagesPlugin + +#endif /* IMAGEEFFECT_INSERTEXT_H */ diff --git a/src/imageplugins/inserttext/imageplugin_inserttext.cpp b/src/imageplugins/inserttext/imageplugin_inserttext.cpp new file mode 100644 index 00000000..a7a5ab6b --- /dev/null +++ b/src/imageplugins/inserttext/imageplugin_inserttext.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "inserttexttool.h" +#include "imageplugin_inserttext.h" +#include "imageplugin_inserttext.moc" + +using namespace DigikamInsertTextImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_inserttext, + KGenericFactory("digikamimageplugin_inserttext")); + +ImagePlugin_InsertText::ImagePlugin_InsertText(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_InsertText") +{ + m_insertTextAction = new TDEAction(i18n("Insert Text..."), "inserttext", + SHIFT+CTRL+Key_T, + this, TQ_SLOT(slotInsertText()), + actionCollection(), "imageplugin_inserttext"); + + setXMLFile("digikamimageplugin_inserttext_ui.rc"); + + DDebug() << "ImagePlugin_InsertText plugin loaded" << endl; +} + +ImagePlugin_InsertText::~ImagePlugin_InsertText() +{ +} + +void ImagePlugin_InsertText::setEnabledActions(bool enable) +{ + m_insertTextAction->setEnabled(enable); +} + +void ImagePlugin_InsertText::slotInsertText() +{ + InsertTextTool *tool = new InsertTextTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/inserttext/imageplugin_inserttext.h b/src/imageplugins/inserttext/imageplugin_inserttext.h new file mode 100644 index 00000000..427b47c6 --- /dev/null +++ b/src/imageplugins/inserttext/imageplugin_inserttext.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_INSERTTEXT_H +#define IMAGEPLUGIN_INSERTTEXT_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_InsertText : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_InsertText(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_InsertText(); + + void setEnabledActions(bool enable); + +private slots: + + void slotInsertText(); + +private: + + TDEAction *m_insertTextAction; +}; + +#endif /* IMAGEPLUGIN_INSERTTEXT_H */ diff --git a/src/imageplugins/inserttext/inserttexttool.cpp b/src/imageplugins/inserttext/inserttexttool.cpp new file mode 100644 index 00000000..3c854896 --- /dev/null +++ b/src/imageplugins/inserttext/inserttexttool.cpp @@ -0,0 +1,336 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "fontchooserwidget.h" +#include "imageiface.h" +#include "inserttextwidget.h" +#include "inserttexttool.h" +#include "inserttexttool.moc" + +using namespace Digikam; + +namespace DigikamInsertTextImagesPlugin +{ + +InsertTextTool::InsertTextTool(TQObject* parent) + : EditorTool(parent) +{ + setName("inserttext"); + setToolName(i18n("Insert Text")); + setToolIcon(SmallIcon("inserttext")); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(0); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* l = new TQVBoxLayout(frame, 5, 0); + m_previewWidget = new InsertTextWidget(480, 320, frame); + l->addWidget(m_previewWidget); + TQWhatsThis::add(m_previewWidget, i18n("

    This previews the text inserted in the image. " + "You can use the mouse to move the text to the right location.")); + setToolView(frame); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout *grid = new TQGridLayout(m_gboxSettings->plainPage(), 9, 1); + + m_textEdit = new KTextEdit(m_gboxSettings->plainPage()); + m_textEdit->setCheckSpellingEnabled(true); + m_textEdit->setWordWrap(TQTextEdit::NoWrap); + TQWhatsThis::add(m_textEdit, i18n("

    Here, enter the text you want to insert in your image.")); + + // ------------------------------------------------------------- + + m_fontChooserWidget = new FontChooserWidget(m_gboxSettings->plainPage()); + TQWhatsThis::add( m_textEdit, i18n("

    Here you can choose the font to be used.")); + + // ------------------------------------------------------------- + + TDEIconLoader icon; + m_alignButtonGroup = new TQHButtonGroup(m_gboxSettings->plainPage()); + + TQPushButton *alignLeft = new TQPushButton(m_alignButtonGroup); + m_alignButtonGroup->insert(alignLeft, ALIGN_LEFT); + alignLeft->setPixmap(icon.loadIcon("format-text-direction-ltr", (TDEIcon::Group) TDEIcon::Small)); + alignLeft->setToggleButton(true); + TQToolTip::add(alignLeft, i18n("Align text to the left")); + + TQPushButton *alignRight = new TQPushButton(m_alignButtonGroup); + m_alignButtonGroup->insert(alignRight, ALIGN_RIGHT); + alignRight->setPixmap(icon.loadIcon("format-text-direction-rtl", (TDEIcon::Group) TDEIcon::Small)); + alignRight->setToggleButton(true); + TQToolTip::add(alignRight, i18n("Align text to the right")); + + TQPushButton *alignCenter = new TQPushButton(m_alignButtonGroup); + m_alignButtonGroup->insert(alignCenter, ALIGN_CENTER); + alignCenter->setPixmap(icon.loadIcon("text_center", (TDEIcon::Group) TDEIcon::Small)); + alignCenter->setToggleButton(true); + TQToolTip::add(alignCenter, i18n("Align text to center")); + + TQPushButton *alignBlock = new TQPushButton(m_alignButtonGroup); + m_alignButtonGroup->insert(alignBlock, ALIGN_BLOCK); + alignBlock->setPixmap(icon.loadIcon("text_block", (TDEIcon::Group) TDEIcon::Small)); + alignBlock->setToggleButton(true); + TQToolTip::add(alignBlock, i18n("Align text to a block")); + + m_alignButtonGroup->setExclusive(true); + m_alignButtonGroup->setFrameShape(TQFrame::NoFrame); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Rotation:"), m_gboxSettings->plainPage()); + m_textRotation = new TQComboBox(false, m_gboxSettings->plainPage()); + m_textRotation->insertItem(i18n("None")); + m_textRotation->insertItem(i18n("90 Degrees")); + m_textRotation->insertItem(i18n("180 Degrees")); + m_textRotation->insertItem(i18n("270 Degrees")); + TQWhatsThis::add( m_textRotation, i18n("

    Select the text rotation to use.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Color:"), m_gboxSettings->plainPage()); + m_fontColorButton = new KColorButton( TQt::black, m_gboxSettings->plainPage() ); + TQWhatsThis::add( m_fontColorButton, i18n("

    Select the font color to use.")); + + // ------------------------------------------------------------- + + m_borderText = new TQCheckBox(i18n("Add border"), m_gboxSettings->plainPage()); + TQToolTip::add(m_borderText, i18n("Add a solid border around text using current text color")); + + m_transparentText = new TQCheckBox(i18n("Semi-transparent"), m_gboxSettings->plainPage()); + TQToolTip::add(m_transparentText, i18n("Use semi-transparent text background under image")); + + grid->addMultiCellWidget(m_textEdit, 0, 2, 0, 1); + grid->addMultiCellWidget(m_fontChooserWidget, 3, 3, 0, 1); + grid->addMultiCellWidget(m_alignButtonGroup, 4, 4, 0, 1); + grid->addMultiCellWidget(label1, 5, 5, 0, 0); + grid->addMultiCellWidget(m_textRotation, 5, 5, 1, 1); + grid->addMultiCellWidget(label2, 6, 6, 0, 0); + grid->addMultiCellWidget(m_fontColorButton, 6, 6, 1, 1); + grid->addMultiCellWidget(m_borderText, 7, 7, 0, 1); + grid->addMultiCellWidget(m_transparentText, 8, 8, 0, 1); + grid->setMargin(0); + grid->setSpacing(m_gboxSettings->spacingHint()); + grid->setRowStretch(9, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_fontChooserWidget, TQ_SIGNAL(fontSelected (const TQFont&)), + this, TQ_SLOT(slotFontPropertiesChanged(const TQFont&))); + + connect(m_fontColorButton, TQ_SIGNAL(changed(const TQColor&)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_textEdit, TQ_SIGNAL(textChanged()), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_alignButtonGroup, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotAlignModeChanged(int))); + + connect(m_borderText, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_transparentText, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(m_textRotation, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotUpdatePreview())); + + connect(this, TQ_SIGNAL(signalUpdatePreview()), + this, TQ_SLOT(slotUpdatePreview())); + + // ------------------------------------------------------------- + + slotUpdatePreview(); +} + +InsertTextTool::~InsertTextTool() +{ +} + +void InsertTextTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("inserttext Tool"); + TQColor black(0, 0, 0); + TQFont defaultFont; + + int orgW = m_previewWidget->imageIface()->originalWidth(); + int orgH = m_previewWidget->imageIface()->originalHeight(); + + if (orgW > orgH) m_defaultSizeFont = (int)(orgH / 8.0); + else m_defaultSizeFont = (int)(orgW / 8.0); + + defaultFont.setPointSize(m_defaultSizeFont); + m_textRotation->setCurrentItem(config->readNumEntry("Text Rotation", 0)); + m_fontColorButton->setColor(config->readColorEntry("Font Color", &black)); + m_textEdit->setText(config->readEntry("Text String", i18n("Enter your text here!"))); + m_textFont = config->readFontEntry("Font Properties", &defaultFont); + m_fontChooserWidget->setFont(m_textFont); + m_alignTextMode = config->readNumEntry("Text Alignment", ALIGN_LEFT); + m_borderText->setChecked(config->readBoolEntry("Border Text", false)); + m_transparentText->setChecked(config->readBoolEntry("Transparent Text", false)); + m_previewWidget->setPositionHint(config->readRectEntry("Position Hint")); + + static_cast(m_alignButtonGroup->find(m_alignTextMode))->setOn(true); + slotAlignModeChanged(m_alignTextMode); +} + +void InsertTextTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("inserttext Tool"); + + config->writeEntry("Text Rotation", m_textRotation->currentItem()); + config->writeEntry("Font Color", m_fontColorButton->color()); + config->writeEntry("Text String", m_textEdit->text()); + config->writeEntry("Font Properties", m_textFont); + config->writeEntry("Text Alignment", m_alignTextMode); + config->writeEntry("Border Text", m_borderText->isChecked()); + config->writeEntry("Transparent Text", m_transparentText->isChecked()); + config->writeEntry("Position Hint", m_previewWidget->getPositionHint()); + + config->sync(); +} + +void InsertTextTool::slotResetSettings() +{ + m_fontColorButton->blockSignals(true); + m_alignButtonGroup->blockSignals(true); + m_fontChooserWidget->blockSignals(true); + + m_textRotation->setCurrentItem(0); // No rotation. + m_fontColorButton->setColor(TQt::black); + TQFont defaultFont; + m_textFont = defaultFont; // Reset to default TDE font. + m_textFont.setPointSize(m_defaultSizeFont); + m_fontChooserWidget->setFont(m_textFont); + m_borderText->setChecked(false); + m_transparentText->setChecked(false); + m_previewWidget->resetEdit(); + static_cast (m_alignButtonGroup->find(ALIGN_LEFT))->setOn(true); + + m_fontChooserWidget->blockSignals(false); + m_fontColorButton->blockSignals(false); + m_alignButtonGroup->blockSignals(false); + slotAlignModeChanged(ALIGN_LEFT); +} + +void InsertTextTool::slotAlignModeChanged(int mode) +{ + m_alignTextMode = mode; + m_textEdit->selectAll(true); + + switch (m_alignTextMode) + { + case ALIGN_LEFT: + m_textEdit->setAlignment( TQt::AlignLeft ); + break; + + case ALIGN_RIGHT: + m_textEdit->setAlignment( TQt::AlignRight ); + break; + + case ALIGN_CENTER: + m_textEdit->setAlignment( TQt::AlignHCenter ); + break; + + case ALIGN_BLOCK: + m_textEdit->setAlignment( TQt::AlignJustify ); + break; + } + + m_textEdit->selectAll(false); + emit signalUpdatePreview(); +} + +void InsertTextTool::slotFontPropertiesChanged(const TQFont& font) +{ + m_textFont = font; + emit signalUpdatePreview(); +} + +void InsertTextTool::slotUpdatePreview() +{ + m_previewWidget->setText(m_textEdit->text(), m_textFont, m_fontColorButton->color(), m_alignTextMode, + m_borderText->isChecked(), m_transparentText->isChecked(), + m_textRotation->currentItem()); +} + +void InsertTextTool::finalRendering() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + + ImageIface iface(0, 0); + DImg dest = m_previewWidget->makeInsertText(); + iface.putOriginalImage(i18n("Insert Text"), dest.bits(), dest.width(), dest.height()); + + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamInsertTextImagesPlugin diff --git a/src/imageplugins/inserttext/inserttexttool.h b/src/imageplugins/inserttext/inserttexttool.h new file mode 100644 index 00000000..c148aa60 --- /dev/null +++ b/src/imageplugins/inserttext/inserttexttool.h @@ -0,0 +1,111 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a plugin to insert a text over an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef INSERTEXTTOOL_H +#define INSERTEXTTOOL_H + +// TQt includes. + +#include +#include +#include + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; +class TQFont; +class TQHButtonGroup; +class TQComboBox; +class TQCheckBox; + +class KTextEdit; +class KColorButton; + +namespace Digikam +{ +class EditorToolSettings; +} + +namespace DigikamInsertTextImagesPlugin +{ + +class InsertTextWidget; +class FontChooserWidget; + +class InsertTextTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + InsertTextTool(TQObject *parent); + ~InsertTextTool(); + +signals: + + void signalUpdatePreview(); + +private slots: + + void slotFontPropertiesChanged(const TQFont& font); + void slotUpdatePreview(); + void slotAlignModeChanged(int mode); + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + +private: + + int m_alignTextMode; + int m_defaultSizeFont; + + TQComboBox *m_textRotation; + + TQCheckBox *m_borderText; + TQCheckBox *m_transparentText; + + TQHButtonGroup *m_alignButtonGroup; + + TQFont m_textFont; + + KColorButton *m_fontColorButton; + + KTextEdit *m_textEdit; + + Digikam::EditorToolSettings *m_gboxSettings; + + FontChooserWidget *m_fontChooserWidget; + + InsertTextWidget *m_previewWidget; +}; + +} // NameSpace DigikamInsertTextImagesPlugin + +#endif /* INSERTEXTTOOL_H */ diff --git a/src/imageplugins/inserttext/inserttextwidget.cpp b/src/imageplugins/inserttext/inserttextwidget.cpp new file mode 100644 index 00000000..dc5b65f4 --- /dev/null +++ b/src/imageplugins/inserttext/inserttextwidget.cpp @@ -0,0 +1,622 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a widget to insert a text over an image. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Digikam includes. + +#include "imageiface.h" + +// Local includes. + +#include "inserttextwidget.h" +#include "inserttextwidget.moc" + +namespace DigikamInsertTextImagesPlugin +{ + +InsertTextWidget::InsertTextWidget(int w, int h, TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + m_currentMoving = false; + + m_iface = new Digikam::ImageIface(w, h); + m_data = m_iface->getPreviewImage(); + m_w = m_iface->previewWidth(); + m_h = m_iface->previewHeight(); + m_pixmap = new TQPixmap(w, h); + m_pixmap->fill(colorGroup().background()); + + setBackgroundMode(TQt::NoBackground); + setMinimumSize(w, h); + setMouseTracking(true); + + m_rect = TQRect(width()/2-m_w/2, height()/2-m_h/2, m_w, m_h); + m_textRect = TQRect(); + + m_backgroundColor = TQColor(0xCC, 0xCC, 0xCC); + m_transparency = 210; +} + +InsertTextWidget::~InsertTextWidget() +{ + delete [] m_data; + delete m_iface; + delete m_pixmap; +} + +Digikam::ImageIface* InsertTextWidget::imageIface() +{ + return m_iface; +} + +void InsertTextWidget::resetEdit() +{ + // signal this needs to be filled by makePixmap + m_textRect = TQRect(); + makePixmap(); + repaint(false); +} + +void InsertTextWidget::setText(TQString text, TQFont font, TQColor color, int alignMode, + bool border, bool transparent, int rotation) +{ + m_textString = text; + m_textColor = color; + m_textBorder = border; + m_textTransparent = transparent; + m_textRotation = rotation; + + switch (alignMode) + { + case ALIGN_LEFT: + m_alignMode = TQt::AlignLeft; + break; + + case ALIGN_RIGHT: + m_alignMode = TQt::AlignRight; + break; + + case ALIGN_CENTER: + m_alignMode = TQt::AlignHCenter; + break; + + case ALIGN_BLOCK: + m_alignMode = TQt::AlignJustify; + break; + } + + // Center text if top left corner text area is not visible. + + /* + if ( m_textFont.pointSize() != font.pointSize() && + !rect().contains( m_textRect.x(), m_textRect.y() ) ) + { + m_textFont = font; + resetEdit(); + return; + } + */ + + m_textFont = font; + + makePixmap(); + repaint(false); +} + +void InsertTextWidget::setPositionHint(TQRect hint) +{ + // interpreted by composeImage + m_positionHint = hint; + if (m_textRect.isValid()) + { + // invalidate current position so that hint is certainly interpreted + m_textRect = TQRect(); + makePixmap(); + repaint(); + } +} + +TQRect InsertTextWidget::getPositionHint() +{ + TQRect hint; + if (m_textRect.isValid()) + { + // We normalize on the size of the image, but we store as int. Precision loss is no problem. + hint.setX( (int) ((float)(m_textRect.x() - m_rect.x()) / (float)m_rect.width() * 10000.0) ); + hint.setY( (int) ((float)(m_textRect.y() - m_rect.y()) / (float)m_rect.height() * 10000.0) ); + hint.setWidth( (int) ((float)m_textRect.width() / (float)m_rect.width() * 10000.0) ); + hint.setHeight( (int) ((float)m_textRect.height() / (float)m_rect.height() * 10000.0) ); + } + return hint; +} + +Digikam::DImg InsertTextWidget::makeInsertText(void) +{ + int orgW = m_iface->originalWidth(); + int orgH = m_iface->originalHeight(); + float ratioW = (float)orgW/(float)m_w; + float ratioH = (float)orgH/(float)m_h; + + int x, y; + if (m_textRect.isValid()) + { + // convert from widget to image coordinates, then to original size + x = lroundf( (m_textRect.x() - m_rect.x()) * ratioW); + y = lroundf( (m_textRect.y() - m_rect.y()) * ratioH); + } + else + { + x = -1; + y = -1; + } + + // Get original image + Digikam::DImg image = m_iface->getOriginalImg()->copy(); + + int borderWidth = TQMAX(1, lroundf(ratioW)); + // compose and draw result on image + composeImage(&image, 0, x, y, + m_textFont, m_textFont.pointSizeFloat(), + m_textRotation, m_textColor, m_alignMode, m_textString, + m_textTransparent, m_backgroundColor, + m_textBorder ? BORDER_NORMAL : BORDER_NONE, borderWidth, borderWidth); + + return image; +} + +void InsertTextWidget::makePixmap(void) +{ + int orgW = m_iface->originalWidth(); + int orgH = m_iface->originalHeight(); + float ratioW = (float)m_w / (float)orgW; + float ratioH = (float)m_h / (float)orgH; + + int x, y; + if (m_textRect.isValid()) + { + // convert from widget to image coordinates + x = m_textRect.x() - m_rect.x(); + y = m_textRect.y() - m_rect.y(); + } + else + { + x = -1; + y = -1; + } + + // get preview image data + uchar *data = m_iface->getPreviewImage(); + Digikam::DImg image(m_iface->previewWidth(), m_iface->previewHeight(), m_iface->previewSixteenBit(), + m_iface->previewHasAlpha(), data); + delete [] data; + + // paint pixmap for drawing this widget + // First, fill with background color + m_pixmap->fill(colorGroup().background()); + TQPainter p(m_pixmap); + // Convert image to pixmap and draw it + TQPixmap imagePixmap = image.convertToPixmap(); + p.drawPixmap(m_rect.x(), m_rect.y(), + imagePixmap, 0, 0, imagePixmap.width(), imagePixmap.height()); + + // prepare painter for use by compose image + p.setClipRect(m_rect); + p.translate(m_rect.x(), m_rect.y()); + + // compose image and draw result directly on pixmap, with correct offset + TQRect textRect = composeImage(&image, &p, x, y, + m_textFont, m_textFont.pointSizeFloat() * ((ratioW > ratioH) ? ratioW : ratioH), + m_textRotation, m_textColor, m_alignMode, m_textString, + m_textTransparent, m_backgroundColor, + m_textBorder ? BORDER_NORMAL : BORDER_SUPPORT, 1, 1); + + p.end(); + + // store new text rectangle + // convert from image to widget coordinates + m_textRect.setX(textRect.x() + m_rect.x()); + m_textRect.setY(textRect.y() + m_rect.y()); + m_textRect.setSize(textRect.size()); +} + +/* + Take data from image, draw text at x|y with specified parameters. + If destPainter is null, draw to image, + if destPainter is not null, draw directly using the painter. + Returns modified area of image. +*/ +TQRect InsertTextWidget::composeImage(Digikam::DImg *image, TQPainter *destPainter, + int x, int y, + TQFont font, float pointSize, int textRotation, TQColor textColor, + int alignMode, const TQString &textString, + bool transparentBackground, TQColor backgroundColor, + BorderMode borderMode, int borderWidth, int spacing) +{ + /* + The problem we have to solve is that we have no pixel access to font rendering, + we have to let TQt do the drawing. On the other hand we need to support 16 bit, which + cannot be done with TQPixmap. + The current solution cuts out the text area, lets TQt do its drawing, converts back and blits to original. + */ + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(Digikam::DColorComposer::PorterDuffNone); + + int maxWidth, maxHeight; + if (x == -1 && y == -1) + { + maxWidth = image->width(); + maxHeight = image->height(); + } + else + { + maxWidth = image->width() - x; + maxHeight = image->height() - y; + } + + // find out size of the area that we are drawing to + font.setPointSizeFloat(pointSize); + TQFontMetrics fontMt( font ); + TQRect fontRect = fontMt.boundingRect(0, 0, maxWidth, maxHeight, 0, textString); + + int fontWidth, fontHeight; + + switch(textRotation) + { + case ROTATION_NONE: + case ROTATION_180: + default: + fontWidth = fontRect.width(); + fontHeight = fontRect.height(); + break; + + case ROTATION_90: + case ROTATION_270: + fontWidth = fontRect.height(); + fontHeight = fontRect.width(); + break; + } + + // x, y == -1 means that we have to find a good initial position for the text here + if (x == -1 && y == -1) + { + // was a valid position hint stored from last use? + if (m_positionHint.isValid()) + { + // We assume that people tend to orient text along the edges, + // so we do some guessing so that positions such as "in the lower right corner" + // will be remembered across different image sizes. + + // get relative positions + float fromTop = (float)m_positionHint.top() / 10000.0; + float fromBottom = 1.0 - (float)m_positionHint.bottom() / 10000.0; + float fromLeft = (float)m_positionHint.left() / 10000.0; + float fromRight = 1.0 - (float)m_positionHint.right() / 10000.0; + + // calculate horizontal position + if (fromLeft < fromRight) + { + x = (int)(fromLeft * maxWidth); + + // we are placing from the smaller distance, + // so if now the larger distance is actually too small, + // fall back to standard placement, nothing to lose. + if (x + fontWidth > maxWidth) + x = TQMAX( (maxWidth - fontWidth) / 2, 0); + } + else + { + x = maxWidth - (int)(fromRight * maxWidth) - fontWidth; + if ( x < 0 ) + x = TQMAX( (maxWidth - fontWidth) / 2, 0); + } + + // calculate vertical position + if (fromTop < fromBottom) + { + y = (int)(fromTop * maxHeight); + if (y + fontHeight > maxHeight) + y = TQMAX( (maxHeight - fontHeight) / 2, 0); + } + else + { + y = maxHeight - (int)(fromBottom * maxHeight) - fontHeight; + if ( y < 0 ) + y = TQMAX( (maxHeight - fontHeight) / 2, 0); + } + + if (! TQRect(x, y, fontWidth, fontHeight). + intersects(TQRect(0, 0, maxWidth, maxHeight)) ) + { + // emergency fallback - nothing is visible + x = TQMAX( (maxWidth - fontWidth) / 2, 0); + y = TQMAX( (maxHeight - fontHeight) / 2, 0); + } + + // invalidate position hint, use only once + m_positionHint = TQRect(); + } + else + { + // use standard position + x = TQMAX( (maxWidth - fontWidth) / 2, 0); + y = TQMAX( (maxHeight - fontHeight) / 2, 0); + } + } + + // create a rectangle relative to image + TQRect drawRect( x, y, fontWidth + 2 * borderWidth + 2 * spacing, fontHeight + 2 * borderWidth + 2 * spacing); + + // create a rectangle relative to textArea, excluding the border + TQRect textAreaBackgroundRect( borderWidth, borderWidth, fontWidth + 2 * spacing, fontHeight + 2 * spacing); + + // create a rectangle relative to textArea, excluding the border and spacing + TQRect textAreaTextRect( borderWidth + spacing, borderWidth + spacing, fontWidth, fontHeight ); + + // create a rectangle relative to textArea, including the border, + // for drawing the rectangle, taking into account that the width of the TQPen goes in and out in equal parts + TQRect textAreaDrawRect( borderWidth / 2, borderWidth / 2, fontWidth + borderWidth + 2 * spacing, + fontHeight + borderWidth + 2 * spacing ); + + // cut out the text area + Digikam::DImg textArea = image->copy(drawRect); + + if (textArea.isNull()) + return TQRect(); + + // compose semi-transparent background over textArea + if (transparentBackground) + { + Digikam::DImg transparentLayer(textAreaBackgroundRect.width(), textAreaBackgroundRect.height(), textArea.sixteenBit(), true); + Digikam::DColor transparent(backgroundColor); + transparent.setAlpha(m_transparency); + if (image->sixteenBit()) + transparent.convertToSixteenBit(); + transparentLayer.fill(transparent); + textArea.bitBlendImage(composer, &transparentLayer, 0, 0, transparentLayer.width(), transparentLayer.height(), + textAreaBackgroundRect.x(), textAreaBackgroundRect.y()); + } + + Digikam::DImg textNotDrawn; + if (textArea.sixteenBit()) + { + textNotDrawn = textArea.copy(); + textNotDrawn.convertToEightBit(); + } + else + textNotDrawn = textArea; + + // We have no direct pixel access to font rendering, so now we need to use TQt/X11 for the drawing + + // convert text area to pixmap + TQPixmap pixmap = textNotDrawn.convertToPixmap(); + // paint on pixmap + TQPainter p(&pixmap); + p.setPen( TQPen(textColor, 1) ) ; + p.setFont( font ); + p.save(); + + // translate to origin of text, leaving space for the border + p.translate(textAreaTextRect.x(), textAreaTextRect.y()); + + switch(textRotation) + { + case ROTATION_NONE: + p.drawText( 0, 0, textAreaTextRect.width(), + textAreaTextRect.height(), alignMode, textString ); + break; + case ROTATION_90: + p.translate(textAreaTextRect.width(), 0); + p.rotate(90.0); + p.drawText( 0, 0, textAreaTextRect.height(), textAreaTextRect.width(), + alignMode, textString ); + break; + case ROTATION_180: + p.translate(textAreaTextRect.width(), textAreaTextRect.height()); + p.rotate(180.0); + p.drawText( 0, 0, textAreaTextRect.width(), textAreaTextRect.height(), + alignMode, textString ); + break; + case ROTATION_270: + p.translate(0, textAreaTextRect.height()); + p.rotate(270.0); + p.drawText( 0, 0, textAreaTextRect.height(), textAreaTextRect.width(), + alignMode, textString ); + break; + } + + p.restore(); + + // Drawing rectangle around text. + + if (borderMode == BORDER_NORMAL) // Decorative border using text color. + { + p.setPen( TQPen(textColor, borderWidth, TQt::SolidLine, + TQt::SquareCap, TQt::RoundJoin) ) ; + p.drawRect(textAreaDrawRect); + } + else if (borderMode == BORDER_SUPPORT) // Make simple dot line border to help user. + { + p.setPen(TQPen(TQt::white, 1, TQt::SolidLine)); + p.drawRect(textAreaDrawRect); + p.setPen(TQPen(TQt::red, 1, TQt::DotLine)); + p.drawRect(textAreaDrawRect); + } + p.end(); + + if (!destPainter) + { + // convert to TQImage, then to DImg + TQImage pixmapImage = pixmap.convertToImage(); + Digikam::DImg textDrawn(pixmapImage.width(), pixmapImage.height(), false, true, pixmapImage.bits()); + + // This does not work: during the conversion, colors are altered significantly (diffs of 1 to 10 in each component), + // so we cannot find out which pixels have actually been touched. + /* + // Compare the result of drawing with the previous version. + // Set all unchanged pixels to transparent + Digikam::DColor color, ncolor; + uchar *ptr, *nptr; + ptr = textDrawn.bits(); + nptr = textNotDrawn.bits(); + int bytesDepth = textDrawn.bytesDepth(); + int numPixels = textDrawn.width() * textDrawn.height(); + for (int i = 0; i < numPixels; i++, ptr+= bytesDepth, nptr += bytesDepth) + { + color.setColor(ptr, false); + ncolor.setColor(nptr, false); + if ( color.red() == ncolor.red() && + color.green() == ncolor.green() && + color.blue() == ncolor.blue()) + { + color.setAlpha(0); + color.setPixel(ptr); + } + } + // convert to 16 bit if needed + */ + textDrawn.convertToDepthOfImage(&textArea); + + // now compose to original: only pixels affected by drawing text and border are changed, not whole area + textArea.bitBlendImage(composer, &textDrawn, 0, 0, textDrawn.width(), textDrawn.height(), 0, 0); + + // copy result to original image + image->bitBltImage(&textArea, drawRect.x(), drawRect.y()); + } + else + { + destPainter->drawPixmap(drawRect.x(), drawRect.y(), pixmap, 0, 0, pixmap.width(), pixmap.height()); + } + + delete composer; + + return drawRect; +} + +void InsertTextWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0, 0, m_pixmap); +} + +void InsertTextWidget::resizeEvent(TQResizeEvent * e) +{ + blockSignals(true); + delete m_pixmap; + + int w = e->size().width(); + int h = e->size().height(); + + int textX = m_textRect.x() - m_rect.x(); + int textY = m_textRect.y() - m_rect.y(); + int old_w = m_w; + int old_h = m_h; + m_data = m_iface->setPreviewImageSize(w, h); + m_w = m_iface->previewWidth(); + m_h = m_iface->previewHeight(); + + m_pixmap = new TQPixmap(w, h); + m_rect = TQRect(w/2-m_w/2, h/2-m_h/2, m_w, m_h); + + if (m_textRect.isValid()) + { + int textWidth = m_textRect.width(); + int textHeight = m_textRect.height(); + + textX = lroundf( textX * (float)m_w / (float)old_w ); + textY = lroundf( textY * (float)m_h / (float)old_h ); + textWidth = lroundf(textWidth * (float)m_w / (float)old_w ); + textHeight = lroundf(textHeight * (float)m_h / (float)old_h ); + + m_textRect.setX(textX + m_rect.x()); + m_textRect.setY(textY + m_rect.y()); + m_textRect.setWidth(textWidth); + m_textRect.setHeight(textHeight); + makePixmap(); + } + + blockSignals(false); +} + +void InsertTextWidget::mousePressEvent ( TQMouseEvent * e ) +{ + if ( e->button() == TQt::LeftButton && + m_textRect.contains( e->x(), e->y() ) ) + { + m_xpos = e->x(); + m_ypos = e->y(); + setCursor ( KCursor::sizeAllCursor() ); + m_currentMoving = true; + } +} + +void InsertTextWidget::mouseReleaseEvent ( TQMouseEvent * ) +{ + setCursor ( KCursor::arrowCursor() ); + m_currentMoving = false; +} + +void InsertTextWidget::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( rect().contains( e->x(), e->y() ) ) + { + if ( e->state() == TQt::LeftButton && m_currentMoving ) + { + uint newxpos = e->x(); + uint newypos = e->y(); + + m_textRect.moveBy(newxpos - m_xpos, newypos - m_ypos); + + makePixmap(); + repaint(false); + + m_xpos = newxpos; + m_ypos = newypos; + setCursor( KCursor::handCursor() ); + } + else if ( m_textRect.contains( e->x(), e->y() ) ) + { + setCursor ( KCursor::sizeAllCursor() ); + } + else + { + setCursor ( KCursor::arrowCursor() ); + } + } +} + +} // NameSpace DigikamInsertTextImagesPlugin diff --git a/src/imageplugins/inserttext/inserttextwidget.h b/src/imageplugins/inserttext/inserttextwidget.h new file mode 100644 index 00000000..bbce9189 --- /dev/null +++ b/src/imageplugins/inserttext/inserttextwidget.h @@ -0,0 +1,156 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-14 + * Description : a widget to insert a text over an image. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef INSERTTEXTWIDGET_H +#define INSERTTEXTWIDGET_H + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "dimg.h" + +class TQPixmap; + +namespace Digikam +{ +class ImageIface; +} + +namespace DigikamInsertTextImagesPlugin +{ + +enum Action +{ + ALIGN_LEFT=0, + ALIGN_RIGHT, + ALIGN_CENTER, + ALIGN_BLOCK, + BORDER_TEXT, + TRANSPARENT_TEXT +}; + +enum TextRotation +{ + ROTATION_NONE=0, + ROTATION_90, + ROTATION_180, + ROTATION_270 +}; + +enum BorderMode +{ + BORDER_NONE, + BORDER_SUPPORT, + BORDER_NORMAL +}; + +class InsertTextWidget : public TQWidget +{ +TQ_OBJECT + + +public: + + InsertTextWidget(int w, int h, TQWidget *parent=0); + ~InsertTextWidget(); + + Digikam::ImageIface* imageIface(); + Digikam::DImg makeInsertText(void); + + void setText(TQString text, TQFont font, TQColor color, int alignMode, + bool border, bool transparent, int rotation); + void resetEdit(void); + + void setPositionHint(TQRect hint); + TQRect getPositionHint(); + +protected: + + void paintEvent(TQPaintEvent *e); + void resizeEvent(TQResizeEvent * e); + void mousePressEvent(TQMouseEvent * e); + void mouseReleaseEvent(TQMouseEvent * e); + void mouseMoveEvent(TQMouseEvent * e); + + void makePixmap(void); + TQRect composeImage(Digikam::DImg *image, TQPainter *destPainter, + int x, int y, + TQFont font, float pointSize, int textRotation, TQColor textColor, + int alignMode, const TQString &textString, + bool transparentBackground, TQColor backgroundColor, + BorderMode borderMode, int borderWidth, int spacing); + +private: + + bool m_currentMoving; + bool m_textBorder; + bool m_textTransparent; + + int m_alignMode; + int m_textRotation; + + uchar *m_data; + int m_w; + int m_h; + + int m_xpos; + int m_ypos; + + int m_transparency; + + TQPixmap *m_pixmap; + + TQRect m_rect; + TQRect m_textRect; + + TQString m_textString; + + TQFont m_textFont; + + TQColor m_textColor; + + TQColor m_backgroundColor; + + TQRect m_positionHint; + + Digikam::ImageIface *m_iface; +}; + +} // NameSpace DigikamInsertTextImagesPlugin + +#endif /* INSERTTEXTWIDGET_H */ diff --git a/src/imageplugins/lensdistortion/Makefile.am b/src/imageplugins/lensdistortion/Makefile.am new file mode 100644 index 00000000..cd157aa1 --- /dev/null +++ b/src/imageplugins/lensdistortion/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_lensdistortion_la_SOURCES = imageplugin_lensdistortion.cpp \ + lensdistortiontool.cpp \ + lensdistortion.cpp pixelaccess.cpp + +digikamimageplugin_lensdistortion_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_lensdistortion_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_lensdistortion.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_lensdistortion.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_lensdistortion_ui.rc diff --git a/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion.desktop b/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion.desktop new file mode 100644 index 00000000..da529559 --- /dev/null +++ b/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion.desktop @@ -0,0 +1,50 @@ +[Desktop Entry] +Name=ImagePlugin_LensDistortion +Name[bg]=ПриÑтавка за Ñнимки - Ðберации от обективи +Name[da]=Billedplugin_Linseforvrængning +Name[el]=ΠÏόσθετοΕικόνας_ΠαÏαμόÏφωσηΦακών +Name[fi]=Linssivääristymä +Name[hr]=IzobliÄena leća +Name[it]=PluginImmagini_DistorsioneLenticolare +Name[nl]=Afbeeldingsplugin_Lensafwijking +Name[sr]=Изобличење Ñочива +Name[sr@Latn]=IzobliÄenje soÄiva +Name[sv]=Insticksprogram för linsförvrängning +Name[tr]=ResimEklentisi_MercekÇarpıtması +Name[xx]=xxImagePlugin_LensDistortionxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Spherical aberration image correction plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за ÐºÐ¾Ñ€ÐµÐºÑ†Ð¸Ñ Ð½Ð° аберации от обективи в Ñнимките +Comment[ca]=Connector pel digiKam per corregir l'aberració esfèrica d'una imatge +Comment[da]=Plugin til korrigering af sfærisk afvigelse for Digikam +Comment[de]=digiKam-Modul zur Reduzierung kugelförmiger Verzerrung durch Linsen +Comment[el]=ΠÏόσθετο διόÏθωσης εικόνας σφαιÏικής παÏέκκλισης για το digiKam +Comment[es]=Plugin para digiKam para corrección de aberraciones de imagen esféricas +Comment[et]=DigiKami sfäärilise aberratsiooni korrigeerimise plugin +Comment[fa]=وصلۀ اصلاح تصویر انحرا٠کروی برای digiKam +Comment[fi]=Palloaberraatio (kalansilmävääristymä) +Comment[gl]=Un plugin de digiKam para corrixir a aberrazón esférica da imaxe +Comment[hr]=digiKam dodatak za ispravljanje kružne aberacije +Comment[is]=Ãforrit fyrir digiKam sem minnkar hringskekkingu linsu +Comment[it]=Plugin di correzione dell'aberrazione sferica delle immagini per digiKam +Comment[ja]=digiKam çƒé¢åŽå·®è£œæ­£ãƒ—ラグイン +Comment[nds]=digiKam-Moduul för't Richten vun Kugel-Vertarren +Comment[nl]=Digikam-plugin voor het corrigeren van bolvormige afwijkingen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਗੋਲਾ à¨à¨¬à©±à¨°à©‡à¨¸à¨¼à¨¨ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam korygujÄ…ca aberracjÄ™ sferycznÄ… +Comment[pt]=Um 'plugin' do digiKam para corrigir a aberração esférica da imagem +Comment[pt_BR]=Um 'plugin' do digiKam para corrigir a aberração esférica da imagem +Comment[ru]=Модуль digiKam коррекции ÑферичеÑких иÑкажений +Comment[sk]=digiKam plugin na korekciu sférickej aberácie Å¡oÅ¡ovky +Comment[sr]=digiKam-ов прикључак за иÑправљање Ñферног иÑкривљења Ñлике +Comment[sr@Latn]=digiKam-ov prikljuÄak za ispravljanje sfernog iskrivljenja slike +Comment[sv]=Digikam insticksprogram för korrigering av sfärisk avvikelse i bild +Comment[tr]=digiKam için resim küresel sapma düzeltme eklentisi +Comment[uk]=Втулок ÐºÐ¾Ñ€ÐµÐºÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñферичних Ñпотворень Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung sá»­a quang sai hình cầu ảnh cho digiKam +Comment[xx]=xxSpherical aberration image correction plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_lensdistortion +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion_ui.rc b/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion_ui.rc new file mode 100644 index 00000000..4705467e --- /dev/null +++ b/src/imageplugins/lensdistortion/digikamimageplugin_lensdistortion_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/lensdistortion/imageeffect_lensdistortion.cpp b/src/imageplugins/lensdistortion/imageeffect_lensdistortion.cpp new file mode 100644 index 00000000..89420a9c --- /dev/null +++ b/src/imageplugins/lensdistortion/imageeffect_lensdistortion.cpp @@ -0,0 +1,317 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ include. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "lensdistortion.h" +#include "imageeffect_lensdistortion.h" +#include "imageeffect_lensdistortion.moc" + +namespace DigikamLensDistortionImagesPlugin +{ + +ImageEffect_LensDistortion::ImageEffect_LensDistortion(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Lens Distortion Correction"), + "lensdistortion", false, true, true, + Digikam::ImageGuideWidget::HVGuideMode) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Lens Distortion Correction"), + digikam_version, + I18N_NOOP("A digiKam image plugin to reduce spherical aberration caused " + "by a lens to an image."), + TDEAboutData::License_GPL, + "(c) 2004-2006, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + about->addAuthor("David Hodson", I18N_NOOP("Lens distortion correction algorithm."), + "hodsond at acm dot org"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 8, 1, spacingHint()); + + m_maskPreviewLabel = new TQLabel( gboxSettings ); + m_maskPreviewLabel->setAlignment ( TQt::AlignHCenter | TQt::AlignVCenter ); + TQWhatsThis::add( m_maskPreviewLabel, i18n("

    You can see here a thumbnail preview of the distortion correction " + "applied to a cross pattern.") ); + gridSettings->addMultiCellWidget(m_maskPreviewLabel, 0, 0, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Main:"), gboxSettings); + + m_mainInput = new KDoubleNumInput(gboxSettings); + m_mainInput->setPrecision(1); + m_mainInput->setRange(-100.0, 100.0, 0.1, true); + TQWhatsThis::add( m_mainInput, i18n("

    This value controls the amount of distortion. Negative values correct lens barrel " + "distortion, while positive values correct lens pincushion distortion.")); + + gridSettings->addMultiCellWidget(label1, 1, 1, 0, 1); + gridSettings->addMultiCellWidget(m_mainInput, 2, 2, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Edge:"), gboxSettings); + + m_edgeInput = new KDoubleNumInput(gboxSettings); + m_edgeInput->setPrecision(1); + m_edgeInput->setRange(-100.0, 100.0, 0.1, true); + TQWhatsThis::add( m_edgeInput, i18n("

    This value controls in the same manner as the Main control, but has more effect " + "at the edges of the image than at the center.")); + + gridSettings->addMultiCellWidget(label2, 3, 3, 0, 1); + gridSettings->addMultiCellWidget(m_edgeInput, 4, 4, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Zoom:"), gboxSettings); + + m_rescaleInput = new KDoubleNumInput(gboxSettings); + m_rescaleInput->setPrecision(1); + m_rescaleInput->setRange(-100.0, 100.0, 0.1, true); + TQWhatsThis::add( m_rescaleInput, i18n("

    This value rescales the overall image size.")); + + gridSettings->addMultiCellWidget(label3, 5, 5, 0, 1); + gridSettings->addMultiCellWidget(m_rescaleInput, 6, 6, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Brighten:"), gboxSettings); + + m_brightenInput = new KDoubleNumInput(gboxSettings); + m_brightenInput->setPrecision(1); + m_brightenInput->setRange(-100.0, 100.0, 0.1, true); + TQWhatsThis::add( m_brightenInput, i18n("

    This value adjusts the brightness in image corners.")); + + gridSettings->addMultiCellWidget(label4, 7, 7, 0, 1); + gridSettings->addMultiCellWidget(m_brightenInput, 8, 8, 0, 1); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_mainInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_edgeInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_rescaleInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_brightenInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + // ------------------------------------------------------------- + + /* Calc transform preview. + We would like a checkered area to demonstrate the effect. + We do not have any drawing support in DImg, so we let TQt draw. + First we create a white TQImage. We convert this to a TQPixmap, + on which we can draw. Then we convert back to TQImage, + convert the TQImage to a DImg which we only need to create once, here. + Later, we apply the effect on a copy and convert the DImg to TQPixmap. + Longing for TQt4 where we can paint directly on the TQImage... + */ + + TQImage preview(120, 120, 32); + memset(preview.bits(), 255, preview.numBytes()); + TQPixmap pix (preview); + TQPainter pt(&pix); + pt.setPen( TQPen(TQt::black, 1) ); + pt.fillRect( 0, 0, pix.width(), pix.height(), TQBrush(TQt::black, TQt::CrossPattern) ); + pt.drawRect( 0, 0, pix.width(), pix.height() ); + pt.end(); + TQImage preview2(pix.convertToImage()); + m_previewRasterImage = Digikam::DImg(preview2.width(), preview2.height(), false, false, preview2.bits()); +} + +ImageEffect_LensDistortion::~ImageEffect_LensDistortion() +{ +} + +void ImageEffect_LensDistortion::readUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("lensdistortion Tool Dialog"); + + m_mainInput->blockSignals(true); + m_edgeInput->blockSignals(true); + m_rescaleInput->blockSignals(true); + m_brightenInput->blockSignals(true); + + m_mainInput->setValue(config->readDoubleNumEntry("2nd Order Distortion", 0.0)); + m_edgeInput->setValue(config->readDoubleNumEntry("4th Order Distortion",0.0)); + m_rescaleInput->setValue(config->readDoubleNumEntry("Zoom Factor", 0.0)); + m_brightenInput->setValue(config->readDoubleNumEntry("Brighten", 0.0)); + + m_mainInput->blockSignals(false); + m_edgeInput->blockSignals(false); + m_rescaleInput->blockSignals(false); + m_brightenInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_LensDistortion::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("lensdistortion Tool Dialog"); + config->writeEntry("2nd Order Distortion", m_mainInput->value()); + config->writeEntry("4th Order Distortion", m_edgeInput->value()); + config->writeEntry("Zoom Factor", m_rescaleInput->value()); + config->writeEntry("Brighten", m_brightenInput->value()); + config->sync(); +} + +void ImageEffect_LensDistortion::resetValues() +{ + m_mainInput->blockSignals(true); + m_edgeInput->blockSignals(true); + m_rescaleInput->blockSignals(true); + m_brightenInput->blockSignals(true); + + m_mainInput->setValue(0.0); + m_edgeInput->setValue(0.0); + m_rescaleInput->setValue(0.0); + m_brightenInput->setValue(0.0); + + m_mainInput->blockSignals(false); + m_edgeInput->blockSignals(false); + m_rescaleInput->blockSignals(false); + m_brightenInput->blockSignals(false); +} + +void ImageEffect_LensDistortion::prepareEffect() +{ + m_mainInput->setEnabled(false); + m_edgeInput->setEnabled(false); + m_rescaleInput->setEnabled(false); + m_brightenInput->setEnabled(false); + + double m = m_mainInput->value(); + double e = m_edgeInput->value(); + double r = m_rescaleInput->value(); + double b = m_brightenInput->value(); + + LensDistortion transformPreview(&m_previewRasterImage, 0L, m, e, r, b, 0, 0); + m_maskPreviewLabel->setPixmap(TQPixmap::TQPixmap(transformPreview.getTargetImage().convertToPixmap())); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + m_threadedFilter = dynamic_cast( + new LensDistortion(iface->getOriginalImg(), this, m, e, r, b, 0, 0)); +} + +void ImageEffect_LensDistortion::prepareFinal() +{ + m_mainInput->setEnabled(false); + m_edgeInput->setEnabled(false); + m_rescaleInput->setEnabled(false); + m_brightenInput->setEnabled(false); + + double m = m_mainInput->value(); + double e = m_edgeInput->value(); + double r = m_rescaleInput->value(); + double b = m_brightenInput->value(); + + Digikam::ImageIface iface(0, 0); + + m_threadedFilter = dynamic_cast( + new LensDistortion(iface.getOriginalImg(), this, m, e, r, b, 0, 0)); +} + +void ImageEffect_LensDistortion::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + Digikam::DImg imDest = m_threadedFilter->getTargetImage() + .smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_imagePreviewWidget->updatePreview(); +} + +void ImageEffect_LensDistortion::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Lens Distortion"), + m_threadedFilter->getTargetImage().bits()); +} + +void ImageEffect_LensDistortion::renderingFinished() +{ + m_mainInput->setEnabled(true); + m_edgeInput->setEnabled(true); + m_rescaleInput->setEnabled(true); + m_brightenInput->setEnabled(true); +} + +} // NameSpace DigikamLensDistortionImagesPlugin + diff --git a/src/imageplugins/lensdistortion/imageeffect_lensdistortion.h b/src/imageplugins/lensdistortion/imageeffect_lensdistortion.h new file mode 100644 index 00000000..2688a5df --- /dev/null +++ b/src/imageplugins/lensdistortion/imageeffect_lensdistortion.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_LENSDISTORTION_H +#define IMAGEEFFECT_LENSDISTORTION_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "dimg.h" +#include "imageguidedlg.h" + +class TQLabel; + +class KDoubleNumInput; + +namespace DigikamLensDistortionImagesPlugin +{ + +class ImageEffect_LensDistortion : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_LensDistortion(TQWidget *parent); + ~ImageEffect_LensDistortion(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_maskPreviewLabel; + + KDoubleNumInput *m_mainInput; + KDoubleNumInput *m_edgeInput; + KDoubleNumInput *m_rescaleInput; + KDoubleNumInput *m_brightenInput; + + Digikam::DImg m_previewRasterImage; +}; + +} // NameSpace DigikamLensDistortionImagesPlugin + +#endif /* IMAGEEFFECT_LENSDISTORTION_H */ diff --git a/src/imageplugins/lensdistortion/imageplugin_lensdistortion.cpp b/src/imageplugins/lensdistortion/imageplugin_lensdistortion.cpp new file mode 100644 index 00000000..73403fa7 --- /dev/null +++ b/src/imageplugins/lensdistortion/imageplugin_lensdistortion.cpp @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "lensdistortiontool.h" +#include "imageplugin_lensdistortion.h" +#include "imageplugin_lensdistortion.moc" + +using namespace DigikamLensDistortionImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_lensdistortion, + KGenericFactory("digikamimageplugin_lensdistortion")); + +ImagePlugin_LensDistortion::ImagePlugin_LensDistortion(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_LensDistortion") +{ + m_lensdistortionAction = new TDEAction(i18n("Lens Distortion..."), "lensdistortion", 0, + this, TQ_SLOT(slotLensDistortion()), + actionCollection(), "imageplugin_lensdistortion"); + + setXMLFile("digikamimageplugin_lensdistortion_ui.rc"); + + DDebug() << "ImagePlugin_LensDistortion plugin loaded" << endl; +} + +ImagePlugin_LensDistortion::~ImagePlugin_LensDistortion() +{ +} + +void ImagePlugin_LensDistortion::setEnabledActions(bool enable) +{ + m_lensdistortionAction->setEnabled(enable); +} + +void ImagePlugin_LensDistortion::slotLensDistortion() +{ + LensDistortionTool *tool = new LensDistortionTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/lensdistortion/imageplugin_lensdistortion.h b/src/imageplugins/lensdistortion/imageplugin_lensdistortion.h new file mode 100644 index 00000000..ceb24756 --- /dev/null +++ b/src/imageplugins/lensdistortion/imageplugin_lensdistortion.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_LENSDISTORTION_H +#define IMAGEPLUGIN_LENSDISTORTION_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_LensDistortion : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_LensDistortion(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_LensDistortion(); + + void setEnabledActions(bool enable); + +private slots: + + void slotLensDistortion(); + +private: + + TDEAction *m_lensdistortionAction; +}; + +#endif /* IMAGEPLUGIN_LENSDISTORTION_H */ diff --git a/src/imageplugins/lensdistortion/lensdistortion.cpp b/src/imageplugins/lensdistortion/lensdistortion.cpp new file mode 100644 index 00000000..7b18cc93 --- /dev/null +++ b/src/imageplugins/lensdistortion/lensdistortion.cpp @@ -0,0 +1,135 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : lens distortion algorithm. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2001-2003 by David Hodson + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "pixelaccess.h" +#include "lensdistortion.h" + +namespace DigikamLensDistortionImagesPlugin +{ + +LensDistortion::LensDistortion(Digikam::DImg *orgImage, TQObject *parent, double main, + double edge, double rescale, double brighten, + int center_x, int center_y) + : Digikam::DImgThreadedFilter(orgImage, parent, "LensDistortion") +{ + m_main = main; + m_edge = edge; + m_rescale = rescale; + m_brighten = brighten; + m_centre_x = center_x; + m_centre_y = center_y; + + initFilter(); +} + +void LensDistortion::filterImage(void) +{ + int Width = m_orgImage.width(); + int Height = m_orgImage.height(); + int bytesDepth = m_orgImage.bytesDepth(); + + uchar *data = m_destImage.bits(); + + // initial copy + + m_destImage.bitBltImage(&m_orgImage, 0, 0); + + // initialize coefficients + + double normallise_radius_sq = 4.0 / (Width * Width + Height * Height); + double center_x = Width * (100.0 + m_centre_x) / 200.0; + double center_y = Height * (100.0 + m_centre_y) / 200.0; + double mult_sq = m_main / 200.0; + double mult_qd = m_edge / 200.0; + double rescale = pow(2.0, - m_rescale / 100.0); + double brighten = - m_brighten / 10.0; + + PixelAccess *pa = new PixelAccess(&m_orgImage); + + /* + * start at image (i, j), increment by (step, step) + * output goes to dst, which is w x h x d in size + * NB: d <= image.bpp + */ + + // We are working on the full image. + int dstWidth = Width; + int dstHeight = Height; + uchar* dst = (uchar*)data; + int step = 1, progress; + + int iLimit, jLimit; + double srcX, srcY, mag; + + iLimit = dstWidth * step; + jLimit = dstHeight * step; + + for (int dstJ = 0 ; !m_cancel && (dstJ < jLimit) ; dstJ += step) + { + for (int dstI = 0 ; !m_cancel && (dstI < iLimit) ; dstI += step) + { + // Get source Coordinates. + double radius_sq; + double off_x; + double off_y; + double radius_mult; + + off_x = dstI - center_x; + off_y = dstJ - center_y; + radius_sq = (off_x * off_x) + (off_y * off_y); + + radius_sq *= normallise_radius_sq; + + radius_mult = radius_sq * mult_sq + radius_sq * radius_sq * mult_qd; + mag = radius_mult; + radius_mult = rescale * (1.0 + radius_mult); + + srcX = center_x + radius_mult * off_x; + srcY = center_y + radius_mult * off_y; + + brighten = 1.0 + mag * brighten; + pa->pixelAccessGetCubic(srcX, srcY, brighten, dst); + dst += bytesDepth; + } + + // Update progress bar in dialog. + + progress = (int) (((double)dstJ * 100.0) / jLimit); + if (m_parent && progress%5 == 0) + postProgress(progress); + } + + delete pa; +} + +} // NameSpace DigikamLensDistortionImagesPlugin diff --git a/src/imageplugins/lensdistortion/lensdistortion.h b/src/imageplugins/lensdistortion/lensdistortion.h new file mode 100644 index 00000000..cbc46105 --- /dev/null +++ b/src/imageplugins/lensdistortion/lensdistortion.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : lens distortion algorithm. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2001-2003 by David Hodson + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef LENS_DISTORTION_H +#define LENS_DISTORTION_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamLensDistortionImagesPlugin +{ + +class LensDistortion : public Digikam::DImgThreadedFilter +{ + +public: + + LensDistortion(Digikam::DImg *orgImage, TQObject *parent=0, double main=0.0, + double edge=0.0, double rescale=0.0, double brighten=0.0, + int center_x=0, int center_y=0); + + ~LensDistortion(){}; + +private: + + virtual void filterImage(void); + +private: + + int m_centre_x; + int m_centre_y; + + double m_main; + double m_edge; + double m_rescale; + double m_brighten; +}; + +} // NameSpace DigikamLensDistortionImagesPlugin + +#endif /* LENS_DISTORTION_H */ diff --git a/src/imageplugins/lensdistortion/lensdistortiontool.cpp b/src/imageplugins/lensdistortion/lensdistortiontool.cpp new file mode 100644 index 00000000..8d33e1df --- /dev/null +++ b/src/imageplugins/lensdistortion/lensdistortiontool.cpp @@ -0,0 +1,326 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "editortoolsettings.h" +#include "lensdistortion.h" +#include "lensdistortiontool.h" +#include "lensdistortiontool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamLensDistortionImagesPlugin +{ + +LensDistortionTool::LensDistortionTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("lensdistortion"); + setToolName(i18n("Lens Distortion")); + setToolIcon(SmallIcon("lensdistortion")); + + m_previewWidget = new ImageWidget("lensdistortion Tool", 0, TQString(), + false, ImageGuideWidget::HVGuideMode); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel, + EditorToolSettings::ColorGuide); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 9, 1); + + m_maskPreviewLabel = new TQLabel( m_gboxSettings->plainPage() ); + m_maskPreviewLabel->setAlignment ( TQt::AlignHCenter | TQt::AlignVCenter ); + TQWhatsThis::add( m_maskPreviewLabel, i18n("

    You can see here a thumbnail preview of the distortion correction " + "applied to a cross pattern.") ); + + // ------------------------------------------------------------- + + TQLabel *label1 = new TQLabel(i18n("Main:"), m_gboxSettings->plainPage()); + + m_mainInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_mainInput->setPrecision(1); + m_mainInput->setRange(-100.0, 100.0, 0.1); + m_mainInput->setDefaultValue(0.0); + TQWhatsThis::add(m_mainInput, i18n("

    This value controls the amount of distortion. Negative values correct lens barrel " + "distortion, while positive values correct lens pincushion distortion.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Edge:"), m_gboxSettings->plainPage()); + + m_edgeInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_edgeInput->setPrecision(1); + m_edgeInput->setRange(-100.0, 100.0, 0.1); + m_edgeInput->setDefaultValue(0.0); + TQWhatsThis::add(m_edgeInput, i18n("

    This value controls in the same manner as the Main control, but has more effect " + "at the edges of the image than at the center.")); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Zoom:"), m_gboxSettings->plainPage()); + + m_rescaleInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_rescaleInput->setPrecision(1); + m_rescaleInput->setRange(-100.0, 100.0, 0.1); + m_rescaleInput->setDefaultValue(0.0); + TQWhatsThis::add(m_rescaleInput, i18n("

    This value rescales the overall image size.")); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Brighten:"), m_gboxSettings->plainPage()); + + m_brightenInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_brightenInput->setPrecision(1); + m_brightenInput->setRange(-100.0, 100.0, 0.1); + m_brightenInput->setDefaultValue(0.0); + TQWhatsThis::add(m_brightenInput, i18n("

    This value adjusts the brightness in image corners.")); + + grid->addMultiCellWidget(m_maskPreviewLabel, 0, 0, 0, 1); + grid->addMultiCellWidget(label1, 1, 1, 0, 1); + grid->addMultiCellWidget(m_mainInput, 2, 2, 0, 1); + grid->addMultiCellWidget(label2, 3, 3, 0, 1); + grid->addMultiCellWidget(m_edgeInput, 4, 4, 0, 1); + grid->addMultiCellWidget(label3, 5, 5, 0, 1); + grid->addMultiCellWidget(m_rescaleInput, 6, 6, 0, 1); + grid->addMultiCellWidget(label4, 7, 7, 0, 1); + grid->addMultiCellWidget(m_brightenInput, 8, 8, 0, 1); + grid->setRowStretch(9, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_mainInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_edgeInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_rescaleInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_brightenInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_gboxSettings, TQ_SIGNAL(signalColorGuideChanged()), + this, TQ_SLOT(slotColorGuideChanged())); + + // ------------------------------------------------------------- + + /* Calc transform preview. + We would like a checkered area to demonstrate the effect. + We do not have any drawing support in DImg, so we let TQt draw. + First we create a white TQImage. We convert this to a TQPixmap, + on which we can draw. Then we convert back to TQImage, + convert the TQImage to a DImg which we only need to create once, here. + Later, we apply the effect on a copy and convert the DImg to TQPixmap. + Longing for TQt4 where we can paint directly on the TQImage... + */ + + TQImage preview(120, 120, 32); + memset(preview.bits(), 255, preview.numBytes()); + TQPixmap pix (preview); + TQPainter pt(&pix); + pt.setPen( TQPen(TQt::black, 1) ); + pt.fillRect( 0, 0, pix.width(), pix.height(), TQBrush(TQt::black, TQt::CrossPattern) ); + pt.drawRect( 0, 0, pix.width(), pix.height() ); + pt.end(); + TQImage preview2(pix.convertToImage()); + m_previewRasterImage = DImg(preview2.width(), preview2.height(), false, false, preview2.bits()); +} + +LensDistortionTool::~LensDistortionTool() +{ +} + +void LensDistortionTool::slotColorGuideChanged() +{ + m_previewWidget->slotChangeGuideColor(m_gboxSettings->guideColor()); + m_previewWidget->slotChangeGuideSize(m_gboxSettings->guideSize()); +} + +void LensDistortionTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("lensdistortion Tool"); + + m_mainInput->blockSignals(true); + m_edgeInput->blockSignals(true); + m_rescaleInput->blockSignals(true); + m_brightenInput->blockSignals(true); + + m_mainInput->setValue(config->readDoubleNumEntry("2nd Order Distortion", m_mainInput->defaultValue())); + m_edgeInput->setValue(config->readDoubleNumEntry("4th Order Distortion",m_edgeInput->defaultValue())); + m_rescaleInput->setValue(config->readDoubleNumEntry("Zoom Factor", m_rescaleInput->defaultValue())); + m_brightenInput->setValue(config->readDoubleNumEntry("Brighten", m_brightenInput->defaultValue())); + m_gboxSettings->setGuideColor(config->readColorEntry("Guide Color", &TQt::red)); + m_gboxSettings->setGuideSize(config->readNumEntry("Guide Width", 1)); + + m_mainInput->blockSignals(false); + m_edgeInput->blockSignals(false); + m_rescaleInput->blockSignals(false); + m_brightenInput->blockSignals(false); + + slotColorGuideChanged(); + slotEffect(); +} + +void LensDistortionTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("lensdistortion Tool"); + config->writeEntry("2nd Order Distortion", m_mainInput->value()); + config->writeEntry("4th Order Distortion", m_edgeInput->value()); + config->writeEntry("Zoom Factor", m_rescaleInput->value()); + config->writeEntry("Brighten", m_brightenInput->value()); + config->writeEntry("Guide Color", m_gboxSettings->guideColor()); + config->writeEntry("Guide Width", m_gboxSettings->guideSize()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void LensDistortionTool::slotResetSettings() +{ + m_mainInput->blockSignals(true); + m_edgeInput->blockSignals(true); + m_rescaleInput->blockSignals(true); + m_brightenInput->blockSignals(true); + + m_mainInput->slotReset(); + m_edgeInput->slotReset(); + m_rescaleInput->slotReset(); + m_brightenInput->slotReset(); + + m_mainInput->blockSignals(false); + m_edgeInput->blockSignals(false); + m_rescaleInput->blockSignals(false); + m_brightenInput->blockSignals(false); +} + +void LensDistortionTool::prepareEffect() +{ + m_mainInput->setEnabled(false); + m_edgeInput->setEnabled(false); + m_rescaleInput->setEnabled(false); + m_brightenInput->setEnabled(false); + + double m = m_mainInput->value(); + double e = m_edgeInput->value(); + double r = m_rescaleInput->value(); + double b = m_brightenInput->value(); + + LensDistortion transformPreview(&m_previewRasterImage, 0L, m, e, r, b, 0, 0); + m_maskPreviewLabel->setPixmap(TQPixmap(transformPreview.getTargetImage().convertToPixmap())); + + ImageIface* iface = m_previewWidget->imageIface(); + + setFilter(dynamic_cast(new LensDistortion(iface->getOriginalImg(), this, m, e, r, b, 0, 0))); +} + +void LensDistortionTool::prepareFinal() +{ + m_mainInput->setEnabled(false); + m_edgeInput->setEnabled(false); + m_rescaleInput->setEnabled(false); + m_brightenInput->setEnabled(false); + + double m = m_mainInput->value(); + double e = m_edgeInput->value(); + double r = m_rescaleInput->value(); + double b = m_brightenInput->value(); + + ImageIface iface(0, 0); + + setFilter(dynamic_cast(new LensDistortion(iface.getOriginalImg(), this, m, e, r, b, 0, 0))); +} + +void LensDistortionTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + + DImg imDest = filter()->getTargetImage().smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_previewWidget->updatePreview(); +} + +void LensDistortionTool::putFinalData() +{ + ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Lens Distortion"), filter()->getTargetImage().bits()); +} + +void LensDistortionTool::renderingFinished() +{ + m_mainInput->setEnabled(true); + m_edgeInput->setEnabled(true); + m_rescaleInput->setEnabled(true); + m_brightenInput->setEnabled(true); +} + +} // NameSpace DigikamLensDistortionImagesPlugin diff --git a/src/imageplugins/lensdistortion/lensdistortiontool.h b/src/imageplugins/lensdistortion/lensdistortiontool.h new file mode 100644 index 00000000..f3c7b67f --- /dev/null +++ b/src/imageplugins/lensdistortion/lensdistortiontool.h @@ -0,0 +1,92 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : a plugin to reduce lens distorsions to an image. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef LENSDISTORTIONTOOL_H +#define LENSDISTORTIONTOOL_H + +// Digikam includes. + +#include "dimg.h" +#include "editortool.h" + +class TQLabel; + +namespace KDcrawIface +{ +class RDoubleNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImageWidget; +} + +namespace DigikamLensDistortionImagesPlugin +{ + +class LensDistortionTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + LensDistortionTool(TQObject *parent); + ~LensDistortionTool(); + +private slots: + + void slotResetSettings(); + void slotColorGuideChanged(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_maskPreviewLabel; + + KDcrawIface::RDoubleNumInput *m_mainInput; + KDcrawIface::RDoubleNumInput *m_edgeInput; + KDcrawIface::RDoubleNumInput *m_rescaleInput; + KDcrawIface::RDoubleNumInput *m_brightenInput; + + Digikam::DImg m_previewRasterImage; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamLensDistortionImagesPlugin + +#endif /* LENSDISTORTIONTOOL_H */ diff --git a/src/imageplugins/lensdistortion/pixelaccess.cpp b/src/imageplugins/lensdistortion/pixelaccess.cpp new file mode 100644 index 00000000..a6041f94 --- /dev/null +++ b/src/imageplugins/lensdistortion/pixelaccess.cpp @@ -0,0 +1,314 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : acess pixels method for lens distortion algorithm. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "pixelaccess.h" + +namespace DigikamLensDistortionImagesPlugin +{ + +PixelAccess::PixelAccess(Digikam::DImg *srcImage) +{ + m_image = srcImage; + + m_width = PixelAccessWidth; + m_height = PixelAccessHeight; + + m_depth = m_image->bytesDepth(); + m_imageWidth = m_image->width(); + m_imageHeight = m_image->height(); + m_sixteenBit = m_image->sixteenBit(); + + for ( int i = 0 ; i < PixelAccessRegions ; i++ ) + { + m_buffer[i] = new Digikam::DImg(m_image->copy(0, 0, m_width, m_height)); + + m_tileMinX[i] = 1; + m_tileMaxX[i] = m_width - 2; + m_tileMinY[i] = 1; + m_tileMaxY[i] = m_height - 2; + } +} + +PixelAccess::~PixelAccess() +{ + for( int i = 0 ; i < PixelAccessRegions ; i++ ) + delete m_buffer[i]; +} + +uchar* PixelAccess::pixelAccessAddress(int i, int j) +{ + return m_buffer[0]->bits() + m_depth * (m_width * (j + 1 - m_tileMinY[0]) + (i + 1 - m_tileMinX[0])); +} + +// Swap region[n] with region[0]. +void PixelAccess::pixelAccessSelectRegion(int n) +{ + Digikam::DImg *temp; + int a, b, c, d; + int i; + + temp = m_buffer[n]; + a = m_tileMinX[n]; + b = m_tileMaxX[n]; + c = m_tileMinY[n]; + d = m_tileMaxY[n]; + + for( i = n ; i > 0 ; i--) + { + m_buffer[i] = m_buffer[i-1]; + m_tileMinX[i] = m_tileMinX[i-1]; + m_tileMaxX[i] = m_tileMaxX[i-1]; + m_tileMinY[i] = m_tileMinY[i-1]; + m_tileMaxY[i] = m_tileMaxY[i-1]; + } + + m_buffer[0] = temp; + m_tileMinX[0] = a; + m_tileMaxX[0] = b; + m_tileMinY[0] = c; + m_tileMaxY[0] = d; +} + +// Buffer[0] is cleared, should start at [i, j], fill rows that overlap image. +void PixelAccess::pixelAccessDoEdge(int i, int j) +{ + int lineStart, lineEnd; + int rowStart, rowEnd; + int lineWidth; + uchar* line; + + lineStart = i; + if (lineStart < 0) lineStart = 0; + lineEnd = i + m_width; + if (lineEnd > m_imageWidth) lineEnd = m_imageWidth; + lineWidth = lineEnd - lineStart; + + if( lineStart >= lineEnd ) + return; + + rowStart = j; + if (rowStart < 0) rowStart = 0; + rowEnd = j + m_height; + if (rowEnd > m_imageHeight) rowEnd = m_imageHeight; + + for( int y = rowStart ; y < rowEnd ; y++ ) + { + line = pixelAccessAddress(lineStart, y); + memcpy(line, m_image->scanLine(y) + lineStart * m_depth, lineWidth * m_depth); + } +} + +// Moves buffer[0] so that [x, y] is inside it. +void PixelAccess::pixelAccessReposition(int xInt, int yInt) +{ + int newStartX = xInt - PixelAccessXOffset; + int newStartY = yInt - PixelAccessYOffset; + + m_tileMinX[0] = newStartX + 1; + m_tileMaxX[0] = newStartX + m_width - 2; + m_tileMinY[0] = newStartY + 1; + m_tileMaxY[0] = newStartY + m_height - 2; + + + if ( (newStartX < 0) || ((newStartX + m_width) >= m_imageWidth) || + (newStartY < 0) || ((newStartY + m_height) >= m_imageHeight) ) + { + // some data is off edge of image + + m_buffer[0]->fill(Digikam::DColor(0,0,0,0, m_sixteenBit)); + + // This could probably be done by bitBltImage but I did not figure out how, + // so leave the working code here. And no, it is not this: + //m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); + + if ( ((newStartX + m_width) < 0) || (newStartX >= m_imageWidth) || + ((newStartY + m_height) < 0) || (newStartY >= m_imageHeight) ) + { + // totally outside, just leave it. + } + else + { + pixelAccessDoEdge(newStartX, newStartY); + } + } + else + { + m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); + } +} + +void PixelAccess::pixelAccessGetCubic(double srcX, double srcY, double brighten, uchar* dst) +{ + int xInt, yInt; + double dx, dy; + uchar *corner; + + xInt = (int)floor(srcX); + dx = srcX - xInt; + yInt = (int)floor(srcY); + dy = srcY - yInt; + + // We need 4x4 pixels, xInt-1 to xInt+2 horz, yInt-1 to yInt+2 vert + // they're probably in the last place we looked... + + if ((xInt >= m_tileMinX[0]) && (xInt < m_tileMaxX[0]) && + (yInt >= m_tileMinY[0]) && (yInt < m_tileMaxY[0]) ) + { + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); + return; + } + + // Or maybe it was a while back... + + for ( int i = 1 ; i < PixelAccessRegions ; i++) + { + if ((xInt >= m_tileMinX[i]) && (xInt < m_tileMaxX[i]) && + (yInt >= m_tileMinY[i]) && (yInt < m_tileMaxY[i]) ) + { + // Check here first next time + + pixelAccessSelectRegion(i); + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); + return; + } + } + + // Nope, recycle an old region. + + pixelAccessSelectRegion(PixelAccessRegions - 1); + pixelAccessReposition(xInt, yInt); + + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); +} + +/* + * Catmull-Rom cubic interpolation + * + * equally spaced points p0, p1, p2, p3 + * interpolate 0 <= u < 1 between p1 and p2 + * + * (1 u u^2 u^3) ( 0.0 1.0 0.0 0.0 ) (p0) + * ( -0.5 0.0 0.5 0.0 ) (p1) + * ( 1.0 -2.5 2.0 -0.5 ) (p2) + * ( -0.5 1.5 -1.5 0.5 ) (p3) + * + */ +void PixelAccess::cubicInterpolate(uchar* src, int rowStride, uchar* dst, + bool sixteenBit, double dx, double dy, double brighten) +{ + float um1, u, up1, up2; + float vm1, v, vp1, vp2; + int c; + const int numberOfComponents = 4; + float verts[4 * numberOfComponents]; + + um1 = ((-0.5 * dx + 1.0) * dx - 0.5) * dx; + u = (1.5 * dx - 2.5) * dx * dx + 1.0; + up1 = ((-1.5 * dx + 2.0) * dx + 0.5) * dx; + up2 = (0.5 * dx - 0.5) * dx * dx; + + vm1 = ((-0.5 * dy + 1.0) * dy - 0.5) * dy; + v = (1.5 * dy - 2.5) * dy * dy + 1.0; + vp1 = ((-1.5 * dy + 2.0) * dy + 0.5) * dy; + vp2 = (0.5 * dy - 0.5) * dy * dy; + + if (sixteenBit) + { + unsigned short *src16 = (unsigned short *)src; + unsigned short *dst16 = (unsigned short *)dst; + + // for each component, read the values of 4 pixels into array + + for (c = 0 ; c < 4 * numberOfComponents ; c++) + { + verts[c] = vm1 * src16[c] + v * src16[c+rowStride] + vp1 * src16[c+rowStride*2] + vp2 * src16[c+rowStride*3]; + } + + // for each component, compute resulting value from array + + for (c = 0 ; c < numberOfComponents ; c++) + { + float result; + result = um1 * verts[c] + u * verts[c+numberOfComponents] + + up1 * verts[c+numberOfComponents*2] + up2 * verts[c+numberOfComponents*3]; + result *= brighten; + + if (result < 0.0) + { + dst16[c] = 0; + } + else if (result > 65535.0) + { + dst16[c] = 65535; + } + else + { + dst16[c] = (uint)result; + } + } + } + else + { + for (c = 0 ; c < 4 * numberOfComponents ; c++) + { + verts[c] = vm1 * src[c] + v * src[c+rowStride] + vp1 * src[c+rowStride*2] + vp2 * src[c+rowStride*3]; + } + + for (c = 0 ; c < numberOfComponents ; c++) + { + float result; + result = um1 * verts[c] + u * verts[c+numberOfComponents] + + up1 * verts[c+numberOfComponents*2] + up2 * verts[c+numberOfComponents*3]; + result *= brighten; + + if (result < 0.0) + { + dst[c] = 0; + } + else if (result > 255.0) + { + dst[c] = 255; + } + else + { + dst[c] = (uint)result; + } + } + } +} + +} // NameSpace DigikamLensDistortionImagesPlugin + diff --git a/src/imageplugins/lensdistortion/pixelaccess.h b/src/imageplugins/lensdistortion/pixelaccess.h new file mode 100644 index 00000000..734d0779 --- /dev/null +++ b/src/imageplugins/lensdistortion/pixelaccess.h @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : acess pixels method for lens distortion algorithm. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PIXEL_ACCESS_H +#define PIXEL_ACCESS_H + +#define PixelAccessRegions 20 +#define PixelAccessWidth 40 +#define PixelAccessHeight 20 +#define PixelAccessXOffset 3 +#define PixelAccessYOffset 3 + +// Digikam includes. + +#include "dimg.h" + +namespace DigikamLensDistortionImagesPlugin +{ + + /* PixelAcess class: solving the eternal problem: random, cubic-interpolated, + * sub-pixel coordinate access to an image. + * Assuming that accesses are at least slightly coherent, + * PixelAccess keeps PixelAccessRegions buffers, each containing a + * PixelAccessWidth x PixelAccessHeight region of pixels. + * Buffer[0] is always checked first, so move the last accessed + * region into that position. + * When a request arrives which is outside all the regions, + * get a new region. + * The new region is placed so that the requested pixel is positioned + * at [PixelAccessXOffset, PixelAccessYOffset] in the region. + */ + +class PixelAccess +{ +public: + + PixelAccess(Digikam::DImg *srcImage); + ~PixelAccess(); + + void pixelAccessGetCubic(double srcX, double srcY, double brighten, uchar* dst); + +private: + + Digikam::DImg *m_image; + + //uchar* m_buffer[PixelAccessRegions]; + Digikam::DImg *m_buffer[PixelAccessRegions]; + + int m_width; + int m_height; + int m_depth; + int m_imageWidth; + int m_imageHeight; + bool m_sixteenBit; + int m_tileMinX[PixelAccessRegions]; + int m_tileMaxX[PixelAccessRegions]; + int m_tileMinY[PixelAccessRegions]; + int m_tileMaxY[PixelAccessRegions]; + +protected: + + inline uchar* pixelAccessAddress(int i, int j); + void pixelAccessSelectRegion(int n); + void pixelAccessDoEdge(int i, int j); + void pixelAccessReposition(int xInt, int yInt); + void cubicInterpolate(uchar* src, int rowStride, uchar* dst, + bool sixteenBit, double dx, double dy, double brighten); +}; + +} // NameSpace DigikamLensDistortionImagesPlugin + +#endif /* PIXEL_ACCESS_H */ diff --git a/src/imageplugins/noisereduction/Makefile.am b/src/imageplugins/noisereduction/Makefile.am new file mode 100644 index 00000000..c1b6ac08 --- /dev/null +++ b/src/imageplugins/noisereduction/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_noisereduction_la_SOURCES = imageplugin_noisereduction.cpp \ + noisereductiontool.cpp noisereduction.cpp + +digikamimageplugin_noisereduction_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_noisereduction_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_noisereduction.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_noisereduction.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_noisereduction_ui.rc + diff --git a/src/imageplugins/noisereduction/digikamimageplugin_noisereduction.desktop b/src/imageplugins/noisereduction/digikamimageplugin_noisereduction.desktop new file mode 100644 index 00000000..9cf408e3 --- /dev/null +++ b/src/imageplugins/noisereduction/digikamimageplugin_noisereduction.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Name=ImagePlugin_NoiseReduction +Name[da]=Plugin for støjreducering +Name[el]=ΠÏόσθετοΕικόνας_ΜείωσηΘοÏÏβου +Name[fi]=Kohinanpoisto +Name[hr]=Uklanjanje Å¡uma +Name[it]=PluginImmagini_RiduzioneDisturbi +Name[nl]=Afbeeldingsplugin_Ruisreductie +Name[pt]=ImagePlugin_Restoration +Name[sr]=Смањење шума +Name[sr@Latn]=Smanjenje Å¡uma +Name[sv]=Insticksprogram för brusreducering +Name[tr]=ResimEklentisi_Onarım +Name[vi]=ImagePlugin_Restoration +Name[xx]=xxImagePlugin_NoiseReductionxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Noise Reduction plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за намалÑване шума в Ñнимки +Comment[ca]=Connector pel digiKam de reducció de soroll +Comment[da]=Plugin til støjreduktion for DigiKam +Comment[de]=digiKam-Modul zum Entfernen von Rauschen +Comment[el]=ΠÏόσθετο μείωσης θοÏÏβου για το digiKam +Comment[es]=Plugin para digiKam de reducción de ruido +Comment[et]=DigiKami müra vähendamise plugin +Comment[fa]=وصلۀ کاهش نوÙÙ‡ برای digiKam +Comment[fi]=Vähentää kuvan kohinaa +Comment[fr]=Module externe pour réduire le bruit numérique dans digiKam +Comment[gl]=Un plugin de digiKam para a reduzón de ruído +Comment[hr]=digiKam dodatak za uklanjanje Å¡uma +Comment[is]=Ãforrit fyrir digiKam sem minnkar truflanir (noise) í mynd +Comment[it]=Plugin di riduzione dei disturbi per digiKam +Comment[ja]=digiKam ノイズ低減プラグイン +Comment[ms]=Plugin Pengurangan Bising untuk digiKam +Comment[nds]=digiKam-Moduul för Ruusminnern +Comment[nl]=Digikam-plugin voor ruisreductie +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਗੜਬੜ ਘਟਾਉਣ ਲਈ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam zmniejszajÄ…ca szum +Comment[pt]=Um 'plugin' do digiKam para a redução de ruído +Comment[pt_BR]=Plugin de redução de ruidos +Comment[ru]=Модуль ÑƒÐ¼ÐµÐ½ÑŒÑˆÐµÐ½Ð¸Ñ ÑˆÑƒÐ¼Ð° Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin na potlaÄenie Å¡umu +Comment[sr]=digiKam-ов прикључак за Ñмањење шума +Comment[sr@Latn]=digiKam-ov prikljuÄak za smanjenje Å¡uma +Comment[sv]=Digikam insticksprogram för brusreducering +Comment[tr]=digiKam için Parazit Azaltma eklentisi +Comment[uk]=Втулок Ð·Ð¼ÐµÐ½ÑˆÐµÐ½Ð½Ñ ÑˆÑƒÐ¼Ñƒ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung giảm nhiá»…u cho digiKam +Comment[xx]=xxNoise Reduction plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_noisereduction +author=Caulier Gilles, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/noisereduction/digikamimageplugin_noisereduction_ui.rc b/src/imageplugins/noisereduction/digikamimageplugin_noisereduction_ui.rc new file mode 100644 index 00000000..b6a663e3 --- /dev/null +++ b/src/imageplugins/noisereduction/digikamimageplugin_noisereduction_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/noisereduction/imageeffect_noisereduction.cpp b/src/imageplugins/noisereduction/imageeffect_noisereduction.cpp new file mode 100644 index 00000000..707b05db --- /dev/null +++ b/src/imageplugins/noisereduction/imageeffect_noisereduction.cpp @@ -0,0 +1,553 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "noisereduction.h" +#include "imageeffect_noisereduction.h" +#include "imageeffect_noisereduction.moc" + +namespace DigikamNoiseReductionImagesPlugin +{ + +ImageEffect_NoiseReduction::ImageEffect_NoiseReduction(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Noise Reduction"), + "noisereduction", true, true, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Noise Reduction"), + digikam_version, + I18N_NOOP("A noise reduction image filter plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Peter Heckert", I18N_NOOP("Noise Reduction algorithm. Developer"), + "peter dot heckert at arcor dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQTabWidget *mainTab = new TQTabWidget(m_imagePreviewWidget); + + TQWidget* firstPage = new TQWidget( mainTab ); + TQGridLayout* gridSettings = new TQGridLayout( firstPage, 6, 1, spacingHint()); + mainTab->addTab( firstPage, i18n("Details") ); + + TQLabel *label1 = new TQLabel(i18n("Radius:"), firstPage); + + m_radiusInput = new KDoubleNumInput(firstPage); + m_radiusInput->setPrecision(1); + m_radiusInput->setRange(0.0, 10.0, 0.1, true); + TQWhatsThis::add( m_radiusInput, i18n("

    Radius: this control selects the " + "gliding window size used for the filter. Larger values do not increase " + "the amount of time needed to filter each pixel in the image but " + "can cause blurring. This window moves across the image, and the " + "color in it is smoothed to remove imperfections. " + "In any case it must be about the same size as the noise granularity " + "or somewhat more. If it is set higher than necessary, then it " + "can cause unwanted blur.")); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_radiusInput, 0, 0, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Threshold:"), firstPage); + + m_thresholdInput = new KDoubleNumInput(firstPage); + m_thresholdInput->setPrecision(2); + m_thresholdInput->setRange(0.0, 1.0, 0.01, true); + TQWhatsThis::add( m_thresholdInput, i18n("

    Threshold: use the slider for coarse adjustment, " + "and the spin control for fine adjustment to control edge detection sensitivity. " + "This value should be set so that edges and details are clearly visible " + "and noise is smoothed out. " + "Adjustment must be made carefully, because the gap between \"noisy\", " + "\"smooth\", and \"blur\" is very small. Adjust it as carefully as you would adjust " + "the focus of a camera.")); + + gridSettings->addMultiCellWidget(label3, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(m_thresholdInput, 1, 1, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Texture:"), firstPage); + + m_textureInput = new KDoubleNumInput(firstPage); + m_textureInput->setPrecision(2); + m_textureInput->setRange(-0.99, 0.99, 0.01, true); + TQWhatsThis::add( m_textureInput, i18n("

    Texture: this control sets the texture accuracy. " + "This value can be used, to get more or less texture accuracy. When decreased, " + "then noise and texture are blurred out, when increased then texture is " + "amplified, but also noise will increase. It has almost no effect on image edges.")); + + gridSettings->addMultiCellWidget(label4, 2, 2, 0, 0); + gridSettings->addMultiCellWidget(m_textureInput, 2, 2, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label7 = new TQLabel(i18n("Sharpness:"), firstPage); // Filter setting "Lookahead". + + m_sharpnessInput = new KDoubleNumInput(firstPage); + m_sharpnessInput->setPrecision(2); + m_sharpnessInput->setRange(0.0, 1.0, 0.1, true); + TQWhatsThis::add( m_sharpnessInput, i18n("

    Sharpness: " + "This value improves the frequency response for the filter. " + "When it is too strong then not all noise can be removed, or spike noise may appear. " + "Set it near to maximum, if you want to remove very weak noise or JPEG-artifacts, " + "without losing detail.")); + + gridSettings->addMultiCellWidget(label7, 3, 3, 0, 0); + gridSettings->addMultiCellWidget(m_sharpnessInput, 3, 3, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label5 = new TQLabel(i18n("Edge Lookahead:"), firstPage); // Filter setting "Sharp". + + m_lookaheadInput = new KDoubleNumInput(firstPage); + m_lookaheadInput->setPrecision(2); + m_lookaheadInput->setRange(0.01, 20.0, 0.01, true); + TQWhatsThis::add( m_lookaheadInput, i18n("

    Edge: " + "This value defines the pixel distance to which the filter looks ahead for edges. " + "When this value is increased, then spike noise is erased. " + "You can eventually re-adjust the Edge filter, when you have changed this setting. " + "When this value is too high, the adaptive filter can no longer accurately track " + "image details, and noise or blurring can occur.")); + + gridSettings->addMultiCellWidget(label5, 4, 4, 0, 0); + gridSettings->addMultiCellWidget(m_lookaheadInput, 4, 4, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label10 = new TQLabel(i18n("Erosion:"), firstPage); + + m_phaseInput = new KDoubleNumInput(firstPage); + m_phaseInput->setPrecision(1); + m_phaseInput->setRange(0.5, 20.0, 0.5, true); + TQWhatsThis::add( m_phaseInput, i18n("

    Erosion: " + "Use this to increase edge noise erosion and spike noise erosion " + "(noise is removed by erosion).")); + + gridSettings->addMultiCellWidget(label10, 5, 5, 0, 0); + gridSettings->addMultiCellWidget(m_phaseInput, 5, 5, 1, 1); + gridSettings->setColStretch(1, 10); + gridSettings->setRowStretch(6, 10); + + // ------------------------------------------------------------- + + TQWidget* secondPage = new TQWidget( mainTab ); + TQGridLayout* gridSettings2 = new TQGridLayout( secondPage, 4, 1, spacingHint()); + mainTab->addTab( secondPage, i18n("Advanced") ); + + TQLabel *label2 = new TQLabel(i18n("Luminance:"), secondPage); + + m_lumToleranceInput = new KDoubleNumInput(secondPage); + m_lumToleranceInput->setPrecision(1); + m_lumToleranceInput->setRange(0.0, 1.0, 0.1, true); + TQWhatsThis::add( m_lumToleranceInput, i18n("

    Luminance: this control sets the luminance tolerance of the image." + "We recommend using either the Color or the Luminance tolerance settings " + "to make an image correction, not both at the same time. These settings " + "do not influence the main smoothing process controlled by the Details " + "settings.")); + + gridSettings2->addMultiCellWidget(label2, 0, 0, 0, 0); + gridSettings2->addMultiCellWidget(m_lumToleranceInput, 0, 0, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label6 = new TQLabel(i18n("Color:"), secondPage); + + m_csmoothInput = new KDoubleNumInput(secondPage); + m_csmoothInput->setPrecision(1); + m_csmoothInput->setRange(0.0, 1.0, 0.1, true); + TQWhatsThis::add( m_csmoothInput, i18n("

    Color: this control sets the color tolerance of the image. It is " + "recommended using either the Color or the Luminance tolerance " + "to make image correction, not both at the same time. These settings " + "do not influence the main smoothing process controlled by the Details " + "settings.")); + + gridSettings2->addMultiCellWidget(label6, 1, 1, 0, 0); + gridSettings2->addMultiCellWidget(m_csmoothInput, 1, 1, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label8 = new TQLabel(i18n("Gamma:"), secondPage); + + m_gammaInput = new KDoubleNumInput(secondPage); + m_gammaInput->setPrecision(1); + m_gammaInput->setRange(0.3, 3.0, 0.1, true); + TQWhatsThis::add( m_gammaInput, i18n("

    Gamma: this control sets the gamma tolerance of the image. This value " + "can be used to increase the tolerance values for darker areas (which commonly " + "are noisier). This results in more blur for shadow areas.")); + + gridSettings2->addMultiCellWidget(label8, 2, 2, 0, 0); + gridSettings2->addMultiCellWidget(m_gammaInput, 2, 2, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label9 = new TQLabel(i18n("Damping:"), secondPage); + + m_dampingInput = new KDoubleNumInput(secondPage); + m_dampingInput->setPrecision(1); + m_dampingInput->setRange(0.5, 20.0, 0.5, true); + TQWhatsThis::add( m_dampingInput, i18n("

    Damping: this control sets the phase-jitter damping adjustment. " + "This value defines how fast the adaptive filter-radius reacts to luminance " + "variations. If increased, then edges appear smoother; if too high, then blur " + "may occur. If at minimum, then noise and phase jitter at the edges can occur. It " + "can suppress spike noise when increased, and this is the preferred method to " + "remove it.")); + + gridSettings2->addMultiCellWidget(label9, 3, 3, 0, 0); + gridSettings2->addMultiCellWidget(m_dampingInput, 3, 3, 1, 1); + gridSettings2->setColStretch(1, 10); + gridSettings2->setRowStretch(4, 10); + + m_imagePreviewWidget->setUserAreaWidget(mainTab); + + // ------------------------------------------------------------- + +// connect(m_radiusInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_lumToleranceInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_thresholdInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_textureInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_sharpnessInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_csmoothInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_lookaheadInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_gammaInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_dampingInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +// +// connect(m_phaseInput, TQ_SIGNAL(valueChanged(double)), +// this, TQ_SLOT(slotTimer())); +} + +ImageEffect_NoiseReduction::~ImageEffect_NoiseReduction() +{ +} + +void ImageEffect_NoiseReduction::renderingFinished() +{ + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); +} + +void ImageEffect_NoiseReduction::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("noisereduction Tool Dialog"); + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); + + m_radiusInput->setValue(config->readDoubleNumEntry("RadiusAjustment", 1.0)); + m_lumToleranceInput->setValue(config->readDoubleNumEntry("LumToleranceAjustment", 1.0)); + m_thresholdInput->setValue(config->readDoubleNumEntry("ThresholdAjustment", 0.08)); + m_textureInput->setValue(config->readDoubleNumEntry("TextureAjustment", 0.0)); + m_sharpnessInput->setValue(config->readDoubleNumEntry("SharpnessAjustment", 0.25)); + m_csmoothInput->setValue(config->readDoubleNumEntry("CsmoothAjustment", 1.0)); + m_lookaheadInput->setValue(config->readDoubleNumEntry("LookAheadAjustment", 2.0)); + m_gammaInput->setValue(config->readDoubleNumEntry("GammaAjustment", 1.4)); + m_dampingInput->setValue(config->readDoubleNumEntry("DampingAjustment", 5.0)); + m_phaseInput->setValue(config->readDoubleNumEntry("PhaseAjustment", 1.0)); + + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); +} + +void ImageEffect_NoiseReduction::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("noisereduction Tool Dialog"); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->writeEntry("LumToleranceAjustment", m_lumToleranceInput->value()); + config->writeEntry("ThresholdAjustment", m_thresholdInput->value()); + config->writeEntry("TextureAjustment", m_textureInput->value()); + config->writeEntry("SharpnessAjustment", m_sharpnessInput->value()); + config->writeEntry("CsmoothAjustment", m_csmoothInput->value()); + config->writeEntry("LookAheadAjustment", m_lookaheadInput->value()); + config->writeEntry("GammaAjustment", m_gammaInput->value()); + config->writeEntry("DampingAjustment", m_dampingInput->value()); + config->writeEntry("PhaseAjustment", m_phaseInput->value()); + config->sync(); +} + +void ImageEffect_NoiseReduction::resetValues() +{ + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); + + m_radiusInput->setValue(1.0); + m_lumToleranceInput->setValue(1.0); + m_thresholdInput->setValue(0.08); + m_textureInput->setValue(0.0); + m_sharpnessInput->setValue(0.25); + m_csmoothInput->setValue(1.0); + m_lookaheadInput->setValue(2.0); + m_gammaInput->setValue(1.4); + m_dampingInput->setValue(5.0); + m_phaseInput->setValue(1.0); + + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); +} + +void ImageEffect_NoiseReduction::prepareEffect() +{ + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); + + double r = m_radiusInput->value(); + double l = m_lumToleranceInput->value(); + double th = m_thresholdInput->value(); + double tx = m_textureInput->value(); + double s = m_sharpnessInput->value(); + double c = m_csmoothInput->value(); + double a = m_lookaheadInput->value(); + double g = m_gammaInput->value(); + double d = m_dampingInput->value(); + double p = m_phaseInput->value(); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + + m_threadedFilter = dynamic_cast(new NoiseReduction(&image, + this, r, l, th, tx, s, c, a, g, d, p)); +} + +void ImageEffect_NoiseReduction::prepareFinal() +{ + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); + + double r = m_radiusInput->value(); + double l = m_lumToleranceInput->value(); + double th = m_thresholdInput->value(); + double tx = m_textureInput->value(); + double s = m_sharpnessInput->value(); + double c = m_csmoothInput->value(); + double a = m_lookaheadInput->value(); + double g = m_gammaInput->value(); + double d = m_dampingInput->value(); + double p = m_phaseInput->value(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast(new NoiseReduction(iface.getOriginalImg(), + this, r, l, th, tx, s, c, a, g, d, p)); +} + +void ImageEffect_NoiseReduction::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_NoiseReduction::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Noise Reduction"), m_threadedFilter->getTargetImage().bits()); +} + +void ImageEffect_NoiseReduction::slotUser3() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Noise Reduction Settings File to Load")) ); + if ( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + if ( stream.readLine() != "# Photograph Noise Reduction Configuration File" ) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Photograph Noise Reduction settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_radiusInput->setValue( stream.readLine().toDouble() ); + m_lumToleranceInput->setValue( stream.readLine().toDouble() ); + m_thresholdInput->setValue( stream.readLine().toDouble() ); + m_textureInput->setValue( stream.readLine().toDouble() ); + m_sharpnessInput->setValue( stream.readLine().toDouble() ); + m_csmoothInput->setValue( stream.readLine().toDouble() ); + m_lookaheadInput->setValue( stream.readLine().toDouble() ); + m_gammaInput->setValue( stream.readLine().toDouble() ); + m_dampingInput->setValue( stream.readLine().toDouble() ); + m_phaseInput->setValue( stream.readLine().toDouble() ); + blockSignals(false); +// slotEffect(); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Photograph Noise Reduction text file.")); + + file.close(); +} + +void ImageEffect_NoiseReduction::slotUser2() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Noise Reduction Settings File to Save")) ); + if ( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Photograph Noise Reduction Configuration File\n"; + stream << m_radiusInput->value() << "\n"; + stream << m_lumToleranceInput->value() << "\n"; + stream << m_thresholdInput->value() << "\n"; + stream << m_textureInput->value() << "\n"; + stream << m_sharpnessInput->value() << "\n"; + stream << m_csmoothInput->value() << "\n"; + stream << m_lookaheadInput->value() << "\n"; + stream << m_gammaInput->value() << "\n"; + stream << m_dampingInput->value() << "\n"; + stream << m_phaseInput->value() << "\n"; + + } + else + KMessageBox::error(this, i18n("Cannot save settings to the Photograph Noise Reduction text file.")); + + file.close(); +} + +} // NameSpace DigikamNoiseReductionImagesPlugin + diff --git a/src/imageplugins/noisereduction/imageeffect_noisereduction.h b/src/imageplugins/noisereduction/imageeffect_noisereduction.h new file mode 100644 index 00000000..b7ee1bb6 --- /dev/null +++ b/src/imageplugins/noisereduction/imageeffect_noisereduction.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_NOISEREDUCTION_H +#define IMAGEEFFECT_NOISEREDUCTION_H + +// Local includes. + +#include "ctrlpaneldlg.h" + +class KDoubleNumInput; + +namespace DigikamNoiseReductionImagesPlugin +{ + +class ImageEffect_NoiseReduction : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_NoiseReduction(TQWidget* parent); + ~ImageEffect_NoiseReduction(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private slots: + + void slotUser2(); + void slotUser3(); + +private: + + KDoubleNumInput *m_radiusInput; + KDoubleNumInput *m_lumToleranceInput; + KDoubleNumInput *m_thresholdInput; + KDoubleNumInput *m_textureInput; + KDoubleNumInput *m_sharpnessInput; + + KDoubleNumInput *m_csmoothInput; + KDoubleNumInput *m_lookaheadInput; + KDoubleNumInput *m_gammaInput; + KDoubleNumInput *m_dampingInput; + KDoubleNumInput *m_phaseInput; +}; + +} // NameSpace DigikamNoiseReductionImagesPlugin + +#endif /* IMAGEEFFECT_NOISEREDUCTION_H */ diff --git a/src/imageplugins/noisereduction/imageplugin_noisereduction.cpp b/src/imageplugins/noisereduction/imageplugin_noisereduction.cpp new file mode 100644 index 00000000..8392933d --- /dev/null +++ b/src/imageplugins/noisereduction/imageplugin_noisereduction.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "noisereductiontool.h" +#include "imageplugin_noisereduction.h" +#include "imageplugin_noisereduction.moc" + +using namespace DigikamNoiseReductionImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_noisereduction, + KGenericFactory("digikamimageplugin_noisereduction")); + +ImagePlugin_NoiseReduction::ImagePlugin_NoiseReduction(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_NoiseReduction") +{ + m_noiseReductionAction = new TDEAction(i18n("Noise Reduction..."), "noisereduction", 0, + this, TQ_SLOT(slotNoiseReduction()), + actionCollection(), "imageplugin_noisereduction"); + + setXMLFile("digikamimageplugin_noisereduction_ui.rc"); + + DDebug() << "ImagePlugin_NoiseReduction plugin loaded" << endl; +} + +ImagePlugin_NoiseReduction::~ImagePlugin_NoiseReduction() +{ +} + +void ImagePlugin_NoiseReduction::setEnabledActions(bool enable) +{ + m_noiseReductionAction->setEnabled(enable); +} + +void ImagePlugin_NoiseReduction::slotNoiseReduction() +{ + NoiseReductionTool *tool = new NoiseReductionTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/noisereduction/imageplugin_noisereduction.h b/src/imageplugins/noisereduction/imageplugin_noisereduction.h new file mode 100644 index 00000000..3cbcb312 --- /dev/null +++ b/src/imageplugins/noisereduction/imageplugin_noisereduction.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_NOISEREDUCTION_H +#define IMAGEPLUGIN_NOISEREDUCTION_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_NoiseReduction : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_NoiseReduction(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_NoiseReduction(); + + void setEnabledActions(bool enable); + +private slots: + + void slotNoiseReduction(); + +private: + + TDEAction *m_noiseReductionAction; +}; + +#endif /* IMAGEPLUGIN_NOISEREDUCTION_H */ diff --git a/src/imageplugins/noisereduction/noisereduction.cpp b/src/imageplugins/noisereduction/noisereduction.cpp new file mode 100644 index 00000000..b9be4729 --- /dev/null +++ b/src/imageplugins/noisereduction/noisereduction.cpp @@ -0,0 +1,809 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Noise Reduction threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Noise Filter algorithm copyright (C) 2005 + * Peter Heckert + * from dcamnoise2 gimp plugin available at this url : + * http://home.arcor.de/peter.heckert/dcamnoise2-0.63.c + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define IIR1(dest,src) (dest) = (d3 = ((((src) * b + d3) * b3 + d2) * b2 + d1) * b1) +#define IIR2(dest,src) (dest) = (d2 = ((((src) * b + d2) * b3 + d1) * b2 + d3) * b1) +#define IIR3(dest,src) (dest) = (d1 = ((((src) * b + d1) * b3 + d3) * b2 + d2) * b1) + +#define IIR1A(dest,src) (dest) = fabs(d3 = ((((src) * b + d3) * b3 + d2) * b2 + d1) * b1) +#define IIR2A(dest,src) (dest) = fabs(d2 = ((((src) * b + d2) * b3 + d1) * b2 + d3) * b1) +#define IIR3A(dest,src) (dest) = fabs(d1 = ((((src) * b + d1) * b3 + d3) * b2 + d2) * b1) + +#define FR 0.212671 +#define FG 0.715160 +#define FB 0.072169 + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "noisereduction.h" + +namespace DigikamNoiseReductionImagesPlugin +{ + +NoiseReduction::NoiseReduction(Digikam::DImg *orgImage, TQObject *parent, + double radius, double lsmooth, double effect, double texture, double sharp, + double csmooth, double lookahead, double gamma, double damping, double phase) + : Digikam::DImgThreadedFilter(orgImage, parent, "NoiseReduction") +{ + m_radius = radius; /* default radius default = 1.0 */ + m_sharp = sharp; /* Sharpness factor default = 0.25 */ + m_lsmooth = lsmooth; /* Luminance Tolerance default = 1.0 */ + m_effect = effect; /* Adaptive filter-effect threshold default = 0.08 */ + m_texture = texture; /* Texture Detail default = 0.0 */ + + m_csmooth = csmooth; /* RGB Tolerance default = 1.0 */ + m_lookahead = lookahead; /* Lookahead default = 2.0 */ + m_gamma = gamma; /* Filter gamma default = 1.0 */ + m_damping = damping; /* Phase jitter Damping default = 5.0 */ + m_phase = phase; /* Area Noise Clip default = 1.0 */ + + m_iir.B = 0.0; + m_iir.b1 = 0.0; + m_iir.b2 = 0.0; + m_iir.b3 = 0.0; + m_iir.b0 = 0.0; + m_iir.r = 0.0; + m_iir.q = 0.0; + m_iir.p = 0; + + m_clampMax = m_orgImage.sixteenBit() ? 65535 : 255; + + initFilter(); +} + +// Remove noise on the region, given a source region, dest. +// region, width and height of the regions, and corner coordinates of +// a subregion to act upon. Everything outside the subregion is unaffected. + +void NoiseReduction::filterImage(void) +{ + int bytes = m_orgImage.bytesDepth(); // Bytes per pixel sample + uchar *srcPR = m_orgImage.bits(); + uchar *destPR = m_destImage.bits(); + int width = m_orgImage.width(); + int height = m_orgImage.height(); + + int row, col, i, progress; + float prob = 0.0; + + int w = (int)((m_radius + m_lookahead + m_damping + m_phase) * 4.0 + 40.0); + + // NOTE: commented from original implementation + // if (radius < m_lookahead) w = m_lookahead * 4.0 + 40.0; + + float csmooth = m_csmooth; + + // Raw Filter preview + + if (csmooth >= 0.99) csmooth = 1.0; + + // Allocate and init buffers + + uchar *src = new uchar[ TQMAX (width, height) * bytes ]; + uchar *dest = new uchar[ TQMAX (width, height) * bytes ]; + float *data = new float[ TQMAX (width, height) + 2*w ]; + float *data2 = new float[ TQMAX (width, height) + 2*w ]; + float *buffer = new float[ TQMAX (width, height) + 2*w ]; + float *rbuf = new float[ TQMAX (width, height) + 2*w ]; + float *tbuf = new float[ TQMAX (width, height) + 2*w ]; + + memset (src, 0, TQMAX (width, height) * bytes); + memset (dest, 0, TQMAX (width, height) * bytes); + + for (i=0 ; i < TQMAX(width,height)+2*w-1 ; i++) + data[i] = data2[i] = buffer[i] = rbuf[i] = tbuf[i] = 0.0; + + // Initialize the damping filter coefficients + + iir_init(m_radius); + + // blur the rows + + for (row = 0 ; !m_cancel && (row < height) ; row++) + { + memcpy(src, srcPR + row*width*bytes, width*bytes); + memcpy(dest, src, width*bytes); + + blur_line (data+w, data2+w, buffer+w, rbuf+w, tbuf+w, src, dest, width); + + memcpy(destPR + row*width*bytes, dest, width*bytes); + + progress = (int)(((double)row * 20.0) / height); + if ( progress%2 == 0 ) + postProgress( progress ); + } + + // blur the cols + + for (col = 0 ; !m_cancel && (col < width) ; col++) + { + for (int n = 0 ; n < height ; n++) + memcpy(src + n*bytes, destPR + (col + width*n)*bytes, bytes); + + for (int n = 0 ; n < height ; n++) + memcpy(dest + n*bytes, srcPR + (col + width*n)*bytes, bytes); + + blur_line (data+w, data2+w, buffer+w, rbuf+w, tbuf+w, src, dest, height); + + for (int n = 0 ; n < height ; n++) + memcpy(destPR + (col + width*n)*bytes, dest + n*bytes, bytes); + + progress = (int)(20.0 + ((double)col * 20.0) / width); + if ( progress%2 == 0 ) + postProgress( progress ); + } + + // merge the source and destination (which currently contains + // the blurred version) images + + for (row = 0 ; !m_cancel && (row < height) ; row++) + { + uchar *s = src; + uchar *d = dest; + unsigned short *s16 = (unsigned short *)src; + unsigned short *d16 = (unsigned short *)dest; + float value; + int u, v; + + // get source row + + memcpy(src, srcPR + row*width*bytes, width*bytes); + memcpy(dest, destPR + row*width*bytes, width*bytes); + + // get dest row and combine the two + + float t = m_csmooth; + float t2 = m_lsmooth; + + // Values are squared, so that sliders get a nonlinear chracteristic + // for better adjustment accuracy when values are small. + t*=t; + t2*=t2; + + for (u = 0 ; !m_cancel && (u < width) ; u++) + { + float dpix[3], spix[3]; + float lum, red, green, blue; + float lum2, red2, green2, blue2; + + if (m_orgImage.sixteenBit()) // 16 bits image + { + red = (float) s16[2]/(float)m_clampMax; + green = (float) s16[1]/(float)m_clampMax; + blue = (float) s16[0]/(float)m_clampMax; + } + else // 8 bits image + { + red = (float) s[2]/(float)m_clampMax; + green = (float) s[1]/(float)m_clampMax; + blue = (float) s[0]/(float)m_clampMax; + } + + spix[2] = red; + spix[1] = green; + spix[0] = blue; + + lum = (FR*red + FG*green + FB*blue); + + if (m_orgImage.sixteenBit()) // 16 bits image + { + red2 = (float) d16[2]/(float)m_clampMax; + green2 = (float) d16[1]/(float)m_clampMax; + blue2 = (float) d16[0]/(float)m_clampMax; + } + else // 8 bits image + { + red2 = (float) d[2]/(float)m_clampMax; + green2 = (float) d[1]/(float)m_clampMax; + blue2 = (float) d[0]/(float)m_clampMax; + } + + lum2 = (FR*red2 + FG*green2 + FB*blue2); + + // Calculate luminance error (contrast error) for filtered template. + // This error is biggest, where edges are. Edges anyway cannot be filtered. + // Therefore we can correct luminance error in edges without increasing noise. + // Should be adjusted carefully, or not so carefully if you intentionally want to add noise. + // Noise, if not colorized, /can/ look good, so this makes sense. + + float dl = lum - lum2; + + // Multiply dl with first derivative of gamma curve divided by derivative value for midtone 0.5 + // So bright tones will be corrected more (get more luminance noise and -information) than + // darker values because bright parts of image generally are less noisy, this is what we want. + + dl *= pow(lum2/0.5, m_gamma-1.0); + + if (t2 > 0.0) + dl *= (1.0 - exp(-dl*dl/(2.0*t2*t2))); + + // NOTE: commented from original implementation + // if (dl > p) dl = p; + // if (dl < -p) dl = -p; + + dpix[2] = red2 + dl; + dpix[1] = green2 + dl; + dpix[0] = blue2 + dl; + + for (v = 0 ; !m_cancel && (v < 3) ; v++) + { + float value = spix[v]; + float fvalue = dpix[v]; + float mvalue = (value + fvalue)/2.0; + float diff = (value) - (fvalue); + + // Multiply diff with first derivative of gamma curve divided by derivative value for midtone 0.5 + // So midtones will stay unchanged, darker values get more blur and brighter values get less blur + // when we increase gamma. + + diff *= pow(mvalue/0.5, m_gamma-1.0); + + // Calculate noise probability for pixel + // TODO : probably it is not probability but an arbitrary curve. + // Probably we should provide a GUI-interface for this!!! + + if (t > 0.0) + prob = exp(-diff*diff/(2.0*t*t)); + else + prob = 0.0; + + // Allow viewing of raw filter output + + if (t >= 0.99) + prob = 1.0; + + dpix[v] = value = fvalue * prob + value * (1.0 - prob); + } + + if (m_orgImage.sixteenBit()) // 16 bits image + { + value = dpix[0]*(float)m_clampMax+0.5; + d16[0] = (unsigned short)CLAMP(value, 0, m_clampMax); + value = dpix[1]*(float)m_clampMax+0.5; + d16[1] = (unsigned short)CLAMP(value, 0, m_clampMax); + value = dpix[2]*(float)m_clampMax+0.5; + d16[2] = (unsigned short)CLAMP(value, 0, m_clampMax); + + d16 += 4; + s16 += 4; + } + else // 8 bits image + { + value = dpix[0]*(float)m_clampMax+0.5; + d[0] = (uchar)CLAMP(value, 0, m_clampMax); + value = dpix[1]*(float)m_clampMax+0.5; + d[1] = (uchar)CLAMP(value, 0, m_clampMax); + value = dpix[2]*(float)m_clampMax+0.5; + d[2] = (uchar)CLAMP(value, 0, m_clampMax); + + d += 4; + s += 4; + } + } + + memcpy(destPR + row*width*bytes, dest, width*bytes); + + progress = (int)(40.0 + ((double)row * 60.0) / height); + if ( progress%2 == 0 ) + postProgress( progress ); + } + + delete [] data; + delete [] data2; + delete [] buffer; + delete [] rbuf; + delete [] tbuf; + delete [] dest; + delete [] src; +} + +// This function is written as if it is blurring a column at a time, +// even though it can operate on rows, too. There is no difference +// in the processing of the lines, at least to the blur_line function. +// 'len' is the length of src and dest + +void NoiseReduction::blur_line(float* const data, float* const data2, float* const buffer, + float* rbuf, float* tbuf, const uchar *src, uchar *dest, int len) +{ + int b; + int row; + int idx; + + unsigned short *src16 = (unsigned short *)src; + unsigned short *dest16 = (unsigned short *)dest; + + // Calculate radius factors + + for (row = 0, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++) + { + // Color weigths are chosen proportional to Bayer Sensor pixel count + + if (m_orgImage.sixteenBit()) // 16 bits image + { + data[idx] = (float) dest16[row+2] / (float)m_clampMax * 0.25; // Red color + data[idx] += (float) dest16[row+1] / (float)m_clampMax * 0.5; // Green color + data[idx] += (float) dest16[row] / (float)m_clampMax * 0.25; // Blue color + data[idx] = mypow(data[idx], m_gamma); + } + else // 8 bits image + { + data[idx] = (float) dest[row+2] / (float)m_clampMax * 0.25; // Red color + data[idx] += (float) dest[row+1] / (float)m_clampMax * 0.5; // Green color + data[idx] += (float) dest[row] / (float)m_clampMax * 0.25; // Blue color + data[idx] = mypow(data[idx], m_gamma); + } + } + + filter(data, data2, buffer, rbuf, tbuf, len, -1); + + // Do actual filtering + + for (b = 0 ; !m_cancel && (b < 3) ; b++) + { + for (row = b, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++) + { + if (m_orgImage.sixteenBit()) // 16 bits image + data[idx] = (float)src16[row] / (float)m_clampMax; + else // 8 bits image + data[idx] = (float)src[row] / (float)m_clampMax; + } + + filter(data, data2, buffer, rbuf, tbuf, len, b); + + for (row = b, idx = 0 ; !m_cancel && (idx < len) ; row += 4, idx++) + { + int value = (int)(data[idx] * (float)m_clampMax + 0.5); + + if (m_orgImage.sixteenBit()) // 16 bits image + dest16[row] = (unsigned short)CLAMP( value, 0, m_clampMax); + else // 8 bits image + dest[row] = (uchar)CLAMP( value, 0, m_clampMax); + } + } +} + +void NoiseReduction::iir_init(double r) +{ + if (m_iir.r == r) + return; + + // damping settings; + m_iir.r = r; + + double q; + + if ( r >= 2.5) + q = 0.98711 * r - 0.96330; + else + q = 3.97156 - 4.14554 * sqrt(1.0 - 0.26891 * r); + + m_iir.q = q; + m_iir.b0 = 1.57825 + ((0.422205 * q + 1.4281) * q + 2.44413) * q; + m_iir.b1 = ((1.26661 * q +2.85619) * q + 2.44413) * q / m_iir.b0; + m_iir.b2 = - ((1.26661*q +1.4281) * q * q ) / m_iir.b0; + m_iir.b3 = 0.422205 * q * q * q / m_iir.b0; + m_iir.B = 1.0 - (m_iir.b1 + m_iir.b2 + m_iir.b3); +} + +void NoiseReduction::box_filter(double *src, double *end, double *dest, double radius) +{ + int boxwidth = 1; + float box = (*src); + float fbw = 2.0 * radius; + + if (fbw < 1.0) + fbw = 1.0; + + while(boxwidth+2 <= (int) fbw) boxwidth+=2, box += (src[boxwidth/2]) + (src[-boxwidth/2]); + + double frac = (fbw - (double) boxwidth) / 2.0; + int bh = boxwidth / 2; + int bh1 = boxwidth / 2+1; + + for ( ; src <= end ; src++, dest++) + { + *dest = (box + frac * ((src[bh1])+(src[-bh1]))) / fbw; + box = box - (src[-bh]) + (src[bh1]); + } +} + +// Bidirectional IIR-filter, speed optimized + +void NoiseReduction::iir_filter(float* const start, float* const end, float* dstart, + double radius, const int type) +{ + if (!dstart) + dstart = start; + + int width; + float *src = start; + float *dest = dstart; + float *dend = dstart + (end - start); + + radius = floor((radius + 0.1) / 0.5) * 0.5; + + // NOTE: commented from original implementation + // gfloat boxwidth = radius * 2.0; + // gint bw = (gint) boxwidth; + + int ofs = (int)radius; + if (ofs < 1) ofs = 1; + + double d1, d2, d3; + + width = end - start + 1; + + if (radius < 0.25) + { + if ( start != dest ) + { + memcpy(dest, start, width*sizeof(*dest)); + return; + } + } + + iir_init(radius); + + const double b1 = m_iir.b1; + const double b2 = m_iir.b2 / m_iir.b1; + const double b3 = m_iir.b3 / m_iir.b2; + const double b = m_iir.B / m_iir.b3; + + switch(type) + { + case Gaussian: + + d1 = d2 = d3 = *dest; + dend -= 6; + src--; + dest--; + + while (dest < dend) + { + IIR1(*(++dest), *(++src)); + IIR2(*(++dest), *(++src)); + IIR3(*(++dest), *(++src)); + IIR1(*(++dest), *(++src)); + IIR2(*(++dest), *(++src)); + IIR3(*(++dest), *(++src)); + } + + dend += 6; + + while (1) + { + if (++dest > dend) break; + IIR1(*dest,*(++src)); + if (++dest > dend) break; + IIR2(*dest,*(++src)); + if (++dest > dend) break; + IIR3(*dest,*(++src)); + } + + d1 = d2 = d3 = dest[-1]; + dstart += 6; + + while (dest > dstart) + { + --dest, IIR1(*dest, *dest); + --dest, IIR2(*dest, *dest); + --dest, IIR3(*dest, *dest); + --dest, IIR1(*dest, *dest); + --dest, IIR2(*dest, *dest); + --dest, IIR3(*dest, *dest); + } + + dstart -= 6; + + while (1) + { + if (--dest < dstart) break; + IIR1(*dest, *dest); + if (--dest < dstart) break; + IIR2(*dest, *dest); + if (--dest < dstart) break; + IIR3(*dest, *dest); + } + + break; + + case SecondDerivative: // rectified and filtered second derivative, source and dest may be equal + + d1 = d2 = d3 = 0.0; + dest[0] = dest[ofs] = 0.0; + dend -= 6; + dest--; + src--; + + while (dest < dend) + { + ++src, IIR1(*(++dest), src[ofs]-src[0]); + ++src, IIR2(*(++dest), src[ofs]-src[0]); + ++src, IIR3(*(++dest), src[ofs]-src[0]); + ++src, IIR1(*(++dest), src[ofs]-src[0]); + ++src, IIR2(*(++dest), src[ofs]-src[0]); + ++src, IIR3(*(++dest), src[ofs]-src[0]); + } + + dend += 6; + + while (1) + { + if (++dest > dend) break; + ++src, IIR1(*dest, src[ofs]-src[0]); + if (++dest > dend) break; + ++src, IIR2(*dest, src[ofs]-src[0]); + if (++dest > dend) break; + ++src, IIR3(*dest, src[ofs]-src[0]); + } + + d1 = d2 = d3 = 0.0; + dest[-1] = dest[-ofs-1] = 0.0; + dstart += 6; + + while (dest > dstart) + { + --dest, IIR1A(*dest, dest[0]-dest[-ofs]); + --dest, IIR2A(*dest, dest[0]-dest[-ofs]); + --dest, IIR3A(*dest, dest[0]-dest[-ofs]); + --dest, IIR1A(*dest, dest[0]-dest[-ofs]); + --dest, IIR2A(*dest, dest[0]-dest[-ofs]); + --dest, IIR3A(*dest, dest[0]-dest[-ofs]); + } + + dstart -= 6; + + while (1) + { + if (--dest < dstart) break; + IIR1A(*dest, dest[0]-dest[-ofs]); + if (--dest < dstart) break; + IIR2A(*dest, dest[0]-dest[-ofs]); + if (--dest < dstart) break; + IIR3A(*dest, dest[0]-dest[-ofs]); + } + + break; + } +} + +// A forward-backward box filter is used here and the radius is adapted to luminance jump. +// Radius is calculated fron 1st and 2nd derivative of intensity values. +// (Its not exactly 2nd derivative, but something similar, optimized by experiment) +// The radius variations are filtered. This reduces spatial phase jitter. + +void NoiseReduction::filter(float *buffer, float *data, float *data2, float *rbuf, + float */*tbuf*/, int width, int color) +{ + float *lp = data; + float *rp = data + width-1; + float *lp2 = data2; + float *blp = buffer; + float *brp = buffer + width-1; + float *rbuflp = rbuf; + float *rbufrp = rbuf + width-1; + float fboxwidth = m_radius*2.0; + float fradius = m_radius; + float *p1, *p2; + + if (fboxwidth < 1.0) fboxwidth = 1.0 ; + if (fradius < 0.5) fradius = 0.5; + + int i, pass; + int ofs, ofs2; + float maxrad; + float fbw; + float val; + double rfact = m_effect*m_effect; + double sharp = m_sharp; + + ofs2 = (int)floor(m_damping * 2.0 + 0.1); + ofs = (int)floor(m_lookahead * 2.0 + 0.1); + int w = (int)(fboxwidth + m_damping + m_lookahead + m_phase + 2.0); + + // Mirror image edges + + for (i=1 ; i <= w ; i++) + blp[-i] = blp[i]; + + for (i=1 ; i <= w ; i++) + brp[i] = brp[-i]; + + if (color < 0) // Calc 2nd derivative + { + // boost high frequency in rbuf + + for (p1 = blp, p2 = rbuflp ; p1 <= brp ; p1++, p2++) + { + *p2 = (sharp+1.0) * p1[0] - sharp * 0.5 * (p1[-ofs]+p1[ofs]); + } + + iir_filter(rbuflp-w, rbufrp+w, blp-w, m_lookahead, SecondDerivative); + + // Mirror image edges + + for (i = 1 ; i <= w ; i++) + blp[-i] = blp[i]; + + for (i = 1 ; i <= w ; i++) + brp[i] = brp[-i]; + + // boost high frequency in rbuf + + for (p1 = blp, p2 = rbuflp ; p1 <= brp ; p1++, p2++) + { + *p2 = ((sharp+1.0) * (p1[0]) - sharp * 0.5 * ((p1[-ofs2])+(p1[ofs2]))); + } + + // Mirror rbuf edges + + for (i = 1 ; i <= w ; i++) + rbuflp[-i] = rbuflp[i]; + + for (i = 1 ; i <= w ; i++) + rbufrp[i] = rbufrp[-i]; + + // Lowpass (gauss) filter rbuf, remove phase jitter + + iir_filter(rbuflp-w+5, rbufrp+w-5, rbuflp-w+5, m_damping, Gaussian); + + for (i = -w+5; i < width-1+w-5 ; i++) + { + // NOTE: commented from original implementation + // val = rbuflp[i]; + + val = rbuflp[i]-rfact; + + // Avoid division by zero, clip negative filter overshoot + + if (val < rfact/fradius) val=rfact/fradius; + + val = rfact/val; + + // NOTE: commented from original implementation + // val = pow(val/fradius,m_phase)*fradius; + + if (val < 0.5) val = 0.5; + + rbuflp[i] = val*2.0; + } + + // Mirror rbuf edges + + for (i=1 ; i <= w ; i++) + rbuflp[-i] = rbuflp[i]; + + for (i=1 ; i <= w ; i++) + rbufrp[i] = rbufrp[-i]; + + return; + } + + // Calc lowpass filtered input signal + + iir_filter(blp-w+1, brp+w-1, lp2-w+1, m_radius, Gaussian); + + // Subtract low frequency from input signal (aka original image data) + // and predistort this signal + + val = m_texture + 1.0; + + for (i = -w+1 ; i <= width-1+w-1 ; i++) + { + blp[i] = mypow(blp[i] - lp2[i], val); + } + + float *src, *dest; + val = m_texture + 1.0; + + pass = 2; + + while (pass--) + { + float sum; + int ibw; + src = blp; + dest = lp; + maxrad = 0.0; + + // Mirror left edge + + for (i=1 ; i <= w ; i++) + src[-i] = src[i]; + + sum = (src[-1] += src[-2]); + + // forward pass + + for (rbuf = rbuflp-(int) m_phase ; rbuf <= rbufrp; src++, dest++, rbuf++) + { + // NOTE: commented from original implementation + //fbw = fabs( rbuf[-ofs2]*ll2+rbuf[-ofs2-1]*rl2); + + fbw = *rbuf; + + if (fbw > (maxrad += 1.0)) fbw = maxrad; + else if (fbw < maxrad) maxrad = fbw; + + ibw = (int)fbw; + *src = sum += *src; + *dest = (sum-src[-ibw]+(src[-ibw]-src[-ibw-1])*(fbw-ibw))/fbw; + } + + src = rp; + dest = brp; + maxrad = 0.0; + + // Mirror right edge + + for (i=1 ; i <= w ; i++) + src[i] = src[-i]; + + sum = (src[1] += src[2]); + + // backward pass + + for ( rbuf = rbufrp +(int) m_phase ; rbuf >= rbuflp; src--, dest--, rbuf--) + { + // NOTE: commented from original implementation + //fbw = fabs( rbuf[ofs2]*ll2+rbuf[ofs2+1]*rl2); + + fbw = *rbuf; + + if (fbw > (maxrad +=1.0)) fbw = maxrad; + else if (fbw < maxrad) maxrad = fbw; + + ibw = (int)fbw; + + *src = sum += *src; + *dest = (sum-src[ibw]+(src[ibw]-src[ibw+1])*(fbw-ibw))/fbw; + } + } + + val = 1.0 / (m_texture + 1.0); + + for (i = -w+1 ; i <= width-1+w-1 ; i++) + { + // Undo predistortion + + blp[i]= mypow(blp[i],val); + + // Add in low frequency + + blp[i] += lp2[i]; + + // NOTE: commented from original implementation + // if (blp[i] >= 0.0) blp[i] = pow(blp[i],val); + // else blp[i] = 0.0; + } +} + +} // NameSpace DigikamNoiseReductionImagesPlugin diff --git a/src/imageplugins/noisereduction/noisereduction.h b/src/imageplugins/noisereduction/noisereduction.h new file mode 100644 index 00000000..69e14123 --- /dev/null +++ b/src/imageplugins/noisereduction/noisereduction.h @@ -0,0 +1,257 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Noise Reduction threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Noise Filter algorithm copyright (C) 2005 + * Peter Heckert + * from dcamnoise2 gimp plugin available at this url : + * http://home.arcor.de/peter.heckert/dcamnoise2-0.63.c + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef NOISE_REDUCTION_H +#define NOISE_REDUCTION_H + +// C++ includes. + +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +/**============= NOTICE TO USE THE FILTER =============================================================== + * + * Let me explain, how the filter works, some understanding is necessary to use it: + * + * Hint for the novice user: + * In most cases only Filter Max Radius, Filter treshold and Texture Detail are needed and the other + * params can be left at their default setting. + * + * Main Filter (Preprocessing) + * First, a filtered template is generated, using an adaptive filter. + * To see this template, we must set _Luminance tolerance, _Color tolerance to 1.0. + *------------------------------------------------------------------------------------------------------------ + * + * "Filter max. Radius" is preset to 5.0 + * This is good for most noise situations. + * In any case it must be about the same size as noise granularity ore somewhat more. + * If it is set higher than necessary, then it can cause unwanted blur. + *------------------------------------------------------------------------------------------------------------ + * + * "Filter Threshold" should be set so that edges are clearly visible and noise is smoothed out. + * This threshold value is not bound to any intensity value, it is bound to the second derivative of + * intensity values. + * Simply adjust it and watch the preview. Adjustment must be made carefully, because the gap + * between "noisy", "smooth", and "blur" is very small. Adjust it as carefully as you would adjust + * the focus of a camera. + *------------------------------------------------------------------------------------------------------------ + * + * "Lookahead" defines the pixel distance in which the filter looks ahead for luminance variations + * Normally the default value should do. + * When _Lookahead is increased, then spikenoise is erased. + * Eventually readjust Filter treshold, when you changed lookahead. + * When the value is to high, then the adaptive filter cannot longer accurately track image details, and + * noise can reappear or blur can occur. + * + * Minimum value is 1.0, this gives best accuracy when blurring very weak noise. + * + * I never had good success with other values than 2.0. + * However, for images with extemely high or low resolution another value possibly is better. + * Use it only as a last ressort. + *------------------------------------------------------------------------------------------------------------ + * + * "Phase Jitter Damping" defines how fast the adaptive filter-radius reacts to luminance variations. + * I have preset a value, that should do in most cases. + * If increased, then edges appear smoother, if too high, then blur may occur. + * If at minimum then noise and phase jitter at edges can occur. + * It can suppress Spike noise when increased and this is the preferred method to remove spike noise. + *------------------------------------------------------------------------------------------------------------ + * + * "Sharpness" does just what it says, it improves sharpness. It improves the frequency response for the filter. + * When it is too strong then not all noise can be removed, or spike noise may appear. + * Set it near to maximum, if you want to remove weak noise or JPEG-artifacts, without loosing detail. + *------------------------------------------------------------------------------------------------------------ + * + * "Erosion". The new filter gives better sharpness and this also gives problems + * with spike noise. The Erosion param erodes singular spikes and it has a smooth effect to edges, and sharpens + * edges by erosion, so noise at edges is eroded. + * The effect is dependant from sharpness,phase-jitter damping and lookahead. + * Set it to minimum (zero), if you want to remove weak noise or JPEG-artifacts. + * When "Erosion" is increased, then also increasing "Phase Jitter Damping" is often useful + * + * It works nicely. Apart from removing spike noise it has a sharpening and antialiasing effect to edges + * (Sharpening occurs by erosion, not by deconvolution) + *------------------------------------------------------------------------------------------------------------ + * + * "Texture Detail" can be used, to get more or less texture accuracy. + * When decreased, then noise and texture are blurred out, when increased then texture is + * amplified, but also noise will increase. + * It has almost no effect to image edges, opposed to Filter theshold, which would blur edges, when increased. + * + * E.g. if Threshold is adjusted in away so that edges are sharp, and there is still too much area noise, then + * Texture detail could be used to reduce noise without blurring edges. + * (Another way would be to decrease radius and to increase threshold) + * + *------------------------------------------------------------------------------------------------------------ + * + * The filtered image that is now seen in the preview, is used as template for the following processing steps, + * therefore it is important to do this adjustment in first place and to do it as good as possible. + *------------------------------------------------------------------------------------------------------------ + * + * Combining original image and filtered image, using tolerance thresholds (Postprocessing) + * This can give a final touch of sharpness to your image. + * It is not necessary to do this, if you want to reduce JPEG-artifacts or weak noise. + * It's purpose is to master strong noise without loosing too much sharpness. + * + * Note, that this all is done in one filter invocation. Preprocessing and postprocessing is done in one run, + * but logically and in the algorithm they are different and ordered processes. + * + * + * Adjust _Color tolerance or/and Luminance tolerance, (if necessary) so that you get the final image. + * I recommend to use only one, either _Color or _Luminance. + * These settings do not influence the main smoothing process. What they really do is this: + * + * The tolerance values are used as error-thresholds to compare the filtered template with the original + * image. The plugin algorithm uses them to combine the filtered template with the original image + * so that noise and filter errors (blur) are thrown out. + * A filtered pixel, that is too far away from the original pixel will be overridden by original image content. + * + * Hint: + * If you cange other sliders, like lookahead or Texture Detail, then you should set color tolerance and + * luminance tolerance to 1.0 (right end), because otherwise the filtered template is partially hidden + * and e.g. the effects for the damping filter cant be seen clearly and cant be optimized. + *------------------------------------------------------------------------------------------------------------ + * + * _Gamma can be used to increase the tolerance values for darker areas (which commonly are more noisy) + * This results in more blur for shadow areas. + * + * Hint for users of previous versions: + * Gamma also influences the main-filter process. While the previous version did not have this feature, + * I have reimplemented it, however, the algorithm used is totally new. + * + * + * Keep in mind, how the filter works, then usage should be easy! + * + * + * ================ THEORY AND TECHNIC ======================================================================= + * + * Some interesting things (theoretic and technic) + * This plugin bases on the assumption, that noise has no 2-dimensional correlation and therefore + * can be removed in a 1-dimensional process. + * To remove noise, I use a four-times boxfilter with variable radius. + * + * The radius is calculated from 2nd derivative of pixeldata. + * A gauss filter is used to calculte 2nd derivative. + * The filter has some inbuilt features to clip low amplitude noise to clip very high values that would + * slow down response time. + * The 2nd derivative is lowpassfiltered and then radius is calculated as (Filter Treshold)/2nd_derivative. + * The radius modulation data is precalulated and buffered an is used to steer filter radius when + * the actual filtering occurs. + * + * Noise and texture can be further suppressed by nonlinear distortion before adaptive filtering. + * To make this possible I subtract low frequency from image data before denoising, so that I get a + * bipolar, zerosymmetric image signal. + * + * The filter works in a /one-dimensional/ way. It is applied to x and then to y axis. + * + * After filtering a zerodimensional point operator (pixel by pixel comparison) is used, where + * filter-errors are thrown out. + * This is meant to limit and control filter errors,it can give "final touch" to the image, but it has + * nothing to do with the main filter process. + * + * I do not know if something like this filter already exists. + * It is all based on my own ideas and experiments. + * Possibly a separable adaptive gauss-filter is a new thing. + * Also it is an impossible thing, from a mathemathical point of view ;-) + * It is possible only for bandwidth limited images. + * Happyly most photographic images are bandwidth limited, or when they are noisy then we want + * to limit banwith locally. And this is, what the filter does: It limits bandwidth locally, dependent + * from (approximately) 2nd derivative of intensity. + * + * Because gauss filtering is essentially linear diffusion, and because this filter uses a variable + * nonlinear modulated gaussfilter (four box passes are almost gauss) we could say, that this filter + * implements a special subclass of nonlinear adaptive diffusion, which is separable, and indeed, + * results are very similar to nonlinear diffusion filters. + * However, because the filter is separable, it is much faster and needs less memory. + */ + +namespace DigikamNoiseReductionImagesPlugin +{ + +class NoiseReduction : public Digikam::DImgThreadedFilter +{ + +public: + + NoiseReduction(Digikam::DImg *orgImage, TQObject *parent, + double radius, double lsmooth, double effect, double texture, double sharp, + double csmooth, double lookahead, double gamma, double damping, double phase); + ~NoiseReduction(){}; + +private: + + void filterImage(void); + + void iir_init(double r); + void box_filter(double *src, double *end, double *dest, double radius); + void iir_filter(float* const start, float* const end, float* dstart, double radius, const int type); + void filter(float *buffer, float *data, float *data2, float *rbuf, float *tbuf, int width, int color); + void blur_line(float* const data, float* const data2, float* const buffer, + float* rbuf, float* tbuf, const uchar *src, uchar *dest, int len); + + inline double mypow(double val, double ex) + { + if (fabs(val) < 1e-16) return 0.0; + if (val > 0.0) return exp(log(val)*ex); + return -exp(log(-val)*ex); + }; + +private: + + struct iir_param + { + double B, b1, b2, b3, b0, r, q; + double *p; + } m_iir; + + enum IIRFilteringMode + { + Gaussian=0, + SecondDerivative + }; + + int m_clampMax; + + double m_radius; + double m_lsmooth; + double m_csmooth; + double m_effect; + double m_lookahead; + double m_gamma; + double m_damping; + double m_phase; + double m_texture; + double m_sharp; +}; + +} // NameSpace DigikamNoiseReductionImagesPlugin + +#endif /* NOISE_REDUCTION_H */ diff --git a/src/imageplugins/noisereduction/noisereductiontool.cpp b/src/imageplugins/noisereduction/noisereductiontool.cpp new file mode 100644 index 00000000..d1e4908a --- /dev/null +++ b/src/imageplugins/noisereduction/noisereductiontool.cpp @@ -0,0 +1,536 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "noisereduction.h" +#include "noisereductiontool.h" +#include "noisereductiontool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamNoiseReductionImagesPlugin +{ + +NoiseReductionTool::NoiseReductionTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("noisereduction"); + setToolName(i18n("Noise Reduction")); + setToolIcon(SmallIcon("noisereduction")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 1, 1); + + TQTabWidget *mainTab = new TQTabWidget(m_gboxSettings->plainPage()); + TQWidget* firstPage = new TQWidget( mainTab ); + TQGridLayout* grid1 = new TQGridLayout(firstPage, 6, 1); + + TQLabel *label1 = new TQLabel(i18n("Radius:"), firstPage); + + m_radiusInput = new RDoubleNumInput(firstPage); + m_radiusInput->setPrecision(1); + m_radiusInput->setRange(0.0, 10.0, 0.1); + m_radiusInput->setDefaultValue(1.0); + TQWhatsThis::add( m_radiusInput, i18n("

    Radius: this control selects the " + "gliding window size used for the filter. Larger values do not increase " + "the amount of time needed to filter each pixel in the image but " + "can cause blurring. This window moves across the image, and the " + "color in it is smoothed to remove imperfections. " + "In any case it must be about the same size as the noise granularity " + "or somewhat more. If it is set higher than necessary, then it " + "can cause unwanted blur.")); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Threshold:"), firstPage); + + m_thresholdInput = new RDoubleNumInput(firstPage); + m_thresholdInput->setPrecision(2); + m_thresholdInput->setRange(0.0, 1.0, 0.01); + m_thresholdInput->setDefaultValue(0.08); + TQWhatsThis::add( m_thresholdInput, i18n("

    Threshold: use the slider for coarse adjustment, " + "and the spin control for fine adjustment to control edge detection sensitivity. " + "This value should be set so that edges and details are clearly visible " + "and noise is smoothed out. " + "Adjustment must be made carefully, because the gap between \"noisy\", " + "\"smooth\", and \"blur\" is very small. Adjust it as carefully as you would adjust " + "the focus of a camera.")); + + // ------------------------------------------------------------- + + TQLabel *label4 = new TQLabel(i18n("Texture:"), firstPage); + + m_textureInput = new RDoubleNumInput(firstPage); + m_textureInput->setPrecision(2); + m_textureInput->setRange(-0.99, 0.99, 0.01); + m_textureInput->setDefaultValue(0.0); + TQWhatsThis::add( m_textureInput, i18n("

    Texture: this control sets the texture accuracy. " + "This value can be used, to get more or less texture accuracy. When decreased, " + "then noise and texture are blurred out, when increased then texture is " + "amplified, but also noise will increase. It has almost no effect on image edges.")); + + // ------------------------------------------------------------- + + TQLabel *label7 = new TQLabel(i18n("Sharpness:"), firstPage); // Filter setting "Lookahead". + + m_sharpnessInput = new RDoubleNumInput(firstPage); + m_sharpnessInput->setPrecision(2); + m_sharpnessInput->setRange(0.0, 1.0, 0.1); + m_sharpnessInput->setDefaultValue(0.25); + TQWhatsThis::add( m_sharpnessInput, i18n("

    Sharpness: " + "This value improves the frequency response for the filter. " + "When it is too strong then not all noise can be removed, or spike noise may appear. " + "Set it near to maximum, if you want to remove very weak noise or JPEG-artifacts, " + "without losing detail.")); + + // ------------------------------------------------------------- + + TQLabel *label5 = new TQLabel(i18n("Edge Lookahead:"), firstPage); // Filter setting "Sharp". + + m_lookaheadInput = new RDoubleNumInput(firstPage); + m_lookaheadInput->setPrecision(2); + m_lookaheadInput->setRange(0.01, 20.0, 0.01); + m_lookaheadInput->setDefaultValue(2.0); + TQWhatsThis::add( m_lookaheadInput, i18n("

    Edge: " + "This value defines the pixel distance to which the filter looks ahead for edges. " + "When this value is increased, then spike noise is erased. " + "You can eventually re-adjust the Edge filter, when you have changed this setting. " + "When this value is too high, the adaptive filter can no longer accurately track " + "image details, and noise or blurring can occur.")); + + // ------------------------------------------------------------- + + TQLabel *label10 = new TQLabel(i18n("Erosion:"), firstPage); + + m_phaseInput = new RDoubleNumInput(firstPage); + m_phaseInput->setPrecision(1); + m_phaseInput->setRange(0.5, 20.0, 0.5); + m_phaseInput->setDefaultValue(1.0); + TQWhatsThis::add( m_phaseInput, i18n("

    Erosion: " + "Use this to increase edge noise erosion and spike noise erosion " + "(noise is removed by erosion).")); + + grid1->addMultiCellWidget(label1, 0, 0, 0, 0); + grid1->addMultiCellWidget(m_radiusInput, 0, 0, 1, 1); + grid1->addMultiCellWidget(label3, 1, 1, 0, 0); + grid1->addMultiCellWidget(m_thresholdInput, 1, 1, 1, 1); + grid1->addMultiCellWidget(label4, 2, 2, 0, 0); + grid1->addMultiCellWidget(m_textureInput, 2, 2, 1, 1); + grid1->addMultiCellWidget(label7, 3, 3, 0, 0); + grid1->addMultiCellWidget(m_sharpnessInput, 3, 3, 1, 1); + grid1->addMultiCellWidget(label5, 4, 4, 0, 0); + grid1->addMultiCellWidget(m_lookaheadInput, 4, 4, 1, 1); + grid1->addMultiCellWidget(label10, 5, 5, 0, 0); + grid1->addMultiCellWidget(m_phaseInput, 5, 5, 1, 1); + grid1->setMargin(m_gboxSettings->spacingHint()); + grid1->setSpacing(m_gboxSettings->spacingHint()); + grid1->setColStretch(1, 10); + grid1->setRowStretch(6, 10); + + mainTab->addTab( firstPage, i18n("Details") ); + + // ------------------------------------------------------------- + + TQWidget* secondPage = new TQWidget( mainTab ); + TQGridLayout* grid2 = new TQGridLayout( secondPage, 4, 1); + + TQLabel *label2 = new TQLabel(i18n("Luminance:"), secondPage); + + m_lumToleranceInput = new RDoubleNumInput(secondPage); + m_lumToleranceInput->setPrecision(1); + m_lumToleranceInput->setRange(0.0, 1.0, 0.1); + m_lumToleranceInput->setDefaultValue(1.0); + TQWhatsThis::add( m_lumToleranceInput, i18n("

    Luminance: this control sets the luminance tolerance of the image." + "We recommend using either the Color or the Luminance tolerance settings " + "to make an image correction, not both at the same time. These settings " + "do not influence the main smoothing process controlled by the Details " + "settings.")); + + // ------------------------------------------------------------- + + TQLabel *label6 = new TQLabel(i18n("Color:"), secondPage); + + m_csmoothInput = new RDoubleNumInput(secondPage); + m_csmoothInput->setPrecision(1); + m_csmoothInput->setRange(0.0, 1.0, 0.1); + m_csmoothInput->setDefaultValue(1.0); + TQWhatsThis::add( m_csmoothInput, i18n("

    Color: this control sets the color tolerance of the image. It is " + "recommended using either the Color or the Luminance tolerance " + "to make image correction, not both at the same time. These settings " + "do not influence the main smoothing process controlled by the Details " + "settings.")); + + // ------------------------------------------------------------- + + TQLabel *label8 = new TQLabel(i18n("Gamma:"), secondPage); + + m_gammaInput = new RDoubleNumInput(secondPage); + m_gammaInput->setPrecision(1); + m_gammaInput->setRange(0.3, 3.0, 0.1); + m_gammaInput->setDefaultValue(1.4); + TQWhatsThis::add( m_gammaInput, i18n("

    Gamma: this control sets the gamma tolerance of the image. This value " + "can be used to increase the tolerance values for darker areas (which commonly " + "are noisier). This results in more blur for shadow areas.")); + + // ------------------------------------------------------------- + + TQLabel *label9 = new TQLabel(i18n("Damping:"), secondPage); + + m_dampingInput = new RDoubleNumInput(secondPage); + m_dampingInput->setPrecision(1); + m_dampingInput->setRange(0.5, 20.0, 0.5); + m_dampingInput->setDefaultValue(5.0); + TQWhatsThis::add( m_dampingInput, i18n("

    Damping: this control sets the phase-jitter damping adjustment. " + "This value defines how fast the adaptive filter-radius reacts to luminance " + "variations. If increased, then edges appear smoother; if too high, then blur " + "may occur. If at minimum, then noise and phase jitter at the edges can occur. It " + "can suppress spike noise when increased, and this is the preferred method to " + "remove it.")); + + grid2->addMultiCellWidget(label2, 0, 0, 0, 0); + grid2->addMultiCellWidget(m_lumToleranceInput, 0, 0, 1, 1); + grid2->addMultiCellWidget(label6, 1, 1, 0, 0); + grid2->addMultiCellWidget(m_csmoothInput, 1, 1, 1, 1); + grid2->addMultiCellWidget(label8, 2, 2, 0, 0); + grid2->addMultiCellWidget(m_gammaInput, 2, 2, 1, 1); + grid2->addMultiCellWidget(label9, 3, 3, 0, 0); + grid2->addMultiCellWidget(m_dampingInput, 3, 3, 1, 1); + grid2->setMargin(m_gboxSettings->spacingHint()); + grid2->setSpacing(m_gboxSettings->spacingHint()); + grid2->setColStretch(1, 10); + grid2->setRowStretch(4, 10); + + mainTab->addTab( secondPage, i18n("Advanced") ); + + grid->addMultiCellWidget(mainTab, 0, 0, 0, 1); + grid->setRowStretch(1, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + m_previewWidget = new ImagePanelWidget(470, 350, "noisereduction Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); +} + +NoiseReductionTool::~NoiseReductionTool() +{ +} + +void NoiseReductionTool::renderingFinished() +{ + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); +} + +void NoiseReductionTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("noisereduction Tool"); + + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); + + m_radiusInput->setValue(config->readDoubleNumEntry("RadiusAjustment", m_radiusInput->defaultValue())); + m_lumToleranceInput->setValue(config->readDoubleNumEntry("LumToleranceAjustment", m_lumToleranceInput->defaultValue())); + m_thresholdInput->setValue(config->readDoubleNumEntry("ThresholdAjustment", m_thresholdInput->defaultValue())); + m_textureInput->setValue(config->readDoubleNumEntry("TextureAjustment", m_textureInput->defaultValue())); + m_sharpnessInput->setValue(config->readDoubleNumEntry("SharpnessAjustment", m_sharpnessInput->defaultValue())); + m_csmoothInput->setValue(config->readDoubleNumEntry("CsmoothAjustment", m_csmoothInput->defaultValue())); + m_lookaheadInput->setValue(config->readDoubleNumEntry("LookAheadAjustment", m_lookaheadInput->defaultValue())); + m_gammaInput->setValue(config->readDoubleNumEntry("GammaAjustment", m_gammaInput->defaultValue())); + m_dampingInput->setValue(config->readDoubleNumEntry("DampingAjustment", m_dampingInput->defaultValue())); + m_phaseInput->setValue(config->readDoubleNumEntry("PhaseAjustment", m_phaseInput->defaultValue())); + + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); +} + +void NoiseReductionTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("noisereduction Tool"); + config->writeEntry("RadiusAjustment", m_radiusInput->value()); + config->writeEntry("LumToleranceAjustment", m_lumToleranceInput->value()); + config->writeEntry("ThresholdAjustment", m_thresholdInput->value()); + config->writeEntry("TextureAjustment", m_textureInput->value()); + config->writeEntry("SharpnessAjustment", m_sharpnessInput->value()); + config->writeEntry("CsmoothAjustment", m_csmoothInput->value()); + config->writeEntry("LookAheadAjustment", m_lookaheadInput->value()); + config->writeEntry("GammaAjustment", m_gammaInput->value()); + config->writeEntry("DampingAjustment", m_dampingInput->value()); + config->writeEntry("PhaseAjustment", m_phaseInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void NoiseReductionTool::slotResetSettings() +{ + m_radiusInput->setEnabled(true); + m_lumToleranceInput->setEnabled(true); + m_thresholdInput->setEnabled(true); + m_textureInput->setEnabled(true); + m_sharpnessInput->setEnabled(true); + m_csmoothInput->setEnabled(true); + m_lookaheadInput->setEnabled(true); + m_gammaInput->setEnabled(true); + m_dampingInput->setEnabled(true); + m_phaseInput->setEnabled(true); + + m_radiusInput->slotReset(); + m_lumToleranceInput->slotReset(); + m_thresholdInput->slotReset(); + m_textureInput->slotReset(); + m_sharpnessInput->slotReset(); + m_csmoothInput->slotReset(); + m_lookaheadInput->slotReset(); + m_gammaInput->slotReset(); + m_dampingInput->slotReset(); + m_phaseInput->slotReset(); + + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); +} + +void NoiseReductionTool::prepareEffect() +{ + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); + + double r = m_radiusInput->value(); + double l = m_lumToleranceInput->value(); + double th = m_thresholdInput->value(); + double tx = m_textureInput->value(); + double s = m_sharpnessInput->value(); + double c = m_csmoothInput->value(); + double a = m_lookaheadInput->value(); + double g = m_gammaInput->value(); + double d = m_dampingInput->value(); + double p = m_phaseInput->value(); + + DImg image = m_previewWidget->getOriginalRegionImage(); + + setFilter(dynamic_cast(new NoiseReduction(&image, this, r, l, th, tx, s, c, a, g, d, p))); +} + +void NoiseReductionTool::prepareFinal() +{ + m_radiusInput->setEnabled(false); + m_lumToleranceInput->setEnabled(false); + m_thresholdInput->setEnabled(false); + m_textureInput->setEnabled(false); + m_sharpnessInput->setEnabled(false); + m_csmoothInput->setEnabled(false); + m_lookaheadInput->setEnabled(false); + m_gammaInput->setEnabled(false); + m_dampingInput->setEnabled(false); + m_phaseInput->setEnabled(false); + + double r = m_radiusInput->value(); + double l = m_lumToleranceInput->value(); + double th = m_thresholdInput->value(); + double tx = m_textureInput->value(); + double s = m_sharpnessInput->value(); + double c = m_csmoothInput->value(); + double a = m_lookaheadInput->value(); + double g = m_gammaInput->value(); + double d = m_dampingInput->value(); + double p = m_phaseInput->value(); + + ImageIface iface(0, 0); + setFilter(dynamic_cast(new NoiseReduction(iface.getOriginalImg(), this, r, l, th, tx, s, c, a, g, d, p))); +} + +void NoiseReductionTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void NoiseReductionTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Noise Reduction"), filter()->getTargetImage().bits()); +} + +void NoiseReductionTool::slotLoadSettings() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Noise Reduction Settings File to Load")) ); + if ( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + if ( stream.readLine() != "# Photograph Noise Reduction Configuration File" ) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Photograph Noise Reduction settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_radiusInput->setValue( stream.readLine().toDouble() ); + m_lumToleranceInput->setValue( stream.readLine().toDouble() ); + m_thresholdInput->setValue( stream.readLine().toDouble() ); + m_textureInput->setValue( stream.readLine().toDouble() ); + m_sharpnessInput->setValue( stream.readLine().toDouble() ); + m_csmoothInput->setValue( stream.readLine().toDouble() ); + m_lookaheadInput->setValue( stream.readLine().toDouble() ); + m_gammaInput->setValue( stream.readLine().toDouble() ); + m_dampingInput->setValue( stream.readLine().toDouble() ); + m_phaseInput->setValue( stream.readLine().toDouble() ); + blockSignals(false); + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Photograph Noise Reduction text file.")); + + file.close(); +} + +void NoiseReductionTool::slotSaveAsSettings() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Noise Reduction Settings File to Save")) ); + if ( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# Photograph Noise Reduction Configuration File\n"; + stream << m_radiusInput->value() << "\n"; + stream << m_lumToleranceInput->value() << "\n"; + stream << m_thresholdInput->value() << "\n"; + stream << m_textureInput->value() << "\n"; + stream << m_sharpnessInput->value() << "\n"; + stream << m_csmoothInput->value() << "\n"; + stream << m_lookaheadInput->value() << "\n"; + stream << m_gammaInput->value() << "\n"; + stream << m_dampingInput->value() << "\n"; + stream << m_phaseInput->value() << "\n"; + + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Noise Reduction text file.")); + + file.close(); +} + +} // NameSpace DigikamNoiseReductionImagesPlugin diff --git a/src/imageplugins/noisereduction/noisereductiontool.h b/src/imageplugins/noisereduction/noisereductiontool.h new file mode 100644 index 00000000..8d7d5828 --- /dev/null +++ b/src/imageplugins/noisereduction/noisereductiontool.h @@ -0,0 +1,92 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-24 + * Description : a plugin to reduce CCD noise. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef NOISEREDUCTIONTOOL_H +#define NOISEREDUCTIONTOOL_H + +// Local includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RDoubleNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamNoiseReductionImagesPlugin +{ + +class NoiseReductionTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + NoiseReductionTool(TQObject* parent); + ~NoiseReductionTool(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotResetSettings(); + +private: + + KDcrawIface::RDoubleNumInput *m_radiusInput; + KDcrawIface::RDoubleNumInput *m_lumToleranceInput; + KDcrawIface::RDoubleNumInput *m_thresholdInput; + KDcrawIface::RDoubleNumInput *m_textureInput; + KDcrawIface::RDoubleNumInput *m_sharpnessInput; + + KDcrawIface::RDoubleNumInput *m_csmoothInput; + KDcrawIface::RDoubleNumInput *m_lookaheadInput; + KDcrawIface::RDoubleNumInput *m_gammaInput; + KDcrawIface::RDoubleNumInput *m_dampingInput; + KDcrawIface::RDoubleNumInput *m_phaseInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamNoiseReductionImagesPlugin + +#endif /* NOISEREDUCTIONTOOL_H */ diff --git a/src/imageplugins/oilpaint/Makefile.am b/src/imageplugins/oilpaint/Makefile.am new file mode 100644 index 00000000..f50d7408 --- /dev/null +++ b/src/imageplugins/oilpaint/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_oilpaint_la_SOURCES = imageplugin_oilpaint.cpp \ + oilpainttool.cpp oilpaint.cpp + +digikamimageplugin_oilpaint_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_oilpaint_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_oilpaint.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_oilpaint.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_oilpaint_ui.rc + diff --git a/src/imageplugins/oilpaint/digikamimageplugin_oilpaint.desktop b/src/imageplugins/oilpaint/digikamimageplugin_oilpaint.desktop new file mode 100644 index 00000000..875c7f16 --- /dev/null +++ b/src/imageplugins/oilpaint/digikamimageplugin_oilpaint.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=ImagePlugin_OilPaint +Name[bg]=ПриÑтавка за Ñнимки - МаÑлени бои +Name[da]=Billedplugin_Oliemaling +Name[el]=ΠÏόσθετοΕικόνας_ΕλαιογÏαφίας +Name[fi]=Öljyvärimaalaus +Name[hr]=Uljana slika +Name[it]=PluginImmagini_PitturaAOlio +Name[nl]=Afbeeldingsplugin_Olieverf +Name[sr]=Слика у уљу +Name[sr@Latn]=Slika u ulju +Name[sv]=Insticksprogram för oljemÃ¥lning +Name[tr]=ResimEklentisi_YaÄŸlıBoya +Name[xx]=xxImagePlugin_OilPaintxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Oil paint image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за наподобÑване на картина Ñ Ð¼Ð°Ñлени бои +Comment[ca]=Connector pel digiKam d'efecte de pintura a l'oli +Comment[da]=Plugin med oliemalingeffekt pÃ¥ billeder i Digikam +Comment[de]=digiKam-Modul zum Erzeugen eines Ölgemäldeeffektes +Comment[el]=ΠÏόσθετο ελαιογÏαφίας για το digiKam +Comment[es]=Plugin para digiKam con efectos de pintura al óleo +Comment[et]=DigiKami õlimaali pildiefektiplugin +Comment[fa]=وصلۀ جلوۀ تصویر رنگ روغن برای digiKam +Comment[fi]=Jäljittelee öljyvärimaalausta +Comment[gl]=Un plugin de digiKam para simular unha pintura ao óleo +Comment[hr]=digiKam dodatak za efekt uljane slike +Comment[is]=Ãforrit fyrir digiKam sem líkir eftir olíumálun +Comment[it]=Plugin per l'effetto di pittura a olio delle immagini per digiKam +Comment[ja]=digiKam 油絵効果プラグイン +Comment[ms]=Plugin kesan imej cat minyak untuk digiKam +Comment[nds]=digiKam-Moduul för Öölbildeffekten +Comment[nl]=Digikam-plugin voor olieverfafbeeldingen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਤੇਲ ਪੇਂਟ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam zmniejszajÄ…ca szum +Comment[pt]=Um 'plugin' do digiKam para simular uma pintura a óleo +Comment[pt_BR]=Plugin de efeito de condensar cor da imagem +Comment[ru]=Модуль Ñффект "маÑлÑнной краÑки" Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre efekt olejomaľby +Comment[sr]=digiKam-ов прикључак за ефекат Ñлике у уљу +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekat slike u ulju +Comment[sv]=Digikam insticksprogram för oljemÃ¥lningsbildeffekt +Comment[tr]=digiKam için resmi yaÄŸlıboyaya benzetme eklentisi +Comment[uk]=Втулок ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐµÑ„ÐµÐºÑ‚Ñƒ олійних фарб Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng ảnh sÆ¡n dầu cho digiKam +Comment[xx]=xxOil paint image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_oilpaint +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/oilpaint/digikamimageplugin_oilpaint_ui.rc b/src/imageplugins/oilpaint/digikamimageplugin_oilpaint_ui.rc new file mode 100644 index 00000000..864c09db --- /dev/null +++ b/src/imageplugins/oilpaint/digikamimageplugin_oilpaint_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/oilpaint/imageeffect_oilpaint.cpp b/src/imageplugins/oilpaint/imageeffect_oilpaint.cpp new file mode 100644 index 00000000..da07be05 --- /dev/null +++ b/src/imageplugins/oilpaint/imageeffect_oilpaint.cpp @@ -0,0 +1,201 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "oilpaint.h" +#include "imageeffect_oilpaint.h" +#include "imageeffect_oilpaint.moc" + +namespace DigikamOilPaintImagesPlugin +{ + +ImageEffect_OilPaint::ImageEffect_OilPaint(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Apply Oil Paint Effect"), + "oilpaint", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Oil Paint"), + digikam_version, + I18N_NOOP("An oil painting image effect plugin for digiKam."), + TDEAboutData::License_GPL, + "(c) 2004-2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://wwww.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Oil paint algorithm"), + "pieter dot voloshyn at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 3, 1, 0, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Brush size:"), gboxSettings); + m_brushSizeInput = new KIntNumInput(gboxSettings); + m_brushSizeInput->setRange(1, 5, 1, true); + TQWhatsThis::add( m_brushSizeInput, i18n("

    Set here the brush size to use for " + "simulating the oil painting.") ); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_brushSizeInput, 1, 1, 0, 1); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Smooth:"), gboxSettings); + m_smoothInput = new KIntNumInput(gboxSettings); + m_smoothInput->setRange(10, 255, 1, true); + TQWhatsThis::add( m_smoothInput, i18n("

    This value controls the smoothing effect " + "of the brush under the canvas.") ); + + gridSettings->addMultiCellWidget(label2, 2, 2, 0, 1); + gridSettings->addMultiCellWidget(m_smoothInput, 3, 3, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_brushSizeInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_smoothInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_OilPaint::~ImageEffect_OilPaint() +{ +} + +void ImageEffect_OilPaint::renderingFinished() +{ + m_brushSizeInput->setEnabled(true); + m_smoothInput->setEnabled(true); +} + +void ImageEffect_OilPaint::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("oilpaint Tool Dialog"); + m_brushSizeInput->blockSignals(true); + m_smoothInput->blockSignals(true); + m_brushSizeInput->setValue(config->readNumEntry("BrushSize", 1)); + m_smoothInput->setValue(config->readNumEntry("SmoothAjustment", 30)); + m_brushSizeInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void ImageEffect_OilPaint::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("oilpaint Tool Dialog"); + config->writeEntry("BrushSize", m_brushSizeInput->value()); + config->writeEntry("SmoothAjustment", m_smoothInput->value()); + config->sync(); +} + +void ImageEffect_OilPaint::resetValues() +{ + m_brushSizeInput->blockSignals(true); + m_smoothInput->blockSignals(true); + m_brushSizeInput->setValue(1); + m_smoothInput->setValue(30); + m_brushSizeInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void ImageEffect_OilPaint::prepareEffect() +{ + m_brushSizeInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + + int b = m_brushSizeInput->value(); + int s = m_smoothInput->value(); + + m_threadedFilter = dynamic_cast(new OilPaint(&image, this, b, s)); +} + +void ImageEffect_OilPaint::prepareFinal() +{ + m_brushSizeInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + int b = m_brushSizeInput->value(); + int s = m_smoothInput->value(); + + Digikam::ImageIface iface(0, 0); + m_threadedFilter = dynamic_cast(new OilPaint(iface.getOriginalImg(), this, b, s)); +} + +void ImageEffect_OilPaint::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_OilPaint::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Oil Paint"), + m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamOilPaintImagesPlugin + diff --git a/src/imageplugins/oilpaint/imageeffect_oilpaint.h b/src/imageplugins/oilpaint/imageeffect_oilpaint.h new file mode 100644 index 00000000..527cc2ef --- /dev/null +++ b/src/imageplugins/oilpaint/imageeffect_oilpaint.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_OILPAINT_H +#define IMAGEEFFECT_OILPAINT_H + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class KIntNumInput; + +namespace DigikamOilPaintImagesPlugin +{ + +class ImageEffect_OilPaint : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_OilPaint(TQWidget* parent); + ~ImageEffect_OilPaint(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KIntNumInput *m_brushSizeInput; + KIntNumInput *m_smoothInput; +}; + +} // NameSpace DigikamOilPaintImagesPlugin + +#endif /* IMAGEEFFECT_OILPAINT_H */ diff --git a/src/imageplugins/oilpaint/imageplugin_oilpaint.cpp b/src/imageplugins/oilpaint/imageplugin_oilpaint.cpp new file mode 100644 index 00000000..740e89fc --- /dev/null +++ b/src/imageplugins/oilpaint/imageplugin_oilpaint.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "oilpainttool.h" +#include "imageplugin_oilpaint.h" +#include "imageplugin_oilpaint.moc" + +using namespace DigikamOilPaintImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_oilpaint, + KGenericFactory("digikamimageplugin_oilpaint")); + +ImagePlugin_OilPaint::ImagePlugin_OilPaint(TQObject *parent, const char*, const TQStringList&) + : Digikam::ImagePlugin(parent, "ImagePlugin_OilPaint") +{ + m_oilpaintAction = new TDEAction(i18n("Oil Paint..."), "oilpaint", 0, + this, TQ_SLOT(slotOilPaint()), + actionCollection(), "imageplugin_oilpaint"); + + setXMLFile( "digikamimageplugin_oilpaint_ui.rc" ); + + DDebug() << "ImagePlugin_OilPaint plugin loaded" << endl; +} + +ImagePlugin_OilPaint::~ImagePlugin_OilPaint() +{ +} + +void ImagePlugin_OilPaint::setEnabledActions(bool enable) +{ + m_oilpaintAction->setEnabled(enable); +} + +void ImagePlugin_OilPaint::slotOilPaint() +{ + OilPaintTool *tool = new OilPaintTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/oilpaint/imageplugin_oilpaint.h b/src/imageplugins/oilpaint/imageplugin_oilpaint.h new file mode 100644 index 00000000..032ee065 --- /dev/null +++ b/src/imageplugins/oilpaint/imageplugin_oilpaint.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_OILPAINT_H +#define IMAGEPLUGIN_OILPAINT_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_OilPaint : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_OilPaint(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_OilPaint(); + + void setEnabledActions(bool enable); + +private slots: + + void slotOilPaint(); + +private: + + TDEAction *m_oilpaintAction; +}; + +#endif /* IMAGEPLUGIN_OILPAINT_H */ diff --git a/src/imageplugins/oilpaint/oilpaint.cpp b/src/imageplugins/oilpaint/oilpaint.cpp new file mode 100644 index 00000000..be9697fa --- /dev/null +++ b/src/imageplugins/oilpaint/oilpaint.cpp @@ -0,0 +1,203 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Oil Painting threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original OilPaint algorithm copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimggaussianblur.h" +#include "dimgimagefilters.h" +#include "oilpaint.h" + +namespace DigikamOilPaintImagesPlugin +{ + +OilPaint::OilPaint(Digikam::DImg *orgImage, TQObject *parent, int brushSize, int smoothness) + : Digikam::DImgThreadedFilter(orgImage, parent, "OilPaint") +{ + m_brushSize = brushSize; + m_smoothness = smoothness; + initFilter(); +} + +void OilPaint::filterImage(void) +{ + oilpaintImage(m_orgImage, m_destImage, m_brushSize, m_smoothness); +} + +// This method have been ported from Pieter Z. Voloshyn algorithm code. + +/* Function to apply the OilPaint effect. + * + * data => The image data in RGBA mode. + * w => Width of image. + * h => Height of image. + * BrushSize => Brush size. + * Smoothness => Smooth value. + * + * Theory => Using MostFrequentColor function we take the main color in + * a matrix and simply write at the original position. + */ + +void OilPaint::oilpaintImage(Digikam::DImg &orgImage, Digikam::DImg &destImage, int BrushSize, int Smoothness) +{ + int progress; + Digikam::DColor mostFrequentColor; + int w,h; + + mostFrequentColor.setSixteenBit(orgImage.sixteenBit()); + w = (int)orgImage.width(); + h = (int)orgImage.height(); + uchar *dest = destImage.bits(); + int bytesDepth = orgImage.bytesDepth(); + uchar *dptr; + + // Allocate some arrays to be used. + // Do this here once for all to save a few million new / delete operations + m_intensityCount = new uchar[Smoothness + 1]; + m_averageColorR = new uint[Smoothness + 1]; + m_averageColorG = new uint[Smoothness + 1]; + m_averageColorB = new uint[Smoothness + 1]; + + for (int h2 = 0; !m_cancel && (h2 < h); h2++) + { + for (int w2 = 0; !m_cancel && (w2 < w); w2++) + { + mostFrequentColor = MostFrequentColor(orgImage, w2, h2, BrushSize, Smoothness); + dptr = dest + w2*bytesDepth + (w*h2*bytesDepth); + mostFrequentColor.setPixel(dptr); + } + + progress = (int) (((double)h2 * 100.0) / h); + if ( progress%5 == 0 ) + postProgress( progress ); + } + + // free all the arrays + delete [] m_intensityCount; + delete [] m_averageColorR; + delete [] m_averageColorG; + delete [] m_averageColorB; +} + +// This method have been ported from Pieter Z. Voloshyn algorithm code. + +/* Function to determine the most frequent color in a matrix + * + * Bits => Bits array + * Width => Image width + * Height => Image height + * X => Position horizontal + * Y => Position vertical + * Radius => Is the radius of the matrix to be analized + * Intensity => Intensity to calcule + * + * Theory => This function creates a matrix with the analized pixel in + * the center of this matrix and find the most frequenty color + */ + +Digikam::DColor OilPaint::MostFrequentColor(Digikam::DImg &src, int X, int Y, int Radius, int Intensity) +{ + int i, w, h, I, Width, Height; + uint red, green, blue; + + uchar *dest = src.bits(); + int bytesDepth = src.bytesDepth(); + uchar *sptr; + bool sixteenBit = src.sixteenBit(); + + Digikam::DColor mostFrequentColor; + + double Scale = Intensity / (sixteenBit ? 65535.0 : 255.0); + Width = (int)src.width(); + Height = (int)src.height(); + + // Erase the array + memset(m_intensityCount, 0, (Intensity + 1) * sizeof (uchar)); + + for (w = X - Radius; w <= X + Radius; w++) + { + for (h = Y - Radius; h <= Y + Radius; h++) + { + // This condition helps to identify when a point doesn't exist + + if ((w >= 0) && (w < Width) && (h >= 0) && (h < Height)) + { + sptr = dest + w*bytesDepth + (Width*h*bytesDepth); + Digikam::DColor color(sptr, sixteenBit); + red = (uint)color.red(); + green = (uint)color.green(); + blue = (uint)color.blue(); + + I = lround(GetIntensity (red, green, blue) * Scale); + m_intensityCount[I]++; + + if (m_intensityCount[I] == 1) + { + m_averageColorR[I] = red; + m_averageColorG[I] = green; + m_averageColorB[I] = blue; + } + else + { + m_averageColorR[I] += red; + m_averageColorG[I] += green; + m_averageColorB[I] += blue; + } + } + } + } + + I = 0; + int MaxInstance = 0; + + for (i = 0 ; i <= Intensity ; i++) + { + if (m_intensityCount[i] > MaxInstance) + { + I = i; + MaxInstance = m_intensityCount[i]; + } + } + + // get Alpha channel value from original (unchanged) + mostFrequentColor = src.getPixelColor(X, Y); + + // Overwrite RGB values to destination. + mostFrequentColor.setRed(m_averageColorR[I] / MaxInstance); + mostFrequentColor.setGreen(m_averageColorG[I] / MaxInstance); + mostFrequentColor.setBlue(m_averageColorB[I] / MaxInstance); + + return mostFrequentColor; +} + +} // NameSpace DigikamOilPaintImagesPlugin diff --git a/src/imageplugins/oilpaint/oilpaint.h b/src/imageplugins/oilpaint/oilpaint.h new file mode 100644 index 00000000..998522a5 --- /dev/null +++ b/src/imageplugins/oilpaint/oilpaint.h @@ -0,0 +1,72 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Oil Painting threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef OILPAINT_H +#define OILPAINT_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamOilPaintImagesPlugin +{ + +class OilPaint : public Digikam::DImgThreadedFilter +{ + +public: + + OilPaint(Digikam::DImg *orgImage, TQObject *parent=0, int brushSize=1, int smoothness=30); + + ~OilPaint(){}; + +private: + + virtual void filterImage(void); + + void oilpaintImage(Digikam::DImg &orgImage, Digikam::DImg &destImage, int BrushSize, int Smoothness); + + Digikam::DColor MostFrequentColor (Digikam::DImg &src, + int X, int Y, int Radius, int Intensity); + + // Function to calculate the color intensity and return the luminance (Y) + // component of YIQ color model. + inline double GetIntensity(uint Red, uint Green, uint Blue) + { return Red * 0.3 + Green * 0.59 + Blue * 0.11; }; + +private: + + uchar *m_intensityCount; + + int m_brushSize; + int m_smoothness; + + uint *m_averageColorR; + uint *m_averageColorG; + uint *m_averageColorB; +}; + +} // NameSpace DigikamOilPaintImagesPlugin + +#endif /* OILPAINT_H */ diff --git a/src/imageplugins/oilpaint/oilpainttool.cpp b/src/imageplugins/oilpaint/oilpainttool.cpp new file mode 100644 index 00000000..25bea302 --- /dev/null +++ b/src/imageplugins/oilpaint/oilpainttool.cpp @@ -0,0 +1,208 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "oilpaint.h" +#include "oilpainttool.h" +#include "oilpainttool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamOilPaintImagesPlugin +{ + +OilPaintTool::OilPaintTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("oilpaint"); + setToolName(i18n("Oil Paint")); + setToolIcon(SmallIcon("oilpaint")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 4, 1); + + TQLabel *label1 = new TQLabel(i18n("Brush size:"), m_gboxSettings->plainPage()); + m_brushSizeInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_brushSizeInput->setRange(1, 5, 1); + m_brushSizeInput->setDefaultValue(1); + TQWhatsThis::add( m_brushSizeInput, i18n("

    Set here the brush size to use for " + "simulating the oil painting.") ); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Smooth:"), m_gboxSettings->plainPage()); + m_smoothInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_smoothInput->setRange(10, 255, 1); + m_smoothInput->setDefaultValue(30); + TQWhatsThis::add( m_smoothInput, i18n("

    This value controls the smoothing effect " + "of the brush under the canvas.") ); + + + grid->addMultiCellWidget(label1, 0, 0, 0, 1); + grid->addMultiCellWidget(m_brushSizeInput, 1, 1, 0, 1); + grid->addMultiCellWidget(label2, 2, 2, 0, 1); + grid->addMultiCellWidget(m_smoothInput, 3, 3, 0, 1); + grid->setRowStretch(4, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "oilpaint Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + // this filter is relative slow, so we should use the try button instead right now + + // connect(m_brushSizeInput, TQ_SIGNAL(valueChanged (int)), + // this, TQ_SLOT(slotTimer())); + // + // connect(m_smoothInput, TQ_SIGNAL(valueChanged (int)), + // this, TQ_SLOT(slotTimer())); +} + +OilPaintTool::~OilPaintTool() +{ +} + +void OilPaintTool::renderingFinished() +{ + m_brushSizeInput->setEnabled(true); + m_smoothInput->setEnabled(true); +} + +void OilPaintTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("oilpaint Tool"); + m_brushSizeInput->blockSignals(true); + m_smoothInput->blockSignals(true); + + m_brushSizeInput->setValue(config->readNumEntry("BrushSize", m_brushSizeInput->defaultValue())); + m_smoothInput->setValue(config->readNumEntry("SmoothAjustment", m_smoothInput->defaultValue())); + + m_brushSizeInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void OilPaintTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("oilpaint Tool"); + config->writeEntry("BrushSize", m_brushSizeInput->value()); + config->writeEntry("SmoothAjustment", m_smoothInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void OilPaintTool::slotResetSettings() +{ + m_brushSizeInput->blockSignals(true); + m_smoothInput->blockSignals(true); + + m_brushSizeInput->slotReset(); + m_smoothInput->slotReset(); + + m_brushSizeInput->blockSignals(false); + m_smoothInput->blockSignals(false); +} + +void OilPaintTool::prepareEffect() +{ + m_brushSizeInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + + int b = m_brushSizeInput->value(); + int s = m_smoothInput->value(); + + setFilter(dynamic_cast(new OilPaint(&image, this, b, s))); +} + +void OilPaintTool::prepareFinal() +{ + m_brushSizeInput->setEnabled(false); + m_smoothInput->setEnabled(false); + + int b = m_brushSizeInput->value(); + int s = m_smoothInput->value(); + + ImageIface iface(0, 0); + setFilter(dynamic_cast(new OilPaint(iface.getOriginalImg(), this, b, s))); +} + +void OilPaintTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void OilPaintTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Oil Paint"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamOilPaintImagesPlugin + diff --git a/src/imageplugins/oilpaint/oilpainttool.h b/src/imageplugins/oilpaint/oilpainttool.h new file mode 100644 index 00000000..9c488a57 --- /dev/null +++ b/src/imageplugins/oilpaint/oilpainttool.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-08-25 + * Description : a plugin to simulate Oil Painting + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef OILPAINTTOOL_H +#define OILPAINTTOOL_H + +// Digikam includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamOilPaintImagesPlugin +{ + +class OilPaintTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + OilPaintTool(TQObject* parent); + ~OilPaintTool(); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KDcrawIface::RIntNumInput *m_brushSizeInput; + KDcrawIface::RIntNumInput *m_smoothInput; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamOilPaintImagesPlugin + +#endif /* OILPAINTTOOL_H */ diff --git a/src/imageplugins/perspective/Makefile.am b/src/imageplugins/perspective/Makefile.am new file mode 100644 index 00000000..028add37 --- /dev/null +++ b/src/imageplugins/perspective/Makefile.am @@ -0,0 +1,35 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_perspective_la_SOURCES = imageplugin_perspective.cpp \ + perspectivetool.cpp \ + perspectivewidget.cpp triangle.cpp matrix.cpp + +digikamimageplugin_perspective_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_perspective_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_perspective.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_perspective.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_perspective_ui.rc + diff --git a/src/imageplugins/perspective/digikamimageplugin_perspective.desktop b/src/imageplugins/perspective/digikamimageplugin_perspective.desktop new file mode 100644 index 00000000..a6fcc4d3 --- /dev/null +++ b/src/imageplugins/perspective/digikamimageplugin_perspective.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_Perspective +Name[bg]=ПриÑтавка за Ñнимки - ПерÑпектива +Name[da]=Billedplugin_Perspektivværktøj +Name[el]=ΠÏόσθετοΕικόνας_ΠÏοοπτική +Name[fi]=Perspektiivimuunnos +Name[hr]=Perspektiva +Name[it]=PluginImmagini_Prospettiva +Name[nl]=Afbeeldingsplugin_Perspectief +Name[sr]=ПерÑпектива +Name[sr@Latn]=Perspektiva +Name[sv]=Insticksprogram för perspektiv +Name[tr]=ResimEklentisi_Perspektif +Name[xx]=xxImagePlugin_Perspectivexx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Perspective tool plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за промÑна на перÑпективата +Comment[ca]=Connector pel digiKam d'eina de perspectiva +Comment[da]=Perspektivværktøjs-plugin for Digikam +Comment[de]=digiKam-Modul zum Justieren der Perspektive eines Bildes +Comment[el]=ΠÏόσθετο εÏγαλείο Ï€Ïοοπτικής για το digiKam +Comment[es]=Plugin para digiKam de herramientas de perspectiva +Comment[et]=DigiKami perspektiiviplugin +Comment[fa]=وصلۀ ابزار منظره برای digiKam +Comment[fi]=Vääristää kuvan perspektiiviä +Comment[fr]=Module externe pour modifier la perspective dans digiKam +Comment[gl]=Un plugin de digiKam para a ferramenta de perspectiva +Comment[hr]=digiKam dodatak za perspektivu +Comment[it]=Plugin per lo strumento di prospettiva per digiKam +Comment[ja]=digiKam 視点調整プラグイン +Comment[ms]=Plugin alatan perspektif untuk digiKam +Comment[nds]=digiKam-Moduul för den Kietwinkel +Comment[nl]=Digikam-plugin voor perspectiefaanpassing +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਪਰੋਸਪੈਕਟਿਵ ਸੰਦ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca korekcjÄ™ perspektywy +Comment[pt]=Um 'plugin' do digiKam para a ferramenta de perspectiva +Comment[pt_BR]=Plugin de ferramenta de Perspectiva +Comment[ru]=Модуль Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ñпективы Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre nástroj s perspektívou +Comment[sr]=digiKam-ов прикључак за перÑпективу +Comment[sr@Latn]=digiKam-ov prikljuÄak za perspektivu +Comment[sv]=Digikam insticksprogram med perspektivverktyg +Comment[tr]=digiKam için perspektif aracı eklentisi +Comment[uk]=Втулок заÑобу побудови перÑпективи Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung công cụ phối cảnh cho digiKam +Comment[xx]=xxPerspective tool plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_perspective +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/perspective/digikamimageplugin_perspective_ui.rc b/src/imageplugins/perspective/digikamimageplugin_perspective_ui.rc new file mode 100644 index 00000000..0f3bca57 --- /dev/null +++ b/src/imageplugins/perspective/digikamimageplugin_perspective_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Tra&nsform + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/perspective/imageeffect_perspective.cpp b/src/imageplugins/perspective/imageeffect_perspective.cpp new file mode 100644 index 00000000..15bef877 --- /dev/null +++ b/src/imageplugins/perspective/imageeffect_perspective.cpp @@ -0,0 +1,252 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective . + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "perspectivewidget.h" +#include "imageeffect_perspective.h" +#include "imageeffect_perspective.moc" + +namespace DigikamPerspectiveImagesPlugin +{ + +ImageEffect_Perspective::ImageEffect_Perspective(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Adjust Photograph Perspective"), + "perspective", false, false) +{ + TQString whatsThis; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Perspective"), + digikam_version, + I18N_NOOP("A digiKam image plugin to process image perspective adjustment."), + TDEAboutData::License_GPL, + "(c) 2005-2006, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(plainPage()); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* l = new TQVBoxLayout(frame, 5, 0); + m_previewWidget = new PerspectiveWidget(525, 350, frame); + l->addWidget(m_previewWidget); + TQWhatsThis::add( m_previewWidget, i18n("

    This is the perspective transformation operation preview. " + "You can use the mouse for dragging the corner to adjust the " + "perspective transformation area.")); + setPreviewAreaWidget(frame); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + TQWidget *gbox2 = new TQWidget(plainPage()); + TQGridLayout *gridLayout = new TQGridLayout( gbox2, 13, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("New width:"), gbox2); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), gbox2); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), gbox2); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), gbox2); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + gridLayout->addMultiCellWidget(label1, 0, 0, 0, 0); + gridLayout->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + gridLayout->addMultiCellWidget(label2, 1, 1, 0, 0); + gridLayout->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + + // ------------------------------------------------------------- + + KSeparator *line = new KSeparator(Horizontal, gbox2); + + TQLabel *angleLabel = new TQLabel(i18n("Angles (in degrees):"), gbox2); + TQLabel *label3 = new TQLabel(i18n(" Top left:"), gbox2); + m_topLeftAngleLabel = new TQLabel(gbox2); + TQLabel *label4 = new TQLabel(i18n(" Top right:"), gbox2); + m_topRightAngleLabel = new TQLabel(gbox2); + TQLabel *label5 = new TQLabel(i18n(" Bottom left:"), gbox2); + m_bottomLeftAngleLabel = new TQLabel(gbox2); + TQLabel *label6 = new TQLabel(i18n(" Bottom right:"), gbox2); + m_bottomRightAngleLabel = new TQLabel(gbox2); + + gridLayout->addMultiCellWidget(line, 2, 2, 0, 2); + gridLayout->addMultiCellWidget(angleLabel, 3, 3, 0, 2); + gridLayout->addMultiCellWidget(label3, 4, 4, 0, 0); + gridLayout->addMultiCellWidget(m_topLeftAngleLabel, 4, 4, 1, 2); + gridLayout->addMultiCellWidget(label4, 5, 5, 0, 0); + gridLayout->addMultiCellWidget(m_topRightAngleLabel, 5, 5, 1, 2); + gridLayout->addMultiCellWidget(label5, 6, 6, 0, 0); + gridLayout->addMultiCellWidget(m_bottomLeftAngleLabel, 6, 6, 1, 2); + gridLayout->addMultiCellWidget(label6, 7, 7, 0, 0); + gridLayout->addMultiCellWidget(m_bottomRightAngleLabel, 7, 7, 1, 2); + + // ------------------------------------------------------------- + + KSeparator *line2 = new KSeparator(Horizontal, gbox2); + + m_drawWhileMovingCheckBox = new TQCheckBox(i18n("Draw preview while moving"), gbox2); + gridLayout->addMultiCellWidget(line2, 8, 8, 0, 2); + gridLayout->addMultiCellWidget(m_drawWhileMovingCheckBox, 9, 9, 0, 2); + + m_drawGridCheckBox = new TQCheckBox(i18n("Draw grid"), gbox2); + gridLayout->addMultiCellWidget(m_drawGridCheckBox, 10, 10, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label7 = new TQLabel(i18n("Guide color:"), gbox2); + m_guideColorBt = new KColorButton( TQColor( TQt::red ), gbox2 ); + TQWhatsThis::add( m_guideColorBt, i18n("

    Set here the color used to draw guides dashed-lines.")); + gridLayout->addMultiCellWidget(label7, 11, 11, 0, 0); + gridLayout->addMultiCellWidget(m_guideColorBt, 11, 11, 2, 2); + + TQLabel *label8 = new TQLabel(i18n("Guide width:"), gbox2); + m_guideSize = new TQSpinBox( 1, 5, 1, gbox2); + TQWhatsThis::add( m_guideSize, i18n("

    Set here the width in pixels used to draw guides dashed-lines.")); + gridLayout->addMultiCellWidget(label8, 12, 12, 0, 0); + gridLayout->addMultiCellWidget(m_guideSize, 12, 12, 2, 2); + + gridLayout->setColStretch(1, 10); + gridLayout->setRowStretch(13, 10); + + setUserAreaWidget(gbox2); + + // ------------------------------------------------------------- + + connect(m_previewWidget, TQ_SIGNAL(signalPerspectiveChanged(TQRect, float, float, float, float)), + this, TQ_SLOT(slotUpdateInfo(TQRect, float, float, float, float))); + + connect(m_drawWhileMovingCheckBox, TQ_SIGNAL(toggled(bool)), + m_previewWidget, TQ_SLOT(slotToggleDrawWhileMoving(bool))); + + connect(m_drawGridCheckBox, TQ_SIGNAL(toggled(bool)), + m_previewWidget, TQ_SLOT(slotToggleDrawGrid(bool))); + + connect(m_guideColorBt, TQ_SIGNAL(changed(const TQColor &)), + m_previewWidget, TQ_SLOT(slotChangeGuideColor(const TQColor &))); + + connect(m_guideSize, TQ_SIGNAL(valueChanged(int)), + m_previewWidget, TQ_SLOT(slotChangeGuideSize(int))); +} + +ImageEffect_Perspective::~ImageEffect_Perspective() +{ +} + +void ImageEffect_Perspective::readUserSettings(void) +{ + TQColor defaultGuideColor(TQt::red); + TDEConfig *config = kapp->config(); + config->setGroup("perspective Tool Dialog"); + m_drawWhileMovingCheckBox->setChecked(config->readBoolEntry("Draw While Moving", true)); + m_drawGridCheckBox->setChecked(config->readBoolEntry("Draw Grid", false)); + m_guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor)); + m_guideSize->setValue(config->readNumEntry("Guide Width", 1)); + m_previewWidget->slotToggleDrawWhileMoving(m_drawWhileMovingCheckBox->isChecked()); + m_previewWidget->slotToggleDrawGrid(m_drawGridCheckBox->isChecked()); + m_previewWidget->slotChangeGuideColor(m_guideColorBt->color()); + m_previewWidget->slotChangeGuideSize(m_guideSize->value()); +} + +void ImageEffect_Perspective::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("perspective Tool Dialog"); + config->writeEntry("Draw While Moving", m_drawWhileMovingCheckBox->isChecked()); + config->writeEntry("Draw Grid", m_drawGridCheckBox->isChecked()); + config->writeEntry("Guide Color", m_guideColorBt->color()); + config->writeEntry("Guide Width", m_guideSize->value()); + config->sync(); +} + +void ImageEffect_Perspective::resetValues() +{ + m_previewWidget->reset(); +} + +void ImageEffect_Perspective::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_previewWidget->applyPerspectiveAdjustment(); + accept(); + kapp->restoreOverrideCursor(); +} + +void ImageEffect_Perspective::slotUpdateInfo(TQRect newSize, float topLeftAngle, float topRightAngle, + float bottomLeftAngle, float bottomRightAngle) +{ + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); + + m_topLeftAngleLabel->setText(temp.setNum( topLeftAngle, 'f', 1 )); + m_topRightAngleLabel->setText(temp.setNum( topRightAngle, 'f', 1 )); + m_bottomLeftAngleLabel->setText(temp.setNum( bottomLeftAngle, 'f', 1 )); + m_bottomRightAngleLabel->setText(temp.setNum( bottomRightAngle, 'f', 1 )); +} + +} // NameSpace DigikamPerspectiveImagesPlugin + diff --git a/src/imageplugins/perspective/imageeffect_perspective.h b/src/imageplugins/perspective/imageeffect_perspective.h new file mode 100644 index 00000000..41388aa7 --- /dev/null +++ b/src/imageplugins/perspective/imageeffect_perspective.h @@ -0,0 +1,89 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective . + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_PERSPECTIVE_H +#define IMAGEEFFECT_PERSPECTIVE_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQLabel; +class TQCheckBox; +class TQSpinBox; + +class KColorButton; + +namespace DigikamPerspectiveImagesPlugin +{ + +class PerspectiveWidget; + +class ImageEffect_Perspective : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_Perspective(TQWidget* parent); + ~ImageEffect_Perspective(); + +private slots: + + void slotUpdateInfo(TQRect newSize, float topLeftAngle, float topRightAngle, + float bottomLeftAngle, float bottomRightAngle); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void finalRendering(); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + TQLabel *m_topLeftAngleLabel; + TQLabel *m_topRightAngleLabel; + TQLabel *m_bottomLeftAngleLabel; + TQLabel *m_bottomRightAngleLabel; + + TQCheckBox *m_drawWhileMovingCheckBox; + TQCheckBox *m_drawGridCheckBox; + + TQSpinBox *m_guideSize; + + KColorButton *m_guideColorBt; + + PerspectiveWidget *m_previewWidget; +}; + +} // NameSpace DigikamPerspectiveImagesPlugin + +#endif /* IMAGEEFFECT_PERSPECTIVE_H */ diff --git a/src/imageplugins/perspective/imageplugin_perspective.cpp b/src/imageplugins/perspective/imageplugin_perspective.cpp new file mode 100644 index 00000000..bb5d0444 --- /dev/null +++ b/src/imageplugins/perspective/imageplugin_perspective.cpp @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective . + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "perspectivetool.h" +#include "imageplugin_perspective.h" +#include "imageplugin_perspective.moc" + +using namespace DigikamPerspectiveImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_perspective, + KGenericFactory("digikamimageplugin_perspective")); + +ImagePlugin_Perspective::ImagePlugin_Perspective(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Perspective") +{ + m_perspectiveAction = new TDEAction(i18n("Perspective Adjustment..."), "perspective", 0, + this, TQ_SLOT(slotPerspective()), + actionCollection(), "imageplugin_perspective"); + + setXMLFile("digikamimageplugin_perspective_ui.rc"); + + DDebug() << "ImagePlugin_Perspective plugin loaded" << endl; +} + +ImagePlugin_Perspective::~ImagePlugin_Perspective() +{ +} + +void ImagePlugin_Perspective::setEnabledActions(bool enable) +{ + m_perspectiveAction->setEnabled(enable); +} + +void ImagePlugin_Perspective::slotPerspective() +{ + PerspectiveTool *tool = new PerspectiveTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/perspective/imageplugin_perspective.h b/src/imageplugins/perspective/imageplugin_perspective.h new file mode 100644 index 00000000..5dee7b1b --- /dev/null +++ b/src/imageplugins/perspective/imageplugin_perspective.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective . + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_PERSPECTIVE_H +#define IMAGEPLUGIN_PERSPECTIVE_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Perspective : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Perspective(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Perspective(); + + void setEnabledActions(bool enable); + +private slots: + + void slotPerspective(); + +private: + + TDEAction *m_perspectiveAction; +}; + +#endif /* IMAGEPLUGIN_PERSPECTIVE_H */ diff --git a/src/imageplugins/perspective/matrix.cpp b/src/imageplugins/perspective/matrix.cpp new file mode 100644 index 00000000..65723c56 --- /dev/null +++ b/src/imageplugins/perspective/matrix.cpp @@ -0,0 +1,177 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a matrix implementation for image + * perspective adjustment. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Matrix3 implementation inspired from gimp 2.0 + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// Local includes. + +#include "matrix.h" + +namespace DigikamPerspectiveImagesPlugin +{ + +static double identityMatrix[3][3] = { { 1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, + { 0.0, 0.0, 1.0 } }; + +Matrix::Matrix() +{ + memcpy(coeff, identityMatrix, sizeof(coeff)); +} + +void Matrix::translate (double x, double y) +{ + double g, h, i; + + g = coeff[2][0]; + h = coeff[2][1]; + i = coeff[2][2]; + + coeff[0][0] += x * g; + coeff[0][1] += x * h; + coeff[0][2] += x * i; + coeff[1][0] += y * g; + coeff[1][1] += y * h; + coeff[1][2] += y * i; +} + +void Matrix::scale(double x, double y) +{ + coeff[0][0] *= x; + coeff[0][1] *= x; + coeff[0][2] *= x; + + coeff[1][0] *= y; + coeff[1][1] *= y; + coeff[1][2] *= y; +} + +void Matrix::multiply(const Matrix &matrix) +{ + int i, j; + Matrix tmp; + double t1, t2, t3; + + for (i = 0; i < 3; i++) + { + t1 = matrix.coeff[i][0]; + t2 = matrix.coeff[i][1]; + t3 = matrix.coeff[i][2]; + + for (j = 0; j < 3; j++) + { + tmp.coeff[i][j] = t1 * coeff[0][j]; + tmp.coeff[i][j] += t2 * coeff[1][j]; + tmp.coeff[i][j] += t3 * coeff[2][j]; + } + } + + *this = tmp; +} + +void Matrix::transformPoint(double x, double y, double *newx, double *newy) const +{ + double w; + + w = coeff[2][0] * x + coeff[2][1] * y + coeff[2][2]; + + if (w == 0.0) + w = 1.0; + else + w = 1.0/w; + + *newx = (coeff[0][0] * x + + coeff[0][1] * y + + coeff[0][2]) * w; + *newy = (coeff[1][0] * x + + coeff[1][1] * y + + coeff[1][2]) * w; +} + +void Matrix::invert() +{ + Matrix inv; + double det; + + det = determinant(); + + if (det == 0.0) + return; + + det = 1.0 / det; + + inv.coeff[0][0] = (coeff[1][1] * coeff[2][2] - + coeff[1][2] * coeff[2][1]) * det; + + inv.coeff[1][0] = - (coeff[1][0] * coeff[2][2] - + coeff[1][2] * coeff[2][0]) * det; + + inv.coeff[2][0] = (coeff[1][0] * coeff[2][1] - + coeff[1][1] * coeff[2][0]) * det; + + inv.coeff[0][1] = - (coeff[0][1] * coeff[2][2] - + coeff[0][2] * coeff[2][1]) * det; + + inv.coeff[1][1] = (coeff[0][0] * coeff[2][2] - + coeff[0][2] * coeff[2][0]) * det; + + inv.coeff[2][1] = - (coeff[0][0] * coeff[2][1] - + coeff[0][1] * coeff[2][0]) * det; + + inv.coeff[0][2] = (coeff[0][1] * coeff[1][2] - + coeff[0][2] * coeff[1][1]) * det; + + inv.coeff[1][2] = - (coeff[0][0] * coeff[1][2] - + coeff[0][2] * coeff[1][0]) * det; + + inv.coeff[2][2] = (coeff[0][0] * coeff[1][1] - + coeff[0][1] * coeff[1][0]) * det; + + *this = inv; +} + +double Matrix::determinant() const +{ + double determinant; + + determinant = (coeff[0][0] * + (coeff[1][1] * coeff[2][2] - + coeff[1][2] * coeff[2][1])); + determinant -= (coeff[1][0] * + (coeff[0][1] * coeff[2][2] - + coeff[0][2] * coeff[2][1])); + determinant += (coeff[2][0] * + (coeff[0][1] * coeff[1][2] - + coeff[0][2] * coeff[1][1])); + + return determinant; +} + +} // namespace DigikamPerspectiveImagesPlugin diff --git a/src/imageplugins/perspective/matrix.h b/src/imageplugins/perspective/matrix.h new file mode 100644 index 00000000..fbc5a1aa --- /dev/null +++ b/src/imageplugins/perspective/matrix.h @@ -0,0 +1,106 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a matrix implementation for image + * perspective adjustment. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_PERSPECTIVE_MATRIX_H +#define IMAGEEFFECT_PERSPECTIVE_MATRIX_H + +namespace DigikamPerspectiveImagesPlugin +{ + +class Matrix +{ +public: + + /** + * Matrix: + * + * Initializes matrix to the identity matrix. + */ + Matrix(); + + /** + * translate: + * @x: Translation in X direction. + * @y: Translation in Y direction. + * + * Translates the matrix by x and y. + */ + void translate(double x, double y); + + /** + * scale: + * @x: X scale factor. + * @y: Y scale factor. + * + * Scales the matrix by x and y + */ + void scale(double x, double y); + + /** + * invert: + * + * Inverts this matrix. + */ + void invert(); + + /** + * multiply: + * @matrix: The other input matrix. + * + * Multiplies this matrix with another matrix + */ + void multiply(const Matrix &matrix1); + + /** + * transformPoint: + * @x: The source X coordinate. + * @y: The source Y coordinate. + * @newx: The transformed X coordinate. + * @newy: The transformed Y coordinate. + * + * Transforms a point in 2D as specified by the transformation matrix. + */ + void transformPoint(double x, double y, double *newx, double *newy) const; + + /** + * determinant: + * + * Calculates the determinant of this matrix. + * + * Returns: The determinant. + */ + double determinant() const; + + /** + * coeff: + * + * The 3x3 matrix data + */ + double coeff[3][3]; +}; + +} // namespace DigikamPerspectiveImagesPlugin + +#endif // IMAGEEFFECT_PERSPECTIVE_MATRIX_H diff --git a/src/imageplugins/perspective/perspectivetool.cpp b/src/imageplugins/perspective/perspectivetool.cpp new file mode 100644 index 00000000..30d0a2cf --- /dev/null +++ b/src/imageplugins/perspective/perspectivetool.cpp @@ -0,0 +1,244 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "editortoolsettings.h" +#include "perspectivewidget.h" +#include "perspectivetool.h" +#include "perspectivetool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamPerspectiveImagesPlugin +{ + +PerspectiveTool::PerspectiveTool(TQObject* parent) + : EditorTool(parent) +{ + setName("perspective"); + setToolName(i18n("Perspective")); + setToolIcon(SmallIcon("perspective")); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(0); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + TQVBoxLayout* l = new TQVBoxLayout(frame, 5, 0); + m_previewWidget = new PerspectiveWidget(525, 350, frame); + l->addWidget(m_previewWidget); + TQWhatsThis::add(m_previewWidget, i18n("

    This is the perspective transformation operation preview. " + "You can use the mouse for dragging the corner to adjust the " + "perspective transformation area.")); + setToolView(frame); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout *gridLayout = new TQGridLayout( m_gboxSettings->plainPage(), 13, 2); + + TQLabel *label1 = new TQLabel(i18n("New width:"), m_gboxSettings->plainPage()); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), m_gboxSettings->plainPage()); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + // ------------------------------------------------------------- + + KSeparator *line = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + TQLabel *angleLabel = new TQLabel(i18n("Angles (in degrees):"), m_gboxSettings->plainPage()); + TQLabel *label3 = new TQLabel(i18n(" Top left:"), m_gboxSettings->plainPage()); + m_topLeftAngleLabel = new TQLabel(m_gboxSettings->plainPage()); + TQLabel *label4 = new TQLabel(i18n(" Top right:"), m_gboxSettings->plainPage()); + m_topRightAngleLabel = new TQLabel(m_gboxSettings->plainPage()); + TQLabel *label5 = new TQLabel(i18n(" Bottom left:"), m_gboxSettings->plainPage()); + m_bottomLeftAngleLabel = new TQLabel(m_gboxSettings->plainPage()); + TQLabel *label6 = new TQLabel(i18n(" Bottom right:"), m_gboxSettings->plainPage()); + m_bottomRightAngleLabel = new TQLabel(m_gboxSettings->plainPage()); + + // ------------------------------------------------------------- + + KSeparator *line2 = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + m_drawWhileMovingCheckBox = new TQCheckBox(i18n("Draw preview while moving"), m_gboxSettings->plainPage()); + gridLayout->addMultiCellWidget(line2, 8, 8, 0, 2); + gridLayout->addMultiCellWidget(m_drawWhileMovingCheckBox, 9, 9, 0, 2); + + m_drawGridCheckBox = new TQCheckBox(i18n("Draw grid"), m_gboxSettings->plainPage()); + + // ------------------------------------------------------------- + + TQLabel *label7 = new TQLabel(i18n("Guide color:"), m_gboxSettings->plainPage()); + m_guideColorBt = new KColorButton( TQColor( TQt::red ), m_gboxSettings->plainPage() ); + TQWhatsThis::add( m_guideColorBt, i18n("

    Set here the color used to draw guides dashed-lines.")); + gridLayout->addMultiCellWidget(label7, 11, 11, 0, 0); + gridLayout->addMultiCellWidget(m_guideColorBt, 11, 11, 2, 2); + + TQLabel *label8 = new TQLabel(i18n("Guide width:"), m_gboxSettings->plainPage()); + m_guideSize = new RIntNumInput(m_gboxSettings->plainPage()); + m_guideSize->input()->setRange(1, 5, 1, false); + m_guideSize->setDefaultValue(1); + TQWhatsThis::add( m_guideSize, i18n("

    Set here the width in pixels used to draw guides dashed-lines.")); + + gridLayout->addMultiCellWidget(label1, 0, 0, 0, 0); + gridLayout->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + gridLayout->addMultiCellWidget(label2, 1, 1, 0, 0); + gridLayout->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + gridLayout->addMultiCellWidget(line, 2, 2, 0, 2); + gridLayout->addMultiCellWidget(angleLabel, 3, 3, 0, 2); + gridLayout->addMultiCellWidget(label3, 4, 4, 0, 0); + gridLayout->addMultiCellWidget(m_topLeftAngleLabel, 4, 4, 1, 2); + gridLayout->addMultiCellWidget(label4, 5, 5, 0, 0); + gridLayout->addMultiCellWidget(m_topRightAngleLabel, 5, 5, 1, 2); + gridLayout->addMultiCellWidget(label5, 6, 6, 0, 0); + gridLayout->addMultiCellWidget(m_bottomLeftAngleLabel, 6, 6, 1, 2); + gridLayout->addMultiCellWidget(label6, 7, 7, 0, 0); + gridLayout->addMultiCellWidget(m_bottomRightAngleLabel, 7, 7, 1, 2); + gridLayout->addMultiCellWidget(m_drawGridCheckBox, 10, 10, 0, 2); + gridLayout->addMultiCellWidget(label8, 12, 12, 0, 0); + gridLayout->addMultiCellWidget(m_guideSize, 12, 12, 2, 2); + gridLayout->setColStretch(1, 10); + gridLayout->setRowStretch(13, 10); + gridLayout->setMargin(m_gboxSettings->spacingHint()); + gridLayout->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_previewWidget, TQ_SIGNAL(signalPerspectiveChanged(TQRect, float, float, float, float)), + this, TQ_SLOT(slotUpdateInfo(TQRect, float, float, float, float))); + + connect(m_drawWhileMovingCheckBox, TQ_SIGNAL(toggled(bool)), + m_previewWidget, TQ_SLOT(slotToggleDrawWhileMoving(bool))); + + connect(m_drawGridCheckBox, TQ_SIGNAL(toggled(bool)), + m_previewWidget, TQ_SLOT(slotToggleDrawGrid(bool))); + + connect(m_guideColorBt, TQ_SIGNAL(changed(const TQColor&)), + m_previewWidget, TQ_SLOT(slotChangeGuideColor(const TQColor&))); + + connect(m_guideSize, TQ_SIGNAL(valueChanged(int)), + m_previewWidget, TQ_SLOT(slotChangeGuideSize(int))); +} + +PerspectiveTool::~PerspectiveTool() +{ +} + +void PerspectiveTool::readSettings() +{ + TQColor defaultGuideColor(TQt::red); + TDEConfig *config = kapp->config(); + config->setGroup("perspective Tool"); + m_drawWhileMovingCheckBox->setChecked(config->readBoolEntry("Draw While Moving", true)); + m_drawGridCheckBox->setChecked(config->readBoolEntry("Draw Grid", false)); + m_guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor)); + m_guideSize->setValue(config->readNumEntry("Guide Width", m_guideSize->defaultValue())); + m_previewWidget->slotToggleDrawWhileMoving(m_drawWhileMovingCheckBox->isChecked()); + m_previewWidget->slotToggleDrawGrid(m_drawGridCheckBox->isChecked()); + m_previewWidget->slotChangeGuideColor(m_guideColorBt->color()); + m_previewWidget->slotChangeGuideSize(m_guideSize->value()); +} + +void PerspectiveTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("perspective Tool"); + config->writeEntry("Draw While Moving", m_drawWhileMovingCheckBox->isChecked()); + config->writeEntry("Draw Grid", m_drawGridCheckBox->isChecked()); + config->writeEntry("Guide Color", m_guideColorBt->color()); + config->writeEntry("Guide Width", m_guideSize->value()); + config->sync(); +} + +void PerspectiveTool::slotResetSettings() +{ + m_previewWidget->reset(); +} + +void PerspectiveTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_previewWidget->applyPerspectiveAdjustment(); + kapp->restoreOverrideCursor(); +} + +void PerspectiveTool::slotUpdateInfo(TQRect newSize, float topLeftAngle, float topRightAngle, + float bottomLeftAngle, float bottomRightAngle) +{ + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); + + m_topLeftAngleLabel->setText(temp.setNum( topLeftAngle, 'f', 1 )); + m_topRightAngleLabel->setText(temp.setNum( topRightAngle, 'f', 1 )); + m_bottomLeftAngleLabel->setText(temp.setNum( bottomLeftAngle, 'f', 1 )); + m_bottomRightAngleLabel->setText(temp.setNum( bottomRightAngle, 'f', 1 )); +} + +} // NameSpace DigikamPerspectiveImagesPlugin diff --git a/src/imageplugins/perspective/perspectivetool.h b/src/imageplugins/perspective/perspectivetool.h new file mode 100644 index 00000000..6186712f --- /dev/null +++ b/src/imageplugins/perspective/perspectivetool.h @@ -0,0 +1,100 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-02-17 + * Description : a plugin to change image perspective. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_PERSPECTIVE_H +#define IMAGEEFFECT_PERSPECTIVE_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQLabel; +class TQCheckBox; + +class KColorButton; + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +} + +namespace DigikamPerspectiveImagesPlugin +{ + +class PerspectiveWidget; + +class PerspectiveTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + PerspectiveTool(TQObject* parent); + ~PerspectiveTool(); + +private slots: + + void slotResetSettings(); + void slotUpdateInfo(TQRect newSize, float topLeftAngle, float topRightAngle, + float bottomLeftAngle, float bottomRightAngle); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + TQLabel *m_topLeftAngleLabel; + TQLabel *m_topRightAngleLabel; + TQLabel *m_bottomLeftAngleLabel; + TQLabel *m_bottomRightAngleLabel; + + TQCheckBox *m_drawWhileMovingCheckBox; + TQCheckBox *m_drawGridCheckBox; + + KDcrawIface::RIntNumInput *m_guideSize; + + KColorButton *m_guideColorBt; + + PerspectiveWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamPerspectiveImagesPlugin + +#endif /* IMAGEEFFECT_PERSPECTIVE_H */ diff --git a/src/imageplugins/perspective/perspectivewidget.cpp b/src/imageplugins/perspective/perspectivewidget.cpp new file mode 100644 index 00000000..ae2c5c03 --- /dev/null +++ b/src/imageplugins/perspective/perspectivewidget.cpp @@ -0,0 +1,839 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-18 + * Description : a widget class to edit perspective. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Matrix3 implementation inspired from gimp 2.0 + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "triangle.h" +#include "ddebug.h" +#include "imageiface.h" +#include "dimgimagefilters.h" +#include "perspectivewidget.h" +#include "perspectivewidget.moc" + +namespace DigikamPerspectiveImagesPlugin +{ + +PerspectiveWidget::PerspectiveWidget(int w, int h, TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + setBackgroundMode(TQt::NoBackground); + setMinimumSize(w, h); + setMouseTracking(true); + + m_drawGrid = false; + m_drawWhileMoving = true; + m_currentResizing = ResizingNone; + m_guideColor = TQt::red; + m_guideSize = 1; + + m_iface = new Digikam::ImageIface(w, h); + uchar *data = m_iface->setPreviewImageSize(w, h); + m_w = m_iface->previewWidth(); + m_h = m_iface->previewHeight(); + m_origW = m_iface->originalWidth(); + m_origH = m_iface->originalHeight(); + m_previewImage = Digikam::DImg(m_w, m_h, m_iface->previewSixteenBit(), m_iface->previewHasAlpha(), data, false); + + m_pixmap = new TQPixmap(w, h); + + m_rect = TQRect(w/2-m_w/2, h/2-m_h/2, m_w, m_h); + m_grid = TQPointArray(60); + + reset(); +} + +PerspectiveWidget::~PerspectiveWidget() +{ + delete m_iface; + delete m_pixmap; +} + +Digikam::ImageIface* PerspectiveWidget::imageIface() +{ + return m_iface; +} + +TQPoint PerspectiveWidget::getTopLeftCorner(void) +{ + return TQPoint( lroundf((float)(m_topLeftPoint.x()*m_origW) / (float)m_w), + lroundf((float)(m_topLeftPoint.y()*m_origH) / (float)m_h)); +} + +TQPoint PerspectiveWidget::getTopRightCorner(void) +{ + return TQPoint( lroundf((float)(m_topRightPoint.x()*m_origW) / (float)m_w), + lroundf((float)(m_topRightPoint.y()*m_origH) / (float)m_h)); +} + +TQPoint PerspectiveWidget::getBottomLeftCorner(void) +{ + return TQPoint( lroundf((float)(m_bottomLeftPoint.x()*m_origW) / (float)m_w), + lroundf((float)(m_bottomLeftPoint.y()*m_origH) / (float)m_h)); +} + +TQPoint PerspectiveWidget::getBottomRightCorner(void) +{ + return TQPoint( lroundf((float)(m_bottomRightPoint.x()*m_origW) / (float)m_w), + lroundf((float)(m_bottomRightPoint.y()*m_origH) / (float)m_h)); +} + +TQRect PerspectiveWidget::getTargetSize(void) +{ + TQPointArray perspectiveArea; + + perspectiveArea.putPoints( 0, 4, + getTopLeftCorner().x(), getTopLeftCorner().y(), + getTopRightCorner().x(), getTopRightCorner().y(), + getBottomRightCorner().x(), getBottomRightCorner().y(), + getBottomLeftCorner().x(), getBottomLeftCorner().y() ); + + return perspectiveArea.boundingRect(); +} + +float PerspectiveWidget::getAngleTopLeft(void) +{ + Triangle topLeft(getTopLeftCorner(), getTopRightCorner(), getBottomLeftCorner()); + return topLeft.angleBAC(); +} + +float PerspectiveWidget::getAngleTopRight(void) +{ + Triangle topLeft(getTopRightCorner(), getBottomRightCorner(), getTopLeftCorner()); + return topLeft.angleBAC(); +} + +float PerspectiveWidget::getAngleBottomLeft(void) +{ + Triangle topLeft(getBottomLeftCorner(), getTopLeftCorner(), getBottomRightCorner()); + return topLeft.angleBAC(); +} + +float PerspectiveWidget::getAngleBottomRight(void) +{ + Triangle topLeft(getBottomRightCorner(), getBottomLeftCorner(), getTopRightCorner()); + return topLeft.angleBAC(); +} + +void PerspectiveWidget::reset(void) +{ + m_topLeftPoint.setX(0); + m_topLeftPoint.setY(0); + + m_topRightPoint.setX(m_w-1); + m_topRightPoint.setY(0); + + m_bottomLeftPoint.setX(0); + m_bottomLeftPoint.setY(m_h-1); + + m_bottomRightPoint.setX(m_w-1); + m_bottomRightPoint.setY(m_h-1); + + m_spot.setX(m_w / 2); + m_spot.setY(m_h / 2); + + m_antiAlias = true; + updatePixmap(); + repaint(false); +} + +void PerspectiveWidget::applyPerspectiveAdjustment(void) +{ + Digikam::DImg *orgImage = m_iface->getOriginalImg(); + Digikam::DImg destImage(orgImage->width(), orgImage->height(), orgImage->sixteenBit(), orgImage->hasAlpha()); + + Digikam::DColor background(0, 0, 0, orgImage->hasAlpha() ? 0 : 255, orgImage->sixteenBit()); + + // Perform perspective adjustment. + + buildPerspective(TQPoint(0, 0), TQPoint(m_origW, m_origH), + getTopLeftCorner(), getTopRightCorner(), + getBottomLeftCorner(), getBottomRightCorner(), + orgImage, &destImage, background); + + // Perform an auto-croping around the image. + + Digikam::DImg targetImg = destImage.copy(getTargetSize()); + + // Update target image. + m_iface->putOriginalImage(i18n("Perspective Adjustment"), + targetImg.bits(), targetImg.width(), targetImg.height()); +} + +void PerspectiveWidget::slotToggleAntiAliasing(bool a) +{ + m_antiAlias = a; + updatePixmap(); + repaint(false); +} + +void PerspectiveWidget::slotToggleDrawWhileMoving(bool draw) +{ + m_drawWhileMoving = draw; +} + +void PerspectiveWidget::slotToggleDrawGrid(bool grid) +{ + m_drawGrid = grid; + updatePixmap(); + repaint(false); +} + +void PerspectiveWidget::slotChangeGuideColor(const TQColor &color) +{ + m_guideColor = color; + updatePixmap(); + repaint(false); +} + +void PerspectiveWidget::slotChangeGuideSize(int size) +{ + m_guideSize = size; + updatePixmap(); + repaint(false); +} + +void PerspectiveWidget::updatePixmap(void) +{ + m_topLeftCorner.setRect(m_topLeftPoint.x() + m_rect.topLeft().x(), + m_topLeftPoint.y() + m_rect.topLeft().y(), 8, 8); + m_topRightCorner.setRect(m_topRightPoint.x() - 7 + m_rect.topLeft().x(), + m_topRightPoint.y() + m_rect.topLeft().y(), 8, 8); + m_bottomLeftCorner.setRect(m_bottomLeftPoint.x() + m_rect.topLeft().x(), + m_bottomLeftPoint.y() - 7 + m_rect.topLeft().y(), 8, 8); + m_bottomRightCorner.setRect(m_bottomRightPoint.x() - 7 + m_rect.topLeft().x(), + m_bottomRightPoint.y() - 7 + m_rect.topLeft().y(), 8, 8); + + // Compute the grid array + + int gXS = m_w / 15; + int gYS = m_h / 15; + + for (int i = 0 ; i < 15 ; i++) + { + int j = i*4; + + //Horizontal line. + m_grid.setPoint(j , 0, i*gYS); + m_grid.setPoint(j+1, m_w, i*gYS); + + //Vertical line. + m_grid.setPoint(j+2, i*gXS, 0); + m_grid.setPoint(j+3, i*gXS, m_h); + } + + // Draw background + + m_pixmap->fill(colorGroup().background()); + + // if we are resizing with the mouse, compute and draw only if drawWhileMoving is set + if (m_currentResizing == ResizingNone || m_drawWhileMoving) + { + // Create preview image + + Digikam::DImg destImage(m_previewImage.width(), m_previewImage.height(), + m_previewImage.sixteenBit(), m_previewImage.hasAlpha()); + + Digikam::DColor background(colorGroup().background()); + + m_transformedCenter = buildPerspective(TQPoint(0, 0), TQPoint(m_w, m_h), + m_topLeftPoint, m_topRightPoint, + m_bottomLeftPoint, m_bottomRightPoint, + &m_previewImage, &destImage, background); + + m_iface->putPreviewImage(destImage.bits()); + + // Draw image + + m_iface->paint(m_pixmap, m_rect.x(), m_rect.y(), + m_rect.width(), m_rect.height()); + } + else + { + m_transformedCenter = buildPerspective(TQPoint(0, 0), TQPoint(m_w, m_h), + m_topLeftPoint, m_topRightPoint, + m_bottomLeftPoint, m_bottomRightPoint); + } + + // Drawing selection borders. + + TQPainter p(m_pixmap); + p.setPen(TQPen(TQColor(255, 64, 64), 1, TQt::SolidLine)); + p.drawLine(m_topLeftPoint+m_rect.topLeft(), m_topRightPoint+m_rect.topLeft()); + p.drawLine(m_topRightPoint+m_rect.topLeft(), m_bottomRightPoint+m_rect.topLeft()); + p.drawLine(m_bottomRightPoint+m_rect.topLeft(), m_bottomLeftPoint+m_rect.topLeft()); + p.drawLine(m_bottomLeftPoint+m_rect.topLeft(), m_topLeftPoint+m_rect.topLeft()); + + // Drawing selection corners. + + TQBrush brush(TQColor(255, 64, 64)); + p.fillRect(m_topLeftCorner, brush); + p.fillRect(m_topRightCorner, brush); + p.fillRect(m_bottomLeftCorner, brush); + p.fillRect(m_bottomRightCorner, brush); + + // Drawing the grid. + + if (m_drawGrid) + { + for (uint i = 0 ; i < m_grid.size() ; i += 4) + { + //Horizontal line. + p.drawLine(m_grid.point(i)+m_rect.topLeft(), m_grid.point(i+1)+m_rect.topLeft()); + + //Vertical line. + p.drawLine(m_grid.point(i+2)+m_rect.topLeft(), m_grid.point(i+3)+m_rect.topLeft()); + } + } + + // Drawing transformed center. + + p.setPen(TQPen(TQColor(255, 64, 64), 3, TQt::SolidLine)); + p.drawEllipse( m_transformedCenter.x()+m_rect.topLeft().x()-2, + m_transformedCenter.y()+m_rect.topLeft().y()-2, 4, 4 ); + + // Drawing vertical and horizontal guide lines. + + int xspot = m_spot.x() + m_rect.x(); + int yspot = m_spot.y() + m_rect.y(); + p.setPen(TQPen(TQt::white, m_guideSize, TQt::SolidLine)); + p.drawLine(xspot, m_rect.top(), xspot, m_rect.bottom()); + p.drawLine(m_rect.left(), yspot, m_rect.right(), yspot); + p.setPen(TQPen(m_guideColor, m_guideSize, TQt::DotLine)); + p.drawLine(xspot, m_rect.top(), xspot, m_rect.bottom()); + p.drawLine(m_rect.left(), yspot, m_rect.right(), yspot); + + p.end(); + + emit signalPerspectiveChanged(getTargetSize(), getAngleTopLeft(), getAngleTopRight(), + getAngleBottomLeft(), getAngleBottomRight()); +} + +TQPoint PerspectiveWidget::buildPerspective(TQPoint orignTopLeft, TQPoint orignBottomRight, + TQPoint transTopLeft, TQPoint transTopRight, + TQPoint transBottomLeft, TQPoint transBottomRight, + Digikam::DImg *orgImage, Digikam::DImg *destImage, + Digikam::DColor background) +{ + Matrix matrix, transform; + double scalex; + double scaley; + + double x1 = (double)orignTopLeft.x(); + double y1 = (double)orignTopLeft.y(); + + double x2 = (double)orignBottomRight.x(); + double y2 = (double)orignBottomRight.y(); + + double tx1 = (double)transTopLeft.x(); + double ty1 = (double)transTopLeft.y(); + + double tx2 = (double)transTopRight.x(); + double ty2 = (double)transTopRight.y(); + + double tx3 = (double)transBottomLeft.x(); + double ty3 = (double)transBottomLeft.y(); + + double tx4 = (double)transBottomRight.x(); + double ty4 = (double)transBottomRight.y(); + + scalex = scaley = 1.0; + + if ((x2 - x1) > 0) + scalex = 1.0 / (double) (x2 - x1); + + if ((y2 - y1) > 0) + scaley = 1.0 / (double) (y2 - y1); + + // Determine the perspective transform that maps from + // the unit cube to the transformed coordinates + + double dx1, dx2, dx3, dy1, dy2, dy3; + + dx1 = tx2 - tx4; + dx2 = tx3 - tx4; + dx3 = tx1 - tx2 + tx4 - tx3; + + dy1 = ty2 - ty4; + dy2 = ty3 - ty4; + dy3 = ty1 - ty2 + ty4 - ty3; + + // Is the mapping affine? + + if ((dx3 == 0.0) && (dy3 == 0.0)) + { + matrix.coeff[0][0] = tx2 - tx1; + matrix.coeff[0][1] = tx4 - tx2; + matrix.coeff[0][2] = tx1; + matrix.coeff[1][0] = ty2 - ty1; + matrix.coeff[1][1] = ty4 - ty2; + matrix.coeff[1][2] = ty1; + matrix.coeff[2][0] = 0.0; + matrix.coeff[2][1] = 0.0; + } + else + { + double det1, det2; + + det1 = dx3 * dy2 - dy3 * dx2; + det2 = dx1 * dy2 - dy1 * dx2; + + if (det1 == 0.0 && det2 == 0.0) + matrix.coeff[2][0] = 1.0; + else + matrix.coeff[2][0] = det1 / det2; + + det1 = dx1 * dy3 - dy1 * dx3; + + if (det1 == 0.0 && det2 == 0.0) + matrix.coeff[2][1] = 1.0; + else + matrix.coeff[2][1] = det1 / det2; + + matrix.coeff[0][0] = tx2 - tx1 + matrix.coeff[2][0] * tx2; + matrix.coeff[0][1] = tx3 - tx1 + matrix.coeff[2][1] * tx3; + matrix.coeff[0][2] = tx1; + + matrix.coeff[1][0] = ty2 - ty1 + matrix.coeff[2][0] * ty2; + matrix.coeff[1][1] = ty3 - ty1 + matrix.coeff[2][1] * ty3; + matrix.coeff[1][2] = ty1; + } + + matrix.coeff[2][2] = 1.0; + + // transform is initialized to the identity matrix + transform.translate(-x1, -y1); + transform.scale (scalex, scaley); + transform.multiply (matrix); + + // Compute perspective transformation to image if image data containers exist. + if (orgImage && destImage) + transformAffine(orgImage, destImage, transform, background); + + // Calculate the grid array points. + double newX, newY; + for (uint i = 0 ; i < m_grid.size() ; i++) + { + transform.transformPoint(m_grid.point(i).x(), m_grid.point(i).y(), &newX, &newY); + m_grid.setPoint(i, lround(newX), lround(newY)); + } + + // Calculate and return new image center. + double newCenterX, newCenterY; + transform.transformPoint(x2/2.0, y2/2.0, &newCenterX, &newCenterY); + + return TQPoint(lround(newCenterX), lround(newCenterY)); +} + +void PerspectiveWidget::transformAffine(Digikam::DImg *orgImage, Digikam::DImg *destImage, + const Matrix &matrix, Digikam::DColor background) +{ + Matrix m(matrix), inv(matrix); + + int x1, y1, x2, y2; // target bounding box + int x, y; // target coordinates + int u1, v1, u2, v2; // source bounding box + double uinc, vinc, winc; // increments in source coordinates + // pr horizontal target coordinate + + double u[5],v[5]; // source coordinates, + // 2 + // / \ 0 is sample in the center of pixel + // 1 0 3 1..4 is offset 1 pixel in each + // \ / direction (in target space) + // 4 + + double tu[5],tv[5],tw[5]; // undivided source coordinates and divisor + + uchar *data, *newData; + bool sixteenBit; + int coords; + int width, height; + int bytesDepth; + int offset; + uchar *dest, *d; + Digikam::DColor color; + + bytesDepth = orgImage->bytesDepth(); + data = orgImage->bits(); + sixteenBit = orgImage->sixteenBit(); + width = orgImage->width(); + height = orgImage->height(); + newData = destImage->bits(); + + if (sixteenBit) + background.convertToSixteenBit(); + + //destImage->fill(background); + + Digikam::DImgImageFilters filters; + + // Find the inverse of the transformation matrix + m.invert(); + + u1 = 0; + v1 = 0; + u2 = u1 + width; + v2 = v1 + height; + + x1 = u1; + y1 = v1; + x2 = u2; + y2 = v2; + + dest = new uchar[width * bytesDepth]; + + uinc = m.coeff[0][0]; + vinc = m.coeff[1][0]; + winc = m.coeff[2][0]; + + coords = 1; + + // these loops could be rearranged, depending on which bit of code + // you'd most like to write more than once. + + for (y = y1; y < y2; y++) + { + // set up inverse transform steps + + tu[0] = uinc * (x1 + 0.5) + m.coeff[0][1] * (y + 0.5) + m.coeff[0][2] - 0.5; + tv[0] = vinc * (x1 + 0.5) + m.coeff[1][1] * (y + 0.5) + m.coeff[1][2] - 0.5; + tw[0] = winc * (x1 + 0.5) + m.coeff[2][1] * (y + 0.5) + m.coeff[2][2]; + + d = dest; + + for (x = x1; x < x2; x++) + { + int i; // normalize homogeneous coords + + for (i = 0; i < coords; i++) + { + if (tw[i] == 1.0) + { + u[i] = tu[i]; + v[i] = tv[i]; + } + else if (tw[i] != 0.0) + { + u[i] = tu[i] / tw[i]; + v[i] = tv[i] / tw[i]; + } + else + { + DDebug() << "homogeneous coordinate = 0...\n" << endl; + } + } + + // Set the destination pixels + + int iu = lround( u [0] ); + int iv = lround( v [0] ); + + if (iu >= u1 && iu < u2 && iv >= v1 && iv < v2) + { + // u, v coordinates into source + + int u = iu - u1; + int v = iv - v1; + + //TODO: Check why antialiasing shows no effect + /*if (m_antiAlias) + { + if (sixteenBit) + { + unsigned short *d16 = (unsigned short *)d; + filters.pixelAntiAliasing16((unsigned short *)data, + width, height, u, v, d16+3, d16+2, d16+1, d16); + } + else + { + filters.pixelAntiAliasing(data, width, height, u, v, + d+3, d+2, d+1, d); + } + } + else + {*/ + offset = (v * width * bytesDepth) + (u * bytesDepth); + color.setColor(data + offset, sixteenBit); + color.setPixel(d); + //} + + d += bytesDepth; + } + else // not in source range + { + // set to background color + + background.setPixel(d); + d += bytesDepth; + } + + for (i = 0; i < coords; i++) + { + tu[i] += uinc; + tv[i] += vinc; + tw[i] += winc; + } + } + + // set the pixel region row + + offset = (y - y1) * width * bytesDepth; + memcpy(newData + offset, dest, width * bytesDepth); + } + + delete [] dest; +} + +void PerspectiveWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0, 0, m_pixmap); +} + +void PerspectiveWidget::resizeEvent(TQResizeEvent * e) +{ + int old_w = m_w; + int old_h = m_h; + + delete m_pixmap; + int w = e->size().width(); + int h = e->size().height(); + uchar *data = m_iface->setPreviewImageSize(w, h); + m_w = m_iface->previewWidth(); + m_h = m_iface->previewHeight(); + m_previewImage = Digikam::DImg(m_w, m_h, m_iface->previewSixteenBit(), m_iface->previewHasAlpha(), data, false); + + m_pixmap = new TQPixmap(w, h); + TQRect oldRect = m_rect; + m_rect = TQRect(w/2-m_w/2, h/2-m_h/2, m_w, m_h); + + float xFactor = (float)m_rect.width()/(float)(oldRect.width()); + float yFactor = (float)m_rect.height()/(float)(oldRect.height()); + + m_topLeftPoint = TQPoint(lroundf(m_topLeftPoint.x()*xFactor), + lroundf(m_topLeftPoint.y()*yFactor)); + m_topRightPoint = TQPoint(lroundf(m_topRightPoint.x()*xFactor), + lroundf(m_topRightPoint.y()*yFactor)); + m_bottomLeftPoint = TQPoint(lroundf(m_bottomLeftPoint.x()*xFactor), + lroundf(m_bottomLeftPoint.y()*yFactor)); + m_bottomRightPoint = TQPoint(lroundf(m_bottomRightPoint.x()*xFactor), + lroundf(m_bottomRightPoint.y()*yFactor)); + m_transformedCenter = TQPoint(lroundf(m_transformedCenter.x()*xFactor), + lroundf(m_transformedCenter.y()*yFactor)); + + m_spot.setX((int)((float)m_spot.x() * ( (float)m_w / (float)old_w))); + m_spot.setY((int)((float)m_spot.y() * ( (float)m_h / (float)old_h))); + + updatePixmap(); +} + +void PerspectiveWidget::mousePressEvent ( TQMouseEvent * e ) +{ + if ( e->button() == TQt::LeftButton && + m_rect.contains( e->x(), e->y() )) + { + if ( m_topLeftCorner.contains( e->x(), e->y() ) ) + m_currentResizing = ResizingTopLeft; + else if ( m_bottomRightCorner.contains( e->x(), e->y() ) ) + m_currentResizing = ResizingBottomRight; + else if ( m_topRightCorner.contains( e->x(), e->y() ) ) + m_currentResizing = ResizingTopRight; + else if ( m_bottomLeftCorner.contains( e->x(), e->y() ) ) + m_currentResizing = ResizingBottomLeft; + else + { + m_spot.setX(e->x()-m_rect.x()); + m_spot.setY(e->y()-m_rect.y()); + } + } +} + +void PerspectiveWidget::mouseReleaseEvent ( TQMouseEvent * e ) +{ + if ( m_currentResizing != ResizingNone ) + { + unsetCursor(); + m_currentResizing = ResizingNone; + + // in this case, the pixmap has not been drawn on mouse move + if (!m_drawWhileMoving) + { + updatePixmap(); + repaint(false); + } + } + else + { + m_spot.setX(e->x()-m_rect.x()); + m_spot.setY(e->y()-m_rect.y()); + updatePixmap(); + repaint(false); + } +} + +void PerspectiveWidget::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( e->state() == TQt::LeftButton ) + { + if ( m_currentResizing != ResizingNone ) + { + TQPointArray unsablePoints; + TQPoint pm(e->x(), e->y()); + + if (!m_rect.contains( pm )) + { + if (pm.x() > m_rect.right()) + pm.setX(m_rect.right()); + else if (pm.x() < m_rect.left()) + pm.setX(m_rect.left()); + + if (pm.y() > m_rect.bottom()) + pm.setY(m_rect.bottom()); + else if (pm.y() < m_rect.top()) + pm.setY(m_rect.top()); + } + + if ( m_currentResizing == ResizingTopLeft ) + { + unsablePoints.putPoints(0, 7, + m_w-1, m_h-1, + 0, m_h-1, + 0, m_bottomLeftPoint.y()-10, + m_bottomLeftPoint.x(), m_bottomLeftPoint.y()-10, + m_topRightPoint.x()-10, m_topRightPoint.y(), + m_topRightPoint.x()-10, 0, + m_w-1, 0 ); + TQRegion unsableArea(unsablePoints); + + if ( unsableArea.contains(pm) ) return; + + m_topLeftPoint = pm - m_rect.topLeft(); + setCursor( KCursor::sizeFDiagCursor() ); + } + + else if ( m_currentResizing == ResizingTopRight ) + { + unsablePoints.putPoints(0, 7, + 0, m_h-1, + 0, 0, + m_topLeftPoint.x()+10, 0, + m_topLeftPoint.x()+10, m_topLeftPoint.y(), + m_bottomRightPoint.x(), m_bottomRightPoint.y()-10, + m_w-1, m_bottomRightPoint.y()-10, + m_w-1, m_h-1); + TQRegion unsableArea(unsablePoints); + + if ( unsableArea.contains(pm) ) return; + + m_topRightPoint = pm - m_rect.topLeft(); + setCursor( KCursor::sizeBDiagCursor() ); + } + + else if ( m_currentResizing == ResizingBottomLeft ) + { + unsablePoints.putPoints(0, 7, + m_w-1, 0, + m_w-1, m_h-1, + m_bottomRightPoint.x()-10, m_h-1, + m_bottomRightPoint.x()-10, m_bottomRightPoint.y()+10, + m_topLeftPoint.x(), m_topLeftPoint.y()+10, + 0, m_topLeftPoint.y(), + 0, 0); + TQRegion unsableArea(unsablePoints); + + if ( unsableArea.contains(pm) ) return; + + m_bottomLeftPoint = pm - m_rect.topLeft(); + setCursor( KCursor::sizeBDiagCursor() ); + } + + else if ( m_currentResizing == ResizingBottomRight ) + { + unsablePoints.putPoints(0, 7, + 0, 0, + m_w-1, 0, + m_w-1, m_topRightPoint.y()+10, + m_topRightPoint.x(), m_topRightPoint.y()+10, + m_bottomLeftPoint.x()+10, m_bottomLeftPoint.y(), + m_bottomLeftPoint.x()+10, m_w-1, + 0, m_w-1); + TQRegion unsableArea(unsablePoints); + + if ( unsableArea.contains(pm) ) return; + + m_bottomRightPoint = pm - m_rect.topLeft(); + setCursor( KCursor::sizeFDiagCursor() ); + } + + else + { + m_spot.setX(e->x()-m_rect.x()); + m_spot.setY(e->y()-m_rect.y()); + } + + updatePixmap(); + repaint(false); + } + } + else + { + if ( m_topLeftCorner.contains( e->x(), e->y() ) || + m_bottomRightCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeFDiagCursor() ); + + else if ( m_topRightCorner.contains( e->x(), e->y() ) || + m_bottomLeftCorner.contains( e->x(), e->y() ) ) + setCursor( KCursor::sizeBDiagCursor() ); + else + unsetCursor(); + } +} + +} // NameSpace DigikamPerspectiveImagesPlugin + diff --git a/src/imageplugins/perspective/perspectivewidget.h b/src/imageplugins/perspective/perspectivewidget.h new file mode 100644 index 00000000..0047c3e8 --- /dev/null +++ b/src/imageplugins/perspective/perspectivewidget.h @@ -0,0 +1,172 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-18 + * Description : a widget class to edit perspective. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PERSPECTIVEWIDGET_H +#define PERSPECTIVEWIDGET_H + +// TQt includes. + +#include +#include +#include +#include +#include + +// Digikam includes. + +#include "dimg.h" + +// Local includes. + +#include "matrix.h" + +class TQPixmap; + +namespace Digikam +{ +class ImageIface; +} + +namespace DigikamPerspectiveImagesPlugin +{ + +class PerspectiveWidget : public TQWidget +{ +TQ_OBJECT + + +public: + + PerspectiveWidget(int width, int height, TQWidget *parent=0); + ~PerspectiveWidget(); + + TQRect getTargetSize(void); + TQPoint getTopLeftCorner(void); + TQPoint getTopRightCorner(void); + TQPoint getBottomLeftCorner(void); + TQPoint getBottomRightCorner(void); + void reset(void); + + float getAngleTopLeft(void); + float getAngleTopRight(void); + float getAngleBottomLeft(void); + float getAngleBottomRight(void); + + void applyPerspectiveAdjustment(void); + + Digikam::ImageIface* imageIface(); + +public slots: + + void slotToggleAntiAliasing(bool a); + void slotToggleDrawWhileMoving(bool draw); + void slotToggleDrawGrid(bool grid); + + void slotChangeGuideColor(const TQColor &color); + void slotChangeGuideSize(int size); + +signals: + + void signalPerspectiveChanged( TQRect newSize, float topLeftAngle, float topRightAngle, + float bottomLeftAngle, float bottomRightAngle ); + +protected: + + void paintEvent( TQPaintEvent *e ); + void resizeEvent( TQResizeEvent * e ); + void mousePressEvent ( TQMouseEvent * e ); + void mouseReleaseEvent ( TQMouseEvent * e ); + void mouseMoveEvent ( TQMouseEvent * e ); + +private: // Widget methods. + + void updatePixmap(void); + + void transformAffine(Digikam::DImg *orgImage, Digikam::DImg *destImage, + const Matrix &matrix, Digikam::DColor background); + + TQPoint buildPerspective(TQPoint orignTopLeft, TQPoint orignBottomRight, + TQPoint transTopLeft, TQPoint transTopRight, + TQPoint transBottomLeft, TQPoint transBottomRight, + Digikam::DImg *orgImage=0, Digikam::DImg *destImage=0, + Digikam::DColor background=Digikam::DColor()); + +private: + + enum ResizingMode + { + ResizingNone = 0, + ResizingTopLeft, + ResizingTopRight, + ResizingBottomLeft, + ResizingBottomRight + }; + + bool m_antiAlias; + bool m_drawWhileMoving; + bool m_drawGrid; + + uint *m_data; + int m_w; + int m_h; + int m_origW; + int m_origH; + + int m_currentResizing; + + int m_guideSize; + + TQRect m_rect; + + // Tranformed center area for mouse position control. + + TQPoint m_transformedCenter; + + // Draggable local region selection corners. + + TQRect m_topLeftCorner; + TQRect m_topRightCorner; + TQRect m_bottomLeftCorner; + TQRect m_bottomRightCorner; + + TQPoint m_topLeftPoint; + TQPoint m_topRightPoint; + TQPoint m_bottomLeftPoint; + TQPoint m_bottomRightPoint; + TQPoint m_spot; + + TQColor m_guideColor; + + // 60 points will be stored to compute a grid of 15x15 lines. + TQPointArray m_grid; + + TQPixmap *m_pixmap; + + Digikam::ImageIface *m_iface; + Digikam::DImg m_previewImage; +}; + +} // NameSpace DigikamPerspectiveImagesPlugin + +#endif /* PERSPECTIVEWIDGET_H */ diff --git a/src/imageplugins/perspective/triangle.cpp b/src/imageplugins/perspective/triangle.cpp new file mode 100644 index 00000000..84f80a07 --- /dev/null +++ b/src/imageplugins/perspective/triangle.cpp @@ -0,0 +1,65 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-18 + * Description : triangle geometry calculation class. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// Local includes. + +#include "triangle.h" + +namespace DigikamPerspectiveImagesPlugin +{ + +Triangle::Triangle(TQPoint A, TQPoint B, TQPoint C) +{ + m_a = distanceP2P(B, C); + m_b = distanceP2P(A, C); + m_c = distanceP2P(A, B); +} + +float Triangle::angleABC(void) +{ + return( 57.295779513082 * acos( (m_b*m_b - m_a*m_a - m_c*m_c ) / (-2*m_a*m_c ) ) ); +} + +float Triangle::angleACB(void) +{ + return( 57.295779513082 * acos( (m_c*m_c - m_a*m_a - m_b*m_b ) / (-2*m_a*m_b ) ) ); +} + +float Triangle::angleBAC(void) +{ + return( 57.295779513082 * acos( (m_a*m_a - m_b*m_b - m_c*m_c ) / (-2*m_b*m_c ) ) ); +} + +float Triangle::distanceP2P(const TQPoint& p1, const TQPoint& p2) +{ + return(sqrt( abs( p2.x()-p1.x() ) * abs( p2.x()-p1.x() ) + + abs( p2.y()-p1.y() ) * abs( p2.y()-p1.y() ) )); +} + +} // NameSpace DigikamPerspectiveImagesPlugin diff --git a/src/imageplugins/perspective/triangle.h b/src/imageplugins/perspective/triangle.h new file mode 100644 index 00000000..27fcc087 --- /dev/null +++ b/src/imageplugins/perspective/triangle.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-18 + * Description : triangle geometry calculation class. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TRIANGLE_H +#define TRIANGLE_H + +// TQt includes. + +#include + +namespace DigikamPerspectiveImagesPlugin +{ + +class Triangle +{ + +public: + + Triangle(TQPoint A, TQPoint B, TQPoint C); + ~Triangle(){}; + + float angleABC(void); + float angleACB(void); + float angleBAC(void); + +private: + + float m_a; + float m_b; + float m_c; + + float distanceP2P(const TQPoint& p1, const TQPoint& p2); +}; + +} // NameSpace DigikamPerspectiveImagesPlugin + +#endif /* TRIANGLE_H */ diff --git a/src/imageplugins/raindrop/Makefile.am b/src/imageplugins/raindrop/Makefile.am new file mode 100644 index 00000000..29899232 --- /dev/null +++ b/src/imageplugins/raindrop/Makefile.am @@ -0,0 +1,34 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_raindrop_la_SOURCES = imageplugin_raindrop.cpp \ + raindroptool.cpp raindrop.cpp + +digikamimageplugin_raindrop_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_raindrop_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_raindrop.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_raindrop.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_raindrop_ui.rc + diff --git a/src/imageplugins/raindrop/digikamimageplugin_raindrop.desktop b/src/imageplugins/raindrop/digikamimageplugin_raindrop.desktop new file mode 100644 index 00000000..847411b7 --- /dev/null +++ b/src/imageplugins/raindrop/digikamimageplugin_raindrop.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_RainDrop +Name[bg]=ПриÑтавка за Ñнимки - Дъждовни капки +Name[da]=Billedplugin_RegndrÃ¥ber +Name[el]=ΠÏοσθετοΕικόνας_Î’Ïοχή +Name[fi]=Sadepisarat +Name[hr]=KiÅ¡ne kapi +Name[it]=PluginImmagini_GocciaDiPioggia +Name[nl]=Afbeeldingsplugin_Regendruppels +Name[sr]=Кишне капи +Name[sr@Latn]=KiÅ¡ne kapi +Name[sv]=Insticksprogram för regndroppar +Name[tr]=ResimEklentisi_YaÄŸmurDamlaları +Name[vi]=ImagePlugin_Perspective +Name[xx]=xxImagePlugin_RainDropxx +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Rain dropping image effect plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за добавÑне на дъждовни капки +Comment[ca]=Connector pel digiKam d'efecte d'imatge de gotes de pluja +Comment[da]=Plugin til regndrÃ¥beeffekt pÃ¥ billeder i Digikam +Comment[de]=digiKam-Modul zum Erzeugen eines Regentropfeneffektes +Comment[el]=ΠÏόσθετο εφέ πτώσης βÏοχής για το digiKam +Comment[es]=Plugin para digiKam de efectos de gotas de lluvia +Comment[et]=DigiKami vihmapiiskade pildiefektiplugin +Comment[fa]=وصلۀ جلوۀ تصویر بارش باران برای digiKam +Comment[fi]=Lisää kuvan pintaan sadepisaroita +Comment[gl]=Un plugin de digiKam para criar un efeito de pingas de chuvia +Comment[hr]=digiKam dodatak za efekt padanja kiÅ¡e +Comment[is]=Ãforrit fyrir digiKam sem setur inn regndropa!!! +Comment[it]=Plugin per l'effetto a goccia di pioggia delle immagini per digiKam +Comment[ja]=digiKam 雨滴効果プラグイン +Comment[ms]=Plugin kesan imej titis hujan untuk digiKam +Comment[nds]=digiKam-Moduul för Regendrüppeneffekten +Comment[nl]=Digikam-plugin voor regendruppels +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਕਣੀ ਚਿੱਤਰ ਪਰਭਾਵ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam dodajÄ…ca efekt kropel deszczu na obrazie +Comment[pt]=Um 'plugin' do digiKam para criar um efeito de pingos de chuva +Comment[pt_BR]=Um 'plugin' do digiKam para criar um efeito de pingos de chuva +Comment[ru]=Модуль Ñффект "идущего дождÑ" Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre efekt padajúcich kvapiek +Comment[sr]=digiKam-ов прикључак за ефекат кишних капи у Ñлици +Comment[sr@Latn]=digiKam-ov prikljuÄak za efekat kiÅ¡nih kapi u slici +Comment[sv]=Digikam insticksprogram för regndroppsbildeffekt +Comment[tr]=digiKam için yaÄŸmur damlaları eklentisi +Comment[uk]=Втулок ефекту Ð¿Ð°Ð´Ð°Ð½Ð½Ñ ÐºÑ€Ð°Ð¿ÐµÐ»ÑŒ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung hiệu ứng ảnh giá»t mÆ°a cho digiKam +Comment[xx]=xxRain dropping image effect plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_raindrop +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/raindrop/digikamimageplugin_raindrop_ui.rc b/src/imageplugins/raindrop/digikamimageplugin_raindrop_ui.rc new file mode 100644 index 00000000..cf28a3f6 --- /dev/null +++ b/src/imageplugins/raindrop/digikamimageplugin_raindrop_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    F&ilters + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/raindrop/imageeffect_raindrop.cpp b/src/imageplugins/raindrop/imageeffect_raindrop.cpp new file mode 100644 index 00000000..8a82829d --- /dev/null +++ b/src/imageplugins/raindrop/imageeffect_raindrop.cpp @@ -0,0 +1,259 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "raindrop.h" +#include "imageeffect_raindrop.h" +#include "imageeffect_raindrop.moc" + +namespace DigikamRainDropImagesPlugin +{ + +ImageEffect_RainDrop::ImageEffect_RainDrop(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Add Raindrops to Photograph"), + "raindrops", false, true, false, + Digikam::ImageGuideWidget::HVGuideMode) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Raindrops"), + digikam_version, + I18N_NOOP("A digiKam image plugin to add raindrops to an image."), + TDEAboutData::License_GPL, + "(c) 2004-2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Raindrops algorithm"), + "pieter dot voloshyn at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + TQWhatsThis::add( m_imagePreviewWidget, i18n("

    This is the preview of the Raindrop effect." + "

    Note: if you have previously selected an area in the editor, " + "this will be unaffected by the filter. You can use this method to " + "disable the Raindrops effect on a human face, for example.") ); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 5, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Drop size:"), gboxSettings); + + m_dropInput = new KIntNumInput(gboxSettings); + m_dropInput->setRange(0, 200, 1, true); + m_dropInput->setValue(80); + TQWhatsThis::add( m_dropInput, i18n("

    Set here the raindrops' size.")); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 2); + gridSettings->addMultiCellWidget(m_dropInput, 1, 1, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Number:"), gboxSettings); + + m_amountInput = new KIntNumInput(gboxSettings); + m_amountInput->setRange(1, 500, 1, true); + m_amountInput->setValue(150); + TQWhatsThis::add( m_amountInput, i18n("

    This value controls the maximum number of raindrops.")); + + gridSettings->addMultiCellWidget(label2, 2, 2, 0, 2); + gridSettings->addMultiCellWidget(m_amountInput, 3, 3, 0, 2); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Fish eyes:"), gboxSettings); + + m_coeffInput = new KIntNumInput(gboxSettings); + m_coeffInput->setRange(1, 100, 1, true); + m_coeffInput->setValue(30); + TQWhatsThis::add( m_coeffInput, i18n("

    This value is the fish-eye-effect optical " + "distortion coefficient.")); + + gridSettings->addMultiCellWidget(label3, 4, 4, 0, 2); + gridSettings->addMultiCellWidget(m_coeffInput, 5, 5, 0, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_dropInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_amountInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_coeffInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_RainDrop::~ImageEffect_RainDrop() +{ +} + +void ImageEffect_RainDrop::renderingFinished() +{ + m_dropInput->setEnabled(true); + m_amountInput->setEnabled(true); + m_coeffInput->setEnabled(true); +} + +void ImageEffect_RainDrop::readUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("raindrops Tool Dialog"); + + m_dropInput->blockSignals(true); + m_amountInput->blockSignals(true); + m_coeffInput->blockSignals(true); + + m_dropInput->setValue(config->readNumEntry("DropAdjustment", 80)); + m_amountInput->setValue(config->readNumEntry("AmountAdjustment", 150)); + m_coeffInput->setValue(config->readNumEntry("CoeffAdjustment", 30)); + + m_dropInput->blockSignals(false); + m_amountInput->blockSignals(false); + m_coeffInput->blockSignals(false); + + slotEffect(); +} + +void ImageEffect_RainDrop::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("raindrops Tool Dialog"); + config->writeEntry("DropAdjustment", m_dropInput->value()); + config->writeEntry("AmountAdjustment", m_amountInput->value()); + config->writeEntry("CoeffAdjustment", m_coeffInput->value()); + config->sync(); +} + +void ImageEffect_RainDrop::resetValues() +{ + m_dropInput->blockSignals(true); + m_amountInput->blockSignals(true); + m_coeffInput->blockSignals(true); + + m_dropInput->setValue(80); + m_amountInput->setValue(150); + m_coeffInput->setValue(30); + + m_dropInput->blockSignals(false); + m_amountInput->blockSignals(false); + m_coeffInput->blockSignals(false); +} + +void ImageEffect_RainDrop::prepareEffect() +{ + m_dropInput->setEnabled(false); + m_amountInput->setEnabled(false); + m_coeffInput->setEnabled(false); + + int d = m_dropInput->value(); + int a = m_amountInput->value(); + int c = m_coeffInput->value(); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + // Selected data from the image + TQRect selection( iface->selectedXOrg(), iface->selectedYOrg(), + iface->selectedWidth(), iface->selectedHeight() ); + + m_threadedFilter = dynamic_cast( + new RainDrop(iface->getOriginalImg(), this, d, a, c, &selection)); +} + +void ImageEffect_RainDrop::prepareFinal() +{ + m_dropInput->setEnabled(false); + m_amountInput->setEnabled(false); + m_coeffInput->setEnabled(false); + + int d = m_dropInput->value(); + int a = m_amountInput->value(); + int c = m_coeffInput->value(); + + Digikam::ImageIface iface(0, 0); + + // Selected data from the image + TQRect selection( iface.selectedXOrg(), iface.selectedYOrg(), + iface.selectedWidth(), iface.selectedHeight() ); + + m_threadedFilter = dynamic_cast( + new RainDrop(iface.getOriginalImg(), this, d, a, c, &selection)); +} + +void ImageEffect_RainDrop::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + + Digikam::DImg imDest = m_threadedFilter->getTargetImage() + .smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_imagePreviewWidget->updatePreview(); +} + +void ImageEffect_RainDrop::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("RainDrop"), + m_threadedFilter->getTargetImage().bits()); +} + +} // NameSpace DigikamRainDropImagesPlugin + diff --git a/src/imageplugins/raindrop/imageeffect_raindrop.h b/src/imageplugins/raindrop/imageeffect_raindrop.h new file mode 100644 index 00000000..fe567ba3 --- /dev/null +++ b/src/imageplugins/raindrop/imageeffect_raindrop.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_RAINDROP_H +#define IMAGEEFFECT_RAINDROP_H + +// Digikam includes. + +#include "imageguidedlg.h" + +class KIntNumInput; + +namespace DigikamRainDropImagesPlugin +{ + +class ImageEffect_RainDrop : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_RainDrop(TQWidget *parent); + ~ImageEffect_RainDrop(); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KIntNumInput *m_dropInput; + KIntNumInput *m_amountInput; + KIntNumInput *m_coeffInput; +}; + +} // NameSpace DigikamRainDropImagesPlugin + +#endif /* IMAGEEFFECT_RAINDROP_H */ diff --git a/src/imageplugins/raindrop/imageplugin_raindrop.cpp b/src/imageplugins/raindrop/imageplugin_raindrop.cpp new file mode 100644 index 00000000..20744961 --- /dev/null +++ b/src/imageplugins/raindrop/imageplugin_raindrop.cpp @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "raindroptool.h" +#include "imageplugin_raindrop.h" +#include "imageplugin_raindrop.moc" + +using namespace DigikamRainDropImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_raindrop, + KGenericFactory("digikamimageplugin_raindrop")); + +ImagePlugin_RainDrop::ImagePlugin_RainDrop(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_RainDrop") +{ + m_raindropAction = new TDEAction(i18n("Raindrops..."), "raindrop", 0, + this, TQ_SLOT(slotRainDrop()), + actionCollection(), "imageplugin_raindrop"); + + setXMLFile( "digikamimageplugin_raindrop_ui.rc" ); + + DDebug() << "ImagePlugin_RainDrop plugin loaded" << endl; +} + +ImagePlugin_RainDrop::~ImagePlugin_RainDrop() +{ +} + +void ImagePlugin_RainDrop::setEnabledActions(bool enable) +{ + m_raindropAction->setEnabled(enable); +} + +void ImagePlugin_RainDrop::slotRainDrop() +{ + RainDropTool *raindrop = new RainDropTool(this); + loadTool(raindrop); +} diff --git a/src/imageplugins/raindrop/imageplugin_raindrop.h b/src/imageplugins/raindrop/imageplugin_raindrop.h new file mode 100644 index 00000000..a85076f8 --- /dev/null +++ b/src/imageplugins/raindrop/imageplugin_raindrop.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_RAINDROP_H +#define IMAGEPLUGIN_RAINDROP_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_RainDrop : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_RainDrop(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_RainDrop(); + + void setEnabledActions(bool enable); + +private slots: + + void slotRainDrop(); + +private: + + TDEAction *m_raindropAction; +}; + +#endif /* IMAGEPLUGIN_RAINDROP_H */ diff --git a/src/imageplugins/raindrop/raindrop.cpp b/src/imageplugins/raindrop/raindrop.cpp new file mode 100644 index 00000000..7be977fc --- /dev/null +++ b/src/imageplugins/raindrop/raindrop.cpp @@ -0,0 +1,457 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Raindrop threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * Original RainDrop algorithm copyrighted 2004-2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "raindrop.h" + +namespace DigikamRainDropImagesPlugin +{ + +RainDrop::RainDrop(Digikam::DImg *orgImage, TQObject *parent, int drop, + int amount, int coeff, TQRect *selection) + : Digikam::DImgThreadedFilter(orgImage, parent, "RainDrop") +{ + m_drop = drop; + m_amount = amount; + m_coeff = coeff; + + m_selectedX = m_selectedY = m_selectedW = m_selectedH = 0; + + if ( selection ) + { + m_selectedX = selection->left(); + m_selectedY = selection->top(); + m_selectedW = selection->width(); + m_selectedH = selection->height(); + } + + initFilter(); +} + +void RainDrop::filterImage(void) +{ + int w = m_orgImage.width(); + int h = m_orgImage.height(); + + // If we have a region selection in image, use it to apply the filter modification around, + // else, applied the filter on the full image. + + if (m_selectedW && m_selectedH) + { + Digikam::DImg zone1, zone2, zone3, zone4, + zone1Dest, zone2Dest, zone3Dest, zone4Dest, + selectedImg; + selectedImg = m_orgImage.copy(m_selectedX, m_selectedY, m_selectedW, m_selectedH); + + // Cut the original image in 4 areas without clipping region. + + zone1 = m_orgImage.copy(0, 0, m_selectedX, w); + zone2 = m_orgImage.copy(m_selectedX, 0, m_selectedX + m_selectedW, m_selectedY); + zone3 = m_orgImage.copy(m_selectedX, m_selectedY + m_selectedH, m_selectedX + m_selectedW, h); + zone4 = m_orgImage.copy(m_selectedX + m_selectedW, 0, w, h); + + zone1Dest = Digikam::DImg(zone1.width(), zone1.height(), zone1.sixteenBit(), zone1.hasAlpha()); + zone2Dest = Digikam::DImg(zone2.width(), zone2.height(), zone2.sixteenBit(), zone2.hasAlpha()); + zone3Dest = Digikam::DImg(zone3.width(), zone3.height(), zone3.sixteenBit(), zone3.hasAlpha()); + zone4Dest = Digikam::DImg(zone4.width(), zone4.height(), zone4.sixteenBit(), zone4.hasAlpha()); + + // Apply effect on each area. + + rainDropsImage(&zone1, &zone1Dest, 0, m_drop, m_amount, m_coeff, true, 0, 25); + rainDropsImage(&zone2, &zone2Dest, 0, m_drop, m_amount, m_coeff, true, 25, 50); + rainDropsImage(&zone3, &zone3Dest, 0, m_drop, m_amount, m_coeff, true, 50, 75); + rainDropsImage(&zone4, &zone4Dest, 0, m_drop, m_amount, m_coeff, true, 75, 100); + + // Build the target image. + + m_destImage.bitBltImage(&zone1Dest, 0, 0); + m_destImage.bitBltImage(&zone2Dest, m_selectedX, 0); + m_destImage.bitBltImage(&zone3Dest, m_selectedX, m_selectedY + m_selectedH); + m_destImage.bitBltImage(&zone4Dest, m_selectedX + m_selectedW, 0); + m_destImage.bitBltImage(&selectedImg, m_selectedX, m_selectedY); + } + else + { + rainDropsImage(&m_orgImage, &m_destImage, 0, m_drop, m_amount, m_coeff, true, 0, 100); + } +} + +/* Function to apply the RainDrops effect backported from ImageProcessing version 2 + * + * orgImage => The image + * MinDropSize => It's the minimum random size for rain drop. + * MaxDropSize => It's the minimum random size for rain drop. + * Amount => It's the maximum number for rain drops inside the image. + * Coeff => It's the fisheye's coefficient. + * bLimitRange => If true, the drop will not be cut. + * progressMin => Min. value for progress bar (can be different if using clipping area). + * progressMax => Max. value for progress bar (can be different if using clipping area). + * + * Theory => This functions does several math's functions and the engine + * is simple to undestand, but a little hard to implement. A + * control will indicate if there is or not a raindrop in that + * area, if not, a fisheye effect with a random size (max=MaxDropSize) + * will be applied, after this, a shadow will be applied too. + * and after this, a blur function will finish the effect. + */ +void RainDrop::rainDropsImage(Digikam::DImg *orgImage, Digikam::DImg *destImage, int MinDropSize, int MaxDropSize, + int Amount, int Coeff, bool bLimitRange, int progressMin, int progressMax) +{ + bool bResp; + int nRandSize, i; + int nRandX, nRandY; + int nCounter = 0; + int nWidth = orgImage->width(); + int nHeight = orgImage->height(); + bool sixteenBit = orgImage->sixteenBit(); + int bytesDepth = orgImage->bytesDepth(); + uchar *data = orgImage->bits(); + uchar *pResBits = destImage->bits(); + + if (Amount <= 0) + return; + + if (MinDropSize >= MaxDropSize) + MaxDropSize = MinDropSize + 1; + + if (MaxDropSize <= 0) + return; + + uchar *pStatusBits = new uchar[nHeight * nWidth]; + memset(pStatusBits, 0, sizeof(nHeight * nWidth)); + + // Initially, copy all pixels to destination + + destImage->bitBltImage(orgImage, 0, 0); + + // Randomize. + + TQDateTime dt = TQDateTime::currentDateTime(); + TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) ); + uint seed = dt.secsTo(Y2000); + + for (i = 0; !m_cancel && (i < Amount); i++) + { + nCounter = 0; + + do + { + nRandX = (int)(rand_r(&seed) * ((double)( nWidth - 1) / RAND_MAX)); + nRandY = (int)(rand_r(&seed) * ((double)(nHeight - 1) / RAND_MAX)); + + nRandSize = (rand() % (MaxDropSize - MinDropSize)) + MinDropSize; + + bResp = CreateRainDrop (data, nWidth, nHeight, sixteenBit, bytesDepth, + pResBits, pStatusBits, + nRandX, nRandY, nRandSize, Coeff, bLimitRange); + + nCounter++; + } + while ((bResp == false) && (nCounter < 10000) && !m_cancel); + + // Update the progress bar in dialog. + if (nCounter >= 10000) + { + i = Amount; + + postProgress(progressMax); + break; + } + + postProgress( (int)(progressMin + ((double)(i) * + (double)(progressMax-progressMin)) / (double)Amount) ); + } + + delete [] pStatusBits; +} + +bool RainDrop::CreateRainDrop(uchar *pBits, int Width, int Height, bool sixteenBit, int bytesDepth, + uchar *pResBits, uchar* pStatusBits, + int X, int Y, int DropSize, double Coeff, bool bLimitRange) +{ + int w, h, nw1, nh1, nw2, nh2; + int nHalfSize = DropSize / 2; + int nBright; + double lfRadius, lfOldRadius, lfAngle, lfDiv; + + Digikam::DColor imageData; + + uint nTotalR, nTotalG, nTotalB, offset; + int nBlurPixels, nBlurRadius; + + if (CanBeDropped(Width, Height, pStatusBits, X, Y, DropSize, bLimitRange)) + { + Coeff *= 0.01; + lfDiv = (double)nHalfSize / log (Coeff * (double)nHalfSize + 1.0); + + for (h = -nHalfSize; !m_cancel && (h <= nHalfSize); h++) + { + for (w = -nHalfSize; !m_cancel && (w <= nHalfSize); w++) + { + lfRadius = sqrt (h * h + w * w); + lfAngle = atan2 ((double)h, (double)w); + + if (lfRadius <= (double)nHalfSize) + { + lfOldRadius = lfRadius; + lfRadius = (exp (lfRadius / lfDiv) - 1.0) / Coeff; + + nw1 = (int)((double)X + lfRadius * cos (lfAngle)); + nh1 = (int)((double)Y + lfRadius * sin (lfAngle)); + + nw2 = X + w; + nh2 = Y + h; + + if (IsInside(Width, Height, nw1, nh1)) + { + if (IsInside(Width, Height, nw2, nh2)) + { + nBright = 0; + + if (lfOldRadius >= 0.9 * (double)nHalfSize) + { + if ((lfAngle >= 0.0) && (lfAngle < 2.25)) + nBright = -80; + else if ((lfAngle >= 2.25) && (lfAngle < 2.5)) + nBright = -40; + else if ((lfAngle >= -0.25) && (lfAngle < 0.0)) + nBright = -40; + } + + else if (lfOldRadius >= 0.8 * (double)nHalfSize) + { + if ((lfAngle >= 0.75) && (lfAngle < 1.50)) + nBright = -40; + else if ((lfAngle >= -0.10) && (lfAngle < 0.75)) + nBright = -30; + else if ((lfAngle >= 1.50) && (lfAngle < 2.35)) + nBright = -30; + } + + else if (lfOldRadius >= 0.7 * (double)nHalfSize) + { + if ((lfAngle >= 0.10) && (lfAngle < 2.0)) + nBright = -20; + else if ((lfAngle >= -2.50) && (lfAngle < -1.90)) + nBright = 60; + } + + else if (lfOldRadius >= 0.6 * (double)nHalfSize) + { + if ((lfAngle >= 0.50) && (lfAngle < 1.75)) + nBright = -20; + else if ((lfAngle >= 0.0) && (lfAngle < 0.25)) + nBright = 20; + else if ((lfAngle >= 2.0) && (lfAngle < 2.25)) + nBright = 20; + } + + else if (lfOldRadius >= 0.5 * (double)nHalfSize) + { + if ((lfAngle >= 0.25) && (lfAngle < 0.50)) + nBright = 30; + else if ((lfAngle >= 1.75 ) && (lfAngle < 2.0)) + nBright = 30; + } + + else if (lfOldRadius >= 0.4 * (double)nHalfSize) + { + if ((lfAngle >= 0.5) && (lfAngle < 1.75)) + nBright = 40; + } + + else if (lfOldRadius >= 0.3 * (double)nHalfSize) + { + if ((lfAngle >= 0.0) && (lfAngle < 2.25)) + nBright = 30; + } + + else if (lfOldRadius >= 0.2 * (double)nHalfSize) + { + if ((lfAngle >= 0.5) && (lfAngle < 1.75)) + nBright = 20; + } + + imageData.setColor(pBits + Offset(Width, nw1, nh1, bytesDepth), sixteenBit); + + if (sixteenBit) + { + // convert difference to 16-bit range + if (nBright > 0) + nBright = (nBright + 1) * 256 - 1; + else + nBright = (nBright - 1) * 256 + 1; + + imageData.setRed (LimitValues16(imageData.red() + nBright)); + imageData.setGreen(LimitValues16(imageData.green() + nBright)); + imageData.setBlue (LimitValues16(imageData.blue() + nBright)); + } + else + { + imageData.setRed (LimitValues8(imageData.red() + nBright)); + imageData.setGreen(LimitValues8(imageData.green() + nBright)); + imageData.setBlue (LimitValues8(imageData.blue() + nBright)); + } + + imageData.setPixel(pResBits + Offset(Width, nw2, nh2, bytesDepth)); + + } + } + } + } + } + + nBlurRadius = DropSize / 25 + 1; + + for (h = -nHalfSize - nBlurRadius; !m_cancel && (h <= nHalfSize + nBlurRadius); h++) + { + for (w = -nHalfSize - nBlurRadius; !m_cancel && (w <= nHalfSize + nBlurRadius); w++) + { + lfRadius = sqrt (h * h + w * w); + + if (lfRadius <= (double)nHalfSize * 1.1) + { + nTotalR = nTotalG = nTotalB = 0; + nBlurPixels = 0; + + for (nh1 = -nBlurRadius; !m_cancel && (nh1 <= nBlurRadius); nh1++) + { + for (nw1 = -nBlurRadius; !m_cancel && (nw1 <= nBlurRadius); nw1++) + { + nw2 = X + w + nw1; + nh2 = Y + h + nh1; + + if (IsInside (Width, Height, nw2, nh2)) + { + imageData.setColor(pResBits + Offset(Width, nw2, nh2, bytesDepth), sixteenBit); + + nTotalR += imageData.red(); + nTotalG += imageData.green(); + nTotalB += imageData.blue(); + nBlurPixels++; + } + } + } + + nw1 = X + w; + nh1 = Y + h; + + if (IsInside (Width, Height, nw1, nh1)) + { + offset = Offset(Width, nw1, nh1, bytesDepth); + + // to preserve alpha channel + imageData.setColor(pResBits + offset, sixteenBit); + + imageData.setRed (nTotalR / nBlurPixels); + imageData.setGreen(nTotalG / nBlurPixels); + imageData.setBlue (nTotalB / nBlurPixels); + + imageData.setPixel(pResBits + offset); + } + } + } + } + + SetDropStatusBits (Width, Height, pStatusBits, X, Y, DropSize); + } + else + return (false); + + return (true); +} + + +bool RainDrop::CanBeDropped(int Width, int Height, uchar *pStatusBits, int X, int Y, + int DropSize, bool bLimitRange) +{ + int w, h, i = 0; + int nHalfSize = DropSize / 2; + + if (pStatusBits == NULL) + return (true); + + for (h = Y - nHalfSize; h <= Y + nHalfSize; h++) + { + for (w = X - nHalfSize; w <= X + nHalfSize; w++) + { + if (IsInside (Width, Height, w, h)) + { + i = h * Width + w; + if (pStatusBits[i]) + return (false); + } + else + { + if (bLimitRange) + return (false); + } + } + } + + return (true); +} + +bool RainDrop::SetDropStatusBits (int Width, int Height, uchar *pStatusBits, + int X, int Y, int DropSize) +{ + int w, h, i = 0; + int nHalfSize = DropSize / 2; + + if (pStatusBits == NULL) + return (false); + + for (h = Y - nHalfSize; h <= Y + nHalfSize; h++) + { + for (w = X - nHalfSize; w <= X + nHalfSize; w++) + { + if (IsInside (Width, Height, w, h)) + { + i = h * Width + w; + pStatusBits[i] = 255; + } + } + } + + return (true); +} + +} // NameSpace DigikamRainDropImagesPlugin diff --git a/src/imageplugins/raindrop/raindrop.h b/src/imageplugins/raindrop/raindrop.h new file mode 100644 index 00000000..9baca0d1 --- /dev/null +++ b/src/imageplugins/raindrop/raindrop.h @@ -0,0 +1,105 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Raindrop threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RAINDROP_H +#define RAINDROP_H + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +class TQRect; + +namespace DigikamRainDropImagesPlugin +{ + +class RainDrop : public Digikam::DImgThreadedFilter +{ + +public: + + RainDrop(Digikam::DImg *orgImage, TQObject *parent=0, int drop=80, + int amount=150, int coeff=30, TQRect *selection=0L); + + ~RainDrop(){}; + +private: + + virtual void filterImage(void); + + void rainDropsImage(Digikam::DImg *orgImage, Digikam::DImg *destImage, int MinDropSize, int MaxDropSize, + int Amount, int Coeff, bool bLimitRange, int progressMin, int progressMax); + + bool CreateRainDrop(uchar *pBits, int Width, int Height, bool sixteenBit, int bytesDepth, + uchar *pResBits, uchar* pStatusBits, + int X, int Y, int DropSize, double Coeff, bool bLimitRange); + + bool CanBeDropped(int Width, int Height, uchar *pStatusBits, int X, int Y, int DropSize, bool bLimitRange); + + bool SetDropStatusBits (int Width, int Height, uchar *pStatusBits, int X, int Y, int DropSize); + + // A color is represented in RGB value (e.g. 0xFFFFFF is white color). + // But R, G and B values has 256 values to be used so, this function analize + // the value and limits to this range. + inline int LimitValues8(int ColorValue) + { + if (ColorValue > 255) ColorValue = 255; + if (ColorValue < 0) ColorValue = 0; + return ColorValue; + }; + + inline int LimitValues16(int ColorValue) + { + if (ColorValue > 65535) ColorValue = 65535; + if (ColorValue < 0) ColorValue = 0; + return ColorValue; + }; + + inline bool IsInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; + + inline int Offset(int Width, int X, int Y, int bytesDepth) + { + return (Y * Width * bytesDepth + X * bytesDepth); + }; + +private: + + int m_drop; + int m_amount; + int m_coeff; + + int m_selectedX; + int m_selectedY; + int m_selectedW; + int m_selectedH; +}; + +} // NameSpace DigikamRainDropImagesPlugin + +#endif /* RAINDROP_H */ diff --git a/src/imageplugins/raindrop/raindroptool.cpp b/src/imageplugins/raindrop/raindroptool.cpp new file mode 100644 index 00000000..3d19238d --- /dev/null +++ b/src/imageplugins/raindrop/raindroptool.cpp @@ -0,0 +1,254 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "editortoolsettings.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "raindrop.h" +#include "raindroptool.h" +#include "raindroptool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamRainDropImagesPlugin +{ + +RainDropTool::RainDropTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("raindrops"); + setToolName(i18n("Raindrops")); + setToolIcon(SmallIcon("raindrop")); + + m_previewWidget = new ImageWidget("raindrops Tool", 0, + i18n("

    This is the preview of the Raindrop effect." + "

    Note: if you have previously selected an area in the editor, " + "this will be unaffected by the filter. You can use this method to " + "disable the Raindrops effect on a human face, for example."), + false); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQGridLayout* gridSettings = new TQGridLayout( m_gboxSettings->plainPage(), 7, 2); + + TQLabel *label1 = new TQLabel(i18n("Drop size:"), m_gboxSettings->plainPage()); + + m_dropInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_dropInput->setRange(0, 200, 1); + m_dropInput->setDefaultValue(80); + TQWhatsThis::add( m_dropInput, i18n("

    Set here the raindrops' size.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Number:"), m_gboxSettings->plainPage()); + + m_amountInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_amountInput->setRange(1, 500, 1); + m_amountInput->setDefaultValue(150); + TQWhatsThis::add( m_amountInput, i18n("

    This value controls the maximum number of raindrops.")); + + // ------------------------------------------------------------- + + TQLabel *label3 = new TQLabel(i18n("Fish eyes:"), m_gboxSettings->plainPage()); + + m_coeffInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_coeffInput->setRange(1, 100, 1); + m_coeffInput->setDefaultValue(30); + TQWhatsThis::add( m_coeffInput, i18n("

    This value is the fish-eye-effect optical " + "distortion coefficient.")); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 1); + gridSettings->addMultiCellWidget(m_dropInput, 1, 1, 0, 1); + gridSettings->addMultiCellWidget(label2, 2, 2, 0, 1); + gridSettings->addMultiCellWidget(m_amountInput, 3, 3, 0, 1); + gridSettings->addMultiCellWidget(label3, 4, 4, 0, 1); + gridSettings->addMultiCellWidget(m_coeffInput, 5, 5, 0, 1); + gridSettings->setRowStretch(6, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_dropInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_amountInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_coeffInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +RainDropTool::~RainDropTool() +{ +} + +void RainDropTool::renderingFinished() +{ + m_dropInput->setEnabled(true); + m_amountInput->setEnabled(true); + m_coeffInput->setEnabled(true); +} + +void RainDropTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("raindrops Tool"); + + m_dropInput->blockSignals(true); + m_amountInput->blockSignals(true); + m_coeffInput->blockSignals(true); + + m_dropInput->setValue(config->readNumEntry("DropAdjustment", m_dropInput->defaultValue())); + m_amountInput->setValue(config->readNumEntry("AmountAdjustment", m_amountInput->defaultValue())); + m_coeffInput->setValue(config->readNumEntry("CoeffAdjustment", m_coeffInput->defaultValue())); + + m_dropInput->blockSignals(false); + m_amountInput->blockSignals(false); + m_coeffInput->blockSignals(false); + + slotEffect(); +} + +void RainDropTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("raindrops Tool"); + config->writeEntry("DropAdjustment", m_dropInput->value()); + config->writeEntry("AmountAdjustment", m_amountInput->value()); + config->writeEntry("CoeffAdjustment", m_coeffInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void RainDropTool::slotResetSettings() +{ + m_dropInput->blockSignals(true); + m_amountInput->blockSignals(true); + m_coeffInput->blockSignals(true); + + m_dropInput->slotReset(); + m_amountInput->slotReset(); + m_coeffInput->slotReset(); + + m_dropInput->blockSignals(false); + m_amountInput->blockSignals(false); + m_coeffInput->blockSignals(false); + + slotEffect(); +} + +void RainDropTool::prepareEffect() +{ + m_dropInput->setEnabled(false); + m_amountInput->setEnabled(false); + m_coeffInput->setEnabled(false); + + int d = m_dropInput->value(); + int a = m_amountInput->value(); + int c = m_coeffInput->value(); + + ImageIface* iface = m_previewWidget->imageIface(); + + // Selected data from the image + TQRect selection(iface->selectedXOrg(), iface->selectedYOrg(), + iface->selectedWidth(), iface->selectedHeight()); + + setFilter(dynamic_cast + (new RainDrop(iface->getOriginalImg(), this, d, a, c, &selection))); +} + +void RainDropTool::prepareFinal() +{ + m_dropInput->setEnabled(false); + m_amountInput->setEnabled(false); + m_coeffInput->setEnabled(false); + + int d = m_dropInput->value(); + int a = m_amountInput->value(); + int c = m_coeffInput->value(); + + ImageIface iface(0, 0); + + // Selected data from the image + TQRect selection(iface.selectedXOrg(), iface.selectedYOrg(), + iface.selectedWidth(), iface.selectedHeight()); + + setFilter(dynamic_cast + (new RainDrop(iface.getOriginalImg(), this, d, a, c, &selection))); +} + +void RainDropTool::putPreviewData(void) +{ + ImageIface* iface = m_previewWidget->imageIface(); + + DImg imDest = filter()->getTargetImage() + .smoothScale(iface->previewWidth(), iface->previewHeight()); + iface->putPreviewImage(imDest.bits()); + + m_previewWidget->updatePreview(); +} + +void RainDropTool::putFinalData(void) +{ + ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("RainDrop"), filter()->getTargetImage().bits()); +} + +} // NameSpace DigikamRainDropImagesPlugin + diff --git a/src/imageplugins/raindrop/raindroptool.h b/src/imageplugins/raindrop/raindroptool.h new file mode 100644 index 00000000..d083eb9c --- /dev/null +++ b/src/imageplugins/raindrop/raindroptool.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-30 + * Description : a plugin to add rain drop over an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RAINDROPTOOL_H +#define RAINDROPTOOL_H + +// Digikam includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +} + +namespace Digikam +{ +class ImageWidget; +} + +namespace DigikamRainDropImagesPlugin +{ + +class RainDropTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + RainDropTool(TQObject *parent); + ~RainDropTool(); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + KDcrawIface::RIntNumInput *m_dropInput; + KDcrawIface::RIntNumInput *m_amountInput; + KDcrawIface::RIntNumInput *m_coeffInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamRainDropImagesPlugin + +#endif /* RAINDROPTOOL_H */ diff --git a/src/imageplugins/restoration/Makefile.am b/src/imageplugins/restoration/Makefile.am new file mode 100644 index 00000000..695609be --- /dev/null +++ b/src/imageplugins/restoration/Makefile.am @@ -0,0 +1,35 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/greycstoration \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_restoration_la_SOURCES = imageplugin_restoration.cpp \ + restorationtool.cpp + +digikamimageplugin_restoration_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_restoration_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -no-undefined -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_restoration.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_restoration.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_restoration_ui.rc + diff --git a/src/imageplugins/restoration/digikamimageplugin_restoration.desktop b/src/imageplugins/restoration/digikamimageplugin_restoration.desktop new file mode 100644 index 00000000..8d0e7bd8 --- /dev/null +++ b/src/imageplugins/restoration/digikamimageplugin_restoration.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_Restoration +Name[bg]=ПриÑтавка за Ñнимки - ВъзÑтановÑване +Name[da]=Plugin for billedrestaurering +Name[el]=ΠÏόσθετοΕικόνας_Αποκατάσταση +Name[fi]=Restaurointi +Name[hr]=Obnavljanje +Name[it]=PluginImmagini_Restauro +Name[ms]=ImagePlugin_Pemulihan +Name[nl]=Afbeeldingsplugin_Restauratie +Name[sr]=РеÑтаурација +Name[sr@Latn]=Restauracija +Name[sv]=Insticksprogram för bildrestaurering +Name[tr]=ResimEklentisi_Onarım +Name[xx]=xxImagePlugin_Restorationxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=digiKam plugin to restore a photograph +Comment[bg]=ПриÑтавка на digiKam за възÑтановÑване на Ñнимки +Comment[ca]=Connector pel digiKam per restaurar una fotografia +Comment[da]=Digikam plugin til restaurering af et fotografi +Comment[de]=digiKam-Modul zum Restaurieren eines Bildes +Comment[el]=ΠÏόσθετο του digiKam για αποκατάσταση μιας φωτογÏαφίας +Comment[es]=Plugin para digiKam para restaurar una fotografía +Comment[et]=DigiKami foto restaureerimimise plugin +Comment[fa]=وصلۀ digiKam برای ذخیرۀ یک عکس +Comment[fi]=Korjaa kuvassa esiintyviä virheitä +Comment[gl]=Un plugin de digiKam para restaurar unha fotografia +Comment[hr]=digiKam dodatak za obnavljanje fotografije +Comment[is]=Ãforrit fyrir digiKam sem fjarlægir óhreinindi úr myndum +Comment[it]=Plugin di digiKam per restaurare una fotografia +Comment[ja]=digiKam 写真復元プラグイン +Comment[nds]=digiKam-Moduul för't Wedderherstellen vun Fotos +Comment[nl]=Digikam-plugin voor het herstellen van een foto +Comment[pa]=ਇੱਕ ਫੋਟੋ ਮà©à©œ-ਸਟੋਰ ਕਰਨ ਲਈ ਡਿਜ਼ੀਕੈਮ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca odrestaurowanie zdjÄ™cia +Comment[pt]=Um 'plugin' do digiKam para restaurar uma fotografia +Comment[pt_BR]=Plugin digiKam para restaurar una fotografia +Comment[ru]=Модуль digiKam Ð´Ð»Ñ Ð²Ð¾ÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ð¸Ð¹ +Comment[sk]=digiKam plugin pre obnovenie fotografie +Comment[sr]=digiKam-ов прикључак за поправку фотографија +Comment[sr@Latn]=digiKam-ov prikljuÄak za popravku fotografija +Comment[sv]=Digikam insticksprogram för restaurering av ett fotografi +Comment[tr]=FotoÄŸraf onarmak için digiKam eklentisi +Comment[uk]=Втулок Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ‚Ð¾Ð³Ñ€Ð°Ñ„Ñ–Ð¹ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung xây dá»±ng lại ảnh chụp nhÆ° cÅ© cho digiKam +Comment[xx]=xxdigiKam plugin to restore a photographxx + +X-TDE-Library=digikamimageplugin_restoration +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/restoration/digikamimageplugin_restoration_ui.rc b/src/imageplugins/restoration/digikamimageplugin_restoration_ui.rc new file mode 100644 index 00000000..27185294 --- /dev/null +++ b/src/imageplugins/restoration/digikamimageplugin_restoration_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Enh&ance + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/restoration/imageeffect_restoration.cpp b/src/imageplugins/restoration/imageeffect_restoration.cpp new file mode 100644 index 00000000..825ef23d --- /dev/null +++ b/src/imageplugins/restoration/imageeffect_restoration.cpp @@ -0,0 +1,349 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "imageiface.h" +#include "greycstorationsettings.h" +#include "greycstorationwidget.h" +#include "greycstorationiface.h" +#include "imageeffect_restoration.h" +#include "imageeffect_restoration.moc" + +namespace DigikamRestorationImagesPlugin +{ + +ImageEffect_Restoration::ImageEffect_Restoration(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Photograph Restoration"), + "restoration", true, true, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Photograph Restoration"), + digikam_version, + I18N_NOOP("A digiKam image plugin to restore a photograph."), + TDEAboutData::License_GPL, + "(c) 2005-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("David Tschumperle", I18N_NOOP("CImg library"), 0, + "http://cimg.sourceforge.net"); + + about->addAuthor("Gerhard Kulzer", I18N_NOOP("Feedback and plugin polishing"), + "gerhard at kulzer.net"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_mainTab = new TQTabWidget( m_imagePreviewWidget ); + + TQWidget* firstPage = new TQWidget( m_mainTab ); + TQGridLayout* grid = new TQGridLayout( firstPage, 2, 2, spacingHint()); + m_mainTab->addTab( firstPage, i18n("Preset") ); + + KURLLabel *cimgLogoLabel = new KURLLabel(firstPage); + cimgLogoLabel->setText(TQString()); + cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + TDEGlobal::dirs()->addResourceType("logo-cimg", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + cimgLogoLabel->setPixmap( TQPixmap( directory + "logo-cimg.png" ) ); + TQToolTip::add(cimgLogoLabel, i18n("Visit CImg library website")); + + TQLabel *typeLabel = new TQLabel(i18n("Filtering type:"), firstPage); + typeLabel->setAlignment ( TQt::AlignRight | TQt::AlignVCenter); + m_restorationTypeCB = new TQComboBox( false, firstPage ); + m_restorationTypeCB->insertItem( i18n("None") ); + m_restorationTypeCB->insertItem( i18n("Reduce Uniform Noise") ); + m_restorationTypeCB->insertItem( i18n("Reduce JPEG Artefacts") ); + m_restorationTypeCB->insertItem( i18n("Reduce Texturing") ); + TQWhatsThis::add( m_restorationTypeCB, i18n("

    Select the filter preset to use for photograph restoration:

    " + "None: Most common values. Puts settings to default.

    " + "Reduce Uniform Noise: reduce small image artifacts like sensor noise.

    " + "Reduce JPEG Artefacts: reduce large image artifacts like JPEG compression mosaic.

    " + "Reduce Texturing: reduce image artifacts like paper texture or Moire patterns " + "of a scanned image.

    ")); + + grid->addMultiCellWidget(cimgLogoLabel, 0, 0, 1, 1); + grid->addMultiCellWidget(typeLabel, 1, 1, 0, 0); + grid->addMultiCellWidget(m_restorationTypeCB, 1, 1, 1, 1); + grid->setRowStretch(1, 10); + + // ------------------------------------------------------------- + + m_settingsWidget = new Digikam::GreycstorationWidget( m_mainTab ); + m_imagePreviewWidget->setUserAreaWidget(m_mainTab); + + // ------------------------------------------------------------- + + connect(cimgLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processCImgURL(const TQString&))); + + connect(m_restorationTypeCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotResetValues(int))); +} + +ImageEffect_Restoration::~ImageEffect_Restoration() +{ +} + +void ImageEffect_Restoration::renderingFinished() +{ + m_imagePreviewWidget->setEnable(true); + m_mainTab->setEnabled(true); +} + +void ImageEffect_Restoration::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("restoration Tool Dialog"); + + Digikam::GreycstorationSettings settings; + settings.fastApprox = config->readBoolEntry("FastApprox", true); + settings.interp = config->readNumEntry("Interpolation", + Digikam::GreycstorationSettings::NearestNeighbor); + settings.amplitude = config->readDoubleNumEntry("Amplitude", 60.0); + settings.sharpness = config->readDoubleNumEntry("Sharpness", 0.7); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", 0.3); + settings.alpha = config->readDoubleNumEntry("Alpha", 0.6); + settings.sigma = config->readDoubleNumEntry("Sigma", 1.1); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", 2.0); + settings.dl = config->readDoubleNumEntry("Dl", 0.8); + settings.da = config->readDoubleNumEntry("Da", 30.0); + settings.nbIter = config->readNumEntry("Iteration", 1); + settings.tile = config->readNumEntry("Tile", 512); + settings.btile = config->readNumEntry("BTile", 4); + m_settingsWidget->setSettings(settings); + + int p = config->readNumEntry("Preset", NoPreset); + m_restorationTypeCB->setCurrentItem(p); + if (p == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); +} + +void ImageEffect_Restoration::writeUserSettings() +{ + Digikam::GreycstorationSettings settings = m_settingsWidget->getSettings(); + TDEConfig* config = kapp->config(); + config->setGroup("restoration Tool Dialog"); + config->writeEntry("Preset", m_restorationTypeCB->currentItem()); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + config->sync(); +} + +void ImageEffect_Restoration::slotResetValues(int i) +{ + if (i == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); + + resetValues(); +} + +void ImageEffect_Restoration::resetValues() +{ + Digikam::GreycstorationSettings settings; + settings.setRestorationDefaultSettings(); + + switch(m_restorationTypeCB->currentItem()) + { + case ReduceUniformNoise: + { + settings.amplitude = 40.0; + break; + } + + case ReduceJPEGArtefacts: + { + settings.sharpness = 0.3; + settings.sigma = 1.0; + settings.amplitude = 100.0; + settings.nbIter = 2; + break; + } + + case ReduceTexturing: + { + settings.sharpness = 0.5; + settings.sigma = 1.5; + settings.amplitude = 100.0; + settings.nbIter = 2; + break; + } + } + + m_settingsWidget->setSettings(settings); +} + +void ImageEffect_Restoration::processCImgURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void ImageEffect_Restoration::prepareEffect() +{ + m_mainTab->setEnabled(false); + + Digikam::DImg previewImage = m_imagePreviewWidget->getOriginalRegionImage(); + + m_threadedFilter = dynamic_cast( + new Digikam::GreycstorationIface( + &previewImage, m_settingsWidget->getSettings(), + Digikam::GreycstorationIface::Restore, + 0, 0, 0, this)); +} + +void ImageEffect_Restoration::prepareFinal() +{ + m_mainTab->setEnabled(false); + + Digikam::ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + Digikam::DImg originalImage(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + + m_threadedFilter = dynamic_cast( + new Digikam::GreycstorationIface( + &originalImage, m_settingsWidget->getSettings(), + Digikam::GreycstorationIface::Restore, + 0, 0, 0, this)); + + delete [] data; +} + +void ImageEffect_Restoration::putPreviewData(void) +{ + Digikam::DImg imDest = m_threadedFilter->getTargetImage(); + m_imagePreviewWidget->setPreviewImage(imDest); +} + +void ImageEffect_Restoration::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + + iface.putOriginalImage(i18n("Restoration"), + m_threadedFilter->getTargetImage().bits()); +} + +void ImageEffect_Restoration::slotUser3() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Restoration Settings File to Load")) ); + if( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!m_settingsWidget->loadSettings(file, TQString("# Photograph Restoration Configuration File V2"))) + { + KMessageBox::error(this, + i18n("\"%1\" is not a Photograph Restoration settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + slotEffect(); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the Photograph Restoration text file.")); + + file.close(); + m_restorationTypeCB->blockSignals(true); + m_restorationTypeCB->setCurrentItem(NoPreset); + m_restorationTypeCB->blockSignals(false); + m_settingsWidget->setEnabled(true); +} + +void ImageEffect_Restoration::slotUser2() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("Photograph Restoration Settings File to Save")) ); + if( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + m_settingsWidget->saveSettings(file, TQString("# Photograph Restoration Configuration File V2")); + else + KMessageBox::error(this, i18n("Cannot save settings to the Photograph Restoration text file.")); + + file.close(); +} + +} // NameSpace DigikamRestorationImagesPlugin + diff --git a/src/imageplugins/restoration/imageeffect_restoration.h b/src/imageplugins/restoration/imageeffect_restoration.h new file mode 100644 index 00000000..7c28bd92 --- /dev/null +++ b/src/imageplugins/restoration/imageeffect_restoration.h @@ -0,0 +1,94 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_RESTORATION_H +#define IMAGEEFFECT_RESTORATION_H + +// TQt include. + +#include + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class TQComboBox; +class TQTabWidget; + +namespace Digikam +{ +class GreycstorationWidget; +} + +namespace DigikamRestorationImagesPlugin +{ + +class ImageEffect_Restoration : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Restoration(TQWidget* parent); + ~ImageEffect_Restoration(); + +private slots: + + void slotUser2(); + void slotUser3(); + void processCImgURL(const TQString&); + void readUserSettings(); + void slotResetValues(int); + +private: + + void writeUserSettings(); + void prepareEffect(void); + void prepareFinal(void); + void putPreviewData(void); + void putFinalData(void); + void resetValues(void); + void renderingFinished(void); + +private: + + enum RestorationFilteringPreset + { + NoPreset=0, + ReduceUniformNoise, + ReduceJPEGArtefacts, + ReduceTexturing + }; + + TQTabWidget *m_mainTab; + + TQComboBox *m_restorationTypeCB; + + Digikam::GreycstorationWidget *m_settingsWidget; +}; + +} // NameSpace DigikamRestorationImagesPlugin + +#endif /* IMAGEEFFECT_RESTORATION_H */ diff --git a/src/imageplugins/restoration/imageplugin_restoration.cpp b/src/imageplugins/restoration/imageplugin_restoration.cpp new file mode 100644 index 00000000..01cd52f7 --- /dev/null +++ b/src/imageplugins/restoration/imageplugin_restoration.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "restorationtool.h" +#include "imageplugin_restoration.h" +#include "imageplugin_restoration.moc" + +using namespace DigikamRestorationImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_restoration, + KGenericFactory("digikamimageplugin_restoration")); + +ImagePlugin_Restoration::ImagePlugin_Restoration(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Restoration") +{ + m_restorationAction = new TDEAction(i18n("Restoration..."), "restoration", 0, + this, TQ_SLOT(slotRestoration()), + actionCollection(), "imageplugin_restoration"); + + setXMLFile( "digikamimageplugin_restoration_ui.rc" ); + + DDebug() << "ImagePlugin_Restoration plugin loaded" << endl; +} + +ImagePlugin_Restoration::~ImagePlugin_Restoration() +{ +} + +void ImagePlugin_Restoration::setEnabledActions(bool enable) +{ + m_restorationAction->setEnabled(enable); +} + +void ImagePlugin_Restoration::slotRestoration() +{ + RestorationTool *tool = new RestorationTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/restoration/imageplugin_restoration.h b/src/imageplugins/restoration/imageplugin_restoration.h new file mode 100644 index 00000000..50ac8326 --- /dev/null +++ b/src/imageplugins/restoration/imageplugin_restoration.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_RESTORATION_H +#define IMAGEPLUGIN_RESTORATION_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Restoration : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Restoration(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Restoration(); + + void setEnabledActions(bool enable); + +private slots: + + void slotRestoration(); + +private: + + TDEAction *m_restorationAction; +}; + +#endif /* IMAGEPLUGIN_RESTORATION_H */ diff --git a/src/imageplugins/restoration/restorationtool.cpp b/src/imageplugins/restoration/restorationtool.cpp new file mode 100644 index 00000000..a6896135 --- /dev/null +++ b/src/imageplugins/restoration/restorationtool.cpp @@ -0,0 +1,356 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "greycstorationsettings.h" +#include "greycstorationwidget.h" +#include "greycstorationiface.h" +#include "restorationtool.h" +#include "restorationtool.moc" + +using namespace Digikam; + +namespace DigikamRestorationImagesPlugin +{ + +RestorationTool::RestorationTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("restoration"); + setToolName(i18n("Restoration")); + setToolIcon(SmallIcon("restoration")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Try, + EditorToolSettings::PanIcon); + + TQGridLayout* gridSettings = new TQGridLayout(m_gboxSettings->plainPage(), 2, 1); + m_mainTab = new TQTabWidget( m_gboxSettings->plainPage() ); + + TQWidget* firstPage = new TQWidget( m_mainTab ); + TQGridLayout* grid = new TQGridLayout(firstPage, 2, 2); + m_mainTab->addTab( firstPage, i18n("Preset") ); + + KURLLabel *cimgLogoLabel = new KURLLabel(firstPage); + cimgLogoLabel->setText(TQString()); + cimgLogoLabel->setURL("http://cimg.sourceforge.net"); + TDEGlobal::dirs()->addResourceType("logo-cimg", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("logo-cimg", "logo-cimg.png"); + cimgLogoLabel->setPixmap( TQPixmap( directory + "logo-cimg.png" ) ); + TQToolTip::add(cimgLogoLabel, i18n("Visit CImg library website")); + + TQLabel *typeLabel = new TQLabel(i18n("Filtering type:"), firstPage); + typeLabel->setAlignment ( TQt::AlignRight | TQt::AlignVCenter); + m_restorationTypeCB = new TQComboBox(false, firstPage); + m_restorationTypeCB->insertItem( i18n("None") ); + m_restorationTypeCB->insertItem( i18n("Reduce Uniform Noise") ); + m_restorationTypeCB->insertItem( i18n("Reduce JPEG Artefacts") ); + m_restorationTypeCB->insertItem( i18n("Reduce Texturing") ); + TQWhatsThis::add( m_restorationTypeCB, i18n("

    Select the filter preset to use for photograph restoration:

    " + "None: Most common values. Puts settings to default.

    " + "Reduce Uniform Noise: reduce small image artifacts like sensor noise.

    " + "Reduce JPEG Artefacts: reduce large image artifacts like JPEG compression mosaic.

    " + "Reduce Texturing: reduce image artifacts like paper texture or Moire patterns " + "of a scanned image.

    ")); + + grid->addMultiCellWidget(cimgLogoLabel, 0, 0, 1, 1); + grid->addMultiCellWidget(typeLabel, 1, 1, 0, 0); + grid->addMultiCellWidget(m_restorationTypeCB, 1, 1, 1, 1); + grid->setRowStretch(1, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + m_settingsWidget = new GreycstorationWidget( m_mainTab ); + gridSettings->addMultiCellWidget(m_mainTab, 0, 0, 1, 1); + gridSettings->addMultiCellWidget(new TQLabel(m_gboxSettings->plainPage()), 1, 1, 1, 1); + gridSettings->setMargin(m_gboxSettings->spacingHint()); + gridSettings->setSpacing(m_gboxSettings->spacingHint()); + gridSettings->setRowStretch(2, 10); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "restoration Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(cimgLogoLabel, TQ_SIGNAL(leftClickedURL(const TQString&)), + this, TQ_SLOT(processCImgURL(const TQString&))); + + connect(m_restorationTypeCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotResetValues(int))); + + // ------------------------------------------------------------- + + GreycstorationSettings defaults; + defaults.setRestorationDefaultSettings(); + m_settingsWidget->setDefaultSettings(defaults); +} + +RestorationTool::~RestorationTool() +{ +} + +void RestorationTool::renderingFinished() +{ + m_previewWidget->setEnable(true); + m_mainTab->setEnabled(true); +} + +void RestorationTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("restoration Tool"); + + GreycstorationSettings settings; + GreycstorationSettings defaults; + defaults.setRestorationDefaultSettings(); + + settings.fastApprox = config->readBoolEntry("FastApprox", defaults.fastApprox); + settings.interp = config->readNumEntry("Interpolation", defaults.interp); + settings.amplitude = config->readDoubleNumEntry("Amplitude", defaults.amplitude); + settings.sharpness = config->readDoubleNumEntry("Sharpness", defaults.sharpness); + settings.anisotropy = config->readDoubleNumEntry("Anisotropy", defaults.anisotropy); + settings.alpha = config->readDoubleNumEntry("Alpha", defaults.alpha); + settings.sigma = config->readDoubleNumEntry("Sigma", defaults.sigma); + settings.gaussPrec = config->readDoubleNumEntry("GaussPrec", defaults.gaussPrec); + settings.dl = config->readDoubleNumEntry("Dl", defaults.dl); + settings.da = config->readDoubleNumEntry("Da", defaults.da); + settings.nbIter = config->readNumEntry("Iteration", defaults.nbIter); + settings.tile = config->readNumEntry("Tile", defaults.tile); + settings.btile = config->readNumEntry("BTile", defaults.btile); + m_settingsWidget->setSettings(settings); + + int p = config->readNumEntry("Preset", NoPreset); + m_restorationTypeCB->setCurrentItem(p); + if (p == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); +} + +void RestorationTool::writeSettings() +{ + GreycstorationSettings settings = m_settingsWidget->getSettings(); + TDEConfig* config = kapp->config(); + config->setGroup("restoration Tool"); + config->writeEntry("Preset", m_restorationTypeCB->currentItem()); + config->writeEntry("FastApprox", settings.fastApprox); + config->writeEntry("Interpolation", settings.interp); + config->writeEntry("Amplitude", settings.amplitude); + config->writeEntry("Sharpness", settings.sharpness); + config->writeEntry("Anisotropy", settings.anisotropy); + config->writeEntry("Alpha", settings.alpha); + config->writeEntry("Sigma", settings.sigma); + config->writeEntry("GaussPrec", settings.gaussPrec); + config->writeEntry("Dl", settings.dl); + config->writeEntry("Da", settings.da); + config->writeEntry("Iteration", settings.nbIter); + config->writeEntry("Tile", settings.tile); + config->writeEntry("BTile", settings.btile); + m_previewWidget->writeSettings(); + config->sync(); +} + +void RestorationTool::slotResetValues(int i) +{ + if (i == NoPreset) + m_settingsWidget->setEnabled(true); + else + m_settingsWidget->setEnabled(false); + + slotResetSettings(); +} + +void RestorationTool::slotResetSettings() +{ + GreycstorationSettings settings; + settings.setRestorationDefaultSettings(); + + switch(m_restorationTypeCB->currentItem()) + { + case ReduceUniformNoise: + { + settings.amplitude = 40.0; + break; + } + + case ReduceJPEGArtefacts: + { + settings.sharpness = 0.3; + settings.sigma = 1.0; + settings.amplitude = 100.0; + settings.nbIter = 2; + break; + } + + case ReduceTexturing: + { + settings.sharpness = 0.5; + settings.sigma = 1.5; + settings.amplitude = 100.0; + settings.nbIter = 2; + break; + } + } + + m_settingsWidget->setSettings(settings); +} + +void RestorationTool::processCImgURL(const TQString& url) +{ + TDEApplication::kApplication()->invokeBrowser(url); +} + +void RestorationTool::prepareEffect() +{ + m_mainTab->setEnabled(false); + + DImg previewImage = m_previewWidget->getOriginalRegionImage(); + + setFilter(dynamic_cast(new GreycstorationIface(&previewImage, + m_settingsWidget->getSettings(), GreycstorationIface::Restore, + 0, 0, 0, this))); +} + +void RestorationTool::prepareFinal() +{ + m_mainTab->setEnabled(false); + + ImageIface iface(0, 0); + uchar *data = iface.getOriginalImage(); + DImg originalImage(iface.originalWidth(), iface.originalHeight(), + iface.originalSixteenBit(), iface.originalHasAlpha(), data); + + setFilter(dynamic_cast(new GreycstorationIface(&originalImage, + m_settingsWidget->getSettings(), GreycstorationIface::Restore, + 0, 0, 0, this))); + + delete [] data; +} + +void RestorationTool::putPreviewData() +{ + DImg imDest = filter()->getTargetImage(); + m_previewWidget->setPreviewImage(imDest); +} + +void RestorationTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Restoration"), filter()->getTargetImage().bits()); +} + +void RestorationTool::slotLoadSettings() +{ + KURL loadRestorationFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Restoration Settings File to Load")) ); + if( loadRestorationFile.isEmpty() ) + return; + + TQFile file(loadRestorationFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + if (!m_settingsWidget->loadSettings(file, TQString("# Photograph Restoration Configuration File V2"))) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a Photograph Restoration settings text file.") + .arg(loadRestorationFile.fileName())); + file.close(); + return; + } + + slotEffect(); + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the Photograph Restoration text file.")); + + file.close(); + m_restorationTypeCB->blockSignals(true); + m_restorationTypeCB->setCurrentItem(NoPreset); + m_restorationTypeCB->blockSignals(false); + m_settingsWidget->setEnabled(true); +} + +void RestorationTool::slotSaveAsSettings() +{ + KURL saveRestorationFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("Photograph Restoration Settings File to Save")) ); + if( saveRestorationFile.isEmpty() ) + return; + + TQFile file(saveRestorationFile.path()); + + if ( file.open(IO_WriteOnly) ) + m_settingsWidget->saveSettings(file, TQString("# Photograph Restoration Configuration File V2")); + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the Photograph Restoration text file.")); + + file.close(); +} + +} // NameSpace DigikamRestorationImagesPlugin + diff --git a/src/imageplugins/restoration/restorationtool.h b/src/imageplugins/restoration/restorationtool.h new file mode 100644 index 00000000..6242a2b6 --- /dev/null +++ b/src/imageplugins/restoration/restorationtool.h @@ -0,0 +1,100 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-26 + * Description : a digiKam image editor plugin to restore + * a photograph + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RESTORATIONTOOL_H +#define RESTORATIONTOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQComboBox; +class TQTabWidget; + +namespace Digikam +{ +class GreycstorationWidget; +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamRestorationImagesPlugin +{ + +class RestorationTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + RestorationTool(TQObject* parent); + ~RestorationTool(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotResetSettings(); + void processCImgURL(const TQString&); + void slotResetValues(int); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum RestorationFilteringPreset + { + NoPreset=0, + ReduceUniformNoise, + ReduceJPEGArtefacts, + ReduceTexturing + }; + + TQTabWidget *m_mainTab; + + TQComboBox *m_restorationTypeCB; + + Digikam::GreycstorationWidget *m_settingsWidget; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamRestorationImagesPlugin + +#endif /* RESTORATIONTOOL_H */ diff --git a/src/imageplugins/sheartool/Makefile.am b/src/imageplugins/sheartool/Makefile.am new file mode 100644 index 00000000..a601b8a5 --- /dev/null +++ b/src/imageplugins/sheartool/Makefile.am @@ -0,0 +1,33 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_sheartool_la_SOURCES = imageplugin_sheartool.cpp shear.cpp \ + sheartool.cpp + +digikamimageplugin_sheartool_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_sheartool_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_sheartool.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_sheartool.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_sheartool_ui.rc diff --git a/src/imageplugins/sheartool/digikamimageplugin_sheartool.desktop b/src/imageplugins/sheartool/digikamimageplugin_sheartool.desktop new file mode 100644 index 00000000..8884ef27 --- /dev/null +++ b/src/imageplugins/sheartool/digikamimageplugin_sheartool.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Name=ImagePlugin_ShearTool +Name[bg]=ПриÑтавка за Ñнимки - ИнÑтрумент за изрÑзване +Name[da]=Billedplugin_Forskydningsværktøj +Name[el]=ΠÏόσθετοΕικόνας_ΕÏγαλείοΣτÏέβλωσης +Name[fi]=Väännin +Name[hr]=Smicanje +Name[it]=PluginImmagini_DistorsioneCurvilinea +Name[ms]=ImagePlugin_AlatanPemotong +Name[nl]=Afbeeldingsplugin_SchuinTrekken +Name[sr]=Ðлат за иÑкошавање +Name[sr@Latn]=Alat za iskoÅ¡avanje +Name[sv]=Insticksprogram med skjuvningsverktyg +Name[tr]=ResimEklentisi_EÄŸmeAracı +Name[xx]=xxImagePlugin_ShearToolxx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Shear tool plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam Ñ Ð¸Ð½Ñтрумент за изрÑзване на Ñнимки +Comment[ca]=Connector pel digiKam d'eina per retallar +Comment[da]=Forskydningsværktøjs-plugin for Digikam +Comment[de]=digiKam-Modul zum Scheren eines Bildes +Comment[el]=ΠÏόσθετο εÏγαλείο στÏέβλωσης για το digiKam +Comment[es]=Plugin para digiKam con herramientas para cizallar una imagen +Comment[et]=DigiKami pildinihkeplugin +Comment[fa]=وصلۀ ابزار چیدن برای digiKam +Comment[fi]=Vääntää kuvaa vaaka- ja pystysuunnassa +Comment[gl]=Un plugin de digiKam para inclinar unha imaxe +Comment[hr]=digiKam dodatak za smicanje +Comment[is]=Ãforrit fyrir digiKam sem snýr upp á myndir +Comment[it]=Plugin per lo strumento di distorsione curvilinea per digiKam +Comment[ja]=digiKam 剪断変形プラグイン +Comment[ms]=Templat plugin pemotong untuk digiKam +Comment[nds]=digiKam-Warktüüchmoduul för't Scheren +Comment[nl]=Digikam-plugin voor het schuintrekken van afbeeldingen +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਸ਼ੀਅਰ ਸੰਦ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca pochylenie obrazu +Comment[pt]=Um 'plugin' do digiKam para inclinar uma imagem +Comment[pt_BR]=Um 'plugin' do digiKam para inclinar uma imagem +Comment[ru]=Модуль Ñдвига фрагментов Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre orezanie obrázku +Comment[sr]=digiKam-ов прикључак за иÑкошавање +Comment[sr@Latn]=digiKam-ov prikljuÄak za iskoÅ¡avanje +Comment[sv]=Digikam insticksprogram med skjuvningsverktyg +Comment[tr]=digiKam için eÄŸme aracı eklentisi +Comment[uk]=Втулок заÑобу Ð¿ÐµÑ€ÐµÐºÐ¾ÑˆÐµÐ½Ð½Ñ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung công cụ kéo cắt cho digiKam +Comment[xx]=xxShear tool plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_sheartool +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/sheartool/digikamimageplugin_sheartool_ui.rc b/src/imageplugins/sheartool/digikamimageplugin_sheartool_ui.rc new file mode 100644 index 00000000..0a2cf60a --- /dev/null +++ b/src/imageplugins/sheartool/digikamimageplugin_sheartool_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    Tra&nsform + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/sheartool/imageeffect_sheartool.cpp b/src/imageplugins/sheartool/imageeffect_sheartool.cpp new file mode 100644 index 00000000..e65bd91b --- /dev/null +++ b/src/imageplugins/sheartool/imageeffect_sheartool.cpp @@ -0,0 +1,322 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "sheartool.h" +#include "imageeffect_sheartool.h" +#include "imageeffect_sheartool.moc" + +namespace DigikamShearToolImagesPlugin +{ + +ImageEffect_ShearTool::ImageEffect_ShearTool(TQWidget* parent) + : Digikam::ImageGuideDlg(parent, i18n("Shear Tool"), "sheartool", + false, true, true, + Digikam::ImageGuideWidget::HVGuideMode) +{ + // No need Abort button action. + showButton(User1, false); + + TQString whatsThis; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Shear Tool"), + digikam_version, + I18N_NOOP("A digiKam image plugin to shear an image."), + TDEAboutData::License_GPL, + "(c) 2004-2008, Gilles Caulier", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pieter Z. Voloshyn", I18N_NOOP("Shear algorithm"), + "pieter dot voloshyn at gmail dot com"); + + setAboutData(about); + + TQWhatsThis::add( m_imagePreviewWidget, i18n("

    This is the shearing image operation preview. " + "If you move the mouse cursor on this preview, " + "a vertical and horizontal dashed line will be drawn " + "to guide you in adjusting the shearing correction. " + "Release the left mouse button to freeze the dashed " + "line's position.")); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 11, 2, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("New width:"), gboxSettings); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), gboxSettings); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), gboxSettings); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), gboxSettings); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + gridSettings->addMultiCellWidget(label2, 1, 1, 0, 0); + gridSettings->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + + KSeparator *line = new KSeparator(Horizontal, gboxSettings); + gridSettings->addMultiCellWidget(line, 2, 2, 0, 2); + + TQLabel *label3 = new TQLabel(i18n("Main horizontal angle:"), gboxSettings); + m_mainHAngleInput = new KIntNumInput(gboxSettings); + m_mainHAngleInput->setRange(-45, 45, 1, true); + m_mainHAngleInput->setValue(0); + TQWhatsThis::add( m_mainHAngleInput, i18n("

    The main horizontal shearing angle, in degrees.")); + gridSettings->addMultiCellWidget(label3, 3, 3, 0, 2); + gridSettings->addMultiCellWidget(m_mainHAngleInput, 4, 4, 0, 2); + + TQLabel *label4 = new TQLabel(i18n("Fine horizontal angle:"), gboxSettings); + m_fineHAngleInput = new KDoubleNumInput(gboxSettings); + m_fineHAngleInput->setRange(-5.0, 5.0, 0.01, true); + m_fineHAngleInput->setValue(0); + TQWhatsThis::add( m_fineHAngleInput, i18n("

    This value in degrees will be added to main horizontal angle value " + "to set fine adjustments.")); + gridSettings->addMultiCellWidget(label4, 5, 5, 0, 2); + gridSettings->addMultiCellWidget(m_fineHAngleInput, 6, 6, 0, 2); + + TQLabel *label5 = new TQLabel(i18n("Main vertical angle:"), gboxSettings); + m_mainVAngleInput = new KIntNumInput(gboxSettings); + m_mainVAngleInput->setRange(-45, 45, 1, true); + m_mainVAngleInput->setValue(0); + TQWhatsThis::add( m_mainVAngleInput, i18n("

    The main vertical shearing angle, in degrees.")); + gridSettings->addMultiCellWidget(label5, 7, 7, 0, 0); + gridSettings->addMultiCellWidget(m_mainVAngleInput, 8, 8, 0, 2); + + TQLabel *label6 = new TQLabel(i18n("Fine vertical angle:"), gboxSettings); + m_fineVAngleInput = new KDoubleNumInput(gboxSettings); + m_fineVAngleInput->setRange(-5.0, 5.0, 0.01, true); + m_fineVAngleInput->setValue(0); + TQWhatsThis::add( m_fineVAngleInput, i18n("

    This value in degrees will be added to main vertical angle value " + "to set fine adjustments.")); + gridSettings->addMultiCellWidget(label6, 9, 9, 0, 2); + gridSettings->addMultiCellWidget(m_fineVAngleInput, 10, 10, 0, 2); + + m_antialiasInput = new TQCheckBox(i18n("Anti-Aliasing"), gboxSettings); + TQWhatsThis::add( m_antialiasInput, i18n("

    Enable this option to apply the anti-aliasing filter " + "to the sheared image. " + "To smooth the target image, it will be blurred a little.")); + gridSettings->addMultiCellWidget(m_antialiasInput, 11, 11, 0, 2); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_mainHAngleInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineHAngleInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_mainVAngleInput, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineVAngleInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_antialiasInput, TQ_SIGNAL(toggled (bool)), + this, TQ_SLOT(slotEffect())); +} + +ImageEffect_ShearTool::~ImageEffect_ShearTool() +{ +} + +void ImageEffect_ShearTool::readUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("sheartool Tool Dialog"); + m_mainHAngleInput->setValue(config->readNumEntry("Main HAngle", 0)); + m_mainVAngleInput->setValue(config->readNumEntry("Main VAngle", 0)); + m_fineHAngleInput->setValue(config->readDoubleNumEntry("Fine HAngle", 0.0)); + m_fineVAngleInput->setValue(config->readDoubleNumEntry("Fine VAngle", 0.0)); + m_antialiasInput->setChecked(config->readBoolEntry("Anti Aliasing", true)); + slotEffect(); +} + +void ImageEffect_ShearTool::writeUserSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup("sheartool Tool Dialog"); + config->writeEntry("Main HAngle", m_mainHAngleInput->value()); + config->writeEntry("Main VAngle", m_mainVAngleInput->value()); + config->writeEntry("Fine HAngle", m_fineHAngleInput->value()); + config->writeEntry("Fine VAngle", m_fineVAngleInput->value()); + config->writeEntry("Anti Aliasing", m_antialiasInput->isChecked()); + config->sync(); +} + +void ImageEffect_ShearTool::resetValues() +{ + m_mainHAngleInput->blockSignals(true); + m_mainVAngleInput->blockSignals(true); + m_fineHAngleInput->blockSignals(true); + m_fineVAngleInput->blockSignals(true); + m_antialiasInput->blockSignals(true); + m_mainHAngleInput->setValue(0); + m_mainVAngleInput->setValue(0); + m_fineHAngleInput->setValue(0.0); + m_fineVAngleInput->setValue(0.0); + m_antialiasInput->setChecked(true); + m_mainHAngleInput->blockSignals(false); + m_mainVAngleInput->blockSignals(false); + m_fineHAngleInput->blockSignals(false); + m_fineVAngleInput->blockSignals(false); + m_antialiasInput->blockSignals(false); +} + +void ImageEffect_ShearTool::prepareEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_mainHAngleInput->setEnabled(false); + m_mainVAngleInput->setEnabled(false); + m_fineHAngleInput->setEnabled(false); + m_fineVAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + + float hAngle = m_mainHAngleInput->value() + m_fineHAngleInput->value(); + float vAngle = m_mainVAngleInput->value() + m_fineVAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + TQColor background = paletteBackgroundColor().rgb(); + + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int orgW = iface->originalWidth(); + int orgH = iface->originalHeight(); + + uchar *data = iface->getPreviewImage(); + Digikam::DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new ShearTool(&image, this, hAngle, vAngle, antialiasing, background, orgW, orgH)); +} + +void ImageEffect_ShearTool::prepareFinal() +{ + m_mainHAngleInput->setEnabled(false); + m_mainVAngleInput->setEnabled(false); + m_fineHAngleInput->setEnabled(false); + m_fineVAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + + float hAngle = m_mainHAngleInput->value() + m_fineHAngleInput->value(); + float vAngle = m_mainVAngleInput->value() + m_fineVAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + TQColor background = TQt::black; + + Digikam::ImageIface iface(0, 0); + int orgW = iface.originalWidth(); + int orgH = iface.originalHeight(); + + uchar *data = iface.getOriginalImage(); + Digikam::DImg orgImage(orgW, orgH, iface.originalSixteenBit(), + iface.originalHasAlpha(), data); + delete [] data; + + m_threadedFilter = dynamic_cast( + new ShearTool(&orgImage, this, hAngle, vAngle, antialiasing, background, orgW, orgH)); +} + +void ImageEffect_ShearTool::putPreviewData(void) +{ + Digikam::ImageIface* iface = m_imagePreviewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + + Digikam::DImg imTemp = m_threadedFilter->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + Digikam::DImg imDest( w, h, m_threadedFilter->getTargetImage().sixteenBit(), + m_threadedFilter->getTargetImage().hasAlpha() ); + + imDest.fill( Digikam::DColor(paletteBackgroundColor().rgb(), + m_threadedFilter->getTargetImage().sixteenBit()) ); + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + + m_imagePreviewWidget->updatePreview(); + TQSize newSize = dynamic_cast(m_threadedFilter)->getNewSize(); + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); +} + +void ImageEffect_ShearTool::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + Digikam::DImg targetImage = m_threadedFilter->getTargetImage(); + iface.putOriginalImage(i18n("Shear Tool"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +void ImageEffect_ShearTool::renderingFinished() +{ + m_mainHAngleInput->setEnabled(true); + m_mainVAngleInput->setEnabled(true); + m_fineHAngleInput->setEnabled(true); + m_fineVAngleInput->setEnabled(true); + m_antialiasInput->setEnabled(true); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamShearToolImagesPlugin + diff --git a/src/imageplugins/sheartool/imageeffect_sheartool.h b/src/imageplugins/sheartool/imageeffect_sheartool.h new file mode 100644 index 00000000..311b4e0d --- /dev/null +++ b/src/imageplugins/sheartool/imageeffect_sheartool.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_SHEARTOOL_H +#define IMAGEEFFECT_SHEARTOOL_H + +// Local includes. + +#include "imageguidedlg.h" + +class TQFrame; +class TQPushButton; +class TQCheckBox; +class TQLabel; + +class KIntNumInput; +class KDoubleNumInput; + +namespace DigikamShearToolImagesPlugin +{ + +class ImageEffect_ShearTool : public Digikam::ImageGuideDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_ShearTool(TQWidget* parent); + ~ImageEffect_ShearTool(); + +private slots: + + void readUserSettings(void); + +protected: + + void writeUserSettings(void); + void prepareEffect(void); + void prepareFinal(void); + void putPreviewData(void); + void putFinalData(void); + void resetValues(void); + void renderingFinished(void); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + + TQCheckBox *m_antialiasInput; + + KIntNumInput *m_mainHAngleInput; + KIntNumInput *m_mainVAngleInput; + + KDoubleNumInput *m_fineHAngleInput; + KDoubleNumInput *m_fineVAngleInput; +}; + +} // NameSpace DigikamShearToolImagesPlugin + +#endif /* IMAGEEFFECT_SHEARTOOL_H */ diff --git a/src/imageplugins/sheartool/imageplugin_sheartool.cpp b/src/imageplugins/sheartool/imageplugin_sheartool.cpp new file mode 100644 index 00000000..34030899 --- /dev/null +++ b/src/imageplugins/sheartool/imageplugin_sheartool.cpp @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "sheartool.h" +#include "imageplugin_sheartool.h" +#include "imageplugin_sheartool.moc" + +using namespace DigikamShearToolImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_sheartool, + KGenericFactory("digikamimageplugin_sheartool")); + +ImagePlugin_ShearTool::ImagePlugin_ShearTool(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_ShearTool") +{ + m_sheartoolAction = new TDEAction(i18n("Shear..."), "shear", 0, + this, TQ_SLOT(slotShearTool()), + actionCollection(), "imageplugin_sheartool"); + + setXMLFile("digikamimageplugin_sheartool_ui.rc"); + + DDebug() << "ImagePlugin_ShearTool plugin loaded" << endl; +} + +ImagePlugin_ShearTool::~ImagePlugin_ShearTool() +{ +} + +void ImagePlugin_ShearTool::setEnabledActions(bool enable) +{ + m_sheartoolAction->setEnabled(enable); +} + +void ImagePlugin_ShearTool::slotShearTool() +{ + ShearTool *tool = new ShearTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/sheartool/imageplugin_sheartool.h b/src/imageplugins/sheartool/imageplugin_sheartool.h new file mode 100644 index 00000000..2423a1b1 --- /dev/null +++ b/src/imageplugins/sheartool/imageplugin_sheartool.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_SHEARTOOL_H +#define IMAGEPLUGIN_SHEARTOOL_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_ShearTool : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_ShearTool(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_ShearTool(); + + void setEnabledActions(bool enable); + +private slots: + + void slotShearTool(); + +private: + + TDEAction *m_sheartoolAction; +}; + +#endif /* IMAGEPLUGIN_SHEARTOOL_H */ diff --git a/src/imageplugins/sheartool/shear.cpp b/src/imageplugins/sheartool/shear.cpp new file mode 100644 index 00000000..29af5390 --- /dev/null +++ b/src/imageplugins/sheartool/shear.cpp @@ -0,0 +1,185 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Shear threaded image filter. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * Original Shear algorithms copyrighted 2005 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Degrees to radian convertion coeff (PI/180). To optimize computation. +#define DEG2RAD 0.017453292519943 + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "dimgimagefilters.h" +#include "shear.h" + +namespace DigikamShearToolImagesPlugin +{ + +Shear::Shear(Digikam::DImg *orgImage, TQObject *parent, float hAngle, float vAngle, + bool antialiasing, TQColor backgroundColor, int orgW, int orgH) + : Digikam::DImgThreadedFilter(orgImage, parent, "sheartool") +{ + m_hAngle = hAngle; + m_vAngle = vAngle; + m_orgW = orgW; + m_orgH = orgH; + m_antiAlias = antialiasing; + m_backgroundColor = backgroundColor; + + initFilter(); +} + +void Shear::filterImage(void) +{ + int progress; + int x, y, p = 0, pt; + int new_width, new_height; + double nx, ny, dx, dy; + double horz_factor, vert_factor; + double horz_add, vert_add; + double horz_beta_angle, vert_beta_angle; + + int nWidth = m_orgImage.width(); + int nHeight = m_orgImage.height(); + + uchar *pBits = m_orgImage.bits(); + unsigned short *pBits16 = (unsigned short*)m_orgImage.bits(); + + // get beta ( complementary ) angle for horizontal and vertical angles + horz_beta_angle = ( ( ( m_hAngle < 0.0 ) ? 180.0 : 90.0 ) - m_hAngle ) * DEG2RAD; + vert_beta_angle = ( ( ( m_vAngle < 0.0 ) ? 180.0 : 90.0 ) - m_vAngle ) * DEG2RAD; + + // get new distance for width and height values + horz_add = nHeight * ( ( m_hAngle < 0.0 ) ? sin( horz_beta_angle ) : cos( horz_beta_angle ) ); + vert_add = nWidth * ( ( m_vAngle < 0.0 ) ? sin( vert_beta_angle ) : cos( vert_beta_angle ) ); + + // get absolute values for the distances + horz_add = fabs( horz_add ); + vert_add = fabs( vert_add ); + + // get new image size ( original size + distance ) + new_width = (int)horz_add + nWidth; + new_height = (int)vert_add + nHeight; + + // get scale factor for width and height + horz_factor = horz_add / new_height; + vert_factor = vert_add / new_width; + + // if horizontal angle is greater than zero... + // else, initial distance is equal to maximum distance ( in negative form ) + if( m_hAngle > 0.0 ) + { + // initial distance is zero and scale is negative ( to decrease ) + dx = 0; + horz_factor *= -1.0; + } + else + { + dx = -horz_add; + } + + // if vertical angle is greater than zero... + // else, initial distance is equal to maximum distance ( in negative form ) + if( m_vAngle > 0.0 ) + { + // initial distance is zero and scale is negative ( to decrease ) + dy = 0; + vert_factor *= -1.0; + } + else + { + dy = -vert_add; + } + + // allocates a new image with the new size + + bool sixteenBit = m_orgImage.sixteenBit(); + + m_destImage = Digikam::DImg(new_width, new_height, sixteenBit, m_orgImage.hasAlpha()); + m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) ); + + uchar *pResBits = m_destImage.bits(); + unsigned short *pResBits16 = (unsigned short *)m_destImage.bits(); + + Digikam::DImgImageFilters filters; + + for( y = 0; y < new_height; y++) + { + for( x = 0; x < new_width; x++, p += 4 ) + { + // get new positions + nx = x + dx + y * horz_factor; + ny = y + dy + x * vert_factor; + + // if is inside the source image + if (isInside (nWidth, nHeight, ROUND( nx ), ROUND( ny ))) + { + if( m_antiAlias ) + { + if (!sixteenBit) + filters.pixelAntiAliasing(pBits, nWidth, nHeight, nx, ny, + &pResBits[p+3], &pResBits[p+2], + &pResBits[p+1], &pResBits[p]); + else + filters.pixelAntiAliasing16(pBits16, nWidth, nHeight, nx, ny, + &pResBits16[p+3], &pResBits16[p+2], + &pResBits16[p+1], &pResBits16[p]); + } + else + { + pt = setPosition (nWidth, ROUND( nx ), ROUND( ny )); + + for (int z = 0 ; z < 4 ; z++) + { + if (!sixteenBit) + pResBits[p+z] = pBits[pt+z]; + else + pResBits16[p+z] = pBits16[pt+z]; + } + } + } + } + + // Update the progress bar in dialog. + progress = (int)(((double)y * 100.0) / new_height); + if (progress%5 == 0) + postProgress( progress ); + } + + // To compute the rotated destination image size using original image dimensions. + int W = (int)(fabs(m_orgH * ( ( m_hAngle < 0.0 ) ? sin( horz_beta_angle ) : cos( horz_beta_angle ))))+ + m_orgW; + int H = (int)(fabs(m_orgW * ( ( m_vAngle < 0.0 ) ? sin( vert_beta_angle ) : cos( vert_beta_angle ))))+ + m_orgH; + + m_newSize.setWidth(W); + m_newSize.setHeight(H); +} + +} // NameSpace DigikamShearToolImagesPlugin diff --git a/src/imageplugins/sheartool/shear.h b/src/imageplugins/sheartool/shear.h new file mode 100644 index 00000000..480d1084 --- /dev/null +++ b/src/imageplugins/sheartool/shear.h @@ -0,0 +1,84 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-18 + * Description : Shear threaded image filter. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SHEAR_H +#define SHEAR_H + +// TQt includes. + +#include +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamShearToolImagesPlugin +{ + +class Shear : public Digikam::DImgThreadedFilter +{ + +public: + + Shear(Digikam::DImg *orgImage, TQObject *parent=0, float hAngle=0.0, float vAngle=0.0, + bool antialiasing=true, TQColor backgroundColor=TQt::black, int orgW=0, int orgH=0); + + ~Shear(){}; + + TQSize getNewSize(void){ return m_newSize; }; + +private: + + virtual void filterImage(void); + + inline int setPosition (int Width, int X, int Y) + { + return (Y *Width*4 + 4*X); + }; + + inline bool isInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; + +private: + + bool m_antiAlias; + + int m_orgW; + int m_orgH; + + float m_hAngle; + float m_vAngle; + + TQColor m_backgroundColor; + + TQSize m_newSize; +}; + +} // NameSpace DigikamShearToolImagesPlugin + +#endif /* SHEAR_H */ diff --git a/src/imageplugins/sheartool/sheartool.cpp b/src/imageplugins/sheartool/sheartool.cpp new file mode 100644 index 00000000..9f65b6d3 --- /dev/null +++ b/src/imageplugins/sheartool/sheartool.cpp @@ -0,0 +1,331 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "editortoolsettings.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "shear.h" +#include "sheartool.h" +#include "sheartool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamShearToolImagesPlugin +{ + +ShearTool::ShearTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("sheartool"); + setToolName(i18n("Shear Tool")); + setToolIcon(SmallIcon("sheartool")); + + m_previewWidget = new ImageWidget("sheartool Tool", 0, + i18n("

    This is the shear operation preview. " + "If you move the mouse cursor on this preview, " + "a vertical and horizontal dashed line will be drawn " + "to guide you in adjusting the shear correction. " + "Release the left mouse button to freeze the dashed " + "line's position."), + false, ImageGuideWidget::HVGuideMode); + + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + TQString temp; + Digikam::ImageIface iface(0, 0); + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel, + EditorToolSettings::ColorGuide); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 12, 2); + + TQLabel *label1 = new TQLabel(i18n("New width:"), m_gboxSettings->plainPage()); + m_newWidthLabel = new TQLabel(temp.setNum( iface.originalWidth()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newWidthLabel->setAlignment( AlignBottom | AlignRight ); + + TQLabel *label2 = new TQLabel(i18n("New height:"), m_gboxSettings->plainPage()); + m_newHeightLabel = new TQLabel(temp.setNum( iface.originalHeight()) + i18n(" px"), m_gboxSettings->plainPage()); + m_newHeightLabel->setAlignment( AlignBottom | AlignRight ); + + KSeparator *line = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + TQLabel *label3 = new TQLabel(i18n("Main horizontal angle:"), m_gboxSettings->plainPage()); + m_mainHAngleInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_mainHAngleInput->setRange(-45, 45, 1); + m_mainHAngleInput->setDefaultValue(0); + TQWhatsThis::add( m_mainHAngleInput, i18n("

    The main horizontal shearing angle, in degrees.")); + + TQLabel *label4 = new TQLabel(i18n("Fine horizontal angle:"), m_gboxSettings->plainPage()); + m_fineHAngleInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_fineHAngleInput->setRange(-5.0, 5.0, 0.01); + m_fineHAngleInput->setDefaultValue(0); + TQWhatsThis::add( m_fineHAngleInput, i18n("

    This value in degrees will be added to main horizontal angle value " + "to set fine adjustments.")); + + TQLabel *label5 = new TQLabel(i18n("Main vertical angle:"), m_gboxSettings->plainPage()); + m_mainVAngleInput = new RIntNumInput(m_gboxSettings->plainPage()); + m_mainVAngleInput->setRange(-45, 45, 1); + m_mainVAngleInput->setDefaultValue(0); + TQWhatsThis::add( m_mainVAngleInput, i18n("

    The main vertical shearing angle, in degrees.")); + + TQLabel *label6 = new TQLabel(i18n("Fine vertical angle:"), m_gboxSettings->plainPage()); + m_fineVAngleInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_fineVAngleInput->setRange(-5.0, 5.0, 0.01); + m_fineVAngleInput->setDefaultValue(0); + TQWhatsThis::add( m_fineVAngleInput, i18n("

    This value in degrees will be added to main vertical angle value " + "to set fine adjustments.")); + + m_antialiasInput = new TQCheckBox(i18n("Anti-Aliasing"), m_gboxSettings->plainPage()); + TQWhatsThis::add( m_antialiasInput, i18n("

    Enable this option to apply the anti-aliasing filter " + "to the sheared image. " + "To smooth the target image, it will be blurred a little.")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(m_newWidthLabel, 0, 0, 1, 2); + grid->addMultiCellWidget(label2, 1, 1, 0, 0); + grid->addMultiCellWidget(m_newHeightLabel, 1, 1, 1, 2); + grid->addMultiCellWidget(line, 2, 2, 0, 2); + grid->addMultiCellWidget(label3, 3, 3, 0, 2); + grid->addMultiCellWidget(m_mainHAngleInput, 4, 4, 0, 2); + grid->addMultiCellWidget(label4, 5, 5, 0, 2); + grid->addMultiCellWidget(m_fineHAngleInput, 6, 6, 0, 2); + grid->addMultiCellWidget(label5, 7, 7, 0, 0); + grid->addMultiCellWidget(m_mainVAngleInput, 8, 8, 0, 2); + grid->addMultiCellWidget(label6, 9, 9, 0, 2); + grid->addMultiCellWidget(m_fineVAngleInput, 10, 10, 0, 2); + grid->addMultiCellWidget(m_antialiasInput, 11, 11, 0, 2); + grid->setRowStretch(12, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_mainHAngleInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineHAngleInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_mainVAngleInput, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineVAngleInput, TQ_SIGNAL(valueChanged(double)), + this, TQ_SLOT(slotTimer())); + + connect(m_antialiasInput, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotEffect())); + + connect(m_gboxSettings, TQ_SIGNAL(signalColorGuideChanged()), + this, TQ_SLOT(slotColorGuideChanged())); +} + +ShearTool::~ShearTool() +{ +} + +void ShearTool::slotColorGuideChanged() +{ + m_previewWidget->slotChangeGuideColor(m_gboxSettings->guideColor()); + m_previewWidget->slotChangeGuideSize(m_gboxSettings->guideSize()); +} + +void ShearTool::readSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("sheartool Tool"); + m_mainHAngleInput->setValue(config->readNumEntry("Main HAngle", m_mainHAngleInput->defaultValue())); + m_mainVAngleInput->setValue(config->readNumEntry("Main VAngle", m_mainVAngleInput->defaultValue())); + m_fineHAngleInput->setValue(config->readDoubleNumEntry("Fine HAngle", m_fineHAngleInput->defaultValue())); + m_fineVAngleInput->setValue(config->readDoubleNumEntry("Fine VAngle", m_fineVAngleInput->defaultValue())); + m_antialiasInput->setChecked(config->readBoolEntry("Anti Aliasing", true)); + m_gboxSettings->setGuideColor(config->readColorEntry("Guide Color", &TQt::red)); + m_gboxSettings->setGuideSize(config->readNumEntry("Guide Width", 1)); + + slotColorGuideChanged(); + slotEffect(); +} + +void ShearTool::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup("sheartool Tool"); + config->writeEntry("Main HAngle", m_mainHAngleInput->value()); + config->writeEntry("Main VAngle", m_mainVAngleInput->value()); + config->writeEntry("Fine HAngle", m_fineHAngleInput->value()); + config->writeEntry("Fine VAngle", m_fineVAngleInput->value()); + config->writeEntry("Anti Aliasing", m_antialiasInput->isChecked()); + config->writeEntry("Guide Color", m_gboxSettings->guideColor()); + config->writeEntry("Guide Width", m_gboxSettings->guideSize()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void ShearTool::slotResetSettings() +{ + m_mainHAngleInput->blockSignals(true); + m_mainVAngleInput->blockSignals(true); + m_fineHAngleInput->blockSignals(true); + m_fineVAngleInput->blockSignals(true); + m_antialiasInput->blockSignals(true); + + m_mainHAngleInput->slotReset(); + m_mainVAngleInput->slotReset(); + m_fineHAngleInput->slotReset(); + m_fineVAngleInput->slotReset(); + m_antialiasInput->setChecked(true); + + m_mainHAngleInput->blockSignals(false); + m_mainVAngleInput->blockSignals(false); + m_fineHAngleInput->blockSignals(false); + m_fineVAngleInput->blockSignals(false); + m_antialiasInput->blockSignals(false); +} + +void ShearTool::prepareEffect() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_mainHAngleInput->setEnabled(false); + m_mainVAngleInput->setEnabled(false); + m_fineHAngleInput->setEnabled(false); + m_fineVAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + + float hAngle = m_mainHAngleInput->value() + m_fineHAngleInput->value(); + float vAngle = m_mainVAngleInput->value() + m_fineVAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + TQColor background = m_previewWidget->paletteBackgroundColor().rgb(); + ImageIface* iface = m_previewWidget->imageIface(); + int orgW = iface->originalWidth(); + int orgH = iface->originalHeight(); + uchar *data = iface->getPreviewImage(); + DImg image(iface->previewWidth(), iface->previewHeight(), iface->previewSixteenBit(), + iface->previewHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new Shear(&image, this, hAngle, vAngle, antialiasing, + background, orgW, orgH))); +} + +void ShearTool::prepareFinal() +{ + m_mainHAngleInput->setEnabled(false); + m_mainVAngleInput->setEnabled(false); + m_fineHAngleInput->setEnabled(false); + m_fineVAngleInput->setEnabled(false); + m_antialiasInput->setEnabled(false); + + float hAngle = m_mainHAngleInput->value() + m_fineHAngleInput->value(); + float vAngle = m_mainVAngleInput->value() + m_fineVAngleInput->value(); + bool antialiasing = m_antialiasInput->isChecked(); + TQColor background = TQt::black; + + ImageIface iface(0, 0); + int orgW = iface.originalWidth(); + int orgH = iface.originalHeight(); + + uchar *data = iface.getOriginalImage(); + DImg orgImage(orgW, orgH, iface.originalSixteenBit(), iface.originalHasAlpha(), data); + delete [] data; + + setFilter(dynamic_cast(new Shear(&orgImage, this, hAngle, vAngle, antialiasing, + background, orgW, orgH))); +} + +void ShearTool::putPreviewData() +{ + ImageIface* iface = m_previewWidget->imageIface(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + DImg imTemp = filter()->getTargetImage().smoothScale(w, h, TQSize::ScaleMin); + DImg imDest( w, h, filter()->getTargetImage().sixteenBit(), + filter()->getTargetImage().hasAlpha() ); + + imDest.fill(DColor(m_previewWidget->paletteBackgroundColor().rgb(), + filter()->getTargetImage().sixteenBit()) ); + imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2); + + iface->putPreviewImage((imDest.smoothScale(iface->previewWidth(), + iface->previewHeight())).bits()); + + m_previewWidget->updatePreview(); + TQSize newSize = dynamic_cast(filter())->getNewSize(); + TQString temp; + m_newWidthLabel->setText(temp.setNum( newSize.width()) + i18n(" px") ); + m_newHeightLabel->setText(temp.setNum( newSize.height()) + i18n(" px") ); +} + +void ShearTool::putFinalData() +{ + ImageIface iface(0, 0); + DImg targetImage = filter()->getTargetImage(); + iface.putOriginalImage(i18n("Shear Tool"), + targetImage.bits(), + targetImage.width(), targetImage.height()); +} + +void ShearTool::renderingFinished() +{ + m_mainHAngleInput->setEnabled(true); + m_mainVAngleInput->setEnabled(true); + m_fineHAngleInput->setEnabled(true); + m_fineVAngleInput->setEnabled(true); + m_antialiasInput->setEnabled(true); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamShearToolImagesPlugin diff --git a/src/imageplugins/sheartool/sheartool.h b/src/imageplugins/sheartool/sheartool.h new file mode 100644 index 00000000..1c8161b4 --- /dev/null +++ b/src/imageplugins/sheartool/sheartool.h @@ -0,0 +1,96 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-23 + * Description : a plugin to shear an image + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SHEARTOOL_H +#define SHEARTOOL_H + +// Local includes. + +#include "editortool.h" + +class TQFrame; +class TQPushButton; +class TQCheckBox; +class TQLabel; + +namespace KDcrawIface +{ +class RIntNumInput; +class RDoubleNumInput; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImageWidget; +} + +namespace DigikamShearToolImagesPlugin +{ + +class ShearTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + ShearTool(TQObject* parent); + ~ShearTool(); + +private slots: + + void slotResetSettings(); + void slotColorGuideChanged(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + TQLabel *m_newWidthLabel; + TQLabel *m_newHeightLabel; + + TQCheckBox *m_antialiasInput; + + KDcrawIface::RIntNumInput *m_mainHAngleInput; + KDcrawIface::RIntNumInput *m_mainVAngleInput; + + KDcrawIface::RDoubleNumInput *m_fineHAngleInput; + KDcrawIface::RDoubleNumInput *m_fineVAngleInput; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamShearToolImagesPlugin + +#endif /* SHEARTOOL_H */ diff --git a/src/imageplugins/superimpose/Makefile.am b/src/imageplugins/superimpose/Makefile.am new file mode 100644 index 00000000..686eff2a --- /dev/null +++ b/src/imageplugins/superimpose/Makefile.am @@ -0,0 +1,35 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/thumbbar \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_superimpose_la_SOURCES = superimposewidget.cpp superimpose.cpp dirselectwidget.cpp \ + imageplugin_superimpose.cpp superimposetool.cpp + + +digikamimageplugin_superimpose_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_superimpose_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_superimpose.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_superimpose.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_superimpose_ui.rc diff --git a/src/imageplugins/superimpose/digikamimageplugin_superimpose.desktop b/src/imageplugins/superimpose/digikamimageplugin_superimpose.desktop new file mode 100644 index 00000000..ef395a4a --- /dev/null +++ b/src/imageplugins/superimpose/digikamimageplugin_superimpose.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_SuperImpose +Name[bg]=ПриÑтавка за Ñнимки - Ðалагане +Name[da]=Billedplugin_Indkopiering +Name[el]=ΠÏόσθετοΕικόνας_ΥπεÏέκθεση +Name[fi]=SuperImpose +Name[hr]=PresvlaÄenje +Name[it]=PluginImmagini_Sovrapposizione +Name[nl]=Afbeeldingsplugin_SjabloonAanbrengen +Name[sr]=Ðадоградња +Name[sr@Latn]=Nadogradnja +Name[sv]=Insticksprogram för överlagring +Name[tr]=ResimEklentisi_Büyüt +Name[xx]=xxImagePlugin_SuperImposexx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=Template superimpose plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за налагане на Ñнимки една върху друга +Comment[ca]=Connector pel digiKam per sobreimposar una plantilla +Comment[da]=Plugin til skabelon-indkopiering i Digikam +Comment[de]=digiKam-Modul zum Anwenden von Schablonen auf ein Bild +Comment[el]=ΠÏόσθετο Ï€Ïότυπης υπεÏέκθεσης για το digiKam +Comment[es]=Plugin de digiKam para superponer una plantilla sobre la imagen +Comment[et]=DigiKami pildile malli lisamise plugin +Comment[fa]=وصلۀ اÙزودن قالب برای digiKam +Comment[fi]=Liittää useampia kuvia päällekäin +Comment[gl]=Un plugin de digiKam para sobrepor modelos +Comment[hr]=digiKam dodatak za presvlaÄenje predloÅ¡kom +Comment[is]=Ãforrit fyrir digiKam sem hleður forsniðinni mynd ofan á þá sem unnið er með +Comment[it]=Plugin di sovrapposizione di modelli per digiKam +Comment[ja]=digiKam テンプレートé‡ã­åˆã‚ã›ãƒ—ラグイン +Comment[ms]=Templat plugin superimpose untuk digiKam +Comment[nds]=digiKam-Moduul för't Vörblennen vun Vörlagen +Comment[nl]=Digikam-plugin voor het aanbrengen van een sjabloon op de afbeelding +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਨਮੂਨਾ ਸà©à¨ªà¨°-ਇਮਪੋਜ਼ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam umożliwiajÄ…ca naÅ‚ożenie szablonu na obraz +Comment[pt]=Um 'plugin' do digiKam para sobrepor modelos +Comment[pt_BR]=Um 'plugin' do digiKam para sobrepor modelos +Comment[ru]=Модуль Ð½Ð°ÐºÐ»Ð°Ð´Ñ‹Ð²Ð°Ð½Ð¸Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð° Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin pre prekrytie fotografie Å¡ablónou +Comment[sr]=digiKam-ов прикључак надоградњу шаблонима +Comment[sr@Latn]=digiKam-ov prikljuÄak nadogradnju Å¡ablonima +Comment[sv]=Digikam insticksprogram för överlagringsmall +Comment[tr]=digiKam için ÅŸablon büyütme eklentisi +Comment[uk]=Втулок Ð½Ð°ÐºÐ»Ð°Ð´Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñƒ Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung đặt biểu mẫu trên cho digiKam +Comment[xx]=xxTemplate superimpose plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_superimpose +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/superimpose/digikamimageplugin_superimpose_ui.rc b/src/imageplugins/superimpose/digikamimageplugin_superimpose_ui.rc new file mode 100644 index 00000000..ced7edac --- /dev/null +++ b/src/imageplugins/superimpose/digikamimageplugin_superimpose_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Decorate + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/superimpose/dirselectwidget.cpp b/src/imageplugins/superimpose/dirselectwidget.cpp new file mode 100644 index 00000000..4856d73c --- /dev/null +++ b/src/imageplugins/superimpose/dirselectwidget.cpp @@ -0,0 +1,182 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dirselectwidget.h" +#include "dirselectwidget.moc" + +namespace DigikamSuperImposeImagesPlugin +{ + +struct DirSelectWidget::Private +{ + KFileTreeBranch* m_item; + TQStringList m_pendingPath; + TQString m_handled; + KURL m_rootUrl; +}; + +DirSelectWidget::DirSelectWidget(TQWidget* parent, const char* name, TQString headerLabel) + : KFileTreeView( parent, name) +{ + d = new Private; + + addColumn( headerLabel ); + + if ( headerLabel.isNull() ) + header()->hide(); + + setAlternateBackground(TQColor()); +} + +DirSelectWidget::DirSelectWidget(KURL rootUrl, KURL currentUrl, + TQWidget* parent, const char* name, TQString headerLabel) + : KFileTreeView( parent, name) +{ + d = new Private; + + addColumn( headerLabel ); + + if ( headerLabel.isNull() ) + header()->hide(); + + setAlternateBackground(TQColor()); + setRootPath(rootUrl, currentUrl); +} + +DirSelectWidget::~DirSelectWidget() +{ + delete d; +} + +KURL DirSelectWidget::path() const +{ + return currentURL(); +} + +void DirSelectWidget::load() +{ + if ( d->m_pendingPath.isEmpty() ) + { + disconnect( d->m_item, TQ_SIGNAL( populateFinished(KFileTreeViewItem *) ), + this, TQ_SLOT( load() ) ); + + emit folderItemSelected(currentURL()); + return; + } + + TQString item = d->m_pendingPath.front(); + d->m_pendingPath.pop_front(); + d->m_handled += item; + KFileTreeViewItem* branch = findItem( d->m_item, d->m_handled ); + + if ( !branch ) + { + DDebug() << "Unable to open " << d->m_handled << endl; + } + else + { + branch->setOpen( true ); + setSelected( branch, true ); + ensureItemVisible ( branch ); + d->m_handled += '/'; + + if ( branch->alreadyListed() ) + load(); + } +} + +void DirSelectWidget::setCurrentPath(KURL currentUrl) +{ + if ( !currentUrl.isValid() ) + return; + + TQString currentPath = TQDir::cleanDirPath(currentUrl.path()); + currentPath = currentPath.mid( d->m_rootUrl.path().length() ); + d->m_pendingPath.clear(); + d->m_handled = TQString(""); + d->m_pendingPath = TQStringList::split( "/", currentPath, true ); + + if ( !d->m_pendingPath[0].isEmpty() ) + d->m_pendingPath.prepend( "" ); // ensure we open the root first. + + connect( d->m_item, TQ_SIGNAL( populateFinished(KFileTreeViewItem *) ), + this, TQ_SLOT( load() ) ); + load(); +} + +void DirSelectWidget::setRootPath(KURL rootUrl, KURL currentUrl) +{ + d->m_rootUrl = rootUrl; + clear(); + TQString root = TQDir::cleanDirPath(rootUrl.path()); + + if ( !root.endsWith("/")) + root.append("/"); + + TQString currentPath = TQDir::cleanDirPath(currentUrl.isValid() ? currentUrl.path() : root); + + d->m_item = addBranch( rootUrl, rootUrl.fileName() ); + setDirOnlyMode( d->m_item, true ); + currentPath = currentPath.mid( root.length() ); + d->m_pendingPath = TQStringList::split( "/", currentPath, true ); + + if ( !d->m_pendingPath[0].isEmpty() ) + d->m_pendingPath.prepend( "" ); // ensure we open the root first. + + connect( d->m_item, TQ_SIGNAL( populateFinished(KFileTreeViewItem *) ), + this, TQ_SLOT( load() ) ); + + load(); + + connect( this, TQ_SIGNAL( executed(TQListViewItem *) ), + this, TQ_SLOT( slotFolderSelected(TQListViewItem *) ) ); +} + +KURL DirSelectWidget::rootPath(void) +{ + return d->m_rootUrl; +} + +void DirSelectWidget::slotFolderSelected(TQListViewItem *) +{ + emit folderItemSelected(currentURL()); +} + +} // NameSpace DigikamSuperImposeImagesPlugin + diff --git a/src/imageplugins/superimpose/dirselectwidget.h b/src/imageplugins/superimpose/dirselectwidget.h new file mode 100644 index 00000000..1ee4abc2 --- /dev/null +++ b/src/imageplugins/superimpose/dirselectwidget.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIRSELECTWIDGET_H +#define DIRSELECTWIDGET_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include + +namespace DigikamSuperImposeImagesPlugin +{ + +class DirSelectWidget : public KFileTreeView +{ +TQ_OBJECT + + +public: + + DirSelectWidget(TQWidget* parent, const char* name=0, TQString headerLabel=TQString()); + + DirSelectWidget(KURL rootUrl=KURL("/"), KURL currentUrl=KURL(), + TQWidget* parent=0, const char* name=0, TQString headerLabel=TQString()); + + ~DirSelectWidget(); + + KURL path() const; + KURL rootPath(void); + void setRootPath(KURL rootUrl, KURL currentUrl=KURL(TQString())); + void setCurrentPath(KURL currentUrl); + +signals : + + void folderItemSelected(const KURL &url); + +protected slots: + + void load(); + void slotFolderSelected(TQListViewItem *); + +private: + + struct Private; + Private* d; +}; + +} // NameSpace DigikamSuperImposeImagesPlugin + +#endif /* DIRSELECTWIDGET_H */ diff --git a/src/imageplugins/superimpose/imageeffect_superimpose.cpp b/src/imageplugins/superimpose/imageeffect_superimpose.cpp new file mode 100644 index 00000000..1cea3e08 --- /dev/null +++ b/src/imageplugins/superimpose/imageeffect_superimpose.cpp @@ -0,0 +1,280 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "thumbbar.h" +#include "superimposewidget.h" +#include "dirselectwidget.h" +#include "imageeffect_superimpose.h" +#include "imageeffect_superimpose.moc" + +namespace DigikamSuperImposeImagesPlugin +{ + +ImageEffect_SuperImpose::ImageEffect_SuperImpose(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("Template Superimpose to Photograph"), + "superimpose", false, false) +{ + TQString whatsThis; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Template Superimpose"), + digikam_version, + I18N_NOOP("A digiKam image plugin to superimpose a template onto a photograph."), + TDEAboutData::License_GPL, + "(c) 2005-2006, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(plainPage()); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + + TQGridLayout* gridFrame = new TQGridLayout( frame, 1, 2, spacingHint()); + m_previewWidget = new SuperImposeWidget(400, 300, frame); + gridFrame->addMultiCellWidget(m_previewWidget, 0, 0, 0, 2); + gridFrame->setRowStretch(0, 10); + TQWhatsThis::add( m_previewWidget, i18n("

    This is the preview of the template " + "superimposed onto the image.") ); + + // ------------------------------------------------------------- + + TQHButtonGroup *bGroup = new TQHButtonGroup(frame); + TDEIconLoader icon; + bGroup->addSpace(0); + TQPushButton *zoomInButton = new TQPushButton( bGroup ); + bGroup->insert(zoomInButton, ZOOMIN); + zoomInButton->setPixmap( icon.loadIcon( "zoom-in", (TDEIcon::Group)TDEIcon::Toolbar ) ); + zoomInButton->setToggleButton(true); + TQToolTip::add( zoomInButton, i18n( "Zoom in" ) ); + bGroup->addSpace(20); + TQPushButton *zoomOutButton = new TQPushButton( bGroup ); + bGroup->insert(zoomOutButton, ZOOMOUT); + zoomOutButton->setPixmap( icon.loadIcon( "zoom-out", (TDEIcon::Group)TDEIcon::Toolbar ) ); + zoomOutButton->setToggleButton(true); + TQToolTip::add( zoomOutButton, i18n( "Zoom out" ) ); + bGroup->addSpace(20); + TQPushButton *moveButton = new TQPushButton( bGroup ); + bGroup->insert(moveButton, MOVE); + moveButton->setPixmap( icon.loadIcon( "move", (TDEIcon::Group)TDEIcon::Toolbar ) ); + moveButton->setToggleButton(true); + moveButton->setOn(true); + TQToolTip::add( moveButton, i18n( "Move" ) ); + bGroup->addSpace(20); + bGroup->setExclusive(true); + bGroup->setFrameShape(TQFrame::NoFrame); + gridFrame->addMultiCellWidget(bGroup, 1, 1, 1, 1); + gridFrame->setColStretch(0, 10); + gridFrame->setColStretch(2, 10); + + setPreviewAreaWidget(frame); + + // ------------------------------------------------------------- + + TQWidget *gbox2 = new TQWidget(plainPage()); + TQGridLayout* grid = new TQGridLayout( gbox2, 1, 1, marginHint(), spacingHint()); + + m_thumbnailsBar = new Digikam::ThumbBarView(gbox2); + m_dirSelect = new DirSelectWidget(gbox2); + TQPushButton *templateDirButton = new TQPushButton( i18n("Root Directory..."), gbox2 ); + TQWhatsThis::add( templateDirButton, i18n("

    Set here the current templates' root directory.") ); + + grid->addMultiCellWidget(m_thumbnailsBar, 0, 1, 0, 0); + grid->addMultiCellWidget(m_dirSelect, 0, 0, 1, 1); + grid->addMultiCellWidget(templateDirButton, 1, 1, 1, 1); + grid->setColStretch(1, 10); + + setUserAreaWidget(gbox2); + + // ------------------------------------------------------------- + + connect(bGroup, TQ_SIGNAL(released(int)), + m_previewWidget, TQ_SLOT(slotEditModeChanged(int))); + + connect(m_thumbnailsBar, TQ_SIGNAL(signalURLSelected(const KURL&)), + m_previewWidget, TQ_SLOT(slotSetCurrentTemplate(const KURL&))); + + connect(m_dirSelect, TQ_SIGNAL(folderItemSelected(const KURL &)), + this, TQ_SLOT(slotTemplateDirChanged(const KURL &))); + + connect(templateDirButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRootTemplateDirChanged())); + + // ------------------------------------------------------------- + + populateTemplates(); +} + +ImageEffect_SuperImpose::~ImageEffect_SuperImpose() +{ +} + +void ImageEffect_SuperImpose::populateTemplates(void) +{ + m_thumbnailsBar->clear(true); + + if (!m_templatesUrl.isValid() || !m_templatesUrl.isLocalFile()) + return; + + TQDir dir(m_templatesUrl.path(), "*.png *.PNG"); + + if (!dir.exists()) + return; + + dir.setFilter ( TQDir::Files | TQDir::NoSymLinks ); + + const TQFileInfoList* fileinfolist = dir.entryInfoList(); + if (!fileinfolist) + return; + + TQFileInfoListIterator it(*fileinfolist); + TQFileInfo* fi; + + while( (fi = it.current() ) ) + { + new Digikam::ThumbBarItem( m_thumbnailsBar, KURL(fi->filePath()) ); + ++it; + } +} + +void ImageEffect_SuperImpose::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Album Settings"); + KURL albumDBUrl( config->readPathEntry("Album Path", TDEGlobalSettings::documentPath()) ); + config->setGroup("superimpose Tool Dialog"); + config->setGroup("Template Superimpose Tool Settings"); + m_templatesRootUrl.setPath( config->readEntry("Templates Root URL", albumDBUrl.path()) ); + m_templatesUrl.setPath( config->readEntry("Templates URL", albumDBUrl.path()) ); + m_dirSelect->setRootPath(m_templatesRootUrl, m_templatesUrl); +} + +void ImageEffect_SuperImpose::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("superimpose Tool Dialog"); + config->writeEntry( "Templates Root URL", m_dirSelect->rootPath().path() ); + config->writeEntry( "Templates URL", m_templatesUrl.path() ); + config->sync();} + +void ImageEffect_SuperImpose::resetValues() +{ + m_previewWidget->resetEdit(); +} + +void ImageEffect_SuperImpose::slotRootTemplateDirChanged(void) +{ + KURL url = KFileDialog::getExistingDirectory(m_templatesRootUrl.path(), kapp->activeWindow(), + i18n("Select Template Root Directory to Use")); + + if( url.isValid() ) + { + m_dirSelect->setRootPath(url); + m_templatesRootUrl = url; + m_templatesUrl = url; + populateTemplates(); + } +} + +void ImageEffect_SuperImpose::slotTemplateDirChanged(const KURL& url) +{ + if( url.isValid() ) + { + m_templatesUrl = url; + populateTemplates(); + } +} + +void ImageEffect_SuperImpose::finalRendering() +{ + setCursor(KCursor::waitCursor()); + m_previewWidget->setEnabled(false); + m_dirSelect->setEnabled(false); + m_thumbnailsBar->setEnabled(false); + + Digikam::ImageIface iface(0, 0); + Digikam::DImg img = m_previewWidget->makeSuperImpose(); + iface.putOriginalImage(i18n("Super Impose"), img.bits(), + img.width(), img.height() ); + + m_previewWidget->setEnabled(true); + m_dirSelect->setEnabled(true); + m_thumbnailsBar->setEnabled(true); + unsetCursor(); + accept(); +} + +} // NameSpace DigikamSuperImposeImagesPlugin + diff --git a/src/imageplugins/superimpose/imageeffect_superimpose.h b/src/imageplugins/superimpose/imageeffect_superimpose.h new file mode 100644 index 00000000..8eb8c18f --- /dev/null +++ b/src/imageplugins/superimpose/imageeffect_superimpose.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_SUPERIMPOSE_H +#define IMAGEEFFECT_SUPERIMPOSE_H + +// KDE include. + +#include + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQPushButton; + +namespace Digikam +{ +class ThumbBarView; +} + +namespace DigikamSuperImposeImagesPlugin +{ + +class DirSelectWidget; +class SuperImposeWidget; + +class ImageEffect_SuperImpose : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_SuperImpose(TQWidget* parent); + ~ImageEffect_SuperImpose(); + +private slots: + + void slotTemplateDirChanged(const KURL& url); + void slotRootTemplateDirChanged(void); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + void populateTemplates(void); + void finalRendering(); + +private: + + KURL m_templatesUrl; + KURL m_templatesRootUrl; + + Digikam::ThumbBarView *m_thumbnailsBar; + + SuperImposeWidget *m_previewWidget; + + DirSelectWidget *m_dirSelect; +}; + +} // NameSpace DigikamSuperImposeImagesPlugin + +#endif /* IMAGEEFFECT_SUPERIMPOSE_H */ diff --git a/src/imageplugins/superimpose/imageplugin_superimpose.cpp b/src/imageplugins/superimpose/imageplugin_superimpose.cpp new file mode 100644 index 00000000..ceba44ef --- /dev/null +++ b/src/imageplugins/superimpose/imageplugin_superimpose.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "superimposetool.h" +#include "imageplugin_superimpose.h" +#include "imageplugin_superimpose.moc" + +using namespace DigikamSuperImposeImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_superimpose, + KGenericFactory("digikamimageplugin_superimpose")); + +ImagePlugin_SuperImpose::ImagePlugin_SuperImpose(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_SuperImpose") +{ + m_superimposeAction = new TDEAction(i18n("Template Superimpose..."), "superimpose", 0, + this, TQ_SLOT(slotSuperImpose()), + actionCollection(), "imageplugin_superimpose"); + + setXMLFile("digikamimageplugin_superimpose_ui.rc"); + + DDebug() << "ImagePlugin_SuperImpose plugin loaded" << endl; +} + +ImagePlugin_SuperImpose::~ImagePlugin_SuperImpose() +{ +} + +void ImagePlugin_SuperImpose::setEnabledActions(bool enable) +{ + m_superimposeAction->setEnabled(enable); +} + +void ImagePlugin_SuperImpose::slotSuperImpose() +{ + SuperImposeTool *tool = new SuperImposeTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/superimpose/imageplugin_superimpose.h b/src/imageplugins/superimpose/imageplugin_superimpose.h new file mode 100644 index 00000000..6f3c3a69 --- /dev/null +++ b/src/imageplugins/superimpose/imageplugin_superimpose.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_SUPERIMPOSE_H +#define IMAGEPLUGIN_SUPERIMPOSE_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_SuperImpose : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_SuperImpose(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_SuperImpose(); + + void setEnabledActions(bool enable); + +private slots: + + void slotSuperImpose(); + +private: + + TDEAction *m_superimposeAction; +}; + +#endif /* IMAGEPLUGIN_SUPERIMPOSE_H */ diff --git a/src/imageplugins/superimpose/superimpose.cpp b/src/imageplugins/superimpose/superimpose.cpp new file mode 100644 index 00000000..7c0ac74b --- /dev/null +++ b/src/imageplugins/superimpose/superimpose.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-18-03 + * Description : Superimpose filter. + * + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Local includes. + +#include "superimpose.h" + +namespace DigikamSuperImposeImagesPlugin +{ + +SuperImpose::SuperImpose(Digikam::DImg *orgImage, Digikam::DImg *templ, + TQRect orgImageSelection, + Digikam::DColorComposer::CompositingOperation compositeRule) +{ + m_orgImage = *orgImage; + m_template = *templ; + m_selection = orgImageSelection; + m_compositeRule = compositeRule; + + filterImage(); +} + +void SuperImpose::filterImage(void) +{ + if (m_template.isNull()) + return; + + int templateWidth = m_template.width(); + int templateHeight = m_template.height(); + + // take selection of src image and scale it to size of template + m_destImage = m_orgImage.smoothScaleSection(m_selection.x(), m_selection.y(), + m_selection.width(), m_selection.height(), templateWidth, templateHeight); + + // convert depth if necessary + m_template.convertToDepthOfImage(&m_destImage); + + // get composer for compositing rule + Digikam::DColorComposer *composer = Digikam::DColorComposer::getComposer(m_compositeRule); + Digikam::DColorComposer::MultiplicationFlags flags = Digikam::DColorComposer::NoMultiplication; + if (m_compositeRule != Digikam::DColorComposer::PorterDuffNone) + flags = Digikam::DColorComposer::MultiplicationFlagsDImg; + + // do alpha blending of template on dest image + m_destImage.bitBlendImage(composer, &m_template, 0, 0, templateWidth, templateHeight, 0, 0, flags); + + delete composer; +} + +} // namespace DigikamSuperImposeImagesPlugin diff --git a/src/imageplugins/superimpose/superimpose.h b/src/imageplugins/superimpose/superimpose.h new file mode 100644 index 00000000..a0b7b6da --- /dev/null +++ b/src/imageplugins/superimpose/superimpose.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-18-03 + * Description : Superimpose filter. + * + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SUPERIMPOSE_H +#define SUPERIMPOSE_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "dimg.h" +#include "dcolor.h" + +namespace DigikamSuperImposeImagesPlugin +{ + +class SuperImpose +{ + +public: + + SuperImpose(Digikam::DImg *orgImage, Digikam::DImg *templ, + TQRect orgImageSelection, + Digikam::DColorComposer::CompositingOperation + compositeRule = Digikam::DColorComposer::PorterDuffNone); + + Digikam::DImg getTargetImage() { return m_destImage; } + +private: + + void filterImage(void); + +private: + + TQRect m_selection; + + Digikam::DImg m_orgImage; + Digikam::DImg m_template; + Digikam::DImg m_destImage; + Digikam::DColorComposer::CompositingOperation m_compositeRule; +}; + +} // namespace DigikamSuperImposeImagesPlugin + +#endif /* SUPERIMPOSE_H */ diff --git a/src/imageplugins/superimpose/superimposetool.cpp b/src/imageplugins/superimpose/superimposetool.cpp new file mode 100644 index 00000000..f035559c --- /dev/null +++ b/src/imageplugins/superimpose/superimposetool.cpp @@ -0,0 +1,271 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "editortoolsettings.h" +#include "thumbbar.h" +#include "superimposewidget.h" +#include "dirselectwidget.h" +#include "superimposetool.h" +#include "superimposetool.moc" + +using namespace Digikam; + +namespace DigikamSuperImposeImagesPlugin +{ + +SuperImposeTool::SuperImposeTool(TQObject* parent) + : EditorTool(parent) +{ + setName("superimpose"); + setToolName(i18n("Template Superimpose")); + setToolIcon(SmallIcon("superimpose")); + + // ------------------------------------------------------------- + + TQFrame *frame = new TQFrame(0); + frame->setFrameStyle(TQFrame::Panel|TQFrame::Sunken); + + TQGridLayout* gridFrame = new TQGridLayout(frame, 1, 2); + m_previewWidget = new SuperImposeWidget(400, 300, frame); + TQWhatsThis::add( m_previewWidget, i18n("

    This is the preview of the template " + "superimposed onto the image.") ); + + // ------------------------------------------------------------- + + TQHButtonGroup *bGroup = new TQHButtonGroup(frame); + TDEIconLoader icon; + bGroup->addSpace(0); + TQPushButton *zoomInButton = new TQPushButton( bGroup ); + bGroup->insert(zoomInButton, ZOOMIN); + zoomInButton->setPixmap( icon.loadIcon( "zoom-in", (TDEIcon::Group)TDEIcon::Toolbar ) ); + zoomInButton->setToggleButton(true); + TQToolTip::add( zoomInButton, i18n( "Zoom in" ) ); + bGroup->addSpace(20); + TQPushButton *zoomOutButton = new TQPushButton( bGroup ); + bGroup->insert(zoomOutButton, ZOOMOUT); + zoomOutButton->setPixmap( icon.loadIcon( "zoom-out", (TDEIcon::Group)TDEIcon::Toolbar ) ); + zoomOutButton->setToggleButton(true); + TQToolTip::add( zoomOutButton, i18n( "Zoom out" ) ); + bGroup->addSpace(20); + TQPushButton *moveButton = new TQPushButton( bGroup ); + bGroup->insert(moveButton, MOVE); + moveButton->setPixmap( icon.loadIcon( "move", (TDEIcon::Group)TDEIcon::Toolbar ) ); + moveButton->setToggleButton(true); + moveButton->setOn(true); + TQToolTip::add( moveButton, i18n( "Move" ) ); + bGroup->addSpace(20); + bGroup->setExclusive(true); + bGroup->setFrameShape(TQFrame::NoFrame); + + gridFrame->addMultiCellWidget(m_previewWidget, 0, 0, 0, 2); + gridFrame->addMultiCellWidget(bGroup, 1, 1, 1, 1); + gridFrame->setRowStretch(0, 10); + gridFrame->setColStretch(0, 10); + gridFrame->setColStretch(2, 10); + gridFrame->setMargin(0); + gridFrame->setSpacing(0); + + setToolView(frame); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + TQGridLayout* grid = new TQGridLayout(m_gboxSettings->plainPage(), 1, 1); + + m_thumbnailsBar = new ThumbBarView(m_gboxSettings->plainPage()); + m_dirSelect = new DirSelectWidget(m_gboxSettings->plainPage()); + TQPushButton *templateDirButton = new TQPushButton( i18n("Root Directory..."), m_gboxSettings->plainPage() ); + TQWhatsThis::add( templateDirButton, i18n("

    Set here the current templates' root directory.") ); + + grid->addMultiCellWidget(m_thumbnailsBar, 0, 1, 0, 0); + grid->addMultiCellWidget(m_dirSelect, 0, 0, 1, 1); + grid->addMultiCellWidget(templateDirButton, 1, 1, 1, 1); + grid->setMargin(0); + grid->setSpacing(m_gboxSettings->spacingHint()); + grid->setColStretch(1, 10); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(bGroup, TQ_SIGNAL(released(int)), + m_previewWidget, TQ_SLOT(slotEditModeChanged(int))); + + connect(m_thumbnailsBar, TQ_SIGNAL(signalURLSelected(const KURL&)), + m_previewWidget, TQ_SLOT(slotSetCurrentTemplate(const KURL&))); + + connect(m_dirSelect, TQ_SIGNAL(folderItemSelected(const KURL &)), + this, TQ_SLOT(slotTemplateDirChanged(const KURL &))); + + connect(templateDirButton, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRootTemplateDirChanged())); + + // ------------------------------------------------------------- + + populateTemplates(); +} + +SuperImposeTool::~SuperImposeTool() +{ +} + +void SuperImposeTool::populateTemplates() +{ + m_thumbnailsBar->clear(true); + + if (!m_templatesUrl.isValid() || !m_templatesUrl.isLocalFile()) + return; + + TQDir dir(m_templatesUrl.path(), "*.png *.PNG"); + + if (!dir.exists()) + return; + + dir.setFilter ( TQDir::Files | TQDir::NoSymLinks ); + + const TQFileInfoList* fileinfolist = dir.entryInfoList(); + if (!fileinfolist) + return; + + TQFileInfoListIterator it(*fileinfolist); + TQFileInfo* fi; + + while( (fi = it.current() ) ) + { + new ThumbBarItem( m_thumbnailsBar, KURL(fi->filePath()) ); + ++it; + } +} + +void SuperImposeTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Album Settings"); + KURL albumDBUrl( config->readPathEntry("Album Path", TDEGlobalSettings::documentPath()) ); + config->setGroup("superimpose Tool"); + config->setGroup("Template Superimpose Tool Settings"); + m_templatesRootUrl.setPath( config->readEntry("Templates Root URL", albumDBUrl.path()) ); + m_templatesUrl.setPath( config->readEntry("Templates URL", albumDBUrl.path()) ); + m_dirSelect->setRootPath(m_templatesRootUrl, m_templatesUrl); +} + +void SuperImposeTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("superimpose Tool"); + config->writeEntry( "Templates Root URL", m_dirSelect->rootPath().path() ); + config->writeEntry( "Templates URL", m_templatesUrl.path() ); + config->sync(); +} + +void SuperImposeTool::slotResetSettings() +{ + m_previewWidget->resetEdit(); +} + +void SuperImposeTool::slotRootTemplateDirChanged() +{ + KURL url = KFileDialog::getExistingDirectory(m_templatesRootUrl.path(), kapp->activeWindow(), + i18n("Select Template Root Directory to Use")); + + if( url.isValid() ) + { + m_dirSelect->setRootPath(url); + m_templatesRootUrl = url; + m_templatesUrl = url; + populateTemplates(); + } +} + +void SuperImposeTool::slotTemplateDirChanged(const KURL& url) +{ + if( url.isValid() ) + { + m_templatesUrl = url; + populateTemplates(); + } +} + +void SuperImposeTool::finalRendering() +{ + kapp->setOverrideCursor(KCursor::waitCursor()); + m_previewWidget->setEnabled(false); + m_dirSelect->setEnabled(false); + m_thumbnailsBar->setEnabled(false); + + ImageIface iface(0, 0); + DImg img = m_previewWidget->makeSuperImpose(); + iface.putOriginalImage(i18n("Super Impose"), img.bits(), + img.width(), img.height() ); + + m_previewWidget->setEnabled(true); + m_dirSelect->setEnabled(true); + m_thumbnailsBar->setEnabled(true); + kapp->restoreOverrideCursor(); +} + +} // NameSpace DigikamSuperImposeImagesPlugin diff --git a/src/imageplugins/superimpose/superimposetool.h b/src/imageplugins/superimpose/superimposetool.h new file mode 100644 index 00000000..7501f23f --- /dev/null +++ b/src/imageplugins/superimpose/superimposetool.h @@ -0,0 +1,90 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_SUPERIMPOSE_H +#define IMAGEEFFECT_SUPERIMPOSE_H + +// KDE includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQPushButton; + +namespace Digikam +{ +class ThumbBarView; +class EditorToolSettings; +} + +namespace DigikamSuperImposeImagesPlugin +{ + +class DirSelectWidget; +class SuperImposeWidget; + +class SuperImposeTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + SuperImposeTool(TQObject* parent); + ~SuperImposeTool(); + +private slots: + + void slotTemplateDirChanged(const KURL& url); + void slotRootTemplateDirChanged(); + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void populateTemplates(); + void finalRendering(); + +private: + + KURL m_templatesUrl; + KURL m_templatesRootUrl; + + Digikam::ThumbBarView *m_thumbnailsBar; + + Digikam::EditorToolSettings *m_gboxSettings; + + SuperImposeWidget *m_previewWidget; + + DirSelectWidget *m_dirSelect; +}; + +} // NameSpace DigikamSuperImposeImagesPlugin + +#endif /* IMAGEEFFECT_SUPERIMPOSE_H */ diff --git a/src/imageplugins/superimpose/superimposewidget.cpp b/src/imageplugins/superimpose/superimposewidget.cpp new file mode 100644 index 00000000..edae15aa --- /dev/null +++ b/src/imageplugins/superimpose/superimposewidget.cpp @@ -0,0 +1,329 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "superimpose.h" +#include "superimposewidget.h" +#include "superimposewidget.moc" + +namespace DigikamSuperImposeImagesPlugin +{ + +SuperImposeWidget::SuperImposeWidget(int w, int h, TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + m_pixmap = new TQPixmap(w, h); + m_editMode = MOVE; + + Digikam::ImageIface iface(0, 0); + m_w = iface.originalWidth(); + m_h = iface.originalHeight(); + + setBackgroundMode(TQt::NoBackground); + setMinimumSize(w, h); + setMouseTracking(true); + + resetEdit(); +} + +SuperImposeWidget::~SuperImposeWidget() +{ + if(m_pixmap) + delete m_pixmap; +} + +Digikam::DImg SuperImposeWidget::makeSuperImpose(void) +{ + Digikam::ImageIface iface(0, 0); + SuperImpose superimpose(iface.getOriginalImg(), &m_template, m_currentSelection); + return superimpose.getTargetImage(); +} + +void SuperImposeWidget::resetEdit(void) +{ + m_zoomFactor = 1.0; + m_currentSelection = TQRect(m_w/2 - m_rect.width()/2, m_h/2 - m_rect.height()/2, + m_rect.width(), m_rect.height()); + makePixmap(); + repaint(false); +} + +void SuperImposeWidget::makePixmap(void) +{ + Digikam::ImageIface iface(0, 0); + SuperImpose superimpose(iface.getOriginalImg(), &m_templateScaled, m_currentSelection); + Digikam::DImg image = superimpose.getTargetImage(); + + m_pixmap->fill(colorGroup().background()); + TQPainter p(m_pixmap); + TQPixmap imagePix = image.convertToPixmap(); + p.drawPixmap(m_rect.x(), m_rect.y(), imagePix, 0, 0, m_rect.width(), m_rect.height()); + p.end(); +} + +void SuperImposeWidget::resizeEvent(TQResizeEvent * e) +{ + blockSignals(true); + delete m_pixmap; + int w = e->size().width(); + int h = e->size().height(); + m_pixmap = new TQPixmap(w, h); + + if (!m_template.isNull()) + { + int templateWidth = m_template.width(); + int templateHeight = m_template.height(); + + if (templateWidth < templateHeight) + { + int neww = (int) ((float)height() / (float)templateHeight * (float)templateWidth); + m_rect = TQRect(width()/2-neww/2, 0, neww, height()); + } + else + { + int newh = (int) ((float)width() / (float)templateWidth * (float)templateHeight); + m_rect = TQRect(0, height()/2-newh/2, width(), newh); + } + + m_templateScaled = m_template.smoothScale(m_rect.width(), m_rect.height()); + makePixmap(); + } + else + { + m_rect = TQRect(); + m_pixmap->fill(colorGroup().background()); + } + + blockSignals(false); +} + +void SuperImposeWidget::paintEvent( TQPaintEvent * ) +{ + bitBlt(this, 0, 0, m_pixmap); +} + +void SuperImposeWidget::slotEditModeChanged(int mode) +{ + m_editMode = mode; +} + +void SuperImposeWidget::slotSetCurrentTemplate(const KURL& url) +{ + m_template.load(url.path()); + + if (m_template.isNull()) + { + m_rect = TQRect(); + return; + } + + int templateWidth = m_template.width(); + int templateHeight = m_template.height(); + + if (templateWidth < templateHeight) + { + int neww = (int) ((float)height() / (float)templateHeight * (float)templateWidth); + m_rect = TQRect(width()/2-neww/2, 0, neww, height()); + } + else + { + int newh = (int) ((float)width() / (float)templateWidth * (float)templateHeight); + m_rect = TQRect(0, height()/2-newh/2, width(), newh); + } + + m_templateScaled = m_template.smoothScale(m_rect.width(), m_rect.height()); + + m_currentSelection = TQRect(m_w/2 - m_rect.width()/2, m_h/2 - m_rect.height()/2, m_rect.width(), m_rect.height()); + zoomSelection(0); +} + +void SuperImposeWidget::moveSelection(int dx, int dy) +{ + TQRect selection = m_currentSelection; + float wf = (float)selection.width() / (float)m_rect.width(); + float hf = (float)selection.height() / (float)m_rect.height(); + + selection.moveBy( -(int)(wf*(float)dx), -(int)(hf*(float)dy) ); + + if (selection.left() < 0) + selection.moveLeft(0); + if (selection.top() < 0) + selection.moveTop(0); + if (selection.bottom() > m_h) + selection.moveBottom(m_h); + if (selection.right() > m_w) + selection.moveRight(m_w); + + m_currentSelection = selection; +} + +bool SuperImposeWidget::zoomSelection(float deltaZoomFactor) +{ + float newZoom = m_zoomFactor + deltaZoomFactor; + + if (newZoom < 0.0) + return false; + + TQRect selection = m_currentSelection; + int wf = (int)((float)m_rect.width() / newZoom); + int hf = (int)((float)m_rect.height() / newZoom); + int deltaX = (m_currentSelection.width() - wf) / 2; + int deltaY = (m_currentSelection.height() - hf) / 2; + + selection.setLeft(m_currentSelection.left() + deltaX); + selection.setTop(m_currentSelection.top() + deltaY); + selection.setWidth(wf); + selection.setHeight(hf); + + // check that selection is still inside original image + TQRect orgImageRect(0, 0, m_w, m_h); + if (!orgImageRect.contains(selection)) + { + // try to adjust + if (selection.left() < 0) + selection.moveLeft(0); + if (selection.top() < 0) + selection.moveTop(0); + if (selection.bottom() > m_h) + selection.moveBottom(m_h); + if (selection.right() > m_w) + selection.moveRight(m_w); + + // was it successful? + if (selection.contains(orgImageRect)) + return false; + + } + + m_zoomFactor = newZoom; + m_currentSelection = selection; + + makePixmap(); + repaint(false); + + return true; +} + +void SuperImposeWidget::mousePressEvent ( TQMouseEvent * e ) +{ + if ( isEnabled() && e->button() == TQt::LeftButton && + rect().contains( e->x(), e->y() ) ) + { + switch (m_editMode) + { + case ZOOMIN: + if (zoomSelection(+0.05)) + moveSelection(width()/2 - e->x(), height()/2 - e->y()); + break; + + case ZOOMOUT: + if (zoomSelection(-0.05)) + moveSelection(width()/2 - e->x(), height()/2 - e->y()); + break; + + case MOVE: + m_xpos = e->x(); + m_ypos = e->y(); + } + } +} + +void SuperImposeWidget::mouseReleaseEvent ( TQMouseEvent * ) +{ + setEditModeCursor(); +} + +void SuperImposeWidget::mouseMoveEvent ( TQMouseEvent * e ) +{ + if ( isEnabled() ) + { + if ( e->state() == TQt::LeftButton ) + { + switch (m_editMode) + { + case ZOOMIN: + case ZOOMOUT: + break; + + case MOVE: + int newxpos = e->x(); + int newypos = e->y(); + + if (newxpos < m_rect.left()) + newxpos = m_rect.left(); + if (newxpos > m_rect.right()) + newxpos = m_rect.right(); + if (newxpos < m_rect.top()) + newxpos = m_rect.top(); + if (newxpos > m_rect.bottom()) + newxpos = m_rect.bottom(); + + moveSelection(newxpos - m_xpos, newypos - m_ypos); + makePixmap(); + repaint(false); + + m_xpos = newxpos; + m_ypos = newypos; + setCursor( KCursor::handCursor() ); + break; + } + } + else if (rect().contains( e->x(), e->y() )) + { + setEditModeCursor(); + } + } +} + +void SuperImposeWidget::setEditModeCursor() +{ + switch (m_editMode) + { + case ZOOMIN: + case ZOOMOUT: + setCursor ( KCursor::crossCursor() ); + break; + + case MOVE: + setCursor ( KCursor::sizeAllCursor() ); + } +} + +} // NameSpace DigikamSuperImposeImagesPlugin diff --git a/src/imageplugins/superimpose/superimposewidget.h b/src/imageplugins/superimpose/superimposewidget.h new file mode 100644 index 00000000..8b3fb048 --- /dev/null +++ b/src/imageplugins/superimpose/superimposewidget.h @@ -0,0 +1,118 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-01-04 + * Description : a Digikam image editor plugin for superimpose a + * template to an image. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef SUPERIMPOSEWIDGET_H +#define SUPERIMPOSEWIDGET_H + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "imageiface.h" +#include "dimg.h" + +class TQPixmap; + +namespace Digikam +{ +class ImageIface; +} + +namespace DigikamSuperImposeImagesPlugin +{ + +enum Action +{ + ZOOMIN=0, + ZOOMOUT, + MOVE +}; + +class SuperImposeWidget : public TQWidget +{ +TQ_OBJECT + + +public: + + SuperImposeWidget(int w, int h, TQWidget *parent=0); + ~SuperImposeWidget(); + + void setEditMode(int mode); + void resetEdit(void); + TQRect getCurrentSelection(void); + TQSize getTemplateSize(void); + Digikam::DImg makeSuperImpose(void); + +public slots: + + void slotEditModeChanged(int mode); + void slotSetCurrentTemplate(const KURL& url); + +protected: + + void paintEvent( TQPaintEvent *e ); + void resizeEvent( TQResizeEvent * e ); + void mousePressEvent ( TQMouseEvent * e ); + void mouseReleaseEvent ( TQMouseEvent * e ); + void mouseMoveEvent ( TQMouseEvent * e ); + + bool zoomSelection(float deltaZoomFactor); + void moveSelection(int x, int y); + void makePixmap(void); + void setEditModeCursor(); + +private: + + int m_w; + int m_h; + + int m_xpos; + int m_ypos; + int m_editMode; + float m_zoomFactor; + + TQPixmap *m_pixmap; // For image region selection manipulations. + + TQRect m_rect; // For mouse drag position. + TQRect m_currentSelection; // Region selection in image displayed in the widget. + + Digikam::DImg m_template; // Full template data. + Digikam::DImg m_templateScaled; // Template scaled to preview widget +}; + +} // NameSpace DigikamSuperImposeImagesPlugin + +#endif /* SUPERIMPOSEWIDGET_H */ diff --git a/src/imageplugins/texture/Makefile.am b/src/imageplugins/texture/Makefile.am new file mode 100644 index 00000000..22697c65 --- /dev/null +++ b/src/imageplugins/texture/Makefile.am @@ -0,0 +1,35 @@ +METASOURCES = AUTO +SUBDIRS = patterns + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_texture_la_SOURCES = imageplugin_texture.cpp \ + texturetool.cpp texture.cpp + +digikamimageplugin_texture_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_texture_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_texture.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_texture.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_texture_ui.rc + diff --git a/src/imageplugins/texture/digikamimageplugin_texture.desktop b/src/imageplugins/texture/digikamimageplugin_texture.desktop new file mode 100644 index 00000000..4caf7e41 --- /dev/null +++ b/src/imageplugins/texture/digikamimageplugin_texture.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=ImagePlugin_Texture +Name[bg]=ПриÑтавка за Ñнимки - ТекÑтури +Name[el]=ΠÏόσθετοΕικόνας_Υφή +Name[fi]=Taustapinta +Name[hr]=Tekstura +Name[it]=PluginImmagini_Trama +Name[ms]=ImagePlugin_Tekstur +Name[nl]=Afbeeldingsplugin_Textuur +Name[sr]=ТекÑтуре +Name[sr@Latn]=Teksture +Name[sv]=Insticksprogram för struktur +Name[tr]=ResimEklentisi_Doku +Name[xx]=xxImagePlugin_Texturexx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=digiKam plugin to apply texture on image +Comment[bg]=ПриÑтавка на digiKam за добавÑне на текÑтури към Ñнимки +Comment[ca]=Connector pel digiKam per aplicar una textura a una imatge +Comment[da]=digiKam-plugin til at anvende tekstur pÃ¥ billedet +Comment[de]=digiKam-Modul für das Anwenden von Texturen auf ein Bild +Comment[el]=ΠÏόσθετο εφαÏμογής υφής σε εικόνα για το digiKam +Comment[es]=Plugin para digiKam para aplicar una textura sobre una imagen +Comment[et]=DigiKami pildile tekstuuri lisamise plugin +Comment[fa]=وصلۀ digiKam جهت اعمال باÙت در تصویر +Comment[fi]=Lisää kuvaan taustapinnan +Comment[fr]=Module externe pour appliquer une texture sur une image dans digiKam +Comment[gl]=Un plugin de digiKam para aplicar unha textura nunha imaxe +Comment[hr]=digiKam dodatak za primjenu teksture u slici +Comment[is]=Ãforrit fyrir digiKam sem setur áferð á mynd +Comment[it]=Plugin di digiKam per applicare una trama su un'immagine +Comment[ja]=digiKam テクスãƒãƒ£é©ç”¨ãƒ—ラグイン +Comment[nds]=digiKam-Moduul för't Överdregen vun Texturen +Comment[nl]=Digikam-plugin voor het aanbrengen van textuur op een afbeelding +Comment[pa]=ਚਿੱਤਰ ਉੱਤੇ ਪਾਠ ਲਿਖਣ ਲਈ ਡਿਜ਼ੀਕੈਮ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam pozwalajÄ…ca na nakÅ‚adanie tekstury na obraz +Comment[pt]=Um 'plugin' do digiKam para aplicar uma textura numa imagem +Comment[pt_BR]=Um 'plugin' do digiKam para aplicar uma textura numa imagem +Comment[ru]=Модуль digiKam Ð´Ð»Ñ Ð½Ð°Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñ‚ÐµÐºÑтур на изображение +Comment[sk]=digiKam plugin pre aplikovanie textúry na obrázok +Comment[sr]=digiKam-ов прикључак за додавање текÑтура у Ñлику +Comment[sr@Latn]=digiKam-ov prikljuÄak za dodavanje tekstura u sliku +Comment[sv]=Digikam insticksprogram för att lägga till struktur pÃ¥ en bild +Comment[tr]=Bir resme doku eklemek için digiKam eklentisi +Comment[uk]=Втулок заÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚ÐµÐºÑтури Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung áp dụng hoạ tiết trên ảnh cho digiKam +Comment[xx]=xxdigiKam plugin to apply texture on imagexx + +X-TDE-Library=digikamimageplugin_texture +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/texture/digikamimageplugin_texture_ui.rc b/src/imageplugins/texture/digikamimageplugin_texture_ui.rc new file mode 100644 index 00000000..122833cc --- /dev/null +++ b/src/imageplugins/texture/digikamimageplugin_texture_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Decorate + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/texture/imageeffect_texture.cpp b/src/imageplugins/texture/imageeffect_texture.cpp new file mode 100644 index 00000000..5f5d7e9f --- /dev/null +++ b/src/imageplugins/texture/imageeffect_texture.cpp @@ -0,0 +1,291 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "texture.h" +#include "imageeffect_texture.h" +#include "imageeffect_texture.moc" + +namespace DigikamTextureImagesPlugin +{ + +ImageEffect_Texture::ImageEffect_Texture(TQWidget* parent) + : Digikam::CtrlPanelDlg(parent, i18n("Apply Texture"), + "texture", false, false, true, + Digikam::ImagePannelWidget::SeparateViewAll) +{ + TQString whatsThis; + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("Apply Texture"), + digikam_version, + I18N_NOOP("A digiKam image plugin to apply a decorative " + "texture to an image."), + TDEAboutData::License_GPL, + "(c) 2005, Gilles Caulier\n" + "(c) 2006-2008, Gilles Caulier and Marcel Wiesweg", + 0, + "http://www.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Marcel Wiesweg", I18N_NOOP("Developer"), + "marcel dot wiesweg at gmx dot de"); + + setAboutData(about); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(m_imagePreviewWidget); + TQGridLayout* gridSettings = new TQGridLayout( gboxSettings, 2, 1, 0, spacingHint()); + TQLabel *label1 = new TQLabel(i18n("Type:"), gboxSettings); + + m_textureType = new TQComboBox( false, gboxSettings ); + m_textureType->insertItem( i18n("Paper") ); + m_textureType->insertItem( i18n("Paper 2") ); + m_textureType->insertItem( i18n("Fabric") ); + m_textureType->insertItem( i18n("Burlap") ); + m_textureType->insertItem( i18n("Bricks") ); + m_textureType->insertItem( i18n("Bricks 2") ); + m_textureType->insertItem( i18n("Canvas") ); + m_textureType->insertItem( i18n("Marble") ); + m_textureType->insertItem( i18n("Marble 2") ); + m_textureType->insertItem( i18n("Blue Jean") ); + m_textureType->insertItem( i18n("Cell Wood") ); + m_textureType->insertItem( i18n("Metal Wire") ); + m_textureType->insertItem( i18n("Modern") ); + m_textureType->insertItem( i18n("Wall") ); + m_textureType->insertItem( i18n("Moss") ); + m_textureType->insertItem( i18n("Stone") ); + TQWhatsThis::add( m_textureType, i18n("

    Set here the texture type to apply to the image.")); + + gridSettings->addMultiCellWidget(label1, 0, 0, 0, 0); + gridSettings->addMultiCellWidget(m_textureType, 0, 0, 1, 1); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Relief:"), gboxSettings); + + m_blendGain = new KIntNumInput(gboxSettings); + m_blendGain->setRange(1, 255, 1, true); + m_blendGain->setValue(200); + TQWhatsThis::add( m_blendGain, i18n("

    Set here the relief gain used to merge texture and image.")); + + gridSettings->addMultiCellWidget(label2, 1, 1, 0, 1); + gridSettings->addMultiCellWidget(m_blendGain, 2, 2, 0, 1); + + m_imagePreviewWidget->setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_textureType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_blendGain, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +ImageEffect_Texture::~ImageEffect_Texture() +{ +} + +void ImageEffect_Texture::renderingFinished() +{ + m_textureType->setEnabled(true); + m_blendGain->setEnabled(true); +} + +void ImageEffect_Texture::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("texture Tool Dialog"); + m_textureType->blockSignals(true); + m_blendGain->blockSignals(true); + m_textureType->setCurrentItem(config->readNumEntry("TextureType", PaperTexture)); + m_blendGain->setValue(config->readNumEntry("BlendGain", 200)); + m_textureType->blockSignals(false); + m_blendGain->blockSignals(false); +} + +void ImageEffect_Texture::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("texture Tool Dialog"); + config->writeEntry("TextureType", m_textureType->currentItem()); + config->writeEntry("BlendGain", m_blendGain->value()); + config->sync(); +} + +void ImageEffect_Texture::resetValues() +{ + m_textureType->blockSignals(true); + m_blendGain->blockSignals(true); + m_textureType->setCurrentItem(PaperTexture); + m_blendGain->setValue(200); + m_textureType->blockSignals(false); + m_blendGain->blockSignals(false); +} + +void ImageEffect_Texture::prepareEffect() +{ + m_textureType->setEnabled(false); + m_blendGain->setEnabled(false); + + Digikam::DImg image = m_imagePreviewWidget->getOriginalRegionImage(); + TQString texture = getTexturePath( m_textureType->currentItem() ); + + int b = 255 - m_blendGain->value(); + + m_threadedFilter = dynamic_cast( + new Texture(&image, this, b, texture)); +} + +void ImageEffect_Texture::prepareFinal() +{ + m_textureType->setEnabled(false); + m_blendGain->setEnabled(false); + + int b = 255 - m_blendGain->value(); + + Digikam::ImageIface iface(0, 0); + TQString texture = getTexturePath( m_textureType->currentItem() ); + + m_threadedFilter = dynamic_cast( + new Texture(iface.getOriginalImg(), this, b, texture)); +} + +void ImageEffect_Texture::putPreviewData(void) +{ + m_imagePreviewWidget->setPreviewImage(m_threadedFilter->getTargetImage()); +} + +void ImageEffect_Texture::putFinalData(void) +{ + Digikam::ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Texture"), m_threadedFilter->getTargetImage().bits()); +} + +TQString ImageEffect_Texture::getTexturePath(int texture) +{ + TQString pattern; + + switch (texture) + { + case PaperTexture: + pattern = "paper-texture"; + break; + + case Paper2Texture: + pattern = "paper2-texture"; + break; + + case FabricTexture: + pattern = "fabric-texture"; + break; + + case BurlapTexture: + pattern = "burlap-texture"; + break; + + case BricksTexture: + pattern = "bricks-texture"; + break; + + case Bricks2Texture: + pattern = "bricks2-texture"; + break; + + case CanvasTexture: + pattern = "canvas-texture"; + break; + + case MarbleTexture: + pattern = "marble-texture"; + break; + + case Marble2Texture: + pattern = "marble2-texture"; + break; + + case BlueJeanTexture: + pattern = "bluejean-texture"; + break; + + case CellWoodTexture: + pattern = "cellwood-texture"; + break; + + case MetalWireTexture: + pattern = "metalwire-texture"; + break; + + case ModernTexture: + pattern = "modern-texture"; + break; + + case WallTexture: + pattern = "wall-texture"; + break; + + case MossTexture: + pattern = "moss-texture"; + break; + + case StoneTexture: + pattern = "stone-texture"; + break; + } + + TDEGlobal::dirs()->addResourceType(pattern.ascii(), TDEGlobal::dirs()->kde_default("data") + + "digikam/data"); + return (TDEGlobal::dirs()->findResourceDir(pattern.ascii(), pattern + ".png") + pattern + ".png" ); +} + +} // NameSpace DigikamTextureImagesPlugin + diff --git a/src/imageplugins/texture/imageeffect_texture.h b/src/imageplugins/texture/imageeffect_texture.h new file mode 100644 index 00000000..d37068b6 --- /dev/null +++ b/src/imageplugins/texture/imageeffect_texture.h @@ -0,0 +1,100 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_TEXTURE_H +#define IMAGEEFFECT_TEXTURE_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "ctrlpaneldlg.h" + +class TQComboBox; + +class KIntNumInput; + +namespace DigikamTextureImagesPlugin +{ + +class ImageEffect_Texture : public Digikam::CtrlPanelDlg +{ + TQ_OBJECT + + +public: + + ImageEffect_Texture(TQWidget* parent); + ~ImageEffect_Texture(); + +private: + + TQString getTexturePath(int texture); + +private slots: + + void readUserSettings(); + +private: + + void writeUserSettings(); + void resetValues(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum TextureTypes + { + PaperTexture=0, + Paper2Texture, + FabricTexture, + BurlapTexture, + BricksTexture, + Bricks2Texture, + CanvasTexture, + MarbleTexture, + Marble2Texture, + BlueJeanTexture, + CellWoodTexture, + MetalWireTexture, + ModernTexture, + WallTexture, + MossTexture, + StoneTexture + }; + + TQComboBox *m_textureType; + + KIntNumInput *m_blendGain; +}; + +} // NameSpace DigikamTextureImagesPlugin + +#endif /* IMAGEEFFECT_TEXTURE_H */ diff --git a/src/imageplugins/texture/imageplugin_texture.cpp b/src/imageplugins/texture/imageplugin_texture.cpp new file mode 100644 index 00000000..ec0e3ba8 --- /dev/null +++ b/src/imageplugins/texture/imageplugin_texture.cpp @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "texturetool.h" +#include "imageplugin_texture.h" +#include "imageplugin_texture.moc" + +using namespace DigikamTextureImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_texture, + KGenericFactory("digikamimageplugin_texture")); + +ImagePlugin_Texture::ImagePlugin_Texture(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_Texture") +{ + m_textureAction = new TDEAction(i18n("Apply Texture..."), "texture", 0, + this, TQ_SLOT(slotTexture()), + actionCollection(), "imageplugin_texture"); + + setXMLFile( "digikamimageplugin_texture_ui.rc" ); + + DDebug() << "ImagePlugin_Texture plugin loaded" << endl; +} + +ImagePlugin_Texture::~ImagePlugin_Texture() +{ +} + +void ImagePlugin_Texture::setEnabledActions(bool enable) +{ + m_textureAction->setEnabled(enable); +} + +void ImagePlugin_Texture::slotTexture() +{ + TextureTool *tool = new TextureTool(this); + loadTool(tool); +} diff --git a/src/imageplugins/texture/imageplugin_texture.h b/src/imageplugins/texture/imageplugin_texture.h new file mode 100644 index 00000000..bae2ba5e --- /dev/null +++ b/src/imageplugins/texture/imageplugin_texture.h @@ -0,0 +1,56 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_TEXTURE_H +#define IMAGEPLUGIN_TEXTURE_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_Texture : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_Texture(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_Texture(); + + void setEnabledActions(bool enable); + +private slots: + + void slotTexture(); + +private: + + TDEAction *m_textureAction; +}; + +#endif /* IMAGEPLUGIN_TEXTURE_H */ diff --git a/src/imageplugins/texture/patterns/Makefile.am b/src/imageplugins/texture/patterns/Makefile.am new file mode 100644 index 00000000..414c7f20 --- /dev/null +++ b/src/imageplugins/texture/patterns/Makefile.am @@ -0,0 +1,5 @@ +borderpicsdir = $(kde_datadir)/digikam/data +borderpics_DATA = bricks2-texture.png bricks-texture.png burlap-texture.png canvas-texture.png \ + marble2-texture.png marble-texture.png paper-texture.png stone-texture.png \ + fabric-texture.png paper2-texture.png bluejean-texture.png cellwood-texture.png \ + metalwire-texture.png modern-texture.png wall-texture.png moss-texture.png \ No newline at end of file diff --git a/src/imageplugins/texture/patterns/bluejean-texture.png b/src/imageplugins/texture/patterns/bluejean-texture.png new file mode 100644 index 00000000..3885d60b Binary files /dev/null and b/src/imageplugins/texture/patterns/bluejean-texture.png differ diff --git a/src/imageplugins/texture/patterns/bricks-texture.png b/src/imageplugins/texture/patterns/bricks-texture.png new file mode 100644 index 00000000..0ebfa881 Binary files /dev/null and b/src/imageplugins/texture/patterns/bricks-texture.png differ diff --git a/src/imageplugins/texture/patterns/bricks2-texture.png b/src/imageplugins/texture/patterns/bricks2-texture.png new file mode 100644 index 00000000..985c5e84 Binary files /dev/null and b/src/imageplugins/texture/patterns/bricks2-texture.png differ diff --git a/src/imageplugins/texture/patterns/burlap-texture.png b/src/imageplugins/texture/patterns/burlap-texture.png new file mode 100644 index 00000000..04494770 Binary files /dev/null and b/src/imageplugins/texture/patterns/burlap-texture.png differ diff --git a/src/imageplugins/texture/patterns/canvas-texture.png b/src/imageplugins/texture/patterns/canvas-texture.png new file mode 100644 index 00000000..6aa3df8e Binary files /dev/null and b/src/imageplugins/texture/patterns/canvas-texture.png differ diff --git a/src/imageplugins/texture/patterns/cellwood-texture.png b/src/imageplugins/texture/patterns/cellwood-texture.png new file mode 100644 index 00000000..b94f618c Binary files /dev/null and b/src/imageplugins/texture/patterns/cellwood-texture.png differ diff --git a/src/imageplugins/texture/patterns/fabric-texture.png b/src/imageplugins/texture/patterns/fabric-texture.png new file mode 100644 index 00000000..ac88eb74 Binary files /dev/null and b/src/imageplugins/texture/patterns/fabric-texture.png differ diff --git a/src/imageplugins/texture/patterns/marble-texture.png b/src/imageplugins/texture/patterns/marble-texture.png new file mode 100644 index 00000000..c22ab4ed Binary files /dev/null and b/src/imageplugins/texture/patterns/marble-texture.png differ diff --git a/src/imageplugins/texture/patterns/marble2-texture.png b/src/imageplugins/texture/patterns/marble2-texture.png new file mode 100644 index 00000000..45b77f05 Binary files /dev/null and b/src/imageplugins/texture/patterns/marble2-texture.png differ diff --git a/src/imageplugins/texture/patterns/metalwire-texture.png b/src/imageplugins/texture/patterns/metalwire-texture.png new file mode 100644 index 00000000..cf0b402c Binary files /dev/null and b/src/imageplugins/texture/patterns/metalwire-texture.png differ diff --git a/src/imageplugins/texture/patterns/modern-texture.png b/src/imageplugins/texture/patterns/modern-texture.png new file mode 100644 index 00000000..45ed8b7a Binary files /dev/null and b/src/imageplugins/texture/patterns/modern-texture.png differ diff --git a/src/imageplugins/texture/patterns/moss-texture.png b/src/imageplugins/texture/patterns/moss-texture.png new file mode 100644 index 00000000..9403189f Binary files /dev/null and b/src/imageplugins/texture/patterns/moss-texture.png differ diff --git a/src/imageplugins/texture/patterns/paper-texture.png b/src/imageplugins/texture/patterns/paper-texture.png new file mode 100644 index 00000000..c9c90205 Binary files /dev/null and b/src/imageplugins/texture/patterns/paper-texture.png differ diff --git a/src/imageplugins/texture/patterns/paper2-texture.png b/src/imageplugins/texture/patterns/paper2-texture.png new file mode 100644 index 00000000..36817ffd Binary files /dev/null and b/src/imageplugins/texture/patterns/paper2-texture.png differ diff --git a/src/imageplugins/texture/patterns/stone-texture.png b/src/imageplugins/texture/patterns/stone-texture.png new file mode 100644 index 00000000..6f26b474 Binary files /dev/null and b/src/imageplugins/texture/patterns/stone-texture.png differ diff --git a/src/imageplugins/texture/patterns/wall-texture.png b/src/imageplugins/texture/patterns/wall-texture.png new file mode 100644 index 00000000..d7322500 Binary files /dev/null and b/src/imageplugins/texture/patterns/wall-texture.png differ diff --git a/src/imageplugins/texture/texture.cpp b/src/imageplugins/texture/texture.cpp new file mode 100644 index 00000000..42477d5d --- /dev/null +++ b/src/imageplugins/texture/texture.cpp @@ -0,0 +1,181 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Texture threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "texture.h" + +namespace DigikamTextureImagesPlugin +{ + +Texture::Texture(Digikam::DImg *orgImage, TQObject *parent, int blendGain, TQString texturePath) + : Digikam::DImgThreadedFilter(orgImage, parent, "Texture") +{ + m_blendGain = blendGain; + m_texturePath = texturePath; + + initFilter(); +} + +// This method is based on the Simulate Texture Film tutorial from GimpGuru.org web site +// available at this url : http://www.gimpguru.org/Tutorials/SimulatedTexture/ + +//#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ( ( (t >> 8) + t ) >> 8)) + +inline static int intMult8(uint a, uint b) +{ + uint t = a * b + 0x80; + return ((t >> 8) + t) >> 8; +} + +inline static int intMult16(uint a, uint b) +{ + uint t = a * b + 0x8000; + return ((t >> 16) + t) >> 16; +} + +void Texture::filterImage(void) +{ + // Texture tile. + + int w = m_orgImage.width(); + int h = m_orgImage.height(); + int bytesDepth = m_orgImage.bytesDepth(); + bool sixteenBit = m_orgImage.sixteenBit(); + + DDebug() << "Texture File: " << m_texturePath << endl; + Digikam::DImg texture(m_texturePath); + if ( texture.isNull() ) return; + + Digikam::DImg textureImg(w, h, m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + + texture.convertToDepthOfImage(&textureImg); + + for (int x = 0 ; x < w ; x+=texture.width()) + for (int y = 0 ; y < h ; y+=texture.height()) + textureImg.bitBltImage(&texture, x, y); + + // Apply texture. + + uchar* data = m_orgImage.bits(); + uchar* pTeData = textureImg.bits(); + uchar* pOutBits = m_destImage.bits(); + uint offset; + + Digikam::DColor teData, transData, inData, outData; + uchar *ptr, *dptr, *tptr; + int progress; + + int blendGain; + if (sixteenBit) + blendGain = (m_blendGain + 1) * 256 - 1; + else + blendGain = m_blendGain; + + // Make textured transparent layout. + + for (int x = 0; !m_cancel && x < w; x++) + { + for (int y = 0; !m_cancel && y < h; y++) + { + offset = x*bytesDepth + (y*w*bytesDepth); + ptr = data + offset; + tptr = pTeData + offset; + + // Read color + teData.setColor(tptr, sixteenBit); + + // in the old algorithm, this was + //teData.channel.red = (teData.channel.red * (255 - m_blendGain) + + // transData.channel.red * m_blendGain) >> 8; + // but transdata was uninitialized, its components were apparently 0, + // so I removed the part after the "+". + + if (sixteenBit) + { + teData.blendInvAlpha16(blendGain); + } + else + { + teData.blendInvAlpha8(blendGain); + } + + // Overwrite RGB. + teData.setPixel(tptr); + } + + // Update progress bar in dialog. + progress = (int) (((double)x * 50.0) / w); + + if (progress%5 == 0) + postProgress(progress); + } + + // Merge layout and image using overlay method. + + for (int x = 0; !m_cancel && x < w; x++) + { + for (int y = 0; !m_cancel && y < h; y++) + { + offset = x*bytesDepth + (y*w*bytesDepth); + ptr = data + offset; + dptr = pOutBits + offset; + tptr = pTeData + offset; + + inData.setColor (ptr, sixteenBit); + outData.setColor(dptr, sixteenBit); + teData.setColor (tptr, sixteenBit); + + if (sixteenBit) + { + outData.setRed ( intMult16 (inData.red(), inData.red() + intMult16(2 * teData.red(), 65535 - inData.red()) ) ); + outData.setGreen( intMult16 (inData.green(), inData.green() + intMult16(2 * teData.green(), 65535 - inData.green()) ) ); + outData.setBlue ( intMult16 (inData.blue(), inData.blue() + intMult16(2 * teData.blue(), 65535 - inData.blue()) ) ); + } + else + { + outData.setRed ( intMult8 (inData.red(), inData.red() + intMult8(2 * teData.red(), 255 - inData.red()) ) ); + outData.setGreen( intMult8 (inData.green(), inData.green() + intMult8(2 * teData.green(), 255 - inData.green()) ) ); + outData.setBlue ( intMult8 (inData.blue(), inData.blue() + intMult8(2 * teData.blue(), 255 - inData.blue()) ) ); + } + outData.setAlpha( inData.alpha() ); + outData.setPixel( dptr ); + } + + // Update progress bar in dialog. + progress = (int) (50.0 + ((double)x * 50.0) / w); + + if (progress%5 == 0) + postProgress(progress); + } +} + +} // NameSpace DigikamTextureImagesPlugin diff --git a/src/imageplugins/texture/texture.h b/src/imageplugins/texture/texture.h new file mode 100644 index 00000000..1bcda2ff --- /dev/null +++ b/src/imageplugins/texture/texture.h @@ -0,0 +1,62 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : Texture threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TEXTURE_H +#define TEXTURE_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "dimgthreadedfilter.h" + +namespace DigikamTextureImagesPlugin +{ + +class Texture : public Digikam::DImgThreadedFilter +{ + +public: + + Texture(Digikam::DImg *orgImage, TQObject *parent=0, int blendGain=200, + TQString texturePath=TQString()); + + ~Texture(){}; + +private: + + virtual void filterImage(void); + +private: + + int m_blendGain; + + TQString m_texturePath; +}; + +} // NameSpace DigikamTextureImagesPlugin + +#endif /* TEXTURE_H */ diff --git a/src/imageplugins/texture/texturetool.cpp b/src/imageplugins/texture/texturetool.cpp new file mode 100644 index 00000000..71e29aea --- /dev/null +++ b/src/imageplugins/texture/texturetool.cpp @@ -0,0 +1,294 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "imageiface.h" +#include "imagepanelwidget.h" +#include "editortoolsettings.h" +#include "texture.h" +#include "texturetool.h" +#include "texturetool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamTextureImagesPlugin +{ + +TextureTool::TextureTool(TQObject* parent) + : EditorToolThreaded(parent) +{ + setName("texture"); + setToolName(i18n("Texture")); + setToolIcon(SmallIcon("texture")); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Ok| + EditorToolSettings::Cancel| + EditorToolSettings::PanIcon); + TQGridLayout* grid = new TQGridLayout( m_gboxSettings->plainPage(), 3, 1); + TQLabel *label1 = new TQLabel(i18n("Type:"), m_gboxSettings->plainPage()); + + m_textureType = new RComboBox(m_gboxSettings->plainPage()); + m_textureType->insertItem(i18n("Paper")); + m_textureType->insertItem(i18n("Paper 2")); + m_textureType->insertItem(i18n("Fabric")); + m_textureType->insertItem(i18n("Burlap")); + m_textureType->insertItem(i18n("Bricks")); + m_textureType->insertItem(i18n("Bricks 2")); + m_textureType->insertItem(i18n("Canvas")); + m_textureType->insertItem(i18n("Marble")); + m_textureType->insertItem(i18n("Marble 2")); + m_textureType->insertItem(i18n("Blue Jean")); + m_textureType->insertItem(i18n("Cell Wood")); + m_textureType->insertItem(i18n("Metal Wire")); + m_textureType->insertItem(i18n("Modern")); + m_textureType->insertItem(i18n("Wall")); + m_textureType->insertItem(i18n("Moss")); + m_textureType->insertItem(i18n("Stone")); + m_textureType->setDefaultItem(PaperTexture); + TQWhatsThis::add( m_textureType, i18n("

    Set here the texture type to apply to the image.")); + + // ------------------------------------------------------------- + + TQLabel *label2 = new TQLabel(i18n("Relief:"), m_gboxSettings->plainPage()); + + m_blendGain = new RIntNumInput(m_gboxSettings->plainPage()); + m_blendGain->setRange(1, 255, 1); + m_blendGain->setDefaultValue(200); + TQWhatsThis::add( m_blendGain, i18n("

    Set here the relief gain used to merge texture and image.")); + + grid->addMultiCellWidget(label1, 0, 0, 0, 0); + grid->addMultiCellWidget(m_textureType, 0, 0, 1, 1); + grid->addMultiCellWidget(label2, 1, 1, 0, 1); + grid->addMultiCellWidget(m_blendGain, 2, 2, 0, 1); + grid->setRowStretch(3, 10); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + + // ------------------------------------------------------------- + + m_previewWidget = new ImagePanelWidget(470, 350, "texture Tool", m_gboxSettings->panIconView()); + + setToolView(m_previewWidget); + init(); + + // ------------------------------------------------------------- + + connect(m_textureType, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotEffect())); + + connect(m_blendGain, TQ_SIGNAL(valueChanged(int)), + this, TQ_SLOT(slotTimer())); +} + +TextureTool::~TextureTool() +{ +} + +void TextureTool::renderingFinished() +{ + m_textureType->setEnabled(true); + m_blendGain->setEnabled(true); +} + +void TextureTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("texture Tool"); + + m_textureType->blockSignals(true); + m_blendGain->blockSignals(true); + + m_textureType->setCurrentItem(config->readNumEntry("TextureType", m_textureType->defaultItem())); + m_blendGain->setValue(config->readNumEntry("BlendGain", m_blendGain->defaultValue())); + + m_textureType->blockSignals(false); + m_blendGain->blockSignals(false); +} + +void TextureTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("texture Tool"); + config->writeEntry("TextureType", m_textureType->currentItem()); + config->writeEntry("BlendGain", m_blendGain->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void TextureTool::slotResetSettings() +{ + m_textureType->blockSignals(true); + m_blendGain->blockSignals(true); + + m_textureType->slotReset(); + m_blendGain->slotReset(); + + m_textureType->blockSignals(false); + m_blendGain->blockSignals(false); +} + +void TextureTool::prepareEffect() +{ + m_textureType->setEnabled(false); + m_blendGain->setEnabled(false); + + DImg image = m_previewWidget->getOriginalRegionImage(); + TQString texture = getTexturePath( m_textureType->currentItem() ); + + int b = 255 - m_blendGain->value(); + + setFilter(dynamic_cast(new Texture(&image, this, b, texture))); +} + +void TextureTool::prepareFinal() +{ + m_textureType->setEnabled(false); + m_blendGain->setEnabled(false); + + int b = 255 - m_blendGain->value(); + + ImageIface iface(0, 0); + TQString texture = getTexturePath( m_textureType->currentItem() ); + + setFilter(dynamic_cast(new Texture(iface.getOriginalImg(), this, b, texture))); +} + +void TextureTool::putPreviewData() +{ + m_previewWidget->setPreviewImage(filter()->getTargetImage()); +} + +void TextureTool::putFinalData() +{ + ImageIface iface(0, 0); + iface.putOriginalImage(i18n("Texture"), filter()->getTargetImage().bits()); +} + +TQString TextureTool::getTexturePath(int texture) +{ + TQString pattern; + + switch (texture) + { + case PaperTexture: + pattern = "paper-texture"; + break; + + case Paper2Texture: + pattern = "paper2-texture"; + break; + + case FabricTexture: + pattern = "fabric-texture"; + break; + + case BurlapTexture: + pattern = "burlap-texture"; + break; + + case BricksTexture: + pattern = "bricks-texture"; + break; + + case Bricks2Texture: + pattern = "bricks2-texture"; + break; + + case CanvasTexture: + pattern = "canvas-texture"; + break; + + case MarbleTexture: + pattern = "marble-texture"; + break; + + case Marble2Texture: + pattern = "marble2-texture"; + break; + + case BlueJeanTexture: + pattern = "bluejean-texture"; + break; + + case CellWoodTexture: + pattern = "cellwood-texture"; + break; + + case MetalWireTexture: + pattern = "metalwire-texture"; + break; + + case ModernTexture: + pattern = "modern-texture"; + break; + + case WallTexture: + pattern = "wall-texture"; + break; + + case MossTexture: + pattern = "moss-texture"; + break; + + case StoneTexture: + pattern = "stone-texture"; + break; + } + + TDEGlobal::dirs()->addResourceType(pattern.ascii(), TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + return (TDEGlobal::dirs()->findResourceDir(pattern.ascii(), pattern + ".png") + pattern + ".png" ); +} + +} // NameSpace DigikamTextureImagesPlugin diff --git a/src/imageplugins/texture/texturetool.h b/src/imageplugins/texture/texturetool.h new file mode 100644 index 00000000..1287bb3e --- /dev/null +++ b/src/imageplugins/texture/texturetool.h @@ -0,0 +1,112 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-10 + * Description : a plugin to apply texture over an image + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TEXTURETOOL_H +#define TEXTURETOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +namespace KDcrawIface +{ +class RIntNumInput; +class RComboBox; +} + +namespace Digikam +{ +class EditorToolSettings; +class ImagePanelWidget; +} + +namespace DigikamTextureImagesPlugin +{ + +class TextureTool : public Digikam::EditorToolThreaded +{ + TQ_OBJECT + + +public: + + TextureTool(TQObject* parent); + ~TextureTool(); + +private: + + TQString getTexturePath(int texture); + +private slots: + + void slotResetSettings(); + +private: + + void readSettings(); + void writeSettings(); + void prepareEffect(); + void prepareFinal(); + void putPreviewData(); + void putFinalData(); + void renderingFinished(); + +private: + + enum TextureTypes + { + PaperTexture=0, + Paper2Texture, + FabricTexture, + BurlapTexture, + BricksTexture, + Bricks2Texture, + CanvasTexture, + MarbleTexture, + Marble2Texture, + BlueJeanTexture, + CellWoodTexture, + MetalWireTexture, + ModernTexture, + WallTexture, + MossTexture, + StoneTexture + }; + + KDcrawIface::RComboBox *m_textureType; + + KDcrawIface::RIntNumInput *m_blendGain; + + Digikam::ImagePanelWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamTextureImagesPlugin + +#endif /* TEXTURETOOL_H */ diff --git a/src/imageplugins/whitebalance/Makefile.am b/src/imageplugins/whitebalance/Makefile.am new file mode 100644 index 00000000..b53cf65a --- /dev/null +++ b/src/imageplugins/whitebalance/Makefile.am @@ -0,0 +1,33 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/src/utilities/imageeditor/editor \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikamimageplugin_whitebalance_la_SOURCES = imageplugin_whitebalance.cpp \ + whitebalancetool.cpp + +digikamimageplugin_whitebalance_la_LIBADD = $(LIB_TDEPARTS) \ + $(top_builddir)/src/digikam/libdigikam.la + +digikamimageplugin_whitebalance_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) -ltdecore -ltdeui $(LIB_TQT) -ltdefx -lkdcraw -ltdeio + +kde_services_DATA = digikamimageplugin_whitebalance.desktop + +kde_module_LTLIBRARIES = digikamimageplugin_whitebalance.la + +rcdir = $(kde_datadir)/digikam +rc_DATA = digikamimageplugin_whitebalance_ui.rc diff --git a/src/imageplugins/whitebalance/digikamimageplugin_whitebalance.desktop b/src/imageplugins/whitebalance/digikamimageplugin_whitebalance.desktop new file mode 100644 index 00000000..df425c58 --- /dev/null +++ b/src/imageplugins/whitebalance/digikamimageplugin_whitebalance.desktop @@ -0,0 +1,53 @@ +[Desktop Entry] +Name=ImagePlugin_WhiteBalance +Name[bg]=ПриÑтавка за Ñнимки - Ð‘Ð°Ð»Ð°Ð½Ñ Ð½Ð° бÑлото +Name[el]=ΠÏόσθετοΕικόνας_ΙσοÏÏÎ¿Ï€Î¯Î±Î›ÎµÏ…ÎºÎ¿Ï +Name[fi]=Valkotasapaino +Name[hr]=Bijeli balans +Name[it]=PluginImmagini_BilanciamentoDelBianco +Name[ms]=ImagePlugin_ImbanganPutih +Name[nl]=Afbeeldingsplugin_Witbalans +Name[sr]=Ð‘Ð°Ð»Ð°Ð½Ñ Ð±ÐµÐ»Ð¾Ð³ +Name[sr@Latn]=Balans belog +Name[sv]=Insticksprogram för vitbalans +Name[tr]=ResimEklentisi_BeyazDengesi +Name[xx]=xxImagePlugin_WhiteBalancexx + +Type=Service +X-TDE-ServiceTypes=Digikam/ImagePlugin +Encoding=UTF-8 +Comment=White balance correction plugin for digiKam +Comment[bg]=ПриÑтавка на digiKam за промÑна баланÑа на бÑлото +Comment[ca]=Connector pel digiKam per corregir el balanç de blancs +Comment[da]=Hvid balance rettelsesplugin for digiKam +Comment[de]=digiKam-Modul zur Korrektur des Weißabgleiches +Comment[el]=ΠÏόσθετο διόÏθωσης ισοÏÏοπίας Î»ÎµÏ…ÎºÎ¿Ï Î³Î¹Î± το digiKam +Comment[es]=Plugin de digiKam para la corrección del equilibrio de blancos +Comment[et]=DigiKami valge tasakaalu korrigeerimise plugin +Comment[fa]=وصلۀ اصلاح تعادل سÙید برای digiKam +Comment[fi]=Korjaa kuvan valkotasapainon +Comment[fr]=Module externe pour corriger la balance des blancs dans digiKam +Comment[gl]=Un plugin de digiKam para corrixir o balance do branco +Comment[hr]=digiKam dodatak za ispravljanje bijelog balansa +Comment[is]=Ãforrit fyrir digiKam sem breytir hvítvægi mynda +Comment[it]=Plugin di correzione del bilanciamento del bianco per digiKam +Comment[ja]=digiKam ホワイトãƒãƒ©ãƒ³ã‚¹è£œæ­£ãƒ—ラグイン +Comment[ms]=Plugin pembetulan imbangan putih untuk digiKam +Comment[nds]=digiKam-Korrektuurmoduul för de Wittbalangs +Comment[nl]=Digikam-plugin voor het corrigeren van de witbalans +Comment[pa]=ਡਿਜ਼ੀਕੈਮ ਲਈ ਚਿੱਟਾ ਸਾਵਾਂ ਸੋਧ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka do programu digiKam korygujÄ…ca balans bieli +Comment[pt]=Um 'plugin' do digiKam para corrigir o balanceamento de branco +Comment[pt_BR]=Plugin para Correção de iluminação +Comment[ru]=Модуль коррекции баланÑа белого Ð´Ð»Ñ digiKam +Comment[sk]=digiKam plugin na korekciu vyváženia bielej +Comment[sr]=digiKam-ов прикључак иÑправку баланÑа белог +Comment[sr@Latn]=digiKam-ov prikljuÄak ispravku balansa belog +Comment[sv]=Digikam insticksprogram för korrigering av vitbalans +Comment[tr]=digiKam için beyaz dengesi düzeltme eklentisi +Comment[uk]=Втулок Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð»Ð°Ð½Ñу білого Ð´Ð»Ñ digiKam +Comment[vi]=Phần bổ sung sá»­a cân bằng trắng cho digiKam +Comment[xx]=xxWhite balance correction plugin for digiKamxx + +X-TDE-Library=digikamimageplugin_whitebalance +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/src/imageplugins/whitebalance/digikamimageplugin_whitebalance_ui.rc b/src/imageplugins/whitebalance/digikamimageplugin_whitebalance_ui.rc new file mode 100644 index 00000000..9bdf8247 --- /dev/null +++ b/src/imageplugins/whitebalance/digikamimageplugin_whitebalance_ui.rc @@ -0,0 +1,20 @@ + + + + + +

    &Color + + + + + + + Main Toolbar + + + + + + + diff --git a/src/imageplugins/whitebalance/imageeffect_whitebalance.cpp b/src/imageplugins/whitebalance/imageeffect_whitebalance.cpp new file mode 100644 index 00000000..f72609dd --- /dev/null +++ b/src/imageplugins/whitebalance/imageeffect_whitebalance.cpp @@ -0,0 +1,842 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2008 by Guillaume Castagnino + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "version.h" +#include "ddebug.h" +#include "dimg.h" +#include "dcolor.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "imagehistogram.h" +#include "whitebalance.h" +#include "colorgradientwidget.h" +#include "histogramwidget.h" +#include "dimgimagefilters.h" +#include "imageeffect_whitebalance.h" +#include "imageeffect_whitebalance.moc" + +namespace DigikamWhiteBalanceImagesPlugin +{ + +ImageEffect_WhiteBalance::ImageEffect_WhiteBalance(TQWidget* parent) + : Digikam::ImageDlgBase(parent, i18n("White Color Balance Correction"), + "whitebalance", true, false) +{ + TQString whatsThis; + + Digikam::ImageIface iface(0, 0); + + m_destinationPreviewData = 0L; + + // About data and help button. + + TDEAboutData* about = new TDEAboutData("digikam", + I18N_NOOP("White Color Balance Correction"), + digikam_version, + I18N_NOOP("A digiKam image plugin to correct white color balance."), + TDEAboutData::License_GPL, + "(c) 2005-2008, Gilles Caulier", + 0, + "http://wwww.digikam.org"); + + about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"), + "caulier dot gilles at gmail dot com"); + + about->addAuthor("Pawel T. Jochym", I18N_NOOP("White color balance correction algorithm"), + "jochym at ifj edu pl"); + + setAboutData(about); + + // ------------------------------------------------------------- + + m_previewWidget = new Digikam::ImageWidget("whitebalance Tool Dialog", plainPage(), + i18n("

    You can see here the image's white-balance " + "adjustments preview. You can pick color on image to " + "see the color level corresponding on histogram.")); + setPreviewAreaWidget(m_previewWidget); + + // ------------------------------------------------------------- + + TQWidget *gboxSettings = new TQWidget(plainPage()); + TQVBoxLayout* layout2 = new TQVBoxLayout( gboxSettings, spacingHint() ); + TQGridLayout *grid = new TQGridLayout( layout2, 2, 4, spacingHint()); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), gboxSettings); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, gboxSettings ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(gboxSettings); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, Digikam::HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, Digikam::HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + grid->addMultiCellLayout(l1, 0, 0, 0, 4); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(gboxSettings); + m_histogramWidget = new Digikam::HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "drawing of the selected image channel. This one is " + "re-computed at any filter settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new Digikam::ColorGradientWidget( Digikam::ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + grid->addMultiCellWidget(histoBox, 1, 2, 0, 4); + + // ------------------------------------------------------------- + + TQGridLayout *grid2 = new TQGridLayout(layout2, 13, 5, spacingHint()); + + m_temperatureLabel = new KActiveLabel(i18n("Color Temperature " + " (K): "), gboxSettings); + m_adjTemperatureLabel = new TQLabel(i18n("Adjustment:"), gboxSettings); + m_temperatureInput = new KDoubleNumInput(gboxSettings); + m_temperatureInput->setPrecision(1); + m_temperatureInput->setRange(2000.0, 12000.0, 10.0, true); + TQWhatsThis::add( m_temperatureInput, i18n("

    Set here the white balance color temperature in Kelvin.")); + + m_temperaturePresetLabel = new TQLabel(i18n("Preset:"), gboxSettings); + m_temperaturePresetCB = new TQComboBox( false, gboxSettings ); + m_temperaturePresetCB->insertItem( i18n("Candle") ); + m_temperaturePresetCB->insertItem( i18n("40W Lamp") ); + m_temperaturePresetCB->insertItem( i18n("100W Lamp") ); + m_temperaturePresetCB->insertItem( i18n("200W Lamp") ); + m_temperaturePresetCB->insertItem( i18n("Sunrise") ); + m_temperaturePresetCB->insertItem( i18n("Studio Lamp") ); + m_temperaturePresetCB->insertItem( i18n("Moonlight") ); + m_temperaturePresetCB->insertItem( i18n("Neutral") ); + m_temperaturePresetCB->insertItem( i18n("Daylight D50") ); + m_temperaturePresetCB->insertItem( i18n("Photo Flash") ); + m_temperaturePresetCB->insertItem( i18n("Sun") ); + m_temperaturePresetCB->insertItem( i18n("Xenon Lamp") ); + m_temperaturePresetCB->insertItem( i18n("Daylight D65") ); + m_temperaturePresetCB->insertItem( i18n("None") ); + TQWhatsThis::add( m_temperaturePresetCB, i18n("

    Select the white balance color temperature " + "preset to use here:

    " + "Candle: candle light (1850K).

    " + "40W Lamp: 40 Watt incandescent lamp (2680K).

    " + "100W Lamp: 100 Watt incandescent lamp (2800K).

    " + "200W Lamp: 200 Watt incandescent lamp (3000K).

    " + "Sunrise: sunrise or sunset light (3200K).

    " + "Studio Lamp: tungsten lamp used in photo studio " + "or light at 1 hour from dusk/dawn (3400K).

    " + "Moonlight: moon light (4100K).

    " + "Neutral: neutral color temperature (4750K).

    " + "Daylight D50: sunny daylight around noon (5000K).

    " + "Photo Flash: electronic photo flash (5500K).

    " + "Sun: effective sun temperature (5770K).

    " + "Xenon Lamp: xenon lamp or light arc (6420K).

    " + "Daylight D65: overcast sky light (6500K).

    " + "None: no preset value.")); + m_pickTemperature = new TQPushButton(gboxSettings); + TDEGlobal::dirs()->addResourceType("color-picker-grey", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-grey", "color-picker-grey.png"); + m_pickTemperature->setPixmap( TQPixmap( directory + "color-picker-grey.png" ) ); + m_pickTemperature->setToggleButton(true); + TQToolTip::add( m_pickTemperature, i18n( "Temperature tone color picker." ) ); + TQWhatsThis::add( m_pickTemperature, i18n("

    With this button, you can pick the color from original " + "image used to set white color balance temperature and " + "green component.")); + + KSeparator *line = new KSeparator(Horizontal, gboxSettings); + + // ------------------------------------------------------------- + + m_blackLabel = new TQLabel(i18n("Black point:"), gboxSettings); + m_blackInput = new KDoubleNumInput(gboxSettings); + m_blackInput->setPrecision(2); + m_blackInput->setRange(0.0, 0.05, 0.01, true); + TQWhatsThis::add( m_blackInput, i18n("

    Set here the black level value.")); + + m_darkLabel = new TQLabel(i18n("Shadows:"), gboxSettings); + m_darkInput = new KDoubleNumInput(gboxSettings); + m_darkInput->setPrecision(2); + m_darkInput->setRange(0.0, 1.0, 0.01, true); + TQWhatsThis::add( m_darkInput, i18n("

    Set here the shadows noise suppresion level.")); + + m_saturationLabel = new TQLabel(i18n("Saturation:"), gboxSettings); + m_saturationInput = new KDoubleNumInput(gboxSettings); + m_saturationInput->setPrecision(2); + m_saturationInput->setRange(0.0, 2.0, 0.01, true); + TQWhatsThis::add( m_saturationInput, i18n("

    Set here the saturation value.")); + + m_gammaLabel = new TQLabel(i18n("Gamma:"), gboxSettings); + m_gammaInput = new KDoubleNumInput(gboxSettings); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01, true); + TQWhatsThis::add( m_gammaInput, i18n("

    Set here the gamma correction value.")); + + m_greenLabel = new TQLabel(i18n("Green:"), gboxSettings); + m_greenInput = new KDoubleNumInput(gboxSettings); + m_greenInput->setPrecision(2); + m_greenInput->setRange(0.2, 2.5, 0.01, true); + TQWhatsThis::add(m_greenInput, i18n("

    Set here the green component to set magenta color " + "cast removal level.")); + + KSeparator *line2 = new KSeparator(Horizontal, gboxSettings); + + // ------------------------------------------------------------- + + m_exposureLabel = new KActiveLabel(i18n("Exposure Compensation " + " (E.V): "), gboxSettings); + m_mainExposureLabel = new TQLabel(i18n("Main:"), gboxSettings); + m_autoAdjustExposure = new TQPushButton(gboxSettings); + m_autoAdjustExposure->setPixmap(kapp->iconLoader()->loadIcon("system-run", (TDEIcon::Group)TDEIcon::Toolbar)); + TQToolTip::add( m_autoAdjustExposure, i18n( "Auto exposure adjustments" ) ); + TQWhatsThis::add( m_autoAdjustExposure, i18n("

    With this button, you can automatically adjust Exposure " + "and Black Point values.")); + m_mainExposureInput = new KDoubleNumInput(gboxSettings); + m_mainExposureInput->setPrecision(2); + m_mainExposureInput->setRange(-6.0, 8.0, 0.1, true); + TQWhatsThis::add( m_mainExposureInput, i18n("

    Set here the main exposure compensation value in E.V.")); + + m_fineExposureLabel = new TQLabel(i18n("Fine:"), gboxSettings); + m_fineExposureInput = new KDoubleNumInput(gboxSettings); + m_fineExposureInput->setPrecision(2); + m_fineExposureInput->setRange(-0.5, 0.5, 0.01, true); + TQWhatsThis::add( m_fineExposureInput, i18n("

    This value in E.V will be added to main exposure " + "compensation value to set fine exposure adjustment.")); + + // ------------------------------------------------------------- + + grid2->addMultiCellWidget(m_temperatureLabel, 0, 0, 0, 5); + grid2->addMultiCellWidget(m_adjTemperatureLabel, 1, 1, 0, 0); + grid2->addMultiCellWidget(m_pickTemperature, 1, 1, 1, 1); + grid2->addMultiCellWidget(m_temperatureInput, 1, 1, 2, 5); + grid2->addMultiCellWidget(m_temperaturePresetLabel, 2, 2, 0, 0); + grid2->addMultiCellWidget(m_temperaturePresetCB, 2, 2, 2, 5); + + grid2->addMultiCellWidget(line, 3, 3, 0, 5); + + grid2->addMultiCellWidget(m_blackLabel, 4, 4, 0, 0); + grid2->addMultiCellWidget(m_blackInput, 4, 4, 1, 5); + grid2->addMultiCellWidget(m_darkLabel, 5, 5, 0, 0); + grid2->addMultiCellWidget(m_darkInput, 5, 5, 1, 5); + grid2->addMultiCellWidget(m_saturationLabel, 6, 6, 0, 0); + grid2->addMultiCellWidget(m_saturationInput, 6, 6, 1, 5); + grid2->addMultiCellWidget(m_gammaLabel, 7, 7, 0, 0); + grid2->addMultiCellWidget(m_gammaInput, 7, 7, 1, 5); + grid2->addMultiCellWidget(m_greenLabel, 8, 8, 0, 0); + grid2->addMultiCellWidget(m_greenInput, 8, 8, 1, 5); + + grid2->addMultiCellWidget(line2, 9, 9, 0, 5); + + grid2->addMultiCellWidget(m_exposureLabel, 10, 10, 0, 5); + grid2->addMultiCellWidget(m_mainExposureLabel, 11, 11, 0, 0); + grid2->addMultiCellWidget(m_autoAdjustExposure, 11, 11, 1, 1); + grid2->addMultiCellWidget(m_mainExposureInput, 11, 11, 2, 5); + grid2->addMultiCellWidget(m_fineExposureLabel, 12, 12, 0, 1); + grid2->addMultiCellWidget(m_fineExposureInput, 12, 12, 2, 5); + grid2->setRowStretch(13, 10); + + setUserAreaWidget(gboxSettings); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromOriginal( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget( const Digikam::DColor &, const TQPoint & )), + this, TQ_SLOT(slotColorSelectedFromTarget( const Digikam::DColor & ))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Correction Filter Slider controls. + + connect(m_temperaturePresetCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotTemperaturePresetChanged(int))); + + connect(m_temperatureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTemperatureChanged(double))); + + connect(m_darkInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_blackInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_mainExposureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineExposureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_saturationInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_greenInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_autoAdjustExposure, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAutoAdjustExposure())); + + connect(m_pickTemperature, TQ_SIGNAL(released()), + this, TQ_SLOT(slotPickerColorButtonActived())); +} + +ImageEffect_WhiteBalance::~ImageEffect_WhiteBalance() +{ + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + delete m_histogramWidget; +} + +void ImageEffect_WhiteBalance::slotTemperatureChanged(double temperature) +{ + switch((uint)temperature) + { + case 1850: + m_temperaturePresetCB->setCurrentItem(Candle); + break; + + case 2680: + m_temperaturePresetCB->setCurrentItem(Lamp40W); + break; + + case 2800: + m_temperaturePresetCB->setCurrentItem(Lamp100W); + break; + + case 3000: + m_temperaturePresetCB->setCurrentItem(Lamp200W); + break; + + case 3200: + m_temperaturePresetCB->setCurrentItem(Sunrise); + break; + + case 3400: + m_temperaturePresetCB->setCurrentItem(StudioLamp); + break; + + case 4100: + m_temperaturePresetCB->setCurrentItem(MoonLight); + break; + + case 4750: + m_temperaturePresetCB->setCurrentItem(Neutral); + break; + + case 5000: + m_temperaturePresetCB->setCurrentItem(DaylightD50); + break; + + case 5500: + m_temperaturePresetCB->setCurrentItem(Flash); + break; + + case 5770: + m_temperaturePresetCB->setCurrentItem(Sun); + break; + + case 6420: + m_temperaturePresetCB->setCurrentItem(XeonLamp); + break; + + case 6500: + m_temperaturePresetCB->setCurrentItem(DaylightD65); + break; + + default: + m_temperaturePresetCB->setCurrentItem(None); + break; + } + + slotTimer(); +} + +void ImageEffect_WhiteBalance::slotTemperaturePresetChanged(int tempPreset) +{ + switch(tempPreset) + { + case Candle: + m_temperatureInput->setValue(1850.0); + break; + + case Lamp40W: + m_temperatureInput->setValue(2680.0); + break; + + case Lamp100W: + m_temperatureInput->setValue(2800.0); + break; + + case Lamp200W: + m_temperatureInput->setValue(3000.0); + break; + + case Sunrise: + m_temperatureInput->setValue(3200.0); + break; + + case StudioLamp: + m_temperatureInput->setValue(3400.0); + break; + + case MoonLight: + m_temperatureInput->setValue(4100.0); + break; + + case Neutral: + m_temperatureInput->setValue(4750.0); + break; + + case DaylightD50: + m_temperatureInput->setValue(5000.0); + break; + + case Flash: + m_temperatureInput->setValue(5500.0); + break; + + case Sun: + m_temperatureInput->setValue(5770.0); + break; + + case XeonLamp: + m_temperatureInput->setValue(6420.0); + break; + + case DaylightD65: + m_temperatureInput->setValue(6500.0); + break; + + default: // None. + break; + } + + slotEffect(); +} + +void ImageEffect_WhiteBalance::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(Digikam::ImageGuideWidget::PreviewOriginalImage); +} + +void ImageEffect_WhiteBalance::slotColorSelectedFromOriginal(const Digikam::DColor &color) +{ + if ( m_pickTemperature->isOn() ) + { + Digikam::DColor dc = color; + TQColor tc = dc.getTQColor(); + double temperatureLevel, greenLevel; + + Digikam::WhiteBalance::autoWBAdjustementFromColor(tc, temperatureLevel, greenLevel); + + m_temperatureInput->setValue(temperatureLevel); + m_greenInput->setValue(greenLevel); + m_pickTemperature->setOn(false); + } + else + return; + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void ImageEffect_WhiteBalance::slotColorSelectedFromTarget( const Digikam::DColor &color ) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void ImageEffect_WhiteBalance::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void ImageEffect_WhiteBalance::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = Digikam::HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void ImageEffect_WhiteBalance::slotAutoAdjustExposure() +{ + parentWidget()->setCursor( KCursor::waitCursor() ); + + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int width = iface->originalWidth(); + int height = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + double blackLevel; + double exposureLevel; + + Digikam::WhiteBalance::autoExposureAdjustement(data, width, height, sb, blackLevel, exposureLevel); + delete [] data; + + m_blackInput->setValue(blackLevel); + m_mainExposureInput->setValue(exposureLevel); + m_fineExposureInput->setValue(0.0); + + parentWidget()->unsetCursor(); + slotEffect(); +} + +void ImageEffect_WhiteBalance::slotEffect() +{ + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + double temperature = m_temperatureInput->value(); + double dark = m_darkInput->value(); + double black = m_blackInput->value(); + double mainExposure = m_mainExposureInput->value(); + double fineExposure = m_fineExposureInput->value(); + double gamma = m_gammaInput->value(); + double saturation = m_saturationInput->value(); + double green = m_greenInput->value(); + + Digikam::WhiteBalance wbFilter(sb); + wbFilter.whiteBalance(data, w, h, sb, + black, mainExposure + fineExposure, + temperature, green, dark, + gamma, saturation); + + iface->putPreviewImage(data); + m_previewWidget->updatePreview(); + + // Update histogram. + memcpy (m_destinationPreviewData, data, w*h*(sb ? 8 : 4)); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] data; +} + +void ImageEffect_WhiteBalance::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + Digikam::ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + double temperature = m_temperatureInput->value(); + double dark = m_darkInput->value(); + double black = m_blackInput->value(); + double mainExposure = m_mainExposureInput->value(); + double fineExposure = m_fineExposureInput->value(); + double gamma = m_gammaInput->value(); + double saturation = m_saturationInput->value(); + double green = m_greenInput->value(); + + Digikam::WhiteBalance wbFilter(sb); + wbFilter.whiteBalance(data, w, h, sb, + black, mainExposure + fineExposure, + temperature, green, dark, + gamma, saturation); + + iface->putOriginalImage(i18n("White Balance"), data); + delete [] data; + kapp->restoreOverrideCursor(); + accept(); +} + +void ImageEffect_WhiteBalance::resetValues() +{ + m_darkInput->blockSignals(true); + m_blackInput->blockSignals(true); + m_mainExposureInput->blockSignals(true); + m_fineExposureInput->blockSignals(true); + m_gammaInput->blockSignals(true); + m_saturationInput->blockSignals(true); + m_greenInput->blockSignals(true); + m_temperaturePresetCB->blockSignals(true); + + // Neutral color temperature settings is D65 + m_darkInput->setValue(0.5); + m_blackInput->setValue(0.0); + m_mainExposureInput->setValue(0.0); + m_fineExposureInput->setValue(0.0); + m_gammaInput->setValue(1.0); + m_saturationInput->setValue(1.0); + m_greenInput->setValue(1.0); + m_temperaturePresetCB->setCurrentItem(DaylightD65); + slotTemperaturePresetChanged(DaylightD65); + + m_previewWidget->resetSpotPosition(); + m_channelCB->setCurrentItem(LuminosityChannel); + slotChannelChanged(LuminosityChannel); + + m_histogramWidget->reset(); + + m_darkInput->blockSignals(false); + m_blackInput->blockSignals(false); + m_mainExposureInput->blockSignals(false); + m_fineExposureInput->blockSignals(false); + m_gammaInput->blockSignals(false); + m_saturationInput->blockSignals(false); + m_greenInput->blockSignals(false); + m_temperaturePresetCB->blockSignals(false); + slotEffect(); +} + +void ImageEffect_WhiteBalance::readUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("whitebalance Tool Dialog"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", Digikam::HistogramWidget::LogScaleHistogram)); + + m_darkInput->setValue(config->readDoubleNumEntry("Dark", 0.5)); + m_blackInput->setValue(config->readDoubleNumEntry("Black", 0.0)); + m_mainExposureInput->setValue(config->readDoubleNumEntry("MainExposure", 0.0)); + m_fineExposureInput->setValue(config->readDoubleNumEntry("FineExposure", 0.0)); + m_gammaInput->setValue(config->readDoubleNumEntry("Gamma", 1.0)); + m_saturationInput->setValue(config->readDoubleNumEntry("Saturation", 1.0)); + m_greenInput->setValue(config->readDoubleNumEntry("Green", 1.0)); + m_temperatureInput->setValue(config->readDoubleNumEntry("Temperature", 6500.0)); + slotTemperatureChanged(m_temperatureInput->value()); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void ImageEffect_WhiteBalance::writeUserSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("whitebalance Tool Dialog"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + config->writeEntry("Dark", m_darkInput->value()); + config->writeEntry("Black", m_blackInput->value()); + config->writeEntry("MainExposure", m_mainExposureInput->value()); + config->writeEntry("FineExposure", m_fineExposureInput->value()); + config->writeEntry("Gamma", m_gammaInput->value()); + config->writeEntry("Saturation", m_saturationInput->value()); + config->writeEntry("Green", m_greenInput->value()); + config->writeEntry("Temperature", m_temperatureInput->value()); + config->sync(); +} + +// Load all settings. +void ImageEffect_WhiteBalance::slotUser3() +{ + KURL loadWhiteBalanceFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("White Color Balance Settings File to Load")) ); + if( loadWhiteBalanceFile.isEmpty() ) + return; + + TQFile file(loadWhiteBalanceFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + + if ( stream.readLine() != "# White Color Balance Configuration File V2" ) + { + KMessageBox::error(this, + i18n("\"%1\" is not a White Color Balance settings text file.") + .arg(loadWhiteBalanceFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_temperatureInput->setValue( stream.readLine().toDouble() ); + m_darkInput->setValue( stream.readLine().toDouble() ); + m_blackInput->setValue( stream.readLine().toDouble() ); + m_mainExposureInput->setValue( stream.readLine().toDouble() ); + m_fineExposureInput->setValue( stream.readLine().toDouble() ); + m_gammaInput->setValue( stream.readLine().toDouble() ); + m_saturationInput->setValue( stream.readLine().toDouble() ); + m_greenInput->setValue( stream.readLine().toDouble() ); + m_histogramWidget->reset(); + blockSignals(false); + slotEffect(); + } + else + KMessageBox::error(this, i18n("Cannot load settings from the White Color Balance text file.")); + + file.close(); +} + +// Save all settings. +void ImageEffect_WhiteBalance::slotUser2() +{ + KURL saveWhiteBalanceFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), this, + TQString( i18n("White Color Balance Settings File to Save")) ); + if( saveWhiteBalanceFile.isEmpty() ) + return; + + TQFile file(saveWhiteBalanceFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# White Color Balance Configuration File V2\n"; + stream << m_temperatureInput->value() << "\n"; + stream << m_darkInput->value() << "\n"; + stream << m_blackInput->value() << "\n"; + stream << m_mainExposureInput->value() << "\n"; + stream << m_fineExposureInput->value() << "\n"; + stream << m_gammaInput->value() << "\n"; + stream << m_saturationInput->value() << "\n"; + stream << m_greenInput->value() << "\n"; + } + else + KMessageBox::error(this, i18n("Cannot save settings to the White Color Balance text file.")); + + file.close(); +} + +} // NameSpace DigikamWhiteBalanceImagesPlugin + diff --git a/src/imageplugins/whitebalance/imageeffect_whitebalance.h b/src/imageplugins/whitebalance/imageeffect_whitebalance.h new file mode 100644 index 00000000..6bef5607 --- /dev/null +++ b/src/imageplugins/whitebalance/imageeffect_whitebalance.h @@ -0,0 +1,168 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2008 by Guillaume Castagnino + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEEFFECT_WHITEBALANCE_H +#define IMAGEEFFECT_WHITEBALANCE_H + +// TQt include. + +#include + +// Digikam includes. + +#include "imagedlgbase.h" + +class TQPushButton; +class TQLabel; +class TQComboBox; +class TQPushButton; +class TQHButtonGroup; + +class KDoubleNumInput; +class KActiveLabel; + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +} + +namespace DigikamWhiteBalanceImagesPlugin +{ + +class ImageEffect_WhiteBalance : public Digikam::ImageDlgBase +{ + TQ_OBJECT + + +public: + + ImageEffect_WhiteBalance(TQWidget* parent); + ~ImageEffect_WhiteBalance(); + +protected: + + void finalRendering(); + +private slots: + + void slotUser2(); + void slotUser3(); + void slotEffect(); + void slotColorSelectedFromOriginal(const Digikam::DColor &color); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotScaleChanged(int scale); + void slotChannelChanged(int channel); + void slotTemperatureChanged(double temperature); + void slotTemperaturePresetChanged(int tempPreset); + void slotAutoAdjustExposure(void); + void slotPickerColorButtonActived(); + +private: + + void readUserSettings(); + void writeUserSettings(); + void resetValues(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum TemperaturePreset + { + Candle=0, + Lamp40W, + Lamp100W, + Lamp200W, + Sunrise, + StudioLamp, + MoonLight, + Neutral, + DaylightD50, + Flash, + Sun, + XeonLamp, + DaylightD65, + None + }; + + uchar *m_destinationPreviewData; + + int m_currentPreviewMode; + + TQPushButton *m_pickTemperature; + TQPushButton *m_autoAdjustExposure; + + TQComboBox *m_temperaturePresetCB; + TQComboBox *m_channelCB; + + TQHButtonGroup *m_scaleBG; + + TQLabel *m_adjTemperatureLabel; + TQLabel *m_temperaturePresetLabel; + TQLabel *m_darkLabel; + TQLabel *m_blackLabel; + TQLabel *m_mainExposureLabel; + TQLabel *m_fineExposureLabel; + TQLabel *m_gammaLabel; + TQLabel *m_saturationLabel; + TQLabel *m_greenLabel; + + KActiveLabel *m_exposureLabel; + KActiveLabel *m_temperatureLabel; + + KDoubleNumInput *m_temperatureInput; + KDoubleNumInput *m_darkInput; + KDoubleNumInput *m_blackInput; + KDoubleNumInput *m_mainExposureInput; + KDoubleNumInput *m_fineExposureInput; + KDoubleNumInput *m_gammaInput; + KDoubleNumInput *m_saturationInput; + KDoubleNumInput *m_greenInput; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::ImageWidget *m_previewWidget; +}; + +} // NameSpace DigikamWhiteBalanceImagesPlugin + +#endif /* IMAGEEFFECT_WHITEBALANCE_H */ diff --git a/src/imageplugins/whitebalance/imageplugin_whitebalance.cpp b/src/imageplugins/whitebalance/imageplugin_whitebalance.cpp new file mode 100644 index 00000000..071097fe --- /dev/null +++ b/src/imageplugins/whitebalance/imageplugin_whitebalance.cpp @@ -0,0 +1,71 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "whitebalancetool.h" +#include "imageplugin_whitebalance.h" +#include "imageplugin_whitebalance.moc" + +using namespace DigikamWhiteBalanceImagesPlugin; + +K_EXPORT_COMPONENT_FACTORY(digikamimageplugin_whitebalance, + KGenericFactory("digikamimageplugin_whitebalance")); + +ImagePlugin_WhiteBalance::ImagePlugin_WhiteBalance(TQObject *parent, const char*, const TQStringList &) + : Digikam::ImagePlugin(parent, "ImagePlugin_WhiteBalance") +{ + m_whitebalanceAction = new TDEAction(i18n("White Balance..."), "whitebalance", + CTRL+SHIFT+Key_W, + this, TQ_SLOT(slotWhiteBalance()), + actionCollection(), "imageplugin_whitebalance"); + + setXMLFile("digikamimageplugin_whitebalance_ui.rc"); + + DDebug() << "ImagePlugin_WhiteBalance plugin loaded" << endl; +} + +ImagePlugin_WhiteBalance::~ImagePlugin_WhiteBalance() +{ +} + +void ImagePlugin_WhiteBalance::setEnabledActions(bool enable) +{ + m_whitebalanceAction->setEnabled(enable); +} + +void ImagePlugin_WhiteBalance::slotWhiteBalance() +{ + WhiteBalanceTool *wb = new WhiteBalanceTool(this); + loadTool(wb); +} diff --git a/src/imageplugins/whitebalance/imageplugin_whitebalance.h b/src/imageplugins/whitebalance/imageplugin_whitebalance.h new file mode 100644 index 00000000..afc870c1 --- /dev/null +++ b/src/imageplugins/whitebalance/imageplugin_whitebalance.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPLUGIN_WHITEBALANCE_H +#define IMAGEPLUGIN_WHITEBALANCE_H + +// Digikam includes. + +#include "imageplugin.h" +#include "digikam_export.h" + +class TDEAction; + +class DIGIKAMIMAGEPLUGINS_EXPORT ImagePlugin_WhiteBalance : public Digikam::ImagePlugin +{ + TQ_OBJECT + + +public: + + ImagePlugin_WhiteBalance(TQObject *parent, const char* name, + const TQStringList &args); + ~ImagePlugin_WhiteBalance(); + + void setEnabledActions(bool enable); + +private slots: + + void slotWhiteBalance(); + +private: + + TDEAction *m_whitebalanceAction; +}; + +#endif /* IMAGEPLUGIN_WHITEBALANCE_H */ diff --git a/src/imageplugins/whitebalance/whitebalancetool.cpp b/src/imageplugins/whitebalance/whitebalancetool.cpp new file mode 100644 index 00000000..fdbe723a --- /dev/null +++ b/src/imageplugins/whitebalance/whitebalancetool.cpp @@ -0,0 +1,850 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2008 by Guillaume Castagnino + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Digikam includes. + +#include "colorgradientwidget.h" +#include "daboutdata.h" +#include "dcolor.h" +#include "ddebug.h" +#include "dimg.h" +#include "dimgimagefilters.h" +#include "editortoolsettings.h" +#include "histogramwidget.h" +#include "imagehistogram.h" +#include "imageiface.h" +#include "imagewidget.h" +#include "whitebalance.h" + +// Local includes. + +#include "whitebalancetool.h" +#include "whitebalancetool.moc" + +using namespace KDcrawIface; +using namespace Digikam; + +namespace DigikamWhiteBalanceImagesPlugin +{ + +WhiteBalanceTool::WhiteBalanceTool(TQObject* parent) + : EditorTool(parent) +{ + setName("whitebalance"); + setToolName(i18n("White Balance")); + setToolIcon(SmallIcon("whitebalance")); + + m_destinationPreviewData = 0; + + // ------------------------------------------------------------- + + m_previewWidget = new ImageWidget("whitebalance Tool", 0, + i18n("

    You can see here the image's white-balance " + "adjustments preview. You can pick color on image to " + "see the color level corresponding on histogram.")); + setToolView(m_previewWidget); + + // ------------------------------------------------------------- + + m_gboxSettings = new EditorToolSettings(EditorToolSettings::Default| + EditorToolSettings::Load| + EditorToolSettings::SaveAs| + EditorToolSettings::Ok| + EditorToolSettings::Cancel); + + TQVBoxLayout* layout2 = new TQVBoxLayout(m_gboxSettings->plainPage(), m_gboxSettings->spacingHint()); + TQGridLayout *grid = new TQGridLayout(layout2, 2, 4); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), m_gboxSettings->plainPage()); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + m_channelCB = new TQComboBox( false, m_gboxSettings->plainPage() ); + m_channelCB->insertItem( i18n("Luminosity") ); + m_channelCB->insertItem( i18n("Red") ); + m_channelCB->insertItem( i18n("Green") ); + m_channelCB->insertItem( i18n("Blue") ); + TQWhatsThis::add( m_channelCB, i18n("

    Select the histogram channel to display here:

    " + "Luminosity: display the image's luminosity values.

    " + "Red: display the red image-channel values.

    " + "Green: display the green image-channel values.

    " + "Blue: display the blue image-channel values.

    ")); + + m_scaleBG = new TQHButtonGroup(m_gboxSettings->plainPage()); + m_scaleBG->setExclusive(true); + m_scaleBG->setFrameShape(TQFrame::NoFrame); + m_scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( m_scaleBG, i18n("

    Select the histogram scale here.

    " + "If the image's maximal counts are small, you can use the linear scale.

    " + "Logarithmic scale can be used when the maximal counts are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

    Linear" ) ); + m_scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( m_scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

    Logarithmic" ) ); + m_scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQHBoxLayout* l1 = new TQHBoxLayout(); + l1->addWidget(label1); + l1->addWidget(m_channelCB); + l1->addStretch(10); + l1->addWidget(m_scaleBG); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(m_gboxSettings->plainPage()); + m_histogramWidget = new HistogramWidget(256, 140, histoBox, false, true, true); + TQWhatsThis::add( m_histogramWidget, i18n("

    Here you can see the target preview image histogram " + "drawing of the selected image channel. This one is " + "re-computed at any filter settings changes.")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + m_hGradient = new ColorGradientWidget( ColorGradientWidget::Horizontal, 10, histoBox ); + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + + grid->addMultiCellLayout(l1, 0, 0, 0, 4); + grid->addMultiCellWidget(histoBox, 1, 2, 0, 4); + grid->setMargin(m_gboxSettings->spacingHint()); + grid->setSpacing(m_gboxSettings->spacingHint()); + + // ------------------------------------------------------------- + + TQGridLayout *grid2 = new TQGridLayout(layout2, 13, 5); + + m_temperatureLabel = new KActiveLabel(i18n("Color Temperature " + " (K): "), m_gboxSettings->plainPage()); + m_adjTemperatureLabel = new TQLabel(i18n("Adjustment:"), m_gboxSettings->plainPage()); + m_temperatureInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_temperatureInput->setPrecision(1); + m_temperatureInput->setRange(1750.0, 12000.0, 10.0); + m_temperatureInput->setDefaultValue(6500.0); + TQWhatsThis::add( m_temperatureInput, i18n("

    Set here the white balance color temperature in Kelvin.")); + + m_temperaturePresetLabel = new TQLabel(i18n("Preset:"), m_gboxSettings->plainPage()); + m_temperaturePresetCB = new RComboBox(m_gboxSettings->plainPage()); + m_temperaturePresetCB->insertItem(i18n("Candle")); + m_temperaturePresetCB->insertItem(i18n("40W Lamp")); + m_temperaturePresetCB->insertItem(i18n("100W Lamp")); + m_temperaturePresetCB->insertItem(i18n("200W Lamp")); + m_temperaturePresetCB->insertItem(i18n("Sunrise")); + m_temperaturePresetCB->insertItem(i18n("Studio Lamp")); + m_temperaturePresetCB->insertItem(i18n("Moonlight")); + m_temperaturePresetCB->insertItem(i18n("Neutral")); + m_temperaturePresetCB->insertItem(i18n("Daylight D50")); + m_temperaturePresetCB->insertItem(i18n("Photo Flash")); + m_temperaturePresetCB->insertItem(i18n("Sun")); + m_temperaturePresetCB->insertItem(i18n("Xenon Lamp")); + m_temperaturePresetCB->insertItem(i18n("Daylight D65")); + m_temperaturePresetCB->insertItem(i18n("None")); + m_temperaturePresetCB->setDefaultItem(DaylightD65); + TQWhatsThis::add( m_temperaturePresetCB, i18n("

    Select the white balance color temperature " + "preset to use here:

    " + "Candle: candle light (1850K).

    " + "40W Lamp: 40 Watt incandescent lamp (2680K).

    " + "100W Lamp: 100 Watt incandescent lamp (2800K).

    " + "200W Lamp: 200 Watt incandescent lamp (3000K).

    " + "Sunrise: sunrise or sunset light (3200K).

    " + "Studio Lamp: tungsten lamp used in photo studio " + "or light at 1 hour from dusk/dawn (3400K).

    " + "Moonlight: moon light (4100K).

    " + "Neutral: neutral color temperature (4750K).

    " + "Daylight D50: sunny daylight around noon (5000K).

    " + "Photo Flash: electronic photo flash (5500K).

    " + "Sun: effective sun temperature (5770K).

    " + "Xenon Lamp: xenon lamp or light arc (6420K).

    " + "Daylight D65: overcast sky light (6500K).

    " + "None: no preset value.")); + m_pickTemperature = new TQPushButton(m_gboxSettings->plainPage()); + TDEGlobal::dirs()->addResourceType("color-picker-grey", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("color-picker-grey", "color-picker-grey.png"); + m_pickTemperature->setPixmap( TQPixmap( directory + "color-picker-grey.png" ) ); + m_pickTemperature->setToggleButton(true); + TQToolTip::add( m_pickTemperature, i18n( "Temperature tone color picker." ) ); + TQWhatsThis::add( m_pickTemperature, i18n("

    With this button, you can pick the color from original " + "image used to set white color balance temperature and " + "green component.")); + + KSeparator *line = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + // ------------------------------------------------------------- + + m_blackLabel = new TQLabel(i18n("Black point:"), m_gboxSettings->plainPage()); + m_blackInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_blackInput->setPrecision(2); + m_blackInput->setRange(0.0, 0.05, 0.01); + m_blackInput->setDefaultValue(0.0); + TQWhatsThis::add( m_blackInput, i18n("

    Set here the black level value.")); + + m_darkLabel = new TQLabel(i18n("Shadows:"), m_gboxSettings->plainPage()); + m_darkInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_darkInput->setPrecision(2); + m_darkInput->setRange(0.0, 1.0, 0.01); + m_darkInput->setDefaultValue(0.5); + TQWhatsThis::add( m_darkInput, i18n("

    Set here the shadows noise suppresion level.")); + + m_saturationLabel = new TQLabel(i18n("Saturation:"), m_gboxSettings->plainPage()); + m_saturationInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_saturationInput->setPrecision(2); + m_saturationInput->setRange(0.0, 2.0, 0.01); + m_saturationInput->setDefaultValue(1.0); + TQWhatsThis::add( m_saturationInput, i18n("

    Set here the saturation value.")); + + m_gammaLabel = new TQLabel(i18n("Gamma:"), m_gboxSettings->plainPage()); + m_gammaInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_gammaInput->setPrecision(2); + m_gammaInput->setRange(0.1, 3.0, 0.01); + m_gammaInput->setDefaultValue(1.0); + TQWhatsThis::add( m_gammaInput, i18n("

    Set here the gamma correction value.")); + + m_greenLabel = new TQLabel(i18n("Green:"), m_gboxSettings->plainPage()); + m_greenInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_greenInput->setPrecision(2); + m_greenInput->setRange(0.2, 2.5, 0.01); + m_greenInput->setDefaultValue(1.0); + TQWhatsThis::add(m_greenInput, i18n("

    Set here the green component to set magenta color " + "cast removal level.")); + + KSeparator *line2 = new KSeparator(Horizontal, m_gboxSettings->plainPage()); + + // ------------------------------------------------------------- + + m_exposureLabel = new KActiveLabel(i18n("Exposure Compensation " + " (E.V): "), m_gboxSettings->plainPage()); + m_mainExposureLabel = new TQLabel(i18n("Main:"), m_gboxSettings->plainPage()); + m_autoAdjustExposure = new TQPushButton(m_gboxSettings->plainPage()); + m_autoAdjustExposure->setPixmap(kapp->iconLoader()->loadIcon("system-run", (TDEIcon::Group)TDEIcon::Toolbar)); + TQToolTip::add( m_autoAdjustExposure, i18n( "Auto exposure adjustments" ) ); + TQWhatsThis::add( m_autoAdjustExposure, i18n("

    With this button, you can automatically adjust Exposure " + "and Black Point values.")); + m_mainExposureInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_mainExposureInput->setPrecision(2); + m_mainExposureInput->setRange(-6.0, 8.0, 0.1); + m_mainExposureInput->setDefaultValue(0.0); + TQWhatsThis::add( m_mainExposureInput, i18n("

    Set here the main exposure compensation value in E.V.")); + + m_fineExposureLabel = new TQLabel(i18n("Fine:"), m_gboxSettings->plainPage()); + m_fineExposureInput = new RDoubleNumInput(m_gboxSettings->plainPage()); + m_fineExposureInput->setPrecision(2); + m_fineExposureInput->setRange(-0.5, 0.5, 0.01); + m_fineExposureInput->setDefaultValue(0.0); + TQWhatsThis::add( m_fineExposureInput, i18n("

    This value in E.V will be added to main exposure " + "compensation value to set fine exposure adjustment.")); + + // ------------------------------------------------------------- + + grid2->addMultiCellWidget(m_temperatureLabel, 0, 0, 0, 5); + grid2->addMultiCellWidget(m_adjTemperatureLabel, 1, 1, 0, 0); + grid2->addMultiCellWidget(m_pickTemperature, 1, 1, 1, 1); + grid2->addMultiCellWidget(m_temperatureInput, 1, 1, 2, 5); + grid2->addMultiCellWidget(m_temperaturePresetLabel, 2, 2, 0, 0); + grid2->addMultiCellWidget(m_temperaturePresetCB, 2, 2, 2, 5); + + grid2->addMultiCellWidget(line, 3, 3, 0, 5); + + grid2->addMultiCellWidget(m_blackLabel, 4, 4, 0, 0); + grid2->addMultiCellWidget(m_blackInput, 4, 4, 1, 5); + grid2->addMultiCellWidget(m_darkLabel, 5, 5, 0, 0); + grid2->addMultiCellWidget(m_darkInput, 5, 5, 1, 5); + grid2->addMultiCellWidget(m_saturationLabel, 6, 6, 0, 0); + grid2->addMultiCellWidget(m_saturationInput, 6, 6, 1, 5); + grid2->addMultiCellWidget(m_gammaLabel, 7, 7, 0, 0); + grid2->addMultiCellWidget(m_gammaInput, 7, 7, 1, 5); + grid2->addMultiCellWidget(m_greenLabel, 8, 8, 0, 0); + grid2->addMultiCellWidget(m_greenInput, 8, 8, 1, 5); + + grid2->addMultiCellWidget(line2, 9, 9, 0, 5); + + grid2->addMultiCellWidget(m_exposureLabel, 10, 10, 0, 5); + grid2->addMultiCellWidget(m_mainExposureLabel, 11, 11, 0, 0); + grid2->addMultiCellWidget(m_autoAdjustExposure, 11, 11, 1, 1); + grid2->addMultiCellWidget(m_mainExposureInput, 11, 11, 2, 5); + grid2->addMultiCellWidget(m_fineExposureLabel, 12, 12, 0, 1); + grid2->addMultiCellWidget(m_fineExposureInput, 12, 12, 2, 5); + grid2->setRowStretch(13, 10); + grid2->setMargin(m_gboxSettings->spacingHint()); + grid2->setSpacing(m_gboxSettings->spacingHint()); + + setToolSettings(m_gboxSettings); + init(); + + // ------------------------------------------------------------- + + connect(m_channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(m_scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromOriginal(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromOriginal(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(spotPositionChangedFromTarget(const Digikam::DColor&, const TQPoint&)), + this, TQ_SLOT(slotColorSelectedFromTarget(const Digikam::DColor&))); + + connect(m_previewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotEffect())); + + // ------------------------------------------------------------- + // Correction Filter Slider controls. + + connect(m_temperaturePresetCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotTemperaturePresetChanged(int))); + + connect(m_temperatureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTemperatureChanged(double))); + + connect(m_darkInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_blackInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_mainExposureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_fineExposureInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_gammaInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_saturationInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + connect(m_greenInput, TQ_SIGNAL(valueChanged (double)), + this, TQ_SLOT(slotTimer())); + + // ------------------------------------------------------------- + // Bouttons slots. + + connect(m_autoAdjustExposure, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotAutoAdjustExposure())); + + connect(m_pickTemperature, TQ_SIGNAL(released()), + this, TQ_SLOT(slotPickerColorButtonActived())); +} + +WhiteBalanceTool::~WhiteBalanceTool() +{ + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; +} + +void WhiteBalanceTool::slotTemperatureChanged(double temperature) +{ + switch((uint)temperature) + { + case 1850: + m_temperaturePresetCB->setCurrentItem(Candle); + break; + + case 2680: + m_temperaturePresetCB->setCurrentItem(Lamp40W); + break; + + case 2800: + m_temperaturePresetCB->setCurrentItem(Lamp100W); + break; + + case 3000: + m_temperaturePresetCB->setCurrentItem(Lamp200W); + break; + + case 3200: + m_temperaturePresetCB->setCurrentItem(Sunrise); + break; + + case 3400: + m_temperaturePresetCB->setCurrentItem(StudioLamp); + break; + + case 4100: + m_temperaturePresetCB->setCurrentItem(MoonLight); + break; + + case 4750: + m_temperaturePresetCB->setCurrentItem(Neutral); + break; + + case 5000: + m_temperaturePresetCB->setCurrentItem(DaylightD50); + break; + + case 5500: + m_temperaturePresetCB->setCurrentItem(Flash); + break; + + case 5770: + m_temperaturePresetCB->setCurrentItem(Sun); + break; + + case 6420: + m_temperaturePresetCB->setCurrentItem(XeonLamp); + break; + + case 6500: + m_temperaturePresetCB->setCurrentItem(DaylightD65); + break; + + default: + m_temperaturePresetCB->setCurrentItem(None); + break; + } + + slotTimer(); +} + +void WhiteBalanceTool::slotTemperaturePresetChanged(int tempPreset) +{ + switch(tempPreset) + { + case Candle: + m_temperatureInput->setValue(1850.0); + break; + + case Lamp40W: + m_temperatureInput->setValue(2680.0); + break; + + case Lamp100W: + m_temperatureInput->setValue(2800.0); + break; + + case Lamp200W: + m_temperatureInput->setValue(3000.0); + break; + + case Sunrise: + m_temperatureInput->setValue(3200.0); + break; + + case StudioLamp: + m_temperatureInput->setValue(3400.0); + break; + + case MoonLight: + m_temperatureInput->setValue(4100.0); + break; + + case Neutral: + m_temperatureInput->setValue(4750.0); + break; + + case DaylightD50: + m_temperatureInput->setValue(5000.0); + break; + + case Flash: + m_temperatureInput->setValue(5500.0); + break; + + case Sun: + m_temperatureInput->setValue(5770.0); + break; + + case XeonLamp: + m_temperatureInput->setValue(6420.0); + break; + + case DaylightD65: + m_temperatureInput->setValue(6500.0); + break; + + default: // None. + break; + } + + slotEffect(); +} + +void WhiteBalanceTool::slotPickerColorButtonActived() +{ + // Save previous rendering mode and toggle to original image. + m_currentPreviewMode = m_previewWidget->getRenderingPreviewMode(); + m_previewWidget->setRenderingPreviewMode(ImageGuideWidget::PreviewOriginalImage); +} + +void WhiteBalanceTool::slotColorSelectedFromOriginal(const DColor &color) +{ + if ( m_pickTemperature->isOn() ) + { + DColor dc = color; + TQColor tc = dc.getTQColor(); + double temperatureLevel, greenLevel; + + WhiteBalance::autoWBAdjustementFromColor(tc, temperatureLevel, greenLevel); + + m_temperatureInput->setValue(temperatureLevel); + m_greenInput->setValue(greenLevel); + m_pickTemperature->setOn(false); + } + else + return; + + // restore previous rendering mode. + m_previewWidget->setRenderingPreviewMode(m_currentPreviewMode); + + slotEffect(); +} + +void WhiteBalanceTool::slotColorSelectedFromTarget(const DColor& color) +{ + m_histogramWidget->setHistogramGuideByColor(color); +} + +void WhiteBalanceTool::slotScaleChanged(int scale) +{ + m_histogramWidget->m_scaleType = scale; + m_histogramWidget->repaint(false); +} + +void WhiteBalanceTool::slotChannelChanged(int channel) +{ + switch(channel) + { + case LuminosityChannel: + m_histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + break; + + case RedChannel: + m_histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + break; + + case GreenChannel: + m_histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + break; + + case BlueChannel: + m_histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + m_hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + break; + } + + m_histogramWidget->repaint(false); +} + +void WhiteBalanceTool::slotAutoAdjustExposure() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int width = iface->originalWidth(); + int height = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + double blackLevel; + double exposureLevel; + + WhiteBalance::autoExposureAdjustement(data, width, height, sb, blackLevel, exposureLevel); + delete [] data; + + m_blackInput->setValue(blackLevel); + m_mainExposureInput->setValue(exposureLevel); + m_fineExposureInput->setValue(0.0); + + kapp->restoreOverrideCursor(); + slotEffect(); +} + +void WhiteBalanceTool::slotEffect() +{ + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getPreviewImage(); + int w = iface->previewWidth(); + int h = iface->previewHeight(); + bool sb = iface->previewSixteenBit(); + + // Create the new empty destination image data space. + m_histogramWidget->stopHistogramComputation(); + + if (m_destinationPreviewData) + delete [] m_destinationPreviewData; + + m_destinationPreviewData = new uchar[w*h*(sb ? 8 : 4)]; + + double temperature = m_temperatureInput->value(); + double dark = m_darkInput->value(); + double black = m_blackInput->value(); + double mainExposure = m_mainExposureInput->value(); + double fineExposure = m_fineExposureInput->value(); + double gamma = m_gammaInput->value(); + double saturation = m_saturationInput->value(); + double green = m_greenInput->value(); + + WhiteBalance wbFilter(sb); + wbFilter.whiteBalance(data, w, h, sb, + black, mainExposure + fineExposure, + temperature, green, dark, + gamma, saturation); + + iface->putPreviewImage(data); + m_previewWidget->updatePreview(); + + // Update histogram. + memcpy (m_destinationPreviewData, data, w*h*(sb ? 8 : 4)); + m_histogramWidget->updateData(m_destinationPreviewData, w, h, sb, 0, 0, 0, false); + delete [] data; +} + +void WhiteBalanceTool::finalRendering() +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + ImageIface* iface = m_previewWidget->imageIface(); + uchar *data = iface->getOriginalImage(); + int w = iface->originalWidth(); + int h = iface->originalHeight(); + bool sb = iface->originalSixteenBit(); + + double temperature = m_temperatureInput->value(); + double dark = m_darkInput->value(); + double black = m_blackInput->value(); + double mainExposure = m_mainExposureInput->value(); + double fineExposure = m_fineExposureInput->value(); + double gamma = m_gammaInput->value(); + double saturation = m_saturationInput->value(); + double green = m_greenInput->value(); + + WhiteBalance wbFilter(sb); + wbFilter.whiteBalance(data, w, h, sb, + black, mainExposure + fineExposure, + temperature, green, dark, + gamma, saturation); + + iface->putOriginalImage(i18n("White Balance"), data); + delete [] data; + kapp->restoreOverrideCursor(); +} + +void WhiteBalanceTool::slotResetSettings() +{ + m_blackInput->blockSignals(true); + m_darkInput->blockSignals(true); + m_fineExposureInput->blockSignals(true); + m_gammaInput->blockSignals(true); + m_greenInput->blockSignals(true); + m_mainExposureInput->blockSignals(true); + m_saturationInput->blockSignals(true); + m_temperatureInput->blockSignals(true); + m_temperaturePresetCB->blockSignals(true); + + // Neutral color temperature settings is D65 + m_blackInput->slotReset(); + m_darkInput->slotReset(); + m_fineExposureInput->slotReset(); + m_gammaInput->slotReset(); + m_greenInput->slotReset(); + m_mainExposureInput->slotReset(); + m_saturationInput->slotReset(); + m_temperaturePresetCB->slotReset(); + slotTemperaturePresetChanged(m_temperaturePresetCB->defaultItem()); + m_temperatureInput->slotReset(); + + m_previewWidget->resetSpotPosition(); + m_channelCB->setCurrentItem(LuminosityChannel); + slotChannelChanged(LuminosityChannel); + + m_histogramWidget->reset(); + + m_blackInput->blockSignals(false); + m_darkInput->blockSignals(false); + m_fineExposureInput->blockSignals(false); + m_gammaInput->blockSignals(false); + m_greenInput->blockSignals(false); + m_mainExposureInput->blockSignals(false); + m_saturationInput->blockSignals(false); + m_temperatureInput->blockSignals(false); + m_temperaturePresetCB->blockSignals(false); + + slotEffect(); +} + +void WhiteBalanceTool::readSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("whitebalance Tool"); + m_channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + m_scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + + m_darkInput->setValue(config->readDoubleNumEntry("Dark",m_darkInput->defaultValue())); + m_blackInput->setValue(config->readDoubleNumEntry("Black", m_blackInput->defaultValue())); + m_mainExposureInput->setValue(config->readDoubleNumEntry("MainExposure", m_mainExposureInput->defaultValue())); + m_fineExposureInput->setValue(config->readDoubleNumEntry("FineExposure", m_fineExposureInput->defaultValue())); + m_gammaInput->setValue(config->readDoubleNumEntry("Gamma", m_gammaInput->defaultValue())); + m_saturationInput->setValue(config->readDoubleNumEntry("Saturation", m_saturationInput->defaultValue())); + m_greenInput->setValue(config->readDoubleNumEntry("Green", m_greenInput->defaultValue())); + m_temperatureInput->setValue(config->readDoubleNumEntry("Temperature", m_temperatureInput->defaultValue())); + + slotTemperatureChanged(m_temperatureInput->value()); + m_histogramWidget->reset(); + slotChannelChanged(m_channelCB->currentItem()); + slotScaleChanged(m_scaleBG->selectedId()); +} + +void WhiteBalanceTool::writeSettings() +{ + TDEConfig* config = kapp->config(); + config->setGroup("whitebalance Tool"); + config->writeEntry("Histogram Channel", m_channelCB->currentItem()); + config->writeEntry("Histogram Scale", m_scaleBG->selectedId()); + + config->writeEntry("Dark", m_darkInput->value()); + config->writeEntry("Black", m_blackInput->value()); + config->writeEntry("MainExposure", m_mainExposureInput->value()); + config->writeEntry("FineExposure", m_fineExposureInput->value()); + config->writeEntry("Gamma", m_gammaInput->value()); + config->writeEntry("Saturation", m_saturationInput->value()); + config->writeEntry("Green", m_greenInput->value()); + config->writeEntry("Temperature", m_temperatureInput->value()); + m_previewWidget->writeSettings(); + config->sync(); +} + +void WhiteBalanceTool::slotLoadSettings() +{ + KURL loadWhiteBalanceFile = KFileDialog::getOpenURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("White Color Balance Settings File to Load")) ); + if( loadWhiteBalanceFile.isEmpty() ) + return; + + TQFile file(loadWhiteBalanceFile.path()); + + if ( file.open(IO_ReadOnly) ) + { + TQTextStream stream( &file ); + + if ( stream.readLine() != "# White Color Balance Configuration File V2" ) + { + KMessageBox::error(kapp->activeWindow(), + i18n("\"%1\" is not a White Color Balance settings text file.") + .arg(loadWhiteBalanceFile.fileName())); + file.close(); + return; + } + + blockSignals(true); + m_temperatureInput->setValue( stream.readLine().toDouble() ); + m_darkInput->setValue( stream.readLine().toDouble() ); + m_blackInput->setValue( stream.readLine().toDouble() ); + m_mainExposureInput->setValue( stream.readLine().toDouble() ); + m_fineExposureInput->setValue( stream.readLine().toDouble() ); + m_gammaInput->setValue( stream.readLine().toDouble() ); + m_saturationInput->setValue( stream.readLine().toDouble() ); + m_greenInput->setValue( stream.readLine().toDouble() ); + m_histogramWidget->reset(); + blockSignals(false); + slotEffect(); + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot load settings from the White Color Balance text file.")); + + file.close(); +} + +void WhiteBalanceTool::slotSaveAsSettings() +{ + KURL saveWhiteBalanceFile = KFileDialog::getSaveURL(TDEGlobalSettings::documentPath(), + TQString( "*" ), kapp->activeWindow(), + TQString( i18n("White Color Balance Settings File to Save")) ); + if( saveWhiteBalanceFile.isEmpty() ) + return; + + TQFile file(saveWhiteBalanceFile.path()); + + if ( file.open(IO_WriteOnly) ) + { + TQTextStream stream( &file ); + stream << "# White Color Balance Configuration File V2\n"; + stream << m_temperatureInput->value() << "\n"; + stream << m_darkInput->value() << "\n"; + stream << m_blackInput->value() << "\n"; + stream << m_mainExposureInput->value() << "\n"; + stream << m_fineExposureInput->value() << "\n"; + stream << m_gammaInput->value() << "\n"; + stream << m_saturationInput->value() << "\n"; + stream << m_greenInput->value() << "\n"; + } + else + KMessageBox::error(kapp->activeWindow(), i18n("Cannot save settings to the White Color Balance text file.")); + + file.close(); +} + +} // NameSpace DigikamWhiteBalanceImagesPlugin diff --git a/src/imageplugins/whitebalance/whitebalancetool.h b/src/imageplugins/whitebalance/whitebalancetool.h new file mode 100644 index 00000000..2df2877e --- /dev/null +++ b/src/imageplugins/whitebalance/whitebalancetool.h @@ -0,0 +1,174 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-11 + * Description : a digiKam image editor plugin to correct + * image white balance + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2008 by Guillaume Castagnino + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef WHITEBALANCETOOL_H +#define WHITEBALANCETOOL_H + +// TQt includes. + +#include + +// Digikam includes. + +#include "editortool.h" + +class TQComboBox; +class TQPushButton; +class TQLabel; +class TQPushButton; +class TQHButtonGroup; + +class KActiveLabel; + +namespace KDcrawIface +{ +class RDoubleNumInput; +class RComboBox; +} + +namespace Digikam +{ +class HistogramWidget; +class ColorGradientWidget; +class ImageWidget; +class DColor; +class EditorToolSettings; +} + +namespace DigikamWhiteBalanceImagesPlugin +{ + +class WhiteBalanceTool : public Digikam::EditorTool +{ + TQ_OBJECT + + +public: + + WhiteBalanceTool(TQObject* parent); + ~WhiteBalanceTool(); + +private: + + void readSettings(); + void writeSettings(); + void finalRendering(); + +private slots: + + void slotSaveAsSettings(); + void slotLoadSettings(); + void slotEffect(); + void slotResetSettings(); + void slotColorSelectedFromOriginal(const Digikam::DColor &color); + void slotColorSelectedFromTarget(const Digikam::DColor &color); + void slotScaleChanged(int scale); + void slotChannelChanged(int channel); + void slotTemperatureChanged(double temperature); + void slotTemperaturePresetChanged(int tempPreset); + void slotAutoAdjustExposure(void); + void slotPickerColorButtonActived(); + +private: + + enum HistogramScale + { + Linear=0, + Logarithmic + }; + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel + }; + + enum TemperaturePreset + { + Candle=0, + Lamp40W, + Lamp100W, + Lamp200W, + Sunrise, + StudioLamp, + MoonLight, + Neutral, + DaylightD50, + Flash, + Sun, + XeonLamp, + DaylightD65, + None + }; + + uchar *m_destinationPreviewData; + + int m_currentPreviewMode; + + TQComboBox *m_channelCB; + + TQPushButton *m_pickTemperature; + TQPushButton *m_autoAdjustExposure; + + KDcrawIface::RComboBox *m_temperaturePresetCB; + + TQHButtonGroup *m_scaleBG; + + TQLabel *m_adjTemperatureLabel; + TQLabel *m_temperaturePresetLabel; + TQLabel *m_darkLabel; + TQLabel *m_blackLabel; + TQLabel *m_mainExposureLabel; + TQLabel *m_fineExposureLabel; + TQLabel *m_gammaLabel; + TQLabel *m_saturationLabel; + TQLabel *m_greenLabel; + + KActiveLabel *m_exposureLabel; + KActiveLabel *m_temperatureLabel; + + KDcrawIface::RDoubleNumInput *m_temperatureInput; + KDcrawIface::RDoubleNumInput *m_darkInput; + KDcrawIface::RDoubleNumInput *m_blackInput; + KDcrawIface::RDoubleNumInput *m_mainExposureInput; + KDcrawIface::RDoubleNumInput *m_fineExposureInput; + KDcrawIface::RDoubleNumInput *m_gammaInput; + KDcrawIface::RDoubleNumInput *m_saturationInput; + KDcrawIface::RDoubleNumInput *m_greenInput; + + Digikam::HistogramWidget *m_histogramWidget; + + Digikam::ColorGradientWidget *m_hGradient; + + Digikam::ImageWidget *m_previewWidget; + + Digikam::EditorToolSettings *m_gboxSettings; +}; + +} // NameSpace DigikamWhiteBalanceImagesPlugin + +#endif /* WHITEBALANCETOOL_H */ diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am new file mode 100644 index 00000000..a9af1e06 --- /dev/null +++ b/src/libs/Makefile.am @@ -0,0 +1,9 @@ +if with_included_sqlite3 + SQLITE3_SUBDIR = sqlite3 +endif + +COMPILE_FIRST = sqlite2 $(SQLITE3_SUBDIR) + +SUBDIRS = sqlite2 $(SQLITE3_SUBDIR) lprof histogram levels curves whitebalance dmetadata \ + dimg threadimageio themeengine widgets greycstoration \ + thumbbar jpegutils imageproperties dialogs diff --git a/src/libs/curves/Makefile.am b/src/libs/curves/Makefile.am new file mode 100644 index 00000000..f183fcc7 --- /dev/null +++ b/src/libs/curves/Makefile.am @@ -0,0 +1,16 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libcurves.la + +libcurves_la_SOURCES = imagecurves.cpp + +libcurves_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/digikam \ + $(all_includes) + +digikaminclude_HEADERS = imagecurves.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/curves/imagecurves.cpp b/src/libs/curves/imagecurves.cpp new file mode 100644 index 00000000..c5e067eb --- /dev/null +++ b/src/libs/curves/imagecurves.cpp @@ -0,0 +1,768 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image curves manipulation methods. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * Some code parts are inspired from gimp 2.0 + * app/base/curves.c, gimplut.c, and app/base/gimpcurvetool.c + * source files. + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x))) + +// C++ includes. + +#include +#include +#include +#include +#include + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "imagecurves.h" + +namespace Digikam +{ + +class ImageCurvesPriv +{ + +public: + + struct _Curves + { + ImageCurves::CurveType curve_type[5]; // Curve types by channels (Smooth or Free). + int points[5][17][2]; // Curve main points in Smooth mode ([channel][point id][x,y]). + unsigned short curve[5][65536]; // Curve values by channels. + }; + + struct _Lut + { + unsigned short **luts; + int nchannels; + }; + +public: + + ImageCurvesPriv() + { + curves = 0; + lut = 0; + dirty = false; + } + + // Curves data. + struct _Curves *curves; + + // Lut data. + struct _Lut *lut; + + int segmentMax; + + bool dirty; +}; + +ImageCurves::CRMatrix CR_basis = +{ + { -0.5, 1.5, -1.5, 0.5 }, + { 1.0, -2.5, 2.0, -0.5 }, + { -0.5, 0.0, 0.5, 0.0 }, + { 0.0, 1.0, 0.0, 0.0 }, +}; + +ImageCurves::ImageCurves(bool sixteenBit) +{ + d = new ImageCurvesPriv; + d->lut = new ImageCurvesPriv::_Lut; + d->curves = new ImageCurvesPriv::_Curves; + d->segmentMax = sixteenBit ? 65535 : 255; + + curvesReset(); +} + +ImageCurves::~ImageCurves() +{ + if (d->lut) + { + if (d->lut->luts) + { + for (int i = 0 ; i < d->lut->nchannels ; i++) + delete [] d->lut->luts[i]; + + delete [] d->lut->luts; + } + + delete d->lut; + } + + if (d->curves) + delete d->curves; + + delete d; +} + +bool ImageCurves::isDirty() +{ + return d->dirty; +} + +bool ImageCurves::isSixteenBits() +{ + return (d->segmentMax == 65535); +} + +void ImageCurves::curvesReset() +{ + memset(d->curves, 0, sizeof(struct ImageCurvesPriv::_Curves)); + d->lut->luts = NULL; + d->lut->nchannels = 0; + d->dirty = false; + + for (int channel = 0 ; channel < 5 ; channel++) + { + setCurveType(channel, CURVE_SMOOTH); + curvesChannelReset(channel); + } +} + +void ImageCurves::curvesChannelReset(int channel) +{ + int j; + + if (!d->curves) return; + + // Contruct a linear curve. + + for (j = 0 ; j <= d->segmentMax ; j++) + d->curves->curve[channel][j] = j; + + // Init coordinates points to null. + + for (j = 0 ; j < 17 ; j++) + { + d->curves->points[channel][j][0] = -1; + d->curves->points[channel][j][1] = -1; + } + + // First and last points init. + + d->curves->points[channel][0][0] = 0; + d->curves->points[channel][0][1] = 0; + d->curves->points[channel][16][0] = d->segmentMax; + d->curves->points[channel][16][1] = d->segmentMax; +} + +void ImageCurves::curvesCalculateCurve(int channel) +{ + int i; + int points[17]; + int num_pts; + int p1, p2, p3, p4; + + if (!d->curves) return; + + switch (d->curves->curve_type[channel]) + { + case CURVE_FREE: + break; + + case CURVE_SMOOTH: + { + // Cycle through the curves + + num_pts = 0; + + for (i = 0 ; i < 17 ; i++) + if (d->curves->points[channel][i][0] != -1) + points[num_pts++] = i; + + // Initialize boundary curve points + + if (num_pts != 0) + { + for (i = 0 ; i < d->curves->points[channel][points[0]][0] ; i++) + { + d->curves->curve[channel][i] = d->curves->points[channel][points[0]][1]; + } + + for (i = d->curves->points[channel][points[num_pts - 1]][0] ; i <= d->segmentMax ; i++) + { + d->curves->curve[channel][i] = d->curves->points[channel][points[num_pts - 1]][1]; + } + } + + for (i = 0 ; i < num_pts - 1 ; i++) + { + p1 = (i == 0) ? points[i] : points[(i - 1)]; + p2 = points[i]; + p3 = points[(i + 1)]; + p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)]; + + curvesPlotCurve(channel, p1, p2, p3, p4); + } + + // Ensure that the control points are used exactly + + for (i = 0 ; i < num_pts ; i++) + { + int x, y; + + x = d->curves->points[channel][points[i]][0]; + y = d->curves->points[channel][points[i]][1]; + d->curves->curve[channel][x] = y; + } + + break; + } + } +} + +float ImageCurves::curvesLutFunc(int n_channels, int channel, float value) +{ + float f; + int index; + double inten; + int j; + + if (!d->curves) return 0.0; + + if (n_channels == 1) + j = 0; + else + j = channel + 1; + + inten = value; + + // For color images this runs through the loop with j = channel +1 + // the first time and j = 0 the second time. + + // For bw images this runs through the loop with j = 0 the first and + // only time. + + for ( ; j >= 0 ; j -= (channel + 1)) + { + // Don't apply the overall curve to the alpha channel. + + if (j == 0 && (n_channels == 2 || n_channels == 4) && channel == n_channels -1) + return inten; + + if (inten < 0.0) + inten = d->curves->curve[j][0]/(float)d->segmentMax; + else if (inten >= 1.0) + inten = d->curves->curve[j][d->segmentMax]/(float)(d->segmentMax); + else // interpolate the curve. + { + index = (int)floor(inten * (float)(d->segmentMax)); + f = inten * (float)(d->segmentMax) - index; + inten = ((1.0 - f) * d->curves->curve[j][index ] + + ( f) * d->curves->curve[j][index + 1] ) / (float)(d->segmentMax); + } + } + + return inten; +} + +void ImageCurves::curvesPlotCurve(int channel, int p1, int p2, int p3, int p4) +{ + CRMatrix geometry; + CRMatrix tmp1, tmp2; + CRMatrix deltas; + double x, dx, dx2, dx3; + double y, dy, dy2, dy3; + double d1, d2, d3; + int lastx, lasty; + int newx, newy; + int i; + int loopdiv = d->segmentMax * 3; + + if (!d->curves) return; + + // Construct the geometry matrix from the segment. + + for (i = 0 ; i < 4 ; i++) + { + geometry[i][2] = 0; + geometry[i][3] = 0; + } + + for (i = 0 ; i < 2 ; i++) + { + geometry[0][i] = d->curves->points[channel][p1][i]; + geometry[1][i] = d->curves->points[channel][p2][i]; + geometry[2][i] = d->curves->points[channel][p3][i]; + geometry[3][i] = d->curves->points[channel][p4][i]; + } + + // Subdivide the curve 1000 times. + // n can be adjusted to give a finer or coarser curve. + + d1 = 1.0 / loopdiv; + d2 = d1 * d1; + d3 = d1 * d1 * d1; + + // Construct a temporary matrix for determining the forward differencing deltas. + + tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1; + tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d1; tmp2[1][3] = 0; + tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0; + tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0; + + // Compose the basis and geometry matrices. + + curvesCRCompose(CR_basis, geometry, tmp1); + + // Compose the above results to get the deltas matrix. + + curvesCRCompose(tmp2, tmp1, deltas); + + // Extract the x deltas. + + x = deltas[0][0]; + dx = deltas[1][0]; + dx2 = deltas[2][0]; + dx3 = deltas[3][0]; + + // Extract the y deltas. + + y = deltas[0][1]; + dy = deltas[1][1]; + dy2 = deltas[2][1]; + dy3 = deltas[3][1]; + + lastx = (int)CLAMP (x, 0, d->segmentMax); + lasty = (int)CLAMP (y, 0, d->segmentMax); + + d->curves->curve[channel][lastx] = lasty; + + // Loop over the curve. + + for (i = 0 ; i < loopdiv ; i++) + { + // Increment the x values. + + x += dx; + dx += dx2; + dx2 += dx3; + + // Increment the y values. + + y += dy; + dy += dy2; + dy2 += dy3; + + newx = CLAMP(ROUND (x), 0, d->segmentMax); + newy = CLAMP(ROUND (y), 0, d->segmentMax); + + // If this point is different than the last one...then draw it. + + if ((lastx != newx) || (lasty != newy)) + d->curves->curve[channel][newx] = newy; + + lastx = newx; + lasty = newy; + } +} + +void ImageCurves::curvesCRCompose(CRMatrix a, CRMatrix b, CRMatrix ab) +{ + int i, j; + + for (i = 0 ; i < 4 ; i++) + { + for (j = 0 ; j < 4 ; j++) + { + ab[i][j] = (a[i][0] * b[0][j] + + a[i][1] * b[1][j] + + a[i][2] * b[2][j] + + a[i][3] * b[3][j]); + } + } +} + +void ImageCurves::curvesLutSetup(int nchannels) +{ + int i; + uint v; + double val; + + if (d->lut->luts) + { + for (i = 0 ; i < d->lut->nchannels ; i++) + delete [] d->lut->luts[i]; + + delete [] d->lut->luts; + } + + d->lut->nchannels = nchannels; + d->lut->luts = new unsigned short*[d->lut->nchannels]; + + for (i = 0 ; i < d->lut->nchannels ; i++) + { + d->lut->luts[i] = new unsigned short[d->segmentMax+1]; + + for (v = 0 ; v <= (uint)d->segmentMax ; v++) + { + // To add gamma correction use func(v ^ g) ^ 1/g instead. + + val = (float)(d->segmentMax) * curvesLutFunc( d->lut->nchannels, i, v / (float)(d->segmentMax)) + 0.5; + + d->lut->luts[i][v] = (unsigned short)CLAMP (val, 0, d->segmentMax); + } + } +} + +void ImageCurves::curvesLutProcess(uchar *srcPR, uchar *destPR, int w, int h) +{ + unsigned short *lut0 = NULL, *lut1 = NULL, *lut2 = NULL, *lut3 = NULL; + + int i; + + if (d->lut->nchannels > 0) + lut0 = d->lut->luts[0]; + if (d->lut->nchannels > 1) + lut1 = d->lut->luts[1]; + if (d->lut->nchannels > 2) + lut2 = d->lut->luts[2]; + if (d->lut->nchannels > 3) + lut3 = d->lut->luts[3]; + + if (d->segmentMax == 255) // 8 bits image. + { + uchar red, green, blue, alpha; + uchar *ptr = srcPR; + uchar *dst = destPR; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if ( d->lut->nchannels > 0 ) + red = lut0[red]; + + if ( d->lut->nchannels > 1 ) + green = lut1[green]; + + if ( d->lut->nchannels > 2 ) + blue = lut2[blue]; + + if ( d->lut->nchannels > 3 ) + alpha = lut3[alpha]; + + dst[0] = blue; + dst[1] = green; + dst[2] = red; + dst[3] = alpha; + + ptr += 4; + dst += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue, alpha; + unsigned short *ptr = (unsigned short *)srcPR; + unsigned short *dst = (unsigned short *)destPR; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if ( d->lut->nchannels > 0 ) + red = lut0[red]; + + if ( d->lut->nchannels > 1 ) + green = lut1[green]; + + if ( d->lut->nchannels > 2 ) + blue = lut2[blue]; + + if ( d->lut->nchannels > 3 ) + alpha = lut3[alpha]; + + dst[0] = blue; + dst[1] = green; + dst[2] = red; + dst[3] = alpha; + + ptr += 4; + dst += 4; + } + } +} + +int ImageCurves::getCurveValue(int channel, int bin) +{ + if ( d->curves && + channel>=0 && channel<5 && + bin>=0 && bin<=d->segmentMax ) + return(d->curves->curve[channel][bin]); + + return 0; +} + +TQPoint ImageCurves::getCurvePoint(int channel, int point) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 ) + return(TQPoint(d->curves->points[channel][point][0], + d->curves->points[channel][point][1]) ); + + return TQPoint(-1, -1); +} + +TQPointArray ImageCurves::getCurvePoints(int channel) +{ + TQPointArray array(18); + + if ( d->curves && + channel>=0 && channel<5) + { + for (int j = 0 ; j <= 17 ; j++) + array.setPoint(j, getCurvePoint(channel, j)); + } + + return array; +} + +int ImageCurves::getCurvePointX(int channel, int point) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 ) + return(d->curves->points[channel][point][0]); + + return(-1); +} + +int ImageCurves::getCurvePointY(int channel, int point) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 ) + return(d->curves->points[channel][point][1]); + + return (-1); +} + +int ImageCurves::getCurveType(int channel) +{ + if ( d->curves && + channel>=0 && channel<5 ) + return ( d->curves->curve_type[channel] ); + + return (-1); +} + +void ImageCurves::setCurveValue(int channel, int bin, int val) +{ + if ( d->curves && + channel>=0 && channel<5 && + bin>=0 && bin<=d->segmentMax ) + { + d->dirty = true; + d->curves->curve[channel][bin] = val; + } +} + +void ImageCurves::setCurvePoint(int channel, int point, const TQPoint& val) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 && + val.x()>=-1 && val.x()<=d->segmentMax && // x can be egal to -1 + val.y()>=0 && val.y()<=d->segmentMax) // if the current point is disable !!! + { + d->dirty = true; + d->curves->points[channel][point][0] = val.x(); + d->curves->points[channel][point][1] = val.y(); + } +} + +void ImageCurves::setCurvePoints(int channel, const TQPointArray& vals) +{ + if ( d->curves && + channel>=0 && channel<5 && + vals.size() == 18 ) + { + d->dirty = true; + for (int j = 0 ; j <= 17 ; j++) + { + setCurvePoint(channel, j, vals.point(j)); + } + } +} + +void ImageCurves::setCurvePointX(int channel, int point, int x) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 && + x>=-1 && x<=d->segmentMax) // x can be egal to -1 if the current point is disable !!! + { + d->dirty = true; + d->curves->points[channel][point][0] = x; + } +} + +void ImageCurves::setCurvePointY(int channel, int point, int y) +{ + if ( d->curves && + channel>=0 && channel<5 && + point>=0 && point<=17 && + y>=0 && y<=d->segmentMax) + { + d->dirty = true; + d->curves->points[channel][point][1] = y; + } +} + +void ImageCurves::setCurveType(int channel, CurveType type) +{ + if ( d->curves && + channel>=0 && channel<5 && + type>=CURVE_SMOOTH && type<=CURVE_FREE ) + d->curves->curve_type[channel] = type; +} + +bool ImageCurves::loadCurvesFromGimpCurvesFile(const KURL& fileUrl) +{ + // TODO : support KURL ! + + FILE *file; + int i, j; + int fields; + char buf[50]; + int index[5][17]; + int value[5][17]; + + file = fopen(TQFile::encodeName(fileUrl.path()), "r"); + if (!file) + return false; + + if (! fgets (buf, sizeof (buf), file)) + { + fclose(file); + return false; + } + + if (strcmp (buf, "# GIMP Curves File\n") != 0) + return false; + + for (i = 0 ; i < 5 ; i++) + { + for (j = 0 ; j < 17 ; j++) + { + fields = fscanf (file, "%d %d ", &index[i][j], &value[i][j]); + if (fields != 2) + { + DWarning() << "Invalid Gimp curves file!" << endl; + fclose(file); + return false; + } + } + } + + curvesReset(); + + for (i = 0 ; i < 5 ; i++) + { + d->curves->curve_type[i] = CURVE_SMOOTH; + + for (j = 0 ; j < 17 ; j++) + { + d->curves->points[i][j][0] = ((d->segmentMax == 65535) && (index[i][j] !=-1) ? + index[i][j]*255 : index[i][j]); + d->curves->points[i][j][1] = ((d->segmentMax == 65535) && (value[i][j] !=-1) ? + value[i][j]*255 : value[i][j]); + } + } + + for (i = 0 ; i < 5 ; i++) + curvesCalculateCurve(i); + + fclose(file); + return true; +} + +bool ImageCurves::saveCurvesToGimpCurvesFile(const KURL& fileUrl) +{ + // TODO : support KURL ! + + FILE *file; + int i, j; + int index; + + file = fopen(TQFile::encodeName(fileUrl.path()), "w"); + + if (!file) + return false; + + for (i = 0 ; i < 5 ; i++) + { + if (d->curves->curve_type[i] == CURVE_FREE) + { + // Pick representative points from the curve and make them control points. + + for (j = 0 ; j <= 8 ; j++) + { + index = CLAMP(j * 32, 0, d->segmentMax); + d->curves->points[i][j * 2][0] = index; + d->curves->points[i][j * 2][1] = d->curves->curve[i][index]; + } + } + } + + fprintf (file, "# GIMP Curves File\n"); + + for (i = 0 ; i < 5 ; i++) + { + for (j = 0 ; j < 17 ; j++) + { + fprintf (file, "%d %d ", + ((d->segmentMax == 65535) && (d->curves->points[i][j][0]!=-1) ? + d->curves->points[i][j][0]/255 : d->curves->points[i][j][0]), + ((d->segmentMax == 65535) && (d->curves->points[i][j][1]!=-1) ? + d->curves->points[i][j][1]/255 : d->curves->points[i][j][1])); + + fprintf (file, "\n"); + } + } + + fflush(file); + fclose(file); + + return true; +} + +} // NameSpace Digikam diff --git a/src/libs/curves/imagecurves.h b/src/libs/curves/imagecurves.h new file mode 100644 index 00000000..69d33c08 --- /dev/null +++ b/src/libs/curves/imagecurves.h @@ -0,0 +1,111 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-01 + * Description : image curves manipulation methods. + * + * Copyright (c) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGECURVES_H +#define IMAGECURVES_H + +#define ROUND(x) ((int) ((x) + 0.5)) + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Digikam includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class ImageCurvesPriv; + +class DIGIKAM_EXPORT ImageCurves +{ + +public: + + enum CurveType + { + CURVE_SMOOTH = 0, // Smooth curve type + CURVE_FREE // Freehand curve type. + }; + + typedef double CRMatrix[4][4]; + +public: + + ImageCurves(bool sixteenBit); + ~ImageCurves(); + + // Methods for to manipulate the curves data. + + bool isDirty(); + bool isSixteenBits(); + void curvesReset(); + void curvesChannelReset(int channel); + void curvesCalculateCurve(int channel); + float curvesLutFunc(int n_channels, int channel, float value); + void curvesLutSetup(int nchannels); + void curvesLutProcess(uchar *srcPR, uchar *destPR, int w, int h); + + // Methods for to set manually the curves values. + + void setCurveValue(int channel, int bin, int val); + void setCurvePointX(int channel, int point, int x); + void setCurvePointY(int channel, int point, int y); + void setCurveType(int channel, CurveType type); + + void setCurvePoint(int channel, int point, const TQPoint& val); + void setCurvePoints(int channel, const TQPointArray& vals); + + int getCurveValue(int channel, int bin); + int getCurvePointX(int channel, int point); + int getCurvePointY(int channel, int point); + int getCurveType(int channel); + + TQPoint getCurvePoint(int channel, int point); + TQPointArray getCurvePoints(int channel); + + // Methods for to save/load the curves values to/from a Gimp curves text file. + + bool saveCurvesToGimpCurvesFile(const KURL& fileUrl); + bool loadCurvesFromGimpCurvesFile(const KURL& fileUrl); + +private: + + void curvesPlotCurve(int channel, int p1, int p2, int p3, int p4); + void curvesCRCompose(CRMatrix a, CRMatrix b, CRMatrix ab); + +private: + + ImageCurvesPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGECURVES_H */ diff --git a/src/libs/dialogs/Makefile.am b/src/libs/dialogs/Makefile.am new file mode 100644 index 00000000..19d50423 --- /dev/null +++ b/src/libs/dialogs/Makefile.am @@ -0,0 +1,33 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdialog.la libdialogshowfoto.la + +# Dialogs collection used by Showfoto. + +libdialogshowfoto_la_SOURCES = iccprofileinfodlg.cpp imagedialog.cpp rawcameradlg.cpp + +libdialogshowfoto_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +# Dialogs collection used by digiKam. + +libdialog_la_SOURCES = deletedialogbase.ui imagedialog.cpp rawcameradlg.cpp \ + iccprofileinfodlg.cpp deletedialog.cpp dprogressdlg.cpp + +libdialog_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/digikam \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/thumbbar \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/metadata \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/imageplugins \ + -I$(top_srcdir)/src/utilities/imageeditor/canvas \ + $(LIBKDCRAW_CFLAGS) \ + $(LIBKEXIV2_CFLAGS) \ + $(all_includes) + +digikaminclude_HEADERS = iccprofileinfodlg.h dprogressdlg.h imagedialog.h rawcameradlg.h +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/dialogs/ctrlpaneldlg.cpp b/src/libs/dialogs/ctrlpaneldlg.cpp new file mode 100644 index 00000000..450d380f --- /dev/null +++ b/src/libs/dialogs/ctrlpaneldlg.cpp @@ -0,0 +1,445 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : A threaded filter control panel dialog for + * image editor plugins using DImg + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimgthreadedfilter.h" +#include "dimginterface.h" +#include "ctrlpaneldlg.h" +#include "ctrlpaneldlg.moc" + +namespace Digikam +{ + +class CtrlPanelDlgPriv +{ +public: + + enum RunningMode + { + NoneRendering=0, + PreviewRendering, + FinalRendering + }; + + CtrlPanelDlgPriv() + { + parent = 0; + timer = 0; + aboutData = 0; + progressBar = true; + tryAction = false; + currentRenderingMode = NoneRendering; + } + + bool tryAction; + bool progressBar; + + int currentRenderingMode; + + TQWidget *parent; + + TQTimer *timer; + + TQString name; + + TDEAboutData *aboutData; +}; + +CtrlPanelDlg::CtrlPanelDlg(TQWidget* parent, TQString title, TQString name, + bool loadFileSettings, bool tryAction, bool progressBar, + int separateViewMode, TQFrame* bannerFrame) + : KDialogBase(Plain, 0, + Help|Default|User1|User2|User3|Try|Ok|Cancel, Ok, + parent, 0, true, true, + i18n("&Abort"), + i18n("&Save As..."), + i18n("&Load...")) +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + setCaption(DImgInterface::defaultInterface()->getImageFileName() + TQString(" - ") + title); + + d = new CtrlPanelDlgPriv; + d->parent = parent; + d->name = name; + d->tryAction = tryAction; + d->progressBar = progressBar; + m_threadedFilter = 0; + TQString whatsThis; + + setButtonWhatsThis ( Default, i18n("

    Reset all filter parameters to their default values.") ); + setButtonWhatsThis ( User1, i18n("

    Abort the current image rendering.") ); + setButtonWhatsThis ( User3, i18n("

    Load all filter parameters from settings text file.") ); + setButtonWhatsThis ( User2, i18n("

    Save all filter parameters to settings text file.") ); + showButton(User2, loadFileSettings); + showButton(User3, loadFileSettings); + showButton(Try, tryAction); + + // disable Abort button on startup + enableButton(User1, false); + + resize(configDialogSize(name + TQString(" Tool Dialog"))); + TQVBoxLayout *topLayout = new TQVBoxLayout( plainPage(), 0, spacingHint()); + + // ------------------------------------------------------------- + + if (bannerFrame) + { + bannerFrame->reparent( plainPage(), TQPoint(0, 0) ); + topLayout->addWidget(bannerFrame); + } + + // ------------------------------------------------------------- + + m_imagePreviewWidget = new ImagePannelWidget(470, 350, name + TQString(" Tool Dialog"), + plainPage(), separateViewMode); + topLayout->addWidget(m_imagePreviewWidget); + + // ------------------------------------------------------------- + + TQTimer::singleShot(0, this, TQ_SLOT(slotInit())); + kapp->restoreOverrideCursor(); +} + +CtrlPanelDlg::~CtrlPanelDlg() +{ + if (d->aboutData) + delete d->aboutData; + + if (d->timer) + delete d->timer; + + if (m_threadedFilter) + delete m_threadedFilter; + + delete d; +} + +void CtrlPanelDlg::slotInit() +{ + // Reset values to defaults. + TQTimer::singleShot(0, this, TQ_SLOT(readUserSettings())); + + if (!d->tryAction) + { + connect(m_imagePreviewWidget, TQ_SIGNAL(signalOriginalClipFocusChanged()), + this, TQ_SLOT(slotFocusChanged())); + } + else + { + connect(m_imagePreviewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotFocusChanged())); + } +} + +void CtrlPanelDlg::setAboutData(TDEAboutData *about) +{ + d->aboutData = about; + TQPushButton *helpButton = actionButton( Help ); + KHelpMenu* helpMenu = new KHelpMenu(this, d->aboutData, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("digiKam Handbook"), this, TQ_SLOT(slotHelp()), 0, -1, 0); + helpButton->setPopup( helpMenu->menu() ); +} + +void CtrlPanelDlg::abortPreview() +{ + d->currentRenderingMode = CtrlPanelDlgPriv::NoneRendering; + m_imagePreviewWidget->setProgress(0); + m_imagePreviewWidget->setPreviewImageWaitCursor(false); + m_imagePreviewWidget->setEnable(true); + m_imagePreviewWidget->setProgressVisible(false); + enableButton(Ok, true); + enableButton(User1, false); + enableButton(User2, true); + enableButton(User3, true); + enableButton(Try, true); + enableButton(Default, true); + renderingFinished(); +} + +void CtrlPanelDlg::slotTry() +{ + slotEffect(); +} + +void CtrlPanelDlg::slotUser1() +{ + if (d->currentRenderingMode != CtrlPanelDlgPriv::NoneRendering) + if (m_threadedFilter) + m_threadedFilter->stopComputation(); +} + +void CtrlPanelDlg::slotDefault() +{ + resetValues(); + slotEffect(); +} + +void CtrlPanelDlg::slotCancel() +{ + if (d->currentRenderingMode != CtrlPanelDlgPriv::NoneRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + + kapp->restoreOverrideCursor(); + } + + saveDialogSize(d->name + TQString(" Tool Dialog")); + done(Cancel); +} + +void CtrlPanelDlg::closeEvent(TQCloseEvent *e) +{ + if (d->currentRenderingMode != CtrlPanelDlgPriv::NoneRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + + kapp->restoreOverrideCursor(); + } + + saveDialogSize(d->name + TQString(" Tool Dialog")); + e->accept(); +} + +void CtrlPanelDlg::slotFocusChanged(void) +{ + if (d->currentRenderingMode == CtrlPanelDlgPriv::FinalRendering) + { + m_imagePreviewWidget->update(); + return; + } + else if (d->currentRenderingMode == CtrlPanelDlgPriv::PreviewRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + } + + TQTimer::singleShot(0, this, TQ_SLOT(slotEffect())); +} + +void CtrlPanelDlg::slotHelp() +{ + // If setAboutData() is called by plugin, well DigikamImagePlugins help is lauched, + // else digiKam help. In this case, setHelp() method must be used to set anchor and handbook name. + + if (d->aboutData) + TDEApplication::kApplication()->invokeHelp(d->name, "digikam"); + else + KDialogBase::slotHelp(); +} + +void CtrlPanelDlg::slotTimer() +{ + if (d->timer) + { + d->timer->stop(); + delete d->timer; + } + + d->timer = new TQTimer( this ); + connect( d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotEffect()) ); + d->timer->start(500, true); +} + +void CtrlPanelDlg::slotEffect() +{ + // Computation already in process. + if (d->currentRenderingMode != CtrlPanelDlgPriv::NoneRendering) + return; + + d->currentRenderingMode = CtrlPanelDlgPriv::PreviewRendering; + DDebug() << "Preview " << d->name << " started..." << endl; + + m_imagePreviewWidget->setEnable(false); + m_imagePreviewWidget->setProgressVisible(true); + enableButton(Ok, false); + enableButton(User1, true); + enableButton(User2, false); + enableButton(User3, false); + enableButton(Try, false); + enableButton(Default, false); + m_imagePreviewWidget->setPreviewImageWaitCursor(true); + m_imagePreviewWidget->setProgress(0); + + if (m_threadedFilter) + { + delete m_threadedFilter; + m_threadedFilter = 0; + } + + prepareEffect(); +} + +void CtrlPanelDlg::slotOk() +{ + d->currentRenderingMode = CtrlPanelDlgPriv::FinalRendering; + DDebug() << "Final " << d->name << " started..." << endl; + saveDialogSize(d->name + TQString(" Tool Dialog")); + writeUserSettings(); + + m_imagePreviewWidget->setEnable(false); + m_imagePreviewWidget->setProgressVisible(true); + enableButton(Ok, false); + enableButton(User1, false); + enableButton(User2, false); + enableButton(User3, false); + enableButton(Try, false); + enableButton(Default, false); + kapp->setOverrideCursor( KCursor::waitCursor() ); + m_imagePreviewWidget->setProgress(0); + + if (m_threadedFilter) + { + delete m_threadedFilter; + m_threadedFilter = 0; + } + + prepareFinal(); +} + +void CtrlPanelDlg::customEvent(TQCustomEvent *event) +{ + if (!event) return; + + DImgThreadedFilter::EventData *ed = (DImgThreadedFilter::EventData*) event->data(); + + if (!ed) return; + + if (ed->starting) // Computation in progress ! + { + m_imagePreviewWidget->setProgress(ed->progress); + } + else + { + if (ed->success) // Computation Completed ! + { + switch (d->currentRenderingMode) + { + case CtrlPanelDlgPriv::PreviewRendering: + { + DDebug() << "Preview " << d->name << " completed..." << endl; + putPreviewData(); + abortPreview(); + break; + } + + case CtrlPanelDlgPriv::FinalRendering: + { + DDebug() << "Final" << d->name << " completed..." << endl; + putFinalData(); + kapp->restoreOverrideCursor(); + accept(); + break; + } + } + } + else // Computation Failed ! + { + switch (d->currentRenderingMode) + { + case CtrlPanelDlgPriv::PreviewRendering: + { + DDebug() << "Preview " << d->name << " failed..." << endl; + // abortPreview() must be call here for set progress bar to 0 properly. + abortPreview(); + break; + } + + case CtrlPanelDlgPriv::FinalRendering: + break; + } + } + } + + delete ed; +} + +// Backport KDialog::keyPressEvent() implementation from KDELibs to ignore Enter/Return Key events +// to prevent any conflicts between dialog keys events and SpinBox keys events. + +void CtrlPanelDlg::keyPressEvent(TQKeyEvent *e) +{ + if ( e->state() == 0 ) + { + switch ( e->key() ) + { + case Key_Escape: + e->accept(); + reject(); + break; + case Key_Enter: + case Key_Return: + e->ignore(); + break; + default: + e->ignore(); + return; + } + } + else + { + // accept the dialog when Ctrl-Return is pressed + if ( e->state() == ControlButton && + (e->key() == Key_Return || e->key() == Key_Enter) ) + { + e->accept(); + accept(); + } + else + { + e->ignore(); + } + } +} + +} // NameSpace Digikam + diff --git a/src/libs/dialogs/ctrlpaneldlg.h b/src/libs/dialogs/ctrlpaneldlg.h new file mode 100644 index 00000000..eb60877e --- /dev/null +++ b/src/libs/dialogs/ctrlpaneldlg.h @@ -0,0 +1,110 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : A threaded filter control panel dialog for + * image editor plugins using DImg + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CTRLPANELDLG_H +#define CTRLPANELDLG_H + +// TQt includes + +#include + +// KDE include. + +#include + +// Local includes + +#include "imagepannelwidget.h" +#include "digikam_export.h" + +class TQFrame; + +namespace Digikam +{ + +class CtrlPanelDlgPriv; +class DImgThreadedFilter; + +class DIGIKAM_EXPORT CtrlPanelDlg : public KDialogBase +{ + TQ_OBJECT + + +public: + + CtrlPanelDlg(TQWidget* parent, TQString title, TQString name, + bool loadFileSettings=false, bool tryAction=false, bool progressBar=true, + int separateViewMode=ImagePannelWidget::SeparateViewAll, + TQFrame* bannerFrame=0); + ~CtrlPanelDlg(); + + void setAboutData(TDEAboutData *about); + +public: + + ImagePannelWidget *m_imagePreviewWidget; + + DImgThreadedFilter *m_threadedFilter; + +public slots: + + void slotTimer(); + void slotEffect(); + void slotOk(); + void slotTry(); + +private slots: + + virtual void slotDefault(); + virtual void slotCancel(); + virtual void slotUser1(); + virtual void slotInit(); + virtual void readUserSettings(void){ slotDefault(); }; + + void slotHelp(); + void slotFocusChanged(void); + +protected: + + void closeEvent(TQCloseEvent *e); + void customEvent(TQCustomEvent *event); + void abortPreview(void); + void keyPressEvent(TQKeyEvent *e); + + virtual void writeUserSettings(void){}; + virtual void resetValues(void){}; + virtual void prepareEffect(void){}; + virtual void prepareFinal(void){}; + virtual void putPreviewData(void){}; + virtual void putFinalData(void){}; + virtual void renderingFinished(void){}; + +private: + + CtrlPanelDlgPriv* d; +}; + +} // NameSpace Digikam + +#endif /* CTRLPANELDLG_H */ diff --git a/src/libs/dialogs/deletedialog.cpp b/src/libs/dialogs/deletedialog.cpp new file mode 100644 index 00000000..1db9b9c4 --- /dev/null +++ b/src/libs/dialogs/deletedialog.cpp @@ -0,0 +1,309 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : a dialog to delete item. + * + * Copyright (C) 2004 by Michael Pyne + * Copyright (C) 2006 by Ian Monroe + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "deletedialog.h" +#include "albumsettings.h" +#include "deletedialog.moc" + +namespace Digikam +{ + +////////////////////////////////////////////////////////////////////////////// +// DeleteWidget implementation +////////////////////////////////////////////////////////////////////////////// + +DeleteWidget::DeleteWidget(TQWidget *parent, const char *name) + : DeleteDialogBase(parent, name), + m_listMode(DeleteDialogMode::Files), + m_deleteMode(DeleteDialogMode::UseTrash) +{ + ddCheckBoxStack->raiseWidget(ddShouldDeletePage); + + bool deleteInstead = !AlbumSettings::instance()->getUseTrash(); + slotShouldDelete(deleteInstead); + ddShouldDelete->setChecked(deleteInstead); +} + +void DeleteWidget::setFiles(const KURL::List &files) +{ + ddFileList->clear(); + for( KURL::List::ConstIterator it = files.begin(); it != files.end(); it++) + { + if( (*it).isLocalFile() ) //path is nil for non-local + ddFileList->insertItem( (*it).path() ); + else if ( (*it).protocol() == "digikamalbums") + ddFileList->insertItem( (*it).path() ); + else + ddFileList->insertItem( (*it).prettyURL() ); + } + updateText(); +} + +void DeleteWidget::slotShouldDelete(bool shouldDelete) +{ + setDeleteMode(shouldDelete ? DeleteDialogMode::DeletePermanently : DeleteDialogMode::UseTrash); +} + +void DeleteWidget::setDeleteMode(DeleteDialogMode::DeleteMode deleteMode) +{ + m_deleteMode = deleteMode; + updateText(); +} + +void DeleteWidget::setListMode(DeleteDialogMode::ListMode listMode) +{ + m_listMode = listMode; + updateText(); +} + +void DeleteWidget::updateText() +{ + switch (m_listMode) + { + case DeleteDialogMode::Files: + + // Delete files + + if (m_deleteMode == DeleteDialogMode::DeletePermanently) + { + ddDeleteText->setText(i18n("These items will be permanently " + "deleted from your hard disk.")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("messagebox_warning", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + else + { + ddDeleteText->setText(i18n("These items will be moved to Trash.")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("trashcan_full", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + ddNumFiles->setText(i18n("1 file selected.", "%n files selected.", ddFileList->count())); + break; + + case DeleteDialogMode::Albums: + + // Delete albums = folders + + if (m_deleteMode == DeleteDialogMode::DeletePermanently) + { + ddDeleteText->setText(i18n("These albums will be permanently " + "deleted from your hard disk.")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("messagebox_warning", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + else + { + ddDeleteText->setText(i18n("These albums will be moved to Trash.")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("trashcan_full", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + ddNumFiles->setText(i18n("1 album selected.", "%n albums selected.", ddFileList->count())); + break; + + case DeleteDialogMode::Subalbums: + + // As above, but display additional warning + + if (m_deleteMode == DeleteDialogMode::DeletePermanently) + { + ddDeleteText->setText(i18n("These albums will be permanently " + "deleted from your hard disk.
    " + "Note that all subalbums " + "are included in this list and will " + "be deleted permanently as well.
    ")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("messagebox_warning", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + else + { + ddDeleteText->setText(i18n("These albums will be moved to Trash.
    " + "Note that all subalbums " + "are included in this list and will " + "be moved to Trash as well.
    ")); + ddWarningIcon->setPixmap(TDEGlobal::iconLoader()->loadIcon("trashcan_full", + TDEIcon::Desktop, TDEIcon::SizeLarge)); + } + ddNumFiles->setText(i18n("1 album selected.", "%n albums selected.", ddFileList->count())); + break; + + } +} + +////////////////////////////////////////////////////////////////////////////// +// DeleteDialog implementation +////////////////////////////////////////////////////////////////////////////// + +DeleteDialog::DeleteDialog(TQWidget *parent, const char *name) + : KDialogBase(Swallow, WStyle_DialogBorder, parent, name, + true, // modal + i18n("About to delete selected files"), // caption + Ok | Cancel, // available buttons + Ok, // default button + true // use separator between buttons and the main widget + ), + m_saveShouldDeleteUserPreference(true), + m_saveDoNotShowAgain(false), + m_trashGuiItem(i18n("&Move to Trash"), "trashcan_full") +{ + m_widget = new DeleteWidget(this, "delete_dialog_widget"); + setMainWidget(m_widget); + + m_widget->setMinimumSize(400, 300); + setMinimumSize(410, 326); + adjustSize(); + + slotShouldDelete(shouldDelete()); + connect(m_widget->ddShouldDelete, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotShouldDelete(bool))); + + actionButton(Ok)->setFocus(); +} + +bool DeleteDialog::confirmDeleteList(const KURL::List& condemnedFiles, + DeleteDialogMode::ListMode listMode, + DeleteDialogMode::DeleteMode deleteMode) +{ + setURLs(condemnedFiles); + presetDeleteMode(deleteMode); + setListMode(listMode); + + if (deleteMode == DeleteDialogMode::NoChoiceTrash) + { + if (!AlbumSettings::instance()->getShowTrashDeleteDialog()) + return true; + } + return exec() == TQDialog::Accepted; +} + +void DeleteDialog::setURLs(const KURL::List &files) +{ + m_widget->setFiles(files); +} + +void DeleteDialog::accept() +{ + // Save user's preference + AlbumSettings *settings = AlbumSettings::instance(); + + if (m_saveShouldDeleteUserPreference) + { + settings->setUseTrash(!shouldDelete()); + } + if (m_saveDoNotShowAgain) + { + settings->setShowTrashDeleteDialog(!m_widget->ddDoNotShowAgain->isChecked()); + } + + settings->saveSettings(); + + KDialogBase::accept(); +} + +void DeleteDialog::slotShouldDelete(bool shouldDelete) +{ + // This is called once from constructor, and then when the user changed the checkbox state. + // In that case, save the user's preference. + m_saveShouldDeleteUserPreference = true; + setButtonGuiItem(Ok, shouldDelete ? KStdGuiItem::del() : m_trashGuiItem); +} + +void DeleteDialog::presetDeleteMode(DeleteDialogMode::DeleteMode mode) +{ + switch (mode) + { + case DeleteDialogMode::NoChoiceTrash: + { + // access the widget directly, signals will be fired to DeleteDialog and DeleteWidget + m_widget->ddShouldDelete->setChecked(false); + m_widget->ddCheckBoxStack->raiseWidget(m_widget->ddDoNotShowAgainPage); + m_saveDoNotShowAgain = true; + break; + } + case DeleteDialogMode::NoChoiceDeletePermanently: + { + m_widget->ddShouldDelete->setChecked(true); + m_widget->ddCheckBoxStack->hide(); + break; + } + case DeleteDialogMode::UserPreference: + { + break; + } + case DeleteDialogMode::UseTrash: + case DeleteDialogMode::DeletePermanently: + { + // toggles signals which do the rest + m_widget->ddShouldDelete->setChecked(mode == DeleteDialogMode::DeletePermanently); + + // the preference set by this preset method will be ignored + // for the next DeleteDialog instance and not stored as user preference. + // Only if the user once changes this value, it will be taken as user preference. + m_saveShouldDeleteUserPreference = false; + break; + } + } +} + +void DeleteDialog::setListMode(DeleteDialogMode::ListMode mode) +{ + m_widget->setListMode(mode); + switch (mode) + { + case DeleteDialogMode::Files: + setCaption(i18n("About to delete selected files")); + break; + + case DeleteDialogMode::Albums: + case DeleteDialogMode::Subalbums: + setCaption(i18n("About to delete selected albums")); + break; + } +} + +} // namespace Digikam diff --git a/src/libs/dialogs/deletedialog.h b/src/libs/dialogs/deletedialog.h new file mode 100644 index 00000000..16ab9f89 --- /dev/null +++ b/src/libs/dialogs/deletedialog.h @@ -0,0 +1,140 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : a dialog to delete item. + * + * Copyright (C) 2004 by Michael Pyne + * Copyright (C) 2006 by Ian Monroe + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef _DELETEDIALOG_H +#define _DELETEDIALOG_H + +// TQt includes. + +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" +#include "deletedialogbase.h" + +class TQStringList; +class TDEListBox; +class KGuiItem; +class TQLabel; +class TQWidgetStack; + +namespace Digikam +{ + +namespace DeleteDialogMode +{ + enum ListMode + { + Files, + Albums, + Subalbums + }; + + enum DeleteMode + { + NoChoiceTrash, // "Do not show again" checkbox, does not show if config entry is set + NoChoiceDeletePermanently, // No checkbox + UserPreference, // Checkbox to toggle trash/permanent, preset to user's last preference + UseTrash, // same beckbox as above, preset to trash + DeletePermanently // same checkbox as above, preset to permanent + }; +} + +class DeleteWidget : public DeleteDialogBase +{ + TQ_OBJECT + + +public: + + DeleteWidget(TQWidget *parent = 0, const char *name = 0); + + void setFiles(const KURL::List &files); + void setListMode(DeleteDialogMode::ListMode mode); + void setDeleteMode(DeleteDialogMode::DeleteMode deleteMode); + +protected slots: + + void slotShouldDelete(bool shouldDelete); + +protected: + + void updateText(); + DeleteDialogMode::ListMode m_listMode; + DeleteDialogMode::DeleteMode m_deleteMode; +}; + +class DIGIKAM_EXPORT DeleteDialog : public KDialogBase +{ + TQ_OBJECT + + +public: + + enum Mode + { + ModeFiles, + ModeAlbums, + ModeSubalbums + }; + +public: + + DeleteDialog(TQWidget *parent, const char *name = "delete_dialog"); + + bool confirmDeleteList(const KURL::List &condemnedURLs, + DeleteDialogMode::ListMode listMode, + DeleteDialogMode::DeleteMode deleteMode); + bool shouldDelete() const { return m_widget->ddShouldDelete->isChecked(); } + + void setURLs(const KURL::List &files); + void presetDeleteMode(DeleteDialogMode::DeleteMode mode); + void setListMode(DeleteDialogMode::ListMode mode); + +protected slots: + + virtual void accept(); + void slotShouldDelete(bool shouldDelete); + +private: + + bool m_saveShouldDeleteUserPreference; + bool m_saveDoNotShowAgain; + + KGuiItem m_trashGuiItem; + + DeleteWidget *m_widget; +}; + +} // namespace Digikam + +#endif // _DELETEDIALOG_H + diff --git a/src/libs/dialogs/deletedialogbase.ui b/src/libs/dialogs/deletedialogbase.ui new file mode 100644 index 00000000..8527a903 --- /dev/null +++ b/src/libs/dialogs/deletedialogbase.ui @@ -0,0 +1,188 @@ + + DeleteDialogBase + + + DeleteDialogBase + + + + 0 + 0 + 517 + 162 + + + + DeleteDialogBase + + + + 0 + 0 + 542 + 374 + + + + + 420 + 320 + + + + + unnamed + + + 0 + + + + layout4 + + + + unnamed + + + + ddWarningIcon + + + + 4 + 4 + 0 + 0 + + + + Icon Placeholder, not in GUI + + + + + layout3 + + + + unnamed + + + + ddDeleteText + + + Deletion method placeholder, never shown to user. + + + WordBreak|AlignCenter + + + + + + + + + ddFileList + + + NoSelection + + + List of files that are about to be deleted. + + + This is the list of items that are about to be deleted. + + + + + ddNumFiles + + + Placeholder for number of files, not in GUI + + + AlignVCenter|AlignRight + + + + + ddCheckBoxStack + + + + ddShouldDeletePage + + + 0 + + + + unnamed + + + 0 + + + + ddShouldDelete + + + &Delete files instead of moving them to the trash + + + If checked, files will be permanently removed instead of being placed in the Trash Bin + + + <qt><p>If this box is checked, files will be <b>permanently removed</b> instead of being placed in the Trash Bin.</p> + + <p><em>Use this option with caution</em>: most filesystems are unable to undelete deleted files reliably.</p></qt> + + + + + + + ddDoNotShowAgainPage + + + 1 + + + + unnamed + + + 0 + + + + ddDoNotShowAgain + + + Do not &ask again + + + If checked, this dialog will no longer be shown, and files will be directly moved to the Trash Bin + + + <qt><p>If this box is checked, this dialog will no longer be shown, and files will be directly moved to the Trash Bin</p> + + + + + + + + + + + + + + tdelistbox.h + + diff --git a/src/libs/dialogs/dprogressdlg.cpp b/src/libs/dialogs/dprogressdlg.cpp new file mode 100644 index 00000000..d4b0dc29 --- /dev/null +++ b/src/libs/dialogs/dprogressdlg.cpp @@ -0,0 +1,224 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-30-08 + * Description : a progress dialog for digiKam + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dprogressdlg.h" +#include "dprogressdlg.moc" + +namespace Digikam +{ + +class DProgressDlgPriv +{ +public: + + DProgressDlgPriv() + { + progress = 0; + actionsList = 0; + logo = 0; + title = 0; + label = 0; + allowCancel = true; + cancelled = false; + } + + bool allowCancel; + bool cancelled; + + TQLabel *logo; + TQLabel *title; + TQLabel *label; + + TQListView *actionsList; + + KProgress *progress; +}; + +DProgressDlg::DProgressDlg(TQWidget *parent, const TQString &caption) + : KDialogBase(parent, 0, true, caption, Cancel) +{ + d = new DProgressDlgPriv; + + TQFrame *page = makeMainWidget(); + TQGridLayout* grid = new TQGridLayout(page, 1, 1, 0, spacingHint()); + TQVBoxLayout *vlay = new TQVBoxLayout(); + d->actionsList = new TQListView(page); + d->label = new TQLabel(page); + d->title = new TQLabel(page); + d->logo = new TQLabel(page); + d->progress = new KProgress(page); + vlay->addWidget(d->logo); + vlay->addWidget(d->progress); + vlay->addWidget(d->title); + vlay->addStretch(); + + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + d->logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 128, TDEIcon::DefaultState, 0, true)); + + d->actionsList->addColumn("Thumb"); // no i18n here: hiden column + d->actionsList->addColumn("Status"); // no i18n here: hiden column + d->actionsList->setSorting(-1); + d->actionsList->setItemMargin(1); + d->actionsList->setSelectionMode(TQListView::NoSelection); + d->actionsList->header()->hide(); + d->actionsList->setResizeMode(TQListView::LastColumn); + + grid->addMultiCellLayout(vlay, 0, 1, 0, 0); + grid->addMultiCellWidget(d->label, 0, 0, 1, 1); + grid->addMultiCellWidget(d->actionsList, 1, 1, 1, 1); + grid->setRowStretch(1, 10); + grid->setColStretch(1, 10); +} + +DProgressDlg::~DProgressDlg() +{ + delete d; +} + +void DProgressDlg::slotCancel() +{ + d->cancelled = true; + + if (d->allowCancel) + { + KDialogBase::slotCancel(); + } +} + +void DProgressDlg::setButtonText(const TQString &text) +{ + KDialogBase::setButtonText(Cancel, text); +} + +void DProgressDlg::addedAction(const TQPixmap& pix, const TQString &text) +{ + TQImage img; + TQListViewItem *item = new TQListViewItem(d->actionsList, + d->actionsList->lastItem(), TQString(), text); + + if (pix.isNull()) + { + TQString dir = TDEGlobal::dirs()->findResourceDir("digikam_imagebroken", + "image-broken.png"); + dir = dir + "/image-broken.png"; + TQPixmap pixbi(dir); + img = pixbi.convertToImage().scale(32, 32, TQImage::ScaleMin); + } + else + { + img = pix.convertToImage().scale(32, 32, TQImage::ScaleMin); + } + + TQPixmap pixmap(img); + item->setPixmap(0, pixmap); + d->actionsList->ensureItemVisible(item); +} + +void DProgressDlg::reset() +{ + d->actionsList->clear(); + d->progress->setValue(0); +} + +void DProgressDlg::setTotalSteps(int total) +{ + d->progress->setTotalSteps(total); +} + +void DProgressDlg::setValue(int value) +{ + d->progress->setValue(value); +} + +void DProgressDlg::advance(int value) +{ + d->progress->advance(value); +} + +void DProgressDlg::setLabel(const TQString &text) +{ + d->label->setText(text); +} + +void DProgressDlg::setTitle(const TQString &text) +{ + d->title->setText(text); +} + +void DProgressDlg::showCancelButton(bool show) +{ + showButtonCancel(show); +} + +void DProgressDlg::setAllowCancel(bool allowCancel) +{ + d->allowCancel = allowCancel; + showCancelButton(allowCancel); +} + +bool DProgressDlg::allowCancel() const +{ + return d->allowCancel; +} + +bool DProgressDlg::wasCancelled() const +{ + return d->cancelled; +} + +KProgress *DProgressDlg::progressBar() const +{ + return d->progress; +} + +void DProgressDlg::setActionListVSBarVisible(bool visible) +{ + if (!visible) + d->actionsList->setVScrollBarMode(TQScrollView::AlwaysOff); + else + d->actionsList->setVScrollBarMode(TQScrollView::Auto); +} + +} // NameSpace Digikam diff --git a/src/libs/dialogs/dprogressdlg.h b/src/libs/dialogs/dprogressdlg.h new file mode 100644 index 00000000..d75f509d --- /dev/null +++ b/src/libs/dialogs/dprogressdlg.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-30-08 + * Description : a progress dialog for digiKam + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DPROGRESSDLG_H +#define DPROGRESSDLG_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class KProgress; + +namespace Digikam +{ + +class DProgressDlgPriv; + +class DIGIKAM_EXPORT DProgressDlg : public KDialogBase +{ +TQ_OBJECT + + + public: + + DProgressDlg(TQWidget *parent=0, const TQString &caption=TQString()); + ~DProgressDlg(); + + void setButtonText(const TQString &text); + void addedAction(const TQPixmap& pix, const TQString &text); + void reset(); + void setTotalSteps(int total); + void setValue(int value); + void advance(int value); + void setLabel(const TQString &text); + void setTitle(const TQString &text); + void setActionListVSBarVisible(bool visible); + void showCancelButton(bool show); + void setAllowCancel(bool allowCancel); + bool wasCancelled() const; + bool allowCancel() const; + + KProgress *progressBar() const; + + protected slots: + + void slotCancel(); + + private: + + DProgressDlgPriv* d; +}; + +} // NameSpace Digikam + +#endif // DPROGRESSDLG_H diff --git a/src/libs/dialogs/iccprofileinfodlg.cpp b/src/libs/dialogs/iccprofileinfodlg.cpp new file mode 100644 index 00000000..04f30c0c --- /dev/null +++ b/src/libs/dialogs/iccprofileinfodlg.cpp @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-16 + * Description : a dialog to display icc profile information. + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "iccprofilewidget.h" +#include "iccprofileinfodlg.h" + +namespace Digikam +{ + +ICCProfileInfoDlg::ICCProfileInfoDlg(TQWidget* parent, const TQString& profilePath, + const TQByteArray& profileData) + : KDialogBase(parent, 0, true, i18n("Color Profile Info"), + Help|Ok, Ok, true) +{ + setHelp("iccprofile.anchor", "digikam"); + setCaption(profilePath); + + ICCProfileWidget *profileWidget = new ICCProfileWidget(this, 0, 340, 256); + + if (profileData.isEmpty()) + profileWidget->loadFromURL(KURL(profilePath)); + else + profileWidget->loadFromData(profilePath, profileData); + + setMainWidget(profileWidget); +} + +ICCProfileInfoDlg::~ICCProfileInfoDlg() +{ +} + +} // NameSpace Digikam + diff --git a/src/libs/dialogs/iccprofileinfodlg.h b/src/libs/dialogs/iccprofileinfodlg.h new file mode 100644 index 00000000..abcc1ed8 --- /dev/null +++ b/src/libs/dialogs/iccprofileinfodlg.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-16 + * Description : a dialog to display ICC profile information. + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICCPROFILEINFODLG_H +#define ICCPROFILEINFODLG_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQWidget; + +namespace Digikam +{ + +class ICCProfileInfoDlgPriv; + +class DIGIKAM_EXPORT ICCProfileInfoDlg : public KDialogBase +{ + +public: + + ICCProfileInfoDlg(TQWidget *parent, const TQString& profilePath, const TQByteArray& profileData=TQByteArray()); + ~ICCProfileInfoDlg(); + +}; + +} // Namespace Digikam + +#endif /* ICCPROFILEINFODLG_H */ diff --git a/src/libs/dialogs/imagedialog.cpp b/src/libs/dialogs/imagedialog.cpp new file mode 100644 index 00000000..d052a2af --- /dev/null +++ b/src/libs/dialogs/imagedialog.cpp @@ -0,0 +1,366 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-03-13 + * Description : image files selector dialog. + * + * Copyright (C) 2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "thumbnailsize.h" +#include "thumbnailjob.h" +#include "imagedialog.h" +#include "imagedialog.moc" + +namespace Digikam +{ + +class ImageDialogPreviewPrivate +{ + +public: + + ImageDialogPreviewPrivate() + { + imageLabel = 0; + infoLabel = 0; + thumbJob = 0; + timer = 0; + } + + TQTimer *timer; + + TQLabel *imageLabel; + TQLabel *infoLabel; + + KURL currentURL; + + DMetadata metaIface; + + TQGuardedPtr thumbJob; +}; + +ImageDialogPreview::ImageDialogPreview(TQWidget *parent) + : KPreviewWidgetBase(parent) +{ + d = new ImageDialogPreviewPrivate; + + TQVBoxLayout *vlay = new TQVBoxLayout(this); + d->imageLabel = new TQLabel(this); + d->imageLabel->setAlignment(TQt::AlignHCenter | TQt::AlignVCenter); + d->imageLabel->setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding)); + + d->infoLabel = new TQLabel(this); + + vlay->setMargin(0); + vlay->setSpacing(KDialog::spacingHint()); + vlay->addWidget(d->imageLabel); + vlay->addWidget(d->infoLabel); + + setSupportedMimeTypes(KImageIO::mimeTypes()); + + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(showPreview()) ); +} + +ImageDialogPreview::~ImageDialogPreview() +{ + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + delete d; +} + +TQSize ImageDialogPreview::sizeHint() const +{ + return TQSize(256, 256); +} + +void ImageDialogPreview::resizeEvent(TQResizeEvent *) +{ + d->timer->start(100, true); +} + +void ImageDialogPreview::showPreview() +{ + KURL url(d->currentURL); + clearPreview(); + showPreview(url); +} + +void ImageDialogPreview::showPreview(const KURL& url) +{ + if (!url.isValid()) + { + clearPreview(); + return; + } + + if (url != d->currentURL) + { + clearPreview(); + d->currentURL = url; + + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + d->thumbJob = new ThumbnailJob(url, ThumbnailSize::Huge, true, true); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnail(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotFailedThumbnail(const KURL&))); + + d->metaIface.load(d->currentURL.path()); + PhotoInfoContainer info = d->metaIface.getPhotographInformations(); + if (!info.isEmpty()) + { + TQString identify; + TQString make, model, dateTime, aperture, focalLength, exposureTime, sensitivity; + TQString unavailable(i18n("unavailable")); + TQString cellBeg(""); + TQString cellMid(""); + TQString cellEnd(""); + + if (info.make.isEmpty()) make = unavailable; + else make = info.make; + + if (info.model.isEmpty()) model = unavailable; + else model = info.model; + + if (!info.dateTime.isValid()) dateTime = unavailable; + else dateTime = TDEGlobal::locale()->formatDateTime(info.dateTime, true, true); + + if (info.aperture.isEmpty()) aperture = unavailable; + else aperture = info.aperture; + + if (info.focalLength.isEmpty()) focalLength = unavailable; + else focalLength = info.focalLength; + + if (info.exposureTime.isEmpty()) exposureTime = unavailable; + else exposureTime = info.exposureTime; + + if (info.sensitivity.isEmpty()) sensitivity = unavailable; + else sensitivity = i18n("%1 ISO").arg(info.sensitivity); + + identify = ""; + identify += cellBeg + i18n("Make:") + cellMid + make + cellEnd; + identify += cellBeg + i18n("Model:") + cellMid + model + cellEnd; + identify += cellBeg + i18n("Created:") + cellMid + dateTime + cellEnd; + identify += cellBeg + i18n("Aperture:") + cellMid + aperture + cellEnd; + identify += cellBeg + i18n("Focal:") + cellMid + focalLength + cellEnd; + identify += cellBeg + i18n("Exposure:") + cellMid + exposureTime + cellEnd; + identify += cellBeg + i18n("Sensitivity:") + cellMid + sensitivity + cellEnd; + identify += "
    "; + + d->infoLabel->setText(identify); + } + else + d->infoLabel->clear(); + } +} + +void ImageDialogPreview::slotGotThumbnail(const KURL& url, const TQPixmap& pix) +{ + if (url == d->currentURL) + { + TQPixmap pixmap; + TQSize s = d->imageLabel->contentsRect().size(); + + if (s.width() < pix.width() || s.height() < pix.height()) + pixmap = pix.convertToImage().smoothScale(s, TQImage::ScaleMin); + else + pixmap = pix; + + d->imageLabel->setPixmap(pixmap); + } +} + +void ImageDialogPreview::slotFailedThumbnail(const KURL& /*url*/) +{ + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + d->imageLabel->setPixmap(iconLoader->loadIcon("image-x-generic", TDEIcon::NoGroup, 128, + TDEIcon::DefaultState, 0, true)); +} + +void ImageDialogPreview::clearPreview() +{ + d->imageLabel->clear(); + d->infoLabel->clear(); + d->currentURL = KURL(); +} + +// ------------------------------------------------------------------------ + +class ImageDialogPrivate +{ + +public: + + ImageDialogPrivate() + { + singleSelect = false; + } + + bool singleSelect; + + TQString fileformats; + + KURL url; + KURL::List urls; +}; + +ImageDialog::ImageDialog(TQWidget* parent, const KURL &url, bool singleSelect, const TQString& caption) +{ + d = new ImageDialogPrivate; + d->singleSelect = singleSelect; + + TQStringList patternList = TQStringList::split('\n', KImageIO::pattern(KImageIO::Reading)); + + // All Images from list must been always the first entry given by KDE API + TQString allPictures = patternList[0]; + +#if KDCRAW_VERSION < 0x000106 + // Add other files format witch are missing to All Images" type mime provided by KDE and remplace current. + if (KDcrawIface::DcrawBinary::instance()->versionIsRight()) + { + allPictures.insert(allPictures.find("|"), TQString(KDcrawIface::DcrawBinary::instance()->rawFiles()) + TQString(" *.JPE *.TIF")); + patternList.remove(patternList[0]); + patternList.prepend(allPictures); + // Added RAW file formats supported by dcraw program like a type mime. + // Nota: we cannot use here "image/x-raw" type mime from KDE because it uncomplete + // or unavailable (see file #121242 in B.K.O). + patternList.append(i18n("\n%1|Camera RAW files").arg(TQString(KDcrawIface::DcrawBinary::instance()->rawFiles()))); + } +#else + allPictures.insert(allPictures.find("|"), TQString(KDcrawIface::KDcraw::rawFiles()) + TQString(" *.JPE *.TIF")); + patternList.remove(patternList[0]); + patternList.prepend(allPictures); + // Added RAW file formats supported by dcraw program like a type mime. + // Nota: we cannot use here "image/x-raw" type mime from KDE because it uncomplete + // or unavailable (see file #121242 in B.K.O). + patternList.append(i18n("\n%1|Camera RAW files").arg(TQString(KDcrawIface::KDcraw::rawFiles()))); +#endif + + d->fileformats = patternList.join("\n"); + + DDebug() << "fileformats=" << d->fileformats << endl; + + KFileDialog dlg(url.path(), d->fileformats, parent, "imageFileOpenDialog", false); + ImageDialogPreview *preview = new ImageDialogPreview(&dlg); + dlg.setPreviewWidget(preview); + dlg.setOperationMode(KFileDialog::Opening); + + if (d->singleSelect) + { + dlg.setMode(KFile::File); + if (caption.isEmpty()) dlg.setCaption(i18n("Select an Image")); + else dlg.setCaption(caption); + dlg.exec(); + d->url = dlg.selectedURL(); + } + else + { + dlg.setMode(KFile::Files); + if (caption.isEmpty()) dlg.setCaption(i18n("Select Images")); + else dlg.setCaption(caption); + dlg.exec(); + d->urls = dlg.selectedURLs(); + } +} + +ImageDialog::~ImageDialog() +{ + delete d; +} + +bool ImageDialog::singleSelect() const +{ + return d->singleSelect; +} + +TQString ImageDialog::fileformats() const +{ + return d->fileformats; +} + +KURL ImageDialog::url() const +{ + return d->url; +} + +KURL::List ImageDialog::urls() const +{ + return d->urls; +} + +KURL::List ImageDialog::getImageURLs(TQWidget* parent, const KURL& url, const TQString& caption) +{ + ImageDialog dlg(parent, url, false, caption); + if (!dlg.urls().isEmpty()) + return dlg.urls(); + else + return KURL::List(); +} + +KURL ImageDialog::getImageURL(TQWidget* parent, const KURL& url, const TQString& caption) +{ + ImageDialog dlg(parent, url, true, caption); + if (dlg.url() != KURL()) + return dlg.url(); + else + return KURL(); +} + +} // namespace Digikam diff --git a/src/libs/dialogs/imagedialog.h b/src/libs/dialogs/imagedialog.h new file mode 100644 index 00000000..275765cd --- /dev/null +++ b/src/libs/dialogs/imagedialog.h @@ -0,0 +1,100 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-03-13 + * Description : image files selector dialog. + * + * Copyright (C) 2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEDIALOG_H +#define IMAGEDIALOG_H + +// KDE includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class ImageDialogPrivate; +class ImageDialogPreviewPrivate; + +class DIGIKAM_EXPORT ImageDialogPreview : public KPreviewWidgetBase +{ + TQ_OBJECT + + +public: + + ImageDialogPreview(TQWidget *parent=0); + ~ImageDialogPreview(); + + TQSize sizeHint() const; + +public slots: + + void showPreview(const KURL &url); + +private slots: + + void showPreview(); + void slotGotThumbnail(const KURL& url, const TQPixmap& pix); + void slotFailedThumbnail(const KURL& url); + void clearPreview(); + +private: + + void resizeEvent(TQResizeEvent *e); + +private: + + class ImageDialogPreviewPrivate *d; +}; + +// ------------------------------------------------------------------------ + +class DIGIKAM_EXPORT ImageDialog +{ + +public: + + ImageDialog(TQWidget* parent, const KURL &url, bool singleSelect=false, const TQString& caption=TQString()); + ~ImageDialog(); + + KURL url() const; + KURL::List urls() const; + + bool singleSelect() const; + TQString fileformats() const; + + static KURL::List getImageURLs(TQWidget* parent, const KURL& url, const TQString& caption=TQString()); + static KURL getImageURL(TQWidget* parent, const KURL& url, const TQString& caption=TQString()); + +private: + + ImageDialogPrivate* d; +}; + +} // namespace Digikam + +#endif /* IMAGEDIALOG_H */ diff --git a/src/libs/dialogs/imagedlgbase.cpp b/src/libs/dialogs/imagedlgbase.cpp new file mode 100644 index 00000000..61666406 --- /dev/null +++ b/src/libs/dialogs/imagedlgbase.cpp @@ -0,0 +1,261 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-23 + * Description : simple plugins dialog without threadable + * filter interface. The dialog layout is + * designed to accept custom widgets in + * preview and settings area. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "sidebar.h" +#include "dimginterface.h" +#include "imagedlgbase.h" +#include "imagedlgbase.moc" + +namespace Digikam +{ + +class ImageDlgBasePriv +{ +public: + + ImageDlgBasePriv() + { + aboutData = 0; + timer = 0; + parent = 0; + mainLayout = 0; + hbox = 0; + settingsSideBar = 0; + splitter = 0; + } + + bool tryAction; + + TQGridLayout *mainLayout; + + TQWidget *parent; + + TQString name; + + TQTimer *timer; + + TQHBox *hbox; + + TQSplitter *splitter; + + TDEAboutData *aboutData; + + Sidebar *settingsSideBar; +}; + +ImageDlgBase::ImageDlgBase(TQWidget* parent, TQString title, TQString name, + bool loadFileSettings, bool tryAction, TQFrame* bannerFrame) + : KDialogBase(Plain, 0, Help|Default|User1|User2|User3|Try|Ok|Cancel, Ok, + parent, 0, true, true, + TQString(), + i18n("&Save As..."), + i18n("&Load...")) +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + setCaption(DImgInterface::defaultInterface()->getImageFileName() + TQString(" - ") + title); + showButton(User1, false); + + d = new ImageDlgBasePriv; + d->parent = parent; + d->name = name; + d->tryAction = tryAction; + + setButtonWhatsThis ( Default, i18n("

    Reset all filter parameters to their default values.") ); + setButtonWhatsThis ( User3, i18n("

    Load all filter parameters from settings text file.") ); + setButtonWhatsThis ( User2, i18n("

    Save all filter parameters to settings text file.") ); + showButton(User2, loadFileSettings); + showButton(User3, loadFileSettings); + showButton(Try, tryAction); + + resize(configDialogSize(name + TQString(" Tool Dialog"))); + + // ------------------------------------------------------------- + + d->mainLayout = new TQGridLayout( plainPage(), 2, 1); + if (bannerFrame) + { + bannerFrame->reparent( plainPage(), TQPoint(0, 0) ); + d->mainLayout->addMultiCellWidget(bannerFrame, 0, 0, 0, 1); + } + + // ------------------------------------------------------------- + + d->hbox = new TQHBox(plainPage()); + d->splitter = new TQSplitter(d->hbox); + d->splitter->setFrameStyle( TQFrame::NoFrame ); + d->splitter->setFrameShadow( TQFrame::Plain ); + d->splitter->setFrameShape( TQFrame::NoFrame ); + d->splitter->setOpaqueResize(false); + + d->mainLayout->addMultiCellWidget(d->hbox, 1, 2, 0, 1); + d->mainLayout->setColStretch(0, 10); + d->mainLayout->setRowStretch(2, 10); + + kapp->restoreOverrideCursor(); +} + +ImageDlgBase::~ImageDlgBase() +{ + if (d->timer) + delete d->timer; + + if (d->aboutData) + delete d->aboutData; + + delete d->settingsSideBar; + delete d; +} + +void ImageDlgBase::readSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup(d->name + TQString(" Tool Dialog")); + if(config->hasKey("SplitterSizes")) + d->splitter->setSizes(config->readIntListEntry("SplitterSizes")); + + readUserSettings(); +} + +void ImageDlgBase::writeSettings() +{ + TDEConfig *config = kapp->config(); + config->setGroup(d->name + TQString(" Tool Dialog")); + config->writeEntry("SplitterSizes", d->splitter->sizes()); + config->sync(); + saveDialogSize(d->name + TQString(" Tool Dialog")); +} + +void ImageDlgBase::closeEvent(TQCloseEvent *e) +{ + writeSettings(); + e->accept(); +} + +void ImageDlgBase::slotCancel() +{ + writeSettings(); + done(Cancel); +} + +void ImageDlgBase::slotOk() +{ + writeSettings(); + writeUserSettings(); + finalRendering(); +} + +void ImageDlgBase::slotDefault() +{ + resetValues(); + slotEffect(); +} + +void ImageDlgBase::slotHelp() +{ + // If setAboutData() is called by plugin, well DigikamImagePlugins help is launched, + // else digiKam help. In this case, setHelp() method must be used to set anchor and handbook name. + + if (d->aboutData) + TDEApplication::kApplication()->invokeHelp(d->name, "digikam"); + else + KDialogBase::slotHelp(); +} + +void ImageDlgBase::setAboutData(TDEAboutData *about) +{ + d->aboutData = about; + TQPushButton *helpButton = actionButton( Help ); + KHelpMenu* helpMenu = new KHelpMenu(this, d->aboutData, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("digiKam Handbook"), this, TQ_SLOT(slotHelp()), 0, -1, 0); + helpButton->setPopup( helpMenu->menu() ); +} + +void ImageDlgBase::setPreviewAreaWidget(TQWidget *w) +{ + w->reparent( d->splitter, TQPoint(0, 0) ); + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, + TQSizePolicy::Expanding, + 2, 1); + w->setSizePolicy(rightSzPolicy); +} + +void ImageDlgBase::setUserAreaWidget(TQWidget *w) +{ + TQString sbName(d->name + TQString(" Image Plugin Sidebar")); + d->settingsSideBar = new Sidebar(d->hbox, sbName.ascii(), Sidebar::Right); + d->settingsSideBar->setSplitter(d->splitter); + d->settingsSideBar->appendTab(w, SmallIcon("configure"), i18n("Settings")); + d->settingsSideBar->loadViewState(); + + readSettings(); +} + +void ImageDlgBase::slotTimer() +{ + if (d->timer) + { + d->timer->stop(); + delete d->timer; + } + + d->timer = new TQTimer( this ); + connect( d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotEffect()) ); + d->timer->start(500, true); +} + +} // NameSpace Digikam + diff --git a/src/libs/dialogs/imagedlgbase.h b/src/libs/dialogs/imagedlgbase.h new file mode 100644 index 00000000..97dc6a35 --- /dev/null +++ b/src/libs/dialogs/imagedlgbase.h @@ -0,0 +1,98 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-23 + * Description : simple plugins dialog without threadable + * filter interface. The dialog layout is + * designed to accept custom widgets in + * preview and settings area. + * + * Copyright 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEDLGBASE_H +#define IMAGEDLGBASE_H + +// TQt includes + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQWidget; + +class TDEAboutData; + +namespace Digikam +{ + +class ImageDlgBasePriv; + +class DIGIKAM_EXPORT ImageDlgBase : public KDialogBase +{ + TQ_OBJECT + + +public: + + ImageDlgBase(TQWidget *parent, TQString title, TQString name, + bool loadFileSettings=true, bool tryAction=false, TQFrame* bannerFrame=0); + ~ImageDlgBase(); + + void setAboutData(TDEAboutData *about); + void setPreviewAreaWidget(TQWidget *w); + void setUserAreaWidget(TQWidget *w); + +protected slots: + + virtual void slotDefault(); + virtual void slotTimer(); + +protected: + + void closeEvent(TQCloseEvent *e); + virtual void finalRendering(){}; + virtual void writeUserSettings(void){}; + virtual void readUserSettings(void){ slotDefault(); }; + virtual void resetValues(void){}; + +private slots: + + void slotHelp(); + void slotCancel(); + void slotOk(); + virtual void slotEffect(){}; + +private: + + void readSettings(void); + void writeSettings(void); + +private: + + ImageDlgBasePriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEDLGBASE */ diff --git a/src/libs/dialogs/imageguidedlg.cpp b/src/libs/dialogs/imageguidedlg.cpp new file mode 100644 index 00000000..9713a872 --- /dev/null +++ b/src/libs/dialogs/imageguidedlg.cpp @@ -0,0 +1,597 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : A threaded filter plugin dialog with a preview + * image guide widget and a settings user area + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "sidebar.h" +#include "dimgthreadedfilter.h" +#include "dimginterface.h" +#include "imageguidedlg.h" +#include "imageguidedlg.moc" + +namespace Digikam +{ + +class ImageGuideDlgPriv +{ +public: + + enum RunningMode + { + NoneRendering=0, + PreviewRendering, + FinalRendering + }; + + ImageGuideDlgPriv() + { + tryAction = false; + progress = true; + currentRenderingMode = NoneRendering; + parent = 0; + settings = 0; + timer = 0; + aboutData = 0; + guideColorBt = 0; + progressBar = 0; + guideSize = 0; + mainLayout = 0; + settingsLayout = 0; + hbox = 0; + settingsSideBar = 0; + splitter = 0; + } + + bool tryAction; + bool progress; + + int currentRenderingMode; + + TQWidget *parent; + TQWidget *settings; + + TQTimer *timer; + + TQString name; + + TQGridLayout *mainLayout; + TQGridLayout *settingsLayout; + + TQSpinBox *guideSize; + + TQHBox *hbox; + + TQSplitter *splitter; + + KProgress *progressBar; + + KColorButton *guideColorBt; + + TDEAboutData *aboutData; + + Sidebar *settingsSideBar; +}; + +ImageGuideDlg::ImageGuideDlg(TQWidget* parent, TQString title, TQString name, + bool loadFileSettings, bool progress, + bool guideVisible, int guideMode, TQFrame* bannerFrame, + bool prevModeOptions, bool useImageSelection, + bool tryAction) + : KDialogBase(Plain, 0, + Help|Default|User1|User2|User3|Try|Ok|Cancel, Ok, + parent, 0, true, true, + i18n("&Abort"), + i18n("&Save As..."), + i18n("&Load...")) +{ + kapp->setOverrideCursor( KCursor::waitCursor() ); + setCaption(DImgInterface::defaultInterface()->getImageFileName() + TQString(" - ") + title); + + d = new ImageGuideDlgPriv; + d->parent = parent; + d->name = name; + d->progress = progress; + d->tryAction = tryAction; + m_threadedFilter = 0; + TQString whatsThis; + + setButtonWhatsThis ( Default, i18n("

    Reset all filter parameters to their default values.") ); + setButtonWhatsThis ( User1, i18n("

    Abort the current image rendering.") ); + setButtonWhatsThis ( User3, i18n("

    Load all filter parameters from settings text file.") ); + setButtonWhatsThis ( User2, i18n("

    Save all filter parameters to settings text file.") ); + showButton(User2, loadFileSettings); + showButton(User3, loadFileSettings); + showButton(Try, tryAction); + + resize(configDialogSize(name + TQString(" Tool Dialog"))); + + // ------------------------------------------------------------- + + d->mainLayout = new TQGridLayout( plainPage(), 2, 1); + + if (bannerFrame) + { + bannerFrame->reparent( plainPage(), TQPoint(0, 0) ); + d->mainLayout->addMultiCellWidget(bannerFrame, 0, 0, 0, 1); + } + + // ------------------------------------------------------------- + + TQString desc; + + if (guideVisible) + desc = i18n("

    This is the the image filter effect preview. " + "If you move the mouse cursor on this area, " + "a vertical and horizontal dashed line will be draw " + "to guide you in adjusting the filter settings. " + "Press the left mouse button to freeze the dashed " + "line's position."); + else + desc = i18n("

    This is the image filter effect preview."); + + d->hbox = new TQHBox(plainPage()); + d->splitter = new TQSplitter(d->hbox); + m_imagePreviewWidget = new ImageWidget(d->name, d->splitter, desc, prevModeOptions, + guideMode, guideVisible, useImageSelection); + + d->splitter->setFrameStyle( TQFrame::NoFrame ); + d->splitter->setFrameShadow( TQFrame::Plain ); + d->splitter->setFrameShape( TQFrame::NoFrame ); + d->splitter->setOpaqueResize(false); + + TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1); + m_imagePreviewWidget->setSizePolicy(rightSzPolicy); + + TQString sbName(d->name + TQString(" Image Plugin Sidebar")); + d->settingsSideBar = new Sidebar(d->hbox, sbName.ascii(), Sidebar::Right); + d->settingsSideBar->setSplitter(d->splitter); + + d->mainLayout->addMultiCellWidget(d->hbox, 1, 2, 0, 1); + d->mainLayout->setColStretch(0, 10); + d->mainLayout->setRowStretch(2, 10); + + // ------------------------------------------------------------- + + d->settings = new TQWidget(plainPage()); + d->settingsLayout = new TQGridLayout( d->settings, 1, 0); + TQVBoxLayout *vLayout = new TQVBoxLayout( spacingHint() ); + + // ------------------------------------------------------------- + + TQWidget *gboxGuideSettings = new TQWidget(d->settings); + TQGridLayout* grid = new TQGridLayout( gboxGuideSettings, 2, 2, marginHint(), spacingHint()); + KSeparator *line = new KSeparator(TQt::Horizontal, gboxGuideSettings); + grid->addMultiCellWidget(line, 0, 0, 0, 2); + + TQLabel *label5 = new TQLabel(i18n("Guide color:"), gboxGuideSettings); + d->guideColorBt = new KColorButton( TQColor( TQt::red ), gboxGuideSettings ); + TQWhatsThis::add( d->guideColorBt, i18n("

    Set here the color used to draw guides dashed-lines.")); + grid->addMultiCellWidget(label5, 1, 1, 0, 0); + grid->addMultiCellWidget(d->guideColorBt, 1, 1, 2, 2); + + TQLabel *label6 = new TQLabel(i18n("Guide width:"), gboxGuideSettings); + d->guideSize = new TQSpinBox( 1, 5, 1, gboxGuideSettings); + TQWhatsThis::add( d->guideSize, i18n("

    Set here the width in pixels used to draw guides dashed-lines.")); + grid->addMultiCellWidget(label6, 2, 2, 0, 0); + grid->addMultiCellWidget(d->guideSize, 2, 2, 2, 2); + grid->setColStretch(1, 10); + + if (guideVisible) gboxGuideSettings->show(); + else gboxGuideSettings->hide(); + + vLayout->addWidget(gboxGuideSettings); + + TQHBox *hbox = new TQHBox(d->settings); + TQLabel *space1 = new TQLabel(hbox); + space1->setFixedWidth(spacingHint()); + d->progressBar = new KProgress(100, hbox); + d->progressBar->setMaximumHeight( fontMetrics().height() ); + TQWhatsThis::add(d->progressBar ,i18n("

    This is the percentage of the task which has been completed up to this point.")); + d->progressBar->setValue(0); + setProgressVisible(false); + TQLabel *space2 = new TQLabel(hbox); + space2->setFixedWidth(spacingHint()); + + vLayout->addWidget(hbox); + vLayout->addStretch(10); + + d->settingsLayout->addMultiCellLayout(vLayout, 1, 1, 0, 0); + + d->settingsSideBar->appendTab(d->settings, SmallIcon("configure"), i18n("Settings")); + d->settingsSideBar->loadViewState(); + + // Reading splitter sizes here prevent flicker effect in dialog. + TDEConfig *config = kapp->config(); + config->setGroup(d->name + TQString(" Tool Dialog")); + if(config->hasKey("SplitterSizes")) + d->splitter->setSizes(config->readIntListEntry("SplitterSizes")); + + // ------------------------------------------------------------- + + TQTimer::singleShot(0, this, TQ_SLOT(slotInit())); + kapp->restoreOverrideCursor(); +} + +ImageGuideDlg::~ImageGuideDlg() +{ + if (d->timer) + delete d->timer; + + if (m_threadedFilter) + delete m_threadedFilter; + + if (d->aboutData) + delete d->aboutData; + + delete d->settingsSideBar; + delete d; +} + +void ImageGuideDlg::readSettings(void) +{ + TQColor defaultGuideColor(TQt::red); + TDEConfig *config = kapp->config(); + config->setGroup(d->name + TQString(" Tool Dialog")); + d->guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor)); + d->guideSize->setValue(config->readNumEntry("Guide Width", 1)); + m_imagePreviewWidget->slotChangeGuideSize(d->guideSize->value()); + m_imagePreviewWidget->slotChangeGuideColor(d->guideColorBt->color()); +} + +void ImageGuideDlg::writeSettings(void) +{ + TDEConfig *config = kapp->config(); + config->setGroup(d->name + TQString(" Tool Dialog")); + config->writeEntry( "Guide Color", d->guideColorBt->color() ); + config->writeEntry( "Guide Width", d->guideSize->value() ); + config->writeEntry( "SplitterSizes", d->splitter->sizes() ); + config->sync(); + saveDialogSize(d->name + TQString(" Tool Dialog")); +} + +void ImageGuideDlg::slotInit() +{ + readSettings(); + // Reset values to defaults. + TQTimer::singleShot(0, this, TQ_SLOT(readUserSettings())); + + if (!d->tryAction) + { + connect(m_imagePreviewWidget, TQ_SIGNAL(signalResized()), + this, TQ_SLOT(slotResized())); + } + + connect(d->guideColorBt, TQ_SIGNAL(changed(const TQColor &)), + m_imagePreviewWidget, TQ_SLOT(slotChangeGuideColor(const TQColor &))); + + connect(d->guideSize, TQ_SIGNAL(valueChanged(int)), + m_imagePreviewWidget, TQ_SLOT(slotChangeGuideSize(int))); +} + +void ImageGuideDlg::setUserAreaWidget(TQWidget *w) +{ + w->reparent( d->settings, TQPoint(0, 0) ); + TQVBoxLayout *vLayout = new TQVBoxLayout( spacingHint() ); + vLayout->addWidget(w); + d->settingsLayout->addMultiCellLayout(vLayout, 0, 0, 0, 0); +} + +void ImageGuideDlg::setAboutData(TDEAboutData *about) +{ + d->aboutData = about; + TQPushButton *helpButton = actionButton( Help ); + KHelpMenu* helpMenu = new KHelpMenu(this, d->aboutData, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("digiKam Handbook"), this, TQ_SLOT(slotHelp()), 0, -1, 0); + helpButton->setPopup( helpMenu->menu() ); +} + +void ImageGuideDlg::setProgressVisible(bool v) +{ + if (v) + d->progressBar->show(); + else + d->progressBar->hide(); +} + +void ImageGuideDlg::abortPreview() +{ + d->currentRenderingMode = ImageGuideDlgPriv::NoneRendering; + d->progressBar->setValue(0); + setProgressVisible(false); + enableButton(Ok, true); + enableButton(User1, false); + enableButton(User2, true); + enableButton(User3, true); + enableButton(Try, true); + enableButton(Default, true); + renderingFinished(); +} + +void ImageGuideDlg::slotTry() +{ + slotEffect(); +} + +void ImageGuideDlg::slotResized(void) +{ + if (d->currentRenderingMode == ImageGuideDlgPriv::FinalRendering) + { + m_imagePreviewWidget->update(); + return; + } + else if (d->currentRenderingMode == ImageGuideDlgPriv::PreviewRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + } + + TQTimer::singleShot(0, this, TQ_SLOT(slotEffect())); +} + +void ImageGuideDlg::slotUser1() +{ + if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering) + if (m_threadedFilter) + m_threadedFilter->stopComputation(); +} + +void ImageGuideDlg::slotDefault() +{ + resetValues(); + slotEffect(); +} + +void ImageGuideDlg::slotCancel() +{ + if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + + kapp->restoreOverrideCursor(); + } + + writeSettings(); + done(Cancel); +} + +void ImageGuideDlg::closeEvent(TQCloseEvent *e) +{ + if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering) + { + if (m_threadedFilter) + m_threadedFilter->stopComputation(); + + kapp->restoreOverrideCursor(); + } + + writeSettings(); + e->accept(); +} + +void ImageGuideDlg::slotHelp() +{ + // If setAboutData() is called by plugin, well DigikamImagePlugins help is lauched, + // else digiKam help. In this case, setHelp() method must be used to set anchor and handbook name. + + if (d->aboutData) + TDEApplication::kApplication()->invokeHelp(d->name, "digikam"); + else + KDialogBase::slotHelp(); +} + +void ImageGuideDlg::slotTimer() +{ + if (d->timer) + { + d->timer->stop(); + delete d->timer; + } + + d->timer = new TQTimer( this ); + connect( d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotEffect()) ); + d->timer->start(500, true); +} + +void ImageGuideDlg::slotEffect() +{ + // Computation already in process. + if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering) + return; + + d->currentRenderingMode = ImageGuideDlgPriv::PreviewRendering; + DDebug() << "Preview " << d->name << " started..." << endl; + + enableButton(Ok, false); + enableButton(User1, true); + enableButton(User2, false); + enableButton(User3, false); + enableButton(Default, false); + enableButton(Try, false); + d->progressBar->setValue(0); + if (d->progress) setProgressVisible(true); + + if (m_threadedFilter) + { + delete m_threadedFilter; + m_threadedFilter = 0; + } + + prepareEffect(); +} + +void ImageGuideDlg::slotOk() +{ + d->currentRenderingMode = ImageGuideDlgPriv::FinalRendering; + DDebug() << "Final " << d->name << " started..." << endl; + writeSettings(); + writeUserSettings(); + + enableButton(Ok, false); + enableButton(User1, false); + enableButton(User2, false); + enableButton(User3, false); + enableButton(Default, false); + enableButton(Try, false); + kapp->setOverrideCursor( KCursor::waitCursor() ); + d->progressBar->setValue(0); + + if (m_threadedFilter) + { + delete m_threadedFilter; + m_threadedFilter = 0; + } + + prepareFinal(); +} + +void ImageGuideDlg::customEvent(TQCustomEvent *event) +{ + if (!event) return; + + DImgThreadedFilter::EventData *ed = (DImgThreadedFilter::EventData*) event->data(); + + if (!ed) return; + + if (ed->starting) // Computation in progress ! + { + d->progressBar->setValue(ed->progress); + } + else + { + if (ed->success) // Computation Completed ! + { + switch (d->currentRenderingMode) + { + case ImageGuideDlgPriv::PreviewRendering: + { + DDebug() << "Preview " << d->name << " completed..." << endl; + putPreviewData(); + abortPreview(); + break; + } + + case ImageGuideDlgPriv::FinalRendering: + { + DDebug() << "Final" << d->name << " completed..." << endl; + putFinalData(); + kapp->restoreOverrideCursor(); + accept(); + break; + } + } + } + else // Computation Failed ! + { + switch (d->currentRenderingMode) + { + case ImageGuideDlgPriv::PreviewRendering: + { + DDebug() << "Preview " << d->name << " failed..." << endl; + // abortPreview() must be call here for set progress bar to 0 properly. + abortPreview(); + break; + } + + case ImageGuideDlgPriv::FinalRendering: + break; + } + } + } + + delete ed; +} + +// Backport KDialog::keyPressEvent() implementation from KDELibs to ignore Enter/Return Key events +// to prevent any conflicts between dialog keys events and SpinBox keys events. + +void ImageGuideDlg::keyPressEvent(TQKeyEvent *e) +{ + if ( e->state() == 0 ) + { + switch ( e->key() ) + { + case Key_Escape: + e->accept(); + reject(); + break; + case Key_Enter: + case Key_Return: + e->ignore(); + break; + default: + e->ignore(); + return; + } + } + else + { + // accept the dialog when Ctrl-Return is pressed + if ( e->state() == ControlButton && + (e->key() == Key_Return || e->key() == Key_Enter) ) + { + e->accept(); + accept(); + } + else + { + e->ignore(); + } + } +} + +} // NameSpace Digikam diff --git a/src/libs/dialogs/imageguidedlg.h b/src/libs/dialogs/imageguidedlg.h new file mode 100644 index 00000000..e6841c43 --- /dev/null +++ b/src/libs/dialogs/imageguidedlg.h @@ -0,0 +1,123 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-07 + * Description : A threaded filter plugin dialog with a preview + * image guide widget and a settings user area + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEGUIDEDLG_H +#define IMAGEGUIDEDLG_H + +// TQt includes + +#include + +// KDE include. + +#include + +// Local includes. + +#include "imagewidget.h" +#include "imageguidewidget.h" +#include "digikam_export.h" + +class TQFrame; + +class TDEAboutData; + +namespace Digikam +{ + +class ImageGuideDlgPriv; +class DImgThreadedFilter; + +class DIGIKAM_EXPORT ImageGuideDlg : public KDialogBase +{ + TQ_OBJECT + + +public: + + ImageGuideDlg(TQWidget* parent, TQString title, TQString name, + bool loadFileSettings=false, bool progress=true, + bool guideVisible=true, + int guideMode=ImageGuideWidget::HVGuideMode, + TQFrame* bannerFrame=0, + bool prevModeOptions=false, + bool useImageSelection=false, + bool tryAction=false); + ~ImageGuideDlg(); + + void setAboutData(TDEAboutData *about); + void setUserAreaWidget(TQWidget *w); + void setProgressVisible(bool v); + +public: + + DImgThreadedFilter *m_threadedFilter; + + ImageWidget *m_imagePreviewWidget; + +public slots: + + void slotTimer(); + void slotEffect(); + void slotOk(); + void slotTry(); + +protected slots: + + virtual void slotCancel(); + virtual void slotUser1(); + virtual void slotDefault(); + virtual void slotInit(); + virtual void readUserSettings(void){ slotDefault(); }; + +private slots: + + void slotResized(); + void slotHelp(); + +protected: + + void closeEvent(TQCloseEvent *e); + void customEvent(TQCustomEvent *event); + void abortPreview(void); + void readSettings(void); + void writeSettings(void); + void keyPressEvent(TQKeyEvent *e); + + virtual void writeUserSettings(void){}; + virtual void resetValues(void){}; + virtual void prepareEffect(void){}; + virtual void prepareFinal(void){}; + virtual void putPreviewData(void){}; + virtual void putFinalData(void){}; + virtual void renderingFinished(void){}; + +private: + + ImageGuideDlgPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEGUIDEDLG_H */ diff --git a/src/libs/dialogs/rawcameradlg.cpp b/src/libs/dialogs/rawcameradlg.cpp new file mode 100644 index 00000000..298b47d4 --- /dev/null +++ b/src/libs/dialogs/rawcameradlg.cpp @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-04-07 + * Description : Raw camera list dialog + * + * Copyright (C) 2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "searchtextbar.h" +#include "rawcameradlg.h" +#include "rawcameradlg.moc" + +namespace Digikam +{ + +class RawCameraDlgPriv +{ +public: + + RawCameraDlgPriv() + { + listView = 0; + searchBar = 0; + } + + TQListView *listView; + + SearchTextBar *searchBar; +}; + +RawCameraDlg::RawCameraDlg(TQWidget *parent) + : KDialogBase(parent, 0, true, TQString(), Help|Ok, Ok, true) +{ + setHelp("digitalstillcamera.anchor", "digikam"); + setCaption(i18n("List of supported RAW cameras")); + + d = new RawCameraDlgPriv; + + TQWidget *page = makeMainWidget(); + TQGridLayout* grid = new TQGridLayout(page, 2, 2, 0, spacingHint()); + +#if KDCRAW_VERSION < 0x000106 + TQStringList list = KDcrawIface::DcrawBinary::instance()->supportedCamera(); + TQString dcrawVer = KDcrawIface::DcrawBinary::instance()->internalVersion(); +#else + TQStringList list = KDcrawIface::KDcraw::supportedCamera(); + TQString librawVer = KDcrawIface::KDcraw::librawVersion(); +#endif + TQString KDcrawVer = KDcrawIface::KDcraw::version(); + + // -------------------------------------------------------- + + TQLabel *logo = new TQLabel(page); + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + + if (TDEApplication::kApplication()->aboutData()->appName() == TQString("digikam")) + logo->setPixmap(iconLoader->loadIcon("digikam", TDEIcon::NoGroup, 96, TDEIcon::DefaultState, 0, true)); + else + logo->setPixmap(iconLoader->loadIcon("showfoto", TDEIcon::NoGroup, 96, TDEIcon::DefaultState, 0, true)); + + // -------------------------------------------------------- + + TQLabel *header = new TQLabel(page); +#if KDCRAW_VERSION < 0x000106 + header->setText(i18n("

    Using KDcraw library version %1" + "

    Using Dcraw program version %2" + "

    %3 models in the list") + .arg(KDcrawVer).arg(dcrawVer).arg(list.count())); +#else + header->setText(i18n("

    Using KDcraw library version %1" + "

    Using LibRaw version %2" + "

    %3 models in the list") + .arg(KDcrawVer).arg(librawVer).arg(list.count())); +#endif + + // -------------------------------------------------------- + + d->searchBar = new SearchTextBar(page, "RawCameraDlgSearchBar"); + d->listView = new TQListView(page); + d->listView->addColumn("Camera Model"); // Header is hiden. No i18n here. + d->listView->setSorting(1); + d->listView->header()->hide(); + d->listView->setResizeMode(TQListView::LastColumn); + + for (TQStringList::Iterator it = list.begin() ; it != list.end() ; ++it) + new TQListViewItem(d->listView, *it); + + // -------------------------------------------------------- + + + grid->addMultiCellWidget(logo, 0, 0, 0, 0); + grid->addMultiCellWidget(header, 0, 0, 1, 2); + grid->addMultiCellWidget(d->listView, 1, 1, 0, 2); + grid->addMultiCellWidget(d->searchBar, 2, 2, 0, 2); + grid->setColStretch(1, 10); + grid->setRowStretch(1, 10); + + // -------------------------------------------------------- + + connect(d->searchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotSearchTextChanged(const TQString&))); + + resize(500, 500); +} + +RawCameraDlg::~RawCameraDlg() +{ + delete d; +} + +void RawCameraDlg::slotSearchTextChanged(const TQString& filter) +{ + bool query = false; + TQString search = filter.lower(); + + TQListViewItemIterator it(d->listView); + + for ( ; it.current(); ++it ) + { + TQListViewItem *item = it.current(); + + if (item->text(0).lower().contains(search)) + { + query = true; + item->setVisible(true); + } + else + { + item->setVisible(false); + } + } + + d->searchBar->slotSearchResult(query); +} + +} // NameSpace Digikam diff --git a/src/libs/dialogs/rawcameradlg.h b/src/libs/dialogs/rawcameradlg.h new file mode 100644 index 00000000..41d4a7de --- /dev/null +++ b/src/libs/dialogs/rawcameradlg.h @@ -0,0 +1,61 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-04-07 + * Description : Raw camera list dialog + * + * Copyright (C) 2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RAWCAMERADLG_H +#define RAWCAMERADLG_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class RawCameraDlgPriv; + +class DIGIKAM_EXPORT RawCameraDlg : public KDialogBase +{ + TQ_OBJECT + + +public: + + RawCameraDlg(TQWidget* parent); + ~RawCameraDlg(); + +private slots: + + void slotSearchTextChanged(const TQString&); + +private: + + RawCameraDlgPriv *d; +}; + +} // NameSpace Digikam + +#endif // RAWCAMERADLG_H diff --git a/src/libs/dimg/Makefile.am b/src/libs/dimg/Makefile.am new file mode 100644 index 00000000..23e746a2 --- /dev/null +++ b/src/libs/dimg/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = loaders filters +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimg.la + +libdimg_la_SOURCES = dimg.cpp dimgscale.cpp dcolor.cpp dcolorcomposer.cpp ddebug.cpp + +libdimg_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +libdimg_la_LIBADD = $(top_builddir)/src/libs/histogram/libhistogram.la \ + $(top_builddir)/src/libs/levels/liblevels.la \ + $(top_builddir)/src/libs/curves/libcurves.la \ + $(top_builddir)/src/libs/whitebalance/libwhitebalance.la \ + $(top_builddir)/src/libs/dimg/loaders/libdimgloaders.la \ + $(top_builddir)/src/libs/dimg/filters/libdimgfilters.la \ + $(top_builddir)/src/libs/dmetadata/libdmetadata.la \ + $(LIBKDCRAW_LIBS) $(LCMS_LIBS) + +INCLUDES = $(all_includes) $(LIBKDCRAW_CFLAGS) \ + -I$(top_srcdir)/src/libs/dimg/loaders \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/digikam + +digikaminclude_HEADERS = dimg.h dcolor.h dcolorpixelaccess.h dcolorcomposer.h \ + dcolorblend.h ddebug.h +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/dimg/README b/src/libs/dimg/README new file mode 100644 index 00000000..4b13f3fe --- /dev/null +++ b/src/libs/dimg/README @@ -0,0 +1,95 @@ + +--------------------------------------------------------------------------- +WHAT'S DIMG FRAMEWORK ? + + +Original post from Renchi Raju on digiKam mailing list : + +[Digikam-devel] Imaging Library +Renchi Raju renchi at pooh.tam.uiuc.edu +Sun Jun 19 23:15:20 CEST 2005 + +as you all know, we have been using imlib2 library for the 0.7 series. we +have had a fairly large number of bugreports because of imlib2 (not +imlib2's fault, but because of tdelibs's handling of ltdl). In addition, +some imlib2 bugreports I reported to upstream have gone unfixed for long +time now. + +Also, we need to think about 16bit imaging support. this won't come from +imlib2 and neither from tqt. with qt4 there was hope of 16bit image +support, but trolltech has made it clear that imaging apps forms only 0.1% +of their customer base and they are not interested in providing custom +support for them. so the only solution I see (without depending on +imagemagick) is to roll our own library. + +i have been working on a imaging library for digikam, its called DImg. +it doesn't aim to be a complete imaging library; it uses TQImage for +rendering and for loading files which are not supported natively by it. +some of the working/planned features: + +* Native Image Loaders, for some imageformats which are of interest to +us: JPEG (complete), TIFF (a rudimentary one currently), PNG (planned), PPM +(planned). for the rest tqt's qimage is used. + +* Metadata preservation: when a file is loaded, its metadata like XMP, +IPTC, EXIF, JFIF are read and held in memory. now when you save back the +file to the original file or to a different file, the metadata is +automatically written (How, we have been handling it currently with +imlib2 is on saving a file: we save the file to a temporary file, reread +the exif info from the original file and then write to a second temporary +file.) + +* Explicitly Shared Container format (see tqt docs): this is necessary for +performance reasons. + +* 8bit and 16bit support: if the file format is 16 bit, it will load up +the image in 16bit format (currently only 16bit tiff support) and all +operations are done in 16 bit format, except when the rendering to screen +is done, when its converted on the fly to a temporary 8bit image and then +rendered. + +* Basic image manipulation: rotate, flip, color modifications, crop, +scale (this has been ported from Imlib2 - originally ported by Mosfet, I +added 16 bit scaling support and support for scaling of only a section of +the image) + +* Rendering to Pixmap: using TQImage/QPixmap. (see above for rendering of +16bit images). + +* Pixel format: the pixel format is different from TQImage/Imlib2 pixel +format. In TQImage/Imlib2 the pixel data is stored as unsigned ints and to +access the individual colors you need to use bit-shifting to ensure +endian correctness. in DImg, the pixel data is stored as unsigned char. +the color layout is B,G,R,A (blue, green, red, alpha) + +for 8bit images: you can access individual color components like this: + +uchar* pixels = image.bits(); +for (int i=0; i + * + * RGB<->HLS transformation algorithms are inspired from methods + * describe at this url : + * http://www.paris-pc-gis.com/MI_Enviro/Colors/color_models.htm + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dcolor.h" + +namespace Digikam +{ + +/* +DColor::DColor(const DColor& color) +{ + m_red = color.m_red; + m_green = color.m_green; + m_blue = color.m_blue; + m_alpha = color.m_alpha; + m_sixteenBit = color.m_sixteenBit; +} +*/ + +DColor::DColor(const TQColor& color, bool sixteenBit) +{ + // initialize as eight bit + m_red = color.red(); + m_green = color.green(); + m_blue = color.blue(); + m_alpha = 255; + m_sixteenBit = false; + + // convert to sixteen bit if requested + if (sixteenBit) + convertToSixteenBit(); +} + +/* +DColor& DColor::operator=(const DColor& color) +{ + m_red = color.m_red; + m_green = color.m_green; + m_blue = color.m_blue; + m_alpha = color.m_alpha; + m_sixteenBit = color.m_sixteenBit; + return *this; +} +*/ + +TQColor DColor::getTQColor() const +{ + if (m_sixteenBit) + { + DColor eightBit(*this); + eightBit.convertToEightBit(); + return eightBit.getTQColor(); + } + + return (TQColor(m_red, m_green, m_blue)); +} + +void DColor::convertToSixteenBit() +{ + if (m_sixteenBit) + return; + + m_red = (m_red + 1) * 256 - 1; + m_green = (m_green + 1) * 256 - 1; + m_blue = (m_blue + 1) * 256 - 1; + m_alpha = (m_alpha + 1) * 256 - 1; + m_sixteenBit = true; +} + +void DColor::convertToEightBit() +{ + if (!m_sixteenBit) + return; + + m_red = (m_red + 1) / 256 - 1; + m_green = (m_green + 1) / 256 - 1; + m_blue = (m_blue + 1) / 256 - 1; + m_alpha = (m_alpha + 1) / 256 - 1; + m_sixteenBit = false; +} + + +void DColor::getHSL(int* h, int* s, int* l) const +{ + double min; + double max; + double red; + double green; + double blue; + double delta; + double sum; + double hue, sat, lig; + + double range = m_sixteenBit ? 65535.0 : 255.0; + + red = m_red / range; + green = m_green / range; + blue = m_blue / range; + + if (red > green) + { + if (red > blue) + max = red; + else + max = blue; + + if (green < blue) + min = green; + else + min = blue; + } + else + { + if (green > blue) + max = green; + else + max = blue; + + if (red < blue) + min = red; + else + min = blue; + } + + sum = max + min; + + lig = sum / 2; + sat = 0; + hue = 0; + + if (max != min) + { + delta = max - min; + + if (lig <= 0.5) + sat = delta / sum; + else + sat = delta / (2 - sum); + + if (red == max) + hue = (green - blue) / delta; + else if (green == max) + hue = 2 + (blue - red) / delta; + else if (blue == max) + hue = 4 + (red - green) / delta; + + if (hue < 0) + hue += 6; + if (hue > 6) + hue -= 6; + + hue *= 60; + } + + *h = lround(hue * range / 360.0); + *s = lround(sat * range); + *l = lround(lig * range); +} + +void DColor::setRGB(int h, int s, int l, bool sixteenBit) +{ + double hue; + double lightness; + double saturation; + double m1, m2; + double r, g, b; + + double range = m_sixteenBit ? 65535.0 : 255.0; + + if (s == 0) + { + m_red = l; + m_green = l; + m_blue = l; + } + else + { + hue = (double)(h * 360.0 / range); + lightness = (double)(l / range); + saturation = (double)(s / range); + + if (lightness <= 0.5) + m2 = lightness * (1 + saturation); + else + m2 = lightness + saturation - lightness * saturation; + + m1 = 2 * lightness - m2; + + double mh; + + mh = hue + 120; + while (mh > 360) + mh -= 360; + while (mh < 0) + mh += 360; + + if (mh < 60) + r = m1 + (m2 - m1) * mh / 60; + else if (mh < 180) + r = m2; + else if (mh < 240) + r = m1 + (m2 - m1) * (240 - mh) / 60; + else + r = m1; + + mh = hue; + while (mh > 360) + mh -= 360; + while (mh < 0) + mh += 360; + + if (mh < 60) + g = m1 + (m2 - m1) * mh / 60; + else if (mh < 180) + g = m2; + else if (mh < 240) + g = m1 + (m2 - m1) * (240 - mh) / 60; + else + g = m1; + + mh = hue - 120; + while (mh > 360) + mh -= 360; + while (mh < 0) + mh += 360; + + if (mh < 60) + b = m1 + (m2 - m1) * mh / 60; + else if (mh < 180) + b = m2; + else if (mh < 240) + b = m1 + (m2 - m1) * (240 - mh) / 60; + else + b = m1; + + m_red = lround(r * range); + m_green = lround(g * range); + m_blue = lround(b * range); + } + + m_sixteenBit = sixteenBit; + + // Fully opaque color. + if (m_sixteenBit) + m_alpha = 65535; + else + m_alpha = 255; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/dcolor.h b/src/libs/dimg/dcolor.h new file mode 100644 index 00000000..16a34f55 --- /dev/null +++ b/src/libs/dimg/dcolor.h @@ -0,0 +1,152 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-12-02 + * Description : 8-16 bits color container. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DCOLOR_H +#define DCOLOR_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DColor +{ +public: + + /** Initialize with default value, fully transparent eight bit black */ + DColor() + : m_red(0), m_green(0), m_blue(0), m_alpha(0), m_sixteenBit(false) + {}; + + /** Read value from data. Equivalent to setColor() */ + DColor(const uchar *data, bool sixteenBit = false) + { setColor(data, sixteenBit); } + + /** Initialize with given RGBA values */ + DColor(int red, int green, int blue, int alpha, bool sixteenBit) + : m_red(red), m_green(green), m_blue(blue), m_alpha(alpha), m_sixteenBit(sixteenBit) + {}; + + /** Read values from TQColor, convert to sixteenBit of sixteenBit is true */ + DColor(const TQColor& color, bool sixteenBit=false); + + // Use default copy constructor, assignment operator and destructor + + /** Read color values as RGBA from the given memory location. + If sixteenBit is false, 4 bytes are read. + If sixteenBit is true, 8 bytes are read. + Inline method. + */ + void setColor(const uchar *data, bool sixteenBit = false); + + /** Write the values of this color to the given memory location. + If sixteenBit is false, 4 bytes are written. + If sixteenBit is true, 8 bytes are written. + Inline method. + */ + void setPixel(uchar *data) const; + + int red () const { return m_red; } + int green() const { return m_green; } + int blue () const { return m_blue; } + int alpha() const { return m_alpha; } + bool sixteenBit() const { return m_sixteenBit; } + + void setRed (int red) { m_red = red; } + void setGreen(int green) { m_green = green; } + void setBlue (int blue) { m_blue = blue; } + void setAlpha(int alpha) { m_alpha = alpha; } + void setSixteenBit(bool sixteenBit) { m_sixteenBit = sixteenBit; } + + TQColor getTQColor() const; + + /** Convert the color values of this color to and from sixteen bit + and set the sixteenBit value accordingly + */ + void convertToSixteenBit(); + void convertToEightBit(); + + /** Premultiply and demultiply this color. + DImg stores the color non-premultiplied. + Inline methods. + */ + void premultiply(); + void demultiply(); + + /** Return the current RGB color values of this color + in the HSL color space. + Alpha is ignored for the conversion. + */ + void getHSL(int* h, int* s, int* l) const; + + /** Set the RGB color values of this color + to the given HSL values converted to RGB. + Alpha is set to be fully opaque. + sixteenBit determines both how the HSL values are interpreted + and the sixteenBit value of this color after this operation. + */ + void setRGB(int h, int s, int l, bool sixteenBit); + +private: + + int m_red; + int m_green; + int m_blue; + int m_alpha; + + bool m_sixteenBit; + +public: + + // Inline alpha blending helper functions. + // These functions are used by DColorComposer. + // Look at that code to learn how to use them for + // composition if you want to use them in optimized code. + void blendZero(); + void blendAlpha8(int alpha); + void blendInvAlpha8(int alpha); + void blendAlpha16(int alpha); + void blendInvAlpha16(int alpha); + void premultiply16(int alpha); + void premultiply8(int alpha); + void demultiply16(int alpha); + void demultiply8(int alpha); + void blendAdd(const DColor &src); + void blendClamp8(); + void blendClamp16(); +}; + +} // NameSpace Digikam + + +// Inline methods +#include "dcolorpixelaccess.h" +#include "dcolorblend.h" + +#endif /* DCOLOR_H */ diff --git a/src/libs/dimg/dcolorblend.h b/src/libs/dimg/dcolorblend.h new file mode 100644 index 00000000..b68322ba --- /dev/null +++ b/src/libs/dimg/dcolorblend.h @@ -0,0 +1,179 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-03-01 + * Description : DColor methods for blending + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Inspired by DirectFB, src/gfx/generic/generic.c: + +/* + (c) Copyright 2000-2002 convergence integrated media GmbH + (c) Copyright 2002-2005 convergence GmbH. + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + +*/ + +#ifndef DCOLORBLEND_H +#define DCOLORBLEND_H + +namespace Digikam +{ + +inline void DColor::premultiply() +{ + if (sixteenBit()) + premultiply16(alpha()); + else + premultiply8(alpha()); +} + +inline void DColor::demultiply() +{ + if (sixteenBit()) + { + demultiply16(alpha()); + blendClamp16(); + } + else + { + demultiply8(alpha()); + blendClamp8(); + } +} + +inline void DColor::blendZero() +{ + setAlpha(0); + setRed(0); + setGreen(0); + setBlue(0); +} + +inline void DColor::blendAlpha16(int alphaValue) +{ + uint Sa = alphaValue + 1; + + setRed ((Sa * (uint)red()) >> 16); + setGreen((Sa * (uint)green()) >> 16); + setBlue ((Sa * (uint)blue()) >> 16); + setAlpha((Sa * (uint)alpha()) >> 16); +} + +inline void DColor::blendAlpha8(int alphaValue) +{ + uint Sa = alphaValue + 1; + + setRed ((Sa * red()) >> 8); + setGreen((Sa * green()) >> 8); + setBlue ((Sa * blue()) >> 8); + setAlpha((Sa * alpha()) >> 8); +} + +inline void DColor::blendInvAlpha16(int alphaValue) +{ + uint Sa = 65536 - alphaValue; + + setRed ((Sa * (uint)red()) >> 16); + setGreen((Sa * (uint)green()) >> 16); + setBlue ((Sa * (uint)blue()) >> 16); + setAlpha((Sa * (uint)alpha()) >> 16); +} + +inline void DColor::blendInvAlpha8(int alphaValue) +{ + uint Sa = 256 - alphaValue; + + setRed ((Sa * red()) >> 8); + setGreen((Sa * green()) >> 8); + setBlue ((Sa * blue()) >> 8); + setAlpha((Sa * alpha()) >> 8); +} + +inline void DColor::premultiply16(int alphaValue) +{ + uint Da = alphaValue + 1; + + setRed ((Da * (uint)red()) >> 16); + setGreen((Da * (uint)green()) >> 16); + setBlue ((Da * (uint)blue()) >> 16); +} + +inline void DColor::premultiply8(int alphaValue) +{ + uint Da = alphaValue + 1; + + setRed ((Da * red()) >> 8); + setGreen((Da * green()) >> 8); + setBlue ((Da * blue()) >> 8); +} + +inline void DColor::demultiply16(int alphaValue) +{ + uint Da = alphaValue + 1; + + setRed (((uint)red() << 16) / Da); + setGreen(((uint)green() << 16) / Da); + setBlue (((uint)blue() << 16) / Da); +} + +inline void DColor::demultiply8(int alphaValue) +{ + uint Da = alphaValue + 1; + + setRed ((red() << 8) / Da); + setGreen((green() << 8) / Da); + setBlue ((blue() << 8) / Da); +} + +inline void DColor::blendAdd(const DColor &src) +{ + setRed (red() + src.red()); + setGreen(green() + src.green()); + setBlue (blue() + src.blue()); + setAlpha(alpha() + src.alpha()); +} + +inline void DColor::blendClamp16() +{ + if (0xFFFF0000 & red()) setRed(65535); + if (0xFFFF0000 & green()) setGreen(65535); + if (0xFFFF0000 & blue()) setBlue(65535); + if (0xFFFF0000 & alpha()) setAlpha(65535); +} + +inline void DColor::blendClamp8() +{ + if (0xFF00 & red()) setRed(255); + if (0xFF00 & green()) setGreen(255); + if (0xFF00 & blue()) setBlue(255); + if (0xFF00 & alpha()) setAlpha(255); +} + +} // namespace Digikam + +#endif // DCOLORBLEND_H + diff --git a/src/libs/dimg/dcolorcomposer.cpp b/src/libs/dimg/dcolorcomposer.cpp new file mode 100644 index 00000000..653dbab3 --- /dev/null +++ b/src/libs/dimg/dcolorcomposer.cpp @@ -0,0 +1,436 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-03-02 + * Description : DColor methods for composing + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// Integer arithmetic inspired by DirectFB, +// src/gfx/generic/generic.c and src/display/idirectfbsurface.c: + +/* + (c) Copyright 2000-2002 convergence integrated media GmbH + (c) Copyright 2002-2005 convergence GmbH. + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrj�l� and + Claudio Ciccani . + +*/ + +// C++ includes. + +#include + +// Local includes. + +#include "dcolorcomposer.h" + +namespace Digikam +{ + +class DColorComposerPorterDuffNone : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffClear : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); + virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags); +}; + +class DColorComposerPorterDuffSrc : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); + virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags); +}; + +class DColorComposerPorterDuffSrcOver : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffDstOver : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffSrcIn : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffDstIn : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffSrcOut : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffDstOut : public DColorComposer +{ +public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffSrcAtop : public DColorComposer +{ + public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffDstAtop : public DColorComposer +{ + public: + virtual void compose(DColor &dest, DColor src); +}; + +class DColorComposerPorterDuffXor : public DColorComposer +{ + public: + virtual void compose(DColor &dest, DColor src); +}; + +// Porter-Duff None +// component = (source * sa + destination * (1-sa)) +// Src blending function Src Alpha +// Dst blending function Inv Src Alpha +void DColorComposerPorterDuffNone::compose(DColor &dest, DColor src) +{ + // preserve src alpha value for dest blending, + // src.alpha() will be changed after blending src + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendAlpha16(sa); + dest.blendInvAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendAlpha8(sa); + dest.blendInvAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Clear +// component = (source * 0 + destination * 0) +// Src blending function Zero +// Dst blending function Zero +void DColorComposerPorterDuffClear::compose(DColor &dest, DColor src) +{ + src.blendZero(); + dest.blendZero(); + dest.blendAdd(src); +} + +void DColorComposerPorterDuffClear::compose(DColor &dest, DColor src, MultiplicationFlags) +{ + // skip pre- and demultiplication + compose(dest, src); +} + +// Porter-Duff Src +// Normal Painter's algorithm +// component = (source * 1 + destination * 0) +// Src blending function One +// Dst blending function Zero +void DColorComposerPorterDuffSrc::compose(DColor &dest, DColor src) +{ + // src: no-op + dest.blendZero(); + dest.blendAdd(src); +} + +void DColorComposerPorterDuffSrc::compose(DColor &dest, DColor src, MultiplicationFlags) +{ + // skip pre- and demultiplication + compose(dest, src); +} + +// Porter-Duff Src Over +// component = (source * 1 + destination * (1-sa)) +// Src blending function One +// Dst blending function Inv Src Alpha +void DColorComposerPorterDuffSrcOver::compose(DColor &dest, DColor src) +{ + if (dest.sixteenBit()) + { + // src: no-op + dest.blendInvAlpha16(src.alpha()); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + // src: no-op + dest.blendInvAlpha8(src.alpha()); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Dst over +// component = (source * (1.0-da) + destination * 1) +// Src blending function Inv Dst Alpha +// Dst blending function One +void DColorComposerPorterDuffDstOver::compose(DColor &dest, DColor src) +{ + if (dest.sixteenBit()) + { + src.blendInvAlpha16(dest.alpha()); + // dest: no-op + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendInvAlpha8(dest.alpha()); + // dest: no-op + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Src In +// component = (source * da + destination * 0) +// Src blending function Dst Alpha +// Dst blending function Zero +void DColorComposerPorterDuffSrcIn::compose(DColor &dest, DColor src) +{ + if (dest.sixteenBit()) + { + src.blendAlpha16(dest.alpha()); + dest.blendZero(); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendAlpha8(dest.alpha()); + dest.blendZero(); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Dst In +// component = (source * 0 + destination * sa) +// Src blending function Zero +// Dst blending function Src Alpha +void DColorComposerPorterDuffDstIn::compose(DColor &dest, DColor src) +{ + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendZero(); + dest.blendAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendZero(); + dest.blendAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Src Out +// component = (source * (1-da) + destination * 0) +// Src blending function Inv Dst Alpha +// Dst blending function Zero +void DColorComposerPorterDuffSrcOut::compose(DColor &dest, DColor src) +{ + if (dest.sixteenBit()) + { + src.blendInvAlpha16(dest.alpha()); + dest.blendZero(); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendInvAlpha8(dest.alpha()); + dest.blendZero(); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Dst Out +// component = (source * 0 + destination * (1-sa)) +// Src blending function Zero +// Dst blending function Inv Src Alpha +void DColorComposerPorterDuffDstOut::compose(DColor &dest, DColor src) +{ + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendZero(); + dest.blendInvAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendZero(); + dest.blendInvAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Src Atop +// component = (source * da + destination * (1-sa)) +// Src blending function Dst Alpha +// Dst blending function Inv Src Alpha +void DColorComposerPorterDuffSrcAtop::compose(DColor &dest, DColor src) +{ + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendAlpha16(dest.alpha()); + dest.blendInvAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendAlpha8(dest.alpha()); + dest.blendInvAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Dst Atop +// component = (source * (1-da) + destination * sa) +// Src blending function Inv Dest Alpha +// Dst blending function Src Alpha +void DColorComposerPorterDuffDstAtop::compose(DColor &dest, DColor src) +{ + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendInvAlpha16(dest.alpha()); + dest.blendAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendInvAlpha8(dest.alpha()); + dest.blendInvAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// Porter-Duff Xor +// component = (source * (1-da) + destination * (1-sa)) +// Src blending function Inv Dst Alpha +// Dst blending function Inv Src Alpha +void DColorComposerPorterDuffXor::compose(DColor &dest, DColor src) +{ + int sa = src.alpha(); + if (dest.sixteenBit()) + { + src.blendInvAlpha16(dest.alpha()); + dest.blendInvAlpha16(sa); + dest.blendAdd(src); + dest.blendClamp16(); + } + else + { + src.blendInvAlpha8(dest.alpha()); + dest.blendInvAlpha8(sa); + dest.blendAdd(src); + dest.blendClamp8(); + } +} + +// ----------------------------------------------------------------------- + +void DColorComposer::compose(DColor &dest, DColor src, DColorComposer::MultiplicationFlags multiplicationFlags) +{ + if (multiplicationFlags & PremultiplySrc) + src.premultiply(); + if (multiplicationFlags & PremultiplyDst) + dest.premultiply(); + + compose(dest, src); + + if (multiplicationFlags & DemultiplyDst) + dest.demultiply(); +} + +DColorComposer *DColorComposer::getComposer(DColorComposer::CompositingOperation rule) +{ + switch(rule) + { + case PorterDuffNone: + return new DColorComposerPorterDuffNone; + case PorterDuffClear: + return new DColorComposerPorterDuffClear; + case PorterDuffSrc: + return new DColorComposerPorterDuffSrc; + case PorterDuffSrcOver: + return new DColorComposerPorterDuffSrcOver; + case PorterDuffDstOver: + return new DColorComposerPorterDuffDstOver; + case PorterDuffSrcIn: + return new DColorComposerPorterDuffSrcIn; + case PorterDuffDstIn: + return new DColorComposerPorterDuffDstIn; + case PorterDuffSrcOut: + return new DColorComposerPorterDuffSrcOut; + case PorterDuffDstOut: + return new DColorComposerPorterDuffDstOut; + case PorterDuffSrcAtop: + return new DColorComposerPorterDuffDstOut; + case PorterDuffDstAtop: + return new DColorComposerPorterDuffDstOut; + case PorterDuffXor: + return new DColorComposerPorterDuffDstOut; + } + return 0; +} + +} // namespace DigiKam diff --git a/src/libs/dimg/dcolorcomposer.h b/src/libs/dimg/dcolorcomposer.h new file mode 100644 index 00000000..3e885ca8 --- /dev/null +++ b/src/libs/dimg/dcolorcomposer.h @@ -0,0 +1,133 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-03-02 + * Description : DColor methods for composing + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DCOLORCOMPOSER_H +#define DCOLORCOMPOSER_H + +// Local includes. + +#include "dcolor.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DColorComposer +{ +public: + /** The available rules to combine src and destination color. + + For the Porter-Duff rules, the formula is + component = (source * fs + destination * fd) + where + fs, fd according to the following table with + sa = source alpha, + da = destination alpha: + + None fs: sa fd: 1.0-sa + Clear fs: 0.0 fd: 0.0 + Src fs: 1.0 fd: 0.0 + Src Over fs: 1.0 fd: 1.0-sa + Dst Over fs: 1.0-da fd: 1.0 + Src In fs: da fd: 0.0 + Dst In fs: 0.0 fd: sa + Src Out fs: 1.0-da fd: 0.0 + Dst Out fs: 0.0 fd: 1.0-sa + + Src Atop fs: da fd: 1.0-sa + Dst Atop fs: 1.0-da fd: sa + Xor fs: 1.0-da fd: 1.0-sa + + None is the default, classical blending mode, a "Src over" simplification: + Blend non-premultiplied RGBA data "src over" a fully opaque background. + Src is the painter's algorithm. + All other operations require premultiplied colors. + The documentation of java.awt.AlphaComposite (Java 1.5) + provides a good introduction and documentation on Porter Duff. + */ + + enum CompositingOperation + { + PorterDuffNone, + PorterDuffClear, + PorterDuffSrc, + PorterDuffSrcOver, + PorterDuffDstOver, + PorterDuffSrcIn, + PorterDuffDstIn, + PorterDuffSrcOut, + PorterDuffDstOut, + PorterDuffSrcAtop, + PorterDuffDstAtop, + PorterDuffXor + }; + + enum MultiplicationFlags + { + NoMultiplication = 0x00, + PremultiplySrc = 0x01, + PremultiplyDst = 0x02, + DemultiplyDst = 0x04, + + MultiplicationFlagsDImg = PremultiplySrc | PremultiplyDst | DemultiplyDst, + MultiplicationFlagsPremultipliedColorOnDImg = PremultiplyDst | DemultiplyDst + }; + + /** + Retrieve a DColorComposer object for one of the predefined rules. + The object needs to be deleted by the caller. + */ + static DColorComposer *getComposer(CompositingOperation rule); + + /** + Carry out the actual composition process. + Src and Dest are composed and the result is written to dest. + No pre-/demultiplication is done by this method, use the other overloaded + methods, which call this method, if you need pre- or demultiplication + (you need it if any of the colors are read from or written to a DImg). + + If you just pass the object to a DImg method, you do not need to call this. + Call this function if you want to compose two colors. + Implement this function if you create a custom DColorComposer. + + The bit depth of source and destination color must be identical. + */ + virtual void compose(DColor &dest, DColor src) = 0; + + /** + Compose the two colors by calling compose(dest, src). + Pre- and demultiplication operations are done as specified. + For PorterDuff operations except PorterDuffNone, you need + + - PremultiplySrc if src is not premultiplied (read from a DImg) + - PremultiplyDst if dst is not premultiplied (read from a DImg) + - DemultiplyDst if dst will be written to non-premultiplied data (a DImg) + */ + virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags); + + virtual ~DColorComposer(){}; +}; + +} // namespace Digikam + +#endif // DCOLORCOMPOSER_H diff --git a/src/libs/dimg/dcolorpixelaccess.h b/src/libs/dimg/dcolorpixelaccess.h new file mode 100644 index 00000000..30695c3e --- /dev/null +++ b/src/libs/dimg/dcolorpixelaccess.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-02 + * Description : methods to access on pixels color + * + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DCOLORPIXELACCESS_H +#define DCOLORPIXELACCESS_H + +namespace Digikam +{ + +// These methods are used in quite a few image effects, +// typically in loops iterating the data. +// Providing them as inline methods allows the compiler to optimize better. + +inline void DColor::setColor(const uchar *data, bool sixteenBit) +{ + m_sixteenBit = sixteenBit; + + if (!sixteenBit) // 8 bits image + { + setBlue (data[0]); + setGreen(data[1]); + setRed (data[2]); + setAlpha(data[3]); + } + else // 16 bits image + { + unsigned short* data16 = (unsigned short*)data; + setBlue (data16[0]); + setGreen(data16[1]); + setRed (data16[2]); + setAlpha(data16[3]); + } +} + +inline void DColor::setPixel(uchar *data) const +{ + if (sixteenBit()) // 16 bits image. + { + unsigned short *data16 = (unsigned short *)data; + data16[0] = (unsigned short)blue(); + data16[1] = (unsigned short)green(); + data16[2] = (unsigned short)red(); + data16[3] = (unsigned short)alpha(); + } + else // 8 bits image. + { + data[0] = (uchar)blue(); + data[1] = (uchar)green(); + data[2] = (uchar)red(); + data[3] = (uchar)alpha(); + } +} + + +} // namespace Digikam + +#endif // DCOLORPIXELACCESS_H diff --git a/src/libs/dimg/ddebug.cpp b/src/libs/dimg/ddebug.cpp new file mode 100644 index 00000000..3f8f07c4 --- /dev/null +++ b/src/libs/dimg/ddebug.cpp @@ -0,0 +1,92 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-11 + * Description : thread safe debugging. + * + * See B.K.O #133026: because kdDebug() is not thread-safe + * we need to use a dedicaced debug statements in threaded + * implementation to prevent crash. + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" + +#undef DDebug +#undef kdDebug + +namespace Digikam +{ + +//static KStaticDeleter deleter; +static TQMutex *_ddebug_mutex_ = 0; + +Ddbgstream::Ddbgstream(kdbgstream stream) + : kdbgstream(stream) +{ + // using a static variable here - we can safely assume that kdDebug + // is called at least once from the main thread before threads start. + if (!_ddebug_mutex_) + { + // leak the mutex object for simplicity + _ddebug_mutex_ = new TQMutex; + //deleter.setObject(mutex, new TQMutex); + //TDEGlobal::unregisterStaticDeleter(&deleter); + } + _ddebug_mutex_->lock(); +} + +Ddbgstream::~Ddbgstream() +{ + _ddebug_mutex_->unlock(); +} + +Dndbgstream::Dndbgstream(kndbgstream stream) + : kndbgstream(stream) +{ + // using a static variable here - we can safely assume that kdDebug + // is called at least once from the main thread before threads start. + if (!_ddebug_mutex_) + { + // leak the mutex object for simplicity + _ddebug_mutex_ = new TQMutex; + //deleter.setObject(mutex, new TQMutex); + //TDEGlobal::unregisterStaticDeleter(&deleter); + } + _ddebug_mutex_->lock(); +} + +Dndbgstream::~Dndbgstream() +{ + _ddebug_mutex_->unlock(); +} + +} // namespace Digikam + +Digikam::Ddbgstream DDebug(int area) { return Digikam::Ddbgstream(kdDebug(area)); } +Digikam::Ddbgstream DError(int area) { return Digikam::Ddbgstream(kdError(area)); } +Digikam::Ddbgstream DWarning(int area) { return Digikam::Ddbgstream(kdWarning(area)); } + +Digikam::Dndbgstream DnDebug(int area) { return Digikam::Dndbgstream(kndDebug(area)); } + diff --git a/src/libs/dimg/ddebug.h b/src/libs/dimg/ddebug.h new file mode 100644 index 00000000..1ea337eb --- /dev/null +++ b/src/libs/dimg/ddebug.h @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-11 + * Description : thread safe debugging. + * + * See B.K.O #133026: because kdDebug() is not thread-safe + * we need to use a dedicaced debug statements in threaded + * implementation to prevent crash. + * + * Copyright (C) 2006 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef _DDEBUG_H_ +#define _DDEBUG_H_ + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT Ddbgstream : public kdbgstream +{ + +public: + + Ddbgstream(kdbgstream stream); + ~Ddbgstream(); +}; + +class DIGIKAM_EXPORT Dndbgstream : public kndbgstream +{ + +public: + + Dndbgstream(kndbgstream stream); + ~Dndbgstream(); +}; + +} // namespace Digikam + +DIGIKAM_EXPORT Digikam::Ddbgstream DDebug(int area = 0); +DIGIKAM_EXPORT Digikam::Ddbgstream DWarning(int area = 0); +DIGIKAM_EXPORT Digikam::Ddbgstream DError(int area = 0); + +DIGIKAM_EXPORT Digikam::Dndbgstream DnDebug(int area = 0); + +#ifdef NDEBUG +#define DDebug DnDebug +#endif + +#endif // _DDEBUG_H_ + diff --git a/src/libs/dimg/dimg.cpp b/src/libs/dimg/dimg.cpp new file mode 100644 index 00000000..d61f2dd0 --- /dev/null +++ b/src/libs/dimg/dimg.cpp @@ -0,0 +1,1700 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : digiKam 8/16 bits image management API + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C ANSI includes. + +extern "C" +{ +#if !defined(__STDC_LIMIT_MACROS) +#define __STDC_LIMIT_MACROS +#endif +#include +} + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "pngloader.h" +#include "jpegloader.h" +#include "tiffloader.h" +#include "ppmloader.h" +#include "rawloader.h" +#include "jp2kloader.h" +#include "qimageloader.h" +#include "icctransform.h" +#include "exposurecontainer.h" +#include "ddebug.h" +#include "dimgprivate.h" +#include "dimgloaderobserver.h" +#include "dimg.h" + +typedef uint64_t ullong; +typedef int64_t llong; + +namespace Digikam +{ + +DImg::DImg() + : m_priv(new DImgPrivate) +{ +} + +DImg::DImg(const TQCString& filePath, DImgLoaderObserver *observer, + DRawDecoding rawDecodingSettings) + : m_priv(new DImgPrivate) +{ + load(filePath, observer, rawDecodingSettings); +} + +DImg::DImg(const TQString& filePath, DImgLoaderObserver *observer, + DRawDecoding rawDecodingSettings) + : m_priv(new DImgPrivate) +{ + load(filePath, observer, rawDecodingSettings); +} + +DImg::DImg(const DImg& image) +{ + m_priv = image.m_priv; + m_priv->ref(); +} + +DImg::DImg(uint width, uint height, bool sixteenBit, bool alpha, uchar* data, bool copyData) + : m_priv(new DImgPrivate) +{ + putImageData(width, height, sixteenBit, alpha, data, copyData); +} + +DImg::DImg(const DImg &image, int w, int h) + : m_priv(new DImgPrivate) +{ + // This private constructor creates a copy of everything except the data. + // The image size is set to the given values and a buffer corresponding to these values is allocated. + // This is used by copy and scale. + copyImageData(image.m_priv); + copyMetaData(image.m_priv); + setImageDimension(w, h); + allocateData(); +} + +DImg::DImg(const TQImage& image) + : m_priv(new DImgPrivate) +{ + if (!image.isNull()) + { + TQImage target = image.convertDepth(32); + + uint w = target.width(); + uint h = target.height(); + uchar* data = new uchar[w*h*4]; + uint* sptr = (uint*)target.bits(); + uchar* dptr = data; + + for (uint i = 0 ; i < w*h ; i++) + { + dptr[0] = tqBlue(*sptr); + dptr[1] = tqGreen(*sptr); + dptr[2] = tqRed(*sptr); + dptr[3] = tqAlpha(*sptr); + + dptr += 4; + sptr++; + } + + putImageData(w, h, false, image.hasAlphaBuffer(), data, false); + } +} + +DImg::~DImg() +{ + if (m_priv->deref()) + delete m_priv; +} + + +//--------------------------------------------------------------------------------------------------- +// data management + + +DImg& DImg::operator=(const DImg& image) +{ + if (m_priv == image.m_priv) + return *this; + + if (m_priv->deref()) + { + delete m_priv; + m_priv = 0; + } + + m_priv = image.m_priv; + m_priv->ref(); + + return *this; +} + +bool DImg::operator==(const DImg& image) const +{ + return m_priv == image.m_priv; +} + +void DImg::reset(void) +{ + if (m_priv->deref()) + delete m_priv; + + m_priv = new DImgPrivate; +} + +void DImg::detach() +{ + // are we being shared? + if (m_priv->count <= 1) + { + return; + } + + DImgPrivate* old = m_priv; + + m_priv = new DImgPrivate; + copyImageData(old); + copyMetaData(old); + + if (old->data) + { + int size = allocateData(); + memcpy(m_priv->data, old->data, size); + } + + old->deref(); +} + +void DImg::putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar *data, bool copyData) +{ + // set image data, metadata is untouched + + bool null = (width == 0) || (height == 0); + // allocateData, or code below will set null to false + setImageData(true, width, height, sixteenBit, alpha); + + // replace data + delete [] m_priv->data; + if (null) + { + // image is null - no data + m_priv->data = 0; + } + else if (copyData) + { + int size = allocateData(); + if (data) + memcpy(m_priv->data, data, size); + } + else + { + if (data) + { + m_priv->data = data; + m_priv->null = false; + } + else + allocateData(); + } +} + +void DImg::putImageData(uchar *data, bool copyData) +{ + if (!data) + { + delete [] m_priv->data; + m_priv->data = 0; + m_priv->null = true; + } + else if (copyData) + { + memcpy(m_priv->data, data, numBytes()); + } + else + { + m_priv->data = data; + } +} + +void DImg::resetMetaData() +{ + m_priv->attributes.clear(); + m_priv->embeddedText.clear(); + m_priv->metaData.clear(); +} + +uchar *DImg::stripImageData() +{ + uchar *data = m_priv->data; + m_priv->data = 0; + m_priv->null = true; + return data; +} + +void DImg::copyMetaData(const DImgPrivate *src) +{ + m_priv->isReadOnly = src->isReadOnly; + m_priv->attributes = src->attributes; + m_priv->embeddedText = src->embeddedText; + + // since qbytearrays are explicitly shared, we need to make sure that they are + // detached from any shared references + + for (TQMap::const_iterator it = src->metaData.begin(); + it != src->metaData.end(); ++it) + { + m_priv->metaData.insert(it.key(), it.data().copy()); + } +} + +void DImg::copyImageData(const DImgPrivate *src) +{ + setImageData(src->null, src->width, src->height, src->sixteenBit, src->alpha); +} + +int DImg::allocateData() +{ + int size = m_priv->width * m_priv->height * (m_priv->sixteenBit ? 8 : 4); + m_priv->data = new uchar[size]; + m_priv->null = false; + return size; +} + +void DImg::setImageDimension(uint width, uint height) +{ + m_priv->width = width; + m_priv->height = height; +} + +void DImg::setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha) +{ + m_priv->null = null; + m_priv->width = width; + m_priv->height = height; + m_priv->alpha = alpha; + m_priv->sixteenBit = sixteenBit; +} + + +//--------------------------------------------------------------------------------------------------- +// load and save + + +bool DImg::load(const TQString& filePath, DImgLoaderObserver *observer, + DRawDecoding rawDecodingSettings) +{ + FORMAT format = fileFormat(filePath); + + switch (format) + { + case(NONE): + { + DDebug() << filePath << " : Unknown image format !!!" << endl; + return false; + break; + } + case(JPEG): + { + DDebug() << filePath << " : JPEG file identified" << endl; + JPEGLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + case(TIFF): + { + DDebug() << filePath << " : TIFF file identified" << endl; + TIFFLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + case(PNG): + { + DDebug() << filePath << " : PNG file identified" << endl; + PNGLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + case(PPM): + { + DDebug() << filePath << " : PPM file identified" << endl; + PPMLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + case(RAW): + { + DDebug() << filePath << " : RAW file identified" << endl; + RAWLoader loader(this, rawDecodingSettings); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + case(JP2K): + { + DDebug() << filePath << " : JPEG2000 file identified" << endl; + JP2KLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + default: + { + DDebug() << filePath << " : TQIMAGE file identified" << endl; + TQImageLoader loader(this); + if (loader.load(filePath, observer)) + { + m_priv->null = false; + m_priv->alpha = loader.hasAlpha(); + m_priv->sixteenBit = loader.sixteenBit(); + m_priv->isReadOnly = loader.isReadOnly(); + return true; + } + break; + } + } + + return false; +} + +bool DImg::save(const TQString& filePath, const TQString& format, DImgLoaderObserver *observer) +{ + if (isNull()) + return false; + + if (format.isEmpty()) + return false; + + TQString frm = format.upper(); + + if (frm == "JPEG" || frm == "JPG" || frm == "JPE") + { + JPEGLoader loader(this); + return loader.save(filePath, observer); + } + else if (frm == "PNG") + { + PNGLoader loader(this); + return loader.save(filePath, observer); + } + else if (frm == "TIFF" || frm == "TIF") + { + TIFFLoader loader(this); + return loader.save(filePath, observer); + } + else if (frm == "PPM") + { + PPMLoader loader(this); + return loader.save(filePath, observer); + } + if (frm == "JP2" || frm == "JPX" || frm == "JPC" || frm == "PGX") + { + JP2KLoader loader(this); + return loader.save(filePath, observer); + } + else + { + setAttribute("format", format); + TQImageLoader loader(this); + return loader.save(filePath, observer); + } + + return false; +} + +DImg::FORMAT DImg::fileFormat(const TQString& filePath) +{ + if ( filePath.isNull() ) + return NONE; + + // In first we trying to check the file extension. This is mandatory because + // some tiff files are detected like RAW files by dcraw::identify method. + + TQFileInfo fileInfo(filePath); + if (!fileInfo.exists()) + { + DDebug() << k_funcinfo << "File \"" << filePath << "\" does not exist" << endl; + return NONE; + } + +#if KDCRAW_VERSION < 0x000106 + TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + TQString ext = fileInfo.extension(false).upper(); + + if (!ext.isEmpty()) + { + if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE")) + return JPEG; + else if (ext == TQString("PNG")) + return PNG; + else if (ext == TQString("TIFF") || ext == TQString("TIF")) + return TIFF; + else if (rawFilesExt.upper().contains(ext)) + return RAW; + if (ext == TQString("JP2") || ext == TQString("JPX") || // JPEG2000 file format + ext == TQString("JPC") || // JPEG2000 code stream + ext == TQString("PGX")) // JPEG2000 WM format + return JP2K; + } + + // In second, we trying to parse file header. + + FILE* f = fopen(TQFile::encodeName(filePath), "rb"); + + if (!f) + { + DDebug() << k_funcinfo << "Failed to open file \"" << filePath << "\"" << endl; + return NONE; + } + + const int headerLen = 9; + unsigned char header[headerLen]; + + if (fread(&header, headerLen, 1, f) != 1) + { + DDebug() << k_funcinfo << "Failed to read header of file \"" << filePath << "\"" << endl; + fclose(f); + return NONE; + } + + fclose(f); + + KDcrawIface::DcrawInfoContainer dcrawIdentify; + KDcrawIface::KDcraw::rawFileIdentify(dcrawIdentify, filePath); + uchar jpegID[2] = { 0xFF, 0xD8 }; + uchar tiffBigID[2] = { 0x4D, 0x4D }; + uchar tiffLilID[2] = { 0x49, 0x49 }; + uchar pngID[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; + uchar jp2ID[5] = { 0x6A, 0x50, 0x20, 0x20, 0x0D, }; + uchar jpcID[2] = { 0xFF, 0x4F }; + + if (memcmp(&header, &jpegID, 2) == 0) // JPEG file ? + { + return JPEG; + } + else if (memcmp(&header, &pngID, 8) == 0) // PNG file ? + { + return PNG; + } + else if (memcmp(&header[0], "P", 1) == 0 && + memcmp(&header[2], "\n", 1) == 0) // PPM 16 bits file ? + { + int width, height, rgbmax; + char nl; + FILE *file = fopen(TQFile::encodeName(filePath), "rb"); + + if (fscanf (file, "P6 %d %d %d%c", &width, &height, &rgbmax, &nl) == 4) + { + if (rgbmax > 255) + { + pclose (file); + return PPM; + } + } + + pclose (file); + } + else if (dcrawIdentify.isDecodable) + { + // RAW File test using dcraw::identify method. + // Need to test it before TIFF because any RAW file + // formats using TIFF header. + return RAW; + } + else if (memcmp(&header, &tiffBigID, 2) == 0 || // TIFF file ? + memcmp(&header, &tiffLilID, 2) == 0) + { + return TIFF; + } + else if (memcmp(&header[4], &jp2ID, 5) == 0 || // JPEG2000 file ? + memcmp(&header, &jpcID, 2) == 0) + { + return JP2K; + } + + // In others cases, TQImage will be used to try to open file. + return TQIMAGE; +} + + +//--------------------------------------------------------------------------------------------------- +// accessing properties + + +bool DImg::isNull() const +{ + return m_priv->null; +} + +uint DImg::width() const +{ + return m_priv->width; +} + +uint DImg::height() const +{ + return m_priv->height; +} + +TQSize DImg::size() const +{ + return TQSize(m_priv->width, m_priv->height); +} + +uchar* DImg::bits() const +{ + return m_priv->data; +} + +uchar* DImg::scanLine(uint i) const +{ + if ( i >= height() ) + return 0; + + uchar *data = bits() + (width() * bytesDepth() * i); + return data; +} + +bool DImg::hasAlpha() const +{ + return m_priv->alpha; +} + +bool DImg::sixteenBit() const +{ + return m_priv->sixteenBit; +} + +bool DImg::isReadOnly() const +{ + return m_priv->isReadOnly; +} + +bool DImg::getICCProfilFromFile(const TQString& filePath) +{ + TQFile file(filePath); + if ( !file.open(IO_ReadOnly) ) + return false; + + TQByteArray data(file.size()); + TQDataStream stream( &file ); + stream.readRawBytes(data.data(), data.size()); + setICCProfil(data); + file.close(); + return true; +} + +bool DImg::setICCProfilToFile(const TQString& filePath) +{ + TQFile file(filePath); + if ( !file.open(IO_WriteOnly) ) + return false; + + TQByteArray data(getICCProfil()); + TQDataStream stream( &file ); + stream.writeRawBytes(data.data(), data.size()); + file.close(); + return true; +} + +TQByteArray DImg::getComments() const +{ + return metadata(COM); +} + +TQByteArray DImg::getExif() const +{ + return metadata(EXIF); +} + +TQByteArray DImg::getIptc() const +{ + return metadata(IPTC); +} + +TQByteArray DImg::getICCProfil() const +{ + return metadata(ICC); +} + +void DImg::setComments(const TQByteArray& commentsData) +{ + m_priv->metaData.replace(COM, commentsData); +} + +void DImg::setExif(const TQByteArray& exifData) +{ + m_priv->metaData.replace(EXIF, exifData); +} + +void DImg::setIptc(const TQByteArray& iptcData) +{ + m_priv->metaData.replace(IPTC, iptcData); +} + +void DImg::setICCProfil(const TQByteArray& profile) +{ + m_priv->metaData.replace(ICC, profile); +} + +TQByteArray DImg::metadata(DImg::METADATA key) const +{ + typedef TQMap MetaDataMap; + + for (MetaDataMap::iterator it = m_priv->metaData.begin(); it != m_priv->metaData.end(); ++it) + { + if (it.key() == key) + return it.data(); + } + + return TQByteArray(); +} + +uint DImg::numBytes() const +{ + return (width() * height() * bytesDepth()); +} + +uint DImg::numPixels() const +{ + return (width() * height()); +} + +int DImg::bytesDepth() const +{ + if (sixteenBit()) + return 8; + + return 4; +} + +int DImg::bitsDepth() const +{ + if (sixteenBit()) + return 16; + + return 8; +} + +void DImg::setAttribute(const TQString& key, const TQVariant& value) +{ + m_priv->attributes.insert(key, value); +} + +TQVariant DImg::attribute(const TQString& key) const +{ + if (m_priv->attributes.contains(key)) + return m_priv->attributes[key]; + + return TQVariant(); +} + +void DImg::setEmbeddedText(const TQString& key, const TQString& text) +{ + m_priv->embeddedText.insert(key, text); +} + +TQString DImg::embeddedText(const TQString& key) const +{ + if (m_priv->embeddedText.contains(key)) + return m_priv->embeddedText[key]; + + return TQString(); +} + +DColor DImg::getPixelColor(uint x, uint y) const +{ + if (isNull() || x > width() || y > height()) + { + DDebug() << k_funcinfo << " : wrong pixel position!" << endl; + return DColor(); + } + + uchar *data = bits() + x*bytesDepth() + (width()*y*bytesDepth()); + + return( DColor(data, sixteenBit()) ); +} + +void DImg::setPixelColor(uint x, uint y, DColor color) +{ + if (isNull() || x > width() || y > height()) + { + DDebug() << k_funcinfo << " : wrong pixel position!" << endl; + return; + } + + if (color.sixteenBit() != sixteenBit()) + { + DDebug() << k_funcinfo << " : wrong color depth!" << endl; + return; + } + + uchar *data = bits() + x*bytesDepth() + (width()*y*bytesDepth()); + color.setPixel(data); +} + + +//--------------------------------------------------------------------------------------------------- +// copying operations + + +DImg DImg::copy() +{ + DImg img(*this); + img.detach(); + return img; +} + +DImg DImg::copyImageData() +{ + DImg img(width(), height(), sixteenBit(), hasAlpha(), bits(), true); + return img; +} + +DImg DImg::copyMetaData() +{ + DImg img; + // copy width, height, alpha, sixteenBit, null + img.copyImageData(m_priv); + // deeply copy metadata + img.copyMetaData(m_priv); + // set image to null + img.m_priv->null = true; + return img; +} + +DImg DImg::copy(TQRect rect) +{ + return copy(rect.x(), rect.y(), rect.width(), rect.height()); +} + +DImg DImg::copy(int x, int y, int w, int h) +{ + if ( isNull() || w <= 0 || h <= 0) + { + DDebug() << k_funcinfo << " : return null image!" << endl; + return DImg(); + } + + DImg image(*this, w, h); + image.bitBltImage(this, x, y, w, h, 0, 0); + + return image; +} + + +//--------------------------------------------------------------------------------------------------- +// bitwise operations + + +void DImg::bitBltImage(const DImg* src, int dx, int dy) +{ + bitBltImage(src, 0, 0, src->width(), src->height(), dx, dy); +} + +void DImg::bitBltImage(const DImg* src, int sx, int sy, int dx, int dy) +{ + bitBltImage(src, sx, sy, src->width() - sx, src->height() - sy, dx, dy); +} + +void DImg::bitBltImage(const DImg* src, int sx, int sy, int w, int h, int dx, int dy) +{ + if (isNull()) + return; + + if (src->sixteenBit() != sixteenBit()) + { + DWarning() << "Blitting from 8-bit to 16-bit or vice versa is not supported" << endl; + return; + } + + if (w == -1 && h == -1) + { + w = src->width(); + h = src->height(); + } + + bitBlt(src->bits(), bits(), sx, sy, w, h, dx, dy, + src->width(), src->height(), width(), height(), sixteenBit(), src->bytesDepth(), bytesDepth()); +} + +void DImg::bitBltImage(const uchar* src, int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, int sdepth) +{ + if (isNull()) + return; + + if (bytesDepth() != sdepth) + { + DWarning() << "Blitting from 8-bit to 16-bit or vice versa is not supported" << endl; + return; + } + + if (w == -1 && h == -1) + { + w = swidth; + h = sheight; + } + + bitBlt(src, bits(), sx, sy, w, h, dx, dy, swidth, sheight, width(), height(), sixteenBit(), sdepth, bytesDepth()); +} + +bool DImg::normalizeRegionArguments(int &sx, int &sy, int &w, int &h, int &dx, int &dy, + uint swidth, uint sheight, uint dwidth, uint dheight) +{ + if (sx < 0) + { + // sx is negative, so + is - and - is + + dx -= sx; + w += sx; + sx = 0; + } + + if (sy < 0) + { + dy -= sy; + h += sy; + sy = 0; + } + + if (dx < 0) + { + sx -= dx; + w += dx; + dx = 0; + } + + if (dy < 0) + { + sy -= dy; + h += dy; + dy = 0; + } + + if (sx + w > (int)swidth) + { + w = swidth - sx; + } + + if (sy + h > (int)sheight) + { + h = sheight - sy; + } + + if (dx + w > (int)dwidth) + { + w = dwidth - dx; + } + + if (dy + h > (int)dheight) + { + h = dheight - dy; + } + + // Nothing left to copy + if (w <= 0 || h <= 0) + return false; + + return true; +} + +void DImg::bitBlt (const uchar *src, uchar *dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool /*sixteenBit*/, int sdepth, int ddepth) +{ + // Normalize + if (!normalizeRegionArguments(sx, sy, w, h, dx, dy, swidth, sheight, dwidth, dheight)) + return; + + // Same pixels + if (src == dest && dx==sx && dy==sy) + return; + + const uchar *sptr; + uchar *dptr; + uint slinelength = swidth * sdepth; + uint dlinelength = dwidth * ddepth; + + int scurY = sy; + int dcurY = dy; + for (int j = 0 ; j < h ; j++, scurY++, dcurY++) + { + sptr = &src [ scurY * slinelength ] + sx * sdepth; + dptr = &dest[ dcurY * dlinelength ] + dx * ddepth; + + // plain and simple bitBlt + for (int i = 0; i < w * sdepth ; i++, sptr++, dptr++) + { + *dptr = *sptr; + } + } +} + +void DImg::bitBlendImage(DColorComposer *composer, const DImg* src, + int sx, int sy, int w, int h, int dx, int dy, + DColorComposer::MultiplicationFlags multiplicationFlags) +{ + if (isNull()) + return; + + if (src->sixteenBit() != sixteenBit()) + { + DWarning() << "Blending from 8-bit to 16-bit or vice versa is not supported" << endl; + return; + } + + bitBlend(composer, src->bits(), bits(), sx, sy, w, h, dx, dy, + src->width(), src->height(), width(), height(), sixteenBit(), + src->bytesDepth(), bytesDepth(), multiplicationFlags); +} + +void DImg::bitBlend (DColorComposer *composer, const uchar *src, uchar *dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool sixteenBit, int sdepth, int ddepth, + DColorComposer::MultiplicationFlags multiplicationFlags) +{ + // Normalize + if (!normalizeRegionArguments(sx, sy, w, h, dx, dy, swidth, sheight, dwidth, dheight)) + return; + + const uchar *sptr; + uchar *dptr; + uint slinelength = swidth * sdepth; + uint dlinelength = dwidth * ddepth; + + int scurY = sy; + int dcurY = dy; + for (int j = 0 ; j < h ; j++, scurY++, dcurY++) + { + sptr = &src [ scurY * slinelength ] + sx * sdepth; + dptr = &dest[ dcurY * dlinelength ] + dx * ddepth; + + // blend src and destination + for (int i = 0 ; i < w ; i++, sptr+=sdepth, dptr+=ddepth) + { + DColor src(sptr, sixteenBit); + DColor dst(dptr, sixteenBit); + + // blend colors + composer->compose(dst, src, multiplicationFlags); + + dst.setPixel(dptr); + } + } +} + + +//--------------------------------------------------------------------------------------------------- +// TQImage / TQPixmap access + + +TQImage DImg::copyTQImage() +{ + if (isNull()) + return TQImage(); + + if (sixteenBit()) + { + DImg img(*this); + img.detach(); + img.convertDepth(32); + return img.copyTQImage(); + } + + TQImage img(width(), height(), 32); + + uchar* sptr = bits(); + uint* dptr = (uint*)img.bits(); + + for (uint i=0; i < width()*height(); i++) + { + *dptr++ = tqRgba(sptr[2], sptr[1], sptr[0], sptr[3]); + sptr += 4; + } + + if (hasAlpha()) + { + img.setAlphaBuffer(true); + } + + return img; +} + +TQImage DImg::copyTQImage(TQRect rect) +{ + return (copyTQImage(rect.x(), rect.y(), rect.width(), rect.height())); +} + +TQImage DImg::copyTQImage(int x, int y, int w, int h) +{ + if (isNull()) + return TQImage(); + + DImg img = copy(x, y, w, h); + + if (img.sixteenBit()) + img.convertDepth(32); + + return img.copyTQImage(); +} + +TQPixmap DImg::convertToPixmap() +{ + if (isNull()) + return TQPixmap(); + + if (sixteenBit()) + { + // make fastaaaa.. + return TQPixmap(copyTQImage(0, 0, width(), height())); + } + + if (TQImage::systemByteOrder() == TQImage::BigEndian) + { + TQImage img(width(), height(), 32); + + uchar* sptr = bits(); + uint* dptr = (uint*)img.bits(); + + for (uint i=0; ihasOutputProfile()) + { + DDebug() << k_funcinfo << " : no monitor ICC profile available!" << endl; + return convertToPixmap(); + } + + DImg img = copy(); + + // Without embedded profile + if (img.getICCProfil().isNull()) + { + TQByteArray fakeProfile; + monitorICCtrans->apply(img, fakeProfile, monitorICCtrans->getRenderingIntent(), + monitorICCtrans->getUseBPC(), false, + monitorICCtrans->inputProfile().isNull()); + } + // With embedded profile. + else + { + monitorICCtrans->getEmbeddedProfile( img ); + monitorICCtrans->apply( img ); + } + + return (img.convertToPixmap()); +} + +TQImage DImg::pureColorMask(ExposureSettingsContainer *expoSettings) +{ + if (isNull() || (!expoSettings->underExposureIndicator && !expoSettings->overExposureIndicator)) + return TQImage(); + + TQImage img(size(), 32); + img.fill(0x00000000); // Full transparent. + img.setAlphaBuffer(true); + + uchar *bits = img.bits(); + int max = sixteenBit() ? 65535 : 255; + int index; + DColor pix; + + for (uint x=0 ; x < width() ; x++) + { + for (uint y=0 ; yunderExposureIndicator && + pix.red() == 0 && pix.green() == 0 && pix.blue() == 0) + { + bits[index ] = expoSettings->underExposureColor.blue(); + bits[index + 1] = expoSettings->underExposureColor.green(); + bits[index + 2] = expoSettings->underExposureColor.red(); + bits[index + 3] = 0xFF; + } + + if (expoSettings->overExposureIndicator && + pix.red() == max && pix.green() == max && pix.blue() == max) + { + bits[index ] = expoSettings->overExposureColor.blue(); + bits[index + 1] = expoSettings->overExposureColor.green(); + bits[index + 2] = expoSettings->overExposureColor.red(); + bits[index + 3] = 0xFF; + } + } + } + + return img; +} + + +//--------------------------------------------------------------------------------------------------- +// basic imaging operations + + +void DImg::crop(TQRect rect) +{ + crop(rect.x(), rect.y(), rect.width(), rect.height()); +} + +void DImg::crop(int x, int y, int w, int h) +{ + if ( isNull() || w <= 0 || h <= 0) + return; + + uint oldw = width(); + uint oldh = height(); + uchar *old = stripImageData(); + + // set new image data, bits(), width(), height() change + setImageDimension(w, h); + allocateData(); + + // copy image region (x|y), wxh, from old data to point (0|0) of new data + bitBlt(old, bits(), x, y, w, h, 0, 0, oldw, oldh, width(), height(), sixteenBit(), bytesDepth(), bytesDepth()); + delete [] old; +} + +void DImg::resize(int w, int h) +{ + if ( w <= 0 || h <= 0) + return; + + DImg image = smoothScale(w, h); + + delete [] m_priv->data; + m_priv->data = image.stripImageData(); + setImageDimension(w, h); +} + +void DImg::rotate(ANGLE angle) +{ + if (isNull()) + return; + + switch (angle) + { + case(ROT90): + { + uint w = height(); + uint h = width(); + + if (sixteenBit()) + { + ullong* newData = new ullong[w*h]; + + ullong *from = (ullong*) m_priv->data; + ullong *to; + + for (int y = w-1; y >=0; y--) + { + to = newData + y; + + for (uint x=0; x < h; x++) + { + *to = *from++; + to += w; + } + } + + setImageDimension(w, h); + + delete [] m_priv->data; + m_priv->data = (uchar*)newData; + } + else + { + uint* newData = new uint[w*h]; + + uint *from = (uint*) m_priv->data; + uint *to; + + for (int y = w-1; y >=0; y--) + { + to = newData + y; + + for (uint x=0; x < h; x++) + { + *to = *from++; + to += w; + } + } + + setImageDimension(w, h); + + delete [] m_priv->data; + m_priv->data = (uchar*)newData; + } + + break; + } + case(ROT180): + { + uint w = width(); + uint h = height(); + + int middle_line = -1; + if (h % 2) + middle_line = h / 2; + + if (sixteenBit()) + { + ullong *line1; + ullong *line2; + + ullong* data = (ullong*) bits(); + ullong tmp; + + // can be done inplace + for (uint y = 0; y < (h+1)/2; y++) + { + line1 = data + y * w; + line2 = data + (h-y) * w; + for (uint x=0; x < w; x++) + { + tmp = *line1; + *line1 = *line2; + *line2 = tmp; + + line1++; + line2--; + if ((int)y == middle_line && x * 2 >= w) + break; + } + } + } + else + { + uint *line1; + uint *line2; + + uint* data = (uint*) bits(); + uint tmp; + + // can be done inplace + for (uint y = 0; y < (h+1)/2; y++) + { + line1 = data + y * w; + line2 = data + (h-y) * w; + + for (uint x=0; x < w; x++) + { + tmp = *line1; + *line1 = *line2; + *line2 = tmp; + + line1++; + line2--; + if ((int)y == middle_line && x * 2 >= w) + break; + } + } + } + + break; + } + case(ROT270): + { + uint w = height(); + uint h = width(); + + if (sixteenBit()) + { + ullong* newData = new ullong[w*h]; + + ullong *from = (ullong*) m_priv->data; + ullong *to; + + for (uint y = 0; y < w; y++) + { + to = newData + y + w*(h-1); + + for (uint x=0; x < h; x++) + { + *to = *from++; + to -= w; + } + } + + setImageDimension(w, h); + + delete [] m_priv->data; + m_priv->data = (uchar*)newData; + } + else + { + uint* newData = new uint[w*h]; + + uint *from = (uint*) m_priv->data; + uint *to; + + for (uint y = 0; y < w; y++) + { + to = newData + y + w*(h-1); + + for (uint x=0; x < h; x++) + { + *to = *from++; + to -= w; + } + } + + setImageDimension(w, h); + + delete [] m_priv->data; + m_priv->data = (uchar*)newData; + } + + break; + } + default: + break; + } +} + +// 15-11-2005: This method have been tested indeep with valgrind by Gilles. + +void DImg::flip(FLIP direction) +{ + if (isNull()) + return; + + switch (direction) + { + case(HORIZONTAL): + { + uint w = width(); + uint h = height(); + + if (sixteenBit()) + { + unsigned short tmp[4]; + unsigned short *beg; + unsigned short *end; + + unsigned short * data = (unsigned short *)bits(); + + // can be done inplace + for (uint y = 0 ; y < h ; y++) + { + beg = data + y * w * 4; + end = beg + (w-1) * 4; + + for (uint x=0 ; x < (w/2) ; x++) + { + memcpy(&tmp, beg, 8); + memcpy(beg, end, 8); + memcpy(end, &tmp, 8); + + beg+=4; + end-=4; + } + } + } + else + { + uchar tmp[4]; + uchar *beg; + uchar *end; + + uchar* data = bits(); + + // can be done inplace + for (uint y = 0 ; y < h ; y++) + { + beg = data + y * w * 4; + end = beg + (w-1) * 4; + + for (uint x=0 ; x < (w/2) ; x++) + { + memcpy(&tmp, beg, 4); + memcpy(beg, end, 4); + memcpy(end, &tmp, 4); + + beg+=4; + end-=4; + } + } + } + + break; + } + case(VERTICAL): + { + uint w = width(); + uint h = height(); + + if (sixteenBit()) + { + unsigned short tmp[4]; + unsigned short *line1; + unsigned short *line2; + + unsigned short* data = (unsigned short*) bits(); + + // can be done inplace + for (uint y = 0 ; y < (h/2) ; y++) + { + line1 = data + y * w * 4; + line2 = data + (h-y-1) * w * 4; + + for (uint x=0 ; x < w ; x++) + { + memcpy(&tmp, line1, 8); + memcpy(line1, line2, 8); + memcpy(line2, &tmp, 8); + + line1+=4; + line2+=4; + } + } + } + else + { + uchar tmp[4]; + uchar *line1; + uchar *line2; + + uchar* data = bits(); + + // can be done inplace + for (uint y = 0 ; y < (h/2) ; y++) + { + line1 = data + y * w * 4; + line2 = data + (h-y-1) * w * 4; + + for (uint x=0 ; x < w ; x++) + { + memcpy(&tmp, line1, 4); + memcpy(line1, line2, 4); + memcpy(line2, &tmp, 4); + + line1+=4; + line2+=4; + } + } + } + + break; + } + default: + break; + } +} + +void DImg::convertToSixteenBit() +{ + convertDepth(64); +} + +void DImg::convertToEightBit() +{ + convertDepth(32); +} + +void DImg::convertToDepthOfImage(const DImg *otherImage) +{ + if (otherImage->sixteenBit()) + convertToSixteenBit(); + else + convertToEightBit(); +} + +void DImg::convertDepth(int depth) +{ + if (isNull()) + return; + + if (depth != 32 && depth != 64) + { + DDebug() << k_funcinfo << " : wrong color depth!" << endl; + return; + } + + if (((depth == 32) && !sixteenBit()) || + ((depth == 64) && sixteenBit())) + return; + + if (depth == 32) + { + // downgrading from 16 bit to 8 bit + + uchar* data = new uchar[width()*height()*4]; + uchar* dptr = data; + ushort* sptr = (ushort*)bits(); + + for (uint i=0; idata; + m_priv->data = data; + m_priv->sixteenBit = false; + } + else if (depth == 64) + { + // upgrading from 8 bit to 16 bit + + uchar* data = new uchar[width()*height()*8]; + ushort* dptr = (ushort*)data; + uchar* sptr = bits(); + + for (uint i=0; idata; + m_priv->data = data; + m_priv->sixteenBit = true; + } +} + +void DImg::fill(DColor color) +{ + if (sixteenBit()) + { + unsigned short *imgData16 = (unsigned short *)m_priv->data; + + for (uint i = 0 ; i < width()*height()*4 ; i+=4) + { + imgData16[ i ] = (unsigned short)color.blue(); + imgData16[i+1] = (unsigned short)color.green(); + imgData16[i+2] = (unsigned short)color.red(); + imgData16[i+3] = (unsigned short)color.alpha(); + } + } + else + { + uchar *imgData = m_priv->data; + + for (uint i = 0 ; i < width()*height()*4 ; i+=4) + { + imgData[ i ] = (uchar)color.blue(); + imgData[i+1] = (uchar)color.green(); + imgData[i+2] = (uchar)color.red(); + imgData[i+3] = (uchar)color.alpha(); + } + } +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/dimg.h b/src/libs/dimg/dimg.h new file mode 100644 index 00000000..48973e47 --- /dev/null +++ b/src/libs/dimg/dimg.h @@ -0,0 +1,353 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : digiKam 8/16 bits image management API + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMG_H +#define DIMG_H + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "digikam_export.h" +#include "drawdecoding.h" +#include "dcolor.h" +#include "dcolorcomposer.h" + +class TQString; + +namespace Digikam +{ + +class ExposureSettingsContainer; +class DImgPrivate; +class IccTransform; +class DImgLoaderObserver; + +class DIGIKAM_EXPORT DImg +{ +public: + + enum FORMAT + { + NONE = 0, + JPEG, + PNG, + TIFF, + RAW, + PPM, + JP2K, + TQIMAGE + }; + + enum METADATA + { + COM, // JFIF comments section data. + EXIF, // EXIF meta-data. + IPTC, // IPTC meta-data. + ICC // ICC color profile. + }; + + enum ANGLE + { + ROT90, + ROT180, + ROT270 + }; + + enum FLIP + { + HORIZONTAL, + VERTICAL + }; + + /** Identify file format */ + static FORMAT fileFormat(const TQString& filePath); + + /** Create null image */ + DImg(); + + /** Load image using TQCString as file path */ + DImg(const TQCString& filePath, DImgLoaderObserver *observer = 0, + DRawDecoding rawDecodingSettings=DRawDecoding()); + + /** Load image using TQString as file path */ + DImg(const TQString& filePath, DImgLoaderObserver *observer = 0, + DRawDecoding rawDecodingSettings=DRawDecoding()); + + /** Copy image: Creates a shallow copy that refers to the same shared data. + The two images will be equal. Call detach() or copy() to create deep copies. + */ + DImg(const DImg& image); + + /** Copy image: Creates a copy of a TQImage object. If the TQImage is null, a + null DImg will be created. + */ + DImg(const TQImage& image); + + /** Create image from data. + If data is 0, a new buffer will be allocated, otherwise the given data will be used: + If copydata is true, the data will be copied to a newly allocated buffer. + If copyData is false, this DImg object will take ownership of the data pointer. + + If there is an alpha channel, the data shall be in non-premultiplied form (unassociated alpha). + */ + DImg(uint width, uint height, bool sixteenBit, bool alpha=false, uchar* data = 0, bool copyData = true); + + ~DImg(); + + /** Equivalent to the copy constructor */ + DImg& operator=(const DImg& image); + + /** Detaches from shared data and makes sure that this image + is the only one referring to the data. + If multiple images share common data, this image makes a copy + of the data and detaches itself from the sharing mechanism. + Nothing is done if there is just a single reference. + */ + void detach(); + + /** Returns whether two images are equal. + Two images are equal if and only if they refer to the same shared data. + (Thus, DImg() == DImg() is not true, both instances refer two their + own shared data. image == DImg(image) is true.) + If two or more images refer to the same data, they have the same + image data, bits() returns the same data, they have the same metadata, + and a change to one image also affects the others. + Call detach() to split one image from the group of equal images. + */ + bool operator==(const DImg& image) const; + + + /** Replaces image data of this object. Metadata is unchanged. Parameters like constructor above. */ + void putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar *data, bool copyData = true); + + /** Overloaded function, provided for convenience, behaves essentially + like the function above if data is not 0. + Uses current width, height, sixteenBit, and alpha values. + If data is 0, the current data is deleted and the image is set to null + (But metadata unchanged). + */ + void putImageData(uchar *data, bool copyData = true); + + /** Reset metadata and image data to null image */ + void reset(void); + + /** Reset metadata, but do not change image data */ + void resetMetaData(void); + + /** Returns the data of this image. + Ownership of the buffer is passed to the caller, this image will be null afterwards. + */ + uchar* stripImageData(); + + + + bool load(const TQString& filePath, DImgLoaderObserver *observer = 0, + DRawDecoding rawDecodingSettings=DRawDecoding()); + + bool save(const TQString& filePath, const TQString& format, DImgLoaderObserver *observer = 0); + + bool isNull() const; + uint width() const; + uint height() const; + TQSize size() const; + uchar* bits() const; + uchar* scanLine(uint i) const; + bool hasAlpha() const; + bool sixteenBit() const; + uint numBytes() const; + uint numPixels() const; + + /** Return the number of bytes depth of one pixel : 4 (non sixteenBit) or 8 (sixteen) */ + int bytesDepth() const; + + /** Return the number of bits depth of one color component for one pixel : 8 (non sixteenBit) or 16 (sixteen) */ + int bitsDepth() const; + + /** Access a single pixel of the image. + These functions add some safety checks and then use the methods from DColor. + In optimized code working directly on the data, + better use the inline methods from DColor. + */ + DColor getPixelColor(uint x, uint y) const; + void setPixelColor(uint x, uint y, DColor color); + + /** + Return true if the original image file format cannot be saved. + This is depending of DImgLoader::save() implementation. For example + RAW file formats are supported by DImg uing dcraw than cannot support + writing operations. + */ + bool isReadOnly() const; + + /** Metadata manipulation methods */ + TQByteArray getComments() const; + TQByteArray getExif() const; + TQByteArray getIptc() const; + TQByteArray getICCProfil() const; + void setComments(const TQByteArray& commentsData); + void setExif(const TQByteArray& exifData); + void setIptc(const TQByteArray& iptcData); + void setICCProfil(const TQByteArray& profile); + + TQByteArray metadata(METADATA key) const; + + bool getICCProfilFromFile(const TQString& filePath); + bool setICCProfilToFile(const TQString& filePath); + + void setAttribute(const TQString& key, const TQVariant& value); + TQVariant attribute(const TQString& key) const; + + void setEmbeddedText(const TQString& key, const TQString& text); + TQString embeddedText(const TQString& key) const; + + + /** Return a deep copy of full image */ + DImg copy(); + + /** Return a deep copy of the image, but do not include metadata. */ + DImg copyImageData(); + + /** Return an image that containes a deep copy of + this image's metadata and the information associated + with the image data (width, height, hasAlpha, sixteenBit), + but no image data, i.e. isNull() is true. + */ + DImg copyMetaData(); + + /** Return a region of image */ + DImg copy(TQRect rect); + DImg copy(int x, int y, int w, int h); + + /** Copy a region of pixels from a source image to this image. + Parameters: + sx|sy Coordinates in the source image of the rectangle to be copied + w h Width and height of the rectangle (Default, or when both are -1: whole source image) + dx|dy Coordinates in this image of the rectangle in which the region will be copied + (Default: 0|0) + The bit depth of source and destination must be identical. + */ + void bitBltImage(const DImg* src, int dx, int dy); + void bitBltImage(const DImg* src, int sx, int sy, int dx, int dy); + void bitBltImage(const DImg* src, int sx, int sy, int w, int h, int dx, int dy); + void bitBltImage(const uchar* src, int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, int sdepth); + + /** Blend src image on this image (this is dest) with the specified composer + and multiplication flags. See documentation of DColorComposer for more info. + For the other arguments, see documentation of bitBltImage above. + */ + void bitBlendImage(DColorComposer *composer, const DImg* src, + int sx, int sy, int w, int h, int dx, int dy, + DColorComposer::MultiplicationFlags multiplicationFlags = + DColorComposer::NoMultiplication); + + /** TQImage wrapper methods */ + TQImage copyTQImage(); + TQImage copyTQImage(TQRect rect); + TQImage copyTQImage(int x, int y, int w, int h); + + /** Crop image to the specified region */ + void crop(TQRect rect); + void crop(int x, int y, int w, int h); + + /** Set width and height of this image, smoothScale it to the given size */ + void resize(int w, int h); + + /** Return a version of this image scaled to the specified size with the specified mode. + See TQSize documentation for information on available modes + */ + DImg smoothScale(int width, int height, TQSize::ScaleMode scaleMode=TQSize::ScaleFree); + + /** Take the region specified by the rectangle sx|sy, width and height sw * sh, + and scale it to an image with size dw * dh + */ + DImg smoothScaleSection(int sx, int sy, int sw, int sh, + int dw, int dh); + + void rotate(ANGLE angle); + void flip(FLIP direction); + + TQPixmap convertToPixmap(); + TQPixmap convertToPixmap(IccTransform* monitorICCtrans); + + /** Return a mask image where pure white and pure black pixels are over-colored. + This way is used to identify over and under exposed pixels. + */ + TQImage pureColorMask(ExposureSettingsContainer *expoSettings); + + /** Convert depth of image. Depth is bytesDepth * bitsDepth. + If depth is 32, converts to 8 bits, + if depth is 64, converts to 16 bits. + */ + void convertDepth(int depth); + + /** Wrapper methods for convertDepth */ + void convertToSixteenBit(); + void convertToEightBit(); + void convertToDepthOfImage(const DImg *otherImage); + + /** Fill whole image with specified color. + The bit depth of the color must be identical to the depth of this image. + */ + void fill(DColor color); + +private: + + DImgPrivate *m_priv; + +private: + + void copyMetaData(const DImgPrivate *src); + void copyImageData(const DImgPrivate *src); + void setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha); + void setImageDimension(uint width, uint height); + int allocateData(); + DImg(const DImg &image, int w, int h); + static void bitBlt(const uchar *src, uchar *dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool sixteenBit, int sdepth, int ddepth); + static void bitBlend(DColorComposer *composer, const uchar *src, uchar *dest, + int sx, int sy, int w, int h, int dx, int dy, + uint swidth, uint sheight, uint dwidth, uint dheight, + bool sixteenBit, int sdepth, int ddepth, + DColorComposer::MultiplicationFlags multiplicationFlags); + static bool normalizeRegionArguments(int &sx, int &sy, int &w, int &h, int &dx, int &dy, + uint swidth, uint sheight, uint dwidth, uint dheight); + + friend class DImgLoader; +}; + +} // NameSpace Digikam + +#endif /* DIMG_H */ diff --git a/src/libs/dimg/dimgprivate.h b/src/libs/dimg/dimgprivate.h new file mode 100644 index 00000000..021208d3 --- /dev/null +++ b/src/libs/dimg/dimgprivate.h @@ -0,0 +1,81 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-15 + * Description : DImg private data members + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGPRIVATE_H +#define DIMGPRIVATE_H + +// TQt includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DImgPrivate : public TQShared +{ +public: + + DImgPrivate() + { + null = true; + width = 0; + height = 0; + data = 0; + alpha = false; + sixteenBit = false; + isReadOnly = false; + } + + ~DImgPrivate() + { + delete [] data; + } + + bool null; + bool alpha; + bool sixteenBit; + bool isReadOnly; + + unsigned int width; + unsigned int height; + + unsigned char *data; + + TQMap metaData; + TQStringVariantMap attributes; + TQMap embeddedText; + +}; + +} // NameSpace Digikam + +#endif /* DIMGPRIVATE_H */ diff --git a/src/libs/dimg/dimgscale.cpp b/src/libs/dimg/dimgscale.cpp new file mode 100644 index 00000000..850e36de --- /dev/null +++ b/src/libs/dimg/dimgscale.cpp @@ -0,0 +1,2127 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : This is the normal smoothscale method, + * based on Imlib2's smoothscale. Added + * smoothScaleSection - Scaling only of a + * section of a image. Added 16bit image support + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * Copyright (C) 2006-2008 by Marcel Wiesweg + * + * Ported to C++/TQImage by Daniel M. Duley + * Following modification are (C) Daniel M. Duley + * Changes include formatting, namespaces and other C++'ings, removal of old + * #ifdef'ed code, and removal of unneeded border calculation code. + * + * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code + * is by Willem Monsuwe . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C ansi includes. + +extern "C" +{ +#include +} + +// C++ includes. + +#include +#include +#include + +// Local includes. + +#include "dimgprivate.h" +#include "dimg.h" + +typedef uint64_t ullong; +typedef int64_t llong; + +namespace Digikam +{ + +namespace DImgScale +{ + typedef struct __dimg_scale_info + { + int *xpoints; + uint **ypoints; + ullong **ypoints16; + int *xapoints; + int *yapoints; + int xup_yup; + } DImgScaleInfo; + + uint** dimgCalcYPoints(uint *src, int sw, int sh, int dh); + ullong** dimgCalcYPoints16(ullong *src, int sw, int sh, int dh); + int* dimgCalcXPoints(int sw, int dw); + int* dimgCalcApoints(int s, int d, int up); + + DImgScaleInfo* dimgFreeScaleInfo(DImgScaleInfo *isi); + DImgScaleInfo *dimgCalcScaleInfo(const DImg &img, + int sw, int sh, + int dw, int dh, + bool sixteenBit, + bool aa); + + void dimgSampleRGBA(DImgScaleInfo *isi, unsigned int *dest, int dxx, + int dyy, int dx, int dy, int dw, int dh, int dow); + void dimgScaleAARGBA(DImgScaleInfo *isi, unsigned int *dest, int dxx, + int dyy, int dx, int dy, int dw, int dh, int dow, + int sow); + void dimgScaleAARGB(DImgScaleInfo *isi, unsigned int *dest, int dxx, + int dyy, int dx, int dy, int dw, int dh, int dow, int + sow); + + void dimgScaleAARGBA16(DImgScaleInfo *isi, ullong *dest, + int dxx, int dyy, int dw, int dh, + int dow, int sow); + void dimgScaleAARGB16(DImgScaleInfo *isi, ullong *dest, + int dxx, int dyy, int dw, int dh, + int dow, int sow); +}; + +using namespace DImgScale; + +DImg DImg::smoothScale(int dw, int dh, TQSize::ScaleMode scaleMode) +{ + if (dw < 0 || dh < 0 || isNull()) + return DImg(); + + uint w = width(); + uint h = height(); + + if (w <= 0 || h <= 0) + return DImg(); + + TQSize newSize(w, h); + newSize.scale( TQSize(dw, dh), scaleMode ); + if (!newSize.isValid()) + return DImg(); + + dw = newSize.width(); + dh = newSize.height(); + + // do we actually need to scale? + if ((w == (uint)dw) && (h == (uint)dh)) + { + return copy(); + } + + DImgScale::DImgScaleInfo *scaleinfo = dimgCalcScaleInfo(*this, w, h, dw, dh, sixteenBit(), true); + if (!scaleinfo) + return *this; + + DImg buffer(*this, dw, dh); + + if (sixteenBit()) + { + if (hasAlpha()) + { + dimgScaleAARGBA16(scaleinfo, (ullong*) buffer.bits(), + 0, 0, dw, dh, dw, w); + } + else + { + dimgScaleAARGB16(scaleinfo, (ullong*) buffer.bits(), + 0, 0, dw, dh, dw, w); + } + } + else + { + if (hasAlpha()) + { + dimgScaleAARGBA(scaleinfo, (unsigned int *)buffer.bits(), + 0, 0, 0, 0, dw, dh, dw, w); + } + else + { + dimgScaleAARGB(scaleinfo, (unsigned int *)buffer.bits(), + 0, 0, 0, 0, dw, dh, dw, w); + } + } + + dimgFreeScaleInfo(scaleinfo); + + return buffer; +} + +#define CLIP(x, y, w, h, xx, yy, ww, hh) \ +if (x < (xx)) {w += (x - (xx)); x = (xx);} \ +if (y < (yy)) {h += (y - (yy)); y = (yy);} \ +if ((x + w) > ((xx) + (ww))) {w = (ww) - (x - xx);} \ +if ((y + h) > ((yy) + (hh))) {h = (hh) - (y - yy);} + +DImg DImg::smoothScaleSection(int sx, int sy, + int sw, int sh, + int dw, int dh) +{ + uint w = width(); + uint h = height(); + + // sanity checks + if ((dw <= 0) || (dh <= 0)) + return DImg(); + + if ((sw <= 0) || (sh <= 0)) + return DImg(); + + // clip the source rect to be within the actual image + int psx, psy, psw, psh; + psx = sx; + psy = sy; + psw = sw; + psh = sh; + CLIP(sx, sy, sw, sh, 0, 0, (int)w, (int)h); + + // clip output coords to clipped input coords + if (psw != sw) + dw = (dw * sw) / psw; + if (psh != sh) + dh = (dh * sh) / psh; + + // do a second check to see if we now have invalid coords + // do not do anything if we have a 0 widht or height image to render + if ((dw <= 0) || (dh <= 0)) + return DImg(); + + // if the input rect size < 0 do not render either + if ((sw <= 0) || (sh <= 0)) + return DImg(); + + // do we actually need to scale? + if ((sw == dw) && (sh == dh)) + { + return copy(sx, sy, sw, sh); + } + + // calculate scaleinfo + DImgScaleInfo *scaleinfo = dimgCalcScaleInfo(*this, sw, sh, dw, dh, sixteenBit(), true); + if (!scaleinfo) + return DImg(); + + DImg buffer(*this, dw, dh); + + if (sixteenBit()) + { + if (hasAlpha()) + { + dimgScaleAARGBA16(scaleinfo, (ullong*) buffer.bits(), + ((sx * dw) / sw), + ((sy * dh) / sh), + dw, dh, + dw, w); + } + else + { + dimgScaleAARGB16(scaleinfo, (ullong*) buffer.bits(), + ((sx * dw) / sw), + ((sy * dh) / sh), + dw, dh, + dw, w); + } + } + else + { + if (hasAlpha()) + { + dimgScaleAARGBA(scaleinfo, + (uint *)buffer.bits(), + ((sx * dw) / sw), + ((sy * dh) / sh), + 0, 0, + dw, dh, + dw, w); + } + else + { + dimgScaleAARGB(scaleinfo, + (uint *)buffer.bits(), + ((sx * dw) / sw), + ((sy * dh) / sh), + 0, 0, + dw, dh, + dw, w); + } + } + + dimgFreeScaleInfo(scaleinfo); + + return buffer; +} + + +// +// Code ported from Imlib2... +// + +// FIXME: replace with mRed, etc... These work on pointers to pixels, not +// pixel values +#define A_VAL(p) ((unsigned char *)(p))[3] +#define R_VAL(p) ((unsigned char *)(p))[2] +#define G_VAL(p) ((unsigned char *)(p))[1] +#define B_VAL(p) ((unsigned char *)(p))[0] + +#define INV_XAP (256 - xapoints[x]) +#define XAP (xapoints[x]) +#define INV_YAP (256 - yapoints[dyy + y]) +#define YAP (yapoints[dyy + y]) + +unsigned int** DImgScale::dimgCalcYPoints(unsigned int *src, int sw, int sh, int dh) +{ + unsigned int **p; + int i, j = 0; + int val, inc; + + p = new unsigned int* [dh+1]; + + val = 0; + inc = (sh << 16) / dh; + for(i = 0; i < dh; i++) + { + p[j++] = src + ((val >> 16) * sw); + val += inc; + } + + return(p); +} + +ullong** DImgScale::dimgCalcYPoints16(ullong* src, int sw, int sh, int dh) +{ + ullong** p; + int i, j = 0; + int val, inc; + + p = new ullong*[(dh+1)]; + + val = 0; + inc = (sh << 16) / dh; + for(i = 0; i < dh; i++) + { + p[j++] = src + ((val >> 16) * sw); + val += inc; + } + + return p; +} + +int* DImgScale::dimgCalcXPoints(int sw, int dw) +{ + int *p, i, j = 0; + int val, inc; + + p = new int[dw+1]; + + val = 0; + inc = (sw << 16) / dw; + for(i = 0; i < dw; i++) + { + p[j++] = (val >> 16); + val += inc; + } + + return(p); +} + +int* DImgScale::dimgCalcApoints(int s, int d, int up) +{ + int *p, i, j = 0; + + p = new int[d]; + + /* scaling up */ + if(up) + { + int val, inc; + + val = 0; + inc = (s << 16) / d; + for(i = 0; i < d; i++) + { + p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00); + if((val >> 16) >= (s - 1)) + p[j - 1] = 0; + val += inc; + } + } + /* scaling down */ + else + { + int val, inc, ap, Cp; + val = 0; + inc = (s << 16) / d; + Cp = ((d << 14) / s) + 1; + + for(i = 0; i < d; i++) + { + ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8; + p[j] = ap | (Cp << 16); + j++; + val += inc; + } + } + + return(p); +} + +DImgScaleInfo* DImgScale::dimgCalcScaleInfo(const DImg &img, + int sw, int sh, + int dw, int dh, + bool /*sixteenBit*/, + bool aa) +{ + DImgScaleInfo *isi; + int scw, sch; + + scw = dw * img.width() / sw; + sch = dh * img.height() / sh; + + isi = new DImgScaleInfo; + if(!isi) + return(NULL); + + memset(isi, 0, sizeof(DImgScaleInfo)); + + isi->xup_yup = (abs(dw) >= sw) + ((abs(dh) >= sh) << 1); + + isi->xpoints = dimgCalcXPoints(img.width(), scw); + if(!isi->xpoints) + return(dimgFreeScaleInfo(isi)); + + if (img.sixteenBit()) + { + isi->ypoints = 0; + isi->ypoints16 = dimgCalcYPoints16((ullong*)img.bits(), img.width(), img.height(), sch); + if (!isi->ypoints16) return(dimgFreeScaleInfo(isi)); + } + else + { + isi->ypoints16 = 0; + isi->ypoints = dimgCalcYPoints((uint*)img.bits(), img.width(), img.height(), sch); + if (!isi->ypoints) return(dimgFreeScaleInfo(isi)); + } + + if (aa) + { + isi->xapoints = dimgCalcApoints(img.width(), scw, isi->xup_yup & 1); + if(!isi->xapoints) return(dimgFreeScaleInfo(isi)); + + isi->yapoints = dimgCalcApoints(img.height(), sch, isi->xup_yup & 2); + if(!isi->yapoints) return(dimgFreeScaleInfo(isi)); + } +/* It doesn't work... + else + { + isi->xapoints = new int[scw]; + if(!isi->xapoints) return(dimgFreeScaleInfo(isi)); + for(int i = 0; i < scw; i++) isi->xapoints[i] = 0; + + isi->yapoints = new int[sch]; + if(!isi->yapoints) return(dimgFreeScaleInfo(isi)); + for(int i = 0; i < sch; i++) isi->yapoints[i] = 0; + }*/ + + return(isi); +} + +DImgScaleInfo* DImgScale::dimgFreeScaleInfo(DImgScaleInfo *isi) +{ + if(isi) + { + delete [] isi->xpoints; + delete [] isi->ypoints; + delete [] isi->ypoints16; + delete [] isi->xapoints; + delete [] isi->yapoints; + delete isi; + } + + return 0; +} + +/** scale by pixel sampling only */ +void DImgScale::dimgSampleRGBA(DImgScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow) +{ + unsigned int *sptr, *dptr; + int x, y, end; + unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + + /* whats the last pixel ont he line so we stop there */ + end = dxx + dw; + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + /* get the pointer to the start of the destination scanline */ + dptr = dest + dx + ((y + dy) * dow); + /* calculate the source line we'll scan from */ + sptr = ypoints[dyy + y]; + /* go thru the scanline and copy across */ + for(x = dxx; x < end; x++) + *dptr++ = sptr[xpoints[x]]; + } +} + +/* FIXME: NEED to optimise ScaleAARGBA - currently its "ok" but needs work*/ + +/** scale by area sampling */ +void DImgScale::dimgScaleAARGBA(DImgScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + unsigned int *sptr, *dptr; + int x, y, end; + unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + end = dxx + dw; + /* scaling up both ways */ + if(isi->xup_yup == 3) + { + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + /* calculate the source line we'll scan from */ + dptr = dest + dx + ((y + dy) * dow); + sptr = ypoints[dyy + y]; + if(YAP > 0) + { + for(x = dxx; x < end; x++) + { + int r, g, b, a; + int rr, gg, bb, aa; + unsigned int *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_XAP; + g = G_VAL(pix) * INV_XAP; + b = B_VAL(pix) * INV_XAP; + a = A_VAL(pix) * INV_XAP; + pix++; + r += R_VAL(pix) * XAP; + g += G_VAL(pix) * XAP; + b += B_VAL(pix) * XAP; + a += A_VAL(pix) * XAP; + pix += sow; + rr = R_VAL(pix) * XAP; + gg = G_VAL(pix) * XAP; + bb = B_VAL(pix) * XAP; + aa = A_VAL(pix) * XAP; + pix--; + rr += R_VAL(pix) * INV_XAP; + gg += G_VAL(pix) * INV_XAP; + bb += B_VAL(pix) * INV_XAP; + aa += A_VAL(pix) * INV_XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + a = ((aa * YAP) + (a * INV_YAP)) >> 16; + + A_VAL(dptr) = a; + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + + dptr++; + } + else + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_YAP; + g = G_VAL(pix) * INV_YAP; + b = B_VAL(pix) * INV_YAP; + a = A_VAL(pix) * INV_YAP; + pix += sow; + r += R_VAL(pix) * YAP; + g += G_VAL(pix) * YAP; + b += B_VAL(pix) * YAP; + a += A_VAL(pix) * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + a >>= 8; + + A_VAL(dptr) = a; + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + + dptr++; + } + } + } + else + { + for(x = dxx; x < end; x++) + { + int r, g, b, a; + unsigned int *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_XAP; + g = G_VAL(pix) * INV_XAP; + b = B_VAL(pix) * INV_XAP; + a = A_VAL(pix) * INV_XAP; + pix++; + r += R_VAL(pix) * XAP; + g += G_VAL(pix) * XAP; + b += B_VAL(pix) * XAP; + a += A_VAL(pix) * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + a >>= 8; + + A_VAL(dptr) = a; + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + + dptr++; + } + else + *dptr++ = sptr[xpoints[x] ]; + } + } + } + } + /* if we're scaling down vertically */ + else if(isi->xup_yup == 1) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cy, j; + unsigned int *pix; + int r, g, b, a, rr, gg, bb, aa; + int yap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL(pix) * yap) >> 10; + g = (G_VAL(pix) * yap) >> 10; + b = (B_VAL(pix) * yap) >> 10; + a = (A_VAL(pix) * yap) >> 10; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix += sow; + r += (R_VAL(pix) * Cy) >> 10; + g += (G_VAL(pix) * Cy) >> 10; + b += (B_VAL(pix) * Cy) >> 10; + a += (A_VAL(pix) * Cy) >> 10; + } + if(j > 0) + { + pix += sow; + r += (R_VAL(pix) * j) >> 10; + g += (G_VAL(pix) * j) >> 10; + b += (B_VAL(pix) * j) >> 10; + a += (A_VAL(pix) * j) >> 10; + } + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + 1; + rr = (R_VAL(pix) * yap) >> 10; + gg = (G_VAL(pix) * yap) >> 10; + bb = (B_VAL(pix) * yap) >> 10; + aa = (A_VAL(pix) * yap) >> 10; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix += sow; + rr += (R_VAL(pix) * Cy) >> 10; + gg += (G_VAL(pix) * Cy) >> 10; + bb += (B_VAL(pix) * Cy) >> 10; + aa += (A_VAL(pix) * Cy) >> 10; + } + if(j > 0) + { + pix += sow; + rr += (R_VAL(pix) * j) >> 10; + gg += (G_VAL(pix) * j) >> 10; + bb += (B_VAL(pix) * j) >> 10; + aa += (A_VAL(pix) * j) >> 10; + } + r = r * INV_XAP; + g = g * INV_XAP; + b = b * INV_XAP; + a = a * INV_XAP; + r = (r + ((rr * XAP))) >> 12; + g = (g + ((gg * XAP))) >> 12; + b = (b + ((bb * XAP))) >> 12; + a = (a + ((aa * XAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + a >>= 4; + } + + A_VAL(dptr) = a; + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + + dptr++; + } + } + } + /* if we're scaling down horizontally */ + else if(isi->xup_yup == 2) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cx, j; + unsigned int *pix; + int r, g, b, a, rr, gg, bb, aa; + int xap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL(pix) * xap) >> 10; + g = (G_VAL(pix) * xap) >> 10; + b = (B_VAL(pix) * xap) >> 10; + a = (A_VAL(pix) * xap) >> 10; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + pix++; + r += (R_VAL(pix) * Cx) >> 10; + g += (G_VAL(pix) * Cx) >> 10; + b += (B_VAL(pix) * Cx) >> 10; + a += (A_VAL(pix) * Cx) >> 10; + } + if(j > 0) + { + pix++; + r += (R_VAL(pix) * j) >> 10; + g += (G_VAL(pix) * j) >> 10; + b += (B_VAL(pix) * j) >> 10; + a += (A_VAL(pix) * j) >> 10; + } + if(YAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + sow; + rr = (R_VAL(pix) * xap) >> 10; + gg = (G_VAL(pix) * xap) >> 10; + bb = (B_VAL(pix) * xap) >> 10; + aa = (A_VAL(pix) * xap) >> 10; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + pix++; + rr += (R_VAL(pix) * Cx) >> 10; + gg += (G_VAL(pix) * Cx) >> 10; + bb += (B_VAL(pix) * Cx) >> 10; + aa += (A_VAL(pix) * Cx) >> 10; + } + if(j > 0) + { + pix++; + rr += (R_VAL(pix) * j) >> 10; + gg += (G_VAL(pix) * j) >> 10; + bb += (B_VAL(pix) * j) >> 10; + aa += (A_VAL(pix) * j) >> 10; + } + r = r * INV_YAP; + g = g * INV_YAP; + b = b * INV_YAP; + a = a * INV_YAP; + r = (r + ((rr * YAP))) >> 12; + g = (g + ((gg * YAP))) >> 12; + b = (b + ((bb * YAP))) >> 12; + a = (a + ((aa * YAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + a >>= 4; + } + + A_VAL(dptr) = a; + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + + dptr++; + } + } + } + /* if we're scaling down horizontally & vertically */ + else + { + /*\ 'Correct' version, with math units prepared for MMXification: + |*| The operation 'b = (b * c) >> 16' translates to pmulhw, + |*| so the operation 'b = (b * c) >> d' would translate to + |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb + \*/ + int Cx, Cy, i, j; + unsigned int *pix; + int a, r, g, b, ax, rx, gx, bx; + int xap, yap; + + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + sptr = ypoints[dyy + y] + xpoints[x]; + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + ax = (A_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + ax += (A_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + ax += (A_VAL(pix) * i) >> 9; + } + + r = (rx * yap) >> 14; + g = (gx * yap) >> 14; + b = (bx * yap) >> 14; + a = (ax * yap) >> 14; + + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + ax = (A_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + ax += (A_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + ax += (A_VAL(pix) * i) >> 9; + } + + r += (rx * Cy) >> 14; + g += (gx * Cy) >> 14; + b += (bx * Cy) >> 14; + a += (ax * Cy) >> 14; + } + if(j > 0) + { + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + ax = (A_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + ax += (A_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + ax += (A_VAL(pix) * i) >> 9; + } + + r += (rx * j) >> 14; + g += (gx * j) >> 14; + b += (bx * j) >> 14; + a += (ax * j) >> 14; + } + + R_VAL(dptr) = r >> 5; + G_VAL(dptr) = g >> 5; + B_VAL(dptr) = b >> 5; + A_VAL(dptr) = a >> 5; + dptr++; + } + } + } +} + +/** scale by area sampling - IGNORE the ALPHA byte */ +void DImgScale::dimgScaleAARGB(DImgScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + unsigned int *sptr, *dptr; + int x, y, end; + unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + end = dxx + dw; + /* scaling up both ways */ + if(isi->xup_yup == 3) + { + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + /* calculate the source line we'll scan from */ + dptr = dest + dx + ((y + dy) * dow); + sptr = ypoints[dyy + y]; + if(YAP > 0) + { + for(x = dxx; x < end; x++) + { + int r = 0, g = 0, b = 0; + int rr = 0, gg = 0, bb = 0; + unsigned int *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_XAP; + g = G_VAL(pix) * INV_XAP; + b = B_VAL(pix) * INV_XAP; + pix++; + r += R_VAL(pix) * XAP; + g += G_VAL(pix) * XAP; + b += B_VAL(pix) * XAP; + pix += sow; + rr = R_VAL(pix) * XAP; + gg = G_VAL(pix) * XAP; + bb = B_VAL(pix) * XAP; + pix --; + rr += R_VAL(pix) * INV_XAP; + gg += G_VAL(pix) * INV_XAP; + bb += B_VAL(pix) * INV_XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + A_VAL(dptr) = 0xFF; + + dptr++; + } + else + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_YAP; + g = G_VAL(pix) * INV_YAP; + b = B_VAL(pix) * INV_YAP; + pix += sow; + r += R_VAL(pix) * YAP; + g += G_VAL(pix) * YAP; + b += B_VAL(pix) * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + A_VAL(dptr) = 0xFF; + + dptr++; + } + } + } + else + { + for(x = dxx; x < end; x++) + { + int r = 0, g = 0, b = 0; + unsigned int *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL(pix) * INV_XAP; + g = G_VAL(pix) * INV_XAP; + b = B_VAL(pix) * INV_XAP; + pix++; + r += R_VAL(pix) * XAP; + g += G_VAL(pix) * XAP; + b += B_VAL(pix) * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + A_VAL(dptr) = 0xFF; + + dptr++; + } + else + *dptr++ = sptr[xpoints[x] ]; + } + } + } + } + /* if we're scaling down vertically */ + else if(isi->xup_yup == 1) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cy, j; + unsigned int *pix; + int r, g, b, rr, gg, bb; + int yap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL(pix) * yap) >> 10; + g = (G_VAL(pix) * yap) >> 10; + b = (B_VAL(pix) * yap) >> 10; + pix += sow; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + r += (R_VAL(pix) * Cy) >> 10; + g += (G_VAL(pix) * Cy) >> 10; + b += (B_VAL(pix) * Cy) >> 10; + pix += sow; + } + if(j > 0) + { + r += (R_VAL(pix) * j) >> 10; + g += (G_VAL(pix) * j) >> 10; + b += (B_VAL(pix) * j) >> 10; + } + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + 1; + rr = (R_VAL(pix) * yap) >> 10; + gg = (G_VAL(pix) * yap) >> 10; + bb = (B_VAL(pix) * yap) >> 10; + pix += sow; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + rr += (R_VAL(pix) * Cy) >> 10; + gg += (G_VAL(pix) * Cy) >> 10; + bb += (B_VAL(pix) * Cy) >> 10; + pix += sow; + } + if(j > 0) + { + rr += (R_VAL(pix) * j) >> 10; + gg += (G_VAL(pix) * j) >> 10; + bb += (B_VAL(pix) * j) >> 10; + } + r = r * INV_XAP; + g = g * INV_XAP; + b = b * INV_XAP; + r = (r + ((rr * XAP))) >> 12; + g = (g + ((gg * XAP))) >> 12; + b = (b + ((bb * XAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + } + + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + A_VAL(dptr) = 0xFF; + + dptr++; + } + } + } + /* if we're scaling down horizontally */ + else if(isi->xup_yup == 2) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cx, j; + unsigned int *pix; + int r, g, b, rr, gg, bb; + int xap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL(pix) * xap) >> 10; + g = (G_VAL(pix) * xap) >> 10; + b = (B_VAL(pix) * xap) >> 10; + pix++; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + r += (R_VAL(pix) * Cx) >> 10; + g += (G_VAL(pix) * Cx) >> 10; + b += (B_VAL(pix) * Cx) >> 10; + pix++; + } + if(j > 0) + { + r += (R_VAL(pix) * j) >> 10; + g += (G_VAL(pix) * j) >> 10; + b += (B_VAL(pix) * j) >> 10; + } + if(YAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + sow; + rr = (R_VAL(pix) * xap) >> 10; + gg = (G_VAL(pix) * xap) >> 10; + bb = (B_VAL(pix) * xap) >> 10; + pix++; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + rr += (R_VAL(pix) * Cx) >> 10; + gg += (G_VAL(pix) * Cx) >> 10; + bb += (B_VAL(pix) * Cx) >> 10; + pix++; + } + if(j > 0) + { + rr += (R_VAL(pix) * j) >> 10; + gg += (G_VAL(pix) * j) >> 10; + bb += (B_VAL(pix) * j) >> 10; + } + r = r * INV_YAP; + g = g * INV_YAP; + b = b * INV_YAP; + r = (r + ((rr * YAP))) >> 12; + g = (g + ((gg * YAP))) >> 12; + b = (b + ((bb * YAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + } + + R_VAL(dptr) = r; + G_VAL(dptr) = g; + B_VAL(dptr) = b; + A_VAL(dptr) = 0xFF; + + dptr++; + } + } + } + /* fully optimized (i think) - onyl change of algorithm can help */ + /* if we're scaling down horizontally & vertically */ + else + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cx, Cy, i, j; + unsigned int *pix; + int r, g, b, rx, gx, bx; + int xap, yap; + + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + dx + ((y + dy) * dow); + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + sptr = ypoints[dyy + y] + xpoints[x]; + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + } + + r = (rx * yap) >> 14; + g = (gx * yap) >> 14; + b = (bx * yap) >> 14; + + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + } + + r += (rx * Cy) >> 14; + g += (gx * Cy) >> 14; + b += (bx * Cy) >> 14; + } + if(j > 0) + { + pix = sptr; + sptr += sow; + rx = (R_VAL(pix) * xap) >> 9; + gx = (G_VAL(pix) * xap) >> 9; + bx = (B_VAL(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL(pix) * Cx) >> 9; + gx += (G_VAL(pix) * Cx) >> 9; + bx += (B_VAL(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL(pix) * i) >> 9; + gx += (G_VAL(pix) * i) >> 9; + bx += (B_VAL(pix) * i) >> 9; + } + + r += (rx * j) >> 14; + g += (gx * j) >> 14; + b += (bx * j) >> 14; + } + + R_VAL(dptr) = r >> 5; + G_VAL(dptr) = g >> 5; + B_VAL(dptr) = b >> 5; + A_VAL(dptr) = 0xFF; + dptr++; + } + } + } +} + +#define A_VAL16(p) ((ushort *)(p))[3] +#define R_VAL16(p) ((ushort *)(p))[2] +#define G_VAL16(p) ((ushort *)(p))[1] +#define B_VAL16(p) ((ushort *)(p))[0] + +/** scale by area sampling - IGNORE the ALPHA byte*/ +void DImgScale::dimgScaleAARGB16(DImgScaleInfo *isi, ullong *dest, + int dxx, int dyy, int dw, int dh, + int dow, int sow) +{ + ullong *sptr, *dptr; + int x, y, end; + ullong **ypoints = isi->ypoints16; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + end = dxx + dw; + + // scaling up both ways + if(isi->xup_yup == 3) + { + // go through every scanline in the output buffer + for(y = 0; y < dh; y++) + { + // calculate the source line we'll scan from + dptr = dest + (y * dow); + sptr = ypoints[dyy + y]; + if(YAP > 0) + { + for(x = dxx; x < end; x++) + { + llong r = 0, g = 0, b = 0; + llong rr = 0, gg = 0, bb = 0; + ullong *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_XAP; + g = G_VAL16(pix) * INV_XAP; + b = B_VAL16(pix) * INV_XAP; + pix++; + r += R_VAL16(pix) * XAP; + g += G_VAL16(pix) * XAP; + b += B_VAL16(pix) * XAP; + pix += sow; + rr = R_VAL16(pix) * XAP; + gg = G_VAL16(pix) * XAP; + bb = B_VAL16(pix) * XAP; + pix --; + rr += R_VAL16(pix) * INV_XAP; + gg += G_VAL16(pix) * INV_XAP; + bb += B_VAL16(pix) * INV_XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = 0xFFFF; + + dptr++; + } + else + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_YAP; + g = G_VAL16(pix) * INV_YAP; + b = B_VAL16(pix) * INV_YAP; + pix += sow; + r += R_VAL16(pix) * YAP; + g += G_VAL16(pix) * YAP; + b += B_VAL16(pix) * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = 0xFFFF; + + dptr++; + } + } + } + else + { + for(x = dxx; x < end; x++) + { + llong r = 0, g = 0, b = 0; + ullong *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_XAP; + g = G_VAL16(pix) * INV_XAP; + b = B_VAL16(pix) * INV_XAP; + pix++; + r += R_VAL16(pix) * XAP; + g += G_VAL16(pix) * XAP; + b += B_VAL16(pix) * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = 0xFFFF; + + dptr++; + } + else + *dptr++ = sptr[xpoints[x] ]; + } + } + } + } + // if we're scaling down vertically + else if(isi->xup_yup == 1) + { + // 'Correct' version, with math units prepared for MMXification + int Cy, j; + ullong *pix; + llong r, g, b, rr, gg, bb; + int yap; + + // go through every scanline in the output buffer + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + y * dow; + for(x = dxx; x < end; x++) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL16(pix) * yap) >> 10; + g = (G_VAL16(pix) * yap) >> 10; + b = (B_VAL16(pix) * yap) >> 10; + pix += sow; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + r += (R_VAL16(pix) * Cy) >> 10; + g += (G_VAL16(pix) * Cy) >> 10; + b += (B_VAL16(pix) * Cy) >> 10; + pix += sow; + } + if(j > 0) + { + r += (R_VAL16(pix) * j) >> 10; + g += (G_VAL16(pix) * j) >> 10; + b += (B_VAL16(pix) * j) >> 10; + } + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + 1; + rr = (R_VAL16(pix) * yap) >> 10; + gg = (G_VAL16(pix) * yap) >> 10; + bb = (B_VAL16(pix) * yap) >> 10; + pix += sow; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + rr += (R_VAL16(pix) * Cy) >> 10; + gg += (G_VAL16(pix) * Cy) >> 10; + bb += (B_VAL16(pix) * Cy) >> 10; + pix += sow; + } + if(j > 0) + { + rr += (R_VAL16(pix) * j) >> 10; + gg += (G_VAL16(pix) * j) >> 10; + bb += (B_VAL16(pix) * j) >> 10; + } + r = r * INV_XAP; + g = g * INV_XAP; + b = b * INV_XAP; + r = (r + ((rr * XAP))) >> 12; + g = (g + ((gg * XAP))) >> 12; + b = (b + ((bb * XAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + } + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = 0xFFFF; + dptr++; + } + } + } + // if we're scaling down horizontally + else if(isi->xup_yup == 2) + { + // 'Correct' version, with math units prepared for MMXification + int Cx, j; + ullong *pix; + llong r, g, b, rr, gg, bb; + int xap; + + // go through every scanline in the output buffer + for(y = 0; y < dh; y++) + { + dptr = dest + y * dow; + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL16(pix) * xap) >> 10; + g = (G_VAL16(pix) * xap) >> 10; + b = (B_VAL16(pix) * xap) >> 10; + pix++; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + r += (R_VAL16(pix) * Cx) >> 10; + g += (G_VAL16(pix) * Cx) >> 10; + b += (B_VAL16(pix) * Cx) >> 10; + pix++; + } + if(j > 0) + { + r += (R_VAL16(pix) * j) >> 10; + g += (G_VAL16(pix) * j) >> 10; + b += (B_VAL16(pix) * j) >> 10; + } + if(YAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + sow; + rr = (R_VAL16(pix) * xap) >> 10; + gg = (G_VAL16(pix) * xap) >> 10; + bb = (B_VAL16(pix) * xap) >> 10; + pix++; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + rr += (R_VAL16(pix) * Cx) >> 10; + gg += (G_VAL16(pix) * Cx) >> 10; + bb += (B_VAL16(pix) * Cx) >> 10; + pix++; + } + if(j > 0) + { + rr += (R_VAL16(pix) * j) >> 10; + gg += (G_VAL16(pix) * j) >> 10; + bb += (B_VAL16(pix) * j) >> 10; + } + r = r * INV_YAP; + g = g * INV_YAP; + b = b * INV_YAP; + r = (r + ((rr * YAP))) >> 12; + g = (g + ((gg * YAP))) >> 12; + b = (b + ((bb * YAP))) >> 12; + } + else{ + r >>= 4; + g >>= 4; + b >>= 4; + } + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = 0xFFFF; + dptr++; + } + } + } + // fully optimized (i think) - onyl change of algorithm can help + // if we're scaling down horizontally & vertically + else + { + // 'Correct' version, with math units prepared for MMXification + int Cx, Cy, i, j; + ullong *pix; + llong r, g, b, rx, gx, bx; + int xap, yap; + + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + dptr = dest + y * dow; + + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + sptr = ypoints[dyy + y] + xpoints[x]; + pix = sptr; + sptr += sow; + + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + } + + r = (rx * yap) >> 14; + g = (gx * yap) >> 14; + b = (bx * yap) >> 14; + + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix = sptr; + sptr += sow; + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + } + + r += (rx * Cy) >> 14; + g += (gx * Cy) >> 14; + b += (bx * Cy) >> 14; + } + if(j > 0) + { + pix = sptr; + sptr += sow; + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + } + + r += (rx * j) >> 14; + g += (gx * j) >> 14; + b += (bx * j) >> 14; + } + + R_VAL16(dptr) = r >> 5; + G_VAL16(dptr) = g >> 5; + B_VAL16(dptr) = b >> 5; + A_VAL16(dptr) = 0xFFFF; + dptr++; + } + } + } +} + +/* scale by area sampling */ +void DImgScale::dimgScaleAARGBA16(DImgScaleInfo *isi, ullong *dest, + int dxx, int dyy, + int dw, int dh, + int dow, int sow) +{ + ullong *sptr, *dptr; + int x, y, end; + ullong **ypoints = isi->ypoints16; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + end = dxx + dw; + /* scaling up both ways */ + if(isi->xup_yup == 3) + { + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + /* calculate the source line we'll scan from */ + dptr = dest + (y * dow); + sptr = ypoints[dyy + y]; + if(YAP > 0) + { + for(x = dxx; x < end; x++) + { + llong r, g, b, a; + llong rr, gg, bb, aa; + ullong *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_XAP; + g = G_VAL16(pix) * INV_XAP; + b = B_VAL16(pix) * INV_XAP; + a = A_VAL16(pix) * INV_XAP; + pix++; + r += R_VAL16(pix) * XAP; + g += G_VAL16(pix) * XAP; + b += B_VAL16(pix) * XAP; + a += A_VAL16(pix) * XAP; + pix += sow; + rr = R_VAL16(pix) * XAP; + gg = G_VAL16(pix) * XAP; + bb = B_VAL16(pix) * XAP; + aa = A_VAL16(pix) * XAP; + pix--; + rr += R_VAL16(pix) * INV_XAP; + gg += G_VAL16(pix) * INV_XAP; + bb += B_VAL16(pix) * INV_XAP; + aa += A_VAL16(pix) * INV_XAP; + r = ((rr * YAP) + (r * INV_YAP)) >> 16; + g = ((gg * YAP) + (g * INV_YAP)) >> 16; + b = ((bb * YAP) + (b * INV_YAP)) >> 16; + a = ((aa * YAP) + (a * INV_YAP)) >> 16; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = a; + + dptr++; + } + else + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_YAP; + g = G_VAL16(pix) * INV_YAP; + b = B_VAL16(pix) * INV_YAP; + a = A_VAL16(pix) * INV_YAP; + pix += sow; + r += R_VAL16(pix) * YAP; + g += G_VAL16(pix) * YAP; + b += B_VAL16(pix) * YAP; + a += A_VAL16(pix) * YAP; + r >>= 8; + g >>= 8; + b >>= 8; + a >>= 8; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = a; + + dptr++; + } + } + } + else + { + for(x = dxx; x < end; x++) + { + llong r, g, b, a; + ullong *pix; + + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = R_VAL16(pix) * INV_XAP; + g = G_VAL16(pix) * INV_XAP; + b = B_VAL16(pix) * INV_XAP; + a = A_VAL16(pix) * INV_XAP; + pix++; + r += R_VAL16(pix) * XAP; + g += G_VAL16(pix) * XAP; + b += B_VAL16(pix) * XAP; + a += A_VAL16(pix) * XAP; + r >>= 8; + g >>= 8; + b >>= 8; + a >>= 8; + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = a; + + dptr++; + } + else + *dptr++ = sptr[xpoints[x] ]; + } + } + } + } + /* if we're scaling down vertically */ + else if(isi->xup_yup == 1) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cy, j; + ullong *pix; + llong r, g, b, a, rr, gg, bb, aa; + int yap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + (y * dow); + for(x = dxx; x < end; x++) + { + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL16(pix) * yap) >> 10; + g = (G_VAL16(pix) * yap) >> 10; + b = (B_VAL16(pix) * yap) >> 10; + a = (A_VAL16(pix) * yap) >> 10; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix += sow; + r += (R_VAL16(pix) * Cy) >> 10; + g += (G_VAL16(pix) * Cy) >> 10; + b += (B_VAL16(pix) * Cy) >> 10; + a += (A_VAL16(pix) * Cy) >> 10; + } + if(j > 0) + { + pix += sow; + r += (R_VAL16(pix) * j) >> 10; + g += (G_VAL16(pix) * j) >> 10; + b += (B_VAL16(pix) * j) >> 10; + a += (A_VAL16(pix) * j) >> 10; + } + if(XAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + 1; + rr = (R_VAL16(pix) * yap) >> 10; + gg = (G_VAL16(pix) * yap) >> 10; + bb = (B_VAL16(pix) * yap) >> 10; + aa = (A_VAL16(pix) * yap) >> 10; + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix += sow; + rr += (R_VAL16(pix) * Cy) >> 10; + gg += (G_VAL16(pix) * Cy) >> 10; + bb += (B_VAL16(pix) * Cy) >> 10; + aa += (A_VAL16(pix) * Cy) >> 10; + } + if(j > 0) + { + pix += sow; + rr += (R_VAL16(pix) * j) >> 10; + gg += (G_VAL16(pix) * j) >> 10; + bb += (B_VAL16(pix) * j) >> 10; + aa += (A_VAL16(pix) * j) >> 10; + } + r = r * INV_XAP; + g = g * INV_XAP; + b = b * INV_XAP; + a = a * INV_XAP; + r = (r + ((rr * XAP))) >> 12; + g = (g + ((gg * XAP))) >> 12; + b = (b + ((bb * XAP))) >> 12; + a = (a + ((aa * XAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + a >>= 4; + } + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = a; + + dptr++; + } + } + } + /* if we're scaling down horizontally */ + else if(isi->xup_yup == 2) + { + /*\ 'Correct' version, with math units prepared for MMXification \*/ + int Cx, j; + ullong *pix; + llong r, g, b, a, rr, gg, bb, aa; + int xap; + + /* go through every scanline in the output buffer */ + for(y = 0; y < dh; y++) + { + dptr = dest + y * dow; + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + pix = ypoints[dyy + y] + xpoints[x]; + r = (R_VAL16(pix) * xap) >> 10; + g = (G_VAL16(pix) * xap) >> 10; + b = (B_VAL16(pix) * xap) >> 10; + a = (A_VAL16(pix) * xap) >> 10; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + pix++; + r += (R_VAL16(pix) * Cx) >> 10; + g += (G_VAL16(pix) * Cx) >> 10; + b += (B_VAL16(pix) * Cx) >> 10; + a += (A_VAL16(pix) * Cx) >> 10; + } + if(j > 0) + { + pix++; + r += (R_VAL16(pix) * j) >> 10; + g += (G_VAL16(pix) * j) >> 10; + b += (B_VAL16(pix) * j) >> 10; + a += (A_VAL16(pix) * j) >> 10; + } + if(YAP > 0) + { + pix = ypoints[dyy + y] + xpoints[x] + sow; + rr = (R_VAL16(pix) * xap) >> 10; + gg = (G_VAL16(pix) * xap) >> 10; + bb = (B_VAL16(pix) * xap) >> 10; + aa = (A_VAL16(pix) * xap) >> 10; + for(j = (1 << 14) - xap; j > Cx; j -= Cx) + { + pix++; + rr += (R_VAL16(pix) * Cx) >> 10; + gg += (G_VAL16(pix) * Cx) >> 10; + bb += (B_VAL16(pix) * Cx) >> 10; + aa += (A_VAL16(pix) * Cx) >> 10; + } + if(j > 0) + { + pix++; + rr += (R_VAL16(pix) * j) >> 10; + gg += (G_VAL16(pix) * j) >> 10; + bb += (B_VAL16(pix) * j) >> 10; + aa += (A_VAL16(pix) * j) >> 10; + } + r = r * INV_YAP; + g = g * INV_YAP; + b = b * INV_YAP; + a = a * INV_YAP; + r = (r + ((rr * YAP))) >> 12; + g = (g + ((gg * YAP))) >> 12; + b = (b + ((bb * YAP))) >> 12; + a = (a + ((aa * YAP))) >> 12; + } + else + { + r >>= 4; + g >>= 4; + b >>= 4; + a >>= 4; + } + + R_VAL16(dptr) = r; + G_VAL16(dptr) = g; + B_VAL16(dptr) = b; + A_VAL16(dptr) = a; + + dptr++; + } + } + } + /* if we're scaling down horizontally & vertically */ + else{ + /*\ 'Correct' version, with math units prepared for MMXification: + |*| The operation 'b = (b * c) >> 16' translates to pmulhw, + |*| so the operation 'b = (b * c) >> d' would translate to + |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb + \*/ + int Cx, Cy, i, j; + ullong *pix; + llong a, r, g, b, ax, rx, gx, bx; + int xap, yap; + + for(y = 0; y < dh; y++) + { + Cy = YAP >> 16; + yap = YAP & 0xffff; + + dptr = dest + y * dow; + for(x = dxx; x < end; x++) + { + Cx = XAP >> 16; + xap = XAP & 0xffff; + + sptr = ypoints[dyy + y] + xpoints[x]; + pix = sptr; + sptr += sow; + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + ax = (A_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + ax += (A_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + ax += (A_VAL16(pix) * i) >> 9; + } + + r = (rx * yap) >> 14; + g = (gx * yap) >> 14; + b = (bx * yap) >> 14; + a = (ax * yap) >> 14; + + + for(j = (1 << 14) - yap; j > Cy; j -= Cy) + { + pix = sptr; + sptr += sow; + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + ax = (A_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + ax += (A_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + ax += (A_VAL16(pix) * i) >> 9; + } + + r += (rx * Cy) >> 14; + g += (gx * Cy) >> 14; + b += (bx * Cy) >> 14; + a += (ax * Cy) >> 14; + } + if(j > 0) + { + pix = sptr; + sptr += sow; + rx = (R_VAL16(pix) * xap) >> 9; + gx = (G_VAL16(pix) * xap) >> 9; + bx = (B_VAL16(pix) * xap) >> 9; + ax = (A_VAL16(pix) * xap) >> 9; + pix++; + for(i = (1 << 14) - xap; i > Cx; i -= Cx) + { + rx += (R_VAL16(pix) * Cx) >> 9; + gx += (G_VAL16(pix) * Cx) >> 9; + bx += (B_VAL16(pix) * Cx) >> 9; + ax += (A_VAL16(pix) * Cx) >> 9; + pix++; + } + if(i > 0) + { + rx += (R_VAL16(pix) * i) >> 9; + gx += (G_VAL16(pix) * i) >> 9; + bx += (B_VAL16(pix) * i) >> 9; + ax += (A_VAL16(pix) * i) >> 9; + } + + r += (rx * j) >> 14; + g += (gx * j) >> 14; + b += (bx * j) >> 14; + a += (ax * j) >> 14; + } + + R_VAL16(dptr) = r >> 5; + G_VAL16(dptr) = g >> 5; + B_VAL16(dptr) = b >> 5; + A_VAL16(dptr) = a >> 5; + dptr++; + } + } + } +} + +/** +//Documentation of the cryptic dimgScaleAARGBA +dimgScaleAARGBA( +DImgScaleInfo *isi, // scaleinfo +unsigned int *dest, // destination img data +int dxx, // destination x location corresponding to start x of src section +int dyy, // destination y location corresponding to start y of src section +int dx, // destination x start location +int dy, // destination y start location +int dw, // destination width +int dh, // destination height +int dow, // destination scanline width +int sow); // src scanline width +*/ + +} // NameSpace Digikam diff --git a/src/libs/dimg/drawdecoding.h b/src/libs/dimg/drawdecoding.h new file mode 100644 index 00000000..e2d82fdc --- /dev/null +++ b/src/libs/dimg/drawdecoding.h @@ -0,0 +1,128 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2008-08-06 + * Description : Raw decoding settings for digiKam: + * standard libkdcraw parameters plus + * few customized for post processing. + * + * Copyright (C) 2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DRAW_DECODING_H +#define DRAW_DECODING_H + +// TQt includes. + +#include +#include + +// LibKDcraw includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DRawDecoding : public KDcrawIface::RawDecodingSettings +{ + +public: + + /** Standard constructor with default settings + */ + DRawDecoding() + { + resetPostProcessingSettings(); + }; + + /** Standard destructor + */ + virtual ~DRawDecoding(){}; + + /** Method to use a settings to optimize time loading, for exemple to compute image histogram + */ + void optimizeTimeLoading() + { + KDcrawIface::RawDecodingSettings::optimizeTimeLoading(); + resetPostProcessingSettings(); + }; + + /** Method to reset to default values all Raw processing settings. + */ + void resetPostProcessingSettings() + { + lightness = 0.0; + contrast = 1.0; + gamma = 1.0; + saturation = 1.0; + exposureComp = 0.0; + curveAdjust = TQPointArray(); + levelsAdjust = TQValueList(); + }; + + /** Method to check is a post-processing setting have been changed + */ + bool postProcessingSettingsIsDirty() const + { + return (lightness != 0.0 || + contrast != 1.0 || + gamma != 1.0 || + saturation != 1.0 || + exposureComp != 0.0 || + !curveAdjust.isEmpty() || + !levelsAdjust.isEmpty()); + } + +public: + + /** Lightness correction value. + */ + double lightness; + + /** Contrast correction value. + */ + double contrast; + + /** Gamma correction value. + */ + double gamma; + + /** Color saturation correction value. + */ + double saturation; + + /** Exposure compensation value. + */ + double exposureComp; + + /** Luminosity curve adjustements. + */ + TQPointArray curveAdjust; + + /** Levels adjustements: 4 channels (L, R, G, B * 2 values). + */ + TQValueList levelsAdjust; +}; + +} // namespace Digikam + +#endif /* DRAW_DECODING_H */ diff --git a/src/libs/dimg/exposurecontainer.h b/src/libs/dimg/exposurecontainer.h new file mode 100644 index 00000000..62469258 --- /dev/null +++ b/src/libs/dimg/exposurecontainer.h @@ -0,0 +1,65 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-12 + * Description : exposure indicator settings container. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef EXPOSURESETTINGSCONTAINER_H +#define EXPOSURESETTINGSCONTAINER_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT ExposureSettingsContainer +{ + +public: + + ExposureSettingsContainer() + { + underExposureIndicator = false; + overExposureIndicator = false; + + underExposureColor = TQt::white; + overExposureColor = TQt::black; + }; + + ~ExposureSettingsContainer(){}; + +public: + + bool underExposureIndicator; + bool overExposureIndicator; + + TQColor underExposureColor; + TQColor overExposureColor; +}; + +} // namespace Digikam + +#endif // EXPOSURESETTINGSCONTAINER_H diff --git a/src/libs/dimg/filters/Makefile.am b/src/libs/dimg/filters/Makefile.am new file mode 100644 index 00000000..25c4ee54 --- /dev/null +++ b/src/libs/dimg/filters/Makefile.am @@ -0,0 +1,21 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgfilters.la + +libdimgfilters_la_SOURCES = bcgmodifier.cpp hslmodifier.cpp icctransform.cpp \ + dimgimagefilters.cpp dimgthreadedfilter.cpp \ + dimggaussianblur.cpp dimgsharpen.cpp colormodifier.cpp + +libdimgfilters_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + + +digikaminclude_HEADERS = bcgmodifier.h hslmodifier.h dimgthreadedfilter.h dimgimagefilters.h \ + icctransform.h colormodifier.h dimgsharpen.h dimggaussianblur.h +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/dimg/filters/bcgmodifier.cpp b/src/libs/dimg/filters/bcgmodifier.cpp new file mode 100644 index 00000000..b3899c20 --- /dev/null +++ b/src/libs/dimg/filters/bcgmodifier.cpp @@ -0,0 +1,208 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-06 + * Description : a Brightness/Contrast/Gamma image filter. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0) +#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0) + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "bcgmodifier.h" + +namespace Digikam +{ + +class BCGModifierPriv +{ +public: + + BCGModifierPriv() + { + channel = BCGModifier::CHANNEL_ALL; + modified = false; + } + + bool modified; + + int channel; + int map16[65536]; + int map[256]; +}; + +BCGModifier::BCGModifier() +{ + d = new BCGModifierPriv; + reset(); +} + +BCGModifier::~BCGModifier() +{ + delete d; +} + +bool BCGModifier::modified() const +{ + return d->modified; +} + +void BCGModifier::reset() +{ + // initialize to linear mapping + + for (int i=0; i<65536; i++) + d->map16[i] = i; + + for (int i=0; i<256; i++) + d->map[i] = i; + + d->modified = false; +} + +void BCGModifier::applyBCG(DImg& image) +{ + if (!d->modified || image.isNull()) + return; + + applyBCG(image.bits(), image.width(), image.height(), image.sixteenBit()); +} + +void BCGModifier::applyBCG(uchar *bits, uint width, uint height, bool sixteenBits) +{ + if (!d->modified || !bits) + return; + + uint size = width*height; + + if (!sixteenBits) // 8 bits image. + { + uchar* data = bits; + + for (uint i=0; ichannel) + { + case CHANNEL_BLUE: + data[0] = CLAMP_0_255(d->map[data[0]]); + break; + + case CHANNEL_GREEN: + data[1] = CLAMP_0_255(d->map[data[1]]); + break; + + case CHANNEL_RED: + data[2] = CLAMP_0_255(d->map[data[2]]); + break; + + default: // CHANNEL_ALL + data[0] = CLAMP_0_255(d->map[data[0]]); + data[1] = CLAMP_0_255(d->map[data[1]]); + data[2] = CLAMP_0_255(d->map[data[2]]); + break; + } + + data += 4; + } + } + else // 16 bits image. + { + ushort* data = (ushort*)bits; + + for (uint i=0; ichannel) + { + case CHANNEL_BLUE: + data[0] = CLAMP_0_65535(d->map16[data[0]]); + break; + + case CHANNEL_GREEN: + data[1] = CLAMP_0_65535(d->map16[data[1]]); + break; + + case CHANNEL_RED: + data[2] = CLAMP_0_65535(d->map16[data[2]]); + break; + + default: // CHANNEL_ALL + data[0] = CLAMP_0_65535(d->map16[data[0]]); + data[1] = CLAMP_0_65535(d->map16[data[1]]); + data[2] = CLAMP_0_65535(d->map16[data[2]]); + break; + } + + data += 4; + } + } +} + +void BCGModifier::setChannel(int channel) +{ + d->channel = channel; +} + +void BCGModifier::setGamma(double val) +{ + val = (val < 0.01) ? 0.01 : val; + + for (int i=0; i<65536; i++) + d->map16[i] = lround(pow(((double)d->map16[i] / 65535.0), (1.0 / val)) * 65535.0); + + for (int i=0; i<256; i++) + d->map[i] = lround(pow(((double)d->map[i] / 255.0), (1.0 / val)) * 255.0); + + d->modified = true; +} + +void BCGModifier::setBrightness(double val) +{ + int val1 = lround(val * 65535); + + for (int i = 0; i < 65536; i++) + d->map16[i] = d->map16[i] + val1; + + val1 = lround(val * 255); + + for (int i = 0; i < 256; i++) + d->map[i] = d->map[i] + val1; + + d->modified = true; +} + +void BCGModifier::setContrast(double val) +{ + for (int i = 0; i < 65536; i++) + d->map16[i] = lround((d->map16[i] - 32767) * val) + 32767; + + for (int i = 0; i < 256; i++) + d->map[i] = lround((d->map[i] - 127) * val) + 127; + + d->modified = true; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/bcgmodifier.h b/src/libs/dimg/filters/bcgmodifier.h new file mode 100644 index 00000000..b0c915d6 --- /dev/null +++ b/src/libs/dimg/filters/bcgmodifier.h @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-06 + * Description : a Brightness/Contrast/Gamma image filter. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef BCGMODIFIER_H +#define BCGMODIFIER_H + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImg; +class BCGModifierPriv; + +class DIGIKAM_EXPORT BCGModifier +{ + +public: + + enum CHANNEL + { + CHANNEL_ALL=0, + CHANNEL_RED, + CHANNEL_GREEN, + CHANNEL_BLUE + }; + +public: + + BCGModifier(); + ~BCGModifier(); + + void reset(); + bool modified() const; + + void setChannel(int channel); + void setGamma(double val); + void setBrightness(double val); + void setContrast(double val); + void applyBCG(DImg& image); + void applyBCG(uchar *bits, uint width, uint height, bool sixteenBits); + +private: + + BCGModifierPriv* d; +}; + +} // NameSpace Digikam + +#endif /* BCGMODIFIER_H */ diff --git a/src/libs/dimg/filters/colormodifier.cpp b/src/libs/dimg/filters/colormodifier.cpp new file mode 100644 index 00000000..74ddf241 --- /dev/null +++ b/src/libs/dimg/filters/colormodifier.cpp @@ -0,0 +1,287 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-18 + * Description : color modifier methods for DImg framework + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0) +#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0) + +// C++ includes. + +#include +#include + +// Local includes. + +#include "dimg.h" +#include "colormodifier.h" + +namespace Digikam +{ + +class ColorModifierPriv +{ +public: + + ColorModifierPriv() + { + modified = false; + } + + bool modified; + + int redMap[256]; + int greenMap[256]; + int blueMap[256]; + int alphaMap[256]; + + int redMap16[65536]; + int greenMap16[65536]; + int blueMap16[65536]; + int alphaMap16[65536]; +}; + +ColorModifier::ColorModifier() +{ + d = new ColorModifierPriv; + reset(); +} + +ColorModifier::~ColorModifier() +{ + delete d; +} + +bool ColorModifier::modified() const +{ + return d->modified; +} + +void ColorModifier::reset() +{ + // initialize to linear mapping + + for (int i=0; i<65536; i++) + { + d->redMap16[i] = i; + d->greenMap16[i] = i; + d->blueMap16[i] = i; + d->alphaMap16[i] = i; + } + + for (int i=0; i<256; i++) + { + d->redMap[i] = i; + d->greenMap[i] = i; + d->blueMap[i] = i; + d->alphaMap[i] = i; + } + + d->modified = false; +} + +void ColorModifier::setTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit) +{ + if (!sixteenBit) + { + for (int i = 0; i < 256; i++) + { + if (redMap) + d->redMap[i] = redMap[i]; + if (greenMap) + d->greenMap[i] = greenMap[i]; + if (blueMap) + d->blueMap[i] = blueMap[i]; + if (alphaMap) + d->alphaMap[i] = alphaMap[i]; + } + } + else + { + for (int i = 0; i < 65536; i++) + { + if (redMap) + d->redMap16[i] = redMap[i]; + if (greenMap) + d->greenMap16[i] = greenMap[i]; + if (blueMap) + d->blueMap16[i] = blueMap[i]; + if (alphaMap) + d->alphaMap16[i] = alphaMap[i]; + } + } + + d->modified = true; +} + +void ColorModifier::getTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit) +{ + if (!sixteenBit) + { + if (redMap) + memcpy(redMap, d->redMap, (256 * sizeof(int))); + if (greenMap) + memcpy(greenMap, d->greenMap, (256 * sizeof(int))); + if (blueMap) + memcpy(blueMap, d->blueMap, (256 * sizeof(int))); + if (alphaMap) + memcpy(alphaMap, d->alphaMap, (256 * sizeof(int))); + } + else + { + if (redMap) + memcpy(redMap, d->redMap16, (65536 * sizeof(int))); + if (greenMap) + memcpy(greenMap, d->greenMap16, (65536 * sizeof(int))); + if (blueMap) + memcpy(blueMap, d->blueMap16, (65536 * sizeof(int))); + if (alphaMap) + memcpy(alphaMap, d->alphaMap16, (65536 * sizeof(int))); + } +} + +void ColorModifier::applyColorModifier(DImg& image, double r, double g, double b, double a) +{ + if (image.isNull()) + return; + + adjustRGB(r, g, b, a, image.sixteenBit()); + + if (!image.sixteenBit()) // 8 bits image. + { + uchar* data = (uchar*) image.bits(); + + for (uint i=0; iblueMap[data[0]]; + data[1] = d->greenMap[data[1]]; + data[2] = d->redMap[data[2]]; + data[3] = d->alphaMap[data[3]]; + + data += 4; + } + } + else // 16 bits image. + { + ushort* data = (ushort*) image.bits(); + + for (uint i=0; iblueMap16[data[0]]; + data[1] = d->greenMap16[data[1]]; + data[2] = d->redMap16[data[2]]; + data[3] = d->alphaMap16[data[3]]; + + data += 4; + } + } +} + +void ColorModifier::setGamma(double val) +{ + val = (val < 0.01) ? 0.01 : val; + int val2; + + for (int i=0; i<65536; i++) + { + val2 = (int)(pow(((double)d->redMap16[i] / 65535), (1 / val)) * 65535); + d->redMap16[i] = CLAMP_0_65535(val2); + + val2 = (int)(pow(((double)d->greenMap16[i] / 65535), (1 / val)) * 65535); + d->greenMap16[i] = CLAMP_0_65535(val2); + + val2 = (int)(pow(((double)d->blueMap16[i] / 65535), (1 / val)) * 65535); + d->blueMap16[i] = CLAMP_0_65535(val2); + + val2 = (int)(pow(((double)d->alphaMap16[i] / 65535), (1 / val)) * 65535); + d->alphaMap16[i] = CLAMP_0_65535(val2); + } + + for (int i=0; i<256; i++) + { + val2 = (int)(pow(((double)d->redMap[i] / 255), (1 / val)) * 255); + d->redMap[i] = CLAMP_0_255(val2); + + val2 = (int)(pow(((double)d->greenMap[i] / 255), (1 / val)) * 255); + d->greenMap[i] = CLAMP_0_255(val2); + + val2 = (int)(pow(((double)d->blueMap[i] / 255), (1 / val)) * 255); + d->blueMap[i] = CLAMP_0_255(val2); + + val2 = (int)(pow(((double)d->alphaMap[i] / 255), (1 / val)) * 255); + d->alphaMap[i] = CLAMP_0_255(val2); + } + + d->modified = true; +} + +void ColorModifier::adjustRGB(double r, double g, double b, double a, bool sixteenBit) +{ + int r_table[65536]; + int g_table[65536]; + int b_table[65536]; + int a_table[65536]; + int dummy_table[65536]; + + if (r == 1.0 && g == 1.0 && b == 1.0 && a == 1.0) + return ; + + if (r == g && r == b && r == a) + { + setGamma(r); + } + else + { + getTables(r_table, g_table, b_table, a_table, sixteenBit); + + if(r != 1.0) + { + setGamma(r); + getTables(r_table, dummy_table, dummy_table, dummy_table, sixteenBit); + reset(); + } + + if(g != 1.0) + { + setGamma(g); + getTables(dummy_table, g_table, dummy_table, dummy_table, sixteenBit); + reset(); + } + + if(b != 1.0) + { + setGamma(b); + getTables(dummy_table, dummy_table, b_table, dummy_table, sixteenBit); + reset(); + } + + if(a != 1.0) + { + setGamma(a); + getTables(dummy_table, dummy_table, dummy_table, a_table, sixteenBit); + reset(); + } + + setTables(r_table, g_table, b_table, a_table, sixteenBit); + } +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/colormodifier.h b/src/libs/dimg/filters/colormodifier.h new file mode 100644 index 00000000..9473b273 --- /dev/null +++ b/src/libs/dimg/filters/colormodifier.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-18 + * Description : color modifier methods for DImg framework + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef COLORMODIFIER_H +#define COLORMODIFIER_H + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImg; +class ColorModifierPriv; + +class DIGIKAM_EXPORT ColorModifier +{ +public: + + ColorModifier(); + ~ColorModifier(); + + void reset(); + bool modified() const; + void applyColorModifier(DImg& image, double r, double g, double b, double a); + +private: + + void setTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit); + void getTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit); + void setGamma(double val); + void adjustRGB(double r, double g, double b, double a, bool sixteenBit); + +private: + + ColorModifierPriv* d; + +}; + +} // NameSpace Digikam + +#endif /* COLORMODIFIER_H */ diff --git a/src/libs/dimg/filters/dimggaussianblur.cpp b/src/libs/dimg/filters/dimggaussianblur.cpp new file mode 100644 index 00000000..63e19909 --- /dev/null +++ b/src/libs/dimg/filters/dimggaussianblur.cpp @@ -0,0 +1,327 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-17-07 + * Description : A Gaussian Blur threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Gaussian Blur algorithm copyrighted 2004 by + * Pieter Z. Voloshyn . + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimgimagefilters.h" +#include "dimggaussianblur.h" + +namespace Digikam +{ + +DImgGaussianBlur::DImgGaussianBlur(DImg *orgImage, TQObject *parent, int radius) + : DImgThreadedFilter(orgImage, parent, "GaussianBlur") +{ + m_radius = radius; + initFilter(); +} + +DImgGaussianBlur::DImgGaussianBlur(DImgThreadedFilter *parentFilter, + const DImg &orgImage, const DImg &destImage, + int progressBegin, int progressEnd, int radius) + : DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd, + parentFilter->filterName() + ": GaussianBlur") +{ + m_radius = radius; + filterImage(); +} + + +void DImgGaussianBlur::filterImage(void) +{ + gaussianBlurImage(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_radius); +} + +/** Function to apply the Gaussian Blur on an image*/ + +void DImgGaussianBlur::gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius) +{ + if (!data || !width || !height) + { + DWarning() << ("DImgGaussianBlur::gaussianBlurImage: no image data available!") + << endl; + return; + } + + if (radius > 100) radius = 100; + if (radius <= 0) + { + m_destImage = m_orgImage; + return; + } + + // Gaussian kernel computation using the Radius parameter. + + int nKSize, nCenter; + double x, sd, factor, lnsd, lnfactor; + int i, j, n, h, w; + + nKSize = 2 * radius + 1; + nCenter = nKSize / 2; + int *Kernel = new int[nKSize]; + + lnfactor = (4.2485 - 2.7081) / 10 * nKSize + 2.7081; + lnsd = (0.5878 + 0.5447) / 10 * nKSize - 0.5447; + factor = exp (lnfactor); + sd = exp (lnsd); + + for (i = 0; !m_cancel && (i < nKSize); i++) + { + x = sqrt ((i - nCenter) * (i - nCenter)); + Kernel[i] = (int)(factor * exp (-0.5 * pow ((x / sd), 2)) / (sd * sqrt (2.0 * M_PI))); + } + + // Now, we need to convolve the image descriptor. + // I've worked hard here, but I think this is a very smart + // way to convolve an array, its very hard to explain how I reach + // this, but the trick here its to store the sum used by the + // previous pixel, so we sum with the other pixels that wasn't get. + + int nSumA, nSumR, nSumG, nSumB, nCount, progress; + int nKernelWidth = radius * 2 + 1; + + // We need to alloc a 2d array to help us to store the values + + int** arrMult = Alloc2DArray (nKernelWidth, sixteenBit ? 65536 : 256); + + for (i = 0; !m_cancel && (i < nKernelWidth); i++) + for (j = 0; !m_cancel && (j < (sixteenBit ? 65536 : 256)); j++) + arrMult[i][j] = j * Kernel[i]; + + // We need to copy our bits to blur bits + + uchar* pOutBits = m_destImage.bits(); + uchar* pBlur = new uchar[m_destImage.numBytes()]; + + memcpy (pBlur, data, m_destImage.numBytes()); + + // We need to initialize all the loop and iterator variables + + nSumA = nSumR = nSumG = nSumB = nCount = i = j = 0; + unsigned short* data16 = (unsigned short*)data; + unsigned short* pBlur16 = (unsigned short*)pBlur; + unsigned short* pOutBits16 = (unsigned short*)pOutBits; + + // Now, we enter in the main loop + + for (h = 0; !m_cancel && (h < height); h++) + { + for (w = 0; !m_cancel && (w < width); w++, i+=4) + { + if (!sixteenBit) // 8 bits image. + { + uchar *org, *dst; + + // first of all, we need to blur the horizontal lines + + for (n = -radius; !m_cancel && (n <= radius); n++) + { + // if is inside... + if (IsInside (width, height, w + n, h)) + { + // we points to the pixel + j = i + 4*n; + + // finally, we sum the pixels using a method similar to assigntables + + org = &data[j]; + nSumA += arrMult[n + radius][org[3]]; + nSumR += arrMult[n + radius][org[2]]; + nSumG += arrMult[n + radius][org[1]]; + nSumB += arrMult[n + radius][org[0]]; + + // we need to add to the counter, the kernel value + nCount += Kernel[n + radius]; + } + } + + if (nCount == 0) nCount = 1; + + // now, we return to blur bits the horizontal blur values + dst = &pBlur[i]; + dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255); + dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255); + dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255); + dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255); + + // ok, now we reinitialize the variables + nSumA = nSumR = nSumG = nSumB = nCount = 0; + } + else // 16 bits image. + { + unsigned short *org, *dst; + + // first of all, we need to blur the horizontal lines + + for (n = -radius; !m_cancel && (n <= radius); n++) + { + // if is inside... + if (IsInside (width, height, w + n, h)) + { + // we points to the pixel + j = i + 4*n; + + // finally, we sum the pixels using a method similar to assigntables + + org = &data16[j]; + nSumA += arrMult[n + radius][org[3]]; + nSumR += arrMult[n + radius][org[2]]; + nSumG += arrMult[n + radius][org[1]]; + nSumB += arrMult[n + radius][org[0]]; + + // we need to add to the counter, the kernel value + nCount += Kernel[n + radius]; + } + } + + if (nCount == 0) nCount = 1; + + // now, we return to blur bits the horizontal blur values + dst = &pBlur16[i]; + dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535); + dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535); + dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535); + dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535); + + // ok, now we reinitialize the variables + nSumA = nSumR = nSumG = nSumB = nCount = 0; + } + } + + progress = (int) (((double)h * 50.0) / height); + if ( progress%5 == 0 ) + postProgress( progress ); + } + + // getting the blur bits, we initialize position variables + i = j = 0; + + // We enter in the second main loop + for (w = 0; !m_cancel && (w < width); w++, i = w*4) + { + for (h = 0; !m_cancel && (h < height); h++, i += width*4) + { + if (!sixteenBit) // 8 bits image. + { + uchar *org, *dst; + + // first of all, we need to blur the vertical lines + for (n = -radius; !m_cancel && (n <= radius); n++) + { + // if is inside... + if (IsInside(width, height, w, h + n)) + { + // we points to the pixel + j = i + n * 4 * width; + + // finally, we sum the pixels using a method similar to assigntables + org = &pBlur[j]; + nSumA += arrMult[n + radius][org[3]]; + nSumR += arrMult[n + radius][org[2]]; + nSumG += arrMult[n + radius][org[1]]; + nSumB += arrMult[n + radius][org[0]]; + + // we need to add to the counter, the kernel value + nCount += Kernel[n + radius]; + } + } + + if (nCount == 0) nCount = 1; + + // To preserve Alpha channel. + memcpy (&pOutBits[i], &data[i], 4); + + // now, we return to bits the vertical blur values + dst = &pOutBits[i]; + dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255); + dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255); + dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255); + dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255); + + // ok, now we reinitialize the variables + nSumA = nSumR = nSumG = nSumB = nCount = 0; + } + else // 16 bits image. + { + unsigned short *org, *dst; + + // first of all, we need to blur the vertical lines + for (n = -radius; !m_cancel && (n <= radius); n++) + { + // if is inside... + if (IsInside(width, height, w, h + n)) + { + // we points to the pixel + j = i + n * 4 * width; + + // finally, we sum the pixels using a method similar to assigntables + org = &pBlur16[j]; + nSumA += arrMult[n + radius][org[3]]; + nSumR += arrMult[n + radius][org[2]]; + nSumG += arrMult[n + radius][org[1]]; + nSumB += arrMult[n + radius][org[0]]; + + // we need to add to the counter, the kernel value + nCount += Kernel[n + radius]; + } + } + + if (nCount == 0) nCount = 1; + + // To preserve Alpha channel. + memcpy (&pOutBits16[i], &data16[i], 8); + + // now, we return to bits the vertical blur values + dst = &pOutBits16[i]; + dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535); + dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535); + dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535); + dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535); + + // ok, now we reinitialize the variables + nSumA = nSumR = nSumG = nSumB = nCount = 0; + } + } + + progress = (int) (50.0 + ((double)w * 50.0) / width); + if ( progress%5 == 0 ) + postProgress( progress ); + } + + // now, we must free memory + Free2DArray (arrMult, nKernelWidth); + delete [] pBlur; + delete [] Kernel; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/dimggaussianblur.h b/src/libs/dimg/filters/dimggaussianblur.h new file mode 100644 index 00000000..e88944bc --- /dev/null +++ b/src/libs/dimg/filters/dimggaussianblur.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-17-07 + * Description : A Gaussian Blur threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGGAUSSIAN_BLUR_H +#define DIMGGAUSSIAN_BLUR_H + +// Digikam includes. + +#include "digikam_export.h" + +// Local includes. + +#include "dimgthreadedfilter.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DImgGaussianBlur : public DImgThreadedFilter +{ + +public: + + DImgGaussianBlur(DImg *orgImage, TQObject *parent=0, int radius=3); + + // Constructor for slave mode: execute immediately in current thread with specified master filter + DImgGaussianBlur(DImgThreadedFilter *parentFilter, const DImg &orgImage, const DImg &destImage, + int progressBegin=0, int progressEnd=100, int radius=3); + + ~DImgGaussianBlur(){}; + +private: // Gaussian blur filter data. + + int m_radius; + +private: // Gaussian blur filter methods. + + virtual void filterImage(void); + + void gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius); + + // function to allocate a 2d array + int** Alloc2DArray (int Columns, int Rows) + { + // First, we declare our future 2d array to be returned + int** lpcArray = 0L; + + // Now, we alloc the main pointer with Columns + lpcArray = new int*[Columns]; + + for (int i = 0; i < Columns; i++) + lpcArray[i] = new int[Rows]; + + return (lpcArray); + }; + + // Function to deallocates the 2d array previously created + void Free2DArray (int** lpcArray, int Columns) + { + // loop to dealocate the columns + for (int i = 0; i < Columns; i++) + delete [] lpcArray[i]; + + // now, we delete the main pointer + delete [] lpcArray; + }; + + inline bool IsInside (int Width, int Height, int X, int Y) + { + bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true); + bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true); + return (bIsWOk && bIsHOk); + }; +}; + +} // NameSpace Digikam + +#endif /* DIMGGAUSSIAN_BLUR_H */ diff --git a/src/libs/dimg/filters/dimgimagefilters.cpp b/src/libs/dimg/filters/dimgimagefilters.cpp new file mode 100644 index 00000000..b964ed4d --- /dev/null +++ b/src/libs/dimg/filters/dimgimagefilters.cpp @@ -0,0 +1,977 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-24-01 + * Description : misc image filters + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * Original Equalise and StretchContrast Algorithms copyright 2002 + * by Daniel M. Duley from KImageEffect API. + * + * Original Normalize Image algorithm copyrighted 1997 by + * Adam D. Moss from Gimp 2.0 implementation. + * + * Original channel mixer algorithm copyrighted 2002 by + * Martin Guldahl from Gimp 2.2 + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include + +// Local includes. + +#include "imagehistogram.h" +#include "imagelevels.h" +#include "dcolor.h" +#include "ddebug.h" +#include "dimggaussianblur.h" +#include "dimgsharpen.h" +#include "dimgimagefilters.h" + +namespace Digikam +{ + +/** Performs an histogram equalisation of the image. + this method adjusts the brightness of colors across the + active image so that the histogram for the value channel + is as nearly as possible flat, that is, so that each possible + brightness value appears at about the same number of pixels + as each other value. Sometimes Equalize works wonderfully at + enhancing the contrasts in an image. Other times it gives + garbage. It is a very powerful operation, which can either work + miracles on an image or destroy it.*/ +void DImgImageFilters::equalizeImage(uchar *data, int w, int h, bool sixteenBit) +{ + if (!data || !w || !h) + { + DWarning() << ("DImgImageFilters::equalizeImage: no image data available!") << endl; + return; + } + + struct double_packet high, low, intensity; + struct double_packet *map; + struct int_packet *equalize_map; + long i; + + // Create an histogram of the current image. + ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit); + + // Memory allocation. + map = new double_packet[histogram->getHistogramSegment()]; + equalize_map = new int_packet[histogram->getHistogramSegment()]; + + if( !histogram || !map || !equalize_map ) + { + if(histogram) + delete histogram; + + if(map) + delete [] map; + + if(equalize_map) + delete [] equalize_map; + + DWarning() << ("DImgImageFilters::equalizeImage: Unable to allocate memory!") << endl; + return; + } + + // Integrate the histogram to get the equalization map. + + memset(&intensity, 0, sizeof(struct double_packet)); + memset(&high, 0, sizeof(struct double_packet)); + memset(&low, 0, sizeof(struct double_packet)); + + for(i = 0 ; i < histogram->getHistogramSegment() ; i++) + { + intensity.red += histogram->getValue(ImageHistogram::RedChannel, i); + intensity.green += histogram->getValue(ImageHistogram::GreenChannel, i); + intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, i); + intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, i); + map[i] = intensity; + } + + // Stretch the histogram. + + low = map[0]; + high = map[histogram->getHistogramSegment()-1]; + memset(equalize_map, 0, histogram->getHistogramSegment()*sizeof(int_packet)); + + for(i = 0 ; i < histogram->getHistogramSegment() ; i++) + { + if(high.red != low.red) + equalize_map[i].red = (uint)(((256*histogram->getHistogramSegment() -1) * + (map[i].red-low.red))/(high.red-low.red)); + + if(high.green != low.green) + equalize_map[i].green = (uint)(((256*histogram->getHistogramSegment() -1) * + (map[i].green-low.green))/(high.green-low.green)); + + if(high.blue != low.blue) + equalize_map[i].blue = (uint)(((256*histogram->getHistogramSegment() -1) * + (map[i].blue-low.blue))/(high.blue-low.blue)); + + if(high.alpha != low.alpha) + equalize_map[i].alpha = (uint)(((256*histogram->getHistogramSegment() -1) * + (map[i].alpha-low.alpha))/(high.alpha-low.alpha)); + } + + delete histogram; + delete [] map; + + // Apply results to image. + + if (!sixteenBit) // 8 bits image. + { + uchar red, green, blue, alpha; + uchar *ptr = data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if(low.red != high.red) + red = (equalize_map[red].red)/257; + + if(low.green != high.green) + green = (equalize_map[green].green)/257; + + if(low.blue != high.blue) + blue = (equalize_map[blue].blue)/257; + + if(low.alpha != high.alpha) + alpha = (equalize_map[alpha].alpha)/257; + + ptr[0] = blue; + ptr[1] = green; + ptr[2] = red; + ptr[3] = alpha; + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue, alpha; + unsigned short *ptr = (unsigned short *)data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if(low.red != high.red) + red = (equalize_map[red].red)/257; + + if(low.green != high.green) + green = (equalize_map[green].green)/257; + + if(low.blue != high.blue) + blue = (equalize_map[blue].blue)/257; + + if(low.alpha != high.alpha) + alpha = (equalize_map[alpha].alpha)/257; + + ptr[0] = blue; + ptr[1] = green; + ptr[2] = red; + ptr[3] = alpha; + ptr += 4; + } + } + + delete [] equalize_map; +} + +/** Performs histogram normalization of the image. The algorithm normalizes + the pixel values from an image for to span the full range + of color values. This is a contrast enhancement technique.*/ +void DImgImageFilters::stretchContrastImage(uchar *data, int w, int h, bool sixteenBit) +{ + if (!data || !w || !h) + { + DWarning() << ("DImgImageFilters::stretchContrastImage: no image data available!") << endl; + return; + } + + struct double_packet high, low, intensity; + struct int_packet *normalize_map; + long long number_pixels; + long i; + unsigned long threshold_intensity; + + // Create an histogram of the current image. + ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit); + + // Memory allocation. + normalize_map = new int_packet[histogram->getHistogramSegment()]; + + if( !histogram || !normalize_map ) + { + if(histogram) + delete histogram; + + if(normalize_map) + delete [] normalize_map; + + DWarning() << ("DImgImageFilters::stretchContrastImage: Unable to allocate memory!") << endl; + return; + } + + // Find the histogram boundaries by locating the 0.1 percent levels. + + number_pixels = (long long)(w*h); + threshold_intensity = number_pixels / 1000; + + memset(&high, 0, sizeof(struct double_packet)); + memset(&low, 0, sizeof(struct double_packet)); + + // Red. + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.red = histogram->getHistogramSegment()-1 ; high.red != 0 ; high.red--) + { + intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)high.red); + + if( intensity.red > threshold_intensity ) + break; + } + + if( low.red == high.red ) + { + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + + for(low.red = 0 ; low.red < histogram->getHistogramSegment()-1 ; low.red++) + { + intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)low.red); + + if( intensity.red > threshold_intensity ) + break; + } + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.red = histogram->getHistogramSegment()-1 ; high.red != 0 ; high.red--) + { + intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)high.red); + + if( intensity.red > threshold_intensity ) + break; + } + } + + // Green. + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.green = histogram->getHistogramSegment()-1 ; high.green != 0 ; high.green--) + { + intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)high.green); + + if( intensity.green > threshold_intensity ) + break; + } + + if( low.green == high.green ) + { + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + + for(low.green = 0 ; low.green < histogram->getHistogramSegment()-1 ; low.green++) + { + intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)low.green); + + if( intensity.green > threshold_intensity ) + break; + } + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.green = histogram->getHistogramSegment()-1 ; high.green != 0 ; high.green--) + { + intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)high.green); + + if( intensity.green > threshold_intensity ) + break; + } + } + + // Blue. + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.blue = histogram->getHistogramSegment()-1 ; high.blue != 0 ; high.blue--) + { + intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)high.blue); + + if( intensity.blue > threshold_intensity ) + break; + } + + if( low.blue == high.blue ) + { + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + + for(low.blue = 0 ; low.blue < histogram->getHistogramSegment()-1 ; low.blue++) + { + intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)low.blue); + + if( intensity.blue > threshold_intensity ) + break; + } + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.blue = histogram->getHistogramSegment()-1 ; high.blue != 0 ; high.blue--) + { + intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)high.blue); + + if( intensity.blue > threshold_intensity ) + break; + } + } + + // Alpha. + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.alpha = histogram->getHistogramSegment()-1 ; high.alpha != 0 ; high.alpha--) + { + intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)high.alpha); + + if( intensity.alpha > threshold_intensity ) + break; + } + + if( low.alpha == high.alpha ) + { + threshold_intensity = 0; + memset(&intensity, 0, sizeof(struct double_packet)); + + for(low.alpha = 0 ; low.alpha < histogram->getHistogramSegment()-1 ; low.alpha++) + { + intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)low.alpha); + + if( intensity.alpha > threshold_intensity ) + break; + } + + memset(&intensity, 0, sizeof(struct double_packet)); + + for(high.alpha = histogram->getHistogramSegment()-1 ; high.alpha != 0 ; high.alpha--) + { + intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)high.alpha); + + if( intensity.alpha > threshold_intensity ) + break; + } + } + + delete histogram; + + // Stretch the histogram to create the normalized image mapping. + + memset(normalize_map, 0, histogram->getHistogramSegment()*sizeof(struct int_packet)); + + for(i = 0 ; i <= (long)histogram->getHistogramSegment()-1 ; i++) + { + if(i < (long) low.red) + normalize_map[i].red = 0; + else if (i > (long) high.red) + normalize_map[i].red = (256*histogram->getHistogramSegment() -1); + else if (low.red != high.red) + normalize_map[i].red = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.red))/(high.red-low.red)); + + if(i < (long) low.green) + normalize_map[i].green = 0; + else if (i > (long) high.green) + normalize_map[i].green = (256*histogram->getHistogramSegment() -1); + else if (low.green != high.green) + normalize_map[i].green = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.green))/(high.green-low.green)); + + if(i < (long) low.blue) + normalize_map[i].blue = 0; + else if (i > (long) high.blue) + normalize_map[i].blue = (256*histogram->getHistogramSegment() -1); + else if (low.blue != high.blue) + normalize_map[i].blue = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.blue))/(high.blue-low.blue)); + + if(i < (long) low.alpha) + normalize_map[i].alpha = 0; + else if (i > (long) high.alpha) + normalize_map[i].alpha = (256*histogram->getHistogramSegment() -1); + else if (low.alpha != high.alpha) + normalize_map[i].alpha = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.alpha))/(high.alpha-low.alpha)); + } + + // Apply result to image. + + if (!sixteenBit) // 8 bits image. + { + uchar red, green, blue, alpha; + uchar *ptr = data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if(low.red != high.red) + red = (normalize_map[red].red)/257; + + if(low.green != high.green) + green = (normalize_map[green].green)/257; + + if(low.blue != high.blue) + blue = (normalize_map[blue].blue)/257; + + if(low.alpha != high.alpha) + alpha = (normalize_map[alpha].alpha)/257; + + ptr[0] = blue; + ptr[1] = green; + ptr[2] = red; + ptr[3] = alpha; + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue, alpha; + unsigned short *ptr = (unsigned short *)data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if(low.red != high.red) + red = (normalize_map[red].red)/257; + + if(low.green != high.green) + green = (normalize_map[green].green)/257; + + if(low.blue != high.blue) + blue = (normalize_map[blue].blue)/257; + + if(low.alpha != high.alpha) + alpha = (normalize_map[alpha].alpha)/257; + + ptr[0] = blue; + ptr[1] = green; + ptr[2] = red; + ptr[3] = alpha; + ptr += 4; + } + } + + delete [] normalize_map; +} + +/** This method scales brightness values across the active + image so that the darkest point becomes black, and the + brightest point becomes as bright as possible without + altering its hue. This is often a magic fix for + images that are dim or washed out.*/ +void DImgImageFilters::normalizeImage(uchar *data, int w, int h, bool sixteenBit) +{ + NormalizeParam param; + int x, i; + unsigned short range; + + int segments = sixteenBit ? 65536 : 256; + + // Memory allocation. + + param.lut = new unsigned short[segments]; + + // Find min. and max. values. + + param.min = segments-1; + param.max = 0; + + if (!sixteenBit) // 8 bits image. + { + uchar red, green, blue; + uchar *ptr = data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + if (red < param.min) param.min = red; + if (red > param.max) param.max = red; + + if (green < param.min) param.min = green; + if (green > param.max) param.max = green; + + if (blue < param.min) param.min = blue; + if (blue > param.max) param.max = blue; + + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue; + unsigned short *ptr = (unsigned short *)data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + if (red < param.min) param.min = red; + if (red > param.max) param.max = red; + + if (green < param.min) param.min = green; + if (green > param.max) param.max = green; + + if (blue < param.min) param.min = blue; + if (blue > param.max) param.max = blue; + + ptr += 4; + } + } + + // Calculate LUT. + + range = (unsigned short)(param.max - param.min); + + if (range != 0) + { + for (x = (int)param.min ; x <= (int)param.max ; x++) + param.lut[x] = (unsigned short)((segments-1) * (x - param.min) / range); + } + else + param.lut[(int)param.min] = (unsigned short)param.min; + + // Apply LUT to image. + + if (!sixteenBit) // 8 bits image. + { + uchar red, green, blue; + uchar *ptr = data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + ptr[0] = param.lut[blue]; + ptr[1] = param.lut[green]; + ptr[2] = param.lut[red]; + + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue; + unsigned short *ptr = (unsigned short *)data; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + ptr[0] = param.lut[blue]; + ptr[1] = param.lut[green]; + ptr[2] = param.lut[red]; + + ptr += 4; + } + } + + delete [] param.lut; +} + +/** Performs histogram auto correction of levels. + This method maximizes the tonal range in the Red, + Green, and Blue channels. It search the image shadow and highlight + limit values and adjust the Red, Green, and Blue channels + to a full histogram range.*/ +void DImgImageFilters::autoLevelsCorrectionImage(uchar *data, int w, int h, bool sixteenBit) +{ + if (!data || !w || !h) + { + DWarning() << ("DImgImageFilters::autoLevelsCorrectionImage: no image data available!") + << endl; + return; + } + uchar* desData; + + // Create the new empty destination image data space. + if (sixteenBit) + desData = new uchar[w*h*8]; + else + desData = new uchar[w*h*4]; + + // Create an histogram of the current image. + ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit); + + // Create an empty instance of levels to use. + ImageLevels *levels = new ImageLevels(sixteenBit); + + // Initialize an auto levels correction of the histogram. + levels->levelsAuto(histogram); + + // Calculate the LUT to apply on the image. + levels->levelsLutSetup(ImageHistogram::AlphaChannel); + + // Apply the lut to the image. + levels->levelsLutProcess(data, desData, w, h); + + if (sixteenBit) + memcpy (data, desData, w*h*8); + else + memcpy (data, desData, w*h*4); + + delete [] desData; + delete histogram; + delete levels; +} + +/** Performs image colors inversion. This tool is used for negate image + resulting of a positive film scanned.*/ +void DImgImageFilters::invertImage(uchar *data, int w, int h, bool sixteenBit) +{ + if (!data || !w || !h) + { + DWarning() << ("DImgImageFilters::invertImage: no image data available!") + << endl; + return; + } + + if (!sixteenBit) // 8 bits image. + { + uchar *ptr = data; + + for (int i = 0 ; i < w*h ; i++) + { + ptr[0] = 255 - ptr[0]; + ptr[1] = 255 - ptr[1]; + ptr[2] = 255 - ptr[2]; + ptr[3] = 255 - ptr[3]; + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short *ptr = (unsigned short *)data; + + for (int i = 0 ; i < w*h ; i++) + { + ptr[0] = 65535 - ptr[0]; + ptr[1] = 65535 - ptr[1]; + ptr[2] = 65535 - ptr[2]; + ptr[3] = 65535 - ptr[3]; + ptr += 4; + } + } +} + +/** Mix RGB channel color from image*/ +void DImgImageFilters::channelMixerImage(uchar *data, int Width, int Height, bool sixteenBit, + bool bPreserveLum, bool bMonochrome, + float rrGain, float rgGain, float rbGain, + float grGain, float ggGain, float gbGain, + float brGain, float bgGain, float bbGain) +{ + if (!data || !Width || !Height) + { + DWarning() << ("DImgImageFilters::channelMixerImage: no image data available!") + << endl; + return; + } + + int i; + + double rnorm = CalculateNorm (rrGain, rgGain, rbGain, bPreserveLum); + double gnorm = CalculateNorm (grGain, ggGain, gbGain, bPreserveLum); + double bnorm = CalculateNorm (brGain, bgGain, bbGain, bPreserveLum); + + if (!sixteenBit) // 8 bits image. + { + uchar nGray, red, green, blue; + uchar *ptr = data; + + for (i = 0 ; i < Width*Height ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + if (bMonochrome) + { + nGray = MixPixel (rrGain, rgGain, rbGain, + (unsigned short)red, (unsigned short)green, (unsigned short)blue, + sixteenBit, rnorm); + ptr[0] = ptr[1] = ptr[2] = nGray; + } + else + { + ptr[0] = (uchar)MixPixel (brGain, bgGain, bbGain, + (unsigned short)red, (unsigned short)green, (unsigned short)blue, + sixteenBit, bnorm); + ptr[1] = (uchar)MixPixel (grGain, ggGain, gbGain, + (unsigned short)red, (unsigned short)green, (unsigned short)blue, + sixteenBit, gnorm); + ptr[2] = (uchar)MixPixel (rrGain, rgGain, rbGain, + (unsigned short)red, (unsigned short)green, (unsigned short)blue, + sixteenBit, rnorm); + } + + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short nGray, red, green, blue; + unsigned short *ptr = (unsigned short *)data; + + for (i = 0 ; i < Width*Height ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + + if (bMonochrome) + { + nGray = MixPixel (rrGain, rgGain, rbGain, red, green, blue, sixteenBit, rnorm); + ptr[0] = ptr[1] = ptr[2] = nGray; + } + else + { + ptr[0] = MixPixel (brGain, bgGain, bbGain, red, green, blue, sixteenBit, bnorm); + ptr[1] = MixPixel (grGain, ggGain, gbGain, red, green, blue, sixteenBit, gnorm); + ptr[2] = MixPixel (rrGain, rgGain, rbGain, red, green, blue, sixteenBit, rnorm); + } + + ptr += 4; + } + } +} + +/** Change color tonality of an image to appling a RGB color mask.*/ +void DImgImageFilters::changeTonality(uchar *data, int width, int height, bool sixteenBit, + int redMask, int greenMask, int blueMask) +{ + if (!data || !width || !height) + { + DWarning() << ("DImgImageFilters::changeTonality: no image data available!") + << endl; + return; + } + + int hue, sat, lig; + + DColor mask(redMask, greenMask, blueMask, 0, sixteenBit); + mask.getHSL(&hue, &sat, &lig); + + if (!sixteenBit) // 8 bits image. + { + uchar *ptr = data; + + for (int i = 0 ; i < width*height ; i++) + { + // Convert to grayscale using tonal mask + + lig = ROUND (0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]); + + mask.setRGB(hue, sat, lig, sixteenBit); + + ptr[0] = (uchar)mask.blue(); + ptr[1] = (uchar)mask.green(); + ptr[2] = (uchar)mask.red(); + ptr += 4; + } + } + else // 16 bits image. + { + unsigned short *ptr = (unsigned short *)data; + + for (int i = 0 ; i < width*height ; i++) + { + // Convert to grayscale using tonal mask + + lig = ROUND (0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]); + + mask.setRGB(hue, sat, lig, sixteenBit); + + ptr[0] = (unsigned short)mask.blue(); + ptr[1] = (unsigned short)mask.green(); + ptr[2] = (unsigned short)mask.red(); + ptr += 4; + } + } +} + +/** Function to apply the GaussianBlur on an image. This method do not use a + dedicaced thread.*/ +void DImgImageFilters::gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius) +{ + if (!data || !width || !height) + { + DWarning() << ("DImgImageFilters::gaussianBlurImage: no image data available!") + << endl; + return; + } + + if (radius > 100) radius = 100; + if (radius <= 0) return; + + DImg orgImage(width, height, sixteenBit, true, data); + DImgGaussianBlur *filter = new DImgGaussianBlur(&orgImage, 0L, radius); + DImg imDest = filter->getTargetImage(); + memcpy( data, imDest.bits(), imDest.numBytes() ); + delete filter; +} + +/** Function to apply the sharpen filter on an image. This method do not use a + dedicaced thread.*/ +void DImgImageFilters::sharpenImage(uchar *data, int width, int height, bool sixteenBit, int radius) +{ + if (!data || !width || !height) + { + DWarning() << ("DImgImageFilters::sharpenImage: no image data available!") + << endl; + return; + } + + if (radius > 100) radius = 100; + if (radius <= 0) return; + + DImg orgImage(width, height, sixteenBit, true, data); + DImgSharpen *filter = new DImgSharpen(&orgImage, 0L, radius); + DImg imDest = filter->getTargetImage(); + memcpy( data, imDest.bits(), imDest.numBytes() ); + delete filter; +} + +/** Function to perform pixel antialiasing with 8 bits/color/pixel images. This method is used to smooth target + image in transformation method like free rotation or shear tool. */ +void DImgImageFilters::pixelAntiAliasing(uchar *data, int Width, int Height, double X, double Y, + uchar *A, uchar *R, uchar *G, uchar *B) +{ + int nX, nY, j; + double lfWeightX[2], lfWeightY[2], lfWeight; + double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0; + + nX = (int)X; + nY = (int)Y; + + if (Y >= 0.0) + lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY); + else + lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY)); + + if (X >= 0.0) + lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX); + else + lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX)); + + for (int loopx = 0; loopx <= 1; loopx++) + { + for (int loopy = 0; loopy <= 1; loopy++) + { + lfWeight = lfWeightX[loopx] * lfWeightY[loopy]; + j = setPositionAdjusted (Width, Height, nX + loopx, nY + loopy); + + lfTotalB += ((double)data[j] * lfWeight); + j++; + lfTotalG += ((double)data[j] * lfWeight); + j++; + lfTotalR += ((double)data[j] * lfWeight); + j++; + lfTotalA += ((double)data[j] * lfWeight); + j++; + } + } + + *B = CLAMP0255((int)lfTotalB); + *G = CLAMP0255((int)lfTotalG); + *R = CLAMP0255((int)lfTotalR); + *A = CLAMP0255((int)lfTotalA); +} + +/** Function to perform pixel antialiasing with 16 bits/color/pixel images. This method is used to smooth target + image in transformation method like free rotation or shear tool. */ +void DImgImageFilters::pixelAntiAliasing16(unsigned short *data, int Width, int Height, double X, double Y, + unsigned short *A, unsigned short *R, unsigned short *G, + unsigned short *B) +{ + int nX, nY, j; + double lfWeightX[2], lfWeightY[2], lfWeight; + double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0; + + nX = (int)X; + nY = (int)Y; + + if (Y >= 0.0) + lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY); + else + lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY)); + + if (X >= 0.0) + lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX); + else + lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX)); + + for (int loopx = 0; loopx <= 1; loopx++) + { + for (int loopy = 0; loopy <= 1; loopy++) + { + lfWeight = lfWeightX[loopx] * lfWeightY[loopy]; + j = setPositionAdjusted (Width, Height, nX + loopx, nY + loopy); + + lfTotalB += ((double)data[j] * lfWeight); + j++; + lfTotalG += ((double)data[j] * lfWeight); + j++; + lfTotalR += ((double)data[j] * lfWeight); + j++; + lfTotalA += ((double)data[j] * lfWeight); + j++; + } + } + + *B = CLAMP065535((int)lfTotalB); + *G = CLAMP065535((int)lfTotalG); + *R = CLAMP065535((int)lfTotalR); + *A = CLAMP065535((int)lfTotalA); +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/dimgimagefilters.h b/src/libs/dimg/filters/dimgimagefilters.h new file mode 100644 index 00000000..009c3efb --- /dev/null +++ b/src/libs/dimg/filters/dimgimagefilters.h @@ -0,0 +1,133 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-24-01 + * Description : misc image filters + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGIMAGE_FILTERS_H +#define DIMGIMAGE_FILTERS_H + +#define CLAMP0255(a) TQMIN(TQMAX(a,0), 255) +#define CLAMP065535(a) TQMIN(TQMAX(a,0), 65535) +#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x))) +#define ROUND(x) ((int) ((x) + 0.5)) + +// C++ includes. + +#include + +// Digikam includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DImgImageFilters +{ +public: + + DImgImageFilters(){}; + ~DImgImageFilters(){}; + +private: // Private structures used internally. + + struct double_packet + { + double red; + double green; + double blue; + double alpha; + }; + + struct int_packet + { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; + }; + + struct NormalizeParam + { + unsigned short *lut; + double min; + double max; + }; + +private: // Private methods used internally. + + // Methods for Channel Mixer. + + inline double CalculateNorm(float RedGain, float GreenGain, float BlueGain, bool bPreserveLum) + { + double lfSum = RedGain + GreenGain + BlueGain; + + if ((lfSum == 0.0) || (bPreserveLum == false)) + return (1.0); + + return( fabs (1.0 / lfSum) ); + }; + + inline unsigned short MixPixel(float RedGain, float GreenGain, float BlueGain, + unsigned short R, unsigned short G, unsigned short B, bool sixteenBit, + double Norm) + { + double lfMix = RedGain * (double)R + GreenGain * (double)G + BlueGain * (double)B; + lfMix *= Norm; + int segment = sixteenBit ? 65535 : 255; + + return( (unsigned short)CLAMP (lfMix, 0, segment) ); + }; + + inline int setPositionAdjusted (int Width, int Height, int X, int Y) + { + X = (X < 0) ? 0 : (X >= Width ) ? Width - 1 : X; + Y = (Y < 0) ? 0 : (Y >= Height) ? Height - 1 : Y; + return (Y*Width*4 + 4*X); + }; + +public: // Public methods. + + void equalizeImage(uchar *data, int w, int h, bool sixteenBit); + void stretchContrastImage(uchar *data, int w, int h, bool sixteenBit); + void normalizeImage(uchar *data, int w, int h, bool sixteenBit); + void autoLevelsCorrectionImage(uchar *data, int w, int h, bool sixteenBit); + void invertImage(uchar *data, int w, int h, bool sixteenBit); + void channelMixerImage(uchar *data, int Width, int Height, bool sixteenBit, + bool bPreserveLum, bool bMonochrome, + float rrGain, float rgGain, float rbGain, + float grGain, float ggGain, float gbGain, + float brGain, float bgGain, float bbGain); + void changeTonality(uchar *data, int width, int height, bool sixteenBit, + int redMask, int greenMask, int blueMask); + void gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius); + void sharpenImage(uchar *data, int width, int height, bool sixteenBit, int radius); + + void pixelAntiAliasing(uchar *data, int Width, int Height, double X, double Y, + uchar *A, uchar *R, uchar *G, uchar *B); + + void pixelAntiAliasing16(unsigned short *data, int Width, int Height, double X, double Y, + unsigned short *A, unsigned short *R, unsigned short *G, unsigned short *B); +}; + +} // NameSpace Digikam + +#endif /* DIMGIMAGE_FILTERS_H */ diff --git a/src/libs/dimg/filters/dimgsharpen.cpp b/src/libs/dimg/filters/dimgsharpen.cpp new file mode 100644 index 00000000..a27d5b10 --- /dev/null +++ b/src/libs/dimg/filters/dimgsharpen.cpp @@ -0,0 +1,243 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-17-07 + * Description : A Sharpen threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * Original Sharpen algorithm copyright 2002 + * by Daniel M. Duley from KImageEffect API. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define SQ2PI 2.50662827463100024161235523934010416269302368164062 +#define Epsilon 1.0e-12 + +// C++ includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimgimagefilters.h" +#include "dimgsharpen.h" + +namespace Digikam +{ + +DImgSharpen::DImgSharpen(DImg *orgImage, TQObject *parent, double radius, double sigma) + : DImgThreadedFilter(orgImage, parent, "Sharpen") +{ + m_radius = radius; + m_sigma = sigma; + initFilter(); +} + +DImgSharpen::DImgSharpen(DImgThreadedFilter *parentFilter, + const DImg &orgImage, const DImg &destImage, + int progressBegin, int progressEnd, double radius, double sigma) + : DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd, + parentFilter->filterName() + ": Sharpen") +{ + m_radius = radius; + m_sigma = sigma; + // We need to provide support for orgImage == destImage. + // The algorithm does not support this out of the box, so use a temporary. + if (orgImage.bits() == destImage.bits()) + m_destImage = DImg(destImage.width(), destImage.height(), destImage.sixteenBit()); + filterImage(); + if (orgImage.bits() == destImage.bits()) + memcpy(destImage.bits(), m_destImage.bits(), m_destImage.numBytes()); +} + +void DImgSharpen::filterImage(void) +{ + sharpenImage(m_radius, m_sigma); +} + +/** Function to apply the sharpen filter on an image*/ + +void DImgSharpen::sharpenImage(double radius, double sigma) +{ + if (m_orgImage.isNull()) + { + DWarning() << k_funcinfo << "No image data available!" + << endl; + return; + } + + if (radius <= 0.0) + { + m_destImage = m_orgImage; + return; + } + + double alpha, normalize=0.0; + long i=0, u, v; + + int kernelWidth = getOptimalKernelWidth(radius, sigma); + + if((int)m_orgImage.width() < kernelWidth) + { + DWarning() << k_funcinfo << "Image is smaller than radius!" + << endl; + return; + } + + double *kernel = new double[kernelWidth*kernelWidth]; + + if(!kernel) + { + DWarning() << k_funcinfo << "Unable to allocate memory!" + << endl; + return; + } + + for(v=(-kernelWidth/2) ; v <= (kernelWidth/2) ; v++) + { + for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++) + { + alpha = exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); + kernel[i] = alpha/(2.0*M_PI*sigma*sigma); + normalize += kernel[i]; + i++; + } + } + + kernel[i/2] = (-2.0)*normalize; + convolveImage(kernelWidth, kernel); + + delete [] kernel; +} + +bool DImgSharpen::convolveImage(const unsigned int order, const double *kernel) +{ + uint x, y; + int mx, my, sx, sy, mcx, mcy, progress; + long kernelWidth, i; + double red, green, blue, alpha, normalize=0.0; + double *k=0; + DColor color; + + kernelWidth = order; + + if((kernelWidth % 2) == 0) + { + DWarning() << k_funcinfo << "Kernel width must be an odd number!" + << endl; + return(false); + } + + double *normal_kernel = new double[kernelWidth*kernelWidth]; + + if(!normal_kernel) + { + DWarning() << k_funcinfo << "Unable to allocate memory!" + << endl; + return(false); + } + + for(i=0 ; i < (kernelWidth*kernelWidth) ; i++) + normalize += kernel[i]; + + if(fabs(normalize) <= Epsilon) + normalize=1.0; + + normalize = 1.0/normalize; + + for(i=0 ; i < (kernelWidth*kernelWidth) ; i++) + normal_kernel[i] = normalize*kernel[i]; + + double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0; + + for(y=0 ; !m_cancel && (y < m_destImage.height()) ; y++) + { + sy = y-(kernelWidth/2); + + for(x=0 ; !m_cancel && (x < m_destImage.width()) ; x++) + { + k = normal_kernel; + red = green = blue = alpha = 0; + sy = y-(kernelWidth/2); + + for(mcy=0 ; !m_cancel && (mcy < kernelWidth) ; mcy++, sy++) + { + my = sy < 0 ? 0 : sy > (int)m_destImage.height()-1 ? m_destImage.height()-1 : sy; + sx = x+(-kernelWidth/2); + + for(mcx=0 ; !m_cancel && (mcx < kernelWidth) ; mcx++, sx++) + { + mx = sx < 0 ? 0 : sx > (int)m_destImage.width()-1 ? m_destImage.width()-1 : sx; + color = m_orgImage.getPixelColor(mx, my); + red += (*k)*(color.red() * 257.0); + green += (*k)*(color.green() * 257.0); + blue += (*k)*(color.blue() * 257.0); + alpha += (*k)*(color.alpha() * 257.0); + k++; + } + } + + red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red+0.5; + green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green+0.5; + blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue+0.5; + alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha+0.5; + + m_destImage.setPixelColor(x, y, DColor((int)(red / 257UL), (int)(green / 257UL), + (int)(blue / 257UL), (int)(alpha / 257UL), + m_destImage.sixteenBit())); + } + + progress = (int)(((double)y * 100.0) / m_destImage.height()); + if ( progress%5 == 0 ) + postProgress( progress ); + } + + delete [] normal_kernel; + return(true); +} + +int DImgSharpen::getOptimalKernelWidth(double radius, double sigma) +{ + double normalize, value; + long kernelWidth; + long u; + + if(radius > 0.0) + return((int)(2.0*ceil(radius)+1.0)); + + for(kernelWidth=5; ;) + { + normalize=0.0; + + for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++) + normalize += exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma); + + u = kernelWidth/2; + value = exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma)/normalize; + + if((long)(65535*value) <= 0) + break; + + kernelWidth+=2; + } + + return((int)kernelWidth-2); +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/dimgsharpen.h b/src/libs/dimg/filters/dimgsharpen.h new file mode 100644 index 00000000..5802769a --- /dev/null +++ b/src/libs/dimg/filters/dimgsharpen.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-17-07 + * Description : A Sharpen threaded image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGSHARPEN_H +#define DIMGSHARPEN_H + +// Digikam includes. + +#include "digikam_export.h" + +// Local includes. + +#include "dimgthreadedfilter.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DImgSharpen : public DImgThreadedFilter +{ + +public: + + DImgSharpen(DImg *orgImage, TQObject *parent=0, double radius=0.0, double sigma=1.0); + + // Constructor for slave mode: execute immediately in current thread with specified master filter + DImgSharpen(DImgThreadedFilter *parentFilter, const DImg &orgImage, const DImg &destImage, + int progressBegin=0, int progressEnd=100, double radius=0.0, double sigma=1.0); + + ~DImgSharpen(){}; + +private: // DImgSharpen filter data. + + double m_radius; + double m_sigma; + +private: // DImgSharpen filter methods. + + virtual void filterImage(void); + + void sharpenImage(double radius, double sigma); + + bool convolveImage(const unsigned int order, const double *kernel); + + int getOptimalKernelWidth(double radius, double sigma); +}; + +} // NameSpace Digikam + +#endif /* DIMGSHARPEN_H */ diff --git a/src/libs/dimg/filters/dimgthreadedfilter.cpp b/src/libs/dimg/filters/dimgthreadedfilter.cpp new file mode 100644 index 00000000..205405e8 --- /dev/null +++ b/src/libs/dimg/filters/dimgthreadedfilter.cpp @@ -0,0 +1,170 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : threaded image filter class. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimgthreadedfilter.h" + +namespace Digikam +{ + +DImgThreadedFilter::DImgThreadedFilter(DImg *orgImage, TQObject *parent, + const TQString& name) + : TQThread() +{ + // remove meta data + m_orgImage = orgImage->copyImageData(); + m_parent = parent; + m_cancel = false; + + // See B.K.O #133026: make a deep copy of Qstring to prevent crash + // on Hyperthreading computer. + m_name = TQDeepCopy(name); + + m_master = 0; + m_slave = 0; + m_progressBegin = 0; + m_progressSpan = 100; +} + +DImgThreadedFilter::DImgThreadedFilter(DImgThreadedFilter *master, const DImg &orgImage, + const DImg &destImage, int progressBegin, int progressEnd, + const TQString& name) +{ + m_orgImage = orgImage; + m_destImage = destImage; + m_parent = 0; + m_cancel = false; + + // See B.K.O #133026: make a deep copy of Qstring to prevent crash + // on Hyperthreading computer. + m_name = TQDeepCopy(name); + + m_master = master; + m_slave = 0; + m_progressBegin = progressBegin; + m_progressSpan = progressEnd - progressBegin; + + m_master->setSlave(this); +} + +DImgThreadedFilter::~DImgThreadedFilter() +{ + stopComputation(); + if (m_master) + m_master->setSlave(0); +} + +void DImgThreadedFilter::initFilter(void) +{ + m_destImage.reset(); + m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + + if (m_orgImage.width() && m_orgImage.height()) + { + if (m_parent) + start(); // m_parent is valide, start thread ==> run() + else + startComputation(); // no parent : no using thread. + } + else // No image data + { + if (m_parent) // If parent then send event about a problem. + { + postProgress(0, false, false); + DDebug() << m_name << "::No valid image data !!! ..." << endl; + } + } +} + +void DImgThreadedFilter::stopComputation(void) +{ + m_cancel = true; + if (m_slave) + { + m_slave->m_cancel = true; + // do not wait on slave, it is not running in its own separate thread! + //m_slave->cleanupFilter(); + } + wait(); + cleanupFilter(); +} + +void DImgThreadedFilter::postProgress(int progress, bool starting, bool success) +{ + if (m_master) + { + progress = modulateProgress(progress); + m_master->postProgress(progress, starting, success); + } + else if (m_parent) + { + EventData *eventData = new EventData(); + eventData->progress = progress; + eventData->starting = starting; + eventData->success = success; + TQApplication::postEvent(m_parent, new TQCustomEvent(TQEvent::User, eventData)); + } +} + +void DImgThreadedFilter::startComputation() +{ + // See B.K.O #133026: do not use kdDebug() statements in threaded implementation + // to prevent crash under Hyperthreaded CPU. + + if (m_parent) + postProgress(0, true, false); + + filterImage(); + + if (!m_cancel) + { + if (m_parent) + postProgress(0, false, true); + } + else + { + if (m_parent) + postProgress(0, false, false); + } +} + +void DImgThreadedFilter::setSlave(DImgThreadedFilter *slave) +{ + m_slave = slave; +} + +int DImgThreadedFilter::modulateProgress(int progress) +{ + return m_progressBegin + (int)((double)progress * (double)m_progressSpan / 100.0); +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/dimgthreadedfilter.h b/src/libs/dimg/filters/dimgthreadedfilter.h new file mode 100644 index 00000000..1d4a97b8 --- /dev/null +++ b/src/libs/dimg/filters/dimgthreadedfilter.h @@ -0,0 +1,153 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-05-25 + * Description : threaded image filter class. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGTHREADEDFILTER_H +#define DIMGTHREADEDFILTER_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "digikam_export.h" + +class TQObject; + +namespace Digikam +{ + +class DIGIKAM_EXPORT DImgThreadedFilter : public TQThread +{ + +public: + +/** Class used to post status of computation to parent. */ +class EventData +{ + public: + + EventData() + { + starting = false; + success = false; + } + + bool starting; + bool success; + int progress; +}; + +public: + + DImgThreadedFilter(DImg *orgImage, TQObject *parent=0, + const TQString& name=TQString()); + + ~DImgThreadedFilter(); + + DImg getTargetImage(void) { return m_destImage; }; + + virtual void startComputation(void); + virtual void stopComputation(void); + + const TQString &filterName() { return m_name; }; + +protected: + + /** Start filter operation before threaded method. Must be calls by your constructor. */ + virtual void initFilter(void); + + /** List of threaded operations by filter. */ + virtual void run(){ startComputation(); }; + + /** Main image filter method. */ + virtual void filterImage(void){}; + + /** Clean up filter data if necessary. Call by stopComputation() method. */ + virtual void cleanupFilter(void){}; + + /** Post Event to parent about progress. Warning: you need to delete + 'EventData' instance to 'customEvent' parent implementation. */ + void postProgress(int progress=0, bool starting=true, bool success=false); + +protected: + + /** + Support for chaining two filters as master and thread. + + Constructor for slave mode: + Constructs a new slave filter with the specified master. + The filter will be executed in the current thread. + orgImage and destImage will not be copied. + progressBegin and progressEnd can indicate the progress span + that the slave filter uses in the parent filter's progress. + Any derived filter class that is publicly available to other filters + should implement an additional constructor using this constructor. + */ + DImgThreadedFilter(DImgThreadedFilter *master, const DImg &orgImage, const DImg &destImage, + int progressBegin=0, int progressEnd=100, const TQString& name=TQString()); + + /** Inform the master that there is currently a slave. At destruction of the slave, call with slave=0. */ + void setSlave(DImgThreadedFilter *slave); + + /** This method modulates the progress value from the 0..100 span to the span of this slave. + Called by postProgress if master is not null. */ + virtual int modulateProgress(int progress); + +protected: + + /** Used to stop compution loop. */ + bool m_cancel; + + /** The progress span that a slave filter uses in the parent filter's progress. */ + int m_progressBegin; + int m_progressSpan; + + /** To post event from thread to parent. */ + TQObject *m_parent; + + /** Filter name.*/ + TQString m_name; + + /** Copy of original Image data. */ + DImg m_orgImage; + + /** Output image data. */ + DImg m_destImage; + + /** The current slave. Any filter might want to use another filter while processing. */ + DImgThreadedFilter *m_slave; + + /** The master of this slave filter. Progress info will be routed to this one. */ + DImgThreadedFilter *m_master; +}; + +} // NameSpace Digikam + +#endif /* DIMGTHREADEDFILTER_H */ diff --git a/src/libs/dimg/filters/hslmodifier.cpp b/src/libs/dimg/filters/hslmodifier.cpp new file mode 100644 index 00000000..4f924d76 --- /dev/null +++ b/src/libs/dimg/filters/hslmodifier.cpp @@ -0,0 +1,240 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-06 + * Description : Hue/Saturation/Lightness image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x))) +#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0) +#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0) + +// C++ includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dcolor.h" +#include "dimg.h" +#include "hslmodifier.h" + +namespace Digikam +{ + +class HSLModifierPriv +{ +public: + + HSLModifierPriv() + { + modified = false; + } + + bool modified; + + int htransfer[256]; + int ltransfer[256]; + int stransfer[256]; + + int htransfer16[65536]; + int ltransfer16[65536]; + int stransfer16[65536]; +}; + +HSLModifier::HSLModifier() +{ + d = new HSLModifierPriv; + reset(); +} + +HSLModifier::~HSLModifier() +{ + delete d; +} + +bool HSLModifier::modified() const +{ + return d->modified; +} + +void HSLModifier::reset() +{ + // initialize to linear mapping + + for (int i=0; i<65536; i++) + { + d->htransfer16[i] = i; + d->ltransfer16[i] = i; + d->stransfer16[i] = i; + } + + for (int i=0; i<256; i++) + { + d->htransfer[i] = i; + d->ltransfer[i] = i; + d->stransfer[i] = i; + } + + d->modified = false; +} + +void HSLModifier::applyHSL(DImg& image) +{ + if (!d->modified || image.isNull()) + return; + + bool sixteenBit = image.sixteenBit(); + uint numberOfPixels = image.numPixels(); + + if (sixteenBit) // 16 bits image. + { + unsigned short* data = (unsigned short*) image.bits(); + + for (uint i=0; ihtransfer16[hue], d->stransfer16[sat], d->ltransfer16[lig], sixteenBit); + + data[2] = color.red(); + data[1] = color.green(); + data[0] = color.blue(); + + data += 4; + } + } + else // 8 bits image. + { + uchar* data = image.bits(); + + for (uint i=0; ihtransfer[hue], d->stransfer[sat], d->ltransfer[lig], sixteenBit); + + data[2] = color.red(); + data[1] = color.green(); + data[0] = color.blue(); + + data += 4; + } + } +} + +void HSLModifier::setHue(double val) +{ + int value; + + for (int i = 0; i < 65536; i++) + { + value = lround(val * 65535.0 / 360.0); + + if ((i + value) < 0) + d->htransfer16[i] = 65535 + (i + value); + else if ((i + value) > 65535) + d->htransfer16[i] = i + value - 65535; + else + d->htransfer16[i] = i + value; + } + + for (int i = 0; i < 256; i++) + { + value = lround(val * 255.0 / 360.0); + + if ((i + value) < 0) + d->htransfer[i] = 255 + (i + value); + else if ((i + value) > 255) + d->htransfer[i] = i + value - 255; + else + d->htransfer[i] = i + value; + } + + d->modified = true; +} + +void HSLModifier::setSaturation(double val) +{ + val = CLAMP(val, -100.0, 100.0); + int value; + + for (int i = 0; i < 65536; i++) + { + value = lround( (i * (100.0 + val)) / 100.0 ); + d->stransfer16[i] = CLAMP_0_65535(value); + } + + for (int i = 0; i < 256; i++) + { + value = lround( (i * (100.0 + val)) / 100.0 ); + d->stransfer[i] = CLAMP_0_255(value); + } + + d->modified = true; +} + +void HSLModifier::setLightness(double val) +{ + // val needs to be in that range so that the result is in the range 0..65535 + val = CLAMP(val, -100.0, 100.0); + + if (val < 0) + { + for (int i = 0; i < 65536; i++) + { + d->ltransfer16[i] = lround( (i * ( val + 100.0 )) / 100.0); + } + + for (int i = 0; i < 256; i++) + { + d->ltransfer[i] = lround( (i * ( val + 100.0 )) / 100.0); + } + } + else + { + for (int i = 0; i < 65536; i++) + { + d->ltransfer16[i] = lround( i * ( 1.0 - val / 100.0 ) + 65535.0 / 100.0 * val ); + } + + for (int i = 0; i < 256; i++) + { + d->ltransfer[i] = lround( i * ( 1.0 - val / 100.0 ) + 255.0 / 100.0 * val ); + } + } + + d->modified = true; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/hslmodifier.h b/src/libs/dimg/filters/hslmodifier.h new file mode 100644 index 00000000..02a1131d --- /dev/null +++ b/src/libs/dimg/filters/hslmodifier.h @@ -0,0 +1,58 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-03-06 + * Description : Hue/Saturation/Lightness image filter. + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * ============================================================ */ + +#ifndef HSLMODIFIER_H +#define HSLMODIFIER_H + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImg; +class HSLModifierPriv; + +class DIGIKAM_EXPORT HSLModifier +{ +public: + + HSLModifier(); + ~HSLModifier(); + + void reset(); + bool modified() const; + + void setHue(double val); + void setSaturation(double val); + void setLightness(double val); + void applyHSL(DImg& image); + +private: + + HSLModifierPriv* d; +}; + +} // NameSpace Digikam + +#endif /* HSLMODIFIER_H */ diff --git a/src/libs/dimg/filters/icctransform.cpp b/src/libs/dimg/filters/icctransform.cpp new file mode 100644 index 00000000..0a1324db --- /dev/null +++ b/src/libs/dimg/filters/icctransform.cpp @@ -0,0 +1,831 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-18 + * Description : a class to apply ICC color correction to image. + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#include + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include + +// Lcms includes. + +#include LCMS_HEADER +#if LCMS_VERSION < 114 +#define cmsTakeCopyright(profile) "Unknown" +#endif // LCMS_VERSION < 114 + +// Local includes. + +#include "ddebug.h" +#include "icctransform.h" + +namespace Digikam +{ + +class IccTransformPriv +{ +public: + + IccTransformPriv() + { + has_embedded_profile = false; + do_proof_profile = false; + } + + bool do_proof_profile; + bool has_embedded_profile; + + TQByteArray embedded_profile; + TQByteArray input_profile; + TQByteArray output_profile; + TQByteArray proof_profile; +}; + +IccTransform::IccTransform() +{ + d = new IccTransformPriv; + cmsErrorAction(LCMS_ERROR_SHOW); +} + +IccTransform::~IccTransform() +{ + delete d; +} + +bool IccTransform::hasInputProfile() +{ + return !(d->input_profile.isEmpty()); +} + +bool IccTransform::hasOutputProfile() +{ + return !(d->output_profile.isEmpty()); +} + +TQByteArray IccTransform::embeddedProfile() const +{ + return d->embedded_profile; +} + +TQByteArray IccTransform::inputProfile() const +{ + return d->input_profile; +} + +TQByteArray IccTransform::outputProfile() const +{ + return d->output_profile; +} + +TQByteArray IccTransform::proofProfile() const +{ + return d->proof_profile; +} + +void IccTransform::getTransformType(bool do_proof_profile) +{ + if (do_proof_profile) + { + d->do_proof_profile = true; + } + else + { + d->do_proof_profile = false; + } +} + +void IccTransform::getEmbeddedProfile(const DImg& image) +{ + if (!image.getICCProfil().isNull()) + { + d->embedded_profile = image.getICCProfil(); + d->has_embedded_profile = true; + } +} + +TQString IccTransform::getProfileDescription(const TQString& profile) +{ + cmsHPROFILE _profile = cmsOpenProfileFromFile(TQFile::encodeName(profile), "r"); + TQString _description = cmsTakeProductDesc(_profile); + cmsCloseProfile(_profile); + return _description; +} + +int IccTransform::getRenderingIntent() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + return config->readNumEntry("RenderingIntent", 0); +} + +bool IccTransform::getUseBPC() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Color Management"); + return config->readBoolEntry("BPCAlgorithm", false); +} + +TQByteArray IccTransform::loadICCProfilFile(const TQString& filePath) +{ + TQFile file(filePath); + if ( !file.open(IO_ReadOnly) ) + return TQByteArray(); + + TQByteArray data(file.size()); + TQDataStream stream( &file ); + stream.readRawBytes(data.data(), data.size()); + file.close(); + return data; +} + +void IccTransform::setProfiles(const TQString& input_profile, const TQString& output_profile) +{ + d->input_profile = loadICCProfilFile(input_profile); + d->output_profile = loadICCProfilFile(output_profile); +} + +void IccTransform::setProfiles(const TQString& input_profile, const TQString& output_profile, + const TQString& proof_profile) +{ + d->input_profile = loadICCProfilFile(input_profile); + d->output_profile = loadICCProfilFile(output_profile); + d->proof_profile = loadICCProfilFile(proof_profile); +} + +void IccTransform::setProfiles(const TQString& output_profile) +{ + d->output_profile = loadICCProfilFile(output_profile); +} + +void IccTransform::setProfiles(const TQString& output_profile, const TQString& proof_profile, bool forProof) +{ + if (forProof) + { + d->output_profile = loadICCProfilFile(output_profile); + d->proof_profile = loadICCProfilFile(proof_profile); + } +} + +TQString IccTransform::getEmbeddedProfileDescriptor() +{ + if (d->embedded_profile.isEmpty()) + return TQString(); + + cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->embedded_profile.data(), + (DWORD)d->embedded_profile.size()); + TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile)); + cmsCloseProfile(tmpProfile); + return embeddedProfileDescriptor; +} + +TQString IccTransform::getInputProfileDescriptor() +{ + if (d->input_profile.isEmpty()) return TQString(); + cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->input_profile.data(), (DWORD)d->input_profile.size()); + TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile)); + cmsCloseProfile(tmpProfile); + return embeddedProfileDescriptor; +} + +TQString IccTransform::getOutpoutProfileDescriptor() +{ + if (d->output_profile.isEmpty()) return TQString(); + cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->output_profile.data(), (DWORD)d->output_profile.size()); + TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile)); + cmsCloseProfile(tmpProfile); + return embeddedProfileDescriptor; +} + +TQString IccTransform::getProofProfileDescriptor() +{ + if (d->proof_profile.isEmpty()) return TQString(); + cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->proof_profile.data(), (DWORD)d->proof_profile.size()); + TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile)); + cmsCloseProfile(tmpProfile); + return embeddedProfileDescriptor; +} + +bool IccTransform::apply(DImg& image) +{ + cmsHPROFILE inprofile=0, outprofile=0, proofprofile=0; + cmsHTRANSFORM transform; + int inputFormat = 0; + int intent = INTENT_PERCEPTUAL; + + switch (getRenderingIntent()) + { + case 0: + intent = INTENT_PERCEPTUAL; + break; + case 1: + intent = INTENT_RELATIVE_COLORIMETRIC; + break; + case 2: + intent = INTENT_SATURATION; + break; + case 3: + intent = INTENT_ABSOLUTE_COLORIMETRIC; + break; + } + + //DDebug() << "Intent is: " << intent << endl; + + if (d->has_embedded_profile) + { + inprofile = cmsOpenProfileFromMem(d->embedded_profile.data(), + (DWORD)d->embedded_profile.size()); + } + else + { + inprofile = cmsOpenProfileFromMem(d->input_profile.data(), + (DWORD)d->input_profile.size()); + } + if (inprofile == NULL) + { + DDebug() << "Error: Input profile is NULL" << endl; + cmsCloseProfile(inprofile); + return false; + } + +// if (d->has_embedded_profile) +// { +// outprofile = cmsOpenProfileFromMem(d->embedded_profile.data(), +// (DWORD)d->embedded_profile.size()); +// } +// else +// { + outprofile = cmsOpenProfileFromMem(d->output_profile.data(), + (DWORD)d->output_profile.size()); +// } + + if (outprofile == NULL) + { + DDebug() << "Error: Output profile is NULL" << endl; + cmsCloseProfile(outprofile); + return false; + } + + if (!d->do_proof_profile) + { + if (image.sixteenBit()) + { + if (image.hasAlpha()) + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAYA_16; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_16; + break; + default: + inputFormat = TYPE_BGRA_16; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGRA_16, + intent, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAY_16; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_16; + break; + default: + inputFormat = TYPE_BGR_16; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGR_16, + intent, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + else + { + if (image.hasAlpha()) + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAYA_8; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_8; + break; + default: + inputFormat = TYPE_BGRA_8; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGRA_8, + intent, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAYA_8; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_8; + //DDebug() << "input profile: cmyk no alpha" << endl; + break; + default: + inputFormat = TYPE_BGR_8; + //DDebug() << "input profile: default no alpha" << endl; + } + + transform = cmsCreateTransform(inprofile, inputFormat, outprofile, + TYPE_BGR_8, intent, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + } + else + { + proofprofile = cmsOpenProfileFromMem(d->proof_profile.data(), + (DWORD)d->proof_profile.size()); + + if (proofprofile == NULL) + { + DDebug() << "Error: Input profile is NULL" << endl; + cmsCloseProfile(inprofile); + cmsCloseProfile(outprofile); + return false; + } + + if (image.sixteenBit()) + { + if (image.hasAlpha()) + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGRA_16, + outprofile, + TYPE_BGRA_16, + proofprofile, + INTENT_ABSOLUTE_COLORIMETRIC, + INTENT_ABSOLUTE_COLORIMETRIC, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_16, + outprofile, + TYPE_BGR_16, + proofprofile, + INTENT_ABSOLUTE_COLORIMETRIC, + INTENT_ABSOLUTE_COLORIMETRIC, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + else + { + if (image.hasAlpha()) + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_8, + outprofile, + TYPE_BGR_8, + proofprofile, + INTENT_ABSOLUTE_COLORIMETRIC, + INTENT_ABSOLUTE_COLORIMETRIC, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_8, + outprofile, + TYPE_BGR_8, + proofprofile, + INTENT_ABSOLUTE_COLORIMETRIC, + INTENT_ABSOLUTE_COLORIMETRIC, + cmsFLAGS_WHITEBLACKCOMPENSATION); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + } + + // We need to work using temp pixel buffer to apply ICC transformations. + uchar transdata[image.bytesDepth()]; + + // Always working with uchar* prevent endianess problem. + uchar *data = image.bits(); + + // We scan all image pixels one by one. + for (uint i=0; i < image.width()*image.height()*image.bytesDepth(); i+=image.bytesDepth()) + { + // Apply ICC transformations. + cmsDoTransform( transform, &data[i], &transdata[0], 1); + + // Copy buffer to source to update original image with ICC corrections. + // Alpha channel is restored in all cases. + memcpy (&data[i], &transdata[0], (image.bytesDepth() == 8) ? 6 : 3); + } + + cmsDeleteTransform(transform); + cmsCloseProfile(inprofile); + cmsCloseProfile(outprofile); + + if (d->do_proof_profile) + cmsCloseProfile(proofprofile); + + return true; +} + +bool IccTransform::apply( DImg& image, TQByteArray& profile, int intent, bool useBPC, + bool checkGamut, bool useBuiltin ) +{ + cmsHPROFILE inprofile=0, outprofile=0, proofprofile=0; + cmsHTRANSFORM transform; + int transformFlags = 0, inputFormat = 0; + + switch (intent) + { + case 0: + intent = INTENT_PERCEPTUAL; + break; + case 1: + intent = INTENT_RELATIVE_COLORIMETRIC; + break; + case 2: + intent = INTENT_SATURATION; + break; + case 3: + intent = INTENT_ABSOLUTE_COLORIMETRIC; + break; + } + + //DDebug() << "Intent is: " << intent << endl; + + if (!profile.isNull()) + { + inprofile = cmsOpenProfileFromMem(profile.data(), + (DWORD)profile.size()); + } + else if (useBuiltin) + { + inprofile = cmsCreate_sRGBProfile(); + } + else + { + inprofile = cmsOpenProfileFromMem(d->input_profile.data(), + (DWORD)d->input_profile.size()); + } + + if (inprofile == NULL) + { + DDebug() << "Error: Input profile is NULL" << endl; + return false; + } + + outprofile = cmsOpenProfileFromMem(d->output_profile.data(), + (DWORD)d->output_profile.size()); + + if (outprofile == NULL) + { + DDebug() << "Error: Output profile is NULL" << endl; + cmsCloseProfile(inprofile); + return false; + } + + if (useBPC) + { + transformFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; + } + + if (!d->do_proof_profile) + { + if (image.sixteenBit()) + { + if (image.hasAlpha()) + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAYA_16; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_16; + break; + default: + inputFormat = TYPE_BGRA_16; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGRA_16, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAY_16; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_16; + break; + default: + inputFormat = TYPE_BGR_16; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGR_16, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + else + { + if (image.hasAlpha()) + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAYA_8; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_8; + break; + default: + inputFormat = TYPE_BGRA_8; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGRA_8, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + switch (cmsGetColorSpace(inprofile)) + { + case icSigGrayData: + inputFormat = TYPE_GRAY_8; + break; + case icSigCmykData: + inputFormat = TYPE_CMYK_8; + break; + default: + inputFormat = TYPE_BGR_8; + } + + transform = cmsCreateTransform( inprofile, + inputFormat, + outprofile, + TYPE_BGR_8, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + } + else + { + proofprofile = cmsOpenProfileFromMem(d->proof_profile.data(), + (DWORD)d->proof_profile.size()); + + if (proofprofile == NULL) + { + DDebug() << "Error: Input profile is NULL" << endl; + cmsCloseProfile(inprofile); + cmsCloseProfile(outprofile); + return false; + } + + transformFlags |= cmsFLAGS_SOFTPROOFING; + if (checkGamut) + { + cmsSetAlarmCodes(126, 255, 255); + transformFlags |= cmsFLAGS_GAMUTCHECK; + } + + if (image.sixteenBit()) + { + if (image.hasAlpha()) + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGRA_16, + outprofile, + TYPE_BGRA_16, + proofprofile, + intent, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_16, + outprofile, + TYPE_BGR_16, + proofprofile, + intent, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + else + { + if (image.hasAlpha()) + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_8, + outprofile, + TYPE_BGR_8, + proofprofile, + intent, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + else + { + transform = cmsCreateProofingTransform( inprofile, + TYPE_BGR_8, + outprofile, + TYPE_BGR_8, + proofprofile, + intent, + intent, + transformFlags); + + if (!transform) + { + DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl; + return false; + } + } + } + } + + //DDebug() << "Transform flags are: " << transformFlags << endl; + + // We need to work using temp pixel buffer to apply ICC transformations. + uchar transdata[image.bytesDepth()]; + + // Always working with uchar* prevent endianess problem. + uchar *data = image.bits(); + + // We scan all image pixels one by one. + for (uint i=0; i < image.width()*image.height()*image.bytesDepth(); i+=image.bytesDepth()) + { + // Apply ICC transformations. + cmsDoTransform( transform, &data[i], &transdata[0], 1); + + // Copy buffer to source to update original image with ICC corrections. + // Alpha channel is restored in all cases. + memcpy (&data[i], &transdata[0], (image.bytesDepth() == 8) ? 6 : 3); + } + + cmsDeleteTransform(transform); + cmsCloseProfile(inprofile); + cmsCloseProfile(outprofile); + + if (d->do_proof_profile) + cmsCloseProfile(proofprofile); + + return true; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/filters/icctransform.h b/src/libs/dimg/filters/icctransform.h new file mode 100644 index 00000000..0590c44f --- /dev/null +++ b/src/libs/dimg/filters/icctransform.h @@ -0,0 +1,94 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-18 + * Description : a class to apply ICC color correction to image. + * + * Copyright (C) 2005-2006 by F.J. Cruz + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef ICCTRANSFORM_H +#define ICCTRANSFORM_H + +// TQt includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class IccTransformPriv; + +class DIGIKAM_EXPORT IccTransform +{ +public: + + IccTransform(); + ~IccTransform(); + + bool apply(DImg& image); + bool apply(DImg& image, TQByteArray& profile, int intent, + bool useBPC=false, bool checkGamut=false, bool useBuiltin=false); + + void getTransformType(bool do_proof_profile); + void getEmbeddedProfile(const DImg& image); + int getRenderingIntent(); + bool getUseBPC(); + + bool hasInputProfile(); + bool hasOutputProfile(); + + TQByteArray embeddedProfile() const; + TQByteArray inputProfile() const; + TQByteArray outputProfile() const; + TQByteArray proofProfile() const; + + /** Input profile from file methods */ + void setProfiles(const TQString& input_profile, const TQString& output_profile); + void setProfiles(const TQString& input_profile, const TQString& output_profile, const TQString& proof_profile); + + /** Embedded input profile methods */ + void setProfiles(const TQString& output_profile); + void setProfiles(const TQString& output_profile, const TQString& proof_profile, bool forProof); + + /** Profile info methods */ + TQString getProfileDescription(const TQString& profile); + + TQString getEmbeddedProfileDescriptor(); + TQString getInputProfileDescriptor(); + TQString getOutpoutProfileDescriptor(); + TQString getProofProfileDescriptor(); + +private: + + TQByteArray loadICCProfilFile(const TQString& filePath); + +private: + + IccTransformPriv* d; + +}; + +} // NameSpace Digikam + +#endif // ICCTRANSFORM_H diff --git a/src/libs/dimg/loaders/Makefile.am b/src/libs/dimg/loaders/Makefile.am new file mode 100644 index 00000000..62e96fe8 --- /dev/null +++ b/src/libs/dimg/loaders/Makefile.am @@ -0,0 +1,21 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdimgloaders.la + +libdimgloaders_la_SOURCES = dimgloader.cpp pngloader.cpp jpegloader.cpp tiffloader.cpp \ + rawloader.cpp ppmloader.cpp qimageloader.cpp iccjpeg.c \ + jp2kloader.cpp jpegsettings.cpp pngsettings.cpp \ + tiffsettings.cpp jp2ksettings.cpp + +libdimgloaders_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) \ + $(LIBJPEG) $(LIB_TIFF) $(LIB_PNG) $(LIB_JASPER) + +INCLUDES = $(all_includes) -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/libs/curves \ + -I$(top_srcdir)/src/libs/levels \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/whitebalance \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/digikam \ + $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS) diff --git a/src/libs/dimg/loaders/README b/src/libs/dimg/loaders/README new file mode 100644 index 00000000..b77e9c4c --- /dev/null +++ b/src/libs/dimg/loaders/README @@ -0,0 +1,42 @@ +--------------------------------------------------------------------------- + +Native DIMG Loaders status (Gilles Caulier - 2006-11-29) + +Format Read Write ICC MetaData Thumb 8bits 16bits depency Remarks + +JPG Done Done Done Done TODO yes N.A libjpeg Metadata are EXIF/IPTC/XMP/ICC profil +PNG Done Done Done Done N.A yes yes libpng Metadata are EXIF/IPTC/XMP/ICC profil +TIF/EP Done Done Done Done TODO yes yes libtiff Metadata are EXIF/IPTC/XMP/ICC profil +RAW Done N.A N.A Done Done yes yes dcraw Metadata are EXIF +PPM Done TODO N.A N.A N.A yes yes none +JPEG2K Done Done Done TODO N.A yes yes libjasper Metadata are EXIF/XMP/ICC profil + +Others file formats are supported only in 8 bits/color/pixel using TQImage/kimgio. +QT3.x + KDE 3.4.x support these formats : + +Format Read Write Remarks + +PSD yes no Photoshop file format +EXR yes no OpenEXR (libopenexr) +XCF yes no Gimp file format +PBM yes yes +PGM yes yes +PPM no yes +TGA yes yes +PCX yes yes +BMP yes yes Win32 bitmap format +RGB yes yes +XBM yes yes +XPM yes yes +EPS yes yes +DDS yes no +ICO yes no Win32 icon format +MNG yes no +GIF yes no + +--------------------------------------------------------------------------- + +TODO : + +Add PCD support using http://linux.bytesex.org/fbida/libpcd.html + diff --git a/src/libs/dimg/loaders/dimgloader.cpp b/src/libs/dimg/loaders/dimgloader.cpp new file mode 100644 index 00000000..615b11e1 --- /dev/null +++ b/src/libs/dimg/loaders/dimgloader.cpp @@ -0,0 +1,200 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : DImg image loader interface + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimgprivate.h" +#include "dmetadata.h" +#include "dimgloaderobserver.h" +#include "dimgloader.h" + +namespace Digikam +{ + +DImgLoader::DImgLoader(DImg* image) + : m_image(image) +{ +} + +int DImgLoader::granularity(DImgLoaderObserver *observer, int total, float progressSlice) +{ + // Splits expect total value into the chunks where checks shall occur + // and combines this with a possible correction factor from observer. + // Progress slice is the part of 100% concerned with the current granularity + // (E.g. in a loop only the values from 10% to 90% are used, then progressSlice is 0.8) + // Current default is 1/20, that is progress info every 5% + int granularity=0; + + if (observer) + granularity = (int)(( total / (20 * progressSlice)) / observer->granularity()); + + return granularity ? granularity : 1; +} + +unsigned char*& DImgLoader::imageData() +{ + return m_image->m_priv->data; +} + +unsigned int& DImgLoader::imageWidth() +{ + return m_image->m_priv->width; +} + +unsigned int& DImgLoader::imageHeight() +{ + return m_image->m_priv->height; +} + +bool DImgLoader::imageHasAlpha() +{ + return m_image->hasAlpha(); +} + +bool DImgLoader::imageSixteenBit() +{ + return m_image->sixteenBit(); +} + +int DImgLoader::imageBitsDepth() +{ + return m_image->bitsDepth(); +} + +int DImgLoader::imageBytesDepth() +{ + return m_image->bytesDepth(); +} + +TQMap& DImgLoader::imageMetaData() +{ + return m_image->m_priv->metaData; +} + +TQVariant DImgLoader::imageGetAttribute(const TQString& key) +{ + return m_image->attribute(key); +} + +TQString DImgLoader::imageGetEmbbededText(const TQString& key) +{ + return m_image->embeddedText(key); +} + +void DImgLoader::imageSetAttribute(const TQString& key, const TQVariant& value) +{ + m_image->setAttribute(key, value); +} + +TQMap& DImgLoader::imageEmbeddedText() +{ + return m_image->m_priv->embeddedText; +} + +void DImgLoader::imageSetEmbbededText(const TQString& key, const TQString& text) +{ + m_image->setEmbeddedText(key, text); +} + +bool DImgLoader::readMetadata(const TQString& filePath, DImg::FORMAT /*ff*/) +{ + TQMap& imageMetadata = imageMetaData(); + imageMetadata.clear(); + + DMetadata metaDataFromFile(filePath); + if (!metaDataFromFile.load(filePath)) + return false; + + // Do not insert null data into metaData map: + // Even if byte array is null, if there is a key in the map, it will + // be interpreted as "There was data, so write it again to the file". + if (!metaDataFromFile.getComments().isNull()) + imageMetadata.insert(DImg::COM, metaDataFromFile.getComments()); + if (!metaDataFromFile.getExif().isNull()) + imageMetadata.insert(DImg::EXIF, metaDataFromFile.getExif()); + if (!metaDataFromFile.getIptc().isNull()) + imageMetadata.insert(DImg::IPTC, metaDataFromFile.getIptc()); + + return true; +} + +bool DImgLoader::saveMetadata(const TQString& filePath) +{ + DMetadata metaDataToFile(filePath); + metaDataToFile.setComments(m_image->getComments()); + metaDataToFile.setExif(m_image->getExif()); + metaDataToFile.setIptc(m_image->getIptc()); + return metaDataToFile.applyChanges(); +} + +bool DImgLoader::checkExifWorkingColorSpace() +{ + DMetadata metaData; + metaData.setExif(m_image->getExif()); + + // Check if Exif data contains an ICC color profile. + TQByteArray profile = metaData.getExifTagData("Exif.Image.InterColorProfile"); + if (!profile.isNull()) + { + DDebug() << "Found an ICC profile in Exif metadata" << endl; + m_image->setICCProfil(profile); + return true; + } + + // Else check the Exif color-space tag and use a default profiles available in digiKam. + TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles"); + + switch(metaData.getImageColorWorkSpace()) + { + case DMetadata::WORKSPACE_SRGB: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "srgb-d65.icm"); + m_image->getICCProfilFromFile(directory + "srgb-d65.icm"); + DDebug() << "Exif color-space tag is sRGB. Using default sRGB ICC profile." << endl; + return true; + break; + } + + case DMetadata::WORKSPACE_ADOBERGB: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "adobergb.icm"); + m_image->getICCProfilFromFile(directory + "adobergb.icm"); + DDebug() << "Exif color-space tag is AdobeRGB. Using default AdobeRGB ICC profile." << endl; + return true; + break; + } + + default: + break; + } + + return false; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/dimgloader.h b/src/libs/dimg/loaders/dimgloader.h new file mode 100644 index 00000000..39025888 --- /dev/null +++ b/src/libs/dimg/loaders/dimgloader.h @@ -0,0 +1,97 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : DImg image loader interface + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGLOADER_H +#define DIMGLOADER_H + +// TQt includes. + +#include +#include +#include +#include + +// Local includes. + +#include "dimg.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DImgLoaderObserver; + +class DIGIKAM_EXPORT DImgLoader +{ +public: + + virtual ~DImgLoader() {}; + + virtual bool load(const TQString& filePath, DImgLoaderObserver *observer) = 0; + virtual bool save(const TQString& filePath, DImgLoaderObserver *observer) = 0; + + virtual bool hasAlpha() const = 0; + virtual bool sixteenBit() const = 0; + virtual bool isReadOnly() const = 0; + +protected: + + DImgLoader(DImg* image); + + unsigned char*& imageData(); + unsigned int& imageWidth(); + unsigned int& imageHeight(); + + bool imageHasAlpha(); + bool imageSixteenBit(); + + int imageBitsDepth(); + int imageBytesDepth(); + + TQMap& imageMetaData(); + TQVariant imageGetAttribute(const TQString& key); + void imageSetAttribute(const TQString& key, const TQVariant& value); + + TQMap& imageEmbeddedText(); + TQString imageGetEmbbededText(const TQString& key); + void imageSetEmbbededText(const TQString& key, const TQString& text); + + virtual bool readMetadata(const TQString& filePath, DImg::FORMAT ff); + virtual bool saveMetadata(const TQString& filePath); + virtual int granularity(DImgLoaderObserver *observer, int total, float progressSlice = 1.0); + + bool checkExifWorkingColorSpace(); + +protected: + + DImg *m_image; + +private: + + DImgLoader(); +}; + +} // NameSpace Digikam + +#endif /* DIMGLOADER_H */ diff --git a/src/libs/dimg/loaders/dimgloaderobserver.h b/src/libs/dimg/loaders/dimgloaderobserver.h new file mode 100644 index 00000000..ea83bede --- /dev/null +++ b/src/libs/dimg/loaders/dimgloaderobserver.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-03 + * Description : DImgLoader observer interface + * + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DIMGLOADEROBSERVER_H +#define DIMGLOADEROBSERVER_H + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DImg; + +class DIGIKAM_EXPORT DImgLoaderObserver +{ + +public: + // posts progress information about image IO + virtual void progressInfo(const DImg *, float /*progress*/) + {}; + + // queries whether the image IO operation shall be continued + virtual bool continueQuery(const DImg *) + { return true; }; + + // Return a relative value which determines the granularity, the frequency + // with which the DImgLoaderObserver is checked and progress is posted. + // Standard is 1.0. Values < 1 mean less granularity (fewer checks), + // values > 1 mean higher granularity (more checks). + virtual float granularity() + { return 1.0; }; + + // This is a hack needed to prevent hanging when a TDEProcess-based loader (raw loader) + // is waiting for the process to finish, but the main thread is waiting + // for the thread to finish and no TDEProcess events are delivered. + // Remove when porting to TQt4. + virtual bool isShuttingDown() + { return false; } + + virtual ~DImgLoaderObserver(){}; +}; + +} // namespace Digikam + +#endif // DIMGLOADEROBSERVER_H diff --git a/src/libs/dimg/loaders/iccjpeg.c b/src/libs/dimg/loaders/iccjpeg.c new file mode 100644 index 00000000..fefa9509 --- /dev/null +++ b/src/libs/dimg/loaders/iccjpeg.c @@ -0,0 +1,270 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * iccprofile.c + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. If you need to do that, + * change all the "unsigned int" variables to "INT32". You'll also need + * to find a malloc() replacement that can allocate more than 64K. + */ + +#include "iccjpeg.h" +#include /* define malloc() */ + + +/* + * Since an ICC profile can be larger than the maximum size of a JPEG marker + * (64K), we need provisions to split it into multiple markers. The format + * defined by the ICC specifies one or more APP2 markers containing the + * following data: + * Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte) + * Number of markers Total number of APP2's used (1 byte) + * Profile data (remainder of APP2 data) + * Decoders should use the marker sequence numbers to reassemble the profile, + * rather than assuming that the APP2 markers appear in the correct sequence. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +void +write_icc_profile (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER; + if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) + num_markers++; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + + +/* + * Prepare for reading an ICC profile + */ + +void +setup_read_icc_profile (j_decompress_ptr cinfo) +{ + /* Tell the library to keep any APP2 data it may find */ + jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); +} + + +/* + * Handy subroutine to test whether a saved marker is an ICC profile marker. + */ + +static boolean +marker_is_icc (jpeg_saved_marker_ptr marker) +{ + return + marker->marker == ICC_MARKER && + marker->data_length >= ICC_OVERHEAD_LEN && + /* verify the identifying string */ + GETJOCTET(marker->data[0]) == 0x49 && + GETJOCTET(marker->data[1]) == 0x43 && + GETJOCTET(marker->data[2]) == 0x43 && + GETJOCTET(marker->data[3]) == 0x5F && + GETJOCTET(marker->data[4]) == 0x50 && + GETJOCTET(marker->data[5]) == 0x52 && + GETJOCTET(marker->data[6]) == 0x4F && + GETJOCTET(marker->data[7]) == 0x46 && + GETJOCTET(marker->data[8]) == 0x49 && + GETJOCTET(marker->data[9]) == 0x4C && + GETJOCTET(marker->data[10]) == 0x45 && + GETJOCTET(marker->data[11]) == 0x0; +} + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + * + * NOTE: if the file contains invalid ICC APP2 markers, we just silently + * return FALSE. You might want to issue an error message instead. + */ + +boolean +read_icc_profile (j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len) +{ + jpeg_saved_marker_ptr marker; + int num_markers = 0; + int seq_no; + JOCTET *icc_data; + unsigned int total_length; +#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ + char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ + unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ + unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ + + *icc_data_ptr = NULL; /* avoid confusion if FALSE return */ + *icc_data_len = 0; + + /* This first pass over the saved markers discovers whether there are + * any ICC markers and verifies the consistency of the marker numbering. + */ + + for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) + marker_present[seq_no] = 0; + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + if (num_markers == 0) + num_markers = GETJOCTET(marker->data[13]); + else if (num_markers != GETJOCTET(marker->data[13])) + return FALSE; /* inconsistent num_markers fields */ + seq_no = GETJOCTET(marker->data[12]); + if (seq_no <= 0 || seq_no > num_markers) + return FALSE; /* bogus sequence number */ + if (marker_present[seq_no]) + return FALSE; /* duplicate sequence numbers */ + marker_present[seq_no] = 1; + data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; + } + } + + if (num_markers == 0) + return FALSE; + + /* Check for missing markers, count total space needed, + * compute offset of each marker's part of the data. + */ + + total_length = 0; + for (seq_no = 1; seq_no <= num_markers; seq_no++) { + if (marker_present[seq_no] == 0) + return FALSE; /* missing sequence number */ + data_offset[seq_no] = total_length; + total_length += data_length[seq_no]; + } + + if (total_length <= 0) + return FALSE; /* found only empty markers? */ + + /* Allocate space for assembled data */ + icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); + if (icc_data == NULL) + return FALSE; /* oops, out of memory */ + + /* and fill it in */ + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + JOCTET FAR *src_ptr; + JOCTET *dst_ptr; + unsigned int length; + seq_no = GETJOCTET(marker->data[12]); + dst_ptr = icc_data + data_offset[seq_no]; + src_ptr = marker->data + ICC_OVERHEAD_LEN; + length = data_length[seq_no]; + while (length--) { + *dst_ptr++ = *src_ptr++; + } + } + } + + *icc_data_ptr = icc_data; + *icc_data_len = total_length; + + return TRUE; +} diff --git a/src/libs/dimg/loaders/iccjpeg.h b/src/libs/dimg/loaders/iccjpeg.h new file mode 100644 index 00000000..2aab4196 --- /dev/null +++ b/src/libs/dimg/loaders/iccjpeg.h @@ -0,0 +1,101 @@ +/* + * Little cms + * Copyright (C) 1998-2004 Marti Maria + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * iccprofile.h + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. See iccprofile.c + * for details. + */ + +#ifndef ICCJPEG_H +#define ICCJPEG_H + +#include /* needed to define "FILE", "NULL" */ +#include + + +/** + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +extern void write_icc_profile JPP((j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len)); + + +/** + * Reading a JPEG file that may contain an ICC profile requires two steps: + * + * 1. After jpeg_create_decompress() but before jpeg_read_header(), + * call setup_read_icc_profile(). This routine tells the IJG library + * to save in memory any APP2 markers it may find in the file. + * + * 2. After jpeg_read_header(), call read_icc_profile() to find out + * whether there was a profile and obtain it if so. + */ + + +/** + * Prepare for reading an ICC profile + */ + +extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo)); + + +/** + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + */ + +extern boolean read_icc_profile JPP((j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len)); + +#endif /* ICCJPEG_H */ diff --git a/src/libs/dimg/loaders/jp2kloader.cpp b/src/libs/dimg/loaders/jp2kloader.cpp new file mode 100644 index 00000000..66351c25 --- /dev/null +++ b/src/libs/dimg/loaders/jp2kloader.cpp @@ -0,0 +1,715 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-14 + * Description : A JPEG2000 IO file for DImg framework + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This implementation use Jasper API + * library : http://www.ece.uvic.ca/~mdadams/jasper + * Other JPEG2000 encoder-decoder : http://www.openjpeg.org + * + * Others Linux JPEG2000 Loader implementation using Jasper: + * http://cvs.graphicsmagick.org/cgi-bin/cvsweb.cgi/GraphicsMagick/coders/jp2.c + * https://subversion.imagemagick.org/subversion/ImageMagick/trunk/coders/jp2.c + * http://svn.ghostscript.com:8080/jasper/trunk/src/appl/jasper.c + * http://websvn.kde.org/trunk/KDE/tdelibs/kimgio/jp2.cpp + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// This line must be commented to prevent any latency time +// when we use threaded image loader interface for each image +// files io. Uncomment this line only for debugging. +//#define ENABLE_DEBUG_MESSAGES + +// C ANSI includes. + +extern "C" +{ +#if !defined(__STDC_LIMIT_MACROS) +#define __STDC_LIMIT_MACROS +#endif +#include +} + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "jp2kloader.h" + +namespace Digikam +{ + +JP2KLoader::JP2KLoader(DImg* image) + : DImgLoader(image) +{ + m_hasAlpha = false; + m_sixteenBit = false; +} + +bool JP2KLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + readMetadata(filePath, DImg::JPEG); + + FILE *file = fopen(TQFile::encodeName(filePath), "rb"); + if (!file) + return false; + + unsigned char header[9]; + + if (fread(&header, 9, 1, file) != 1) + { + fclose(file); + return false; + } + + unsigned char jp2ID[5] = { 0x6A, 0x50, 0x20, 0x20, 0x0D, }; + unsigned char jpcID[2] = { 0xFF, 0x4F }; + + if (memcmp(&header[4], &jp2ID, 5) != 0 && + memcmp(&header, &jpcID, 2) != 0) + { + // not a jpeg2000 file + fclose(file); + return false; + } + + fclose(file); + + // ------------------------------------------------------------------- + // Initialize JPEG 2000 API. + + long i, x, y; + int components[4]; + unsigned int maximum_component_depth, scale[4], x_step[4], y_step[4]; + unsigned long number_components; + + jas_image_t *jp2_image = 0; + jas_stream_t *jp2_stream = 0; + jas_matrix_t *pixels[4]; + + int init = jas_init(); + if (init != 0) + { + DDebug() << "Unable to init JPEG2000 decoder" << endl; + return false; + } + + jp2_stream = jas_stream_fopen(TQFile::encodeName(filePath), "rb"); + if (jp2_stream == 0) + { + DDebug() << "Unable to open JPEG2000 stream" << endl; + return false; + } + + jp2_image = jas_image_decode(jp2_stream, -1, 0); + if (jp2_image == 0) + { + jas_stream_close(jp2_stream); + DDebug() << "Unable to decode JPEG2000 image" << endl; + return false; + } + + jas_stream_close(jp2_stream); + + // some pseudo-progress + if (observer) + observer->progressInfo(m_image, 0.1); + + // ------------------------------------------------------------------- + // Check color space. + + switch (jas_clrspc_fam(jas_image_clrspc(jp2_image))) + { + case JAS_CLRSPC_FAM_RGB: + { + components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_R); + components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_G); + components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_B); + if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0)) + { + jas_image_destroy(jp2_image); + DDebug() << "Error parsing JPEG2000 image : Missing Image Channel" << endl; + return false; + } + + number_components = 3; + components[3] = jas_image_getcmptbytype(jp2_image, 3); + if (components[3] > 0) + { + m_hasAlpha = true; + number_components++; + } + break; + } + case JAS_CLRSPC_FAM_GRAY: + { + components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_GRAY_Y); + if (components[0] < 0) + { + jas_image_destroy(jp2_image); + DDebug() << "Error parsing JP2000 image : Missing Image Channel" << endl; + return false; + } + number_components=1; + break; + } + case JAS_CLRSPC_FAM_YCBCR: + { + components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_Y); + components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CB); + components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CR); + if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0)) + { + jas_image_destroy(jp2_image); + DDebug() << "Error parsing JP2000 image : Missing Image Channel" << endl; + return false; + } + number_components = 3; + components[3] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_UNKNOWN); + if (components[3] > 0) + { + m_hasAlpha = true; + number_components++; + } + // FIXME : image->colorspace=YCbCrColorspace; + break; + } + default: + { + jas_image_destroy(jp2_image); + DDebug() << "Error parsing JP2000 image : Colorspace Model Is Not Supported" << endl; + return false; + } + } + + // ------------------------------------------------------------------- + // Check image geometry. + + imageWidth() = jas_image_width(jp2_image); + imageHeight() = jas_image_height(jp2_image); + + for (i = 0; i < (long)number_components; i++) + { + if ((((jas_image_cmptwidth(jp2_image, components[i])* + jas_image_cmpthstep(jp2_image, components[i])) != (long)imageWidth())) || + (((jas_image_cmptheight(jp2_image, components[i])* + jas_image_cmptvstep(jp2_image, components[i])) != (long)imageHeight())) || + (jas_image_cmpttlx(jp2_image, components[i]) != 0) || + (jas_image_cmpttly(jp2_image, components[i]) != 0) || + (jas_image_cmptsgnd(jp2_image, components[i]) != false)) + { + jas_image_destroy(jp2_image); + DDebug() << "Error parsing JPEG2000 image : Irregular Channel Geometry Not Supported" << endl; + return false; + } + x_step[i] = jas_image_cmpthstep(jp2_image, components[i]); + y_step[i] = jas_image_cmptvstep(jp2_image, components[i]); + } + + // ------------------------------------------------------------------- + // Convert image data. + + m_hasAlpha = number_components > 3; + maximum_component_depth = 0; + + for (i = 0; i < (long)number_components; i++) + { + maximum_component_depth = TQMAX(jas_image_cmptprec(jp2_image,components[i]), + (long)maximum_component_depth); + pixels[i] = jas_matrix_create(1, ((unsigned int)imageWidth())/x_step[i]); + if (!pixels[i]) + { + jas_image_destroy(jp2_image); + DDebug() << "Error decoding JPEG2000 image data : Memory Allocation Failed" << endl; + return false; + } + } + + if (maximum_component_depth > 8) + m_sixteenBit = true; + + for (i = 0 ; i < (long)number_components ; i++) + { + scale[i] = 1; + int prec = jas_image_cmptprec(jp2_image, components[i]); + if (m_sixteenBit && prec < 16) + scale[i] = (1 << (16 - jas_image_cmptprec(jp2_image, components[i]))); + } + + uchar* data = 0; + if (m_sixteenBit) // 16 bits image. + data = new uchar[imageWidth()*imageHeight()*8]; + else + data = new uchar[imageWidth()*imageHeight()*4]; + + if (!data) + { + DDebug() << "Error decoding JPEG2000 image data : Memory Allocation Failed" << endl; + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + return false; + } + + uint checkPoint = 0; + uchar *dst = data; + unsigned short *dst16 = (unsigned short *)data; + + for (y = 0 ; y < (long)imageHeight() ; y++) + { + for (i = 0 ; i < (long)number_components; i++) + { + int ret = jas_image_readcmpt(jp2_image, (short)components[i], 0, + ((unsigned int) y) / y_step[i], + ((unsigned int) imageWidth()) / x_step[i], + 1, pixels[i]); + if (ret != 0) + { + DDebug() << "Error decoding JPEG2000 image data" << endl; + delete [] data; + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + return false; + } + } + + switch (number_components) + { + case 1: // Grayscale. + { + for (x = 0 ; x < (long)imageWidth() ; x++) + { + dst[0] = (uchar)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0])); + dst[1] = dst[0]; + dst[2] = dst[0]; + dst[3] = 0xFF; + + dst += 4; + } + break; + } + case 3: // RGB. + { + if (!m_sixteenBit) // 8 bits image. + { + for (x = 0 ; x < (long)imageWidth() ; x++) + { + // Blue + dst[0] = (uchar)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2])); + // Green + dst[1] = (uchar)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1])); + // Red + dst[2] = (uchar)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0])); + // Alpha + dst[3] = 0xFF; + + dst += 4; + } + } + else // 16 bits image. + { + for (x = 0 ; x < (long)imageWidth() ; x++) + { + // Blue + dst16[0] = (unsigned short)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2])); + // Green + dst16[1] = (unsigned short)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1])); + // Red + dst16[2] = (unsigned short)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0])); + // Alpha + dst16[3] = 0xFFFF; + + dst16 += 4; + } + } + + break; + } + case 4: // RGBA. + { + if (!m_sixteenBit) // 8 bits image. + { + for (x = 0 ; x < (long)imageWidth() ; x++) + { + // Blue + dst[0] = (uchar)(scale[2] * jas_matrix_getv(pixels[2], x/x_step[2])); + // Green + dst[1] = (uchar)(scale[1] * jas_matrix_getv(pixels[1], x/x_step[1])); + // Red + dst[2] = (uchar)(scale[0] * jas_matrix_getv(pixels[0], x/x_step[0])); + // Alpha + dst[3] = (uchar)(scale[3] * jas_matrix_getv(pixels[3], x/x_step[3])); + + dst += 4; + } + } + else // 16 bits image. + { + for (x = 0 ; x < (long)imageWidth() ; x++) + { + // Blue + dst16[0] = (unsigned short)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2])); + // Green + dst16[1] = (unsigned short)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1])); + // Red + dst16[2] = (unsigned short)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0])); + // Alpha + dst16[3] = (unsigned short)(scale[3]*jas_matrix_getv(pixels[3], x/x_step[3])); + + dst16 += 4; + } + } + + break; + } + } + + // use 0-10% and 90-100% for pseudo-progress + if (observer && y >= (long)checkPoint) + { + checkPoint += granularity(observer, y, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)imageHeight()) ))); + } + } + + // ------------------------------------------------------------------- + // Get ICC color profile. + + jas_iccprof_t *icc_profile = 0; + jas_stream_t *icc_stream = 0; + jas_cmprof_t *cm_profile = 0; + + cm_profile = jas_image_cmprof(jp2_image); + if (cm_profile != 0) + icc_profile = jas_iccprof_createfromcmprof(cm_profile); + + if (icc_profile != 0) + { + icc_stream = jas_stream_memopen(NULL, 0); + + if (icc_stream != 0) + { + if (jas_iccprof_save(icc_profile, icc_stream) == 0) + { + if (jas_stream_flush(icc_stream) == 0) + { + TQMap& metaData = imageMetaData(); + jas_stream_memobj_t *blob = (jas_stream_memobj_t *) icc_stream->obj_; + TQByteArray profile_rawdata(blob->len_); + memcpy(profile_rawdata.data(), blob->buf_, blob->len_); + metaData.insert(DImg::ICC, profile_rawdata); + jas_stream_close(icc_stream); + } + } + } + } + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageSetAttribute("format", "JP2K"); + imageData() = data; + + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + + return true; +} + +bool JP2KLoader::save(const TQString& filePath, DImgLoaderObserver *observer) +{ + FILE *file = fopen(TQFile::encodeName(filePath), "wb"); + if (!file) + return false; + + fclose(file); + + // ------------------------------------------------------------------- + // Initialize JPEG 2000 API. + + long i, x, y; + unsigned long number_components; + + jas_image_t *jp2_image = 0; + jas_stream_t *jp2_stream = 0; + jas_matrix_t *pixels[4]; + jas_image_cmptparm_t component_info[4]; + + int init = jas_init(); + if (init != 0) + { + DDebug() << "Unable to init JPEG2000 decoder" << endl; + return false; + } + + jp2_stream = jas_stream_fopen(TQFile::encodeName(filePath), "wb"); + if (jp2_stream == 0) + { + DDebug() << "Unable to open JPEG2000 stream" << endl; + return false; + } + + number_components = imageHasAlpha() ? 4 : 3; + + for (i = 0 ; i < (long)number_components ; i++) + { + component_info[i].tlx = 0; + component_info[i].tly = 0; + component_info[i].hstep = 1; + component_info[i].vstep = 1; + component_info[i].width = imageWidth(); + component_info[i].height = imageHeight(); + component_info[i].prec = imageBitsDepth(); + component_info[i].sgnd = false; + } + + jp2_image = jas_image_create(number_components, component_info, JAS_CLRSPC_UNKNOWN); + if (jp2_image == 0) + { + jas_stream_close(jp2_stream); + DDebug() << "Unable to create JPEG2000 image" << endl; + return false; + } + + if (observer) + observer->progressInfo(m_image, 0.1); + + // ------------------------------------------------------------------- + // Check color space. + + if (number_components >= 3 ) // RGB & RGBA + { + // Alpha Channel + if (number_components == 4 ) + jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_OPACITY); + + jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB); + jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R)); + jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G)); + jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B)); + } + + // ------------------------------------------------------------------- + // Set ICC color profile. + + // FIXME : doesn't work yet! + + jas_cmprof_t *cm_profile = 0; + jas_iccprof_t *icc_profile = 0; + + TQByteArray profile_rawdata = m_image->getICCProfil(); + + icc_profile = jas_iccprof_createfrombuf((uchar*)profile_rawdata.data(), profile_rawdata.size()); + if (icc_profile != 0) + { + cm_profile = jas_cmprof_createfromiccprof(icc_profile); + if (cm_profile != 0) + { + jas_image_setcmprof(jp2_image, cm_profile); + } + } + + // ------------------------------------------------------------------- + // Convert to JPEG 2000 pixels. + + for (i = 0 ; i < (long)number_components ; i++) + { + pixels[i] = jas_matrix_create(1, (unsigned int)imageWidth()); + if (pixels[i] == 0) + { + for (x = 0 ; x < i ; x++) + jas_matrix_destroy(pixels[x]); + + jas_image_destroy(jp2_image); + DDebug() << "Error encoding JPEG2000 image data : Memory Allocation Failed" << endl; + return false; + } + } + + unsigned char* data = imageData(); + unsigned char* pixel; + unsigned short r, g, b, a=0; + uint checkpoint = 0; + + for (y = 0 ; y < (long)imageHeight() ; y++) + { + if (observer && y == (long)checkpoint) + { + checkpoint += granularity(observer, imageHeight(), 0.8); + if (!observer->continueQuery(m_image)) + { + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)imageHeight()) ))); + } + + for (x = 0 ; x < (long)imageWidth() ; x++) + { + pixel = &data[((y * imageWidth()) + x) * imageBytesDepth()]; + + if ( imageSixteenBit() ) // 16 bits image. + { + b = (unsigned short)(pixel[0]+256*pixel[1]); + g = (unsigned short)(pixel[2]+256*pixel[3]); + r = (unsigned short)(pixel[4]+256*pixel[5]); + + if (imageHasAlpha()) + a = (unsigned short)(pixel[6]+256*pixel[7]); + } + else // 8 bits image. + { + b = (unsigned short)pixel[0]; + g = (unsigned short)pixel[1]; + r = (unsigned short)pixel[2]; + + if (imageHasAlpha()) + a = (unsigned short)(pixel[3]); + } + + jas_matrix_setv(pixels[0], x, r); + jas_matrix_setv(pixels[1], x, g); + jas_matrix_setv(pixels[2], x, b); + + if (number_components > 3) + jas_matrix_setv(pixels[3], x, a); + } + + for (i = 0 ; i < (long)number_components ; i++) + { + int ret = jas_image_writecmpt(jp2_image, (short) i, 0, (unsigned int)y, + (unsigned int)imageWidth(), 1, pixels[i]); + if (ret != 0) + { + DDebug() << "Error encoding JPEG2000 image data" << endl; + + jas_image_destroy(jp2_image); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + return false; + } + } + } + + TQVariant qualityAttr = imageGetAttribute("quality"); + int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; + + if (quality < 0) + quality = 90; + if (quality > 100) + quality = 100; + + TQString rate; + TQTextStream ts( &rate, IO_WriteOnly ); + + // NOTE: to have a lossless compression use quality=100. + // jp2_encode()::optstr: + // - rate=#B => the resulting file size is about # bytes + // - rate=0.0 .. 1.0 => the resulting file size is about the factor times + // the uncompressed size + ts << "rate=" << ( quality / 100.0F ); + + DDebug() << "JPEG2000 quality: " << quality << endl; + DDebug() << "JPEG2000 " << rate << endl; + +# if defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3) + const jas_image_fmtinfo_t *jp2_fmtinfo = jas_image_lookupfmtbyname("jp2"); + int ret = -1; + if (jp2_fmtinfo) + { + ret = jas_image_encode(jp2_image, jp2_stream, jp2_fmtinfo->id, rate.utf8().data()); + } +# else + int ret = jp2_encode(jp2_image, jp2_stream, rate.utf8().data()); +# endif + + if (ret != 0) + { + DDebug() << "Unable to encode JPEG2000 image" << endl; + + jas_image_destroy(jp2_image); + jas_stream_close(jp2_stream); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + + return false; + } + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageSetAttribute("savedformat", "JP2K"); + + saveMetadata(filePath); + + jas_image_destroy(jp2_image); + jas_stream_close(jp2_stream); + for (i = 0 ; i < (long)number_components ; i++) + jas_matrix_destroy(pixels[i]); + + jas_cleanup(); + + return true; +} + +bool JP2KLoader::hasAlpha() const +{ + return m_hasAlpha; +} + +bool JP2KLoader::sixteenBit() const +{ + return m_sixteenBit; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/jp2kloader.h b/src/libs/dimg/loaders/jp2kloader.h new file mode 100644 index 00000000..04ec214e --- /dev/null +++ b/src/libs/dimg/loaders/jp2kloader.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-06-14 + * Description : A JPEG2000 IO file for DImg framework + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef JP2KLOADER_H +#define JP2KLOADER_H + +// C ansi includes. + +extern "C" +{ +#include +} + +// TQt includes. + +#include + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT JP2KLoader : public DImgLoader +{ + +public: + + JP2KLoader(DImg* image); + + bool load(const TQString& filePath, DImgLoaderObserver *observer); + bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const; + virtual bool sixteenBit() const; + virtual bool isReadOnly() const { return false; }; + +private: + + bool m_sixteenBit; + bool m_hasAlpha; +}; + +} // NameSpace Digikam + +#endif /* JP2KLOADER_H */ diff --git a/src/libs/dimg/loaders/jp2ksettings.cpp b/src/libs/dimg/loaders/jp2ksettings.cpp new file mode 100644 index 00000000..af0737c1 --- /dev/null +++ b/src/libs/dimg/loaders/jp2ksettings.cpp @@ -0,0 +1,139 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save JPEG 2000 image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "jp2ksettings.h" +#include "jp2ksettings.moc" + +namespace Digikam +{ + +class JP2KSettingsPriv +{ + +public: + + JP2KSettingsPriv() + { + JPEG2000Grid = 0; + labelJPEG2000compression = 0; + JPEG2000compression = 0; + JPEG2000LossLess = 0; + } + + TQGridLayout *JPEG2000Grid; + + TQLabel *labelJPEG2000compression; + + TQCheckBox *JPEG2000LossLess; + + KIntNumInput *JPEG2000compression; +}; + +JP2KSettings::JP2KSettings(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new JP2KSettingsPriv; + + d->JPEG2000Grid = new TQGridLayout(this, 1, 1, KDialog::spacingHint()); + d->JPEG2000LossLess = new TQCheckBox(i18n("Lossless JPEG 2000 files"), this); + + TQWhatsThis::add(d->JPEG2000LossLess, i18n("

    Toggle lossless compression for JPEG 2000 images.

    " + "If you enable this option, you will use a lossless method " + "to compress JPEG 2000 pictures.

    ")); + + d->JPEG2000compression = new KIntNumInput(75, this); + d->JPEG2000compression->setRange(1, 100, 1, true ); + d->labelJPEG2000compression = new TQLabel(i18n("JPEG 2000 quality:"), this); + + TQWhatsThis::add( d->JPEG2000compression, i18n("

    The quality value for JPEG 2000 images:

    " + "1: low quality (high compression and small " + "file size)

    " + "50: medium quality

    " + "75: good quality (default)

    " + "100: high quality (no compression and " + "large file size)

    " + "Note: JPEG 2000 is not a lossless image " + "compression format when you use this setting.")); + + d->JPEG2000Grid->addMultiCellWidget(d->JPEG2000LossLess, 0, 0, 0, 1); + d->JPEG2000Grid->addMultiCellWidget(d->labelJPEG2000compression, 1, 1, 0, 0); + d->JPEG2000Grid->addMultiCellWidget(d->JPEG2000compression, 1, 1, 1, 1); + d->JPEG2000Grid->setColStretch(1, 10); + + connect(d->JPEG2000LossLess, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleJPEG2000LossLess(bool))); + + connect(d->JPEG2000LossLess, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotToggleJPEG2000LossLess(bool))); +} + +JP2KSettings::~JP2KSettings() +{ + delete d; +} + +void JP2KSettings::setCompressionValue(int val) +{ + d->JPEG2000compression->setValue(val); +} + +int JP2KSettings::getCompressionValue() +{ + return d->JPEG2000compression->value(); +} + +void JP2KSettings::setLossLessCompression(bool b) +{ + d->JPEG2000LossLess->setChecked(b); + slotToggleJPEG2000LossLess(d->JPEG2000LossLess->isChecked()); +} + +bool JP2KSettings::getLossLessCompression() +{ + return d->JPEG2000LossLess->isChecked(); +} + +void JP2KSettings::slotToggleJPEG2000LossLess(bool b) +{ + d->JPEG2000compression->setEnabled(!b); + d->labelJPEG2000compression->setEnabled(!b); +} + +} // namespace Digikam + diff --git a/src/libs/dimg/loaders/jp2ksettings.h b/src/libs/dimg/loaders/jp2ksettings.h new file mode 100644 index 00000000..951cb2fc --- /dev/null +++ b/src/libs/dimg/loaders/jp2ksettings.h @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save JPEG 2000 image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef JP2KSETTINGS_H +#define JP2KSETTINGS_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class JP2KSettingsPriv; + +class DIGIKAM_EXPORT JP2KSettings : public TQWidget +{ +TQ_OBJECT + + +public: + + JP2KSettings(TQWidget *parent=0); + ~JP2KSettings(); + + void setCompressionValue(int val); + int getCompressionValue(); + + void setLossLessCompression(bool b); + bool getLossLessCompression(); + +private slots: + + void slotToggleJPEG2000LossLess(bool); + +private: + + JP2KSettingsPriv* d; +}; + +} // namespace Digikam + +#endif /* JP2KSETTINGS_H */ diff --git a/src/libs/dimg/loaders/jpegloader.cpp b/src/libs/dimg/loaders/jpegloader.cpp new file mode 100644 index 00000000..58f6e20a --- /dev/null +++ b/src/libs/dimg/loaders/jpegloader.cpp @@ -0,0 +1,676 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : A JPEG IO file for DImg framework + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define XMD_H + +// This line must be commented to prevent any latency time +// when we use threaded image loader interface for each image +// files io. Uncomment this line only for debugging. +//#define ENABLE_DEBUG_MESSAGES + +// C ansi includes. + +extern "C" +{ +#include "iccjpeg.h" +} + +// C+ includes. + +#include +#include + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "jpegloader.h" + +namespace Digikam +{ + +// To manage Errors/Warnings handling provide by libjpeg + +void JPEGLoader::dimg_jpeg_error_exit(j_common_ptr cinfo) +{ + dimg_jpeg_error_mgr* myerr = (dimg_jpeg_error_mgr*) cinfo->err; + + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << endl; +#endif + + longjmp(myerr->setjmp_buffer, 1); +} + +void JPEGLoader::dimg_jpeg_emit_message(j_common_ptr cinfo, int msg_level) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << " (" << msg_level << ")" << endl; +#else + Q_UNUSED(msg_level); +#endif +} + +void JPEGLoader::dimg_jpeg_output_message(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << endl; +#endif +} + +JPEGLoader::JPEGLoader(DImg* image) + : DImgLoader(image) +{ +} + +bool JPEGLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + readMetadata(filePath, DImg::JPEG); + + FILE *file = fopen(TQFile::encodeName(filePath), "rb"); + if (!file) + return false; + + unsigned char header[2]; + + if (fread(&header, 2, 1, file) != 1) + { + fclose(file); + return false; + } + + unsigned char jpegID[] = { 0xFF, 0xD8 }; + + if (memcmp(header, jpegID, 2) != 0) + { + // not a jpeg file + fclose(file); + return false; + } + + fseek(file, 0L, SEEK_SET); + + struct jpeg_decompress_struct cinfo; + struct dimg_jpeg_error_mgr jerr; + + // ------------------------------------------------------------------- + // JPEG error handling. + + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = dimg_jpeg_error_exit; + cinfo.err->emit_message = dimg_jpeg_emit_message; + cinfo.err->output_message = dimg_jpeg_output_message; + + // If an error occurs during reading, libjpeg will jump here + + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + fclose(file); + return false; + } + + // ------------------------------------------------------------------- + // Find out if we do the fast-track loading with reduced size. Jpeg specific. + int scaledLoadingSize = 0; + TQVariant attribute = imageGetAttribute("jpegScaledLoadingSize"); + if (attribute.isValid()) + scaledLoadingSize = attribute.toInt(); + + // ------------------------------------------------------------------- + // Set JPEG decompressor instance + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, file); + + // Recording ICC profile marker (from iccjpeg.c) + setup_read_icc_profile(&cinfo); + + // read image information + jpeg_read_header(&cinfo, true); + + // set decompression parameters + cinfo.do_fancy_upsampling = false; + cinfo.do_block_smoothing = false; + + if (scaledLoadingSize) + { + int imgSize = TQMAX(cinfo.image_width, cinfo.image_height); + + // libjpeg supports 1/1, 1/2, 1/4, 1/8 + int scale=1; + while(scaledLoadingSize*scale*2<=imgSize) + { + scale*=2; + } + if(scale>8) scale=8; + + cinfo.scale_num=1; + cinfo.scale_denom=scale; + } + + // Libjpeg handles the following conversions: + // YCbCr => GRAYSCALE, YCbCr => RGB, GRAYSCALE => RGB, YCCK => CMYK + // So we cannot get RGB from CMYK or YCCK, CMYK conversion is handled below + switch (cinfo.jpeg_color_space) + { + case JCS_UNKNOWN: + // perhaps jpeg_read_header did some guessing, leave value unchanged + break; + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo.out_color_space = JCS_CMYK; + break; + } + + // initialize decompression + jpeg_start_decompress(&cinfo); + + // some pseudo-progress + if (observer) + observer->progressInfo(m_image, 0.1); + + // ------------------------------------------------------------------- + // Get image data. + + int w = cinfo.output_width; + int h = cinfo.output_height; + uchar *dest = 0; + + uchar *ptr, *line[16], *data=0; + uchar *ptr2=0; + int x, y, l, i, scans, count, prevy; + + if (cinfo.rec_outbuf_height > 16) + { + jpeg_destroy_decompress(&cinfo); + fclose(file); + DDebug() << k_funcinfo << "Height of JPEG scanline buffer out of range!" << endl; + return false; + } + + // We only take RGB with 1 or 3 components, or CMYK with 4 components + if (!( + (cinfo.out_color_space == JCS_RGB && (cinfo.output_components == 3 || cinfo.output_components == 1)) + || (cinfo.out_color_space == JCS_CMYK && cinfo.output_components == 4) + )) + { + jpeg_destroy_decompress(&cinfo); + fclose(file); + DDebug() << k_funcinfo + << "JPEG colorspace (" + << cinfo.out_color_space + << ") or Number of JPEG color components (" + << cinfo.output_components + << ") unsupported!" << endl; + return false; + } + + data = new uchar[w * 16 * cinfo.output_components]; + + if (!data) + { + jpeg_destroy_decompress(&cinfo); + fclose(file); + DDebug() << k_funcinfo << "Cannot allocate memory!" << endl; + return false; + } + + dest = new uchar[w * h * 4]; + + if (!dest) + { + delete [] data; + jpeg_destroy_decompress(&cinfo); + fclose(file); + DDebug() << k_funcinfo << "Cannot allocate memory!" << endl; + return false; + } + + ptr2 = dest; + count = 0; + prevy = 0; + + if (cinfo.output_components == 3) + { + for (i = 0; i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 3); + + int checkPoint = 0; + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + // use 0-10% and 90-100% for pseudo-progress + if (observer && l >= checkPoint) + { + checkPoint += granularity(observer, h, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + delete [] dest; + jpeg_destroy_decompress(&cinfo); + fclose(file); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) ))); + } + + jpeg_read_scanlines(&cinfo, &line[0], cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + + if ((h - l) < scans) + scans = h - l; + + ptr = data; + + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + ptr2[3] = 0xFF; + ptr2[2] = ptr[0]; + ptr2[1] = ptr[1]; + ptr2[0] = ptr[2]; + + ptr += 3; + ptr2 += 4; + } + } + } + } + else if (cinfo.output_components == 1) + { + for (i = 0; i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w); + + int checkPoint = 0; + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + if (observer && l >= checkPoint) + { + checkPoint += granularity(observer, h, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + delete [] dest; + jpeg_destroy_decompress(&cinfo); + fclose(file); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) ))); + } + + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + + if ((h - l) < scans) + scans = h - l; + + ptr = data; + + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + ptr2[3] = 0xFF; + ptr2[2] = ptr[0]; + ptr2[1] = ptr[0]; + ptr2[0] = ptr[0]; + + ptr ++; + ptr2 += 4; + } + } + } + } + else // CMYK + { + for (i = 0; i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 4); + + int checkPoint = 0; + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + // use 0-10% and 90-100% for pseudo-progress + if (observer && l >= checkPoint) + { + checkPoint += granularity(observer, h, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + delete [] dest; + jpeg_destroy_decompress(&cinfo); + fclose(file); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) ))); + } + + jpeg_read_scanlines(&cinfo, &line[0], cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + + if ((h - l) < scans) + scans = h - l; + + ptr = data; + + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + // Inspired by TQt's JPEG loader + + int k = ptr[3]; + + ptr2[3] = 0xFF; + ptr2[2] = k * ptr[0] / 255; + ptr2[1] = k * ptr[1] / 255; + ptr2[0] = k * ptr[2] / 255; + + ptr += 4; + ptr2 += 4; + } + } + } + } + + delete [] data; + + // ------------------------------------------------------------------- + // Read image ICC profile + + TQMap& metaData = imageMetaData(); + + JOCTET *profile_data=NULL; + uint profile_size; + + read_icc_profile (&cinfo, &profile_data, &profile_size); + + if (profile_data != NULL) + { + TQByteArray profile_rawdata(profile_size); + memcpy(profile_rawdata.data(), profile_data, profile_size); + metaData.insert(DImg::ICC, profile_rawdata); + free (profile_data); + } + else + { + // If ICC profile is null, check Exif metadata. + checkExifWorkingColorSpace(); + } + + // ------------------------------------------------------------------- + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + // ------------------------------------------------------------------- + + fclose(file); + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageWidth() = w; + imageHeight() = h; + imageData() = dest; + imageSetAttribute("format", "JPEG"); + + return true; +} + +bool JPEGLoader::save(const TQString& filePath, DImgLoaderObserver *observer) +{ + FILE *file = fopen(TQFile::encodeName(filePath), "wb"); + if (!file) + return false; + + struct jpeg_compress_struct cinfo; + struct dimg_jpeg_error_mgr jerr; + + // ------------------------------------------------------------------- + // JPEG error handling. + + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = dimg_jpeg_error_exit; + cinfo.err->emit_message = dimg_jpeg_emit_message; + cinfo.err->output_message = dimg_jpeg_output_message; + + // If an error occurs during writing, libjpeg will jump here + + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_compress(&cinfo); + fclose(file); + return false; + } + + // ------------------------------------------------------------------- + // Set JPEG compressor instance + + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, file); + + uint& w = imageWidth(); + uint& h = imageHeight(); + unsigned char*& data = imageData(); + + // Size of image. + cinfo.image_width = w; + cinfo.image_height = h; + + // Color components of image in RGB. + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + TQVariant qualityAttr = imageGetAttribute("quality"); + int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; + + if (quality < 0) + quality = 90; + if (quality > 100) + quality = 100; + + TQVariant subSamplingAttr = imageGetAttribute("subsampling"); + int subsampling = subSamplingAttr.isValid() ? subSamplingAttr.toInt() : 1; // Medium + + jpeg_set_defaults(&cinfo); + + // B.K.O #149578: set horizontal and vertical chroma subsampling factor to encoder. + // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling + + switch (subsampling) + { + case 1: // 2x1, 1x1, 1x1 (4:2:2) : Medium + { + DDebug() << "Using LibJPEG medium chroma-subsampling (4:2:2)" << endl; + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + } + case 2: // 2x2, 1x1, 1x1 (4:1:1) : High + { + DDebug() << "Using LibJPEG high chroma-subsampling (4:1:1)" << endl; + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + } + default: // 1x1 1x1 1x1 (4:4:4) : None + { + DDebug() << "Using LibJPEG none chroma-subsampling (4:4:4)" << endl; + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + } + } + + jpeg_set_quality(&cinfo, quality, true); + jpeg_start_compress(&cinfo, true); + + DDebug() << "Using LibJPEG quality compression value: " << quality << endl; + + if (observer) + observer->progressInfo(m_image, 0.1); + + // ------------------------------------------------------------------- + // Write ICC profil. + + TQByteArray profile_rawdata = m_image->getICCProfil(); + + if (!profile_rawdata.isEmpty()) + { + write_icc_profile (&cinfo, (JOCTET *)profile_rawdata.data(), profile_rawdata.size()); + } + + if (observer) + observer->progressInfo(m_image, 0.2); + + // ------------------------------------------------------------------- + // Write Image data. + + uchar* line = new uchar[w*3]; + uchar* dstPtr = 0; + uint checkPoint = 0; + + if (!imageSixteenBit()) // 8 bits image. + { + + uchar* srcPtr = data; + + for (uint j=0; jcontinueQuery(m_image)) + { + delete [] line; + jpeg_destroy_compress(&cinfo); + fclose(file); + return false; + } + // use 0-20% for pseudo-progress, now fill 20-100% + observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)j)/((float)h) ))); + } + + dstPtr = line; + + for (uint i = 0; i < w; i++) + { + dstPtr[2] = srcPtr[0]; + dstPtr[1] = srcPtr[1]; + dstPtr[0] = srcPtr[2]; + + srcPtr += 4; + dstPtr += 3; + } + + jpeg_write_scanlines(&cinfo, &line, 1); + } + } + else + { + unsigned short* srcPtr = (unsigned short*)data; + + for (uint j=0; jcontinueQuery(m_image)) + { + delete [] line; + jpeg_destroy_compress(&cinfo); + fclose(file); + return false; + } + // use 0-20% for pseudo-progress, now fill 20-100% + observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)j)/((float)h) ))); + } + + dstPtr = line; + + for (uint i = 0; i < w; i++) + { + dstPtr[2] = (srcPtr[0] * 255UL)/65535UL; + dstPtr[1] = (srcPtr[1] * 255UL)/65535UL; + dstPtr[0] = (srcPtr[2] * 255UL)/65535UL; + + srcPtr += 4; + dstPtr += 3; + } + + jpeg_write_scanlines(&cinfo, &line, 1); + } + } + + delete [] line; + + // ------------------------------------------------------------------- + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + fclose(file); + + imageSetAttribute("savedformat", "JPEG"); + + saveMetadata(filePath); + + return true; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/jpegloader.h b/src/libs/dimg/loaders/jpegloader.h new file mode 100644 index 00000000..b5d64f18 --- /dev/null +++ b/src/libs/dimg/loaders/jpegloader.h @@ -0,0 +1,80 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : A JPEG IO file for DImg framework + * + * Copyright (C) 2005 by Renchi Raju , Gilles Caulier + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef JPEGLOADER_H +#define JPEGLOADER_H + +// C ansi includes. + +extern "C" +{ +#include +#include +} + +// TQt includes. + +#include + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT JPEGLoader : public DImgLoader +{ + +public: + + JPEGLoader(DImg* image); + + bool load(const TQString& filePath, DImgLoaderObserver *observer); + bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const { return false; } + virtual bool sixteenBit() const { return false; } + virtual bool isReadOnly() const { return false; }; + +private: + + // To manage Errors/Warnings handling provide by libjpeg + + struct dimg_jpeg_error_mgr : public jpeg_error_mgr + { + jmp_buf setjmp_buffer; + }; + + static void dimg_jpeg_error_exit(j_common_ptr cinfo); + static void dimg_jpeg_emit_message(j_common_ptr cinfo, int msg_level); + static void dimg_jpeg_output_message(j_common_ptr cinfo); + +}; + +} // NameSpace Digikam + +#endif /* JPEGLOADER_H */ diff --git a/src/libs/dimg/loaders/jpegsettings.cpp b/src/libs/dimg/loaders/jpegsettings.cpp new file mode 100644 index 00000000..62bd6365 --- /dev/null +++ b/src/libs/dimg/loaders/jpegsettings.cpp @@ -0,0 +1,154 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save JPEG image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include + +// Local includes. + +#include "jpegsettings.h" +#include "jpegsettings.moc" + +namespace Digikam +{ + +class JPEGSettingsPriv +{ + +public: + + JPEGSettingsPriv() + { + JPEGGrid = 0; + labelJPEGcompression = 0; + JPEGcompression = 0; + labelWarning = 0; + labelSubSampling = 0; + subSamplingCB = 0; + } + + TQGridLayout *JPEGGrid; + + TQLabel *labelJPEGcompression; + TQLabel *labelSubSampling; + + TQComboBox *subSamplingCB; + + KActiveLabel *labelWarning; + + KIntNumInput *JPEGcompression; +}; + +JPEGSettings::JPEGSettings(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new JPEGSettingsPriv; + + d->JPEGGrid = new TQGridLayout(this, 1, 2, KDialog::spacingHint()); + d->JPEGcompression = new KIntNumInput(75, this); + d->JPEGcompression->setRange(1, 100, 1, true ); + d->labelJPEGcompression = new TQLabel(i18n("JPEG quality:"), this); + + TQWhatsThis::add(d->JPEGcompression, i18n("

    The JPEG image quality:

    " + "1: low quality (high compression and small " + "file size)

    " + "50: medium quality

    " + "75: good quality (default)

    " + "100: high quality (no compression and " + "large file size)

    " + "Note: JPEG always uses lossy compression.")); + + d->labelWarning = new KActiveLabel(i18n("" + "Warning: JPEG is a
    " + "lossy compression
    " + "image format!

    " + ""), this); + + d->labelWarning->setFrameStyle(TQFrame::Box | TQFrame::Plain); + d->labelWarning->setLineWidth(1); + d->labelWarning->setFrameShape(TQFrame::Box); + + d->labelSubSampling = new TQLabel(i18n("Chroma subsampling:"), this); + + d->subSamplingCB = new TQComboBox(false, this); + d->subSamplingCB->insertItem(i18n("None")); // 1x1, 1x1, 1x1 (4:4:4) + d->subSamplingCB->insertItem(i18n("Medium")); // 2x1, 1x1, 1x1 (4:2:2) + d->subSamplingCB->insertItem(i18n("High")); // 2x2, 1x1, 1x1 (4:1:1) + TQWhatsThis::add(d->subSamplingCB, i18n("

    JPEG Chroma subsampling level \n(color is saved with less resolution " "than luminance):

    " + "None=best: uses 4:4:4 ratio. Does not employ chroma " + "subsampling at all. This preserves edges and contrasting " + "colors, whilst adding no additional compression

    " + "Medium: uses 4:2:2 ratio. Medium compression: reduces " + "the color resolution by one-third with little to " + "no visual difference

    " + "High: use 4:1:1 ratio. High compression: suits " + "images with soft edges but tends to alter colors

    " + "Note: JPEG always uses lossy compression.")); + + d->JPEGGrid->addMultiCellWidget(d->labelJPEGcompression, 0, 0, 0, 0); + d->JPEGGrid->addMultiCellWidget(d->JPEGcompression, 0, 0, 1, 1); + d->JPEGGrid->addMultiCellWidget(d->labelSubSampling, 1, 1, 0, 0); + d->JPEGGrid->addMultiCellWidget(d->subSamplingCB, 1, 1, 1, 1); + d->JPEGGrid->addMultiCellWidget(d->labelWarning, 0, 1, 2, 2); + d->JPEGGrid->setColStretch(1, 10); + d->JPEGGrid->setRowStretch(2, 10); +} + +JPEGSettings::~JPEGSettings() +{ + delete d; +} + +void JPEGSettings::setCompressionValue(int val) +{ + d->JPEGcompression->setValue(val); +} + +int JPEGSettings::getCompressionValue() +{ + return d->JPEGcompression->value(); +} + +void JPEGSettings::setSubSamplingValue(int val) +{ + d->subSamplingCB->setCurrentItem(val); +} + +int JPEGSettings::getSubSamplingValue() +{ + return d->subSamplingCB->currentItem(); +} + +} // namespace Digikam diff --git a/src/libs/dimg/loaders/jpegsettings.h b/src/libs/dimg/loaders/jpegsettings.h new file mode 100644 index 00000000..70e20d42 --- /dev/null +++ b/src/libs/dimg/loaders/jpegsettings.h @@ -0,0 +1,63 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save JPEG image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef JPEGSETTINGS_H +#define JPEGSETTINGS_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class JPEGSettingsPriv; + +class DIGIKAM_EXPORT JPEGSettings : public TQWidget +{ +TQ_OBJECT + + +public: + + JPEGSettings(TQWidget *parent=0); + ~JPEGSettings(); + + void setCompressionValue(int val); + int getCompressionValue(); + + void setSubSamplingValue(int val); + int getSubSamplingValue(); + +private: + + JPEGSettingsPriv* d; +}; + +} // namespace Digikam + +#endif /* JPEGSETTINGS_H */ diff --git a/src/libs/dimg/loaders/pngloader.cpp b/src/libs/dimg/loaders/pngloader.cpp new file mode 100644 index 00000000..402cf944 --- /dev/null +++ b/src/libs/dimg/loaders/pngloader.cpp @@ -0,0 +1,993 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-01 + * Description : a PNG image loader for DImg framework. + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// This line must be commented to prevent any latency time +// when we use threaded image loader interface for each image +// files io. Uncomment this line only for debugging. +//#define ENABLE_DEBUG_MESSAGES + +#define PNG_BYTES_TO_CHECK 4 + +// C Ansi includes. + +extern "C" +{ +#include +#include +} + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "pngloader.h" + +namespace Digikam +{ + +#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) + typedef png_bytep iCCP_data; +#else + typedef png_charp iCCP_data; +#endif + +PNGLoader::PNGLoader(DImg* image) + : DImgLoader(image) +{ + m_hasAlpha = false; + m_sixteenBit = false; +} + +bool PNGLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + png_uint_32 w32, h32; + int width, height; + FILE *f; + int bit_depth, color_type, interlace_type; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + readMetadata(filePath, DImg::PNG); + + // ------------------------------------------------------------------- + // Open the file + + f = fopen(TQFile::encodeName(filePath), "rb"); + if ( !f ) + { + DDebug() << k_funcinfo << "Cannot open image file." << endl; + return false; + } + + unsigned char buf[PNG_BYTES_TO_CHECK]; + + fread(buf, 1, PNG_BYTES_TO_CHECK, f); + if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK)) + { + DDebug() << k_funcinfo << "Not a PNG image file." << endl; + fclose(f); + return false; + } + rewind(f); + + // ------------------------------------------------------------------- + // Initialize the internal structures + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + DDebug() << k_funcinfo << "Invalid PNG image file structure." << endl; + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + DDebug() << k_funcinfo << "Cannot reading PNG image file structure." << endl; + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(f); + return false; + } + + // ------------------------------------------------------------------- + // PNG error handling. If an error occurs during reading, libpng + // will jump here + + if (setjmp(png_jmpbuf(png_ptr))) + { + DDebug() << k_funcinfo << "Internal libPNG error during reading file. Process aborted!" << endl; + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return false; + } + + png_init_io(png_ptr, f); + + // ------------------------------------------------------------------- + // Read all PNG info up to image data + + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32), + (png_uint_32 *) (&h32), &bit_depth, &color_type, + &interlace_type, NULL, NULL); + + width = (int)w32; + height = (int)h32; + + // TODO: Endianness: + // You may notice that the code for little and big endian + // below is now identical. This was found to work by PPC users. + // If this proves right, all the conditional clauses can be removed. + + if (bit_depth == 16) + { +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in 16 bits/color/pixel." << endl; +#endif + m_sixteenBit = true; + + switch (color_type) + { + case PNG_COLOR_TYPE_RGB : // RGB +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl; +#endif + m_hasAlpha = false; + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + + break; + + case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl; +#endif + m_hasAlpha = true; + break; + + case PNG_COLOR_TYPE_GRAY : // Grayscale +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl; +#endif + png_set_gray_to_rgb(png_ptr); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + + m_hasAlpha = false; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + Alpha +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl; +#endif + png_set_gray_to_rgb(png_ptr); + m_hasAlpha = true; + break; + + case PNG_COLOR_TYPE_PALETTE : // Indexed +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl; +#endif + png_set_palette_to_rgb(png_ptr); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER); + + m_hasAlpha = false; + break; + + default: +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << "PNG color type unknown." << endl; +#endif + return false; + } + } + else + { +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << "PNG in >=8 bits/color/pixel." << endl; +#endif + m_sixteenBit = false; + png_set_packing(png_ptr); + + switch (color_type) + { + case PNG_COLOR_TYPE_RGB : // RGB +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl; +#endif + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + + m_hasAlpha = false; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl; +#endif + m_hasAlpha = true; + break; + + case PNG_COLOR_TYPE_GRAY : // Grayscale +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl; +#endif + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_set_gray_to_rgb(png_ptr); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + + m_hasAlpha = false; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + alpha +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl; +#endif + png_set_gray_to_rgb(png_ptr); + m_hasAlpha = true; + break; + + case PNG_COLOR_TYPE_PALETTE : // Indexed +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl; +#endif + png_set_packing(png_ptr); + png_set_palette_to_rgb(png_ptr); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + else // PPC + png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); + + m_hasAlpha = true; + break; + + default: +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << "PNG color type unknown." << endl; +#endif + return false; + } + } + + if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_bgr(png_ptr); + else // PPC + png_set_bgr(png_ptr); + //png_set_swap_alpha(png_ptr); + + if (observer) + observer->progressInfo(m_image, 0.1); + + // ------------------------------------------------------------------- + // Get image data. + + // Call before png_read_update_info and png_start_read_image() + // For non-interlaced images number_passes will be 1 + int number_passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + uchar *data = 0; + + if (m_sixteenBit) + data = new uchar[width*height*8]; // 16 bits/color/pixel + else + data = new uchar[width*height*4]; // 8 bits/color/pixel + + uchar **lines = 0; + lines = (uchar **)malloc(height * sizeof(uchar *)); + if (!lines) + { + DDebug() << k_funcinfo << "Cannot allocate memory to load PNG image data." << endl; + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(f); + delete [] data; + return false; + } + + for (int i = 0; i < height; i++) + { + if (m_sixteenBit) + lines[i] = data + (i * width * 8); + else + lines[i] = data + (i * width * 4); + } + + // The easy way to read the whole image + // png_read_image(png_ptr, lines); + // The other way to read images is row by row. Necessary for observer. + // Now we need to deal with interlacing. + + for (int pass = 0; pass < number_passes; pass++) + { + int y; + int checkPoint = 0; + for (y = 0; y < height; y++) + { + if (observer && y == checkPoint) + { + checkPoint += granularity(observer, height, 0.7); + if (!observer->continueQuery(m_image)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(f); + delete [] data; + free(lines); + return false; + } + // use 10% - 80% for progress while reading rows + observer->progressInfo(m_image, 0.1 + (0.7 * ( ((float)y)/((float)height) )) ); + } + + png_read_rows(png_ptr, lines+y, NULL, 1); + } + } + + free(lines); + + // Swap bytes in 16 bits/color/pixel for DImg + + if (m_sixteenBit) + { + uchar ptr[8]; // One pixel to swap + + for (int p = 0; p < width*height*8; p+=8) + { + memcpy (&ptr[0], &data[p], 8); // Current pixel + + data[ p ] = ptr[1]; // Blue + data[p+1] = ptr[0]; + data[p+2] = ptr[3]; // Green + data[p+3] = ptr[2]; + data[p+4] = ptr[5]; // Red + data[p+5] = ptr[4]; + data[p+6] = ptr[7]; // Alpha + data[p+7] = ptr[6]; + } + } + + if (observer) + observer->progressInfo(m_image, 0.9); + + // ------------------------------------------------------------------- + // Read image ICC profile + + TQMap& metaData = imageMetaData(); + +#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) + png_charp profile_name; + iCCP_data profile_data=NULL; +#else + png_charp profile_name, profile_data=NULL; +#endif + png_uint_32 profile_size; + int compression_type; + + png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_size); + + if (profile_data != NULL) + { + TQByteArray profile_rawdata(profile_size); + memcpy(profile_rawdata.data(), profile_data, profile_size); + metaData.insert(DImg::ICC, profile_rawdata); + } + else + { + // If ICC profile is null, check Exif metadata. + checkExifWorkingColorSpace(); + } + + // ------------------------------------------------------------------- + // Get embbeded text data. + + png_text* text_ptr; + int num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, NULL); + + /* + Standard Embedded text includes in PNG : + + Title Short (one line) title or caption for image + Author Name of image's creator + Description Description of image (possibly long) + Copyright Copyright notice + Creation Time Time of original image creation + Software Software used to create the image + Disclaimer Legal disclaimer + Warning Warning of nature of content + Source Device used to create the image + Comment Miscellaneous comment; conversion from GIF comment + + Extra Raw profiles tag are used by ImageMAgick and defines at this URL : + http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-5.87/html/TagNames/PNG.html#TextualData + */ + + for (int i = 0; i < num_comments; i++) + { + // Check if we have a Raw profile embedded using ImageMagick technic. + + if (memcmp(text_ptr[i].key, "Raw profile type exif", 21) != 0 || + memcmp(text_ptr[i].key, "Raw profile type APP1", 21) != 0 || + memcmp(text_ptr[i].key, "Raw profile type iptc", 21) != 0) + { + imageSetEmbbededText(text_ptr[i].key, text_ptr[i].text); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "Reading PNG Embedded text: key=" << text_ptr[i].key + << " text=" << text_ptr[i].text << endl; +#endif + } + } + + // ------------------------------------------------------------------- + + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(f); + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageWidth() = width; + imageHeight() = height; + imageData() = data; + imageSetAttribute("format", "PNG"); + + return true; +} + +bool PNGLoader::save(const TQString& filePath, DImgLoaderObserver *observer) +{ + FILE *f; + png_structp png_ptr; + png_infop info_ptr; + uchar *ptr, *data = 0; + uint x, y, j; + png_bytep row_ptr; + png_color_8 sig_bit; + int quality = 75; + int compression = 3; + + // ------------------------------------------------------------------- + // Open the file + + f = fopen(TQFile::encodeName(filePath), "wb"); + if ( !f ) + { + DDebug() << k_funcinfo << "Cannot open target image file." << endl; + return false; + } + + + // ------------------------------------------------------------------- + // Initialize the internal structures + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + DDebug() << k_funcinfo << "Invalid target PNG image file structure." << endl; + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + DDebug() << k_funcinfo << "Cannot create PNG image file structure." << endl; + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + fclose(f); + return false; + } + + // ------------------------------------------------------------------- + // PNG error handling. If an error occurs during writing, libpng + // will jump here + + if (setjmp(png_jmpbuf(png_ptr))) + { + DDebug() << k_funcinfo << "Internal libPNG error during writing file. Process aborted!" << endl; + fclose(f); + png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); + png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); + return false; + } + + png_init_io(png_ptr, f); + + if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel + png_set_bgr(png_ptr); + else // PPC + png_set_swap_alpha(png_ptr); + + if (imageHasAlpha()) + { + png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(), + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + if (imageSixteenBit()) + data = new uchar[imageWidth() * 8 * sizeof(uchar)]; + else + data = new uchar[imageWidth() * 4 * sizeof(uchar)]; + } + else + { + png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(), + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + if (imageSixteenBit()) + data = new uchar[imageWidth() * 6 * sizeof(uchar)]; + else + data = new uchar[imageWidth() * 3 * sizeof(uchar)]; + } + + sig_bit.red = imageBitsDepth(); + sig_bit.green = imageBitsDepth(); + sig_bit.blue = imageBitsDepth(); + sig_bit.alpha = imageBitsDepth(); + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + // ------------------------------------------------------------------- + // Quality to convert to compression + + TQVariant qualityAttr = imageGetAttribute("quality"); + quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; + + if (quality < 1) + quality = 1; + if (quality > 99) + quality = 99; + + quality = quality / 10; + compression = 9 - quality; + + if (compression < 0) + compression = 0; + if (compression > 9) + compression = 9; + + png_set_compression_level(png_ptr, compression); + + // ------------------------------------------------------------------- + // Write ICC profil. + + TQByteArray profile_rawdata = m_image->getICCProfil(); + + if (!profile_rawdata.isEmpty()) + { +#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) + png_set_iCCP(png_ptr, info_ptr, (png_charp)("icc"), PNG_COMPRESSION_TYPE_BASE, reinterpret_cast(profile_rawdata.data()), profile_rawdata.size()); +#else + png_set_iCCP(png_ptr, info_ptr, (png_charp)"icc", PNG_COMPRESSION_TYPE_BASE, profile_rawdata.data(), profile_rawdata.size()); +#endif + } + + // ------------------------------------------------------------------- + // Write embbeded Text + + typedef TQMap EmbeddedTextMap; + EmbeddedTextMap map = imageEmbeddedText(); + + for (EmbeddedTextMap::iterator it = map.begin(); it != map.end(); ++it) + { + if (it.key() != TQString("Software") && it.key() != TQString("Comment")) + { + png_text text; + text.key = (char*)it.key().ascii(); + text.text = (char*)it.data().ascii(); +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl; +#endif + text.compression = PNG_TEXT_COMPRESSION_zTXt; + png_set_text(png_ptr, info_ptr, &(text), 1); + } + } + + // Update 'Software' text tag. + TQString software("digiKam "); + software.append(digikam_version); + TQString libpngver(PNG_HEADER_VERSION_STRING); + libpngver.replace('\n', ' '); + software.append(TQString(" (%1)").arg(libpngver)); + png_text text; + text.key = (png_charp)("Software"); + text.text = (char *)software.ascii(); +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl; +#endif + text.compression = PNG_TEXT_COMPRESSION_zTXt; + png_set_text(png_ptr, info_ptr, &(text), 1); + + // Write embedded Raw profiles metadata (Exif/Iptc) in text tag using ImageMagick technic. + // Write digiKam comment like an iTXt chunk using UTF8 encoding. + // NOTE: iTXt will be enable by default with libpng >= 1.3.0. + + typedef TQMap MetaDataMap; + MetaDataMap metaDataMap = imageMetaData(); + + for (MetaDataMap::iterator it = metaDataMap.begin(); it != metaDataMap.end(); ++it) + { + TQByteArray ba = it.data(); + + switch (it.key()) + { + +#ifdef PNG_iTXt_SUPPORTED + + // TODO : this code is not yet tested. It require libpng 1.3.0. + + case(DImg::COM): + { + png_text comment; + comment.key = "Comment"; + comment.text = ba.data(); + comment.itxt_length = ba.size(); + comment.compression = PNG_ITXT_COMPRESSION_zTXt; + png_set_text(png_ptr, info_ptr, &(comment), 1); + + DDebug() << "Writing digiKam comment into iTXt PNG chunk : " << ba << endl; + break; + } +#endif + + case(DImg::EXIF): + { + const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + TQByteArray profile; + + // If bytes array do not start with ImageMagick header, Exif metadata have been created from + // scratch using Exiv2. In this case, we need to add Exif header from start. + if (memcmp(ba.data(), "exif", 4) != 0 && + memcmp(ba.data(), "iptc", 4) != 0 && + memcmp(ba.data(), "profile", 7) != 0) + { + profile = TQByteArray(ba.size() + sizeof(ExifHeader)); + memcpy(profile.data(), ExifHeader, sizeof(ExifHeader)); + memcpy(profile.data()+sizeof(ExifHeader), ba.data(), ba.size()); + } + else + { + profile = ba; + } + + writeRawProfile(png_ptr, info_ptr, (png_charp)("exif"), profile.data(), (png_uint_32) profile.size()); + break; + } + case(DImg::IPTC): + { + writeRawProfile(png_ptr, info_ptr, (png_charp)("iptc"), ba.data(), (png_uint_32) ba.size()); + break; + } + default: + break; + } + } + + if (observer) + observer->progressInfo(m_image, 0.2); + + // ------------------------------------------------------------------- + // Write image data + + png_write_info(png_ptr, info_ptr); + png_set_shift(png_ptr, &sig_bit); + png_set_packing(png_ptr); + ptr = imageData(); + + uint checkPoint = 0; + for (y = 0; y < imageHeight(); y++) + { + + if (observer && y == checkPoint) + { + checkPoint += granularity(observer, imageHeight(), 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + fclose(f); + png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); + png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); + return false; + } + observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)y)/((float)imageHeight()) ))); + } + + j = 0; + + for (x = 0; x < imageWidth()*imageBytesDepth(); x+=imageBytesDepth()) + { + if (imageSixteenBit()) + { + if (imageHasAlpha()) + { + data[j++] = ptr[x+1]; // Blue + data[j++] = ptr[ x ]; + data[j++] = ptr[x+3]; // Green + data[j++] = ptr[x+2]; + data[j++] = ptr[x+5]; // Red + data[j++] = ptr[x+4]; + data[j++] = ptr[x+7]; // Alpha + data[j++] = ptr[x+6]; + } + else + { + data[j++] = ptr[x+1]; // Blue + data[j++] = ptr[ x ]; + data[j++] = ptr[x+3]; // Green + data[j++] = ptr[x+2]; + data[j++] = ptr[x+5]; // Red + data[j++] = ptr[x+4]; + } + } + else + { + if (imageHasAlpha()) + { + data[j++] = ptr[ x ]; // Blue + data[j++] = ptr[x+1]; // Green + data[j++] = ptr[x+2]; // Red + data[j++] = ptr[x+3]; // Alpha + } + else + { + data[j++] = ptr[ x ]; // Blue + data[j++] = ptr[x+1]; // Green + data[j++] = ptr[x+2]; // Red + } + } + } + + row_ptr = (png_bytep) data; + + png_write_rows(png_ptr, &row_ptr, 1); + ptr += (imageWidth() * imageBytesDepth()); + } + + delete [] data; + + // ------------------------------------------------------------------- + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); + png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); + + fclose(f); + + imageSetAttribute("savedformat", "PNG"); + + saveMetadata(filePath); + + return true; +} + +bool PNGLoader::hasAlpha() const +{ + return m_hasAlpha; +} + +bool PNGLoader::sixteenBit() const +{ + return m_sixteenBit; +} + +void PNGLoader::writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type, + char *profile_data, png_uint_32 length) +{ + png_textp text; + + long i; + + uchar *sp; + + png_charp dp; + + png_uint_32 allocated_length, description_length; + + const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + DDebug() << "Writing Raw profile: type=" << profile_type << ", length=" << length << endl; + + text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text)); + description_length = strlen((const char *) profile_type); + allocated_length = (png_uint_32) (length*2 + (length >> 5) + 20 + description_length); + + text[0].text = (png_charp) png_malloc(ping, allocated_length); + text[0].key = (png_charp) png_malloc(ping, (png_uint_32) 80); + text[0].key[0] = '\0'; + + concatenateString(text[0].key, "Raw profile type ", 4096); + concatenateString(text[0].key, (const char *) profile_type, 62); + + sp = (uchar*)profile_data; + dp = text[0].text; + *dp++='\n'; + + copyString(dp, (const char *) profile_type, allocated_length); + + dp += description_length; + *dp++='\n'; + + formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", length); + + dp += 8; + + for (i=0; i < (long) length; i++) + { + if (i%36 == 0) + *dp++='\n'; + + *(dp++)=(char) hex[((*sp >> 4) & 0x0f)]; + *(dp++)=(char) hex[((*sp++ ) & 0x0f)]; + } + + *dp++='\n'; + *dp='\0'; + text[0].text_length = (png_size_t) (dp-text[0].text); + text[0].compression = -1; + + if (text[0].text_length <= allocated_length) + png_set_text(ping, ping_info,text, 1); + + png_free(ping, text[0].text); + png_free(ping, text[0].key); + png_free(ping, text); +} + +size_t PNGLoader::concatenateString(char *destination, const char *source, const size_t length) +{ + char *q; + + const char *p; + + size_t i; + + size_t count; + + if ( !destination || !source || length == 0 ) + return 0; + + p = source; + q = destination; + i = length; + + while ((i-- != 0) && (*q != '\0')) + q++; + + count = (size_t) (q-destination); + i = length-count; + + if (i == 0) + return(count+strlen(p)); + + while (*p != '\0') + { + if (i != 1) + { + *q++=(*p); + i--; + } + p++; + } + + *q='\0'; + + return(count+(p-source)); +} + +size_t PNGLoader::copyString(char *destination, const char *source, const size_t length) +{ + char *q; + + const char *p; + + size_t i; + + if ( !destination || !source || length == 0 ) + return 0; + + p = source; + q = destination; + i = length; + + if ((i != 0) && (--i != 0)) + { + do + { + if ((*q++=(*p++)) == '\0') + break; + } + while (--i != 0); + } + + if (i == 0) + { + if (length != 0) + *q='\0'; + + do + { + } + while (*p++ != '\0'); + } + + return((size_t) (p-source-1)); +} + +long PNGLoader::formatString(char *string, const size_t length, const char *format,...) +{ + long n; + + va_list operands; + + va_start(operands,format); + n = (long) formatStringList(string, length, format, operands); + va_end(operands); + return(n); +} + +long PNGLoader::formatStringList(char *string, const size_t length, const char *format, va_list operands) +{ + int n = vsnprintf(string, length, format, operands); + + if (n < 0) + string[length-1] = '\0'; + + return((long) n); +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/pngloader.h b/src/libs/dimg/loaders/pngloader.h new file mode 100644 index 00000000..202a278c --- /dev/null +++ b/src/libs/dimg/loaders/pngloader.h @@ -0,0 +1,73 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-01 + * Description : a PNG image loader for DImg framework. + * + * Copyright (C) 2005-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PNGLOADER_H +#define PNGLOADER_H + +extern "C" +{ +#include +#include +} + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT PNGLoader : public DImgLoader +{ +public: + + PNGLoader(DImg* image); + + bool load(const TQString& filePath, DImgLoaderObserver *observer); + bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const; + virtual bool sixteenBit() const; + virtual bool isReadOnly() const { return false; }; + +private: + + void writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type, + char *profile_data, png_uint_32 length); + + size_t concatenateString(char *destination, const char *source, const size_t length); + size_t copyString(char *destination, const char *source, const size_t length); + long formatString(char *string, const size_t length, const char *format,...); + long formatStringList(char *string, const size_t length, const char *format, va_list operands); + +private: + + bool m_sixteenBit; + bool m_hasAlpha; +}; + +} // NameSpace Digikam + +#endif /* PNGLOADER_H */ diff --git a/src/libs/dimg/loaders/pngsettings.cpp b/src/libs/dimg/loaders/pngsettings.cpp new file mode 100644 index 00000000..ce39219b --- /dev/null +++ b/src/libs/dimg/loaders/pngsettings.cpp @@ -0,0 +1,102 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save PNG image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "pngsettings.h" +#include "pngsettings.moc" + +namespace Digikam +{ + +class PNGSettingsPriv +{ + +public: + + PNGSettingsPriv() + { + PNGGrid = 0; + labelPNGcompression = 0; + PNGcompression = 0; + } + + TQGridLayout *PNGGrid; + + TQLabel *labelPNGcompression; + + KIntNumInput *PNGcompression; +}; + +PNGSettings::PNGSettings(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new PNGSettingsPriv; + + d->PNGGrid = new TQGridLayout(this, 1, 1, KDialog::spacingHint()); + d->PNGcompression = new KIntNumInput(9, this); + d->PNGcompression->setRange(1, 9, 1, true ); + d->labelPNGcompression = new TQLabel(i18n("PNG compression:"), this); + + TQWhatsThis::add(d->PNGcompression, i18n("

    The compression value for PNG images:

    " + "1: low compression (large file size but " + "short compression duration - default)

    " + "5: medium compression

    " + "9: high compression (small file size but " + "long compression duration)

    " + "Note: PNG is always a lossless image " + "compression format.")); + d->PNGGrid->addMultiCellWidget(d->labelPNGcompression, 0, 0, 0, 0); + d->PNGGrid->addMultiCellWidget(d->PNGcompression, 0, 0, 1, 1); + d->PNGGrid->setColStretch(1, 10); +} + +PNGSettings::~PNGSettings() +{ + delete d; +} + +void PNGSettings::setCompressionValue(int val) +{ + d->PNGcompression->setValue(val); +} + +int PNGSettings::getCompressionValue() +{ + return d->PNGcompression->value(); +} + +} // namespace Digikam diff --git a/src/libs/dimg/loaders/pngsettings.h b/src/libs/dimg/loaders/pngsettings.h new file mode 100644 index 00000000..aecca935 --- /dev/null +++ b/src/libs/dimg/loaders/pngsettings.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save PNG image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PNGSETTINGS_H +#define PNGSETTINGS_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class PNGSettingsPriv; + +class DIGIKAM_EXPORT PNGSettings : public TQWidget +{ +TQ_OBJECT + + +public: + + PNGSettings(TQWidget *parent=0); + ~PNGSettings(); + + void setCompressionValue(int val); + int getCompressionValue(); + +private: + + PNGSettingsPriv* d; +}; + +} // namespace Digikam + +#endif /* PNGSETTINGS_H */ diff --git a/src/libs/dimg/loaders/ppmloader.cpp b/src/libs/dimg/loaders/ppmloader.cpp new file mode 100644 index 00000000..15c19423 --- /dev/null +++ b/src/libs/dimg/loaders/ppmloader.cpp @@ -0,0 +1,178 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-21-11 + * Description : A 16 bits/color/pixel PPM IO file for + * DImg framework + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// This line must be commented to prevent any latency time +// when we use threaded image loader interface for each image +// files io. Uncomment this line only for debugging. +//#define ENABLE_DEBUG_MESSAGES + +// C ansi includes. + +extern "C" +{ +#include +} + +// C++ includes. + +#include +#include + +// TQt includes. + +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "ppmloader.h" + +namespace Digikam +{ + +PPMLoader::PPMLoader(DImg* image) + : DImgLoader(image) +{ +} + +bool PPMLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + //TODO: progress information + int width, height, rgbmax; + char nl; + + FILE *file = fopen(TQFile::encodeName(filePath), "rb"); + if (!file) + { + DDebug() << k_funcinfo << "Cannot open image file." << endl; + return false; + } + + ushort header; + + if (fread(&header, 2, 1, file) != 1) + { + DDebug() << k_funcinfo << "Cannot read header of file." << endl; + fclose(file); + return false; + } + + uchar* c = (uchar*) &header; + if (*c != 'P') + { + DDebug() << k_funcinfo << "Not a PPM file." << endl; + fclose(file); + return false; + } + + c++; + if (*c != '6') + { + DDebug() << k_funcinfo << "Not a PPM file." << endl; + fclose(file); + return false; + } + + rewind(file); + + if (fscanf (file, "P6 %d %d %d%c", &width, &height, &rgbmax, &nl) != 4) + { + DDebug() << "Corrupted PPM file." << endl; + pclose (file); + return false; + } + + if (rgbmax <= 255) + { + DDebug() << k_funcinfo << "Not a 16 bits per color per pixel PPM file." << endl; + pclose (file); + return false; + } + + if (observer) + observer->progressInfo(m_image, 0.1); + + unsigned short *data; + + data = new unsigned short[width*height*4]; + unsigned short *dst = data; + uchar src[6]; + float fac = 65535.0 / rgbmax; + int checkpoint = 0; + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << "rgbmax=" << rgbmax << " fac=" << fac << endl; +#endif + + for (int h = 0; h < height; h++) + { + + if (observer && h == checkpoint) + { + checkpoint += granularity(observer, height, 0.9); + if (!observer->continueQuery(m_image)) + { + delete [] data; + pclose( file ); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.9 * ( ((float)h)/((float)height) ))); + } + + for (int w = 0; w < width; w++) + { + + fread (src, 6 *sizeof(unsigned char), 1, file); + + dst[0] = (unsigned short)((src[4]*256 + src[5]) * fac); // Blue + dst[1] = (unsigned short)((src[2]*256 + src[3]) * fac); // Green + dst[2] = (unsigned short)((src[0]*256 + src[1]) * fac); // Red + dst[3] = 0xFFFF; + + dst += 4; + } + } + + fclose( file ); + + //---------------------------------------------------------- + + imageWidth() = width; + imageHeight() = height; + imageData() = (uchar*)data; + imageSetAttribute("format", "PPM"); + + return true; +} + +bool PPMLoader::save(const TQString& /*filePath*/, DImgLoaderObserver */*observer*/) +{ + return false; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/ppmloader.h b/src/libs/dimg/loaders/ppmloader.h new file mode 100644 index 00000000..283fdd26 --- /dev/null +++ b/src/libs/dimg/loaders/ppmloader.h @@ -0,0 +1,59 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-21-11 + * Description : A 16 bits/color/pixel PPM IO file for + * DImg framework + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2005-2006 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PPMLOADER_H +#define PPMLOADER_H + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT PPMLoader : public DImgLoader +{ +public: + + PPMLoader(DImg* image); + + bool load(const TQString& filePath, DImgLoaderObserver *observer); + bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const { return false; }; + virtual bool sixteenBit() const { return true; }; + virtual bool isReadOnly() const { return true; }; + +private: + + bool m_alpha; + bool m_sixteenBit; +}; + +} // NameSpace Digikam + +#endif /* PPMLOADER_H */ diff --git a/src/libs/dimg/loaders/qimageloader.cpp b/src/libs/dimg/loaders/qimageloader.cpp new file mode 100644 index 00000000..f35335cf --- /dev/null +++ b/src/libs/dimg/loaders/qimageloader.cpp @@ -0,0 +1,126 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : A TQImage loader for DImg framework. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Caulier Gilles + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "qimageloader.h" + +namespace Digikam +{ + +TQImageLoader::TQImageLoader(DImg* image) + : DImgLoader(image) +{ +} + +bool TQImageLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + // Loading is opaque to us. No support for stopping from observer, + // progress info are only pseudo values + TQImage image(filePath); + + if (observer) + observer->progressInfo(m_image, 0.9); + + if (image.isNull()) + { + DDebug() << "Cannot loading \"" << filePath << "\" using DImg::TQImageLoader!" << endl; + return false; + } + + m_hasAlpha = image.hasAlphaBuffer(); + TQImage target = image.convertDepth(32); + + uint w = target.width(); + uint h = target.height(); + uchar* data = new uchar[w*h*4]; + uint* sptr = (uint*)target.bits(); + uchar* dptr = data; + + for (uint i = 0 ; i < w*h ; i++) + { + dptr[0] = tqBlue(*sptr); + dptr[1] = tqGreen(*sptr); + dptr[2] = tqRed(*sptr); + dptr[3] = tqAlpha(*sptr); + + dptr += 4; + sptr++; + } + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageWidth() = w; + imageHeight() = h; + imageData() = data; + + // We considering that PNG is the most representative format of an image loaded by TQt + imageSetAttribute("format", "PNG"); + + return true; +} + +bool TQImageLoader::save(const TQString& filePath, DImgLoaderObserver *observer) +{ + TQVariant qualityAttr = imageGetAttribute("quality"); + int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; + + if (quality < 0) + quality = 90; + if (quality > 100) + quality = 100; + + TQVariant formatAttr = imageGetAttribute("format"); + TQCString format = formatAttr.toCString(); + + TQImage image = m_image->copyTQImage(); + + if (observer) + observer->progressInfo(m_image, 0.1); + + // Saving is opaque to us. No support for stopping from observer, + // progress info are only pseudo values + bool success = image.save(filePath, format.upper(), quality); + if (observer && success) + observer->progressInfo(m_image, 1.0); + + imageSetAttribute("format", format.upper()); + + return success; +} + +bool TQImageLoader::hasAlpha() const +{ + return m_hasAlpha; +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/qimageloader.h b/src/libs/dimg/loaders/qimageloader.h new file mode 100644 index 00000000..f81cf7ef --- /dev/null +++ b/src/libs/dimg/loaders/qimageloader.h @@ -0,0 +1,57 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-14 + * Description : A TQImage loader for DImg framework. + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2007 by Caulier Gilles + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TQIMAGELOADER_H +#define TQIMAGELOADER_H + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT TQImageLoader : public DImgLoader +{ +public: + + TQImageLoader(DImg* image); + + virtual bool load(const TQString& filePath, DImgLoaderObserver *observer); + virtual bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const; + virtual bool sixteenBit() const { return false; }; + virtual bool isReadOnly() const { return false; }; + +private: + + bool m_hasAlpha; +}; + +} // NameSpace Digikam + +#endif /* TQIMAGELOADER_H */ diff --git a/src/libs/dimg/loaders/rawloader.cpp b/src/libs/dimg/loaders/rawloader.cpp new file mode 100644 index 00000000..8ecaa1f3 --- /dev/null +++ b/src/libs/dimg/loaders/rawloader.cpp @@ -0,0 +1,371 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-01 + * Description : A digital camera RAW files loader for DImg + * framework using an external dcraw instance. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagecurves.h" +#include "imagelevels.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "bcgmodifier.h" +#include "whitebalance.h" +#include "rawloader.h" +#include "rawloader.moc" + +namespace Digikam +{ + +RAWLoader::RAWLoader(DImg* image, DRawDecoding rawDecodingSettings) + : DImgLoader(image) +{ + m_rawDecodingSettings = rawDecodingSettings; + m_customRawSettings = rawDecodingSettings; + m_observer = 0; +} + +bool RAWLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + m_observer = observer; + + // We are using TDEProcess here, and make two assumptions: + // - there is an event loop (not for ioslaves) + // - we are not called from the event loop thread + // These assumptions are currently true for all use cases in digikam, + // except the thumbnails iosalve, which will set this attribute. + // I hope when porting to TQt4, all the event loop stuff (and this problem) can be removed. + if (imageGetAttribute("noeventloop").isValid()) + return false; + + readMetadata(filePath, DImg::RAW); + + // NOTE: Here, we don't check a possible embedded work-space color profile using + // the method checkExifWorkingColorSpace() like with JPEG, PNG, and TIFF loaders, + // because RAW file are always in linear mode. + + int width, height, rgbmax; + TQByteArray data; + if (!KDcrawIface::KDcraw::decodeRAWImage(filePath, m_rawDecodingSettings, + data, width, height, rgbmax)) + return false; + + return (loadedFromDcraw(data, width, height, rgbmax, observer)); +} + +bool RAWLoader::checkToCancelWaitingData() +{ + return (m_observer ? !m_observer->continueQuery(m_image) : false); +} + +void RAWLoader::setWaitingDataProgress(double value) +{ + if (m_observer) + m_observer->progressInfo(m_image, value); +} + +#if KDCRAW_VERSION < 0x000106 +bool RAWLoader::checkToCancelRecievingData() +{ + return (m_observer ? m_observer->isShuttingDown() : false); +} + +void RAWLoader::setRecievingDataProgress(double value) +{ + if (m_observer) + m_observer->progressInfo(m_image, value); +} +#endif + +bool RAWLoader::loadedFromDcraw(TQByteArray data, int width, int height, int rgbmax, + DImgLoaderObserver *observer) +{ + int checkpoint = 0; + + if (m_rawDecodingSettings.sixteenBitsImage) // 16 bits image + { + uchar *image = new uchar[width*height*8]; + + unsigned short *dst = (unsigned short *)image; + uchar *src = (uchar*)data.data(); + float fac = 65535.0 / rgbmax; + checkpoint = 0; + + for (int h = 0; h < height; h++) + { + if (observer && h == checkpoint) + { + checkpoint += granularity(observer, height, 1.0); + if (!observer->continueQuery(m_image)) + { + return false; + } + observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) ); + } + + for (int w = 0; w < width; w++) + { +#if KDCRAW_VERSION < 0x000106 + dst[0] = (unsigned short)((src[4]*256 + src[5]) * fac); // Blue + dst[1] = (unsigned short)((src[2]*256 + src[3]) * fac); // Green + dst[2] = (unsigned short)((src[0]*256 + src[1]) * fac); // Red +#else + dst[0] = (unsigned short)((src[5]*256 + src[4]) * fac); // Blue + dst[1] = (unsigned short)((src[3]*256 + src[2]) * fac); // Green + dst[2] = (unsigned short)((src[1]*256 + src[0]) * fac); // Red +#endif + dst[3] = 0xFFFF; + + dst += 4; + src += 6; + } + } + + +#if KDCRAW_VERSION < 0x000106 + // ---------------------------------------------------------- + + // Special case : if Color Management is not used here, output color space is in sRGB* color space + // RAW decoded image is a linear-histogram image with 16 bits color depth. + // No auto white balance and no gamma adjustemnts are performed. Image is a black hole. + // We need to reproduce all dcraw 8 bits color depth adjustements here. + + if (m_rawDecodingSettings.outputColorSpace != DRawDecoding::RAWCOLOR) + { + ImageHistogram histogram(image, width, height, true); + + int perc, val, total; + float white=0.0, r, gamma=2.222222; + unsigned short lut[65536]; + + // Search 99th percentile white level. + + perc = (int)(width * height * 0.01); + DDebug() << "White Level: " << perc << endl; + for (int c = 1 ; c < 4 ; c++) + { + total = 0; + for (val = 65535 ; val > 256 ; --val) + if ((total += (int)histogram.getValue(c, val)) > perc) + break; + + if (white < val) white = (float)val; + } + + white *= 1.0 / m_rawDecodingSettings.brightness; + + DDebug() << "White Point: " << white << endl; + + // Compute the Gamma lut accordingly. + + for (int i=0; i < 65536; i++) + { + r = i / white; + val = (int)(65536.0 * (r <= 0.018 ? r*4.5 : pow(r, 1.0/gamma) * 1.099-0.099)); + if (val > 65535) val = 65535; + lut[i] = val; + } + + // Apply Gamma lut to the whole image. + + unsigned short *im = (unsigned short *)image; + for (int i = 0; i < width*height; i++) + { + im[0] = lut[im[0]]; // Blue + im[1] = lut[im[1]]; // Green + im[2] = lut[im[2]]; // Red + im += 4; + } + } +#endif + + // ---------------------------------------------------------- + + imageData() = (uchar *)image; + } + else // 8 bits image + { + uchar *image = new uchar[width*height*4]; + uchar *dst = image; + uchar *src = (uchar*)data.data(); + checkpoint = 0; + + for (int h = 0; h < height; h++) + { + + if (observer && h == checkpoint) + { + checkpoint += granularity(observer, height, 1.0); + if (!observer->continueQuery(m_image)) + { + return false; + } + observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) ); + } + + for (int w = 0; w < width; w++) + { + // No need to adapt RGB components accordinly with rgbmax value because dcraw + // always return rgbmax to 255 in 8 bits/color/pixels. + + dst[0] = src[2]; // Blue + dst[1] = src[1]; // Green + dst[2] = src[0]; // Red + dst[3] = 0xFF; // Alpha + + dst += 4; + src += 3; + } + } + + // NOTE: if Color Management is not used here, output color space is in sRGB* color space. + // Gamma and White balance are previously adjusted by dcraw in 8 bits color depth. + + imageData() = image; + } + + //---------------------------------------------------------- + // Assign the right color-space profile. + + TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles"); + switch(m_rawDecodingSettings.outputColorSpace) + { + case DRawDecoding::SRGB: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "srgb.icm"); + m_image->getICCProfilFromFile(directory + "srgb.icm"); + break; + } + case DRawDecoding::ADOBERGB: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "adobergb.icm"); + m_image->getICCProfilFromFile(directory + "adobergb.icm"); + break; + } + case DRawDecoding::WIDEGAMMUT: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "widegamut.icm"); + m_image->getICCProfilFromFile(directory + "widegamut.icm"); + break; + } + case DRawDecoding::PROPHOTO: + { + TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "prophoto.icm"); + m_image->getICCProfilFromFile(directory + "prophoto.icm"); + break; + } + default: + // No icc color-space profile to assign in RAW color mode. + break; + } + + //---------------------------------------------------------- + + + imageWidth() = width; + imageHeight() = height; + imageSetAttribute("format", "RAW"); + + postProcessing(observer); + + return true; +} + +void RAWLoader::postProcessing(DImgLoaderObserver *observer) +{ + if (!m_customRawSettings.postProcessingSettingsIsDirty()) + return; + + if (m_customRawSettings.exposureComp != 0.0 || m_customRawSettings.saturation != 1.0) + { + WhiteBalance wb(m_rawDecodingSettings.sixteenBitsImage); + wb.whiteBalance(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage, + 0.0, // black + m_customRawSettings.exposureComp, // exposure + 6500.0, // temperature (neutral) + 1.0, // green + 0.5, // dark + 1.0, // gamma + m_customRawSettings.saturation); // saturation + } + if (observer) observer->progressInfo(m_image, 0.92); + + if (m_customRawSettings.lightness != 0.0 || + m_customRawSettings.contrast != 1.0 || + m_customRawSettings.gamma != 1.0) + { + BCGModifier bcg; + bcg.setBrightness(m_customRawSettings.lightness); + bcg.setContrast(m_customRawSettings.contrast); + bcg.setGamma(m_customRawSettings.gamma); + bcg.applyBCG(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); + } + if (observer) observer->progressInfo(m_image, 0.94); + + if (!m_customRawSettings.curveAdjust.isEmpty()) + { + DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); + ImageCurves curves(m_rawDecodingSettings.sixteenBitsImage); + curves.setCurvePoints(ImageHistogram::ValueChannel, m_customRawSettings.curveAdjust); + curves.curvesCalculateCurve(ImageHistogram::ValueChannel); + curves.curvesLutSetup(ImageHistogram::AlphaChannel); + curves.curvesLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight()); + memcpy(imageData(), tmp.bits(), tmp.numBytes()); + } + if (observer) observer->progressInfo(m_image, 0.96); + + if (!m_customRawSettings.levelsAdjust.isEmpty()) + { + DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); + ImageLevels levels(m_rawDecodingSettings.sixteenBitsImage); + int j=0; + for (int i = 0 ; i < 4; i++) + { + levels.setLevelLowInputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelHighInputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelLowOutputValue(i, m_customRawSettings.levelsAdjust[j++]); + levels.setLevelHighOutputValue(i, m_customRawSettings.levelsAdjust[j++]); + } + + levels.levelsLutSetup(ImageHistogram::AlphaChannel); + levels.levelsLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight()); + memcpy(imageData(), tmp.bits(), tmp.numBytes()); + } + if (observer) observer->progressInfo(m_image, 0.98); +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/rawloader.h b/src/libs/dimg/loaders/rawloader.h new file mode 100644 index 00000000..22171a20 --- /dev/null +++ b/src/libs/dimg/loaders/rawloader.h @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-11-01 + * Description : A digital camera RAW files loader for DImg + * framework using an external dcraw instance. + * + * Copyright (C) 2005-2008 by Gilles Caulier + * Copyright (C) 2005-2008 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef RAWLOADER_H +#define RAWLOADER_H + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "drawdecoding.h" +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ +class DImg; + +class DIGIKAM_EXPORT RAWLoader : public KDcrawIface::KDcraw, public DImgLoader +{ + TQ_OBJECT + + +public: + + RAWLoader(DImg* image, DRawDecoding rawDecodingSettings=DRawDecoding()); + + bool load(const TQString& filePath, DImgLoaderObserver *observer=0); + + // NOTE: RAW files are always Read only. + bool save(const TQString& /*filePath*/, DImgLoaderObserver */*observer=0*/) { return false; }; + + bool hasAlpha() const { return false; }; + bool isReadOnly() const { return true; }; + bool sixteenBit() const { return m_rawDecodingSettings.sixteenBitsImage; }; + +private: + + // Methods to load RAW image using external dcraw instance. + + bool loadedFromDcraw(TQByteArray data, int width, int height, int rgbmax, + DImgLoaderObserver *observer); + + bool checkToCancelWaitingData(); + void setWaitingDataProgress(double value); + void postProcessing(DImgLoaderObserver *observer); + +#if KDCRAW_VERSION < 0x000106 + bool checkToCancelRecievingData(); + void setRecievingDataProgress(double value); +#endif + +private: + + DImgLoaderObserver *m_observer; + DRawDecoding m_customRawSettings; +}; + +} // NameSpace Digikam + +#endif /* RAWLOADER_H */ diff --git a/src/libs/dimg/loaders/tiffloader.cpp b/src/libs/dimg/loaders/tiffloader.cpp new file mode 100644 index 00000000..2e554143 --- /dev/null +++ b/src/libs/dimg/loaders/tiffloader.cpp @@ -0,0 +1,806 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-17 + * Description : A TIFF IO file for DImg framework + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * Specifications & references: + * - TIFF 6.0 : http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf + * - TIFF/EP : http://www.map.tu.chiba-u.ac.jp/IEC/100/TA2/recdoc/N4378.pdf + * - TIFF/Tags : http://www.awaresystems.be/imaging/tiff/tifftags.html + * - DNG : http://www.adobe.com/products/dng/pdfs/dng_spec.pdf + * + * Others Linux Tiff Loader implementation using libtiff: + * - http://websvn.kde.org/trunk/koffice/filters/krita/tiff/kis_tiff_converter.cc + * - http://artis.inrialpes.fr/Software/TiffIO/ + * - http://cvs.graphicsmagick.org/cgi-bin/cvsweb.cgi/GraphicsMagick/coders/tiff.c + * - http://freeimage.cvs.sourceforge.net/freeimage/FreeImage/Source/FreeImage/PluginTIFF.cpp + * - http://freeimage.cvs.sourceforge.net/freeimage/FreeImage/Source/Metadata/XTIFF.cpp + * - https://subversion.imagemagick.org/subversion/ImageMagick/trunk/coders/tiff.c + * + * Test images repository: + * - http://www.remotesensing.org/libtiff/images.html + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// This line must be commented to prevent any latency time +// when we use threaded image loader interface for each image +// files io. Uncomment this line only for debugging. +//#define ENABLE_DEBUG_MESSAGES + +// C ANSI includes. + +extern "C" +{ +#include +} + +// C++ includes. + +#include + +// TQt includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "dmetadata.h" +#include "tiffloader.h" + +namespace Digikam +{ + +// To manage Errors/Warnings handling provide by libtiff + +void TIFFLoader::dimg_tiff_warning(const char* module, const char* format, va_list warnings) +{ +#ifdef ENABLE_DEBUG_MESSAGES + char message[4096]; + vsnprintf(message, 4096, format, warnings); + DDebug() << module << "::" << message << endl; +#else + Q_UNUSED(module); + Q_UNUSED(format); + Q_UNUSED(warnings); +#endif +} + +void TIFFLoader::dimg_tiff_error(const char* module, const char* format, va_list errors) +{ +#ifdef ENABLE_DEBUG_MESSAGES + char message[4096]; + vsnprintf(message, 4096, format, errors); + DDebug() << module << "::" << message << endl; +#else + Q_UNUSED(module); + Q_UNUSED(format); + Q_UNUSED(errors); +#endif +} + +TIFFLoader::TIFFLoader(DImg* image) + : DImgLoader(image) +{ + m_hasAlpha = false; + m_sixteenBit = false; +} + +bool TIFFLoader::load(const TQString& filePath, DImgLoaderObserver *observer) +{ + readMetadata(filePath, DImg::TIFF); + + // ------------------------------------------------------------------- + // TIFF error handling. If an errors/warnings occurs during reading, + // libtiff will call these methods + + TIFFSetWarningHandler(dimg_tiff_warning); + TIFFSetErrorHandler(dimg_tiff_error); + + // ------------------------------------------------------------------- + // Open the file + + TIFF* tif = TIFFOpen(TQFile::encodeName(filePath), "r"); + if (!tif) + { + DDebug() << k_funcinfo << "Cannot open image file." << endl; + return false; + } + +#ifdef ENABLE_DEBUG_MESSAGES + TIFFPrintDirectory(tif, stdout, 0); +#endif + + // ------------------------------------------------------------------- + // Get image information. + + uint32 w, h; + uint16 bits_per_sample; + uint16 samples_per_pixel; + uint16 photometric; + uint32 rows_per_strip; + tsize_t strip_size; + tstrip_t num_of_strips; + + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, &w); + TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, &h); + + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); + + if (TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip) == 0 || + rows_per_strip == 0 || rows_per_strip == (unsigned int)-1) + { + DWarning() << "TIFF loader: Cannot handle non-stripped images. Loading file " << filePath << endl; + TIFFClose(tif); + return false; + } + + if (bits_per_sample == 0 || samples_per_pixel == 0 || + rows_per_strip == 0 || rows_per_strip > h) + { + DWarning() << "TIFF loader: Encountered invalid value 0 in image." + << " bits_per_sample " << bits_per_sample + << " samples_per_pixel " << samples_per_pixel + << " rows_per_strip " << rows_per_strip + << " Loading file " << filePath << endl; + TIFFClose(tif); + return false; + } + + // TODO: check others TIFF color-spaces here. Actually, only RGB and MINISBLACK + // have been tested. + // Complete description of TIFFTAG_PHOTOMETRIC tag can be found at this url: + // http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html + + TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric); + if (photometric != PHOTOMETRIC_RGB && + photometric != PHOTOMETRIC_MINISBLACK) + { + DWarning() << "Can't handle image without RGB color-space: " + << photometric << endl; + TIFFClose(tif); + return false; + } + + if (samples_per_pixel == 4) + m_hasAlpha = true; + else + m_hasAlpha = false; + + if (bits_per_sample == 16) + m_sixteenBit = true; + else + m_sixteenBit = false; + + // ------------------------------------------------------------------- + // Read image ICC profile + + TQMap& metaData = imageMetaData(); + + uchar *profile_data=0; + uint32 profile_size; + + if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &profile_data)) + { + TQByteArray profile_rawdata(profile_size); + memcpy(profile_rawdata.data(), profile_data, profile_size); + metaData.insert(DImg::ICC, profile_rawdata); + } + else + { + // If ICC profile is null, check Exif metadata. + checkExifWorkingColorSpace(); + } + + // ------------------------------------------------------------------- + // Get image data. + + if (observer) + observer->progressInfo(m_image, 0.1); + + uchar* data = 0; + + strip_size = TIFFStripSize(tif); + num_of_strips = TIFFNumberOfStrips(tif); + + if (bits_per_sample == 16) // 16 bits image. + { + data = new uchar[w*h*8]; + uchar* strip = new uchar[strip_size]; + long offset = 0; + long bytesRead = 0; + + uint checkpoint = 0; + + for (tstrip_t st=0; st < num_of_strips; st++) + { + if (observer && st == checkpoint) + { + checkpoint += granularity(observer, num_of_strips, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + delete [] strip; + TIFFClose(tif); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)st)/((float)num_of_strips) ))); + } + + bytesRead = TIFFReadEncodedStrip(tif, st, strip, strip_size); + + if (bytesRead == -1) + { + DDebug() << k_funcinfo << "Failed to read strip" << endl; + delete [] data; + TIFFClose(tif); + return false; + } + + ushort *stripPtr = (ushort*)(strip); + ushort *dataPtr = (ushort*)(data + offset); + ushort *p; + + // tiff data is read as BGR or ABGR or Greyscale + + if (samples_per_pixel == 3) + { + for (int i=0; i < bytesRead/6; i++) + { + p = dataPtr; + + // See B.K.O #148037 : take a care about byte order with Motorola computers. + if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC + { + p[3] = *stripPtr++; + p[0] = *stripPtr++; + p[1] = *stripPtr++; + p[2] = 0xFFFF; + } + else + { + p[2] = *stripPtr++; + p[1] = *stripPtr++; + p[0] = *stripPtr++; + p[3] = 0xFFFF; + } + + dataPtr += 4; + } + + offset += bytesRead/6 * 8; + } + else if (samples_per_pixel == 1) // See B.K.O #148400: Greyscale pictures only have _one_ sample per pixel + { + for (int i=0; i < bytesRead/2; i++) + { + // We have to read two bytes for one pixel + p = dataPtr; + + // See B.K.O #148037 : take a care about byte order with Motorola computers. + if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC + { + p[3] = 0xFFFF; + p[0] = *stripPtr; + p[1] = *stripPtr; + p[2] = *stripPtr++; + } + else + { + p[0] = *stripPtr; // RGB have to be set to the _same_ value + p[1] = *stripPtr; + p[2] = *stripPtr++; + p[3] = 0xFFFF; // set alpha to 100% + } + dataPtr += 4; + } + + offset += bytesRead*4; // The _byte_offset in the data array is, of course, four times bytesRead + } + else // ABGR + { + for (int i=0; i < bytesRead/8; i++) + { + p = dataPtr; + + // See B.K.O #148037 : take a care about byte order with Motorola computers. + if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC + { + p[3] = *stripPtr++; + p[0] = *stripPtr++; + p[1] = *stripPtr++; + p[2] = *stripPtr++; + } + else + { + p[2] = *stripPtr++; + p[1] = *stripPtr++; + p[0] = *stripPtr++; + p[3] = *stripPtr++; + } + + dataPtr += 4; + } + + offset += bytesRead; + } + } + + delete [] strip; + } + else // Non 16 bits images ==> get it on BGRA 8 bits. + { + data = new uchar[w*h*4]; + uchar* strip = new uchar[w*rows_per_strip*4]; + long offset = 0; + long pixelsRead = 0; + + // this is inspired by TIFFReadRGBAStrip, tif_getimage.c + char emsg[1024] = ""; + TIFFRGBAImage img; + uint32 rows_to_read; + + uint checkpoint = 0; + + // test whether libtiff can read format and initiate reading + + if (!TIFFRGBAImageOK(tif, emsg) || !TIFFRGBAImageBegin(&img, tif, 0, emsg)) + { + DDebug() << k_funcinfo << "Failed to set up RGBA reading of image, filename " + << TIFFFileName(tif) << " error message from Libtiff: " << emsg << endl; + delete [] data; + delete [] strip; + TIFFClose(tif); + return false; + } + + img.req_orientation = ORIENTATION_TOPLEFT; + + // read strips from image: read rows_per_strip, so always start at beginning of a strip + for (uint row = 0; row < h; row += rows_per_strip) + { + if (observer && row >= checkpoint) + { + checkpoint += granularity(observer, h, 0.8); + if (!observer->continueQuery(m_image)) + { + delete [] data; + delete [] strip; + TIFFClose(tif); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)row)/((float)h) ))); + } + + img.row_offset = row; + img.col_offset = 0; + + if( row + rows_per_strip > img.height ) + rows_to_read = img.height - row; + else + rows_to_read = rows_per_strip; + + // Read data + + if (TIFFRGBAImageGet(&img, (uint32*)strip, img.width, rows_to_read ) == -1) + { + DDebug() << k_funcinfo << "Failed to read image data" << endl; + delete [] data; + delete [] strip; + TIFFClose(tif); + return false; + } + + pixelsRead = rows_to_read * img.width; + + uchar *stripPtr = (uchar*)(strip); + uchar *dataPtr = (uchar*)(data + offset); + uchar *p; + + // Reverse red and blue + + for (int i=0; i < pixelsRead; i++) + { + p = dataPtr; + + // See B.K.O #148037 : take a care about byte order with Motorola computers. + if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC + { + p[3] = *stripPtr++; + p[0] = *stripPtr++; + p[1] = *stripPtr++; + p[2] = *stripPtr++; + } + else + { + p[2] = *stripPtr++; + p[1] = *stripPtr++; + p[0] = *stripPtr++; + p[3] = *stripPtr++; + } + + dataPtr += 4; + } + + offset += pixelsRead * 4; + } + + TIFFRGBAImageEnd(&img); + delete [] strip; + } + + // ------------------------------------------------------------------- + + TIFFClose(tif); + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageWidth() = w; + imageHeight() = h; + imageData() = data; + imageSetAttribute("format", "TIFF"); + + return true; +} + +bool TIFFLoader::save(const TQString& filePath, DImgLoaderObserver *observer) +{ + TIFF *tif; + uchar *data; + uint32 w, h; + + w = imageWidth(); + h = imageHeight(); + data = imageData(); + + // ------------------------------------------------------------------- + // TIFF error handling. If an errors/warnings occurs during reading, + // libtiff will call these methods + + TIFFSetWarningHandler(dimg_tiff_warning); + TIFFSetErrorHandler(dimg_tiff_error); + + // ------------------------------------------------------------------- + // Open the file + + tif = TIFFOpen(TQFile::encodeName(filePath), "w"); + + if (!tif) + { + DDebug() << k_funcinfo << "Cannot open target image file." << endl; + return false; + } + + // ------------------------------------------------------------------- + // Set image properties + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); + + // Image must be compressed using deflate algorithm ? + TQVariant compressAttr = imageGetAttribute("compress"); + bool compress = compressAttr.isValid() ? compressAttr.toBool() : false; + + if (compress) + { + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); + TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); + // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL. + // Use horizontal differencing for images which are + // likely to be continuous tone. The TIFF spec says that this + // usually leads to better compression. + // See this url for more details: + // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html + TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); + } + else + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + + // Image has an alpha channel ? + if (imageHasAlpha()) + { + uint16 sampleinfo[1] = { EXTRASAMPLE_UNASSALPHA }; + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); + TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, EXTRASAMPLE_ASSOCALPHA, sampleinfo); + } + else + { + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + } + + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16)imageBitsDepth()); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); + + // ------------------------------------------------------------------- + // Write meta-data Tags contents. + + DMetadata metaData; + metaData.setExif(m_image->getExif()); + metaData.setIptc(m_image->getIptc()); + + // Standard IPTC tag (available with libtiff 3.6.1) + + TQByteArray ba = metaData.getIptc(true); + if (!ba.isEmpty()) + { +#if defined(TIFFTAG_PHOTOSHOP) + TIFFSetField (tif, TIFFTAG_PHOTOSHOP, (uint32)ba.size(), (uchar *)ba.data()); +#endif + } + + // Standard XMP tag (available with libtiff 3.6.1) + +#if defined(TIFFTAG_XMLPACKET) + tiffSetExifDataTag(tif, TIFFTAG_XMLPACKET, &metaData, "Exif.Image.XMLPacket"); +#endif + + // Standard Exif Ascii tags (available with libtiff 3.6.1) + + tiffSetExifAsciiTag(tif, TIFFTAG_DOCUMENTNAME, &metaData, "Exif.Image.DocumentName"); + tiffSetExifAsciiTag(tif, TIFFTAG_IMAGEDESCRIPTION, &metaData, "Exif.Image.ImageDescription"); + tiffSetExifAsciiTag(tif, TIFFTAG_MAKE, &metaData, "Exif.Image.Make"); + tiffSetExifAsciiTag(tif, TIFFTAG_MODEL, &metaData, "Exif.Image.Model"); + tiffSetExifAsciiTag(tif, TIFFTAG_DATETIME, &metaData, "Exif.Image.DateTime"); + tiffSetExifAsciiTag(tif, TIFFTAG_ARTIST, &metaData, "Exif.Image.Artist"); + tiffSetExifAsciiTag(tif, TIFFTAG_COPYRIGHT, &metaData, "Exif.Image.Copyright"); + + TQString soft = metaData.getExifTagString("Exif.Image.Software"); + TQString libtiffver(TIFFLIB_VERSION_STR); + libtiffver.replace('\n', ' '); + soft.append(TQString(" ( %1 )").arg(libtiffver)); + TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)soft.ascii()); + + // NOTE: All others Exif tags will be written by Exiv2 (<= 0.18) + + // ------------------------------------------------------------------- + // Write ICC profil. + + TQByteArray profile_rawdata = m_image->getICCProfil(); + + if (!profile_rawdata.isEmpty()) + { +#if defined(TIFFTAG_ICCPROFILE) + TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32)profile_rawdata.size(), (uchar *)profile_rawdata.data()); +#endif + } + + // ------------------------------------------------------------------- + // Write full image data in tiff directory IFD0 + + if (observer) + observer->progressInfo(m_image, 0.1); + + uint8 *buf=0; + uchar *pixel; + double alpha_factor; + uint32 x, y; + uint8 r8, g8, b8, a8=0; + uint16 r16, g16, b16, a16=0; + int i=0; + + buf = (uint8 *) _TIFFmalloc(TIFFScanlineSize(tif)); + + if (!buf) + { + DDebug() << k_funcinfo << "Cannot allocate memory buffer for main image." << endl; + TIFFClose(tif); + return false; + } + + uint checkpoint = 0; + + for (y = 0; y < h; y++) + { + + if (observer && y == checkpoint) + { + checkpoint += granularity(observer, h, 0.8); + if (!observer->continueQuery(m_image)) + { + _TIFFfree(buf); + TIFFClose(tif); + return false; + } + observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)h) ))); + } + + i = 0; + + for (x = 0; x < w; x++) + { + pixel = &data[((y * w) + x) * imageBytesDepth()]; + + if ( imageSixteenBit() ) // 16 bits image. + { + b16 = (uint16)(pixel[0]+256*pixel[1]); + g16 = (uint16)(pixel[2]+256*pixel[3]); + r16 = (uint16)(pixel[4]+256*pixel[5]); + + if (imageHasAlpha()) + { + // TIFF makes you pre-mutiply the rgb components by alpha + + a16 = (uint16)(pixel[6]+256*pixel[7]); + alpha_factor = ((double)a16 / 65535.0); + r16 = (uint16)(r16*alpha_factor); + g16 = (uint16)(g16*alpha_factor); + b16 = (uint16)(b16*alpha_factor); + } + + // This might be endian dependent + + buf[i++] = (uint8)(r16); + buf[i++] = (uint8)(r16 >> 8); + buf[i++] = (uint8)(g16); + buf[i++] = (uint8)(g16 >> 8); + buf[i++] = (uint8)(b16); + buf[i++] = (uint8)(b16 >> 8); + + if (imageHasAlpha()) + { + buf[i++] = (uint8)(a16) ; + buf[i++] = (uint8)(a16 >> 8) ; + } + } + else // 8 bits image. + { + b8 = (uint8)pixel[0]; + g8 = (uint8)pixel[1]; + r8 = (uint8)pixel[2]; + + if (imageHasAlpha()) + { + // TIFF makes you pre-mutiply the rgb components by alpha + + a8 = (uint8)(pixel[3]); + alpha_factor = ((double)a8 / 255.0); + r8 = (uint8)(r8*alpha_factor); + g8 = (uint8)(g8*alpha_factor); + b8 = (uint8)(b8*alpha_factor); + } + + // This might be endian dependent + + buf[i++] = r8; + buf[i++] = g8; + buf[i++] = b8; + + if (imageHasAlpha()) + buf[i++] = a8; + } + } + + if (!TIFFWriteScanline(tif, buf, y, 0)) + { + DDebug() << k_funcinfo << "Cannot write main image to target file." << endl; + _TIFFfree(buf); + TIFFClose(tif); + return false; + } + } + + _TIFFfree(buf); + TIFFWriteDirectory(tif); + + // ------------------------------------------------------------------- + // Write thumbnail in tiff directory IFD1 + + TQImage thumb = m_image->smoothScale(160, 120, TQSize::ScaleMin).copyTQImage(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)thumb.width()); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)thumb.height()); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); + + uchar *pixelThumb; + uchar *dataThumb = thumb.bits(); + uint8 *bufThumb = (uint8 *) _TIFFmalloc(TIFFScanlineSize(tif)); + + if (!bufThumb) + { + DDebug() << k_funcinfo << "Cannot allocate memory buffer for thumbnail." << endl; + TIFFClose(tif); + return false; + } + + for (y = 0 ; y < uint32(thumb.height()) ; y++) + { + i = 0; + + for (x = 0 ; x < uint32(thumb.width()) ; x++) + { + pixelThumb = &dataThumb[((y * thumb.width()) + x) * 4]; + + // This might be endian dependent + bufThumb[i++] = (uint8)pixelThumb[2]; + bufThumb[i++] = (uint8)pixelThumb[1]; + bufThumb[i++] = (uint8)pixelThumb[0]; + } + + if (!TIFFWriteScanline(tif, bufThumb, y, 0)) + { + DDebug() << k_funcinfo << "Cannot write thumbnail to target file." << endl; + _TIFFfree(bufThumb); + TIFFClose(tif); + return false; + } + } + + _TIFFfree(bufThumb); + TIFFClose(tif); + + // ------------------------------------------------------------------- + + if (observer) + observer->progressInfo(m_image, 1.0); + + imageSetAttribute("savedformat", "TIFF"); + + saveMetadata(filePath); + + return true; +} + +bool TIFFLoader::hasAlpha() const +{ + return m_hasAlpha; +} + +bool TIFFLoader::sixteenBit() const +{ + return m_sixteenBit; +} + +void TIFFLoader::tiffSetExifAsciiTag(TIFF* tif, ttag_t tiffTag, + const DMetadata *metaData, const char* exifTagName) +{ + TQByteArray tag = metaData->getExifTagData(exifTagName); + if (!tag.isEmpty()) + { + TQCString str(tag.data(), tag.size()); + TIFFSetField(tif, tiffTag, (const char*)str); + } +} + +void TIFFLoader::tiffSetExifDataTag(TIFF* tif, ttag_t tiffTag, + const DMetadata *metaData, const char* exifTagName) +{ + TQByteArray tag = metaData->getExifTagData(exifTagName); + if (!tag.isEmpty()) + { + TIFFSetField (tif, tiffTag, (uint32)tag.size(), (char *)tag.data()); + } +} + +} // NameSpace Digikam diff --git a/src/libs/dimg/loaders/tiffloader.h b/src/libs/dimg/loaders/tiffloader.h new file mode 100644 index 00000000..484afd06 --- /dev/null +++ b/src/libs/dimg/loaders/tiffloader.h @@ -0,0 +1,76 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-06-17 + * Description : A TIFF IO file for DImg framework + * + * Copyright (C) 2005 by Renchi Raju + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TIFFLOADER_H +#define TIFFLOADER_H + +// C ansi includes. + +extern "C" +{ +#include +#include +} + +// Local includes. + +#include "dimgloader.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DImg; +class DMetadata; + +class DIGIKAM_EXPORT TIFFLoader : public DImgLoader +{ +public: + + TIFFLoader(DImg* image); + + bool load(const TQString& filePath, DImgLoaderObserver *observer); + bool save(const TQString& filePath, DImgLoaderObserver *observer); + + virtual bool hasAlpha() const; + virtual bool sixteenBit() const; + virtual bool isReadOnly() const { return false; }; + +private: + + void tiffSetExifAsciiTag(TIFF* tif, ttag_t tiffTag, const DMetadata *metaData, const char* exifTagName); + void tiffSetExifDataTag(TIFF* tif, ttag_t tiffTag, const DMetadata *metaData, const char* exifTagName); + + static void dimg_tiff_warning(const char* module, const char* format, va_list warnings); + static void dimg_tiff_error(const char* module, const char* format, va_list errors); + +private: + + bool m_sixteenBit; + bool m_hasAlpha; +}; + +} // NameSpace Digikam + +#endif /* TIFFLOADER_H */ diff --git a/src/libs/dimg/loaders/tiffsettings.cpp b/src/libs/dimg/loaders/tiffsettings.cpp new file mode 100644 index 00000000..3ea7e20c --- /dev/null +++ b/src/libs/dimg/loaders/tiffsettings.cpp @@ -0,0 +1,94 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save TIFF image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// Local includes. + +#include "tiffsettings.h" +#include "tiffsettings.moc" + +namespace Digikam +{ + +class TIFFSettingsPriv +{ + +public: + + TIFFSettingsPriv() + { + TIFFGrid = 0; + TIFFcompression = 0; + } + + TQGridLayout *TIFFGrid; + + TQCheckBox *TIFFcompression; +}; + +TIFFSettings::TIFFSettings(TQWidget *parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new TIFFSettingsPriv; + + d->TIFFGrid = new TQGridLayout(this, 1, 1, KDialog::spacingHint()); + d->TIFFcompression = new TQCheckBox(i18n("Compress TIFF files"), this); + + TQWhatsThis::add( d->TIFFcompression, i18n("

    Toggle compression for TIFF images.

    " + "If you enable this option, you can reduce " + "the final file size of the TIFF image.

    " + "

    A lossless compression format (Deflate) " + "is used to save the file.

    ")); + d->TIFFGrid->addMultiCellWidget(d->TIFFcompression, 0, 0, 0, 1); + d->TIFFGrid->setColStretch(1, 10); +} + +TIFFSettings::~TIFFSettings() +{ + delete d; +} + +void TIFFSettings::setCompression(bool b) +{ + d->TIFFcompression->setChecked(b); +} + +bool TIFFSettings::getCompression() +{ + return d->TIFFcompression->isChecked(); +} + +} // namespace Digikam + diff --git a/src/libs/dimg/loaders/tiffsettings.h b/src/libs/dimg/loaders/tiffsettings.h new file mode 100644 index 00000000..e546ce0f --- /dev/null +++ b/src/libs/dimg/loaders/tiffsettings.h @@ -0,0 +1,60 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-08-02 + * Description : save TIFF image options. + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TIFFSETTINGS_H +#define TIFFSETTINGS_H + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class TIFFSettingsPriv; + +class DIGIKAM_EXPORT TIFFSettings : public TQWidget +{ +TQ_OBJECT + + +public: + + TIFFSettings(TQWidget *parent=0); + ~TIFFSettings(); + + void setCompression(bool b); + bool getCompression(); + +private: + + TIFFSettingsPriv* d; +}; + +} // namespace Digikam + +#endif /* TIFFSETTINGS_H */ diff --git a/src/libs/dmetadata/Makefile.am b/src/libs/dmetadata/Makefile.am new file mode 100644 index 00000000..73ff6010 --- /dev/null +++ b/src/libs/dmetadata/Makefile.am @@ -0,0 +1,18 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libdmetadata.la + +libdmetadata_la_SOURCES = dmetadata.cpp + +libdmetadata_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +libdmetadata_la_LIBADD = $(LIBKEXIV2_LIBS) $(LIBKDCRAW_LIBS) + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/digikam \ + $(LIBKEXIV2_CFLAGS) \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikaminclude_HEADERS = dmetadata.h photoinfocontainer.h +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/dmetadata/dmetadata.cpp b/src/libs/dmetadata/dmetadata.cpp new file mode 100644 index 00000000..bb37506c --- /dev/null +++ b/src/libs/dmetadata/dmetadata.cpp @@ -0,0 +1,544 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-23 + * Description : image metadata interface + * + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "daboutdata.h" +#include "constants.h" +#include "ddebug.h" +#include "dmetadata.h" + +namespace Digikam +{ + +DMetadata::DMetadata() + : KExiv2Iface::KExiv2() +{ +} + +DMetadata::DMetadata(const TQString& filePath) + : KExiv2Iface::KExiv2() +{ + load(filePath); +} + +DMetadata::~DMetadata() +{ +} + +bool DMetadata::load(const TQString& filePath) +{ + // In first, we trying to get metadata using Exiv2, + // else we will use dcraw to extract minimal information. + + if (!KExiv2::load(filePath)) + { + if (!loadUsingDcraw(filePath)) + return false; + } + + return true; +} + +bool DMetadata::loadUsingDcraw(const TQString& filePath) +{ + KDcrawIface::DcrawInfoContainer identify; + if (KDcrawIface::KDcraw::rawFileIdentify(identify, filePath)) + { + long int num=1, den=1; + + if (!identify.model.isNull()) + setExifTagString("Exif.Image.Model", identify.model.latin1(), false); + + if (!identify.make.isNull()) + setExifTagString("Exif.Image.Make", identify.make.latin1(), false); + + if (!identify.owner.isNull()) + setExifTagString("Exif.Image.Artist", identify.owner.latin1(), false); + + if (identify.sensitivity != -1) + setExifTagLong("Exif.Photo.ISOSpeedRatings", identify.sensitivity, false); + + if (identify.dateTime.isValid()) + setImageDateTime(identify.dateTime, false, false); + + if (identify.exposureTime != -1.0) + { + convertToRational(1/identify.exposureTime, &num, &den, 8); + setExifTagRational("Exif.Photo.ExposureTime", num, den, false); + } + + if (identify.aperture != -1.0) + { + convertToRational(identify.aperture, &num, &den, 8); + setExifTagRational("Exif.Photo.ApertureValue", num, den, false); + } + + if (identify.focalLength != -1.0) + { + convertToRational(identify.focalLength, &num, &den, 8); + setExifTagRational("Exif.Photo.FocalLength", num, den, false); + } + + if (identify.imageSize.isValid()) + setImageDimensions(identify.imageSize, false); + + // A RAW image is always uncalibrated. */ + setImageColorWorkSpace(WORKSPACE_UNCALIBRATED, false); + + return true; + } + + return false; +} + +TQString DMetadata::getImageComment() const +{ + if (getFilePath().isEmpty()) + return TQString(); + + // In first we trying to get image comments, outside of Exif and IPTC. + + TQString comment = getCommentsDecoded(); + if (!comment.isEmpty()) + return comment; + + // In second, we trying to get Exif comments + + if (!getExif().isEmpty()) + { + TQString exifComment = getExifComment(); + if (!exifComment.isEmpty()) + return exifComment; + } + + // In third, we trying to get IPTC comments + + if (!getIptc().isEmpty()) + { + TQString iptcComment = getIptcTagString("Iptc.Application2.Caption", false); + if (!iptcComment.isEmpty() && !iptcComment.stripWhiteSpace().isEmpty()) + return iptcComment; + } + + return TQString(); +} + +bool DMetadata::setImageComment(const TQString& comment) +{ + //See bug #139313: An empty string is also a valid value + //if (comment.isEmpty()) + // return false; + + DDebug() << getFilePath() << " ==> Comment: " << comment << endl; + + if (!setProgramId()) + return false; + + // In first we trying to set image comments, outside of Exif and IPTC. + + if (!setComments(comment.utf8())) + return false; + + // In Second we write comments into Exif. + + if (!setExifComment(comment)) + return false; + + // In Third we write comments into Iptc. + // Note that Caption IPTC tag is limited to 2000 char and ASCII charset. + + TQString commentIptc = comment; + commentIptc.truncate(2000); + + if (!setIptcTagString("Iptc.Application2.Caption", commentIptc)) + return false; + + return true; +} + +/* +Iptc.Application2.Urgency <==> digiKam Rating links: + +digiKam IPTC +Rating Urgency + +0 star <=> 8 // Least important +1 star <=> 7 +1 star <== 6 +2 star <=> 5 +3 star <=> 4 +4 star <== 3 +4 star <=> 2 +5 star <=> 1 // Most important +*/ + +int DMetadata::getImageRating() const +{ + if (getFilePath().isEmpty()) + return -1; + + // Check Exif rating tag set by Windows Vista + // Note : no need to check rating in percent tags (Exif.image.0x4747) here because + // its appear always with rating tag value (Exif.image.0x4749). + + if (!getExif().isEmpty()) + { + long rating = -1; + if (getExifTagLong("Exif.Image.0x4746", rating)) + { + if (rating >= RatingMin && rating <= RatingMax) + return rating; + } + } + + // Check Iptc Urgency tag content + + if (!getIptc().isEmpty()) + { + TQString IptcUrgency(getIptcTagData("Iptc.Application2.Urgency")); + + if (!IptcUrgency.isEmpty()) + { + if (IptcUrgency == TQString("1")) + return 5; + else if (IptcUrgency == TQString("2")) + return 4; + else if (IptcUrgency == TQString("3")) + return 4; + else if (IptcUrgency == TQString("4")) + return 3; + else if (IptcUrgency == TQString("5")) + return 2; + else if (IptcUrgency == TQString("6")) + return 1; + else if (IptcUrgency == TQString("7")) + return 1; + else if (IptcUrgency == TQString("8")) + return 0; + } + } + + return -1; +} + +bool DMetadata::setImageRating(int rating) +{ + if (rating < RatingMin || rating > RatingMax) + { + DDebug() << k_funcinfo << "Rating value to write is out of range!" << endl; + return false; + } + + DDebug() << getFilePath() << " ==> Rating: " << rating << endl; + + if (!setProgramId()) + return false; + + // Set Exif rating tag used by Windows Vista. + + if (!setExifTagLong("Exif.Image.0x4746", rating)) + return false; + + // Wrapper around rating percents managed by Windows Vista. + int ratePercents = 0; + switch(rating) + { + case 0: + ratePercents = 0; + break; + case 1: + ratePercents = 1; + break; + case 2: + ratePercents = 25; + break; + case 3: + ratePercents = 50; + break; + case 4: + ratePercents = 75; + break; + case 5: + ratePercents = 99; + break; + } + + if (!setExifTagLong("Exif.Image.0x4749", ratePercents)) + return false; + + // Set Iptc Urgency tag value. + + TQString urgencyTag; + + switch(rating) + { + case 0: + urgencyTag = TQString("8"); + break; + case 1: + urgencyTag = TQString("7"); + break; + case 2: + urgencyTag = TQString("5"); + break; + case 3: + urgencyTag = TQString("4"); + break; + case 4: + urgencyTag = TQString("3"); + break; + case 5: + urgencyTag = TQString("1"); + break; + } + + if (!setIptcTagString("Iptc.Application2.Urgency", urgencyTag)) + return false; + + return true; +} + +bool DMetadata::setIptcTag(const TQString& text, int maxLength, const char* debugLabel, const char* tagKey) +{ + TQString truncatedText = text; + truncatedText.truncate(maxLength); + DDebug() << getFilePath() << " ==> " << debugLabel << ": " << truncatedText << endl; + return setIptcTagString(tagKey, truncatedText); // returns false if failed +} + +bool DMetadata::setImagePhotographerId(const TQString& author, const TQString& authorTitle) +{ + if (!setProgramId()) + return false; + + //TODO Exernalize the hard-coded values + if (!setIptcTag(author, 32, "Author", "Iptc.Application2.Byline")) return false; + if (!setIptcTag(authorTitle, 32, "Author Title", "Iptc.Application2.BylineTitle")) return false; + + return true; +} + +bool DMetadata::setImageCredits(const TQString& credit, const TQString& source, const TQString& copyright) +{ + if (!setProgramId()) + return false; + + //TODO Exernalize the hard-coded values + if (!setIptcTag(credit, 32, "Credit", "Iptc.Application2.Credit")) return false; + if (!setIptcTag(source, 32, "Source", "Iptc.Application2.Source")) return false; + if (!setIptcTag(copyright, 128, "Copyright", "Iptc.Application2.Copyright")) return false; + + return true; +} + +bool DMetadata::setProgramId(bool on) +{ + if (on) + { + TQString version(digikam_version); + TQString software("digiKam"); + return setImageProgramId(software, version); + } + + return true; +} + +PhotoInfoContainer DMetadata::getPhotographInformations() const +{ + PhotoInfoContainer photoInfo; + + if (!getExif().isEmpty()) + { + photoInfo.dateTime = getImageDateTime(); + photoInfo.make = getExifTagString("Exif.Image.Make"); + photoInfo.model = getExifTagString("Exif.Image.Model"); + + photoInfo.aperture = getExifTagString("Exif.Photo.FNumber"); + if (photoInfo.aperture.isEmpty()) + photoInfo.aperture = getExifTagString("Exif.Photo.ApertureValue"); + + photoInfo.exposureTime = getExifTagString("Exif.Photo.ExposureTime"); + if (photoInfo.exposureTime.isEmpty()) + photoInfo.exposureTime = getExifTagString("Exif.Photo.ShutterSpeedValue"); + + photoInfo.exposureMode = getExifTagString("Exif.Photo.ExposureMode"); + photoInfo.exposureProgram = getExifTagString("Exif.Photo.ExposureProgram"); + + photoInfo.focalLength = getExifTagString("Exif.Photo.FocalLength"); + photoInfo.focalLength35mm = getExifTagString("Exif.Photo.FocalLengthIn35mmFilm"); + + photoInfo.sensitivity = getExifTagString("Exif.Photo.ISOSpeedRatings"); + if (photoInfo.sensitivity.isEmpty()) + photoInfo.sensitivity = getExifTagString("Exif.Photo.ExposureIndex"); + + photoInfo.flash = getExifTagString("Exif.Photo.Flash"); + photoInfo.whiteBalance = getExifTagString("Exif.Photo.WhiteBalance"); + } + + return photoInfo; +} + +/** +The following methods set and get an XML dataset into a private IPTC.Application2 tags +to backup digiKam image properties. The XML text data are compressed using zlib and stored +like a byte array. The XML text data format are like below: + + + + + + + + + + + + + +*/ + +bool DMetadata::getXMLImageProperties(TQString& comments, TQDateTime& date, + int& rating, TQStringList& tagsPath) +{ + rating = 0; + + TQByteArray data = getIptcTagData("Iptc.Application2.0x00ff"); + if (data.isEmpty()) + return false; + TQByteArray decompressedData = tqUncompress(data); + TQString doc; + TQDataStream ds(decompressedData, IO_ReadOnly); + ds >> doc; + + TQDomDocument xmlDoc; + TQString error; + int row, col; + if (!xmlDoc.setContent(doc, true, &error, &row, &col)) + { + DDebug() << doc << endl; + DDebug() << error << " :: row=" << row << " , col=" << col << endl; + return false; + } + + TQDomElement rootElem = xmlDoc.documentElement(); + if (rootElem.tagName() != TQString::fromLatin1("digikamproperties")) + return false; + + for (TQDomNode node = rootElem.firstChild(); + !node.isNull(); node = node.nextSibling()) + { + TQDomElement e = node.toElement(); + TQString name = e.tagName(); + TQString val = e.attribute(TQString::fromLatin1("value")); + + if (name == TQString::fromLatin1("comments")) + { + comments = val; + } + else if (name == TQString::fromLatin1("date")) + { + if (val.isEmpty()) continue; + date = TQDateTime::fromString(val, TQt::ISODate); + } + else if (name == TQString::fromLatin1("rating")) + { + if (val.isEmpty()) continue; + bool ok=false; + rating = val.toInt(&ok); + if (!ok) rating = 0; + } + else if (name == TQString::fromLatin1("tagslist")) + { + for (TQDomNode node2 = e.firstChild(); + !node2.isNull(); node2 = node2.nextSibling()) + { + TQDomElement e2 = node2.toElement(); + TQString name2 = e2.tagName(); + TQString val2 = e2.attribute(TQString::fromLatin1("path")); + + if (name2 == TQString::fromLatin1("tag")) + { + if (val2.isEmpty()) continue; + tagsPath.append(val2); + } + } + } + } + + return true; +} + +bool DMetadata::setXMLImageProperties(const TQString& comments, const TQDateTime& date, + int rating, const TQStringList& tagsPath) +{ + TQDomDocument xmlDoc; + + xmlDoc.appendChild(xmlDoc.createProcessingInstruction( TQString::fromLatin1("xml"), + TQString::fromLatin1("version=\"1.0\" encoding=\"UTF-8\"") ) ); + + TQDomElement propertiesElem = xmlDoc.createElement(TQString::fromLatin1("digikamproperties")); + xmlDoc.appendChild( propertiesElem ); + + TQDomElement c = xmlDoc.createElement(TQString::fromLatin1("comments")); + c.setAttribute(TQString::fromLatin1("value"), comments); + propertiesElem.appendChild(c); + + TQDomElement d = xmlDoc.createElement(TQString::fromLatin1("date")); + d.setAttribute(TQString::fromLatin1("value"), date.toString(TQt::ISODate)); + propertiesElem.appendChild(d); + + TQDomElement r = xmlDoc.createElement(TQString::fromLatin1("rating")); + r.setAttribute(TQString::fromLatin1("value"), rating); + propertiesElem.appendChild(r); + + TQDomElement tagsElem = xmlDoc.createElement(TQString::fromLatin1("tagslist")); + propertiesElem.appendChild(tagsElem); + + TQStringList path = tagsPath; + for ( TQStringList::Iterator it = path.begin(); it != path.end(); ++it ) + { + TQDomElement e = xmlDoc.createElement(TQString::fromLatin1("tag")); + e.setAttribute(TQString::fromLatin1("path"), *it); + tagsElem.appendChild(e); + } + + TQByteArray data, compressedData; + TQDataStream ds(data, IO_WriteOnly); + ds << xmlDoc.toString(); + compressedData = tqCompress(data); + return (setIptcTagData("Iptc.Application2.0x00ff", compressedData)); +} + +} // NameSpace Digikam diff --git a/src/libs/dmetadata/dmetadata.h b/src/libs/dmetadata/dmetadata.h new file mode 100644 index 00000000..a33bc61c --- /dev/null +++ b/src/libs/dmetadata/dmetadata.h @@ -0,0 +1,86 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-23 + * Description : image metadata interface + * + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2006-2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef DMETADATA_H +#define DMETADATA_H + +// TQt includes. + +#include + +// LibKExiv2 includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "photoinfocontainer.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT DMetadata : public KExiv2Iface::KExiv2 +{ + +public: + + DMetadata(); + DMetadata(const TQString& filePath); + ~DMetadata(); + + /** Re-implemented from libKexiv2 to use dcraw identify method if Exiv2 failed. */ + bool load(const TQString& filePath); + + /** Try to extract metadata using dcraw identify method */ + bool loadUsingDcraw(const TQString& filePath); + + /** Metadata manipulation methods */ + + TQString getImageComment() const; + bool setImageComment(const TQString& comment); + + int getImageRating() const; + bool setImageRating(int rating); + + bool setImagePhotographerId(const TQString& author, const TQString& authorTitle); + bool setImageCredits(const TQString& credit, const TQString& source, const TQString& copyright); + + PhotoInfoContainer getPhotographInformations() const; + + bool getXMLImageProperties(TQString& comments, TQDateTime& date, + int& rating, TQStringList& tagsPath); + bool setXMLImageProperties(const TQString& comments, const TQDateTime& date, + int rating, const TQStringList& tagsPath); + +private: + + bool setProgramId(bool on=true); + bool setIptcTag(const TQString& text, int maxLength, const char* debugLabel, const char* tagKey); +}; + +} // NameSpace Digikam + +#endif /* DMETADATA_H */ diff --git a/src/libs/dmetadata/photoinfocontainer.h b/src/libs/dmetadata/photoinfocontainer.h new file mode 100644 index 00000000..b008b6b4 --- /dev/null +++ b/src/libs/dmetadata/photoinfocontainer.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-21 + * Description : main photograph information container + * + * Copyright (C) 2006-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef PHOTOINFOCONTAINER_H +#define PHOTOINFOCONTAINER_H + +// TQt includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT PhotoInfoContainer +{ + +public: + + PhotoInfoContainer(){}; + + bool isEmpty() + { + if ( make.isEmpty() && + model.isEmpty() && + exposureTime.isEmpty() && + exposureMode.isEmpty() && + exposureProgram.isEmpty() && + aperture.isEmpty() && + focalLength.isEmpty() && + focalLength35mm.isEmpty() && + sensitivity.isEmpty() && + flash.isEmpty() && + whiteBalance.isEmpty() && + !dateTime.isValid() ) + return true; + else + return false; + }; + + TQString make; + TQString model; + TQString exposureTime; + TQString exposureMode; + TQString exposureProgram; + TQString aperture; + TQString focalLength; + TQString focalLength35mm; + TQString sensitivity; + TQString flash; + TQString whiteBalance; + + TQDateTime dateTime; +}; + +} // namespace Digikam + +#endif /* PHOTOINFOCONTAINER_H */ diff --git a/src/libs/greycstoration/CImg.h b/src/libs/greycstoration/CImg.h new file mode 100644 index 00000000..9f6662e1 --- /dev/null +++ b/src/libs/greycstoration/CImg.h @@ -0,0 +1,36837 @@ +/* + # + # File : CImg.h + # ( C++ header file ) + # + # Description : The C++ Template Image Processing Library. + # This file is the main part of the CImg Library project. + # ( http://cimg.sourceforge.net ) + # + # Project manager : David Tschumperle. + # ( http://www.greyc.ensicaen.fr/~dtschump/ ) + # + # The complete contributor list can be seen in the 'README.txt' file. + # + # Licenses : This file is "dual-licensed", you have to choose one + # of the two licenses below to apply on this file. + # + # CeCILL-C + # The CeCILL-C license is close to the GNU LGPL. + # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html ) + # + # or CeCILL v2.0 + # The CeCILL license is compatible with the GNU GPL. + # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) + # + # This software is governed either by the CeCILL or the CeCILL-C license + # under French law and abiding by the rules of distribution of free software. + # You can use, modify and or redistribute the software under the terms of + # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA + # at the following URL : "http://www.cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. + # +*/ + +// Define version number of the current file. +// +#ifndef cimg_version +#define cimg_version 130 + +/*----------------------------------------------------------- + # + # Test/auto-set CImg configuration variables + # and include required headers. + # + # If you find that default configuration variables are + # not adapted, you can override their values before including + # the header file "CImg.h" (using the #define directive). + # + ------------------------------------------------------------*/ + +// Include required standard C++ headers. +// +#include +#include +#include +#include +#include +#include + +// Operating system configuration. +// +// Define 'cimg_OS' to : 0 for an unknown OS (will try to minize library dependancies). +// 1 for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). +// 2 for Microsoft Windows. +// +#ifndef cimg_OS +#if defined(unix) || defined(__unix) || defined(__unix__) \ + || defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(sun) || defined(__sun) \ + || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__FreeBSD__) || defined __DragonFly__ \ + || defined(sgi) || defined(__sgi) \ + || defined(__MACOSX__) || defined(__APPLE__) \ + || defined(__CYGWIN__) +#define cimg_OS 1 +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +#define cimg_OS 2 +#else +#define cimg_OS 0 +#endif +#elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) +#error CImg Library : Configuration variable 'cimg_OS' is badly defined. +#error (valid values are '0=unknown OS', '1=Unix-like OS', '2=Microsoft Windows'). +#endif + +// Compiler configuration. +// +// Try to detect Microsoft VC++ compilers. +// (lot of workarounds are needed afterwards to +// make CImg working, particularly with VC++ 6.0). +// +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4800) +#pragma warning(disable:4804) +#pragma warning(disable:4996) +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#if _MSC_VER<1300 +#define cimg_use_visualcpp6 +#define cimg_std +#define _WIN32_WINNT 0x0500 +#endif +#endif + +// Include OS-specific headers. +// +#if cimg_OS==1 +#include +#include +#elif cimg_OS==2 +#include +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#endif + +// Define defaut pipe for output messages +// +// Define 'cimg_stdout' to : stdout to print CImg messages on the standard output. +// stderr to print CImg messages on the standart error output (default behavior). +// +#ifndef cimg_std +#define cimg_std std +#endif +#ifndef cimg_stdout +#define cimg_stdout stderr +#endif + +// Output messages configuration. +// +// Define 'cimg_debug' to : 0 to hide debug messages (quiet mode, but exceptions are still thrown). +// 1 to display debug messages on the console. +// 2 to display debug messages with dialog windows (default behavior). +// 3 to do as 1 + add extra warnings (may slow down the code !). +// 4 to do as 2 + add extra warnings (may slow down the code !). +// +// Define 'cimg_strict_warnings' to replace warning messages by exception throwns. +// +// Define 'cimg_use_vt100' to allow output of color messages (require VT100-compatible terminal). +// +#ifndef cimg_debug +#define cimg_debug 2 +#elif !(cimg_debug==0 || cimg_debug==1 || cimg_debug==2 || cimg_debug==3 || cimg_debug==4) +#error CImg Library : Configuration variable 'cimg_debug' is badly defined. +#error (valid values are '0=quiet', '1=console', '2=dialog', '3=console+warnings', '4=dialog+warnings'). +#endif + +// Display framework configuration. +// +// Define 'cimg_display' to : 0 to disable display capabilities. +// 1 to use X-Window framework (X11). +// 2 to use Microsoft GDI32 framework. +// 3 to use Apple Carbon framework. +// +#ifndef cimg_display +#if cimg_OS==0 +#define cimg_display 0 +#elif cimg_OS==1 +#if defined(__MACOSX__) || defined(__APPLE__) +#define cimg_display 1 +#else +#define cimg_display 1 +#endif +#elif cimg_OS==2 +#define cimg_display 2 +#endif +#elif !(cimg_display==0 || cimg_display==1 || cimg_display==2 || cimg_display==3) +#error CImg Library : Configuration variable 'cimg_display' is badly defined. +#error (valid values are '0=disable', '1=X-Window (X11)', '2=Microsoft GDI32', '3=Apple Carbon'). +#endif + +// Include display-specific headers. +// +#if cimg_display==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#elif cimg_display==3 +#include +#include +#endif + +// OpenMP configuration. +// (http://www.openmp.org) +// +// Define 'cimg_use_openmp' to enable OpenMP support. +// +// OpenMP directives can be used in few CImg functions to get +// advantages of multi-core CPUs. Using OpenMP is not mandatory. +// +#ifdef cimg_use_openmp +#include "omp.h" +#endif + +// LibPNG configuration. +// (http://www.libpng.org) +// +// Define 'cimg_use_png' to enable LibPNG support. +// +// LibPNG can be used in functions 'CImg::{load,save}_png()' +// to get a builtin support of PNG files. Using LibPNG is not mandatory. +// +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif + +// LibJPEG configuration. +// (http://en.wikipedia.org/wiki/Libjpeg) +// +// Define 'cimg_use_jpeg' to enable LibJPEG support. +// +// LibJPEG can be used in functions 'CImg::{load,save}_jpeg()' +// to get a builtin support of JPEG files. Using LibJPEG is not mandatory. +// +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +} +#endif + +// LibTIFF configuration. +// (http://www.libtiff.org) +// +// Define 'cimg_use_tiff' to enable LibTIFF support. +// +// LibTIFF can be used in functions 'CImg[List]::{load,save}_tiff()' +// to get a builtin support of TIFF files. Using LibTIFF is not mandatory. +// +#ifdef cimg_use_tiff +extern "C" { +#include "tiffio.h" +} +#endif + +// FFMPEG Avcodec and Avformat libraries configuration. +// (http://www.ffmpeg.org) +// +// Define 'cimg_use_ffmpeg' to enable FFMPEG lib support. +// +// Avcodec and Avformat libraries can be used in functions +// 'CImg[List]::load_ffmpeg()' to get a builtin +// support of various image sequences files. +// Using FFMPEG libraries is not mandatory. +// +#ifdef cimg_use_ffmpeg +extern "C" { +#include "avformat.h" +#include "avcodec.h" +#include "swscale.h" +} +#endif + +// Zlib configuration +// (http://www.zlib.net) +// +// Define 'cimg_use_zlib' to enable Zlib support. +// +// Zlib can be used in functions 'CImg[List]::{load,save}_cimg()' +// to allow compressed data in '.cimg' files. Using Zlib is not mandatory. +// +#ifdef cimg_use_zlib +extern "C" { +#include "zlib.h" +} +#endif + +// Magick++ configuration. +// (http://www.imagemagick.org/Magick++) +// +// Define 'cimg_use_magick' to enable Magick++ support. +// +// Magick++ library can be used in functions 'CImg::{load,save}()' +// to get a builtin support of various image formats (PNG,JPEG,TIFF,...). +// Using Magick++ is not mandatory. +// +#ifdef cimg_use_magick +#include "Magick++.h" +#endif + +// FFTW3 configuration. +// (http://www.fftw.org) +// +// Define 'cimg_use_fftw3' to enable libFFTW3 support. +// +// FFTW3 library can be used in functions 'CImg[List]::FFT()' to +// efficiently compile the Fast Fourier Transform of image data. +// +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +// Board configuration. +// (http://libboard.sourceforge.net/) +// +// Define 'cimg_use_board' to enable Board support. +// +// Board library can be used in functions 'CImg::draw_object3d()' +// to draw objects 3D in vector-graphics canvas that can be saved +// as .PS or .SVG files afterwards. +// +#ifdef cimg_use_board +#include "Board.h" +#endif + +// Lapack configuration. +// (http://www.netlib.org/lapack) +// +// Define 'cimg_use_lapack' to enable LAPACK support. +// +// Lapack can be used in various CImg functions dealing with +// matrix computation and algorithms (eigenvalues, inverse, ...). +// Using Lapack is not mandatory. +// +#ifdef cimg_use_lapack +extern "C" { + extern void sgetrf_(int*, int*, float*, int*, int*, int*); + extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); + extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); + extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); + extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); + extern void dgetrf_(int*, int*, double*, int*, int*, int*); + extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); + extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); + extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*); + extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); +} +#endif + +// Check if min/max macros are defined. +// +// CImg does not compile if macros 'min' or 'max' are defined, +// because min() and max() functions are also defined in the cimg:: namespace. +// so it '#undef' these macros if necessary, and restore them to reasonable +// values at the end of the file. +// +#ifdef min +#undef min +#define _cimg_redefine_min +#endif +#ifdef max +#undef max +#define _cimg_redefine_max +#endif + +// Set the current working directory for native MacOSX bundled applications. +// +// By default, MacOS bundled applications set the cwd at the root directory '/', +// the code below allows to set it to the current exec directory instead when +// a CImg-based program is executed. +// +#if cimg_OS==1 && cimg_display==3 +static struct _cimg_macosx_setcwd { + _cimg_macosx_setcwd() { + FSRef location; + ProcessSerialNumber psn; + char filePath[512]; + if (GetCurrentProcess(&psn)!=noErr) return; + if (GetProcessBundleLocation(&psn,&location)!=noErr) return; + FSRefMakePath(&location,(UInt8*)filePath,sizeof(filePath)-1); + int p = cimg_std::strlen(filePath); + while (filePath[p] != '/') --p; + filePath[p] = 0; + chdir(filePath); + } +} cimg_macosx_setcwd; +#endif + +/*------------------------------------------------------------------------------ + # + # Define user-friendly macros. + # + # User macros are prefixed by 'cimg_' and can be used in your own code. + # They are particularly useful for option parsing, and image loops creation. + # + ------------------------------------------------------------------------------*/ + +// Define the program usage, and retrieve command line arguments. +// +#define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage) +#define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage) +#define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv) +#define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0) +#define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1) +#define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2) +#define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) +#define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) +#define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) +#define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) +#define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) +#define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) + +// Define and manipulate local neighborhoods. +// +#define CImg_2x2(I,T) T I[4]; \ + T& I##cc = I[0]; T& I##nc = I[1]; \ + T& I##cn = I[2]; T& I##nn = I[3]; \ + I##cc = I##nc = \ + I##cn = I##nn = 0 + +#define CImg_3x3(I,T) T I[9]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ + T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ + T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ + I##pp = I##cp = I##np = \ + I##pc = I##cc = I##nc = \ + I##pn = I##cn = I##nn = 0 + +#define CImg_4x4(I,T) T I[16]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ + T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ + T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ + T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ + I##pp = I##cp = I##np = I##ap = \ + I##pc = I##cc = I##nc = I##ac = \ + I##pn = I##cn = I##nn = I##an = \ + I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_5x5(I,T) T I[25]; \ + T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ + T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ + T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ + T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ + T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ + I##bb = I##pb = I##cb = I##nb = I##ab = \ + I##bp = I##pp = I##cp = I##np = I##ap = \ + I##bc = I##pc = I##cc = I##nc = I##ac = \ + I##bn = I##pn = I##cn = I##nn = I##an = \ + I##ba = I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_2x2x2(I,T) T I[8]; \ + T& I##ccc = I[0]; T& I##ncc = I[1]; \ + T& I##cnc = I[2]; T& I##nnc = I[3]; \ + T& I##ccn = I[4]; T& I##ncn = I[5]; \ + T& I##cnn = I[6]; T& I##nnn = I[7]; \ + I##ccc = I##ncc = \ + I##cnc = I##nnc = \ + I##ccn = I##ncn = \ + I##cnn = I##nnn = 0 + +#define CImg_3x3x3(I,T) T I[27]; \ + T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ + T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ + T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ + T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ + T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ + T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ + T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ + T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ + T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ + I##ppp = I##cpp = I##npp = \ + I##pcp = I##ccp = I##ncp = \ + I##pnp = I##cnp = I##nnp = \ + I##ppc = I##cpc = I##npc = \ + I##pcc = I##ccc = I##ncc = \ + I##pnc = I##cnc = I##nnc = \ + I##ppn = I##cpn = I##npn = \ + I##pcn = I##ccn = I##ncn = \ + I##pnn = I##cnn = I##nnn = 0 + +#define cimg_get2x2(img,x,y,z,v,I) \ + I[0] = (img)(x,y,z,v), I[1] = (img)(_n1##x,y,z,v), I[2] = (img)(x,_n1##y,z,v), I[3] = (img)(_n1##x,_n1##y,z,v) + +#define cimg_get3x3(img,x,y,z,v,I) \ + I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), I[3] = (img)(_p1##x,y,z,v), \ + I[4] = (img)(x,y,z,v), I[5] = (img)(_n1##x,y,z,v), I[6] = (img)(_p1##x,_n1##y,z,v), I[7] = (img)(x,_n1##y,z,v), \ + I[8] = (img)(_n1##x,_n1##y,z,v) + +#define cimg_get4x4(img,x,y,z,v,I) \ + I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), I[3] = (img)(_n2##x,_p1##y,z,v), \ + I[4] = (img)(_p1##x,y,z,v), I[5] = (img)(x,y,z,v), I[6] = (img)(_n1##x,y,z,v), I[7] = (img)(_n2##x,y,z,v), \ + I[8] = (img)(_p1##x,_n1##y,z,v), I[9] = (img)(x,_n1##y,z,v), I[10] = (img)(_n1##x,_n1##y,z,v), I[11] = (img)(_n2##x,_n1##y,z,v), \ + I[12] = (img)(_p1##x,_n2##y,z,v), I[13] = (img)(x,_n2##y,z,v), I[14] = (img)(_n1##x,_n2##y,z,v), I[15] = (img)(_n2##x,_n2##y,z,v) + +#define cimg_get5x5(img,x,y,z,v,I) \ + I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), \ + I[4] = (img)(_n2##x,_p2##y,z,v), I[5] = (img)(_p2##x,_p1##y,z,v), I[6] = (img)(_p1##x,_p1##y,z,v), I[7] = (img)(x,_p1##y,z,v), \ + I[8] = (img)(_n1##x,_p1##y,z,v), I[9] = (img)(_n2##x,_p1##y,z,v), I[10] = (img)(_p2##x,y,z,v), I[11] = (img)(_p1##x,y,z,v), \ + I[12] = (img)(x,y,z,v), I[13] = (img)(_n1##x,y,z,v), I[14] = (img)(_n2##x,y,z,v), I[15] = (img)(_p2##x,_n1##y,z,v), \ + I[16] = (img)(_p1##x,_n1##y,z,v), I[17] = (img)(x,_n1##y,z,v), I[18] = (img)(_n1##x,_n1##y,z,v), I[19] = (img)(_n2##x,_n1##y,z,v), \ + I[20] = (img)(_p2##x,_n2##y,z,v), I[21] = (img)(_p1##x,_n2##y,z,v), I[22] = (img)(x,_n2##y,z,v), I[23] = (img)(_n1##x,_n2##y,z,v), \ + I[24] = (img)(_n2##x,_n2##y,z,v) + +#define cimg_get6x6(img,x,y,z,v,I) \ + I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), \ + I[4] = (img)(_n2##x,_p2##y,z,v), I[5] = (img)(_n3##x,_p2##y,z,v), I[6] = (img)(_p2##x,_p1##y,z,v), I[7] = (img)(_p1##x,_p1##y,z,v), \ + I[8] = (img)(x,_p1##y,z,v), I[9] = (img)(_n1##x,_p1##y,z,v), I[10] = (img)(_n2##x,_p1##y,z,v), I[11] = (img)(_n3##x,_p1##y,z,v), \ + I[12] = (img)(_p2##x,y,z,v), I[13] = (img)(_p1##x,y,z,v), I[14] = (img)(x,y,z,v), I[15] = (img)(_n1##x,y,z,v), \ + I[16] = (img)(_n2##x,y,z,v), I[17] = (img)(_n3##x,y,z,v), I[18] = (img)(_p2##x,_n1##y,z,v), I[19] = (img)(_p1##x,_n1##y,z,v), \ + I[20] = (img)(x,_n1##y,z,v), I[21] = (img)(_n1##x,_n1##y,z,v), I[22] = (img)(_n2##x,_n1##y,z,v), I[23] = (img)(_n3##x,_n1##y,z,v), \ + I[24] = (img)(_p2##x,_n2##y,z,v), I[25] = (img)(_p1##x,_n2##y,z,v), I[26] = (img)(x,_n2##y,z,v), I[27] = (img)(_n1##x,_n2##y,z,v), \ + I[28] = (img)(_n2##x,_n2##y,z,v), I[29] = (img)(_n3##x,_n2##y,z,v), I[30] = (img)(_p2##x,_n3##y,z,v), I[31] = (img)(_p1##x,_n3##y,z,v), \ + I[32] = (img)(x,_n3##y,z,v), I[33] = (img)(_n1##x,_n3##y,z,v), I[34] = (img)(_n2##x,_n3##y,z,v), I[35] = (img)(_n3##x,_n3##y,z,v) + +#define cimg_get7x7(img,x,y,z,v,I) \ + I[0] = (img)(_p3##x,_p3##y,z,v), I[1] = (img)(_p2##x,_p3##y,z,v), I[2] = (img)(_p1##x,_p3##y,z,v), I[3] = (img)(x,_p3##y,z,v), \ + I[4] = (img)(_n1##x,_p3##y,z,v), I[5] = (img)(_n2##x,_p3##y,z,v), I[6] = (img)(_n3##x,_p3##y,z,v), I[7] = (img)(_p3##x,_p2##y,z,v), \ + I[8] = (img)(_p2##x,_p2##y,z,v), I[9] = (img)(_p1##x,_p2##y,z,v), I[10] = (img)(x,_p2##y,z,v), I[11] = (img)(_n1##x,_p2##y,z,v), \ + I[12] = (img)(_n2##x,_p2##y,z,v), I[13] = (img)(_n3##x,_p2##y,z,v), I[14] = (img)(_p3##x,_p1##y,z,v), I[15] = (img)(_p2##x,_p1##y,z,v), \ + I[16] = (img)(_p1##x,_p1##y,z,v), I[17] = (img)(x,_p1##y,z,v), I[18] = (img)(_n1##x,_p1##y,z,v), I[19] = (img)(_n2##x,_p1##y,z,v), \ + I[20] = (img)(_n3##x,_p1##y,z,v), I[21] = (img)(_p3##x,y,z,v), I[22] = (img)(_p2##x,y,z,v), I[23] = (img)(_p1##x,y,z,v), \ + I[24] = (img)(x,y,z,v), I[25] = (img)(_n1##x,y,z,v), I[26] = (img)(_n2##x,y,z,v), I[27] = (img)(_n3##x,y,z,v), \ + I[28] = (img)(_p3##x,_n1##y,z,v), I[29] = (img)(_p2##x,_n1##y,z,v), I[30] = (img)(_p1##x,_n1##y,z,v), I[31] = (img)(x,_n1##y,z,v), \ + I[32] = (img)(_n1##x,_n1##y,z,v), I[33] = (img)(_n2##x,_n1##y,z,v), I[34] = (img)(_n3##x,_n1##y,z,v), I[35] = (img)(_p3##x,_n2##y,z,v), \ + I[36] = (img)(_p2##x,_n2##y,z,v), I[37] = (img)(_p1##x,_n2##y,z,v), I[38] = (img)(x,_n2##y,z,v), I[39] = (img)(_n1##x,_n2##y,z,v), \ + I[40] = (img)(_n2##x,_n2##y,z,v), I[41] = (img)(_n3##x,_n2##y,z,v), I[42] = (img)(_p3##x,_n3##y,z,v), I[43] = (img)(_p2##x,_n3##y,z,v), \ + I[44] = (img)(_p1##x,_n3##y,z,v), I[45] = (img)(x,_n3##y,z,v), I[46] = (img)(_n1##x,_n3##y,z,v), I[47] = (img)(_n2##x,_n3##y,z,v), \ + I[48] = (img)(_n3##x,_n3##y,z,v) + +#define cimg_get8x8(img,x,y,z,v,I) \ + I[0] = (img)(_p3##x,_p3##y,z,v), I[1] = (img)(_p2##x,_p3##y,z,v), I[2] = (img)(_p1##x,_p3##y,z,v), I[3] = (img)(x,_p3##y,z,v), \ + I[4] = (img)(_n1##x,_p3##y,z,v), I[5] = (img)(_n2##x,_p3##y,z,v), I[6] = (img)(_n3##x,_p3##y,z,v), I[7] = (img)(_n4##x,_p3##y,z,v), \ + I[8] = (img)(_p3##x,_p2##y,z,v), I[9] = (img)(_p2##x,_p2##y,z,v), I[10] = (img)(_p1##x,_p2##y,z,v), I[11] = (img)(x,_p2##y,z,v), \ + I[12] = (img)(_n1##x,_p2##y,z,v), I[13] = (img)(_n2##x,_p2##y,z,v), I[14] = (img)(_n3##x,_p2##y,z,v), I[15] = (img)(_n4##x,_p2##y,z,v), \ + I[16] = (img)(_p3##x,_p1##y,z,v), I[17] = (img)(_p2##x,_p1##y,z,v), I[18] = (img)(_p1##x,_p1##y,z,v), I[19] = (img)(x,_p1##y,z,v), \ + I[20] = (img)(_n1##x,_p1##y,z,v), I[21] = (img)(_n2##x,_p1##y,z,v), I[22] = (img)(_n3##x,_p1##y,z,v), I[23] = (img)(_n4##x,_p1##y,z,v), \ + I[24] = (img)(_p3##x,y,z,v), I[25] = (img)(_p2##x,y,z,v), I[26] = (img)(_p1##x,y,z,v), I[27] = (img)(x,y,z,v), \ + I[28] = (img)(_n1##x,y,z,v), I[29] = (img)(_n2##x,y,z,v), I[30] = (img)(_n3##x,y,z,v), I[31] = (img)(_n4##x,y,z,v), \ + I[32] = (img)(_p3##x,_n1##y,z,v), I[33] = (img)(_p2##x,_n1##y,z,v), I[34] = (img)(_p1##x,_n1##y,z,v), I[35] = (img)(x,_n1##y,z,v), \ + I[36] = (img)(_n1##x,_n1##y,z,v), I[37] = (img)(_n2##x,_n1##y,z,v), I[38] = (img)(_n3##x,_n1##y,z,v), I[39] = (img)(_n4##x,_n1##y,z,v), \ + I[40] = (img)(_p3##x,_n2##y,z,v), I[41] = (img)(_p2##x,_n2##y,z,v), I[42] = (img)(_p1##x,_n2##y,z,v), I[43] = (img)(x,_n2##y,z,v), \ + I[44] = (img)(_n1##x,_n2##y,z,v), I[45] = (img)(_n2##x,_n2##y,z,v), I[46] = (img)(_n3##x,_n2##y,z,v), I[47] = (img)(_n4##x,_n2##y,z,v), \ + I[48] = (img)(_p3##x,_n3##y,z,v), I[49] = (img)(_p2##x,_n3##y,z,v), I[50] = (img)(_p1##x,_n3##y,z,v), I[51] = (img)(x,_n3##y,z,v), \ + I[52] = (img)(_n1##x,_n3##y,z,v), I[53] = (img)(_n2##x,_n3##y,z,v), I[54] = (img)(_n3##x,_n3##y,z,v), I[55] = (img)(_n4##x,_n3##y,z,v), \ + I[56] = (img)(_p3##x,_n4##y,z,v), I[57] = (img)(_p2##x,_n4##y,z,v), I[58] = (img)(_p1##x,_n4##y,z,v), I[59] = (img)(x,_n4##y,z,v), \ + I[60] = (img)(_n1##x,_n4##y,z,v), I[61] = (img)(_n2##x,_n4##y,z,v), I[62] = (img)(_n3##x,_n4##y,z,v), I[63] = (img)(_n4##x,_n4##y,z,v); + +#define cimg_get9x9(img,x,y,z,v,I) \ + I[0] = (img)(_p4##x,_p4##y,z,v), I[1] = (img)(_p3##x,_p4##y,z,v), I[2] = (img)(_p2##x,_p4##y,z,v), I[3] = (img)(_p1##x,_p4##y,z,v), \ + I[4] = (img)(x,_p4##y,z,v), I[5] = (img)(_n1##x,_p4##y,z,v), I[6] = (img)(_n2##x,_p4##y,z,v), I[7] = (img)(_n3##x,_p4##y,z,v), \ + I[8] = (img)(_n4##x,_p4##y,z,v), I[9] = (img)(_p4##x,_p3##y,z,v), I[10] = (img)(_p3##x,_p3##y,z,v), I[11] = (img)(_p2##x,_p3##y,z,v), \ + I[12] = (img)(_p1##x,_p3##y,z,v), I[13] = (img)(x,_p3##y,z,v), I[14] = (img)(_n1##x,_p3##y,z,v), I[15] = (img)(_n2##x,_p3##y,z,v), \ + I[16] = (img)(_n3##x,_p3##y,z,v), I[17] = (img)(_n4##x,_p3##y,z,v), I[18] = (img)(_p4##x,_p2##y,z,v), I[19] = (img)(_p3##x,_p2##y,z,v), \ + I[20] = (img)(_p2##x,_p2##y,z,v), I[21] = (img)(_p1##x,_p2##y,z,v), I[22] = (img)(x,_p2##y,z,v), I[23] = (img)(_n1##x,_p2##y,z,v), \ + I[24] = (img)(_n2##x,_p2##y,z,v), I[25] = (img)(_n3##x,_p2##y,z,v), I[26] = (img)(_n4##x,_p2##y,z,v), I[27] = (img)(_p4##x,_p1##y,z,v), \ + I[28] = (img)(_p3##x,_p1##y,z,v), I[29] = (img)(_p2##x,_p1##y,z,v), I[30] = (img)(_p1##x,_p1##y,z,v), I[31] = (img)(x,_p1##y,z,v), \ + I[32] = (img)(_n1##x,_p1##y,z,v), I[33] = (img)(_n2##x,_p1##y,z,v), I[34] = (img)(_n3##x,_p1##y,z,v), I[35] = (img)(_n4##x,_p1##y,z,v), \ + I[36] = (img)(_p4##x,y,z,v), I[37] = (img)(_p3##x,y,z,v), I[38] = (img)(_p2##x,y,z,v), I[39] = (img)(_p1##x,y,z,v), \ + I[40] = (img)(x,y,z,v), I[41] = (img)(_n1##x,y,z,v), I[42] = (img)(_n2##x,y,z,v), I[43] = (img)(_n3##x,y,z,v), \ + I[44] = (img)(_n4##x,y,z,v), I[45] = (img)(_p4##x,_n1##y,z,v), I[46] = (img)(_p3##x,_n1##y,z,v), I[47] = (img)(_p2##x,_n1##y,z,v), \ + I[48] = (img)(_p1##x,_n1##y,z,v), I[49] = (img)(x,_n1##y,z,v), I[50] = (img)(_n1##x,_n1##y,z,v), I[51] = (img)(_n2##x,_n1##y,z,v), \ + I[52] = (img)(_n3##x,_n1##y,z,v), I[53] = (img)(_n4##x,_n1##y,z,v), I[54] = (img)(_p4##x,_n2##y,z,v), I[55] = (img)(_p3##x,_n2##y,z,v), \ + I[56] = (img)(_p2##x,_n2##y,z,v), I[57] = (img)(_p1##x,_n2##y,z,v), I[58] = (img)(x,_n2##y,z,v), I[59] = (img)(_n1##x,_n2##y,z,v), \ + I[60] = (img)(_n2##x,_n2##y,z,v), I[61] = (img)(_n3##x,_n2##y,z,v), I[62] = (img)(_n4##x,_n2##y,z,v), I[63] = (img)(_p4##x,_n3##y,z,v), \ + I[64] = (img)(_p3##x,_n3##y,z,v), I[65] = (img)(_p2##x,_n3##y,z,v), I[66] = (img)(_p1##x,_n3##y,z,v), I[67] = (img)(x,_n3##y,z,v), \ + I[68] = (img)(_n1##x,_n3##y,z,v), I[69] = (img)(_n2##x,_n3##y,z,v), I[70] = (img)(_n3##x,_n3##y,z,v), I[71] = (img)(_n4##x,_n3##y,z,v), \ + I[72] = (img)(_p4##x,_n4##y,z,v), I[73] = (img)(_p3##x,_n4##y,z,v), I[74] = (img)(_p2##x,_n4##y,z,v), I[75] = (img)(_p1##x,_n4##y,z,v), \ + I[76] = (img)(x,_n4##y,z,v), I[77] = (img)(_n1##x,_n4##y,z,v), I[78] = (img)(_n2##x,_n4##y,z,v), I[79] = (img)(_n3##x,_n4##y,z,v), \ + I[80] = (img)(_n4##x,_n4##y,z,v) + +#define cimg_get2x2x2(img,x,y,z,v,I) \ + I[0] = (img)(x,y,z,v), I[1] = (img)(_n1##x,y,z,v), I[2] = (img)(x,_n1##y,z,v), I[3] = (img)(_n1##x,_n1##y,z,v), \ + I[4] = (img)(x,y,_n1##z,v), I[5] = (img)(_n1##x,y,_n1##z,v), I[6] = (img)(x,_n1##y,_n1##z,v), I[7] = (img)(_n1##x,_n1##y,_n1##z,v) + +#define cimg_get3x3x3(img,x,y,z,v,I) \ + I[0] = (img)(_p1##x,_p1##y,_p1##z,v), I[1] = (img)(x,_p1##y,_p1##z,v), I[2] = (img)(_n1##x,_p1##y,_p1##z,v), \ + I[3] = (img)(_p1##x,y,_p1##z,v), I[4] = (img)(x,y,_p1##z,v), I[5] = (img)(_n1##x,y,_p1##z,v), \ + I[6] = (img)(_p1##x,_n1##y,_p1##z,v), I[7] = (img)(x,_n1##y,_p1##z,v), I[8] = (img)(_n1##x,_n1##y,_p1##z,v), \ + I[9] = (img)(_p1##x,_p1##y,z,v), I[10] = (img)(x,_p1##y,z,v), I[11] = (img)(_n1##x,_p1##y,z,v), \ + I[12] = (img)(_p1##x,y,z,v), I[13] = (img)(x,y,z,v), I[14] = (img)(_n1##x,y,z,v), \ + I[15] = (img)(_p1##x,_n1##y,z,v), I[16] = (img)(x,_n1##y,z,v), I[17] = (img)(_n1##x,_n1##y,z,v), \ + I[18] = (img)(_p1##x,_p1##y,_n1##z,v), I[19] = (img)(x,_p1##y,_n1##z,v), I[20] = (img)(_n1##x,_p1##y,_n1##z,v), \ + I[21] = (img)(_p1##x,y,_n1##z,v), I[22] = (img)(x,y,_n1##z,v), I[23] = (img)(_n1##x,y,_n1##z,v), \ + I[24] = (img)(_p1##x,_n1##y,_n1##z,v), I[25] = (img)(x,_n1##y,_n1##z,v), I[26] = (img)(_n1##x,_n1##y,_n1##z,v) + +// Define various image loops. +// +// These macros generally avoid the use of iterators, but you are not forced to used them ! +// +#define cimg_for(img,ptr,T_ptr) for (T_ptr *ptr = (img).data + (img).size(); (ptr--)>(img).data; ) +#define cimg_foroff(img,off) for (unsigned int off = 0, _max##off = (unsigned int)(img).size(); off<_max##off; ++off) +#define cimglist_for(list,l) for (unsigned int l=0; l<(list).size; ++l) +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn + +#define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) +#define cimg_forX(img,x) cimg_for1((img).width,x) +#define cimg_forY(img,y) cimg_for1((img).height,y) +#define cimg_forZ(img,z) cimg_for1((img).depth,z) +#define cimg_forV(img,v) cimg_for1((img).dim,v) +#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) +#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) +#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) +#define cimg_forXV(img,x,v) cimg_forV(img,v) cimg_forX(img,x) +#define cimg_forYV(img,y,v) cimg_forV(img,v) cimg_forY(img,y) +#define cimg_forZV(img,z,v) cimg_forV(img,v) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYV(img,x,y,v) cimg_forV(img,v) cimg_forXY(img,x,y) +#define cimg_forXZV(img,x,z,v) cimg_forV(img,v) cimg_forXZ(img,x,z) +#define cimg_forYZV(img,y,z,v) cimg_forV(img,v) cimg_forYZ(img,y,z) +#define cimg_forXYZV(img,x,y,z,v) cimg_forV(img,v) cimg_forXYZ(img,x,y,z) + +#define cimg_for_in1(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i) +#define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img).width,x0,x1,x) +#define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img).height,y0,y1,y) +#define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img).depth,z0,z1,z) +#define cimg_for_inV(img,v0,v1,v) cimg_for_in1((img).dim,v0,v1,v) +#define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXV(img,x0,v0,x1,v1,x,v) cimg_for_inV(img,v0,v1,v) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inYV(img,y0,v0,y1,v1,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inZV(img,z0,v0,z1,v1,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inZ(img,z0,z1,z) +#define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXYV(img,x0,y0,v0,x1,y1,v1,x,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXZV(img,x0,z0,v0,x1,z1,v1,x,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) +#define cimg_for_inYZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_inXYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img).width-1-(n),x) +#define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img).height-1-(n),y) +#define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img).depth-1-(n),z) +#define cimg_for_insideV(img,v,n) cimg_for_inV(img,n,(img).dim-1-(n),v) +#define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) +#define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) +#define cimg_for_insideXYZV(img,x,y,z,v,n) cimg_for_inXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) + +#define cimg_for_out1(boundi,i0,i1,i) \ + for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i) +#define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ + for (int j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ + ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i)) +#define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ + for (int k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ + ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i)) +#define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ + for (int l = 0; l<(int)(boundl); ++l) \ + for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \ + ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i)) +#define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img).width,x0,x1,x) +#define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img).height,y0,y1,y) +#define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img).depth,z0,z1,z) +#define cimg_for_outV(img,v0,v1,v) cimg_for_out1((img).dim,v0,v1,v) +#define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img).width,(img).height,x0,y0,x1,y1,x,y) +#define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img).width,(img).depth,x0,z0,x1,z1,x,z) +#define cimg_for_outXV(img,x0,v0,x1,v1,x,v) cimg_for_out2((img).width,(img).dim,x0,v0,x1,v1,x,v) +#define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img).height,(img).depth,y0,z0,y1,z1,y,z) +#define cimg_for_outYV(img,y0,v0,y1,v1,y,v) cimg_for_out2((img).height,(img).dim,y0,v0,y1,v1,y,v) +#define cimg_for_outZV(img,z0,v0,z1,v1,z,v) cimg_for_out2((img).depth,(img).dim,z0,v0,z1,v1,z,v) +#define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_out3((img).width,(img).height,(img).depth,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_outXYV(img,x0,y0,v0,x1,y1,v1,x,y,v) cimg_for_out3((img).width,(img).height,(img).dim,x0,y0,v0,x1,y1,v1,x,y,v) +#define cimg_for_outXZV(img,x0,z0,v0,x1,z1,v1,x,z,v) cimg_for_out3((img).width,(img).depth,(img).dim,x0,z0,v0,x1,z1,v1,x,z,v) +#define cimg_for_outYZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_out3((img).height,(img).depth,(img).dim,y0,z0,v0,y1,z1,v1,y,z,v) +#define cimg_for_outXYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) \ + cimg_for_out4((img).width,(img).height,(img).depth,(img).dim,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) +#define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img).width-1-(n),x) +#define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img).height-1-(n),y) +#define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img).depth-1-(n),z) +#define cimg_for_borderV(img,v,n) cimg_for_outV(img,n,(img).dim-1-(n),v) +#define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) +#define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) +#define cimg_for_borderXYZV(img,x,y,z,v,n) \ + cimg_for_outXYZV(img,n,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),(img).dim-1-(n),x,y,z,v) + +#define cimg_for_spiralXY(img,x,y) \ + for (int x = 0, y = 0, _n1##x = 1, _n1##y = (int)((img).width*(img).height); _n1##y; \ + --_n1##y, _n1##x += (_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img).width-1-++x:((_n1##x&3)==2?(img).height-1-++y:--x))))?0:1) + +#define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ + for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ + _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \ + _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \ + _counter = _dx, \ + _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ + _counter>=0; \ + --_counter, x+=_steep? \ + (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ + (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) + +#define cimg_for2(bound,i) \ + for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + ++i, ++_n1##i) +#define cimg_for2X(img,x) cimg_for2((img).width,x) +#define cimg_for2Y(img,y) cimg_for2((img).height,y) +#define cimg_for2Z(img,z) cimg_for2((img).depth,z) +#define cimg_for2V(img,v) cimg_for2((img).dim,v) +#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) +#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) +#define cimg_for2XV(img,x,v) cimg_for2V(img,v) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2YV(img,y,v) cimg_for2V(img,v) cimg_for2Y(img,y) +#define cimg_for2ZV(img,z,v) cimg_for2V(img,v) cimg_for2Z(img,z) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for2XZV(img,x,z,v) cimg_for2V(img,v) cimg_for2XZ(img,x,z) +#define cimg_for2YZV(img,y,z,v) cimg_for2V(img,v) cimg_for2YZ(img,y,z) +#define cimg_for2XYZV(img,x,y,z,v) cimg_for2V(img,v) cimg_for2XYZ(img,x,y,z) + +#define cimg_for_in2(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + ++i, ++_n1##i) +#define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img).width,x0,x1,x) +#define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img).height,y0,y1,y) +#define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img).depth,z0,z1,z) +#define cimg_for_in2V(img,v0,v1,v) cimg_for_in2((img).dim,v0,v1,v) +#define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XV(img,x0,v0,x1,v1,x,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2YV(img,y0,v0,y1,v1,y,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2ZV(img,z0,v0,z1,v1,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Z(img,z0,z1,z) +#define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in2XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in2YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in2XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i) +#define cimg_for3X(img,x) cimg_for3((img).width,x) +#define cimg_for3Y(img,y) cimg_for3((img).height,y) +#define cimg_for3Z(img,z) cimg_for3((img).depth,z) +#define cimg_for3V(img,v) cimg_for3((img).dim,v) +#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) +#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) +#define cimg_for3XV(img,x,v) cimg_for3V(img,v) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3YV(img,y,v) cimg_for3V(img,v) cimg_for3Y(img,y) +#define cimg_for3ZV(img,z,v) cimg_for3V(img,v) cimg_for3Z(img,z) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for3XZV(img,x,z,v) cimg_for3V(img,v) cimg_for3XZ(img,x,z) +#define cimg_for3YZV(img,y,z,v) cimg_for3V(img,v) cimg_for3YZ(img,y,z) +#define cimg_for3XYZV(img,x,y,z,v) cimg_for3V(img,v) cimg_for3XYZ(img,x,y,z) + +#define cimg_for_in3(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + _p1##i = i++, ++_n1##i) +#define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img).width,x0,x1,x) +#define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img).height,y0,y1,y) +#define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img).depth,z0,z1,z) +#define cimg_for_in3V(img,v0,v1,v) cimg_for_in3((img).dim,v0,v1,v) +#define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XV(img,x0,v0,x1,v1,x,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3YV(img,y0,v0,y1,v1,y,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3ZV(img,z0,v0,z1,v1,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Z(img,z0,z1,z) +#define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in3XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in3YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in3XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for4(bound,i) \ + for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(bound)?(int)(bound)-1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for4X(img,x) cimg_for4((img).width,x) +#define cimg_for4Y(img,y) cimg_for4((img).height,y) +#define cimg_for4Z(img,z) cimg_for4((img).depth,z) +#define cimg_for4V(img,v) cimg_for4((img).dim,v) +#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) +#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) +#define cimg_for4XV(img,x,v) cimg_for4V(img,v) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4YV(img,y,v) cimg_for4V(img,v) cimg_for4Y(img,y) +#define cimg_for4ZV(img,z,v) cimg_for4V(img,v) cimg_for4Z(img,z) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for4XZV(img,x,z,v) cimg_for4V(img,v) cimg_for4XZ(img,x,z) +#define cimg_for4YZV(img,y,z,v) cimg_for4V(img,v) cimg_for4YZ(img,y,z) +#define cimg_for4XYZV(img,x,y,z,v) cimg_for4V(img,v) cimg_for4XYZ(img,x,y,z) + +#define cimg_for_in4(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img).width,x0,x1,x) +#define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img).height,y0,y1,y) +#define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img).depth,z0,z1,z) +#define cimg_for_in4V(img,v0,v1,v) cimg_for_in4((img).dim,v0,v1,v) +#define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XV(img,x0,v0,x1,v1,x,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4YV(img,y0,v0,y1,v1,y,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4ZV(img,z0,v0,z1,v1,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Z(img,z0,z1,z) +#define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in4XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in4YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in4XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for5(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(bound)?(int)(bound)-1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for5X(img,x) cimg_for5((img).width,x) +#define cimg_for5Y(img,y) cimg_for5((img).height,y) +#define cimg_for5Z(img,z) cimg_for5((img).depth,z) +#define cimg_for5V(img,v) cimg_for5((img).dim,v) +#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) +#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) +#define cimg_for5XV(img,x,v) cimg_for5V(img,v) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5YV(img,y,v) cimg_for5V(img,v) cimg_for5Y(img,y) +#define cimg_for5ZV(img,z,v) cimg_for5V(img,v) cimg_for5Z(img,z) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) +#define cimg_for5XZV(img,x,z,v) cimg_for5V(img,v) cimg_for5XZ(img,x,z) +#define cimg_for5YZV(img,y,z,v) cimg_for5V(img,v) cimg_for5YZ(img,y,z) +#define cimg_for5XYZV(img,x,y,z,v) cimg_for5V(img,v) cimg_for5XYZ(img,x,y,z) + +#define cimg_for_in5(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i-2<0?0:i-2, \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img).width,x0,x1,x) +#define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img).height,y0,y1,y) +#define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img).depth,z0,z1,z) +#define cimg_for_in5V(img,v0,v1,v) cimg_for_in5((img).dim,v0,v1,v) +#define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XV(img,x0,v0,x1,v1,x,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5YV(img,y0,v0,y1,v1,y,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5ZV(img,z0,v0,z1,v1,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Z(img,z0,z1,z) +#define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in5XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in5YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in5XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for6(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(bound)?(int)(bound)-1:2, \ + _n3##i = 3>=(bound)?(int)(bound)-1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for6X(img,x) cimg_for6((img).width,x) +#define cimg_for6Y(img,y) cimg_for6((img).height,y) +#define cimg_for6Z(img,z) cimg_for6((img).depth,z) +#define cimg_for6V(img,v) cimg_for6((img).dim,v) +#define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) +#define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) +#define cimg_for6XV(img,x,v) cimg_for6V(img,v) cimg_for6X(img,x) +#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) +#define cimg_for6YV(img,y,v) cimg_for6V(img,v) cimg_for6Y(img,y) +#define cimg_for6ZV(img,z,v) cimg_for6V(img,v) cimg_for6Z(img,z) +#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) +#define cimg_for6XZV(img,x,z,v) cimg_for6V(img,v) cimg_for6XZ(img,x,z) +#define cimg_for6YZV(img,y,z,v) cimg_for6V(img,v) cimg_for6YZ(img,y,z) +#define cimg_for6XYZV(img,x,y,z,v) cimg_for6V(img,v) cimg_for6XYZ(img,x,y,z) + +#define cimg_for_in6(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i-2<0?0:i-2, \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ + _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ + i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img).width,x0,x1,x) +#define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img).height,y0,y1,y) +#define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img).depth,z0,z1,z) +#define cimg_for_in6V(img,v0,v1,v) cimg_for_in6((img).dim,v0,v1,v) +#define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XV(img,x0,v0,x1,v1,x,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6YV(img,y0,v0,y1,v1,y,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6ZV(img,z0,v0,z1,v1,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Z(img,z0,z1,z) +#define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in6XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in6YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in6XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for7(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(bound)?(int)(bound)-1:2, \ + _n3##i = 3>=(bound)?(int)(bound)-1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for7X(img,x) cimg_for7((img).width,x) +#define cimg_for7Y(img,y) cimg_for7((img).height,y) +#define cimg_for7Z(img,z) cimg_for7((img).depth,z) +#define cimg_for7V(img,v) cimg_for7((img).dim,v) +#define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) +#define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) +#define cimg_for7XV(img,x,v) cimg_for7V(img,v) cimg_for7X(img,x) +#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) +#define cimg_for7YV(img,y,v) cimg_for7V(img,v) cimg_for7Y(img,y) +#define cimg_for7ZV(img,z,v) cimg_for7V(img,v) cimg_for7Z(img,z) +#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) +#define cimg_for7XZV(img,x,z,v) cimg_for7V(img,v) cimg_for7XZ(img,x,z) +#define cimg_for7YZV(img,y,z,v) cimg_for7V(img,v) cimg_for7YZ(img,y,z) +#define cimg_for7XYZV(img,x,y,z,v) cimg_for7V(img,v) cimg_for7XYZ(img,x,y,z) + +#define cimg_for_in7(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i-3<0?0:i-3, \ + _p2##i = i-2<0?0:i-2, \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ + _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ + i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img).width,x0,x1,x) +#define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img).height,y0,y1,y) +#define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img).depth,z0,z1,z) +#define cimg_for_in7V(img,v0,v1,v) cimg_for_in7((img).dim,v0,v1,v) +#define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XV(img,x0,v0,x1,v1,x,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7YV(img,y0,v0,y1,v1,y,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7ZV(img,z0,v0,z1,v1,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Z(img,z0,z1,z) +#define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in7XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in7YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in7XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for8(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(bound)?(int)(bound)-1:2, \ + _n3##i = 3>=(bound)?(int)(bound)-1:3, \ + _n4##i = 4>=(bound)?(int)(bound)-1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for8X(img,x) cimg_for8((img).width,x) +#define cimg_for8Y(img,y) cimg_for8((img).height,y) +#define cimg_for8Z(img,z) cimg_for8((img).depth,z) +#define cimg_for8V(img,v) cimg_for8((img).dim,v) +#define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) +#define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) +#define cimg_for8XV(img,x,v) cimg_for8V(img,v) cimg_for8X(img,x) +#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) +#define cimg_for8YV(img,y,v) cimg_for8V(img,v) cimg_for8Y(img,y) +#define cimg_for8ZV(img,z,v) cimg_for8V(img,v) cimg_for8Z(img,z) +#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) +#define cimg_for8XZV(img,x,z,v) cimg_for8V(img,v) cimg_for8XZ(img,x,z) +#define cimg_for8YZV(img,y,z,v) cimg_for8V(img,v) cimg_for8YZ(img,y,z) +#define cimg_for8XYZV(img,x,y,z,v) cimg_for8V(img,v) cimg_for8XYZ(img,x,y,z) + +#define cimg_for_in8(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i-3<0?0:i-3, \ + _p2##i = i-2<0?0:i-2, \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ + _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \ + _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img).width,x0,x1,x) +#define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img).height,y0,y1,y) +#define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img).depth,z0,z1,z) +#define cimg_for_in8V(img,v0,v1,v) cimg_for_in8((img).dim,v0,v1,v) +#define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XV(img,x0,v0,x1,v1,x,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8YV(img,y0,v0,y1,v1,y,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8ZV(img,z0,v0,z1,v1,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8Z(img,z0,z1,z) +#define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in8XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in8YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in8XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in8V(img,v0,v1,v) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for9(bound,i) \ + for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \ + _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \ + _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \ + _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for9X(img,x) cimg_for9((img).width,x) +#define cimg_for9Y(img,y) cimg_for9((img).height,y) +#define cimg_for9Z(img,z) cimg_for9((img).depth,z) +#define cimg_for9V(img,v) cimg_for9((img).dim,v) +#define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) +#define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) +#define cimg_for9XV(img,x,v) cimg_for9V(img,v) cimg_for9X(img,x) +#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) +#define cimg_for9YV(img,y,v) cimg_for9V(img,v) cimg_for9Y(img,y) +#define cimg_for9ZV(img,z,v) cimg_for9V(img,v) cimg_for9Z(img,z) +#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) +#define cimg_for9XZV(img,x,z,v) cimg_for9V(img,v) cimg_for9XZ(img,x,z) +#define cimg_for9YZV(img,y,z,v) cimg_for9V(img,v) cimg_for9YZ(img,y,z) +#define cimg_for9XYZV(img,x,y,z,v) cimg_for9V(img,v) cimg_for9XYZ(img,x,y,z) + +#define cimg_for_in9(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p4##i = i-4<0?0:i-4, \ + _p3##i = i-3<0?0:i-3, \ + _p2##i = i-2<0?0:i-2, \ + _p1##i = i-1<0?0:i-1, \ + _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ + _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ + _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \ + _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img).width,x0,x1,x) +#define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img).height,y0,y1,y) +#define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img).depth,z0,z1,z) +#define cimg_for_in9V(img,v0,v1,v) cimg_for_in9((img).dim,v0,v1,v) +#define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XV(img,x0,v0,x1,v1,x,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9YV(img,y0,v0,y1,v1,y,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9ZV(img,z0,v0,z1,v1,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9Z(img,z0,z1,z) +#define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in9XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in9YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in9XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in9V(img,v0,v1,v) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for2x2(img,x,y,z,v,I) \ + cimg_for2((img).height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (img)(0,y,z,v)), \ + (I[2] = (img)(0,_n1##y,z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[1] = (img)(_n1##x,y,z,v)), \ + (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in2((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (img)(x,y,z,v)), \ + (I[2] = (img)(x,_n1##y,z,v)), \ + x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ + (I[1] = (img)(_n1##x,y,z,v)), \ + (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for3x3(img,x,y,z,v,I) \ + cimg_for3((img).height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ + (I[3] = I[4] = (img)(0,y,z,v)), \ + (I[6] = I[7] = (img)(0,_n1##y,z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in3((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = (int)( \ + (I[0] = (img)(_p1##x,_p1##y,z,v)), \ + (I[3] = (img)(_p1##x,y,z,v)), \ + (I[6] = (img)(_p1##x,_n1##y,z,v)), \ + (I[1] = (img)(x,_p1##y,z,v)), \ + (I[4] = (img)(x,y,z,v)), \ + (I[7] = (img)(x,_n1##y,z,v)), \ + x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for4x4(img,x,y,z,v,I) \ + cimg_for4((img).height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ + (I[4] = I[5] = (img)(0,y,z,v)), \ + (I[8] = I[9] = (img)(0,_n1##y,z,v)), \ + (I[12] = I[13] = (img)(0,_n2##y,z,v)), \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[6] = (img)(_n1##x,y,z,v)), \ + (I[10] = (img)(_n1##x,_n1##y,z,v)), \ + (I[14] = (img)(_n1##x,_n2##y,z,v)), \ + 2>=(img).width?(int)((img).width)-1:2); \ + (_n2##x<(int)((img).width) && ( \ + (I[3] = (img)(_n2##x,_p1##y,z,v)), \ + (I[7] = (img)(_n2##x,y,z,v)), \ + (I[11] = (img)(_n2##x,_n1##y,z,v)), \ + (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in4((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ + _n2##x = (int)( \ + (I[0] = (img)(_p1##x,_p1##y,z,v)), \ + (I[4] = (img)(_p1##x,y,z,v)), \ + (I[8] = (img)(_p1##x,_n1##y,z,v)), \ + (I[12] = (img)(_p1##x,_n2##y,z,v)), \ + (I[1] = (img)(x,_p1##y,z,v)), \ + (I[5] = (img)(x,y,z,v)), \ + (I[9] = (img)(x,_n1##y,z,v)), \ + (I[13] = (img)(x,_n2##y,z,v)), \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[6] = (img)(_n1##x,y,z,v)), \ + (I[10] = (img)(_n1##x,_n1##y,z,v)), \ + (I[14] = (img)(_n1##x,_n2##y,z,v)), \ + x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ + x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ + (I[3] = (img)(_n2##x,_p1##y,z,v)), \ + (I[7] = (img)(_n2##x,y,z,v)), \ + (I[11] = (img)(_n2##x,_n1##y,z,v)), \ + (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for5x5(img,x,y,z,v,I) \ + cimg_for5((img).height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ + (I[5] = I[6] = I[7] = (img)(0,_p1##y,z,v)), \ + (I[10] = I[11] = I[12] = (img)(0,y,z,v)), \ + (I[15] = I[16] = I[17] = (img)(0,_n1##y,z,v)), \ + (I[20] = I[21] = I[22] = (img)(0,_n2##y,z,v)), \ + (I[3] = (img)(_n1##x,_p2##y,z,v)), \ + (I[8] = (img)(_n1##x,_p1##y,z,v)), \ + (I[13] = (img)(_n1##x,y,z,v)), \ + (I[18] = (img)(_n1##x,_n1##y,z,v)), \ + (I[23] = (img)(_n1##x,_n2##y,z,v)), \ + 2>=(img).width?(int)((img).width)-1:2); \ + (_n2##x<(int)((img).width) && ( \ + (I[4] = (img)(_n2##x,_p2##y,z,v)), \ + (I[9] = (img)(_n2##x,_p1##y,z,v)), \ + (I[14] = (img)(_n2##x,y,z,v)), \ + (I[19] = (img)(_n2##x,_n1##y,z,v)), \ + (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in5((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p2##x = x-2<0?0:x-2, \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ + _n2##x = (int)( \ + (I[0] = (img)(_p2##x,_p2##y,z,v)), \ + (I[5] = (img)(_p2##x,_p1##y,z,v)), \ + (I[10] = (img)(_p2##x,y,z,v)), \ + (I[15] = (img)(_p2##x,_n1##y,z,v)), \ + (I[20] = (img)(_p2##x,_n2##y,z,v)), \ + (I[1] = (img)(_p1##x,_p2##y,z,v)), \ + (I[6] = (img)(_p1##x,_p1##y,z,v)), \ + (I[11] = (img)(_p1##x,y,z,v)), \ + (I[16] = (img)(_p1##x,_n1##y,z,v)), \ + (I[21] = (img)(_p1##x,_n2##y,z,v)), \ + (I[2] = (img)(x,_p2##y,z,v)), \ + (I[7] = (img)(x,_p1##y,z,v)), \ + (I[12] = (img)(x,y,z,v)), \ + (I[17] = (img)(x,_n1##y,z,v)), \ + (I[22] = (img)(x,_n2##y,z,v)), \ + (I[3] = (img)(_n1##x,_p2##y,z,v)), \ + (I[8] = (img)(_n1##x,_p1##y,z,v)), \ + (I[13] = (img)(_n1##x,y,z,v)), \ + (I[18] = (img)(_n1##x,_n1##y,z,v)), \ + (I[23] = (img)(_n1##x,_n2##y,z,v)), \ + x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ + x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ + (I[4] = (img)(_n2##x,_p2##y,z,v)), \ + (I[9] = (img)(_n2##x,_p1##y,z,v)), \ + (I[14] = (img)(_n2##x,y,z,v)), \ + (I[19] = (img)(_n2##x,_n1##y,z,v)), \ + (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for6x6(img,x,y,z,v,I) \ + cimg_for6((img).height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ + _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ + (I[6] = I[7] = I[8] = (img)(0,_p1##y,z,v)), \ + (I[12] = I[13] = I[14] = (img)(0,y,z,v)), \ + (I[18] = I[19] = I[20] = (img)(0,_n1##y,z,v)), \ + (I[24] = I[25] = I[26] = (img)(0,_n2##y,z,v)), \ + (I[30] = I[31] = I[32] = (img)(0,_n3##y,z,v)), \ + (I[3] = (img)(_n1##x,_p2##y,z,v)), \ + (I[9] = (img)(_n1##x,_p1##y,z,v)), \ + (I[15] = (img)(_n1##x,y,z,v)), \ + (I[21] = (img)(_n1##x,_n1##y,z,v)), \ + (I[27] = (img)(_n1##x,_n2##y,z,v)), \ + (I[33] = (img)(_n1##x,_n3##y,z,v)), \ + (I[4] = (img)(_n2##x,_p2##y,z,v)), \ + (I[10] = (img)(_n2##x,_p1##y,z,v)), \ + (I[16] = (img)(_n2##x,y,z,v)), \ + (I[22] = (img)(_n2##x,_n1##y,z,v)), \ + (I[28] = (img)(_n2##x,_n2##y,z,v)), \ + (I[34] = (img)(_n2##x,_n3##y,z,v)), \ + 3>=(img).width?(int)((img).width)-1:3); \ + (_n3##x<(int)((img).width) && ( \ + (I[5] = (img)(_n3##x,_p2##y,z,v)), \ + (I[11] = (img)(_n3##x,_p1##y,z,v)), \ + (I[17] = (img)(_n3##x,y,z,v)), \ + (I[23] = (img)(_n3##x,_n1##y,z,v)), \ + (I[29] = (img)(_n3##x,_n2##y,z,v)), \ + (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in6((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ + _p2##x = x-2<0?0:x-2, \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ + _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ + _n3##x = (int)( \ + (I[0] = (img)(_p2##x,_p2##y,z,v)), \ + (I[6] = (img)(_p2##x,_p1##y,z,v)), \ + (I[12] = (img)(_p2##x,y,z,v)), \ + (I[18] = (img)(_p2##x,_n1##y,z,v)), \ + (I[24] = (img)(_p2##x,_n2##y,z,v)), \ + (I[30] = (img)(_p2##x,_n3##y,z,v)), \ + (I[1] = (img)(_p1##x,_p2##y,z,v)), \ + (I[7] = (img)(_p1##x,_p1##y,z,v)), \ + (I[13] = (img)(_p1##x,y,z,v)), \ + (I[19] = (img)(_p1##x,_n1##y,z,v)), \ + (I[25] = (img)(_p1##x,_n2##y,z,v)), \ + (I[31] = (img)(_p1##x,_n3##y,z,v)), \ + (I[2] = (img)(x,_p2##y,z,v)), \ + (I[8] = (img)(x,_p1##y,z,v)), \ + (I[14] = (img)(x,y,z,v)), \ + (I[20] = (img)(x,_n1##y,z,v)), \ + (I[26] = (img)(x,_n2##y,z,v)), \ + (I[32] = (img)(x,_n3##y,z,v)), \ + (I[3] = (img)(_n1##x,_p2##y,z,v)), \ + (I[9] = (img)(_n1##x,_p1##y,z,v)), \ + (I[15] = (img)(_n1##x,y,z,v)), \ + (I[21] = (img)(_n1##x,_n1##y,z,v)), \ + (I[27] = (img)(_n1##x,_n2##y,z,v)), \ + (I[33] = (img)(_n1##x,_n3##y,z,v)), \ + (I[4] = (img)(_n2##x,_p2##y,z,v)), \ + (I[10] = (img)(_n2##x,_p1##y,z,v)), \ + (I[16] = (img)(_n2##x,y,z,v)), \ + (I[22] = (img)(_n2##x,_n1##y,z,v)), \ + (I[28] = (img)(_n2##x,_n2##y,z,v)), \ + (I[34] = (img)(_n2##x,_n3##y,z,v)), \ + x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ + x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ + (I[5] = (img)(_n3##x,_p2##y,z,v)), \ + (I[11] = (img)(_n3##x,_p1##y,z,v)), \ + (I[17] = (img)(_n3##x,y,z,v)), \ + (I[23] = (img)(_n3##x,_n1##y,z,v)), \ + (I[29] = (img)(_n3##x,_n2##y,z,v)), \ + (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for7x7(img,x,y,z,v,I) \ + cimg_for7((img).height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ + _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (img)(0,_p3##y,z,v)), \ + (I[7] = I[8] = I[9] = I[10] = (img)(0,_p2##y,z,v)), \ + (I[14] = I[15] = I[16] = I[17] = (img)(0,_p1##y,z,v)), \ + (I[21] = I[22] = I[23] = I[24] = (img)(0,y,z,v)), \ + (I[28] = I[29] = I[30] = I[31] = (img)(0,_n1##y,z,v)), \ + (I[35] = I[36] = I[37] = I[38] = (img)(0,_n2##y,z,v)), \ + (I[42] = I[43] = I[44] = I[45] = (img)(0,_n3##y,z,v)), \ + (I[4] = (img)(_n1##x,_p3##y,z,v)), \ + (I[11] = (img)(_n1##x,_p2##y,z,v)), \ + (I[18] = (img)(_n1##x,_p1##y,z,v)), \ + (I[25] = (img)(_n1##x,y,z,v)), \ + (I[32] = (img)(_n1##x,_n1##y,z,v)), \ + (I[39] = (img)(_n1##x,_n2##y,z,v)), \ + (I[46] = (img)(_n1##x,_n3##y,z,v)), \ + (I[5] = (img)(_n2##x,_p3##y,z,v)), \ + (I[12] = (img)(_n2##x,_p2##y,z,v)), \ + (I[19] = (img)(_n2##x,_p1##y,z,v)), \ + (I[26] = (img)(_n2##x,y,z,v)), \ + (I[33] = (img)(_n2##x,_n1##y,z,v)), \ + (I[40] = (img)(_n2##x,_n2##y,z,v)), \ + (I[47] = (img)(_n2##x,_n3##y,z,v)), \ + 3>=(img).width?(int)((img).width)-1:3); \ + (_n3##x<(int)((img).width) && ( \ + (I[6] = (img)(_n3##x,_p3##y,z,v)), \ + (I[13] = (img)(_n3##x,_p2##y,z,v)), \ + (I[20] = (img)(_n3##x,_p1##y,z,v)), \ + (I[27] = (img)(_n3##x,y,z,v)), \ + (I[34] = (img)(_n3##x,_n1##y,z,v)), \ + (I[41] = (img)(_n3##x,_n2##y,z,v)), \ + (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in7((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x-3<0?0:x-3, \ + _p2##x = x-2<0?0:x-2, \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ + _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ + _n3##x = (int)( \ + (I[0] = (img)(_p3##x,_p3##y,z,v)), \ + (I[7] = (img)(_p3##x,_p2##y,z,v)), \ + (I[14] = (img)(_p3##x,_p1##y,z,v)), \ + (I[21] = (img)(_p3##x,y,z,v)), \ + (I[28] = (img)(_p3##x,_n1##y,z,v)), \ + (I[35] = (img)(_p3##x,_n2##y,z,v)), \ + (I[42] = (img)(_p3##x,_n3##y,z,v)), \ + (I[1] = (img)(_p2##x,_p3##y,z,v)), \ + (I[8] = (img)(_p2##x,_p2##y,z,v)), \ + (I[15] = (img)(_p2##x,_p1##y,z,v)), \ + (I[22] = (img)(_p2##x,y,z,v)), \ + (I[29] = (img)(_p2##x,_n1##y,z,v)), \ + (I[36] = (img)(_p2##x,_n2##y,z,v)), \ + (I[43] = (img)(_p2##x,_n3##y,z,v)), \ + (I[2] = (img)(_p1##x,_p3##y,z,v)), \ + (I[9] = (img)(_p1##x,_p2##y,z,v)), \ + (I[16] = (img)(_p1##x,_p1##y,z,v)), \ + (I[23] = (img)(_p1##x,y,z,v)), \ + (I[30] = (img)(_p1##x,_n1##y,z,v)), \ + (I[37] = (img)(_p1##x,_n2##y,z,v)), \ + (I[44] = (img)(_p1##x,_n3##y,z,v)), \ + (I[3] = (img)(x,_p3##y,z,v)), \ + (I[10] = (img)(x,_p2##y,z,v)), \ + (I[17] = (img)(x,_p1##y,z,v)), \ + (I[24] = (img)(x,y,z,v)), \ + (I[31] = (img)(x,_n1##y,z,v)), \ + (I[38] = (img)(x,_n2##y,z,v)), \ + (I[45] = (img)(x,_n3##y,z,v)), \ + (I[4] = (img)(_n1##x,_p3##y,z,v)), \ + (I[11] = (img)(_n1##x,_p2##y,z,v)), \ + (I[18] = (img)(_n1##x,_p1##y,z,v)), \ + (I[25] = (img)(_n1##x,y,z,v)), \ + (I[32] = (img)(_n1##x,_n1##y,z,v)), \ + (I[39] = (img)(_n1##x,_n2##y,z,v)), \ + (I[46] = (img)(_n1##x,_n3##y,z,v)), \ + (I[5] = (img)(_n2##x,_p3##y,z,v)), \ + (I[12] = (img)(_n2##x,_p2##y,z,v)), \ + (I[19] = (img)(_n2##x,_p1##y,z,v)), \ + (I[26] = (img)(_n2##x,y,z,v)), \ + (I[33] = (img)(_n2##x,_n1##y,z,v)), \ + (I[40] = (img)(_n2##x,_n2##y,z,v)), \ + (I[47] = (img)(_n2##x,_n3##y,z,v)), \ + x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ + x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ + (I[6] = (img)(_n3##x,_p3##y,z,v)), \ + (I[13] = (img)(_n3##x,_p2##y,z,v)), \ + (I[20] = (img)(_n3##x,_p1##y,z,v)), \ + (I[27] = (img)(_n3##x,y,z,v)), \ + (I[34] = (img)(_n3##x,_n1##y,z,v)), \ + (I[41] = (img)(_n3##x,_n2##y,z,v)), \ + (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for8x8(img,x,y,z,v,I) \ + cimg_for8((img).height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img).width)?(int)((img).width)-1:1, \ + _n2##x = 2>=((img).width)?(int)((img).width)-1:2, \ + _n3##x = 3>=((img).width)?(int)((img).width)-1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (img)(0,_p3##y,z,v)), \ + (I[8] = I[9] = I[10] = I[11] = (img)(0,_p2##y,z,v)), \ + (I[16] = I[17] = I[18] = I[19] = (img)(0,_p1##y,z,v)), \ + (I[24] = I[25] = I[26] = I[27] = (img)(0,y,z,v)), \ + (I[32] = I[33] = I[34] = I[35] = (img)(0,_n1##y,z,v)), \ + (I[40] = I[41] = I[42] = I[43] = (img)(0,_n2##y,z,v)), \ + (I[48] = I[49] = I[50] = I[51] = (img)(0,_n3##y,z,v)), \ + (I[56] = I[57] = I[58] = I[59] = (img)(0,_n4##y,z,v)), \ + (I[4] = (img)(_n1##x,_p3##y,z,v)), \ + (I[12] = (img)(_n1##x,_p2##y,z,v)), \ + (I[20] = (img)(_n1##x,_p1##y,z,v)), \ + (I[28] = (img)(_n1##x,y,z,v)), \ + (I[36] = (img)(_n1##x,_n1##y,z,v)), \ + (I[44] = (img)(_n1##x,_n2##y,z,v)), \ + (I[52] = (img)(_n1##x,_n3##y,z,v)), \ + (I[60] = (img)(_n1##x,_n4##y,z,v)), \ + (I[5] = (img)(_n2##x,_p3##y,z,v)), \ + (I[13] = (img)(_n2##x,_p2##y,z,v)), \ + (I[21] = (img)(_n2##x,_p1##y,z,v)), \ + (I[29] = (img)(_n2##x,y,z,v)), \ + (I[37] = (img)(_n2##x,_n1##y,z,v)), \ + (I[45] = (img)(_n2##x,_n2##y,z,v)), \ + (I[53] = (img)(_n2##x,_n3##y,z,v)), \ + (I[61] = (img)(_n2##x,_n4##y,z,v)), \ + (I[6] = (img)(_n3##x,_p3##y,z,v)), \ + (I[14] = (img)(_n3##x,_p2##y,z,v)), \ + (I[22] = (img)(_n3##x,_p1##y,z,v)), \ + (I[30] = (img)(_n3##x,y,z,v)), \ + (I[38] = (img)(_n3##x,_n1##y,z,v)), \ + (I[46] = (img)(_n3##x,_n2##y,z,v)), \ + (I[54] = (img)(_n3##x,_n3##y,z,v)), \ + (I[62] = (img)(_n3##x,_n4##y,z,v)), \ + 4>=((img).width)?(int)((img).width)-1:4); \ + (_n4##x<(int)((img).width) && ( \ + (I[7] = (img)(_n4##x,_p3##y,z,v)), \ + (I[15] = (img)(_n4##x,_p2##y,z,v)), \ + (I[23] = (img)(_n4##x,_p1##y,z,v)), \ + (I[31] = (img)(_n4##x,y,z,v)), \ + (I[39] = (img)(_n4##x,_n1##y,z,v)), \ + (I[47] = (img)(_n4##x,_n2##y,z,v)), \ + (I[55] = (img)(_n4##x,_n3##y,z,v)), \ + (I[63] = (img)(_n4##x,_n4##y,z,v)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in8((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x-3<0?0:x-3, \ + _p2##x = x-2<0?0:x-2, \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)((img).width)?(int)((img).width)-1:x+1, \ + _n2##x = x+2>=(int)((img).width)?(int)((img).width)-1:x+2, \ + _n3##x = x+3>=(int)((img).width)?(int)((img).width)-1:x+3, \ + _n4##x = (int)( \ + (I[0] = (img)(_p3##x,_p3##y,z,v)), \ + (I[8] = (img)(_p3##x,_p2##y,z,v)), \ + (I[16] = (img)(_p3##x,_p1##y,z,v)), \ + (I[24] = (img)(_p3##x,y,z,v)), \ + (I[32] = (img)(_p3##x,_n1##y,z,v)), \ + (I[40] = (img)(_p3##x,_n2##y,z,v)), \ + (I[48] = (img)(_p3##x,_n3##y,z,v)), \ + (I[56] = (img)(_p3##x,_n4##y,z,v)), \ + (I[1] = (img)(_p2##x,_p3##y,z,v)), \ + (I[9] = (img)(_p2##x,_p2##y,z,v)), \ + (I[17] = (img)(_p2##x,_p1##y,z,v)), \ + (I[25] = (img)(_p2##x,y,z,v)), \ + (I[33] = (img)(_p2##x,_n1##y,z,v)), \ + (I[41] = (img)(_p2##x,_n2##y,z,v)), \ + (I[49] = (img)(_p2##x,_n3##y,z,v)), \ + (I[57] = (img)(_p2##x,_n4##y,z,v)), \ + (I[2] = (img)(_p1##x,_p3##y,z,v)), \ + (I[10] = (img)(_p1##x,_p2##y,z,v)), \ + (I[18] = (img)(_p1##x,_p1##y,z,v)), \ + (I[26] = (img)(_p1##x,y,z,v)), \ + (I[34] = (img)(_p1##x,_n1##y,z,v)), \ + (I[42] = (img)(_p1##x,_n2##y,z,v)), \ + (I[50] = (img)(_p1##x,_n3##y,z,v)), \ + (I[58] = (img)(_p1##x,_n4##y,z,v)), \ + (I[3] = (img)(x,_p3##y,z,v)), \ + (I[11] = (img)(x,_p2##y,z,v)), \ + (I[19] = (img)(x,_p1##y,z,v)), \ + (I[27] = (img)(x,y,z,v)), \ + (I[35] = (img)(x,_n1##y,z,v)), \ + (I[43] = (img)(x,_n2##y,z,v)), \ + (I[51] = (img)(x,_n3##y,z,v)), \ + (I[59] = (img)(x,_n4##y,z,v)), \ + (I[4] = (img)(_n1##x,_p3##y,z,v)), \ + (I[12] = (img)(_n1##x,_p2##y,z,v)), \ + (I[20] = (img)(_n1##x,_p1##y,z,v)), \ + (I[28] = (img)(_n1##x,y,z,v)), \ + (I[36] = (img)(_n1##x,_n1##y,z,v)), \ + (I[44] = (img)(_n1##x,_n2##y,z,v)), \ + (I[52] = (img)(_n1##x,_n3##y,z,v)), \ + (I[60] = (img)(_n1##x,_n4##y,z,v)), \ + (I[5] = (img)(_n2##x,_p3##y,z,v)), \ + (I[13] = (img)(_n2##x,_p2##y,z,v)), \ + (I[21] = (img)(_n2##x,_p1##y,z,v)), \ + (I[29] = (img)(_n2##x,y,z,v)), \ + (I[37] = (img)(_n2##x,_n1##y,z,v)), \ + (I[45] = (img)(_n2##x,_n2##y,z,v)), \ + (I[53] = (img)(_n2##x,_n3##y,z,v)), \ + (I[61] = (img)(_n2##x,_n4##y,z,v)), \ + (I[6] = (img)(_n3##x,_p3##y,z,v)), \ + (I[14] = (img)(_n3##x,_p2##y,z,v)), \ + (I[22] = (img)(_n3##x,_p1##y,z,v)), \ + (I[30] = (img)(_n3##x,y,z,v)), \ + (I[38] = (img)(_n3##x,_n1##y,z,v)), \ + (I[46] = (img)(_n3##x,_n2##y,z,v)), \ + (I[54] = (img)(_n3##x,_n3##y,z,v)), \ + (I[62] = (img)(_n3##x,_n4##y,z,v)), \ + x+4>=(int)((img).width)?(int)((img).width)-1:x+4); \ + x<=(int)(x1) && ((_n4##x<(int)((img).width) && ( \ + (I[7] = (img)(_n4##x,_p3##y,z,v)), \ + (I[15] = (img)(_n4##x,_p2##y,z,v)), \ + (I[23] = (img)(_n4##x,_p1##y,z,v)), \ + (I[31] = (img)(_n4##x,y,z,v)), \ + (I[39] = (img)(_n4##x,_n1##y,z,v)), \ + (I[47] = (img)(_n4##x,_n2##y,z,v)), \ + (I[55] = (img)(_n4##x,_n3##y,z,v)), \ + (I[63] = (img)(_n4##x,_n4##y,z,v)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for9x9(img,x,y,z,v,I) \ + cimg_for9((img).height,y) for (int x = 0, \ + _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img).width)?(int)((img).width)-1:1, \ + _n2##x = 2>=((img).width)?(int)((img).width)-1:2, \ + _n3##x = 3>=((img).width)?(int)((img).width)-1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = I[4] = (img)(0,_p4##y,z,v)), \ + (I[9] = I[10] = I[11] = I[12] = I[13] = (img)(0,_p3##y,z,v)), \ + (I[18] = I[19] = I[20] = I[21] = I[22] = (img)(0,_p2##y,z,v)), \ + (I[27] = I[28] = I[29] = I[30] = I[31] = (img)(0,_p1##y,z,v)), \ + (I[36] = I[37] = I[38] = I[39] = I[40] = (img)(0,y,z,v)), \ + (I[45] = I[46] = I[47] = I[48] = I[49] = (img)(0,_n1##y,z,v)), \ + (I[54] = I[55] = I[56] = I[57] = I[58] = (img)(0,_n2##y,z,v)), \ + (I[63] = I[64] = I[65] = I[66] = I[67] = (img)(0,_n3##y,z,v)), \ + (I[72] = I[73] = I[74] = I[75] = I[76] = (img)(0,_n4##y,z,v)), \ + (I[5] = (img)(_n1##x,_p4##y,z,v)), \ + (I[14] = (img)(_n1##x,_p3##y,z,v)), \ + (I[23] = (img)(_n1##x,_p2##y,z,v)), \ + (I[32] = (img)(_n1##x,_p1##y,z,v)), \ + (I[41] = (img)(_n1##x,y,z,v)), \ + (I[50] = (img)(_n1##x,_n1##y,z,v)), \ + (I[59] = (img)(_n1##x,_n2##y,z,v)), \ + (I[68] = (img)(_n1##x,_n3##y,z,v)), \ + (I[77] = (img)(_n1##x,_n4##y,z,v)), \ + (I[6] = (img)(_n2##x,_p4##y,z,v)), \ + (I[15] = (img)(_n2##x,_p3##y,z,v)), \ + (I[24] = (img)(_n2##x,_p2##y,z,v)), \ + (I[33] = (img)(_n2##x,_p1##y,z,v)), \ + (I[42] = (img)(_n2##x,y,z,v)), \ + (I[51] = (img)(_n2##x,_n1##y,z,v)), \ + (I[60] = (img)(_n2##x,_n2##y,z,v)), \ + (I[69] = (img)(_n2##x,_n3##y,z,v)), \ + (I[78] = (img)(_n2##x,_n4##y,z,v)), \ + (I[7] = (img)(_n3##x,_p4##y,z,v)), \ + (I[16] = (img)(_n3##x,_p3##y,z,v)), \ + (I[25] = (img)(_n3##x,_p2##y,z,v)), \ + (I[34] = (img)(_n3##x,_p1##y,z,v)), \ + (I[43] = (img)(_n3##x,y,z,v)), \ + (I[52] = (img)(_n3##x,_n1##y,z,v)), \ + (I[61] = (img)(_n3##x,_n2##y,z,v)), \ + (I[70] = (img)(_n3##x,_n3##y,z,v)), \ + (I[79] = (img)(_n3##x,_n4##y,z,v)), \ + 4>=((img).width)?(int)((img).width)-1:4); \ + (_n4##x<(int)((img).width) && ( \ + (I[8] = (img)(_n4##x,_p4##y,z,v)), \ + (I[17] = (img)(_n4##x,_p3##y,z,v)), \ + (I[26] = (img)(_n4##x,_p2##y,z,v)), \ + (I[35] = (img)(_n4##x,_p1##y,z,v)), \ + (I[44] = (img)(_n4##x,y,z,v)), \ + (I[53] = (img)(_n4##x,_n1##y,z,v)), \ + (I[62] = (img)(_n4##x,_n2##y,z,v)), \ + (I[71] = (img)(_n4##x,_n3##y,z,v)), \ + (I[80] = (img)(_n4##x,_n4##y,z,v)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \ + I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \ + I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \ + I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \ + I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,v,I) \ + cimg_for_in9((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p4##x = x-4<0?0:x-4, \ + _p3##x = x-3<0?0:x-3, \ + _p2##x = x-2<0?0:x-2, \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = x+1>=(int)((img).width)?(int)((img).width)-1:x+1, \ + _n2##x = x+2>=(int)((img).width)?(int)((img).width)-1:x+2, \ + _n3##x = x+3>=(int)((img).width)?(int)((img).width)-1:x+3, \ + _n4##x = (int)( \ + (I[0] = (img)(_p4##x,_p4##y,z,v)), \ + (I[9] = (img)(_p4##x,_p3##y,z,v)), \ + (I[18] = (img)(_p4##x,_p2##y,z,v)), \ + (I[27] = (img)(_p4##x,_p1##y,z,v)), \ + (I[36] = (img)(_p4##x,y,z,v)), \ + (I[45] = (img)(_p4##x,_n1##y,z,v)), \ + (I[54] = (img)(_p4##x,_n2##y,z,v)), \ + (I[63] = (img)(_p4##x,_n3##y,z,v)), \ + (I[72] = (img)(_p4##x,_n4##y,z,v)), \ + (I[1] = (img)(_p3##x,_p4##y,z,v)), \ + (I[10] = (img)(_p3##x,_p3##y,z,v)), \ + (I[19] = (img)(_p3##x,_p2##y,z,v)), \ + (I[28] = (img)(_p3##x,_p1##y,z,v)), \ + (I[37] = (img)(_p3##x,y,z,v)), \ + (I[46] = (img)(_p3##x,_n1##y,z,v)), \ + (I[55] = (img)(_p3##x,_n2##y,z,v)), \ + (I[64] = (img)(_p3##x,_n3##y,z,v)), \ + (I[73] = (img)(_p3##x,_n4##y,z,v)), \ + (I[2] = (img)(_p2##x,_p4##y,z,v)), \ + (I[11] = (img)(_p2##x,_p3##y,z,v)), \ + (I[20] = (img)(_p2##x,_p2##y,z,v)), \ + (I[29] = (img)(_p2##x,_p1##y,z,v)), \ + (I[38] = (img)(_p2##x,y,z,v)), \ + (I[47] = (img)(_p2##x,_n1##y,z,v)), \ + (I[56] = (img)(_p2##x,_n2##y,z,v)), \ + (I[65] = (img)(_p2##x,_n3##y,z,v)), \ + (I[74] = (img)(_p2##x,_n4##y,z,v)), \ + (I[3] = (img)(_p1##x,_p4##y,z,v)), \ + (I[12] = (img)(_p1##x,_p3##y,z,v)), \ + (I[21] = (img)(_p1##x,_p2##y,z,v)), \ + (I[30] = (img)(_p1##x,_p1##y,z,v)), \ + (I[39] = (img)(_p1##x,y,z,v)), \ + (I[48] = (img)(_p1##x,_n1##y,z,v)), \ + (I[57] = (img)(_p1##x,_n2##y,z,v)), \ + (I[66] = (img)(_p1##x,_n3##y,z,v)), \ + (I[75] = (img)(_p1##x,_n4##y,z,v)), \ + (I[4] = (img)(x,_p4##y,z,v)), \ + (I[13] = (img)(x,_p3##y,z,v)), \ + (I[22] = (img)(x,_p2##y,z,v)), \ + (I[31] = (img)(x,_p1##y,z,v)), \ + (I[40] = (img)(x,y,z,v)), \ + (I[49] = (img)(x,_n1##y,z,v)), \ + (I[58] = (img)(x,_n2##y,z,v)), \ + (I[67] = (img)(x,_n3##y,z,v)), \ + (I[76] = (img)(x,_n4##y,z,v)), \ + (I[5] = (img)(_n1##x,_p4##y,z,v)), \ + (I[14] = (img)(_n1##x,_p3##y,z,v)), \ + (I[23] = (img)(_n1##x,_p2##y,z,v)), \ + (I[32] = (img)(_n1##x,_p1##y,z,v)), \ + (I[41] = (img)(_n1##x,y,z,v)), \ + (I[50] = (img)(_n1##x,_n1##y,z,v)), \ + (I[59] = (img)(_n1##x,_n2##y,z,v)), \ + (I[68] = (img)(_n1##x,_n3##y,z,v)), \ + (I[77] = (img)(_n1##x,_n4##y,z,v)), \ + (I[6] = (img)(_n2##x,_p4##y,z,v)), \ + (I[15] = (img)(_n2##x,_p3##y,z,v)), \ + (I[24] = (img)(_n2##x,_p2##y,z,v)), \ + (I[33] = (img)(_n2##x,_p1##y,z,v)), \ + (I[42] = (img)(_n2##x,y,z,v)), \ + (I[51] = (img)(_n2##x,_n1##y,z,v)), \ + (I[60] = (img)(_n2##x,_n2##y,z,v)), \ + (I[69] = (img)(_n2##x,_n3##y,z,v)), \ + (I[78] = (img)(_n2##x,_n4##y,z,v)), \ + (I[7] = (img)(_n3##x,_p4##y,z,v)), \ + (I[16] = (img)(_n3##x,_p3##y,z,v)), \ + (I[25] = (img)(_n3##x,_p2##y,z,v)), \ + (I[34] = (img)(_n3##x,_p1##y,z,v)), \ + (I[43] = (img)(_n3##x,y,z,v)), \ + (I[52] = (img)(_n3##x,_n1##y,z,v)), \ + (I[61] = (img)(_n3##x,_n2##y,z,v)), \ + (I[70] = (img)(_n3##x,_n3##y,z,v)), \ + (I[79] = (img)(_n3##x,_n4##y,z,v)), \ + x+4>=(int)((img).width)?(int)((img).width)-1:x+4); \ + x<=(int)(x1) && ((_n4##x<(int)((img).width) && ( \ + (I[8] = (img)(_n4##x,_p4##y,z,v)), \ + (I[17] = (img)(_n4##x,_p3##y,z,v)), \ + (I[26] = (img)(_n4##x,_p2##y,z,v)), \ + (I[35] = (img)(_n4##x,_p1##y,z,v)), \ + (I[44] = (img)(_n4##x,y,z,v)), \ + (I[53] = (img)(_n4##x,_n1##y,z,v)), \ + (I[62] = (img)(_n4##x,_n2##y,z,v)), \ + (I[71] = (img)(_n4##x,_n3##y,z,v)), \ + (I[80] = (img)(_n4##x,_n4##y,z,v)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \ + I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \ + I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \ + I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \ + I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for2x2x2(img,x,y,z,v,I) \ + cimg_for2((img).depth,z) cimg_for2((img).height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (img)(0,y,z,v)), \ + (I[2] = (img)(0,_n1##y,z,v)), \ + (I[4] = (img)(0,y,_n1##z,v)), \ + (I[6] = (img)(0,_n1##y,_n1##z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[1] = (img)(_n1##x,y,z,v)), \ + (I[3] = (img)(_n1##x,_n1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,_n1##z,v)), \ + (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ + cimg_for_in2((img).depth,z0,z1,z) cimg_for_in2((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (img)(x,y,z,v)), \ + (I[2] = (img)(x,_n1##y,z,v)), \ + (I[4] = (img)(x,y,_n1##z,v)), \ + (I[6] = (img)(x,_n1##y,_n1##z,v)), \ + x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ + (I[1] = (img)(_n1##x,y,z,v)), \ + (I[3] = (img)(_n1##x,_n1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,_n1##z,v)), \ + (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for3x3x3(img,x,y,z,v,I) \ + cimg_for3((img).depth,z) cimg_for3((img).height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (img)(0,_p1##y,_p1##z,v)), \ + (I[3] = I[4] = (img)(0,y,_p1##z,v)), \ + (I[6] = I[7] = (img)(0,_n1##y,_p1##z,v)), \ + (I[9] = I[10] = (img)(0,_p1##y,z,v)), \ + (I[12] = I[13] = (img)(0,y,z,v)), \ + (I[15] = I[16] = (img)(0,_n1##y,z,v)), \ + (I[18] = I[19] = (img)(0,_p1##y,_n1##z,v)), \ + (I[21] = I[22] = (img)(0,y,_n1##z,v)), \ + (I[24] = I[25] = (img)(0,_n1##y,_n1##z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ + (I[5] = (img)(_n1##x,y,_p1##z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ + (I[11] = (img)(_n1##x,_p1##y,z,v)), \ + (I[14] = (img)(_n1##x,y,z,v)), \ + (I[17] = (img)(_n1##x,_n1##y,z,v)), \ + (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ + (I[23] = (img)(_n1##x,y,_n1##z,v)), \ + (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ + cimg_for_in3((img).depth,z0,z1,z) cimg_for_in3((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x-1<0?0:x-1, \ + _n1##x = (int)( \ + (I[0] = (img)(_p1##x,_p1##y,_p1##z,v)), \ + (I[3] = (img)(_p1##x,y,_p1##z,v)), \ + (I[6] = (img)(_p1##x,_n1##y,_p1##z,v)), \ + (I[9] = (img)(_p1##x,_p1##y,z,v)), \ + (I[12] = (img)(_p1##x,y,z,v)), \ + (I[15] = (img)(_p1##x,_n1##y,z,v)), \ + (I[18] = (img)(_p1##x,_p1##y,_n1##z,v)), \ + (I[21] = (img)(_p1##x,y,_n1##z,v)), \ + (I[24] = (img)(_p1##x,_n1##y,_n1##z,v)), \ + (I[1] = (img)(x,_p1##y,_p1##z,v)), \ + (I[4] = (img)(x,y,_p1##z,v)), \ + (I[7] = (img)(x,_n1##y,_p1##z,v)), \ + (I[10] = (img)(x,_p1##y,z,v)), \ + (I[13] = (img)(x,y,z,v)), \ + (I[16] = (img)(x,_n1##y,z,v)), \ + (I[19] = (img)(x,_p1##y,_n1##z,v)), \ + (I[22] = (img)(x,y,_n1##z,v)), \ + (I[25] = (img)(x,_n1##y,_n1##z,v)), \ + x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ + x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ + (I[5] = (img)(_n1##x,y,_p1##z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ + (I[11] = (img)(_n1##x,_p1##y,z,v)), \ + (I[14] = (img)(_n1##x,y,z,v)), \ + (I[17] = (img)(_n1##x,_n1##y,z,v)), \ + (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ + (I[23] = (img)(_n1##x,y,_n1##z,v)), \ + (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +/*------------------------------------------------ + # + # + # Definition of the cimg_library:: namespace + # + # + -------------------------------------------------*/ +//! This namespace encompasses all classes and functions of the %CImg library. +/** + This namespace is defined to avoid functions and class names collisions + that could happen with the include of other C++ header files. + Anyway, it should not happen often and you should reasonnably start most of your + %CImg-based programs with + \code + #include "CImg.h" + using namespace cimg_library; + \endcode + to simplify the declaration of %CImg Library variables afterwards. +**/ +namespace cimg_library { + + // Declare the only four classes of the CImg Library. + // + template struct CImg; + template struct CImgList; + struct CImgDisplay; + struct CImgException; + + // (Pre)declare the cimg namespace. + // This is not the complete namespace declaration. It only contains some + // necessary stuffs to ensure a correct declaration order of classes and functions + // defined afterwards. + // + namespace cimg { + +#ifdef cimg_use_vt100 + const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' }; + const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' }; + const char t_bold[] = { 0x1b,'[','1','m','\0' }; + const char t_purple[] = { 0x1b,'[','0',';','3','5',';','5','9','m','\0' }; + const char t_green[] = { 0x1b,'[','0',';','3','2',';','5','9','m','\0' }; +#else + const char t_normal[] = { '\0' }; + const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal, + *const t_purple = cimg::t_normal, *const t_green = cimg::t_normal; +#endif + + inline void info(); + + //! Get/set the current CImg exception mode. + /** + The way error messages are handled by CImg can be changed dynamically, using this function. + Possible values are : + - 0 to hide debug messages (quiet mode, but exceptions are still thrown). + - 1 to display debug messages on standard error (console). + - 2 to display debug messages in modal windows (default behavior). + - 3 to do as 1 + add extra warnings (may slow down the code !). + - 4 to do as 2 + add extra warnings (may slow down the code !). + **/ + inline unsigned int& exception_mode() { static unsigned int mode = cimg_debug; return mode; } + + inline int dialog(const char *title, const char *msg, const char *button1_txt="OK", + const char *button2_txt=0, const char *button3_txt=0, + const char *button4_txt=0, const char *button5_txt=0, + const char *button6_txt=0, const bool centering=false); + } + + /*---------------------------------------------- + # + # Definition of the CImgException structures + # + ----------------------------------------------*/ + //! Instances of this class are thrown when errors occur during a %CImg library function call. + /** + \section ex1 Overview + + CImgException is the base class of %CImg exceptions. + Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call. + CImgException is seldom thrown itself. Children classes that specify the kind of error encountered + are generally used instead. These sub-classes are : + + - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not + correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example + below will throw a \a CImgInstanceException. + \code + CImg img; // Construct an empty image. + img.blur(10); // Try to blur the image. + \endcode + + - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct. + Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values. + The example below will throw a \a CImgArgumentException. + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float pixels. + img = 0; // Try to fill pixels from the 0 pointer (invalid argument to operator=() ). + \endcode + + - \b CImgIOException : Thrown when an error occured when trying to load or save image files. + The example below will throw a \a CImgIOException. + \code + CImg img("file_doesnt_exist.jpg"); // Try to load a file that doesn't exist. + \endcode + + - \b CImgDisplayException : Thrown when an error occured when trying to display an image in a window. + This exception is thrown when image display request cannot be satisfied. + + The parent class CImgException may be thrown itself when errors that cannot be classified in one of + the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally + reserved to %CImg Library functions. + \b CImgInstanceException, \b CImgArgumentException, \b CImgIOException and \b CImgDisplayException are simple + subclasses of CImgException and are thus not detailled more in this reference documentation. + + \section ex2 Exception handling + + When an error occurs, the %CImg Library first displays the error in a modal window. + Then, it throws an instance of the corresponding exception class, generally leading the program to stop + (this is the default behavior). + You can bypass this default behavior by handling the exceptions yourself, + using a code block try { ... } catch() { ... }. + In this case, you can avoid the apparition of the modal window, by + defining the environment variable cimg_debug to 0 before including the %CImg header file. + The example below shows how to cleanly handle %CImg Library exceptions : + \code + #define cimg_debug 0 // Disable modal window in CImg exceptions. + #define "CImg.h" + int main() { + try { + ...; // Here, do what you want. + } + catch (CImgInstanceException &e) { + std::fprintf(stderr,"CImg Library Error : %s",e.message); // Display your own error message + ... // Do what you want now. + } + } + \endcode + **/ + struct CImgException { +#define _cimg_exception_err(etype,disp_flag) \ + cimg_std::va_list ap; va_start(ap,format); cimg_std::vsprintf(message,format,ap); va_end(ap); \ + switch (cimg::exception_mode()) { \ + case 0 : break; \ + case 2 : case 4 : try { cimg::dialog(etype,message,"Abort"); } catch (CImgException&) { \ + cimg_std::fprintf(cimg_stdout,"\n%s# %s%s :\n%s\n\n",cimg::t_red,etype,cimg::t_normal,message); \ + } break; \ + default : cimg_std::fprintf(cimg_stdout,"\n%s# %s%s :\n%s\n\n",cimg::t_red,etype,cimg::t_normal,message); \ + } \ + if (cimg::exception_mode()>=3) cimg_library::cimg::info(); + + char message[1024]; //!< Message associated with the error that thrown the exception. + CImgException() { message[0]='\0'; } + CImgException(const char *format, ...) { _cimg_exception_err("CImgException",true); } + }; + + // The \ref CImgInstanceException class is used to throw an exception related + // to a non suitable instance encountered in a library function call. + struct CImgInstanceException: public CImgException { + CImgInstanceException(const char *format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; + + // The \ref CImgArgumentException class is used to throw an exception related + // to invalid arguments encountered in a library function call. + struct CImgArgumentException: public CImgException { + CImgArgumentException(const char *format, ...) { _cimg_exception_err("CImgArgumentException",true); } + }; + + // The \ref CImgIOException class is used to throw an exception related + // to Input/Output file problems encountered in a library function call. + struct CImgIOException: public CImgException { + CImgIOException(const char *format, ...) { _cimg_exception_err("CImgIOException",true); } + }; + + // The CImgDisplayException class is used to throw an exception related to display problems + // encountered in a library function call. + struct CImgDisplayException: public CImgException { + CImgDisplayException(const char *format, ...) { _cimg_exception_err("CImgDisplayException",false); } + }; + + // The CImgWarningException class is used to throw an exception for warnings + // encountered in a library function call. + struct CImgWarningException: public CImgException { + CImgWarningException(const char *format, ...) { _cimg_exception_err("CImgWarningException",false); } + }; + + /*------------------------------------- + # + # Definition of the namespace 'cimg' + # + --------------------------------------*/ + //! Namespace that encompasses \a low-level functions and variables of the %CImg Library. + /** + Most of the functions and variables within this namespace are used by the library for low-level processing. + Nevertheless, documented variables and functions of this namespace may be used safely in your own source code. + + \warning Never write using namespace cimg_library::cimg; in your source code, since a lot of functions of the + cimg:: namespace have prototypes similar to standard C functions that could defined in the global namespace ::. + **/ + namespace cimg { + + // Define the traits that will be used to determine the best data type to work with. + // + template struct type { + static const char* string() { + static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", + "unknown32", "unknown40", "unknown48", "unknown56", + "unknown64", "unknown72", "unknown80", "unknown88", + "unknown96", "unknown104", "unknown112", "unknown120", + "unknown128" }; + return s[(sizeof(T)<17)?sizeof(T):0]; + } + static bool is_float() { return false; } + static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); } + static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); } + static const char* format() { return "%s"; } + static const char* format(const T val) { static const char *s = "unknown"; return s; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "bool"; return s; } + static bool is_float() { return false; } + static bool min() { return false; } + static bool max() { return true; } + static const char* format() { return "%s"; } + static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned char"; return s; } + static bool is_float() { return false; } + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)~0U; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned char val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static char min() { return (char)(-1L<<(8*sizeof(char)-1)); } + static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); } + static const char* format() { return "%d"; } + static int format(const char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "signed char"; return s; } + static bool is_float() { return false; } + static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); } + static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); } + static const char* format() { return "%d"; } + static unsigned int format(const signed char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned short"; return s; } + static bool is_float() { return false; } + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)~0U; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned short val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "short"; return s; } + static bool is_float() { return false; } + static short min() { return (short)(-1L<<(8*sizeof(short)-1)); } + static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); } + static const char* format() { return "%d"; } + static int format(const short val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int"; return s; } + static bool is_float() { return false; } + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)~0U; } + static const char* format() { return "%u"; } + static unsigned int format(const unsigned int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int"; return s; } + static bool is_float() { return false; } + static int min() { return (int)(-1L<<(8*sizeof(int)-1)); } + static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); } + static const char* format() { return "%d"; } + static int format(const int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned long"; return s; } + static bool is_float() { return false; } + static unsigned long min() { return 0; } + static unsigned long max() { return (unsigned long)~0UL; } + static const char* format() { return "%lu"; } + static unsigned long format(const unsigned long val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "long"; return s; } + static bool is_float() { return false; } + static long min() { return (long)(-1L<<(8*sizeof(long)-1)); } + static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); } + static const char* format() { return "%ld"; } + static long format(const long val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "float"; return s; } + static bool is_float() { return true; } + static float min() { return -3.4E38f; } + static float max() { return 3.4E38f; } + static const char* format() { return "%g"; } + static double format(const float val) { return (double)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "double"; return s; } + static bool is_float() { return true; } + static double min() { return -1.7E308; } + static double max() { return 1.7E308; } + static const char* format() { return "%g"; } + static double format(const double val) { return val; } + }; + + template struct superset { typedef T type; }; + template<> struct superset { typedef unsigned char type; }; + template<> struct superset { typedef char type; }; + template<> struct superset { typedef signed char type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef unsigned long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef long type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + + template struct superset2 { + typedef typename superset::type>::type type; + }; + + template struct superset3 { + typedef typename superset::type>::type type; + }; + + template struct last { typedef t2 type; }; + +#define _cimg_Tuchar typename cimg::superset::type +#define _cimg_Tint typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_Tdouble typename cimg::superset::type +#define _cimg_Tt typename cimg::superset::type + + // Define internal library variables. + // +#if cimg_display==1 + struct X11info { + volatile unsigned int nb_wins; + pthread_t* event_thread; + CImgDisplay* wins[1024]; + Display* display; + unsigned int nb_bits; + GC* gc; + bool blue_first; + bool byte_order; + bool shm_enabled; +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11info():nb_wins(0),event_thread(0),display(0), + nb_bits(0),gc(0),blue_first(false),byte_order(false),shm_enabled(false) { +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + }; +#if defined(cimg_module) + X11info& X11attr(); +#elif defined(cimg_main) + X11info& X11attr() { static X11info val; return val; } +#else + inline X11info& X11attr() { static X11info val; return val; } +#endif + +#elif cimg_display==2 + struct Win32info { + HANDLE wait_event; + Win32info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } + }; +#if defined(cimg_module) + Win32info& Win32attr(); +#elif defined(cimg_main) + Win32info& Win32attr() { static Win32info val; return val; } +#else + inline Win32info& Win32attr() { static Win32info val; return val; } +#endif + +#elif cimg_display==3 + struct CarbonInfo { + MPCriticalRegionID windowListCR; // Protects access to the list of windows + int windowCount; // Count of displays used on the screen + pthread_t event_thread; // The background event thread + MPSemaphoreID sync_event; // Event used to perform tasks synchronizations + MPSemaphoreID wait_event; // Event used to notify that new events occured on the display + MPQueueID com_queue; // The message queue + CarbonInfo(): windowCount(0),event_thread(0),sync_event(0),com_queue(0) { + if (MPCreateCriticalRegion(&windowListCR) != noErr) // Create the critical region + throw CImgDisplayException("MPCreateCriticalRegion failed."); + if (MPCreateSemaphore(1, 0, &sync_event) != noErr) // Create the inter-thread sync object + throw CImgDisplayException("MPCreateSemaphore failed."); + if (MPCreateSemaphore(1, 0, &wait_event) != noErr) // Create the event sync object + throw CImgDisplayException("MPCreateSemaphore failed."); + if (MPCreateQueue(&com_queue) != noErr) // Create the shared queue + throw CImgDisplayException("MPCreateQueue failed."); + } + ~CarbonInfo() { + if (event_thread != 0) { // Terminates the resident thread, if needed + pthread_cancel(event_thread); + pthread_join(event_thread, NULL); + event_thread = 0; + } + if (MPDeleteCriticalRegion(windowListCR) != noErr) // Delete the critical region + throw CImgDisplayException("MPDeleteCriticalRegion failed."); + if (MPDeleteSemaphore(wait_event) != noErr) // Delete the event sync event + throw CImgDisplayException("MPDeleteEvent failed."); + if (MPDeleteSemaphore(sync_event) != noErr) // Delete the inter-thread sync event + throw CImgDisplayException("MPDeleteEvent failed."); + if (MPDeleteQueue(com_queue) != noErr) // Delete the shared queue + throw CImgDisplayException("MPDeleteQueue failed."); + } + }; +#if defined(cimg_module) + CarbonInfo& CarbonAttr(); +#elif defined(cimg_main) + CarbonInfo CarbonAttr() { static CarbonInfo val; return val; } +#else + inline CarbonInfo& CarbonAttr() { static CarbonInfo val; return val; } +#endif +#endif + +#if cimg_display==1 + // Keycodes for X11-based graphical systems. + // + const unsigned int keyESC = XK_Escape; + const unsigned int keyF1 = XK_F1; + const unsigned int keyF2 = XK_F2; + const unsigned int keyF3 = XK_F3; + const unsigned int keyF4 = XK_F4; + const unsigned int keyF5 = XK_F5; + const unsigned int keyF6 = XK_F6; + const unsigned int keyF7 = XK_F7; + const unsigned int keyF8 = XK_F8; + const unsigned int keyF9 = XK_F9; + const unsigned int keyF10 = XK_F10; + const unsigned int keyF11 = XK_F11; + const unsigned int keyF12 = XK_F12; + const unsigned int keyPAUSE = XK_Pause; + const unsigned int key1 = XK_1; + const unsigned int key2 = XK_2; + const unsigned int key3 = XK_3; + const unsigned int key4 = XK_4; + const unsigned int key5 = XK_5; + const unsigned int key6 = XK_6; + const unsigned int key7 = XK_7; + const unsigned int key8 = XK_8; + const unsigned int key9 = XK_9; + const unsigned int key0 = XK_0; + const unsigned int keyBACKSPACE = XK_BackSpace; + const unsigned int keyINSERT = XK_Insert; + const unsigned int keyHOME = XK_Home; + const unsigned int keyPAGEUP = XK_Page_Up; + const unsigned int keyTAB = XK_Tab; + const unsigned int keyQ = XK_q; + const unsigned int keyW = XK_w; + const unsigned int keyE = XK_e; + const unsigned int keyR = XK_r; + const unsigned int keyT = XK_t; + const unsigned int keyY = XK_y; + const unsigned int keyU = XK_u; + const unsigned int keyI = XK_i; + const unsigned int keyO = XK_o; + const unsigned int keyP = XK_p; + const unsigned int keyDELETE = XK_Delete; + const unsigned int keyEND = XK_End; + const unsigned int keyPAGEDOWN = XK_Page_Down; + const unsigned int keyCAPSLOCK = XK_Caps_Lock; + const unsigned int keyA = XK_a; + const unsigned int keyS = XK_s; + const unsigned int keyD = XK_d; + const unsigned int keyF = XK_f; + const unsigned int keyG = XK_g; + const unsigned int keyH = XK_h; + const unsigned int keyJ = XK_j; + const unsigned int keyK = XK_k; + const unsigned int keyL = XK_l; + const unsigned int keyENTER = XK_Return; + const unsigned int keySHIFTLEFT = XK_Shift_L; + const unsigned int keyZ = XK_z; + const unsigned int keyX = XK_x; + const unsigned int keyC = XK_c; + const unsigned int keyV = XK_v; + const unsigned int keyB = XK_b; + const unsigned int keyN = XK_n; + const unsigned int keyM = XK_m; + const unsigned int keySHIFTRIGHT = XK_Shift_R; + const unsigned int keyARROWUP = XK_Up; + const unsigned int keyCTRLLEFT = XK_Control_L; + const unsigned int keyAPPLEFT = XK_Super_L; + const unsigned int keyALT = XK_Alt_L; + const unsigned int keySPACE = XK_space; + const unsigned int keyALTGR = XK_Alt_R; + const unsigned int keyAPPRIGHT = XK_Super_R; + const unsigned int keyMENU = XK_Menu; + const unsigned int keyCTRLRIGHT = XK_Control_R; + const unsigned int keyARROWLEFT = XK_Left; + const unsigned int keyARROWDOWN = XK_Down; + const unsigned int keyARROWRIGHT = XK_Right; + const unsigned int keyPAD0 = XK_KP_0; + const unsigned int keyPAD1 = XK_KP_1; + const unsigned int keyPAD2 = XK_KP_2; + const unsigned int keyPAD3 = XK_KP_3; + const unsigned int keyPAD4 = XK_KP_4; + const unsigned int keyPAD5 = XK_KP_5; + const unsigned int keyPAD6 = XK_KP_6; + const unsigned int keyPAD7 = XK_KP_7; + const unsigned int keyPAD8 = XK_KP_8; + const unsigned int keyPAD9 = XK_KP_9; + const unsigned int keyPADADD = XK_KP_Add; + const unsigned int keyPADSUB = XK_KP_Subtract; + const unsigned int keyPADMUL = XK_KP_Multiply; + const unsigned int keyPADDIV = XK_KP_Divide; + +#elif cimg_display==2 + // Keycodes for Windows. + // + const unsigned int keyESC = VK_ESCAPE; + const unsigned int keyF1 = VK_F1; + const unsigned int keyF2 = VK_F2; + const unsigned int keyF3 = VK_F3; + const unsigned int keyF4 = VK_F4; + const unsigned int keyF5 = VK_F5; + const unsigned int keyF6 = VK_F6; + const unsigned int keyF7 = VK_F7; + const unsigned int keyF8 = VK_F8; + const unsigned int keyF9 = VK_F9; + const unsigned int keyF10 = VK_F10; + const unsigned int keyF11 = VK_F11; + const unsigned int keyF12 = VK_F12; + const unsigned int keyPAUSE = VK_PAUSE; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = VK_BACK; + const unsigned int keyINSERT = VK_INSERT; + const unsigned int keyHOME = VK_HOME; + const unsigned int keyPAGEUP = VK_PRIOR; + const unsigned int keyTAB = VK_TAB; + const unsigned int keyQ = 'Q'; + const unsigned int keyW = 'W'; + const unsigned int keyE = 'E'; + const unsigned int keyR = 'R'; + const unsigned int keyT = 'T'; + const unsigned int keyY = 'Y'; + const unsigned int keyU = 'U'; + const unsigned int keyI = 'I'; + const unsigned int keyO = 'O'; + const unsigned int keyP = 'P'; + const unsigned int keyDELETE = VK_DELETE; + const unsigned int keyEND = VK_END; + const unsigned int keyPAGEDOWN = VK_NEXT; + const unsigned int keyCAPSLOCK = VK_CAPITAL; + const unsigned int keyA = 'A'; + const unsigned int keyS = 'S'; + const unsigned int keyD = 'D'; + const unsigned int keyF = 'F'; + const unsigned int keyG = 'G'; + const unsigned int keyH = 'H'; + const unsigned int keyJ = 'J'; + const unsigned int keyK = 'K'; + const unsigned int keyL = 'L'; + const unsigned int keyENTER = VK_RETURN; + const unsigned int keySHIFTLEFT = VK_SHIFT; + const unsigned int keyZ = 'Z'; + const unsigned int keyX = 'X'; + const unsigned int keyC = 'C'; + const unsigned int keyV = 'V'; + const unsigned int keyB = 'B'; + const unsigned int keyN = 'N'; + const unsigned int keyM = 'M'; + const unsigned int keySHIFTRIGHT = VK_SHIFT; + const unsigned int keyARROWUP = VK_UP; + const unsigned int keyCTRLLEFT = VK_CONTROL; + const unsigned int keyAPPLEFT = VK_LWIN; + const unsigned int keyALT = VK_LMENU; + const unsigned int keySPACE = VK_SPACE; + const unsigned int keyALTGR = VK_CONTROL; + const unsigned int keyAPPRIGHT = VK_RWIN; + const unsigned int keyMENU = VK_APPS; + const unsigned int keyCTRLRIGHT = VK_CONTROL; + const unsigned int keyARROWLEFT = VK_LEFT; + const unsigned int keyARROWDOWN = VK_DOWN; + const unsigned int keyARROWRIGHT = VK_RIGHT; + const unsigned int keyPAD0 = 0x60; + const unsigned int keyPAD1 = 0x61; + const unsigned int keyPAD2 = 0x62; + const unsigned int keyPAD3 = 0x63; + const unsigned int keyPAD4 = 0x64; + const unsigned int keyPAD5 = 0x65; + const unsigned int keyPAD6 = 0x66; + const unsigned int keyPAD7 = 0x67; + const unsigned int keyPAD8 = 0x68; + const unsigned int keyPAD9 = 0x69; + const unsigned int keyPADADD = VK_ADD; + const unsigned int keyPADSUB = VK_SUBTRACT; + const unsigned int keyPADMUL = VK_MULTIPLY; + const unsigned int keyPADDIV = VK_DIVIDE; + +#elif cimg_display==3 + // Keycodes for MacOSX, when using the Carbon framework. + // + const unsigned int keyESC = kEscapeCharCode; + const unsigned int keyF1 = 2U; + const unsigned int keyF2 = 3U; + const unsigned int keyF3 = 4U; + const unsigned int keyF4 = 5U; + const unsigned int keyF5 = 6U; + const unsigned int keyF6 = 7U; + const unsigned int keyF7 = 8U; + const unsigned int keyF8 = 9U; + const unsigned int keyF9 = 10U; + const unsigned int keyF10 = 11U; + const unsigned int keyF11 = 12U; + const unsigned int keyF12 = 13U; + const unsigned int keyPAUSE = 14U; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = kBackspaceCharCode; + const unsigned int keyINSERT = 26U; + const unsigned int keyHOME = kHomeCharCode; + const unsigned int keyPAGEUP = kPageUpCharCode; + const unsigned int keyTAB = kTabCharCode; + const unsigned int keyQ = 'q'; + const unsigned int keyW = 'w'; + const unsigned int keyE = 'e'; + const unsigned int keyR = 'r'; + const unsigned int keyT = 't'; + const unsigned int keyY = 'y'; + const unsigned int keyU = 'u'; + const unsigned int keyI = 'i'; + const unsigned int keyO = 'o'; + const unsigned int keyP = 'p'; + const unsigned int keyDELETE = kDeleteCharCode; + const unsigned int keyEND = kEndCharCode; + const unsigned int keyPAGEDOWN = kPageDownCharCode; + const unsigned int keyCAPSLOCK = 43U; + const unsigned int keyA = 'a'; + const unsigned int keyS = 's'; + const unsigned int keyD = 'd'; + const unsigned int keyF = 'f'; + const unsigned int keyG = 'g'; + const unsigned int keyH = 'h'; + const unsigned int keyJ = 'j'; + const unsigned int keyK = 'k'; + const unsigned int keyL = 'l'; + const unsigned int keyENTER = kEnterCharCode; + const unsigned int keySHIFTLEFT = 54U; //Macintosh modifier key, emulated + const unsigned int keyZ = 'z'; + const unsigned int keyX = 'x'; + const unsigned int keyC = 'c'; + const unsigned int keyV = 'v'; + const unsigned int keyB = 'b'; + const unsigned int keyN = 'n'; + const unsigned int keyM = 'm'; + const unsigned int keySHIFTRIGHT = 62U; //Macintosh modifier key, emulated + const unsigned int keyARROWUP = kUpArrowCharCode; + const unsigned int keyCTRLLEFT = 64U; //Macintosh modifier key, emulated + const unsigned int keyAPPLEFT = 65U; //Macintosh modifier key, emulated + const unsigned int keyALT = 66U; + const unsigned int keySPACE = kSpaceCharCode; + const unsigned int keyALTGR = 67U; //Macintosh modifier key, emulated + const unsigned int keyAPPRIGHT = 68U; //Aliased on keyAPPLEFT + const unsigned int keyMENU = 69U; + const unsigned int keyCTRLRIGHT = 70U; //Macintosh modifier key, emulated + const unsigned int keyARROWLEFT = kLeftArrowCharCode; + const unsigned int keyARROWDOWN = kDownArrowCharCode; + const unsigned int keyARROWRIGHT = kRightArrowCharCode; + const unsigned int keyPAD0 = 74U; + const unsigned int keyPAD1 = 75U; + const unsigned int keyPAD2 = 76U; + const unsigned int keyPAD3 = 77U; + const unsigned int keyPAD4 = 78U; + const unsigned int keyPAD5 = 79U; + const unsigned int keyPAD6 = 80U; + const unsigned int keyPAD7 = 81U; + const unsigned int keyPAD8 = 82U; + const unsigned int keyPAD9 = 83U; + const unsigned int keyPADADD = 84U; + const unsigned int keyPADSUB = 85U; + const unsigned int keyPADMUL = 86U; + const unsigned int keyPADDIV = 87U; + +#else + // Define unknow keycodes when no display are available. + // (should rarely be used then !). + // + const unsigned int keyESC = 1U; + const unsigned int keyF1 = 2U; + const unsigned int keyF2 = 3U; + const unsigned int keyF3 = 4U; + const unsigned int keyF4 = 5U; + const unsigned int keyF5 = 6U; + const unsigned int keyF6 = 7U; + const unsigned int keyF7 = 8U; + const unsigned int keyF8 = 9U; + const unsigned int keyF9 = 10U; + const unsigned int keyF10 = 11U; + const unsigned int keyF11 = 12U; + const unsigned int keyF12 = 13U; + const unsigned int keyPAUSE = 14U; + const unsigned int key1 = 15U; + const unsigned int key2 = 16U; + const unsigned int key3 = 17U; + const unsigned int key4 = 18U; + const unsigned int key5 = 19U; + const unsigned int key6 = 20U; + const unsigned int key7 = 21U; + const unsigned int key8 = 22U; + const unsigned int key9 = 23U; + const unsigned int key0 = 24U; + const unsigned int keyBACKSPACE = 25U; + const unsigned int keyINSERT = 26U; + const unsigned int keyHOME = 27U; + const unsigned int keyPAGEUP = 28U; + const unsigned int keyTAB = 29U; + const unsigned int keyQ = 30U; + const unsigned int keyW = 31U; + const unsigned int keyE = 32U; + const unsigned int keyR = 33U; + const unsigned int keyT = 34U; + const unsigned int keyY = 35U; + const unsigned int keyU = 36U; + const unsigned int keyI = 37U; + const unsigned int keyO = 38U; + const unsigned int keyP = 39U; + const unsigned int keyDELETE = 40U; + const unsigned int keyEND = 41U; + const unsigned int keyPAGEDOWN = 42U; + const unsigned int keyCAPSLOCK = 43U; + const unsigned int keyA = 44U; + const unsigned int keyS = 45U; + const unsigned int keyD = 46U; + const unsigned int keyF = 47U; + const unsigned int keyG = 48U; + const unsigned int keyH = 49U; + const unsigned int keyJ = 50U; + const unsigned int keyK = 51U; + const unsigned int keyL = 52U; + const unsigned int keyENTER = 53U; + const unsigned int keySHIFTLEFT = 54U; + const unsigned int keyZ = 55U; + const unsigned int keyX = 56U; + const unsigned int keyC = 57U; + const unsigned int keyV = 58U; + const unsigned int keyB = 59U; + const unsigned int keyN = 60U; + const unsigned int keyM = 61U; + const unsigned int keySHIFTRIGHT = 62U; + const unsigned int keyARROWUP = 63U; + const unsigned int keyCTRLLEFT = 64U; + const unsigned int keyAPPLEFT = 65U; + const unsigned int keyALT = 66U; + const unsigned int keySPACE = 67U; + const unsigned int keyALTGR = 68U; + const unsigned int keyAPPRIGHT = 69U; + const unsigned int keyMENU = 70U; + const unsigned int keyCTRLRIGHT = 71U; + const unsigned int keyARROWLEFT = 72U; + const unsigned int keyARROWDOWN = 73U; + const unsigned int keyARROWRIGHT = 74U; + const unsigned int keyPAD0 = 75U; + const unsigned int keyPAD1 = 76U; + const unsigned int keyPAD2 = 77U; + const unsigned int keyPAD3 = 78U; + const unsigned int keyPAD4 = 79U; + const unsigned int keyPAD5 = 80U; + const unsigned int keyPAD6 = 81U; + const unsigned int keyPAD7 = 82U; + const unsigned int keyPAD8 = 83U; + const unsigned int keyPAD9 = 84U; + const unsigned int keyPADADD = 85U; + const unsigned int keyPADSUB = 86U; + const unsigned int keyPADMUL = 87U; + const unsigned int keyPADDIV = 88U; +#endif + + const double valuePI = 3.14159265358979323846; //!< Definition of the mathematical constant PI + + // Definition of a 7x11 font, used to return a default font for drawing text. + const unsigned int font7x11[7*11*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x7f0000,0x40000,0x0,0x0,0x4010c0a4,0x82000040,0x11848402,0x18480050,0x80430292,0x8023,0x9008000, + 0x40218140,0x4000040,0x21800402,0x18000051,0x1060500,0x8083,0x10000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24002,0x4031,0x80000000,0x10000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c0400,0x40020000,0x80070080,0x40440e00,0x0,0x0,0x1,0x88180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x200000,0x0,0x0,0x80000,0x0,0x0,0x20212140,0x5000020,0x22400204,0x240000a0,0x40848500,0x4044,0x80010038,0x20424285,0xa000020, + 0x42428204,0x2428e0a0,0x82090a14,0x4104,0x85022014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10240a7,0x88484040,0x40800000,0x270c3,0x87811e0e, + 0x7c70e000,0x78,0x3c23c1ef,0x1f3e1e89,0xf1c44819,0xa23cf0f3,0xc3cff120,0xc18307f4,0x4040400,0x20000,0x80080080,0x40200,0x0, + 0x40000,0x2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8188,0x50603800,0xf3c00000,0x1c004003,0xc700003e,0x18180,0xc993880,0x10204081, + 0x2071ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x7d1224,0x48906048,0x0,0x4000000,0x0,0x9000,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x10240aa,0x14944080,0x23610000,0x68940,0x40831010,0x8891306,0x802044,0x44522208,0x90202088,0x40448819,0xb242890a,0x24011111, + 0x49448814,0x4040a00,0xe2c3c7,0x8e3f3cb9,0xc1c44216,0xee38b0f2,0xe78f9120,0xc18507e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x101c207,0x88a04001,0x9c00000,0x2200a041,0x8200113a,0x8240,0x50a3110,0x2850a142,0x850c2081,0x2040204,0x8104592,0x142850a1, + 0x42cd1224,0x4888bc48,0x70e1c387,0xe3b3c70,0xe1c38e1c,0x38707171,0xc3870e1c,0x10791224,0x48906c41,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x10003ee,0x15140080,0x21810000,0x48840,0x40851020,0x8911306,0x31fd804,0x9c522408,0x90204088,0x4045081a,0xba42890a,0x24011111, + 0x49285024,0x2041b00,0x132408,0x910844c8,0x4044821b,0x7244c913,0x24041111,0x49488822,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x28204,0x85006001,0x6a414000,0x3a004043,0xc700113a,0x8245,0x50a3a00,0x2850a142,0x850c4081,0x2040204,0x81045d2,0x142850a1, + 0x24951224,0x48852250,0x8102040,0x81054089,0x12244204,0x8108992,0x24489122,0x991224,0x4888b222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1000143,0xa988080,0x2147c01f,0x88840,0x83091c2c,0x1070f000,0xc000608,0xa48bc408,0x9e3c46f8,0x40460816,0xaa42f10b,0xc3811111, + 0x35102044,0x1041100,0xf22408,0x9f084488,0x40470212,0x62448912,0x6041111,0x55308846,0x8061c80,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1028704,0x8f805801,0x4be28fdf,0x220001f0,0x111a,0x60000182,0x82c5c710,0x44891224,0x489640f1,0xe3c78204,0x810e552,0x142850a1, + 0x18a51224,0x48822250,0x78f1e3c7,0x8f1f40f9,0xf3e7c204,0x8108912,0x24489122,0x7ea91224,0x4888a222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x10007e2,0x85648080,0x20010000,0x88841,0x8f8232,0x20881000,0xc1fc610,0xbefa2408,0x90204288,0x40450816,0xa642810a,0x4041110a, + 0x36282084,0x1042080,0x1122408,0x90084488,0x40450212,0x62448912,0x184110a,0x55305082,0x8042700,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1028207,0x82004801,0x68050040,0x1c000040,0x110a,0x60000001,0x45484d10,0x7cf9f3e7,0xcf944081,0x2040204,0x8104532,0x142850a1, + 0x18a51224,0x48822248,0x89122448,0x91244081,0x2040204,0x8108912,0x24489122,0xc91224,0x48852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x282, + 0x89630080,0x20010c00,0x30108842,0x810222,0x20882306,0x3001800,0x408a2208,0x90202288,0x40448814,0xa642810a,0x2041110a,0x26442104, + 0x840000,0x1122408,0x90084488,0x40448212,0x62448912,0x84130a,0x36485102,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x101c208,0x4f802801, + 0x8028040,0x40,0x130a,0x2,0x85e897a0,0x44891224,0x489c2081,0x2040204,0x8104532,0x142850a1,0x24cd1224,0x48823c44,0x89122448, + 0x91244081,0x2040204,0x8108912,0x24489122,0xc93264,0xc9852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100028f,0x109f0080,0x20010c00, + 0x303071f3,0xc7011c1c,0x4071c306,0x802010,0x3907c1ef,0x1f201e89,0xf3844f90,0xa23c80f2,0x17810e04,0x228223f4,0x840000,0xfbc3c7, + 0x8f083c88,0x40444212,0x6238f0f2,0x7039d04,0x228423e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1008780,0x2201800,0xf0014000,0x1f0, + 0x1d0a,0x5,0x851e140,0x83060c18,0x30671ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x42f8e1c3,0x8702205c,0x7cf9f3e7,0xcf9b3c78,0xf1e3c204, + 0x8107111,0xc3870e1c,0x10f1d3a7,0x4e823c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x40,0x40000400,0x200000,0x0,0x2,0x0,0x0,0x0,0x0,0x18, + 0x0,0x4,0x44007f,0x0,0x400,0x400000,0x8010,0x0,0x6002,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x200800,0x0,0x0,0x100a, + 0x400000,0x44,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x62018,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x80000800, + 0x400000,0x0,0x4,0x0,0x0,0x0,0x0,0xc,0x0,0x7,0x3c0000,0x0,0x3800,0x3800000,0x8010,0x0,0x1c001,0x881c0000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x207000,0x0,0x0,0x100a,0xc00000,0x3c,0x0,0xc00,0x0,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x0,0x1c2070 + }; + + // Definition of a 10x13 font (used in dialog boxes). + const unsigned int font10x13[256*10*13/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0, + 0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120, + 0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001, + 0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801, + 0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0, + 0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010, + 0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480, + 0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140, + 0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010, + 0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008, + 0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006, + 0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088, + 0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000, + 0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220, + 0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208, + 0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040, + 0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508, + 0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018, + 0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220, + 0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484, + 0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808, + 0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264, + 0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010, + 0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100, + 0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800, + 0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044, + 0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822, + 0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200, + 0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000, + 0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8, + 0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008, + 0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000, + 0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220, + 0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402, + 0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980, + 0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421, + 0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020, + 0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701, + 0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0, + 0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c, + 0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0, + 0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000, + 0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000, + 0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000, + 0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0, + 0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040 + }; + + // Definition of a 8x17 font. + const unsigned int font8x17[8*17*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x2400,0x2400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20081834,0x1c0000,0x20081800,0x20081800,0x342008, + 0x18340000,0x200818,0x80000,0x0,0x180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4200000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x380000,0x4000,0x2000c00,0x40100840,0x70000000,0x0,0x0,0x1c,0x10700000,0x7,0x0, + 0x1800,0x1800,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1010242c,0x14140000,0x10102414,0x10102414,0x2c1010,0x242c1400, + 0x101024,0x14100038,0x0,0x240000,0x0,0x0,0x30000000,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x8100000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x80000,0x10004000,0x2001000,0x40000040,0x10000000,0x0,0x0,0x10,0x10100000,0x4, + 0x0,0x18000000,0x0,0x0,0x0,0x34002400,0x2400,0x0,0x0,0x0,0x3c,0x0,0x8000000,0x0,0x60607800,0x0,0x140000,0x0,0x0,0x0,0x0,0x0, + 0x44,0x10081834,0x240000,0x10081800,0x10081800,0x1c341008,0x18340000,0x100818,0x84000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102812, + 0x8601c10,0x8100800,0x2,0x1c383e3e,0x67e1e7f,0x3e3c0000,0x38,0x1e087e1e,0x7c7f7f1e,0x417c1c42,0x4063611c,0x7e1c7e3e,0xfe414181, + 0x63827f10,0x40081000,0x8004000,0x2001000,0x40000040,0x10000000,0x0,0x10000000,0x10,0x10100000,0x3c000008,0x0,0x24003e00, + 0x3f007f00,0x0,0x0,0x2ce91800,0x1882,0x10101c,0xc2103c,0x143c3c00,0x3c00,0x18003c3c,0x10001f00,0x181c00,0x20200810,0x8080808, + 0x8083e1e,0x7f7f7f7f,0x7c7c7c7c,0x7c611c1c,0x1c1c1c00,0x1e414141,0x41824044,0x810242c,0x14180000,0x8102414,0x8102414,0x382c0810, + 0x242c1400,0x81024,0x14104014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102816,0x3e902010,0x10084910,0x4,0x22084343,0xa402102,0x41620000, + 0x44,0x33144121,0x42404021,0x41100444,0x40636122,0x43224361,0x10416381,0x22440310,0x20082800,0x4000,0x2001000,0x40000040, + 0x10000000,0x0,0x10000000,0x10,0x10100000,0x24000008,0x0,0x606100,0x68000300,0x8106c,0x34000000,0x4f0000,0x44,0x101020,0x441040, + 0x420200,0x4200,0x24000404,0x7d00,0x82200,0x20203010,0x14141414,0x14082821,0x40404040,0x10101010,0x42612222,0x22222200,0x23414141, + 0x41447e48,0x0,0x0,0x0,0x0,0x4000000,0x18,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10287f,0x49902010,0x10083e10,0x4,0x41080101, + 0x1a404002,0x41411818,0x1004004,0x21144140,0x41404040,0x41100448,0x40555141,0x41414140,0x10412281,0x14280610,0x20084400,0x1c7c1c, + 0x3e3c7c3a,0x5c703844,0x107f5c3c,0x7c3e3c3c,0x7e424281,0x66427e10,0x10100000,0x40100008,0x1010,0xa04000,0x48100610,0x100c3024, + 0x24000000,0x4f3c00,0x2c107e28,0x3820,0x42281060,0x9d1e12,0xbd00,0x24100818,0x427d00,0x82248,0x20200800,0x14141414,0x14142840, + 0x40404040,0x10101010,0x41514141,0x41414142,0x43414141,0x41284350,0x1c1c1c1c,0x1c1c6c1c,0x3c3c3c3c,0x70707070,0x3c5c3c3c, + 0x3c3c3c18,0x3e424242,0x42427c42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102824,0x48623010,0x10081c10,0x8,0x41080103,0x127c5e04, + 0x41411818,0xe7f3808,0x4f144140,0x41404040,0x41100450,0x40555141,0x41414160,0x1041225a,0x1c280410,0x1008c600,0x226622,0x66661066, + 0x62100848,0x10496266,0x66663242,0x10426681,0x24220260,0x100c0000,0xf8280008,0x1010,0x606000,0x48280428,0x28042014,0x48000000, + 0x494200,0x52280228,0x105420,0x3cee1058,0xa12236,0xa500,0x18101004,0x427d00,0x8226c,0x76767e10,0x14141414,0x14142840,0x40404040, + 0x10101010,0x41514141,0x41414124,0x45414141,0x41284150,0x22222222,0x22221222,0x66666666,0x10101010,0x66626666,0x66666600, + 0x66424242,0x42226622,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100024,0x381c4900,0x10086bfe,0x8,0x4908021c,0x22036304,0x3e630000, + 0x70000710,0x51227e40,0x417f7f43,0x7f100470,0x40554941,0x43417e3e,0x1041225a,0x8100810,0x10080000,0x24240,0x42421042,0x42100850, + 0x10494242,0x42422040,0x1042245a,0x18240410,0x10103900,0x407c003e,0x1818,0x1c3e10,0x4f7c087c,0x7c002010,0x48000000,0x4008, + 0x527c0410,0x105078,0x2410104c,0xa13e6c,0x7f00b900,0xfe3c3c,0x421d18,0x1c1c36,0x38383810,0x22222222,0x22144e40,0x7f7f7f7f, + 0x10101010,0xf1494141,0x41414118,0x49414141,0x4110435c,0x2020202,0x2021240,0x42424242,0x10101010,0x42424242,0x424242ff,0x4e424242, + 0x42244224,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000fe,0xe664d00,0x10080810,0x380010,0x41080c03,0x42014108,0x633d0000,0x70000710, + 0x51224140,0x41404041,0x41100448,0x40494541,0x7e414203,0x1041145a,0x14101010,0x10080000,0x3e4240,0x427e1042,0x42100870,0x10494242, + 0x4242203c,0x1042245a,0x18241810,0x10104600,0xf8f60008,0x1010,0x600320,0x48f610f6,0xf6000000,0x187eff,0x3c04,0x5ef61810,0x105020, + 0x24fe0064,0x9d006c,0x138ad00,0x100000,0x420518,0x36,0xc0c0c020,0x22222222,0x22224840,0x40404040,0x10101010,0x41454141,0x41414118, + 0x51414141,0x41107e46,0x3e3e3e3e,0x3e3e7e40,0x7e7e7e7e,0x10101010,0x42424242,0x42424200,0x5a424242,0x42244224,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x28,0x9094500,0x10080010,0x10,0x41081801,0x7f014118,0x41010000,0xe7f3800,0x513e4140,0x41404041,0x41100444, + 0x40414541,0x40414101,0x10411466,0x36103010,0x8080000,0x424240,0x42401042,0x42100848,0x10494242,0x42422002,0x10423c5a,0x18142010, + 0x10100000,0x407c0010,0x1010,0x260140,0x487c307c,0x7c000000,0x180000,0x202,0x507c2010,0x105020,0x3c10003c,0x423e36,0x1004200, + 0x100000,0x420500,0x3e6c,0x41e0440,0x3e3e3e3e,0x3e3e7840,0x40404040,0x10101010,0x41454141,0x41414124,0x61414141,0x41104042, + 0x42424242,0x42425040,0x40404040,0x10101010,0x42424242,0x42424218,0x72424242,0x42144214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048, + 0x49096200,0x8100010,0x18001810,0x22082043,0x2432310,0x61421818,0x1004010,0x4f634121,0x42404021,0x41104444,0x40414322,0x40234143, + 0x10411466,0x22106010,0x8080000,0x466622,0x66621066,0x42100844,0x10494266,0x66662042,0x10461824,0x24184010,0x10100000,0x24381010, + 0x34001018,0xda4320,0x68386038,0x38000000,0x0,0x4204,0x50384010,0x105420,0x4210100c,0x3c0012,0x3c00,0x0,0x460500,0x48,0xc020c44, + 0x63636363,0x63228821,0x40404040,0x10101010,0x42432222,0x22222242,0x62414141,0x41104042,0x46464646,0x46465022,0x62626262, + 0x10101010,0x66426666,0x66666618,0x66464646,0x46186618,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048,0x3e063d00,0x8100000,0x18001820, + 0x1c3e7f3e,0x23c1e20,0x3e3c1818,0x10,0x20417e1e,0x7c7f401e,0x417c3842,0x7f41431c,0x401e40be,0x103e0866,0x41107f10,0x4080000, + 0x3a5c1c,0x3a3c103a,0x427c0842,0xe49423c,0x7c3e203c,0xe3a1824,0x66087e10,0x10100000,0x3c103010,0x245a1010,0x5a3e10,0x3f107f10, + 0x10000000,0x0,0x3c08,0x2e107e10,0x1038fc,0x101004,0x0,0x0,0xfe0000,0x7f0500,0x0,0x14041438,0x41414141,0x41418e1e,0x7f7f7f7f, + 0x7c7c7c7c,0x7c431c1c,0x1c1c1c00,0xbc3e3e3e,0x3e10405c,0x3a3a3a3a,0x3a3a6e1c,0x3c3c3c3c,0x7c7c7c7c,0x3c423c3c,0x3c3c3c00, + 0x7c3a3a3a,0x3a087c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x4200000,0x10000020,0x0,0x0,0x10,0x0,0x30000000,0x0, + 0x0,0x0,0x60000,0x0,0x1c,0x4380000,0x0,0x2,0x800,0x0,0x40020000,0x0,0x8000c,0x10600000,0x2010,0x48000000,0x240000,0x0,0x0, + 0x0,0x0,0x0,0x1000,0x1078,0x0,0x0,0x0,0x400500,0x0,0x1e081e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x84008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x20000040,0x0,0x0,0x20,0x0,0x1e000000,0x0,0x0,0x0,0x20000,0x0, + 0x0,0x2000000,0x0,0x26,0x800,0x0,0x40020000,0x0,0x100000,0x10000000,0x2030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x1000,0x0, + 0x0,0x0,0x400000,0x8000000,0x41e0400,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x104010,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x1c,0x7000,0x0,0x40020000,0x0,0x300000, + 0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x0,0x0,0x400000,0x38000000,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1c,0x0,0x0,0x0,0x0,0x0,0x304030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 10x19 font. + const unsigned int font10x19[10*19*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3600000,0x36000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x180181c0,0xe81b0300,0x1801,0x81c06c18,0x181c06c,0xe8180,0x181c0e81,0xb0000006,0x60701b,0x1800000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x1c000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xc030360,0xb81b0480,0xc03,0x3606c0c,0x303606c,0xb80c0,0x30360b81,0xb0000003,0xc0d81b,0x3000000,0x0, + 0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x0,0x2200000, + 0x22000,0x0,0x0,0x0,0x0,0x0,0x0,0x30000,0x0,0xe0,0x38078000,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000c080,0x480,0x3000, + 0xc0800030,0xc08000,0x300,0xc080000,0xc,0x302000,0xc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x41c01,0xe020060c, + 0x800000,0x4,0x1e0703e0,0xf8060fc1,0xe1fe1e07,0x80000000,0x78,0x307e0,0x3c7c1fe7,0xf83c408f,0x80f10440,0x18660878,0x7e0787e0, + 0x78ff9024,0xa0140a0,0x27f83840,0x700e000,0x18000400,0x8000,0x70004002,0x410078,0x0,0x0,0x0,0x0,0x1808,0xc000000,0xf000000, + 0xe000000,0x1400,0x1e0001f,0x8007f800,0x0,0x0,0x3a3b,0x61400000,0x14202,0x20000,0x38002020,0x3c1b00,0x3e00000,0xf8,0x1c0001c0, + 0x78060001,0xf800000e,0x1e00020,0x8004020,0xc0300c0,0x300c0301,0xf83c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1821e0,0x781e0781,0xe0001f10, + 0x24090240,0xa02400f8,0x18018140,0xe81b0480,0x1801,0x81406c18,0x181406c,0x190e8180,0x18140e81,0xb0000006,0x60501b,0x184006c, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x26042202,0x200c06,0x800000,0x8,0x210d0611,0x40e0803,0x10026188,0x40000000, + 0x8c,0xf030418,0xc6431004,0xc64082,0x110840,0x18660884,0x41084410,0x8c081024,0xa012110,0x40082020,0x101b000,0xc000400,0x8000, + 0x80004002,0x410008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x18800000,0x10000000,0x2200,0x2300024,0x800,0x0,0x0,0x2e13,0x60800000, + 0x8104,0x20040,0x64001040,0x80401b07,0x80100000,0x1e000,0x22000020,0x40c0003,0xc8000002,0x3300020,0x8004020,0xc0300c0,0x300c0301, + 0x40c64010,0x4010008,0x2008020,0x43182210,0x84210842,0x10002190,0x24090240,0x9044018c,0xc030220,0xb81b0300,0xc03,0x2206c0c, + 0x302206c,0x1e0b80c0,0x30220b81,0xb0000003,0xc0881b,0x304006c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x241f2202, + 0x200802,0x4900000,0x8,0x21010408,0x20a0802,0x44090,0x20000000,0x4,0x11878408,0x80411004,0x804082,0x111040,0x1ce50986,0x40986409, + 0x81022,0x12012108,0x80102020,0x1031800,0x400,0x8000,0x80004000,0x10008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x10000000, + 0x10000000,0x18,0x4000044,0x1000,0x30180,0xd81b0000,0x13,0xe0000000,0x88,0x40,0x400018c0,0x80400018,0x61f00000,0x61800,0x22020020, + 0x4000007,0xc8000002,0x2100020,0x8038000,0x1e0781e0,0x781e0301,0x40804010,0x4010008,0x2008020,0x41142619,0x86619866,0x18002190, + 0x24090240,0x8887e104,0x0,0x0,0x0,0x0,0x0,0x2000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x2434a202, + 0x200802,0x3e00000,0x10,0x40810008,0x21a0804,0x44090,0x20000000,0x80040004,0x20848409,0x409004,0x1004082,0x112040,0x14a50902, + 0x40902409,0x81022,0x11321208,0x80202010,0x1060c00,0x7c5e0,0x781e8783,0xf07a5f0e,0x1c10808,0xfc5f078,0x5e07a170,0x7c7e1024, + 0xa016190,0x27f82008,0x2000000,0x20000000,0x10000000,0x80200024,0x4000044,0x2000,0x18180,0xc8320000,0x12,0xa1f00037,0x7f888, + 0x1e0,0x40410880,0x80600017,0xa2100000,0x5e800,0x22020040,0x38001027,0xc8000002,0x2100020,0x8004020,0x12048120,0x48120482, + 0x41004010,0x4010008,0x2008020,0x40942409,0x2409024,0x9044390,0x24090240,0x88841918,0x1f07c1f0,0x7c1f07c3,0x70781e07,0x81e07838, + 0xe0380e0,0x1f17c1e0,0x781e0781,0xe0001f90,0x24090240,0x9025e102,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xff241c41, + 0x1001,0x1c02000,0x10,0x40810008,0x6120f85,0xe0086190,0x20c03007,0x8007800c,0x27848419,0x409004,0x1004082,0x114040,0x14a48902, + 0x40902409,0x81022,0x11321205,0x602010,0x1000000,0x86610,0x84218840,0x80866182,0x411008,0x9261884,0x61086189,0x82101022,0x12012108, + 0x40082008,0x2000000,0x20030000,0x20000000,0x80200024,0x4000044,0x3006030,0xc018100,0x4c260000,0x12,0x26080048,0x83000850, + 0x20250,0x403e0500,0x8078002c,0x12302200,0x92400,0x1c0200c0,0x4001027,0xc8000002,0x3308820,0x8004020,0x12048120,0x48120482, + 0x41004010,0x4010008,0x2008020,0x40922409,0x2409024,0x8884690,0x24090240,0x85040920,0x21886218,0x86218860,0x88842108,0x42108408, + 0x2008020,0x21186210,0x84210842,0x10302190,0x24090240,0x88461084,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x4c240182, + 0x80001001,0x6b02000,0x20,0x4c810010,0x78220846,0x10081e10,0x20c0301c,0x1fe0e018,0x4d8487e1,0x409fe7,0xf9007f82,0x11a040, + 0x13248902,0x41102418,0xe0081022,0x11320c05,0x402008,0x1000000,0x2409,0x409020,0x81024082,0x412008,0x9240902,0x40902101,0x101022, + 0x11321208,0x40102008,0x2000000,0x7e0c8000,0xfc000003,0xf0fc0018,0x43802047,0x8c8040c8,0x32008300,0x44240000,0x0,0x4000048, + 0x8c801050,0x20440,0x40221dc0,0x808c0028,0x11d0667f,0x8009c400,0x1fc180,0x4001023,0xc8300002,0x1e0ccfb,0x3ec7b020,0x12048120, + 0x48120482,0x79007f9f,0xe7f9fe08,0x2008020,0xf0922409,0x2409024,0x8504490,0x24090240,0x85040920,0x802008,0x2008020,0x89004090, + 0x24090208,0x2008020,0x40902409,0x2409024,0x8304390,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, + 0x481c0606,0xc8001001,0x802000,0x20,0x4c810020,0x4220024,0x8102108,0x60000070,0x3820,0x48884419,0x409004,0x10e4082,0x112040, + 0x13244902,0x7e1027e0,0x3c081021,0x21320c02,0x802008,0x1000000,0x7e409,0x409020,0x81024082,0x414008,0x9240902,0x40902101, + 0x80101022,0x11320c08,0x40202008,0x2038800,0x200bc000,0x20000000,0x80200003,0x80f04044,0xbc080bc,0x2f000200,0x0,0x0,0x6001048, + 0x8bc02020,0x20441,0xf8220200,0x80820028,0x1000cc00,0x80094400,0x201e0,0x78001021,0xc830000f,0x8000663c,0xf03c0c0,0x21084210, + 0x84210846,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8204890,0x24090240,0x82040930,0x1f87e1f8,0x7e1f87e0,0x89004090, + 0x24090208,0x2008020,0x40902409,0x2409024,0x8004690,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, + 0x480719c4,0x48001001,0x81fc00,0x7800020,0x40810040,0x2420024,0x8104087,0xa0000070,0x3820,0x48884409,0x409004,0x1024082,0x111040, + 0x13244902,0x40102410,0x2081021,0x214a1202,0x1802008,0x1000000,0x182409,0x409fe0,0x81024082,0x41a008,0x9240902,0x40902100, + 0xf8101021,0x214a0c04,0x80c0c008,0x1847000,0x7c1ee000,0x20000000,0x8020000c,0x8c044,0x1ee181ee,0x7b800000,0x707,0xf3ff0000, + 0x3e0084f,0x9ee0c020,0x20440,0x40221fc0,0xc2002c,0x13f11000,0x87892400,0x20000,0x1020,0x48000000,0x3f011c6,0x31cc6180,0x21084210, + 0x84210844,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8505090,0x24090240,0x8204191c,0x60982609,0x82609823,0xf9007f9f, + 0xe7f9fe08,0x2008020,0x40902409,0x2409024,0x9fe4c90,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xfe048224, + 0x28001001,0x2000,0x40,0x40810080,0x27f8024,0x8104080,0x2000001c,0x1fe0e020,0x488fc409,0x409004,0x1024082,0x110840,0x10242902, + 0x40102408,0x2081021,0x214a1202,0x1002004,0x1000000,0x102409,0x409000,0x81024082,0x411008,0x9240902,0x40902100,0x6101021, + 0x214a0c04,0x81002008,0x2000000,0x201dc000,0x20000000,0x80200000,0x98044,0x1dc101dc,0x77000000,0x700,0x0,0x180448,0x1dc10020, + 0x20440,0x403e0200,0x620017,0xa000cc00,0x80052800,0x20000,0x1020,0x48000000,0x6606,0x206100,0x3f0fc3f0,0xfc3f0fc7,0xc1004010, + 0x4010008,0x2008020,0x4090a409,0x2409024,0x8886090,0x24090240,0x8207e106,0x40902409,0x2409024,0x81004010,0x4010008,0x2008020, + 0x40902409,0x2409024,0x8005890,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98048224,0x30001001,0x2000, + 0x40,0x21010100,0x2020024,0x8204080,0x40000007,0x80078000,0x48884408,0x80411004,0x824082,0x110840,0x10242986,0x40086409,0x2081021, + 0xe14a2102,0x2002004,0x1000000,0x106409,0x409000,0x81024082,0x410808,0x9240902,0x40902100,0x2101021,0x214a1202,0x82002008, + 0x2000000,0x300f8000,0x20000000,0x80fc001d,0xe4088044,0xf8200f8,0x3e000000,0x300,0x0,0x80c48,0xf820020,0x20640,0x40410200, + 0x803c0018,0x60006600,0x61800,0x0,0x1020,0x48000000,0xcc0a,0x20a100,0x21084210,0x84210844,0x40804010,0x4010008,0x2008020, + 0x4110a619,0x86619866,0x19046110,0x24090240,0x82040102,0x41906419,0x6419064,0x81004010,0x4010008,0x2008020,0x40902409,0x2409024, + 0x8307090,0x24090240,0x82840828,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x90248222,0x30000802,0x200c,0xc080,0x21010301, + 0x4021042,0x10202108,0xc0c03000,0x80040020,0x4d902418,0xc6431004,0xc24082,0x6210440,0x10241884,0x40084409,0x86080840,0xc0842102, + 0x4002002,0x1000000,0x18e610,0x84218820,0x80864082,0x410408,0x9240884,0x61086101,0x6101860,0xc0842103,0x4002008,0x2000000, + 0x10850180,0x20330000,0x80200013,0x26184024,0x5040050,0x14000000,0x0,0x0,0x4180848,0x85040020,0x20350,0x40000200,0x800c0007, + 0x80002200,0x1e000,0x0,0x1860,0x48000000,0x880a,0x40a188,0x40902409,0x2409028,0x40c64010,0x4010008,0x2008020,0x43106210,0x84210842, + 0x10006108,0x42108421,0x2040102,0x6398e639,0x8e6398e4,0x88842088,0x22088208,0x2008020,0x21102210,0x84210842,0x10306118,0x66198661, + 0x83061030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0x901f01c1,0xe8000802,0xc,0xc080,0x1e07c7f8,0xf8020f81,0xe0401e07, + 0x80c03000,0x20,0x279027e0,0x3c7c1fe4,0x3c408f,0x83c1027f,0x90241878,0x4007c404,0xf8080780,0xc0844082,0x7f82002,0x1000000, + 0xfa5e0,0x781e87c0,0x807a409f,0xc0410207,0x9240878,0x5e07a100,0xf80e0fa0,0xc0846183,0x7f82008,0x2000000,0xf020100,0x40321360, + 0x80200014,0xa3e0201f,0x8207f820,0x8000000,0x0,0x0,0x3e01037,0x207f820,0x201e1,0xfc000200,0x80040000,0x0,0x0,0x1fc000,0x17b0, + 0x48000000,0x12,0xc120f0,0x40902409,0x2409028,0x783c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1061e0,0x781e0781,0xe000be07,0x81e0781e, + 0x204017c,0x3e8fa3e8,0xfa3e8fa3,0x70781f07,0xc1f07c7f,0x1fc7f1fc,0x1e1021e0,0x781e0781,0xe0007e0f,0xa3e8fa3e,0x8305e030,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0xc06,0xc,0x100,0x0,0x0,0x0,0x3000,0x0,0x20000000,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x0,0x2001,0x1000000,0x0,0x0,0x20000,0x400000,0x0,0x40002000,0x0,0x1,0x2008,0x2000000,0x100,0x40240000,0x80200008,0x40000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80040000,0x0,0x0,0x0,0x1000,0x48000000,0x1f,0x181f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1040010,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0x60c,0x18,0x0, + 0x0,0x0,0x0,0x6000,0x0,0x10000000,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x3800,0x7000000,0x0,0x0,0x840000,0x400000,0x0,0x40002000, + 0x0,0x2,0x2008,0x2000000,0x200,0x40440000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80780000,0x0,0x0,0x0,0x1000,0x48000400, + 0x2,0x1e02000,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x2040020,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x4000,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3800000,0x0,0x40002000,0x0,0xe,0x1808,0xc000000,0x3,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000, + 0x0,0x0,0x0,0x1000,0x1c00,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0xe0400e0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 12x24 font. + const unsigned int font12x24[12*24*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0, + 0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c, + 0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003, + 0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019, + 0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000, + 0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000, + 0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6, + 0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f, + 0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603, + 0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8, + 0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3, + 0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00, + 0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019, + 0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc, + 0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003, + 0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f, + 0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006, + 0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009, + 0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018, + 0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00, + 0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000, + 0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000, + 0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3, + 0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980, + 0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000, + 0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60, + 0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600, + 0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f, + 0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600, + 0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0, + 0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000, + 0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606, + 0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6, + 0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f, + 0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001, + 0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061, + 0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6, + 0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000, + 0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660, + 0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d, + 0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180, + 0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020, + 0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660, + 0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060, + 0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000, + 0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006, + 0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06, + 0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090, + 0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006, + 0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066, + 0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183, + 0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044, + 0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001, + 0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003, + 0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1, + 0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f, + 0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660, + 0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60, + 0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000, + 0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001, + 0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003, + 0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603, + 0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00, + 0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066, + 0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, + 0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004, + 0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006, + 0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06, + 0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de, + 0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6, + 0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066, + 0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, + 0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000, + 0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006, + 0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06, + 0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc, + 0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000, + 0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8, + 0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000, + 0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000, + 0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e, + 0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c, + 0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000, + 0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140, + 0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060, + 0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0, + 0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030, + 0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60, + 0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c, + 0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000, + 0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240, + 0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0, + 0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71, + 0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300, + 0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8, + 0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8, + 0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0, + 0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000, + 0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000, + 0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83, + 0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000, + 0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6, + 0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6, + 0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0, + 0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0, + 0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f, + 0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c, + 0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0, + 0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0, + 0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6, + 0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000, + 0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000, + 0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000, + 0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0, + 0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000, + 0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0, + 0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0, + 0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 16x32 font. + const unsigned int font16x32[16*32*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730, + 0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180, + 0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, + 0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380, + 0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380, + 0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000, + 0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070, + 0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c, + 0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000, + 0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700, + 0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180, + 0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0, + 0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000, + 0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000, + 0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f, + 0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0, + 0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038, + 0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0, + 0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0, + 0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0, + 0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007, + 0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0, + 0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0, + 0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000, + 0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc, + 0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0, + 0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0, + 0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0, + 0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000, + 0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0, + 0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e, + 0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0, + 0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038, + 0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0, + 0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300, + 0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08, + 0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380, + 0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0, + 0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c, + 0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00, + 0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838, + 0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0, + 0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000, + 0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0, + 0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0, + 0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0, + 0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000, + 0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000, + 0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180, + 0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000, + 0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000, + 0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007, + 0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70, + 0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180, + 0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0, + 0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838, + 0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180, + 0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770, + 0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770, + 0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc, + 0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380, + 0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12, + 0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770, + 0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038, + 0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0, + 0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380, + 0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00, + 0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c, + 0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400, + 0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe, + 0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770, + 0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038, + 0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78, + 0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0, + 0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c, + 0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0, + 0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8, + 0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0, + 0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0, + 0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c, + 0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c, + 0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0, + 0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0, + 0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800, + 0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380, + 0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0, + 0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860, + 0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0, + 0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c, + 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70, + 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe, + 0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc, + 0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc, + 0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70, + 0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180, + 0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0, + 0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0, + 0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc, + 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70, + 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe, + 0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000, + 0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0, + 0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc, + 0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0, + 0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000, + 0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc, + 0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838, + 0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0, + 0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000, + 0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0, + 0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc, + 0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0, + 0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838, + 0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c, + 0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0, + 0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180, + 0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000, + 0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0, + 0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c, + 0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0, + 0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838, + 0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c, + 0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0, + 0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180, + 0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c, + 0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c, + 0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0, + 0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0, + 0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0, + 0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8, + 0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800, + 0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000, + 0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000, + 0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0, + 0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78, + 0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000, + 0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838, + 0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0, + 0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c, + 0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0, + 0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180, + 0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18, + 0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380, + 0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78, + 0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0, + 0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000, + 0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000, + 0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c, + 0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78, + 0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000, + 0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8, + 0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380, + 0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8, + 0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380, + 0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe, + 0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc, + 0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc, + 0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8, + 0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180, + 0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8, + 0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0, + 0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38, + 0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000, + 0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000, + 0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078, + 0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0, + 0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0, + 0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000, + 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0, + 0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000, + 0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000, + 0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0, + 0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, + 0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000, + 0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0, + 0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, + 0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000, + 0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000, + 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 19x38 font. + const unsigned int font19x38[19*38*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c380000,0x0,0x1c380,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800007,0x3c003,0x86000000, + 0x1e00000,0x3,0x80000700,0x3c00000,0x380000,0x70003c00,0x0,0xe1800e,0x1c00,0xf000e18,0x0,0x0,0x700000e0,0x780000,0x7000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe700000,0x0,0xe700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0000e,0x7e003,0xe60071c0,0x7f80000,0x1,0xc0000e00,0x7e0038e,0x1c0000, + 0xe0007e00,0x38e00000,0xf98007,0x3800,0x1f800f98,0x1c70000,0x0,0x380001c0,0xfc0071,0xc000e000,0x0,0x0,0x0,0x0,0x3e00000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x7e00000,0x0,0x7e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0001c,0xe7006,0x7c0071c0,0xe180000,0x0,0xe0001c00,0xe70038e,0xe0001,0xc000e700,0x38e00000, + 0x19f0003,0x80007000,0x39c019f0,0x1c70000,0x0,0x1c000380,0x1ce0071,0xc001c000,0x0,0x0,0x0,0x0,0x7f00000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, + 0x0,0x3c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x700038,0x1c3806,0x3c0071c0,0xc0c0000,0x0,0x70003800,0x1c38038e,0x70003,0x8001c380,0x38e00000,0x18f0001,0xc000e000, + 0x70e018f0,0x1c70000,0x0,0xe000700,0x3870071,0xc0038000,0x0,0x0,0x0,0x0,0xe380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60000000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c38,0x0,0x1,0xc3800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000003,0x80018000,0x0,0xc180000, + 0xe,0x380,0x1800000,0xe00000,0x38001800,0x0,0x38,0xe00,0x6000000,0x0,0x1,0xc0000070,0x300000,0x3800,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78c00,0xc30, + 0x0,0x0,0xc3000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800000,0x0,0x0,0x0,0xe0,0x1c000f,0xc0000000,0x0,0x0, + 0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000007,0x3c003,0xc6000000,0xc180000,0x7,0x700, + 0x3c00000,0x700000,0x70003c00,0x0,0xf1801c,0x1c00,0xf000f18,0x0,0x0,0xe00000e0,0x780000,0x7000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x3800000,0x700000,0x38, + 0x7,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf800e,0x3e0000,0x0,0x0,0x0,0x1e00000,0x0,0x1, + 0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7cc00,0x660,0x0,0x0,0x66000000,0x0,0x0,0x0,0x0,0x7,0x1c000000,0x0,0x0,0x0,0x3fe00000, + 0x0,0x0,0x7000000,0x0,0x0,0x0,0x3e0,0x7c001f,0xe0000000,0x0,0x0,0x0,0xe1c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x1f80,0x380000e,0x7e007,0xe60071c0,0xc180000,0x3,0x80000e00,0x7e0038e,0x380000,0xe0007e00,0x38e00f00,0x1f9800e, + 0x3800,0x1f801f98,0x1c70000,0x0,0x700001c0,0xfc0071,0xc000e007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61c00600,0x1e00007e,0x70000,0x18003000,0x1800000,0x0,0x0,0x1c01f0,0x7e003f,0xc003f800, + 0x1e03ffc,0x7f01ff,0xfc03f000,0x7e000000,0x0,0x0,0xfc0,0x1e,0x7fe000,0x7e03fe00,0x3fff07ff,0xe007e038,0x383ffe0,0xff81c01, + 0xe1c000f8,0xf8f00e0,0xfc01ffc,0x3f00ff,0xc000fe07,0xfffc7007,0x1c007700,0x73c01ef,0x78ffff,0xfe0380,0xfe000,0x38000000,0x1800000, + 0x700000,0x38,0x1f,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0xfc0000, + 0x0,0x7f00000,0x0,0x1,0x98000000,0x7f00000,0x3ffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0xcf81f,0xee3807e0,0x0,0x0,0x7e03c01e,0x1c, + 0x0,0x1f800000,0xf0078038,0xfc007,0x1c000000,0xfe00000,0x0,0x0,0x3fe000f0,0xf,0xc001f800,0x6000000,0xffc000,0x0,0x1c0007e0, + 0x360,0x6c0010,0x70000700,0xf0001e,0x3c000,0x78000f00,0x7f800ff,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0, + 0x7807007,0xe000fc00,0x1f8003f0,0x7e0000,0x1f867,0x70e00e,0x1c01c380,0x38f00787,0x3fe0,0x180000c,0x66006,0x7c0071c0,0xe380000, + 0x1,0x80000c00,0x660038e,0x180000,0xc0006600,0x38e0078e,0x19f0006,0x3000,0x198019f0,0x1c70000,0x0,0x30000180,0xcc0071,0xc000c007, + 0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61800600,0x7f8001ff,0x70000, + 0x38003800,0x1800000,0x0,0x0,0x3807fc,0x1fe00ff,0xf00ffe00,0x3e03ffc,0xff81ff,0xfc07fc01,0xff800000,0x0,0x0,0x3fe0,0xfe001e, + 0x7ff801,0xff83ff80,0x3fff07ff,0xe01ff838,0x383ffe0,0xff81c03,0xc1c000f8,0xf8f80e0,0x3ff01fff,0xffc0ff,0xf003ff87,0xfffc7007, + 0x1e00f700,0x71c03c7,0x70ffff,0xfe01c0,0xfe000,0x7c000000,0xc00000,0x700000,0x38,0x3f,0xe000001c,0x1c00,0x1c00700,0x7fc0000, + 0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0x3fe0000,0x0,0xff00000,0x0,0x3,0xc000000,0x1ffc0000,0xfffe00, + 0xffff0,0x0,0x0,0x0,0x0,0x0,0xc781f,0xee3803c0,0x0,0x0,0x3c01c01c,0x1c,0xc000,0x7fc00000,0x70070038,0x3fe007,0x1c000000,0x1ff80000, + 0x0,0x0,0x3fe003fc,0x1f,0xe003fc00,0xc000000,0x3ffc000,0x0,0x7c000ff0,0x60,0xc0000,0x30000700,0xf0001e,0x3c000,0x78000f00, + 0x3f000ff,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x7c0701f,0xf803ff00,0x7fe00ffc,0x1ff8000,0x7fe67, + 0x70e00e,0x1c01c380,0x38700707,0x7ff0,0xc00018,0xc3006,0x3c0071c0,0x7f00000,0x0,0xc0001800,0xc30038e,0xc0001,0x8000c300,0x38e003fc, + 0x18f0003,0x6000,0x30c018f0,0x1c70000,0x0,0x18000300,0x1860071,0xc0018007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe1801fc0,0x618001ff,0x70000,0x30001800,0x21840000,0x0,0x0,0x380ffe,0x1fe00ff, + 0xfc0fff00,0x3e03ffc,0x1ff81ff,0xfc0ffe03,0xffc00000,0x0,0x0,0x7ff0,0x3ff803f,0x7ffc03,0xffc3ffc0,0x3fff07ff,0xe03ffc38,0x383ffe0, + 0xff81c07,0x81c000f8,0xf8f80e0,0x7ff81fff,0x81ffe0ff,0xf80fff87,0xfffc7007,0xe00e700,0x70e0387,0x80f0ffff,0xe001c0,0xe000, + 0xfe000000,0xe00000,0x700000,0x38,0x3c,0x1c,0x1c00,0x1c00700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x78000e,0x3c000, + 0x0,0x7ff0000,0x0,0xf100000,0x0,0x7,0xe000000,0x7ffc0000,0x1fffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0x3,0xf780180,0x0,0x0,0x1801e03c, + 0x1c,0xc000,0xffc00000,0x780f0038,0x786000,0x7f00,0x18380000,0x0,0xfe00,0x30c,0x10,0x70020e00,0x1c000000,0x7f8c000,0x0,0x6c001c38, + 0x60,0xc0000,0x70000700,0x1f8003f,0x7e000,0xfc001f80,0x3f000ff,0xf03ffc1f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc, + 0x7c0703f,0xfc07ff80,0xfff01ffe,0x3ffc000,0xffec7,0x70e00e,0x1c01c380,0x38780f07,0xf070,0xe00038,0x1c3800,0x0,0x3e00000,0x0, + 0xe0003800,0x1c380000,0xe0003,0x8001c380,0x3e0,0x3,0x8000e000,0x70e00000,0x0,0x0,0x1c000700,0x3870000,0x38007,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe3807ff0,0xc0c003c1,0x70000,0x70001c00, + 0x718e0000,0x0,0x0,0x700f1e,0x1ce00c0,0x3c0c0f80,0x7e03800,0x3e08000,0x381e0f03,0xc1e00000,0x0,0x0,0x7078,0x783c03f,0x701e07, + 0xc1c383e0,0x38000700,0x7c1c38,0x3801c00,0x381c0f,0x1c000fc,0x1f8f80e0,0x78781c07,0x81e1e0e0,0x780f0180,0xe007007,0xe00e380, + 0xe0f0783,0x80e0000e,0xe000e0,0xe001,0xef000000,0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000, + 0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xf830000,0x0,0x1e000000,0x0,0x0,0x10000,0x780c0000,0x3e38000,0xe0,0x0,0x0,0x0,0x0,0x0,0x3, + 0xd580000,0x0,0x0,0xe038,0x1c,0xc000,0xf0400000,0x380e0038,0x702000,0x1ffc0,0xc0000,0x0,0x3ff80,0x606,0x0,0x30000600,0x0, + 0x7f8c000,0x0,0xc001818,0x60,0xc0003,0xe0000700,0x1f8003f,0x7e000,0xfc001f80,0x73801ee,0x7c1c1c,0x38000,0x70000e00,0xe0001, + 0xc0003800,0x700383e,0x7c0703c,0x3c078780,0xf0f01e1e,0x3c3c000,0xf0f87,0x70e00e,0x1c01c380,0x38380e07,0xe038,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xff0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xc380fff0,0xc0c00380,0x70000,0x70001c00,0x3dbc0070,0x0,0x0,0x701e0f,0xe0000,0x1e000380, + 0x6e03800,0x7800000,0x781c0707,0x80e00000,0x0,0x0,0x4038,0xe00c03f,0x700e07,0x4380f0,0x38000700,0x700438,0x3801c00,0x381c0e, + 0x1c000ec,0x1b8fc0e0,0xf03c1c03,0xc3c0f0e0,0x3c1e0000,0xe007007,0xe00e380,0xe070703,0xc1e0001e,0xe000e0,0xe001,0xc7000000, + 0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xe010000,0x0, + 0x1c000000,0x10,0x20000,0x6c000,0xf0000000,0x3838000,0x1e0,0x0,0xf000f,0xf1e00,0x78f00000,0x0,0x3,0xdd80000,0x0,0x0,0xf078, + 0x0,0xc001,0xe0000000,0x1c1c0038,0x700000,0x3c1e0,0xc0000,0x0,0x783c0,0x606,0x0,0x30000e00,0x0,0xff8c000,0x0,0xc00300c,0x60, + 0xc0003,0xe0000000,0x1f8003f,0x7e000,0xfc001f80,0x73801ce,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x7e07078, + 0x1e0f03c1,0xe0783c0f,0x781e000,0x1c0787,0x70e00e,0x1c01c380,0x383c1e07,0xff00e038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x878, + 0x0,0x0,0x0,0x7,0x80000080,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c, + 0x1c7000,0xc301e630,0xc0c00380,0x70000,0xe0000e00,0xff00070,0x0,0x0,0xe01c07,0xe0000,0xe000380,0xce03800,0x7000000,0x701c0707, + 0x600000,0x0,0x4000010,0x38,0x1c00e07f,0x80700e0e,0x38070,0x38000700,0xe00038,0x3801c00,0x381c1c,0x1c000ec,0x1b8ec0e0,0xe01c1c01, + 0xc38070e0,0x1c1c0000,0xe007007,0x701c380,0xe078e01,0xc1c0003c,0xe00070,0xe003,0x83800000,0x7f,0x71f000,0x3e003e38,0x3f007ff, + 0xe01f1c1c,0x7801fc00,0x3fc00701,0xe01c0077,0x8f071e00,0xf801c7c,0x7c700e,0x3e01fc03,0xfff8380e,0xe007700,0x73c0787,0x387ffc, + 0x70000e,0x1c000,0x0,0xe000000,0x0,0x1c000000,0x10,0x20000,0xc2000,0xe0000000,0x3838000,0x3c0,0x0,0xf000f,0x78e00,0x70e00000, + 0x0,0x3,0xc980fe0,0x1f0,0xf8000007,0xffc07070,0x0,0x3f801,0xc0000000,0x1e3c0038,0x700000,0x70070,0x7fc0000,0x0,0xe00e0,0x606, + 0x1c0000,0x70007c00,0x380e,0xff8c000,0x0,0xc00300c,0x60,0xc0000,0x70000000,0x3fc007f,0x800ff001,0xfe003fc0,0x73801ce,0xe0001c, + 0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807,0x7607070,0xe0e01c1,0xc0383807,0x700e000,0x1c0387,0x70e00e,0x1c01c380,0x381c1c07, + 0xffc0e0f8,0x3f8007f,0xfe001,0xfc003f80,0x7f007e3,0xe003e001,0xf8003f00,0x7e000fc,0xfe001f,0xc003f800,0x7f00003c,0x38f0007, + 0xc000f800,0x1f0003e0,0x7c0007,0x8003f0c3,0x80e0701c,0xe0381c0,0x70700387,0x1f01c00e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0xc0c00380,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03c07, + 0x800e0000,0xe000380,0x1ce03800,0x7000000,0x701c0707,0x7003c0,0x780000,0x3c00001e,0x38,0x18006073,0x80700e0e,0x38070,0x38000700, + 0xe00038,0x3801c00,0x381c38,0x1c000ee,0x3b8ee0e1,0xe01e1c01,0xc78078e0,0x1c1c0000,0xe007007,0x701c387,0xe03de00,0xe3800038, + 0xe00070,0xe007,0x1c00000,0x1ff,0xc077f801,0xff807fb8,0xff807ff,0xe03fdc1d,0xfc01fc00,0x3fc00703,0xc01c007f,0xdf877f00,0x3fe01dfe, + 0xff700e,0xff07ff03,0xfff8380e,0x700f700,0x71e0f03,0x80707ffc,0x70000e,0x1c000,0x0,0x1c000008,0x0,0x1c000000,0x10,0x20000, + 0x82000,0xe0000000,0x7038000,0x80000380,0x2000040,0x7000e,0x38700,0xf1e00000,0x0,0x3,0xc183ff8,0x3fd,0xfc008007,0xffc038e0, + 0x0,0xffc01,0xc0008008,0xe380038,0x380000,0xe3e38,0x1ffc0040,0x80000000,0x1cfc70,0x606,0x1c0000,0xe0007c00,0x380e,0xff8c000, + 0x0,0xc00300c,0x8100060,0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0x73801ce,0xe0001c,0x38000,0x70000e00,0xe0001, + 0xc0003800,0x7003807,0x77070f0,0xf1e01e3,0xc03c7807,0x8f00f080,0x83c0787,0x70e00e,0x1c01c380,0x380e3807,0xffe0e1c0,0xffe01ff, + 0xc03ff807,0xff00ffe0,0x1ffc0ff7,0xf01ff807,0xfc00ff80,0x1ff003fe,0xfe001f,0xc003f800,0x7f0003fc,0x3bf801f,0xf003fe00,0x7fc00ff8, + 0x1ff0007,0x8007fd83,0x80e0701c,0xe0381c0,0x70380707,0x7f80e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0x618081c0,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03803,0x800e0000,0xe000380,0x18e03800, + 0xf000000,0xf01c0707,0x7003c0,0x780000,0xfc00001f,0x80000078,0x301e6073,0x80700e1c,0x38038,0x38000700,0x1c00038,0x3801c00, + 0x381c70,0x1c000e6,0x338ee0e1,0xc00e1c01,0xc70038e0,0x1c1c0000,0xe007007,0x701c387,0xe01dc00,0xf7800078,0xe00070,0xe00e,0xe00000, + 0x3ff,0xe07ffc03,0xffc0fff8,0x1ffc07ff,0xe07ffc1d,0xfe01fc00,0x3fc00707,0x801c007f,0xdf877f80,0x7ff01fff,0x1fff00e,0xff07ff03, + 0xfff8380e,0x700e380,0xe0e0e03,0x80707ffc,0x70000e,0x1c000,0x0,0x7ffc001c,0x0,0x1c000000,0x10,0x20000,0x82000,0xe0000000, + 0x7038001,0xc0000780,0x70000e0,0x3800e,0x38700,0xe1c00000,0x0,0x3,0xc183ff8,0x7ff,0xfc01c007,0xffc03de0,0x0,0x1ffc01,0xc001c01c, + 0xf780038,0x3c0000,0xcff18,0x380c00c1,0x80000000,0x18fe30,0x30c,0x1c0001,0xc0000e00,0x380e,0xff8c000,0x0,0xc00300c,0xc180060, + 0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x877070e0, + 0x71c00e3,0x801c7003,0x8e0071c0,0x1c380fc7,0x70e00e,0x1c01c380,0x380f7807,0x1e0e380,0x1fff03ff,0xe07ffc0f,0xff81fff0,0x3ffe0fff, + 0xf03ffc0f,0xfe01ffc0,0x3ff807ff,0xfe001f,0xc003f800,0x7f0007fe,0x3bfc03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x800fff83,0x80e0701c, + 0xe0381c0,0x70380707,0xffc0e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f, + 0xfff1c600,0x7f8381e0,0x70000,0xc0000600,0xff00070,0x0,0x0,0x1c03803,0x800e0000,0xe000f00,0x38e03fe0,0xe000000,0xe00e0e07, + 0x7003c0,0x780007,0xf0ffff87,0xf00000f0,0x307fe0f3,0xc0703c1c,0x38038,0x38000700,0x1c00038,0x3801c00,0x381ce0,0x1c000e6,0x338e70e1, + 0xc00e1c01,0xc70038e0,0x3c1e0000,0xe007007,0x783c38f,0x8e01fc00,0x770000f0,0xe00038,0xe01c,0x700000,0x381,0xe07c1e07,0xc0c1e0f8, + 0x3c1e0038,0xf07c1f,0xe001c00,0x1c0070f,0x1c0079,0xf3c7c380,0xf0781f07,0x83c1f00f,0xc10f0300,0x1c00380e,0x700e380,0xe0f1e03, + 0xc0f00078,0x70000e,0x1c000,0x0,0xfff8003e,0x0,0x3c000000,0x10,0x20000,0xc6000,0xf0000000,0x7038003,0xe0000f00,0xf8001f0, + 0x3801c,0x18300,0xe1800000,0x0,0x3,0xc187818,0x70f,0x9e03e000,0x7801dc0,0x1c,0x3cc401,0xc000efb8,0x7f7f0038,0x3f0000,0x1ce11c, + 0x300c01c3,0x80000000,0x38c638,0x3fc,0x1c0003,0x80000600,0x380e,0xff8c000,0x0,0xc00300c,0xe1c0060,0xc0010,0x70000700,0x79e00f3, + 0xc01e7803,0xcf0079e0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003, + 0x8e0070e0,0x38381dc7,0x70e00e,0x1c01c380,0x38077007,0xf0e700,0x1c0f0381,0xe0703c0e,0x781c0f0,0x381e083e,0x787c0c1e,0xf03c1e0, + 0x783c0f07,0x800e0001,0xc0003800,0x7000fff,0x3e1c078,0x3c0f0781,0xe0f03c1e,0x783c000,0x1e0f03,0x80e0701c,0xe0381c0,0x70380f07, + 0xc1e0e03c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x8701c600,0x1e0f01e0,0x1, + 0xc0000700,0x3dbc0070,0x0,0x0,0x1c03803,0x800e0000,0x1e01fe00,0x70e03ff8,0xe3e0001,0xe007fc07,0x80f003c0,0x78001f,0xc0ffff81, + 0xfc0001e0,0x30e1e0e1,0xc07ff81c,0x38038,0x3ffe07ff,0xc1c0003f,0xff801c00,0x381de0,0x1c000e7,0x738e70e1,0xc00e1c03,0xc70038e0, + 0x780f8000,0xe007007,0x383838d,0x8e00f800,0x7f0000e0,0xe00038,0xe000,0x0,0x200,0xf0780e07,0x8041c078,0x380e0038,0xe03c1e, + 0xf001c00,0x1c0071e,0x1c0070,0xe1c783c0,0xe0381e03,0x8380f00f,0xe0000,0x1c00380e,0x381c380,0xe07bc01,0xc0e00078,0x70000e, + 0x1c000,0x0,0x1c000061,0x0,0x38000000,0x10,0x20000,0x7c000,0x7c000000,0x703fc06,0x10000e00,0x18400308,0x1801c,0x1c381,0xc3800000, + 0x0,0x0,0x7000,0xe0f,0xe061000,0x7801fc0,0x1c,0x38c001,0xc0007ff0,0x7fff0038,0x77c000,0x19c00c,0x301c0387,0x0,0x30c618,0xf0, + 0x1c0007,0x600,0x380e,0x7f8c007,0x80000000,0xc001818,0x70e03fc,0x387f871f,0xe0e00700,0x70e00e1,0xc01c3803,0x870070e0,0xe1c038f, + 0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003,0x8e007070,0x703839c7,0x70e00e, + 0x1c01c380,0x3807f007,0x70e700,0x10078200,0xf0401e08,0x3c10078,0x200f001c,0x3878041c,0x70380e0,0x701c0e03,0x800e0001,0xc0003800, + 0x7001e0f,0x3c1e070,0x1c0e0381,0xc070380e,0x701c000,0x1c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80e07038,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600e600,0x7803f0,0x1,0xc0000700,0x718e0070,0x0,0x0,0x38038c3, + 0x800e0000,0x3c01f800,0x60e03ffc,0xeff8001,0xc001f003,0xc1f003c0,0x7800fe,0xffff80,0x3f8003c0,0x60c0e0e1,0xc07fe01c,0x38038, + 0x3ffe07ff,0xc1c07e3f,0xff801c00,0x381fe0,0x1c000e3,0x638e30e1,0xc00e1c07,0x870038ff,0xf00ff800,0xe007007,0x38381cd,0x9c007000, + 0x3e0001e0,0xe0001c,0xe000,0x0,0x0,0x70780f0f,0x3c078,0x70070038,0x1e03c1c,0x7001c00,0x1c0073c,0x1c0070,0xe1c701c1,0xe03c1e03, + 0xc780f00f,0xe0000,0x1c00380e,0x381c387,0xe03f801,0xc0e000f0,0x70000e,0x1c007,0xe0100000,0x1c0000cd,0x80000003,0xffc00000, + 0x3ff,0x807ff000,0xe0,0x7fc00060,0x703fc0c,0xd8001e00,0x3360066c,0x1c018,0xc181,0x83000000,0x0,0x0,0x7000,0x300e07,0xe0cd800, + 0xf000f80,0x1c,0x78c00f,0xff0038e0,0x3e00038,0xe1e000,0x19800c,0x383c070e,0x7fffc00,0x30fc18,0x0,0xffff80e,0x20e00,0x380e, + 0x7f8c007,0x80000000,0xc001c38,0x38703ff,0xf87fff0f,0xcfe00f00,0x70e00e1,0xc01c3803,0x870070e0,0x1e1e078f,0xe1c0001f,0xff03ffe0, + 0x7ffc0fff,0x800e0001,0xc0003800,0x700ff83,0x871870e0,0x71c00e3,0x801c7003,0x8e007038,0xe03871c7,0x70e00e,0x1c01c380,0x3803e007, + 0x70e700,0x38000,0x70000e00,0x1c00038,0x7001c,0x38f00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7001c07,0x380e0f0,0x1e1e03c3, + 0xc078780f,0xf01e000,0x3c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80f07038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600ff00,0x1e00778,0x38000001,0xc0000700,0x21843fff,0xe0000000,0x0,0x38039e3,0x800e0000, + 0x7c01fe00,0xe0e0203e,0xeffc001,0xc00ffe03,0xff700000,0x7f0,0x0,0x7f00380,0x618060e1,0xc07ffc1c,0x38038,0x3ffe07ff,0xc1c07e3f, + 0xff801c00,0x381ff0,0x1c000e3,0x638e38e1,0xc00e1fff,0x870038ff,0xc003fe00,0xe007007,0x38381cd,0x9c00f800,0x3e0003c0,0xe0001c, + 0xe000,0x0,0x0,0x7070070e,0x38038,0x70070038,0x1c01c1c,0x7001c00,0x1c00778,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0xfc000, + 0x1c00380e,0x381c3c7,0x1e01f001,0xe1e001e0,0xf0000e,0x1e01f,0xf8300000,0x1c00019c,0xc0000003,0xffc00000,0x10,0x20000,0x700, + 0x1ff000c0,0x703fc19,0xcc003c00,0x67300ce6,0xc038,0xc181,0x83000000,0x0,0x0,0x7e00,0x180e07,0xe19cc00,0x1e000f80,0x1c,0x70c00f, + 0xff007070,0x3e00038,0xe0f000,0x19800c,0x1fec0e1c,0x7fffc00,0x30f818,0x0,0xffff81f,0xf003fc00,0x380e,0x3f8c007,0x80000000, + 0x7f800ff0,0x1c3803f,0xe007fc00,0xff800e00,0x70e00e1,0xc01c3803,0x870070e0,0x1c0e070f,0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001, + 0xc0003800,0x700ff83,0x871c70e0,0x71c00e3,0x801c7003,0x8e00701d,0xc038e1c7,0x70e00e,0x1c01c380,0x3803e007,0x70e3c0,0x38000, + 0x70000e00,0x1c00038,0x7001c,0x38e00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7003c07,0x8380e0e0,0xe1c01c3,0x80387007, + 0xe00e1ff,0xfe381b83,0x80e0701c,0xe0381c0,0x701e1e07,0x707878,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1c,0x3,0xe007fe0,0x7800e3c,0x38000001,0xc0000700,0x1803fff,0xe0000000,0x0,0x70039c3,0x800e0000,0xf8000f80, + 0xc0e0000e,0xf83c003,0xc01e0f01,0xff700000,0x7c0,0x0,0x1f00780,0x618061c0,0xe0701e1c,0x38038,0x38000700,0x1c07e38,0x3801c00, + 0x381e78,0x1c000e3,0xe38e18e1,0xc00e1fff,0x70038ff,0xe0007f80,0xe007007,0x1c701dd,0x9c00f800,0x1c000780,0xe0000e,0xe000,0x0, + 0x7f,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x7fc00,0x1c00380e, + 0x1c381c7,0x1c01f000,0xe1c001c0,0xfe0000e,0xfe1f,0xfff00000,0x7ff003fc,0xe0000003,0xffc00000,0x10,0x20000,0x3800,0x3fc0180, + 0x703803f,0xce007800,0xff381fe7,0x30,0x0,0xc0,0x0,0x0,0x3fe0,0xc0e07,0xfe3fce00,0x1c000700,0x1c,0x70c00f,0xff006030,0x1c00000, + 0xe07800,0x19800c,0xfcc1c38,0x7fffc00,0x30d818,0x0,0xffff81f,0xf001f800,0x380e,0xf8c007,0x80000000,0x7f8007e0,0xe1c3fe,0x7fc00f, + 0xf8001e00,0xe0701c0,0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700ff83,0x870c70e0, + 0x71c00e3,0x801c7003,0x8e00700f,0x8038c1c7,0x70e00e,0x1c01c380,0x3801c007,0xf0e3e0,0x3ff807f,0xf00ffe01,0xffc03ff8,0x7ff03ff, + 0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe383383,0x80e0701c, + 0xe0381c0,0x700e1c07,0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0xc000ff0, + 0x3c1e1c1c,0x38000001,0xc0000700,0x1803fff,0xe0000007,0xf8000000,0x7003803,0x800e0001,0xf0000381,0xc0e00007,0xf01e003,0x801c0700, + 0x7c700000,0x7c0,0x0,0x1f00700,0x618061c0,0xe0700e1c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381e38,0x1c000e1,0xc38e1ce1, + 0xc00e1ffc,0x70038e0,0xf0000780,0xe007007,0x1c701dd,0xdc01fc00,0x1c000780,0xe0000e,0xe000,0x0,0x1ff,0xf070070e,0x38038,0x7fff0038, + 0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3ff00,0x1c00380e,0x1c381cd,0x9c00e000,0xe1c003c0, + 0xf80000e,0x3e18,0x3ff00000,0xffe007fd,0xf0000000,0x38000000,0x10,0x20000,0x1c000,0x3c0300,0x703807f,0xdf007801,0xff7c3fef, + 0x80000000,0x0,0x3e0,0x7ffe7ff,0xff000000,0x1ff8,0x60e07,0xfe7fdf00,0x3c000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0xf03800, + 0x19800c,0x1c38,0x1c07,0xf830cc18,0x0,0x1c0000,0x0,0x380e,0x18c007,0x80000000,0x0,0xe1cfe0,0x1fc003f,0x80003c00,0xe0701c0, + 0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003, + 0x8e007007,0x3981c7,0x70e00e,0x1c01c380,0x3801c007,0x1e0e0f8,0xfff81ff,0xf03ffe07,0xffc0fff8,0x1fff07ff,0xf8e0003f,0xff87fff0, + 0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe386383,0x80e0701c,0xe0381c0,0x700e1c07, + 0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x7f,0xffc00678,0x707f9c1e,0x38000001, + 0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0003,0xe00001c3,0x80e00007,0xe00e007,0x80380380,0x700000,0x7f0,0x0,0x7f00700, + 0x618061ff,0xe070071c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381c3c,0x1c000e1,0xc38e1ce1,0xc00e1c00,0x70038e0,0x700003c0, + 0xe007007,0x1c701d8,0xdc03dc00,0x1c000f00,0xe00007,0xe000,0x0,0x3ff,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007fc, + 0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3f00,0x1c00380e,0x1c381cd,0x9c01f000,0x73800780,0xfe0000e,0xfe10,0x7c00000,0x1c000ffb, + 0xf8000000,0x38000000,0x10,0x20000,0x20000,0x1e0700,0x70380ff,0xbf80f003,0xfefe7fdf,0xc0000000,0x0,0x3f0,0x7ffe7ff,0xff000000, + 0x1f8,0x30e07,0xfeffbf80,0x78000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0x783800,0x1ce11c,0xe1c,0x1c07,0xf838ce38,0x0,0x1c0000, + 0x0,0x380e,0x18c000,0x0,0x0,0x1c38c00,0x1800030,0x7800,0xfff01ff,0xe03ffc07,0xff80fff0,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00, + 0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003,0x8e00700f,0x803b81c7,0x70e00e,0x1c01c380,0x3801c007,0xffe0e03c, + 0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0fff,0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3, + 0x80387007,0xe00e000,0x38c383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0063c,0x40619c0f,0x30000001,0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0007,0xc00001c3, + 0xfffc0007,0xe00e007,0x380380,0xf00000,0xfe,0xffff80,0x3f800700,0x618063ff,0xf070071c,0x38038,0x38000700,0x1c00e38,0x3801c00, + 0x381c1e,0x1c000e0,0x38e0ee1,0xc00e1c00,0x70038e0,0x380001c0,0xe007007,0x1ef01d8,0xdc038e00,0x1c001e00,0xe00007,0xe000,0x0, + 0x7c0,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0079e,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x780,0x1c00380e, + 0xe701cd,0x9c01f000,0x73800f00,0xe0000e,0xe000,0x0,0x1c0007f7,0xf0000000,0x70000000,0x10,0x20000,0x0,0xe0e00,0x703807f,0x7f01e001, + 0xfdfc3fbf,0x80000000,0x0,0x7f0,0x0,0x0,0x3c,0x18e07,0x7f7f00,0xf0000700,0x1c,0x70c001,0xc0007070,0x1c00000,0x3e7000,0xcff18, + 0x3ffc070e,0x1c07,0xf818c630,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x3870000,0xe000fc00,0x380f000,0x1fff83ff,0xf07ffe0f, + 0xffc1fff8,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870770e0,0x71c00e3,0x801c7003,0x8e00701d, + 0xc03f01c7,0x70e00e,0x1c01c380,0x3801c007,0xffc0e01c,0x3e0387c0,0x70f80e1f,0x1c3e038,0x7c071e1c,0xe00038,0x70000,0xe0001c00, + 0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x398383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0061c,0xc0dc07,0xf0000001,0xc0000700, + 0x70,0x0,0x0,0x1c003c07,0x800e000f,0x1c3,0xfffc0007,0xe00e007,0x380380,0xe00000,0x1f,0xc0ffff81,0xfc000700,0x618063ff,0xf070070e, + 0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0e,0x1c000e0,0x38e0ee1,0xe01e1c00,0x78078e0,0x380001c0,0xe007007,0xee01f8,0xfc078f00, + 0x1c001c00,0xe00003,0x8000e000,0x0,0x700,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0070e,0x1c0070,0xe1c701c1, + 0xc01c1c01,0xc700700e,0x380,0x1c00380e,0xe700ed,0xb803f800,0x77800f00,0x70000e,0x1c000,0x0,0xe0003f7,0xe0000000,0x70000000, + 0x10,0x20000,0x1c0e0,0xe1c00,0x703803f,0x7e01c000,0xfdf81fbf,0x0,0x0,0x3f0,0x0,0x0,0x1c,0x1ce07,0x3f7e00,0xf0000700,0x1c, + 0x70c001,0xc00038e0,0x1c00038,0xf7000,0xe3e38,0x3ffc0387,0x1c00,0x1cc770,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x70e0001, + 0xe001fe00,0x780e000,0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0ffe,0xe0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807, + 0x70770f0,0xf1e01e3,0xc03c7807,0x8f00f038,0xe03e03c7,0x70e00e,0x1c01c380,0x3801c007,0xff00e00e,0x38038700,0x70e00e1c,0x1c38038, + 0x70071c1c,0xe00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x3b0383,0x80e0701c, + 0xe0381c0,0x70077807,0x701de0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1c00061c, + 0xc0de03,0xe0000001,0xc0000700,0x70,0x0,0x0,0x1c001c07,0xe001e,0x1c3,0xfffc0007,0x600e00e,0x380380,0xe00000,0x7,0xf0ffff87, + 0xf0000000,0x60c0e380,0x7070070e,0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0f,0x1c000e0,0x38e06e0,0xe01c1c00,0x38070e0, + 0x1c0001c0,0xe007007,0xee00f8,0xf80f0700,0x1c003c00,0xe00003,0x8000e000,0x0,0x700,0x70780f0f,0x3c078,0x70000038,0x1e03c1c, + 0x7001c00,0x1c0070f,0x1c0070,0xe1c701c1,0xe03c1e03,0xc780f00e,0x380,0x1c00380e,0xe700f8,0xf807bc00,0x3f001e00,0x70000e,0x1c000, + 0x0,0xe0001ff,0xc0000000,0x70000000,0x10,0x20000,0x33110,0xe0e00,0x383801f,0xfc03c000,0x7ff00ffe,0x0,0x0,0x3e0,0x0,0x0,0x1c, + 0x38e07,0x1ffc01,0xe0000700,0x1c,0x78c001,0xc0007ff0,0x1c00038,0x7c000,0x70070,0x1c3,0x80001c00,0xe00e0,0x0,0x1c0000,0x0, + 0x380e,0x18c000,0x0,0x0,0xe1c0001,0xe0010700,0x780e000,0x1c038380,0x70700e0e,0x1c1c038,0x78070e0e,0xe0001c,0x38000,0x70000e00, + 0xe0001,0xc0003800,0x7003807,0x7037070,0xe0e01c1,0xc0383807,0x700e070,0x701c0387,0x70e00e,0x1c01c380,0x3801c007,0xe00e,0x38038700, + 0x70e00e1c,0x1c38038,0x70071c1c,0xf00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003c07,0x8380e0f0,0x1e1e03c3,0xc078780f, + 0xf01e007,0x803e0783,0x80e0701c,0xe0381c0,0x7003f007,0x80f00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x6,0x1800061c,0xc0de01,0xc0000000,0xc0000e00,0x70,0xf0000,0x3c00,0x38001c0f,0xe003c,0x3c0,0xe0000e,0x701e00e, + 0x3c0780,0x1e003c0,0x780000,0xfc00001f,0x80000000,0x60e1e780,0x78700f07,0x4380f0,0x38000700,0xf00e38,0x3801c00,0xc0781c07, + 0x81c000e0,0x38e07e0,0xe03c1c00,0x380f0e0,0x1e0003c0,0xe00780f,0xee00f0,0x780e0780,0x1c007800,0xe00001,0xc000e000,0x0,0x700, + 0xf0780e07,0x8041c078,0x38020038,0xe03c1c,0x7001c00,0x1c00707,0x801c0070,0xe1c701c0,0xe0381e03,0x8380f00e,0x80380,0x1c003c1e, + 0x7e00f8,0xf80f1e00,0x3f003c00,0x70000e,0x1c000,0x0,0xf0100f7,0x80078000,0x700078f0,0x10,0x7ff000,0x61208,0x1e0700,0x383800f, + 0x78078000,0x3de007bc,0x0,0x0,0x0,0x0,0x0,0x401c,0x70e0f,0xf7803,0xc0000700,0x1c,0x38c001,0xc000efb8,0x1c00038,0x1e000,0x3c1e0, + 0xc1,0x80000000,0x783c0,0x0,0x0,0x0,0x3c1e,0x18c000,0x0,0x0,0xc180003,0x60000300,0xd80e010,0x3c03c780,0x78f00f1e,0x1e3c03c, + 0x70039c0e,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x703f070,0x1e0e03c1,0xc078380f,0x701e0e0,0x381c0787, + 0x80f0f01e,0x1e03c3c0,0x7801c007,0xe00e,0x38078700,0xf0e01e1c,0x3c38078,0x700f1c1c,0x78041c,0x1038020,0x70040e00,0x800e0001, + 0xc0003800,0x7001c07,0x380e070,0x1c0e0381,0xc070380e,0x701c007,0x801e0703,0xc1e0783c,0xf0781e0,0xf003f007,0x80e00fc0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xe,0x1801867c,0xc0cf83,0xe0000000,0xe0000e00, + 0x70,0xf0000,0x3c00,0x38000f1e,0xe0070,0x180780,0xe0603e,0x783c01e,0x1e0f01,0x7c003c0,0x780000,0x3c00001e,0x700,0x307fe700, + 0x38701e07,0xc1c383e0,0x38000700,0x7c1e38,0x3801c00,0xe0f01c03,0x81c000e0,0x38e03e0,0x78781c00,0x1e1e0e0,0xe180780,0xe003c1e, + 0x7c00f0,0x781e03c0,0x1c007000,0xe00001,0xc000e000,0x0,0x783,0xf07c1e07,0xc0c1e0f8,0x3e0e0038,0xf07c1c,0x7001c00,0x1c00703, + 0xc01e0070,0xe1c701c0,0xf0781f07,0x83c1f00e,0xe0f80,0x1e003c3e,0x7e00f8,0xf80e0e00,0x3f003800,0x70000e,0x1c000,0x0,0x7830077, + 0xf0000,0x700078f0,0x10,0x20000,0x41208,0xc03c0380,0x3c38007,0x70070000,0x1dc003b8,0x0,0x0,0x0,0x0,0x0,0x707c,0x6070f,0x86077003, + 0x80000700,0x1c,0x3ec401,0xc001c01c,0x1c00038,0xf000,0x1ffc0,0x40,0x80000000,0x3ff80,0x0,0x0,0x0,0x3e3e,0x18c000,0x0,0x0, + 0x8100006,0x60000300,0x1980f070,0x3801c700,0x38e0071c,0xe3801c,0x70039c0e,0x7c1c1c,0x38000,0x70000e00,0xe0001,0xc0003800, + 0x700383e,0x701f03c,0x3c078780,0xf0f01e1e,0x3c3c1c0,0x1c3f0f03,0xc1e0783c,0xf0781e0,0xf001c007,0xe81e,0x3c1f8783,0xf0f07e1e, + 0xfc3c1f8,0x783f1e3e,0x187c0c1f,0x703e0e0,0x7c1c0f83,0x800e0001,0xc0003800,0x7001e0f,0x380e078,0x3c0f0781,0xe0f03c1e,0x783c007, + 0x801e0f03,0xc3e0787c,0xf0f81e1,0xf003f007,0xc1e00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x1c,0xe,0x3801fff8,0x6187ff,0xe0000000,0xe0000e00,0x70,0xf0000,0x3c00,0x38000ffe,0x1fff0ff,0xfe1fff80,0xe07ffc,0x3ffc01c, + 0x1fff01,0xff8003c0,0x780000,0x4000010,0x700,0x301e6700,0x387ffe03,0xffc3ffc0,0x3fff0700,0x3ffe38,0x383ffe0,0xfff01c03,0xc1fff8e0, + 0x38e03e0,0x7ff81c00,0x1ffe0e0,0xf1fff80,0xe003ffe,0x7c00f0,0x781c01c0,0x1c00ffff,0xe00001,0xc000e000,0x0,0x3ff,0x707ffc03, + 0xffc0fff8,0x1ffe0038,0x7ffc1c,0x707fff0,0x1c00701,0xc00ff070,0xe1c701c0,0x7ff01fff,0x1fff00e,0xfff00,0xff81fee,0x7e00f0, + 0x781e0f00,0x1e007ffc,0x70000e,0x1c000,0x0,0x3ff003e,0xf0000,0xe00070e0,0x60830010,0x20000,0x41208,0xfffc01c0,0x1fffe03,0xe00ffff0, + 0xf8001f0,0x0,0x0,0x0,0x0,0x0,0x7ff8,0xc07fd,0xfe03e007,0xffc00700,0x1c,0x1ffc1f,0xffc08008,0x1c00038,0x7000,0x7f00,0x0,0x0, + 0xfe00,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0x6,0x60000700,0x19807ff0,0x3801c700,0x38e0071c,0xe3801c,0x70039c0f,0xf03ffc1f, + 0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc,0x701f03f,0xfc07ff80,0xfff01ffe,0x3ffc080,0x83fff03,0xffe07ffc,0xfff81ff, + 0xf001c007,0xeffc,0x1ffb83ff,0x707fee0f,0xfdc1ffb8,0x3ff70ff7,0xf83ffc0f,0xff01ffe0,0x3ffc07ff,0x83fff87f,0xff0fffe1,0xfffc0ffe, + 0x380e03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x803ffe01,0xfee03fdc,0x7fb80ff,0x7001e007,0xffc00780,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x3801fff0,0x7f83fe,0x70000000,0xe0000e00,0x0,0xf0000,0x3c00,0x700007fc, + 0x1fff0ff,0xfe1ffe00,0xe07ff8,0x1ff801c,0xffe01,0xff0003c0,0x780000,0x0,0x700,0x38000f00,0x3c7ffc01,0xff83ff80,0x3fff0700, + 0x1ffc38,0x383ffe0,0x7fe01c01,0xe1fff8e0,0x38e03e0,0x3ff01c00,0xffc0e0,0x71fff00,0xe001ffc,0x7c00f0,0x783c01e0,0x1c00ffff, + 0xe00000,0xe000e000,0x0,0x1ff,0x7077f801,0xff807fb8,0xffc0038,0x3fdc1c,0x707fff0,0x1c00701,0xe007f070,0xe1c701c0,0x3fe01dfe, + 0xff700e,0x7fe00,0xff80fee,0x3c0070,0x703c0780,0x1e007ffc,0x70000e,0x1c000,0x0,0x1fe001c,0xe0000,0xe000e1c0,0x71c78010,0x20000, + 0x21318,0xfff800c0,0xfffe01,0xc00ffff0,0x70000e0,0x0,0x0,0x0,0x0,0x0,0x3ff0,0x1803fd,0xfe01c007,0xffc00700,0x1c,0xffc1f,0xffc00000, + 0x1c00038,0x7000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0xc,0x60000e00,0x31803fe0,0x7801ef00,0x3de007bc, + 0xf7801e,0xf003fc0f,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x701f01f,0xf803ff00,0x7fe00ffc,0x1ff8000, + 0x67fe01,0xffc03ff8,0x7ff00ff,0xe001c007,0xeff8,0xffb81ff,0x703fee07,0xfdc0ffb8,0x1ff70ff7,0xf81ff807,0xfe00ffc0,0x1ff803ff, + 0x3fff87f,0xff0fffe1,0xfffc07fc,0x380e01f,0xf003fe00,0x7fc00ff8,0x1ff0000,0x37fc00,0xfee01fdc,0x3fb807f,0x7001e007,0x7f800780, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x30007fc0,0x1e00f8,0x78000000,0x70001c00, + 0x0,0xe0000,0x3c00,0x700001f0,0x1fff0ff,0xfe07f800,0xe01fe0,0x7e0038,0x3f800,0xfc0003c0,0x700000,0x0,0x700,0x18000e00,0x1c7ff000, + 0x7e03fe00,0x3fff0700,0x7f038,0x383ffe0,0x1f801c00,0xf1fff8e0,0x38e01e0,0xfc01c00,0x3f80e0,0x787fc00,0xe0007f0,0x7c00f0,0x387800f0, + 0x1c00ffff,0xe00000,0xe000e000,0x0,0xfc,0x7071f000,0x3f003e38,0x3f00038,0x1f1c1c,0x707fff0,0x1c00700,0xf003f070,0xe1c701c0, + 0x1f801c7c,0x7c700e,0x1f800,0x3f8078e,0x3c0070,0x707803c0,0x1c007ffc,0x70000e,0x1c000,0x0,0x7c0008,0x1e0000,0xe000e1c0,0x71c30010, + 0x20000,0x1e1f0,0x3fe00020,0x3ffe00,0x800ffff0,0x2000040,0x0,0x0,0x0,0x0,0x0,0xfc0,0x3001f0,0x78008007,0xffc00700,0x1c,0x3f81f, + 0xffc00000,0x1c00038,0x407000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x39c7,0x18c000,0x0,0x0,0x18,0x60001c00,0x61801f80,0x7000ee00, + 0x1dc003b8,0x77000e,0xe001f80f,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0,0x700f007,0xe000fc00,0x1f8003f0, + 0x7e0000,0xe1f800,0x7f000fe0,0x1fc003f,0x8001c007,0xe7f0,0x7e380fc,0x701f8e03,0xf1c07e38,0xfc703c1,0xe003f001,0xf8003f00, + 0x7e000fc,0x3fff87f,0xff0fffe1,0xfffc03f8,0x380e00f,0xc001f800,0x3f0007e0,0xfc0000,0x61f800,0x78e00f1c,0x1e3803c,0x7001c007, + 0x1f000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x70001c00,0x0, + 0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, + 0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, + 0x70000e,0x1c000,0x0,0x0,0x1c0000,0xe000c180,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x70e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x2000,0x0,0x1f,0xf8003800,0x7fe00000,0x0,0x0,0x0,0x0,0x4000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000, + 0x0,0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x30001800, + 0x0,0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e000, + 0x0,0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, + 0x70000e,0x1c000,0x0,0x0,0x1c0001,0xe001c380,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x7fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x3000,0x0,0x1f,0xf8007000,0x7fe00000,0x0,0x0,0x0,0x0,0x6000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x38003800, + 0x0,0x380000,0x1,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x3c18000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000, + 0x0,0x0,0x0,0x0,0x0,0xfe0000,0x380fe000,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x38000000, + 0x78000e,0x3c000,0x0,0x0,0x180001,0xc0018300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0, + 0x38,0x1f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x1800,0x0,0x0,0x6000e000,0x1800000,0x0,0x0,0x0,0x0,0x3000,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x38007,0xe00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x18003000, + 0x0,0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0, + 0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x0,0x0,0x0,0x0,0x607800,0x0,0x3c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x78000000, + 0x3f800e,0x3f8000,0x0,0x0,0x300043,0xc0018200,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, + 0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x11800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x23000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78007, + 0x1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000, + 0xfe000,0x0,0x0,0x0,0x0,0x0,0x7ff000,0x0,0x7f800000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf8000000,0x3f800e,0x3f8000,0x0, + 0x0,0x10007f,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x38,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x3800,0x0,0x1f800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f8007,0xfe00,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x7fe000,0x0, + 0x7f000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf0000000,0xf800e,0x3e0000,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1f000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3f0007,0xfc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x1fc000,0x0,0x7e000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xc0000000,0xe,0x0, + 0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x3800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0007,0xf000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 29x57 font. + const unsigned int font29x57[29*57*256/32] = { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7, + 0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000, + 0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000, + 0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000, + 0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0, + 0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f, + 0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00, + 0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800, + 0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3, + 0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e, + 0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003, + 0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0, + 0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0, + 0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00, + 0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0, + 0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000, + 0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0, + 0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0, + 0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0, + 0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0, + 0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000, + 0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000, + 0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000, + 0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800, + 0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3, + 0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0, + 0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000, + 0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80, + 0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0, + 0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000, + 0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000, + 0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000, + 0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f, + 0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000, + 0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc, + 0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00, + 0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e, + 0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000, + 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe, + 0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000, + 0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff, + 0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00, + 0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80, + 0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000, + 0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0, + 0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff, + 0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0, + 0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003, + 0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d, + 0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc, + 0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff, + 0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff, + 0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000, + 0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, + 0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f, + 0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000, + 0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0, + 0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff, + 0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8, + 0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003, + 0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380, + 0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000, + 0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80, + 0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff, + 0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c, + 0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000, + 0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80, + 0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00, + 0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000, + 0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe, + 0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800, + 0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f, + 0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700, + 0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f, + 0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007, + 0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007, + 0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f, + 0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0, + 0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000, + 0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000, + 0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000, + 0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000, + 0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007, + 0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003, + 0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000, + 0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007, + 0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00, + 0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1, + 0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0, + 0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c, + 0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000, + 0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00, + 0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000, + 0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007, + 0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80, + 0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0, + 0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f, + 0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800, + 0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000, + 0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000, + 0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000, + 0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0, + 0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00, + 0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803, + 0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f, + 0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780, + 0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e, + 0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0, + 0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0, + 0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078, + 0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007, + 0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780, + 0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e, + 0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003, + 0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800, + 0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001, + 0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc, + 0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000, + 0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03, + 0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0, + 0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000, + 0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, + 0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc, + 0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0, + 0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000, + 0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780, + 0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0, + 0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000, + 0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f, + 0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff, + 0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000, + 0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0, + 0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c, + 0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0, + 0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000, + 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00, + 0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f, + 0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f, + 0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f, + 0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e, + 0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001, + 0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001, + 0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f, + 0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780, + 0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007, + 0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000, + 0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0, + 0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787, + 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f, + 0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001, + 0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f, + 0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001, + 0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00, + 0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0, + 0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079, + 0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c, + 0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00, + 0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f, + 0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000, + 0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000, + 0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780, + 0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00, + 0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000, + 0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e, + 0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff, + 0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff, + 0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, + 0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e, + 0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c, + 0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001, + 0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801, + 0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc, + 0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780, + 0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f, + 0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000, + 0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078, + 0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001, + 0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, + 0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8, + 0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0, + 0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007, + 0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00, + 0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0, + 0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007, + 0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807, + 0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c, + 0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000, + 0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18, + 0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0, + 0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe, + 0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078, + 0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, + 0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008, + 0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0, + 0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003, + 0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00, + 0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80, + 0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e, + 0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000, + 0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000, + 0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c, + 0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00, + 0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00, + 0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000, + 0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00, + 0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000, + 0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00, + 0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c, + 0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00, + 0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0, + 0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078, + 0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000, + 0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0, + 0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c, + 0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000, + 0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0, + 0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e, + 0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c, + 0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0, + 0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e, + 0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0, + 0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007, + 0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000, + 0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800, + 0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1, + 0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff, + 0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000, + 0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000, + 0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000, + 0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000, + 0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003, + 0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce, + 0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00, + 0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f, + 0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0, + 0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e, + 0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800, + 0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0, + 0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007, + 0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00, + 0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff, + 0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0, + 0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0, + 0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00, + 0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000, + 0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000, + 0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f, + 0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0, + 0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007, + 0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8, + 0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0, + 0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000, + 0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00, + 0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80, + 0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0, + 0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e, + 0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c, + 0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f, + 0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c, + 0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003, + 0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000, + 0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303, + 0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc, + 0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000, + 0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780, + 0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000, + 0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078, + 0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007, + 0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00, + 0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f, + 0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0, + 0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c, + 0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000, + 0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000, + 0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000, + 0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800, + 0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0, + 0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, + 0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003, + 0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00, + 0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f, + 0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3, + 0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070, + 0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0, + 0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0, + 0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e, + 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0, + 0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000, + 0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80, + 0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303, + 0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00, + 0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe, + 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07, + 0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f, + 0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f, + 0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007, + 0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800, + 0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007, + 0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003, + 0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0, + 0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00, + 0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00, + 0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000, + 0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000, + 0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c, + 0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01, + 0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000, + 0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800, + 0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000, + 0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070, + 0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0, + 0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0, + 0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e, + 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0, + 0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff, + 0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001, + 0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000, + 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801, + 0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe, + 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, + 0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000, + 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000, + 0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800, + 0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000, + 0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, + 0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800, + 0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0, + 0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078, + 0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000, + 0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0, + 0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000, + 0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0, + 0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000, + 0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078, + 0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001, + 0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e, + 0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007, + 0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001, + 0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007, + 0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000, + 0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003, + 0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff, + 0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00, + 0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80, + 0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000, + 0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00, + 0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000, + 0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, + 0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000, + 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000, + 0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800, + 0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001, + 0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, + 0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003, + 0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001, + 0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800, + 0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000, + 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03, + 0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, + 0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000, + 0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000, + 0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c, + 0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00, + 0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff, + 0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e, + 0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c, + 0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f, + 0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e, + 0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f, + 0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000, + 0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0, + 0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807, + 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f, + 0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80, + 0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000, + 0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800, + 0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00, + 0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03, + 0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800, + 0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, + 0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, + 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000, + 0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff, + 0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c, + 0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81, + 0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000, + 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0, + 0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c, + 0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00, + 0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f, + 0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e, + 0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03, + 0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780, + 0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, + 0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, + 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000, + 0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f, + 0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000, + 0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00, + 0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, + 0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f, + 0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07, + 0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00, + 0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0, + 0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, + 0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000, + 0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e, + 0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf, + 0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f, + 0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e, + 0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000, + 0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000, + 0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0, + 0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, + 0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e, + 0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80, + 0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000, + 0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00, + 0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, + 0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000, + 0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e, + 0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf, + 0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f, + 0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e, + 0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000, + 0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0, + 0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0, + 0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00, + 0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007, + 0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c, + 0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000, + 0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0, + 0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0, + 0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007, + 0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0, + 0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f, + 0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007, + 0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000, + 0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018, + 0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0, + 0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00, + 0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070, + 0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003, + 0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8, + 0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80, + 0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0, + 0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f, + 0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780, + 0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff, + 0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000, + 0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000, + 0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff, + 0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000, + 0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f, + 0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000, + 0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1, + 0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f, + 0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff, + 0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8, + 0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f, + 0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780, + 0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff, + 0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000, + 0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000, + 0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff, + 0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000, + 0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7, + 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381, + 0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f, + 0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3, + 0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0, + 0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003, + 0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780, + 0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff, + 0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000, + 0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000, + 0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff, + 0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000, + 0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7, + 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff, + 0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0, + 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007, + 0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1, + 0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0, + 0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, + 0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000, + 0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000, + 0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000, + 0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e, + 0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0, + 0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f, + 0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800, + 0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0, + 0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, + 0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000, + 0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000, + 0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000, + 0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0, + 0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000, + 0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0, + 0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0, + 0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000, + 0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff, + 0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000, + 0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0, + 0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f, + 0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, + 0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0, + 0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0, + 0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0, + 0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, + 0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0, + 0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000, + 0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; + + // Definition of a 40x38 'danger' color logo. + const unsigned char logo40x38[4576] = { + 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200, + 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0, + 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200, + 1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0, + 2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255, + 255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189, + 189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189, + 189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123, + 22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200, + 1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0, + 0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1, + 123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189, + 189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255, + 0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189, + 189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,255, + 0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,123, + 123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,1,189, + 189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,255,255, + 0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,1,189,189, + 189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,255,255,0,1, + 0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,123,0,26,255, + 255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,0,4,123,123, + 123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,123,123,123,86, + 200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0}; + + //! Display a warning message. + /** + \param format is a C-string describing the format of the message, as in std::printf(). + **/ + inline void warn(const char *format, ...) { + if (cimg::exception_mode()>=1) { + char message[8192]; + cimg_std::va_list ap; + va_start(ap,format); + cimg_std::vsprintf(message,format,ap); + va_end(ap); +#ifdef cimg_strict_warnings + throw CImgWarningException(message); +#else + cimg_std::fprintf(cimg_stdout,"\n%s# CImg Warning%s :\n%s\n",cimg::t_red,cimg::t_normal,message); +#endif + } + } + + // Execute an external system command. + /** + \note This function is similar to std::system() + and is here because using the std:: version on + Windows may open undesired consoles. + **/ + inline int system(const char *const command, const char *const module_name=0) { +#if cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFO si; + cimg_std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); + cimg_std::memset(&si,0,sizeof(STARTUPINFO)); + GetStartupInfo(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE; + const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi); + if (res) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; + } else +#endif + return cimg_std::system(command); + return module_name?0:1; + } + + //! Return a reference to a temporary variable of type T. + template + inline T& temporary(const T&) { + static T temp; + return temp; + } + + //! Exchange values of variables \p a and \p b. + template + inline void swap(T& a, T& b) { T t = a; a = b; b = t; } + + //! Exchange values of variables (\p a1,\p a2) and (\p b1,\p b2). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + + //! Exchange values of variables (\p a1,\p a2,\p a3) and (\p b1,\p b2,\p b3). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + + //! Exchange values of variables (\p a1,\p a2,...,\p a4) and (\p b1,\p b2,...,\p b4). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + + //! Exchange values of variables (\p a1,\p a2,...,\p a5) and (\p b1,\p b2,...,\p b5). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + + //! Exchange values of variables (\p a1,\p a2,...,\p a6) and (\p b1,\p b2,...,\p b6). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + //! Exchange values of variables (\p a1,\p a2,...,\p a7) and (\p b1,\p b2,...,\p b7). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); + } + + //! Exchange values of variables (\p a1,\p a2,...,\p a8) and (\p b1,\p b2,...,\p b8). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7, T8& a8, T8& b8) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); + } + + //! Return the current endianness of the CPU. + /** + \return \c false for "Little Endian", \c true for "Big Endian". + **/ + inline bool endianness() { + const int x = 1; + return ((unsigned char*)&x)[0]?false:true; + } + + //! Invert endianness of a memory buffer. + template + inline void invert_endianness(T* const buffer, const unsigned int size) { + if (size) switch (sizeof(T)) { + case 1 : break; + case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) { + const unsigned short val = *(--ptr); + *ptr = (unsigned short)((val>>8)|((val<<8))); + }} break; + case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) { + const unsigned int val = *(--ptr); + *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24); + }} break; + default : { for (T* ptr = buffer+size; ptr>buffer; ) { + unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); + for (int i=0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); + }} + } + } + + //! Invert endianness of a single variable. + template + inline T& invert_endianness(T& a) { + invert_endianness(&a,1); + return a; + } + + //! Get the value of a system timer with a millisecond precision. + inline unsigned long time() { +#if cimg_OS==1 + struct timeval st_time; + gettimeofday(&st_time,0); + return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); +#elif cimg_OS==2 + static SYSTEMTIME st_time; + GetSystemTime(&st_time); + return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); +#else + return 0; +#endif + } + + //! Sleep for a certain numbers of milliseconds. + /** + This function frees the CPU ressources during the sleeping time. + It may be used to temporize your program properly, without wasting CPU time. + **/ + inline void sleep(const unsigned int milliseconds) { +#if cimg_OS==1 + struct timespec tv; + tv.tv_sec = milliseconds/1000; + tv.tv_nsec = (milliseconds%1000)*1000000; + nanosleep(&tv,0); +#elif cimg_OS==2 + Sleep(milliseconds); +#endif + } + + inline unsigned int _sleep(const unsigned int milliseconds, unsigned long& timer) { + if (!timer) timer = cimg::time(); + const unsigned long current_time = cimg::time(); + if (current_time>=timer+milliseconds) { timer = current_time; return 0; } + const unsigned long time_diff = timer + milliseconds - current_time; + timer = current_time + time_diff; + cimg::sleep(time_diff); + return (unsigned int)time_diff; + } + + //! Wait for a certain number of milliseconds since the last call. + /** + This function is equivalent to sleep() but the waiting time is computed with regard to the last call + of wait(). It may be used to temporize your program properly. + **/ + inline unsigned int wait(const unsigned int milliseconds) { + static unsigned long timer = 0; + if (!timer) timer = cimg::time(); + return _sleep(milliseconds,timer); + } + + // Use a specific srand initialization to avoid multi-threads to have to the + // same series of random numbers (executed only once for a single program). + inline void srand() { + static bool first_time = true; + if (first_time) { + cimg_std::srand(cimg::time()); + unsigned char *const rand_ptr = new unsigned char[1+cimg_std::rand()%2048]; + cimg_std::srand((unsigned int)cimg_std::rand() + *(unsigned int*)(void*)rand_ptr); + delete[] rand_ptr; + first_time = false; + } + } + + //! Return a left bitwise-rotated number. + template + inline const T rol(const T a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3)-n))):a; + } + + //! Return a right bitwise-rotated number. + template + inline const T ror(const T a, const unsigned int n=1) { + return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a; + } + + //! Return the absolute value of a number. + /** + \note This function is different from std::abs() or std::fabs() + because it is able to consider a variable of any type, without cast needed. + **/ + template + inline T abs(const T a) { + return a>=0?a:-a; + } + inline bool abs(const bool a) { + return a; + } + inline unsigned char abs(const unsigned char a) { + return a; + } + inline unsigned short abs(const unsigned short a) { + return a; + } + inline unsigned int abs(const unsigned int a) { + return a; + } + inline unsigned long abs(const unsigned long a) { + return a; + } + inline double abs(const double a) { + return cimg_std::fabs(a); + } + inline float abs(const float a) { + return (float)cimg_std::fabs((double)a); + } + inline int abs(const int a) { + return cimg_std::abs(a); + } + + //! Return the square of a number. + template + inline T sqr(const T val) { + return val*val; + } + + //! Return 1 + log_10(x). + inline int xln(const int x) { + return x>0?(int)(1+cimg_std::log10((double)x)):1; + } + + //! Return the minimum value between two numbers. + template + inline typename cimg::superset::type min(const t1& a, const t2& b) { + typedef typename cimg::superset::type t1t2; + return (t1t2)(a<=b?a:b); + } + + //! Return the minimum value between three numbers. + template + inline typename cimg::superset2::type min(const t1& a, const t2& b, const t3& c) { + typedef typename cimg::superset2::type t1t2t3; + return (t1t2t3)cimg::min(cimg::min(a,b),c); + } + + //! Return the minimum value between four numbers. + template + inline typename cimg::superset3::type min(const t1& a, const t2& b, const t3& c, const t4& d) { + typedef typename cimg::superset3::type t1t2t3t4; + return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d); + } + + //! Return the maximum value between two numbers. + template + inline typename cimg::superset::type max(const t1& a, const t2& b) { + typedef typename cimg::superset::type t1t2; + return (t1t2)(a>=b?a:b); + } + + //! Return the maximum value between three numbers. + template + inline typename cimg::superset2::type max(const t1& a, const t2& b, const t3& c) { + typedef typename cimg::superset2::type t1t2t3; + return (t1t2t3)cimg::max(cimg::max(a,b),c); + } + + //! Return the maximum value between four numbers. + template + inline typename cimg::superset3::type max(const t1& a, const t2& b, const t3& c, const t4& d) { + typedef typename cimg::superset3::type t1t2t3t4; + return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d); + } + + //! Return the sign of a number. + template + inline T sign(const T x) { + return (x<0)?(T)(-1):(x==0?(T)0:(T)1); + } + + //! Return the nearest power of 2 higher than a given number. + template + inline unsigned long nearest_pow2(const T x) { + unsigned long i = 1; + while (x>i) i<<=1; + return i; + } + + //! Return the modulo of a number. + /** + \note This modulo function accepts negative and floating-points modulo numbers, as well as + variable of any type. + **/ + template + inline T mod(const T& x, const T& m) { + const double dx = (double)x, dm = (double)m; + if (x<0) { return (T)(dm+dx+dm*cimg_std::floor(-dx/dm)); } + return (T)(dx-dm*cimg_std::floor(dx/dm)); + } + inline int mod(const bool x, const bool m) { + return m?(x?1:0):0; + } + inline int mod(const char x, const char m) { + return x>=0?x%m:(x%m?m+x%m:0); + } + inline int mod(const short x, const short m) { + return x>=0?x%m:(x%m?m+x%m:0); + } + inline int mod(const int x, const int m) { + return x>=0?x%m:(x%m?m+x%m:0); + } + inline int mod(const long x, const long m) { + return x>=0?x%m:(x%m?m+x%m:0); + } + inline int mod(const unsigned char x, const unsigned char m) { + return x%m; + } + inline int mod(const unsigned short x, const unsigned short m) { + return x%m; + } + inline int mod(const unsigned int x, const unsigned int m) { + return x%m; + } + inline int mod(const unsigned long x, const unsigned long m) { + return x%m; + } + + //! Return the minmod of two numbers. + /** + minmod(\p a,\p b) is defined to be : + - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. + - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. + **/ + template + inline T minmod(const T a, const T b) { + return a*b<=0?0:(a>0?(a=1.0); + return x1*cimg_std::sqrt((-2*cimg_std::log(w))/w); + } + + //! Return a random variable following a Poisson distribution of parameter z. + inline unsigned int prand(const double z) { + if (z<=1.0e-10) return 0; + if (z>100.0) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z); + unsigned int k = 0; + const double y = std::exp(-z); + for (double s = 1.0; s>=y; ++k) s*=cimg::rand(); + return k-1; + } + + //! Return a rounded number. + /** + \param x is the number to be rounded. + \param y is the rounding precision. + \param rounding_type defines the type of rounding (0=nearest, -1=backward, 1=forward). + **/ + inline double round(const double x, const double y, const int rounding_type=0) { + if (y<=0) return x; + const double delta = cimg::mod(x,y); + if (delta==0.0) return x; + const double + backward = x - delta, + forward = backward + y; + return rounding_type<0?backward:(rounding_type>0?forward:(2*deltaabsb) { const double tmp = absb/absa; return absa*cimg_std::sqrt(1.0+tmp*tmp); } + else { const double tmp = absa/absb; return (absb==0?0:absb*cimg_std::sqrt(1.0+tmp*tmp)); } + } + + //! Remove the 'case' of an ASCII character. + inline char uncase(const char x) { + return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); + } + + //! Remove the 'case' of a C string. + /** + Acts in-place. + **/ + inline void uncase(char *const string) { + if (string) for (char *ptr = string; *ptr; ++ptr) *ptr = uncase(*ptr); + } + + //! Read a float number from a C-string. + /** + \note This function is quite similar to std::atof(), + but that it allows the retrieval of fractions as in "1/2". + **/ + inline float atof(const char *const str) { + float x = 0,y = 1; + if (!str) return 0; else { cimg_std::sscanf(str,"%g/%g",&x,&y); return x/y; } + } + + //! Compute the length of a C-string. + /** + \note This function is similar to std::strlen() + and is here because some old compilers do not + define the std:: version. + **/ + inline int strlen(const char *const s) { + if (!s) return -1; + int k = 0; + for (const char *ns = s; *ns; ++ns) ++k; + return k; + } + + //! Compare the first \p n characters of two C-strings. + /** + \note This function is similar to std::strncmp() + and is here because some old compilers do not + define the std:: version. + **/ + inline int strncmp(const char *const s1, const char *const s2, const int l) { + if (!s1) return s2?-1:0; + const char *ns1 = s1, *ns2 = s2; + int k, diff = 0; for (k = 0; kstd::strncasecmp() + and is here because some old compilers do not + define the std:: version. + **/ + inline int strncasecmp(const char *const s1, const char *const s2, const int l) { + if (!s1) return s2?-1:0; + const char *ns1 = s1, *ns2 = s2; + int k, diff = 0; for (k = 0; kstd::strcmp() + and is here because some old compilers do not + define the std:: version. + **/ + inline int strcmp(const char *const s1, const char *const s2) { + const int l1 = cimg::strlen(s1), l2 = cimg::strlen(s2); + return cimg::strncmp(s1,s2,1+(l1std::strcasecmp() + and is here because some old compilers do not + define the std:: version. + **/ + inline int strcasecmp(const char *const s1, const char *const s2) { + const int l1 = cimg::strlen(s1), l2 = cimg::strlen(s2); + return cimg::strncasecmp(s1,s2,1+(l1=0 && s[l]!=c; --l) {} + return l; + } + + //! Remove useless delimiters on the borders of a C-string + inline bool strpare(char *const s, const char delimiter=' ', const bool symmetric=false) { + if (!s) return false; + const int l = cimg::strlen(s); + int p, q; + if (symmetric) for (p = 0, q = l-1; pp && s[q]==delimiter; ) --q; + } + const int n = q - p + 1; + if (n!=l) { cimg_std::memmove(s,s+p,n); s[n] = '\0'; return true; } + return false; + } + + //! Remove useless spaces and symmetric delimiters ', " and ` from a C-string. + inline void strclean(char *const s) { + if (!s) return; + strpare(s,' ',false); + for (bool need_iter = true; need_iter; ) { + need_iter = false; + need_iter |= strpare(s,'\'',true); + need_iter |= strpare(s,'\"',true); + need_iter |= strpare(s,'`',true); + } + } + + //! Replace explicit escape sequences '\x' in C-strings (where x in [ntvbrfa?'"0]). + inline void strescape(char *const s) { +#define cimg_strescape(ci,co) case ci: *nd = co; break; + char *ns, *nd; + for (ns = nd = s; *ns; ++ns, ++nd) + if (*ns=='\\') switch (*(++ns)) { + cimg_strescape('n','\n'); + cimg_strescape('t','\t'); + cimg_strescape('v','\v'); + cimg_strescape('b','\b'); + cimg_strescape('r','\r'); + cimg_strescape('f','\f'); + cimg_strescape('a','\a'); + cimg_strescape('\\','\\'); + cimg_strescape('\?','\?'); + cimg_strescape('\'','\''); + cimg_strescape('\"','\"'); + cimg_strescape('\0','\0'); + } + else *nd = *ns; + *nd = 0; + } + + //! Compute the basename of a filename. + inline const char* basename(const char *const s) { + return (cimg_OS!=2)?(s?s+1+cimg::strfind(s,'/'):0):(s?s+1+cimg::strfind(s,'\\'):0); + } + + // Generate a random filename. + inline const char* filenamerand() { + static char id[9] = { 0,0,0,0,0,0,0,0,0 }; + cimg::srand(); + for (unsigned int k=0; k<8; ++k) { + const int v = (int)cimg_std::rand()%3; + id[k] = (char)(v==0?('0'+(cimg_std::rand()%10)):(v==1?('a'+(cimg_std::rand()%26)):('A'+(cimg_std::rand()%26)))); + } + return id; + } + + // Convert filename into a Windows-style filename. + inline void winformat_string(char *const s) { + if (s && s[0]) { +#if cimg_OS==2 + char *const ns = new char[MAX_PATH]; + if (GetShortPathNameA(s,ns,MAX_PATH)) cimg_std::strcpy(s,ns); +#endif + } + } + + //! Return or set path to store temporary files. + inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) { +#define _cimg_test_temporary_path(p) \ + if (!path_found) { \ + cimg_std::sprintf(st_path,"%s",p); \ + cimg_std::sprintf(tmp,"%s%s%s",st_path,cimg_OS==2?"\\":"/",filetmp); \ + if ((file=cimg_std::fopen(tmp,"wb"))!=0) { cimg_std::fclose(file); cimg_std::remove(tmp); path_found = true; } \ + } + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + char tmp[1024], filetmp[512]; + cimg_std::FILE *file = 0; + cimg_std::sprintf(filetmp,"%s.tmp",cimg::filenamerand()); + char *tmpPath = getenv("TMP"); + if (!tmpPath) { tmpPath = getenv("TEMP"); winformat_string(tmpPath); } + if (tmpPath) _cimg_test_temporary_path(tmpPath); +#if cimg_OS==2 + _cimg_test_temporary_path("C:\\WINNT\\Temp"); + _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("C:\\Temp"); + _cimg_test_temporary_path("C:"); + _cimg_test_temporary_path("D:\\WINNT\\Temp"); + _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("D:\\Temp"); + _cimg_test_temporary_path("D:"); +#else + _cimg_test_temporary_path("/tmp"); + _cimg_test_temporary_path("/var/tmp"); +#endif + if (!path_found) { + st_path[0]='\0'; + cimg_std::strcpy(tmp,filetmp); + if ((file=cimg_std::fopen(tmp,"wb"))!=0) { cimg_std::fclose(file); cimg_std::remove(tmp); path_found = true; } + } + if (!path_found) + throw CImgIOException("cimg::temporary_path() : Unable to find a temporary path accessible for writing\n" + "you have to set the macro 'cimg_temporary_path' to a valid path where you have writing access :\n" + "#define cimg_temporary_path \"path\" (before including 'CImg.h')"); + } + return st_path; + } + + // Return or set path to the "Program files/" directory (windows only). +#if cimg_OS==2 + inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[MAX_PATH]; + cimg_std::memset(st_path,0,MAX_PATH); + // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). +#if !defined(__INTEL_COMPILER) + if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) { + const char *pfPath = getenv("PROGRAMFILES"); + if (pfPath) cimg_std::strncpy(st_path,pfPath,MAX_PATH-1); + else cimg_std::strcpy(st_path,"C:\\PROGRA~1"); + } +#else + cimg_std::strcpy(st_path,"C:\\PROGRA~1"); +#endif + } + return st_path; + } +#endif + + //! Return or set path to the ImageMagick's \c convert tool. + inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + const char *pf_path = programfiles_path(); + if (!path_found) { + cimg_std::sprintf(st_path,".\\convert.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + if (!path_found) cimg_std::strcpy(st_path,"convert.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./convert"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"convert"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return path of the GraphicsMagick's \c gm tool. + inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + const char* pf_path = programfiles_path(); + if (!path_found) { + cimg_std::sprintf(st_path,".\\gm.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=10 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=9; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + { for (int k=32; k>=0 && !path_found; --k) { + cimg_std::sprintf(st_path,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + }} + if (!path_found) cimg_std::strcpy(st_path,"gm.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./gm"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"gm"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return or set path of the \c XMedcon tool. + inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + const char* pf_path = programfiles_path(); + if (!path_found) { + cimg_std::sprintf(st_path,".\\medcon.bat"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_std::sprintf(st_path,".\\medcon.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.bat",pf_path); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.exe",pf_path); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"medcon.bat"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./medcon"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"medcon"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return or set path to the 'ffmpeg' command. + inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + cimg_std::sprintf(st_path,".\\ffmpeg.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"ffmpeg.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./ffmpeg"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"ffmpeg"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return or set path to the 'gzip' command. + inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + cimg_std::sprintf(st_path,".\\gzip.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"gzip.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./gzip"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"gzip"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return or set path to the 'gunzip' command. + inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + cimg_std::sprintf(st_path,".\\gunzip.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"gunzip.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./gunzip"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"gunzip"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Return or set path to the 'dcraw' command. + inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) { + static char *st_path = 0; + if (reinit_path && st_path) { delete[] st_path; st_path = 0; } + if (user_path) { + if (!st_path) st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + cimg_std::strncpy(st_path,user_path,1023); + } else if (!st_path) { + st_path = new char[1024]; + cimg_std::memset(st_path,0,1024); + bool path_found = false; + cimg_std::FILE *file = 0; +#if cimg_OS==2 + if (!path_found) { + cimg_std::sprintf(st_path,".\\dcraw.exe"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"dcraw.exe"); +#else + if (!path_found) { + cimg_std::sprintf(st_path,"./dcraw"); + if ((file=cimg_std::fopen(st_path,"r"))!=0) { cimg_std::fclose(file); path_found = true; } + } + if (!path_found) cimg_std::strcpy(st_path,"dcraw"); +#endif + winformat_string(st_path); + } + return st_path; + } + + //! Split a filename into two strings 'body' and 'extension'. + inline const char *split_filename(const char *const filename, char *const body=0) { + if (!filename) { if (body) body[0]='\0'; return 0; } + int l = cimg::strfind(filename,'.'); + if (l>=0) { if (body) { cimg_std::strncpy(body,filename,l); body[l]='\0'; }} + else { if (body) cimg_std::strcpy(body,filename); l = (int)cimg::strlen(filename)-1; } + return filename+l+1; + } + + //! Create a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const string) { + if (!filename) { if (string) string[0]='\0'; return 0; } + char format[1024],body[1024]; + const char *ext = cimg::split_filename(filename,body); + if (n>0) cimg_std::sprintf(format,"%s_%%.%ud.%s",body,n,ext); + else cimg_std::sprintf(format,"%s_%%d.%s",body,ext); + cimg_std::sprintf(string,format,number); + return string; + } + + //! Open a file, and check for possible errors. + inline cimg_std::FILE *fopen(const char *const path, const char *const mode) { + if(!path || !mode) + throw CImgArgumentException("cimg::fopen() : File '%s', cannot open with mode '%s'.", + path?path:"(null)",mode?mode:"(null)"); + if (path[0]=='-') return (mode[0]=='r')?stdin:stdout; + cimg_std::FILE *dest = cimg_std::fopen(path,mode); + if (!dest) + throw CImgIOException("cimg::fopen() : File '%s', cannot open file %s", + path,mode[0]=='r'?"for reading.":(mode[0]=='w'?"for writing.":"."),path); + return dest; + } + + //! Close a file, and check for possible errors. + inline int fclose(cimg_std::FILE *file) { + if (!file) warn("cimg::fclose() : Can't close (null) file"); + if (!file || file==stdin || file==stdout) return 0; + const int errn = cimg_std::fclose(file); + if (errn!=0) warn("cimg::fclose() : Error %d during file closing",errn); + return errn; + } + + //! Try to guess the image format of a filename, using its magick numbers. + inline const char *file_type(cimg_std::FILE *const file, const char *const filename) { + static const char + *const _pnm = "pnm", + *const _bmp = "bmp", + *const _gif = "gif", + *const _jpeg = "jpeg", + *const _off = "off", + *const _pan = "pan", + *const _png = "png", + *const _tiff = "tiff"; + if (!filename && !file) throw CImgArgumentException("cimg::file_type() : Cannot load (null) filename."); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + const char *ftype = 0, *head; + char header[2048], item[1024]; + const unsigned char *const uheader = (unsigned char*)header; + int err; + const unsigned int siz = (unsigned int)cimg_std::fread(header,2048,1,nfile); // Read first 2048 bytes. + if (!file) cimg::fclose(nfile); + if (!ftype) { // Check for BMP format. + if (header[0]=='B' && header[1]=='M') ftype = _bmp; + } + if (!ftype) { // Check for GIF format. + if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && + (header[4]=='7' || header[4]=='9')) ftype = _gif; + } + if (!ftype) { // Check for JPEG format. + if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) ftype = _jpeg; + } + if (!ftype) { // Check for OFF format. + if (header[0]=='O' && header[1]=='F' && header[2]=='F' && header[3]=='\n') ftype = _off; + } + if (!ftype) { // Check for PAN format. + if (header[0]=='P' && header[1]=='A' && header[2]=='N' && header[3]=='D' && header[4]=='O' && + header[5]=='R' && header[6]=='E') ftype = _pan; + } + if (!ftype) { // Check for PNG format. + if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && + uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) ftype = _png; + } + if (!ftype) { // Check for PNM format. + head = header; + while (head + inline int fread(T *const ptr, const unsigned int nmemb, cimg_std::FILE *stream) { + if (!ptr || nmemb<=0 || !stream) + throw CImgArgumentException("cimg::fread() : Can't read %u x %u bytes of file pointer '%p' in buffer '%p'", + nmemb,sizeof(T),stream,ptr); + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned int toread = nmemb, alread = 0, ltoread = 0, lalread = 0; + do { + ltoread = (toread*sizeof(T))0); + if (toread>0) warn("cimg::fread() : File reading problems, only %u/%u elements read",alread,nmemb); + return alread; + } + + //! Write data to a file, and check for possible errors. + template + inline int fwrite(const T *ptr, const unsigned int nmemb, cimg_std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite() : Can't write %u x %u bytes of file pointer '%p' from buffer '%p'", + nmemb,sizeof(T),stream,ptr); + if (nmemb<=0) return 0; + const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + unsigned int towrite = nmemb, alwrite = 0, ltowrite = 0, lalwrite = 0; + do { + ltowrite = (towrite*sizeof(T))0); + if (towrite>0) warn("cimg::fwrite() : File writing problems, only %u/%u elements written",alwrite,nmemb); + return alwrite; + } + + inline const char* option(const char *const name, const int argc, const char *const *const argv, + const char *defaut, const char *const usage=0) { + static bool first = true, visu = false; + const char *res = 0; + if (first) { + first=false; + visu = (cimg::option("-h",argc,argv,(char*)0)!=0); + visu |= (cimg::option("-help",argc,argv,(char*)0)!=0); + visu |= (cimg::option("--help",argc,argv,(char*)0)!=0); + } + if (!name && visu) { + if (usage) { + cimg_std::fprintf(cimg_stdout,"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + cimg_std::fprintf(cimg_stdout," : %s",usage); + cimg_std::fprintf(cimg_stdout," (%s, %s)\n\n",__DATE__,__TIME__); + } + if (defaut) cimg_std::fprintf(cimg_stdout,"%s\n",defaut); + } + if (name) { + if (argc>0) { + int k = 0; + while (k Operating System : %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), + cimg::t_normal,cimg::t_green, + cimg_OS, + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > CPU endianness : %s%s Endian%s\n", + cimg::t_bold, + cimg::endianness()?"Big":"Little", + cimg::t_normal); + +#ifdef cimg_use_visualcpp6 + cimg_std::fprintf(cimg_stdout," > Using Visual C++ 6.0 : %s%-13s%s %s('cimg_use_visualcpp6' defined)%s\n", + cimg::t_bold,"Yes",cimg::t_normal,cimg::t_green,cimg::t_normal); +#endif + + cimg_std::fprintf(cimg_stdout," > Debug messages : %s%-13s%s %s('cimg_debug'=%d)%s\n", + cimg::t_bold, + cimg_debug==0?"Quiet":(cimg_debug==1?"Console":(cimg_debug==2?"Dialog":(cimg_debug==3?"Console+Warnings":"Dialog+Warnings"))), + cimg::t_normal,cimg::t_green, + cimg_debug, + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Stricts warnings : %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", + cimg::t_bold, +#ifdef cimg_strict_warnings + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using VT100 messages : %s%-13s%s %s('cimg_use_vt100' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_vt100 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Display type : %s%-13s%s %s('cimg_display'=%d)%s\n", + cimg::t_bold, + cimg_display==0?"No display": + (cimg_display==1?"X11": + (cimg_display==2?"Windows GDI": + (cimg_display==3?"Carbon":"Unknow"))), + cimg::t_normal,cimg::t_green, + cimg_display, + cimg::t_normal); + +#if cimg_display==1 + cimg_std::fprintf(cimg_stdout," > Using XShm for X11 : %s%-13s%s %s('cimg_use_xshm' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xshm + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using XRand for X11 : %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xrandr + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); +#endif + cimg_std::fprintf(cimg_stdout," > Using OpenMP : %s%-13s%s %s('cimg_use_openmp' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_openmp + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + cimg_std::fprintf(cimg_stdout," > Using PNG library : %s%-13s%s %s('cimg_use_png' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_png + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + cimg_std::fprintf(cimg_stdout," > Using JPEG library : %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_jpeg + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using TIFF library : %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_tiff + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_magick + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using FFTW3 library : %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_fftw3 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout," > Using LAPACK library : %s%-13s%s %s('cimg_use_lapack' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_lapack + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::imagemagick_path()); + cimg_std::fprintf(cimg_stdout," > Path of ImageMagick : %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::graphicsmagick_path()); + cimg_std::fprintf(cimg_stdout," > Path of GraphicsMagick : %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::medcon_path()); + cimg_std::fprintf(cimg_stdout," > Path of 'medcon' : %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_std::sprintf(tmp,"\"%.1020s\"",cimg::temporary_path()); + cimg_std::fprintf(cimg_stdout," > Temporary path : %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_std::fprintf(cimg_stdout,"\n"); + } + + // Declare LAPACK function signatures if necessary. + // +#ifdef cimg_use_lapack + template + inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { + dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { + sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + template + inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { + dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { + sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + template + inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, + T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { + dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, + float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { + sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + template + inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { + int one = 1; + dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { + int one = 1; + sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + template + inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { + dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { + ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } +#endif + + // End of the 'cimg' namespace + } + + /*------------------------------------------------ + # + # + # Definition of mathematical operators and + # external functions. + # + # + -------------------------------------------------*/ + // + // These functions are extern to any classes and can be used for a "functional-style" programming, + // such as writting : + // cos(img); + // instead of img.get_cos(); + // + // Note that only the arithmetic operators and functions are implemented here. + // + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator+(const CImg& img, const t val) { + return CImg(img,false)+=val; + } +#else + template + inline CImg::type> operator+(const CImg& img, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImg(img,false)+=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator+(const t val, const CImg& img) { + return img + val; + } +#else + template + inline CImg::type> operator+(const t1 val, const CImg& img) { + return img + val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator+(const CImgList& list, const t val) { + return CImgList(list)+=val; + } +#else + template + inline CImgList::type> operator+(const CImgList& list, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)+=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator+(const t val, const CImgList& list) { + return list + val; + } +#else + template + inline CImgList::type> operator+(const t1 val, const CImgList& list) { + return list + val; + } +#endif + + template + inline CImg::type> operator+(const CImg& img1, const CImg& img2) { + typedef typename cimg::superset::type t1t2; + return CImg(img1,false)+=img2; + } + + template + inline CImgList::type> operator+(const CImg& img, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)+=img; + } + + template + inline CImgList::type> operator+(const CImgList& list, const CImg& img) { + return img + list; + } + + template + inline CImgList::type> operator+(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::superset::type t1t2; + return CImgList(list1)+=list2; + } + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator-(const CImg& img, const t val) { + return CImg(img,false)-=val; + } +#else + template + inline CImg::type> operator-(const CImg& img, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImg(img,false)-=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator-(const t val, const CImg& img) { + return CImg(img.width,img.height,img.depth,img.dim,val)-=img; + } +#else + template + inline CImg::type> operator-(const t1 val, const CImg& img) { + typedef typename cimg::superset::type t1t2; + return CImg(img.width,img.height,img.depth,img.dim,(t1t2)val)-=img; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator-(const CImgList& list, const t val) { + return CImgList(list)-=val; + } +#else + template + inline CImgList::type> operator-(const CImgList& list, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)-=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator-(const t val, const CImgList& list) { + CImgList res(list.size); + cimglist_for(res,l) res[l] = val - list[l]; + return res; + } +#else + template + inline CImgList::type> operator-(const t1 val, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = val - list[l]; + return res; + } +#endif + + template + inline CImg::type> operator-(const CImg& img1, const CImg& img2) { + typedef typename cimg::superset::type t1t2; + return CImg(img1,false)-=img2; + } + + template + inline CImgList::type> operator-(const CImg& img, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img - list[l]; + return res; + } + + template + inline CImgList::type> operator-(const CImgList& list, const CImg& img) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)-=img; + } + + template + inline CImgList::type> operator-(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::superset::type t1t2; + return CImgList(list1)-=list2; + } + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator*(const CImg& img, const double val) { + return CImg(img,false)*=val; + } +#else + template + inline CImg::type> operator*(const CImg& img, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImg(img,false)*=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator*(const double val, const CImg& img) { + return img*val; + } +#else + template + inline CImg::type> operator*(const t1 val, const CImg& img) { + return img*val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator*(const CImgList& list, const double val) { + return CImgList(list)*=val; + } +#else + template + inline CImgList::type> operator*(const CImgList& list, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)*=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator*(const double val, const CImgList& list) { + return list*val; + } +#else + template + inline CImgList::type> operator*(const t1 val, const CImgList& list) { + return list*val; + } +#endif + + template + inline CImg::type> operator*(const CImg& img1, const CImg& img2) { + typedef typename cimg::superset::type t1t2; + if (img1.width!=img2.height) + throw CImgArgumentException("operator*() : can't multiply a matrix (%ux%u) by a matrix (%ux%u)", + img1.width,img1.height,img2.width,img2.height); + CImg res(img2.width,img1.height); + t1t2 val; +#ifdef cimg_use_openmp +#pragma omp parallel for if (img1.size()>=1000 && img2.size()>=1000) private(val) +#endif + cimg_forXY(res,i,j) { val = 0; cimg_forX(img1,k) val+=img1(k,j)*img2(i,k); res(i,j) = val; } + return res; + } + + template + inline CImgList::type> operator*(const CImg& img, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img*list[l]; + return res; + } + + template + inline CImgList::type> operator*(const CImgList& list, const CImg& img) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = list[l]*img; + return res; + } + + template + inline CImgList::type> operator*(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::superset::type t1t2; + CImgList res(cimg::min(list1.size,list2.size)); + cimglist_for(res,l) res[l] = list1[l]*list2[l]; + return res; + } + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator/(const CImg& img, const double val) { + return CImg(img,false)/=val; + } +#else + template + inline CImg::type> operator/(const CImg& img, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImg(img,false)/=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImg operator/(const double val, CImg& img) { + return val*img.get_invert(); + } +#else + template + inline CImg::type> operator/(const t1 val, CImg& img) { + return val*img.get_invert(); + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator/(const CImgList& list, const double val) { + return CImgList(list)/=val; + } +#else + template + inline CImgList::type> operator/(const CImgList& list, const t2 val) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)/=val; + } +#endif + +#ifdef cimg_use_visualcpp6 + template + inline CImgList operator/(const double val, const CImgList& list) { + CImgList res(list.size); + cimglist_for(res,l) res[l] = val/list[l]; + return res; + } +#else + template + inline CImgList::type> operator/(const t1 val, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = val/list[l]; + return res; + } +#endif + + template + inline CImg::type> operator/(const CImg& img1, const CImg& img2) { + typedef typename cimg::superset::type t1t2; + return CImg(img1,false)*=img2.get_invert(); + } + + template + inline CImg::type> operator/(const CImg& img, const CImgList& list) { + typedef typename cimg::superset::type t1t2; + CImgList res(list.size); + cimglist_for(res,l) res[l] = img/list[l]; + return res; + } + + template + inline CImgList::type> operator/(const CImgList& list, const CImg& img) { + typedef typename cimg::superset::type t1t2; + return CImgList(list)/=img; + } + + template + inline CImgList::type> operator/(const CImgList& list1, const CImgList& list2) { + typedef typename cimg::superset::type t1t2; + return CImgList(list1)/=list2; + } + + template + inline CImg<_cimg_Tfloat> sqr(const CImg& instance) { + return instance.get_sqr(); + } + + template + inline CImg<_cimg_Tfloat> sqrt(const CImg& instance) { + return instance.get_sqrt(); + } + + template + inline CImg<_cimg_Tfloat> exp(const CImg& instance) { + return instance.get_exp(); + } + + template + inline CImg<_cimg_Tfloat> log(const CImg& instance) { + return instance.get_log(); + } + + template + inline CImg<_cimg_Tfloat> log10(const CImg& instance) { + return instance.get_log10(); + } + + template + inline CImg<_cimg_Tfloat> abs(const CImg& instance) { + return instance.get_abs(); + } + + template + inline CImg<_cimg_Tfloat> cos(const CImg& instance) { + return instance.get_cos(); + } + + template + inline CImg<_cimg_Tfloat> sin(const CImg& instance) { + return instance.get_sin(); + } + + template + inline CImg<_cimg_Tfloat> tan(const CImg& instance) { + return instance.get_tan(); + } + + template + inline CImg<_cimg_Tfloat> acos(const CImg& instance) { + return instance.get_acos(); + } + + template + inline CImg<_cimg_Tfloat> asin(const CImg& instance) { + return instance.get_asin(); + } + + template + inline CImg<_cimg_Tfloat> atan(const CImg& instance) { + return instance.get_atan(); + } + + template + inline CImg transpose(const CImg& instance) { + return instance.get_transpose(); + } + + template + inline CImg<_cimg_Tfloat> invert(const CImg& instance) { + return instance.get_invert(); + } + + template + inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance) { + return instance.get_pseudoinvert(); + } + + /*------------------------------------------- + # + # + # + # Definition of the CImgDisplay structure + # + # + # + --------------------------------------------*/ + + //! This class represents a window which can display \ref CImg images and handles mouse and keyboard events. + /** + Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg image + of a \c CImgList image list inside. When a display is created, associated window events + (such as mouse motion, keyboard and window size changes) are handled and can be easily + detected by testing specific \c CImgDisplay data fields. + See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class. + **/ + + struct CImgDisplay { + + //! Width of the display + unsigned int width; + + //! Height of the display + unsigned int height; + + //! Normalization type used for the display + unsigned int normalization; + + //! Display title + char* title; + + //! X-pos of the display on the screen + volatile int window_x; + + //! Y-pos of the display on the screen + volatile int window_y; + + //! Width of the underlying window + volatile unsigned int window_width; + + //! Height of the underlying window + volatile unsigned int window_height; + + //! X-coordinate of the mouse pointer on the display + volatile int mouse_x; + + //! Y-coordinate of the mouse pointer on the display + volatile int mouse_y; + + //! Button state of the mouse + volatile unsigned int buttons[512]; + volatile unsigned int& button; + + //! Wheel state of the mouse + volatile int wheel; + + //! Key value if pressed + volatile unsigned int& key; + volatile unsigned int keys[512]; + + //! Key value if released + volatile unsigned int& released_key; + volatile unsigned int released_keys[512]; + + //! Closed state of the window + volatile bool is_closed; + + //! Resized state of the window + volatile bool is_resized; + + //! Moved state of the window + volatile bool is_moved; + + //! Event state of the window + volatile bool is_event; + + //! Current state of the corresponding key (exists for all referenced keys). + volatile bool is_keyESC; + volatile bool is_keyF1; + volatile bool is_keyF2; + volatile bool is_keyF3; + volatile bool is_keyF4; + volatile bool is_keyF5; + volatile bool is_keyF6; + volatile bool is_keyF7; + volatile bool is_keyF8; + volatile bool is_keyF9; + volatile bool is_keyF10; + volatile bool is_keyF11; + volatile bool is_keyF12; + volatile bool is_keyPAUSE; + volatile bool is_key1; + volatile bool is_key2; + volatile bool is_key3; + volatile bool is_key4; + volatile bool is_key5; + volatile bool is_key6; + volatile bool is_key7; + volatile bool is_key8; + volatile bool is_key9; + volatile bool is_key0; + volatile bool is_keyBACKSPACE; + volatile bool is_keyINSERT; + volatile bool is_keyHOME; + volatile bool is_keyPAGEUP; + volatile bool is_keyTAB; + volatile bool is_keyQ; + volatile bool is_keyW; + volatile bool is_keyE; + volatile bool is_keyR; + volatile bool is_keyT; + volatile bool is_keyY; + volatile bool is_keyU; + volatile bool is_keyI; + volatile bool is_keyO; + volatile bool is_keyP; + volatile bool is_keyDELETE; + volatile bool is_keyEND; + volatile bool is_keyPAGEDOWN; + volatile bool is_keyCAPSLOCK; + volatile bool is_keyA; + volatile bool is_keyS; + volatile bool is_keyD; + volatile bool is_keyF; + volatile bool is_keyG; + volatile bool is_keyH; + volatile bool is_keyJ; + volatile bool is_keyK; + volatile bool is_keyL; + volatile bool is_keyENTER; + volatile bool is_keySHIFTLEFT; + volatile bool is_keyZ; + volatile bool is_keyX; + volatile bool is_keyC; + volatile bool is_keyV; + volatile bool is_keyB; + volatile bool is_keyN; + volatile bool is_keyM; + volatile bool is_keySHIFTRIGHT; + volatile bool is_keyARROWUP; + volatile bool is_keyCTRLLEFT; + volatile bool is_keyAPPLEFT; + volatile bool is_keyALT; + volatile bool is_keySPACE; + volatile bool is_keyALTGR; + volatile bool is_keyAPPRIGHT; + volatile bool is_keyMENU; + volatile bool is_keyCTRLRIGHT; + volatile bool is_keyARROWLEFT; + volatile bool is_keyARROWDOWN; + volatile bool is_keyARROWRIGHT; + volatile bool is_keyPAD0; + volatile bool is_keyPAD1; + volatile bool is_keyPAD2; + volatile bool is_keyPAD3; + volatile bool is_keyPAD4; + volatile bool is_keyPAD5; + volatile bool is_keyPAD6; + volatile bool is_keyPAD7; + volatile bool is_keyPAD8; + volatile bool is_keyPAD9; + volatile bool is_keyPADADD; + volatile bool is_keyPADSUB; + volatile bool is_keyPADMUL; + volatile bool is_keyPADDIV; + + //! Fullscreen state of the display + bool is_fullscreen; + + float fps_fps, min, max; + unsigned long timer, fps_frames, fps_timer; + +#ifdef cimgdisplay_plugin +#include cimgdisplay_plugin +#endif +#ifdef cimgdisplay_plugin1 +#include cimgdisplay_plugin1 +#endif +#ifdef cimgdisplay_plugin2 +#include cimgdisplay_plugin2 +#endif +#ifdef cimgdisplay_plugin3 +#include cimgdisplay_plugin3 +#endif +#ifdef cimgdisplay_plugin4 +#include cimgdisplay_plugin4 +#endif +#ifdef cimgdisplay_plugin5 +#include cimgdisplay_plugin5 +#endif +#ifdef cimgdisplay_plugin6 +#include cimgdisplay_plugin6 +#endif +#ifdef cimgdisplay_plugin7 +#include cimgdisplay_plugin7 +#endif +#ifdef cimgdisplay_plugin8 +#include cimgdisplay_plugin8 +#endif + + //! Create an empty display window. + CImgDisplay(): + width(0),height(0),normalization(0),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), + min(0),max(0) {} + + //! Create a display window with a specified size \p pwidth x \p height. + /** \param dimw Width of the display window. + \param dimh Height of the display window. + \param title Title of the display window. + \param normalization_type Normalization type of the display window (0=none, 1=always, 2=once). + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + A black image will be initially displayed in the display window. + **/ + CImgDisplay(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false): + width(0),height(0),normalization(0),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), + min(0),max(0) { + assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + } + + //! Create a display window from an image. + /** \param img : Image that will be used to create the display window. + \param title : Title of the display window + \param normalization_type : Normalization type of the display window. + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + **/ + template + CImgDisplay(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false): + width(0),height(0),normalization(0),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { + assign(img,title,normalization_type,fullscreen_flag,closed_flag); + } + + //! Create a display window from an image list. + /** \param list : The list of images to display. + \param title : Title of the display window + \param normalization_type : Normalization type of the display window. + \param fullscreen_flag : Fullscreen mode. + \param closed_flag : Initially visible mode. + **/ + template + CImgDisplay(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false): + width(0),height(0),normalization(0),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { + assign(list,title,normalization_type,fullscreen_flag,closed_flag); + } + + //! Create a display window by copying another one. + /** + \param disp : Display window to copy. + **/ + CImgDisplay(const CImgDisplay& disp): + width(0),height(0),normalization(0),title(0), + window_x(0),window_y(0),window_width(0),window_height(0), + mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), + is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { + assign(disp); + } + + //! Destructor. + ~CImgDisplay() { + assign(); + } + + //! Assignment operator. + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return true is display is empty. + bool is_empty() const { + return (!width || !height); + } + + //! Return true if display is not empty. + operator bool() const { + return !is_empty(); + } + + //! Return display width. + int dimx() const { + return (int)width; + } + + //! Return display height. + int dimy() const { + return (int)height; + } + + //! Return display window width. + int window_dimx() const { + return (int)window_width; + } + + //! Return display window height. + int window_dimy() const { + return (int)window_height; + } + + //! Return X-coordinate of the window. + int window_posx() const { + return window_x; + } + + //! Return Y-coordinate of the window. + int window_posy() const { + return window_y; + } + + //! Synchronized waiting function. Same as cimg::wait(). + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::_sleep(milliseconds,timer); + return *this; + } + + //! Wait for an event occuring on the current display. + CImgDisplay& wait() { + if (!is_empty()) wait(*this); + return *this; + } + + //! Wait for any event occuring on the display \c disp1. + static void wait(CImgDisplay& disp1) { + disp1.is_event = 0; + while (!disp1.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1 or \c disp2. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { + disp1.is_event = disp2.is_event = 0; + while (!disp1.is_event && !disp2.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { + disp1.is_event = disp2.is_event = disp3.is_event = 0; + while (!disp1.is_event && !disp2.is_event && !disp3.is_event) wait_all(); + } + + //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { + disp1.is_event = disp2.is_event = disp3.is_event = disp4.is_event = 0; + while (!disp1.is_event && !disp2.is_event && !disp3.is_event && !disp4.is_event) wait_all(); + } + + //! Return the frame per second rate. + float frames_per_second() { + if (!fps_timer) fps_timer = cimg::time(); + const float delta = (cimg::time()-fps_timer)/1000.0f; + ++fps_frames; + if (delta>=1) { + fps_fps = fps_frames/delta; + fps_frames = 0; + fps_timer = cimg::time(); + } + return fps_fps; + } + + //! Display an image list CImgList into a display window. + /** First, all images of the list are appended into a single image used for visualization, + then this image is displayed in the current display window. + \param list : The list of images to display. + \param axis : The axis used to append the image for visualization. Can be 'x' (default),'y','z' or 'v'. + \param align : Defines the relative alignment of images when displaying images of different sizes. + Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment). + **/ + template + CImgDisplay& display(const CImgList& list, const char axis='x', const char align='p') { + return display(list.get_append(axis,align)); + } + + //! Display an image CImg into a display window. + template + CImgDisplay& operator<<(const CImg& img) { + return display(img); + } + + //! Display an image CImg into a display window. + template + CImgDisplay& operator<<(const CImgList& list) { + return display(list); + } + + //! Resize a display window with the size of an image. + /** \param img : Input image. \p image.width and \p image.height give the new dimensions of the display window. + \param redraw : If \p true (default), the current displayed image in the display window will + be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window. + **/ + template + CImgDisplay& resize(const CImg& img, const bool redraw=true) { + return resize(img.width,img.height,redraw); + } + + //! Resize a display window using the size of the given display \p disp. + CImgDisplay& resize(const CImgDisplay& disp, const bool redraw=true) { + return resize(disp.width,disp.height,redraw); + } + + //! Resize a display window in its current size. + CImgDisplay& resize(const bool redraw=true) { + resize(window_width,window_height,redraw); + return *this; + } + + //! Set fullscreen mode. + CImgDisplay& fullscreen(const bool redraw=true) { + if (is_empty() || is_fullscreen) return *this; + return toggle_fullscreen(redraw); + } + + //! Set normal screen mode. + CImgDisplay& normalscreen(const bool redraw=true) { + if (is_empty() || !is_fullscreen) return *this; + return toggle_fullscreen(redraw); + } + + // Inner routine used for fast resizing of buffer to display size. + template + static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; + float s, curr, old; + s = (float)ws/wd; + poffx = offx; curr = 0; for (unsigned int x=0; x=keys; --ptrs) if (*ptrs) { if (remove) *ptrs = 0; return true; } + return false; + } + + //! Test if a key has been pressed. + bool is_key(const unsigned int key1, const bool remove) { + for (unsigned int *ptrs=(unsigned int*)keys+512-1; ptrs>=keys; --ptrs) if (*ptrs==key1) { if (remove) *ptrs = 0; return true; } + return false; + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const bool remove) { + const unsigned int seq[] = { key1, key2 }; + return is_key(seq,2,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const bool remove) { + const unsigned int seq[] = { key1, key2, key3 }; + return is_key(seq,3,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4 }; + return is_key(seq,4,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const unsigned int key5, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4, key5 }; + return is_key(seq,5,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const unsigned int key5, const unsigned int key6, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4, key5, key6 }; + return is_key(seq,6,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const unsigned int key5, const unsigned int key6, + const unsigned int key7, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7 }; + return is_key(seq,7,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const unsigned int key5, const unsigned int key6, + const unsigned int key7, const unsigned int key8, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8 }; + return is_key(seq,8,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, + const unsigned int key4, const unsigned int key5, const unsigned int key6, + const unsigned int key7, const unsigned int key8, const unsigned int key9, const bool remove) { + const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8, key9 }; + return is_key(seq,9,remove); + } + + //! Test if a key sequence has been typed. + bool is_key(const unsigned int *const keyseq, const unsigned int N, const bool remove=true) { + if (keyseq && N) { + const unsigned int *const ps_end = keyseq+N-1, k = *ps_end, *const pk_end = (unsigned int*)keys+1+512-N; + for (unsigned int *pk = (unsigned int*)keys; pk1?dz:0), nh = dy + (dz>1?dz:0); + const unsigned int + sw = CImgDisplay::screen_dimx(), sh = CImgDisplay::screen_dimy(), + mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin, + mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin, + Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax, + Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax; + if (nwMw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; } + if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; } + if (nw + CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + throw CImgDisplayException("CImgDisplay()::assign() : Display has been required but is not available (cimg_display=0)"); + const char* avoid_warning = title + img.width + normalization_type + (int)fullscreen_flag + (int)closed_flag; + avoid_warning = 0; + return assign(0,0); + } + + //! In-place version of the previous constructor. + template + CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + throw CImgDisplayException("CImgDisplay()::assign() : Display has been required but is not available (cimg_display=0)"); + const char* avoid_warning = title + list.size + normalization_type + (int)fullscreen_flag + (int)closed_flag; + avoid_warning = 0; + return assign(0,0); + } + + //! In-place version of the previous constructor. + CImgDisplay& assign(const CImgDisplay &disp) { + return assign(disp.width,disp.height); + } + + //! Resize window. + CImgDisplay& resize(const int width, const int height, const bool redraw=true) { + int avoid_warning = width | height | (int)redraw; + avoid_warning = 0; + return *this; + } + + //! Toggle fullscreen mode. + CImgDisplay& toggle_fullscreen(const bool redraw=true) { + bool avoid_warning = redraw; + avoid_warning = false; + return *this; + } + + //! Show a closed display. + CImgDisplay& show() { + return *this; + } + + //! Close a visible display. + CImgDisplay& close() { + return *this; + } + + //! Move window. + CImgDisplay& move(const int posx, const int posy) { + int avoid_warning = posx | posy; + avoid_warning = 0; + return *this; + } + + //! Show mouse pointer. + CImgDisplay& show_mouse() { + return *this; + } + + //! Hide mouse pointer. + CImgDisplay& hide_mouse() { + return *this; + } + + //! Move mouse pointer to a specific location. + CImgDisplay& set_mouse(const int posx, const int posy) { + int avoid_warning = posx | posy; + avoid_warning = 0; + return *this; + } + + //! Set the window title. + CImgDisplay& set_title(const char *format, ...) { + const char *avoid_warning = format; + avoid_warning = 0; + return *this; + } + + //! Display an image in a window. + template + CImgDisplay& display(const CImg& img) { + unsigned int avoid_warning = img.width; + avoid_warning = 0; + return *this; + } + + //! Re-paint image content in window. + CImgDisplay& paint() { + return *this; + } + + //! Render image buffer into GDI native image format. + template + CImgDisplay& render(const CImg& img) { + unsigned int avoid_warning = img.width; + avoid_warning = 0; + return *this; + } + + //! Take a snapshot of the display in the specified image. + template + const CImgDisplay& snapshot(CImg& img) const { + img.assign(width,height,1,3,0); + return *this; + } + + // X11-based display + //------------------- +#elif cimg_display==1 + Atom wm_delete_window, wm_delete_protocol; + Window window, background_window; + Colormap colormap; + XImage *image; + void *data; +#ifdef cimg_use_xshm + XShmSegmentInfo *shminfo; +#endif + + static int screen_dimx() { + int res = 0; + if (!cimg::X11attr().display) { + Display *disp = XOpenDisplay((cimg_std::getenv("DISPLAY")?cimg_std::getenv("DISPLAY"):":0.0")); + if (!disp) + throw CImgDisplayException("CImgDisplay::screen_dimx() : Can't open X11 display."); + res = DisplayWidth(disp,DefaultScreen(disp)); + XCloseDisplay(disp); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) + res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width; + else +#endif + res = DisplayWidth(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + } + return res; + } + + static int screen_dimy() { + int res = 0; + if (!cimg::X11attr().display) { + Display *disp = XOpenDisplay((cimg_std::getenv("DISPLAY") ? cimg_std::getenv("DISPLAY") : ":0.0")); + if (!disp) + throw CImgDisplayException("CImgDisplay::screen_dimy() : Can't open X11 display."); + res = DisplayHeight(disp,DefaultScreen(disp)); + XCloseDisplay(disp); + } else { +#ifdef cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) + res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height; + else +#endif + res = DisplayHeight(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + } + return res; + } + + static void wait_all() { + if (cimg::X11attr().display) { + XLockDisplay(cimg::X11attr().display); + bool flag = true; + XEvent event; + while (flag) { + XNextEvent(cimg::X11attr().display, &event); + for (unsigned int i = 0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) { + cimg::X11attr().wins[i]->_handle_events(&event); + if (cimg::X11attr().wins[i]->is_event) flag = false; + } + } + XUnlockDisplay(cimg::X11attr().display); + } + } + + void _handle_events(const XEvent *const pevent) { + XEvent event = *pevent; + switch (event.type) { + case ClientMessage : { + if ((int)event.xclient.message_type==(int)wm_delete_protocol && + (int)event.xclient.data.l[0]==(int)wm_delete_window) { + XUnmapWindow(cimg::X11attr().display,window); + mouse_x = mouse_y = -1; + if (button) { cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button = 0; } + if (key) { cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } + if (released_key) { cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } + is_closed = is_event = true; + } + } break; + case ConfigureNotify : { + while (XCheckWindowEvent(cimg::X11attr().display,window,StructureNotifyMask,&event)) {} + const unsigned int + nw = event.xconfigure.width, + nh = event.xconfigure.height; + const int + nx = event.xconfigure.x, + ny = event.xconfigure.y; + if (nw && nh && (nw!=window_width || nh!=window_height)) { + window_width = nw; + window_height = nh; + mouse_x = mouse_y = -1; + XResizeWindow(cimg::X11attr().display,window,window_width,window_height); + is_resized = is_event = true; + } + if (nx!=window_x || ny!=window_y) { + window_x = nx; + window_y = ny; + is_moved = is_event = true; + } + } break; + case Expose : { + while (XCheckWindowEvent(cimg::X11attr().display,window,ExposureMask,&event)) {} + _paint(false); + if (is_fullscreen) { + XWindowAttributes attr; + XGetWindowAttributes(cimg::X11attr().display, window, &attr); + while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); + XSetInputFocus(cimg::X11attr().display, window, RevertToParent, CurrentTime); + } + } break; + case ButtonPress : { + do { + mouse_x = event.xmotion.x; + mouse_y = event.xmotion.y; + if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; + switch (event.xbutton.button) { + case 1 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=1; is_event = true; break; + case 2 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=4; is_event = true; break; + case 3 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=2; is_event = true; break; + } + } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonPressMask,&event)); + } break; + case ButtonRelease : { + do { + mouse_x = event.xmotion.x; + mouse_y = event.xmotion.y; + if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; + switch (event.xbutton.button) { + case 1 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~1U; is_event = true; break; + case 2 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~4U; is_event = true; break; + case 3 : cimg_std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~2U; is_event = true; break; + case 4 : ++wheel; is_event = true; break; + case 5 : --wheel; is_event = true; break; + } + } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonReleaseMask,&event)); + } break; + case KeyPress : { + char tmp; + KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + update_iskey((unsigned int)ksym,true); + if (key) cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); + key = (unsigned int)ksym; + if (released_key) { cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } + is_event = true; + } break; + case KeyRelease : { + char tmp; + KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + update_iskey((unsigned int)ksym,false); + if (key) { cimg_std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } + if (released_key) cimg_std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); + released_key = (unsigned int)ksym; + is_event = true; + } break; + case EnterNotify: { + while (XCheckWindowEvent(cimg::X11attr().display,window,EnterWindowMask,&event)) {} + mouse_x = event.xmotion.x; + mouse_y = event.xmotion.y; + if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; + } break; + case LeaveNotify : { + while (XCheckWindowEvent(cimg::X11attr().display,window,LeaveWindowMask,&event)) {} + mouse_x = mouse_y =-1; + is_event = true; + } break; + case MotionNotify : { + while (XCheckWindowEvent(cimg::X11attr().display,window,PointerMotionMask,&event)) {} + mouse_x = event.xmotion.x; + mouse_y = event.xmotion.y; + if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; + is_event = true; + } break; + } + } + + static void* _events_thread(void *arg) { + arg = 0; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + for (;;) { + XLockDisplay(cimg::X11attr().display); + bool event_flag = XCheckTypedEvent(cimg::X11attr().display, ClientMessage, &event); + if (!event_flag) event_flag = XCheckMaskEvent(cimg::X11attr().display, + ExposureMask|StructureNotifyMask|ButtonPressMask| + KeyPressMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask| + ButtonReleaseMask|KeyReleaseMask,&event); + if (event_flag) { + for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) + cimg::X11attr().wins[i]->_handle_events(&event); + } + XUnlockDisplay(cimg::X11attr().display); + pthread_testcancel(); + cimg::sleep(7); + } + return 0; + } + + void _set_colormap(Colormap& colormap, const unsigned int dim) { + XColor palette[256]; + switch (dim) { + case 1 : { // palette for greyscale images + for (unsigned int index=0; index<256; ++index) { + palette[index].pixel = index; + palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8); + palette[index].flags = DoRed | DoGreen | DoBlue; + } + } break; + case 2 : { // palette for RG images + for (unsigned int index=0, r=8; r<256; r+=16) + for (unsigned int g=8; g<256; g+=16) { + palette[index].pixel = index; + palette[index].red = palette[index].blue = (unsigned short)(r<<8); + palette[index].green = (unsigned short)(g<<8); + palette[index++].flags = DoRed | DoGreen | DoBlue; + } + } break; + default : { // palette for RGB images + for (unsigned int index=0, r=16; r<256; r+=32) + for (unsigned int g=16; g<256; g+=32) + for (unsigned int b=32; b<256; b+=64) { + palette[index].pixel = index; + palette[index].red = (unsigned short)(r<<8); + palette[index].green = (unsigned short)(g<<8); + palette[index].blue = (unsigned short)(b<<8); + palette[index++].flags = DoRed | DoGreen | DoBlue; + } + } + } + XStoreColors(cimg::X11attr().display,colormap,palette,256); + } + + void _map_window() { + XWindowAttributes attr; + XEvent event; + bool exposed = false, mapped = false; + XMapRaised(cimg::X11attr().display,window); + XSync(cimg::X11attr().display,False); + do { + XWindowEvent(cimg::X11attr().display,window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify : mapped = true; break; + case Expose : exposed = true; break; + default : XSync(cimg::X11attr().display, False); cimg::sleep(10); + } + } while (!(exposed && mapped)); + do { + XGetWindowAttributes(cimg::X11attr().display, window, &attr); + if (attr.map_state!=IsViewable) { XSync(cimg::X11attr().display,False); cimg::sleep(10); } + } while (attr.map_state != IsViewable); + window_x = attr.x; + window_y = attr.y; + } + + void _paint(const bool wait_expose=true) { + if (!is_closed) { + if (wait_expose) { + static XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = True; + event.xexpose.display = cimg::X11attr().display; + event.xexpose.window = window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = dimx(); + event.xexpose.height = dimy(); + event.xexpose.count = 0; + XSendEvent(cimg::X11attr().display, window, False, 0, &event); + } else { +#ifdef cimg_use_xshm + if (shminfo) XShmPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height,False); + else +#endif + XPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height); + XSync(cimg::X11attr().display, False); + } + } + } + + template + void _resize(T foo, const unsigned int ndimx, const unsigned int ndimy, const bool redraw) { + foo = 0; +#ifdef cimg_use_xshm + if (shminfo) { + XShmSegmentInfo *nshminfo = new XShmSegmentInfo; + XImage *nimage = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); + if (!nimage) { + delete nshminfo; + return; + } else { + nshminfo->shmid = shmget(IPC_PRIVATE, ndimx*ndimy*sizeof(T), IPC_CREAT | 0777); + if (nshminfo->shmid==-1) { + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); + if (nshminfo->shmaddr==(char*)-1) { + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + nshminfo->readOnly = False; + cimg::X11attr().shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(cimg::X11attr().display, nshminfo); + XSync(cimg::X11attr().display, False); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11attr().shm_enabled) { + shmdt(nshminfo->shmaddr); + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + T *const ndata = (T*)nimage->data; + if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); + else cimg_std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(cimg::X11attr().display, shminfo); + XDestroyImage(image); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + delete shminfo; + shminfo = nshminfo; + image = nimage; + data = (void*)ndata; + } + } + } + } + } else +#endif + { + T *ndata = (T*)cimg_std::malloc(ndimx*ndimy*sizeof(T)); + if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); + else cimg_std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + data = (void*)ndata; + XDestroyImage(image); + image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + background_window = 0; + if (is_fullscreen && !is_closed) { +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(cimg::X11attr().display,&foo,&foo)) { + XRRRotations(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display), &cimg::X11attr().curr_rotation); + if (!cimg::X11attr().resolutions) { + cimg::X11attr().resolutions = XRRSizes(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display),&foo); + cimg::X11attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11attr().resolutions) { + cimg::X11attr().curr_resolution = 0; + for (unsigned int i=0; i=width && nh>=height && + nw<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height)) + cimg::X11attr().curr_resolution = i; + } + if (cimg::X11attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); + XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), + cimg::X11attr().curr_resolution, cimg::X11attr().curr_rotation, CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(cimg::X11attr().display, False); + } + } + } + if (!cimg::X11attr().resolutions) + cimg::warn("CImgDisplay::_create_window() : Xrandr extension is not supported by the X server."); +#endif + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + XSetWindowAttributes winattr; + winattr.override_redirect = True; + if (sx!=width || sy!=height) { + background_window = XCreateWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)),0,0, + sx,sy,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + const unsigned int bufsize = sx*sy*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); + void *background_data = cimg_std::malloc(bufsize); + cimg_std::memset(background_data,0,bufsize); + XImage *background_image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)background_data,sx,sy,8,0); + XEvent event; + XSelectInput(cimg::X11attr().display,background_window,StructureNotifyMask); + XMapRaised(cimg::X11attr().display,background_window); + do XWindowEvent(cimg::X11attr().display,background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); +#ifdef cimg_use_xshm + if (shminfo) XShmPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy,False); + else +#endif + XPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy); + XWindowAttributes attr; + XGetWindowAttributes(cimg::X11attr().display, background_window, &attr); + while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); + XDestroyImage(background_image); + } + } + } + + void _desinit_fullscreen() { + if (is_fullscreen) { + XUngrabKeyboard(cimg::X11attr().display,CurrentTime); +#ifdef cimg_use_xrandr + if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); + XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), + 0, cimg::X11attr().curr_rotation, CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(cimg::X11attr().display, False); + cimg::X11attr().curr_resolution = 0; + } +#endif + if (background_window) XDestroyWindow(cimg::X11attr().display,background_window); + background_window = 0; + is_fullscreen = false; + } + } + + static int _assign_xshm(Display *dpy, XErrorEvent *error) { + dpy = 0; error = 0; + cimg::X11attr().shm_enabled = false; + return 0; + } + + void _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const int s = cimg::strlen(ptitle)+1; + char *tmp_title = s?new char[s]:0; + if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); + + // Destroy previous display window if existing + if (!is_empty()) assign(); + + // Open X11 display if necessary. + if (!cimg::X11attr().display) { + static bool xinit_threads = false; + if (!xinit_threads) { XInitThreads(); xinit_threads = true; } + cimg::X11attr().nb_wins = 0; + cimg::X11attr().display = XOpenDisplay((cimg_std::getenv("DISPLAY")?cimg_std::getenv("DISPLAY"):":0.0")); + if (!cimg::X11attr().display) + throw CImgDisplayException("CImgDisplay::_create_window() : Can't open X11 display"); + cimg::X11attr().nb_bits = DefaultDepth(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display)); + if (cimg::X11attr().nb_bits!=8 && cimg::X11attr().nb_bits!=16 && cimg::X11attr().nb_bits!=24 && cimg::X11attr().nb_bits!=32) + throw CImgDisplayException("CImgDisplay::_create_window() : %u bits mode is not supported " + "(only 8, 16, 24 and 32 bits modes are supported)",cimg::X11attr().nb_bits); + cimg::X11attr().gc = new GC; + *cimg::X11attr().gc = DefaultGC(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + Visual *visual = DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); + XVisualInfo vtemplate; + vtemplate.visualid = XVisualIDFromVisual(visual); + int nb_visuals; + XVisualInfo *vinfo = XGetVisualInfo(cimg::X11attr().display,VisualIDMask,&vtemplate,&nb_visuals); + if (vinfo && vinfo->red_maskblue_mask) cimg::X11attr().blue_first = true; + cimg::X11attr().byte_order = ImageByteOrder(cimg::X11attr().display); + XFree(vinfo); + XLockDisplay(cimg::X11attr().display); + cimg::X11attr().event_thread = new pthread_t; + pthread_create(cimg::X11attr().event_thread,0,_events_thread,0); + } else XLockDisplay(cimg::X11attr().display); + + // Set display variables + width = cimg::min(dimw,(unsigned int)screen_dimx()); + height = cimg::min(dimh,(unsigned int)screen_dimy()); + normalization = normalization_type<4?normalization_type:3; + is_fullscreen = fullscreen_flag; + window_x = window_y = 0; + is_closed = closed_flag; + title = tmp_title; + flush(); + + // Create X11 window and palette (if 8bits display) + if (is_fullscreen) { + if (!is_closed) _init_fullscreen(); + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + XSetWindowAttributes winattr; + winattr.override_redirect = True; + window = XCreateWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + (sx-width)/2,(sy-height)/2, + width,height,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); + } else + window = XCreateSimpleWindow(cimg::X11attr().display, + RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + 0,0,width,height,2,0,0x0L); + XStoreName(cimg::X11attr().display,window,title?title:" "); + if (cimg::X11attr().nb_bits==8) { + colormap = XCreateColormap(cimg::X11attr().display,window,DefaultVisual(cimg::X11attr().display, + DefaultScreen(cimg::X11attr().display)),AllocAll); + _set_colormap(colormap,3); + XSetWindowColormap(cimg::X11attr().display,window,colormap); + } + window_width = width; + window_height = height; + + // Create XImage + const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); +#ifdef cimg_use_xshm + shminfo = 0; + if (XShmQueryExtension(cimg::X11attr().display)) { + shminfo = new XShmSegmentInfo; + image = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,shminfo,width,height); + if (!image) { + delete shminfo; + shminfo = 0; + } else { + shminfo->shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777); + if (shminfo->shmid==-1) { + XDestroyImage(image); + delete shminfo; + shminfo = 0; + } else { + shminfo->shmaddr = image->data = (char*)(data = shmat(shminfo->shmid,0,0)); + if (shminfo->shmaddr==(char*)-1) { + shmctl(shminfo->shmid,IPC_RMID,0); + XDestroyImage(image); + delete shminfo; + shminfo = 0; + } else { + shminfo->readOnly = False; + cimg::X11attr().shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(cimg::X11attr().display, shminfo); + XSync(cimg::X11attr().display, False); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11attr().shm_enabled) { + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + XDestroyImage(image); + delete shminfo; + shminfo = 0; + } + } + } + } + } + if (!shminfo) +#endif + { + data = cimg_std::malloc(bufsize); + image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), + cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,width,height,8,0); + } + + wm_delete_window = XInternAtom(cimg::X11attr().display, "WM_DELETE_WINDOW", False); + wm_delete_protocol = XInternAtom(cimg::X11attr().display, "WM_PROTOCOLS", False); + XSetWMProtocols(cimg::X11attr().display, window, &wm_delete_window, 1); + XSelectInput(cimg::X11attr().display,window, + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + if (is_fullscreen) XGrabKeyboard(cimg::X11attr().display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); + cimg::X11attr().wins[cimg::X11attr().nb_wins++]=this; + if (!is_closed) _map_window(); else { window_x = window_y = cimg::type::min(); } + XUnlockDisplay(cimg::X11attr().display); + } + + CImgDisplay& assign() { + if (is_empty()) return *this; + XLockDisplay(cimg::X11attr().display); + + // Remove display window from event thread list. + unsigned int i; + for (i = 0; ishmaddr); + shmctl(shminfo->shmid,IPC_RMID,0); + delete shminfo; + shminfo = 0; + } else +#endif + XDestroyImage(image); + data = 0; image = 0; + if (cimg::X11attr().nb_bits==8) XFreeColormap(cimg::X11attr().display,colormap); + colormap = 0; + XSync(cimg::X11attr().display, False); + + // Reset display variables + if (title) delete[] title; + width = height = normalization = window_width = window_height = 0; + window_x = window_y = 0; + is_fullscreen = false; + is_closed = true; + min = max = 0; + title = 0; + flush(); + + // End event thread and close display if necessary + XUnlockDisplay(cimg::X11attr().display); + + /* The code below was used to close the X11 display when not used anymore, + unfortunately, since the latest Xorg versions, it randomely hangs, so + I prefer to remove it. A fix would be needed anyway. + + if (!cimg::X11attr().nb_wins) { + // Kill event thread + pthread_cancel(*cimg::X11attr().event_thread); + XUnlockDisplay(cimg::X11attr().display); + pthread_join(*cimg::X11attr().event_thread,0); + delete cimg::X11attr().event_thread; + cimg::X11attr().event_thread = 0; + XCloseDisplay(cimg::X11attr().display); + cimg::X11attr().display = 0; + delete cimg::X11attr().gc; + cimg::X11attr().gc = 0; + } else XUnlockDisplay(cimg::X11attr().display); + */ + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + min = max = 0; + cimg_std::memset(data,0,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*width*height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return render(nimg).paint(); + } + + template + CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list.get_append('x','p'), + &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& win) { + if (!win) return assign(); + _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); + cimg_std::memcpy(data,win.data,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): + cimg::X11attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*width*height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + XLockDisplay(cimg::X11attr().display); + if (window_width!=dimx || window_height!=dimy) XResizeWindow(cimg::X11attr().display,window,dimx,dimy); + if (width!=dimx || height!=dimy) switch (cimg::X11attr().nb_bits) { + case 8 : { unsigned char foo = 0; _resize(foo,dimx,dimy,redraw); } break; + case 16 : { unsigned short foo = 0; _resize(foo,dimx,dimy,redraw); } break; + default : { unsigned int foo = 0; _resize(foo,dimx,dimy,redraw); } + } + window_width = width = dimx; window_height = height = dimy; + is_resized = false; + XUnlockDisplay(cimg::X11attr().display); + if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); + if (redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool redraw=true) { + if (is_empty()) return *this; + if (redraw) { + const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); + void *odata = cimg_std::malloc(bufsize); + cimg_std::memcpy(odata,data,bufsize); + assign(width,height,title,normalization,!is_fullscreen,false); + cimg_std::memcpy(data,odata,bufsize); + cimg_std::free(odata); + return paint(false); + } + return assign(width,height,title,normalization,!is_fullscreen,false); + } + + CImgDisplay& show() { + if (!is_empty() && is_closed) { + XLockDisplay(cimg::X11attr().display); + if (is_fullscreen) _init_fullscreen(); + _map_window(); + is_closed = false; + XUnlockDisplay(cimg::X11attr().display); + return paint(); + } + return *this; + } + + CImgDisplay& close() { + if (!is_empty() && !is_closed) { + XLockDisplay(cimg::X11attr().display); + if (is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(cimg::X11attr().display,window); + window_x = window_y = -1; + is_closed = true; + XUnlockDisplay(cimg::X11attr().display); + } + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + show(); + XLockDisplay(cimg::X11attr().display); + XMoveWindow(cimg::X11attr().display,window,posx,posy); + window_x = posx; window_y = posy; + is_moved = false; + XUnlockDisplay(cimg::X11attr().display); + return paint(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + XLockDisplay(cimg::X11attr().display); + XDefineCursor(cimg::X11attr().display,window,None); + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + XLockDisplay(cimg::X11attr().display); + const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(cimg::X11attr().display,window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(cimg::X11attr().display,pix,pix,&col,&col,0,0); + XFreePixmap(cimg::X11attr().display,pix); + XDefineCursor(cimg::X11attr().display,window,cur); + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || is_closed) return *this; + XLockDisplay(cimg::X11attr().display); + XWarpPointer(cimg::X11attr().display,None,window,0,0,0,0,posx,posy); + mouse_x = posx; mouse_y = posy; + is_moved = false; + XSync(cimg::X11attr().display, False); + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + CImgDisplay& set_title(const char *format, ...) { + if (is_empty()) return *this; + char tmp[1024] = {0}; + va_list ap; + va_start(ap, format); + cimg_std::vsprintf(tmp,format,ap); + va_end(ap); + if (title) delete[] title; + const int s = cimg::strlen(tmp)+1; + title = new char[s]; + cimg_std::memcpy(title,tmp,s*sizeof(char)); + XLockDisplay(cimg::X11attr().display); + XStoreName(cimg::X11attr().display,window,tmp); + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::display() : Cannot display empty image."); + if (is_empty()) assign(img.width,img.height); + return render(img).paint(false); + } + + CImgDisplay& paint(const bool wait_expose=true) { + if (is_empty()) return *this; + XLockDisplay(cimg::X11attr().display); + _paint(wait_expose); + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + template + CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (is_empty()) return *this; + if (!img) + throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + if (cimg::X11attr().nb_bits==8 && (img.width!=width || img.height!=height)) return render(img.get_resize(width,height,1,-100,1)); + if (cimg::X11attr().nb_bits==8 && !flag8 && img.dim==3) return render(img.get_RGBtoLUT(true),true); + + const T + *data1 = img.data, + *data2 = (img.dim>1)?img.ptr(0,0,0,1):data1, + *data3 = (img.dim>2)?img.ptr(0,0,0,2):data1; + + if (cimg::X11attr().blue_first) cimg::swap(data1,data3); + XLockDisplay(cimg::X11attr().display); + + if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { + min = max = 0; + switch (cimg::X11attr().nb_bits) { + case 8 : { // 256 color palette, no normalization + _set_colormap(colormap,img.dim); + unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1 : for (unsigned int xy = img.width*img.height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++); + break; + case 2 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++); + (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } + } break; + case 16 : { // 16 bits colors, no normalization + unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + *(ptrd++) = (val&M) | (G>>3); + *(ptrd++) = (G<<5) | (G>>1); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + *(ptrd++) = (G<<5) | (G>>1); + *(ptrd++) = (val&M) | (G>>3); + } + break; + case 2 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); + *(ptrd++) = (G<<5); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + *(ptrd++) = (G<<5); + *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); + } + break; + default : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); + *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); + *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } + } break; + default : { // 24 bits colors, no normalization + unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++)<<8; + *(ptrd++) = (val<<16) | (val<<8) | val; + } + break; + case 2 : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + break; + default : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + *(ptrd++) = 0; + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + break; + case 2 : + if (cimg::X11attr().byte_order) cimg::swap(data1,data2); + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + break; + default : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(ptrd++) = 0; + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data3++); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(ptrd++) = (unsigned char)*(data3++); + *(ptrd++) = (unsigned char)*(data2++); + *(ptrd++) = (unsigned char)*(data1++); + *(ptrd++) = 0; + } + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned int*)data,width,height); delete[] ndata; } + } + }; + } else { + if (normalization==3) { + if (cimg::type::is_float()) min = (float)img.minmax(max); + else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } + } else if ((min>max) || normalization==1) min = (float)img.minmax(max); + const float delta = max-min, mm = delta?delta:1.0f; + switch (cimg::X11attr().nb_bits) { + case 8 : { // 256 color palette, with normalization + _set_colormap(colormap,img.dim); + unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm); + *(ptrd++) = R; + } break; + case 2 : for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm), + B = (unsigned char)(255*(*(data3++)-min)/mm); + *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } + } break; + case 16 : { // 16 bits colors, with normalization + unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm), G = val>>2; + *(ptrd++) = (val&M) | (G>>3); + *(ptrd++) = (G<<5) | (val>>3); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm), G = val>>2; + *(ptrd++) = (G<<5) | (val>>3); + *(ptrd++) = (val&M) | (G>>3); + } + break; + case 2 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; + *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); + *(ptrd++) = (G<<5); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; + *(ptrd++) = (G<<5); + *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); + } + break; + default : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; + *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); + *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char G = (unsigned char)(255*(*(data2++)-min)/mm)>>2; + *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); + *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } + } break; + default : { // 24 bits colors, with normalization + unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); + *(ptrd++) = (val<<24) | (val<<16) | (val<<8); + } + break; + case 2 : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)(255*(*(data1++)-min)/mm)<<16) | + ((unsigned char)(255*(*(data2++)-min)/mm)<<8); + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)(255*(*(data2++)-min)/mm)<<16) | + ((unsigned char)(255*(*(data1++)-min)/mm)<<8); + break; + default : + if (cimg::X11attr().byte_order==cimg::endianness()) + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)(255*(*(data1++)-min)/mm)<<16) | + ((unsigned char)(255*(*(data2++)-min)/mm)<<8) | + (unsigned char)(255*(*(data3++)-min)/mm); + else + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)(255*(*(data3++)-min)/mm)<<24) | + ((unsigned char)(255*(*(data2++)-min)/mm)<<16) | + ((unsigned char)(255*(*(data1++)-min)/mm)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img.dim) { + case 1 : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); + (*ptrd++) = 0; + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = val; + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = val; + (*ptrd++) = 0; + } + break; + case 2 : + if (cimg::X11attr().byte_order) cimg::swap(data1,data2); + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + (*ptrd++) = 0; + (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); + (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); + (*ptrd++) = 0; + } + break; + default : + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + (*ptrd++) = 0; + (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); + (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); + (*ptrd++) = (unsigned char)(255*(*(data3++)-min)/mm); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + (*ptrd++) = (unsigned char)(255*(*(data3++)-min)/mm); + (*ptrd++) = (unsigned char)(255*(*(data2++)-min)/mm); + (*ptrd++) = (unsigned char)(255*(*(data1++)-min)/mm); + (*ptrd++) = 0; + } + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned int*)data,width,height); delete[] ndata; } + } + } + } + XUnlockDisplay(cimg::X11attr().display); + return *this; + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) img.assign(); + else { + img.assign(width,height,1,3); + T + *data1 = img.ptr(0,0,0,0), + *data2 = img.ptr(0,0,0,1), + *data3 = img.ptr(0,0,0,2); + if (cimg::X11attr().blue_first) cimg::swap(data1,data3); + switch (cimg::X11attr().nb_bits) { + case 8 : { + unsigned char *ptrs = (unsigned char*)data; + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = *(ptrs++); + *(data1++) = val&0xe0; + *(data2++) = (val&0x1c)<<3; + *(data3++) = val<<6; + } + } break; + case 16 : { + unsigned char *ptrs = (unsigned char*)data; + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val0 = *(ptrs++), val1 = *(ptrs++); + *(data1++) = val0&0xf8; + *(data2++) = (val0<<5) | ((val1&0xe0)>>5); + *(data3++) = val1<<3; + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned short val0 = *(ptrs++), val1 = *(ptrs++); + *(data1++) = val1&0xf8; + *(data2++) = (val1<<5) | ((val0&0xe0)>>5); + *(data3++) = val0<<3; + } + } break; + default : { + unsigned char *ptrs = (unsigned char*)data; + if (cimg::X11attr().byte_order) for (unsigned int xy = img.width*img.height; xy>0; --xy) { + ++ptrs; + *(data1++) = *(ptrs++); + *(data2++) = *(ptrs++); + *(data3++) = *(ptrs++); + } else for (unsigned int xy = img.width*img.height; xy>0; --xy) { + *(data3++) = *(ptrs++); + *(data2++) = *(ptrs++); + *(data1++) = *(ptrs++); + ++ptrs; + } + } + } + } + return *this; + } + + // Windows-based display + //----------------------- +#elif cimg_display==2 + CLIENTCREATESTRUCT ccs; + BITMAPINFO bmi; + unsigned int *data; + DEVMODE curr_mode; + HWND window; + HWND background_window; + HDC hdc; + HANDLE thread; + HANDLE created; + HANDLE mutex; + bool mouse_tracking; + bool visible_cursor; + + static int screen_dimx() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsWidth; + } + + static int screen_dimy() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return mode.dmPelsHeight; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32attr().wait_event,INFINITE); + } + + static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay* disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay* disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); +#endif + MSG st_msg; + + switch (msg) { + case WM_CLOSE : + disp->mouse_x = disp->mouse_y = -1; + disp->window_x = disp->window_y = 0; + if (disp->button) { + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button = 0; + } + if (disp->key) { + cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); + disp->key = 0; + } + if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } + disp->is_closed = true; + ReleaseMutex(disp->mutex); + ShowWindow(disp->window,SW_HIDE); + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + return 0; + case WM_SIZE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->mutex,INFINITE); + const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); + if (nw && nh && (nw!=disp->width || nh!=disp->height)) { + disp->window_width = nw; + disp->window_height = nh; + disp->mouse_x = disp->mouse_y = -1; + disp->is_resized = disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } + ReleaseMutex(disp->mutex); + } break; + case WM_MOVE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->mutex,INFINITE); + const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); + if (nx!=disp->window_x || ny!=disp->window_y) { + disp->window_x = nx; + disp->window_y = ny; + disp->is_moved = disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } + ReleaseMutex(disp->mutex); + } break; + case WM_PAINT : + disp->paint(); + break; + case WM_KEYDOWN : + disp->update_iskey((unsigned int)wParam,true); + if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); + disp->key = (unsigned int)wParam; + if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MOUSEMOVE : { + while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} + disp->mouse_x = LOWORD(lParam); + disp->mouse_y = HIWORD(lParam); +#if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) + if (!disp->mouse_tracking) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = disp->window; + if (TrackMouseEvent(&tme)) disp->mouse_tracking = true; + } +#endif + if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) + disp->mouse_x = disp->mouse_y = -1; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + } break; + case WM_MOUSELEAVE : { + disp->mouse_x = disp->mouse_y = -1; + disp->mouse_tracking = false; + } break; + case WM_LBUTTONDOWN : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button|=1U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_RBUTTONDOWN : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button|=2U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MBUTTONDOWN : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button|=4U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case 0x020A : // WM_MOUSEWHEEL: + disp->wheel+=(int)((short)HIWORD(wParam))/120; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + case WM_KEYUP : + disp->update_iskey((unsigned int)wParam,false); + if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } + if (disp->released_key) cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); + disp->released_key = (unsigned int)wParam; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_LBUTTONUP : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button&=~1U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_RBUTTONUP : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button&=~2U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_MBUTTONUP : + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button&=~4U; + disp->is_event = true; + SetEvent(cimg::Win32attr().wait_event); + break; + case WM_SETCURSOR : + if (disp->visible_cursor) ShowCursor(TRUE); + else ShowCursor(FALSE); + break; + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]); + const char *title = (const char*)(((void**)arg)[1]); + MSG msg; + delete[] (void**)arg; + disp->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + disp->bmi.bmiHeader.biWidth = disp->width; + disp->bmi.bmiHeader.biHeight = -(int)disp->height; + disp->bmi.bmiHeader.biPlanes = 1; + disp->bmi.bmiHeader.biBitCount = 32; + disp->bmi.bmiHeader.biCompression = BI_RGB; + disp->bmi.bmiHeader.biSizeImage = 0; + disp->bmi.bmiHeader.biXPelsPerMeter = 1; + disp->bmi.bmiHeader.biYPelsPerMeter = 1; + disp->bmi.bmiHeader.biClrUsed = 0; + disp->bmi.bmiHeader.biClrImportant = 0; + disp->data = new unsigned int[disp->width*disp->height]; + if (!disp->is_fullscreen) { // Normal window + RECT rect; + rect.left = rect.top = 0; rect.right = disp->width-1; rect.bottom = disp->height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-disp->width)/2, border2 = rect.bottom-rect.top+1-disp->height-border1; + disp->window = CreateWindowA("MDICLIENT",title?title:" ", + WS_OVERLAPPEDWINDOW | (disp->is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT, + disp->width + 2*border1, disp->height + border1 + border2, + 0,0,0,&(disp->ccs)); + if (!disp->is_closed) { + GetWindowRect(disp->window,&rect); + disp->window_x = rect.left + border1; + disp->window_y = rect.top + border2; + } else disp->window_x = disp->window_y = 0; + } else { // Fullscreen window + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + disp->window = CreateWindowA("MDICLIENT",title?title:" ", + WS_POPUP | (disp->is_closed?0:WS_VISIBLE), (sx-disp->width)/2, (sy-disp->height)/2, + disp->width,disp->height,0,0,0,&(disp->ccs)); + disp->window_x = disp->window_y = 0; + } + SetForegroundWindow(disp->window); + disp->hdc = GetDC(disp->window); + disp->window_width = disp->width; + disp->window_height = disp->height; + disp->flush(); +#ifdef _WIN64 + SetWindowLongPtr(disp->window,GWLP_USERDATA,(LONG_PTR)disp); + SetWindowLongPtr(disp->window,GWLP_WNDPROC,(LONG_PTR)_handle_events); +#else + SetWindowLong(disp->window,GWL_USERDATA,(LONG)disp); + SetWindowLong(disp->window,GWL_WNDPROC,(LONG)_handle_events); +#endif + SetEvent(disp->created); + while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); + return 0; + } + + CImgDisplay& _update_window_pos() { + if (!is_closed) { + RECT rect; + rect.left = rect.top = 0; rect.right = width-1; rect.bottom = height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; + GetWindowRect(window,&rect); + window_x = rect.left + border1; + window_y = rect.top + border2; + } else window_x = window_y = -1; + return *this; + } + + void _init_fullscreen() { + background_window = 0; + if (is_fullscreen && !is_closed) { + DEVMODE mode; + unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; + for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { + const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; + if (nw>=width && nh>=height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { + bestbpp = mode.dmBitsPerPel; + ibest = imode; + bw = nw; bh = nh; + } + } + if (bestbpp) { + curr_mode.dmSize = sizeof(DEVMODE); curr_mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&curr_mode); + EnumDisplaySettings(0,ibest,&mode); + ChangeDisplaySettings(&mode,0); + } else curr_mode.dmSize = 0; + + const unsigned int sx = screen_dimx(), sy = screen_dimy(); + if (sx!=width || sy!=height) { + CLIENTCREATESTRUCT background_ccs; + background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); + SetForegroundWindow(background_window); + } + } else curr_mode.dmSize = 0; + } + + void _desinit_fullscreen() { + if (is_fullscreen) { + if (background_window) DestroyWindow(background_window); + background_window = 0; + if (curr_mode.dmSize) ChangeDisplaySettings(&curr_mode,0); + is_fullscreen = false; + } + } + + CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const int s = cimg::strlen(ptitle)+1; + char *tmp_title = s?new char[s]:0; + if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + width = cimg::min(dimw,(unsigned int)screen_dimx()); + height = cimg::min(dimh,(unsigned int)screen_dimy()); + normalization = normalization_type<4?normalization_type:3; + is_fullscreen = fullscreen_flag; + window_x = window_y = 0; + is_closed = closed_flag; + visible_cursor = true; + mouse_tracking = false; + title = tmp_title; + flush(); + if (is_fullscreen) _init_fullscreen(); + + // Create event thread + void *arg = (void*)(new void*[2]); + ((void**)arg)[0]=(void*)this; + ((void**)arg)[1]=(void*)title; + unsigned long ThreadID = 0; + mutex = CreateMutex(0,FALSE,0); + created = CreateEvent(0,FALSE,FALSE,0); + thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); + WaitForSingleObject(created,INFINITE); + return *this; + } + + CImgDisplay& assign() { + if (is_empty()) return *this; + DestroyWindow(window); + TerminateThread(thread,0); + if (data) delete[] data; + if (title) delete[] title; + if (is_fullscreen) _desinit_fullscreen(); + width = height = normalization = window_width = window_height = 0; + window_x = window_y = 0; + is_fullscreen = false; + is_closed = true; + min = max = 0; + title = 0; + flush(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + min = max = 0; + cimg_std::memset(data,0,sizeof(unsigned int)*width*height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list.get_append('x','p'), + &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& win) { + if (!win) return assign(); + _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); + cimg_std::memcpy(data,win.data,sizeof(unsigned int)*width*height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + const unsigned int + tmpdimx=(nwidth>0)?nwidth:(-nwidth*width/100), + tmpdimy=(nheight>0)?nheight:(-nheight*height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (window_width!=dimx || window_height!=dimy) { + RECT rect; rect.left = rect.top = 0; rect.right = dimx-1; rect.bottom = dimy-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right-rect.left+1, cheight = rect.bottom-rect.top+1; + SetWindowPos(window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + } + if (width!=dimx || height!=dimy) { + unsigned int *ndata = new unsigned int[dimx*dimy]; + if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); + else cimg_std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] data; + data = ndata; + bmi.bmiHeader.biWidth = dimx; + bmi.bmiHeader.biHeight = -(int)dimy; + width = dimx; + height = dimy; + } + window_width = dimx; window_height = dimy; + is_resized = false; + if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); + if (redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool redraw=true) { + if (is_empty()) return *this; + if (redraw) { + const unsigned int bufsize = width*height*4; + void *odata = cimg_std::malloc(bufsize); + cimg_std::memcpy(odata,data,bufsize); + assign(width,height,title,normalization,!is_fullscreen,false); + cimg_std::memcpy(data,odata,bufsize); + cimg_std::free(odata); + return paint(); + } + return assign(width,height,title,normalization,!is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty()) return *this; + if (is_closed) { + is_closed = false; + if (is_fullscreen) _init_fullscreen(); + ShowWindow(window,SW_SHOW); + _update_window_pos(); + } + return paint(); + } + + CImgDisplay& close() { + if (is_empty()) return *this; + if (!is_closed && !is_fullscreen) { + if (is_fullscreen) _desinit_fullscreen(); + ShowWindow(window,SW_HIDE); + is_closed = true; + window_x = window_y = 0; + } + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + if (!is_fullscreen) { + RECT rect; rect.left = rect.top = 0; rect.right=window_width-1; rect.bottom=window_height-1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; + SetWindowPos(window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER); + } else SetWindowPos(window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + window_x = posx; + window_y = posy; + is_moved = false; + return show(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + visible_cursor = true; + ShowCursor(TRUE); + SendMessage(window,WM_SETCURSOR,0,0); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + visible_cursor = false; + ShowCursor(FALSE); + SendMessage(window,WM_SETCURSOR,0,0); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (!is_closed && posx>=0 && posy>=0) { + _update_window_pos(); + const int res = (int)SetCursorPos(window_x+posx,window_y+posy); + if (res) { mouse_x = posx; mouse_y = posy; } + } + return *this; + } + + CImgDisplay& set_title(const char *format, ...) { + if (is_empty()) return *this; + char tmp[1024] = {0}; + va_list ap; + va_start(ap, format); + cimg_std::vsprintf(tmp,format,ap); + va_end(ap); + if (title) delete[] title; + const int s = cimg::strlen(tmp)+1; + title = new char[s]; + cimg_std::memcpy(title,tmp,s*sizeof(char)); + SetWindowTextA(window, tmp); + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (img.is_empty()) + throw CImgArgumentException("CImgDisplay::display() : Cannot display empty image."); + if (is_empty()) assign(img.width,img.height); + return render(img).paint(); + } + + CImgDisplay& paint() { + if (!is_closed) { + WaitForSingleObject(mutex,INFINITE); + SetDIBitsToDevice(hdc,0,0,width,height,0,0,0,height,data,&bmi,DIB_RGB_COLORS); + ReleaseMutex(mutex); + } + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (is_empty()) return *this; + if (!img) + throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + + const T + *data1 = img.data, + *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, + *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; + + WaitForSingleObject(mutex,INFINITE); + unsigned int + *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], + *ptrd = ndata; + + if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { + min = max = 0; + switch (img.dim) { + case 1 : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + }} break; + case 2 : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + } break; + default : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + } + } + } else { + if (normalization==3) { + if (cimg::type::is_float()) min = (float)img.minmax(max); + else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } + } else if ((min>max) || normalization==1) min = (float)img.minmax(max); + const float delta = max-min, mm = delta?delta:1.0f; + switch (img.dim) { + case 1 : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char val = (unsigned char)(255*(*(data1++)-min)/mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + }} break; + case 2 : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm); + *(ptrd++) = (R<<16) | (G<<8); + }} break; + default : { + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm), + B = (unsigned char)(255*(*(data3++)-min)/mm); + *(ptrd++) = (R<<16) | (G<<8) | B; + }} + } + } + if (ndata!=data) { _render_resize(ndata,img.width,img.height,data,width,height); delete[] ndata; } + ReleaseMutex(mutex); + return *this; + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) img.assign(); + else { + img.assign(width,height,1,3); + T + *data1 = img.ptr(0,0,0,0), + *data2 = img.ptr(0,0,0,1), + *data3 = img.ptr(0,0,0,2); + unsigned int *ptrs = data; + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (unsigned char)(val>>16); + *(data2++) = (unsigned char)((val>>8)&0xFF); + *(data3++) = (unsigned char)(val&0xFF); + } + } + return *this; + } + + // MacOSX - Carbon-based display + //------------------------------- + // (Code by Adrien Reboisson && Romain Blei, supervised by Jean-Marie Favreau) + // +#elif cimg_display==3 + unsigned int *data; // The bits of the picture + WindowRef carbonWindow; // The opaque carbon window struct associated with the display + MPCriticalRegionID paintCriticalRegion; // Critical section used when drawing + CGColorSpaceRef csr; // Needed for painting + CGDataProviderRef dataProvider; // Needed for painting + CGImageRef imageRef; // The image + UInt32 lastKeyModifiers; // Buffer storing modifiers state + + // Define the kind of the queries which can be serialized using the event thread. + typedef enum { + COM_CREATEWINDOW = 0, // Create window query + COM_RELEASEWINDOW, // Release window query + COM_SHOWWINDOW, // Show window query + COM_HIDEWINDOW, // Hide window query + COM_SHOWMOUSE, // Show mouse query + COM_HIDEMOUSE, // Hide mouse query + COM_RESIZEWINDOW, // Resize window query + COM_MOVEWINDOW, // Move window query + COM_SETTITLE, // Set window title query + COM_SETMOUSEPOS // Set cursor position query + } CImgCarbonQueryKind; + + // The query destructor send to the event thread. + struct CbSerializedQuery { + CImgDisplay* sender; // Query's sender + CImgCarbonQueryKind kind; // The kind of the query sent to the background thread + short x, y; // X:Y values for move/resize operations + char *c; // Char values for window title + bool createFullScreenWindow; // Boolean value used for full-screen window creation + bool createClosedWindow; // Boolean value used for closed-window creation + bool update; // Boolean value used for resize + bool success; // Succes or failure of the message, used as return value + CbSerializedQuery(CImgDisplay *s, CImgCarbonQueryKind k):sender(s),kind(k),success(false) {}; + + inline static CbSerializedQuery BuildReleaseWindowQuery(CImgDisplay* sender) { + return CbSerializedQuery(sender, COM_RELEASEWINDOW); + } + inline static CbSerializedQuery BuildCreateWindowQuery(CImgDisplay* sender, const bool fullscreen, const bool closed) { + CbSerializedQuery q(sender, COM_CREATEWINDOW); + q.createFullScreenWindow = fullscreen; + q.createClosedWindow = closed; + return q; + } + inline static CbSerializedQuery BuildShowWindowQuery(CImgDisplay* sender) { + return CbSerializedQuery(sender, COM_SHOWWINDOW); + } + inline static CbSerializedQuery BuildHideWindowQuery(CImgDisplay* sender) { + return CbSerializedQuery(sender, COM_HIDEWINDOW); + } + inline static CbSerializedQuery BuildShowMouseQuery(CImgDisplay* sender) { + return CbSerializedQuery(sender, COM_SHOWMOUSE); + } + inline static CbSerializedQuery BuildHideMouseQuery(CImgDisplay* sender) { + return CbSerializedQuery(sender, COM_HIDEMOUSE); + } + inline static CbSerializedQuery BuildResizeWindowQuery(CImgDisplay* sender, const int x, const int y, bool update) { + CbSerializedQuery q(sender, COM_RESIZEWINDOW); + q.x = x, q.y = y; + q.update = update; + return q; + } + inline static CbSerializedQuery BuildMoveWindowQuery(CImgDisplay* sender, const int x, const int y) { + CbSerializedQuery q(sender, COM_MOVEWINDOW); + q.x = x, q.y = y; + return q; + } + inline static CbSerializedQuery BuildSetWindowTitleQuery(CImgDisplay* sender, char* c) { + CbSerializedQuery q(sender, COM_SETTITLE); + q.c = c; + return q; + } + inline static CbSerializedQuery BuildSetWindowPosQuery(CImgDisplay* sender, const int x, const int y) { + CbSerializedQuery q(sender, COM_SETMOUSEPOS); + q.x = x, q.y = y; + return q; + } + }; + + // Send a serialized query in a synchroneous way. + // @param c Application Carbon global settings. + // @param m The query to send. + // @result Success/failure of the operation returned by the event thread. + bool _CbSendMsg(cimg::CarbonInfo& c, CbSerializedQuery m) { + MPNotifyQueue(c.com_queue,&m,0,0); // Send the given message + MPWaitOnSemaphore(c.sync_event,kDurationForever); // Wait end of processing notification + return m.success; + } + + // Free the window attached to the current display. + // @param c Application Carbon global settings. + // @result Success/failure of the operation. + bool _CbFreeAttachedWindow(cimg::CarbonInfo& c) { + if (!_CbSendMsg(c, CbSerializedQuery::BuildReleaseWindowQuery(this))) // Ask the main thread to free the given window + throw CImgDisplayException("Cannot release window associated with the current display."); + // If a window existed, ask to release it + MPEnterCriticalRegion(c.windowListCR,kDurationForever); // Lock the list of the windows + --c.windowCount; //Decrement the window count + MPExitCriticalRegion(c.windowListCR); // Unlock the list + return c.windowCount == 0; + } + + // Create the window attached to the current display. + // @param c Application Carbon global settings. + // @param title The window title, if any. + // @param fullscreen Shoud we start in fullscreen mode ? + // @param create_closed If true, the window is created but not displayed. + // @result Success/failure of the operation. + void _CbCreateAttachedWindow(cimg::CarbonInfo& c, const char* title, const bool fullscreen, const bool create_closed) { + if (!_CbSendMsg(c,CbSerializedQuery::BuildCreateWindowQuery(this,fullscreen,create_closed))) // Ask the main thread to create the window + throw CImgDisplayException("Cannot create the window associated with the current display."); + if (title) set_title(title); // Set the title, if any + // Now we can register the window + MPEnterCriticalRegion(c.windowListCR,kDurationForever); // Lock the list of the windows + ++c.windowCount; //Increment the window count + MPExitCriticalRegion(c.windowListCR); // Unlock the list + } + + // Destroy graphic objects previously allocated. We free the image, the data provider, then the colorspace. + void _CbFinalizeGraphics() { + CGImageRelease (imageRef); // Release the picture + CGDataProviderRelease(dataProvider); // Release the DP + CGColorSpaceRelease(csr); // Free the cs + } + + // Create graphic objects associated to a display. We have to create a colormap, a data provider, and the image. + void _CbInitializeGraphics() { + csr = CGColorSpaceCreateDeviceRGB(); // Create the color space first + if (!csr) + throw CImgDisplayException("CGColorSpaceCreateDeviceRGB() failed."); + // Create the DP + dataProvider = CGDataProviderCreateWithData(0,data,height*width*sizeof(unsigned int),0); + if (!dataProvider) + throw CImgDisplayException("CGDataProviderCreateWithData() failed."); + // ... and finally the image. + if (cimg::endianness()) + imageRef = CGImageCreate(width,height,8,32,width*sizeof(unsigned int),csr, + kCGImageAlphaNoneSkipFirst,dataProvider,0,false,kCGRenderingIntentDefault); + else + imageRef = CGImageCreate(width,height,8,32,width*sizeof(unsigned int),csr, + kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host,dataProvider,0,false,kCGRenderingIntentDefault); + if (!imageRef) + throw CImgDisplayException("CGImageCreate() failed."); + } + + // Reinit graphic objects. Free them, then reallocate all. + // This is used when image bounds are changed or when data source get invalid. + void _CbReinitGraphics() { + MPEnterCriticalRegion(paintCriticalRegion, kDurationForever); + _CbFinalizeGraphics(); + _CbInitializeGraphics(); + MPExitCriticalRegion(paintCriticalRegion); + } + + // Convert a point having global coordonates into the window coordonates. + // We use this function to replace the deprecated GlobalToLocal QuickDraw API. + // @param mouseEvent The mouse event which triggered the event handler. + // @param window The window where the event occured. + // @param point The modified point struct. + // @result True if the point struct has been converted successfully. + static bool _CbToLocalPointFromMouseEvent(EventRef mouseEvent, WindowRef window, HIPoint* point) { + Rect bounds; + if (GetWindowBounds(window,kWindowStructureRgn,&bounds)==noErr) { + point->x -= bounds.left; + point->y -= bounds.top; + HIViewRef view = NULL; + if (HIViewGetViewForMouseEvent(HIViewGetRoot(window),mouseEvent,&view)==noErr) + return HIViewConvertPoint(point, NULL, view) == noErr; + } + return false; + } + + static int screen_dimx() { + return CGDisplayPixelsWide(kCGDirectMainDisplay); + } + + static int screen_dimy() { + return CGDisplayPixelsHigh(kCGDirectMainDisplay); + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + min = max = 0; + cimg_std::memset(data,0,sizeof(unsigned int)*width*height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list.get_append('x','p'), + &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + _assign(nimg.width,nimg.height,title,normalization_type,fullscreen_flag,closed_flag); + if (normalization==2) min = (float)nimg.minmax(max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay &win) { + if (!win) return assign(); + _assign(win.width,win.height,win.title,win.normalization,win.is_fullscreen,win.is_closed); + cimg_std::memcpy(data,win.data,sizeof(unsigned int)*width*height); + return paint(); + } + + template + CImgDisplay& display(const CImg& img) { + if (is_empty()) assign(img.width,img.height); + return render(img).paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + cimg::CarbonInfo& c = cimg::CarbonAttr(); + + if ((window_width!=dimx || window_height!=dimy) && + !_CbSendMsg(c,CbSerializedQuery::BuildResizeWindowQuery(this,dimx,dimy,redraw))) + throw CImgDisplayException("CImgDisplay::resize() : Cannot resize the window associated to the current display."); + + if (width!=dimx || height!=dimy) { + unsigned int *ndata = new unsigned int[dimx*dimy]; + if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); + else cimg_std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + unsigned int const* old_data = data; + data = ndata; + delete[] old_data; + _CbReinitGraphics(); + } + window_width = width = dimx; window_height = height = dimy; + is_resized = false; + if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); + if (redraw) return paint(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + if (!is_fullscreen) { + // If the operation succeeds, window_x and window_y are updated by the event thread + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Send the query + if (!_CbSendMsg(c,CbSerializedQuery::BuildMoveWindowQuery(this,posx,posy))) + throw CImgDisplayException("CImgDisplay::move() : Cannot move the window associated to the current display."); + } + return show(); + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (!is_closed && posx>=0 && posy>=0) { + // If the operation succeeds, mouse_x and mouse_y are updated by the event thread + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Send the query + if (!_CbSendMsg(c,CbSerializedQuery::BuildSetWindowPosQuery(this,posx,posy))) + throw CImgDisplayException("CImgDisplay::set_mouse() : Cannot set the mouse position to the current display."); + } + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Send the query + if (!_CbSendMsg(c,CbSerializedQuery::BuildHideMouseQuery(this))) + throw CImgDisplayException("CImgDisplay::hide_mouse() : Cannot hide the mouse associated to the current display."); + return *this; + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Send the query + if (!_CbSendMsg(c,CbSerializedQuery::BuildShowMouseQuery(this))) + throw CImgDisplayException("CImgDisplay::show_mouse() : Cannot show the mouse associated to the current display."); + return *this; + } + + static void wait_all() { + cimg::CarbonInfo& c = cimg::CarbonAttr(); + MPWaitOnSemaphore(c.wait_event,kDurationForever); + } + + CImgDisplay& show() { + if (is_empty()) return *this; + if (is_closed) { + cimg::CarbonInfo& c = cimg::CarbonAttr(); + if (!_CbSendMsg(c,CbSerializedQuery::BuildShowWindowQuery(this))) + throw CImgDisplayException("CImgDisplay::show() : Cannot show the window associated to the current display."); + } + return paint(); + } + + CImgDisplay& close() { + if (is_empty()) return *this; + if (!is_closed && !is_fullscreen) { + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // If the operation succeeds, window_x and window_y are updated on the event thread + if (!_CbSendMsg(c,CbSerializedQuery::BuildHideWindowQuery(this))) + throw CImgDisplayException("CImgDisplay::close() : Cannot hide the window associated to the current display."); + } + return *this; + } + + CImgDisplay& set_title(const char *format, ...) { + if (is_empty()) return *this; + char tmp[1024] = {0}; + va_list ap; + va_start(ap, format); + cimg_std::vsprintf(tmp,format,ap); + va_end(ap); + if (title) delete[] title; + const int s = cimg::strlen(tmp)+1; + title = new char[s]; + cimg_std::memcpy(title,tmp,s*sizeof(char)); + cimg::CarbonInfo& c = cimg::CarbonAttr(); + if (!_CbSendMsg(c,CbSerializedQuery::BuildSetWindowTitleQuery(this,tmp))) + throw CImgDisplayException("CImgDisplay::set_title() : Cannot set the window title associated to the current display."); + return *this; + } + + CImgDisplay& paint() { + if (!is_closed) { + MPEnterCriticalRegion(paintCriticalRegion,kDurationForever); + CGrafPtr portPtr = GetWindowPort(carbonWindow); + CGContextRef currentContext = 0; + TQDBeginCGContext(portPtr,¤tContext); + CGContextSetRGBFillColor(currentContext,255,255,255,255); + CGContextFillRect(currentContext,CGRectMake(0,0,window_width,window_height)); + CGContextDrawImage(currentContext,CGRectMake(0,int(window_height-height)<0?0:window_height-height,width,height),imageRef); + CGContextFlush(currentContext); + TQDEndCGContext(portPtr, ¤tContext); + MPExitCriticalRegion(paintCriticalRegion); + } + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (is_empty()) return *this; + if (!img) + throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", + img.width,img.height,img.depth,img.dim,img.data); + if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); + const T + *data1 = img.data, + *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, + *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; + MPEnterCriticalRegion(paintCriticalRegion, kDurationForever); + unsigned int + *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], + *ptrd = ndata; + if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { + min = max = 0; + for (unsigned int xy = img.width*img.height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); + } else { + if (normalization==3) { + if (cimg::type::is_float()) min = (float)img.minmax(max); + else { + min = (float)cimg::type::min(); + max = (float)cimg::type::max(); + } + } else if ((min>max) || normalization==1) min = (float)img.minmax(max); + const float delta = max-min, mm = delta?delta:1.0f; + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned char + R = (unsigned char)(255*(*(data1++)-min)/mm), + G = (unsigned char)(255*(*(data2++)-min)/mm), + B = (unsigned char)(255*(*(data3++)-min)/mm); + *(ptrd++) = (R<<16) | (G<<8) | (B); + } + } + if (ndata!=data) { + _render_resize(ndata,img.width,img.height,data,width,height); + delete[] ndata; + } + MPExitCriticalRegion(paintCriticalRegion); + return *this; + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) img.assign(); + else { + img.assign(width,height,1,3); + T + *data1 = img.ptr(0,0,0,0), + *data2 = img.ptr(0,0,0,1), + *data3 = img.ptr(0,0,0,2); + unsigned int *ptrs = data; + for (unsigned int xy = img.width*img.height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (unsigned char)(val>>16); + *(data2++) = (unsigned char)((val>>8)&0xFF); + *(data3++) = (unsigned char)(val&0xFF); + } + } + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool redraw=true) { + if (is_empty()) return *this; + if (redraw) { + const unsigned int bufsize = width*height*4; + void *odata = cimg_std::malloc(bufsize); + cimg_std::memcpy(odata,data,bufsize); + assign(width,height,title,normalization,!is_fullscreen,false); + cimg_std::memcpy(data,odata,bufsize); + cimg_std::free(odata); + return paint(); + } + return assign(width,height,title,normalization,!is_fullscreen,false); + } + + static OSStatus CarbonEventHandler(EventHandlerCallRef myHandler, EventRef theEvent, void* userData) { + OSStatus result = eventNotHandledErr; + CImgDisplay* disp = (CImgDisplay*) userData; + (void)myHandler; // Avoid "unused parameter" + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Gets the associated display + if (disp) { + // Window events are always handled + if (GetEventClass(theEvent)==kEventClassWindow) switch (GetEventKind (theEvent)) { + case kEventWindowClose : + disp->mouse_x = disp->mouse_y = -1; + disp->window_x = disp->window_y = 0; + if (disp->button) { + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + disp->button = 0; + } + if (disp->key) { + cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); + disp->key = 0; + } + if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } + disp->is_closed = true; + HideWindow(disp->carbonWindow); + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + result = noErr; + break; + // There is a lot of case where we have to redraw our window + case kEventWindowBoundsChanging : + case kEventWindowResizeStarted : + case kEventWindowCollapsed : //Not sure it's really needed :-) + break; + case kEventWindowZoomed : + case kEventWindowExpanded : + case kEventWindowResizeCompleted : { + MPEnterCriticalRegion(disp->paintCriticalRegion, kDurationForever); + // Now we retrieve the new size of the window + Rect newContentRect; + GetWindowBounds(disp->carbonWindow,kWindowContentRgn,&newContentRect); + const unsigned int + nw = (unsigned int)(newContentRect.right - newContentRect.left), + nh = (unsigned int)(newContentRect.bottom - newContentRect.top); + + // Then we update CImg internal settings + if (nw && nh && (nw!=disp->width || nh!=disp->height)) { + disp->window_width = nw; + disp->window_height = nh; + disp->mouse_x = disp->mouse_y = -1; + disp->is_resized = true; + } + disp->is_event = true; + MPExitCriticalRegion(disp->paintCriticalRegion); + disp->paint(); // Coords changed, must update the screen + MPSignalSemaphore(c.wait_event); + result = noErr; + } break; + case kEventWindowDragStarted : + case kEventWindowDragCompleted : { + MPEnterCriticalRegion(disp->paintCriticalRegion, kDurationForever); + // Now we retrieve the new size of the window + Rect newContentRect ; + GetWindowBounds(disp->carbonWindow,kWindowStructureRgn,&newContentRect); + const int nx = (int)(newContentRect.left), ny = (int)(newContentRect.top); + // Then we update CImg internal settings + if (nx!=disp->window_x || ny!=disp->window_y) { + disp->window_x = nx; + disp->window_y = ny; + disp->is_moved = true; + } + disp->is_event = true; + MPExitCriticalRegion(disp->paintCriticalRegion); + disp->paint(); // Coords changed, must update the screen + MPSignalSemaphore(c.wait_event); + result = noErr; + } break; + case kEventWindowPaint : + disp->paint(); + break; + } + + switch (GetEventClass(theEvent)) { + case kEventClassKeyboard : { + if (GetEventKind(theEvent)==kEventRawKeyModifiersChanged) { + // Apple has special keys named "notifiers", we have to convert this (exotic ?) key handling into the regular CImg processing. + UInt32 newModifiers; + if (GetEventParameter(theEvent,kEventParamKeyModifiers,typeUInt32,0,sizeof(UInt32),0,&newModifiers)==noErr) { + int newKeyCode = -1; + UInt32 changed = disp->lastKeyModifiers^newModifiers; + // Find what changed here + if ((changed & rightShiftKey)!=0) newKeyCode = cimg::keySHIFTRIGHT; + if ((changed & shiftKey)!=0) newKeyCode = cimg::keySHIFTLEFT; + + // On the Mac, the "option" key = the ALT key + if ((changed & (optionKey | rightOptionKey))!=0) newKeyCode = cimg::keyALTGR; + if ((changed & controlKey)!=0) newKeyCode = cimg::keyCTRLLEFT; + if ((changed & rightControlKey)!=0) newKeyCode = cimg::keyCTRLRIGHT; + if ((changed & cmdKey)!=0) newKeyCode = cimg::keyAPPLEFT; + if ((changed & alphaLock)!=0) newKeyCode = cimg::keyCAPSLOCK; + if (newKeyCode != -1) { // Simulate keystroke + if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); + disp->key = (int)newKeyCode; + } + disp->lastKeyModifiers = newModifiers; // Save current state + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + } + if (GetEventKind(theEvent)==kEventRawKeyDown || GetEventKind(theEvent)==kEventRawKeyRepeat) { + char keyCode; + if (GetEventParameter(theEvent,kEventParamKeyMacCharCodes,typeChar,0,sizeof(keyCode),0,&keyCode)==noErr) { + disp->update_iskey((unsigned int)keyCode,true); + if (disp->key) cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); + disp->key = (unsigned int)keyCode; + if (disp->released_key) { cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + } + } break; + + case kEventClassMouse : + switch (GetEventKind(theEvent)) { + case kEventMouseDragged : + // When you push the main button on the Apple mouse while moving it, you got NO kEventMouseMoved msg, + // but a kEventMouseDragged one. So we merge them here. + case kEventMouseMoved : + HIPoint point; + if (GetEventParameter(theEvent,kEventParamMouseLocation,typeHIPoint,0,sizeof(point),0,&point)==noErr) { + if (_CbToLocalPointFromMouseEvent(theEvent,disp->carbonWindow,&point)) { + disp->mouse_x = (int)point.x; + disp->mouse_y = (int)point.y; + if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) + disp->mouse_x = disp->mouse_y = -1; + } else disp->mouse_x = disp->mouse_y = -1; + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + break; + case kEventMouseDown : + UInt16 btn; + if (GetEventParameter(theEvent,kEventParamMouseButton,typeMouseButton,0,sizeof(btn),0,&btn)==noErr) { + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + if (btn==kEventMouseButtonPrimary) disp->button|=1U; + // For those who don't have a multi-mouse button (as me), I think it's better to allow the user + // to emulate a right click by using the Control key + if ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0) + cimg::warn("CImgDisplay::CarbonEventHandler() : Will emulate right click now [Down]"); + if (btn==kEventMouseButtonSecondary || ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0)) disp->button|=2U; + if (btn==kEventMouseButtonTertiary) disp->button|=4U; + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + break; + case kEventMouseWheelMoved : + EventMouseWheelAxis wheelax; + SInt32 delta; + if (GetEventParameter(theEvent,kEventParamMouseWheelAxis,typeMouseWheelAxis,0,sizeof(wheelax),0,&wheelax)==noErr) + if (wheelax==kEventMouseWheelAxisY) { + if (GetEventParameter(theEvent,kEventParamMouseWheelDelta,typeLongInteger,0,sizeof(delta),0,&delta)==noErr) + if (delta>0) disp->wheel+=delta/120; //FIXME: why 120 ? + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + } + break; + } + } + + switch (GetEventClass(theEvent)) { + case kEventClassKeyboard : + if (GetEventKind(theEvent)==kEventRawKeyUp) { + UInt32 keyCode; + if (GetEventParameter(theEvent,kEventParamKeyCode,typeUInt32,0,sizeof(keyCode),0,&keyCode)==noErr) { + disp->update_iskey((unsigned int)keyCode,false); + if (disp->key) { cimg_std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } + if (disp->released_key) cimg_std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); + disp->released_key = (int)keyCode; + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + } + break; + + case kEventClassMouse : + switch (GetEventKind(theEvent)) { + case kEventMouseUp : + UInt16 btn; + if (GetEventParameter(theEvent,kEventParamMouseButton,typeMouseButton,0,sizeof(btn),0,&btn)==noErr) { + cimg_std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); + if (btn==kEventMouseButtonPrimary) disp->button&=~1U; + // See note in kEventMouseDown handler. + if ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0) + cimg::warn("CImgDisplay::CarbonEventHandler() : Will emulate right click now [Up]"); + if (btn==kEventMouseButtonSecondary || ((disp->lastKeyModifiers & (controlKey | rightControlKey))!=0)) disp->button&=~2U; + if (btn==kEventMouseButtonTertiary) disp->button&=~2U; + } + disp->is_event = true; + MPSignalSemaphore(c.wait_event); + break; + } + } + } + return (result); + } + + static void* _events_thread(void* args) { + (void)args; // Make the compiler happy + cimg::CarbonInfo& c = cimg::CarbonAttr(); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + MPSignalSemaphore(c.sync_event); // Notify the caller that all goes fine + EventRef theEvent; + EventTargetRef theTarget; + OSStatus err; + CbSerializedQuery* query; + theTarget = GetEventDispatcherTarget(); + + // Enter in the main loop + while (true) { + pthread_testcancel(); /* Check if cancelation happens */ + err = ReceiveNextEvent(0,0,kDurationImmediate,true,&theEvent); // Fetch new events + if (err==noErr) { // Received a carbon event, so process it ! + SendEventToEventTarget (theEvent, theTarget); + ReleaseEvent(theEvent); + } else if (err == eventLoopTimedOutErr) { // There is no event to process, so check if there is new messages to process + OSStatus r =MPWaitOnQueue(c.com_queue,(void**)&query,0,0,10*kDurationMillisecond); + if (r!=noErr) continue; //nothing in the queue or an error.., bye + // If we're here, we've something to do now. + if (query) { + switch (query->kind) { + case COM_SETMOUSEPOS : { // change the cursor position + query->success = CGDisplayMoveCursorToPoint(kCGDirectMainDisplay,CGPointMake(query->sender->window_x+query->x,query->sender->window_y+query->y)) + == kCGErrorSuccess; + if (query->success) { + query->sender->mouse_x = query->x; + query->sender->mouse_y = query->y; + } else cimg::warn("CImgDisplay::_events_thread() : CGDisplayMoveCursorToPoint failed."); + } break; + case COM_SETTITLE : { // change the title bar caption + CFStringRef windowTitle = CFStringCreateWithCString(0,query->c,kCFStringEncodingMacRoman); + query->success = SetWindowTitleWithCFString(query->sender->carbonWindow,windowTitle)==noErr; + if (!query->success) + cimg::warn("CImgDisplay::_events_thread() : SetWindowTitleWithCFString failed."); + CFRelease(windowTitle); + } break; + case COM_RESIZEWINDOW : { // Resize a window + SizeWindow(query->sender->carbonWindow,query->x,query->y,query->update); + // If the window has been resized successfully, update display informations + query->sender->window_width = query->x; + query->sender->window_height = query->y; + query->success = true; + } break; + case COM_MOVEWINDOW : { // Move a window + MoveWindow(query->sender->carbonWindow,query->x,query->y,false); + query->sender->window_x = query->x; + query->sender->window_y = query->y; + query->sender->is_moved = false; + query->success = true; + } break; + case COM_SHOWMOUSE : { // Show the mouse + query->success = CGDisplayShowCursor(kCGDirectMainDisplay)==noErr; + if (!query->success) + cimg::warn("CImgDisplay::_events_thread() : CGDisplayShowCursor failed."); + } break; + case COM_HIDEMOUSE : { // Hide the mouse + query->success = CGDisplayHideCursor(kCGDirectMainDisplay)==noErr; + if (!query->success) + cimg::warn("CImgDisplay::_events_thread() : CGDisplayHideCursor failed."); + } break; + case COM_SHOWWINDOW : { // We've to show a window + ShowWindow(query->sender->carbonWindow); + query->success = true; + query->sender->is_closed = false; + } break; + case COM_HIDEWINDOW : { // We've to show a window + HideWindow(query->sender->carbonWindow); + query->sender->is_closed = true; + query->sender->window_x = query->sender->window_y = 0; + query->success = true; + } break; + case COM_RELEASEWINDOW : { // We have to release a given window handle + query->success = true; + CFRelease(query->sender->carbonWindow); + } break; + case COM_CREATEWINDOW : { // We have to create a window + query->success = true; + WindowAttributes windowAttrs; + Rect contentRect; + if (query->createFullScreenWindow) { + // To simulate a "true" full screen, we remove menus and close boxes + windowAttrs = (1L << 9); //Why ? kWindowNoTitleBarAttribute seems to be not defined on 10.3 + // Define a full screen bound rect + SetRect(&contentRect,0,0,CGDisplayPixelsWide(kCGDirectMainDisplay),CGDisplayPixelsHigh(kCGDirectMainDisplay)); + } else { // Set the window size + SetRect(&contentRect,0,0,query->sender->width,query->sender->height); // Window will be centered with RepositionWindow. + // Use default attributes + windowAttrs = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowInWindowMenuAttribute | kWindowLiveResizeAttribute; + } + // Update window position + if (query->createClosedWindow) query->sender->window_x = query->sender->window_y = 0; + else { + query->sender->window_x = contentRect.left; + query->sender->window_y = contentRect.top; + } + // Update window flags + query->sender->window_width = query->sender->width; + query->sender->window_height = query->sender->height; + query->sender->flush(); + // Create the window + if (CreateNewWindow(kDocumentWindowClass,windowAttrs,&contentRect,&query->sender->carbonWindow)!=noErr) { + query->success = false; + cimg::warn("CImgDisplay::_events_thread() : CreateNewWindow() failed."); + } + // Send it to the foreground + if (RepositionWindow(query->sender->carbonWindow,0,kWindowCenterOnMainScreen)!=noErr) query->success = false; + // Show it, if needed + if (!query->createClosedWindow) ShowWindow(query->sender->carbonWindow); + + // Associate a valid event handler + EventTypeSpec eventList[] = { + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowResizeStarted }, + { kEventClassWindow, kEventWindowResizeCompleted }, + { kEventClassWindow, kEventWindowDragStarted}, + { kEventClassWindow, kEventWindowDragCompleted }, + { kEventClassWindow, kEventWindowPaint }, + { kEventClassWindow, kEventWindowBoundsChanging }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowZoomed }, + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseDragged } + }; + + // Set up the handler + if (InstallWindowEventHandler(query->sender->carbonWindow,NewEventHandlerUPP(CarbonEventHandler),GetEventTypeCount(eventList), + eventList,(void*)query->sender,0)!=noErr) { + query->success = false; + cimg::warn("CImgDisplay::_events_thread() : InstallWindowEventHandler failed."); + } + + // Paint + query->sender->paint(); + } break; + default : + cimg::warn("CImgDisplay::_events_thread() : Received unknow code %d.",query->kind); + } + // Signal that the message has been processed + MPSignalSemaphore(c.sync_event); + } + } + } + // If we are here, the application is now finished + pthread_exit(0); + } + + CImgDisplay& assign() { + if (is_empty()) return *this; + cimg::CarbonInfo& c = cimg::CarbonAttr(); + // Destroy the window associated to the display + _CbFreeAttachedWindow(c); + // Don't destroy the background thread here. + // If you check whether _CbFreeAttachedWindow() returned true, + // - saying that there were no window left on screen - and + // you destroy the background thread here, ReceiveNextEvent won't + // work anymore if you create a new window after. So the + // background thread must be killed (pthread_cancel() + pthread_join()) + // only on the application shutdown. + + // Finalize graphics + _CbFinalizeGraphics(); + + // Do some cleanup + if (data) delete[] data; + if (title) delete[] title; + width = height = normalization = window_width = window_height = 0; + window_x = window_y = 0; + is_fullscreen = false; + is_closed = true; + min = max = 0; + title = 0; + flush(); + if (MPDeleteCriticalRegion(paintCriticalRegion)!=noErr) + throw CImgDisplayException("CImgDisplay()::assign() : MPDeleteCriticalRegion failed."); + return *this; + } + + CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + cimg::CarbonInfo& c = cimg::CarbonAttr(); + + // Allocate space for window title + const int s = cimg::strlen(ptitle)+1; + char *tmp_title = s?new char[s]:0; + if (s) cimg_std::memcpy(tmp_title,ptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + width = cimg::min(dimw,(unsigned int)screen_dimx()); + height = cimg::min(dimh,(unsigned int)screen_dimy()); + normalization = normalization_type<4?normalization_type:3; + is_fullscreen = fullscreen_flag; + is_closed = closed_flag; + lastKeyModifiers = 0; + title = tmp_title; + flush(); + + // Create the paint CR + if (MPCreateCriticalRegion(&paintCriticalRegion) != noErr) + throw CImgDisplayException("CImgDisplay::_assign() : MPCreateCriticalRegion() failed."); + + // Create the thread if it's not already created + if (c.event_thread==0) { + // Background thread does not exists, so create it ! + if (pthread_create(&c.event_thread,0,_events_thread,0)!=0) + throw CImgDisplayException("CImgDisplay::_assign() : pthread_create() failed."); + // Wait for thread initialization + MPWaitOnSemaphore(c.sync_event, kDurationForever); + } + + // Init disp. graphics + data = new unsigned int[width*height]; + _CbInitializeGraphics(); + + // Now ask the thread to create the window + _CbCreateAttachedWindow(c,ptitle,fullscreen_flag,closed_flag); + return *this; + } + +#endif + + }; + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \ref CImg<\c T>, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), while the number of channels + is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \ref CImgList<\c T> rather than simple images \ref CImg<\c T>. + + Thus, the \ref CImg<\c T> class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1D scalar signal, 2D color images, ...). + Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T : + fully supported template types are the basic C++ types : unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \ref CImg<\c T> structure contains \a six fields : + - \ref width defines the number of \a columns of the image (size along the X-axis). + - \ref height defines the number of \a rows of the image (size along the Y-axis). + - \ref depth defines the number of \a slices of the image (size along the Z-axis). + - \ref dim defines the number of \a channels of the image (size along the V-axis). + - \ref data defines a \a pointer to the \a pixel \a data (of type \c T). + - \ref is_shared is a boolean that tells if the memory buffer \ref data is shared with + another image. + + You can access these fields publicly although it is recommended to use the dedicated functions + dimx(), dimy(), dimz(), dimv() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used : + + - Construct images from arbitrary dimensions : + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note : images pixels are not automatically initialized to 0. You may use the function \ref fill() to + do it, or use the specific constructor taking 5 parameters like this : + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames : + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". + - \b Note : You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). + + - Construct images from C-style arrays : + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \ref CImg<\c T> class contains a lot of functions that operates on images. + Some of the most useful are : + + - operator()(), operator[]() : allows to access or write pixel values. + - display() : displays the image in a new window. + **/ + template + struct CImg { + + //! Variable representing the width of the instance image (i.e. dimensions along the X-axis). + /** + \remark + - Prefer using the function CImg::dimx() to get information about the width of an image. + - Use function CImg::resize() to set a new width for an image. Setting directly the variable \c width would probably + result in a library crash. + - Empty images have \c width defined to \c 0. + **/ + unsigned int width; + + //! Variable representing the height of the instance image (i.e. dimensions along the Y-axis). + /** + \remark + - Prefer using the function CImg::dimy() to get information about the height of an image. + - Use function CImg::resize() to set a new height for an image. Setting directly the variable \c height would probably + result in a library crash. + - 1D signals have \c height defined to \c 1. + - Empty images have \c height defined to \c 0. + **/ + unsigned int height; + + //! Variable representing the depth of the instance image (i.e. dimensions along the Z-axis). + /** + \remark + - Prefer using the function CImg::dimz() to get information about the depth of an image. + - Use function CImg::resize() to set a new depth for an image. Setting directly the variable \c depth would probably + result in a library crash. + - Classical 2D images have \c depth defined to \c 1. + - Empty images have \c depth defined to \c 0. + **/ + unsigned int depth; + + //! Variable representing the number of channels of the instance image (i.e. dimensions along the V-axis). + /** + \remark + - Prefer using the function CImg::dimv() to get information about the depth of an image. + - Use function CImg::resize() to set a new vector dimension for an image. Setting directly the variable \c dim would probably + result in a library crash. + - Scalar-valued images (one value per pixel) have \c dim defined to \c 1. + - Empty images have \c depth defined to \c 0. + **/ + unsigned int dim; + + //! Variable telling if pixel buffer of the instance image is shared with another one. + bool is_shared; + + //! Pointer to the first pixel of the pixel buffer. + T *data; + + //! Iterator type for CImg. + /** + \remark + - An \p iterator is a T* pointer (address of a pixel value in the pixel buffer). + - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. + **/ + typedef T* iterator; + + //! Const iterator type for CImg. + /** + \remark + - A \p const_iterator is a const T* pointer (address of a pixel value in the pixel buffer). + - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. + **/ + typedef const T* const_iterator; + + //! Get value type + typedef T value_type; + + // Define common T-dependant types. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif +#ifdef cimg_plugin1 +#include cimg_plugin1 +#endif +#ifdef cimg_plugin2 +#include cimg_plugin2 +#endif +#ifdef cimg_plugin3 +#include cimg_plugin3 +#endif +#ifdef cimg_plugin4 +#include cimg_plugin4 +#endif +#ifdef cimg_plugin5 +#include cimg_plugin5 +#endif +#ifdef cimg_plugin6 +#include cimg_plugin6 +#endif +#ifdef cimg_plugin7 +#include cimg_plugin7 +#endif +#ifdef cimg_plugin8 +#include cimg_plugin8 +#endif +#ifndef cimg_plugin_greycstoration +#define cimg_plugin_greycstoration_count +#endif +#ifndef cimg_plugin_greycstoration_lock +#define cimg_plugin_greycstoration_lock +#endif +#ifndef cimg_plugin_greycstoration_unlock +#define cimg_plugin_greycstoration_unlock +#endif + + //@} + + //-------------------------------------- + // + //! \name Constructors-Destructor-Copy + //@{ + //-------------------------------------- + + //! Destructor. + /** + The destructor destroys the instance image. + \remark + - Destructing an empty or shared image does nothing. + - Otherwise, all memory used to store the pixel data of the instance image is freed. + - When destroying a non-shared image, be sure that every shared instances of the same image are + also destroyed to avoid further access to desallocated memory buffers. + **/ + ~CImg() { + if (data && !is_shared) delete[] data; + } + + //! Default constructor. + /** + The default constructor creates an empty instance image. + \remark + - An empty image does not contain any data and has all of its dimensions \ref width, \ref height, \ref depth, \ref dim + set to 0 as well as its pointer to the pixel buffer \ref data. + - An empty image is non-shared. + **/ + CImg(): + width(0),height(0),depth(0),dim(0),is_shared(false),data(0) {} + + //! Constructs a new image with given size (\p dx,\p dy,\p dz,\p dv). + /** + This constructors create an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \ref dim. + \remark + - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the created image is empty + and all has its dimensions set to 0. No memory for pixel data is then allocated. + - This constructor creates only non-shared images. + - Image pixels allocated by this constructor are \b not \b initialized. + Use the constructor CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T) + to get an image of desired size with pixels set to a particular value. + **/ + explicit CImg(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1): + is_shared(false) { + const unsigned long siz = dx*dy*dz*dv; + if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; } + else { width = height = depth = dim = 0; data = 0; } + } + + //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with pixel having a default value \p val. + /** + This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel + values of the created instance image to \p val. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + \param val Default value for image pixels. + \remark + - This constructor has the same properties as CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + **/ + CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T val): + is_shared(false) { + const unsigned long siz = dx*dy*dz*dv; + if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(val); } + else { width = height = depth = dim = 0; data = 0; } + } + + //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (int version). + CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, + const int val0, const int val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { +#define _CImg_stdarg(img,a0,a1,N,t) { \ + unsigned int _siz = (unsigned int)N; \ + if (_siz--) { \ + va_list ap; \ + va_start(ap,a1); \ + T *ptrd = (img).data; \ + *(ptrd++) = (T)a0; \ + if (_siz--) { \ + *(ptrd++) = (T)a1; \ + for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + } \ + va_end(ap); \ + }} + assign(dx,dy,dz,dv); + _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); + } + + //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (double version). + CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, + const double val0, const double val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + assign(dx,dy,dz,dv); + _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); + } + + //! Construct an image with given size and with specified values given in a string. + CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, + const char *const values, const bool repeat_pattern):is_shared(false) { + const unsigned long siz = dx*dy*dz*dv; + if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(values,repeat_pattern); } + else { width = height = depth = dim = 0; data = 0; } + } + + //! Construct an image from a raw memory buffer. + /** + This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) and fill its pixel buffer by + copying data values from the input raw pixel buffer \p data_buffer. + **/ + template + CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1, const bool shared=false):is_shared(false) { + if (shared) + throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared instance image from a (%s*) buffer " + "(different pixel types).", + pixel_type(),CImg::pixel_type()); + const unsigned long siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; + const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + +#ifndef cimg_use_visualcpp6 + CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1, const bool shared=false) +#else + CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) +#endif + { + const unsigned long siz = dx*dy*dz*dv; + if (data_buffer && siz) { + width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; + if (is_shared) data = const_cast(data_buffer); + else { data = new T[siz]; cimg_std::memcpy(data,data_buffer,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + //! Default copy constructor. + /** + The default copy constructor creates a new instance image having same dimensions + (\ref width, \ref height, \ref depth, \ref dim) and same pixel values as the input image \p img. + \param img The input image to copy. + \remark + - If the input image \p img is non-shared or have a different template type \p t != \p T, + the default copy constructor allocates a new pixel buffer and copy the pixel data + of \p img into it. In this case, the pointers \ref data to the pixel buffers of the two images are different + and the resulting instance image is non-shared. + - If the input image \p img is shared and has the same template type \p t == \p T, + the default copy constructor does not allocate a new pixel buffer and the resulting instance image + shares its pixel buffer with the input image \p img, which means that modifying pixels of \p img also modifies + the created instance image. + - Copying an image having a different template type \p t != \p T performs a crude static cast conversion of each pixel value from + type \p t to type \p T. + - Copying an image having the same template type \p t == \p T is significantly faster. + **/ + template + CImg(const CImg& img):is_shared(false) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; + const t *ptrs = img.data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + + CImg(const CImg& img) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = img.is_shared; + if (is_shared) data = const_cast(img.data); + else { data = new T[siz]; cimg_std::memcpy(data,img.data,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + //! Advanced copy constructor. + /** + The advanced copy constructor - as the default constructor CImg(const CImg< t >&) - creates a new instance image having same dimensions + \ref width, \ref height, \ref depth, \ref dim and same pixel values as the input image \p img. + But it also decides if the created instance image shares its memory with the input image \p img (if the input parameter + \p shared is set to \p true) or not (if the input parameter \p shared is set to \p false). + \param img The input image to copy. + \param shared Boolean flag that decides if the copy is shared on non-shared. + \remark + - It is not possible to create a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. + - If a non-shared copy of the input image \p img is created, a new memory buffer is allocated for pixel data. + - If a shared copy of the input image \p img is created, no extra memory is allocated and the pixel buffer of the instance + image is the same as the one used by the input image \p img. + **/ + template + CImg(const CImg& img, const bool shared):is_shared(false) { + if (shared) + throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared instance image from a CImg<%s> instance " + "(different pixel types).", + pixel_type(),CImg::pixel_type()); + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; + const t *ptrs = img.data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + } else { width = height = depth = dim = 0; data = 0; } + } + + CImg(const CImg& img, const bool shared) { + const unsigned int siz = img.size(); + if (img.data && siz) { + width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = shared; + if (is_shared) data = const_cast(img.data); + else { data = new T[siz]; cimg_std::memcpy(data,img.data,siz*sizeof(T)); } + } else { width = height = depth = dim = 0; is_shared = false; data = 0; } + } + + //! Construct an image using dimensions of another image + template + CImg(const CImg& img, const char *const dimensions):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + assign(img,dimensions); + } + + //! Construct an image using dimensions of another image, and fill it with a default value + template + CImg(const CImg& img, const char *const dimensions, const T val): + width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + assign(img,dimensions).fill(val); + } + + //! Construct an image from an image file. + /** + This constructor creates an instance image by reading it from a file. + \param filename Filename of the image file. + \remark + - The image format is deduced from the filename only by looking for the filename extension i.e. without + analyzing the file itself. + - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. + More informations on this topic can be found in cimg_files_io. + - If the filename is not found, a CImgIOException is thrown by this constructor. + **/ + CImg(const char *const filename):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + assign(filename); + } + + //! Construct an image from the content of a CImgDisplay instance. + CImg(const CImgDisplay &disp):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { + disp.snapshot(*this); + } + + //! In-place version of the default constructor/destructor. + /** + This function replaces the instance image by an empty image. + \remark + - Memory used by the previous content of the instance image is freed if necessary. + - If the instance image was initially shared, it is replaced by a (non-shared) empty image. + - This function is useful to free memory used by an image that is not of use, but which + has been created in the current code scope (i.e. not destroyed yet). + **/ + CImg& assign() { + if (data && !is_shared) delete[] data; + width = height = depth = dim = 0; is_shared = false; data = 0; + return *this; + } + + //! In-place version of the default constructor. + /** + This function is strictly equivalent to \ref assign() and has been + introduced for having a STL-compliant function name. + **/ + CImg& clear() { + return assign(); + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the instance image becomes empty + and all has its dimensions set to 0. No memory for pixel data is then allocated. + - Memory buffer used to store previous pixel values is freed if necessary. + - If the instance image is shared, this constructor actually does nothing more than verifying + that new and old image dimensions fit. + - Image pixels allocated by this function are \b not \b initialized. + Use the function assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T) + to assign an image of desired size with pixels set to a particular value. + **/ + CImg& assign(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { + const unsigned long siz = dx*dy*dz*dv; + if (!siz) return assign(); + const unsigned long curr_siz = size(); + if (siz!=curr_siz) { + if (is_shared) + throw CImgArgumentException("CImg<%s>::assign() : Cannot assign image (%u,%u,%u,%u) to shared instance image (%u,%u,%u,%u,%p).", + pixel_type(),dx,dy,dz,dv,width,height,depth,dim,data); + else { if (data) delete[] data; data = new T[siz]; } + } + width = dx; height = dy; depth = dz; dim = dv; + return *this; + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T + and sets all pixel values of the instance image to \p val. + \param dx Desired size along the X-axis, i.e. the \ref width of the image. + \param dy Desired size along the Y-axis, i.e. the \ref height of the image. + \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. + \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. + \param val Default value for image pixels. + \remark + - This function has the same properties as assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). + **/ + CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T val) { + return assign(dx,dy,dz,dv).fill(val); + } + + //! In-place version of the previous constructor. + CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, + const int val0, const int val1, ...) { + assign(dx,dy,dz,dv); + _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); + return *this; + } + + //! In-place version of the previous constructor. + CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, + const double val0, const double val1, ...) { + assign(dx,dy,dz,dv); + _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); + return *this; + } + + //! In-place version of the previous constructor. + template + CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + const unsigned long siz = dx*dy*dz*dv; + if (!data_buffer || !siz) return assign(); + assign(dx,dy,dz,dv); + const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); + return *this; + } + +#ifndef cimg_use_visualcpp6 + CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) +#else + CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv) +#endif + { + const unsigned long siz = dx*dy*dz*dv; + if (!data_buffer || !siz) return assign(); + const unsigned long curr_siz = size(); + if (data_buffer==data && siz==curr_siz) return assign(dx,dy,dz,dv); + if (is_shared || data_buffer+siz=data+size()) { + assign(dx,dy,dz,dv); + if (is_shared) cimg_std::memmove(data,data_buffer,siz*sizeof(T)); + else cimg_std::memcpy(data,data_buffer,siz*sizeof(T)); + } else { + T *new_data = new T[siz]; + cimg_std::memcpy(new_data,data_buffer,siz*sizeof(T)); + delete[] data; data = new_data; width = dx; height = dy; depth = dz; dim = dv; + } + return *this; + } + + //! In-place version of the previous constructor, allowing to force the shared state of the instance image. + template + CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) { + if (shared) + throw CImgArgumentException("CImg<%s>::assign() : Cannot assign buffer (%s*) to shared instance image (%u,%u,%u,%u,%p)" + "(different pixel types).", + pixel_type(),CImg::pixel_type(),width,height,depth,dim,data); + return assign(data_buffer,dx,dy,dz,dv); + } + + CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv, const bool shared) { + const unsigned long siz = dx*dy*dz*dv; + if (!data_buffer || !siz) return assign(); + if (!shared) { if (is_shared) assign(); assign(data_buffer,dx,dy,dz,dv); } + else { + if (!is_shared) { + if (data_buffer+siz=data+size()) assign(); + else cimg::warn("CImg<%s>::assign() : Shared instance image has overlapping memory !", + pixel_type()); + } + width = dx; height = dy; depth = dz; dim = dv; is_shared = true; + data = const_cast(data_buffer); + } + return *this; + } + + //! In-place version of the default copy constructor. + /** + This function assigns a copy of the input image \p img to the current instance image. + \param img The input image to copy. + \remark + - If the instance image is not shared, the content of the input image \p img is copied into a new buffer + becoming the new pixel buffer of the instance image, while the old pixel buffer is freed if necessary. + - If the instance image is shared, the content of the input image \p img is copied into the current (shared) pixel buffer + of the instance image, modifying then the image referenced by the shared instance image. The instance image still remains shared. + **/ + template + CImg& assign(const CImg& img) { + return assign(img.data,img.width,img.height,img.depth,img.dim); + } + + //! In-place version of the advanced constructor. + /** + This function - as the simpler function assign(const CImg< t >&) - assigns a copy of the input image \p img to the + current instance image. But it also decides if the copy is shared (if the input parameter \p shared is set to \c true) + or non-shared (if the input parameter \p shared is set to \c false). + \param img The input image to copy. + \param shared Boolean flag that decides if the copy is shared or non-shared. + \remark + - It is not possible to assign a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. + - If a non-shared copy of the input image \p img is assigned, a new memory buffer is allocated for pixel data. + - If a shared copy of the input image \p img is assigned, no extra memory is allocated and the pixel buffer of the instance + image is the same as the one used by the input image \p img. + **/ + template + CImg& assign(const CImg& img, const bool shared) { + return assign(img.data,img.width,img.height,img.depth,img.dim,shared); + } + + //! In-place version of the previous constructor. + template + CImg& assign(const CImg& img, const char *const dimensions) { + if (dimensions) { + unsigned int siz[4] = { 0,1,1,1 }; + const char *s = dimensions; + char tmp[256] = { 0 }, c = 0; + int val = 0; + for (unsigned int k=0; k<4; ++k) { + const int err = cimg_std::sscanf(s,"%[-0-9]%c",tmp,&c); + if (err>=1) { + const int err = cimg_std::sscanf(s,"%d",&val); + if (err==1) { + int val2 = val<0?-val:(c=='%'?val:-1); + if (val2>=0) { + val = (int)((k==0?img.width:(k==1?img.height:(k==2?img.depth:img.dim)))*val2/100); + if (c!='%' && !val) val = 1; + } + siz[k] = val; + } + s+=cimg::strlen(tmp); + if (c=='%') ++s; + } + if (!err) { + if (!cimg::strncasecmp(s,"x",1)) { ++s; siz[k] = img.width; } + else if (!cimg::strncasecmp(s,"y",1)) { ++s; siz[k] = img.height; } + else if (!cimg::strncasecmp(s,"z",1)) { ++s; siz[k] = img.depth; } + else if (!cimg::strncasecmp(s,"v",1)) { ++s; siz[k] = img.dim; } + else if (!cimg::strncasecmp(s,"dx",2)) { s+=2; siz[k] = img.width; } + else if (!cimg::strncasecmp(s,"dy",2)) { s+=2; siz[k] = img.height; } + else if (!cimg::strncasecmp(s,"dz",2)) { s+=2; siz[k] = img.depth; } + else if (!cimg::strncasecmp(s,"dv",2)) { s+=2; siz[k] = img.dim; } + else if (!cimg::strncasecmp(s,"dimx",4)) { s+=4; siz[k] = img.width; } + else if (!cimg::strncasecmp(s,"dimy",4)) { s+=4; siz[k] = img.height; } + else if (!cimg::strncasecmp(s,"dimz",4)) { s+=4; siz[k] = img.depth; } + else if (!cimg::strncasecmp(s,"dimv",4)) { s+=4; siz[k] = img.dim; } + else if (!cimg::strncasecmp(s,"width",5)) { s+=5; siz[k] = img.width; } + else if (!cimg::strncasecmp(s,"height",6)) { s+=6; siz[k] = img.height; } + else if (!cimg::strncasecmp(s,"depth",5)) { s+=5; siz[k] = img.depth; } + else if (!cimg::strncasecmp(s,"dim",3)) { s+=3; siz[k] = img.dim; } + else { ++s; --k; } + } + } + return assign(siz[0],siz[1],siz[2],siz[3]); + } + return assign(); + } + + //! In-place version of the previous constructor. + template + CImg& assign(const CImg& img, const char *const dimensions, const T val) { + return assign(img,dimensions).fill(val); + } + + //! In-place version of the previous constructor. + /** + This function replaces the instance image by the one that have been read from the given file. + \param filename Filename of the image file. + - The image format is deduced from the filename only by looking for the filename extension i.e. without + analyzing the file itself. + - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. + More informations on this topic can be found in cimg_files_io. + - If the filename is not found, a CImgIOException is thrown by this constructor. + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! In-place version of the previous constructor. + CImg& assign(const CImgDisplay &disp) { + disp.snapshot(*this); + return *this; + } + + //! Transfer the content of the instance image into another one in a way that memory copies are avoided if possible. + /** + The instance image is always empty after a call to this function. + **/ + template + CImg& transfer_to(CImg& img) { + img.assign(*this); + assign(); + return img; + } + + CImg& transfer_to(CImg& img) { + if (is_shared || img.is_shared) { img.assign(*this); assign(); } else { img.assign(); swap(img); } + return img; + } + + //! Swap all fields of two images. Use with care ! + CImg& swap(CImg& img) { + cimg::swap(width,img.width); + cimg::swap(height,img.height); + cimg::swap(depth,img.depth); + cimg::swap(dim,img.dim); + cimg::swap(data,img.data); + cimg::swap(is_shared,img.is_shared); + return img; + } + + //@} + //------------------------------------- + // + //! \name Image Informations + //@{ + //------------------------------------- + + //! Return the type of the pixel values. + /** + \return a string describing the type of the image pixels (template parameter \p T). + - The string returned may contains spaces ("unsigned char"). + - If the template parameter T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the total number of pixel values in an image. + /** + - Equivalent to : dimx() * dimy() * dimz() * dimv(). + + \par example: + \code + CImg<> img(100,100,1,3); + if (img.size()==100*100*3) std::fprintf(stderr,"This statement is true"); + \endcode + **/ + unsigned long size() const { + return width*height*depth*dim; + } + + //! Return the number of columns of the instance image (size along the X-axis, i.e image width). + int dimx() const { + return (int)width; + } + + //! Return the number of rows of the instance image (size along the Y-axis, i.e image height). + int dimy() const { + return (int)height; + } + + //! Return the number of slices of the instance image (size along the Z-axis). + int dimz() const { + return (int)depth; + } + + //! Return the number of vector channels of the instance image (size along the V-axis). + int dimv() const { + return (int)dim; + } + + //! Return \c true if image (*this) has the specified width. + bool is_sameX(const unsigned int dx) const { + return (width==dx); + } + + //! Return \c true if images \c (*this) and \c img have same width. + template + bool is_sameX(const CImg& img) const { + return is_sameX(img.width); + } + + //! Return \c true if images \c (*this) and the display \c disp have same width. + bool is_sameX(const CImgDisplay& disp) const { + return is_sameX(disp.width); + } + + //! Return \c true if image (*this) has the specified height. + bool is_sameY(const unsigned int dy) const { + return (height==dy); + } + + //! Return \c true if images \c (*this) and \c img have same height. + template + bool is_sameY(const CImg& img) const { + return is_sameY(img.height); + } + + //! Return \c true if images \c (*this) and the display \c disp have same height. + bool is_sameY(const CImgDisplay& disp) const { + return is_sameY(disp.height); + } + + //! Return \c true if image (*this) has the specified depth. + bool is_sameZ(const unsigned int dz) const { + return (depth==dz); + } + + //! Return \c true if images \c (*this) and \c img have same depth. + template + bool is_sameZ(const CImg& img) const { + return is_sameZ(img.depth); + } + + //! Return \c true if image (*this) has the specified number of channels. + bool is_sameV(const unsigned int dv) const { + return (dim==dv); + } + + //! Return \c true if images \c (*this) and \c img have same dim. + template + bool is_sameV(const CImg& img) const { + return is_sameV(img.dim); + } + + //! Return \c true if image (*this) has the specified width and height. + bool is_sameXY(const unsigned int dx, const unsigned int dy) const { + return (is_sameX(dx) && is_sameY(dy)); + } + + //! Return \c true if images have same width and same height. + template + bool is_sameXY(const CImg& img) const { + return (is_sameX(img) && is_sameY(img)); + } + + //! Return \c true if image \c (*this) and the display \c disp have same width and same height. + bool is_sameXY(const CImgDisplay& disp) const { + return (is_sameX(disp) && is_sameY(disp)); + } + + //! Return \c true if image (*this) has the specified width and depth. + bool is_sameXZ(const unsigned int dx, const unsigned int dz) const { + return (is_sameX(dx) && is_sameZ(dz)); + } + + //! Return \c true if images have same width and same depth. + template + bool is_sameXZ(const CImg& img) const { + return (is_sameX(img) && is_sameZ(img)); + } + + //! Return \c true if image (*this) has the specified width and number of channels. + bool is_sameXV(const unsigned int dx, const unsigned int dv) const { + return (is_sameX(dx) && is_sameV(dv)); + } + + //! Return \c true if images have same width and same number of channels. + template + bool is_sameXV(const CImg& img) const { + return (is_sameX(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified height and depth. + bool is_sameYZ(const unsigned int dy, const unsigned int dz) const { + return (is_sameY(dy) && is_sameZ(dz)); + } + + //! Return \c true if images have same height and same depth. + template + bool is_sameYZ(const CImg& img) const { + return (is_sameY(img) && is_sameZ(img)); + } + + //! Return \c true if image (*this) has the specified height and number of channels. + bool is_sameYV(const unsigned int dy, const unsigned int dv) const { + return (is_sameY(dy) && is_sameV(dv)); + } + + //! Return \c true if images have same height and same number of channels. + template + bool is_sameYV(const CImg& img) const { + return (is_sameY(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified depth and number of channels. + bool is_sameZV(const unsigned int dz, const unsigned int dv) const { + return (is_sameZ(dz) && is_sameV(dv)); + } + + //! Return \c true if images have same depth and same number of channels. + template + bool is_sameZV(const CImg& img) const { + return (is_sameZ(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified width, height and depth. + bool is_sameXYZ(const unsigned int dx, const unsigned int dy, const unsigned int dz) const { + return (is_sameXY(dx,dy) && is_sameZ(dz)); + } + + //! Return \c true if images have same width, same height and same depth. + template + bool is_sameXYZ(const CImg& img) const { + return (is_sameXY(img) && is_sameZ(img)); + } + + //! Return \c true if image (*this) has the specified width, height and depth. + bool is_sameXYV(const unsigned int dx, const unsigned int dy, const unsigned int dv) const { + return (is_sameXY(dx,dy) && is_sameV(dv)); + } + + //! Return \c true if images have same width, same height and same number of channels. + template + bool is_sameXYV(const CImg& img) const { + return (is_sameXY(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified width, height and number of channels. + bool is_sameXZV(const unsigned int dx, const unsigned int dz, const unsigned int dv) const { + return (is_sameXZ(dx,dz) && is_sameV(dv)); + } + + //! Return \c true if images have same width, same depth and same number of channels. + template + bool is_sameXZV(const CImg& img) const { + return (is_sameXZ(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified height, depth and number of channels. + bool is_sameYZV(const unsigned int dy, const unsigned int dz, const unsigned int dv) const { + return (is_sameYZ(dy,dz) && is_sameV(dv)); + } + + //! Return \c true if images have same height, same depth and same number of channels. + template + bool is_sameYZV(const CImg& img) const { + return (is_sameYZ(img) && is_sameV(img)); + } + + //! Return \c true if image (*this) has the specified width, height, depth and number of channels. + bool is_sameXYZV(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) const { + return (is_sameXYZ(dx,dy,dz) && is_sameV(dv)); + } + + //! Return \c true if images \c (*this) and \c img have same width, same height, same depth and same number of channels. + template + bool is_sameXYZV(const CImg& img) const { + return (is_sameXYZ(img) && is_sameV(img)); + } + + //! Return \c true if current image is empty. + bool is_empty() const { + return !(data && width && height && depth && dim); + } + + //! Return \p true if image is not empty. + operator bool() const { + return !is_empty(); + } + + //! Return an iterator to the first image pixel + iterator begin() { + return data; + } + + const_iterator begin() const { + return data; + } + + //! Return reference to the first image pixel + const T& first() const { + return *data; + } + + T& first() { + return *data; + } + + //! Return an iterator pointing after the last image pixel + iterator end() { + return data + size(); + } + + const_iterator end() const { + return data + size(); + } + + //! Return a reference to the last image pixel + const T& last() const { + return data[size() - 1]; + } + + T& last() { + return data[size() - 1]; + } + + //! Return a pointer to the pixel buffer. + T* ptr() { + return data; + } + + const T* ptr() const { + return data; + } + + //! Return a pointer to the pixel value located at (\p x,\p y,\p z,\p v). + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - When called without parameters, ptr() returns a pointer to the begining of the pixel buffer. + - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear if + given coordinates are outside the image range (but function performances decrease). + + \par example: + \code + CImg img(100,100,1,1,0); // Define a 100x100 greyscale image with float-valued pixels. + float *ptr = ptr(10,10); // Get a pointer to the pixel located at (10,10). + float val = *ptr; // Get the pixel value. + \endcode + **/ +#if cimg_debug>=3 + T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + const long off = offset(x,y,z,v); + if (off<0 || off>=(long)size()) { + cimg::warn("CImg<%s>::ptr() : Asked for a pointer at coordinates (%u,%u,%u,%u) (offset=%ld), " + "outside image range (%u,%u,%u,%u) (size=%lu)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return data; + } + return data + off; + } + + const T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + return const_cast*>(this)->ptr(x,y,z,v); + } +#else + T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + return data + (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; + } + + const T* ptr(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + return data + (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; + } +#endif + + //! Return \c true if the memory buffers of the two images overlaps. + /** + May happen when using shared images. + **/ + template + bool is_overlapped(const CImg& img) const { + const unsigned long csiz = size(), isiz = img.size(); + return !((void*)(data+csiz)<=(void*)img.data || (void*)data>=(void*)(img.data+isiz)); + } + + //! Return the offset of the pixel coordinates (\p x,\p y,\p z,\p v) with respect to the data pointer \c data. + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - No checking is done on the validity of the given coordinates. + + \par Example: + \code + CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. + long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10). + float val = img[off]; // Get the blue value of the pixel. + \endcode + **/ + long offset(const int x, const int y=0, const int z=0, const int v=0) const { + return (long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth; + } + + //! Fast access to pixel value for reading or writing. + /** + \param x X-coordinate of the pixel. + \param y Y-coordinate of the pixel. + \param z Z-coordinate of the pixel. + \param v V-coordinate of the pixel. + + - If one image dimension is equal to 1, it can be omitted in the coordinate list (see example below). + - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear + (but function performances decrease). + + \par example: + \code + CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. + const float valR = img(10,10,0,0); // Read the red component at coordinates (10,10). + const float valG = img(10,10,0,1); // Read the green component at coordinates (10,10) + const float valB = img(10,10,2); // Read the blue component at coordinates (10,10) (Z-coordinate omitted here). + const float avg = (valR + valG + valB)/3; // Compute average pixel value. + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the pixel (10,10) by the average grey value. + \endcode + **/ +#if cimg_debug>=3 + T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + const long off = offset(x,y,z,v); + if (!data || off>=(long)size()) { + cimg::warn("CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%ld) " + "outside the image range (%u,%u,%u,%u) (size=%lu)", + pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); + return *data; + } + else return data[off]; + } + + const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + return const_cast*>(this)->operator()(x,y,z,v); + } +#else + T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { + return data[(long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth]; + } + + const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { + return data[(long)x + (long)y*width + (long)z*width*height + (long)v*width*height*depth]; + } +#endif + + //! Fast access to pixel value for reading or writing, using an offset to the image pixel. + /** + \param off Offset of the pixel according to the begining of the pixel buffer, given by ptr(). + + - If the macro \c 'cimg_debug'>=3, boundary checking is performed and warning messages may appear + (but function performances decrease). + - As pixel values are aligned in memory, this operator can sometime useful to access values easier than + with operator()() (see example below). + + \par example: + \code + CImg vec(1,10); // Define a vector of float values (10 lines, 1 row). + const float val1 = vec(0,4); // Get the fifth element using operator()(). + const float val2 = vec[4]; // Get the fifth element using operator[]. Here, val2==val1. + \endcode + **/ +#if cimg_debug>=3 + T& operator[](const unsigned long off) { + if (!data || off>=size()) { + cimg::warn("CImg<%s>::operator[] : Pixel access requested at offset=%lu " + "outside the image range (%u,%u,%u,%u) (size=%lu)", + pixel_type(),off,width,height,depth,dim,size()); + return *data; + } + else return data[off]; + } + + const T& operator[](const unsigned long off) const { + return const_cast*>(this)->operator[](off); + } +#else + T& operator[](const unsigned long off) { + return data[off]; + } + + const T& operator[](const unsigned long off) const { + return data[off]; + } +#endif + + //! Return a reference to the last image value + T& back() { + return operator()(size()-1); + } + + const T& back() const { + return operator()(size()-1); + } + + //! Return a reference to the first image value + T& front() { + return *data; + } + + const T& front() const { + return *data; + } + + //! Return \c true if pixel (x,y,z,v) is inside image boundaries. + bool containsXYZV(const int x, const int y=0, const int z=0, const int v=0) const { + return !is_empty() && x>=0 && x=0 && y=0 && z=0 && v + bool contains(const T& pixel, t& x, t& y, t& z, t& v) const { + const unsigned long wh = width*height, whz = wh*depth, siz = whz*dim; + const T *const ppixel = &pixel; + if (is_empty() || ppixel=data+siz) return false; + unsigned long off = (unsigned long)(ppixel - data); + const unsigned long nv = off/whz; + off%=whz; + const unsigned long nz = off/wh; + off%=wh; + const unsigned long ny = off/width, nx = off%width; + x = (t)nx; y = (t)ny; z = (t)nz; v = (t)nv; + return true; + } + + //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y,z). + template + bool contains(const T& pixel, t& x, t& y, t& z) const { + const unsigned long wh = width*height, whz = wh*depth, siz = whz*dim; + const T *const ppixel = &pixel; + if (is_empty() || ppixel=data+siz) return false; + unsigned long off = ((unsigned long)(ppixel - data))%whz; + const unsigned long nz = off/wh; + off%=wh; + const unsigned long ny = off/width, nx = off%width; + x = (t)nx; y = (t)ny; z = (t)nz; + return true; + } + + //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y). + template + bool contains(const T& pixel, t& x, t& y) const { + const unsigned long wh = width*height, siz = wh*depth*dim; + const T *const ppixel = &pixel; + if (is_empty() || ppixel=data+siz) return false; + unsigned long off = ((unsigned long)(ppixel - data))%wh; + const unsigned long ny = off/width, nx = off%width; + x = (t)nx; y = (t)ny; + return true; + } + + //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x). + template + bool contains(const T& pixel, t& x) const { + const T *const ppixel = &pixel; + if (is_empty() || ppixel=data+size()) return false; + x = (t)(((unsigned long)(ppixel - data))%width); + return true; + } + + //! Return \c true if specified referenced value is inside the image boundaries. + bool contains(const T& pixel) const { + const T *const ppixel = &pixel; + return !is_empty() && ppixel>=data && ppixel=(int)size())?(cimg::temporary(out_val)=out_val):(*this)[off]; + } + + T at(const int off, const T out_val) const { + return (off<0 || off>=(int)size())?out_val:(*this)[off]; + } + + //! Read a pixel value with Neumann boundary conditions. + T& at(const int off) { + if (!size()) + throw CImgInstanceException("CImg<%s>::at() : Instance image is empty.", + pixel_type()); + return _at(off); + } + + T at(const int off) const { + if (!size()) + throw CImgInstanceException("CImg<%s>::at() : Instance image is empty.", + pixel_type()); + return _at(off); + } + + T& _at(const int off) { + const unsigned int siz = (unsigned int)size(); + return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off]; + } + + T _at(const int off) const { + const unsigned int siz = (unsigned int)size(); + return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off]; + } + + //! Read a pixel value with Dirichlet boundary conditions. + T& atXYZV(const int x, const int y, const int z, const int v, const T out_val) { + return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())? + (cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); + } + + T atXYZV(const int x, const int y, const int z, const int v, const T out_val) const { + return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())?out_val:(*this)(x,y,z,v); + } + + //! Read a pixel value with Neumann boundary conditions. + T& atXYZV(const int x, const int y, const int z, const int v) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXYZV() : Instance image is empty.", + pixel_type()); + return _atXYZV(x,y,z,v); + } + + T atXYZV(const int x, const int y, const int z, const int v) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXYZV() : Instance image is empty.", + pixel_type()); + return _atXYZV(x,y,z,v); + } + + T& _atXYZV(const int x, const int y, const int z, const int v) { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); + } + + T _atXYZV(const int x, const int y, const int z, const int v) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); + } + + //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c x,\c y,\c z). + T& atXYZ(const int x, const int y, const int z, const int v, const T out_val) { + return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())? + (cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); + } + + T atXYZ(const int x, const int y, const int z, const int v, const T out_val) const { + return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())?out_val:(*this)(x,y,z,v); + } + + //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c x,\c y,\c z). + T& atXYZ(const int x, const int y, const int z, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXYZ() : Instance image is empty.", + pixel_type()); + return _atXYZ(x,y,z,v); + } + + T atXYZ(const int x, const int y, const int z, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXYZ() : Instance image is empty.", + pixel_type()); + return _atXYZ(x,y,z,v); + } + + T& _atXYZ(const int x, const int y, const int z, const int v=0) { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z),v); + } + + T _atXYZ(const int x, const int y, const int z, const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y<0?0:(y>=dimy()?dimy()-1:y), + z<0?0:(z>=dimz()?dimz()-1:z),v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c x,\c y). + T& atXY(const int x, const int y, const int z, const int v, const T out_val) { + return (x<0 || y<0 || x>=dimx() || y>=dimy())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); + } + + T atXY(const int x, const int y, const int z, const int v, const T out_val) const { + return (x<0 || y<0 || x>=dimx() || y>=dimy())?out_val:(*this)(x,y,z,v); + } + + //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c x,\c y). + T& atXY(const int x, const int y, const int z=0, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXY() : Instance image is empty.", + pixel_type()); + return _atXY(x,y,z,v); + } + + T atXY(const int x, const int y, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atXY() : Instance image is empty.", + pixel_type()); + return _atXY(x,y,z,v); + } + + T& _atXY(const int x, const int y, const int z=0, const int v=0) { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); + } + + T _atXY(const int x, const int y, const int z=0, const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c x). + T& atX(const int x, const int y, const int z, const int v, const T out_val) { + return (x<0 || x>=dimx())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,v); + } + + T atX(const int x, const int y, const int z, const int v, const T out_val) const { + return (x<0 || x>=dimx())?out_val:(*this)(x,y,z,v); + } + + //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c x). + T& atX(const int x, const int y=0, const int z=0, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atX() : Instance image is empty.", + pixel_type()); + return _atX(x,y,z,v); + } + + T atX(const int x, const int y=0, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::atX() : Instance image is empty.", + pixel_type()); + return _atX(x,y,z,v); + } + + T& _atX(const int x, const int y=0, const int z=0, const int v=0) { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); + } + + T _atX(const int x, const int y=0, const int z=0, const int v=0) const { + return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); + } + + //! Read a pixel value using linear interpolation and Dirichlet boundary conditions. + Tfloat linear_atXYZV(const float fx, const float fy, const float fz, const float fv, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), nx = x+1, + y = (int)fy-(fy>=0?0:1), ny = y+1, + z = (int)fz-(fz>=0?0:1), nz = z+1, + v = (int)fv-(fv>=0?0:1), nv = v+1; + const float + dx = fx-x, + dy = fy-y, + dz = fz-z, + dv = fv-v; + const Tfloat + Icccc = (Tfloat)atXYZV(x,y,z,v,out_val), Inccc = (Tfloat)atXYZV(nx,y,z,v,out_val), + Icncc = (Tfloat)atXYZV(x,ny,z,v,out_val), Inncc = (Tfloat)atXYZV(nx,ny,z,v,out_val), + Iccnc = (Tfloat)atXYZV(x,y,nz,v,out_val), Incnc = (Tfloat)atXYZV(nx,y,nz,v,out_val), + Icnnc = (Tfloat)atXYZV(x,ny,nz,v,out_val), Innnc = (Tfloat)atXYZV(nx,ny,nz,v,out_val), + Icccn = (Tfloat)atXYZV(x,y,z,nv,out_val), Inccn = (Tfloat)atXYZV(nx,y,z,nv,out_val), + Icncn = (Tfloat)atXYZV(x,ny,z,nv,out_val), Inncn = (Tfloat)atXYZV(nx,ny,z,nv,out_val), + Iccnn = (Tfloat)atXYZV(x,y,nz,nv,out_val), Incnn = (Tfloat)atXYZV(nx,y,nz,nv,out_val), + Icnnn = (Tfloat)atXYZV(x,ny,nz,nv,out_val), Innnn = (Tfloat)atXYZV(nx,ny,nz,nv,out_val); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dv*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dv*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dv*(Icccc+Iccnn-Iccnc-Icccn)) + + dv*(Icccn-Icccc); + } + + //! Read a pixel value using linear interpolation and Neumann boundary conditions. + Tfloat linear_atXYZV(const float fx, const float fy=0, const float fz=0, const float fv=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::linear_atXYZV() : Instance image is empty.", + pixel_type()); + return _linear_atXYZV(fx,fy,fz,fv); + } + + Tfloat _linear_atXYZV(const float fx, const float fy=0, const float fz=0, const float fv=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx), + nfy = fy<0?0:(fy>height-1?height-1:fy), + nfz = fz<0?0:(fz>depth-1?depth-1:fz), + nfv = fv<0?0:(fv>dim-1?dim-1:fv); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + v = (unsigned int)nfv; + const float + dx = nfx-x, + dy = nfy-y, + dz = nfz-z, + dv = nfv-v; + const unsigned int + nx = dx>0?x+1:x, + ny = dy>0?y+1:y, + nz = dz>0?z+1:z, + nv = dv>0?v+1:v; + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,v), Inccc = (Tfloat)(*this)(nx,y,z,v), + Icncc = (Tfloat)(*this)(x,ny,z,v), Inncc = (Tfloat)(*this)(nx,ny,z,v), + Iccnc = (Tfloat)(*this)(x,y,nz,v), Incnc = (Tfloat)(*this)(nx,y,nz,v), + Icnnc = (Tfloat)(*this)(x,ny,nz,v), Innnc = (Tfloat)(*this)(nx,ny,nz,v), + Icccn = (Tfloat)(*this)(x,y,z,nv), Inccn = (Tfloat)(*this)(nx,y,z,nv), + Icncn = (Tfloat)(*this)(x,ny,z,nv), Inncn = (Tfloat)(*this)(nx,ny,z,nv), + Iccnn = (Tfloat)(*this)(x,y,nz,nv), Incnn = (Tfloat)(*this)(nx,y,nz,nv), + Icnnn = (Tfloat)(*this)(x,ny,nz,nv), Innnn = (Tfloat)(*this)(nx,ny,nz,nv); + return Icccc + + dx*(Inccc-Icccc + + dy*(Icccc+Inncc-Icncc-Inccc + + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + + dz*(Icccc+Incnc-Iccnc-Inccc + + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + + dv*(Icccc+Inccn-Inccc-Icccn)) + + dy*(Icncc-Icccc + + dz*(Icccc+Icnnc-Iccnc-Icncc + + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + + dv*(Icccc+Icncn-Icncc-Icccn)) + + dz*(Iccnc-Icccc + + dv*(Icccc+Iccnn-Iccnc-Icccn)) + + dv*(Icccn-Icccc); + } + + //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first three coordinates). + Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int v, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), nx = x+1, + y = (int)fy-(fy>=0?0:1), ny = y+1, + z = (int)fz-(fz>=0?0:1), nz = z+1; + const float + dx = fx-x, + dy = fy-y, + dz = fz-z; + const Tfloat + Iccc = (Tfloat)atXYZ(x,y,z,v,out_val), Incc = (Tfloat)atXYZ(nx,y,z,v,out_val), + Icnc = (Tfloat)atXYZ(x,ny,z,v,out_val), Innc = (Tfloat)atXYZ(nx,ny,z,v,out_val), + Iccn = (Tfloat)atXYZ(x,y,nz,v,out_val), Incn = (Tfloat)atXYZ(nx,y,nz,v,out_val), + Icnn = (Tfloat)atXYZ(x,ny,nz,v,out_val), Innn = (Tfloat)atXYZ(nx,ny,nz,v,out_val); + return Iccc + + dx*(Incc-Iccc + + dy*(Iccc+Innc-Icnc-Incc + + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + + dz*(Iccc+Incn-Iccn-Incc)) + + dy*(Icnc-Iccc + + dz*(Iccc+Icnn-Iccn-Icnc)) + + dz*(Iccn-Iccc); + } + + //! Read a pixel value using linear interpolation and Neumann boundary conditions (first three coordinates). + Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::linear_atXYZ() : Instance image is empty.", + pixel_type()); + return _linear_atXYZ(fx,fy,fz,v); + } + + Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int v=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx), + nfy = fy<0?0:(fy>height-1?height-1:fy), + nfz = fz<0?0:(fz>depth-1?depth-1:fz); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx-x, + dy = nfy-y, + dz = nfz-z; + const unsigned int + nx = dx>0?x+1:x, + ny = dy>0?y+1:y, + nz = dz>0?z+1:z; + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,v), Incc = (Tfloat)(*this)(nx,y,z,v), + Icnc = (Tfloat)(*this)(x,ny,z,v), Innc = (Tfloat)(*this)(nx,ny,z,v), + Iccn = (Tfloat)(*this)(x,y,nz,v), Incn = (Tfloat)(*this)(nx,y,nz,v), + Icnn = (Tfloat)(*this)(x,ny,nz,v), Innn = (Tfloat)(*this)(nx,ny,nz,v); + return Iccc + + dx*(Incc-Iccc + + dy*(Iccc+Innc-Icnc-Incc + + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + + dz*(Iccc+Incn-Iccn-Incc)) + + dy*(Icnc-Iccc + + dz*(Iccc+Icnn-Iccn-Icnc)) + + dz*(Iccn-Iccc); + } + + //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first two coordinates). + Tfloat linear_atXY(const float fx, const float fy, const int z, const int v, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), nx = x+1, + y = (int)fy-(fy>=0?0:1), ny = y+1; + const float + dx = fx-x, + dy = fy-y; + const Tfloat + Icc = (Tfloat)atXY(x,y,z,v,out_val), Inc = (Tfloat)atXY(nx,y,z,v,out_val), + Icn = (Tfloat)atXY(x,ny,z,v,out_val), Inn = (Tfloat)atXY(nx,ny,z,v,out_val); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + //! Read a pixel value using linear interpolation and Neumann boundary conditions (first two coordinates). + Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::linear_atXY() : Instance image is empty.", + pixel_type()); + return _linear_atXY(fx,fy,z,v); + } + + Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int v=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx), + nfy = fy<0?0:(fy>height-1?height-1:fy); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx-x, + dy = nfy-y; + const unsigned int + nx = dx>0?x+1:x, + ny = dy>0?y+1:y; + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,v), Inc = (Tfloat)(*this)(nx,y,z,v), + Icn = (Tfloat)(*this)(x,ny,z,v), Inn = (Tfloat)(*this)(nx,ny,z,v); + return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); + } + + //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first coordinate). + Tfloat linear_atX(const float fx, const int y, const int z, const int v, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), nx = x+1; + const float + dx = fx-x; + const Tfloat + Ic = (Tfloat)atX(x,y,z,v,out_val), In = (Tfloat)atXY(nx,y,z,v,out_val); + return Ic + dx*(In-Ic); + } + + //! Read a pixel value using linear interpolation and Neumann boundary conditions (first coordinate). + Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::linear_atX() : Instance image is empty.", + pixel_type()); + return _linear_atX(fx,y,z,v); + } + + Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int v=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx-x; + const unsigned int + nx = dx>0?x+1:x; + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,v), In = (Tfloat)(*this)(nx,y,z,v); + return Ic + dx*(In-Ic); + } + + //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions. + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int v, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2, + y = (int)fy-(fy>=0?0:1), py = y-1, ny = y+1, ay = y+2; + const float + dx = fx-x, dx2 = dx*dx, dx3 = dx2*dx, + dy = fy-y; + const Tfloat + Ipp = (Tfloat)atXY(px,py,z,v,out_val), Icp = (Tfloat)atXY(x,py,z,v,out_val), + Inp = (Tfloat)atXY(nx,py,z,v,out_val), Iap = (Tfloat)atXY(ax,py,z,v,out_val), + Ipc = (Tfloat)atXY(px,y,z,v,out_val), Icc = (Tfloat)atXY(x,y,z,v,out_val), + Inc = (Tfloat)atXY(nx,y,z,v,out_val), Iac = (Tfloat)atXY(ax,y,z,v,out_val), + Ipn = (Tfloat)atXY(px,ny,z,v,out_val), Icn = (Tfloat)atXY(x,ny,z,v,out_val), + Inn = (Tfloat)atXY(nx,ny,z,v,out_val), Ian = (Tfloat)atXY(ax,ny,z,v,out_val), + Ipa = (Tfloat)atXY(px,ay,z,v,out_val), Ica = (Tfloat)atXY(x,ay,z,v,out_val), + Ina = (Tfloat)atXY(nx,ay,z,v,out_val), Iaa = (Tfloat)atXY(ax,ay,z,v,out_val), + valm = cimg::min(cimg::min(Ipp,Icp,Inp,Iap),cimg::min(Ipc,Icc,Inc,Iac),cimg::min(Ipn,Icn,Inn,Ian),cimg::min(Ipa,Ica,Ina,Iaa)), + valM = cimg::max(cimg::max(Ipp,Icp,Inp,Iap),cimg::max(Ipc,Icc,Inc,Iac),cimg::max(Ipn,Icn,Inn,Ian),cimg::max(Ipa,Ica,Ina,Iaa)), + u0p = Icp - Ipp, + u1p = Iap - Inp, + ap = 2*(Icp-Inp) + u0p + u1p, + bp = 3*(Inp-Icp) - 2*u0p - u1p, + u0c = Icc - Ipc, + u1c = Iac - Inc, + ac = 2*(Icc-Inc) + u0c + u1c, + bc = 3*(Inc-Icc) - 2*u0c - u1c, + u0n = Icn - Ipn, + u1n = Ian - Inn, + an = 2*(Icn-Inn) + u0n + u1n, + bn = 3*(Inn-Icn) - 2*u0n - u1n, + u0a = Ica - Ipa, + u1a = Iaa - Ina, + aa = 2*(Ica-Ina) + u0a + u1a, + ba = 3*(Ina-Ica) - 2*u0a - u1a, + valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, + valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, + valn = an*dx3 + bn*dx2 + u0n*dx + Icn, + vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, + u0 = valc - valp, + u1 = vala - valn, + a = 2*(valc-valn) + u0 + u1, + b = 3*(valn-valc) - 2*u0 - u1, + val = a*dy*dy*dy + b*dy*dy + u0*dy + valc; + return valvalM?valM:val); + } + + //! Read a pixel value using cubic interpolation and Neumann boundary conditions. + Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::cubic_atXY() : Instance image is empty.", + pixel_type()); + return _cubic_atXY(fx,fy,z,v); + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int v=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx), + nfy = fy<0?0:(fy>height-1?height-1:fy); + const int + x = (int)nfx, + y = (int)nfy; + const float + dx = nfx-x, dx2 = dx*dx, dx3 = dx2*dx, + dy = nfy-y; + const int + px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2, + py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=dimy()?dimy()-1:y+2; + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,v), Icp = (Tfloat)(*this)(x,py,z,v), + Inp = (Tfloat)(*this)(nx,py,z,v), Iap = (Tfloat)(*this)(ax,py,z,v), + Ipc = (Tfloat)(*this)(px,y,z,v), Icc = (Tfloat)(*this)(x,y,z,v), + Inc = (Tfloat)(*this)(nx,y,z,v), Iac = (Tfloat)(*this)(ax,y,z,v), + Ipn = (Tfloat)(*this)(px,ny,z,v), Icn = (Tfloat)(*this)(x,ny,z,v), + Inn = (Tfloat)(*this)(nx,ny,z,v), Ian = (Tfloat)(*this)(ax,ny,z,v), + Ipa = (Tfloat)(*this)(px,ay,z,v), Ica = (Tfloat)(*this)(x,ay,z,v), + Ina = (Tfloat)(*this)(nx,ay,z,v), Iaa = (Tfloat)(*this)(ax,ay,z,v), + valm = cimg::min(cimg::min(Ipp,Icp,Inp,Iap),cimg::min(Ipc,Icc,Inc,Iac),cimg::min(Ipn,Icn,Inn,Ian),cimg::min(Ipa,Ica,Ina,Iaa)), + valM = cimg::max(cimg::max(Ipp,Icp,Inp,Iap),cimg::max(Ipc,Icc,Inc,Iac),cimg::max(Ipn,Icn,Inn,Ian),cimg::max(Ipa,Ica,Ina,Iaa)), + u0p = Icp - Ipp, + u1p = Iap - Inp, + ap = 2*(Icp-Inp) + u0p + u1p, + bp = 3*(Inp-Icp) - 2*u0p - u1p, + u0c = Icc - Ipc, + u1c = Iac - Inc, + ac = 2*(Icc-Inc) + u0c + u1c, + bc = 3*(Inc-Icc) - 2*u0c - u1c, + u0n = Icn - Ipn, + u1n = Ian - Inn, + an = 2*(Icn-Inn) + u0n + u1n, + bn = 3*(Inn-Icn) - 2*u0n - u1n, + u0a = Ica - Ipa, + u1a = Iaa - Ina, + aa = 2*(Ica-Ina) + u0a + u1a, + ba = 3*(Ina-Ica) - 2*u0a - u1a, + valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, + valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, + valn = an*dx3 + bn*dx2 + u0n*dx + Icn, + vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, + u0 = valc - valp, + u1 = vala - valn, + a = 2*(valc-valn) + u0 + u1, + b = 3*(valn-valc) - 2*u0 - u1, + val = a*dy*dy*dy + b*dy*dy + u0*dy + valc; + return valvalM?valM:val); + } + + //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions (first coordinates). + Tfloat cubic_atX(const float fx, const int y, const int z, const int v, const T out_val) const { + const int + x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2; + const float + dx = fx-x; + const Tfloat + Ip = (Tfloat)atX(px,y,z,v,out_val), Ic = (Tfloat)atX(x,y,z,v,out_val), + In = (Tfloat)atX(nx,y,z,v,out_val), Ia = (Tfloat)atX(ax,y,z,v,out_val), + valm = cimg::min(Ip,In,Ic,Ia), valM = cimg::max(Ip,In,Ic,Ia), + u0 = Ic - Ip, + u1 = Ia - In, + a = 2*(Ic-In) + u0 + u1, + b = 3*(In-Ic) - 2*u0 - u1, + val = a*dx*dx*dx + b*dx*dx + u0*dx + Ic; + return valvalM?valM:val); + } + + //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates). + Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::cubic_atX() : Instance image is empty.", + pixel_type()); + return _cubic_atX(fx,y,z,v); + } + + Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int v=0) const { + const float + nfx = fx<0?0:(fx>width-1?width-1:fx); + const int + x = (int)nfx; + const float + dx = nfx-x; + const int + px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2; + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,v), Ic = (Tfloat)(*this)(x,y,z,v), + In = (Tfloat)(*this)(nx,y,z,v), Ia = (Tfloat)(*this)(ax,y,z,v), + valm = cimg::min(Ip,In,Ic,Ia), valM = cimg::max(Ip,In,Ic,Ia), + u0 = Ic - Ip, + u1 = Ia - In, + a = 2*(Ic-In) + u0 + u1, + b = 3*(In-Ic) - 2*u0 - u1, + val = a*dx*dx*dx + b*dx*dx + u0*dx + Ic; + return valvalM?valM:val); + } + + //! Set a pixel value, with 3D float coordinates, using linear interpolation. + CImg& set_linear_atXYZ(const T& val, const float fx, const float fy=0, const float fz=0, const int v=0, + const bool add=false) { + const int + x = (int)fx-(fx>=0?0:1), nx = x+1, + y = (int)fy-(fy>=0?0:1), ny = y+1, + z = (int)fz-(fz>=0?0:1), nz = z+1; + const float + dx = fx-x, + dy = fy-y, + dz = fz-z; + if (v>=0 && v=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0?0:1), nx = x+1, + y = (int)fy-(fy>=0?0:1), ny = y+1; + const float + dx = fx-x, + dy = fy-y; + if (z>=0 && z=0 && v=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx::min() : Instance image is empty.", + pixel_type()); + const T *ptrmin = data; + T min_value = *ptrmin; + cimg_for(*this,ptr,T) if ((*ptr)::min() : Instance image is empty.", + pixel_type()); + T *ptrmin = data; + T min_value = *ptrmin; + cimg_for(*this,ptr,T) if ((*ptr)::max() : Instance image is empty.", + pixel_type()); + const T *ptrmax = data; + T max_value = *ptrmax; + cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); + return *ptrmax; + } + + //! Return a reference to the maximum pixel value of the instance image + T& max() { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::max() : Instance image is empty.", + pixel_type()); + T *ptrmax = data; + T max_value = *ptrmax; + cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); + return *ptrmax; + } + + //! Return a reference to the minimum pixel value and return also the maximum pixel value. + template + const T& minmax(t& max_val) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.", + pixel_type()); + const T *ptrmin = data; + T min_value = *ptrmin, max_value = min_value; + cimg_for(*this,ptr,T) { + const T val = *ptr; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptrmin; + } + + //! Return a reference to the minimum pixel value and return also the maximum pixel value. + template + T& minmax(t& max_val) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.", + pixel_type()); + T *ptrmin = data; + T min_value = *ptrmin, max_value = min_value; + cimg_for(*this,ptr,T) { + const T val = *ptr; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptrmin; + } + + //! Return a reference to the maximum pixel value and return also the minimum pixel value. + template + const T& maxmin(t& min_val) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.", + pixel_type()); + const T *ptrmax = data; + T max_value = *ptrmax, min_value = max_value; + cimg_for(*this,ptr,T) { + const T val = *ptr; + if (val>max_value) { max_value = val; ptrmax = ptr; } + if (val + T& maxmin(t& min_val) { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.", + pixel_type()); + T *ptrmax = data; + T max_value = *ptrmax, min_value = max_value; + cimg_for(*this,ptr,T) { + const T val = *ptr; + if (val>max_value) { max_value = val; ptrmax = ptr; } + if (val::sum() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + Tfloat res = 0; + cimg_for(*this,ptr,T) res+=*ptr; + return res; + } + + //! Return the mean pixel value of the instance image. + Tfloat mean() const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::mean() : Instance image is empty.", + pixel_type()); + Tfloat val = 0; + cimg_for(*this,ptr,T) val+=*ptr; + return val/size(); + } + + //! Return the variance of the image. + /** + @param variance_method Determines how to calculate the variance + + + + + + + + + +
    0Second moment: + @f$ v = 1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 + = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right) @f$ + with @f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$
    1Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$
    2Least median of squares
    3Least trimmed of squares
    + */ + Tfloat variance(const unsigned int variance_method=1) const { + Tfloat foo; + return variancemean(variance_method,foo); + } + + //! Return the variance and the mean of the image. + template + Tfloat variancemean(const unsigned int variance_method, t& mean) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::variance() : Instance image is empty.", + pixel_type()); + Tfloat variance = 0, average = 0; + const unsigned int siz = size(); + switch (variance_method) { + case 3 : { // Least trimmed of Squares + CImg buf(*this); + const unsigned int siz2 = siz>>1; + { cimg_for(buf,ptrs,Tfloat) { const Tfloat val = *ptrs; (*ptrs)*=val; average+=val; }} + buf.sort(); + Tfloat a = 0; + const Tfloat *ptrs = buf.ptr(); + for (unsigned int j = 0; j buf(*this); + buf.sort(); + const unsigned int siz2 = siz>>1; + const Tfloat med_i = buf[siz2]; + cimg_for(buf,ptrs,Tfloat) { const Tfloat val = *ptrs; *ptrs = cimg::abs(val - med_i); average+=val; } + buf.sort(); + const Tfloat sig = (Tfloat)(1.4828*buf[siz2]); + variance = sig*sig; + } break; + case 1 : { // Least mean square (robust definition) + Tfloat S = 0, S2 = 0; + cimg_for(*this,ptr,T) { const Tfloat val = (Tfloat)*ptr; S+=val; S2+=val*val; } + variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + average = S; + } break; + case 0 :{ // Least mean square (standard definition) + Tfloat S = 0, S2 = 0; + cimg_for(*this,ptr,T) { const Tfloat val = (Tfloat)*ptr; S+=val; S2+=val*val; } + variance = (S2 - S*S/siz)/siz; + average = S; + } break; + default : + throw CImgArgumentException("CImg<%s>::variancemean() : Incorrect parameter 'variance_method = %d' (correct values are 0,1,2 or 3).", + pixel_type(),variance_method); + } + mean = (t)(average/siz); + return variance>0?variance:0; + } + + //! Return the kth smallest element of the image. + // (Adapted from the numerical recipies for CImg) + T kth_smallest(const unsigned int k) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::kth_smallest() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + CImg arr(*this); + unsigned long l = 0, ir = size()-1; + for (;;) { + if (ir<=l+1) { + if (ir==l+1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l+1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]); + if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]); + unsigned long i = l+1, j = ir; + const T pivot = arr[l+1]; + for (;;) { + do ++i; while (arr[i]pivot); + if (j=k) ir=j-1; + if (j<=k) l=i; + } + } + return 0; + } + + //! Compute a statistics vector (min,max,mean,variance,xmin,ymin,zmin,vmin,xmax,ymax,zmax,vmax). + CImg& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).transfer_to(*this); + } + + CImg get_stats(const unsigned int variance_method=1) const { + if (is_empty()) return CImg(); + const unsigned long siz = size(); + const T *const odata = data; + const T *pm = odata, *pM = odata; + Tfloat S = 0, S2 = 0; + T m = *pm, M = m; + cimg_for(*this,ptr,T) { + const T val = *ptr; + const Tfloat fval = (Tfloat)val; + if (valM) { M = val; pM = ptr; } + S+=fval; + S2+=fval*fval; + } + const Tfloat + mean_value = S/siz, + _variance_value = variance_method==0?(S2 - S*S/siz)/siz: + (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0): + variance(variance_method)), + variance_value = _variance_value>0?_variance_value:0; + int + xm = 0, ym = 0, zm = 0, vm = 0, + xM = 0, yM = 0, zM = 0, vM = 0; + contains(*pm,xm,ym,zm,vm); + contains(*pM,xM,yM,zM,vM); + return CImg(1,12).fill((Tfloat)m,(Tfloat)M,mean_value,variance_value, + (Tfloat)xm,(Tfloat)ym,(Tfloat)zm,(Tfloat)vm, + (Tfloat)xM,(Tfloat)yM,(Tfloat)zM,(Tfloat)vM); + } + + //! Return the median value of the image. + T median() const { + const unsigned int s = size(); + const T res = kth_smallest(s>>1); + return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); + } + + //! Compute the MSE (Mean-Squared Error) between two images. + template + Tfloat MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException("CImg<%s>::MSE() : Instance image (%u,%u,%u,%u) and given image (%u,%u,%u,%u) have different dimensions.", + pixel_type(),width,height,depth,dim,img.width,img.height,img.depth,img.dim); + + Tfloat vMSE = 0; + const t* ptr2 = img.end(); + cimg_for(*this,ptr1,T) { + const Tfloat diff = (Tfloat)*ptr1 - (Tfloat)*(--ptr2); + vMSE += diff*diff; + } + vMSE/=img.size(); + return vMSE; + } + + //! Compute the PSNR between two images. + template + Tfloat PSNR(const CImg& img, const Tfloat valmax=(Tfloat)255) const { + const Tfloat vMSE = (Tfloat)cimg_std::sqrt(MSE(img)); + return (vMSE!=0)?(Tfloat)(20*cimg_std::log10(valmax/vMSE)):(Tfloat)(cimg::type::max()); + } + + //! Return the trace of the image, viewed as a matrix. + Tfloat trace() const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::trace() : Instance matrix (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + Tfloat res = 0; + cimg_forX(*this,k) res+=(*this)(k,k); + return res; + } + + //! Return the dot product of the current vector/matrix with the vector/matrix \p img. + template + Tfloat dot(const CImg& img) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::dot() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + if (!img) + throw CImgArgumentException("CImg<%s>::trace() : Specified argument (%u,%u,%u,%u,%p) is empty.", + pixel_type(),img.width,img.height,img.depth,img.dim,img.data); + const unsigned long nb = cimg::min(size(),img.size()); + Tfloat res = 0; + for (unsigned long off = 0; off::det() : Instance matrix (%u,%u,%u,%u,%p) is not square or is empty.", + pixel_type(),width,height,depth,dim,data); + switch (width) { + case 1 : return (Tfloat)((*this)(0,0)); + case 2 : return (Tfloat)((*this)(0,0))*(Tfloat)((*this)(1,1)) - (Tfloat)((*this)(0,1))*(Tfloat)((*this)(1,0)); + case 3 : { + const Tfloat + a = (Tfloat)data[0], d = (Tfloat)data[1], g = (Tfloat)data[2], + b = (Tfloat)data[3], e = (Tfloat)data[4], h = (Tfloat)data[5], + c = (Tfloat)data[6], f = (Tfloat)data[7], i = (Tfloat)data[8]; + return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; + } + default : { + CImg lu(*this); + CImg indx; + bool d; + lu._LU(indx,d); + Tfloat res = d?(Tfloat)1:(Tfloat)-1; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + return 0; + } + + //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf). + Tfloat norm(const int norm_type=2) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::norm() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + Tfloat res = 0; + switch (norm_type) { + case -1 : { + cimg_foroff(*this,off) { + const Tfloat tmp = cimg::abs((Tfloat)data[off]); + if (tmp>res) res = tmp; + } + return res; + } break; + case 1 : { + cimg_foroff(*this,off) res+=cimg::abs((Tfloat)data[off]); + return res; + } break; + case 2 : return (Tfloat)cimg_std::sqrt(dot(*this)); break; + default : + throw CImgArgumentException("CImg<%s>::norm() : Incorrect parameter 'norm_type=%d' (correct values are -1,1 or 2).", + pixel_type(),norm_type); + } + return 0; + } + + //! Return a C-string containing the values of the instance image. + CImg value_string(const char separator=',', const unsigned int max_size=0) const { + if (is_empty()) return CImg(1,1,1,1,0); + const unsigned int siz = (unsigned int)size(); + CImgList items; + char item[256] = { 0 }; + const T *ptrs = ptr(); + for (unsigned int off = 0; off::format(),cimg::type::format(*(ptrs++))); + const int l = cimg::strlen(item); + items.insert(CImg(item,l+1)); + items[items.size-1](l) = separator; + } + cimg_std::sprintf(item,cimg::type::format(),cimg::type::format(*ptrs)); + items.insert(CImg(item,cimg::strlen(item)+1)); + CImg res = items.get_append('x'); + if (max_size) { res.crop(0,max_size); res(max_size) = 0; } + return res; + } + + //! Display informations about the image on the standard error output. + /** + \param title Name for the considered image (optional). + \param display_stats Compute and display image statistics (optional). + **/ + const CImg& print(const char *title=0, const bool display_stats=true) const { + int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; + static CImg st; + if (!is_empty() && display_stats) { + st = get_stats(); + xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; + xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; + } + const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1; + const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = width-1; + char ntitle[64] = { 0 }; + if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); + cimg_std::fprintf(cimg_stdout,"%s: this = %p, size = (%u,%u,%u,%u) [%lu %s], data = (%s*)%p (%s) = [ ", + title?title:ntitle,(void*)this,width,height,depth,dim, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kb":"Mb"), + pixel_type(),(void*)data,is_shared?"shared":"not shared"); + if (!is_empty()) cimg_foroff(*this,off) { + cimg_std::fprintf(cimg_stdout,cimg::type::format(),cimg::type::format(data[off])); + if (off!=siz1) cimg_std::fprintf(cimg_stdout,"%s",off%width==width1?" ; ":" "); + if (off==7 && siz>16) { off = siz1-8; if (off!=7) cimg_std::fprintf(cimg_stdout,"... "); } + } + if (!is_empty() && display_stats) + cimg_std::fprintf(cimg_stdout," ], min = %g, max = %g, mean = %g, std = %g, coords(min) = (%u,%u,%u,%u), coords(max) = (%u,%u,%u,%u).\n", + st[0],st[1],st[2],cimg_std::sqrt(st[3]),xm,ym,zm,vm,xM,yM,zM,vM); + else cimg_std::fprintf(cimg_stdout,"%s].\n",is_empty()?"":" "); + return *this; + } + + //@} + //------------------------------------------ + // + //! \name Arithmetic and Boolean Operators + //@{ + //------------------------------------------ + + //! Assignment operator. + /** + This operator assigns a copy of the input image \p img to the current instance image. + \param img The input image to copy. + \remark + - This operator is strictly equivalent to the function assign(const CImg< t >&) and has exactly the same properties. + **/ + template + CImg& operator=(const CImg& img) { + return assign(img); + } + + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Assign values of a C-array to the instance image. + /** + \param buf Pointer to a C-style array having a size of (at least) this->size(). + + - Replace pixel values by the content of the array \c buf. + - Warning : the value types in the array and in the image must be the same. + + \par example: + \code + float tab[4*4] = { 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16 }; // Define a 4x4 matrix in C-style. + CImg matrice(4,4); // Define a 4x4 greyscale image. + matrice = tab; // Fill the image by the values in tab. + \endcode + **/ + CImg& operator=(const T *buf) { + return assign(buf,width,height,depth,dim); + } + + //! Assign a value to each image pixel of the instance image. + CImg& operator=(const T val) { + return fill(val); + } + + //! Operator+ + /** + \remark + - This operator can be used to get a non-shared copy of an image. + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Operator+=; +#ifdef cimg_use_visualcpp6 + CImg& operator+=(const T val) +#else + template + CImg& operator+=(const t val) +#endif + { + cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)+val); + return *this; + } + + //! Operator+= + template + CImg& operator+=(const CImg& img) { + if (is_overlapped(img)) return *this+=+img; + const unsigned int smin = cimg::min(size(),img.size()); + t *ptrs = img.data + smin; + for (T *ptrd = data + smin; ptrd>data; --ptrd, (*ptrd)=(T)((*ptrd)+(*(--ptrs)))) {} + return *this; + } + + //! Operator++ (prefix) + CImg& operator++() { + cimg_for(*this,ptr,T) ++(*ptr); + return *this; + } + + //! Operator++ (postfix) + CImg operator++(int) { + const CImg copy(*this,false); + ++*this; + return copy; + } + + //! Operator-. + CImg operator-() const { + return CImg(width,height,depth,dim,0)-=*this; + } + + //! Operator-=. +#ifdef cimg_use_visualcpp6 + CImg& operator-=(const T val) +#else + template + CImg& operator-=(const t val) +#endif + { + cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)-val); + return *this; + } + + //! Operator-=. + template + CImg& operator-=(const CImg& img) { + if (is_overlapped(img)) return *this-=+img; + const unsigned int smin = cimg::min(size(),img.size()); + t *ptrs = img.data+smin; + for (T *ptrd = data+smin; ptrd>data; --ptrd, (*ptrd) = (T)((*ptrd)-(*(--ptrs)))) {} + return *this; + } + + //! Operator-- (prefix). + CImg& operator--() { + cimg_for(*this,ptr,T) *ptr = *ptr-(T)1; + return *this; + } + + //! Operator-- (postfix). + CImg operator--(int) { + CImg copy(*this,false); + --*this; + return copy; + } + + //! Operator*=. +#ifdef cimg_use_visualcpp6 + CImg& operator*=(const double val) +#else + template + CImg& operator*=(const t val) +#endif + { + cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)*val); + return *this; + } + + //! Operator*=. + template + CImg& operator*=(const CImg& img) { + return ((*this)*img).transfer_to(*this); + } + + //! Operator/=. +#ifdef cimg_use_visualcpp6 + CImg& operator/=(const double val) +#else + template + CImg& operator/=(const t val) +#endif + { + cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)/val); + return *this; + } + + //! Operator/=. + template + CImg& operator/=(const CImg& img) { + return assign(*this*img.get_invert()); + } + + //! Modulo. + template + CImg::type> operator%(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false)%=img; + } + + //! Modulo. + CImg operator%(const T val) const { + return (+*this)%=val; + } + + //! In-place modulo. + CImg& operator%=(const T val) { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg::mod(*ptr,val); + return *this; + } + + //! In-place modulo. + template + CImg& operator%=(const CImg& img) { + if (is_overlapped(img)) return *this%=+img; + typedef typename cimg::superset::type Tt; + const unsigned int smin = cimg::min(size(),img.size()); + const t *ptrs = img.data + smin; + for (T *ptrd = data + smin; ptrd>data; ) { + T& val = *(--ptrd); + val = (T)cimg::mod((Tt)val,(Tt)*(--ptrs)); + } + return *this; + } + + //! Bitwise AND. + template + CImg::type> operator&(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false)&=img; + } + + //! Bitwise AND. + CImg operator&(const T val) const { + return (+*this)&=val; + } + + //! In-place bitwise AND. + template + CImg& operator&=(const CImg& img) { + if (is_overlapped(img)) return *this&=+img; + const unsigned int smin = cimg::min(size(),img.size()); + const t *ptrs = img.data + smin; + for (T *ptrd = data + smin; ptrd>data; ) { + T& val = *(--ptrd); + val = (T)((unsigned long)val & (unsigned long)*(--ptrs)); + } + return *this; + } + + //! In-place bitwise AND. + CImg& operator&=(const T val) { + cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr & (unsigned long)val); + return *this; + } + + //! Bitwise OR. + template + CImg::type> operator|(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false)|=img; + } + + //! Bitwise OR. + CImg operator|(const T val) const { + return (+*this)|=val; + } + + //! In-place bitwise OR. + template + CImg& operator|=(const CImg& img) { + if (is_overlapped(img)) return *this|=+img; + const unsigned int smin = cimg::min(size(),img.size()); + const t *ptrs = img.data + smin; + for (T *ptrd = data + smin; ptrd>data; ) { + T& val = *(--ptrd); + val = (T)((unsigned long)val | (unsigned long)*(--ptrs)); + } + return *this; + } + + //! In-place bitwise OR. + CImg& operator|=(const T val) { + cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr | (unsigned long)val); + return *this; + } + + //! Bitwise XOR. + template + CImg::type> operator^(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false)^=img; + } + + //! Bitwise XOR. + CImg operator^(const T val) const { + return (+*this)^=val; + } + + //! In-place bitwise XOR. + template + CImg& operator^=(const CImg& img) { + if (is_overlapped(img)) return *this^=+img; + const unsigned int smin = cimg::min(size(),img.size()); + const t *ptrs = img.data + smin; + for (T *ptrd = data+smin; ptrd>data; ) { + T& val = *(--ptrd); + val =(T)((unsigned long)val ^ (unsigned long)*(--ptrs)); + } + return *this; + } + + //! In-place bitwise XOR. + CImg& operator^=(const T val) { + cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr ^ (unsigned long)val); + return *this; + } + + //! Bitwise NOT. + CImg operator~() const { + CImg res(width,height,depth,dim); + const T *ptrs = end(); + cimg_for(res,ptrd,T) { const unsigned long val = (unsigned long)*(--ptrs); *ptrd = (T)~val; } + return res; + } + + //! Bitwise left shift. + CImg& operator<<=(const int n) { + cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)< operator<<(const int n) const { + return (+*this)<<=n; + } + + //! Bitwise right shift. + CImg& operator>>=(const int n) { + cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)>>n); + return *this; + } + + //! Bitwise right shift. + CImg operator>>(const int n) const { + return (+*this)>>=n; + } + + //! Boolean equality. + template + bool operator==(const CImg& img) const { + const unsigned int siz = size(); + bool vequal = true; + if (siz!=img.size()) return false; + t *ptrs = img.data + siz; + for (T *ptrd = data + siz; vequal && ptrd>data; vequal = vequal && ((*(--ptrd))==(*(--ptrs)))) {} + return vequal; + } + + //! Boolean difference. + template + bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Return a list of two images { *this, img }. + template + CImgList::type> operator<<(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImgList(*this,img); + } + + //! Return a copy of \p list, where image *this has been inserted at first position. + template + CImgList::type> operator<<(const CImgList& list) const { + typedef typename cimg::superset::type Tt; + return CImgList(list).insert(*this,0); + } + + //! Return a list of two images { *this, img }. + template + CImgList::type> operator>>(const CImg& img) const { + return (*this)< + CImgList& operator>>(const CImgList& list) const { + return list.insert(*this,0); + } + + //! Display an image into a CImgDisplay. + const CImg& operator>>(CImgDisplay& disp) const { + return display(disp); + } + + //@} + //--------------------------------------- + // + //! \name Usual Mathematics Functions + //@{ + //--------------------------------------- + + //! Apply a R->R function on all pixel values. + template + CImg& apply(t& func) { + cimg_for(*this,ptr,T) *ptr = func(*ptr); + return *this; + } + + template + CImg get_apply(t& func) const { + return (+*this).apply(func); + } + + //! Pointwise multiplication between two images. + template + CImg& mul(const CImg& img) { + if (is_overlapped(img)) return mul(+img); + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd + CImg::type> get_mul(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false).mul(img); + } + + //! Pointwise division between two images. + template + CImg& div(const CImg& img) { + if (is_overlapped(img)) return div(+img); + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd + CImg::type> get_div(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false).div(img); + } + + //! Pointwise max operator between two images. + template + CImg& max(const CImg& img) { + if (is_overlapped(img)) return max(+img); + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd + CImg::type> get_max(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false).max(img); + } + + //! Pointwise max operator between an image and a value. + CImg& max(const T val) { + cimg_for(*this,ptr,T) (*ptr) = cimg::max(*ptr,val); + return *this; + } + + CImg get_max(const T val) const { + return (+*this).max(val); + } + + //! Pointwise min operator between two images. + template + CImg& min(const CImg& img) { + if (is_overlapped(img)) return min(+img); + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd + CImg::type> get_min(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this,false).min(img); + } + + //! Pointwise min operator between an image and a value. + CImg& min(const T val) { + cimg_for(*this,ptr,T) (*ptr) = cimg::min(*ptr,val); + return *this; + } + + CImg get_min(const T val) const { + return (+*this).min(val); + } + + //! Compute the square value of each pixel. + CImg& sqr() { + cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)(val*val); }; + return *this; + } + + CImg get_sqr() const { + return CImg(*this,false).sqr(); + } + + //! Compute the square root of each pixel value. + CImg& sqrt() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sqrt((double)(*ptr)); + return *this; + } + + CImg get_sqrt() const { + return CImg(*this,false).sqrt(); + } + + //! Compute the exponential of each pixel value. + CImg& exp() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::exp((double)(*ptr)); + return *this; + } + + CImg get_exp() const { + return CImg(*this,false).exp(); + } + + //! Compute the log of each each pixel value. + CImg& log() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log((double)(*ptr)); + return *this; + } + + CImg get_log() const { + return CImg(*this,false).log(); + } + + //! Compute the log10 of each each pixel value. + CImg& log10() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::log10((double)(*ptr)); + return *this; + } + + CImg get_log10() const { + return CImg(*this,false).log10(); + } + + //! Compute the power by p of each pixel value. + CImg& pow(const double p) { + if (p==0) return fill(1); + if (p==0.5) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)cimg_std::sqrt((double)val); } return *this; } + if (p==1) return *this; + if (p==2) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val; } return *this; } + if (p==3) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val*val; } return *this; } + if (p==4) { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = val*val*val*val; } return *this; } + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::pow((double)(*ptr),p); + return *this; + } + + CImg get_pow(const double p) const { + return CImg(*this,false).pow(p); + } + + //! Compute the power of each pixel value. + template + CImg& pow(const CImg& img) { + if (is_overlapped(img)) return pow(+img); + t *ptrs = img.data; + T *ptrf = data + cimg::min(size(),img.size()); + for (T* ptrd = data; ptrd + CImg get_pow(const CImg& img) const { + return CImg(*this,false).pow(img); + } + + //! Compute the absolute value of each pixel value. + CImg& abs() { + cimg_for(*this,ptr,T) (*ptr) = cimg::abs(*ptr); + return *this; + } + + CImg get_abs() const { + return CImg(*this,false).abs(); + } + + //! Compute the cosinus of each pixel value. + CImg& cos() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::cos((double)(*ptr)); + return *this; + } + + CImg get_cos() const { + return CImg(*this,false).cos(); + } + + //! Compute the sinus of each pixel value. + CImg& sin() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::sin((double)(*ptr)); + return *this; + } + + CImg get_sin() const { + return CImg(*this,false).sin(); + } + + //! Compute the tangent of each pixel. + CImg& tan() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::tan((double)(*ptr)); + return *this; + } + + CImg get_tan() const { + return CImg(*this,false).tan(); + } + + //! Compute the arc-cosine of each pixel value. + CImg& acos() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::acos((double)(*ptr)); + return *this; + } + + CImg get_acos() const { + return CImg(*this,false).acos(); + } + + //! Compute the arc-sinus of each pixel value. + CImg& asin() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::asin((double)(*ptr)); + return *this; + } + + CImg get_asin() const { + return CImg(*this,false).asin(); + } + + //! Compute the arc-tangent of each pixel. + CImg& atan() { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg_std::atan((double)(*ptr)); + return *this; + } + + CImg get_atan() const { + return CImg(*this,false).atan(); + } + + //! Compute image with rounded pixel values. + /** + \param x Rounding precision. + \param rounding_type Roundin type, can be 0 (nearest), 1 (forward), -1(backward). + **/ + CImg& round(const float x, const int rounding_type=0) { + cimg_for(*this,ptr,T) (*ptr) = (T)cimg::round(*ptr,x,rounding_type); + return *this; + } + + CImg get_round(const float x, const unsigned int rounding_type=0) const { + return (+*this).round(x,rounding_type); + } + + //! Fill the instance image with random values between specified range. + CImg& rand(const T val_min, const T val_max) { + const float delta = (float)val_max - (float)val_min; + cimg_for(*this,ptr,T) *ptr = (T)(val_min + cimg::rand()*delta); + return *this; + } + + CImg get_rand(const T val_min, const T val_max) const { + return (+*this).rand(val_min,val_max); + } + + //@} + //----------------------------------- + // + //! \name Usual Image Transformations + //@{ + //----------------------------------- + + //! Fill an image by a value \p val. + /** + \param val = fill value + \note All pixel values of the instance image will be initialized by \p val. + **/ + CImg& fill(const T val) { + if (is_empty()) return *this; + if (val && sizeof(T)!=1) cimg_for(*this,ptr,T) *ptr = val; + else cimg_std::memset(data,(int)val,size()*sizeof(T)); + return *this; + } + + CImg get_fill(const T val) const { + return CImg(width,height,depth,dim).fill(val); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively. + CImg& fill(const T val0, const T val1) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-1; + for (ptr = data; ptr get_fill(const T val0, const T val1) const { + return CImg(width,height,depth,dim).fill(val0,val1); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2. + CImg& fill(const T val0, const T val1, const T val2) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-2; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3. + CImg& fill(const T val0, const T val1, const T val2, const T val3) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-3; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-4; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4); + } + + //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4 and \a val5. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-5; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-6; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-7; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-8; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-9; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-10; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-11; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-12; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-13; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-14; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13,val14); + } + + //! Fill sequentially pixel values. + CImg& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14, const T val15) { + if (is_empty()) return *this; + T *ptr, *ptr_end = end()-15; + for (ptr = data; ptr get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6, + const T val7, const T val8, const T val9, const T val10, const T val11, const T val12, + const T val13, const T val14, const T val15) const { + return CImg(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12, + val13,val14,val15); + } + + //! Fill image values according to the values found in the specified string. + CImg& fill(const char *const values, const bool repeat_pattern) { + if (is_empty() || !values) return *this; + T *ptrd = data, *ptr_end = data + size(); + const char *nvalues = values; + const unsigned int siz = size(); + char cval[64] = { 0 }, sep = 0; + int err = 0; double val = 0; unsigned int nb = 0; + while ((err=cimg_std::sscanf(nvalues,"%63[ \n\t0-9e.+-]%c",cval,&sep))>0 && + cimg_std::sscanf(cval,"%lf",&val)>0 && nb get_fill(const char *const values, const bool repeat_pattern) const { + return repeat_pattern?CImg(width,height,depth,dim).fill(values,repeat_pattern):(+*this).fill(values,repeat_pattern); + } + + //! Fill image values according to the values found in the specified image. + template + CImg& fill(const CImg& values, const bool repeat_pattern=true) { + if (is_empty() || !values) return *this; + T *ptrd = data, *ptrd_end = ptrd + size(); + for (t *ptrs = values.data, *ptrs_end = ptrs + values.size(); ptrs + CImg get_fill(const CImg& values, const bool repeat_pattern=true) const { + return repeat_pattern?CImg(width,height,depth,dim).fill(values,repeat_pattern):(+*this).fill(values,repeat_pattern); + } + + //! Fill image values along the X-axis at the specified pixel position (y,z,v). + CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int v, const int a0, ...) { +#define _cimg_fill1(x,y,z,v,off,siz,t) { \ + va_list ap; va_start(ap,a0); T *ptrd = ptr(x,y,z,v); *ptrd = (T)a0; \ + for (unsigned int k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int v, const double a0, ...) { + if (y& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const int a0, ...) { + if (x& fillY(const unsigned int x, const unsigned int z, const unsigned int v, const double a0, ...) { + if (x& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const int a0, ...) { + const unsigned int wh = width*height; + if (x& fillZ(const unsigned int x, const unsigned int y, const unsigned int v, const double a0, ...) { + const unsigned int wh = width*height; + if (x& fillV(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { + const unsigned int whz = width*height*depth; + if (x& fillV(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { + const unsigned int whz = width*height*depth; + if (x& normalize(const T a, const T b) { + if (is_empty()) return *this; + const T na = a get_normalize(const T a, const T b) const { + return (+*this).normalize(a,b); + } + + //! Cut pixel values between \a a and \a b. + CImg& cut(const T a, const T b) { + if (is_empty()) return *this; + const T na = anb)?nb:*ptr); + return *this; + } + + CImg get_cut(const T a, const T b) const { + return (+*this).cut(a,b); + } + + //! Quantize pixel values into \n levels. + CImg& quantize(const unsigned int n, const bool keep_range=true) { + if (is_empty()) return *this; + if (!n) + throw CImgArgumentException("CImg<%s>::quantize() : Cannot quantize image to 0 values.", + pixel_type()); + Tfloat m, M = (Tfloat)maxmin(m), range = M - m; + if (range>0) { + if (keep_range) cimg_for(*this,ptr,T) { + const unsigned int val = (unsigned int)((*ptr-m)*n/range); + *ptr = (T)(m + cimg::min(val,n-1)*range/n); + } else cimg_for(*this,ptr,T) { + const unsigned int val = (unsigned int)((*ptr-m)*n/range); + *ptr = (T)cimg::min(val,n-1); + } + } + return *this; + } + + CImg get_quantize(const unsigned int n, const bool keep_range=true) const { + return (+*this).quantize(n,keep_range); + } + + //! Threshold the image. + /** + \param value Threshold value. + \param soft Enable soft thresholding. + \param strict Tells if the threshold is strict. + **/ + CImg& threshold(const T value, const bool soft=false, const bool strict=false) { + if (is_empty()) return *this; + if (strict) { + if (soft) cimg_for(*this,ptr,T) { const T v = *ptr; *ptr = v>value?(T)(v-value):v<-value?(T)(v+value):(T)0; } + else cimg_for(*this,ptr,T) *ptr = *ptr>value?(T)1:(T)0; + } else { + if (soft) cimg_for(*this,ptr,T) { const T v = *ptr; *ptr = v>=value?(T)(v-value):v<=-value?(T)(v+value):(T)0; } + else cimg_for(*this,ptr,T) *ptr = *ptr>=value?(T)1:(T)0; + } + return *this; + } + + CImg get_threshold(const T value, const bool soft=false, const bool strict=false) const { + return (+*this).threshold(value,soft,strict); + } + + //! Rotate an image. + /** + \param angle = rotation angle (in degrees). + \param cond = rotation type. can be : + - 0 = zero-value at borders + - 1 = nearest pixel. + - 2 = Fourier style. + \note Returned image will probably have a different size than the instance image *this. + **/ + CImg& rotate(const float angle, const unsigned int border_conditions=3, const unsigned int interpolation=1) { + return get_rotate(angle,border_conditions,interpolation).transfer_to(*this); + } + + CImg get_rotate(const float angle, const unsigned int border_conditions=3, const unsigned int interpolation=1) const { + if (is_empty()) return *this; + CImg dest; + const float nangle = cimg::mod(angle,360.0f); + if (border_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles + const int wm1 = dimx()-1, hm1 = dimy()-1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { + dest.assign(height,width,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(y,hm1-x,z,v); + } break; + case 2 : { + dest.assign(width,height,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-x,hm1-y,z,v); + } break; + case 3 : { + dest.assign(height,width,depth,dim); + cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-y,x,z,v); + } break; + default : + return *this; + } + } else { // generic version + const float + rad = (float)(nangle*cimg::valuePI/180.0), + ca = (float)cimg_std::cos(rad), + sa = (float)cimg_std::sin(rad), + ux = cimg::abs(width*ca), uy = cimg::abs(width*sa), + vx = cimg::abs(height*sa), vy = cimg::abs(height*ca), + w2 = 0.5f*width, h2 = 0.5f*height, + dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy); + dest.assign((int)(ux+vx), (int)(uy+vy),depth,dim); + switch (border_conditions) { + case 0 : { + switch (interpolation) { + case 2 : { + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v,0); + } break; + case 1 : { + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v,0); + } break; + default : { + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v,0); + } + } + } break; + case 1 : { + switch (interpolation) { + case 2 : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v); + break; + case 1 : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,v); + break; + default : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v); + } + } break; + case 2 : { + switch (interpolation) { + case 2 : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)dimx()), + cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)dimy()),z,v); + break; + case 1 : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (T)linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)dimx()), + cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)dimy()),z,v); + break; + default : + cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) + dest(x,y,z,v) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),dimx()), + cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),dimy()),z,v); + } + } break; + default : + throw CImgArgumentException("CImg<%s>::get_rotate() : Invalid border conditions %d (should be 0,1 or 2).", + pixel_type(),border_conditions); + } + } + return dest; + } + + //! Rotate an image around a center point (\c cx,\c cy). + /** + \param angle = rotation angle (in degrees). + \param cx = X-coordinate of the rotation center. + \param cy = Y-coordinate of the rotation center. + \param zoom = zoom. + \param cond = rotation type. can be : + - 0 = zero-value at borders + - 1 = repeat image at borders + - 2 = zero-value at borders and linear interpolation + **/ + CImg& rotate(const float angle, const float cx, const float cy, const float zoom, + const unsigned int border_conditions=3, const unsigned int interpolation=1) { + return get_rotate(angle,cx,cy,zoom,border_conditions,interpolation).transfer_to(*this); + } + + CImg get_rotate(const float angle, const float cx, const float cy, const float zoom, + const unsigned int border_conditions=3, const unsigned int interpolation=1) const { + if (interpolation>2) + throw CImgArgumentException("CImg<%s>::get_rotate() : Invalid interpolation parameter %d (should be {0=none, 1=linear or 2=cubic}).", + pixel_type(),interpolation); + if (is_empty()) return *this; + CImg dest(width,height,depth,dim); + const float nangle = cimg::mod(angle,360.0f); + if (border_conditions!=1 && zoom==1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { + dest.fill(0); + const unsigned int + xmin = cimg::max(0,(dimx()-dimy())/2), xmax = cimg::min(width,xmin+height), + ymin = cimg::max(0,(dimy()-dimx())/2), ymax = cimg::min(height,ymin+width), + xoff = xmin + cimg::min(0,(dimx()-dimy())/2), + yoff = ymin + cimg::min(0,(dimy()-dimx())/2); + cimg_forZV(dest,z,v) for (unsigned int y = ymin; y::get_rotate() : Incorrect border conditions %d (should be 0,1 or 2).", + pixel_type(),border_conditions); + } + } + return dest; + } + + //! Resize an image. + /** + \param pdx Number of columns (new size along the X-axis). + \param pdy Number of rows (new size along the Y-axis). + \param pdz Number of slices (new size along the Z-axis). + \param pdv Number of vector-channels (new size along the V-axis). + \param interpolation_type Method of interpolation : + - -1 = no interpolation : raw memory resizing. + - 0 = no interpolation : additional space is filled according to \p border_condition. + - 1 = bloc interpolation (nearest point). + - 2 = moving average interpolation. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \param border_condition Border condition type. + \param center Set centering type (only if \p interpolation_type=0). + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int pdx, const int pdy=-100, const int pdz=-100, const int pdv=-100, + const int interpolation_type=1, const int border_condition=-1, const bool center=false) { + if (!pdx || !pdy || !pdz || !pdv) return assign(); + const unsigned int + tdx = pdx<0?-pdx*width/100:pdx, + tdy = pdy<0?-pdy*height/100:pdy, + tdz = pdz<0?-pdz*depth/100:pdz, + tdv = pdv<0?-pdv*dim/100:pdv, + dx = tdx?tdx:1, + dy = tdy?tdy:1, + dz = tdz?tdz:1, + dv = tdv?tdv:1; + if (width==dx && height==dy && depth==dz && dim==dv) return *this; + if (interpolation_type==-1 && dx*dy*dz*dv==size()) { + width = dx; height = dy; depth = dz; dim = dv; + return *this; + } + return get_resize(dx,dy,dz,dv,interpolation_type,border_condition,center).transfer_to(*this); + } + + CImg get_resize(const int pdx, const int pdy=-100, const int pdz=-100, const int pdv=-100, + const int interpolation_type=1, const int border_condition=-1, const bool center=false) const { + if (!pdx || !pdy || !pdz || !pdv) return CImg(); + const unsigned int + tdx = pdx<0?-pdx*width/100:pdx, + tdy = pdy<0?-pdy*height/100:pdy, + tdz = pdz<0?-pdz*depth/100:pdz, + tdv = pdv<0?-pdv*dim/100:pdv, + dx = tdx?tdx:1, + dy = tdy?tdy:1, + dz = tdz?tdz:1, + dv = tdv?tdv:1; + if (width==dx && height==dy && depth==dz && dim==dv) return +*this; + if (is_empty()) return CImg(dx,dy,dz,dv,0); + + CImg res; + + switch (interpolation_type) { + case -1 : // Raw resizing + cimg_std::memcpy(res.assign(dx,dy,dz,dv,0).data,data,sizeof(T)*cimg::min(size(),(long unsigned int)dx*dy*dz*dv)); + break; + + case 0 : { // No interpolation + const unsigned int bx = width-1, by = height-1, bz = depth-1, bv = dim-1; + res.assign(dx,dy,dz,dv); + switch (border_condition) { + case 1 : { + if (center) { + const int + x0 = (res.dimx()-dimx())/2, + y0 = (res.dimy()-dimy())/2, + z0 = (res.dimz()-dimz())/2, + v0 = (res.dimv()-dimv())/2, + x1 = x0 + (int)bx, + y1 = y0 + (int)by, + z1 = z0 + (int)bz, + v1 = v0 + (int)bv; + res.draw_image(x0,y0,z0,v0,*this); + cimg_for_outXYZV(res,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) res(x,y,z,v) = _atXYZV(x-x0,y-y0,z-z0,v-v0); + } else { + res.draw_image(*this); + cimg_for_outXYZV(res,0,0,0,0,bx,by,bz,bv,x,y,z,v) res(x,y,z,v) = _atXYZV(x,y,z,v); + } + } break; + case 2 : { + int nx0 = 0, ny0 = 0, nz0 = 0, nv0 = 0; + if (center) { + const int + x0 = (res.dimx()-dimx())/2, + y0 = (res.dimy()-dimy())/2, + z0 = (res.dimz()-dimz())/2, + v0 = (res.dimv()-dimv())/2; + nx0 = x0>0?x0-(1+x0/width)*width:x0; + ny0 = y0>0?y0-(1+y0/height)*height:y0; + nz0 = z0>0?z0-(1+z0/depth)*depth:z0; + nv0 = v0>0?v0-(1+v0/dim)*dim:v0; + } + for (int k = nv0; k<(int)dv; k+=dimv()) + for (int z = nz0; z<(int)dz; z+=dimz()) + for (int y = ny0; y<(int)dy; y+=dimy()) + for (int x = nx0; x<(int)dx; x+=dimx()) res.draw_image(x,y,z,k,*this); + } break; + default : { + res.fill(0); + if (center) res.draw_image((res.dimx()-dimx())/2,(res.dimy()-dimy())/2,(res.dimz()-dimz())/2,(res.dimv()-dimv())/2,*this); + else res.draw_image(*this); + } + } + } break; + + case 1 : { // Nearest-neighbor interpolation + res.assign(dx,dy,dz,dv); + unsigned int + *const offx = new unsigned int[dx], + *const offy = new unsigned int[dy+1], + *const offz = new unsigned int[dz+1], + *const offv = new unsigned int[dv+1], + *poffx, *poffy, *poffz, *poffv, + curr, old; + const unsigned int wh = width*height, whd = width*height*depth, rwh = dx*dy, rwhd = dx*dy*dz; + poffx = offx; curr = 0; { cimg_forX(res,x) { old=curr; curr=(x+1)*width/dx; *(poffx++) = (unsigned int)curr-(unsigned int)old; }} + poffy = offy; curr = 0; { cimg_forY(res,y) { old=curr; curr=(y+1)*height/dy; *(poffy++) = width*((unsigned int)curr-(unsigned int)old); }} *poffy=0; + poffz = offz; curr = 0; { cimg_forZ(res,z) { old=curr; curr=(z+1)*depth/dz; *(poffz++) = wh*((unsigned int)curr-(unsigned int)old); }} *poffz=0; + poffv = offv; curr = 0; { cimg_forV(res,k) { old=curr; curr=(k+1)*dim/dv; *(poffv++) = whd*((unsigned int)curr-(unsigned int)old); }} *poffv=0; + T *ptrd = res.data; + const T* ptrv = data; + poffv = offv; + for (unsigned int k=0; k tmp(dx,height,depth,dim,0); + for (unsigned int a = width*dx, b = width, c = dx, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)/=width; ++t; b = width; } + if (!c) { ++s; c = dx; } + } + tmp.transfer_to(res); + instance_first = false; + } + if (dy!=height) { + CImg tmp(dx,dy,depth,dim,0); + for (unsigned int a = height*dy, b = height, c = dy, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)/=height; ++t; b = height; } + if (!c) { ++s; c = dy; } + } + tmp.transfer_to(res); + instance_first = false; + } + if (dz!=depth) { + CImg tmp(dx,dy,dz,dim,0); + for (unsigned int a = depth*dz, b = depth, c = dz, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)/=depth; ++t; b = depth; } + if (!c) { ++s; c = dz; } + } + tmp.transfer_to(res); + instance_first = false; + } + if (dv!=dim) { + CImg tmp(dx,dy,dz,dv,0); + for (unsigned int a = dim*dv, b = dim, c = dv, s = 0, t = 0; a; ) { + const unsigned int d = cimg::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; + else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; + if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=dim; ++t; b = dim; } + if (!c) { ++s; c = dv; } + } + tmp.transfer_to(res); + instance_first = false; + } + } break; + + case 3 : { // Linear interpolation + const unsigned int dimmax = cimg::max(dx,dy,dz,dv); + const float + sx = (border_condition<0 && dx>width )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, + sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, + sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, + sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; + + unsigned int *const off = new unsigned int[dimmax], *poff; + float *const foff = new float[dimmax], *pfoff, old, curr; + CImg resx, resy, resz, resv; + T *ptrd; + + if (dx!=width) { + if (width==1) resx = get_resize(dx,height,depth,dim,1,0); + else { + resx.assign(dx,height,depth,dim); + curr = old = 0; poff = off; pfoff = foff; + cimg_forX(resx,x) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sx; *(poff++) = (unsigned int)curr-(unsigned int)old; } + ptrd = resx.data; + const T *ptrs0 = data; + cimg_forYZV(resx,y,z,k) { + poff = off; pfoff = foff; + const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (width-1); + cimg_forX(resx,x) { + const float alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrswidth )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, + sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, + sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, + sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; + res.assign(dx,dy,dz,dv); + T *ptrd = res.ptr(); + float cx, cy, cz, ck = 0; + cimg_forV(res,k) { cz = 0; + cimg_forZ(res,z) { cy = 0; + cimg_forY(res,y) { cx = 0; + cimg_forX(res,x) { + *(ptrd++) = (T)(border_condition?_cubic_atXY(cx,cy,(int)cz,(int)ck):cubic_atXY(cx,cy,(int)cz,(int)ck,0)); + cx+=sx; + } cy+=sy; + } cz+=sz; + } ck+=sv; + } + } break; + + default : // Invalid interpolation method + throw CImgArgumentException("CImg<%s>::resize() : Invalid interpolation_type %d " + "(should be { -1=raw, 0=zero, 1=nearest, 2=average, 3=linear, 4=grid, 5=bicubic}).", + pixel_type(),interpolation_type); + } + return res; + } + + //! Resize an image. + /** + \param src Image giving the geometry of the resize. + \param interpolation_type Interpolation method : + - 1 = raw memory + - 0 = no interpolation : additional space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + \param border_condition Border condition type. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + template + CImg& resize(const CImg& src, const int interpolation_type=1, + const int border_condition=-1, const bool center=false) { + return resize(src.width,src.height,src.depth,src.dim,interpolation_type,border_condition,center); + } + + template + CImg get_resize(const CImg& src, const int interpolation_type=1, + const int border_condition=-1, const bool center=false) const { + return get_resize(src.width,src.height,src.depth,src.dim,interpolation_type,border_condition,center); + } + + //! Resize an image. + /** + \param disp = Display giving the geometry of the resize. + \param interpolation_type = Resizing type : + - 0 = no interpolation : additional space is filled with 0. + - 1 = bloc interpolation (nearest point). + - 2 = mosaic : image is repeated if necessary. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = bi-cubic interpolation. + - 6 = moving average (best quality for photographs) + \param border_condition Border condition type. + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const CImgDisplay& disp, const int interpolation_type=1, + const int border_condition=-1, const bool center=false) { + return resize(disp.width,disp.height,depth,dim,interpolation_type,border_condition,center); + } + + CImg get_resize(const CImgDisplay& disp, const int interpolation_type=1, + const int border_condition=-1, const bool center=false) const { + return get_resize(disp.width,disp.height,depth,dim,interpolation_type,border_condition,center); + } + + //! Half-resize an image, using a special optimized filter. + CImg& resize_halfXY() { + return get_resize_halfXY().transfer_to(*this); + } + + CImg get_resize_halfXY() const { + if (is_empty()) return *this; + const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + T I[9] = { 0 }; + CImg dest(width/2,height/2,depth,dim); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) + if (x%2 && y%2) dest(x/2,y/2,z,k) = (T) + (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); + return dest; + } + + //! Upscale an image by a factor 2x. + /** + Use anisotropic upscaling algorithm described at + http://scale2x.sourceforge.net/algorithm.html + **/ + CImg& resize_doubleXY() { + return get_resize_doubleXY().transfer_to(*this); + } + + CImg get_resize_doubleXY() const { +#define _cimg_gs2x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=(res).width, ptrd2+=(res).width) + +#define _cimg_gs2x_for3x3(img,x,y,z,v,I) \ + _cimg_gs2x_for3((img).height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[1] = (img)(0,_p1##y,z,v)), \ + (I[3] = I[4] = (img)(0,y,z,v)), \ + (I[7] = (img)(0,_n1##y,z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x; \ + I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(2*width,2*height,depth,dim); + CImg_3x3(I,T); + cimg_forZV(*this,z,k) { + T + *ptrd1 = res.ptr(0,0,0,k), + *ptrd2 = ptrd1 + res.width; + _cimg_gs2x_for3x3(*this,x,y,0,k,I) { + if (Icp!=Icn && Ipc!=Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = Ipc==Icn?Ipc:Icc; + *(ptrd2++) = Icn==Inc?Inc:Icc; + } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } + } + } + return res; + } + + //! Upscale an image by a factor 3x. + /** + Use anisotropic upscaling algorithm described at + http://scale2x.sourceforge.net/algorithm.html + **/ + CImg& resize_tripleXY() { + return get_resize_tripleXY().transfer_to(*this); + } + + CImg get_resize_tripleXY() const { +#define _cimg_gs3x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound)-1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=2*(res).width, ptrd2+=2*(res).width, ptrd3+=2*(res).width) + +#define _cimg_gs3x_for3x3(img,x,y,z,v,I) \ + _cimg_gs3x_for3((img).height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ + (I[3] = I[4] = (img)(0,y,z,v)), \ + (I[6] = I[7] = (img)(0,_n1##y,z,v)), \ + 1>=(img).width?(int)((img).width)-1:1); \ + (_n1##x<(int)((img).width) && ( \ + (I[2] = (img)(_n1##x,_p1##y,z,v)), \ + (I[5] = (img)(_n1##x,y,z,v)), \ + (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(3*width,3*height,depth,dim); + CImg_3x3(I,T); + cimg_forZV(*this,z,k) { + T + *ptrd1 = res.ptr(0,0,0,k), + *ptrd2 = ptrd1 + res.width, + *ptrd3 = ptrd2 + res.width; + _cimg_gs3x_for3x3(*this,x,y,0,k,I) { + if (Icp != Icn && Ipc != Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; + *(ptrd2++) = Icc; + *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; + *(ptrd3++) = Ipc==Icn?Ipc:Icc; + *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; + *(ptrd3++) = Icn==Inc?Inc:Icc; + } else { + *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; + *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; + *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; + } + } + } + return res; + } + + // Warp an image. + template + CImg& warp(const CImg& warp, const bool relative=false, + const bool interpolation=true, const unsigned int border_conditions=0) { + return get_warp(warp,relative,interpolation,border_conditions).transfer_to(*this); + } + + template + CImg get_warp(const CImg& warp, const bool relative=false, + const bool interpolation=true, const unsigned int border_conditions=0) const { + if (is_empty() || !warp) return *this; + if (!is_sameXYZ(warp)) + throw CImgArgumentException("CImg<%s>::warp() : Instance image (%u,%u,%u,%u,%p) and warping field (%u,%u,%u,%u,%p) " + "have different XYZ dimensions.", + pixel_type(),width,height,depth,dim,data, + warp.width,warp.height,warp.depth,warp.dim,warp.data); + CImg res(width,height,depth,dim); + switch (warp.dim) { + case 1 : // 1D warping. + if (relative) { // Relative warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atX(cimg::mod(x-(float)warp(x,y,z,0),(float)width),y,z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atX(x-(float)warp(x,y,z,0),y,z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atX(x-(float)warp(x,y,z,0),y,z,v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width),y,z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atX(x-(int)warp(x,y,z,0),y,z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atX(x-(int)warp(x,y,z,0),y,z,v,0); + } + } + } else { // Absolute warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atX(cimg::mod((float)warp(x,y,z,0),(float)width),y,z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atX((float)warp(x,y,z,0),y,z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atX((float)warp(x,y,z,0),y,z,v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width),y,z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atX((int)warp(x,y,z,0),y,z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atX((int)warp(x,y,z,0),y,z,v,0); + } + } + } + break; + + case 2 : // 2D warping + if (relative) { // Relative warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXY(cimg::mod(x-(float)warp(x,y,z,0),(float)width), + cimg::mod(y-(float)warp(x,y,z,1),(float)height),z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXY(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXY(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z,v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), + cimg::mod(y-(int)warp(x,y,z,1),(int)height),z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXY(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXY(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z,v,0); + } + } + } else { // Absolute warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXY(cimg::mod((float)warp(x,y,z,0),(float)width), + cimg::mod((float)warp(x,y,z,1),(float)height),z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXY((float)warp(x,y,z,0),(float)warp(x,y,z,1),z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXY((float)warp(x,y,z,0),(float)warp(x,y,z,1),z,v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), + cimg::mod((int)warp(x,y,z,1),(int)depth),z,v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXY((int)warp(x,y,z,0),(int)warp(x,y,z,1),z,v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXY((int)warp(x,y,z,0),(int)warp(x,y,z,1),z,v,0); + } + } + } + break; + + case 3 : // 3D warping + if (relative) { // Relative warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZ(cimg::mod(x-(float)warp(x,y,z,0),(float)width), + cimg::mod(y-(float)warp(x,y,z,1),(float)height), + cimg::mod(z-(float)warp(x,y,z,2),(float)depth),v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZ(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXYZ(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), + cimg::mod(y-(int)warp(x,y,z,1),(int)height), + cimg::mod(z-(int)warp(x,y,z,2),(int)depth),v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v,0); + } + } + } else { // Absolute warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZ(cimg::mod((float)warp(x,y,z,0),(float)width), + cimg::mod((float)warp(x,y,z,1),(float)height), + cimg::mod((float)warp(x,y,z,2),(float)depth),v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZ((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXYZ((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),v,0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), + cimg::mod((int)warp(x,y,z,1),(int)height), + cimg::mod((int)warp(x,y,z,2),(int)depth),v); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXYZ((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),v); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXYZ((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),v,0); + } + } + } + break; + + default : // 4D warping + if (relative) { // Relative warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZV(cimg::mod(x-(float)warp(x,y,z,0),(float)width), + cimg::mod(y-(float)warp(x,y,z,1),(float)height), + cimg::mod(z-(float)warp(x,y,z,2),(float)depth), + cimg::mod(z-(float)warp(x,y,z,3),(float)dim)); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZV(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v-(float)warp(x,y,z,3)); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXYZV(x-(float)warp(x,y,z,0),y-(float)warp(x,y,z,1),z-(float)warp(x,y,z,2),v-(float)warp(x,y,z,3),0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod(x-(int)warp(x,y,z,0),(int)width), + cimg::mod(y-(int)warp(x,y,z,1),(int)height), + cimg::mod(z-(int)warp(x,y,z,2),(int)depth), + cimg::mod(v-(int)warp(x,y,z,3),(int)dim)); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXYZV(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v-(int)warp(x,y,z,3)); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXYZ(x-(int)warp(x,y,z,0),y-(int)warp(x,y,z,1),z-(int)warp(x,y,z,2),v-(int)warp(x,y,z,3),0); + } + } + } else { // Absolute warp coordinates + if (interpolation) switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZV(cimg::mod((float)warp(x,y,z,0),(float)width), + cimg::mod((float)warp(x,y,z,1),(float)height), + cimg::mod((float)warp(x,y,z,2),(float)depth), + cimg::mod((float)warp(x,y,z,3),(float)dim)); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)_linear_atXYZV((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),(float)warp(x,y,z,3)); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (T)linear_atXYZV((float)warp(x,y,z,0),(float)warp(x,y,z,1),(float)warp(x,y,z,2),(float)warp(x,y,z,3),0); + } + } else switch (border_conditions) { + case 2 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = (*this)(cimg::mod((int)warp(x,y,z,0),(int)width), + cimg::mod((int)warp(x,y,z,1),(int)height), + cimg::mod((int)warp(x,y,z,2),(int)depth), + cimg::mod((int)warp(x,y,z,3),(int)dim)); + } break; + case 1 : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = _atXYZV((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),(int)warp(x,y,z,3)); + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) + res(x,y,z,v) = atXYZV((int)warp(x,y,z,0),(int)warp(x,y,z,1),(int)warp(x,y,z,2),(int)warp(x,y,z,3),0); + } + } + } + } + return res; + } + + // Permute axes order (internal). + template + CImg _get_permute_axes(const char *permut, const t&) const { + if (is_empty() || !permut) return CImg(*this,false); + CImg res; + const T* ptrs = data; + if (!cimg::strncasecmp(permut,"xyzv",4)) return (+*this); + if (!cimg::strncasecmp(permut,"xyvz",4)) { + res.assign(width,height,dim,depth); + cimg_forXYZV(*this,x,y,z,v) res(x,y,v,z) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xzyv",4)) { + res.assign(width,depth,height,dim); + cimg_forXYZV(*this,x,y,z,v) res(x,z,y,v) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xzvy",4)) { + res.assign(width,depth,dim,height); + cimg_forXYZV(*this,x,y,z,v) res(x,z,v,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xvyz",4)) { + res.assign(width,dim,height,depth); + cimg_forXYZV(*this,x,y,z,v) res(x,v,y,z) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"xvzy",4)) { + res.assign(width,dim,depth,height); + cimg_forXYZV(*this,x,y,z,v) res(x,v,z,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yxzv",4)) { + res.assign(height,width,depth,dim); + cimg_forXYZV(*this,x,y,z,v) res(y,x,z,v) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yxvz",4)) { + res.assign(height,width,dim,depth); + cimg_forXYZV(*this,x,y,z,v) res(y,x,v,z) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yzxv",4)) { + res.assign(height,depth,width,dim); + cimg_forXYZV(*this,x,y,z,v) res(y,z,x,v) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yzvx",4)) { + res.assign(height,depth,dim,width); + switch (width) { + case 1 : { + t *ptrR = res.ptr(0,0,0,0); + for (unsigned long siz = height*depth*dim; siz; --siz) { + *(ptrR++) = (t)*(ptrs++); + } + } break; + case 2 : { + t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1); + for (unsigned long siz = height*depth*dim; siz; --siz) { + *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); + } + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1), *ptrB = res.ptr(0,0,0,2); + for (unsigned long siz = height*depth*dim; siz; --siz) { + *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); *(ptrB++) = (t)*(ptrs++); + } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t *ptrR = res.ptr(0,0,0,0), *ptrG = res.ptr(0,0,0,1), *ptrB = res.ptr(0,0,0,2), *ptrA = res.ptr(0,0,0,3); + for (unsigned long siz = height*depth*dim; siz; --siz) { + *(ptrR++) = (t)*(ptrs++); *(ptrG++) = (t)*(ptrs++); *(ptrB++) = (t)*(ptrs++); *(ptrA++) = (t)*(ptrs++); + } + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) res(y,z,v,x) = *(ptrs++); + return res; + } + } + } + if (!cimg::strncasecmp(permut,"yvxz",4)) { + res.assign(height,dim,width,depth); + cimg_forXYZV(*this,x,y,z,v) res(y,v,x,z) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"yvzx",4)) { + res.assign(height,dim,depth,width); + cimg_forXYZV(*this,x,y,z,v) res(y,v,z,x) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zxyv",4)) { + res.assign(depth,width,height,dim); + cimg_forXYZV(*this,x,y,z,v) res(z,x,y,v) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zxvy",4)) { + res.assign(depth,width,dim,height); + cimg_forXYZV(*this,x,y,z,v) res(z,x,v,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zyxv",4)) { + res.assign(depth,height,width,dim); + cimg_forXYZV(*this,x,y,z,v) res(z,y,x,v) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zyvx",4)) { + res.assign(depth,height,dim,width); + cimg_forXYZV(*this,x,y,z,v) res(z,y,v,x) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zvxy",4)) { + res.assign(depth,dim,width,height); + cimg_forXYZV(*this,x,y,z,v) res(z,v,x,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"zvyx",4)) { + res.assign(depth,dim,height,width); + cimg_forXYZV(*this,x,y,z,v) res(z,v,y,x) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"vxyz",4)) { + res.assign(dim,width,height,depth); + switch (dim) { + case 1 : { + const T *ptrR = ptr(0,0,0,0); + t *ptrd = res.ptr(); + for (unsigned long siz = width*height*depth; siz; --siz) { + *(ptrd++) = (t)*(ptrR++); + } + } break; + case 2 : { + const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1); + t *ptrd = res.ptr(); + for (unsigned long siz = width*height*depth; siz; --siz) { + *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1), *ptrB = ptr(0,0,0,2); + t *ptrd = res.ptr(); + for (unsigned long siz = width*height*depth; siz; --siz) { + *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); *(ptrd++) = (t)*(ptrB++); + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptrR = ptr(0,0,0,0), *ptrG = ptr(0,0,0,1), *ptrB = ptr(0,0,0,2), *ptrA = ptr(0,0,0,3); + t *ptrd = res.ptr(); + for (unsigned long siz = width*height*depth; siz; --siz) { + *(ptrd++) = (t)*(ptrR++); *(ptrd++) = (t)*(ptrG++); *(ptrd++) = (t)*(ptrB++); *(ptrd++) = (t)*(ptrA++); + } + } break; + default : { + cimg_forXYZV(*this,x,y,z,v) res(v,x,y,z) = (t)*(ptrs++); + } + } + } + if (!cimg::strncasecmp(permut,"vxzy",4)) { + res.assign(dim,width,depth,height); + cimg_forXYZV(*this,x,y,z,v) res(v,x,z,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"vyxz",4)) { + res.assign(dim,height,width,depth); + cimg_forXYZV(*this,x,y,z,v) res(v,y,x,z) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"vyzx",4)) { + res.assign(dim,height,depth,width); + cimg_forXYZV(*this,x,y,z,v) res(v,y,z,x) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"vzxy",4)) { + res.assign(dim,depth,width,height); + cimg_forXYZV(*this,x,y,z,v) res(v,z,x,y) = (t)*(ptrs++); + } + if (!cimg::strncasecmp(permut,"vzyx",4)) { + res.assign(dim,depth,height,width); + cimg_forXYZV(*this,x,y,z,v) res(v,z,y,x) = (t)*(ptrs++); + } + if (!res) + throw CImgArgumentException("CImg<%s>::permute_axes() : Invalid input permutation '%s'.", + pixel_type(),permut); + return res; + } + + //! Permute axes order. + /** + This function permutes image axes. + \param permut = String describing the permutation (4 characters). + **/ + CImg& permute_axes(const char *order) { + return get_permute_axes(order).transfer_to(*this); + } + + CImg get_permute_axes(const char *order) const { + const T foo = (T)0; + return _get_permute_axes(order,foo); + } + + //! Invert endianness. + CImg& invert_endianness() { + cimg::invert_endianness(data,size()); + return *this; + } + + CImg get_invert_endianness() const { + return (+*this).invert_endianness(); + } + + //! Mirror an image along the specified axis. + CImg& mirror(const char axis) { + if (is_empty()) return *this; + T *pf, *pb, *buf = 0; + switch (cimg::uncase(axis)) { + case 'x' : { + pf = data; pb = ptr(width-1); + const unsigned int width2 = width/2; + for (unsigned int yzv = 0; yzv::mirror() : unknow axis '%c', must be 'x','y','z' or 'v'.", + pixel_type(),axis); + } + if (buf) delete[] buf; + return *this; + } + + CImg get_mirror(const char axis) const { + return (+*this).mirror(axis); + } + + //! Translate the image. + /** + \param deltax Amount of displacement along the X-axis. + \param deltay Amount of displacement along the Y-axis. + \param deltaz Amount of displacement along the Z-axis. + \param deltav Amount of displacement along the V-axis. + \param border_condition Border condition. + + - \c border_condition can be : + - 0 : Zero border condition (Dirichlet). + - 1 : Nearest neighbors (Neumann). + - 2 : Repeat Pattern (Fourier style). + **/ + CImg& translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, + const int border_condition=0) { + if (is_empty()) return *this; + if (deltax) // Translate along X-axis + switch (border_condition) { + case 0 : + if (cimg::abs(deltax)>=dimx()) return fill(0); + if (deltax>0) cimg_forYZV(*this,y,z,k) { + cimg_std::memmove(ptr(0,y,z,k),ptr(deltax,y,z,k),(width-deltax)*sizeof(T)); + cimg_std::memset(ptr(width-deltax,y,z,k),0,deltax*sizeof(T)); + } else cimg_forYZV(*this,y,z,k) { + cimg_std::memmove(ptr(-deltax,y,z,k),ptr(0,y,z,k),(width+deltax)*sizeof(T)); + cimg_std::memset(ptr(0,y,z,k),0,-deltax*sizeof(T)); + } + break; + case 1 : + if (deltax>0) { + const int ndeltax = (deltax>=dimx())?width-1:deltax; + if (!ndeltax) return *this; + cimg_forYZV(*this,y,z,k) { + cimg_std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); + T *ptrd = ptr(width-1,y,z,k); + const T val = *ptrd; + for (int l = 0; l=dimx())?width-1:-deltax; + if (!ndeltax) return *this; + cimg_forYZV(*this,y,z,k) { + cimg_std::memmove(ptr(ndeltax,y,z,k),ptr(0,y,z,k),(width-ndeltax)*sizeof(T)); + T *ptrd = ptr(0,y,z,k); + const T val = *ptrd; + for (int l = 0; l0) cimg_forYZV(*this,y,z,k) { + cimg_std::memcpy(buf,ptr(0,y,z,k),ndeltax*sizeof(T)); + cimg_std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); + cimg_std::memcpy(ptr(width-ndeltax,y,z,k),buf,ndeltax*sizeof(T)); + } else cimg_forYZV(*this,y,z,k) { + cimg_std::memcpy(buf,ptr(width+ndeltax,y,z,k),-ndeltax*sizeof(T)); + cimg_std::memmove(ptr(-ndeltax,y,z,k),ptr(0,y,z,k),(width+ndeltax)*sizeof(T)); + cimg_std::memcpy(ptr(0,y,z,k),buf,-ndeltax*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltay) // Translate along Y-axis + switch (border_condition) { + case 0 : + if (cimg::abs(deltay)>=dimy()) return fill(0); + if (deltay>0) cimg_forZV(*this,z,k) { + cimg_std::memmove(ptr(0,0,z,k),ptr(0,deltay,z,k),width*(height-deltay)*sizeof(T)); + cimg_std::memset(ptr(0,height-deltay,z,k),0,width*deltay*sizeof(T)); + } else cimg_forZV(*this,z,k) { + cimg_std::memmove(ptr(0,-deltay,z,k),ptr(0,0,z,k),width*(height+deltay)*sizeof(T)); + cimg_std::memset(ptr(0,0,z,k),0,-deltay*width*sizeof(T)); + } + break; + case 1 : + if (deltay>0) { + const int ndeltay = (deltay>=dimy())?height-1:deltay; + if (!ndeltay) return *this; + cimg_forZV(*this,z,k) { + cimg_std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); + T *ptrd = ptr(0,height-ndeltay,z,k), *ptrs = ptr(0,height-1,z,k); + for (int l = 0; l=dimy())?height-1:-deltay; + if (!ndeltay) return *this; + cimg_forZV(*this,z,k) { + cimg_std::memmove(ptr(0,ndeltay,z,k),ptr(0,0,z,k),width*(height-ndeltay)*sizeof(T)); + T *ptrd = ptr(0,1,z,k), *ptrs = ptr(0,0,z,k); + for (int l = 0; l0) cimg_forZV(*this,z,k) { + cimg_std::memcpy(buf,ptr(0,0,z,k),width*ndeltay*sizeof(T)); + cimg_std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); + cimg_std::memcpy(ptr(0,height-ndeltay,z,k),buf,width*ndeltay*sizeof(T)); + } else cimg_forZV(*this,z,k) { + cimg_std::memcpy(buf,ptr(0,height+ndeltay,z,k),-ndeltay*width*sizeof(T)); + cimg_std::memmove(ptr(0,-ndeltay,z,k),ptr(0,0,z,k),width*(height+ndeltay)*sizeof(T)); + cimg_std::memcpy(ptr(0,0,z,k),buf,-ndeltay*width*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltaz) // Translate along Z-axis + switch (border_condition) { + case 0 : + if (cimg::abs(deltaz)>=dimz()) return fill(0); + if (deltaz>0) cimg_forV(*this,k) { + cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,deltaz,k),width*height*(depth-deltaz)*sizeof(T)); + cimg_std::memset(ptr(0,0,depth-deltaz,k),0,width*height*deltaz*sizeof(T)); + } else cimg_forV(*this,k) { + cimg_std::memmove(ptr(0,0,-deltaz,k),ptr(0,0,0,k),width*height*(depth+deltaz)*sizeof(T)); + cimg_std::memset(ptr(0,0,0,k),0,-deltaz*width*height*sizeof(T)); + } + break; + case 1 : + if (deltaz>0) { + const int ndeltaz = (deltaz>=dimz())?depth-1:deltaz; + if (!ndeltaz) return *this; + cimg_forV(*this,k) { + cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); + T *ptrd = ptr(0,0,depth-ndeltaz,k), *ptrs = ptr(0,0,depth-1,k); + for (int l = 0; l=dimz())?depth-1:-deltaz; + if (!ndeltaz) return *this; + cimg_forV(*this,k) { + cimg_std::memmove(ptr(0,0,ndeltaz,k),ptr(0,0,0,k),width*height*(depth-ndeltaz)*sizeof(T)); + T *ptrd = ptr(0,0,1,k), *ptrs = ptr(0,0,0,k); + for (int l = 0; l0) cimg_forV(*this,k) { + cimg_std::memcpy(buf,ptr(0,0,0,k),width*height*ndeltaz*sizeof(T)); + cimg_std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); + cimg_std::memcpy(ptr(0,0,depth-ndeltaz,k),buf,width*height*ndeltaz*sizeof(T)); + } else cimg_forV(*this,k) { + cimg_std::memcpy(buf,ptr(0,0,depth+ndeltaz,k),-ndeltaz*width*height*sizeof(T)); + cimg_std::memmove(ptr(0,0,-ndeltaz,k),ptr(0,0,0,k),width*height*(depth+ndeltaz)*sizeof(T)); + cimg_std::memcpy(ptr(0,0,0,k),buf,-ndeltaz*width*height*sizeof(T)); + } + delete[] buf; + } break; + } + + if (deltav) // Translate along V-axis + switch (border_condition) { + case 0 : + if (cimg::abs(deltav)>=dimv()) return fill(0); + if (deltav>0) { + cimg_std::memmove(data,ptr(0,0,0,deltav),width*height*depth*(dim-deltav)*sizeof(T)); + cimg_std::memset(ptr(0,0,0,dim-deltav),0,width*height*depth*deltav*sizeof(T)); + } else cimg_forV(*this,k) { + cimg_std::memmove(ptr(0,0,0,-deltav),data,width*height*depth*(dim+deltav)*sizeof(T)); + cimg_std::memset(data,0,-deltav*width*height*depth*sizeof(T)); + } + break; + case 1 : + if (deltav>0) { + const int ndeltav = (deltav>=dimv())?dim-1:deltav; + if (!ndeltav) return *this; + cimg_std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); + T *ptrd = ptr(0,0,0,dim-ndeltav), *ptrs = ptr(0,0,0,dim-1); + for (int l = 0; l=dimv())?dim-1:-deltav; + if (!ndeltav) return *this; + cimg_std::memmove(ptr(0,0,0,ndeltav),data,width*height*depth*(dim-ndeltav)*sizeof(T)); + T *ptrd = ptr(0,0,0,1); + for (int l = 0; l0) { + cimg_std::memcpy(buf,data,width*height*depth*ndeltav*sizeof(T)); + cimg_std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); + cimg_std::memcpy(ptr(0,0,0,dim-ndeltav),buf,width*height*depth*ndeltav*sizeof(T)); + } else { + cimg_std::memcpy(buf,ptr(0,0,0,dim+ndeltav),-ndeltav*width*height*depth*sizeof(T)); + cimg_std::memmove(ptr(0,0,0,-ndeltav),data,width*height*depth*(dim+ndeltav)*sizeof(T)); + cimg_std::memcpy(data,buf,-ndeltav*width*height*depth*sizeof(T)); + } + delete[] buf; + } break; + } + return *this; + } + + CImg get_translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, + const int border_condition=0) const { + return (+*this).translate(deltax,deltay,deltaz,deltav,border_condition); + } + + //! Get a square region of the image. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param v0 = V-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param v1 = V-coordinate of the lower-right crop rectangle corner. + \param border_condition = Dirichlet (false) or Neumann border conditions. + **/ + CImg& crop(const int x0, const int y0, const int z0, const int v0, + const int x1, const int y1, const int z1, const int v1, + const bool border_condition=false) { + return get_crop(x0,y0,z0,v0,x1,y1,z1,v1,border_condition).transfer_to(*this); + } + + CImg get_crop(const int x0, const int y0, const int z0, const int v0, + const int x1, const int y1, const int z1, const int v1, + const bool border_condition=false) const { + if (is_empty()) return *this; + const int + nx0 = x0 dest(1U+nx1-nx0,1U+ny1-ny0,1U+nz1-nz0,1U+nv1-nv0); + if (nx0<0 || nx1>=dimx() || ny0<0 || ny1>=dimy() || nz0<0 || nz1>=dimz() || nv0<0 || nv1>=dimv()) { + if (border_condition) cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = _atXYZV(nx0+x,ny0+y,nz0+z,nv0+v); + else dest.fill(0).draw_image(-nx0,-ny0,-nz0,-nv0,*this); + } else dest.draw_image(-nx0,-ny0,-nz0,-nv0,*this); + return dest; + } + + //! Get a rectangular part of the instance image. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param border_condition = determine the type of border condition if + some of the desired region is outside the image. + **/ + CImg& crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const bool border_condition=false) { + return crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); + } + + CImg get_crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const bool border_condition=false) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); + } + + //! Get a rectangular part of the instance image. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param border_condition = determine the type of border condition if + some of the desired region is outside the image. + **/ + CImg& crop(const int x0, const int y0, + const int x1, const int y1, + const bool border_condition=false) { + return crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); + } + + CImg get_crop(const int x0, const int y0, + const int x1, const int y1, + const bool border_condition=false) const { + return get_crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); + } + + //! Get a rectangular part of the instance image. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param border_condition = determine the type of border condition if + some of the desired region is outside the image. + **/ + CImg& crop(const int x0, const int x1, const bool border_condition=false) { + return crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); + } + + CImg get_crop(const int x0, const int x1, const bool border_condition=false) const { + return get_crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); + } + + //! Autocrop an image, regarding of the specified backround value. + CImg& autocrop(const T value, const char *const axes="vzyx") { + if (is_empty()) return *this; + const int lmax = cimg::strlen(axes); + for (int l = 0; l get_autocrop(const T value, const char *const axes="vzyx") const { + return (+*this).autocrop(value,axes); + } + + //! Autocrop an image, regarding of the specified backround color. + CImg& autocrop(const T *const color, const char *const axes="zyx") { + if (is_empty()) return *this; + const int lmax = cimg::strlen(axes); + for (int l = 0; l get_autocrop(const T *const color, const char *const axes="zyx") const { + return (+*this).autocrop(color,axes); + } + + //! Autocrop an image, regarding of the specified backround color. + template CImg& autocrop(const CImg& color, const char *const axes="zyx") { + return get_autocrop(color,axes).transfer_to(*this); + } + + template CImg get_autocrop(const CImg& color, const char *const axes="zyx") const { + return get_autocrop(color.data,axes); + } + + //! Autocrop an image along specified axis, regarding of the specified backround value. + CImg& autocrop(const T value, const char axis) { + return get_autocrop(value,axis).transfer_to(*this); + } + + CImg get_autocrop(const T value, const char axis) const { + if (is_empty()) return *this; + CImg res; + const CImg coords = _get_autocrop(value,axis); + switch (cimg::uncase(axis)) { + case 'x' : { + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) res = get_crop(x0,x1); + } break; + case 'y' : { + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) res = get_crop(0,y0,width-1,y1); + } break; + case 'z' : { + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) res = get_crop(0,0,z0,width-1,height-1,z1); + } break; + case 'v' : { + const int v0 = coords[0], v1 = coords[1]; + if (v0>=0 && v1>=0) res = get_crop(0,0,0,v0,width-1,height-1,depth-1,v1); + } break; + } + return res; + } + + //! Autocrop an image along specified axis, regarding of the specified backround color. + CImg& autocrop(const T *const color, const char axis) { + return get_autocrop(color,axis).transfer_to(*this); + } + + CImg get_autocrop(const T *const color, const char axis) const { + if (is_empty()) return *this; + CImg res; + switch (cimg::uncase(axis)) { + case 'x' : { + int x0 = width, x1 = -1; + cimg_forV(*this,k) { + const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); + const int nx0 = coords[0], nx1 = coords[1]; + if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); } + } + if (x0<=x1) res = get_crop(x0,x1); + } break; + case 'y' : { + int y0 = height, y1 = -1; + cimg_forV(*this,k) { + const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); + const int ny0 = coords[0], ny1 = coords[1]; + if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); } + } + if (y0<=y1) res = get_crop(0,y0,width-1,y1); + } break; + case 'z' : { + int z0 = depth, z1 = -1; + cimg_forV(*this,k) { + const CImg coords = get_shared_channel(k)._get_autocrop(color[k],axis); + const int nz0 = coords[0], nz1 = coords[1]; + if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); } + } + if (z0<=z1) res = get_crop(0,0,z0,width-1,height-1,z1); + } break; + default : + throw CImgArgumentException("CImg<%s>::autocrop() : Invalid axis '%c', must be 'x','y' or 'z'.", + pixel_type(),axis); + } + return res; + } + + //! Autocrop an image along specified axis, regarding of the specified backround color. + template CImg& autocrop(const CImg& color, const char axis) { + return get_autocrop(color,axis).transfer_to(*this); + } + + template CImg get_autocrop(const CImg& color, const char axis) const { + return get_autocrop(color.data,axis); + } + + CImg _get_autocrop(const T value, const char axis) const { + CImg res; + int x0 = -1, y0 = -1, z0 = -1, v0 = -1, x1 = -1, y1 = -1, z1 = -1, v1 = -1; + switch (cimg::uncase(axis)) { + case 'x' : { + cimg_forX(*this,x) cimg_forYZV(*this,y,z,v) + if ((*this)(x,y,z,v)!=value) { x0 = x; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } + if (x0>=0) { + for (int x = dimx()-1; x>=0; --x) cimg_forYZV(*this,y,z,v) + if ((*this)(x,y,z,v)!=value) { x1 = x; x = 0; y = dimy(); z = dimz(); v = dimv(); } + } + res = CImg::vector(x0,x1); + } break; + case 'y' : { + cimg_forY(*this,y) cimg_forXZV(*this,x,z,v) + if ((*this)(x,y,z,v)!=value) { y0 = y; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } + if (y0>=0) { + for (int y = dimy()-1; y>=0; --y) cimg_forXZV(*this,x,z,v) + if ((*this)(x,y,z,v)!=value) { y1 = y; x = dimx(); y = 0; z = dimz(); v = dimv(); } + } + res = CImg::vector(y0,y1); + } break; + case 'z' : { + cimg_forZ(*this,z) cimg_forXYV(*this,x,y,v) + if ((*this)(x,y,z,v)!=value) { z0 = z; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } + if (z0>=0) { + for (int z = dimz()-1; z>=0; --z) cimg_forXYV(*this,x,y,v) + if ((*this)(x,y,z,v)!=value) { z1 = z; x = dimx(); y = dimy(); z = 0; v = dimv(); } + } + res = CImg::vector(z0,z1); + } break; + case 'v' : { + cimg_forV(*this,v) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,v)!=value) { v0 = v; x = dimx(); y = dimy(); z = dimz(); v = dimv(); } + if (v0>=0) { + for (int v = dimv()-1; v>=0; --v) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,v)!=value) { v1 = v; x = dimx(); y = dimy(); z = dimz(); v = 0; } + } + res = CImg::vector(v0,v1); + } break; + default : + throw CImgArgumentException("CImg<%s>::autocrop() : unknow axis '%c', must be 'x','y','z' or 'v'", + pixel_type(),axis); + } + return res; + } + + //! Get a set of columns. + CImg& columns(const unsigned int x0, const unsigned int x1) { + return get_columns(x0,x1).transfer_to(*this); + } + + CImg get_columns(const unsigned int x0, const unsigned int x1) const { + return get_crop((int)x0,0,0,0,(int)x1,dimy()-1,dimz()-1,dimv()-1); + } + + //! Get one column. + CImg& column(const unsigned int x0) { + return columns(x0,x0); + } + + CImg get_column(const unsigned int x0) const { + return get_columns(x0,x0); + } + + //! Get a set of lines. + CImg& lines(const unsigned int y0, const unsigned int y1) { + return get_lines(y0,y1).transfer_to(*this); + } + + CImg get_lines(const unsigned int y0, const unsigned int y1) const { + return get_crop(0,(int)y0,0,0,dimx()-1,(int)y1,dimz()-1,dimv()-1); + } + + //! Get a line. + CImg& line(const unsigned int y0) { + return lines(y0,y0); + } + + CImg get_line(const unsigned int y0) const { + return get_lines(y0,y0); + } + + //! Get a set of slices. + CImg& slices(const unsigned int z0, const unsigned int z1) { + return get_slices(z0,z1).transfer_to(*this); + } + + CImg get_slices(const unsigned int z0, const unsigned int z1) const { + return get_crop(0,0,(int)z0,0,dimx()-1,dimy()-1,(int)z1,dimv()-1); + } + + //! Get a slice. + CImg& slice(const unsigned int z0) { + return slices(z0,z0); + } + + CImg get_slice(const unsigned int z0) const { + return get_slices(z0,z0); + } + + //! Get a set of channels. + CImg& channels(const unsigned int v0, const unsigned int v1) { + return get_channels(v0,v1).transfer_to(*this); + } + + CImg get_channels(const unsigned int v0, const unsigned int v1) const { + return get_crop(0,0,0,(int)v0,dimx()-1,dimy()-1,dimz()-1,(int)v1); + } + + //! Get a channel. + CImg& channel(const unsigned int v0) { + return channels(v0,v0); + } + + CImg get_channel(const unsigned int v0) const { + return get_channels(v0,v0); + } + + //! Get a shared-memory image referencing a set of points of the instance image. + CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) { + const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); + return CImg(data+beg,x1-x0+1,1,1,1,true); + } + + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) const { + const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); + return CImg(data+beg,x1-x0+1,1,1,1,true); + } + + //! Return a shared-memory image referencing a set of lines of the instance image. + CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int v0=0) { + const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); + return CImg(data+beg,width,y1-y0+1,1,1,true); + } + + const CImg get_shared_lines(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int v0=0) const { + const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); + return CImg(data+beg,width,y1-y0+1,1,1,true); + } + + //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image. + CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { + return get_shared_lines(y0,y0,z0,v0); + } + + const CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) const { + return get_shared_lines(y0,y0,z0,v0); + } + + //! Return a shared memory image referencing a set of planes (z0->z1,v0) of the instance image. + CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { + const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); + return CImg(data+beg,width,height,z1-z0+1,1,true); + } + + const CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) const { + const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); + return CImg(data+beg,width,height,z1-z0+1,1,true); + } + + //! Return a shared-memory image referencing one plane (z0,v0) of the instance image. + CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) { + return get_shared_planes(z0,z0,v0); + } + + const CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) const { + return get_shared_planes(z0,z0,v0); + } + + //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image. + CImg get_shared_channels(const unsigned int v0, const unsigned int v1) { + const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); + return CImg(data+beg,width,height,depth,v1-v0+1,true); + } + + const CImg get_shared_channels(const unsigned int v0, const unsigned int v1) const { + const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " + "a (%u,%u,%u,%u) image.", + pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); + return CImg(data+beg,width,height,depth,v1-v0+1,true); + } + + //! Return a shared-memory image referencing one channel v0 of the instance image. + CImg get_shared_channel(const unsigned int v0) { + return get_shared_channels(v0,v0); + } + + const CImg get_shared_channel(const unsigned int v0) const { + return get_shared_channels(v0,v0); + } + + //! Return a shared version of the instance image. + CImg get_shared() { + return CImg(data,width,height,depth,dim,true); + } + + const CImg get_shared() const { + return CImg(data,width,height,depth,dim,true); + } + + //! Return a 2D representation of a 3D image, with three slices. + CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, + const int dx=-100, const int dy=-100, const int dz=-100) { + return get_projections2d(x0,y0,z0,dx,dy,dz).transfer_to(*this); + } + + CImg get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, + const int dx=-100, const int dy=-100, const int dz=-100) const { + if (is_empty()) return *this; + const unsigned int + nx0 = (x0>=width)?width-1:x0, + ny0 = (y0>=height)?height-1:y0, + nz0 = (z0>=depth)?depth-1:z0; + CImg + imgxy(width,height,1,dim), + imgzy(depth,height,1,dim), + imgxz(width,depth,1,dim); + { cimg_forXYV(*this,x,y,k) imgxy(x,y,k) = (*this)(x,y,nz0,k); } + { cimg_forYZV(*this,y,z,k) imgzy(z,y,k) = (*this)(nx0,y,z,k); } + { cimg_forXZV(*this,x,z,k) imgxz(x,z,k) = (*this)(x,ny0,z,k); } + imgxy.resize(dx,dy,1,dim,1); + imgzy.resize(dz,dy,1,dim,1); + imgxz.resize(dx,dz,1,dim,1); + return CImg(imgxy.width+imgzy.width,imgxy.height+imgxz.height,1,dim,0). + draw_image(imgxy).draw_image(imgxy.width,imgzy).draw_image(0,imgxy.height,imgxz); + } + + //! Compute the image histogram. + /** + The histogram H of an image I is a 1D-function where H(x) is the number of + occurences of the value x in I. + \param nblevels = Number of different levels of the computed histogram. + For classical images, this value is 256. You should specify more levels + if you are working with CImg or images with high range of pixel values. + \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min + won't be counted. + \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max + won't be counted. + \note If val_min==val_max==0 (default values), the function first estimates the minimum and maximum + pixel values of the current image, then uses these values for the histogram computation. + \result The histogram is returned as a 1D CImg image H, having a size of (nblevels,1,1,1) such that + H(0) and H(nblevels-1) are respectively equal to the number of occurences of the values val_min and val_max in I. + \note Histogram computation always returns a 1D function. Histogram of multi-valued (such as color) images + are not multi-dimensional. + **/ + CImg& histogram(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) { + return get_histogram(nblevels,val_min,val_max).transfer_to(*this); + } + + CImg get_histogram(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { + if (is_empty()) return CImg(); + if (!nblevels) + throw CImgArgumentException("CImg<%s>::get_histogram() : Can't compute an histogram with 0 levels", + pixel_type()); + T vmin = val_min, vmax = val_max; + CImg res(nblevels,1,1,1,0); + if (vmin>=vmax && vmin==0) vmin = minmax(vmax); + if (vmin=0 && pos<(int)nblevels) ++res[pos]; + } else res[0]+=size(); + return res; + } + + //! Compute the histogram-equalized version of the instance image. + /** + The histogram equalization is a classical image processing algorithm that enhances the image contrast + by expanding its histogram. + \param nblevels = Number of different levels of the computed histogram. + For classical images, this value is 256. You should specify more levels + if you are working with CImg or images with high range of pixel values. + \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min + won't be changed. + \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max + won't be changed. + \note If val_min==val_max==0 (default values), the function acts on all pixel values of the image. + \return A new image with same size is returned, where pixels have been equalized. + **/ + CImg& equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) { + if (is_empty()) return *this; + T vmin = val_min, vmax = val_max; + if (vmin==vmax && vmin==0) vmin = minmax(vmax); + if (vmin hist = get_histogram(nblevels,vmin,vmax); + float cumul = 0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos]=cumul; } + cimg_for(*this,ptr,T) { + const int pos = (unsigned int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin)); + if (pos>=0 && pos<(int)nblevels) *ptr = (T)(vmin + (vmax-vmin)*hist[pos]/size()); + } + } + return *this; + } + + CImg get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const { + return (+*this).equalize(nblevels,val_min,val_max); + } + + //! Get a label map of disconnected regions with same intensities. + CImg& label_regions() { + return get_label_regions().transfer_to(*this); + } + + CImg get_label_regions() const { +#define _cimg_get_label_test(p,q) { \ + flag = true; \ + const T *ptr1 = ptr(x,y) + siz, *ptr2 = ptr(p,q) + siz; \ + for (unsigned int i = dim; flag && i; --i) { ptr1-=wh; ptr2-=wh; flag = (*ptr1==*ptr2); } \ +} + if (depth>1) + throw CImgInstanceException("CImg<%s>::label_regions() : Instance image must be a 2D image"); + CImg res(width,height,depth,1,0); + unsigned int label = 1; + const unsigned int wh = width*height, siz = width*height*dim; + const int W1 = dimx()-1, H1 = dimy()-1; + bool flag; + cimg_forXY(*this,x,y) { + bool done = false; + if (y) { + _cimg_get_label_test(x,y-1); + if (flag) { + const unsigned int lab = (res(x,y) = res(x,y-1)); + done = true; + if (x && res(x-1,y)!=lab) { + _cimg_get_label_test(x-1,y); + if (flag) { + const unsigned int lold = res(x-1,y), *const cptr = res.ptr(x,y); + for (unsigned int *ptr = res.ptr(); ptr=0; --y) for (int x=W1; x>=0; --x) { + bool done = false; + if (ycptr; --ptr) if (*ptr==lold) *ptr = lab; + } + } + } + } + if (x1), this function computes the L1,L2 or Linf norm of each + vector-valued pixel. + \param norm_type = Type of the norm being computed (1 = L1, 2 = L2, -1 = Linf). + \return A scalar-valued image CImg with size (dimx(),dimy(),dimz(),1), where each pixel is the norm + of the corresponding pixels in the original vector-valued image. + **/ + CImg& pointwise_norm(int norm_type=2) { + return get_pointwise_norm(norm_type).transfer_to(*this); + } + + CImg get_pointwise_norm(int norm_type=2) const { + if (is_empty()) return *this; + if (dim==1) return get_abs(); + CImg res(width,height,depth); + switch (norm_type) { + case -1 : { // Linf norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; cimg_forV(*this,v) { + const Tfloat tmp = (Tfloat)cimg::abs((*this)(x,y,z,v)); + if (tmp>n) n=tmp; res(x,y,z) = n; + } + } + } break; + case 1 : { // L1 norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; cimg_forV(*this,v) n+=cimg::abs((*this)(x,y,z,v)); res(x,y,z) = n; + } + } break; + default : { // L2 norm + cimg_forXYZ(*this,x,y,z) { + Tfloat n = 0; cimg_forV(*this,v) n+=(*this)(x,y,z,v)*(*this)(x,y,z,v); res(x,y,z) = (Tfloat)cimg_std::sqrt((double)n); + } + } + } + return res; + } + + //! Compute the image of normalized vectors. + /** + When dealing with vector-valued images (i.e images with dimv()>1), this function return the image of normalized vectors + (unit vectors). Null vectors are unchanged. The L2-norm is computed for the normalization. + \return A new vector-valued image with same size, where each vector-valued pixels have been normalized. + **/ + CImg& pointwise_orientation() { + cimg_forXYZ(*this,x,y,z) { + float n = 0; + cimg_forV(*this,v) n+=(float)((*this)(x,y,z,v)*(*this)(x,y,z,v)); + n = (float)cimg_std::sqrt(n); + if (n>0) cimg_forV(*this,v) (*this)(x,y,z,v) = (T)((*this)(x,y,z,v)/n); + else cimg_forV(*this,v) (*this)(x,y,z,v) = 0; + } + return *this; + } + + CImg get_pointwise_orientation() const { + if (is_empty()) return *this; + return CImg(*this,false).pointwise_orientation(); + } + + //! Split image into a list. + CImgList get_split(const char axis, const unsigned int nb=0) const { + if (is_empty()) return CImgList(); + CImgList res; + switch (cimg::uncase(axis)) { + case 'x' : { + if (nb>width) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'x' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:width); + const unsigned int delta = (unsigned int)cimg::round((float)width/res.size,1); + unsigned int l, x; + for (l = 0, x = 0; lheight) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'y' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:height); + const unsigned int delta = (unsigned int)cimg::round((float)height/res.size,1); + unsigned int l, y; + for (l = 0, y = 0; ldepth) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'z' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:depth); + const unsigned int delta = (unsigned int)cimg::round((float)depth/res.size,1); + unsigned int l, z; + for (l = 0, z = 0; ldim) + throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'v' into %u images.", + pixel_type(),width,height,depth,dim,data,nb); + res.assign(nb?nb:dim); + const unsigned int delta = (unsigned int)cimg::round((float)dim/res.size,1); + unsigned int l, v; + for (l = 0, v = 0; l::get_split() : Unknow axis '%c', must be 'x','y','z' or 'v'", + pixel_type(),axis); + } + return res; + } + + // Split image into a list of vectors, according to a given splitting value. + CImgList get_split(const T value, const bool keep_values, const bool shared) const { + CImgList res; + const T *ptr0 = data, *const ptr_end = data + size(); + while (ptr0(ptr0,1,siz0,1,1,shared)); + ptr0 = ptr1; + while (ptr1(ptr0,1,siz1,1,1,shared),~0U,shared); + ptr0 = ptr1; + } + return res; + } + + //! Append an image to another one. + CImg& append(const CImg& img, const char axis, const char align='p') { + if (!img) return *this; + if (is_empty()) return (*this=img); + return get_append(img,axis,align).transfer_to(*this); + } + + CImg get_append(const CImg& img, const char axis, const char align='p') const { + if (!img) return *this; + if (is_empty()) return img; + CImgList temp(2); + temp[0].width = width; temp[0].height = height; temp[0].depth = depth; + temp[0].dim = dim; temp[0].data = data; + temp[1].width = img.width; temp[1].height = img.height; temp[1].depth = img.depth; + temp[1].dim = img.dim; temp[1].data = img.data; + const CImg res = temp.get_append(axis,align); + temp[0].width = temp[0].height = temp[0].depth = temp[0].dim = 0; temp[0].data = 0; + temp[1].width = temp[1].height = temp[1].depth = temp[1].dim = 0; temp[1].data = 0; + return res; + } + + //! Compute the list of images, corresponding to the XY-gradients of an image. + /** + \param scheme = Numerical scheme used for the gradient computation : + - -1 = Backward finite differences + - 0 = Centered finite differences + - 1 = Forward finite differences + - 2 = Using Sobel masks + - 3 = Using rotation invariant masks + - 4 = Using Deriche recusrsive filter. + **/ + CImgList get_gradient(const char *const axes=0, const int scheme=3) const { + CImgList grad(2,width,height,depth,dim); + bool threed = false; + if (axes) { + for (unsigned int a = 0; axes[a]; ++a) { + const char axis = cimg::uncase(axes[a]); + switch (axis) { + case 'x' : case 'y' : break; + case 'z' : threed = true; break; + default : + throw CImgArgumentException("CImg<%s>::get_gradient() : Unknown specified axis '%c'.", + pixel_type(),axis); + } + } + } else threed = (depth>1); + if (threed) { + grad.insert(1); grad[2].assign(width,height,depth,dim); + switch (scheme) { // Compute 3D gradient + case -1 : { // backward finite differences + CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = (Tfloat)Iccc - Ipcc; + grad[1](x,y,z,k) = (Tfloat)Iccc - Icpc; + grad[2](x,y,z,k) = (Tfloat)Iccc - Iccp; + } + } break; + case 1 : { // forward finite differences + CImg_2x2x2(I,T); + cimg_forV(*this,k) cimg_for2x2x2(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = (Tfloat)Incc - Iccc; + grad[1](x,y,z,k) = (Tfloat)Icnc - Iccc; + grad[2](x,y,z,k) = (Tfloat)Iccn - Iccc; + } + } break; + case 4 : { // using Deriche filter with low standard variation + grad[0] = get_deriche(0,1,'x'); + grad[1] = get_deriche(0,1,'y'); + grad[2] = get_deriche(0,1,'z'); + } break; + default : { // central finite differences + CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = 0.5f*((Tfloat)Incc - Ipcc); + grad[1](x,y,z,k) = 0.5f*((Tfloat)Icnc - Icpc); + grad[2](x,y,z,k) = 0.5f*((Tfloat)Iccn - Iccp); + } + } + } + } else switch (scheme) { // Compute 2D-gradient + case -1 : { // backward finite differences + CImg_3x3(I,T); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = (Tfloat)Icc - Ipc; + grad[1](x,y,z,k) = (Tfloat)Icc - Icp; + } + } break; + case 1 : { // forward finite differences + CImg_2x2(I,T); + cimg_forZV(*this,z,k) cimg_for2x2(*this,x,y,z,k,I) { + grad[0](x,y,0,k) = (Tfloat)Inc - Icc; + grad[1](x,y,z,k) = (Tfloat)Icn - Icc; + } + } break; + case 2 : { // using Sobel mask + CImg_3x3(I,T); + const Tfloat a = 1, b = 2; + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + grad[1](x,y,z,k) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + } break; + case 3 : { // using rotation invariant mask + CImg_3x3(I,T); + const Tfloat a = (Tfloat)(0.25f*(2-cimg_std::sqrt(2.0f))), b = (Tfloat)(0.5f*(cimg_std::sqrt(2.0f)-1)); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + grad[1](x,y,z,k) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + } break; + case 4 : { // using Deriche filter with low standard variation + grad[0] = get_deriche(0,1,'x'); + grad[1] = get_deriche(0,1,'y'); + } break; + default : { // central finite differences + CImg_3x3(I,T); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { + grad[0](x,y,z,k) = 0.5f*((Tfloat)Inc - Ipc); + grad[1](x,y,z,k) = 0.5f*((Tfloat)Icn - Icp); + } + } + } + if (!axes) return grad; + CImgList res; + for (unsigned int l = 0; axes[l]; ++l) { + const char axis = cimg::uncase(axes[l]); + switch (axis) { + case 'x' : res.insert(grad[0]); break; + case 'y' : res.insert(grad[1]); break; + case 'z' : res.insert(grad[2]); break; + } + } + grad.assign(); + return res; + } + + //! Compute the structure tensor field of an image. + CImg& structure_tensor(const bool central_scheme=false) { + return get_structure_tensor(central_scheme).transfer_to(*this); + } + + CImg get_structure_tensor(const bool central_scheme=false) const { + if (is_empty()) return *this; + CImg res; + if (depth>1) { // 3D version + res.assign(width,height,depth,6,0); + CImg_3x3x3(I,T); + if (central_scheme) cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { // classical central finite differences + const Tfloat + ix = 0.5f*((Tfloat)Incc - Ipcc), + iy = 0.5f*((Tfloat)Icnc - Icpc), + iz = 0.5f*((Tfloat)Iccn - Iccp); + res(x,y,z,0)+=ix*ix; + res(x,y,z,1)+=ix*iy; + res(x,y,z,2)+=ix*iz; + res(x,y,z,3)+=iy*iy; + res(x,y,z,4)+=iy*iz; + res(x,y,z,5)+=iz*iz; + } else cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { // Precise forward/backward finite differences + const Tfloat + ixf = (Tfloat)Incc - Iccc, ixb = (Tfloat)Iccc - Ipcc, + iyf = (Tfloat)Icnc - Iccc, iyb = (Tfloat)Iccc - Icpc, + izf = (Tfloat)Iccn - Iccc, izb = (Tfloat)Iccc - Iccp; + res(x,y,z,0) += 0.5f*(ixf*ixf + ixb*ixb); + res(x,y,z,1) += 0.25f*(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb); + res(x,y,z,2) += 0.25f*(ixf*izf + ixf*izb + ixb*izf + ixb*izb); + res(x,y,z,3) += 0.5f*(iyf*iyf + iyb*iyb); + res(x,y,z,4) += 0.25f*(iyf*izf + iyf*izb + iyb*izf + iyb*izb); + res(x,y,z,5) += 0.5f*(izf*izf + izb*izb); + } + } else { // 2D version + res.assign(width,height,depth,3,0); + CImg_3x3(I,T); + if (central_scheme) cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { // classical central finite differences + const Tfloat + ix = 0.5f*((Tfloat)Inc - Ipc), + iy = 0.5f*((Tfloat)Icn - Icp); + res(x,y,0,0)+=ix*ix; + res(x,y,0,1)+=ix*iy; + res(x,y,0,2)+=iy*iy; + } else cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { // Precise forward/backward finite differences + const Tfloat + ixf = (Tfloat)Inc - Icc, ixb = (Tfloat)Icc - Ipc, + iyf = (Tfloat)Icn - Icc, iyb = (Tfloat)Icc - Icp; + res(x,y,0,0) += 0.5f*(ixf*ixf+ixb*ixb); + res(x,y,0,1) += 0.25f*(ixf*iyf+ixf*iyb+ixb*iyf+ixb*iyb); + res(x,y,0,2) += 0.5f*(iyf*iyf+iyb*iyb); + } + } + return res; + } + + //! Get components of the Hessian matrix of an image. + CImgList get_hessian(const char *const axes=0) const { + const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz"; + if (!axes) naxes = depth>1?def_axes3d:def_axes2d; + CImgList res; + const int lmax = cimg::strlen(naxes); + if (lmax%2) + throw CImgArgumentException("CImg<%s>::get_hessian() : Incomplete parameter axes = '%s'.", + pixel_type(),naxes); + res.assign(lmax/2,width,height,depth,dim); + if (!cimg::strcasecmp(naxes,def_axes3d)) { // Default 3D version + CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + res[0](x,y,z,k) = (Tfloat)Ipcc + Incc - 2*Iccc; // Ixx + res[1](x,y,z,k) = 0.25f*((Tfloat)Ippc + Innc - Ipnc - Inpc); // Ixy + res[2](x,y,z,k) = 0.25f*((Tfloat)Ipcp + Incn - Ipcn - Incp); // Ixz + res[3](x,y,z,k) = (Tfloat)Icpc + Icnc - 2*Iccc; // Iyy + res[4](x,y,z,k) = 0.25f*((Tfloat)Icpp + Icnn - Icpn - Icnp); // Iyz + res[5](x,y,z,k) = (Tfloat)Iccn + Iccp - 2*Iccc; // Izz + } + } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // Default 2D version + CImg_3x3(I,T); + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + res[0](x,y,0,k) = (Tfloat)Ipc + Inc - 2*Icc; // Ixx + res[1](x,y,0,k) = 0.25f*((Tfloat)Ipp + Inn - Ipn - Inp); // Ixy + res[2](x,y,0,k) = (Tfloat)Icp + Icn - 2*Icc; // Iyy + } + } else for (int l = 0; laxis2) cimg::swap(axis1,axis2); + bool valid_axis = false; + if (axis1=='x' && axis2=='x') { // Ixx + valid_axis = true; CImg_3x3(I,T); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Ipc + Inc - 2*Icc; + } + else if (axis1=='x' && axis2=='y') { // Ixy + valid_axis = true; CImg_3x3(I,T); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Ipp + Inn - Ipn - Inp); + } + else if (axis1=='x' && axis2=='z') { // Ixz + valid_axis = true; CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Ipcp + Incn - Ipcn - Incp); + } + else if (axis1=='y' && axis2=='y') { // Iyy + valid_axis = true; CImg_3x3(I,T); + cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Icp + Icn - 2*Icc; + } + else if (axis1=='y' && axis2=='z') { // Iyz + valid_axis = true; CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = 0.25f*((Tfloat)Icpp + Icnn - Icpn - Icnp); + } + else if (axis1=='z' && axis2=='z') { // Izz + valid_axis = true; CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) res[l2](x,y,z,k) = (Tfloat)Iccn + Iccp - 2*Iccc; + } + else if (!valid_axis) throw CImgArgumentException("CImg<%s>::get_hessian() : Invalid parameter axes = '%s'.", + pixel_type(),naxes); + } + return res; + } + + //! Compute distance function from 0-valued isophotes by the application of an Hamilton-Jacobi PDE. + CImg& distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) { + if (is_empty()) return *this; + CImg veloc(*this); + for (unsigned int iter = 0; iter1) { // 3D version + CImg_3x3x3(I,T); + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) if (band_size<=0 || cimg::abs(Iccc)0?(Tfloat)Incc - Iccc:(Tfloat)Iccc - Ipcc, + iy = gy*sgn>0?(Tfloat)Icnc - Iccc:(Tfloat)Iccc - Icpc, + iz = gz*sgn>0?(Tfloat)Iccn - Iccc:(Tfloat)Iccc - Iccp, + ng = 1e-5f + (Tfloat)cimg_std::sqrt(gx*gx + gy*gy + gz*gz), + ngx = gx/ng, + ngy = gy/ng, + ngz = gz/ng; + veloc(x,y,z,k) = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); + } + } else { // 2D version + CImg_3x3(I,T); + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) if (band_size<=0 || cimg::abs(Icc)0?(Tfloat)Inc - Icc:(Tfloat)Icc - Ipc, + iy = gy*sgn>0?(Tfloat)Icn - Icc:(Tfloat)Icc - Icp, + ng = 1e-5f + (Tfloat)cimg_std::sqrt(gx*gx + gy*gy), + ngx = gx/ng, + ngy = gy/ng; + veloc(x,y,k) = sgn*(ngx*ix + ngy*iy - 1); + } + } + float m, M = (float)veloc.maxmin(m), xdt = precision/(float)cimg::max(cimg::abs(m),cimg::abs(M)); + *this+=(veloc*=xdt); + } + return *this; + } + + CImg get_distance_hamilton(const unsigned int nb_iter, const float band_size=0, const float precision=0.5f) const { + return CImg(*this,false).distance_hamilton(nb_iter,band_size,precision); + } + + //! Compute the Euclidean distance map to a shape of specified isovalue. + CImg& distance(const T isovalue, + const float sizex=1, const float sizey=1, const float sizez=1, + const bool compute_sqrt=true) { + return get_distance(isovalue,sizex,sizey,sizez,compute_sqrt).transfer_to(*this); + } + + CImg get_distance(const T isovalue, + const float sizex=1, const float sizey=1, const float sizez=1, + const bool compute_sqrt=true) const { + if (is_empty()) return *this; + const int dx = dimx(), dy = dimy(), dz = dimz(); + CImg res(dx,dy,dz,dim); + const float maxdist = (float)cimg_std::sqrt((float)dx*dx + dy*dy + dz*dz); + cimg_forV(*this,k) { + bool is_isophote = false; + + if (depth>1) { // 3D version + { cimg_forYZ(*this,y,z) { + if ((*this)(0,y,z,k)==isovalue) { is_isophote = true; res(0,y,z,k) = 0; } else res(0,y,z,k) = maxdist; + for (int x = 1; x=0; --x) if (res(x+1,y,z,k)::max()); continue; } + CImg tmp(cimg::max(dy,dz)); + CImg s(tmp.width), t(s.width); + { cimg_forXZ(*this,x,z) { + { cimg_forY(*this,y) tmp[y] = res(x,y,z,k); } + int q = s[0] = t[0] = 0; + { for (int y = 1; y=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizey)>_distance_f(t[q],y,val2,sizey)) --q; + if (q<0) { q = 0; s[0] = y; } + else { + const int w = 1 + _distance_sep(s[q],y,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizey); + if (w=0; --y) { + res(x,y,z,k) = _distance_f(y,s[q],cimg::sqr(tmp[s[q]]),sizey); + if (y==t[q]) --q; + }} + }} + { cimg_forXY(*this,x,y) { + { cimg_forZ(*this,z) tmp[z] = res(x,y,z,k); } + int q = s[0] = t[0] = 0; + { for (int z = 1; z=0 && _distance_f(t(q),s[q],tmp[s[q]],sizez)>_distance_f(t[q],z,tmp[z],sizez)) --q; + if (q<0) { q = 0; s[0] = z; } + else { + const int w = 1 + _distance_sep(s[q],z,(int)tmp[s[q]],(int)val,sizez); + if (w=0; --z) { + const float val = _distance_f(z,s[q],tmp[s[q]],sizez); + res(x,y,z,k) = compute_sqrt?(float)cimg_std::sqrt(val):val; + if (z==t[q]) --q; + }} + }} + } else { // 2D version (with small optimizations) + cimg_forX(*this,x) { + const T *ptrs = ptr(x,0,0,k); + float *ptrd = res.ptr(x,0,0,k), d = *ptrd = *ptrs==isovalue?(is_isophote=true),0:maxdist; + for (int y = 1; y=0; --y) { ptrd-=width; if (d<*ptrd) *ptrd = (d+=sizey); else d = *ptrd; }} + } + if (!is_isophote) { res.get_shared_channel(k).fill(cimg::type::max()); continue; } + CImg tmp(dx); + CImg s(dx), t(dx); + cimg_forY(*this,y) { + float *ptmp = tmp.ptr(); + cimg_std::memcpy(ptmp,res.ptr(0,y,0,k),sizeof(float)*dx); + int q = s[0] = t[0] = 0; + for (int x = 1; x=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizex)>_distance_f(t[q],x,val2,sizex)) --q; + if (q<0) { q = 0; s[0] = x; } + else { + const int w = 1 + _distance_sep(s[q],x,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizex); + if (w=0; --x) { + const float val = _distance_f(x,s[q],cimg::sqr(tmp[s[q]]),sizex); + *(--pres) = compute_sqrt?(float)cimg_std::sqrt(val):val; + if (x==t[q]) --q; + }} + } + } + } + return res; + } + + static float _distance_f(const int x, const int i, const float gi2, const float fact) { + const float xmi = fact*((float)x - i); + return xmi*xmi + gi2; + } + static int _distance_sep(const int i, const int u, const int gi2, const int gu2, const float fact) { + const float fact2 = fact*fact; + return (int)(fact2*(u*u - i*i) + gu2 - gi2)/(int)(2*fact2*(u - i)); + } + + //! Compute minimal path in a graph, using the Dijkstra algorithm. + /** + \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j). + \param nb_nodes Number of graph nodes. + \param starting_node Indice of the starting node. + \param ending_node Indice of the ending node (set to ~0U to ignore ending node). + \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). + \return Array of distances of each node to the starting node. + **/ + template + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node, + CImg& previous) { + + CImg dist(1,nb_nodes,1,1,cimg::type::max()); + dist(starting_node) = 0; + previous.assign(1,nb_nodes,1,1,(t)-1); + previous(starting_node) = (t)starting_node; + CImg Q(nb_nodes); + cimg_forX(Q,u) Q(u) = u; + cimg::swap(Q(starting_node),Q(0)); + unsigned int sizeQ = nb_nodes; + while (sizeQ) { + // Update neighbors from minimal vertex + const unsigned int umin = Q(0); + if (umin==ending_node) sizeQ = 0; + else { + const T dmin = dist(umin); + const T infty = cimg::type::max(); + for (unsigned int q=1; qdist(Q(left))) || (rightdist(Q(right)));) { + if (right + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node=~0U) { + CImg foo; + return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + /** + Instance image corresponds to the adjacency matrix of the graph. + \param starting_node Indice of the starting node. + \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). + \return Array of distances of each node to the starting node. + **/ + template + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { + return get_dijkstra(starting_node,ending_node,previous).transfer_to(*this); + } + + template + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) const { + if (width!=height || depth!=1 || dim!=1) + throw CImgInstanceException("CImg<%s>::dijkstra() : Instance image (%u,%u,%u,%u,%p) is not a graph adjacency matrix", + pixel_type(),width,height,depth,dim,data); + return dijkstra(*this,width,starting_node,ending_node,previous); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { + return get_dijkstra(starting_node,ending_node).transfer_to(*this); + } + + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { + CImg foo; + return get_dijkstra(starting_node,ending_node,foo); + } + + //@} + //------------------------------------- + // + //! \name Meshes and Triangulations + //@{ + //------------------------------------- + + //! Return a 3D centered cube. + template + static CImg cube3d(CImgList& primitives, const float size=100) { + const double s = size/2.0; + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + -s,s,s,-s,-s,s,s,-s, + -s,-s,s,s,-s,-s,s,s, + -s,-s,-s,-s,s,s,s,s); + } + + //! Return a 3D centered cuboid. + template + static CImg cuboid3d(CImgList& primitives, const float sizex=200, + const float sizey=100, const float sizez=100) { + const double sx = sizex/2.0, sy = sizey/2.0, sz = sizez/2.0; + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + -sx,sx,sx,-sx,-sx,sx,sx,-sx, + -sy,-sy,sy,sy,-sy,-sy,sy,sy, + -sz,-sz,-sz,-sz,sz,sz,sz,sz); + } + + //! Return a 3D centered cone. + template + static CImg cone3d(CImgList& primitives, const float radius=50, const float height=100, + const unsigned int subdivisions=24, const bool symetrize=false) { + primitives.assign(); + if (!subdivisions) return CImg(); + const double r = (double)radius, h = (double)height/2; + CImgList points(2,1,3,1,1, + 0.0,0.0,h, + 0.0,0.0,-h); + const float delta = 360.0f/subdivisions, nh = symetrize?0:-(float)h; + for (float angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::valuePI/180); + points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),nh)); + } + const unsigned int nbr = points.size-2; + for (unsigned int p = 0; p::vector(1,next,curr)). + insert(CImg::vector(0,curr,next)); + } + return points.get_append('x'); + } + + //! Return a 3D centered cylinder. + template + static CImg cylinder3d(CImgList& primitives, const float radius=50, const float height=100, + const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + const double r = (double)radius, h = (double)height/2; + CImgList points(2,1,3,1,1, + 0.0,0.0,-h, + 0.0,0.0,h); + + const float delta = 360.0f/subdivisions; + for (float angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::valuePI/180); + points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),-(float)h)); + points.insert(CImg::vector((float)(r*cimg_std::cos(a)),(float)(r*cimg_std::sin(a)),(float)h)); + } + const unsigned int nbr = (points.size-2)/2; + for (unsigned int p = 0; p::vector(0,next,curr)). + insert(CImg::vector(1,curr+1,next+1)). + insert(CImg::vector(curr,next,next+1,curr+1)); + } + return points.get_append('x'); + } + + //! Return a 3D centered torus. + template + static CImg torus3d(CImgList& primitives, const float radius1=100, const float radius2=30, + const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { + primitives.assign(); + if (!subdivisions1 || !subdivisions2) return CImg(); + CImgList points; + for (unsigned int v = 0; v::vector(x,y,z)); + } + } + for (unsigned int vv = 0; vv::vector(svv+nu,svv+uu,snv+uu)); + primitives.insert(CImg::vector(svv+nu,snv+uu,snv+nu)); + } + } + return points.get_append('x'); + } + + //! Return a 3D centered XY plane. + template + static CImg plane3d(CImgList& primitives, const float sizex=100, const float sizey=100, + const unsigned int subdivisionsx=3, const unsigned int subdivisionsy=3, + const bool double_sided=false) { + primitives.assign(); + if (!subdivisionsx || !subdivisionsy) return CImg(); + CImgList points; + const unsigned int w = subdivisionsx + 1, h = subdivisionsy + 1; + const float w2 = subdivisionsx/2.0f, h2 = subdivisionsy/2.0f, fx = (float)sizex/w, fy = (float)sizey/h; + for (unsigned int yy = 0; yy::vector(fx*(xx-w2),fy*(yy-h2),0)); + for (unsigned int y = 0; y::vector(off1,off4,off3,off2)); + if (double_sided) primitives.insert(CImg::vector(off1,off2,off3,off4)); + } + return points.get_append('x'); + } + + //! Return a 3D centered sphere. + template + static CImg sphere3d(CImgList& primitives, const float radius=50, const unsigned int subdivisions=3) { + + // Create initial icosahedron + primitives.assign(); + if (!subdivisions) return CImg(); + const double tmp = (1+cimg_std::sqrt(5.0f))/2, a = 1.0/cimg_std::sqrt(1+tmp*tmp), b = tmp*a; + CImgList points(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b, + -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a); + primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6, + 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, + 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); + + // Recurse subdivisions + for (unsigned int i = 0; i::vector(nx0,ny0,nz0)); i0 = points.size-1; } + if (i1<0) { points.insert(CImg::vector(nx1,ny1,nz1)); i1 = points.size-1; } + if (i2<0) { points.insert(CImg::vector(nx2,ny2,nz2)); i2 = points.size-1; } + primitives.remove(0); + primitives.insert(CImg::vector(p0,i0,i1)). + insert(CImg::vector((tf)i0,(tf)p1,(tf)i2)). + insert(CImg::vector((tf)i1,(tf)i2,(tf)p2)). + insert(CImg::vector((tf)i1,(tf)i0,(tf)i2)); + } + } + return points.get_append('x')*=radius; + } + + //! Return a 3D centered ellipsoid. + template + static CImg ellipsoid3d(CImgList& primitives, const CImg& tensor, + const unsigned int subdivisions=3) { + primitives.assign(); + if (!subdivisions) return CImg(); + typedef typename cimg::superset::type tfloat; + CImg S,V; + tensor.symmetric_eigen(S,V); + const tfloat l0 = S[0], l1 = S[1], l2 = S[2]; + CImg points = sphere(primitives,subdivisions); + cimg_forX(points,p) { + points(p,0) = (float)(points(p,0)*l0); + points(p,1) = (float)(points(p,1)*l1); + points(p,2) = (float)(points(p,2)*l2); + } + V.transpose(); + points = V*points; + return points; + } + + //! Return a 3D elevation object of the instance image. + template + CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { + primitives.assign(); + colors.assign(); + if (is_empty()) return *this; + if (depth>1) + throw CImgInstanceException("CImg<%s>::get_elevation3d() : Instance image (%u,%u,%u,%u,%p) is not a 2D image.", + pixel_type(),width,height,depth,dim,data); + if (!is_sameXY(elevation)) + throw CImgArgumentException("CImg<%s>::get_elevation3d() : Elevation image (%u,%u,%u,%u,%p) and instance image (%u,%u,%u,%u,%p) " + "have different sizes.",pixel_type(), + elevation.width,elevation.height,elevation.depth,elevation.dim,elevation.data, + width,height,depth,dim,data,pixel_type()); + float m, M = (float)maxmin(m); + if (M==m) ++M; + const unsigned int w = width + 1, h = height + 1; + CImg points(w*h,3); + cimg_forXY(*this,x,y) { + const int yw = y*w, xpyw = x + yw, xpyww = xpyw + w; + points(xpyw,0) = points(xpyw+1,0) = points(xpyww+1,0) = points(xpyww,0) = (float)x; + points(xpyw,1) = points(xpyw+1,1) = points(xpyww+1,1) = points(xpyww,1) = (float)y; + points(xpyw,2) = points(xpyw+1,2) = points(xpyww+1,2) = points(xpyww,2) = (float)elevation(x,y); + primitives.insert(CImg::vector(xpyw,xpyw+1,xpyww+1,xpyww)); + const unsigned char + r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)), + g = dim>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r, + b = dim>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(dim>1?0:r); + colors.insert(CImg::vector((tc)r,(tc)g,(tc)b)); + } + return points; + } + + // Inner routine used by the Marching square algorithm. + template + static int _marching_squares_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0 : return (int)indices1(x,0); + case 1 : return (int)indices1(nx,1); + case 2 : return (int)indices2(x,0); + case 3 : return (int)indices1(x,1); + } + return 0; + } + + //! Polygonize an implicit 2D function by the marching squares algorithm. + template + static CImg marching_squares(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, + const float x1, const float y1, + const float resx, const float resy) { + static unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, + { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, + { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, + { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; + const unsigned int + nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, + ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1; + if (!nxm1 || !nym1) return CImg(); + + primitives.assign(); + CImgList points; + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = 0, Y = 0, nX = 0, nY = 0; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=resx; } + + // Run the marching squares algorithm + Y = y0; nY = Y + resy; + for (unsigned int yi = 0, nyi = 1; yi::vector(Xi,Y)); + } + if ((edge&2) && indices1(nxi,1)<0) { + const float Yi = Y + (isovalue-val1)*resy/(val2-val1); + indices1(nxi,1) = points.size; + points.insert(CImg::vector(nX,Yi)); + } + if ((edge&4) && indices2(xi,0)<0) { + const float Xi = X + (isovalue-val3)*resx/(val2-val3); + indices2(xi,0) = points.size; + points.insert(CImg::vector(Xi,nY)); + } + if ((edge&8) && indices1(xi,1)<0) { + const float Yi = Y + (isovalue-val0)*resy/(val3-val0); + indices1(xi,1) = points.size; + points.insert(CImg::vector(X,Yi)); + } + + // Create segments + for (int *segment = segments[configuration]; *segment!=-1; ) { + const unsigned int p0 = *(segment++), p1 = *(segment++); + const tf + i0 = (tf)(_marching_squares_indice(p0,indices1,indices2,xi,nxi)), + i1 = (tf)(_marching_squares_indice(p1,indices1,indices2,xi,nxi)); + primitives.insert(CImg::vector(i0,i1)); + } + } + } + values1.swap(values2); + indices1.swap(indices2); + } + return points.get_append('x'); + } + + // Inner routine used by the Marching cube algorithm. + template + static int _marching_cubes_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0 : return indices1(x,y,0); + case 1 : return indices1(nx,y,1); + case 2 : return indices1(x,ny,0); + case 3 : return indices1(x,y,1); + case 4 : return indices2(x,y,0); + case 5 : return indices2(nx,y,1); + case 6 : return indices2(x,ny,0); + case 7 : return indices2(x,y,1); + case 8 : return indices1(x,y,2); + case 9 : return indices1(nx,y,2); + case 10 : return indices1(nx,ny,2); + case 11 : return indices1(x,ny,2); + } + return 0; + } + + //! Polygonize an implicit function + // This function uses the Marching Cubes Tables published on the web page : + // http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ + template + static CImg marching_cubes(CImgList& primitives, + const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const float resx, const float resy, const float resz, + const bool invert_faces=false) { + + static unsigned int edges[256] = { + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 }; + + static int triangles[256][16] = + {{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 3, 8, -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 }}; + + const unsigned int + nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, + ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1, + nz = (unsigned int)((z1-z0+1)/resz), nzm1 = nz-1; + if (!nxm1 || !nym1 || !nzm1) return CImg(); + + primitives.assign(); + CImgList points; + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; + + // Fill the first plane with function values + Y = y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=resx; } + Y+=resy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + resz; + for (unsigned int zi = 0; zi::vector(Xi,Y,Z)); + } + if ((edge&2) && indices1(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val1)*resy/(val2-val1); + indices1(nxi,yi,1) = points.size; + points.insert(CImg::vector(nX,Yi,Z)); + } + if ((edge&4) && indices1(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val3)*resx/(val2-val3); + indices1(xi,nyi,0) = points.size; + points.insert(CImg::vector(Xi,nY,Z)); + } + if ((edge&8) && indices1(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val0)*resy/(val3-val0); + indices1(xi,yi,1) = points.size; + points.insert(CImg::vector(X,Yi,Z)); + } + if ((edge&16) && indices2(xi,yi,0)<0) { + const float Xi = X + (isovalue-val4)*resx/(val5-val4); + indices2(xi,yi,0) = points.size; + points.insert(CImg::vector(Xi,Y,nZ)); + } + if ((edge&32) && indices2(nxi,yi,1)<0) { + const float Yi = Y + (isovalue-val5)*resy/(val6-val5); + indices2(nxi,yi,1) = points.size; + points.insert(CImg::vector(nX,Yi,nZ)); + } + if ((edge&64) && indices2(xi,nyi,0)<0) { + const float Xi = X + (isovalue-val7)*resx/(val6-val7); + indices2(xi,nyi,0) = points.size; + points.insert(CImg::vector(Xi,nY,nZ)); + } + if ((edge&128) && indices2(xi,yi,1)<0) { + const float Yi = Y + (isovalue-val4)*resy/(val7-val4); + indices2(xi,yi,1) = points.size; + points.insert(CImg::vector(X,Yi,nZ)); + } + if ((edge&256) && indices1(xi,yi,2)<0) { + const float Zi = Z+ (isovalue-val0)*resz/(val4-val0); + indices1(xi,yi,2) = points.size; + points.insert(CImg::vector(X,Y,Zi)); + } + if ((edge&512) && indices1(nxi,yi,2)<0) { + const float Zi = Z + (isovalue-val1)*resz/(val5-val1); + indices1(nxi,yi,2) = points.size; + points.insert(CImg::vector(nX,Y,Zi)); + } + if ((edge&1024) && indices1(nxi,nyi,2)<0) { + const float Zi = Z + (isovalue-val2)*resz/(val6-val2); + indices1(nxi,nyi,2) = points.size; + points.insert(CImg::vector(nX,nY,Zi)); + } + if ((edge&2048) && indices1(xi,nyi,2)<0) { + const float Zi = Z + (isovalue-val3)*resz/(val7-val3); + indices1(xi,nyi,2) = points.size; + points.insert(CImg::vector(X,nY,Zi)); + } + + // Create triangles + for (int *triangle = triangles[configuration]; *triangle!=-1; ) { + const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); + const tf + i0 = (tf)(_marching_cubes_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), + i1 = (tf)(_marching_cubes_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), + i2 = (tf)(_marching_cubes_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); + else primitives.insert(CImg::vector(i0,i2,i1)); + } + } + } + } + cimg::swap(values1,values2); + cimg::swap(indices1,indices2); + } + return points.get_append('x'); + } + + struct _marching_squares_func { + const CImg& ref; + _marching_squares_func(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _marching_cubes_func { + const CImg& ref; + _marching_cubes_func(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _marching_squares_func_float { + const CImg& ref; + _marching_squares_func_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref._linear_atXY(x,y); + } + }; + + struct _marching_cubes_func_float { + const CImg& ref; + _marching_cubes_func_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref._linear_atXYZ(x,y,z); + } + }; + + //! Compute a vectorization of an implicit function. + template + CImg get_isovalue3d(CImgList& primitives, const float isovalue, + const float resx=1, const float resy=1, const float resz=1, + const bool invert_faces=false) const { + primitives.assign(); + if (is_empty()) return *this; + if (dim>1) + throw CImgInstanceException("CImg<%s>::get_isovalue3d() : Instance image (%u,%u,%u,%u,%p) is not a scalar image.", + pixel_type(),width,height,depth,dim,data); + CImg points; + if (depth>1) { + if (resx==1 && resy==1 && resz==1) { + const _marching_cubes_func func(*this); + points = marching_cubes(primitives,func,isovalue,0,0,0,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f,resx,resy,resz,invert_faces); + } else { + const _marching_cubes_func_float func(*this); + points = marching_cubes(primitives,func,isovalue,0,0,0,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f,resx,resy,resz,invert_faces); + } + } else { + if (resx==1 && resy==1) { + const _marching_squares_func func(*this); + points = marching_squares(primitives,func,isovalue,0,0,dimx()-1.0f,dimy()-1.0f,resx,resy); + } else { + const _marching_squares_func_float func(*this); + points = marching_squares(primitives,func,isovalue,0,0,dimx()-1.0f,dimy()-1.0f,resx,resy); + } + if (points) points.resize(-100,3,1,1,0); + } + return points; + } + + //! Translate a 3D object. + CImg& translate_object3d(const float tx, const float ty=0, const float tz=0) { + get_shared_line(0)+=tx; get_shared_line(1)+=ty; get_shared_line(2)+=tz; + return *this; + } + + CImg get_translate_object3d(const float tx, const float ty=0, const float tz=0) const { + return CImg(*this,false).translate_object3d(tx,ty,tz); + } + + //! Translate a 3D object so that it becomes centered. + CImg& translate_object3d() { + CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); + float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); + xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; + return *this; + } + + CImg get_translate_object3d() const { + return CImg(*this,false).translate_object3d(); + } + + //! Resize a 3D object. + CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { + CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); + float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); + if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } + if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } + if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } + return *this; + } + + CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { + return CImg(*this,false).resize_object3d(sx,sy,sz); + } + + // Resize a 3D object so that its max dimension if one. + CImg resize_object3d() const { + CImg xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2); + float xm, xM = (float)xcoords.maxmin(xm), ym, yM = (float)ycoords.maxmin(ym), zm, zM = (float)zcoords.maxmin(zm); + const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); + if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } + return *this; + } + + CImg get_resize_object3d() const { + return CImg(*this,false).resize_object3d(); + } + + //! Append a 3D object to another one. + template + CImg& append_object3d(CImgList& primitives, const CImg& obj_points, const CImgList& obj_primitives) { + const unsigned int P = width; + append(obj_points,'x'); + const unsigned int N = primitives.size; + primitives.insert(obj_primitives); + for (unsigned int i = N; i &p = primitives[i]; + if (p.size()!=5) p+=P; + else { p[0]+=P; if (p[2]==0) p[1]+=P; } + } + return *this; + } + + //@} + //---------------------------- + // + //! \name Color bases + //@{ + //---------------------------- + + //! Return a default indexed color palette with 256 (R,G,B) entries. + /** + The default color palette is used by %CImg when displaying images on 256 colors displays. + It consists in the quantification of the (R,G,B) color space using 3:3:2 bits for color coding + (i.e 8 levels for the Red and Green and 4 levels for the Blue). + \return a 1x256x1x3 color image defining the palette entries. + **/ + static CImg default_LUT8() { + static CImg palette; + if (!palette) { + palette.assign(1,256,1,3); + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + palette(0,index,0) = (Tuchar)r; + palette(0,index,1) = (Tuchar)g; + palette(0,index++,2) = (Tuchar)b; + } + } + return palette; + } + + //! Return a rainbow color palette with 256 (R,G,B) entries. + static CImg rainbow_LUT8() { + static CImg palette; + if (!palette) { + CImg tmp(1,256,1,3,1); + tmp.get_shared_channel(0).sequence(0,359); + palette = tmp.HSVtoRGB(); + } + return palette; + } + + //! Return a contrasted color palette with 256 (R,G,B) entries. + static CImg contrast_LUT8() { + static const unsigned char pal[] = { + 217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226, + 17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119, + 238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20, + 233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74, + 81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219, + 1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12, + 87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0, + 223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32, + 233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4, + 137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224, + 4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247, + 11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246, + 0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10, + 141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143, + 116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244, + 255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0, + 235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251, + 129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30, + 243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215, + 95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3, + 141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174, + 154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87, + 33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21, + 23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 }; + static const CImg palette(pal,1,256,1,3,false); + return palette; + } + + //! Convert (R,G,B) color image to indexed color image. + template + CImg& RGBtoLUT(const CImg& palette, const bool dithering=true, const bool indexing=false) { + return get_RGBtoLUT(palette,dithering,indexing).transfer_to(*this); + } + + template + CImg get_RGBtoLUT(const CImg& palette, const bool dithering=true, const bool indexing=false) const { + if (is_empty()) return CImg(); + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoLUT() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.", + pixel_type(),dim); + if (palette.data && palette.dim!=3) + throw CImgArgumentException("CImg<%s>::RGBtoLUT() : Given palette dimension is dim=%u, " + "should be a (R,G,B) palette", + pixel_type(),palette.dim); + CImg res(width,height,depth,indexing?1:3); + float *line1 = new float[3*width], *line2 = new float[3*width]; + t *pRd = res.ptr(0,0,0,0), *pGd = indexing?pRd:res.ptr(0,0,0,1), *pBd = indexing?pRd:res.ptr(0,0,0,2); + cimg_forZ(*this,z) { + const T *pRs = ptr(0,0,z,0), *pGs = ptr(0,0,z,1), *pBs = ptr(0,0,z,2); + float *ptrd = line2; cimg_forX(*this,x) { *(ptrd++) = (float)*(pRs++); *(ptrd++) = (float)*(pGs++); *(ptrd++) = (float)*(pBs++); } + cimg_forY(*this,y) { + cimg::swap(line1,line2); + if (y255?255:R); G = G<0?0:(G>255?255:G); B = B<0?0:(B>255?255:B); + t Rbest = 0, Gbest = 0, Bbest = 0; + int best_index = 0; + if (palette) { // find best match in given color palette + const t *pRs = palette.ptr(0,0,0,0), *pGs = palette.ptr(0,0,0,1), *pBs = palette.ptr(0,0,0,2); + const unsigned int Npal = palette.width*palette.height*palette.depth; + float min = cimg::type::max(); + for (unsigned int off = 0; off>3) | ((unsigned char)Bbest>>6); + } + if (indexing) *(pRd++) = (t)best_index; else { *(pRd++) = Rbest; *(pGd++) = Gbest; *(pBd++) = Bbest; } + if (dithering) { // apply dithering to neighborhood pixels if needed + const float dR = (float)(R-Rbest), dG = (float)(G-Gbest), dB = (float)(B-Bbest); + if (x0) { *(--ptr2)+= dB*3/16; *(--ptr2)+= dG*3/16; *(--ptr2)+= dR*3/16; ptr2+=3; } + if (x& RGBtoLUT(const bool dithering=true, const bool indexing=false) { + return get_RGBtoLUT(dithering,indexing).transfer_to(*this); + } + + CImg get_RGBtoLUT(const bool dithering=true, const bool indexing=false) const { + static const CImg empty; + return get_RGBtoLUT(empty,dithering,indexing); + } + + //! Convert an indexed image to a (R,G,B) image using the specified color palette. + CImg& LUTtoRGB(const CImg& palette) { + return get_LUTtoRGB(palette).transfer_to(*this); + } + + template + CImg get_LUTtoRGB(const CImg& palette) const { + if (is_empty()) return CImg(); + if (dim!=1) + throw CImgInstanceException("CImg<%s>::LUTtoRGB() : Input image dimension is dim=%u, " + "should be a LUT image", + pixel_type(),dim); + if (palette.data && palette.dim!=3) + throw CImgArgumentException("CImg<%s>::LUTtoRGB() : Given palette dimension is dim=%u, " + "should be a (R,G,B) palette", + pixel_type(),palette.dim); + const CImg pal = palette.data?palette:CImg(default_LUT8()); + CImg res(width,height,depth,3); + const t *pRs = pal.ptr(0,0,0,0), *pGs = pal.ptr(0,0,0,1), *pBs = pal.ptr(0,0,0,2); + t *pRd = res.ptr(0,0,0,1), *pGd = pRd + width*height*depth, *pBd = pGd + width*height*depth; + const unsigned int Npal = palette.width*palette.height*palette.depth; + cimg_for(*this,ptr,T) { + const unsigned int index = ((unsigned int)*ptr)%Npal; + *(--pRd) = pRs[index]; *(--pGd) = pGs[index]; *(--pBd) = pBs[index]; + } + return res; + } + + //! Convert an indexed image (with the default palette) to a (R,G,B) image. + CImg& LUTtoRGB() { + return get_LUTtoRGB().transfer_to(*this); + } + + CImg get_LUTtoRGB() const { + static const CImg empty; + return get_LUTtoRGB(empty); + } + + //! Convert color pixels from (R,G,B) to (H,S,V). + CImg& RGBtoHSV() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoHSV() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1, + G = (Tfloat)*p2, + B = (Tfloat)*p3, + nR = (R<0?0:(R>255?255:R))/255, + nG = (G<0?0:(G>255?255:G))/255, + nB = (B<0?0:(B>255?255:B))/255, + m = cimg::min(nR,nG,nB), + M = cimg::max(nR,nG,nB); + Tfloat H = 0, S = 0; + if (M!=m) { + const Tfloat + f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), + i = (Tfloat)((nR==m)?3:((nG==m)?5:1)); + H = (i-f/(M-m)); + if (H>=6) H-=6; + H*=60; + S = (M-m)/M; + } + *(p1++) = (T)H; + *(p2++) = (T)S; + *(p3++) = (T)M; + } + return *this; + } + + CImg get_RGBtoHSV() const { + return CImg(*this,false).RGBtoHSV(); + } + + //! Convert color pixels from (H,S,V) to (R,G,B). + CImg& HSVtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::HSVtoRGB() : Input image dimension is dim=%u, " + "should be a (H,S,V) image", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + Tfloat + H = (Tfloat)*p1, + S = (Tfloat)*p2, + V = (Tfloat)*p3, + R = 0, G = 0, B = 0; + if (H==0 && S==0) R = G = B = V; + else { + H/=60; + const int i = (int)cimg_std::floor(H); + const Tfloat + f = (i&1)?(H-i):(1-H+i), + m = V*(1-S), + n = V*(1-S*f); + switch (i) { + case 6 : + case 0 : R = V; G = n; B = m; break; + case 1 : R = n; G = V; B = m; break; + case 2 : R = m; G = V; B = n; break; + case 3 : R = m; G = n; B = V; break; + case 4 : R = n; G = m; B = V; break; + case 5 : R = V; G = m; B = n; break; + } + } + R*=255; G*=255; B*=255; + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_HSVtoRGB() const { + return CImg(*this,false).HSVtoRGB(); + } + + //! Convert color pixels from (R,G,B) to (H,S,L). + CImg& RGBtoHSL() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoHSL() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1, + G = (Tfloat)*p2, + B = (Tfloat)*p3, + nR = (R<0?0:(R>255?255:R))/255, + nG = (G<0?0:(G>255?255:G))/255, + nB = (B<0?0:(B>255?255:B))/255, + m = cimg::min(nR,nG,nB), + M = cimg::max(nR,nG,nB), + L = (m+M)/2; + Tfloat H = 0, S = 0; + if (M==m) H = S = 0; + else { + const Tfloat + f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)), + i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f); + H = (i-f/(M-m)); + if (H>=6) H-=6; + H*=60; + S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m)); + } + *(p1++) = (T)H; + *(p2++) = (T)S; + *(p3++) = (T)L; + } + return *this; + } + + CImg get_RGBtoHSL() const { + return CImg< Tfloat>(*this,false).RGBtoHSL(); + } + + //! Convert color pixels from (H,S,L) to (R,G,B). + CImg& HSLtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::HSLtoRGB() : Input image dimension is dim=%u, " + "should be a (H,S,V) image", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + H = (Tfloat)*p1, + S = (Tfloat)*p2, + L = (Tfloat)*p3, + q = 2*L<1?L*(1+S):(L+S-L*S), + p = 2*L-q, + h = H/360, + tr = h + 1.0f/3, + tg = h, + tb = h - 1.0f/3, + ntr = tr<0?tr+1:(tr>1?tr-1:tr), + ntg = tg<0?tg+1:(tg>1?tg-1:tg), + ntb = tb<0?tb+1:(tb>1?tb-1:tb), + R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f/3-ntr):p))), + G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f/3-ntg):p))), + B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f/3-ntb):p))); + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert color pixels from (R,G,B) to (H,S,I). + //! Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. + CImg& RGBtoHSI() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoHSI() : Input image dimension is dim=%u, " + "should be a (R,G,B) image.", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1, + G = (Tfloat)*p2, + B = (Tfloat)*p3, + nR = (R<0?0:(R>255?255:R))/255, + nG = (G<0?0:(G>255?255:G))/255, + nB = (B<0?0:(B>255?255:B))/255, + m = cimg::min(nR,nG,nB), + theta = (Tfloat)(cimg_std::acos(0.5f*((nR-nG)+(nR-nB))/cimg_std::sqrt(cimg_std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::valuePI), + sum = nR + nG + nB; + Tfloat H = 0, S = 0, I = 0; + if (theta>0) H = (nB<=nG)?theta:360-theta; + if (sum>0) S = 1 - 3/sum*m; + I = sum/3; + *(p1++) = (T)H; + *(p2++) = (T)S; + *(p3++) = (T)I; + } + return *this; + } + + CImg get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert color pixels from (H,S,I) to (R,G,B). + CImg& HSItoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::HSItoRGB() : Input image dimension is dim=%u, " + "should be a (H,S,I) image", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + Tfloat + H = (Tfloat)*p1, + S = (Tfloat)*p2, + I = (Tfloat)*p3, + a = I*(1-S), + R = 0, G = 0, B = 0; + if (H<120) { + B = a; + R = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); + G = 3*I-(R+B); + } else if (H<240) { + H-=120; + R = a; + G = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); + B = 3*I-(R+G); + } else { + H-=240; + G = a; + B = (Tfloat)(I*(1+S*cimg_std::cos(H*cimg::valuePI/180)/cimg_std::cos((60-H)*cimg::valuePI/180))); + R = 3*I-(G+B); + } + R*=255; G*=255; B*=255; + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8. + CImg& RGBtoYCbCr() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoYCbCr() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1, + G = (Tfloat)*p2, + B = (Tfloat)*p3, + Y = (66*R + 129*G + 25*B + 128)/256 + 16, + Cb = (-38*R - 74*G + 112*B + 128)/256 + 128, + Cr = (112*R - 94*G - 18*B + 128)/256 + 128; + *(p1++) = (T)(Y<0?0:(Y>255?255:Y)); + *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb)); + *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr)); + } + return *this; + } + + CImg get_RGBtoYCbCr() const { + return CImg(*this,false).RGBtoYCbCr(); + } + + //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8. + CImg& YCbCrtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::YCbCrtoRGB() : Input image dimension is dim=%u, " + "should be a (Y,Cb,Cr)_8 image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + Y = (Tfloat)*p1 - 16, + Cb = (Tfloat)*p2 - 128, + Cr = (Tfloat)*p3 - 128, + R = (298*Y + 409*Cr + 128)/256, + G = (298*Y - 100*Cb - 208*Cr + 128)/256, + B = (298*Y + 516*Cb + 128)/256; + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_YCbCrtoRGB() const { + return CImg(*this,false).YCbCrtoRGB(); + } + + //! Convert color pixels from (R,G,B) to (Y,U,V). + CImg& RGBtoYUV() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoYUV() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1/255, + G = (Tfloat)*p2/255, + B = (Tfloat)*p3/255, + Y = 0.299f*R + 0.587f*G + 0.114f*B; + *(p1++) = (T)Y; + *(p2++) = (T)(0.492f*(B-Y)); + *(p3++) = (T)(0.877*(R-Y)); + } + return *this; + } + + CImg get_RGBtoYUV() const { + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert color pixels from (Y,U,V) to (R,G,B). + CImg& YUVtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::YUVtoRGB() : Input image dimension is dim=%u, " + "should be a (Y,U,V) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + Y = (Tfloat)*p1, + U = (Tfloat)*p2, + V = (Tfloat)*p3, + R = (Y + 1.140f*V)*255, + G = (Y - 0.395f*U - 0.581f*V)*255, + B = (Y + 2.032f*U)*255; + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_YUVtoRGB() const { + return CImg< Tuchar>(*this,false).YUVtoRGB(); + } + + //! Convert color pixels from (R,G,B) to (C,M,Y). + CImg& RGBtoCMY() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoCMY() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1/255, + G = (Tfloat)*p2/255, + B = (Tfloat)*p3/255; + *(p1++) = (T)(1 - R); + *(p2++) = (T)(1 - G); + *(p3++) = (T)(1 - B); + } + return *this; + } + + CImg get_RGBtoCMY() const { + return CImg(*this,false).RGBtoCMY(); + } + + //! Convert (C,M,Y) pixels of a color image into the (R,G,B) color space. + CImg& CMYtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::CMYtoRGB() : Input image dimension is dim=%u, " + "should be a (C,M,Y) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + C = (Tfloat)*p1, + M = (Tfloat)*p2, + Y = (Tfloat)*p3, + R = 255*(1 - C), + G = 255*(1 - M), + B = 255*(1 - Y); + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_CMYtoRGB() const { + return CImg(*this,false).CMYtoRGB(); + } + + //! Convert color pixels from (C,M,Y) to (C,M,Y,K). + CImg& CMYtoCMYK() { + return get_CMYtoCMYK().transfer_to(*this); + } + + CImg get_CMYtoCMYK() const { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::CMYtoCMYK() : Input image dimension is dim=%u, " + "should be a (C,M,Y) image (dim=3)", + pixel_type(),dim); + CImg res(width,height,depth,4); + const T *ps1 = ptr(0,0,0,0), *ps2 = ptr(0,0,0,1), *ps3 = ptr(0,0,0,2); + Tfloat *pd1 = res.ptr(0,0,0,0), *pd2 = res.ptr(0,0,0,1), *pd3 = res.ptr(0,0,0,2), *pd4 = res.ptr(0,0,0,3); + for (unsigned long N = width*height*depth; N; --N) { + Tfloat + C = (Tfloat)*(ps1++), + M = (Tfloat)*(ps2++), + Y = (Tfloat)*(ps3++), + K = cimg::min(C,M,Y); + if (K==1) C = M = Y = 0; + else { const Tfloat K1 = 1 - K; C = (C - K)/K1; M = (M - K)/K1; Y = (Y - K)/K1; } + *(pd1++) = C; + *(pd2++) = M; + *(pd3++) = Y; + *(pd4++) = K; + } + return res; + } + + //! Convert (C,M,Y,K) pixels of a color image into the (C,M,Y) color space. + CImg& CMYKtoCMY() { + return get_CMYKtoCMY().transfer_to(*this); + } + + CImg get_CMYKtoCMY() const { + if (is_empty()) return *this; + if (dim!=4) + throw CImgInstanceException("CImg<%s>::CMYKtoCMY() : Input image dimension is dim=%u, " + "should be a (C,M,Y,K) image (dim=4)", + pixel_type(),dim); + CImg res(width,height,depth,3); + const T *ps1 = ptr(0,0,0,0), *ps2 = ptr(0,0,0,1), *ps3 = ptr(0,0,0,2), *ps4 = ptr(0,0,0,3); + Tfloat *pd1 = res.ptr(0,0,0,0), *pd2 = res.ptr(0,0,0,1), *pd3 = res.ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + C = (Tfloat)*ps1, + M = (Tfloat)*ps2, + Y = (Tfloat)*ps3, + K = (Tfloat)*ps4, + K1 = 1 - K; + *(pd1++) = C*K1 + K; + *(pd2++) = M*K1 + K; + *(pd3++) = Y*K1 + K; + } + return res; + } + + //! Convert color pixels from (R,G,B) to (X,Y,Z)_709. + CImg& RGBtoXYZ() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoXYZ() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + R = (Tfloat)*p1/255, + G = (Tfloat)*p2/255, + B = (Tfloat)*p3/255; + *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B); + *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B); + *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B); + } + return *this; + } + + CImg get_RGBtoXYZ() const { + return CImg(*this,false).RGBtoXYZ(); + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space. + CImg& XYZtoRGB() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::XYZtoRGB() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + X = (Tfloat)*p1*255, + Y = (Tfloat)*p2*255, + Z = (Tfloat)*p3*255, + R = 3.240479f*X - 1.537150f*Y - 0.498535f*Z, + G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z, + B = 0.055648f*X - 0.204043f*Y + 1.057311f*Z; + *(p1++) = (T)(R<0?0:(R>255?255:R)); + *(p2++) = (T)(G<0?0:(G>255?255:G)); + *(p3++) = (T)(B<0?0:(B>255?255:B)); + } + return *this; + } + + CImg get_XYZtoRGB() const { + return CImg(*this,false).XYZtoRGB(); + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space. + CImg& XYZtoLab() { +#define _cimg_Labf(x) ((x)>=0.008856f?(cimg_std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116)) + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::XYZtoLab() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)", + pixel_type(),dim); + const Tfloat + Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), + Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), + Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + X = (Tfloat)*p1, + Y = (Tfloat)*p2, + Z = (Tfloat)*p3, + XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn, + fX = (Tfloat)_cimg_Labf(XXn), + fY = (Tfloat)_cimg_Labf(YYn), + fZ = (Tfloat)_cimg_Labf(ZZn); + *(p1++) = (T)(116*fY - 16); + *(p2++) = (T)(500*(fX - fY)); + *(p3++) = (T)(200*(fY - fZ)); + } + return *this; + } + + CImg get_XYZtoLab() const { + return CImg(*this,false).XYZtoLab(); + } + + //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space. + CImg& LabtoXYZ() { +#define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f)) + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::LabtoXYZ() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)", + pixel_type(),dim); + const Tfloat + Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f), + Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f), + Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + L = (Tfloat)*p1, + a = (Tfloat)*p2, + b = (Tfloat)*p3, + cY = (L + 16)/116, + Y = (Tfloat)(Yn*_cimg_Labfi(cY)), + pY = (Tfloat)cimg_std::pow(Y/Yn,(Tfloat)1/3), + cX = a/500 + pY, + X = Xn*cX*cX*cX, + cZ = pY - b/200, + Z = Zn*cZ*cZ*cZ; + *(p1++) = (T)(X); + *(p2++) = (T)(Y); + *(p3++) = (T)(Z); + } + return *this; + } + + CImg get_LabtoXYZ() const { + return CImg(*this,false).LabtoXYZ(); + } + + //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space. + CImg& XYZtoxyY() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::XYZtoxyY() : Input image dimension is dim=%u, " + "should be a (X,Y,Z) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + X = (Tfloat)*p1, + Y = (Tfloat)*p2, + Z = (Tfloat)*p3, + sum = (X+Y+Z), + nsum = sum>0?sum:1; + *(p1++) = (T)(X/nsum); + *(p2++) = (T)(Y/nsum); + *(p3++) = (T)Y; + } + return *this; + } + + CImg get_XYZtoxyY() const { + return CImg(*this,false).XYZtoxyY(); + } + + //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space. + CImg& xyYtoXYZ() { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::xyYtoXYZ() : Input image dimension is dim=%u, " + "should be a (x,y,Y) image (dim=3)", + pixel_type(),dim); + T *p1 = ptr(0,0,0,0), *p2 = ptr(0,0,0,1), *p3 = ptr(0,0,0,2); + for (unsigned long N = width*height*depth; N; --N) { + const Tfloat + px = (Tfloat)*p1, + py = (Tfloat)*p2, + Y = (Tfloat)*p3, + ny = py>0?py:1; + *(p1++) = (T)(px*Y/ny); + *(p2++) = (T)Y; + *(p3++) = (T)((1-px-py)*Y/ny); + } + return *this; + } + + CImg get_xyYtoXYZ() const { + return CImg(*this,false).xyYtoXYZ(); + } + + //! Convert a (R,G,B) image to a (L,a,b) one. + CImg& RGBtoLab() { + return RGBtoXYZ().XYZtoLab(); + } + + CImg get_RGBtoLab() const { + return CImg(*this,false).RGBtoLab(); + } + + //! Convert a (L,a,b) image to a (R,G,B) one. + CImg& LabtoRGB() { + return LabtoXYZ().XYZtoRGB(); + } + + CImg get_LabtoRGB() const { + return CImg(*this,false).LabtoRGB(); + } + + //! Convert a (R,G,B) image to a (x,y,Y) one. + CImg& RGBtoxyY() { + return RGBtoXYZ().XYZtoxyY(); + } + + CImg get_RGBtoxyY() const { + return CImg(*this,false).RGBtoxyY(); + } + + //! Convert a (x,y,Y) image to a (R,G,B) one. + CImg& xyYtoRGB() { + return xyYtoXYZ().XYZtoRGB(); + } + + CImg get_xyYtoRGB() const { + return CImg(*this,false).xyYtoRGB(); + } + + //! Convert a (R,G,B) image to a (C,M,Y,K) one. + CImg& RGBtoCMYK() { + return RGBtoCMY().CMYtoCMYK(); + } + + CImg get_RGBtoCMYK() const { + return CImg(*this,false).RGBtoCMYK(); + } + + //! Convert a (C,M,Y,K) image to a (R,G,B) one. + CImg& CMYKtoRGB() { + return CMYKtoCMY().CMYtoRGB(); + } + + CImg get_CMYKtoRGB() const { + return CImg(*this,false).CMYKtoRGB(); + } + + //! Convert a (R,G,B) image to a Bayer-coded representation. + /** + \note First (upper-left) pixel if the red component of the pixel color. + **/ + CImg& RGBtoBayer() { + return get_RGBtoBayer().transfer_to(*this); + } + + CImg get_RGBtoBayer() const { + if (is_empty()) return *this; + if (dim!=3) + throw CImgInstanceException("CImg<%s>::RGBtoBayer() : Input image dimension is dim=%u, " + "should be a (R,G,B) image (dim=3)", + pixel_type(),dim); + CImg res(width,height,depth,1); + const T *pR = ptr(0,0,0,0), *pG = ptr(0,0,0,1), *pB = ptr(0,0,0,2); + T *ptrd = res.data; + cimg_forXYZ(*this,x,y,z) { + if (y%2) { + if (x%2) *(ptrd++) = *pB; + else *(ptrd++) = *pG; + } else { + if (x%2) *(ptrd++) = *pG; + else *(ptrd++) = *pR; + } + ++pR; ++pG; ++pB; + } + return res; + } + + //! Convert a Bayer-coded image to a (R,G,B) color image. + CImg& BayertoRGB(const unsigned int interpolation_type=3) { + return get_BayertoRGB(interpolation_type).transfer_to(*this); + } + + CImg get_BayertoRGB(const unsigned int interpolation_type=3) const { + if (is_empty()) return *this; + if (dim!=1) + throw CImgInstanceException("CImg<%s>::BayertoRGB() : Input image dimension is dim=%u, " + "should be a Bayer image (dim=1)", + pixel_type(),dim); + CImg res(width,height,depth,3); + CImg_3x3(I,T); + Tuchar *pR = res.ptr(0,0,0,0), *pG = res.ptr(0,0,0,1), *pB = res.ptr(0,0,0,2); + switch (interpolation_type) { + case 3 : { // Edge-directed + CImg_3x3(R,T); + CImg_3x3(G,T); + CImg_3x3(B,T); + cimg_forXYZ(*this,x,y,z) { + const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x + CImg& _draw_scanline(const int x0, const int x1, const int y, + const tc *const color, const float opacity=1, + const float brightness=1, const bool init=false) { + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + static float nopacity = 0, copacity = 0; + static unsigned int whz = 0; + static const tc *col = 0; + if (init) { + nopacity = cimg::abs(opacity); + copacity = 1 - cimg::max(opacity,0); + whz = width*height*depth; + } else { + const int nx0 = x0>0?x0:0, nx1 = x1=0) { + col = color; + const unsigned int off = whz-dx-1; + T *ptrd = ptr(nx0,y); + if (opacity>=1) { // ** Opaque drawing ** + if (brightness==1) { // Brightness==1 + if (sizeof(T)!=1) cimg_forV(*this,k) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forV(*this,k) { + const T val = (T)*(col++); + cimg_std::memset(ptrd,(int)val,dx+1); + ptrd+=whz; + } + } else if (brightness<1) { // Brightness<1 + if (sizeof(T)!=1) cimg_forV(*this,k) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forV(*this,k) { + const T val = (T)(*(col++)*brightness); + cimg_std::memset(ptrd,(int)val,dx+1); + ptrd+=whz; + } + } else { // Brightness>1 + if (sizeof(T)!=1) cimg_forV(*this,k) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forV(*this,k) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + cimg_std::memset(ptrd,(int)val,dx+1); + ptrd+=whz; + } + } + } else { // ** Transparent drawing ** + if (brightness==1) { // Brightness==1 + cimg_forV(*this,k) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else if (brightness<=1) { // Brightness<1 + cimg_forV(*this,k) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else { // Brightness>1 + cimg_forV(*this,k) { + const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval); + for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } + } + } + } + return *this; + } + + template + CImg& _draw_scanline(const tc *const color, const float opacity=1) { + return _draw_scanline(0,0,0,color,opacity,0,true); + } + + //! Draw a 2D colored point (pixel). + /** + \param x0 X-coordinate of the point. + \param y0 Y-coordinate of the point. + \param color Pointer to \c dimv() consecutive values, defining the color values. + \param opacity Drawing opacity (optional). + \note + - Clipping is supported. + - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_point(50,50,color); + \endcode + **/ + template + CImg& draw_point(const int x0, const int y0, + const tc *const color, const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + //! Draw a 2D colored point (pixel). + template + CImg& draw_point(const int x0, const int y0, + const CImg& color, const float opacity=1) { + return draw_point(x0,y0,color.data,opacity); + } + + //! Draw a 3D colored point (voxel). + template + CImg& draw_point(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_point() : Specified color is (null)", + pixel_type()); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } + } + return *this; + } + + //! Draw a 3D colored point (voxel). + template + CImg& draw_point(const int x0, const int y0, const int z0, + const CImg& color, const float opacity=1) { + return draw_point(x0,y0,z0,color.data,opacity); + } + + // Draw a cloud of colored point (internal). + template + CImg& _draw_point(const t& points, const unsigned int W, const unsigned int H, + const tc *const color, const float opacity) { + if (is_empty() || !points || !W) return *this; + switch (H) { + case 0 : case 1 : + throw CImgArgumentException("CImg<%s>::draw_point() : Given list of points is not valid.", + pixel_type()); + case 2 : { + for (unsigned int i = 0; i img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + CImgList points; + points.insert(CImg::vector(0,0)). + .insert(CImg::vector(70,10)). + .insert(CImg::vector(80,60)). + .insert(CImg::vector(10,90)); + img.draw_point(points,color); + \endcode + **/ + template + CImg& draw_point(const CImgList& points, + const tc *const color, const float opacity=1) { + unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); + return _draw_point(points,points.size,H,color,opacity); + } + + //! Draw a cloud of colored points. + template + CImg& draw_point(const CImgList& points, + const CImg& color, const float opacity=1) { + return draw_point(points,color.data,opacity); + } + + //! Draw a cloud of colored points. + /** + \note + - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image + (sequence of vectors aligned along the x-axis). + **/ + template + CImg& draw_point(const CImg& points, + const tc *const color, const float opacity=1) { + return _draw_point(points,points.width,points.height,color,opacity); + } + + //! Draw a cloud of colored points. + template + CImg& draw_point(const CImg& points, + const CImg& color, const float opacity=1) { + return draw_point(points,color.data,opacity); + } + + //! Draw a 2D colored line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity (optional). + \param pattern An integer whose bits describe the line pattern (optional). + \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional). + \note + - Clipping is supported. + - Line routine uses Bresenham's algorithm. + - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,color); + \endcode + **/ + template + CImg& draw_line(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)", + pixel_type()); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=dimx()) return *this; + if (xleft<0) { yleft-=xleft*(yright - yleft)/(xright - xleft); xleft = 0; } + if (xright>=dimx()) { yright-=(xright - dimx())*(yright - yleft)/(xright - xleft); xright = dimx()-1; } + if (ydown<0 || yup>=dimy()) return *this; + if (yup<0) { xup-=yup*(xdown - xup)/(ydown - yup); yup = 0; } + if (ydown>=dimy()) { xdown-=(ydown - dimy())*(xdown - xup)/(ydown - yup); ydown = dimy()-1; } + T *ptrd0 = ptr(nx0,ny0); + int dx = xright - xleft, dy = ydown - yup; + const bool steep = dy>dx; + if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); + const int + offx = (nx0=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; }} + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + T *ptrd = ptrd0; const tc* col = color; + cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + T *ptrd = ptrd0; const tc* col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a 2D colored line. + template + CImg& draw_line(const int x0, const int y0, + const int x1, const int y1, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_line(x0,y0,x1,y1,color.data,opacity,pattern,init_hatch); + } + + //! Draw a 2D colored line, with z-buffering. + template + CImg& draw_line(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (!is_empty() && z0>0 && z1>0) { + if (!color) + throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null).", + pixel_type()); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=dimx()) return *this; + if (xleft<0) { + const int D = xright - xleft; + yleft-=xleft*(yright - yleft)/D; + zleft-=xleft*(zright - zleft)/D; + xleft = 0; + } + if (xright>=dimx()) { + const int d = xright - dimx(), D = xright - xleft; + yright-=d*(yright - yleft)/D; + zright-=d*(zright - zleft)/D; + xright = dimx()-1; + } + if (ydown<0 || yup>=dimy()) return *this; + if (yup<0) { + const int D = ydown - yup; + xup-=yup*(xdown - xup)/D; + zup-=yup*(zdown - zup)/D; + yup = 0; + } + if (ydown>=dimy()) { + const int d = ydown - dimy(), D = ydown - yup; + xdown-=d*(xdown - xup)/D; + zdown-=d*(zdown - zup)/D; + ydown = dimy()-1; + } + T *ptrd0 = ptr(nx0,ny0); + float *ptrz = zbuffer + nx0 + ny0*width; + int dx = xright - xleft, dy = ydown - yup; + const bool steep = dy>dx; + if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); + const int + offx = (nx00?dx:1; + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz && pattern&hatch) { + *ptrz = z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } + } + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz && pattern&hatch) { + *ptrz = z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + T *ptrd = ptrd0; const tc *col = color; + cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; } + } + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } + } + } + return *this; + } + + //! Draw a 2D colored line, with z-buffering. + template + CImg& draw_line(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color.data,opacity,pattern,init_hatch); + } + + //! Draw a 3D colored line. + template + CImg& draw_line(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)", + pixel_type()); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1; + if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (nx1<0 || nx0>=dimx()) return *this; + if (nx0<0) { const int D = 1 + nx1 - nx0; ny0-=nx0*(1 + ny1 - ny0)/D; nz0-=nx0*(1 + nz1 - nz0)/D; nx0 = 0; } + if (nx1>=dimx()) { const int d = nx1-dimx(), D = 1 + nx1 - nx0; ny1+=d*(1 + ny0 - ny1)/D; nz1+=d*(1 + nz0 - nz1)/D; nx1 = dimx()-1; } + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (ny1<0 || ny0>=dimy()) return *this; + if (ny0<0) { const int D = 1 + ny1 - ny0; nx0-=ny0*(1 + nx1 - nx0)/D; nz0-=ny0*(1 + nz1 - nz0)/D; ny0 = 0; } + if (ny1>=dimy()) { const int d = ny1-dimy(), D = 1 + ny1 - ny0; nx1+=d*(1 + nx0 - nx1)/D; nz1+=d*(1 + nz0 - nz1)/D; ny1 = dimy()-1; } + if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (nz1<0 || nz0>=dimz()) return *this; + if (nz0<0) { const int D = 1 + nz1 - nz0; nx0-=nz0*(1 + nx1 - nx0)/D; ny0-=nz0*(1 + ny1 - ny0)/D; nz0 = 0; } + if (nz1>=dimz()) { const int d = nz1-dimz(), D = 1 + nz1 - nz0; nx1+=d*(1 + nx0 - nx1)/D; ny1+=d*(1 + ny0 - ny1)/D; nz1 = dimz()-1; } + const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0), whz = width*height*depth; + const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax; + float x = (float)nx0, y = (float)ny0, z = (float)nz0; + if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) { + if (!(~pattern) || (~pattern && pattern&hatch)) { + T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z); + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } + } + x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + for (unsigned int t = 0; t<=dmax; ++t) { + if (!(~pattern) || (~pattern && pattern&hatch)) { + T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z); + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } + } + x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); } + } + } + return *this; + } + + //! Draw a 3D colored line. + template + CImg& draw_line(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_line(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern,init_hatch); + } + + //! Draw a 2D textured line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity (optional). + \param pattern An integer whose bits describe the line pattern (optional). + \param init_hatch Flag telling if the hash variable must be reinitialized (optional). + \note + - Clipping is supported but not for texture coordinates. + - Line routine uses the well known Bresenham's algorithm. + \par Example: + \code + CImg img(100,100,1,3,0), texture("texture256x256.ppm"); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,texture,0,0,255,255); + \endcode + **/ + template + CImg& draw_line(const int x0, const int y0, + const int x1, const int y1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty()) return *this; + if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=dimx()) return *this; + if (xleft<0) { + const int D = xright - xleft; + yleft-=xleft*(yright - yleft)/D; + txleft-=xleft*(txright - txleft)/D; + tyleft-=xleft*(tyright - tyleft)/D; + xleft = 0; + } + if (xright>=dimx()) { + const int d = xright - dimx(), D = xright - xleft; + yright-=d*(yright - yleft)/D; + txright-=d*(txright - txleft)/D; + tyright-=d*(tyright - tyleft)/D; + xright = dimx()-1; + } + if (ydown<0 || yup>=dimy()) return *this; + if (yup<0) { + const int D = ydown - yup; + xup-=yup*(xdown - xup)/D; + txup-=yup*(txdown - txup)/D; + tyup-=yup*(tydown - tyup)/D; + yup = 0; + } + if (ydown>=dimy()) { + const int d = ydown - dimy(), D = ydown - yup; + xdown-=d*(xdown - xup)/D; + txdown-=d*(txdown - txup)/D; + tydown-=d*(tydown - tyup)/D; + ydown = dimy()-1; + } + T *ptrd0 = ptr(nx0,ny0); + int dx = xright - xleft, dy = ydown - yup; + const bool steep = dy>dx; + if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); + const int + offx = (nx00?dx:1; + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + T *ptrd = ptrd0; + const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; + cimg_forV(*this,k) { *ptrd = (T)texture(tx,ty,0,k); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + T *ptrd = ptrd0; + const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; + cimg_forV(*this,k) { *ptrd = (T)texture(tx,ty,0,k); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + T *ptrd = ptrd0; + if (pattern&hatch) { + const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; + cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture(tx,ty,0,k) + *ptrd*copacity); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + T *ptrd = ptrd0; + const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; + cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture(tx,ty,0,k) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a 2D textured line, with perspective correction. + template + CImg& draw_line(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() && z0<=0 && z1<=0) return *this; + if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=dimx()) return *this; + if (xleft<0) { + const int D = xright - xleft; + yleft-=xleft*(yright - yleft)/D; + zleft-=xleft*(zright - zleft)/D; + txleft-=xleft*(txright - txleft)/D; + tyleft-=xleft*(tyright - tyleft)/D; + xleft = 0; + } + if (xright>=dimx()) { + const int d = xright - dimx(), D = xright - xleft; + yright-=d*(yright - yleft)/D; + zright-=d*(zright - zleft)/D; + txright-=d*(txright - txleft)/D; + tyright-=d*(tyright - tyleft)/D; + xright = dimx()-1; + } + if (ydown<0 || yup>=dimy()) return *this; + if (yup<0) { + const int D = ydown - yup; + xup-=yup*(xdown - xup)/D; + zup-=yup*(zdown - zup)/D; + txup-=yup*(txdown - txup)/D; + tyup-=yup*(tydown - tyup)/D; + yup = 0; + } + if (ydown>=dimy()) { + const int d = ydown - dimy(), D = ydown - yup; + xdown-=d*(xdown - xup)/D; + zdown-=d*(zdown - zup)/D; + txdown-=d*(txdown - txup)/D; + tydown-=d*(tydown - tyup)/D; + ydown = dimy()-1; + } + T *ptrd0 = ptr(nx0,ny0); + int dx = xright - xleft, dy = ydown - yup; + const bool steep = dy>dx; + if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); + const int + offx = (nx00?dx:1; + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; + cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } + ptrd0+=offx; + if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } + } + } + return *this; + } + + //! Draw a 2D textured line, with z-buffering and perspective correction. + template + CImg& draw_line(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (!is_empty() && z0>0 && z1>0) { + if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + const bool xdir = x0=dimx()) return *this; + if (xleft<0) { + const int D = xright - xleft; + yleft-=xleft*(yright - yleft)/D; + zleft-=xleft*(zright - zleft)/D; + txleft-=xleft*(txright - txleft)/D; + tyleft-=xleft*(tyright - tyleft)/D; + xleft = 0; + } + if (xright>=dimx()) { + const int d = xright - dimx(), D = xright - xleft; + yright-=d*(yright - yleft)/D; + zright-=d*(zright - zleft)/D; + txright-=d*(txright - txleft)/D; + tyright-=d*(tyright - tyleft)/D; + xright = dimx()-1; + } + if (ydown<0 || yup>=dimy()) return *this; + if (yup<0) { + const int D = ydown - yup; + xup-=yup*(xdown - xup)/D; + zup-=yup*(zdown - zup)/D; + txup-=yup*(txdown - txup)/D; + tyup-=yup*(tydown - tyup)/D; + yup = 0; + } + if (ydown>=dimy()) { + const int d = ydown - dimy(), D = ydown - yup; + xdown-=d*(xdown - xup)/D; + zdown-=d*(zdown - zup)/D; + txdown-=d*(txdown - txup)/D; + tydown-=d*(tydown - tyup)/D; + ydown = dimy()-1; + } + T *ptrd0 = ptr(nx0,ny0); + float *ptrz = zbuffer + nx0 + ny0*width; + int dx = xright - xleft, dy = ydown - yup; + const bool steep = dy>dx; + if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); + const int + offx = (nx00?dx:1; + if (opacity>=1) { + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } + } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } + } + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } + } else { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) { + if (pattern&hatch) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } + } + } + hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; } + } else for (int error = dx>>1, x = 0; x<=dx; ++x) { + const float z = Z0 + x*dz/ndx; + if (z>*ptrz) { + *ptrz = z; + const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; + T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,k) + *ptrd*copacity); ptrd+=wh; } + } + ptrd0+=offx; ptrz+=offx; + if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; } + } + } + } + return *this; + } + + // Inner routine for drawing set of consecutive lines with generic type for coordinates. + template + CImg& _draw_line(const t& points, const unsigned int W, const unsigned int H, + const tc *const color, const float opacity, + const unsigned int pattern, const bool init_hatch) { + if (is_empty() || !points || W<2) return *this; + bool ninit_hatch = init_hatch; + switch (H) { + case 0 : case 1 : + throw CImgArgumentException("CImg<%s>::draw_line() : Given list of points is not valid.", + pixel_type()); + case 2 : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + CImgList points; + points.insert(CImg::vector(0,0)). + .insert(CImg::vector(70,10)). + .insert(CImg::vector(80,60)). + .insert(CImg::vector(10,90)); + img.draw_line(points,color); + \endcode + **/ + template + CImg& draw_line(const CImgList& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); + return _draw_line(points,points.size,H,color,opacity,pattern,init_hatch); + } + + //! Draw a set of consecutive colored lines in the instance image. + template + CImg& draw_line(const CImgList& points, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_line(points,color.data,opacity,pattern,init_hatch); + } + + //! Draw a set of consecutive colored lines in the instance image. + /** + \note + - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image + (sequence of vectors aligned along the x-axis). + **/ + template + CImg& draw_line(const CImg& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return _draw_line(points,points.width,points.height,color,opacity,pattern,init_hatch); + } + + //! Draw a set of consecutive colored lines in the instance image. + template + CImg& draw_line(const CImg& points, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_line(points,color.data,opacity,pattern,init_hatch); + } + + // Inner routine for a drawing filled polygon with generic type for coordinates. + template + CImg& _draw_polygon(const t& points, const unsigned int N, + const tc *const color, const float opacity) { + if (is_empty() || !points || N<3) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_polygon() : Specified color is (null).", + pixel_type()); + _draw_scanline(color,opacity); + int xmin = (int)(~0U>>1), xmax = 0, ymin = (int)(~0U>>1), ymax = 0; + { for (unsigned int p = 0; pxmax) xmax = x; + if (yymax) ymax = y; + }} + if (xmax<0 || xmin>=dimx() || ymax<0 || ymin>=dimy()) return *this; + const unsigned int + nymin = ymin<0?0:(unsigned int)ymin, + nymax = ymax>=dimy()?height-1:(unsigned int)ymax, + dy = 1 + nymax - nymin; + CImg X(1+2*N,dy,1,1,0), tmp; + int cx = (int)points(0,0), cy = (int)points(0,1); + for (unsigned int cp = 0, p = 0; pay && cy>ny))?1:0; + for (int x = cx, y = y0, _sx = 1, _sy = 1, + _dx = nx>cx?nx-cx:((_sx=-1),cx-nx), + _dy = y1>y0?y1-y0:((_sy=-1),y0-y1), + _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy), + _err = _dx>>1, + _rx = _dy?(nx-cx)/_dy:0; + _counter>=countermin; + --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0)) + if (y>=0 && y<(int)dy) X(++X(0,y),y) = x; + cp = np; cx = nx; cy = ny; + } else { + const int pp = (cp?cp-1:N-1), py = (int)points(pp,1); + if ((cy>py && ay>cy) || (cy + CImg& draw_polygon(const CImgList& points, + const tc *const color, const float opacity=1) { + if (!points.is_sameY(2)) + throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", + pixel_type()); + return _draw_polygon(points,points.size,color,opacity); + } + + //! Draw a filled polygon in the instance image. + template + CImg& draw_polygon(const CImgList& points, + const CImg& color, const float opacity=1) { + return draw_polygon(points,color.data,opacity); + } + + //! Draw a filled polygon in the instance image. + template + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity=1) { + if (points.height<2) + throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", + pixel_type()); + return _draw_polygon(points,points.width,color,opacity); + } + + //! Draw a filled polygon in the instance image. + template + CImg& draw_polygon(const CImg& points, + const CImg& color, const float opacity=1) { + return draw_polygon(points,color.data,opacity); + } + + // Inner routine for drawing an outlined polygon with generic point coordinates. + template + CImg& _draw_polygon(const t& points, const unsigned int W, const unsigned int H, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty() || !points || W<3) return *this; + bool ninit_hatch = true; + switch (H) { + case 0 : case 1 : + throw CImgArgumentException("CImg<%s>::draw_polygon() : Given list of points is not valid.", + pixel_type()); + case 2 : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i + CImg& draw_polygon(const CImgList& points, + const tc *const color, const float opacity, + const unsigned int pattern) { + unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); + return _draw_polygon(points,points.size,H,color,opacity,pattern); + } + + //! Draw a polygon outline. + template + CImg& draw_polygon(const CImgList& points, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_polygon(points,color.data,opacity,pattern); + } + + //! Draw a polygon outline. + template + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity, + const unsigned int pattern) { + return _draw_polygon(points,points.width,points.height,color,opacity,pattern); + } + + //! Draw a polygon outline. + template + CImg& draw_polygon(const CImg& points, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_polygon(points,color.data,opacity,pattern); + } + + //! Draw a cubic spline curve in the instance image. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. + \param precision Curve drawing precision (optional). + \param opacity Drawing opacity (optional). + \param pattern An integer whose bits describe the line pattern (optional). + \param init_hatch If \c true, init hatch motif. + \note + - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points + and corresponding velocity vectors. + - The spline is drawn as a serie of connected segments. The \p precision parameter sets the + average number of pixels in each drawn segment. + - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) } + where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two + \e control points. + The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as + \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,255,255 }; + img.draw_spline(30,30,0,100,90,40,0,-100,color); + \endcode + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const tc *const color, const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)", + pixel_type()); + bool ninit_hatch = init_hatch; + const float + dx = (float)(x1 - x0), + dy = (float)(y1 - y0), + dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), + ax = -2*dx + u0 + u1, + bx = 3*dx - 2*u0 - u1, + ay = -2*dy + v0 + v1, + by = 3*dy - 2*v0 - v1, + xprecision = dmax>0?precision/dmax:1.0f, + tmax = 1 + (dmax>0?xprecision:0.0f); + int ox = x0, oy = y0; + for (float t = 0; t + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& color, const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,color.data,opacity,precision,pattern,init_hatch); + } + + //! Draw a cubic spline curve in the instance image (for volumetric images). + /** + \note + - Similar to CImg::draw_spline() for a 3D spline in a volumetric image. + **/ + template + CImg& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, + const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, + const tc *const color, const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)", + pixel_type()); + bool ninit_hatch = init_hatch; + const float + dx = (float)(x1 - x0), + dy = (float)(y1 - y0), + dz = (float)(z1 - z0), + dmax = cimg::max(cimg::abs(dx),cimg::abs(dy),cimg::abs(dz)), + ax = -2*dx + u0 + u1, + bx = 3*dx - 2*u0 - u1, + ay = -2*dy + v0 + v1, + by = 3*dy - 2*v0 - v1, + az = -2*dz + w0 + w1, + bz = 3*dz - 2*w0 - w1, + xprecision = dmax>0?precision/dmax:1.0f, + tmax = 1 + (dmax>0?xprecision:0.0f); + int ox = x0, oy = y0, oz = z0; + for (float t = 0; t + CImg& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, + const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, + const CImg& color, const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + return draw_spline(x0,y0,z0,u0,v0,w0,x1,y1,z1,u1,v1,w1,color.data,opacity,precision,pattern,init_hatch); + } + + //! Draw a cubic spline curve in the instance image. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param texture Texture image defining line pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param precision Curve drawing precision (optional). + \param opacity Drawing opacity (optional). + \param pattern An integer whose bits describe the line pattern (optional). + \param init_hatch if \c true, reinit hatch motif. + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& texture, + const int tx0, const int ty0, const int tx1, const int ty1, + const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!texture || texture.dim::draw_line() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); + bool ninit_hatch = true; + const float + dx = (float)(x1 - x0), + dy = (float)(y1 - y0), + dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), + ax = -2*dx + u0 + u1, + bx = 3*dx - 2*u0 - u1, + ay = -2*dy + v0 + v1, + by = 3*dy - 2*v0 - v1, + xprecision = dmax>0?precision/dmax:1.0f, + tmax = 1 + (dmax>0?xprecision:0.0f); + int ox = x0, oy = y0, otx = tx0, oty = ty0; + for (float t1 = 0; t1 + CImg& _draw_spline(const tp& points, const tt& tangents, const unsigned int W, const unsigned int H, + const tc *const color, const float opacity, + const bool close_set, const float precision, + const unsigned int pattern, const bool init_hatch) { + if (is_empty() || !points || !tangents || W<2) return *this; + bool ninit_hatch = init_hatch; + switch (H) { + case 0 : case 1 : + throw CImgArgumentException("CImg<%s>::draw_spline() : Given list of points or tangents is not valid.", + pixel_type()); + case 2 : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); + int ox = x0, oy = y0; + float ou = u0, ov = v0; + for (unsigned int i = 1; i + CImg& _draw_spline(const tp& points, const unsigned int W, const unsigned int H, + const tc *const color, const float opacity, + const bool close_set, const float precision, + const unsigned int pattern, const bool init_hatch) { + if (is_empty() || !points || W<2) return *this; + CImg tangents; + switch (H) { + case 0 : case 1 : + throw CImgArgumentException("CImg<%s>::draw_spline() : Given list of points or tangents is not valid.", + pixel_type()); + case 2 : { + tangents.assign(W,H); + for (unsigned int p = 0; p + CImg& draw_spline(const CImgList& points, const CImgList& tangents, + const tc *const color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size()),(unsigned int)(tangents[p].size())); + return _draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch,points.size,H); + } + + //! Draw a set of consecutive colored splines in the instance image. + template + CImg& draw_spline(const CImgList& points, const CImgList& tangents, + const CImg& color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_spline(points,tangents,color.data,opacity,close_set,precision,pattern,init_hatch); + } + + //! Draw a set of consecutive colored splines in the instance image. + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const tc *const color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return _draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch,points.width,points.height); + } + + //! Draw a set of consecutive colored splines in the instance image. + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const CImg& color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_spline(points,tangents,color.data,opacity,close_set,precision,pattern,init_hatch); + } + + //! Draw a set of consecutive colored splines in the instance image. + template + CImg& draw_spline(const CImgList& points, + const tc *const color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + unsigned int H = ~0U; + cimglist_for(points,p) { const unsigned int s = points[p].size(); if (s + CImg& draw_spline(const CImgList& points, + CImg& color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_spline(points,color.data,opacity,close_set,precision,pattern,init_hatch); + } + + //! Draw a set of consecutive colored lines in the instance image. + template + CImg& draw_spline(const CImg& points, + const tc *const color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return _draw_spline(points,color,opacity,close_set,precision,pattern,init_hatch,points.width,points.height); + } + + //! Draw a set of consecutive colored lines in the instance image. + template + CImg& draw_spline(const CImg& points, + const CImg& color, const float opacity=1, + const bool close_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + return draw_spline(points,color.data,opacity,close_set,precision,pattern,init_hatch); + } + + //! Draw a colored arrow in the instance image. + /** + \param x0 X-coordinate of the starting arrow point (tail). + \param y0 Y-coordinate of the starting arrow point (tail). + \param x1 X-coordinate of the ending arrow point (head). + \param y1 Y-coordinate of the ending arrow point (head). + \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. + \param angle Aperture angle of the arrow head (optional). + \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional). + \param opacity Drawing opacity (optional). + \param pattern An integer whose bits describe the line pattern (optional). + \note + - Clipping is supported. + **/ + template + CImg& draw_arrow(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const float angle=30, const float length=-10, + const unsigned int pattern=~0U) { + if (is_empty()) return *this; + const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, + deg = (float)(angle*cimg::valuePI/180), ang = (sq>0)?(float)cimg_std::atan2(v,u):0.0f, + l = (length>=0)?length:-length*(float)cimg_std::sqrt(sq)/100; + if (sq>0) { + const float + cl = (float)cimg_std::cos(ang - deg), sl = (float)cimg_std::sin(ang - deg), + cr = (float)cimg_std::cos(ang + deg), sr = (float)cimg_std::sin(ang + deg); + const int + xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), + xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), + xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2; + draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); + } else draw_point(x0,y0,color,opacity); + return *this; + } + + //! Draw a colored arrow in the instance image. + template + CImg& draw_arrow(const int x0, const int y0, + const int x1, const int y1, + const CImg& color, const float opacity=1, + const float angle=30, const float length=-10, + const unsigned int pattern=~0U) { + return draw_arrow(x0,y0,x1,y1,color.data,opacity,angle,length,pattern); + } + + //! Draw an image. + /** + \param sprite Sprite image. + \param x0 X-coordinate of the sprite position. + \param y0 Y-coordinate of the sprite position. + \param z0 Z-coordinate of the sprite position. + \param v0 V-coordinate of the sprite position. + \param opacity Drawing opacity (optional). + \note + - Clipping is supported. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int v0, + const CImg& sprite, const float opacity=1) { + if (is_empty()) return *this; + if (!sprite) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,opacity); + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); + const int + lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); + const t + *ptrs = sprite.data - + (bx?x0:0) - + (by?y0*sprite.dimx():0) - + (bz?z0*sprite.dimx()*sprite.dimy():0) - + (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); + const unsigned int + offX = width - lX, soffX = sprite.width - lX, + offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), + offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (lX>0 && lY>0 && lZ>0 && lV>0) { + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + for (int v = 0; v=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int v0, + const CImg& sprite, const float opacity=1) { + if (is_empty()) return *this; + if (!sprite) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,opacity); + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); + const int + lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); + const T + *ptrs = sprite.data - + (bx?x0:0) - + (by?y0*sprite.dimx():0) - + (bz?z0*sprite.dimx()*sprite.dimy():0) - + (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); + const unsigned int + offX = width - lX, soffX = sprite.width - lX, + offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), + offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ), + slX = lX*sizeof(T); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + if (lX>0 && lY>0 && lZ>0 && lV>0) { + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + for (int v = 0; v=1) for (int y = 0; y + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,z0,0,sprite,opacity); + } + + //! Draw an image. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,0,sprite,opacity); + } + + //! Draw an image. + template + CImg& draw_image(const int x0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,0,sprite,opacity); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const float opacity=1) { + return draw_image(0,sprite,opacity); + } + + //! Draw a sprite image in the instance image (masked version). + /** + \param sprite Sprite image. + \param mask Mask image. + \param x0 X-coordinate of the sprite position in the instance image. + \param y0 Y-coordinate of the sprite position in the instance image. + \param z0 Z-coordinate of the sprite position in the instance image. + \param v0 V-coordinate of the sprite position in the instance image. + \param mask_valmax Maximum pixel value of the mask image \c mask (optional). + \param opacity Drawing opacity. + \note + - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. + - Clipping is supported. + - Dimensions along x,y and z of \p sprite and \p mask must be the same. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int v0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_valmax=1) { + if (is_empty()) return *this; + if (!sprite) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); + if (!mask) + throw CImgArgumentException("CImg<%s>::draw_image() : Specified mask image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,v0,+sprite,mask,opacity,mask_valmax); + if (is_overlapped(mask)) return draw_image(x0,y0,z0,v0,sprite,+mask,opacity,mask_valmax); + if (mask.width!=sprite.width || mask.height!=sprite.height || mask.depth!=sprite.depth) + throw CImgArgumentException("CImg<%s>::draw_image() : Mask dimension is (%u,%u,%u,%u), while sprite is (%u,%u,%u,%u)", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,sprite.width,sprite.height,sprite.depth,sprite.dim); + const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bv = (v0<0); + const int + lX = sprite.dimx() - (x0 + sprite.dimx()>dimx()?x0 + sprite.dimx() - dimx():0) + (bx?x0:0), + lY = sprite.dimy() - (y0 + sprite.dimy()>dimy()?y0 + sprite.dimy() - dimy():0) + (by?y0:0), + lZ = sprite.dimz() - (z0 + sprite.dimz()>dimz()?z0 + sprite.dimz() - dimz():0) + (bz?z0:0), + lV = sprite.dimv() - (v0 + sprite.dimv()>dimv()?v0 + sprite.dimv() - dimv():0) + (bv?v0:0); + const int + coff = -(bx?x0:0)-(by?y0*mask.dimx():0)-(bz?z0*mask.dimx()*mask.dimy():0)-(bv?v0*mask.dimx()*mask.dimy()*mask.dimz():0), + ssize = mask.dimx()*mask.dimy()*mask.dimz(); + const ti *ptrs = sprite.data + coff; + const tm *ptrm = mask.data + coff; + const unsigned int + offX = width - lX, soffX = sprite.width - lX, + offY = width*(height - lY), soffY = sprite.width*(sprite.height - lY), + offZ = width*height*(depth - lZ), soffZ = sprite.width*sprite.height*(sprite.depth - lZ); + if (lX>0 && lY>0 && lZ>0 && lV>0) { + T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); + for (int v = 0; v + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_valmax=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax); + } + + //! Draw an image. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_valmax=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax); + } + + //! Draw an image. + template + CImg& draw_image(const int x0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_valmax=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_valmax); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_valmax=1) { + return draw_image(0,sprite,mask,opacity,mask_valmax); + } + + //! Draw a 4D filled rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0,\c v0)-(\c x1,\c y1,\c z1,\c v1). + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param z0 Z-coordinate of the upper-left rectangle corner. + \param v0 V-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param z1 Z-coordinate of the lower-right rectangle corner. + \param v1 V-coordinate of the lower-right rectangle corner. + \param val Scalar value used to fill the rectangle area. + \param opacity Drawing opacity (optional). + \note + - Clipping is supported. + **/ + CImg& draw_rectangle(const int x0, const int y0, const int z0, const int v0, + const int x1, const int y1, const int z1, const int v1, + const T val, const float opacity=1) { + if (is_empty()) return *this; + const bool bx = (x0=dimx()?dimx() - 1 - nx1:0) + (nx0<0?nx0:0), + lY = (1 + ny1 - ny0) + (ny1>=dimy()?dimy() - 1 - ny1:0) + (ny0<0?ny0:0), + lZ = (1 + nz1 - nz0) + (nz1>=dimz()?dimz() - 1 - nz1:0) + (nz0<0?nz0:0), + lV = (1 + nv1 - nv0) + (nv1>=dimv()?dimv() - 1 - nv1:0) + (nv0<0?nv0:0); + const unsigned int offX = width - lX, offY = width*(height - lY), offZ = width*height*(depth - lZ); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + T *ptrd = ptr(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nv0<0?0:nv0); + if (lX>0 && lY>0 && lZ>0 && lV>0) + for (int v = 0; v=1) { + if (sizeof(T)!=1) { for (int x = 0; x + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1) { + if (!color) + throw CImgArgumentException("CImg<%s>::draw_rectangle : specified color is (null)", + pixel_type()); + cimg_forV(*this,k) draw_rectangle(x0,y0,z0,k,x1,y1,z1,k,color[k],opacity); + return *this; + } + + //! Draw a 3D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0)-(\c x1,\c y1,\c z1). + template + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const CImg& color, const float opacity=1) { + return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity); + } + + //! Draw a 3D outlined colored rectangle in the instance image. + template + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity, + const unsigned int pattern) { + return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). + draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). + draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). + draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). + draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). + draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). + draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). + draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). + draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). + draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). + draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); + } + + //! Draw a 3D outlined colored rectangle in the instance image. + template + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern); + } + + //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity (optional). + \note + - Clipping is supported. + **/ + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1) { + return draw_rectangle(x0,y0,0,x1,y1,depth-1,color,opacity); + } + + //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const CImg& color, const float opacity=1) { + return draw_rectangle(x0,y0,x1,y1,color.data,opacity); + } + + //! Draw a 2D outlined colored rectangle. + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); + if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); + const bool bx = (x0 + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_rectangle(x0,y0,x1,y1,color.data,opacity,pattern); + } + + // Inner macro for drawing triangles. +#define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \ + for (int y = y0<0?0:y0, \ + xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ + xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ + _sxn=1, \ + _sxr=1, \ + _sxl=1, \ + _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ + _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ + _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ + _dyn = y2-y1, \ + _dyr = y2-y0, \ + _dyl = y1-y0, \ + _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ + _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ + _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ + cimg::min((int)(img).height-y-1,y2-y)), \ + _errn = _dyn/2, \ + _errr = _dyr/2, \ + _errl = _dyl/2, \ + _rxn = _dyn?(x2-x1)/_dyn:0, \ + _rxr = _dyr?(x2-x0)/_dyr:0, \ + _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \ + _counter>=0; --_counter, ++y, \ + xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ + xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) + +#define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \ + for (int y = y0<0?0:y0, \ + xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ + cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ + xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ + cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ + _sxn=1, _scn=1, \ + _sxr=1, _scr=1, \ + _sxl=1, _scl=1, \ + _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ + _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ + _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ + _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ + _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ + _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ + _dyn = y2-y1, \ + _dyr = y2-y0, \ + _dyl = y1-y0, \ + _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ + _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ + _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ + _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ + _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ + _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ + cimg::min((int)(img).height-y-1,y2-y)), \ + _errn = _dyn/2, _errcn = _errn, \ + _errr = _dyr/2, _errcr = _errr, \ + _errl = _dyl/2, _errcl = _errl, \ + _rxn = _dyn?(x2-x1)/_dyn:0, \ + _rcn = _dyn?(c2-c1)/_dyn:0, \ + _rxr = _dyr?(x2-x0)/_dyr:0, \ + _rcr = _dyr?(c2-c0)/_dyr:0, \ + _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ + _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ + (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \ + _counter>=0; --_counter, ++y, \ + xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ + cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ + xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ + _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ + (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ + _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) + +#define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \ + for (int y = y0<0?0:y0, \ + xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ + txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ + tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ + xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ + txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ + tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ + _sxn=1, _stxn=1, _styn=1, \ + _sxr=1, _stxr=1, _styr=1, \ + _sxl=1, _stxl=1, _styl=1, \ + _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ + _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ + _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ + _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ + _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ + _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ + _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ + _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ + _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ + _dyn = y2-y1, \ + _dyr = y2-y0, \ + _dyl = y1-y0, \ + _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ + _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ + _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ + _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ + _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ + _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ + _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ + _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ + _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ + cimg::min((int)(img).height-y-1,y2-y)), \ + _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \ + _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \ + _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \ + _rxn = _dyn?(x2-x1)/_dyn:0, \ + _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ + _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ + _rxr = _dyr?(x2-x0)/_dyr:0, \ + _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ + _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ + _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ + _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ + (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ + _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ + (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ + _counter>=0; --_counter, ++y, \ + xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ + txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ + tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ + xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ + tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ + _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ + (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ + _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\ + _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) + +#define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \ + for (int y = y0<0?0:y0, \ + xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ + cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ + txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ + tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ + xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ + cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ + txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ + tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ + _sxn=1, _scn=1, _stxn=1, _styn=1, \ + _sxr=1, _scr=1, _stxr=1, _styr=1, \ + _sxl=1, _scl=1, _stxl=1, _styl=1, \ + _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ + _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ + _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ + _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ + _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ + _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ + _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ + _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ + _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ + _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ + _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ + _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ + _dyn = y2-y1, \ + _dyr = y2-y0, \ + _dyl = y1-y0, \ + _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ + _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ + _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ + _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ + _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ + _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ + _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ + _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ + _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ + _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ + _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ + _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ + cimg::min((int)(img).height-y-1,y2-y)), \ + _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \ + _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \ + _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \ + _rxn = _dyn?(x2-x1)/_dyn:0, \ + _rcn = _dyn?(c2-c1)/_dyn:0, \ + _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ + _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ + _rxr = _dyr?(x2-x0)/_dyr:0, \ + _rcr = _dyr?(c2-c0)/_dyr:0, \ + _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ + _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ + _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ + _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ + (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \ + _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ + (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ + _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ + (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ + _counter>=0; --_counter, ++y, \ + xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ + cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ + txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ + tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ + xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ + txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ + tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ + _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ + (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ + _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ + _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ + _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) + +#define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \ + for (int y = y0<0?0:y0, \ + xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ + txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ + tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ + lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \ + lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \ + xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ + txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ + tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ + lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \ + lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \ + _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \ + _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \ + _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \ + _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \ + _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \ + _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \ + _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ + _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ + _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ + _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ + _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ + _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ + _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \ + _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \ + _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \ + _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \ + _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \ + _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \ + _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ + _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ + _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ + _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ + _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ + _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ + _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ + _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ + _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ + _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \ + _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \ + _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \ + _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \ + _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \ + _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \ + cimg::min((int)(img).height-y-1,y2-y)), \ + _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \ + _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \ + _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \ + _rxn = _dyn?(x2-x1)/_dyn:0, \ + _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ + _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ + _rlxn = _dyn?(lx2-lx1)/_dyn:0, \ + _rlyn = _dyn?(ly2-ly1)/_dyn:0, \ + _rxr = _dyr?(x2-x0)/_dyr:0, \ + _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ + _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ + _rlxr = _dyr?(lx2-lx0)/_dyr:0, \ + _rlyr = _dyr?(ly2-ly0)/_dyr:0, \ + _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ + (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ + _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ + (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ + _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ + (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \ + _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \ + (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \ + _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \ + (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \ + _counter>=0; --_counter, ++y, \ + xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ + txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ + tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ + lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \ + lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \ + xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ + tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ + lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \ + lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \ + _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ + (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ + _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ + _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \ + _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \ + _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) + + // Draw a colored triangle (inner routine, uses bresenham's algorithm). + template + CImg& _draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const float brightness) { + _draw_scanline(color,opacity); + const float nbrightness = brightness<0?0:(brightness>2?2:brightness); + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); + if (ny0=0) { + if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0) + _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness); + else + _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness); + } + return *this; + } + + //! Draw a 2D filled colored triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", + pixel_type()); + _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); + return *this; + } + + //! Draw a 2D filled colored triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& color, const float opacity=1) { + return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity); + } + + //! Draw a 2D outlined colored triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", + pixel_type()); + draw_line(x0,y0,x1,y1,color,opacity,pattern,true). + draw_line(x1,y1,x2,y2,color,opacity,pattern,false). + draw_line(x2,y2,x0,y0,color,opacity,pattern,false); + return *this; + } + + //! Draw a 2D outlined colored triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity,pattern); + } + + //! Draw a 2D filled colored triangle, with z-buffering. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const tc *const color, const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", + pixel_type()); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const int whz = width*height*depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); + _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { + if (y==ny1) { zl = nz1; pzl = pzn; } + int xleft = xleft0, xright = xright0; + float zleft = zl, zright = zr; + if (xright=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } + ptrd-=offx; + } + zleft+=pentez; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whz; } + ptrd-=offx; + } + zleft+=pentez; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whz; } + ptrd-=offx; + } + zleft+=pentez; + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whz; } + ptrd-=offx; + } + zleft+=pentez; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; cimg_forV(*this,k) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whz; } + ptrd-=offx; + } + zleft+=pentez; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; + cimg_forV(*this,k) { + const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; + } + ptrd-=offx; + } + zleft+=pentez; + } + } + zr+=pzr; zl+=pzl; + } + return *this; + } + + //! Draw a 2D filled colored triangle, with z-buffering. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& color, const float opacity=1, + const float brightness=1) { + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opacity,brightness); + } + + //! Draw a 2D Gouraud-shaded colored triangle. + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param color = array of dimv() values of type \c T, defining the global drawing color. + \param brightness0 = brightness of the first corner (in [0,2]). + \param brightness1 = brightness of the second corner (in [0,2]). + \param brightness2 = brightness of the third corner (in [0,2]). + \param opacity = opacity of the drawing. + \note Clipping is supported. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", + pixel_type()); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), + nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), + nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc-(dx?dx*(dc/dx):0); + int errc = dx>>1; + if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx; + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = color; + cimg_forV(*this,k) { + *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + ptrd+=whz; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } else for (int x = xleft; x<=xright; ++x) { + const tc *col = color; + cimg_forV(*this,k) { + const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + } + return *this; + } + + //! Draw a 2D Gouraud-shaded colored triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& color, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,brightness0,brightness1,brightness2,opacity); + } + + //! Draw a 2D Gouraud-shaded colored triangle, with z-buffering. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const tc *const color, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", + pixel_type()); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), + nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), + nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); + float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + float + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + if (y==ny1) { zl = nz1; pzl = pzn; } + int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; + float zleft = zl, zright = zr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright-cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc-(dx?dx*(dc/dx):0); + const float pentez = (zright - zleft)/dx; + int errc = dx>>1; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + zleft-=xleft*(zright - zleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T *ptrd = ptr(xleft,y); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; + cimg_forV(*this,k) { + *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + ptrd+=whz; + } + ptrd-=offx; + } + zleft+=pentez; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tc *col = color; + cimg_forV(*this,k) { + const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; + } + ptrd-=offx; + } + zleft+=pentez; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + zr+=pzr; zl+=pzl; + } + return *this; + } + + //! Draw a Gouraud triangle with z-buffer consideration. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& color, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,brightness0,brightness1,brightness2,opacity); + } + + //! Draw a 2D textured triangle. + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param opacity = opacity of the drawing. + \param brightness = brightness of the drawing (in [0,2]). + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty()) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2); + if (ny0>=dimy() || ny2<0) return *this; + _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y, + nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrighttxleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errtx = dx>>1, errty = errtx; + if (xleft<0 && dx) { + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)*col; + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)(nbrightness**col); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } + } + } + return *this; + } + + //! Draw a 2D textured triangle, with perspective correction. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int xleft = xleft0, xright = xright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xright=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)*col; + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nbrightness**col); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a 2D textured triangle, with z-buffering and perspective correction. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float + nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), + nbrightness = brightness<0?0:(brightness>2?2:brightness); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int xleft = xleft0, xright = xright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xright=dimx()-1) xright = dimx()-1; + T *ptrd = ptr(xleft,y,0,0); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)*col; + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nbrightness**col); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } else { + if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + } + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded triangle. + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param color = array of dimv() values of type \c T, defining the global drawing color. + \param light = light image. + \param lx0 = X-coordinate of the first corner in the light image. + \param ly0 = Y-coordinate of the first corner in the light image. + \param lx1 = X-coordinate of the second corner in the light image. + \param ly1 = Y-coordinate of the second corner in the light image. + \param lx2 = X-coordinate of the third corner in the light image. + \param ly2 = Y-coordinate of the third corner in the light image. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).", + pixel_type()); + if (!light) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + const int whz = width*height*depth, offx = dim*whz-1; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2); + if (ny0>=dimy() || ny2<0) return *this; + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tl l = light(lxleft,lyleft); + const tc *col = color; + cimg_forV(*this,k) { + *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); + ptrd+=whz; + } + ptrd-=offx; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } else for (int x = xleft; x<=xright; ++x) { + const tl l = light(lxleft,lyleft); + const tc *col = color; + cimg_forV(*this,k) { + const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval)); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; + } + ptrd-=offx; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded triangle. + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + } + + //! Draw a 2D Pseudo-Phong-shaded triangle, with z-buffering. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const tc *const color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified color is (null).", + pixel_type()); + if (!light) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))); + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + if (y==ny1) { zl = nz1; pzl = pzn; } + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + float zleft = zl, zright = zr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + const float pentez = (zright - zleft)/dx; + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + zleft-=xleft*(zright - zleft)/dx; + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T *ptrd = ptr(xleft,y,0,0); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tl l = light(lxleft,lyleft); + const tc *col = color; + cimg_forV(*this,k) { + const tc cval = *(col++); + *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); + ptrd+=whz; + } + ptrd-=offx; + } + zleft+=pentez; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const tl l = light(lxleft,lyleft); + const tc *col = color; + cimg_forV(*this,k) { + const tc cval = *(col++); + const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; + } + ptrd-=offx; + } + zleft+=pentez; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } + zr+=pzr; zl+=pzl; + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded triangle, with z-buffering. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& color, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + } + + //! Draw a 2D Gouraud-shaded textured triangle. + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param brightness0 = brightness value of the first corner. + \param brightness1 = brightness value of the second corner. + \param brightness2 = brightness value of the third corner. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty()) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, + nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), + nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), + nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y, + nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + cleft = cleft0, cright = cright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrightcleft?cright - cleft:cleft - cright, + dtx = txright>txleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rc = dx?(cright - cleft)/dx:0, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + sc = cright>cleft?1:-1, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0), + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errc = dx>>1, errtx = errc, errty = errc; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else for (int x = xleft; x<=xright; ++x) { + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } + } + return *this; + } + + //! Draw a 2D Gouraud-shaded textured triangle, with perspective correction. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), + nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), + nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + cleft = cleft0, cright = cright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errc = dx>>1; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + zleft-=xleft*(zright - zleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } else for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a 2D Gouraud-shaded textured triangle, with z-buffering and perspective correction. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const float brightness0, + const float brightness1, + const float brightness2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + brightness0,brightness1,brightness2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nc0 = (int)((brightness0<0?0:(brightness0>2?2:brightness0))*256), + nc1 = (int)((brightness1<0?0:(brightness1>2?2:brightness1))*256), + nc2 = (int)((brightness2<0?0:(brightness2>2?2:brightness2))*256); + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + cleft = cleft0, cright = cright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightcleft?cright - cleft:cleft - cright, + rc = dx?(cright - cleft)/dx:0, + sc = cright>cleft?1:-1, + ndc = dc - (dx?dx*(dc/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errc = dx>>1; + if (xleft<0 && dx) { + cleft-=xleft*(cright - cleft)/dx; + zleft-=xleft*(zright - zleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded textured triangle. + /** + \param x0 = X-coordinate of the first corner in the instance image. + \param y0 = Y-coordinate of the first corner in the instance image. + \param x1 = X-coordinate of the second corner in the instance image. + \param y1 = Y-coordinate of the second corner in the instance image. + \param x2 = X-coordinate of the third corner in the instance image. + \param y2 = Y-coordinate of the third corner in the instance image. + \param texture = texture image used to fill the triangle. + \param tx0 = X-coordinate of the first corner in the texture image. + \param ty0 = Y-coordinate of the first corner in the texture image. + \param tx1 = X-coordinate of the second corner in the texture image. + \param ty1 = Y-coordinate of the second corner in the texture image. + \param tx2 = X-coordinate of the third corner in the texture image. + \param ty2 = Y-coordinate of the third corner in the texture image. + \param light = light image. + \param lx0 = X-coordinate of the first corner in the light image. + \param ly0 = Y-coordinate of the first corner in the light image. + \param lx1 = X-coordinate of the second corner in the light image. + \param ly1 = Y-coordinate of the second corner in the light image. + \param lx2 = X-coordinate of the third corner in the light image. + \param ly2 = Y-coordinate of the third corner in the light image. + \param opacity = opacity of the drawing. + \note Clipping is supported, but texture coordinates do not support clipping. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (!light) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2); + if (ny0>=dimy() || ny2<0) return *this; + _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y, + nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) { + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0, + txleft = txleft0, txright = txright0, + tyleft = tyleft0, tyright = tyright0; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + dtx = txright>txleft?txright - txleft:txleft - txright, + dty = tyright>tyleft?tyright - tyleft:tyleft - tyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + rtx = dx?(txright - txleft)/dx:0, + rty = dx?(tyright - tyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + stx = txright>txleft?1:-1, + sty = tyright>tyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0), + ndtx = dtx - (dx?dx*(dtx/dx):0), + ndty = dty - (dx?dx*(dty/dx):0); + int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx; + if (xleft<0 && dx) { + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } else for (int x = xleft; x<=xright; ++x) { + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr(txleft,tyleft); + cimg_forV(*this,k) { + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); + tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); + } + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded textured triangle, with perspective correction. + template + CImg& draw_triangle(const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (!light) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + zleft-=xleft*(zright - zleft)/dx; + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y,0,0); + if (opacity>=1) for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } else for (int x = xleft; x<=xright; ++x) { + const float invz = 1/zleft; + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + //! Draw a 2D Pseudo-Phong-shaded textured triangle, with z-buffering and perspective correction. + template + CImg& draw_triangle(float *const zbuffer, + const int x0, const int y0, const float z0, + const int x1, const int y1, const float z1, + const int x2, const int y2, const float z2, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const int tx2, const int ty2, + const CImg& light, + const int lx0, const int ly0, + const int lx1, const int ly1, + const int lx2, const int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!texture || texture.dim::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is not a valid argument.", + pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); + if (!light) + throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", + pixel_type(),light.width,light.height,light.depth,light.dim,light.data); + if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + static const T maxval = (T)cimg::min(cimg::type::max(),cimg::type::max()); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz; + int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, + nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; + float + ntx0 = tx0/z0, nty0 = ty0/z0, + ntx1 = tx1/z1, nty1 = ty1/z1, + ntx2 = tx2/z2, nty2 = ty2/z2, + nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2; + if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); + if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); + if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); + if (ny0>=dimy() || ny2<0) return *this; + float + ptxl = (ntx1 - ntx0)/(ny1 - ny0), + ptxr = (ntx2 - ntx0)/(ny2 - ny0), + ptxn = (ntx2 - ntx1)/(ny2 - ny1), + ptyl = (nty1 - nty0)/(ny1 - ny0), + ptyr = (nty2 - nty0)/(ny2 - ny0), + ptyn = (nty2 - nty1)/(ny2 - ny1), + pzl = (nz1 - nz0)/(ny1 - ny0), + pzr = (nz2 - nz0)/(ny2 - ny0), + pzn = (nz2 - nz1)/(ny2 - ny1), + zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)), + txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)), + tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)), + zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))), + txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))), + tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1))); + _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, + nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { + if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } + int + xleft = xleft0, xright = xright0, + lxleft = lxleft0, lxright = lxright0, + lyleft = lyleft0, lyright = lyright0; + float + zleft = zl, zright = zr, + txleft = txl, txright = txr, + tyleft = tyl, tyright = tyr; + if (xrightlxleft?lxright - lxleft:lxleft - lxright, + dly = lyright>lyleft?lyright - lyleft:lyleft - lyright, + rlx = dx?(lxright - lxleft)/dx:0, + rly = dx?(lyright - lyleft)/dx:0, + slx = lxright>lxleft?1:-1, + sly = lyright>lyleft?1:-1, + ndlx = dlx - (dx?dx*(dlx/dx):0), + ndly = dly - (dx?dx*(dly/dx):0); + const float + pentez = (zright - zleft)/dx, + pentetx = (txright - txleft)/dx, + pentety = (tyright - tyleft)/dx; + int errlx = dx>>1, errly = errlx; + if (xleft<0 && dx) { + zleft-=xleft*(zright - zleft)/dx; + lxleft-=xleft*(lxright - lxleft)/dx; + lyleft-=xleft*(lyright - lyleft)/dx; + txleft-=xleft*(txright - txleft)/dx; + tyleft-=xleft*(tyright - tyleft)/dx; + } + if (xleft<0) xleft = 0; + if (xright>=dimx()-1) xright = dimx()-1; + T* ptrd = ptr(xleft,y); + float *ptrz = zbuffer + xleft + y*width; + if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) { + if (zleft>*ptrz) { + *ptrz = zleft; + const float invz = 1/zleft; + const tl l = light(lxleft,lyleft); + const tc *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); + cimg_forV(*this,k) { + const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval); + *ptrd = (T)(nopacity*val + *ptrd*copacity); + ptrd+=whz; col+=twhz; + } + ptrd-=offx; + } + zleft+=pentez; txleft+=pentetx; tyleft+=pentety; + lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); + lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); + } + zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; + } + return *this; + } + + // Draw a 2D ellipse (inner routine). + template + CImg& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_ellipse : Specified color is (null).", + pixel_type()); + _draw_scanline(color,opacity); + const float + nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), + norm = (float)cimg_std::sqrt(ru*ru+rv*rv), + u = norm>0?ru/norm:1, + v = norm>0?rv/norm:0, + rmax = cimg::max(nr1,nr2), + l1 = (float)cimg_std::pow(rmax/(nr1>0?nr1:1e-6),2), + l2 = (float)cimg_std::pow(rmax/(nr2>0?nr2:1e-6),2), + a = l1*u*u + l2*v*v, + b = u*v*(l1-l2), + c = l1*v*v + l2*u*u; + const int + yb = (int)cimg_std::sqrt(a*rmax*rmax/(a*c-b*b)), + tymin = y0 - yb - 1, + tymax = y0 + yb + 1, + ymin = tymin<0?0:tymin, + ymax = tymax>=dimy()?height-1:tymax; + int oxmin = 0, oxmax = 0; + bool first_line = true; + for (int y = ymin; y<=ymax; ++y) { + const float + Y = y-y0 + (y0?(float)cimg_std::sqrt(delta)/a:0.0f, + bY = b*Y/a, + fxmin = x0-0.5f-bY-sdelta, + fxmax = x0+0.5f-bY+sdelta; + const int xmin = (int)fxmin, xmax = (int)fxmax; + if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity); + else { + if (first_line) { + if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity); + else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); + first_line = false; + } else { + if (xmin + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + return _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,0U); + } + + //! Draw a filled ellipse. + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, + const CImg& color, const float opacity=1) { + return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity); + } + + //! Draw a filled ellipse. + /** + \param x0 = X-coordinate of the ellipse center. + \param y0 = Y-coordinate of the ellipse center. + \param tensor = Diffusion tensor describing the ellipse. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity); + } + + //! Draw a filled ellipse. + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const CImg& color, const float opacity=1) { + return draw_ellipse(x0,y0,tensor,color.data,opacity); + } + + //! Draw an outlined ellipse. + /** + \param x0 = X-coordinate of the ellipse center. + \param y0 = Y-coordinate of the ellipse center. + \param r1 = First radius of the ellipse. + \param r2 = Second radius of the ellipse. + \param ru = X-coordinate of the orientation vector related to the first radius. + \param rv = Y-coordinate of the orientation vector related to the first radius. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (pattern) _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,pattern); + return *this; + } + + //! Draw an outlined ellipse. + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity,pattern); + } + + //! Draw an outlined ellipse. + /** + \param x0 = X-coordinate of the ellipse center. + \param y0 = Y-coordinate of the ellipse center. + \param tensor = Diffusion tensor describing the ellipse. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity, + const unsigned int pattern) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity,pattern); + } + + //! Draw an outlined ellipse. + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const CImg& color, const float opacity, + const unsigned int pattern) { + return draw_ellipse(x0,y0,tensor,color.data,opacity,pattern); + } + + //! Draw a filled circle. + /** + \param x0 X-coordinate of the circle center. + \param y0 Y-coordinate of the circle center. + \param radius Circle radius. + \param color Array of dimv() values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \note + - Circle version of the Bresenham's algorithm is used. + **/ + template + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity=1) { + if (!is_empty()) { + if (!color) + throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).", + pixel_type()); + _draw_scanline(color,opacity); + if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; + if (y0>=0 && y0=0) { + const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& draw_circle(const int x0, const int y0, int radius, + const CImg& color, const float opacity=1) { + return draw_circle(x0,y0,radius,color.data,opacity); + } + + //! Draw an outlined circle. + /** + \param x0 X-coordinate of the circle center. + \param y0 Y-coordinate of the circle center. + \param radius Circle radius. + \param color Array of dimv() values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity, + const unsigned int) { + if (!is_empty()) { + if (!color) + throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).", + pixel_type()); + if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; + if (!radius) return draw_point(x0,y0,color,opacity); + draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity). + draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity); + if (radius==1) return *this; + for (int f=1-radius, ddFx=0, ddFy=-(radius<<1), x=0, y=radius; x=0) { f+=(ddFy+=2); --y; } + ++x; ++(f+=(ddFx+=2)); + if (x!=y+1) { + const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y; + draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). + draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); + if (x!=y) + draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). + draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); + } + } + } + return *this; + } + + //! Draw an outlined circle. + template + CImg& draw_circle(const int x0, const int y0, int radius, const CImg& color, + const float opacity, + const unsigned int pattern) { + return draw_circle(x0,y0,radius,color.data,opacity,pattern); + } + + // Draw a text (internal). + template + CImg& _draw_text(const int x0, const int y0, const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font) { + if (!text) return *this; + if (!font) + throw CImgArgumentException("CImg<%s>::draw_text() : Specified font (%u,%p) is empty.", + pixel_type(),font.size,font.data); + const int text_length = cimg::strlen(text); + + if (is_empty()) { + // If needed, pre-compute necessary size of the image + int x = 0, y = 0, w = 0; + unsigned char c = 0; + for (int i = 0; iw) w = x; x = 0; break; + case '\t' : x+=4*font[' '].width; break; + default : if (cw) w=x; + y+=font[' '].height; + } + assign(x0+w,y0+y,1,font[' '].dim,0); + if (background_color) cimg_forV(*this,k) get_shared_channel(k).fill((T)background_color[k]); + } + + int x = x0, y = y0; + CImg letter; + for (int i = 0; i& mask = (c+256)<(int)font.size?font[c+256]:font[c]; + if (foreground_color) for (unsigned int p = 0; p=512) draw_image(x,y,letter,mask,opacity,(T)1); + else draw_image(x,y,letter,opacity); + x+=letter.width; + } + } + } + return *this; + } + + //! Draw a text. + /** + \param x0 X-coordinate of the text in the instance image. + \param y0 Y-coordinate of the text in the instance image. + \param foreground_color Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). + \param background_color Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). + \param font Font used for drawing text. + \param opacity Drawing opacity. + \param format 'printf'-style format string, followed by arguments. + \note Clipping is supported. + **/ + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, ...) { + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); + cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const CImg& foreground_color, const CImg& background_color, + const float opacity, const CImgList& font, ...) { + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); + cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const tc *const foreground_color, const int background_color, + const float opacity, const CImgList& font, ...) { + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); + cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)background_color,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const int foreground_color, const tc *const background_color, + const float opacity, const CImgList& font, ...) { + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font); + cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)foreground_color,background_color,opacity,font); + } + + //! Draw a text. + /** + \param x0 X-coordinate of the text in the instance image. + \param y0 Y-coordinate of the text in the instance image. + \param foreground_color Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). + \param background_color Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). + \param font_size Size of the font (nearest match). + \param opacity Drawing opacity. + \param format 'printf'-style format string, followed by arguments. + \note Clipping is supported. + **/ + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity=1, const unsigned int font_size=11, ...) { + static CImgList font; + static unsigned int fsize = 0; + if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const CImg& foreground_color, const CImg& background_color, + const float opacity=1, const unsigned int font_size=11, ...) { + static CImgList font; + static unsigned int fsize = 0; + if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color.data,background_color.data,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const tc *const foreground_color, const int background_color=0, + const float opacity=1, const unsigned int font_size=11, ...) { + static CImgList font; + static unsigned int fsize = 0; + if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(const tc*)background_color,opacity,font); + } + + //! Draw a text. + template + CImg& draw_text(const int x0, const int y0, const char *const text, + const int foreground_color, const tc *const background_color, + const float opacity=1, const unsigned int font_size=11, ...) { + static CImgList font; + static unsigned int fsize = 0; + if (fsize!=font_size) { font = CImgList::font(font_size); fsize = font_size; } + char tmp[2048] = { 0 }; cimg_std::va_list ap; va_start(ap,font_size); cimg_std::vsprintf(tmp,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)foreground_color,background_color,opacity,font); + } + + //! Draw a vector field in the instance image, using a colormap. + /** + \param flow Image of 2d vectors used as input data. + \param color Image of dimv()-D vectors corresponding to the color of each arrow. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). + \param opacity Opacity of the drawing. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const t2 *const color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const int quiver_type=0, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,dim,1,1,1,true),opacity,sampling,factor,quiver_type,pattern); + } + + //! Draw a vector field in the instance image, using a colormap. + /** + \param flow Image of 2d vectors used as input data. + \param color Image of dimv()-D vectors corresponding to the color of each arrow. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). + \param opacity Opacity of the drawing. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const CImg& color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const int quiver_type=0, const unsigned int pattern=~0U) { + if (!is_empty()) { + if (!flow || flow.dim!=2) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified flow (%u,%u,%u,%u,%p) has wrong dimensions.", + pixel_type(),flow.width,flow.height,flow.depth,flow.dim,flow.data); + if (sampling<=0) + throw CImgArgumentException("CImg<%s>::draw_quiver() : Incorrect sampling value = %g", + pixel_type(),sampling); + const bool colorfield = (color.width==flow.width && color.height==flow.height && color.depth==1 && color.dim==dim); + if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,quiver_type,pattern); + + float vmax,fact; + if (factor<=0) { + float m, M = (float)flow.get_pointwise_norm(2).maxmin(m); + vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y=sampling/2; y + CImg& draw_graph(const CImg& data, + const tc *const color, const float opacity=1, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const double ymin=0, const double ymax=0, + const unsigned int pattern=~0U) { + if (is_empty() || height<=1) return *this;; + const unsigned long siz = data.size(); + if (!color) + throw CImgArgumentException("CImg<%s>::draw_graph() : Specified color is (null)", + pixel_type()); + tc *color1 = 0, *color2 = 0; + if (plot_type==3) { + color1 = new tc[dim]; color2 = new tc[dim]; + cimg_forV(*this,k) { color1[k] = (tc)(color[k]*0.6f); color2[k] = (tc)(color[k]*0.3f); } + } + + double m = ymin, M = ymax; + if (ymin==ymax) m = (double)data.maxmin(M); + if (m==M) { --m; ++M; } + const float ca = (float)(M-m)/(height-1); + bool init_hatch = true; + + // Draw graph edges + switch (plot_type%4) { + case 1 : { // Segments + int oX = 0, oY = (int)((data[0]-m)/ca); + for (unsigned long off = 1; off ndata = data.get_shared_points(0,siz-1); + int oY = (int)((data[0]-m)/ca); + cimg_forX(*this,x) { + const int Y = (int)((ndata._cubic_atX((float)x*ndata.width/width)-m)/ca); + if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch); + init_hatch = false; + oY = Y; + } + } break; + case 3 : { // Bars + const int Y0 = (int)(-m/ca); + int oX = 0; + cimg_foroff(data,off) { + const int + X = (off+1)*width/siz-1, + Y = (int)((data[off]-m)/ca); + draw_rectangle(oX,Y0,X,Y,color1,opacity). + draw_line(oX,Y,oX,Y0,color2,opacity). + draw_line(oX,Y0,X,Y0,Y<=Y0?color2:color,opacity). + draw_line(X,Y,X,Y0,color,opacity). + draw_line(oX,Y,X,Y,Y<=Y0?color:color2,opacity); + oX = X+1; + } + } break; + default : break; // No edges + } + + // Draw graph points + switch (vertex_type%8) { + case 1 : { // Point + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_point(X,Y,color,opacity); + } + } break; + case 2 : { // Standard Cross + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity); + } + } break; + case 3 : { // Rotated Cross + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity); + } + } break; + case 4 : { // Filled Circle + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity); + } + } break; + case 5 : { // Outlined circle + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity,0U); + } + } break; + case 6 : { // Square + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U); + } + } break; + case 7 : { // Diamond + cimg_foroff(data,off) { + const int X = off*width/siz, Y = (int)((data[off]-m)/ca); + draw_line(X,Y-4,X+4,Y,color,opacity). + draw_line(X+4,Y,X,Y+4,color,opacity). + draw_line(X,Y+4,X-4,Y,color,opacity). + draw_line(X-4,Y,X,Y-4,color,opacity); + } + } break; + default : break; // No vertices + } + + if (color1) delete[] color1; if (color2) delete[] color2; + return *this; + } + + //! Draw a 1D graph on the instance image. + template + CImg& draw_graph(const CImg& data, + const CImg& color, const float opacity=1, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const double ymin=0, const double ymax=0, + const unsigned int pattern=~0U) { + return draw_graph(data,color.data,opacity,plot_type,vertex_type,ymin,ymax,pattern); + } + + //! Draw a labeled horizontal axis on the instance image. + /** + \param xvalues Lower bound of the x-range. + \param y Y-coordinate of the horizontal axis in the instance image. + \param color Array of dimv() values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param opacity_out Drawing opacity of 'outside' axes. + \note if \c precision==0, precision of the labels is automatically computed. + **/ + template + CImg& draw_axis(const CImg& xvalues, const int y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U) { + if (!is_empty()) { + int siz = (int)xvalues.size()-1; + if (siz<=0) draw_line(0,y,width-1,y,color,opacity,pattern); + else { + if (xvalues[0] + CImg& draw_axis(const CImg& xvalues, const int y, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U) { + return draw_axis(xvalues,y,color.data,opacity,pattern); + } + + //! Draw a labeled vertical axis on the instance image. + template + CImg& draw_axis(const int x, const CImg& yvalues, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U) { + if (!is_empty()) { + int siz = (int)yvalues.size()-1; + if (siz<=0) draw_line(x,0,x,height-1,color,opacity,pattern); + else { + if (yvalues[0]=dimy()-11?dimy()-11:tmp), + xt = x-(int)cimg::strlen(txt)*7; + draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); + if (xt>0) draw_text(xt,nyi,txt,color,(tc*)0,opacity,11); + else draw_text(x+3,nyi,txt,color,(tc*)0,opacity,11); + } + } + } + return *this; + } + + //! Draw a labeled vertical axis on the instance image. + template + CImg& draw_axis(const int x, const CImg& yvalues, + const CImg& color, const float opacity=1, + const unsigned int pattern=~0U) { + return draw_axis(x,yvalues,color.data,opacity,pattern); + } + + //! Draw a labeled horizontal+vertical axis on the instance image. + template + CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, + const tc *const color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + if (!is_empty()) { + const CImg nxvalues(xvalues.data,xvalues.size(),1,1,1,true); + const int sizx = (int)xvalues.size()-1, wm1 = (int)(width)-1; + if (sizx>0) { + float ox = (float)nxvalues[0]; + for (unsigned int x = 1; x nyvalues(yvalues.data,yvalues.size(),1,1,1,true); + const int sizy = (int)yvalues.size()-1, hm1 = (int)(height)-1; + if (sizy>0) { + float oy = (float)nyvalues[0]; + for (unsigned int y = 1; y + CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, + const CImg& color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + return draw_axis(xvalues,yvalues,color.data,opacity,patternx,patterny); + } + + //! Draw a labeled horizontal+vertical axis on the instance image. + template + CImg& draw_axis(const float x0, const float x1, const float y0, const float y1, + const tc *const color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + if (!is_empty()) { + const float + dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0), + px = (precisionx==0)?(float)cimg_std::pow(10.0,(int)cimg_std::log10(dx)-2.0):precisionx, + py = (precisiony==0)?(float)cimg_std::pow(10.0,(int)cimg_std::log10(dy)-2.0):precisiony; + draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-dimx()/subdivisionx,x0,x1).round(px), + CImg::sequence(subdivisiony>0?subdivisiony:1-dimy()/subdivisiony,y0,y1).round(py), + color,opacity,patternx,patterny); + } + return *this; + } + + //! Draw a labeled horizontal+vertical axis on the instance image. + template + CImg& draw_axis(const float x0, const float x1, const float y0, const float y1, + const CImg& color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + return draw_axis(x0,x1,y0,y1,color.data,opacity,subdivisionx,subdivisiony,precisionx,precisiony,patternx,patterny); + } + + //! Draw grid. + template + CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, + const tc *const color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + if (!is_empty()) { + if (xvalues) cimg_foroff(xvalues,x) { + const int xi = (int)xvalues[x]; + if (xi>=0 && xi=0 && yi + CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, + const CImg& color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + return draw_grid(xvalues,yvalues,color.data,opacity,patternx,patterny); + } + + //! Draw grid. + template + CImg& draw_grid(const float deltax, const float deltay, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const tc *const color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + CImg seqx, seqy; + if (deltax!=0) { + const float dx = deltax>0?deltax:width*-deltax/100; + const unsigned int nx = (unsigned int)(width/dx); + seqx = CImg::sequence(1+nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = width-1-seqx(x); + } + + if (deltay!=0) { + const float dy = deltay>0?deltay:height*-deltay/100; + const unsigned int ny = (unsigned int)(height/dy); + seqy = CImg::sequence(1+ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = height-1-seqy(y); + } + return draw_grid(seqx,seqy,color,opacity,patternx,patterny); + } + + //! Draw grid. + template + CImg& draw_grid(const float deltax, const float deltay, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const CImg& color, const float opacity=1, + const unsigned int patternx=~0U, const unsigned int patterny=~0U) { + return draw_grid(deltax,deltay,offsetx,offsety,invertx,inverty,color.data,opacity,patternx,patterny); + } + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + /** + \param x X-coordinate of the starting point of the region to fill. + \param y Y-coordinate of the starting point of the region to fill. + \param z Z-coordinate of the starting point of the region to fill. + \param color An array of dimv() values of type \c T, defining the drawing color. + \param region Image that will contain the mask of the filled region mask, as an output. + \param sigma Tolerance concerning neighborhood values. + \param opacity Opacity of the drawing. + \param high_connexity Tells if 8-connexity must be used (only for 2D images). + \return \p region is initialized with the binary mask of the filled region. + **/ + template + CImg& draw_fill(const int x, const int y, const int z, + const tc *const color, const float opacity, + CImg& region, const float sigma=0, + const bool high_connexity=false) { + +#define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \ + res = true; \ + const T *reference_col = reference_color.ptr() + dim, *ptrs = ptr(x,y,z) + siz; \ + for (unsigned int i = dim; res && i; --i) { ptrs-=whz; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \ + region(x,y,z) = (t)(res?1:noregion); \ +} + +#define _cimg_draw_fill_set(x,y,z) { \ + const tc *col = color; \ + T *ptrd = ptr(x,y,z); \ + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } \ + else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } \ +} + +#define _cimg_draw_fill_insert(x,y,z) { \ + if (posr1>=remaining.height) remaining.resize(3,remaining.height<<1,1,1,0); \ + unsigned int *ptrr = remaining.ptr(0,posr1); \ + *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \ +} + +#define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \ + const unsigned int tx = x, ty = y, tz = z; \ + _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \ +} + + if (!color) + throw CImgArgumentException("CImg<%s>::draw_fill() : Specified color is (null).", + pixel_type()); + region.assign(width,height,depth,1,(t)0); + if (x>=0 && x=0 && y=0 && z1; + const CImg reference_color = get_vector_at(x,y,z); + CImg remaining(3,512,1,1,0); + remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z; + unsigned int posr0 = 0, posr1 = 1; + region(x,y,z) = (t)1; + const t noregion = ((t)1==(t)2)?(t)0:(t)(-1); + if (threed) do { // 3D version of the filling algorithm + const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++); + if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } + bool cont, res; + unsigned int nxc = xc; + do { // X-backward + _cimg_draw_fill_set(nxc,yc,zc); + _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0); + _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,ycposr0); + else do { // 2D version of the filling algorithm + const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++); + if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } + bool cont, res; + unsigned int nxc = xc; + do { // X-backward + _cimg_draw_fill_set(nxc,yc,0); + _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0); + _cimg_draw_fill_test_neighbor(nxc,yc+1,0,ycposr0); + if (noregion) cimg_for(region,ptr,t) if (*ptr==noregion) *ptr = (t)0; + } + return *this; + } + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + template + CImg& draw_fill(const int x, const int y, const int z, + const CImg& color, const float opacity, + CImg& region, const float sigma=0, const bool high_connexity=false) { + return draw_fill(x,y,z,color.data,opacity,region,sigma,high_connexity); + } + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + /** + \param x = X-coordinate of the starting point of the region to fill. + \param y = Y-coordinate of the starting point of the region to fill. + \param z = Z-coordinate of the starting point of the region to fill. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param sigma = tolerance concerning neighborhood values. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_fill(const int x, const int y, const int z, + const tc *const color, const float opacity=1, + const float sigma=0, const bool high_connexity=false) { + CImg tmp; + return draw_fill(x,y,z,color,opacity,tmp,sigma,high_connexity); + } + + //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. + template + CImg& draw_fill(const int x, const int y, const int z, + const CImg& color, const float opacity=1, + const float sigma=0, const bool high_connexity=false) { + return draw_fill(x,y,z,color.data,opacity,sigma,high_connexity); + } + + //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. + /** + \param x = X-coordinate of the starting point of the region to fill. + \param y = Y-coordinate of the starting point of the region to fill. + \param color = an array of dimv() values of type \c T, defining the drawing color. + \param sigma = tolerance concerning neighborhood values. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_fill(const int x, const int y, + const tc *const color, const float opacity=1, + const float sigma=0, const bool high_connexity=false) { + CImg tmp; + return draw_fill(x,y,0,color,opacity,tmp,sigma,high_connexity); + } + + //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. + template + CImg& draw_fill(const int x, const int y, + const CImg& color, const float opacity=1, + const float sigma=0, const bool high_connexity=false) { + return draw_fill(x,y,color.data,opacity,sigma,high_connexity); + } + + //! Draw a plasma random texture. + /** + \param x0 = X-coordinate of the upper-left corner of the plasma. + \param y0 = Y-coordinate of the upper-left corner of the plasma. + \param x1 = X-coordinate of the lower-right corner of the plasma. + \param y1 = Y-coordinate of the lower-right corner of the plasma. + \param alpha = Alpha-parameter of the plasma. + \param beta = Beta-parameter of the plasma. + \param opacity = opacity of the drawing. + **/ + CImg& draw_plasma(const int x0, const int y0, const int x1, const int y1, + const float alpha=1, const float beta=1, + const float opacity=1) { + if (!is_empty()) { + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1; + if (nx1=dimx()) nx1 = width-1; + if (ny0<0) ny0 = 0; + if (ny1>=dimy()) ny1 = height-1; + const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0); + const Tfloat dc = (Tfloat)(cimg_std::sqrt((float)(dx*dx+dy*dy))*alpha + beta); + Tfloat val = 0; + cimg_forV(*this,k) { + if (opacity>=1) { + const Tfloat + val0 = (Tfloat)((*this)(nx0,ny0,0,k)), val1 = (Tfloat)((*this)(nx1,ny0,0,k)), + val2 = (Tfloat)((*this)(nx0,ny1,0,k)), val3 = (Tfloat)((*this)(nx1,ny1,0,k)); + (*this)(xc,ny0,0,k) = (T)((val0+val1)/2); + (*this)(xc,ny1,0,k) = (T)((val2+val3)/2); + (*this)(nx0,yc,0,k) = (T)((val0+val2)/2); + (*this)(nx1,yc,0,k) = (T)((val1+val3)/2); + do { + val = (Tfloat)(0.25f*((Tfloat)((*this)(nx0,ny0,0,k)) + + (Tfloat)((*this)(nx1,ny0,0,k)) + + (Tfloat)((*this)(nx1,ny1,0,k)) + + (Tfloat)((*this)(nx0,ny1,0,k))) + + dc*cimg::grand()); + } while (val<(Tfloat)cimg::type::min() || val>(Tfloat)cimg::type::max()); + (*this)(xc,yc,0,k) = (T)val; + } else { + const Tfloat + val0 = (Tfloat)((*this)(nx0,ny0,0,k)), val1 = (Tfloat)((*this)(nx1,ny0,0,k)), + val2 = (Tfloat)((*this)(nx0,ny1,0,k)), val3 = (Tfloat)((*this)(nx1,ny1,0,k)); + (*this)(xc,ny0,0,k) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,k))/2); + (*this)(xc,ny1,0,k) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,k))/2); + (*this)(nx0,yc,0,k) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,k))/2); + (*this)(nx1,yc,0,k) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,k))/2); + do { + val = (Tfloat)(0.25f*(((Tfloat)((*this)(nx0,ny0,0,k)) + + (Tfloat)((*this)(nx1,ny0,0,k)) + + (Tfloat)((*this)(nx1,ny1,0,k)) + + (Tfloat)((*this)(nx0,ny1,0,k))) + + dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,k)); + } while (val<(Tfloat)cimg::type::min() || val>(Tfloat)cimg::type::max()); + (*this)(xc,yc,0,k) = (T)val; + } + } + if (xc!=nx0 || yc!=ny0) { + draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity); + draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity); + draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity); + draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity); + } + } + return *this; + } + + //! Draw a plasma random texture. + /** + \param alpha = Alpha-parameter of the plasma. + \param beta = Beta-parameter of the plasma. + \param opacity = opacity of the drawing. + **/ + CImg& draw_plasma(const float alpha=1, const float beta=1, + const float opacity=1) { + return draw_plasma(0,0,width-1,height-1,alpha,beta,opacity); + } + + //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm. + template + CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, + const CImg& color_palette, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int itermax=255, + const bool normalized_iteration=false, + const bool julia_set=false, + const double paramr=0, const double parami=0) { + if (is_empty()) return *this; + CImg palette; + if (color_palette) palette.assign(color_palette.data,color_palette.size()/color_palette.dim,1,1,color_palette.dim,true); + if (palette && palette.dim!=dim) + throw CImgArgumentException("CImg<%s>::draw_mandelbrot() : Specified color palette (%u,%u,%u,%u,%p) is not \n" + "compatible with instance image (%u,%u,%u,%u,%p).", + pixel_type(),color_palette.width,color_palette.height,color_palette.depth,color_palette.dim, + color_palette.data,width,height,depth,dim,data); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)cimg_std::log(2.0); + unsigned int iter = 0; + cimg_for_inXY(*this,x0,y0,x1,y1,p,q) { + const double x = z0r + p*(z1r-z0r)/width, y = z0i + q*(z1i-z0i)/height; + double zr, zi, cr, ci; + if (julia_set) { zr = x; zi = y; cr = paramr; ci = parami; } + else { zr = paramr; zi = parami; cr = x; ci = y; } + for (iter=1; zr*zr + zi*zi<=4 && iter<=itermax; ++iter) { + const double temp = zr*zr - zi*zi + cr; + zi = 2*zr*zi + ci; + zr = temp; + } + if (iter>itermax) { + if (palette) { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette(0,k); + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette(0,k)*nopacity + (*this)(p,q,0,k)*copacity); + } else { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)0; + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)((*this)(p,q,0,k)*copacity); + } + } else if (normalized_iteration) { + const float + normz = (float)cimg::abs(zr*zr+zi*zi), + niter = (float)(iter + 1 - cimg_std::log(cimg_std::log(normz))/ln2); + if (palette) { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette._linear_atX(niter,k); + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette._linear_atX(niter,k)*nopacity + (*this)(p,q,0,k)*copacity); + } else { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)niter; + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(niter*nopacity + (*this)(p,q,0,k)*copacity); + } + } else { + if (palette) { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)palette._atX(iter,k); + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(palette(iter,k)*nopacity + (*this)(p,q,0,k)*copacity); + } else { + if (opacity>=1) cimg_forV(*this,k) (*this)(p,q,0,k) = (T)iter; + else cimg_forV(*this,k) (*this)(p,q,0,k) = (T)(iter*nopacity + (*this)(p,q,0,k)*copacity); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm. + template + CImg& draw_mandelbrot(const CImg& color_palette, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int itermax=255, + const bool normalized_iteration=false, + const bool julia_set=false, + const double paramr=0, const double parami=0) { + return draw_mandelbrot(0,0,width-1,height-1,color_palette,opacity,z0r,z0i,z1r,z1i,itermax,normalized_iteration,julia_set,paramr,parami); + } + + //! Draw a 1D gaussian function in the instance image. + /** + \param xc = X-coordinate of the gaussian center. + \param sigma = Standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_gaussian(const float xc, const float sigma, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)", + pixel_type()); + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned int whz = width*height*depth; + const tc *col = color; + cimg_forX(*this,x) { + const float dx = (x - xc), val = (float)cimg_std::exp(-dx*dx/sigma2); + T *ptrd = ptr(x,0,0,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } + col-=dim; + } + return *this; + } + + //! Draw a 1D gaussian function in the instance image. + template + CImg& draw_gaussian(const float xc, const float sigma, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,sigma,color.data,opacity); + } + + //! Draw an anisotropic 2D gaussian function. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param tensor = 2x2 covariance matrix. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename cimg::superset::type tfloat; + if (tensor.width!=2 || tensor.height!=2 || tensor.depth!=1 || tensor.dim!=1) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); + if (!color) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)", + pixel_type()); + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned int whz = width*height*depth; + const tc *col = color; + float dy = -yc; + cimg_forY(*this,y) { + float dx = -xc; + cimg_forX(*this,x) { + const float val = (float)cimg_std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = ptr(x,y,0,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } + col-=dim; + ++dx; + } + ++dy; + } + return *this; + } + + //! Draw an anisotropic 2D gaussian function. + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,yc,tensor,color.data,opacity); + } + + //! Draw an anisotropic 2D gaussian function. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + const double + a = r1*ru*ru + r2*rv*rv, + b = (r1-r2)*ru*rv, + c = r1*rv*rv + r2*ru*ru; + const CImg tensor(2,2,1,1, a,b,b,c); + return draw_gaussian(xc,yc,tensor,color,opacity); + } + + //! Draw an anisotropic 2D gaussian function. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,yc,r1,r2,ru,rv,color.data,opacity); + } + + //! Draw an isotropic 2D gaussian function. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param sigma = standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw an isotropic 2D gaussian function. + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,yc,sigma,color.data,opacity); + } + + //! Draw an anisotropic 3D gaussian function. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param zc = Z-coordinate of the gaussian center. + \param tensor = 3x3 covariance matrix. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename cimg::superset::type tfloat; + if (tensor.width!=3 || tensor.height!=3 || tensor.depth!=1 || tensor.dim!=1) + throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0); + const tfloat a = invT(0,0), b = 2*invT(1,0), c = 2*invT(2,0), d = invT(1,1), e = 2*invT(2,1), f = invT(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0); + const unsigned int whz = width*height*depth; + const tc *col = color; + cimg_forXYZ(*this,x,y,z) { + const float + dx = (x - xc), dy = (y - yc), dz = (z - zc), + val = (float)cimg_std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = ptr(x,y,z,0); + if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } + else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } + col-=dim; + } + return *this; + } + + //! Draw an anisotropic 3D gaussian function. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,tensor,color.data,opacity); + } + + //! Draw an isotropic 3D gaussian function. + /** + \param xc = X-coordinate of the gaussian center. + \param yc = Y-coordinate of the gaussian center. + \param zc = Z-coordinate of the gaussian center. + \param sigma = standard variation of the gaussian distribution. + \param color = array of dimv() values of type \c T, defining the drawing color. + \param opacity = opacity of the drawing. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw an isotropic 3D gaussian function. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const CImg& color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,sigma,color.data,opacity); + } + + // Draw a 3D object (internal) + template + void _draw_object3d_sprite(const int x, const int y, + const CImg& color, const CImg& opacity, const CImg& sprite) { + if (opacity.width==color.width && opacity.height==color.height) + draw_image(x,y,sprite,opacity.get_resize(sprite.width,sprite.height,1,sprite.dim,1)); + else + draw_image(x,y,sprite,opacity(0)); + } + + template + void _draw_object3d_sprite(const int x, const int y, + const CImg& color, const float opacity, const CImg& sprite) { + if (color) draw_image(x,y,sprite,opacity); + } + + template + CImg& _draw_object3d(void *const pboard, float *const zbuffer, + const float X, const float Y, const float Z, + const tp& points, const unsigned int nb_points, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, const unsigned int nb_opacities, + const unsigned int render_type, + const bool double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_light, const float specular_shine) { + if (is_empty()) return *this; +#ifndef cimg_use_board + if (pboard) return *this; +#endif + const float + nspec = 1-(specular_light<0?0:(specular_light>1?1:specular_light)), + nspec2 = 1+(specular_shine<0?0:specular_shine), + nsl1 = (nspec2-1)/cimg::sqr(nspec-1), + nsl2 = (1-2*nsl1*nspec), + nsl3 = nspec2-nsl1-nsl2; + + // Create light texture for phong-like rendering + static CImg light_texture; + if (render_type==5) { + if (colors.size>primitives.size) light_texture.assign(colors[primitives.size])/=255; + else { + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0; + if (!light_texture || lightx!=olightx || lighty!=olighty || lightz!=olightz || specular_shine!=ospecular_shine) { + light_texture.assign(512,512); + const float white[] = { 1 }, + dlx = lightx-X, dly = lighty-Y, dlz = lightz-Z, + nl = (float)cimg_std::sqrt(dlx*dlx+dly*dly+dlz*dlz), + nlx = light_texture.width/2*(1+dlx/nl), + nly = light_texture.height/2*(1+dly/nl); + light_texture.draw_gaussian(nlx,nly,light_texture.width/3.0f,white); + cimg_forXY(light_texture,x,y) { + const float factor = light_texture(x,y); + if (factor>nspec) light_texture(x,y) = cimg::min(2,nsl1*factor*factor+nsl2*factor+nsl3); + } + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shine = specular_shine; + } + } + } + + // Compute 3D to 2D projection + CImg projections(nb_points,2); + cimg_forX(projections,l) { + const float + x = (float)points(l,0), + y = (float)points(l,1), + z = (float)points(l,2); + const float projectedz = z + Z + focale; + projections(l,1) = Y + focale*y/projectedz; + projections(l,0) = X + focale*x/projectedz; + } + + // Compute and sort visible primitives + CImg visibles(primitives.size); + CImg zrange(primitives.size); + unsigned int nb_visibles = 0; + const float zmin = -focale+1.5f; + { cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + + case 1 : { // Point + const unsigned int i0 = (unsigned int)primitive(0); + const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)); + if (z0>zmin && x0>=0 && x0=0 && y0zmin && x0+radius>=0 && x0-radius=0 && y0-radiuszmin && z1>zmin && xM>=0 && xm=0 && ymxM) xM = x2; + if (y0yM) yM = y2; + if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm=0 && ymxM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm=0 && ym::draw_object3d() : Primitive %u is invalid (size = %u, can be 1,2,3,4,5,6,9 or 12)", + pixel_type(),l,primitive.size()); + }} + } + if (nb_visibles<=0) return *this; + CImg permutations; + CImg(zrange.data,nb_visibles,1,1,1,true).sort(permutations,false); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3 : { // Flat Shading + lightprops.assign(nb_visibles); + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const float + x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), + x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), + x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nx = dy1*dz2 - dz1*dy2, + ny = dz1*dx2 - dx1*dz2, + nz = dx1*dy2 - dy1*dx2, + norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + lx = X + (x0 + x1 + x2)/3 - lightx, + ly = Y + (y0 + y1 + y2)/3 - lighty, + lz = Z + (z0 + z1 + z2)/3 - lightz, + nl = (float)cimg_std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), + factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } else lightprops[l] = 1; + } + } break; + + case 4 : // Gouraud Shading + case 5 : { // Phong-Shading + CImg points_normals(nb_points,3,1,1,0); + for (unsigned int l=0; l& primitive = primitives[visibles(l)]; + const unsigned int psize = primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + rectangle_flag = (psize==4) || (psize==12); + if (triangle_flag || rectangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = rectangle_flag?(unsigned int)primitive(3):0; + const float + x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), + x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), + x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nnx = dy1*dz2 - dz1*dy2, + nny = dz1*dx2 - dx1*dz2, + nnz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + (float)cimg_std::sqrt(nnx*nnx + nny*nny + nnz*nnz), + nx = nnx/norm, + ny = nny/norm, + nz = nnz/norm; + points_normals(i0,0)+=nx; points_normals(i0,1)+=ny; points_normals(i0,2)+=nz; + points_normals(i1,0)+=nx; points_normals(i1,1)+=ny; points_normals(i1,2)+=nz; + points_normals(i2,0)+=nx; points_normals(i2,1)+=ny; points_normals(i2,2)+=nz; + if (rectangle_flag) { points_normals(i3,0)+=nx; points_normals(i3,1)+=ny; points_normals(i3,2)+=nz; } + } + } + + if (double_sided) cimg_forX(points_normals,p) if (points_normals(p,2)>0) { + points_normals(p,0) = -points_normals(p,0); + points_normals(p,1) = -points_normals(p,1); + points_normals(p,2) = -points_normals(p,2); + } + + if (render_type==4) { + lightprops.assign(nb_points); + cimg_forX(lightprops,ll) { + const float + nx = points_normals(ll,0), + ny = points_normals(ll,1), + nz = points_normals(ll,2), + norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + lx = (float)(X + points(ll,0) - lightx), + ly = (float)(Y + points(ll,1) - lighty), + lz = (float)(Z + points(ll,2) - lightz), + nl = (float)cimg_std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz), + factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0); + lightprops[ll] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } + } else { + const unsigned int + lw2 = light_texture.width/2 - 1, + lh2 = light_texture.height/2 - 1; + lightprops.assign(nb_points,2); + cimg_forX(lightprops,ll) { + const float + nx = points_normals(ll,0), + ny = points_normals(ll,1), + nz = points_normals(ll,2), + norm = (float)cimg_std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz), + nnx = nx/norm, + nny = ny/norm; + lightprops(ll,0) = lw2*(1 + nnx); + lightprops(ll,1) = lh2*(1 + nny); + } + } + } break; + } + + // Draw visible primitives + const CImg default_color(1,dim,1,1,(tc)200); + { for (unsigned int l = 0; l& primitive = primitives[n_primitive]; + const CImg& color = n_primitive=0 && x0-sw=0 && y0-sh sprite = color.get_resize(-factor,-factor,1,-100,render_type<=3?1:3); + _draw_object3d_sprite(x0-sw,y0-sh,color,opacities[n_primitive%nb_opacities],sprite); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(BoardLib::Color::none); + board.drawRectangle((float)x0-sw,dimy()-(float)y0+sh,sw,sh); + } +#endif + } + } + } break; + case 2 : { // Colored line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac); + else draw_line(x0,y0,x1,y1,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,x1,dimy()-(float)y1); + } +#endif + } else { + draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + } +#endif + } + } break; + case 5 : { // Colored sphere + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); + int radius; + if (n2) radius = (int)(n2*focale/(Z+points(n0,2)+focale)); + else { + const int + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + deltax = x1-x0, deltay = y1-y0; + radius = (int)cimg_std::sqrt((float)(deltax*deltax + deltay*deltay)); + } + switch (render_type) { + case 0 : + draw_point(x0,y0,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.fillCircle((float)x0,dimy()-(float)y0,0); + } +#endif + break; + case 1 : + draw_circle(x0,y0,radius,color,opac,~0U); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.setFillColor(BoardLib::Color::none); + board.drawCircle((float)x0,dimy()-(float)y0,(float)radius); + } +#endif + break; + default : + draw_circle(x0,y0,radius,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.fillCircle((float)x0,dimy()-(float)y0,(float)radius); + } +#endif + break; + } + } break; + case 6 : { // Textured line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + tx0 = (unsigned int)primitive[2], + ty0 = (unsigned int)primitive[3], + tx1 = (unsigned int)primitive[4], + ty1 = (unsigned int)primitive[5]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac); + else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); + } +#endif + } else { + draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + } +#endif + } + } break; + case 3 : { // Colored triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale, + z2 = points(n2,2) + Z + focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac).draw_point(x2,y2,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + board.drawCircle((float)x2,dimy()-(float)y2,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,opac). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,opac); + else + draw_line(x0,y0,x1,y1,color,opac).draw_line(x0,y0,x2,y2,color,opac). + draw_line(x1,y1,x2,y2,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); + board.drawLine((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2); + board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,opac); + else draw_triangle(x0,y0,x1,y1,x2,y2,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opac,lightprops(l)); + else _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)); +#ifdef cimg_use_board + if (pboard) { + const float lp = cimg::min(lightprops(l),1); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp), + (unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 4 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,lightprops(n0),lightprops(n1),lightprops(n2),opac); + else draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprops(n0),lightprops(n1),lightprops(n2),opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprops(n0), + (float)x1,dimy()-(float)y1,lightprops(n1), + (float)x2,dimy()-(float)y2,lightprops(n2)); + } +#endif + break; + case 5 : { + const unsigned int + lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), + lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), + lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1); + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); + else draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n0,0))), (int)(light_texture.dimy()/2*(1+lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n1,0))), (int)(light_texture.dimy()/2*(1+lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n2,0))), (int)(light_texture.dimy()/2*(1+lightprops(n2,1)))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, + (float)x1,dimy()-(float)y1,l1, + (float)x2,dimy()-(float)y2,l2); + } +#endif + } break; + } + } break; + case 4 : { // Colored rectangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), + x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale, + z2 = points(n2,2) + Z + focale, + z3 = points(n3,2) + Z + focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac). + draw_point(x2,y2,color,opac).draw_point(x3,y3,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + board.drawCircle((float)x2,dimy()-(float)y2,0); + board.drawCircle((float)x3,dimy()-(float)y3,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,opac). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,opac); + else + draw_line(x0,y0,x1,y1,color,opac).draw_line(x1,y1,x2,y2,color,opac). + draw_line(x2,y2,x3,y3,color,opac).draw_line(x3,y3,x0,y0,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); + board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.drawLine((float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + board.drawLine((float)x3,dimy()-(float)y3,(float)x0,dimy()-(float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,opac); + else + draw_triangle(x0,y0,x1,y1,x2,y2,color,opac).draw_triangle(x0,y0,x2,y2,x3,y3,color,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color.data,opac,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color.data,opac,lightprops(l)); + else + _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)). + _draw_triangle(x0,y0,x2,y2,x3,y3,color.data,opac,lightprops(l)); +#ifdef cimg_use_board + if (pboard) { + const float lp = cimg::min(lightprops(l),1); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp),(unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,lightprop0,lightprop1,lightprop2,opac). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,lightprop0,lightprop2,lightprop3,opac); + else + draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprop0,lightprop1,lightprop2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color,lightprop0,lightprop2,lightprop3,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, + (float)x1,dimy()-(float)y1,lightprop1, + (float)x2,dimy()-(float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, + (float)x2,dimy()-(float)y2,lightprop2, + (float)x3,dimy()-(float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), + lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), + lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), + lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + else + draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(x0,y0,x2,y2,x3,y3,color,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.dimx()/2*(1+lx0)), (int)(light_texture.dimy()/2*(1+ly0))), + l1 = light_texture((int)(light_texture.dimx()/2*(1+lx1)), (int)(light_texture.dimy()/2*(1+ly1))), + l2 = light_texture((int)(light_texture.dimx()/2*(1+lx2)), (int)(light_texture.dimy()/2*(1+ly2))), + l3 = light_texture((int)(light_texture.dimx()/2*(1+lx3)), (int)(light_texture.dimy()/2*(1+ly3))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, + (float)x1,dimy()-(float)y1,l1, + (float)x2,dimy()-(float)y2,l2); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, + (float)x2,dimy()-(float)y2,l2, + (float)x3,dimy()-(float)y3,l3); + } +#endif + } break; + } + } break; + case 9 : { // Textured triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + tx0 = (unsigned int)primitive[3], + ty0 = (unsigned int)primitive[4], + tx1 = (unsigned int)primitive[5], + ty1 = (unsigned int)primitive[6], + tx2 = (unsigned int)primitive[7], + ty2 = (unsigned int)primitive[8]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale, + z2 = points(n2,2) + Z + focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). + draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + board.drawCircle((float)x2,dimy()-(float)y2,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). + draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). + draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); + board.drawLine((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2); + board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); +#ifdef cimg_use_board + if (pboard) { + const float lp = cimg::min(lightprops(l),1); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprops(n0), + (float)x1,dimy()-(float)y1,lightprops(n1), + (float)x2,dimy()-(float)y2,lightprops(n2)); + } +#endif + break; + case 5 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), + opac); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), + opac); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n0,0))), (int)(light_texture.dimy()/2*(1+lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n1,0))), (int)(light_texture.dimy()/2*(1+lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.dimx()/2*(1+lightprops(n2,0))), (int)(light_texture.dimy()/2*(1+lightprops(n2,1)))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0,(float)x1,dimy()-(float)y1,l1,(float)x2,dimy()-(float)y2,l2); + } +#endif + break; + } + } break; + case 12 : { // Textured rectangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3], + tx0 = (unsigned int)primitive[4], + ty0 = (unsigned int)primitive[5], + tx1 = (unsigned int)primitive[6], + ty1 = (unsigned int)primitive[7], + tx2 = (unsigned int)primitive[8], + ty2 = (unsigned int)primitive[9], + tx3 = (unsigned int)primitive[10], + ty3 = (unsigned int)primitive[11]; + const int + x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), + x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), + x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), + x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); + const float + z0 = points(n0,2) + Z + focale, + z1 = points(n1,2) + Z + focale, + z2 = points(n2,2) + Z + focale, + z3 = points(n3,2) + Z + focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). + draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). + draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac). + draw_point(x3,y3,color.get_vector_at(tx3,ty3),opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawCircle((float)x0,dimy()-(float)y0,0); + board.drawCircle((float)x1,dimy()-(float)y1,0); + board.drawCircle((float)x2,dimy()-(float)y2,0); + board.drawCircle((float)x3,dimy()-(float)y3,0); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). + draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). + draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). + draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.drawLine((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1); + board.drawLine((float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.drawLine((float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + board.drawLine((float)x3,dimy()-(float)y3,(float)x0,dimy()-(float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); +#ifdef cimg_use_board + if (pboard) { + const float lp = cimg::min(lightprops(l),1); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opac*255)); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x1,dimy()-(float)y1,(float)x2,dimy()-(float)y2); + board.fillTriangle((float)x0,dimy()-(float)y0,(float)x2,dimy()-(float)y2,(float)x3,dimy()-(float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, + (float)x1,dimy()-(float)y1,lightprop1, + (float)x2,dimy()-(float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,lightprop0, + (float)x2,dimy()-(float)y2,lightprop2, + (float)x3,dimy()-(float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), + lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), + lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), + lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.dimx()/2*(1+lx0)), (int)(light_texture.dimy()/2*(1+ly0))), + l1 = light_texture((int)(light_texture.dimx()/2*(1+lx1)), (int)(light_texture.dimy()/2*(1+ly1))), + l2 = light_texture((int)(light_texture.dimx()/2*(1+lx2)), (int)(light_texture.dimy()/2*(1+ly2))), + l3 = light_texture((int)(light_texture.dimx()/2*(1+lx3)), (int)(light_texture.dimy()/2*(1+ly3))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255)); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, + (float)x1,dimy()-(float)y1,l1, + (float)x2,dimy()-(float)y2,l2); + board.fillGouraudTriangle((float)x0,dimy()-(float)y0,l0, + (float)x2,dimy()-(float)y2,l2, + (float)x3,dimy()-(float)y3,l3); + } +#endif + } break; + } + } break; + } + } + } + return *this; + } + + //! Draw a 3D object. + /** + \param X = X-coordinate of the 3d object position + \param Y = Y-coordinate of the 3d object position + \param Z = Z-coordinate of the 3d object position + \param points = Image N*3 describing 3D point coordinates + \param primitives = List of P primitives + \param colors = List of P color (or textures) + \param opacities = Image of P opacities + \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param double_sided = Tell if object faces have two sides or are oriented. + \param focale = length of the focale + \param lightx = X-coordinate of the light + \param lighty = Y-coordinate of the light + \param lightz = Z-coordinate of the light + \param specular_shine = Shininess of the object + **/ + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d(0,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, + primitives,colors,opacities,opacities.size, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(BoardLib::Board& board, + const float x0, const float y0, const float z0, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, + primitives,colors,opacities,opacities.size, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } +#endif + + //! Draw a 3D object. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d(0,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(BoardLib::Board& board, + const float x0, const float y0, const float z0, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } +#endif + + //! Draw a 3D object. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d(0,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width, + primitives,colors,opacities,opacities.size(), + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(BoardLib::Board& board, + const float x0, const float y0, const float z0, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points.height<3?points:points.get_resize(-100,3,1,1,0),points.width + ,primitives,colors,opacities,opacities.size(), + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } +#endif + + //! Draw a 3D object. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d(0,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size(), + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(BoardLib::Board& board, + const float x0, const float y0, const float z0, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + if (!points) return *this; + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,points,points.size,primitives,colors,opacities,opacities.size(), + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine); + } +#endif + + //! Draw a 3D object. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const tp& points, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + static const CImg opacities; + return draw_object3d(x0,y0,z0,points,primitives,colors,opacities, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(BoardLib::Board& board, + const float x0, const float y0, const float z0, + const tp& points, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool double_sided=false, const float focale=500, + const float lightx=0, const float lighty=0, const float lightz=-5000, + const float specular_light=0.2f, const float specular_shine=0.1f, + float *const zbuffer=0) { + static const CImg opacities; + return draw_object3d(x0,y0,z0,points,primitives,colors,opacities, + render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer); + } +#endif + + //@} + //---------------------------- + // + //! \name Image Filtering + //@{ + //---------------------------- + + //! Compute the correlation of the instance image by a mask. + /** + The correlation of the instance image \p *this by the mask \p mask is defined to be : + + res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k) + + \param mask = the correlation kernel. + \param cond = the border condition type (0=zero, 1=dirichlet) + \param weighted_correl = enable local normalization. + **/ + template + CImg& correlate(const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) { + return get_correlate(mask,cond,weighted_correl).transfer_to(*this); + } + + template + CImg::type> get_correlate(const CImg& mask, const unsigned int cond=1, + const bool weighted_correl=false) const { + typedef typename cimg::superset2::type Ttfloat; + if (is_empty()) return *this; + if (!mask || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::correlate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + if (cond && mask.width==mask.height && ((mask.depth==1 && mask.width<=5) || (mask.depth==mask.width && mask.width<=3))) { + // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with cond=1) + switch (mask.depth) { + case 3 : { + T I[27] = { 0 }; + cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) { + const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + + I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 2 : { + T I[8] = { 0 }; + cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[0]*mask[0] + I[1]*mask[1] + + I[2]*mask[2] + I[3]*mask[3] + + I[4]*mask[4] + I[5]*mask[5] + + I[6]*mask[6] + I[7]*mask[7]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) { + const double weight = (double)(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3] + + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + default : + case 1 : + switch (mask.width) { + case 6 : { + T I[36] = { 0 }; + cimg_forZV(*this,z,v) cimg_for6x6(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26] + I[27]*mask[27] + I[28]*mask[28] + I[29]*mask[29] + + I[30]*mask[30] + I[31]*mask[31] + I[32]*mask[32] + I[33]*mask[33] + I[34]*mask[34] + I[35]*mask[35]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { + const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + + I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] + + I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 5 : { + T I[25] = { 0 }; + cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { + const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 4 : { + T I[16] = { 0 }; + cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) { + const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 3 : { + T I[9] = { 0 }; + cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) { + const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + + I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + + I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 2 : { + T I[4] = { 0 }; + cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (Ttfloat) + (I[0]*mask[0] + I[1]*mask[1] + + I[2]*mask[2] + I[3]*mask[3]); + if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) { + const double weight = (double)(I[0]*I[0] + I[1]*I[1] + + I[2]*I[2] + I[3]*I[3]); + if (weight>0) dest(x,y,z,v)/=(Ttfloat)cimg_std::sqrt(weight); + } + } break; + case 1 : (dest.assign(*this))*=mask(0); break; + } + } + } else { // Generic version for other masks + const int + mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, + mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), + mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; + cimg_forV(*this,v) + if (!weighted_correl) { // Classical correlation + for (int z = mz1; z=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0; + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) + val+=_atXYZ(x+xm,y+ym,z+zm,v)*mask(mx1+xm,my1+ym,mz1+zm); + dest(x,y,z,v) = (Ttfloat)val; + } + else + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0; + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) + val+=atXYZ(x+xm,y+ym,z+zm,v,0)*mask(mx1+xm,my1+ym,mz1+zm); + dest(x,y,z,v) = (Ttfloat)val; + } + } else { // Weighted correlation + for (int z = mz1; z(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; + } + if (cond) + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0, weight = 0; + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const Ttfloat cval = (Ttfloat)_atXYZ(x+xm,y+ym,z+zm,v); + val+=cval*mask(mx1+xm,my1+ym,mz1+zm); + weight+=cval*cval; + } + dest(x,y,z,v) = (weight>(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; + } + else + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Ttfloat val = 0, weight = 0; + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const Ttfloat cval = (Ttfloat)atXYZ(x+xm,y+ym,z+zm,v,0); + val+=cval*mask(mx1+xm,my1+ym,mz1+zm); + weight+=cval*cval; + } + dest(x,y,z,v) = (weight>(Ttfloat)0)?(Ttfloat)(val/cimg_std::sqrt((double)weight)):(Ttfloat)0; + } + } + } + return dest; + } + + //! Compute the convolution of the image by a mask. + /** + The result \p res of the convolution of an image \p img by a mask \p mask is defined to be : + + res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k) + + \param mask = the correlation kernel. + \param cond = the border condition type (0=zero, 1=dirichlet) + \param weighted_convol = enable local normalization. + **/ + template + CImg& convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) { + return get_convolve(mask,cond,weighted_convol).transfer_to(*this); + } + + template + CImg::type> get_convolve(const CImg& mask, const unsigned int cond=1, + const bool weighted_convol=false) const { + typedef typename cimg::superset2::type Ttfloat; + if (is_empty()) return *this; + if (!mask || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::convolve() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + return get_correlate(CImg(mask.ptr(),mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),cond,weighted_convol); + } + + //! Return the erosion of the image by a structuring element. + template + CImg& erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) { + return get_erode(mask,cond,weighted_erosion).transfer_to(*this); + } + + template + CImg::type> get_erode(const CImg& mask, const unsigned int cond=1, + const bool weighted_erosion=false) const { + typedef typename cimg::superset::type Tt; + if (is_empty()) return *this; + if (!mask || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::erode() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + const int + mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, + mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), + mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; + cimg_forV(*this,v) + if (!weighted_erosion) { // Classical erosion + for (int z = mz1; z::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,v); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,v); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,v,0); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,v) + mval); + if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,v) + mval); + if (mval && cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,v,0) + mval); + if (mval && cval& erode(const unsigned int n, const unsigned int cond=1) { + if (n<2) return *this; + return get_erode(n,cond).transfer_to(*this); + } + + CImg get_erode(const unsigned int n, const unsigned int cond=1) const { + static CImg mask; + if (n<2) return *this; + if (mask.width!=n) mask.assign(n,n,1,1,1); + const CImg res = get_erode(mask,cond,false); + if (n>20) mask.assign(); + return res; + } + + //! Dilate the image by a structuring element. + template + CImg& dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) { + return get_dilate(mask,cond,weighted_dilatation).transfer_to(*this); + } + + template + CImg::type> get_dilate(const CImg& mask, const unsigned int cond=1, + const bool weighted_dilatation=false) const { + typedef typename cimg::superset::type Tt; + if (is_empty()) return *this; + if (!mask || mask.dim!=1) + throw CImgArgumentException("CImg<%s>::dilate() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); + CImg dest(width,height,depth,dim); + const int + mx2 = mask.dimx()/2, my2 = mask.dimy()/2, mz2 = mask.dimz()/2, + mx1 = mx2 - 1 + (mask.dimx()%2), my1 = my2 - 1 + (mask.dimy()%2), mz1 = mz2 - 1 + (mask.dimz()%2), + mxe = dimx() - mx2, mye = dimy() - my2, mze = dimz() - mz2; + cimg_forV(*this,v) + if (!weighted_dilatation) { // Classical dilatation + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,v); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + if (cond) + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,v); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + else + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,v,0); + if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + } else { // Weighted dilatation + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,v) - mval); + if (mval && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + if (cond) + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,v) - mval); + if (mval && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + else + cimg_forYZV(*this,y,z,v) + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = mask(mx1+xm,my1+ym,mz1+zm); + const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,v,0) - mval); + if (mval && cval>max_val) max_val = cval; + } + dest(x,y,z,v) = max_val; + } + } + return dest; + } + + //! Dilate the image by a square structuring element of size n. + CImg& dilate(const unsigned int n, const unsigned int cond=1) { + if (n<2) return *this; + return get_dilate(n,cond).transfer_to(*this); + } + + CImg get_dilate(const unsigned int n, const unsigned int cond=1) const { + static CImg mask; + if (n<2) return *this; + if (mask.width!=n) mask.assign(n,n,1,1,1); + const CImg res = get_dilate(mask,cond,false); + if (n>20) mask.assign(); + return res; + } + + //! Add noise to the image. + /** + \param sigma = power of the noise. if sigma<0, it corresponds to the percentage of the maximum image value. + \param ntype = noise type. can be 0=gaussian, 1=uniform or 2=Salt and Pepper, 3=Poisson, 4=Rician. + \return A noisy version of the instance image. + **/ + CImg& noise(const double sigma, const unsigned int noise_type=0) { + if (!is_empty()) { + double nsigma = sigma, max = (double)cimg::type::max(), min = (double)cimg::type::min(); + Tfloat m = 0, M = 0; + if (nsigma==0 && noise_type!=3) return *this; + if (nsigma<0 || noise_type==2) m = (Tfloat)minmax(M); + if (nsigma<0) nsigma = -nsigma*(M-m)/100.0; + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_for(*this,ptr,T) { + double val = *ptr + nsigma*cimg::grand(); + if (val>max) val = max; + if (valmax) val = max; + if (val::is_float()?1:cimg::type::max()); } + cimg_for(*this,ptr,T) if (cimg::rand()*100max) val = max; + if (val::noise() : Invalid noise type %d " + "(should be {0=Gaussian, 1=Uniform, 2=Salt&Pepper, 3=Poisson}).",pixel_type(),noise_type); + } + } + return *this; + } + + CImg get_noise(const double sigma, const unsigned int noise_type=0) const { + return (+*this).noise(sigma,noise_type); + } + + //! Compute the result of the Deriche filter. + /** + The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of + order 0,1 or 2 of an image. + **/ + CImg& deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) { +#define _cimg_deriche2_apply \ + Tfloat *ptrY = Y.data, yb = 0, yp = 0; \ + T xp = (T)0; \ + if (cond) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \ + for (int m=0; m=0; --n) { \ + const T xc = *(ptrX-=off); \ + const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \ + xa = xn; xn = xc; ya = yn; yn = yc; \ + *ptrX = (T)(*(--ptrY)+yc); \ + } + if (sigma<0) + throw CImgArgumentException("CImg<%s>::deriche() : Given filter variance (sigma = %g) is negative", + pixel_type(),sigma); + if (is_empty() || (sigma<0.1 && !order)) return *this; + const float + nsigma = sigma<0.1f?0.1f:sigma, + alpha = 1.695f/nsigma, + ema = (float)cimg_std::exp(-alpha), + ema2 = (float)cimg_std::exp(-2*alpha), + b1 = -2*ema, + b2 = ema2; + float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; + switch (order) { + case 0 : { + const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2); + a0 = k; + a1 = k*(alpha-1)*ema; + a2 = k*(alpha+1)*ema; + a3 = -k*ema2; + } break; + case 1 : { + const float k = (1-ema)*(1-ema)/ema; + a0 = k*ema; + a1 = a3 = 0; + a2 = -a0; + } break; + case 2 : { + const float + ea = (float)cimg_std::exp(-alpha), + k = -(ema2-1)/(2*alpha*ema), + kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea)); + a0 = kn; + a1 = -kn*(1+k*alpha)*ema; + a2 = kn*(1-k*alpha)*ema; + a3 = -kn*ema2; + } break; + default : + throw CImgArgumentException("CImg<%s>::deriche() : Given filter order (order = %u) must be 0,1 or 2", + pixel_type(),order); + } + coefp = (a0+a1)/(1+b1+b2); + coefn = (a2+a3)/(1+b1+b2); + switch (cimg::uncase(axis)) { + case 'x' : { + const int N = width, off = 1; + CImg Y(N); + cimg_forYZV(*this,y,z,v) { T *ptrX = ptr(0,y,z,v); _cimg_deriche2_apply; } + } break; + case 'y' : { + const int N = height, off = width; + CImg Y(N); + cimg_forXZV(*this,x,z,v) { T *ptrX = ptr(x,0,z,v); _cimg_deriche2_apply; } + } break; + case 'z' : { + const int N = depth, off = width*height; + CImg Y(N); + cimg_forXYV(*this,x,y,v) { T *ptrX = ptr(x,y,0,v); _cimg_deriche2_apply; } + } break; + case 'v' : { + const int N = dim, off = width*height*depth; + CImg Y(N); + cimg_forXYZ(*this,x,y,z) { T *ptrX = ptr(x,y,z,0); _cimg_deriche2_apply; } + } break; + } + return *this; + } + + CImg get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const { + return CImg(*this,false).deriche(sigma,order,axis,cond); + } + + //! Return a blurred version of the image, using a Canny-Deriche filter. + /** + Blur the image with an anisotropic exponential filter (Deriche filter of order 0). + **/ + CImg& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) { + if (!is_empty()) { + if (width>1 && sigmax>0) deriche(sigmax,0,'x',cond); + if (height>1 && sigmay>0) deriche(sigmay,0,'y',cond); + if (depth>1 && sigmaz>0) deriche(sigmaz,0,'z',cond); + } + return *this; + } + + CImg get_blur(const float sigmax, const float sigmay, const float sigmaz, + const bool cond=true) const { + return CImg(*this,false).blur(sigmax,sigmay,sigmaz,cond); + } + + //! Return a blurred version of the image, using a Canny-Deriche filter. + CImg& blur(const float sigma, const bool cond=true) { + return blur(sigma,sigma,sigma,cond); + } + + CImg get_blur(const float sigma, const bool cond=true) const { + return CImg(*this,false).blur(sigma,cond); + } + + //! Blur the image anisotropically following a field of diffusion tensors. + /** + \param G = Field of square roots of diffusion tensors used to drive the smoothing. + \param amplitude = amplitude of the smoothing. + \param dl = spatial discretization. + \param da = angular discretization. + \param gauss_prec = precision of the gaussian function. + \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) + \param fast_approx = Tell to use the fast approximation or not. + **/ + template + CImg& blur_anisotropic(const CImg& G, const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, const bool fast_approx=true) { +#define _cimg_valign2d(i,j) \ + { Tfloat &u = W(i,j,0,0), &v = W(i,j,0,1); \ + if (u*curru + v*currv<0) { u=-u; v=-v; }} +#define _cimg_valign3d(i,j,k) \ + { Tfloat &u = W(i,j,k,0), &v = W(i,j,k,1), &w = W(i,j,k,2); \ + if (u*curru + v*currv + w*currw<0) { u=-u; v=-v; w=-w; }} + + // Check arguments and init variables + if (!is_empty() && amplitude>0) { + if (!G || (G.dim!=3 && G.dim!=6) || G.width!=width || G.height!=height || G.depth!=depth) + throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Specified tensor field (%u,%u,%u,%u) is not valid.", + pixel_type(),G.width,G.height,G.depth,G.dim); + + const float sqrt2amplitude = (float)cimg_std::sqrt(2*amplitude); + const bool threed = (G.dim>=6); + const int + dx1 = dimx()-1, + dy1 = dimy()-1, + dz1 = dimz()-1; + CImg + dest(width,height,depth,dim,0), + W(width,height,depth,threed?4:3), + tmp(dim); + int N = 0; + + if (threed) + // 3D version of the algorithm + for (float phi=(180%(int)da)/2.0f; phi<=180; phi+=da) { + const float + phir = (float)(phi*cimg::valuePI/180), + datmp = (float)(da/cimg_std::cos(phir)), + da2 = datmp<1?360.0f:datmp; + + for (float theta=0; theta<360; (theta+=da2),++N) { + const float + thetar = (float)(theta*cimg::valuePI/180), + vx = (float)(cimg_std::cos(thetar)*cimg_std::cos(phir)), + vy = (float)(cimg_std::sin(thetar)*cimg_std::cos(phir)), + vz = (float)cimg_std::sin(phir); + const t + *pa = G.ptr(0,0,0,0), + *pb = G.ptr(0,0,0,1), + *pc = G.ptr(0,0,0,2), + *pd = G.ptr(0,0,0,3), + *pe = G.ptr(0,0,0,4), + *pf = G.ptr(0,0,0,5); + Tfloat + *pd0 = W.ptr(0,0,0,0), + *pd1 = W.ptr(0,0,0,1), + *pd2 = W.ptr(0,0,0,2), + *pd3 = W.ptr(0,0,0,3); + cimg_forXYZ(G,xg,yg,zg) { + const t + a = *(pa++), b = *(pb++), c = *(pc++), + d = *(pd++), e = *(pe++), f = *(pf++); + const float + u = (float)(a*vx + b*vy + c*vz), + v = (float)(b*vx + d*vy + e*vz), + w = (float)(c*vx + e*vy + f*vz), + n = (float)cimg_std::sqrt(1e-5+u*u+v*v+w*w), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)(w*dln); + *(pd3++) = (Tfloat)n; + } + + cimg_forXYZ(*this,x,y,z) { + tmp.fill(0); + const float + cu = (float)W(x,y,z,0), + cv = (float)W(x,y,z,1), + cw = (float)W(x,y,z,2), + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + length = gauss_prec*fsigma, + fsigma2 = 2*fsigma*fsigma; + float + S = 0, + pu = cu, + pv = cv, + pw = cw, + X = (float)x, + Y = (float)y, + Z = (float)z; + + switch (interpolation_type) { + case 0 : { + // Nearest neighbor + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f), + cz = (int)(Z+0.5f); + float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)(*this)(cx,cy,cz,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*(*this)(cx,cy,cz,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } + } break; + + case 1 : { + // Linear interpolation + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, + cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; + const float + curru = (float)W(cx,cy,cz,0), + currv = (float)W(cx,cy,cz,1), + currw = (float)W(cx,cy,cz,2); + _cimg_valign3d(px,py,pz); _cimg_valign3d(cx,py,pz); _cimg_valign3d(nx,py,pz); + _cimg_valign3d(px,cy,pz); _cimg_valign3d(cx,cy,pz); _cimg_valign3d(nx,cy,pz); + _cimg_valign3d(px,ny,pz); _cimg_valign3d(cx,ny,pz); _cimg_valign3d(nx,ny,pz); + _cimg_valign3d(px,py,cz); _cimg_valign3d(cx,py,cz); _cimg_valign3d(nx,py,cz); + _cimg_valign3d(px,cy,cz); _cimg_valign3d(nx,cy,cz); + _cimg_valign3d(px,ny,cz); _cimg_valign3d(cx,ny,cz); _cimg_valign3d(nx,ny,cz); + _cimg_valign3d(px,py,nz); _cimg_valign3d(cx,py,nz); _cimg_valign3d(nx,py,nz); + _cimg_valign3d(px,cy,nz); _cimg_valign3d(cx,cy,nz); _cimg_valign3d(nx,cy,nz); + _cimg_valign3d(px,ny,nz); _cimg_valign3d(cx,ny,nz); _cimg_valign3d(nx,ny,nz); + float + u = (float)(W._linear_atXYZ(X,Y,Z,0)), + v = (float)(W._linear_atXYZ(X,Y,Z,1)), + w = (float)(W._linear_atXYZ(X,Y,Z,2)); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXYZ(X,Y,Z,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } + } break; + + default : { + // 2nd order Runge Kutta + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, + cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; + const float + curru = (float)W(cx,cy,cz,0), + currv = (float)W(cx,cy,cz,1), + currw = (float)W(cx,cy,cz,2); + _cimg_valign3d(px,py,pz); _cimg_valign3d(cx,py,pz); _cimg_valign3d(nx,py,pz); + _cimg_valign3d(px,cy,pz); _cimg_valign3d(cx,cy,pz); _cimg_valign3d(nx,cy,pz); + _cimg_valign3d(px,ny,pz); _cimg_valign3d(cx,ny,pz); _cimg_valign3d(nx,ny,pz); + _cimg_valign3d(px,py,cz); _cimg_valign3d(cx,py,cz); _cimg_valign3d(nx,py,cz); + _cimg_valign3d(px,cy,cz); _cimg_valign3d(nx,cy,cz); + _cimg_valign3d(px,ny,cz); _cimg_valign3d(cx,ny,cz); _cimg_valign3d(nx,ny,cz); + _cimg_valign3d(px,py,nz); _cimg_valign3d(cx,py,nz); _cimg_valign3d(nx,py,nz); + _cimg_valign3d(px,cy,nz); _cimg_valign3d(cx,cy,nz); _cimg_valign3d(nx,cy,nz); + _cimg_valign3d(px,ny,nz); _cimg_valign3d(cx,ny,nz); _cimg_valign3d(nx,ny,nz); + const float + u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), + v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), + w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)); + float + u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)), + v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)), + w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2)); + if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXYZ(X,Y,Z,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); Z+=(pw=w); + } + } break; + } + if (S>0) cimg_forV(dest,k) dest(x,y,z,k)+=tmp[k]/S; + else cimg_forV(dest,k) dest(x,y,z,k)+=(Tfloat)((*this)(x,y,z,k)); + cimg_plugin_greycstoration_count; + } + } + } else + // 2D version of the algorithm + for (float theta=(360%(int)da)/2.0f; theta<360; (theta+=da),++N) { + const float + thetar = (float)(theta*cimg::valuePI/180), + vx = (float)(cimg_std::cos(thetar)), + vy = (float)(cimg_std::sin(thetar)); + const t + *pa = G.ptr(0,0,0,0), + *pb = G.ptr(0,0,0,1), + *pc = G.ptr(0,0,0,2); + Tfloat + *pd0 = W.ptr(0,0,0,0), + *pd1 = W.ptr(0,0,0,1), + *pd2 = W.ptr(0,0,0,2); + cimg_forXY(G,xg,yg) { + const t a = *(pa++), b = *(pb++), c = *(pc++); + const float + u = (float)(a*vx + b*vy), + v = (float)(b*vx + c*vy), + n = (float)cimg_std::sqrt(1e-5+u*u+v*v), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)n; + } + + cimg_forXY(*this,x,y) { + tmp.fill(0); + const float + cu = (float)W(x,y,0,0), + cv = (float)W(x,y,0,1), + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + length = gauss_prec*fsigma, + fsigma2 = 2*fsigma*fsigma; + float + S = 0, + pu = cu, + pv = cv, + X = (float)x, + Y = (float)y; + + switch (interpolation_type) { + + case 0 : { + // Nearest-neighbor interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X+0.5f), + cy = (int)(Y+0.5f); + float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)(*this)(cx,cy,0,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*(*this)(cx,cy,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } + } break; + + case 1 : { + // Linear interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; + const float + curru = (float)W(cx,cy,0,0), + currv = (float)W(cx,cy,0,1); + _cimg_valign2d(px,py); _cimg_valign2d(cx,py); _cimg_valign2d(nx,py); + _cimg_valign2d(px,cy); _cimg_valign2d(nx,cy); + _cimg_valign2d(px,ny); _cimg_valign2d(cx,ny); _cimg_valign2d(nx,ny); + float + u = (float)(W._linear_atXY(X,Y,0,0)), + v = (float)(W._linear_atXY(X,Y,0,1)); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXY(X,Y,0,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXY(X,Y,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } + } break; + + default : { + // 2nd-order Runge-kutta interpolation for 2D images + for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, + cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; + const float + curru = (float)W(cx,cy,0,0), + currv = (float)W(cx,cy,0,1); + _cimg_valign2d(px,py); _cimg_valign2d(cx,py); _cimg_valign2d(nx,py); + _cimg_valign2d(px,cy); _cimg_valign2d(nx,cy); + _cimg_valign2d(px,ny); _cimg_valign2d(cx,ny); _cimg_valign2d(nx,ny); + const float + u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), + v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)); + float + u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)), + v = (float)(W._linear_atXY(X+u0,Y+v0,0,1)); + if ((pu*u + pv*v)<0) { u=-u; v=-v; } + if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(Tfloat)_linear_atXY(X,Y,0,k); ++S; } + else { + const float coef = (float)cimg_std::exp(-l*l/fsigma2); + cimg_forV(*this,k) tmp[k]+=(Tfloat)(coef*_linear_atXY(X,Y,0,k)); + S+=coef; + } + X+=(pu=u); Y+=(pv=v); + } + } + } + if (S>0) cimg_forV(dest,k) dest(x,y,0,k)+=tmp[k]/S; + else cimg_forV(dest,k) dest(x,y,0,k)+=(Tfloat)((*this)(x,y,0,k)); + cimg_plugin_greycstoration_count; + } + } + const Tfloat *ptrs = dest.data+dest.size(); + const T m = cimg::type::min(), M = cimg::type::max(); + cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = valM?M:(T)val); } + } + return *this; + } + + template + CImg get_blur_anisotropic(const CImg& G, const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, const bool fast_approx=true) const { + return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); + } + + //! Blur an image in an anisotropic way. + /** + \param mask Binary mask. + \param amplitude Amplitude of the anisotropic blur. + \param sharpness Contour preservation. + \param anisotropy Smoothing anisotropy. + \param alpha Image pre-blurring (gaussian). + \param sigma Regularity of the tensor-valued geometry. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the gaussian function. + \param interpolation_type Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) + \param fast_approx Tell to use the fast approximation or not + \param geom_factor Geometry factor. + **/ + template + CImg& blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, const bool fast_approx=true, + const float geom_factor=1) { + if (!is_empty() && amplitude>0) { + if (amplitude==0) return *this; + if (amplitude<0 || sharpness<0 || anisotropy<0 || anisotropy>1 || alpha<0 || sigma<0 || dl<0 || da<0 || gauss_prec<0) + throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Given parameters are amplitude(%g), sharpness(%g), " + "anisotropy(%g), alpha(%g), sigma(%g), dl(%g), da(%g), gauss_prec(%g).\n" + "Admissible parameters are in the range : amplitude>0, sharpness>0, anisotropy in [0,1], " + "alpha>0, sigma>0, dl>0, da>0, gauss_prec>0.", + pixel_type(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec); + const bool threed = (depth>1), no_mask = mask.is_empty(); + const float nsharpness = cimg::max(sharpness,1e-5f), power1 = 0.5f*nsharpness, power2 = power1/(1e-7f+1-anisotropy); + CImg blurred = CImg(*this,false).blur(alpha); + if (geom_factor>0) blurred*=geom_factor; + else blurred.normalize(0,-geom_factor); + + if (threed) { // Field for 3D volumes + cimg_plugin_greycstoration_lock; + CImg val(3), vec(3,3), G(blurred.get_structure_tensor()); + if (sigma>0) G.blur(sigma); + cimg_forXYZ(*this,x,y,z) { + if (no_mask || mask(x,y,z)) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float l1 = val[2], l2 = val[1], l3 = val[0], + ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), + vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), + wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), + n1 = (float)cimg_std::pow(1+l1+l2+l3,-power1), + n2 = (float)cimg_std::pow(1+l1+l2+l3,-power2); + G(x,y,z,0) = n1*(ux*ux + vx*vx) + n2*wx*wx; + G(x,y,z,1) = n1*(ux*uy + vx*vy) + n2*wx*wy; + G(x,y,z,2) = n1*(ux*uz + vx*vz) + n2*wx*wz; + G(x,y,z,3) = n1*(uy*uy + vy*vy) + n2*wy*wy; + G(x,y,z,4) = n1*(uy*uz + vy*vz) + n2*wy*wz; + G(x,y,z,5) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } else G(x,y,z,0) = G(x,y,z,1) = G(x,y,z,2) = G(x,y,z,3) = G(x,y,z,4) = G(x,y,z,5) = 0; + cimg_plugin_greycstoration_count; + } + cimg_plugin_greycstoration_unlock; + blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); + } else { // Field for 2D images + cimg_plugin_greycstoration_lock; + CImg val(2), vec(2,2), G(blurred.get_structure_tensor()); + if (sigma>0) G.blur(sigma); + cimg_forXY(*this,x,y) { + if (no_mask || mask(x,y)) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float l1 = val[1], l2 = val[0], + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)cimg_std::pow(1+l1+l2,-power1), + n2 = (float)cimg_std::pow(1+l1+l2,-power2); + G(x,y,0,0) = n1*ux*ux + n2*vx*vx; + G(x,y,0,1) = n1*ux*uy + n2*vx*vy; + G(x,y,0,2) = n1*uy*uy + n2*vy*vy; + } else G(x,y,0,0) = G(x,y,0,1) = G(x,y,0,2) = 0; + cimg_plugin_greycstoration_count; + } + cimg_plugin_greycstoration_unlock; + blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx); + } + } + return *this; + } + + template + CImg get_blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool fast_approx=true, const float geom_factor=1) const { + return (+*this).blur_anisotropic(mask,amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); + } + + //! Blur an image following in an anisotropic way. + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, const bool fast_approx=true, + const float geom_factor=1) { + return blur_anisotropic(CImg(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); + } + + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool fast_approx=true, const float geom_factor=1) const { + return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx,geom_factor); + } + + //! Blur an image using the bilateral filter. + /** + \param sigmax Amount of blur along the X-axis. + \param sigmay Amount of blur along the Y-axis. + \param sigmaz Amount of blur along the Z-axis. + \param sigmar Amount of blur along the range axis. + \param bgridx Size of the bilateral grid along the X-axis. + \param bgridy Size of the bilateral grid along the Y-axis. + \param bgridz Size of the bilateral grid along the Z-axis. + \param bgridr Size of the bilateral grid along the range axis. + \param interpolation_type Use interpolation for image slicing. + \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 + (extended for 3D volumetric images). + **/ + CImg& blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, + const int bgridx, const int bgridy, const int bgridz, const int bgridr, + const bool interpolation_type=true) { + T m, M = maxmin(m); + const float range = (float)(1.0f+M-m); + const unsigned int + bx0 = bgridx>=0?bgridx:width*(-bgridx)/100, + by0 = bgridy>=0?bgridy:height*(-bgridy)/100, + bz0 = bgridz>=0?bgridz:depth*(-bgridz)/100, + br0 = bgridr>=0?bgridr:(int)(-range*bgridr/100), + bx = bx0>0?bx0:1, + by = by0>0?by0:1, + bz = bz0>0?bz0:1, + br = br0>0?br0:1; + const float + nsigmax = sigmax*bx/width, + nsigmay = sigmay*by/height, + nsigmaz = sigmaz*bz/depth, + nsigmar = sigmar*br/range; + if (nsigmax>0 || nsigmay>0 || nsigmaz>0 || nsigmar>0) { + const bool threed = depth>1; + if (threed) { // 3d version of the algorithm + CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); + cimg_forV(*this,k) { + bgrid.fill(0); bgridw.fill(0); + cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,k); + const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); + bgrid(X,Y,Z,R) = (float)val; + bgridw(X,Y,Z,R) = 1; + } + bgrid.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); + bgridw.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); + if (interpolation_type) cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,k); + const float X = (float)x*bx/width, Y = (float)y*by/height, Z = (float)z*bz/depth, R = (float)((val-m)*br/range), + bval0 = bgrid._linear_atXYZV(X,Y,Z,R), bval1 = bgridw._linear_atXYZV(X,Y,Z,R); + (*this)(x,y,z,k) = (T)(bval0/bval1); + } else cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,k); + const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); + const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R); + (*this)(x,y,z,k) = (T)(bval0/bval1); + } + } + } else { // 2d version of the algorithm + CImg bgrid(bx,by,br,2); + cimg_forV(*this,k) { + bgrid.fill(0); + cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,k); + const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); + bgrid(X,Y,R,0) = (float)val; + bgrid(X,Y,R,1) = 1; + } + bgrid.blur(nsigmax,nsigmay,0,true).blur(0,0,nsigmar,false); + if (interpolation_type) cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,k); + const float X = (float)x*bx/width, Y = (float)y*by/height, R = (float)((val-m)*br/range), + bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); + (*this)(x,y,k) = (T)(bval0/bval1); + } else cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,k); + const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); + const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1); + (*this)(x,y,k) = (T)(bval0/bval1); + } + } + } + } + return *this; + } + + CImg get_blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, + const int bgridx, const int bgridy, const int bgridz, const int bgridr, + const bool interpolation_type=true) const { + return (+*this).blur_bilateral(sigmax,sigmay,sigmaz,sigmar,bgridx,bgridy,bgridz,bgridr,interpolation_type); + } + + //! Blur an image using the bilateral filter. + CImg& blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, + const bool interpolation_type=true) { + return blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation_type); + } + + CImg get_blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, + const bool interpolation_type=true) const { + return (+*this).blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation_type); + } + + //! Blur an image in its patch-based space. + CImg& blur_patch(const unsigned int patch_size, const float sigma_p, const float sigma_s=10, + const unsigned int lookup_size=4, const bool fast_approx=true) { + +#define _cimg_blur_patch_fastfunc(x) ((x)>3?0:1) +#define _cimg_blur_patch_slowfunc(x) cimg_std::exp(-(x)) +#define _cimg_blur_patch3d(N,func) { \ + const unsigned int N3 = N*N*N; \ + cimg_for##N##XYZ(*this,x,y,z) { \ + cimg_plugin_greycstoration_count; \ + cimg_forV(*this,k) cimg_get##N##x##N##x##N(*this,x,y,z,k,P.ptr(N3*k)); \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + float sum_weights = 0; \ + cimg_for_in##N##XYZ(*this,x0,y0,z0,x1,y1,z1,p,q,r) { \ + cimg_forV(*this,k) cimg_get##N##x##N##x##N(*this,p,q,r,k,Q.ptr(N3*k)); \ + float distance2 = 0; \ + const T *pQ = Q.end(); \ + cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \ + alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)func(alldist); \ + sum_weights+=weight; \ + { cimg_forV(*this,k) res(x,y,z,k)+=weight*(*this)(p,q,r,k); } \ + } \ + if (sum_weights>0) cimg_forV(*this,k) res(x,y,z,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,z,k) = (Tfloat)((*this)(x,y,z,k)); \ + }} +#define _cimg_blur_patch2d(N,func) { \ + const unsigned int N2 = N*N; \ + cimg_for##N##XY(*this,x,y) { \ + cimg_plugin_greycstoration_count; \ + cimg_forV(*this,k) cimg_get##N##x##N(*this,x,y,0,k,P.ptr(N2*k)); \ + const int x0 = x-rsize1, y0 = y-rsize1, x1 = x+rsize2, y1 = y+rsize2; \ + float sum_weights = 0; \ + cimg_for_in##N##XY(*this,x0,y0,x1,y1,p,q) { \ + cimg_forV(*this,k) cimg_get##N##x##N(*this,p,q,0,k,Q.ptr(N2*k)); \ + float distance2 = 0; \ + const T *pQ = Q.end(); \ + cimg_for(P,pP,T) { const float dI = (float)*pP-(float)*(--pQ); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const float dx = (float)p-x, dy = (float)q-y, \ + alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)func(alldist); \ + sum_weights+=weight; \ + { cimg_forV(*this,k) res(x,y,k)+=weight*(*this)(p,q,k); } \ + } \ + if (sum_weights>0) cimg_forV(*this,k) res(x,y,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,k) = (Tfloat)((*this)(x,y,k)); \ + }} + + CImg res(width,height,depth,dim,0); + CImg P(patch_size*patch_size*dim), Q(P); + const float sigma_s2 = sigma_s*sigma_s, sigma_p2 = sigma_p*sigma_p, Pnorm = P.size()*sigma_p2; + const int rsize2 = (int)lookup_size/2, rsize1 = rsize2-1+(lookup_size%2); + if (depth>1) switch (patch_size) { // 3D version + case 2 : + if (fast_approx) { _cimg_blur_patch3d(2,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch3d(2,_cimg_blur_patch_slowfunc); } + break; + case 3 : + if (fast_approx) { _cimg_blur_patch3d(3,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch3d(3,_cimg_blur_patch_slowfunc); } + break; + default : { + const int psize1 = (int)patch_size/2, psize0 = psize1-1+(patch_size%2); + cimg_forXYZ(*this,x,y,z) { + cimg_plugin_greycstoration_count; + P = get_crop(x - psize0,y - psize0,z - psize0,x + psize1,y + psize1,z + psize1,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + float sum_weights = 0; + cimg_for_inXYZ(*this,x0,y0,z0,x1,y1,z1,p,q,r) { + (Q = get_crop(p - psize0,q - psize0,r - psize0,p + psize1,q + psize1,r + psize1,true))-=P; + const float + dx = (float)x - p, dy = (float)y - q, dz = (float)z - r, + distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), + weight = (float)cimg_std::exp(-distance2); + sum_weights+=weight; + cimg_forV(*this,k) res(x,y,z,k)+=weight*(*this)(p,q,r,k); + } + if (sum_weights>0) cimg_forV(*this,k) res(x,y,z,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,z,k) = (Tfloat)((*this)(x,y,z,k)); + } + } + } else switch (patch_size) { // 2D version + case 2 : + if (fast_approx) { _cimg_blur_patch2d(2,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(2,_cimg_blur_patch_slowfunc); } + break; + case 3 : + if (fast_approx) { _cimg_blur_patch2d(3,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(3,_cimg_blur_patch_slowfunc); } + break; + case 4 : + if (fast_approx) { _cimg_blur_patch2d(4,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(4,_cimg_blur_patch_slowfunc); } + break; + case 5 : + if (fast_approx) { _cimg_blur_patch2d(5,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(5,_cimg_blur_patch_slowfunc); } + break; + case 6 : + if (fast_approx) { _cimg_blur_patch2d(6,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(6,_cimg_blur_patch_slowfunc); } + break; + case 7 : + if (fast_approx) { _cimg_blur_patch2d(7,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(7,_cimg_blur_patch_slowfunc); } + break; + case 8 : + if (fast_approx) { _cimg_blur_patch2d(8,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(8,_cimg_blur_patch_slowfunc); } + break; + case 9 : + if (fast_approx) { _cimg_blur_patch2d(9,_cimg_blur_patch_fastfunc); } + else { _cimg_blur_patch2d(9,_cimg_blur_patch_slowfunc); } + break; + default : { + const int psize1 = (int)patch_size/2, psize0 = psize1-1+(patch_size%2); + cimg_forXY(*this,x,y) { + cimg_plugin_greycstoration_count; + P = get_crop(x - psize0,y - psize0,x + psize1,y + psize1,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + float sum_weights = 0; + cimg_for_inXY(*this,x0,y0,x1,y1,p,q) { + (Q = get_crop(p - psize0,q - psize0,p + psize1,q + psize1,true))-=P; + const float + dx = (float)x - p, dy = (float)y - q, + distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), + weight = (float)cimg_std::exp(-distance2); + sum_weights+=weight; + cimg_forV(*this,k) res(x,y,0,k)+=weight*(*this)(p,q,0,k); + } + if (sum_weights>0) cimg_forV(*this,k) res(x,y,0,k)/=sum_weights; else cimg_forV(*this,k) res(x,y,0,k) = (Tfloat)((*this)(x,y,0,k)); + } + } + } + return res.transfer_to(*this); + } + + CImg get_blur_patch(const unsigned int patch_size, const float sigma_p, const float sigma_s=10, + const unsigned int lookup_size=4, const bool fast_approx=true) const { + return (+*this).blur_patch(patch_size,sigma_p,sigma_s,lookup_size,fast_approx); + } + + //! Compute the Fast Fourier Transform of an image (along a specified axis). + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this).FFT(axis,invert); + } + + //! Compute the Fast Fourier Transform on an image. + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this).FFT(invert); + } + + //! Apply a median filter. + CImg& blur_median(const unsigned int n) { + return get_blur_median(n).transfer_to(*this); + } + + CImg get_blur_median(const unsigned int n) { + CImg res(width,height,depth,dim); + if (!n || n==1) return *this; + const int hl=n/2, hr=hl-1+n%2; + if (res.depth!=1) { // 3D median filter + CImg vois; + cimg_forXYZV(*this,x,y,z,k) { + const int + x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1, nz1 = z1>=dimz()?dimz()-1:z1; + vois = get_crop(nx0,ny0,nz0,k,nx1,ny1,nz1,k); + res(x,y,z,k) = vois.median(); + } + } else { +#define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) + if (res.height!=1) switch (n) { // 2D median filter + case 3 : { + T I[9] = { 0 }; + CImg_3x3(J,T); + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + cimg_std::memcpy(J,I,9*sizeof(T)); + _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); + _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn); + _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn); + _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn); + _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc); + _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc); + _cimg_median_sort(Jcc, Jnp); + res(x,y,0,k) = Jcc; + } + } break; + case 5 : { + T I[25] = { 0 }; + CImg_5x5(J,T); + cimg_forV(*this,k) cimg_for5x5(*this,x,y,0,k,I) { + cimg_std::memcpy(J,I,25*sizeof(T)); + _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb); + _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc); + _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc); + _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn); + _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca); + _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp); + _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp); + _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac); + _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc); + _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna); + _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa); + _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn); + _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan); + _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc); + _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca); + _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna); + _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn); + _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna); + _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc); + _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc); + _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp); + _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc); + _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn); + _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn); + _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc); + res(x,y,0,k) = Jcc; + } + } break; + default : { + CImg vois; + cimg_forXYV(*this,x,y,k) { + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1; + vois = get_crop(nx0,ny0,0,k,nx1,ny1,0,k); + res(x,y,0,k) = vois.median(); + } + } + } else switch (n) { // 1D median filter + case 2 : { + T I[4] = { 0 }; + cimg_forV(*this,k) cimg_for2x2(*this,x,y,0,k,I) res(x,0,0,k) = (T)(0.5f*(I[0]+I[1])); + } break; + case 3 : { + T I[9] = { 0 }; + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + res(x,0,0,k) = I[3] vois; + cimg_forXV(*this,x,k) { + const int + x0 = x - hl, x1 = x + hr, + nx0 = x0<0?0:x0, nx1 = x1>=dimx()?dimx()-1:x1; + vois = get_crop(nx0,0,0,k,nx1,0,0,k); + res(x,0,0,k) = vois.median(); + } + } + } + } + return res; + } + + //! Sharpen image using anisotropic shock filters or inverse diffusion. + CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) { + if (is_empty()) return *this; + T valm, valM = maxmin(valm); + const bool threed = (depth>1); + const float nedge = 0.5f*edge; + CImg val, vec, veloc(width,height,depth,dim); + + if (threed) { + CImg_3x3x3(I,T); + if (sharpen_type) { // 3D Shock filter. + CImg G = (alpha>0?get_blur(alpha).get_structure_tensor():get_structure_tensor()); + if (sigma>0) G.blur(sigma); + + cimg_forXYZ(G,x,y,z) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + G(x,y,z,0) = vec(0,0); + G(x,y,z,1) = vec(0,1); + G(x,y,z,2) = vec(0,2); + G(x,y,z,3) = 1 - (Tfloat)cimg_std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge); + } + cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { + const Tfloat + u = G(x,y,z,0), + v = G(x,y,z,1), + w = G(x,y,z,2), + amp = G(x,y,z,3), + ixx = (Tfloat)Incc + Ipcc - 2*Iccc, + ixy = 0.25f*((Tfloat)Innc + Ippc - Inpc - Ipnc), + ixz = 0.25f*((Tfloat)Incn + Ipcp - Incp - Ipcn), + iyy = (Tfloat)Icnc + Icpc - 2*Iccc, + iyz = 0.25f*((Tfloat)Icnn + Icpp - Icnp - Icpn), + izz = (Tfloat)Iccn + Iccp - 2*Iccc, + ixf = (Tfloat)Incc - Iccc, + ixb = (Tfloat)Iccc - Ipcc, + iyf = (Tfloat)Icnc - Iccc, + iyb = (Tfloat)Iccc - Icpc, + izf = (Tfloat)Iccn - Iccc, + izb = (Tfloat)Iccc - Iccp, + itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb); + veloc(x,y,z,k) = -amp*cimg::sign(itt)*cimg::abs(it); + } + } else cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) veloc(x,y,z,k) = -(Tfloat)Ipcc-Incc-Icpc-Icnc-Iccp-Iccn+6*Iccc; // 3D Inverse diffusion. + } else { + CImg_3x3(I,T); + if (sharpen_type) { // 2D Shock filter. + CImg G = (alpha>0?get_blur(alpha).get_structure_tensor():get_structure_tensor()); + if (sigma>0) G.blur(sigma); + cimg_forXY(G,x,y) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + G(x,y,0) = vec(0,0); + G(x,y,1) = vec(0,1); + G(x,y,2) = 1 - (Tfloat)cimg_std::pow(1+val[0]+val[1],-(Tfloat)nedge); + } + cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { + const Tfloat + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = (Tfloat)Inc + Ipc - 2*Icc, + ixy = 0.25f*((Tfloat)Inn + Ipp - Inp - Ipn), + iyy = (Tfloat)Icn + Icp - 2*Icc, + ixf = (Tfloat)Inc - Icc, + ixb = (Tfloat)Icc - Ipc, + iyf = (Tfloat)Icn - Icc, + iyb = (Tfloat)Icc - Icp, + itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb); + veloc(x,y,k) = -amp*cimg::sign(itt)*cimg::abs(it); + } + } else cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) veloc(x,y,k) = -(Tfloat)Ipc-Inc-Icp-Icn+4*Icc; // 3D Inverse diffusion. + } + float m, M = (float)veloc.maxmin(m); + const float vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); + if (vmax!=0) { veloc*=amplitude/vmax; (*this)+=veloc; } + return cut(valm,valM); + } + + CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const { + return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); + } + + //! Compute the Haar multiscale wavelet transform (monodimensional version). + /** + \param axis Axis considered for the transform. + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(axis,invert,nb_scales).transfer_to(*this); + } + + CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { + if (is_empty() || !nb_scales) return *this; + CImg res; + + if (nb_scales==1) { + switch (cimg::uncase(axis)) { // Single scale transform + case 'x' : { + const unsigned int w = width/2; + if (w) { + if (w%2) + throw CImgInstanceException("CImg<%s>::haar() : Sub-image width = %u is not even at a particular scale (=%u).", + pixel_type(),w); + res.assign(width,height,depth,dim); + if (invert) cimg_forYZV(*this,y,z,v) { // Inverse transform along X + for (unsigned int x=0, xw=w, x2=0; x::haar() : Sub-image height = %u is not even at a particular scale.", + pixel_type(),h); + res.assign(width,height,depth,dim); + if (invert) cimg_forXZV(*this,x,z,v) { // Inverse transform along Y + for (unsigned int y=0, yh=h, y2=0; y::haar() : Sub-image depth = %u is not even at a particular scale.", + pixel_type(),d); + res.assign(width,height,depth,dim); + if (invert) cimg_forXYV(*this,x,y,v) { // Inverse transform along Z + for (unsigned int z=0, zd=d, z2=0; z::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", + pixel_type(),axis); + } + } else { // Multi-scale version + if (invert) { + res.assign(*this); + switch (cimg::uncase(axis)) { + case 'x' : { + unsigned int w = width; + for (unsigned int s=1; w && s::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", + pixel_type(),axis); + } + } else { // Direct transform + res = get_haar(axis,false,1); + switch (cimg::uncase(axis)) { + case 'x' : { + for (unsigned int s=1, w=width/2; w && s::haar() : Invalid axis '%c', must be 'x','y' or 'z'.", + pixel_type(),axis); + } + } + } + return res; + } + + //! Compute the Haar multiscale wavelet transform. + /** + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(invert,nb_scales).transfer_to(*this); + } + + CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { + CImg res; + + if (nb_scales==1) { // Single scale transform + if (width>1) get_haar('x',invert,1).transfer_to(res); + if (height>1) { if (res) res.get_haar('y',invert,1).transfer_to(res); else get_haar('y',invert,1).transfer_to(res); } + if (depth>1) { if (res) res.get_haar('z',invert,1).transfer_to(res); else get_haar('z',invert,1).transfer_to(res); } + if (res) return res; + } else { // Multi-scale transform + if (invert) { // Inverse transform + res.assign(*this); + if (width>1) { + if (height>1) { + if (depth>1) { + unsigned int w = width, h = height, d = depth; for (unsigned int s=1; w && h && d && s1) { + unsigned int w = width, d = depth; for (unsigned int s=1; w && d && s1) { + if (depth>1) { + unsigned int h = height, d = depth; for (unsigned int s=1; h && d && s1) { + unsigned int d = depth; for (unsigned int s=1; d && s1) { + if (height>1) { + if (depth>1) for (unsigned int s=1, w=width/2, h=height/2, d=depth/2; w && h && d && s1) for (unsigned int s=1, w=width/2, d=depth/2; w && d && s1) { + if (depth>1) for (unsigned int s=1, h=height/2, d=depth/2; h && d && s1) for (unsigned int s=1, d=depth/2; d && s& displacement_field(const CImg& target, const float smooth=0.1f, const float precision=0.1f, + const unsigned int nb_scales=0, const unsigned int itermax=10000) { + return get_displacement_field(target,smooth,precision,nb_scales,itermax).transfer_to(*this); + } + + CImg get_displacement_field(const CImg& target, + const float smoothness=0.1f, const float precision=0.1f, + const unsigned int nb_scales=0, const unsigned int itermax=10000) const { + if (is_empty() || !target) return *this; + if (!is_sameXYZV(target)) + throw CImgArgumentException("CImg<%s>::displacement_field() : Instance image (%u,%u,%u,%u,%p) and target image (%u,%u,%u,%u,%p) " + "have different size.", + pixel_type(),width,height,depth,dim,data, + target.width,target.height,target.depth,target.dim,target.data); + if (smoothness<0) + throw CImgArgumentException("CImg<%s>::displacement_field() : Smoothness parameter %g is negative.", + pixel_type(),smoothness); + if (precision<0) + throw CImgArgumentException("CImg<%s>::displacement_field() : Precision parameter %g is negative.", + pixel_type(),precision); + + const unsigned int nscales = nb_scales>0?nb_scales:(unsigned int)(2*cimg_std::log((double)(cimg::max(width,height,depth)))); + Tfloat m1, M1 = (Tfloat)maxmin(m1), m2, M2 = (Tfloat)target.maxmin(m2); + const Tfloat factor = cimg::max(cimg::abs(m1),cimg::abs(M1),cimg::abs(m2),cimg::abs(M2)); + CImg U0; + const bool threed = (depth>1); + + // Begin multi-scale motion estimation + for (int scale = (int)nscales-1; scale>=0; --scale) { + const float sfactor = (float)cimg_std::pow(1.5f,(float)scale), sprecision = (float)(precision/cimg_std::pow(2.25,1+scale)); + const int + sw = (int)(width/sfactor), sh = (int)(height/sfactor), sd = (int)(depth/sfactor), + swidth = sw?sw:1, sheight = sh?sh:1, sdepth = sd?sd:1; + CImg + I1 = get_resize(swidth,sheight,sdepth,-100,2), + I2 = target.get_resize(swidth,sheight,sdepth,-100,2); + I1/=factor; I2/=factor; + CImg U; + if (U0) U = (U0*=1.5f).get_resize(I1.dimx(),I1.dimy(),I1.dimz(),-100,3); + else U.assign(I1.dimx(),I1.dimy(),I1.dimz(),threed?3:2,0); + + // Begin single-scale motion estimation + CImg veloc(U); + float dt = 2, Energy = cimg::type::max(); + const CImgList dI = I2.get_gradient(); + for (unsigned int iter=0; iter vector(const T& a0) { + static CImg r(1,1); r[0] = a0; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1) { + static CImg r(1,2); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2) { + static CImg r(1,3); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { + static CImg r(1,4); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + static CImg r(1,5); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + static CImg r(1,6); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + static CImg r(1,7); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + static CImg r(1,8); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + static CImg r(1,9); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + static CImg r(1,10); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + static CImg r(1,11); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + static CImg r(1,12); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + static CImg r(1,13); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + static CImg r(1,14); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + static CImg r(1,15); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + return r; + } + + //! Return a vector with specified coefficients. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + static CImg r(1,16); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 1x1 square matrix with specified coefficients. + static CImg matrix(const T& a0) { + return vector(a0); + } + + //! Return a 2x2 square matrix with specified coefficients. + static CImg matrix(const T& a0, const T& a1, + const T& a2, const T& a3) { + static CImg r(2,2); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; + *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a 3x3 square matrix with specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { + static CImg r(3,3); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; + return r; + } + + //! Return a 4x4 square matrix with specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + static CImg r(4,4); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 5x5 square matrix with specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, + const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, + const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, + const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { + static CImg r(5,5); T *ptr = r.data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; + *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; + *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; + return r; + } + + //! Return a 1x1 symmetric matrix with specified coefficients. + static CImg tensor(const T& a1) { + return matrix(a1); + } + + //! Return a 2x2 symmetric matrix tensor with specified coefficients. + static CImg tensor(const T& a1, const T& a2, const T& a3) { + return matrix(a1,a2,a2,a3); + } + + //! Return a 3x3 symmetric matrix with specified coefficients. + static CImg tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { + return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6); + } + + //! Return a 1x1 diagonal matrix with specified coefficients. + static CImg diagonal(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 diagonal matrix with specified coefficients. + static CImg diagonal(const T& a0, const T& a1) { + return matrix(a0,0,0,a1); + } + + //! Return a 3x3 diagonal matrix with specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2) { + return matrix(a0,0,0,0,a1,0,0,0,a2); + } + + //! Return a 4x4 diagonal matrix with specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { + return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); + } + + //! Return a 5x5 diagonal matrix with specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); + } + + //! Return a NxN identity matrix. + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x) = 1; + return res; + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + static CImg sequence(const unsigned int N, const T a0, const T a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. + static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) { + float X,Y,Z,W; + if (!quaternion_data) { + const float norm = (float)cimg_std::sqrt(x*x + y*y + z*z), + nx = norm>0?x/norm:0, + ny = norm>0?y/norm:0, + nz = norm>0?z/norm:1, + nw = norm>0?w:0, + sina = (float)cimg_std::sin(nw/2), + cosa = (float)cimg_std::cos(nw/2); + X = nx*sina; + Y = ny*sina; + Z = nz*sina; + W = cosa; + } else { + const float norm = (float)cimg_std::sqrt(x*x + y*y + z*z + w*w); + if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; } + else { X = Y = Z = 0; W = 1; } + } + const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W; + return CImg::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)), + (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)), + (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy))); + } + + //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image. + CImg get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + static CImg dest; + if (dest.height!=dim) dest.assign(1,dim); + const unsigned int whz = width*height*depth; + const T *ptrs = ptr(x,y,z); + T *ptrd = dest.data; + cimg_forV(*this,k) { *(ptrd++) = *ptrs; ptrs+=whz; } + return dest; + } + + //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + template + CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { + if (x get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)cimg_std::sqrt((double)dim); + CImg dest(n,n); + cimg_forV(*this,k) dest[k]=(*this)(x,y,z,k); + return dest; + } + + //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + template + CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image. + CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + if (dim==6) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2), + (*this)(x,y,z,3),(*this)(x,y,z,4),(*this)(x,y,z,5)); + if (dim==3) return tensor((*this)(x,y,z,0),(*this)(x,y,z,1),(*this)(x,y,z,2)); + return tensor((*this)(x,y,z,0)); + } + + //! Set the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. + template + CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + if (ten.height==2) { + (*this)(x,y,z,0) = (T)ten[0]; + (*this)(x,y,z,1) = (T)ten[1]; + (*this)(x,y,z,2) = (T)ten[3]; + } + else { + (*this)(x,y,z,0) = (T)ten[0]; + (*this)(x,y,z,1) = (T)ten[1]; + (*this)(x,y,z,2) = (T)ten[2]; + (*this)(x,y,z,3) = (T)ten[4]; + (*this)(x,y,z,4) = (T)ten[5]; + (*this)(x,y,z,5) = (T)ten[8]; + } + return *this; + } + + //! Unroll all images values into a one-column vector. + CImg& vector() { + return unroll('y'); + } + + CImg get_vector() const { + return get_unroll('y'); + } + + //! Realign pixel values of the instance image as a square matrix + CImg& matrix() { + const unsigned int siz = size(); + switch (siz) { + case 1 : break; + case 4 : width = height = 2; break; + case 9 : width = height = 3; break; + case 16 : width = height = 4; break; + case 25 : width = height = 5; break; + case 36 : width = height = 6; break; + case 49 : width = height = 7; break; + case 64 : width = height = 8; break; + case 81 : width = height = 9; break; + case 100 : width = height = 10; break; + default : { + unsigned int i = 11, i2 = i*i; + while (i2::matrix() : Image size = %u is not a square number", + pixel_type(),siz); + } + } + return *this; + } + + CImg get_matrix() const { + return (+*this).matrix(); + } + + //! Realign pixel values of the instance image as a symmetric tensor. + CImg& tensor() { + return get_tensor().transfer_to(*this); + } + + CImg get_tensor() const { + CImg res; + const unsigned int siz = size(); + switch (siz) { + case 1 : break; + case 3 : + res.assign(2,2); + res(0,0) = (*this)(0); + res(1,0) = res(0,1) = (*this)(1); + res(1,1) = (*this)(2); + break; + case 6 : + res.assign(3,3); + res(0,0) = (*this)(0); + res(1,0) = res(0,1) = (*this)(1); + res(2,0) = res(0,2) = (*this)(2); + res(1,1) = (*this)(3); + res(2,1) = res(1,2) = (*this)(4); + res(2,2) = (*this)(5); + break; + default : + throw CImgInstanceException("CImg<%s>::tensor() : Wrong vector dimension = %u in instance image.", + pixel_type(), dim); + } + return res; + } + + //! Unroll all images values into specified axis. + CImg& unroll(const char axis) { + const unsigned int siz = size(); + if (siz) switch (axis) { + case 'x' : width = siz; height=depth=dim=1; break; + case 'y' : height = siz; width=depth=dim=1; break; + case 'z' : depth = siz; width=height=dim=1; break; + case 'v' : dim = siz; width=height=depth=1; break; + default : + throw CImgArgumentException("CImg<%s>::unroll() : Given axis is '%c' which is not 'x','y','z' or 'v'", + pixel_type(),axis); + } + return *this; + } + + CImg get_unroll(const char axis) const { + return (+*this).unroll(axis); + } + + //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image. + CImg& diagonal() { + return get_diagonal().transfer_to(*this); + } + + CImg get_diagonal() const { + if (is_empty()) return *this; + CImg res(size(),size(),1,1,0); + cimg_foroff(*this,off) res(off,off) = (*this)(off); + return res; + } + + //! Get an identity matrix having same dimension than instance image. + CImg& identity_matrix() { + return identity_matrix(cimg::max(width,height)).transfer_to(*this); + } + + CImg get_identity_matrix() const { + return identity_matrix(cimg::max(width,height)); + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + CImg& sequence(const T a0, const T a1) { + if (is_empty()) return *this; + const unsigned int siz = size() - 1; + T* ptr = data; + if (siz) { + const Tfloat delta = (Tfloat)a1 - a0; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } else *ptr = a0; + return *this; + } + + CImg get_sequence(const T a0, const T a1) const { + return (+*this).sequence(a0,a1); + } + + //! Transpose the current matrix. + CImg& transpose() { + if (width==1) { width=height; height=1; return *this; } + if (height==1) { height=width; width=1; return *this; } + if (width==height) { + cimg_forYZV(*this,y,z,v) for (int x=y; x get_transpose() const { + return get_permute_axes("yxzv"); + } + + //! Invert the current matrix. + CImg& invert(const bool use_LU=true) { + if (!is_empty()) { + if (width!=height || depth!=1 || dim!=1) + throw CImgInstanceException("CImg<%s>::invert() : Instance matrix (%u,%u,%u,%u,%p) is not square.", + pixel_type(),width,height,depth,dim,data); +#ifdef cimg_use_lapack + int INFO = (int)use_LU, N = width, LWORK = 4*N, *IPIV = new int[N]; + Tfloat + *lapA = new Tfloat[N*N], + *WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn("CImg<%s>::invert() : LAPACK library function dgetrf_() returned error code %d.", + pixel_type(),INFO); + else { + cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); + if (INFO) + cimg::warn("CImg<%s>::invert() : LAPACK library function dgetri_() returned Error code %d", + pixel_type(),INFO); + } + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] WORK; +#else + const double dete = width>3?-1.0:det(); + if (dete!=0.0 && width==2) { + const double + a = data[0], c = data[1], + b = data[2], d = data[3]; + data[0] = (T)(d/dete); data[1] = (T)(-c/dete); + data[2] = (T)(-b/dete); data[3] = (T)(a/dete); + } else if (dete!=0.0 && width==3) { + const double + a = data[0], d = data[1], g = data[2], + b = data[3], e = data[4], h = data[5], + c = data[6], f = data[7], i = data[8]; + data[0] = (T)((i*e-f*h)/dete), data[1] = (T)((g*f-i*d)/dete), data[2] = (T)((d*h-g*e)/dete); + data[3] = (T)((h*c-i*b)/dete), data[4] = (T)((i*a-c*g)/dete), data[5] = (T)((g*b-a*h)/dete); + data[6] = (T)((b*f-e*c)/dete), data[7] = (T)((d*c-a*f)/dete), data[8] = (T)((a*e-d*b)/dete); + } else { + if (use_LU) { // LU-based inverse computation + CImg A(*this), indx, col(1,width); + bool d; + A._LU(indx,d); + cimg_forX(*this,j) { + col.fill(0); + col(j) = 1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = (T)col(i); + } + } else { // SVD-based inverse computation + CImg U(width,width), S(1,width), V(width,width); + SVD(U,S,V,false); + U.transpose(); + cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; + S.diagonal(); + *this = V*S*U; + } + } +#endif + } + return *this; + } + + CImg get_invert(const bool use_LU=true) const { + return CImg(*this,false).invert(use_LU); + } + + //! Compute the pseudo-inverse (Moore-Penrose) of the matrix. + CImg& pseudoinvert() { + return get_pseudoinvert().transfer_to(*this); + } + + CImg get_pseudoinvert() const { + CImg U, S, V; + SVD(U,S,V); + cimg_forX(V,x) { + const Tfloat s = S(x), invs = s!=0?1/s:(Tfloat)0; + cimg_forY(V,y) V(x,y)*=invs; + } + return V*U.transpose(); + } + + //! Compute the cross product between two 3d vectors. + template + CImg& cross(const CImg& img) { + if (width!=1 || height<3 || img.width!=1 || img.height<3) + throw CImgInstanceException("CImg<%s>::cross() : Arguments (%u,%u,%u,%u,%p) and (%u,%u,%u,%u,%p) must be both 3d vectors.", + pixel_type(),width,height,depth,dim,data,img.width,img.height,img.depth,img.dim,img.data); + const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; + (*this)[0] = (T)(y*img[2]-z*img[1]); + (*this)[1] = (T)(z*img[0]-x*img[2]); + (*this)[2] = (T)(x*img[1]-y*img[0]); + return *this; + } + + template + CImg::type> get_cross(const CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImg(*this).cross(img); + } + + //! Solve a linear system AX=B where B=*this. + template + CImg& solve(const CImg& A) { + if (width!=1 || depth!=1 || dim!=1 || height!=A.height || A.depth!=1 || A.dim!=1) + throw CImgArgumentException("CImg<%s>::solve() : Instance matrix size is (%u,%u,%u,%u) while " + "size of given matrix A is (%u,%u,%u,%u).", + pixel_type(),width,height,depth,dim,A.width,A.height,A.depth,A.dim); + + typedef typename cimg::superset2::type Ttfloat; + if (A.width==A.height) { +#ifdef cimg_use_lapack + char TRANS='N'; + int INFO, N = height, LWORK = 4*N, one = 1, *IPIV = new int[N]; + Ttfloat + *lapA = new Ttfloat[N*N], + *lapB = new Ttfloat[N], + *WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l)); + cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrf_() returned error code %d.", + pixel_type(),INFO); + if (!INFO) { + cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); + if (INFO) + cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrs_() returned Error code %d", + pixel_type(),INFO); + } + if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; +#else + CImg lu(A); + CImg indx; + bool d; + lu._LU(indx,d); + _solve(lu,indx); +#endif + } else assign(A.get_pseudoinvert()*(*this)); + return *this; + } + + template + CImg::type> get_solve(const CImg& A) const { + typedef typename cimg::superset2::type Ttfloat; + return CImg(*this,false).solve(A); + } + + template + CImg& _solve(const CImg& A, const CImg& indx) { + typedef typename cimg::superset2::type Ttfloat; + const int N = size(); + int ii = -1; + Ttfloat sum; + for (int i=0; i=0) for (int j=ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii=i; + (*this)(i) = (T)sum; + } + { for (int i=N-1; i>=0; --i) { + sum = (*this)(i); + for (int j=i+1; j + CImg& solve_tridiagonal(const CImg& a, const CImg& b, const CImg& c) { + const int siz = (int)size(); + if ((int)a.size()!=siz || (int)b.size()!=siz || (int)c.size()!=siz) + throw CImgArgumentException("CImg<%s>::solve_tridiagonal() : arrays of triagonal coefficients have different size.",pixel_type); + typedef typename cimg::superset2::type Ttfloat; + CImg nc(siz); + const T *ptra = a.data, *ptrb = b.data, *ptrc = c.data; + T *ptrnc = nc.data, *ptrd = data; + const Ttfloat valb0 = (Ttfloat)*(ptrb++); + *ptrnc = *(ptrc++)/valb0; + Ttfloat vald = (Ttfloat)(*(ptrd++)/=valb0); + for (int i = 1; i=0; --i) vald = (*(--ptrd)-=*(--ptrnc)*vald); + return *this; + } + + template + CImg::type> get_solve_tridiagonal(const CImg& a, const CImg& b, const CImg& c) const { + typedef typename cimg::superset2::type Ttfloat; + return CImg(*this,false).solve_tridiagonal(a,b,c); + } + + //! Sort values of a vector and get permutations. + template + CImg& sort(CImg& permutations, const bool increasing=true) { + if (is_empty()) permutations.assign(); + else { + if (permutations.size()!=size()) permutations.assign(size()); + cimg_foroff(permutations,off) permutations[off] = (t)off; + _quicksort(0,size()-1,permutations,increasing); + } + return *this; + } + + template + CImg get_sort(CImg& permutations, const bool increasing=true) const { + return (+*this).sort(permutations,increasing); + } + + // Sort image values. + CImg& sort(const bool increasing=true) { + CImg foo; + return sort(foo,increasing); + } + + CImg get_sort(const bool increasing=true) const { + return (+*this).sort(increasing); + } + + template + CImg& _quicksort(const int min, const int max, CImg& permutations, const bool increasing) { + if (min(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + if ((*this)[mid]>(*this)[max]) { + cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } + if ((*this)[min]>(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + } else { + if ((*this)[min]<(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + if ((*this)[mid]<(*this)[max]) { + cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } + if ((*this)[min]<(*this)[mid]) { + cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } + } + if (max-min>=3) { + const T pivot = (*this)[mid]; + int i = min, j = max; + if (increasing) { + do { + while ((*this)[i]pivot) --j; + if (i<=j) { + cimg::swap((*this)[i],(*this)[j]); + cimg::swap(permutations[i++],permutations[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) ++i; + while ((*this)[j] + CImg& permute(const CImg& permutation) { + return get_permute(permutation).transfer_to(*this); + } + + template + CImg get_permute(const CImg& permutation) const { + if (permutation.size()!=size()) + throw CImgArgumentException("CImg<%s>::permute() : Instance image (%u,%u,%u,%u,%p) and permutation (%u,%u,%u,%u,%p)" + "have different sizes.", + pixel_type(),width,height,depth,dim,data, + permutation.width,permutation.height,permutation.depth,permutation.dim,permutation.data); + CImg res(width,height,depth,dim); + const t *p = permutation.ptr(permutation.size()); + cimg_for(res,ptr,T) *ptr = (*this)[*(--p)]; + return res; + } + + //! Compute the SVD of a general matrix. + template + const CImg& SVD(CImg& U, CImg& S, CImg& V, + const bool sorting=true, const unsigned int max_iter=40, const float lambda=0) const { + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else { + U = *this; + if (lambda!=0) { + const unsigned int delta = cimg::min(U.width,U.height); + for (unsigned int i=0; i rv1(width); + t anorm = 0, c, f, g = 0, h, s, scale = 0; + int l = 0, nm = 0; + + cimg_forX(U,i) { + l = i+1; rv1[i] = scale*g; g = s = scale = 0; + if (i=0?-1:1)*cimg_std::sqrt(s)); h=f*g-s; U(i,i) = f-g; + for (int j=l; j=0?-1:1)*cimg_std::sqrt(s)); h = f*g-s; U(l,i) = f-g; + { for (int k=l; k=0; --i) { + if (i=0; --i) { + l = i+1; g = S[i]; + for (int j=l; j=0; --k) { + for (unsigned int its=0; its=1; --l) { + nm = l-1; + if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm])+anorm)==anorm) break; + } + if (flag) { + c = 0; s = 1; + for (int i=l; i<=k; ++i) { + f = s*rv1[i]; rv1[i] = c*rv1[i]; + if ((cimg::abs(f)+anorm)==anorm) break; + g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; + cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c+z*s; U(i,j) = z*c-y*s; } + } + } + const t z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k-1; + t x = S[l], y = S[nm]; + g = rv1[nm]; h = rv1[k]; + f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y); + g = (t)cimg::_pythagore(f,1.0); + f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; + c = s = 1; + for (int j=l; j<=nm; ++j) { + const int i = j+1; + g = rv1[i]; h = s*g; g = c*g; + t y = S[i]; + t z = (t)cimg::_pythagore(f,h); + rv1[j] = z; c = f/z; s = h/z; + f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; + cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c+z*s; V(i,jj) = z*c-x*s; } + z = (t)cimg::_pythagore(f,h); S[j] = z; + if (z) { z = 1/z; c = f*z; s = h*z; } + f = c*g+s*y; x = c*y-s*g; + { cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c+z*s; U(i,jj) = z*c-y*s; }} + } + rv1[l] = 0; rv1[k]=f; S[k]=x; + } + } + + if (sorting) { + CImg permutations(width); + CImg tmp(width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forX(permutations,x) tmp(x) = U(permutations(x),k); + cimg_std::memcpy(U.ptr(0,k),tmp.data,sizeof(t)*width); + } + { cimg_forY(V,k) { + cimg_forX(permutations,x) tmp(x) = V(permutations(x),k); + cimg_std::memcpy(V.ptr(0,k),tmp.data,sizeof(t)*width); + }} + } + } + return *this; + } + + //! Compute the SVD of a general matrix. + template + const CImg& SVD(CImgList& USV) const { + if (USV.size<3) USV.assign(3); + return SVD(USV[0],USV[1],USV[2]); + } + + //! Compute the SVD of a general matrix. + CImgList get_SVD(const bool sorting=true) const { + CImgList res(3); + SVD(res[0],res[1],res[2],sorting); + return res; + } + + // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies) + template + CImg& _LU(CImg& indx, bool& d) { + const int N = dimx(); + int imax = 0; + CImg vv(N); + indx.assign(N); + d = true; + cimg_forX(*this,i) { + Tfloat vmax = 0; + cimg_forX(*this,j) { + const Tfloat tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) { indx.fill(0); return fill(0); } + vv[i] = 1/vmax; + } + cimg_forX(*this,j) { + for (int i=0; i=vmax) { vmax=tmp; imax=i; } + }} + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d =!d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; + if (j + const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (width!=height || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + if (val.size()::eigen() : Complex eigenvalues", + pixel_type()); + f = cimg_std::sqrt(f); + const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); + const double theta1 = cimg_std::atan2(l2-a,b), theta2 = cimg_std::atan2(l1-a,b); + val[0]=(t)l2; + val[1]=(t)l1; + vec(0,0) = (t)cimg_std::cos(theta1); + vec(0,1) = (t)cimg_std::sin(theta1); + vec(1,0) = (t)cimg_std::cos(theta2); + vec(1,1) = (t)cimg_std::sin(theta2); + } break; + default : + throw CImgInstanceException("CImg<%s>::eigen() : Eigenvalues computation of general matrices is limited" + "to 2x2 matrices (given is %ux%u)", + pixel_type(),width,height); + } + } + return *this; + } + + //! Compute the eigenvalues and eigenvectors of a matrix. + CImgList get_eigen() const { + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + template + const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { +#ifdef cimg_use_lapack + char JOB = 'V', UPLO = 'U'; + int N = width, LWORK = 4*N, INFO; + Tfloat + *lapA = new Tfloat[N*N], + *lapW = new Tfloat[N], + *WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l)); + cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); + if (INFO) + cimg::warn("CImg<%s>::symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.", + pixel_type(),INFO); + val.assign(1,N); + vec.assign(N,N); + if (!INFO) { + cimg_forY(val,i) val(i) = (T)lapW[N-1-i]; + cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]); + } else { val.fill(0); vec.fill(0); } + delete[] lapA; delete[] lapW; delete[] WORK; +#else + if (width!=height || depth>1 || dim>1) + throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + val.assign(1,width); + if (vec.data) vec.assign(width,width); + if (width<3) return eigen(val,vec); + CImg V(width,width); + SVD(vec,val,V,false); + bool ambiguous = false; + float eig = 0; + cimg_forY(val,p) { // check for ambiguous cases. + if (val[p]>eig) eig = (float)val[p]; + t scal = 0; + cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); + if (cimg::abs(scal)<0.9f) ambiguous = true; + if (scal<0) val[p] = -val[p]; + } + if (ambiguous) { + (eig*=2)++; + SVD(vec,val,V,false,40,eig); + val-=eig; + } + CImg permutations(width); // sort eigenvalues in decreasing order + CImg tmp(width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forX(permutations,x) tmp(x) = vec(permutations(x),k); + cimg_std::memcpy(vec.ptr(0,k),tmp.data,sizeof(t)*width); + } +#endif + } + return *this; + } + + //! Compute the eigenvalues and eigenvectors of a symmetric matrix. + CImgList get_symmetric_eigen() const { + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //@} + //------------------- + // + //! \name Display + //@{ + //------------------- + + //! Display an image into a CImgDisplay window. + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n + const CImg& display(CImgDisplay &disp, const bool display_info) const { + return _display(disp,0,display_info); + } + + //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n + const CImg& display(const char *const title=0, const bool display_info=true) const { + CImgDisplay disp; + return _display(disp,title,display_info); + } + + const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::display() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0, mkey = 0; + int x0 = 0, y0 = 0, z0 = 0, x1 = dimx()-1, y1 = dimy()-1, z1 = dimz()-1; + float frametiming = 5; + + char ntitle[256] = { 0 }; + if (!disp) { + if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); + disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); + } + cimg_std::strncpy(ntitle,disp.title,255); + if (display_info) print(ntitle); + + CImg zoom; + for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed; ) { + if (reset_view) { + XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2; + x0 = 0; y0 = 0; z0 = 0; x1 = width-1; y1 = height-1; z1 = depth-1; + oldw = disp.width; oldh = disp.height; + reset_view = false; + } + if (!x0 && !y0 && !z0 && x1==dimx()-1 && y1==dimy()-1 && z1==dimz()-1) zoom.assign(); + else zoom = get_crop(x0,y0,z0,x1,y1,z1); + + const unsigned int + dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0, + tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0); + if (resize_disp) { + const unsigned int + ttw = tw*disp.width/oldw, tth = th*disp.height/oldh, + dM = cimg::max(ttw,tth), diM = cimg::max(disp.width,disp.height), + imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM); + disp.normalscreen().resize(cimg_fitscreen(imgw,imgh,1),false); + resize_disp = false; + } + oldw = tw; oldh = th; + + bool + go_up = false, go_down = false, go_left = false, go_right = false, + go_inc = false, go_dec = false, go_in = false, go_out = false, + go_in_center = false; + const CImg& visu = zoom?zoom:*this; + const CImg selection = visu._get_select(disp,0,2,XYZ,0,x0,y0,z0); + if (disp.wheel) { + if (disp.is_keyCTRLLEFT) { if (!mkey || mkey==1) go_out = !(go_in = disp.wheel>0); go_in_center = false; mkey = 1; } + else if (disp.is_keySHIFTLEFT) { if (!mkey || mkey==2) go_right = !(go_left = disp.wheel>0); mkey = 2; } + else if (disp.is_keyALT || depth==1) { if (!mkey || mkey==3) go_down = !(go_up = disp.wheel>0); mkey = 3; } + else mkey = 0; + disp.wheel = 0; + } else mkey = 0; + const int + sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), + sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); + if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { + x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0; + if (sx0==sx1 && sy0==sy1 && sz0==sz1) reset_view = true; + resize_disp = true; + } else switch (key = disp.key) { + case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT : case cimg::keyALT : disp.key = key = 0; break; + case cimg::keyP : if (visu.depth>1 && disp.is_keyCTRLLEFT) { // Special mode : play stack of frames + const unsigned int + w1 = visu.width*disp.width/(visu.width+(visu.depth>1?visu.depth:0)), + h1 = visu.height*disp.height/(visu.height+(visu.depth>1?visu.depth:0)); + disp.resize(cimg_fitscreen(w1,h1,1),false).key = disp.wheel = key = 0; + for (unsigned int timer = 0; !key && !disp.is_closed && !disp.button; ) { + if (disp.is_resized) disp.resize(); + if (!timer) { + visu.get_slice(XYZ[2]).display(disp.set_title("%s | z=%d",ntitle,XYZ[2])); + if (++XYZ[2]>=visu.depth) XYZ[2] = 0; + } + if (++timer>(unsigned int)frametiming) timer = 0; + if (disp.wheel) { frametiming-=disp.wheel/3.0f; disp.wheel = 0; } + switch (key = disp.key) { + case 0 : case cimg::keyCTRLLEFT : disp.key = key = 0; break; + case cimg::keyPAGEUP : frametiming-=0.3f; key = 0; break; + case cimg::keyPAGEDOWN : frametiming+=0.3f; key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false); + disp.key = key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false); + disp.key = key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(width,height,depth),false); + disp.key = key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT) { + disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen(); + disp.key = key = 0; + } break; + } + frametiming = frametiming<1?1:(frametiming>39?39:frametiming); + disp.wait(20); + } + const unsigned int + w2 = (visu.width + (visu.depth>1?visu.depth:0))*disp.width/visu.width, + h2 = (visu.height + (visu.depth>1?visu.depth:0))*disp.height/visu.height; + disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(ntitle); + key = disp.key = disp.button = disp.wheel = 0; + } break; + case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; break; + case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; + case cimg::keyPADSUB : go_out = true; key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; + case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; + case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; + case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; + case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; + case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; + case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; + case cimg::keyPAGEUP : go_inc = true; key = 0; break; + case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; + } + if (go_in) { + const int + mx = go_in_center?disp.dimx()/2:disp.mouse_x, + my = go_in_center?disp.dimy()/2:disp.mouse_y, + mX = mx*(width+(depth>1?depth:0))/disp.width, + mY = my*(height+(depth>1?depth:0))/disp.height; + int X = XYZ[0], Y = XYZ[1], Z = XYZ[2]; + if (mX=dimy()) { X = x0 + mX*(1+x1-x0)/width; Z = z0 + (mY-height)*(1+z1-z0)/depth; Y = XYZ[1]; } + if (mX>=dimx() && mY4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; } + if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; } + if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; } + } + if (go_out) { + const int + deltax = (x1-x0)/8, deltay = (y1-y0)/8, deltaz = (z1-z0)/8, + ndeltax = deltax?deltax:(width>1?1:0), + ndeltay = deltay?deltay:(height>1?1:0), + ndeltaz = deltaz?deltaz:(depth>1?1:0); + x0-=ndeltax; y0-=ndeltay; z0-=ndeltaz; + x1+=ndeltax; y1+=ndeltay; z1+=ndeltaz; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=dimx()) x1 = dimx()-1; } + if (y0<0) { y1-=y0; y0 = 0; if (y1>=dimy()) y1 = dimy()-1; } + if (z0<0) { z1-=z0; z0 = 0; if (z1>=dimz()) z1 = dimz()-1; } + if (x1>=dimx()) { x0-=(x1-dimx()+1); x1 = dimx()-1; if (x0<0) x0 = 0; } + if (y1>=dimy()) { y0-=(y1-dimy()+1); y1 = dimy()-1; if (y0<0) y0 = 0; } + if (z1>=dimz()) { z0-=(z1-dimz()+1); z1 = dimz()-1; if (z0<0) z0 = 0; } + } + if (go_left) { + const int delta = (x1-x0)/5, ndelta = delta?delta:(width>1?1:0); + if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + } + if (go_right) { + const int delta = (x1-x0)/5, ndelta = delta?delta:(width>1?1:0); + if (x1+ndelta1?1:0); + if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; } + else { y1-=y0; y0 = 0; } + } + if (go_down) { + const int delta = (y1-y0)/5, ndelta = delta?delta:(height>1?1:0); + if (y1+ndelta1?1:0); + if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; } + else { z1-=z0; z0 = 0; } + } + if (go_dec) { + const int delta = (z1-z0)/5, ndelta = delta?delta:(depth>1?1:0); + if (z1+ndelta& select(CImgDisplay &disp, + const int select_type=2, unsigned int *const XYZ=0, + const unsigned char *const color=0) { + return get_select(disp,select_type,XYZ,color).transfer_to(*this); + } + + //! Simple interface to select a shape from an image. + CImg& select(const char *const title, + const int select_type=2, unsigned int *const XYZ=0, + const unsigned char *const color=0) { + return get_select(title,select_type,XYZ,color).transfer_to(*this); + } + + //! Simple interface to select a shape from an image. + CImg get_select(CImgDisplay &disp, + const int select_type=2, unsigned int *const XYZ=0, + const unsigned char *const color=0) const { + return _get_select(disp,0,select_type,XYZ,color,0,0,0); + } + + //! Simple interface to select a shape from an image. + CImg get_select(const char *const title, + const int select_type=2, unsigned int *const XYZ=0, + const unsigned char *const color=0) const { + CImgDisplay disp; + return _get_select(disp,title,select_type,XYZ,color,0,0,0); + } + + CImg _get_select(CImgDisplay &disp, const char *const title, + const int coords_type, unsigned int *const XYZ, + const unsigned char *const color, + const int origX, const int origY, const int origZ) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::select() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + if (!disp) { + char ntitle[64] = { 0 }; if (!title) { cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); } + disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); + } + + const unsigned int + old_normalization = disp.normalization, + hatch = 0x55555555; + + bool old_is_resized = disp.is_resized; + disp.normalization = 0; + disp.show().key = 0; + + unsigned char foreground_color[] = { 255,255,105 }, background_color[] = { 0,0,0 }; + if (color) cimg_std::memcpy(foreground_color,color,sizeof(unsigned char)*cimg::min(3,dimv())); + + int area = 0, clicked_area = 0, phase = 0, + X0 = (int)((XYZ?XYZ[0]:width/2)%width), Y0 = (int)((XYZ?XYZ[1]:height/2)%height), Z0 = (int)((XYZ?XYZ[2]:depth/2)%depth), + X1 =-1, Y1 = -1, Z1 = -1, + X = -1, Y = -1, Z = -1, + oX = X, oY = Y, oZ = Z; + unsigned int old_button = 0, key = 0; + + bool shape_selected = false, text_down = false; + CImg visu, visu0; + char text[1024] = { 0 }; + + while (!key && !disp.is_closed && !shape_selected) { + + // Handle mouse motion and selection + oX = X; oY = Y; oZ = Z; + int mx = disp.mouse_x, my = disp.mouse_y; + const int mX = mx*(width+(depth>1?depth:0))/disp.width, mY = my*(height+(depth>1?depth:0))/disp.height; + + area = 0; + if (mX=dimy()) { area = 2; X = mX; Z = mY-height; Y = phase?Y1:Y0; } + if (mX>=dimx() && mY=2) { + switch (clicked_area) { + case 1 : Z1 = Z; break; + case 2 : Y1 = Y; break; + case 3 : X1 = X; break; + } + } + if (disp.button&2) { if (phase) { X1 = X; Y1 = Y; Z1 = Z; } else { X0 = X; Y0 = Y; Z0 = Z; } } + if (disp.button&4) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu.assign(); } + if (disp.wheel) { + if (depth>1 && !disp.is_keyCTRLLEFT && !disp.is_keySHIFTLEFT && !disp.is_keyALT) { + switch (area) { + case 1 : if (phase) Z = (Z1+=disp.wheel); else Z = (Z0+=disp.wheel); break; + case 2 : if (phase) Y = (Y1+=disp.wheel); else Y = (Y0+=disp.wheel); break; + case 3 : if (phase) X = (X1+=disp.wheel); else X = (X0+=disp.wheel); break; + } + disp.wheel = 0; + } else key = ~0U; + } + if ((disp.button&1)!=old_button) { + switch (phase++) { + case 0 : X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; clicked_area = area; break; + case 1 : X1 = X; Y1 = Y; Z1 = Z; break; + } + old_button = disp.button&1; + } + if (depth>1 && (X!=oX || Y!=oY || Z!=oZ)) visu0.assign(); + } + + if (phase) { + if (!coords_type) shape_selected = phase?true:false; + else { + if (depth>1) shape_selected = (phase==3)?true:false; + else shape_selected = (phase==2)?true:false; + } + } + + if (X0<0) X0 = 0; if (X0>=dimx()) X0 = dimx()-1; if (Y0<0) Y0 = 0; if (Y0>=dimy()) Y0 = dimy()-1; + if (Z0<0) Z0 = 0; if (Z0>=dimz()) Z0 = dimz()-1; + if (X1<1) X1 = 0; if (X1>=dimx()) X1 = dimx()-1; if (Y1<0) Y1 = 0; if (Y1>=dimy()) Y1 = dimy()-1; + if (Z1<0) Z1 = 0; if (Z1>=dimz()) Z1 = dimz()-1; + + // Draw visualization image on the display + if (oX!=X || oY!=Y || oZ!=Z || !visu0) { + if (!visu0) { + CImg tmp, tmp0; + if (depth!=1) { + tmp0 = (!phase)?get_projections2d(X0,Y0,Z0):get_projections2d(X1,Y1,Z1); + tmp = tmp0.get_channels(0,cimg::min(2U,dim-1)); + } else tmp = get_channels(0,cimg::min(2U,dim-1)); + switch (old_normalization) { + case 0 : visu0 = tmp; break; + case 3 : + if (cimg::type::is_float()) visu0 = tmp.normalize(0,(T)255); + else { + const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); + visu0.assign(tmp.width,tmp.height,1,tmp.dim); + unsigned char *ptrd = visu0.end(); + cimg_for(tmp,ptrs,Tuchar) *(--ptrd) = (unsigned char)((*ptrs-m)*255.0f/(M-m)); + } break; + default : visu0 = tmp.normalize(0,255); + } + visu0.resize(disp); + } + visu = visu0; + if (!color) { + if (visu.mean()<200) { + foreground_color[0] = foreground_color[1] = foreground_color[2] = 255; + background_color[0] = background_color[1] = background_color[2] = 0; + } else { + foreground_color[0] = foreground_color[1] = foreground_color[2] = 0; + background_color[0] = background_color[1] = background_color[2] = 255; + } + } + + const int d = (depth>1)?depth:0; + if (phase) switch (coords_type) { + case 1 : { + const int + x0 = (int)((X0+0.5f)*disp.width/(width+d)), + y0 = (int)((Y0+0.5f)*disp.height/(height+d)), + x1 = (int)((X1+0.5f)*disp.width/(width+d)), + y1 = (int)((Y1+0.5f)*disp.height/(height+d)); + visu.draw_arrow(x0,y0,x1,y1,foreground_color,0.6f,30,5,hatch); + if (d) { + const int + zx0 = (int)((width+Z0+0.5f)*disp.width/(width+d)), + zx1 = (int)((width+Z1+0.5f)*disp.width/(width+d)), + zy0 = (int)((height+Z0+0.5f)*disp.height/(height+d)), + zy1 = (int)((height+Z1+0.5f)*disp.height/(height+d)); + visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.6f,30,5,hatch). + draw_arrow(x0,zy0,x1,zy1,foreground_color,0.6f,30,5,hatch); + } + } break; + case 2 : { + const int + x0 = (X0=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,foreground_color,0.4f,~0U); + } + + if (my<12) text_down = true; + if (my>=visu.dimy()-11) text_down = false; + if (!coords_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X1) cimg_std::sprintf(text,"Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z); + else cimg_std::sprintf(text,"Point (%d,%d) = [ ",origX+X,origY+Y); + char *ctext = text + cimg::strlen(text), *const ltext = text + 512; + for (unsigned int k=0; k::format(),cimg::type::format((*this)(X,Y,Z,k))); + ctext = text + cimg::strlen(text); + *(ctext++) = ' '; *ctext = '\0'; + } + cimg_std::sprintf(text + cimg::strlen(text),"]"); + } + } else switch (coords_type) { + case 1 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = cimg_std::sqrt(dX*dX+dY*dY+dZ*dZ); + if (depth>1) cimg_std::sprintf(text,"Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g", + origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm); + else cimg_std::sprintf(text,"Vect (%d,%d)-(%d,%d), Norm = %g", + origX+X0,origY+Y0,origX+X1,origY+Y1,norm); + } break; + case 2 : + if (depth>1) cimg_std::sprintf(text,"Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d)", + origX+(X01) cimg_std::sprintf(text,"Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d)", + origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1, + 1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1)); + else cimg_std::sprintf(text,"Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d)", + origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1)); + + } + if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.dimy()-11:0,text,foreground_color,background_color,0.7f,11); + disp.display(visu).wait(25); + } else if (!shape_selected) disp.wait(); + + if (disp.is_resized) { disp.resize(false); old_is_resized = true; disp.is_resized = false; visu0.assign(); } + } + + // Return result + CImg res(1,6,1,1,-1); + if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } + if (shape_selected) { + if (coords_type==2) { + if (X0>X1) cimg::swap(X0,X1); + if (Y0>Y1) cimg::swap(Y0,Y1); + if (Z0>Z1) cimg::swap(Z0,Z1); + } + if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; + switch (coords_type) { + case 1 : + case 2 : res[3] = X1; res[4] = Y1; res[5] = Z1; + default : res[0] = X0; res[1] = Y0; res[2] = Z0; + } + } + disp.button = 0; + disp.normalization = old_normalization; + disp.is_resized = old_is_resized; + if (key!=~0U) disp.key = key; + return res; + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(CImgDisplay& disp, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return _display_object3d(disp,0,points,points.width,primitives,colors,opacities,centering,render_static, + render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(const char *const title, + const CImg& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgDisplay disp; + return _display_object3d(disp,title,points,points.width,primitives,colors,opacities,centering,render_static, + render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(CImgDisplay& disp, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return _display_object3d(disp,0,points,points.size,primitives,colors,opacities,centering,render_static, + render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(const char *const title, + const CImgList& points, const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + CImgDisplay disp; + return _display_object3d(disp,title,points,points.size,primitives,colors,opacities,centering,render_static, + render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(CImgDisplay &disp, + const tp& points, const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,points,primitives,colors,CImg(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(const char *const title, + const tp& points, const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,points,primitives,colors,CImg(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(CImgDisplay &disp, + const tp& points, const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,points,primitives,CImgList(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(const char *const title, + const tp& points, const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,points,primitives,CImgList(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(CImgDisplay &disp, + const tp& points, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(disp,points,CImgList(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + //! High-level interface for displaying a 3d object. + template + const CImg& display_object3d(const char *const title, + const tp& points, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool double_sided=false, const float focale=500, + const float specular_light=0.2f, const float specular_shine=0.1f, + const bool display_axes=true, float *const pose_matrix=0) const { + return display_object3d(title,points,CImgList(),centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + + T _display_object3d_at2(const int i, const int j) const { + return atXY(i,j,0,0,0); + } + + template + const CImg& _display_object3d(CImgDisplay& disp, const char *const title, + const tp& points, const unsigned int Npoints, + const CImgList& primitives, + const CImgList& colors, const to& opacities, + const bool centering, + const int render_static, const int render_motion, + const bool double_sided, const float focale, + const float specular_light, const float specular_shine, + const bool display_axes, float *const pose_matrix) const { + + // Check input arguments + if (!points || !Npoints) + throw CImgArgumentException("CImg<%s>::display_object3d() : Given points are empty.", + pixel_type()); + if (is_empty()) { + if (disp) return CImg(disp.width,disp.height,1,colors[0].size(),0). + _display_object3d(disp,title,points,Npoints,primitives,colors,opacities,centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + else return CImg(cimg_fitscreen(640,480,1),1,colors[0].size(),0). + _display_object3d(disp,title,points,Npoints,primitives,colors,opacities,centering, + render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + if (!primitives) { + CImgList nprimitives(Npoints,1,1,1,1); + cimglist_for(nprimitives,l) nprimitives(l,0) = l; + return _display_object3d(disp,title,points,Npoints,nprimitives,colors,opacities, + centering,render_static,render_motion,double_sided,focale,specular_light,specular_shine, + display_axes,pose_matrix); + } + if (!disp) { + char ntitle[64] = { 0 }; if (!title) { cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); } + disp.assign(cimg_fitscreen(width,height,depth),title?title:ntitle,1); + } + + CImgList _colors; + if (!colors) _colors.insert(primitives.size,CImg::vector(200,200,200)); + const CImgList &ncolors = colors?colors:_colors; + + // Init 3D objects and compute object statistics + CImg + pose, rot_mat, zbuffer, + centered_points = centering?CImg(Npoints,3):CImg(), + rotated_points(Npoints,3), + bbox_points, rotated_bbox_points, + axes_points, rotated_axes_points, + bbox_opacities, axes_opacities; + CImgList bbox_primitives, axes_primitives; + CImgList bbox_colors, bbox_colors2, axes_colors; + float dx = 0, dy = 0, dz = 0, ratio = 1; + + T minval = (T)0, maxval = (T)255; + if (disp.normalization && colors) { + minval = colors.minmax(maxval); + if (minval==maxval) { minval = (T)0; maxval = (T)255; } + } + const float meanval = (float)mean(); + bool color_model = true; + if (cimg::abs(meanval-minval)>cimg::abs(meanval-maxval)) color_model = false; + const CImg + background_color(1,1,1,dim,color_model?minval:maxval), + foreground_color(1,1,1,dim,color_model?maxval:minval); + + float xm = cimg::type::max(), xM = 0, ym = xm, yM = 0, zm = xm, zM = 0; + for (unsigned int i = 0; ixM) xM = x; + if (yyM) yM = y; + if (zzM) zM = z; + } + const float delta = cimg::max(xM-xm,yM-ym,zM-zm); + + if (display_axes) { + rotated_axes_points = axes_points.assign(7,3,1,1, + 0,20,0,0,22,-6,-6, + 0,0,20,0,-6,22,-6, + 0,0,0,20,0,0,22); + axes_opacities.assign(3,1,1,1,1); + axes_colors.assign(3,dim,1,1,1,foreground_color[0]); + axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); + } + + // Begin user interaction loop + CImg visu0(*this), visu; + bool init = true, clicked = false, redraw = true; + unsigned int key = 0; + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + disp.show().flush(); + + while (!disp.is_closed && !key) { + + // Init object position and scale if necessary + if (init) { + ratio = delta>0?(2.0f*cimg::min(disp.width,disp.height)/(3.0f*delta)):0; + dx = 0.5f*(xM + xm); dy = 0.5f*(yM + ym); dz = 0.5f*(zM + zm); + if (centering) { + cimg_forX(centered_points,l) { + centered_points(l,0) = (float)((points(l,0) - dx)*ratio); + centered_points(l,1) = (float)((points(l,1) - dy)*ratio); + centered_points(l,2) = (float)((points(l,2) - dz)*ratio); + } + } + + if (render_static<0 || render_motion<0) { + rotated_bbox_points = bbox_points.assign(8,3,1,1, + xm,xM,xM,xm,xm,xM,xM,xm, + ym,ym,yM,yM,ym,ym,yM,yM, + zm,zm,zm,zm,zM,zM,zM,zM); + bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6); + bbox_colors.assign(6,dim,1,1,1,background_color[0]); + bbox_colors2.assign(6,dim,1,1,1,foreground_color[0]); + bbox_opacities.assign(bbox_colors.size,1,1,1,0.3f); + } + + if (!pose) { + if (pose_matrix) pose = CImg(pose_matrix,4,4,1,1,false); + else pose = CImg::identity_matrix(4); + } + init = false; + redraw = true; + } + + // Rotate and Draw 3D object + if (redraw) { + const float + r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), + r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), + r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); + if ((clicked && render_motion>=0) || (!clicked && render_static>=0)) { + if (centering) cimg_forX(centered_points,l) { + const float x = centered_points(l,0), y = centered_points(l,1), z = centered_points(l,2); + rotated_points(l,0) = r00*x + r10*y + r20*z + r30; + rotated_points(l,1) = r01*x + r11*y + r21*z + r31; + rotated_points(l,2) = r02*x + r12*y + r22*z + r32; + } else for (unsigned int l = 0; l0)?zbuffer.fill(0).ptr():0); + + // Draw axes + if (display_axes) { + const float Xaxes = 25, Yaxes = visu.height - 35.0f; + cimg_forX(axes_points,l) { + const float x = axes_points(l,0), y = axes_points(l,1), z = axes_points(l,2); + rotated_axes_points(l,0) = r00*x + r10*y + r20*z; + rotated_axes_points(l,1) = r01*x + r11*y + r21*z; + rotated_axes_points(l,2) = r02*x + r12*y + r22*z; + } + axes_opacities(0,0) = (rotated_axes_points(1,2)>0)?0.5f:1.0f; + axes_opacities(1,0) = (rotated_axes_points(2,2)>0)?0.5f:1.0f; + axes_opacities(2,0) = (rotated_axes_points(3,2)>0)?0.5f:1.0f; + visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_points,axes_primitives,axes_colors,axes_opacities,1,false,focale). + draw_text((int)(Xaxes+rotated_axes_points(4,0)), + (int)(Yaxes+rotated_axes_points(4,1)), + "X",axes_colors[0].data,0,axes_opacities(0,0),11). + draw_text((int)(Xaxes+rotated_axes_points(5,0)), + (int)(Yaxes+rotated_axes_points(5,1)), + "Y",axes_colors[1].data,0,axes_opacities(1,0),11). + draw_text((int)(Xaxes+rotated_axes_points(6,0)), + (int)(Yaxes+rotated_axes_points(6,1)), + "Z",axes_colors[2].data,0,axes_opacities(2,0),11); + } + visu.display(disp); + if (!clicked || render_motion==render_static) redraw = false; + } + + // Handle user interaction + disp.wait(); + if ((disp.button || disp.wheel) && disp.mouse_x>=0 && disp.mouse_y>=0) { + redraw = true; + if (!clicked) { x0 = x1 = disp.mouse_x; y0 = y1 = disp.mouse_y; if (!disp.wheel) clicked = true; } + else { x1 = disp.mouse_x; y1 = disp.mouse_y; } + if (disp.button&1) { + const float + R = 0.45f*cimg::min(disp.width,disp.height), + R2 = R*R, + u0 = (float)(x0-disp.dimx()/2), + v0 = (float)(y0-disp.dimy()/2), + u1 = (float)(x1-disp.dimx()/2), + v1 = (float)(y1-disp.dimy()/2), + n0 = (float)cimg_std::sqrt(u0*u0+v0*v0), + n1 = (float)cimg_std::sqrt(u1*u1+v1*v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)cimg_std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)cimg_std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)), + u = nv0*nw1-nw0*nv1, + v = nw0*nu1-nu0*nw1, + w = nv0*nu1-nu0*nv1, + n = (float)cimg_std::sqrt(u*u+v*v+w*w), + alpha = (float)cimg_std::asin(n/R2); + rot_mat = CImg::rotation_matrix(u,v,w,alpha); + rot_mat *= pose.get_crop(0,0,2,2); + pose.draw_image(rot_mat); + x0=x1; y0=y1; + } + if (disp.button&2) { pose(3,2)+=(y1-y0); x0 = x1; y0 = y1; } + if (disp.wheel) { pose(3,2)-=focale*disp.wheel/10; disp.wheel = 0; } + if (disp.button&4) { pose(3,0)+=(x1-x0); pose(3,1)+=(y1-y0); x0 = x1; y0 = y1; } + if ((disp.button&1) && (disp.button&2)) { init = true; disp.button = 0; x0 = x1; y0 = y1; pose = CImg::identity_matrix(4); } + } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + + switch (key = disp.key) { + case 0 : case cimg::keyCTRLLEFT : disp.key = key = 0; break; + case cimg::keyD: if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false).is_resized = true; + disp.key = key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false).is_resized = true; + disp.key = key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(width,height,depth),false).is_resized = true; + disp.key = key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT) { + disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; + disp.key = key = 0; + } break; + case cimg::keyZ : if (disp.is_keyCTRLLEFT) { // Enable/Disable Z-buffer + if (zbuffer) zbuffer.assign(); + else zbuffer.assign(disp.width,disp.height); + disp.key = key = 0; redraw = true; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT) { // Save snapshot + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); + if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); + } while (file); + (+visu).draw_text(2,2,"Saving BMP snapshot...",foreground_color,background_color,1,11).display(disp); + visu.save(filename); + visu.draw_text(2,2,"Snapshot '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); + disp.key = key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT) { // Save object as an .OFF file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filename,"CImg_%.4u.off",snap_number++); + if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); + } while (file); + visu.draw_text(2,2,"Saving object...",foreground_color,background_color,1,11).display(disp); + points.save_off(filename,primitives,ncolors); + visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); + disp.key = key = 0; + } break; +#ifdef cimg_use_board + case cimg::keyP : if (disp.is_keyCTRLLEFT) { // Save object as a .EPS file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filename,"CImg_%.4u.eps",snap_number++); + if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); + } while (file); + visu.draw_text(2,2,"Saving EPS snapshot...",foreground_color,background_color,1,11).display(disp); + BoardLib::Board board; + (+visu).draw_object3d(board,visu.width/2.0f, visu.height/2.0f, 0, + rotated_points,primitives,ncolors,opacities,clicked?render_motion:render_static, + double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000,specular_light,specular_shine, + zbuffer.fill(0).ptr()); + board.saveEPS(filename); + visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); + disp.key = key = 0; + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT) { // Save object as a .SVG file + static unsigned int snap_number = 0; + char filename[32] = { 0 }; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filename,"CImg_%.4u.svg",snap_number++); + if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); + } while (file); + visu.draw_text(2,2,"Saving SVG snapshot...",foreground_color,background_color,1,11).display(disp); + BoardLib::Board board; + (+visu).draw_object3d(board,visu.width/2.0f, visu.height/2.0f, 0, + rotated_points,primitives,ncolors,opacities,clicked?render_motion:render_static, + double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000,specular_light,specular_shine, + zbuffer.fill(0).ptr()); + board.saveSVG(filename); + visu.draw_text(2,2,"Object '%s' saved.",foreground_color,background_color,1,11,filename).display(disp); + disp.key = key = 0; + } break; +#endif + } + if (disp.is_resized) { disp.resize(false); visu0 = get_resize(disp,1); if (zbuffer) zbuffer.assign(disp.width,disp.height); redraw = true; } + } + if (pose_matrix) cimg_std::memcpy(pose_matrix,pose.data,16*sizeof(float)); + disp.button = 0; + disp.key = key; + return *this; + } + + //! High-level interface for displaying a graph. + const CImg& display_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + const unsigned int siz = width*height*depth, onormalization = disp.normalization; + if (!disp) { char ntitle[64] = { 0 }; cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); } + disp.show().flush().normalization = 0; + double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; + if (nxmin==nxmax) { nxmin = 0; nxmax = siz - 1.0; } + int x0 = 0, x1 = size()/dimv()-1, key = 0; + + for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed; ) { + if (reset_view) { x0 = 0; x1 = size()/dimv()-1; y0 = ymin; y1 = ymax; reset_view = false; } + CImg zoom(x1-x0+1,1,1,dimv()); + cimg_forV(*this,k) zoom.get_shared_channel(k) = CImg(ptr(x0,0,0,k),x1-x0+1,1,1,1,true); + + if (y0==y1) y0 = zoom.minmax(y1); + if (y0==y1) { --y0; ++y1; } + const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, + labelx,nxmin + x0*(nxmax-nxmin)/siz,nxmin + x1*(nxmax-nxmin)/siz, + labely,y0,y1); + + const int mouse_x = disp.mouse_x, mouse_y = disp.mouse_y; + if (selection[0]>=0 && selection[2]>=0) { + x1 = x0 + selection[2]; + x0 += selection[0]; + if (x0==x1) reset_view = true; + if (selection[1]>=0 && selection[3]>=0) { + y0 = y1 - selection[3]*(y1-y0)/(disp.dimy()-32); + y1 -= selection[1]*(y1-y0)/(disp.dimy()-32); + } + } else { + bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; + switch (key = disp.key) { + case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; break; + case cimg::keyPADADD : go_in = true; key = 0; break; + case cimg::keyPADSUB : go_out = true; key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; key = 0; break; + case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; key = 0; break; + case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; key = 0; break; + case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; break; + case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; break; + case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; break; + case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; break; + } + if (disp.wheel) go_out = !(go_in = disp.wheel>0); + + if (go_in) { + const int + xsiz = x1 - x0, + mx = (mouse_x-16)*xsiz/(disp.dimx()-32), + cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx)); + if (x1-x0>4) { + x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8; + if (disp.is_keyCTRLLEFT) { + const double + ysiz = y1 - y0, + my = (mouse_y-16)*ysiz/(disp.dimy()-32), + cy = y1 - (my<0?0:(my>=ysiz?ysiz:my)); + y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8; + } else y0 = y1 = 0; + } + } + if (go_out) { + const int deltax = (x1-x0)/8, ndeltax = deltax?deltax:(siz>1?1:0); + x0-=ndeltax; x1+=ndeltax; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz-1; } + if (x1>=(int)siz) { x0-=(x1-siz+1); x1 = (int)siz-1; if (x0<0) x0 = 0; } + if (disp.is_keyCTRLLEFT) { + const double deltay = (y1-y0)/8, ndeltay = deltay?deltay:0.01; + y0-=ndeltay; y1+=ndeltay; + } + } + if (go_left) { + const int delta = (x1-x0)/5, ndelta = delta?delta:1; + if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + go_left = false; + } + if (go_right) { + const int delta = (x1-x0)/5, ndelta = delta?delta:1; + if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } + else { x0+=(siz-1-x1); x1 = siz-1; } + go_right = false; + } + if (go_up) { + const double delta = (y1-y0)/10, ndelta = delta?delta:1; + y0+=ndelta; y1+=ndelta; + go_up = false; + } + if (go_down) { + const double delta = (y1-y0)/10, ndelta = delta?delta:1; + y0-=ndelta; y1-=ndelta; + go_down = false; + } + } + } + disp.normalization = onormalization; + return *this; + } + + //! High-level interface for displaying a graph. + const CImg& display_graph(const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + char ntitle[64] = { 0 }; if (!title) cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); + CImgDisplay disp(cimg_fitscreen(640,480,1),title?title:ntitle,0); + return display_graph(disp,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax); + } + + //! Select sub-graph in a graph. + CImg get_select_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::display_graph() : Instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),width,height,depth,dim,data); + const unsigned int siz = width*height*depth, onormalization = disp.normalization; + if (!disp) { char ntitle[64] = { 0 }; cimg_std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); } + disp.show().key = disp.normalization = disp.button = disp.wheel = 0; // Must keep 'key' field unchanged. + double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; + if (nymin==nymax) nymin = (Tfloat)minmax(nymax); + if (nymin==nymax) { --nymin; ++nymax; } + if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; } + + const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 220,220,220 }; + const unsigned char gray2[] = { 110,110,110 }, ngray[] = { 35,35,35 }; + static unsigned int odimv = 0; + static CImg palette; + if (odimv!=dim) { + odimv = dim; + palette = CImg(3,dim,1,1,120).noise(70,1); + if (dim==1) { palette[0] = palette[1] = 120; palette[2] = 200; } + else { + palette(0,0) = 220; palette(1,0) = 10; palette(2,0) = 10; + if (dim>1) { palette(0,1) = 10; palette(1,1) = 220; palette(2,1) = 10; } + if (dim>2) { palette(0,2) = 10; palette(1,2) = 10; palette(2,2) = 220; } + } + } + + CImg visu0, visu, graph, text, axes; + const unsigned int whz = width*height*depth; + int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; + char message[1024] = { 0 }; + unsigned int okey = 0, obutton = 0; + CImg_3x3(I,unsigned char); + + for (bool selected = false; !selected && !disp.is_closed && !okey && !disp.wheel; ) { + const int mouse_x = disp.mouse_x, mouse_y = disp.mouse_y; + const unsigned int key = disp.key, button = disp.button; + + // Generate graph representation. + if (!visu0) { + visu0.assign(disp.dimx(),disp.dimy(),1,3,220); + const int gdimx = disp.dimx() - 32, gdimy = disp.dimy() - 32; + if (gdimx>0 && gdimy>0) { + graph.assign(gdimx,gdimy,1,3,255); + graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); + cimg_forV(*this,k) graph.draw_graph(get_shared_channel(k),&palette(0,k),(plot_type!=3 || dim==1)?1:0.6f, + plot_type,vertex_type,nymax,nymin); + + axes.assign(gdimx,gdimy,1,1,0); + const float + dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin), + px = (float)cimg_std::pow(10.0,(int)cimg_std::log10(dx)-2.0), + py = (float)cimg_std::pow(10.0,(int)cimg_std::log10(dy)-2.0); + const CImg + seqx = CImg::sequence(1 + gdimx/60,nxmin,nxmax).round(px), + seqy = CImg::sequence(1 + gdimy/60,nymax,nymin).round(py); + axes.draw_axis(seqx,seqy,white); + if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray); + if (nymax<0) axes.draw_axis(seqx,0,gray); + if (nxmin>0) axes.draw_axis(0,seqy,gray); + if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray); + + cimg_for3x3(axes,x,y,0,0,I) + if (Icc) { + if (Icc==255) cimg_forV(graph,k) graph(x,y,k) = 0; + else cimg_forV(graph,k) graph(x,y,k) = (unsigned char)(2*graph(x,y,k)/3); + } + else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forV(graph,k) graph(x,y,k) = (graph(x,y,k)+255)/2; + + visu0.draw_image(16,16,graph); + visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2). + draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white); + } else graph.assign(); + text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1); + visu0.draw_image((visu0.dimx()-text.dimx())/2,visu0.dimy()-14,~text); + text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1).rotate(-90); + visu0.draw_image(2,(visu0.dimy()-text.dimy())/2,~text); + visu.assign(); + } + + // Generate and display current view. + if (!visu) { + visu.assign(visu0); + if (graph && x0>=0 && x1>=0) { + const int + nx0 = x0<=x1?x0:x1, + nx1 = x0<=x1?x1:x0, + ny0 = y0<=y1?y0:y1, + ny1 = y0<=y1?y1:y0, + sx0 = 16 + nx0*(visu.dimx()-32)/whz, + sx1 = 15 + (nx1+1)*(visu.dimx()-32)/whz, + sy0 = 16 + ny0, + sy1 = 16 + ny1; + + if (y0>=0 && y1>=0) + visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); + else visu.draw_rectangle(sx0,0,sx1,visu.dimy()-17,gray,0.5f). + draw_line(sx0,16,sx0,visu.dimy()-17,black,0.5f,0xCCCCCCCCU). + draw_line(sx1,16,sx1,visu.dimy()-17,black,0.5f,0xCCCCCCCCU); + } + if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) + cimg_std::sprintf(message,"Value[%g] = ( %g %g %g ... %g %g %g )",cx, + (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), + (double)(*this)(x,0,0,dim-4),(double)(*this)(x,0,0,dim-3),(double)(*this)(x,0,0,dim-1)); + else { + cimg_std::sprintf(message,"Value[%g] = ( ",cx); + cimg_forV(*this,k) cimg_std::sprintf(message+cimg::strlen(message),"%g ",(double)(*this)(x,0,0,k)); + cimg_std::sprintf(message+cimg::strlen(message),")"); + } + if (x0>=0 && x1>=0) { + const int + nx0 = x0<=x1?x0:x1, + nx1 = x0<=x1?x1:x0, + ny0 = y0<=y1?y0:y1, + ny1 = y0<=y1?y1:y0; + const double + cx0 = nxmin + nx0*(nxmax-nxmin)/(visu.dimx()-32), + cx1 = nxmin + nx1*(nxmax-nxmin)/(visu.dimx()-32), + cy0 = nymax - ny0*(nymax-nymin)/(visu.dimy()-32), + cy1 = nymax - ny1*(nymax-nymin)/(visu.dimy()-32); + if (y0>=0 && y1>=0) + cimg_std::sprintf(message+cimg::strlen(message)," - Range ( %g, %g ) - ( %g, %g )",cx0,cy0,cx1,cy1); + else + cimg_std::sprintf(message+cimg::strlen(message)," - Range [ %g - %g ]",cx0,cx1); + } + text.assign().draw_text(0,0,message,white,ngray,1); + visu.draw_image((visu.dimx()-text.dimx())/2,2,~text); + } + visu.display(disp); + } + + // Test keys. + switch (okey = key) { + case cimg::keyCTRLLEFT : okey = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width/2,3*disp.height/2,1,128,-100,true),false).is_resized = true; + disp.key = okey = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(2*disp.width/3,2*disp.height/3,1),false).is_resized = true; + disp.key = okey = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT) { + disp.normalscreen().resize(cimg_fitscreen(640,480,1),false).is_resized = true; + disp.key = okey = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT) { + disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; + disp.key = okey = 0; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + char filename[32] = { 0 }; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); + if ((file=cimg_std::fopen(filename,"r"))!=0) cimg_std::fclose(file); + } while (file); + (+screen).draw_text(2,2,"Saving BMP snapshot...",black,gray,1,11).display(disp); + screen.save(filename); + screen.draw_text(2,2,"Snapshot '%s' saved.",black,gray,1,11,filename).display(disp); + } + disp.key = okey = 0; + } break; + } + + // Handle mouse motion and mouse buttons + if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { + visu.assign(); + if (disp.mouse_x>=0 && disp.mouse_y>=0) { + const int + mx = (mouse_x-16)*(int)whz/(disp.dimx()-32), + cx = mx<0?0:(mx>=(int)whz?whz-1:mx), + my = mouse_y-16, + cy = my<=0?0:(my>=(disp.dimy()-32)?(disp.dimy()-32):my); + if (button&1) { if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }} + else if (button&2) { if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }} + else if (obutton) { x1 = cx; y1 = y1>=0?cy:-1; selected = true; } + } else if (!button && obutton) selected = true; + obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; + } + if (disp.is_resized) { disp.resize(false); visu0.assign(); } + if (visu && visu0) disp.wait(); + } + disp.normalization = onormalization; + if (x1(4,1,1,1,x0,y0,x1,y1); + } + + //@} + //--------------------------- + // + //! \name Image File Loading + //@{ + //--------------------------- + + //! Load an image from a file. + /** + \param filename is the name of the image file to load. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load a .cimg file. + **/ + CImg& load(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load() : Cannot load (null) filename.", + pixel_type()); + const char *ext = cimg::split_filename(filename); + const unsigned int odebug = cimg::exception_mode(); + cimg::exception_mode() = 0; + assign(); + try { +#ifdef cimg_load_plugin + cimg_load_plugin(filename); +#endif +#ifdef cimg_load_plugin1 + cimg_load_plugin1(filename); +#endif +#ifdef cimg_load_plugin2 + cimg_load_plugin2(filename); +#endif +#ifdef cimg_load_plugin3 + cimg_load_plugin3(filename); +#endif +#ifdef cimg_load_plugin4 + cimg_load_plugin4(filename); +#endif +#ifdef cimg_load_plugin5 + cimg_load_plugin5(filename); +#endif +#ifdef cimg_load_plugin6 + cimg_load_plugin6(filename); +#endif +#ifdef cimg_load_plugin7 + cimg_load_plugin7(filename); +#endif +#ifdef cimg_load_plugin8 + cimg_load_plugin8(filename); +#endif + // ASCII formats + if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); + if (!cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) load_dlm(filename); + + // 2D binary formats + if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); + if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); + if (!cimg::strcasecmp(ext,"png")) load_png(filename); + if (!cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"pnm")) load_pnm(filename); + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + if (!cimg::strcasecmp(ext,"cr2") || + !cimg::strcasecmp(ext,"crw") || + !cimg::strcasecmp(ext,"dcr") || + !cimg::strcasecmp(ext,"mrw") || + !cimg::strcasecmp(ext,"nef") || + !cimg::strcasecmp(ext,"orf") || + !cimg::strcasecmp(ext,"pix") || + !cimg::strcasecmp(ext,"ptx") || + !cimg::strcasecmp(ext,"raf") || + !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); + + // 3D binary formats + if (!cimg::strcasecmp(ext,"dcm") || + !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); + if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) load_analyze(filename); + if (!cimg::strcasecmp(ext,"par") || + !cimg::strcasecmp(ext,"rec")) load_parrec(filename); + if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); + if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); + if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + *ext=='\0') return load_cimg(filename); + + // Archive files + if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + + // Image sequences + if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename); + if (is_empty()) throw CImgIOException("CImg<%s>::load()",pixel_type()); + } catch (CImgException& e) { + if (!cimg::strncasecmp(e.message,"cimg::fopen()",13)) { + cimg::exception_mode() = odebug; + throw CImgIOException("CImg<%s>::load() : File '%s' cannot be opened.",pixel_type(),filename); + } else try { + const char *const ftype = cimg::file_type(0,filename); + assign(); + if (!cimg::strcmp(ftype,"pnm")) load_pnm(filename); + if (!cimg::strcmp(ftype,"bmp")) load_bmp(filename); + if (!cimg::strcmp(ftype,"jpeg")) load_jpeg(filename); + if (!cimg::strcmp(ftype,"pan")) load_pandore(filename); + if (!cimg::strcmp(ftype,"png")) load_png(filename); + if (!cimg::strcmp(ftype,"tiff")) load_tiff(filename); + if (is_empty()) throw CImgIOException("CImg<%s>::load()",pixel_type()); + } catch (CImgException&) { + try { + load_other(filename); + } catch (CImgException&) { + assign(); + } + } + } + cimg::exception_mode() = odebug; + if (is_empty()) + throw CImgIOException("CImg<%s>::load() : File '%s', format not recognized.",pixel_type(),filename); + return *this; + } + + static CImg get_load(const char *const filename) { + return CImg().load(filename); + } + + //! Load an image from an ASCII file. + CImg& load_ascii(const char *const filename) { + return _load_ascii(0,filename); + } + + static CImg get_load_ascii(const char *const filename) { + return CImg().load_ascii(filename); + } + + //! Load an image from an ASCII file. + CImg& load_ascii(cimg_std::FILE *const file) { + return _load_ascii(file,0); + } + + static CImg get_load_ascii(cimg_std::FILE *const file) { + return CImg().load_ascii(file); + } + + CImg& _load_ascii(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_ascii() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char line[256] = { 0 }; + int err = cimg_std::fscanf(nfile,"%*[^0-9]%255[^\n]",line); + unsigned int off, dx = 0, dy = 1, dz = 1, dv = 1; + cimg_std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dv); + err = cimg_std::fscanf(nfile,"%*[^0-9.+-]"); + if (!dx || !dy || !dz || !dv) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_ascii() : File '%s', invalid .ASC header, specified image dimensions are (%u,%u,%u,%u).", + pixel_type(),filename?filename:"(FILE*)",dx,dy,dz,dv); + } + assign(dx,dy,dz,dv); + const unsigned long siz = size(); + double val; + T *ptr = data; + for (err = 1, off = 0; off::load_ascii() : File '%s', only %u/%lu values read.", + pixel_type(),filename?filename:"(FILE*)",off-1,siz); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a DLM file. + CImg& load_dlm(const char *const filename) { + return _load_dlm(0,filename); + } + + static CImg get_load_dlm(const char *const filename) { + return CImg().load_dlm(filename); + } + + //! Load an image from a DLM file. + CImg& load_dlm(cimg_std::FILE *const file) { + return _load_dlm(file,0); + } + + static CImg get_load_dlm(cimg_std::FILE *const file) { + return CImg().load_dlm(file); + } + + CImg& _load_dlm(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_dlm() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + assign(256,256); + char c, delimiter[256] = { 0 }, tmp[256]; + unsigned int cdx = 0, dx = 0, dy = 0; + int oerr = 0, err; + double val; + while ((err = cimg_std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))!=EOF) { + oerr = err; + if (err>0) (*this)(cdx++,dy) = (T)val; + if (cdx>=width) resize(width+256,1,1,1,0); + c = 0; if (!cimg_std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { + dx = cimg::max(cdx,dx); + ++dy; + if (dy>=height) resize(width,height+256,1,1,0); + cdx = 0; + } + } + if (cdx && oerr==1) { dx=cdx; ++dy; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_dlm() : File '%s', invalid DLM file, specified image dimensions are (%u,%u).", + pixel_type(),filename?filename:"(FILE*)",dx,dy); + } + resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a BMP file. + CImg& load_bmp(const char *const filename) { + return _load_bmp(0,filename); + } + + static CImg get_load_bmp(const char *const filename) { + return CImg().load_bmp(filename); + } + + //! Load an image from a BMP file. + CImg& load_bmp(cimg_std::FILE *const file) { + return _load_bmp(file,0); + } + + static CImg get_load_bmp(cimg_std::FILE *const file) { + return CImg().load_bmp(file); + } + + CImg& _load_bmp(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_bmp() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned char header[64]; + cimg::fread(header,54,nfile); + if (header[0]!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_bmp() : Invalid valid BMP file (filename '%s').", + pixel_type(),filename?filename:"(FILE*)"); + } + assign(); + + // Read header and pixel buffer + int + file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), + offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), + dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), + dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), + compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), + nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), + bpp = header[0x1C] + (header[0x1D]<<8), + *palette = 0; + const int + dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), + align = (4-dx_bytes%4)%4, + buf_size = cimg::min(cimg::abs(dy)*(dx_bytes+align),file_size-offset); + + if (bpp<16) { if (!nb_colors) nb_colors=1<0) cimg_std::fseek(nfile,xoffset,SEEK_CUR); + unsigned char *buffer = new unsigned char[buf_size], *ptrs = buffer; + cimg::fread(buffer,buf_size,nfile); + if (!file) cimg::fclose(nfile); + + // Decompress buffer (if necessary) + if (compression) { + delete[] buffer; + if (file) { + throw CImgIOException("CImg<%s>::load_bmp() : Not able to read a compressed BMP file using a *FILE input", + pixel_type()); + } else return load_other(filename); + } + + // Read pixel data + assign(dx,cimg::abs(dy),1,3); + switch (bpp) { + case 1 : { // Monochrome + for (int y=height-1; y>=0; --y) { + unsigned char mask = 0x80, val = 0; + cimg_forX(*this,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(palette+(val&mask?1:0)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask); + } ptrs+=align; } + } break; + case 4 : { // 16 colors + for (int y=height-1; y>=0; --y) { + unsigned char mask = 0xF0, val = 0; + cimg_forX(*this,x) { + if (mask==0xF0) val = *(ptrs++); + const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); + unsigned char *col = (unsigned char*)(palette+color); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask,4); + } ptrs+=align; } + } break; + case 8 : { // 256 colors + for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { + const unsigned char *col = (unsigned char*)(palette+*(ptrs++)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + } ptrs+=align; } + } break; + case 16 : { // 16 bits colors + for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { + const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); + const unsigned short col = (unsigned short)(c1|(c2<<8)); + (*this)(x,y,2) = (T)(col&0x1F); + (*this)(x,y,1) = (T)((col>>5)&0x1F); + (*this)(x,y,0) = (T)((col>>10)&0x1F); + } ptrs+=align; } + } break; + case 24 : { // 24 bits colors + for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + } ptrs+=align; } + } break; + case 32 : { // 32 bits colors + for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + ++ptrs; + } ptrs+=align; } + } break; + } + if (palette) delete[] palette; + delete[] buffer; + if (dy<0) mirror('y'); + return *this; + } + + //! Load an image from a JPEG file. + CImg& load_jpeg(const char *const filename) { + return _load_jpeg(0,filename); + } + + static CImg get_load_jpeg(const char *const filename) { + return CImg().load_jpeg(filename); + } + + //! Load an image from a JPEG file. + CImg& load_jpeg(cimg_std::FILE *const file) { + return _load_jpeg(file,0); + } + + static CImg get_load_jpeg(cimg_std::FILE *const file) { + return CImg().load_jpeg(file); + } + + CImg& _load_jpeg(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_jpeg() : Cannot load (null) filename.", + pixel_type()); +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException("CImg<%s>::load_jpeg() : File '(FILE*)' cannot be read without using libjpeg.", + pixel_type()); + else return load_other(filename); +#else + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo,nfile); + jpeg_read_header(&cinfo,TRUE); + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { + cimg::warn("CImg<%s>::load_jpeg() : Don't know how to read image '%s' with libpeg, trying ImageMagick's convert", + pixel_type(),filename?filename:"(FILE*)"); + if (!file) return load_other(filename); + else { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_jpeg() : Cannot read JPEG image '%s' using a *FILE input.", + pixel_type(),filename?filename:"(FILE*)"); + } + } + + const unsigned int row_stride = cinfo.output_width * cinfo.output_components; + unsigned char *buf = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components], *buf2 = buf; + JSAMPROW row_pointer[1]; + while (cinfo.output_scanline < cinfo.output_height) { + row_pointer[0] = &buf[cinfo.output_scanline*row_stride]; + jpeg_read_scanlines(&cinfo,row_pointer,1); + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + if (!file) cimg::fclose(nfile); + + assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); + switch (dim) { + case 1 : { + T *ptr_g = data; + cimg_forXY(*this,x,y) *(ptr_g++) = (T)*(buf2++); + } break; + case 3 : { + T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)*(buf2++); + *(ptr_g++) = (T)*(buf2++); + *(ptr_b++) = (T)*(buf2++); + } + } break; + case 4 : { + T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), + *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)*(buf2++); + *(ptr_g++) = (T)*(buf2++); + *(ptr_b++) = (T)*(buf2++); + *(ptr_a++) = (T)*(buf2++); + } + } break; + } + delete[] buf; + return *this; +#endif + } + + //! Load an image from a file, using Magick++ library. + // Added April/may 2006 by Christoph Hormann + // This is experimental code, not much tested, use with care. + CImg& load_magick(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_magick() : Cannot load (null) filename.", + pixel_type()); +#ifdef cimg_use_magick + Magick::Image image(filename); + const unsigned int W = image.size().width(), H = image.size().height(); + switch (image.type()) { + case Magick::PaletteMatteType : + case Magick::TrueColorMatteType : + case Magick::ColorSeparationType : { + assign(W,H,1,4); + T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2), *adata = ptr(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned int off = W*H; off; --off) { + *(rdata++) = (T)(pixels->red); + *(gdata++) = (T)(pixels->green); + *(bdata++) = (T)(pixels->blue); + *(adata++) = (T)(pixels->opacity); + ++pixels; + } + } break; + case Magick::PaletteType : + case Magick::TrueColorType : { + assign(W,H,1,3); + T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned int off = W*H; off; --off) { + *(rdata++) = (T)(pixels->red); + *(gdata++) = (T)(pixels->green); + *(bdata++) = (T)(pixels->blue); + ++pixels; + } + } break; + case Magick::GrayscaleMatteType : { + assign(W,H,1,2); + T *data = ptr(0,0,0,0), *adata = ptr(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned int off = W*H; off; --off) { + *(data++) = (T)(pixels->red); + *(adata++) = (T)(pixels->opacity); + ++pixels; + } + } break; + default : { + assign(W,H,1,1); + T *data = ptr(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (unsigned int off = W*H; off; --off) { + *(data++) = (T)(pixels->red); + ++pixels; + } + } + } +#else + throw CImgIOException("CImg<%s>::load_magick() : File '%s', Magick++ library has not been linked.", + pixel_type(),filename); +#endif + return *this; + } + + static CImg get_load_magick(const char *const filename) { + return CImg().load_magick(filename); + } + + //! Load an image from a PNG file. + CImg& load_png(const char *const filename) { + return _load_png(0,filename); + } + + static CImg get_load_png(const char *const filename) { + return CImg().load_png(filename); + } + + //! Load an image from a PNG file. + CImg& load_png(cimg_std::FILE *const file) { + return _load_png(file,0); + } + + static CImg get_load_png(cimg_std::FILE *const file) { + return CImg().load_png(file); + } + + // (Note : Most of this function has been written by Eric Fausett) + CImg& _load_png(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_png() : Cannot load (null) filename.", + pixel_type()); +#ifndef cimg_use_png + if (file) + throw CImgIOException("CImg<%s>::load_png() : File '(FILE*)' cannot be read without using libpng.", + pixel_type()); + else return load_other(filename); +#else + // Open file and check for PNG validity + const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. + cimg_std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); + + unsigned char pngCheck[8]; + cimg::fread(pngCheck,8,(cimg_std::FILE*)nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_png() : File '%s' is not a valid PNG file.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + + // Setup PNG structures for read + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); + if (!png_ptr) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'png_ptr' data structure.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); + throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'info_ptr' data structure.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); + throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'end_info' data structure.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))) { + if (!file) cimg::fclose((cimg_std::FILE*)nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException("CImg<%s>::load_png() : File '%s', unknown fatal error.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_set_sig_bytes(png_ptr, 8); + + // Get PNG Header Info up to data block + png_read_info(png_ptr,info_ptr); + png_uint_32 W, H; + int bit_depth, color_type, interlace_type; + png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,int_p_NULL,int_p_NULL); + int new_bit_depth = bit_depth; + int new_color_type = color_type; + + // Transforms to unify image data + if (new_color_type == PNG_COLOR_TYPE_PALETTE){ + png_set_palette_to_rgb(png_ptr); + new_color_type -= PNG_COLOR_MASK_PALETTE; + new_bit_depth = 8; + } + if (new_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){ + png_set_expand_gray_1_2_4_to_8(png_ptr); + new_bit_depth = 8; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){ + png_set_gray_to_rgb(png_ptr); + new_color_type |= PNG_COLOR_MASK_COLOR; + } + if (new_color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, 0xffffU, PNG_FILLER_AFTER); + png_read_update_info(png_ptr,info_ptr); + if (!(new_bit_depth==8 || new_bit_depth==16)) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException("CImg<%s>::load_png() : File '%s', wrong bit coding (bit_depth=%u)", + pixel_type(),nfilename?nfilename:"(FILE*)",new_bit_depth); + } + const int byte_depth = new_bit_depth>>3; + + // Allocate Memory for Image Read + png_bytep *imgData = new png_bytep[H]; + for (unsigned int row = 0; row::load_png() : File '%s', wrong color coding (new_color_type=%u)", + pixel_type(),nfilename?nfilename:"(FILE*)",new_color_type); + } + const bool no_alpha_channel = (new_color_type==PNG_COLOR_TYPE_RGB); + assign(W,H,1,no_alpha_channel?3:4); + T *ptr1 = ptr(0,0,0,0), *ptr2 = ptr(0,0,0,1), *ptr3 = ptr(0,0,0,2), *ptr4 = ptr(0,0,0,3); + switch (new_bit_depth) { + case 8 : { + cimg_forY(*this,y){ + const unsigned char *ptrs = (unsigned char*)imgData[y]; + cimg_forX(*this,x){ + *(ptr1++) = (T)*(ptrs++); + *(ptr2++) = (T)*(ptrs++); + *(ptr3++) = (T)*(ptrs++); + if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); + } + } + } break; + case 16 : { + cimg_forY(*this,y){ + const unsigned short *ptrs = (unsigned short*)(imgData[y]); + if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*width); + cimg_forX(*this,x){ + *(ptr1++) = (T)*(ptrs++); + *(ptr2++) = (T)*(ptrs++); + *(ptr3++) = (T)*(ptrs++); + if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); + } + } + } break; + } + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + + // Deallocate Image Read Memory + cimg_forY(*this,n) delete[] imgData[n]; + delete[] imgData; + if (!file) cimg::fclose(nfile); + return *this; +#endif + } + + //! Load an image from a PNM file. + CImg& load_pnm(const char *const filename) { + return _load_pnm(0,filename); + } + + static CImg get_load_pnm(const char *const filename) { + return CImg().load_pnm(filename); + } + + //! Load an image from a PNM file. + CImg& load_pnm(cimg_std::FILE *const file) { + return _load_pnm(file,0); + } + + static CImg get_load_pnm(cimg_std::FILE *const file) { + return CImg().load_pnm(file); + } + + CImg& _load_pnm(cimg_std::FILE *const file, const char *const filename) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_pnm() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type, W, H, colormax = 255; + char item[1024] = { 0 }; + int err, rval, gval, bval; + const int cimg_iobuffer = 12*1024*1024; + while ((err=cimg_std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); + if (cimg_std::sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PNM header 'P?' not found.", + pixel_type(),filename?filename:"(FILE*)"); + } + while ((err=cimg_std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); + if ((err=cimg_std::sscanf(item," %u %u %u",&W,&H,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_pnm() : File '%s', WIDTH and HEIGHT fields are not defined in PNM header.", + pixel_type(),filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=cimg_std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) cimg_std::fgetc(nfile); + if (cimg_std::sscanf(item,"%u",&colormax)!=1) + cimg::warn("CImg<%s>::load_pnm() : File '%s', COLORMAX field is not defined in PNM header.", + pixel_type(),filename?filename:"(FILE*)"); + } + cimg_std::fgetc(nfile); + assign(); + + switch (ppm_type) { + case 2 : { // Grey Ascii + assign(W,H,1,1); + T* rdata = data; + cimg_foroff(*this,off) { if (cimg_std::fscanf(nfile,"%d",&rval)>0) *(rdata++) = (T)rval; else break; } + } break; + case 3 : { // Color Ascii + assign(W,H,1,3); + T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); + cimg_forXY(*this,x,y) { + if (cimg_std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(rdata++) = (T)rval; *(gdata++) = (T)gval; *(bdata++) = (T)bval; } + else break; + } + } break; + case 5 : { // Grey Binary + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,1,1); + T *ptrd = ptr(0,0,0,0); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer)); + cimg::fread(raw.data,raw.width,nfile); + toread-=raw.width; + const unsigned char *ptrs = raw.data; + for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } else { // 16 bits + CImg raw; + assign(W,H,1,1); + T *ptrd = ptr(0,0,0,0); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer/2)); + cimg::fread(raw.data,raw.width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); + toread-=raw.width; + const unsigned short *ptrs = raw.data; + for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } + } break; + case 6 : { // Color Binary + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = ptr(0,0,0,0), + *ptr_g = ptr(0,0,0,1), + *ptr_b = ptr(0,0,0,2); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer)); + cimg::fread(raw.data,raw.width,nfile); + toread-=raw.width; + const unsigned char *ptrs = raw.data; + for (unsigned int off = raw.width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { // 16 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = ptr(0,0,0,0), + *ptr_g = ptr(0,0,0,1), + *ptr_b = ptr(0,0,0,2); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer/2)); + cimg::fread(raw.data,raw.width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); + toread-=raw.width; + const unsigned short *ptrs = raw.data; + for (unsigned int off = raw.width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PPM type 'P%d' not supported.", + pixel_type(),filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a RGB file. + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(0,filename,dimw,dimh); + } + + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(filename,dimw,dimh); + } + + //! Load an image from a RGB file. + CImg& load_rgb(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(file,0,dimw,dimh); + } + + static CImg get_load_rgb(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(file,dimw,dimh); + } + + CImg& _load_rgb(cimg_std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_rgb() : Cannot load (null) filename.", + pixel_type()); + if (!dimw || !dimh) return assign(); + const int cimg_iobuffer = 12*1024*1024; + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,3); + T + *ptr_r = ptr(0,0,0,0), + *ptr_g = ptr(0,0,0,1), + *ptr_b = ptr(0,0,0,2); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer)); + cimg::fread(raw.data,raw.width,nfile); + toread-=raw.width; + const unsigned char *ptrs = raw.data; + for (unsigned int off = raw.width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a RGBA file. + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(0,filename,dimw,dimh); + } + + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(filename,dimw,dimh); + } + + //! Load an image from a RGBA file. + CImg& load_rgba(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(file,0,dimw,dimh); + } + + static CImg get_load_rgba(cimg_std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(file,dimw,dimh); + } + + CImg& _load_rgba(cimg_std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_rgba() : Cannot load (null) filename.", + pixel_type()); + if (!dimw || !dimh) return assign(); + const int cimg_iobuffer = 12*1024*1024; + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,4); + T + *ptr_r = ptr(0,0,0,0), + *ptr_g = ptr(0,0,0,1), + *ptr_b = ptr(0,0,0,2), + *ptr_a = ptr(0,0,0,3); + for (int toread = (int)size(); toread>0; ) { + raw.assign(cimg::min(toread,cimg_iobuffer)); + cimg::fread(raw.data,raw.width,nfile); + toread-=raw.width; + const unsigned char *ptrs = raw.data; + for (unsigned int off = raw.width/4; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a TIFF file. + CImg& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_tiff() : Cannot load (null) filename.", + pixel_type()); + const unsigned int + nfirst_frame = first_frame1) + throw CImgArgumentException("CImg<%s>::load_tiff() : File '%s', reading sub-images from a tiff file requires the use of libtiff.\n" + "('cimg_use_tiff' must be defined).", + pixel_type(),filename); + return load_other(filename); +#else + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn("CImg<%s>::load_tiff() : File '%s' contains %u image(s), specified frame range is [%u,%u] (step %u).", + pixel_type(),filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images-1; + TIFFSetDirectory(tif,0); + CImg frame; + for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { + frame._load_tiff(tif,l); + if (l==nfirst_frame) assign(frame.width,frame.height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame.dim); + if (frame.width>width || frame.height>height || frame.dim>dim) + resize(cimg::max(frame.width,width),cimg::max(frame.height,height),-100,cimg::max(frame.dim,dim),0); + draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame); + } + TIFFClose(tif); + } else throw CImgException("CImg<%s>::load_tiff() : File '%s' cannot be opened.", + pixel_type(),filename); + return *this; +#endif + } + + static CImg get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame); + } + + // (Original contribution by Jerome Boulanger). +#ifdef cimg_use_tiff + CImg& _load_tiff(TIFF *tif, const unsigned int directory) { + if (!TIFFSetDirectory(tif,directory)) return assign(); + uint16 samplesperpixel, bitspersample; + uint32 nx,ny; + const char *const filename = TIFFFileName(tif); + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + if (samplesperpixel!=1 && samplesperpixel!=3 && samplesperpixel!=4) { + cimg::warn("CImg<%s>::load_tiff() : File '%s', unknow value for tag : TIFFTAG_SAMPLESPERPIXEL, will force it to 1.", + pixel_type(),filename); + samplesperpixel = 1; + } + TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); + assign(nx,ny,1,samplesperpixel); + if (bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4)) { + uint16 photo, config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (TIFFIsTiled(tif)) { + uint32 tw, th; + TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); + TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : { + unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + unsigned char *ptr = buf; + for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + unsigned short *ptr = buf; + for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + float *ptr = buf; + for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + unsigned char *ptr = buf; + for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + unsigned short *ptr = buf; + for (unsigned int rr = row; rr::load_tiff() : File '%s', an error occure while reading a tile.", + pixel_type(),filename); + } else { + float *ptr = buf; + for (unsigned int rr = row; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", + pixel_type(),filename); + } + unsigned char *ptr = buf; + for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename); + } + unsigned short *ptr = buf; + for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename); + } + float *ptr = buf; + for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", + pixel_type(),filename); + } + unsigned char *ptr = buf; + for (unsigned int rr = 0;rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename); + } + unsigned short *ptr = buf; + for (unsigned int rr = 0; rrny?ny-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", + pixel_type(),filename); + } + float *ptr = buf; + for (unsigned int rr = 0; rr::load_tiff() : File '%s', not enough memory for buffer allocation.", + pixel_type(),filename); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (samplesperpixel) { + case 1 : { + cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x]+ 128) / 257); + } break; + case 3 : { + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); + } + } break; + case 4 : { + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); + (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]); + } + } break; + } + _TIFFfree(raster); + } + return *this; + } +#endif + + //! Load an image from an ANALYZE7.5/NIFTI file. + CImg& load_analyze(const char *const filename, float *const voxsize=0) { + return _load_analyze(0,filename,voxsize); + } + + static CImg get_load_analyze(const char *const filename, float *const voxsize=0) { + return CImg().load_analyze(filename,voxsize); + } + + //! Load an image from an ANALYZE7.5/NIFTI file. + CImg& load_analyze(cimg_std::FILE *const file, float *const voxsize=0) { + return _load_analyze(file,0,voxsize); + } + + static CImg get_load_analyze(cimg_std::FILE *const file, float *const voxsize=0) { + return CImg().load_analyze(file,voxsize); + } + + CImg& _load_analyze(cimg_std::FILE *const file, const char *const filename, float *const voxsize=0) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_analyze() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *nfile_header = 0, *nfile = 0; + if (!file) { + char body[1024]; + const char *ext = cimg::split_filename(filename,body); + if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file. + nfile_header = cimg::fopen(filename,"rb"); + cimg_std::sprintf(body+cimg::strlen(body),".img"); + nfile = cimg::fopen(body,"rb"); + } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file. + nfile = cimg::fopen(filename,"rb"); + cimg_std::sprintf(body+cimg::strlen(body),".hdr"); + nfile_header = cimg::fopen(body,"rb"); + } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file. + } else nfile_header = nfile = file; // File is a Niftii file. + if (!nfile || !nfile_header) + throw CImgIOException("CImg<%s>::load_analyze() : File '%s', not recognized as an Analyze7.5 or NIFTI file.", + pixel_type(),filename?filename:"(FILE*)"); + + // Read header. + bool endian = false; + unsigned int header_size; + cimg::fread(&header_size,1,nfile_header); + if (!header_size) + throw CImgIOException("CImg<%s>::load_analyze() : File '%s', zero-sized header found.", + pixel_type(),filename?filename:"(FILE*)"); + if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } + unsigned char *header = new unsigned char[header_size]; + cimg::fread(header+4,header_size-4,nfile_header); + if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); + if (endian) { + cimg::invert_endianness((short*)(header+40),5); + cimg::invert_endianness((short*)(header+70),1); + cimg::invert_endianness((short*)(header+72),1); + cimg::invert_endianness((float*)(header+76),4); + cimg::invert_endianness((float*)(header+112),1); + } + unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; + if (!dim[0]) + cimg::warn("CImg<%s>::load_analyze() : File '%s', tells that image has zero dimensions.", + pixel_type(),filename?filename:"(FILE*)"); + if (dim[0]>4) + cimg::warn("CImg<%s>::load_analyze() : File '%s', number of image dimension is %u, reading only the 4 first dimensions", + pixel_type(),filename?filename:"(FILE*)",dim[0]); + if (dim[0]>=1) dimx = dim[1]; + if (dim[0]>=2) dimy = dim[2]; + if (dim[0]>=3) dimz = dim[3]; + if (dim[0]>=4) dimv = dim[4]; + float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1; + const unsigned short datatype = *(short*)(header+70); + if (voxsize) { + const float *vsize = (float*)(header+76); + voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3]; + } + delete[] header; + + // Read pixel data. + assign(dimx,dimy,dimz,dimv); + switch (datatype) { + case 2 : { + unsigned char *buffer = new unsigned char[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 4 : { + short *buffer = new short[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 8 : { + int *buffer = new int[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 16 : { + float *buffer = new float[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 64 : { + double *buffer = new double[dimx*dimy*dimz*dimv]; + cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); + if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv); + cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_analyze() : File '%s', cannot read images with 'datatype = %d'", + pixel_type(),filename?filename:"(FILE*)",datatype); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image (list) from a .cimg file. + CImg& load_cimg(const char *const filename, const char axis='z', const char align='p') { + CImgList list; + list.load_cimg(filename); + if (list.size==1) return list[0].transfer_to(*this); + return assign(list.get_append(axis,align)); + } + + static CImg get_load_cimg(const char *const filename, const char axis='z', const char align='p') { + return CImg().load_cimg(filename,axis,align); + } + + //! Load an image (list) from a .cimg file. + CImg& load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') { + CImgList list; + list.load_cimg(file); + if (list.size==1) return list[0].transfer_to(*this); + return assign(list.get_append(axis,align)); + } + + static CImg get_load_cimg(cimg_std::FILE *const file, const char axis='z', const char align='p') { + return CImg().load_cimg(file,axis,align); + } + + //! Load a sub-image (list) from a .cimg file. + CImg& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, + const char axis='z', const char align='p') { + CImgList list; + list.load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + if (list.size==1) return list[0].transfer_to(*this); + return assign(list.get_append(axis,align)); + } + + static CImg get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, + const char axis='z', const char align='p') { + return CImg().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); + } + + //! Load a sub-image (list) from a non-compressed .cimg file. + CImg& load_cimg(cimg_std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, + const char axis='z', const char align='p') { + CImgList list; + list.load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + if (list.size==1) return list[0].transfer_to(*this); + return assign(list.get_append(axis,align)); + } + + static CImg get_load_cimg(cimg_std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, + const char axis='z', const char align='p') { + return CImg().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); + } + + //! Load an image from an INRIMAGE-4 file. + CImg& load_inr(const char *const filename, float *const voxsize=0) { + return _load_inr(0,filename,voxsize); + } + + static CImg get_load_inr(const char *const filename, float *const voxsize=0) { + return CImg().load_inr(filename,voxsize); + } + + //! Load an image from an INRIMAGE-4 file. + CImg& load_inr(cimg_std::FILE *const file, float *const voxsize=0) { + return _load_inr(file,0,voxsize); + } + + static CImg get_load_inr(cimg_std::FILE *const file, float *voxsize=0) { + return CImg().load_inr(file,voxsize); + } + + // Load an image from an INRIMAGE-4 file (internal). + static void _load_inr_header(cimg_std::FILE *file, int out[8], float *const voxsize) { + char item[1024], tmp1[64], tmp2[64]; + out[0] = cimg_std::fscanf(file,"%63s",item); + out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; + if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) + throw CImgIOException("CImg<%s>::load_inr() : File does not appear to be a valid INR file.\n" + "(INRIMAGE-4 identifier not found)", + pixel_type()); + while (cimg_std::fscanf(file," %63[^\n]%*c",item)!=EOF && cimg::strncmp(item,"##}",3)) { + cimg_std::sscanf(item," XDIM%*[^0-9]%d",out); + cimg_std::sscanf(item," YDIM%*[^0-9]%d",out+1); + cimg_std::sscanf(item," ZDIM%*[^0-9]%d",out+2); + cimg_std::sscanf(item," VDIM%*[^0-9]%d",out+3); + cimg_std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); + if (voxsize) { + cimg_std::sscanf(item," VX%*[^0-9.+-]%f",voxsize); + cimg_std::sscanf(item," VY%*[^0-9.+-]%f",voxsize+1); + cimg_std::sscanf(item," VZ%*[^0-9.+-]%f",voxsize+2); + } + if (cimg_std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (cimg_std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { + case 0 : break; + case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; cimg_std::strcpy(tmp1,tmp2); + case 1 : + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; + if (out[4]>=0) break; + default : + throw CImgIOException("cimg::inr_header_read() : Invalid TYPE '%s'",tmp2); + } + } + if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::load_inr() : Bad dimensions in .inr file = ( %d , %d , %d , %d )", + pixel_type(),out[0],out[1],out[2],out[3]); + if(out[4]<0 || out[5]<0) + throw CImgIOException("CImg<%s>::load_inr() : TYPE is not fully defined", + pixel_type()); + if(out[6]<0) + throw CImgIOException("CImg<%s>::load_inr() : PIXSIZE is not fully defined", + pixel_type()); + if(out[7]<0) + throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type is not defined", + pixel_type()); + } + + CImg& _load_inr(cimg_std::FILE *const file, const char *const filename, float *const voxsize) { +#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *val = new Ts[fopt[0]*fopt[3]]; \ + cimg_forYZ(*this,y,z) { \ + cimg::fread(val,fopt[0]*fopt[3],nfile); \ + if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ + xval = val; cimg_forX(*this,x) cimg_forV(*this,k) (*this)(x,y,z,k) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_inr() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian=cimg::endianness()?1:0; + bool loaded = false; + if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1; + _load_inr_header(nfile,fopt,voxsize); + assign(fopt[0],fopt[1],fopt[2],fopt[3]); + _cimg_load_inr_case(0,0,8, unsigned char); + _cimg_load_inr_case(0,1,8, char); + _cimg_load_inr_case(0,0,16,unsigned short); + _cimg_load_inr_case(0,1,16,short); + _cimg_load_inr_case(0,0,32,unsigned int); + _cimg_load_inr_case(0,1,32,int); + _cimg_load_inr_case(1,0,32,float); + _cimg_load_inr_case(1,1,32,float); + _cimg_load_inr_case(1,0,64,double); + _cimg_load_inr_case(1,1,64,double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_inr() : File '%s', cannot read images of the type specified in the file", + pixel_type(),filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a PANDORE file. + CImg& load_pandore(const char *const filename) { + return _load_pandore(0,filename); + } + + static CImg get_load_pandore(const char *const filename) { + return CImg().load_pandore(filename); + } + + //! Load an image from a PANDORE file. + CImg& load_pandore(cimg_std::FILE *const file) { + return _load_pandore(file,0); + } + + static CImg get_load_pandore(cimg_std::FILE *const file) { + return CImg().load_pandore(file); + } + + CImg& _load_pandore(cimg_std::FILE *const file, const char *const filename) { +#define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ + cimg::fread(dims,nbdim,nfile); \ + if (endian) cimg::invert_endianness(dims,nbdim); \ + assign(nwidth,nheight,ndepth,ndim); \ + const unsigned int siz = size(); \ + stype *buffer = new stype[siz]; \ + cimg::fread(buffer,siz,nfile); \ + if (endian) cimg::invert_endianness(buffer,siz); \ + T *ptrd = data; \ + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ + buffer-=siz; \ + delete[] buffer + +#define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ + if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ + else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ + else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ + else throw CImgIOException("CImg<%s>::load_pandore() : File '%s' cannot be read, datatype not supported on this architecture.", \ + pixel_type(),filename?filename:"(FILE*)"); } + + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_pandore() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + char header[32]; + cimg::fread(header,12,nfile); + if (cimg::strncasecmp("PANDORE",header,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_pandore() : File '%s' is not a valid PANDORE file, " + "(PANDORE identifier not found).", + pixel_type(),filename?filename:"(FILE*)"); + } + unsigned int imageid, dims[8]; + cimg::fread(&imageid,1,nfile); + const bool endian = (imageid>255); + if (endian) cimg::invert_endianness(imageid); + cimg::fread(header,20,nfile); + + switch (imageid) { + case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,uchar,uchar,uchar,1); break; + case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; + case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; + case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,uchar,uchar,uchar,1); break; + case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; + case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; + case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,uchar,uchar,uchar,1); break; + case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; + case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; + case 11 : { // Region 1D + cimg::fread(dims,3,nfile); + if (endian) cimg::invert_endianness(dims,3); + assign(dims[1],1,1,1); + const unsigned siz = size(); + if (dims[2]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[2]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 12 : { // Region 2D + cimg::fread(dims,4,nfile); + if (endian) cimg::invert_endianness(dims,4); + assign(dims[2],dims[1],1,1); + const unsigned int siz = size(); + if (dims[3]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[3]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned long *buffer = new unsigned long[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 13 : { // Region 3D + cimg::fread(dims,5,nfile); + if (endian) cimg::invert_endianness(dims,5); + assign(dims[3],dims[2],dims[1],1); + const unsigned int siz = size(); + if (dims[4]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[4]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,uchar,uchar,uchar,1); break; + case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; + case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; + case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,uchar,uchar,uchar,1); break; + case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; + case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; + case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],uchar,uchar,uchar,1); break; + case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); + case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],ulong,uint,ushort,4); break; + case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; + case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],uchar,uchar,uchar,1); break; + case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; + case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],ulong,uint,ushort,4); break; + case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; + case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],uchar,uchar,uchar,1); break; + case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; + case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],ulong,uint,ushort,4); break; + case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; + case 34 : { // Points 1D + int ptbuf[4]; + cimg::fread(ptbuf,1,nfile); + if (endian) cimg::invert_endianness(ptbuf,1); + assign(1); (*this)(0) = (T)ptbuf[0]; + } break; + case 35 : { // Points 2D + int ptbuf[4]; + cimg::fread(ptbuf,2,nfile); + if (endian) cimg::invert_endianness(ptbuf,2); + assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; + } break; + case 36 : { // Points 3D + int ptbuf[4]; + cimg::fread(ptbuf,3,nfile); + if (endian) cimg::invert_endianness(ptbuf,3); + assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_pandore() : File '%s', cannot read images with ID_type = %u", + pixel_type(),filename?filename:"(FILE*)",imageid); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a PAR-REC (Philips) file. + CImg& load_parrec(const char *const filename, const char axis='v', const char align='p') { + CImgList list; + list.load_parrec(filename); + if (list.size==1) return list[0].transfer_to(*this); + return assign(list.get_append(axis,align)); + } + + static CImg get_load_parrec(const char *const filename, const char axis='v', const char align='p') { + return CImg().load_parrec(filename,axis,align); + } + + //! Load an image from a .RAW file. + CImg& load_raw(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed=false, const bool invert_endianness=false) { + return _load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + } + + static CImg get_load_raw(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed=false, const bool invert_endianness=false) { + return CImg().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + } + + //! Load an image from a .RAW file. + CImg& load_raw(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed=false, const bool invert_endianness=false) { + return _load_raw(file,0,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + } + + static CImg get_load_raw(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int sizez=1, const unsigned int sizev=1, + const bool multiplexed=false, const bool invert_endianness=false) { + return CImg().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness); + } + + CImg& _load_raw(cimg_std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey, + const unsigned int sizez, const unsigned int sizev, + const bool multiplexed, const bool invert_endianness) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_raw() : Cannot load (null) filename.", + pixel_type()); + assign(sizex,sizey,sizez,sizev,0); + const unsigned int siz = size(); + if (siz) { + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!multiplexed) { + cimg::fread(data,siz,nfile); + if (invert_endianness) cimg::invert_endianness(data,siz); + } + else { + CImg buf(1,1,1,sizev); + cimg_forXYZ(*this,x,y,z) { + cimg::fread(buf.data,sizev,nfile); + if (invert_endianness) cimg::invert_endianness(buf.data,sizev); + set_vector_at(buf,x,y,z); } + } + if (!file) cimg::fclose(nfile); + } + return *this; + } + + //! Load a video sequence using FFMPEG av's libraries. + CImg& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, + const char axis='z', const char align='p') { + return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).transfer_to(*this); + } + + static CImg get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false, + const char axis='z', const char align='p') { + return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align); + } + + //! Load an image sequence from a YUV file. + CImg& load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') { + return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).transfer_to(*this); + } + + static CImg get_load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') { + return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); + } + + //! Load an image sequence from a YUV file. + CImg& load_yuv(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') { + return get_load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).transfer_to(*this); + } + + static CImg get_load_yuv(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') { + return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align); + } + + //! Load a 3D object from a .OFF file. + template + CImg& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { + return _load_off(0,filename,primitives,colors,invert_faces); + } + + template + static CImg get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return CImg().load_off(filename,primitives,colors,invert_faces); + } + + //! Load a 3D object from a .OFF file. + template + CImg& load_off(cimg_std::FILE *const file, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { + return _load_off(file,0,primitives,colors,invert_faces); + } + + template + static CImg get_load_off(cimg_std::FILE *const file, CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return CImg().load_off(file,primitives,colors,invert_faces); + } + + template + CImg& _load_off(cimg_std::FILE *const file, const char *const filename, + CImgList& primitives, CImgList& colors, const bool invert_faces) { + if (!filename && !file) + throw CImgArgumentException("CImg<%s>::load_off() : Cannot load (null) filename.", + pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; + char line[256] = { 0 }; + int err; + + // Skip comments, and read magic string OFF + do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); + if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_off() : File '%s', keyword 'OFF' not found.", + pixel_type(),filename?filename:"(FILE*)"); + } + do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); + if ((err = cimg_std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_off() : File '%s', invalid vertices/primitives numbers.", + pixel_type(),filename?filename:"(FILE*)"); + } + + // Read points data + assign(nb_points,3); + float X = 0, Y = 0, Z = 0; + cimg_forX(*this,l) { + do { err = cimg_std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); + if ((err = cimg_std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::load_off() : File '%s', cannot read point %u/%u.\n", + pixel_type(),filename?filename:"(FILE*)",l+1,nb_points); + } + (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; + } + + // Read primitive data + primitives.assign(); + colors.assign(); + bool stopflag = false; + while (!stopflag) { + float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; + unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; + line[0]='\0'; + if ((err = cimg_std::fscanf(nfile,"%u",&prim))!=1) stopflag=true; + else { + ++nb_read; + switch (prim) { + case 1 : { + if ((err = cimg_std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + primitives.insert(CImg::vector(i0)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + } + } break; + case 2 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + primitives.insert(CImg::vector(i0,i1)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + } + } break; + case 3 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); + else primitives.insert(CImg::vector(i0,i2,i1)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + } + } break; + case 4 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2,i3)); + else primitives.insert(CImg::vector(i0,i3,i2,i1)); + colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + } + } break; + case 5 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) { + primitives.insert(CImg::vector(i0,i1,i2,i3)); + primitives.insert(CImg::vector(i0,i3,i4)); + } + else { + primitives.insert(CImg::vector(i0,i3,i2,i1)); + primitives.insert(CImg::vector(i0,i4,i3)); + } + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 6 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) { + primitives.insert(CImg::vector(i0,i1,i2,i3)); + primitives.insert(CImg::vector(i0,i3,i4,i5)); + } + else { + primitives.insert(CImg::vector(i0,i3,i2,i1)); + primitives.insert(CImg::vector(i0,i5,i4,i3)); + } + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 7 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) { + primitives.insert(CImg::vector(i0,i1,i3,i4)); + primitives.insert(CImg::vector(i0,i4,i5,i6)); + primitives.insert(CImg::vector(i1,i2,i3)); + } + else { + primitives.insert(CImg::vector(i0,i4,i3,i1)); + primitives.insert(CImg::vector(i0,i6,i5,i4)); + primitives.insert(CImg::vector(i3,i2,i1)); + } + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + case 8 : { + if ((err = cimg_std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) { + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_std::sscanf(line,"%f%f%f",&c0,&c1,&c2); + if (invert_faces) { + primitives.insert(CImg::vector(i0,i1,i2,i3)); + primitives.insert(CImg::vector(i0,i3,i4,i5)); + primitives.insert(CImg::vector(i0,i5,i6,i7)); + } + else { + primitives.insert(CImg::vector(i0,i3,i2,i1)); + primitives.insert(CImg::vector(i0,i5,i4,i3)); + primitives.insert(CImg::vector(i0,i7,i6,i5)); + } + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + default : + cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u (%u vertices).", + pixel_type(),filename?filename:"(FILE*)",nb_read,nb_primitives,prim); + err = cimg_std::fscanf(nfile,"%*[^\n] "); + } + } + } + if (!file) cimg::fclose(nfile); + if (primitives.size!=nb_primitives) + cimg::warn("CImg<%s>::load_off() : File '%s', read only %u primitives instead of %u as claimed in the header.", + pixel_type(),filename?filename:"(FILE*)",primitives.size,nb_primitives); + return *this; + } + + //! Load a video sequence using FFMPEG's external tool 'ffmpeg'. + CImg& load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') { + return get_load_ffmpeg_external(filename,axis,align).transfer_to(*this); + } + + static CImg get_load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') { + return CImgList().load_ffmpeg_external(filename).get_append(axis,align); + } + + //! Load an image using GraphicsMagick's external tool 'gm'. + CImg& load_graphicsmagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_graphicsmagick_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512]; + cimg_std::FILE *file = 0; +#if cimg_OS==1 + cimg_std::sprintf(command,"%s convert \"%s\" ppm:-",cimg::graphicsmagick_path(),filename); + file = popen(command,"r"); + if (file) { load_pnm(file); pclose(file); return *this; } +#endif + do { + cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s convert \"%s\" %s",cimg::graphicsmagick_path(),filename,filetmp); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file = cimg_std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::load_graphicsmagick_external() : Failed to open image '%s'.\n\n" + "Path of 'GraphicsMagick's gm' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); + } else cimg::fclose(file); + load_pnm(filetmp); + cimg_std::remove(filetmp); + return *this; + } + + static CImg get_load_graphicsmagick_external(const char *const filename) { + return CImg().load_graphicsmagick_external(filename); + } + + //! Load a gzipped image file, using external tool 'gunzip'. + CImg& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException("CImg<%s>::load_gzip_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512], body[512]; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + cimg_std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext2); + else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } else { + if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext); + else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); + cimg::system(command); + if (!(file = cimg_std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::load_gzip_external() : File '%s' cannot be opened.", + pixel_type(),filename); + } else cimg::fclose(file); + load(filetmp); + cimg_std::remove(filetmp); + return *this; + } + + static CImg get_load_gzip_external(const char *const filename) { + return CImg().load_gzip_external(filename); + } + + //! Load an image using ImageMagick's external tool 'convert'. + CImg& load_imagemagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_imagemagick_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512]; + cimg_std::FILE *file = 0; +#if cimg_OS==1 + cimg_std::sprintf(command,"%s \"%s\" ppm:-",cimg::imagemagick_path(),filename); + file = popen(command,"r"); + if (file) { load_pnm(file); pclose(file); return *this; } +#endif + do { + cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s \"%s\" %s",cimg::imagemagick_path(),filename,filetmp); + cimg::system(command,cimg::imagemagick_path()); + if (!(file = cimg_std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::load_imagemagick_external() : Failed to open image '%s'.\n\n" + "Path of 'ImageMagick's convert' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::imagemagick_path(),filetmp); + } else cimg::fclose(file); + load_pnm(filetmp); + cimg_std::remove(filetmp); + return *this; + } + + static CImg get_load_imagemagick_external(const char *const filename) { + return CImg().load_imagemagick_external(filename); + } + + //! Load a DICOM image file, using XMedcon's external tool 'medcon'. + CImg& load_medcon_external(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_medcon_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512], body[512]; + cimg::fclose(cimg::fopen(filename,"r")); + cimg_std::FILE *file = 0; + do { + cimg_std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename); + cimg::system(command); + cimg::split_filename(filetmp,body); + cimg_std::sprintf(command,"m000-%s.hdr",body); + file = cimg_std::fopen(command,"rb"); + if (!file) { + throw CImgIOException("CImg<%s>::load_medcon_external() : Failed to open image '%s'.\n\n" + "Path of 'medcon' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::medcon_path(),filetmp); + } else cimg::fclose(file); + load_analyze(command); + cimg_std::remove(command); + cimg_std::sprintf(command,"m000-%s.img",body); + cimg_std::remove(command); + return *this; + } + + static CImg get_load_medcon_external(const char *const filename) { + return CImg().load_medcon_external(filename); + } + + //! Load a RAW Color Camera image file, using external tool 'dcraw'. + CImg& load_dcraw_external(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_dcraw_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512]; + cimg_std::FILE *file = 0; +#if cimg_OS==1 + cimg_std::sprintf(command,"%s -4 -c \"%s\"",cimg::dcraw_path(),filename); + file = popen(command,"r"); + if (file) { load_pnm(file); pclose(file); return *this; } +#endif + do { + cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp); + cimg::system(command,cimg::dcraw_path()); + if (!(file = cimg_std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::load_dcraw_external() : Failed to open image '%s'.\n\n" + "Path of 'dcraw' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::dcraw_path(),filetmp); + } else cimg::fclose(file); + load_pnm(filetmp); + cimg_std::remove(filetmp); + return *this; + } + + static CImg get_load_dcraw_external(const char *const filename) { + return CImg().load_dcraw_external(filename); + } + + //! Load an image using ImageMagick's or GraphicsMagick's executables. + CImg& load_other(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImg<%s>::load_other() : Cannot load (null) filename.", + pixel_type()); + const unsigned int odebug = cimg::exception_mode(); + cimg::exception_mode() = 0; + try { load_magick(filename); } + catch (CImgException&) { + try { load_imagemagick_external(filename); } + catch (CImgException&) { + try { load_graphicsmagick_external(filename); } + catch (CImgException&) { + assign(); + } + } + } + cimg::exception_mode() = odebug; + if (is_empty()) + throw CImgIOException("CImg<%s>::load_other() : File '%s' cannot be opened.", + pixel_type(),filename); + return *this; + } + + static CImg get_load_other(const char *const filename) { + return CImg().load_other(filename); + } + + //@} + //--------------------------- + // + //! \name Image File Saving + //@{ + //--------------------------- + + //! Save the image as a file. + /** + The used file format is defined by the file extension in the filename \p filename. + Parameter \p number can be used to add a 6-digit number to the filename before saving. + **/ + const CImg& save(const char *const filename, const int number=-1) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p) cannot be saved as a (null) filename.", + pixel_type(),width,height,depth,dim,data); + const char *ext = cimg::split_filename(filename); + char nfilename[1024]; + const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; +#ifdef cimg_save_plugin + cimg_save_plugin(fn); +#endif +#ifdef cimg_save_plugin1 + cimg_save_plugin1(fn); +#endif +#ifdef cimg_save_plugin2 + cimg_save_plugin2(fn); +#endif +#ifdef cimg_save_plugin3 + cimg_save_plugin3(fn); +#endif +#ifdef cimg_save_plugin4 + cimg_save_plugin4(fn); +#endif +#ifdef cimg_save_plugin5 + cimg_save_plugin5(fn); +#endif +#ifdef cimg_save_plugin6 + cimg_save_plugin6(fn); +#endif +#ifdef cimg_save_plugin7 + cimg_save_plugin7(fn); +#endif +#ifdef cimg_save_plugin8 + cimg_save_plugin8(fn); +#endif + // ASCII formats + if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); + if (!cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); + if (!cimg::strcasecmp(ext,"cpp") || + !cimg::strcasecmp(ext,"hpp") || + !cimg::strcasecmp(ext,"h") || + !cimg::strcasecmp(ext,"c")) return save_cpp(fn); + + // 2D binary formats + if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); + if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); + if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); + if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); + if (!cimg::strcasecmp(ext,"png")) return save_png(fn); + if (!cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); + + // 3D binary formats + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + if (!cimg::strcasecmp(ext,"cimg") || ext[0]=='\0') return save_cimg(fn,false); + if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); + if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); + if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); + if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); + if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); + + // Archive files + if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + + // Image sequences + if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn); + return save_other(fn); + } + + // Save the image as an ASCII file (ASCII Raw + simple header) (internal). + const CImg& _save_ascii(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_ascii() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + cimg_std::fprintf(nfile,"%u %u %u %u\n",width,height,depth,dim); + const T* ptrs = data; + cimg_forYZV(*this,y,z,v) { + cimg_forX(*this,x) cimg_std::fprintf(nfile,"%g ",(double)*(ptrs++)); + cimg_std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as an ASCII file (ASCII Raw + simple header). + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save the image as an ASCII file (ASCII Raw + simple header). + const CImg& save_ascii(cimg_std::FILE *const file) const { + return _save_ascii(file,0); + } + + // Save the image as a C or CPP source file (internal). + const CImg& _save_cpp(cimg_std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_cpp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_cpp() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + char varname[1024] = { 0 }; + if (filename) cimg_std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname); + if (varname[0]=='\0') cimg_std::sprintf(varname,"unnamed"); + cimg_std::fprintf(nfile, + "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" + "%s data_%s[] = { \n ", + varname,width,height,depth,dim,pixel_type(),pixel_type(),varname); + for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) { + cimg_std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); + if (off==siz) cimg_std::fprintf(nfile," };\n"); + else if (!((off+1)%16)) cimg_std::fprintf(nfile,",\n "); + else cimg_std::fprintf(nfile,", "); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a CPP source file. + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); + } + + //! Save the image as a CPP source file. + const CImg& save_cpp(cimg_std::FILE *const file) const { + return _save_cpp(file,0); + } + + // Save the image as a DLM file (internal). + const CImg& _save_dlm(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (depth>1) + cimg::warn("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Pixel values along Z will be unrolled.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (dim>1) + cimg::warn("CImg<%s>::save_dlm() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. " + "Pixel values along V will be unrolled.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = data; + cimg_forYZV(*this,y,z,v) { + cimg_forX(*this,x) cimg_std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==dimx()-1)?"":","); + cimg_std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a DLM file. + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); + } + + //! Save the image as a DLM file. + const CImg& save_dlm(cimg_std::FILE *const file) const { + return _save_dlm(file,0); + } + + // Save the image as a BMP file (internal). + const CImg& _save_bmp(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (depth>1) + cimg::warn("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (dim>3) + cimg::warn("CImg<%s>::save_bmp() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[54] = { 0 }, align_buf[4] = { 0 }; + const unsigned int + align = (4 - (3*width)%4)%4, + buf_size = (3*width+align)*dimy(), + file_size = 54 + buf_size; + header[0] = 'B'; header[1] = 'M'; + header[0x02] = file_size&0xFF; + header[0x03] = (file_size>>8)&0xFF; + header[0x04] = (file_size>>16)&0xFF; + header[0x05] = (file_size>>24)&0xFF; + header[0x0A] = 0x36; + header[0x0E] = 0x28; + header[0x12] = width&0xFF; + header[0x13] = (width>>8)&0xFF; + header[0x14] = (width>>16)&0xFF; + header[0x15] = (width>>24)&0xFF; + header[0x16] = height&0xFF; + header[0x17] = (height>>8)&0xFF; + header[0x18] = (height>>16)&0xFF; + header[0x19] = (height>>24)&0xFF; + header[0x1A] = 1; + header[0x1B] = 0; + header[0x1C] = 24; + header[0x1D] = 0; + header[0x22] = buf_size&0xFF; + header[0x23] = (buf_size>>8)&0xFF; + header[0x24] = (buf_size>>16)&0xFF; + header[0x25] = (buf_size>>24)&0xFF; + header[0x27] = 0x1; + header[0x2B] = 0x1; + cimg::fwrite(header,54,nfile); + + const T + *pR = ptr(0,height-1,0,0), + *pG = (dim>=2)?ptr(0,height-1,0,1):0, + *pB = (dim>=3)?ptr(0,height-1,0,2):0; + + switch (dim) { + case 1 : { + cimg_forY(*this,y) { cimg_forX(*this,x) { + const unsigned char val = (unsigned char)*(pR++); + cimg_std::fputc(val,nfile); cimg_std::fputc(val,nfile); cimg_std::fputc(val,nfile); + } + cimg::fwrite(align_buf,align,nfile); + pR-=2*width; + }} break; + case 2 : { + cimg_forY(*this,y) { cimg_forX(*this,x) { + cimg_std::fputc(0,nfile); + cimg_std::fputc((unsigned char)(*(pG++)),nfile); + cimg_std::fputc((unsigned char)(*(pR++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + pR-=2*width; pG-=2*width; + }} break; + default : { + cimg_forY(*this,y) { cimg_forX(*this,x) { + cimg_std::fputc((unsigned char)(*(pB++)),nfile); + cimg_std::fputc((unsigned char)(*(pG++)),nfile); + cimg_std::fputc((unsigned char)(*(pR++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + pR-=2*width; pG-=2*width; pB-=2*width; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a BMP file. + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); + } + + //! Save the image as a BMP file. + const CImg& save_bmp(cimg_std::FILE *const file) const { + return _save_bmp(file,0); + } + + // Save a file in JPEG format (internal). + const CImg& _save_jpeg(cimg_std::FILE *const file, const char *const filename, const unsigned int quality) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_jpeg() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + if (depth>1) + cimg::warn("CImg<%s>::save_jpeg() : File '%s, instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException("CImg<%s>::save_jpeg() : Cannot save a JPEG image in a *FILE output. Use libjpeg instead.", + pixel_type()); +#else + // Fill pixel buffer + unsigned char *buf; + unsigned int dimbuf = 0; + J_COLOR_SPACE colortype = JCS_RGB; + switch (dim) { + case 1 : { // Greyscale images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=1)]; + colortype = JCS_GRAYSCALE; + const T *ptr_g = data; + cimg_forXY(*this,x,y) *(buf2++) = (unsigned char)*(ptr_g++); + } break; + case 2 : { // RG images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; + const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1); + colortype = JCS_RGB; + cimg_forXY(*this,x,y) { + *(buf2++) = (unsigned char)*(ptr_r++); + *(buf2++) = (unsigned char)*(ptr_g++); + *(buf2++) = 0; + } + } break; + case 3 : { // RGB images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; + const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); + colortype = JCS_RGB; + cimg_forXY(*this,x,y) { + *(buf2++) = (unsigned char)*(ptr_r++); + *(buf2++) = (unsigned char)*(ptr_g++); + *(buf2++) = (unsigned char)*(ptr_b++); + } + } break; + default : { // CMYK images + unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=4)]; + const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); + colortype = JCS_CMYK; + cimg_forXY(*this,x,y) { + *(buf2++) = (unsigned char)*(ptr_r++); + *(buf2++) = (unsigned char)*(ptr_g++); + *(buf2++) = (unsigned char)*(ptr_b++); + *(buf2++) = (unsigned char)*(ptr_a++); + } + } + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + jpeg_stdio_dest(&cinfo,nfile); + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = dimbuf; + cinfo.in_color_space = colortype; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); + jpeg_start_compress(&cinfo,TRUE); + + const unsigned int row_stride = width*dimbuf; + JSAMPROW row_pointer[1]; + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = &buf[cinfo.next_scanline*row_stride]; + jpeg_write_scanlines(&cinfo,row_pointer,1); + } + jpeg_finish_compress(&cinfo); + + delete[] buf; + if (!file) cimg::fclose(nfile); + jpeg_destroy_compress(&cinfo); + return *this; +#endif + } + + //! Save a file in JPEG format. + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); + } + + //! Save a file in JPEG format. + const CImg& save_jpeg(cimg_std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); + } + + //! Save the image using built-in ImageMagick++ library. + const CImg& save_magick(const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_magick() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); +#ifdef cimg_use_magick + Magick::Image image(Magick::Geometry(width,height),"black"); + image.type(Magick::TrueColorType); + const T + *rdata = ptr(0,0,0,0), + *gdata = dim>1?ptr(0,0,0,1):0, + *bdata = dim>2?ptr(0,0,0,2):0; + Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); + switch (dim) { + case 1 : // Scalar images + for (unsigned int off = width*height; off; --off) { + pixels->red = pixels->green = pixels->blue = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); + ++pixels; + } + break; + case 2 : // RG images + for (unsigned int off = width*height; off; --off) { + pixels->red = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); + pixels->green = Magick::Color::scaleDoubleToQuantum(*(gdata++)/255.0); + pixels->blue = 0; + ++pixels; + } + break; + default : // RGB images + for (unsigned int off = width*height; off; --off) { + pixels->red = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); + pixels->green = Magick::Color::scaleDoubleToQuantum(*(gdata++)/255.0); + pixels->blue = Magick::Color::scaleDoubleToQuantum(*(bdata++)/255.0); + ++pixels; + } + } + image.syncPixels(); + image.write(filename); +#else + throw CImgIOException("CImg<%s>::save_magick() : File '%s', Magick++ library has not been linked.", + pixel_type(),filename); +#endif + return *this; + } + + // Save an image to a PNG file (internal). + // Most of this function has been written by Eric Fausett + const CImg& _save_png(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_png() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + if (depth>1) + cimg::warn("CImg<%s>::save_png() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); +#ifndef cimg_use_png + if (!file) return save_other(filename); + else throw CImgIOException("CImg<%s>::save_png() : Cannot save a PNG image in a *FILE output. You must use 'libpng' to do this instead.", + pixel_type()); +#else + const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'. + cimg_std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); + + // Setup PNG structures for write + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn); + if(!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'png_ptr' data structure.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)0); + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'info_ptr' data structure.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_uint_32 width = dimx(), height = dimy(); + float vmin, vmax = (float)maxmin(vmin); + const int bit_depth = (vmin<0 || vmax>=256)?16:8; + int color_type; + switch (dimv()) { + case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; + case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3 : color_type = PNG_COLOR_TYPE_RGB; break; + default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + const int interlace_type = PNG_INTERLACE_NONE; + const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; + const int filter_method = PNG_FILTER_TYPE_DEFAULT; + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type,compression_type, filter_method); + png_write_info(png_ptr, info_ptr); + const int byte_depth = bit_depth>>3; + const int numChan = dimv()>4?4:dimv(); + const int pixel_bit_depth_flag = numChan * (bit_depth-1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *imgData = new png_byte*[height]; + for (unsigned int row = 0; row::save_png() : File '%s', unknown fatal error.", + pixel_type(),nfilename?nfilename:"(FILE*)"); + } + png_write_image(png_ptr, imgData); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Deallocate Image Write Memory + cimg_forY(*this,n) delete[] imgData[n]; + delete[] imgData; + if (!file) cimg::fclose(nfile); + return *this; +#endif + } + + //! Save a file in PNG format + const CImg& save_png(const char *const filename) const { + return _save_png(0,filename); + } + + //! Save a file in PNG format + const CImg& save_png(cimg_std::FILE *const file) const { + return _save_png(file,0); + } + + // Save the image as a PNM file (internal function). + const CImg& _save_pnm(cimg_std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + double stmin, stmax = (double)maxmin(stmin); + if (depth>1) + cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (dim>3) + cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (stmin<0 || stmax>65535) + cimg::warn("CImg<%s>::save_pnm() : File '%s', instance image (%u,%u,%u,%u,%p) has pixel values in [%g,%g]. Probable type overflow.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data,stmin,stmax); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptrR = ptr(0,0,0,0), + *ptrG = (dim>=2)?ptr(0,0,0,1):0, + *ptrB = (dim>=3)?ptr(0,0,0,2):0; + const unsigned int buf_size = width*height*(dim==1?1:3); + + cimg_std::fprintf(nfile,"P%c\n# CREATOR: CImg Library (original size = %ux%ux%ux%u)\n%u %u\n%u\n", + (dim==1?'5':'6'),width,height,depth,dim,width,height,stmax<256?255:(stmax<4096?4095:65535)); + + switch (dim) { + case 1 : { // Scalar image + if (stmax<256) { // Binary PGM 8 bits + unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) *(xptrd++) = (unsigned char)*(ptrR++); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } else { // Binary PGM 16 bits + unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) *(xptrd++) = (unsigned short)*(ptrR++); + if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } + } break; + case 2 : { // RG image + if (stmax<256) { // Binary PPM 8 bits + unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned char)*(ptrR++); + *(xptrd++) = (unsigned char)*(ptrG++); + *(xptrd++) = 0; + } + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } else { // Binary PPM 16 bits + unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned short)*(ptrR++); + *(xptrd++) = (unsigned short)*(ptrG++); + *(xptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } + } break; + default : { // RGB image + if (stmax<256) { // Binary PPM 8 bits + unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned char)*(ptrR++); + *(xptrd++) = (unsigned char)*(ptrG++); + *(xptrd++) = (unsigned char)*(ptrB++); + } + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } else { // Binary PPM 16 bits + unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; + cimg_forXY(*this,x,y) { + *(xptrd++) = (unsigned short)*(ptrR++); + *(xptrd++) = (unsigned short)*(ptrG++); + *(xptrd++) = (unsigned short)*(ptrB++); + } + if (!cimg::endianness()) cimg::invert_endianness(ptrd,buf_size); + cimg::fwrite(ptrd,buf_size,nfile); + delete[] ptrd; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a PNM file. + const CImg& save_pnm(const char *const filename) const { + return _save_pnm(0,filename); + } + + //! Save the image as a PNM file. + const CImg& save_pnm(cimg_std::FILE *const file) const { + return _save_pnm(file,0); + } + + // Save the image as a RGB file (internal). + const CImg& _save_rgb(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_rgb() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (dim!=3) + cimg::warn("CImg<%s>::save_rgb() : File '%s', instance image (%u,%u,%u,%u,%p) has not exactly 3 channels.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned int wh = width*height; + unsigned char *buffer = new unsigned char[3*wh], *nbuffer=buffer; + const T + *ptr1 = ptr(0,0,0,0), + *ptr2 = dim>1?ptr(0,0,0,1):0, + *ptr3 = dim>2?ptr(0,0,0,2):0; + switch (dim) { + case 1 : { // Scalar image + for (unsigned int k=0; k& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save the image as a RGB file. + const CImg& save_rgb(cimg_std::FILE *const file) const { + return _save_rgb(file,0); + } + + // Save the image as a RGBA file (internal). + const CImg& _save_rgba(cimg_std::FILE *const file, const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_rgba() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + if (dim!=4) + cimg::warn("CImg<%s>::save_rgba() : File '%s, instance image (%u,%u,%u,%u,%p) has not exactly 4 channels.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned int wh = width*height; + unsigned char *buffer = new unsigned char[4*wh], *nbuffer=buffer; + const T + *ptr1 = ptr(0,0,0,0), + *ptr2 = dim>1?ptr(0,0,0,1):0, + *ptr3 = dim>2?ptr(0,0,0,2):0, + *ptr4 = dim>3?ptr(0,0,0,3):0; + switch (dim) { + case 1 : { // Scalar images + for (unsigned int k=0; k& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); + } + + //! Save the image as a RGBA file. + const CImg& save_rgba(cimg_std::FILE *const file) const { + return _save_rgba(file,0); + } + + // Save a plane into a tiff file +#ifdef cimg_use_tiff + const CImg& _save_tiff(TIFF *tif, const unsigned int directory) const { + if (is_empty() || !tif) return *this; + const char *const filename = TIFFFileName(tif); + uint32 rowsperstrip = (uint32)-1; + uint16 spp = dim, bpp = sizeof(T)*8, photometric, compression = COMPRESSION_NONE; + if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; + else photometric = PHOTOMETRIC_MINISBLACK; + TIFFSetDirectory(tif,directory); + TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,width); + TIFFSetField(tif,TIFFTAG_IMAGELENGTH,height); + TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); + TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); + if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); + else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); + else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); + TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); + TIFFSetField(tif,TIFFTAG_COMPRESSION,compression); + rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); + TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); + TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + T *buf = (T*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf){ + for (unsigned int row = 0; rowheight?height-row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif,row,0); + tsize_t i = 0; + for (unsigned int rr = 0; rr::save_tiff() : File '%s', an error has occured while writing a strip.", + pixel_type(),filename?filename:"(FILE*)"); + } + _TIFFfree(buf); + } + TIFFWriteDirectory(tif); + return (*this); + } +#endif + + //! Save a file in TIFF format. + const CImg& save_tiff(const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_tiff() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_tiff() : Specified filename is (null) for instance image (%u,%u,%u,%u,%p).", + pixel_type(),width,height,depth,dim,data); +#ifdef cimg_use_tiff + TIFF *tif = TIFFOpen(filename,"w"); + if (tif) { + cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z); + TIFFClose(tif); + } else throw CImgException("CImg<%s>::save_tiff() : File '%s', error while opening file stream for writing.", + pixel_type(),filename); +#else + return save_other(filename); +#endif + return *this; + } + + //! Save the image as an ANALYZE7.5 or NIFTI file. + const CImg& save_analyze(const char *const filename, const float *const voxsize=0) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_analyze() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_analyze() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + cimg_std::FILE *file; + char header[348], hname[1024], iname[1024]; + const char *ext = cimg::split_filename(filename); + short datatype=-1; + cimg_std::memset(header,0,348); + if (!ext[0]) { cimg_std::sprintf(hname,"%s.hdr",filename); cimg_std::sprintf(iname,"%s.img",filename); } + if (!cimg::strncasecmp(ext,"hdr",3)) { + cimg_std::strcpy(hname,filename); cimg_std::strcpy(iname,filename); cimg_std::sprintf(iname+cimg::strlen(iname)-3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + cimg_std::strcpy(hname,filename); cimg_std::strcpy(iname,filename); cimg_std::sprintf(hname+cimg::strlen(iname)-3,"hdr"); + } + if (!cimg::strncasecmp(ext,"nii",3)) { + cimg_std::strcpy(hname,filename); iname[0] = 0; + } + ((int*)(header))[0] = 348; + cimg_std::sprintf(header+4,"CImg"); + cimg_std::sprintf(header+14," "); + ((short*)(header+36))[0] = 4096; + ((char*)(header+38))[0] = 114; + ((short*)(header+40))[0] = 4; + ((short*)(header+40))[1] = width; + ((short*)(header+40))[2] = height; + ((short*)(header+40))[3] = depth; + ((short*)(header+40))[4] = dim; + if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (datatype<0) + throw CImgIOException("CImg<%s>::save_analyze() : Cannot save image '%s' since pixel type (%s)" + "is not handled in Analyze7.5 specifications.\n", + pixel_type(),filename,pixel_type()); + ((short*)(header+70))[0] = datatype; + ((short*)(header+72))[0] = sizeof(T); + ((float*)(header+112))[0] = 1; + ((float*)(header+76))[0] = 0; + if (voxsize) { + ((float*)(header+76))[1] = voxsize[0]; + ((float*)(header+76))[2] = voxsize[1]; + ((float*)(header+76))[3] = voxsize[2]; + } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1; + file = cimg::fopen(hname,"wb"); + cimg::fwrite(header,348,file); + if (iname[0]) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } + cimg::fwrite(data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save the image as a .cimg file. + const CImg& save_cimg(const char *const filename, const bool compress=false) const { + CImgList(*this,true).save_cimg(filename,compress); + return *this; + } + + // Save the image as a .cimg file. + const CImg& save_cimg(cimg_std::FILE *const file, const bool compress=false) const { + CImgList(*this,true).save_cimg(file,compress); + return *this; + } + + //! Insert the image into an existing .cimg file, at specified coordinates. + const CImg& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int v0) const { + CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,v0); + return *this; + } + + //! Insert the image into an existing .cimg file, at specified coordinates. + const CImg& save_cimg(cimg_std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int v0) const { + CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,v0); + return *this; + } + + //! Save an empty .cimg file with specified dimensions. + static void save_empty_cimg(const char *const filename, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dv); + } + + //! Save an empty .cimg file with specified dimensions. + static void save_empty_cimg(cimg_std::FILE *const file, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + return CImgList::save_empty_cimg(file,1,dx,dy,dz,dv); + } + + // Save the image as an INRIMAGE-4 file (internal). + const CImg& _save_inr(cimg_std::FILE *const file, const char *const filename, const float *const voxsize) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_inr() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + int inrpixsize=-1; + const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } + if (!cimg::strcasecmp(pixel_type(),"char")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; } + if (!cimg::strcasecmp(pixel_type(),"short")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"int")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"float")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; } + if (!cimg::strcasecmp(pixel_type(),"double")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; } + if (inrpixsize<=0) + throw CImgIOException("CImg<%s>::save_inr() : Don't know how to save images of '%s'", + pixel_type(),pixel_type()); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + char header[257]; + int err = cimg_std::sprintf(header,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",width,height,depth,dim); + if (voxsize) err += cimg_std::sprintf(header+err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]); + err += cimg_std::sprintf(header+err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + cimg_std::memset(header+err,'\n',252-err); + cimg_std::memcpy(header+252,"##}\n",4); + cimg::fwrite(header,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forV(*this,k) cimg::fwrite(&((*this)(x,y,z,k)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as an INRIMAGE-4 file. + const CImg& save_inr(const char *const filename, const float *const voxsize=0) const { + return _save_inr(0,filename,voxsize); + } + + //! Save the image as an INRIMAGE-4 file. + const CImg& save_inr(cimg_std::FILE *const file, const float *const voxsize=0) const { + return _save_inr(file,0,voxsize); + } + + // Save the image as a PANDORE-5 file (internal). + unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { + unsigned int nbdims = 0; + if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = width; nbdims = 2; } + if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = height; dims[2] = width; nbdims=3; } + if (id==8 || id==9 || id==10) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } + if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = height; dims[2] = width; dims[3] = colorspace; nbdims = 4; } + if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = depth; dims[2] = height; dims[3] = width; dims[4] = colorspace; nbdims = 5; } + if (id==22 || id==23 || id==25) { dims[0] = dim; dims[1] = width; nbdims = 2; } + if (id==26 || id==27 || id==29) { dims[0] = dim; dims[1] = height; dims[2] = width; nbdims=3; } + if (id==30 || id==31 || id==33) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } + return nbdims; + } + + const CImg& _save_pandore(cimg_std::FILE *const file, const char *const filename, const unsigned int colorspace) const { + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + +#define __cimg_save_pandore_case(dtype) \ + dtype *buffer = new dtype[size()]; \ + const T *ptrs = data; \ + cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer + +#define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ + if (!saved && (sy?(sy==height):true) && (sz?(sz==depth):true) && (sv?(sv==dim):true) && !cimg::strcmp(stype,pixel_type())) { \ + unsigned int *iheader = (unsigned int*)(header+12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ + if (sizeof(ulong)==4) { ulong ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (ulong)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else if (sizeof(uint)==4) { uint ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (uint)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else if (sizeof(ushort)==4) { ushort ndims[5]; for (int d = 0; d<5; ++d) ndims[d] = (ushort)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \ + else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ + "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ + depth,dim,data); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + __cimg_save_pandore_case(uchar); \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + if (sizeof(ulong)==4) { __cimg_save_pandore_case(ulong); } \ + else if (sizeof(uint)==4) { __cimg_save_pandore_case(uint); } \ + else if (sizeof(ushort)==4) { __cimg_save_pandore_case(ushort); } \ + else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ + "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ + depth,dim,data); \ + } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ + if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ + else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ + else throw CImgIOException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p), output type is not" \ + "supported on this architecture.",pixel_type(),filename?filename:"(FILE*)",width,height, \ + depth,dim,data); \ + } \ + saved = true; \ + } + + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_pandore() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 }; + unsigned int nbdims, dims[5]; + bool saved = false; + _cimg_save_pandore_case(1,1,1,"unsigned char",2); + _cimg_save_pandore_case(1,1,1,"char",3); + _cimg_save_pandore_case(1,1,1,"short",3); + _cimg_save_pandore_case(1,1,1,"unsigned short",3); + _cimg_save_pandore_case(1,1,1,"unsigned int",3); + _cimg_save_pandore_case(1,1,1,"int",3); + _cimg_save_pandore_case(1,1,1,"unsigned long",4); + _cimg_save_pandore_case(1,1,1,"long",3); + _cimg_save_pandore_case(1,1,1,"float",4); + _cimg_save_pandore_case(1,1,1,"double",4); + + _cimg_save_pandore_case(0,1,1,"unsigned char",5); + _cimg_save_pandore_case(0,1,1,"char",6); + _cimg_save_pandore_case(0,1,1,"short",6); + _cimg_save_pandore_case(0,1,1,"unsigned short",6); + _cimg_save_pandore_case(0,1,1,"unsigned int",6); + _cimg_save_pandore_case(0,1,1,"int",6); + _cimg_save_pandore_case(0,1,1,"unsigned long",7); + _cimg_save_pandore_case(0,1,1,"long",6); + _cimg_save_pandore_case(0,1,1,"float",7); + _cimg_save_pandore_case(0,1,1,"double",7); + + _cimg_save_pandore_case(0,0,1,"unsigned char",8); + _cimg_save_pandore_case(0,0,1,"char",9); + _cimg_save_pandore_case(0,0,1,"short",9); + _cimg_save_pandore_case(0,0,1,"unsigned short",9); + _cimg_save_pandore_case(0,0,1,"unsigned int",9); + _cimg_save_pandore_case(0,0,1,"int",9); + _cimg_save_pandore_case(0,0,1,"unsigned long",10); + _cimg_save_pandore_case(0,0,1,"long",9); + _cimg_save_pandore_case(0,0,1,"float",10); + _cimg_save_pandore_case(0,0,1,"double",10); + + _cimg_save_pandore_case(0,1,3,"unsigned char",16); + _cimg_save_pandore_case(0,1,3,"char",17); + _cimg_save_pandore_case(0,1,3,"short",17); + _cimg_save_pandore_case(0,1,3,"unsigned short",17); + _cimg_save_pandore_case(0,1,3,"unsigned int",17); + _cimg_save_pandore_case(0,1,3,"int",17); + _cimg_save_pandore_case(0,1,3,"unsigned long",18); + _cimg_save_pandore_case(0,1,3,"long",17); + _cimg_save_pandore_case(0,1,3,"float",18); + _cimg_save_pandore_case(0,1,3,"double",18); + + _cimg_save_pandore_case(0,0,3,"unsigned char",19); + _cimg_save_pandore_case(0,0,3,"char",20); + _cimg_save_pandore_case(0,0,3,"short",20); + _cimg_save_pandore_case(0,0,3,"unsigned short",20); + _cimg_save_pandore_case(0,0,3,"unsigned int",20); + _cimg_save_pandore_case(0,0,3,"int",20); + _cimg_save_pandore_case(0,0,3,"unsigned long",21); + _cimg_save_pandore_case(0,0,3,"long",20); + _cimg_save_pandore_case(0,0,3,"float",21); + _cimg_save_pandore_case(0,0,3,"double",21); + + _cimg_save_pandore_case(1,1,0,"unsigned char",22); + _cimg_save_pandore_case(1,1,0,"char",23); + _cimg_save_pandore_case(1,1,0,"short",23); + _cimg_save_pandore_case(1,1,0,"unsigned short",23); + _cimg_save_pandore_case(1,1,0,"unsigned int",23); + _cimg_save_pandore_case(1,1,0,"int",23); + _cimg_save_pandore_case(1,1,0,"unsigned long",25); + _cimg_save_pandore_case(1,1,0,"long",23); + _cimg_save_pandore_case(1,1,0,"float",25); + _cimg_save_pandore_case(1,1,0,"double",25); + + _cimg_save_pandore_case(0,1,0,"unsigned char",26); + _cimg_save_pandore_case(0,1,0,"char",27); + _cimg_save_pandore_case(0,1,0,"short",27); + _cimg_save_pandore_case(0,1,0,"unsigned short",27); + _cimg_save_pandore_case(0,1,0,"unsigned int",27); + _cimg_save_pandore_case(0,1,0,"int",27); + _cimg_save_pandore_case(0,1,0,"unsigned long",29); + _cimg_save_pandore_case(0,1,0,"long",27); + _cimg_save_pandore_case(0,1,0,"float",29); + _cimg_save_pandore_case(0,1,0,"double",29); + + _cimg_save_pandore_case(0,0,0,"unsigned char",30); + _cimg_save_pandore_case(0,0,0,"char",31); + _cimg_save_pandore_case(0,0,0,"short",31); + _cimg_save_pandore_case(0,0,0,"unsigned short",31); + _cimg_save_pandore_case(0,0,0,"unsigned int",31); + _cimg_save_pandore_case(0,0,0,"int",31); + _cimg_save_pandore_case(0,0,0,"unsigned long",33); + _cimg_save_pandore_case(0,0,0,"long",31); + _cimg_save_pandore_case(0,0,0,"float",33); + _cimg_save_pandore_case(0,0,0,"double",33); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a PANDORE-5 file. + const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { + return _save_pandore(0,filename,colorspace); + } + + //! Save the image as a PANDORE-5 file. + const CImg& save_pandore(cimg_std::FILE *const file, const unsigned int colorspace=0) const { + return _save_pandore(file,0,colorspace); + } + + // Save the image as a RAW file (internal). + const CImg& _save_raw(cimg_std::FILE *const file, const char *const filename, const bool multiplexed) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_raw() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", + pixel_type(),width,height,depth,dim,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!multiplexed) cimg::fwrite(data,size(),nfile); + else { + CImg buf(dim); + cimg_forXYZ(*this,x,y,z) { + cimg_forV(*this,k) buf[k] = (*this)(x,y,z,k); + cimg::fwrite(buf.data,dim,nfile); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save the image as a RAW file. + const CImg& save_raw(const char *const filename, const bool multiplexed=false) const { + return _save_raw(0,filename,multiplexed); + } + + //! Save the image as a RAW file. + const CImg& save_raw(cimg_std::FILE *const file, const bool multiplexed=false) const { + return _save_raw(file,0,multiplexed); + } + + //! Save the image as a video sequence file, using FFMPEG library. + const CImg& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int fps=25) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_ffmpeg() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_ffmpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + if (!fps) + throw CImgArgumentException("CImg<%s>::save_ffmpeg() : File '%s', specified framerate is 0.", + pixel_type(),filename); +#ifndef cimg_use_ffmpeg + return save_ffmpeg_external(filename,first_frame,last_frame); +#else + get_split('z').save_ffmpeg(filename,first_frame,last_frame,fps); +#endif + return *this; + } + + //! Save the image as a YUV video sequence file. + const CImg& save_yuv(const char *const filename, const bool rgb2yuv=true) const { + get_split('z').save_yuv(filename,rgb2yuv); + return *this; + } + + //! Save the image as a YUV video sequence file. + const CImg& save_yuv(cimg_std::FILE *const file, const bool rgb2yuv=true) const { + get_split('z').save_yuv(file,rgb2yuv); + return *this; + } + + // Save OFF files (internal). + template + const CImg& _save_off(cimg_std::FILE *const file, const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_off() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",width,height,depth,dim,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_off() : Specified filename is (null).", + pixel_type()); + if (height<3) return get_resize(-100,3,1,1,0)._save_off(file,filename,primitives,colors,invert_faces); + CImgList _colors; + if (!colors) _colors.insert(primitives.size,CImg::vector(200,200,200)); + const CImgList& ncolors = colors?colors:_colors; + + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + cimg_std::fprintf(nfile,"OFF\n%u %u %u\n",width,primitives.size,3*primitives.size); + cimg_forX(*this,i) cimg_std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const unsigned int prim = primitives[l].size(); + const bool textured = (prim>4); + const CImg& color = ncolors[l]; + const unsigned int s = textured?color.dimv():color.size(); + const float + r = textured?(s>0?(float)(color.get_shared_channel(0).mean()/255.0f):1.0f):(s>0?(float)(color(0)/255.0f):1.0f), + g = textured?(s>1?(float)(color.get_shared_channel(1).mean()/255.0f):r) :(s>1?(float)(color(1)/255.0f):r), + b = textured?(s>2?(float)(color.get_shared_channel(2).mean()/255.0f):r) :(s>2?(float)(color(2)/255.0f):r); + + switch (prim) { + case 1 : + cimg_std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); + break; + case 2 : case 6 : + cimg_std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); + break; + case 3 : case 9 : + if (invert_faces) + cimg_std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),r,g,b); + else + cimg_std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); + break; + case 4 : case 12 : + if (invert_faces) + cimg_std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),(unsigned int)primitives(l,3),r,g,b); + else + cimg_std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); + break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save OFF files. + template + const CImg& save_off(const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + return _save_off(0,filename,primitives,colors,invert_faces); + } + + //! Save OFF files. + template + const CImg& save_off(cimg_std::FILE *const file, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + return _save_off(file,0,primitives,colors,invert_faces); + } + + //! Save the image as a video sequence file, using the external tool 'ffmpeg'. + const CImg& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const char *const codec="mpeg2video") const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_ffmpeg_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_ffmpeg_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + get_split('z').save_ffmpeg_external(filename,first_frame,last_frame,codec); + return *this; + } + + //! Save the image using GraphicsMagick's gm. + /** Function that saves the image for other file formats that are not natively handled by CImg, + using the tool 'gm' from the GraphicsMagick package.\n + This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install + the GraphicsMagick package in order to get + this function working properly (see http://www.graphicsmagick.org ). + **/ + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_graphicsmagick_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_graphicsmagick_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + char command[1024],filetmp[512]; + cimg_std::FILE *file; + do { + if (dim==1) cimg_std::sprintf(filetmp,"%s%s%s.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + else cimg_std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + save_pnm(filetmp); + cimg_std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename); + cimg::system(command); + file = cimg_std::fopen(filename,"rb"); + if (!file) + throw CImgIOException("CImg<%s>::save_graphicsmagick_external() : Failed to save image '%s'.\n\n" + "Path of 'gm' : \"%s\"\n" + "Path of temporary filename : \"%s\"\n", + pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); + if (file) cimg::fclose(file); + cimg_std::remove(filetmp); + return *this; + } + + //! Save an image as a gzipped file, using external tool 'gzip'. + const CImg& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException("CImg<%s>::save_gzip_external() : Cannot save (null) filename.", + pixel_type()); + char command[1024], filetmp[512], body[512]; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + cimg_std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext2); + else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } else { + if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext); + else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + save(filetmp); + cimg_std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); + cimg::system(command); + file = cimg_std::fopen(filename,"rb"); + if (!file) + throw CImgIOException("CImgList<%s>::save_gzip_external() : File '%s' cannot be saved.", + pixel_type(),filename); + else cimg::fclose(file); + cimg_std::remove(filetmp); + return *this; + } + + //! Save the image using ImageMagick's convert. + /** Function that saves the image for other file formats that are not natively handled by CImg, + using the tool 'convert' from the ImageMagick package.\n + This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install + the ImageMagick package in order to get + this function working properly (see http://www.imagemagick.org ). + **/ + const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_imagemagick_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_imagemagick_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + char command[1024], filetmp[512]; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand(),dim==1?"pgm":"ppm"); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + save_pnm(filetmp); + cimg_std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename); + cimg::system(command); + file = cimg_std::fopen(filename,"rb"); + if (!file) + throw CImgIOException("CImg<%s>::save_imagemagick_external() : Failed to save image '%s'.\n\n" + "Path of 'convert' : \"%s\"\n" + "Path of temporary filename : \"%s\"\n", + pixel_type(),filename,cimg::imagemagick_path(),filetmp); + if (file) cimg::fclose(file); + cimg_std::remove(filetmp); + return *this; + } + + //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) + const CImg& save_medcon_external(const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_medcon_external() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save_medcon_external() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type(),width,height,depth,dim,data); + + char command[1024], filetmp[512], body[512]; + cimg_std::FILE *file; + do { + cimg_std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + save_analyze(filetmp); + cimg_std::sprintf(command,"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp); + cimg::system(command); + cimg_std::remove(filetmp); + cimg::split_filename(filetmp,body); + cimg_std::sprintf(filetmp,"%s.img",body); + cimg_std::remove(filetmp); + cimg_std::sprintf(command,"m000-%s",filename); + file = cimg_std::fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::save_medcon_external() : Failed to save image '%s'.\n\n" + "Path of 'medcon' : \"%s\"\n" + "Path of temporary filename : \"%s\"", + pixel_type(),filename,cimg::medcon_path(),filetmp); + } else cimg::fclose(file); + cimg_std::rename(command,filename); + return *this; + } + + // Try to save the image if other extension is provided. + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + if (is_empty()) + throw CImgInstanceException("CImg<%s>::save_other() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",width,height,depth,dim,data); + if (!filename) + throw CImgIOException("CImg<%s>::save_other() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", + pixel_type()); + const unsigned int odebug = cimg::exception_mode(); + bool is_saved = true; + cimg::exception_mode() = 0; + try { save_magick(filename); } + catch (CImgException&) { + try { save_imagemagick_external(filename,quality); } + catch (CImgException&) { + try { save_graphicsmagick_external(filename,quality); } + catch (CImgException&) { + is_saved = false; + } + } + } + cimg::exception_mode() = odebug; + if (!is_saved) + throw CImgIOException("CImg<%s>::save_other() : File '%s' cannot be saved.\n" + "Check you have either the ImageMagick or GraphicsMagick package installed.", + pixel_type(),filename); + return *this; + } + + // Get a 40x38 color logo of a 'danger' item (internal). + static CImg logo40x38() { + static bool first_time = true; + static CImg res(40,38,1,3); + if (first_time) { + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.ptr(0,0,0,0), *ptr2 = res.ptr(0,0,0,1), *ptr3 = res.ptr(0,0,0,2); + for (unsigned int off = 0; off structure + # + # + # + #------------------------------------------ + */ + + //! Class representing list of images CImg. + template + struct CImgList { + + //! Size of the list (number of elements inside). + unsigned int size; + + //! Allocation size of the list. + unsigned int allocsize; + + //! Pointer to the first list element. + CImg *data; + + //! Define a CImgList::iterator. + typedef CImg* iterator; + + //! Define a CImgList::const_iterator. + typedef const CImg* const_iterator; + + //! Get value type. + typedef T value_type; + + // Define common T-dependant types. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif +#ifdef cimglist_plugin1 +#include cimglist_plugin1 +#endif +#ifdef cimglist_plugin2 +#include cimglist_plugin2 +#endif +#ifdef cimglist_plugin3 +#include cimglist_plugin3 +#endif +#ifdef cimglist_plugin4 +#include cimglist_plugin4 +#endif +#ifdef cimglist_plugin5 +#include cimglist_plugin5 +#endif +#ifdef cimglist_plugin6 +#include cimglist_plugin6 +#endif +#ifdef cimglist_plugin7 +#include cimglist_plugin7 +#endif +#ifdef cimglist_plugin8 +#include cimglist_plugin8 +#endif + //@} + + //------------------------------------------ + // + //! \name Constructors - Destructor - Copy + //@{ + //------------------------------------------ + + //! Destructor. + ~CImgList() { + if (data) delete[] data; + } + + //! Default constructor. + CImgList(): + size(0),allocsize(0),data(0) {} + + //! Construct an image list containing n empty images. + explicit CImgList(const unsigned int n): + size(n) { + data = new CImg[allocsize = cimg::max(16UL,cimg::nearest_pow2(n))]; + } + + //! Default copy constructor. + template + CImgList(const CImgList& list): + size(0),allocsize(0),data(0) { + assign(list.size); + cimglist_for(*this,l) data[l].assign(list[l],false); + } + + CImgList(const CImgList& list): + size(0),allocsize(0),data(0) { + assign(list.size); + cimglist_for(*this,l) data[l].assign(list[l],list[l].is_shared); + } + + //! Advanced copy constructor. + template + CImgList(const CImgList& list, const bool shared): + size(0),allocsize(0),data(0) { + assign(list.size); + if (shared) + throw CImgArgumentException("CImgList<%s>::CImgList() : Cannot construct a list instance with shared images from " + "a CImgList<%s> (different pixel types).", + pixel_type(),CImgList::pixel_type()); + cimglist_for(*this,l) data[l].assign(list[l],false); + } + + CImgList(const CImgList& list, const bool shared): + size(0),allocsize(0),data(0) { + assign(list.size); + cimglist_for(*this,l) data[l].assign(list[l],shared); + } + + //! Construct an image list containing n images with specified size. + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int dim=1): + size(0),allocsize(0),data(0) { + assign(n); + cimglist_for(*this,l) data[l].assign(width,height,depth,dim); + } + + //! Construct an image list containing n images with specified size, filled with specified value. + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const T val): + size(0),allocsize(0),data(0) { + assign(n); + cimglist_for(*this,l) data[l].assign(width,height,depth,dim,val); + } + + //! Construct an image list containing n images with specified size and specified pixel values (int version). + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const int val0, const int val1, ...): + size(0),allocsize(0),data(0) { +#define _CImgList_stdarg(t) { \ + assign(n,width,height,depth,dim); \ + const unsigned int siz = width*height*depth*dim, nsiz = siz*n; \ + T *ptrd = data->data; \ + va_list ap; \ + va_start(ap,val1); \ + for (unsigned int l=0, s=0, i=0; i + CImgList(const unsigned int n, const CImg& img): + size(0),allocsize(0),data(0) { + assign(n); + cimglist_for(*this,l) data[l].assign(img,img.is_shared); + } + + //! Construct a list containing n copies of the image img, forcing the shared state. + template + CImgList(const unsigned int n, const CImg& img, const bool shared): + size(0),allocsize(0),data(0) { + assign(n); + cimglist_for(*this,l) data[l].assign(img,shared); + } + + //! Construct an image list from one image. + template + explicit CImgList(const CImg& img): + size(0),allocsize(0),data(0) { + assign(1); + data[0].assign(img,img.is_shared); + } + + //! Construct an image list from one image, forcing the shared state. + template + explicit CImgList(const CImg& img, const bool shared): + size(0),allocsize(0),data(0) { + assign(1); + data[0].assign(img,shared); + } + + //! Construct an image list from two images. + template + CImgList(const CImg& img1, const CImg& img2): + size(0),allocsize(0),data(0) { + assign(2); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); + } + + //! Construct an image list from two images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const bool shared): + size(0),allocsize(0),data(0) { + assign(2); + data[0].assign(img1,shared); data[1].assign(img2,shared); + } + + //! Construct an image list from three images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3): + size(0),allocsize(0),data(0) { + assign(3); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); + } + + //! Construct an image list from three images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared): + size(0),allocsize(0),data(0) { + assign(3); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); + } + + //! Construct an image list from four images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4): + size(0),allocsize(0),data(0) { + assign(4); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); + } + + //! Construct an image list from four images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared): + size(0),allocsize(0),data(0) { + assign(4); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + } + + //! Construct an image list from five images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5): + size(0),allocsize(0),data(0) { + assign(5); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); + data[4].assign(img5,img5.is_shared); + } + + //! Construct an image list from five images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool shared): + size(0),allocsize(0),data(0) { + assign(5); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); + } + + //! Construct an image list from six images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6): + size(0),allocsize(0),data(0) { + assign(6); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); + data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); + } + + //! Construct an image list from six images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool shared): + size(0),allocsize(0),data(0) { + assign(6); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); + } + + //! Construct an image list from seven images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7): + size(0),allocsize(0),data(0) { + assign(7); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); + data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); data[6].assign(img7,img7.is_shared); + } + + //! Construct an image list from seven images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool shared): + size(0),allocsize(0),data(0) { + assign(7); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); + } + + //! Construct an image list from eight images. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8): + size(0),allocsize(0),data(0) { + assign(8); + data[0].assign(img1,img1.is_shared); data[1].assign(img2,img2.is_shared); data[2].assign(img3,img3.is_shared); data[3].assign(img4,img4.is_shared); + data[4].assign(img5,img5.is_shared); data[5].assign(img6,img6.is_shared); data[6].assign(img7,img7.is_shared); data[7].assign(img8,img8.is_shared); + } + + //! Construct an image list from eight images, forcing the shared state. + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared): + size(0),allocsize(0),data(0) { + assign(8); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); + } + + //! Construct an image list from a filename. + CImgList(const char *const filename): + size(0),allocsize(0),data(0) { + assign(filename); + } + + //! In-place version of the default constructor and default destructor. + CImgList& assign() { + if (data) delete[] data; + size = allocsize = 0; + data = 0; + return *this; + } + + //! Equivalent to assign() (STL-compliant name). + CImgList& clear() { + return assign(); + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const unsigned int n) { + if (n) { + if (allocsize(n<<2)) { + if (data) delete[] data; + data = new CImg[allocsize=cimg::max(16UL,cimg::nearest_pow2(n))]; + } + size = n; + } else assign(); + return *this; + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int dim=1) { + assign(n); + cimglist_for(*this,l) data[l].assign(width,height,depth,dim); + return *this; + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const T val) { + assign(n); + cimglist_for(*this,l) data[l].assign(width,height,depth,dim,val); + return *this; + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const int val0, const int val1, ...) { + _CImgList_stdarg(int); + return *this; + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int dim, const double val0, const double val1, ...) { + _CImgList_stdarg(double); + return *this; + } + + //! In-place version of the copy constructor. + template + CImgList& assign(const CImgList& list) { + assign(list.size); + cimglist_for(*this,l) data[l].assign(list[l],list[l].is_shared); + return *this; + } + + //! In-place version of the copy constructor. + template + CImgList& assign(const CImgList& list, const bool shared) { + assign(list.size); + cimglist_for(*this,l) data[l].assign(list[l],shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const unsigned int n, const CImg& img, const bool shared=false) { + assign(n); + cimglist_for(*this,l) data[l].assign(img,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img, const bool shared=false) { + assign(1); + data[0].assign(img,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const bool shared=false) { + assign(2); + data[0].assign(img1,shared); data[1].assign(img2,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false) { + assign(3); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool shared=false) { + assign(4); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool shared=false) { + assign(5); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool shared=false) { + assign(6); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false) { + assign(7); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false) { + assign(8); + data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); + data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); + return *this; + } + + //! In-place version of the corresponding constructor. + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Transfer the content of the instance image list into another one. + template + CImgList& transfer_to(CImgList& list) { + list.assign(*this); + assign(); + return list; + } + + CImgList& transfer_to(CImgList& list) { + list.assign(); + return swap(list); + } + + //! Swap all fields of two CImgList instances (use with care !) + CImgList& swap(CImgList& list) { + cimg::swap(size,list.size); + cimg::swap(allocsize,list.allocsize); + cimg::swap(data,list.data); + return list; + } + + //! Return a string describing the type of the image pixels in the list (template parameter \p T). + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return \p true if list is empty. + bool is_empty() const { + return (!data || !size); + } + + //! Return \p true if list is not empty. + operator bool() const { + return !is_empty(); + } + + //! Return \p true if list if of specified size. + bool is_sameN(const unsigned int n) const { + return (size==n); + } + + //! Return \p true if list if of specified size. + template + bool is_sameN(const CImgList& list) const { + return (size==list.size); + } + + // Define useful dimension check functions. + // (not documented because they are macro-generated). +#define _cimglist_def_is_same1(axis) \ + bool is_same##axis(const unsigned int val) const { \ + bool res = true; for (unsigned int l = 0; l bool is_same##axis(const CImg& img) const { \ + bool res = true; for (unsigned int l = 0; l bool is_same##axis(const CImgList& list) const { \ + const unsigned int lmin = cimg::min(size,list.size); \ + bool res = true; for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ + return (is_sameN(n) && is_same##axis(img)); \ + } \ + template bool is_sameN##axis(const CImgList& list) const { \ + return (is_sameN(list) && is_same##axis(list)); \ + } + + _cimglist_def_is_same(XY) + _cimglist_def_is_same(XZ) + _cimglist_def_is_same(XV) + _cimglist_def_is_same(YZ) + _cimglist_def_is_same(YV) + _cimglist_def_is_same(XYZ) + _cimglist_def_is_same(XYV) + _cimglist_def_is_same(YZV) + _cimglist_def_is_same(XYZV) + _cimglist_def_is_same1(X) + _cimglist_def_is_same1(Y) + _cimglist_def_is_same1(Z) + _cimglist_def_is_same1(V) + _cimglist_def_is_same2(X,Y) + _cimglist_def_is_same2(X,Z) + _cimglist_def_is_same2(X,V) + _cimglist_def_is_same2(Y,Z) + _cimglist_def_is_same2(Y,V) + _cimglist_def_is_same2(Z,V) + _cimglist_def_is_same3(X,Y,Z) + _cimglist_def_is_same3(X,Y,V) + _cimglist_def_is_same3(X,Z,V) + _cimglist_def_is_same3(Y,Z,V) + + bool is_sameXYZV(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) const { + bool res = true; + for (unsigned int l = 0; l=0 && n<(int)size && x>=0 && x=0 && y=0 && z=0 && v=0 && n<(int)size; + } + + //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z,v). + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& v) const { + if (is_empty()) return false; + cimglist_for(*this,l) if (data[l].contains(pixel,x,y,z,v)) { n = (t)l; return true; } + return false; + } + + //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z). + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { + t v; + return contains(pixel,n,x,y,z,v); + } + + //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y). + template + bool contains(const T& pixel, t& n, t& x, t&y) const { + t z,v; + return contains(pixel,n,x,y,z,v); + } + + //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x). + template + bool contains(const T& pixel, t& n, t& x) const { + t y,z,v; + return contains(pixel,n,x,y,z,v); + } + + //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n). + template + bool contains(const T& pixel, t& n) const { + t x,y,z,v; + return contains(pixel,n,x,y,z,v); + } + + //! Return \c true if one of the image list contains the specified referenced value. + bool contains(const T& pixel) const { + unsigned int n,x,y,z,v; + return contains(pixel,n,x,y,z,v); + } + + //! Return \c true if the list contains the image 'img'. If true, returns the position (n) of the image in the list. + template + bool contains(const CImg& img, t& n) const { + if (is_empty()) return false; + const CImg *const ptr = &img; + cimglist_for(*this,i) if (data+i==ptr) { n = (t)i; return true; } + return false; + } + + //! Return \c true if the list contains the image img. + bool contains(const CImg& img) const { + unsigned int n; + return contains(img,n); + } + + //@} + //------------------------------ + // + //! \name Arithmetics Operators + //@{ + //------------------------------ + + //! Assignment operator + template + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Assignment operator. + template + CImgList& operator=(const CImg& img) { + cimglist_for(*this,l) data[l] = img; + return *this; + } + + //! Assignment operator. + CImgList& operator=(const T val) { + cimglist_for(*this,l) data[l].fill(val); + return *this; + } + + //! Operator+. + CImgList operator+() const { + return CImgList(*this); + } + + //! Operator+=. +#ifdef cimg_use_visualcpp6 + CImgList& operator+=(const T val) +#else + template + CImgList& operator+=(const t val) +#endif + { + cimglist_for(*this,l) (*this)[l]+=val; + return *this; + } + + //! Operator+=. + template + CImgList& operator+=(const CImgList& list) { + const unsigned int sizemax = cimg::min(size,list.size); + for (unsigned int l=0; l& operator++() { + cimglist_for(*this,l) ++(*this)[l]; + return *this; + } + + //! Operator++ (postfix). + CImgList operator++(int) { + CImgList copy(*this); + ++*this; + return copy; + } + + //! Operator-. + CImgList operator-() const { + CImgList res(size); + cimglist_for(res,l) res[l].assign(-data[l]); + return res; + } + + //! Operator-=. +#ifdef cimg_use_visualcpp6 + CImgList& operator-=(const T val) +#else + template + CImgList& operator-=(const t val) +#endif + { + cimglist_for(*this,l) (*this)[l]-=val; + return *this; + } + + //! Operator-=. + template + CImgList& operator-=(const CImgList& list) { + const unsigned int sizemax = min(size,list.size); + for (unsigned int l=0; l& operator--() { + cimglist_for(*this,l) --(*this)[l]; + return *this; + } + + //! Operator-- (postfix). + CImgList operator--(int) { + CImgList copy(*this); + --*this; + return copy; + } + + //! Operator*=. +#ifdef cimg_use_visualcpp6 + CImgList& operator*=(const double val) +#else + template + CImgList& operator*=(const t val) +#endif + { + cimglist_for(*this,l) (*this)[l]*=val; + return *this; + } + + //! Operator*=. + template + CImgList& operator*=(const CImgList& list) { + const unsigned int N = cimg::min(size,list.size); + for (unsigned int l=0; l& operator/=(const double val) +#else + template + CImgList& operator/=(const t val) +#endif + { + cimglist_for(*this,l) (*this)[l]/=val; + return *this; + } + + //! Operator/=. + template + CImgList& operator/=(const CImgList& list) { + const unsigned int N = cimg::min(size,list.size); + for (unsigned int l=0; l::max() : Instance image list is empty.", + pixel_type()); + const T *ptrmax = data->data; + T max_value = *ptrmax; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); + } + return *ptrmax; + } + + //! Return a reference to the maximum pixel value of the instance list. + T& max() { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::max() : Instance image list is empty.", + pixel_type()); + T *ptrmax = data->data; + T max_value = *ptrmax; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); + } + return *ptrmax; + } + + //! Return a reference to the minimum pixel value of the instance list. + const T& min() const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::min() : Instance image list is empty.", + pixel_type()); + const T *ptrmin = data->data; + T min_value = *ptrmin; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) if ((*ptr)::min() : Instance image list is empty.", + pixel_type()); + T *ptrmin = data->data; + T min_value = *ptrmin; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) if ((*ptr) + const T& minmax(t& max_val) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.", + pixel_type()); + const T *ptrmin = data->data; + T min_value = *ptrmin, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) { + const T val = *ptr; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptrmin; + } + + //! Return a reference to the minimum pixel value of the instance list. + template + T& minmax(t& max_val) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.", + pixel_type()); + T *ptrmin = data->data; + T min_value = *ptrmin, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) { + const T val = *ptr; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptrmin; + } + + //! Return a reference to the minimum pixel value of the instance list. + template + const T& maxmin(t& min_val) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.", + pixel_type()); + const T *ptrmax = data->data; + T min_value = *ptrmax, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) { + const T val = *ptr; + if (val>max_value) { max_value = val; ptrmax = ptr; } + if (val + T& maxmin(t& min_val) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.", + pixel_type()); + T *ptrmax = data->data; + T min_value = *ptrmax, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) { + const T val = *ptr; + if (val>max_value) { max_value = val; ptrmax = ptr; } + if (val::mean() : Instance image list is empty.", + pixel_type()); + double val = 0; + unsigned int siz = 0; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) val+=(double)*ptr; + siz+=img.size(); + } + return val/siz; + } + + //! Return the variance of the instance list. + double variance() { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::variance() : Instance image list is empty.", + pixel_type()); + double res = 0; + unsigned int siz = 0; + double S = 0, S2 = 0; + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_for(img,ptr,T) { const double val = (double)*ptr; S+=val; S2+=val*val; } + siz+=img.size(); + } + res = (S2 - S*S/siz)/siz; + return res; + } + + //! Compute a list of statistics vectors (min,max,mean,variance,xmin,ymin,zmin,vmin,xmax,ymax,zmax,vmax). + CImgList& stats(const unsigned int variance_method=1) { + if (is_empty()) return *this; + cimglist_for(*this,l) data[l].stats(variance_method); + return *this; + } + + CImgList get_stats(const unsigned int variance_method=1) const { + CImgList res(size); + cimglist_for(*this,l) res[l] = data[l].get_stats(variance_method); + return res; + } + + //@} + //------------------------- + // + //! \name List Manipulation + //@{ + //------------------------- + + //! Return a reference to the i-th element of the image list. + CImg& operator[](const unsigned int pos) { +#if cimg_debug>=3 + if (pos>=size) { + cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images", + pixel_type(),pos,size); + return *data; + } +#endif + return data[pos]; + } + + const CImg& operator[](const unsigned int pos) const { +#if cimg_debug>=3 + if (pos>=size) { + cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images", + pixel_type(),pos,size); + return *data; + } +#endif + return data[pos]; + } + + //! Equivalent to CImgList::operator[] + CImg& operator()(const unsigned int pos) { + return (*this)[pos]; + } + + const CImg& operator()(const unsigned int pos) const { + return (*this)[pos]; + } + + //! Return a reference to (x,y,z,v) pixel of the pos-th image of the list + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int v=0) { + return (*this)[pos](x,y,z,v); + } + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int v=0) const { + return (*this)[pos](x,y,z,v); + } + + // This function is only here for template tricks. + T _display_object3d_at2(const int i, const int j) const { + return atNXY(i,0,j,0,0,0); + } + + //! Read an image in specified position. + CImg& at(const int pos) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::at() : Instance list is empty.", + pixel_type()); + return data[pos<0?0:pos>=(int)size?(int)size-1:pos]; + } + + //! Read a pixel value with Dirichlet boundary conditions. + T& atNXYZV(const int pos, const int x, const int y, const int z, const int v, const T out_val) { + return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXYZV(x,y,z,v,out_val); + } + + T atNXYZV(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { + return (pos<0 || pos>=(int)size)?out_val:data[pos].atXYZV(x,y,z,v,out_val); + } + + //! Read a pixel value with Neumann boundary conditions. + T& atNXYZV(const int pos, const int x, const int y, const int z, const int v) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXYZV() : Instance list is empty.", + pixel_type()); + return _atNXYZV(pos,x,y,z,v); + } + + T atNXYZV(const int pos, const int x, const int y, const int z, const int v) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXYZV() : Instance list is empty.", + pixel_type()); + return _atNXYZV(pos,x,y,z,v); + } + + T& _atNXYZV(const int pos, const int x, const int y, const int z, const int v) { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZV(x,y,z,v); + } + + T _atNXYZV(const int pos, const int x, const int y, const int z, const int v) const { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZV(x,y,z,v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). + T& atNXYZ(const int pos, const int x, const int y, const int z, const int v, const T out_val) { + return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXYZ(x,y,z,v,out_val); + } + + T atNXYZ(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { + return (pos<0 || pos>=(int)size)?out_val:data[pos].atXYZ(x,y,z,v,out_val); + } + + //! Read a pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z). + T& atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXYZ() : Instance list is empty.", + pixel_type()); + return _atNXYZ(pos,x,y,z,v); + } + + T atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXYZ() : Instance list is empty.", + pixel_type()); + return _atNXYZ(pos,x,y,z,v); + } + + T& _atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZ(x,y,z,v); + } + + T _atNXYZ(const int pos, const int x, const int y, const int z, const int v=0) const { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXYZ(x,y,z,v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y). + T& atNXY(const int pos, const int x, const int y, const int z, const int v, const T out_val) { + return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atXY(x,y,z,v,out_val); + } + + T atNXY(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { + return (pos<0 || pos>=(int)size)?out_val:data[pos].atXY(x,y,z,v,out_val); + } + + //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y). + T& atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXY() : Instance list is empty.", + pixel_type()); + return _atNXY(pos,x,y,z,v); + } + + T atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNXY() : Instance list is empty.", + pixel_type()); + return _atNXY(pos,x,y,z,v); + } + + T& _atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXY(x,y,z,v); + } + + T _atNXY(const int pos, const int x, const int y, const int z=0, const int v=0) const { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atXY(x,y,z,v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x). + T& atNX(const int pos, const int x, const int y, const int z, const int v, const T out_val) { + return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):data[pos].atX(x,y,z,v,out_val); + } + + T atNX(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { + return (pos<0 || pos>=(int)size)?out_val:data[pos].atX(x,y,z,v,out_val); + } + + //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x). + T& atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNX() : Instance list is empty.", + pixel_type()); + return _atNX(pos,x,y,z,v); + } + + T atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atNX() : Instance list is empty.", + pixel_type()); + return _atNX(pos,x,y,z,v); + } + + T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atX(x,y,z,v); + } + + T _atNX(const int pos, const int x, const int y=0, const int z=0, const int v=0) const { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)].atX(x,y,z,v); + } + + //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c pos). + T& atN(const int pos, const int x, const int y, const int z, const int v, const T out_val) { + return (pos<0 || pos>=(int)size)?(cimg::temporary(out_val)=out_val):(*this)(pos,x,y,z,v); + } + + T atN(const int pos, const int x, const int y, const int z, const int v, const T out_val) const { + return (pos<0 || pos>=(int)size)?out_val:(*this)(pos,x,y,z,v); + } + + //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c pos). + T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atN() : Instance list is empty.", + pixel_type()); + return _atN(pos,x,y,z,v); + } + + T atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::atN() : Instance list is empty.", + pixel_type()); + return _atN(pos,x,y,z,v); + } + + T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)](x,y,z,v); + } + + T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int v=0) const { + return data[pos<0?0:(pos>=(int)size?(int)size-1:pos)](x,y,z,v); + } + + //! Returns a reference to the last element. + CImg& back() { + return (*this)(size-1); + } + + const CImg& back() const { + return (*this)(size-1); + } + + //! Returns a reference to the first element. + CImg& front() { + return *data; + } + + const CImg& front() const { + return *data; + } + + //! Returns an iterator to the beginning of the vector. + iterator begin() { + return data; + } + + const_iterator begin() const { + return data; + } + + //! Return a reference to the first image. + const CImg& first() const { + return *data; + } + + CImg& first() { + return *data; + } + + //! Returns an iterator just past the last element. + iterator end() { + return data + size; + } + + const_iterator end() const { + return data + size; + } + + //! Return a reference to the last image. + const CImg& last() const { + return data[size - 1]; + } + + CImg& last() { + return data[size - 1]; + } + + //! Insert a copy of the image \p img into the current image list, at position \p pos. + template + CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { + const unsigned int npos = pos==~0U?size:pos; + if (npos>size) + throw CImgArgumentException("CImgList<%s>::insert() : Cannot insert at position %u into a list with %u elements", + pixel_type(),npos,size); + if (shared) + throw CImgArgumentException("CImgList<%s>::insert(): Cannot insert a shared image CImg<%s> into a CImgList<%s>", + pixel_type(),img.pixel_type(),pixel_type()); + CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=16)]:0; + if (!size || !data) { + data = new_data; + *data = img; + } else { + if (new_data) { + if (npos) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos); + if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); + cimg_std::memset(data,0,sizeof(CImg)*(size-1)); + delete[] data; + data = new_data; + } + else if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); + data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; + data[npos] = img; + } + return *this; + } + + CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { + const unsigned int npos = pos==~0U?size:pos; + if (npos>size) + throw CImgArgumentException("CImgList<%s>::insert() : Can't insert at position %u into a list with %u elements", + pixel_type(),npos,size); + if (&img>=data && &img *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=16)]:0; + if (!size || !data) { + data = new_data; + if (shared && img) { + data->width = img.width; data->height = img.height; data->depth = img.depth; data->dim = img.dim; + data->is_shared = true; data->data = img.data; + } else *data = img; + } + else { + if (new_data) { + if (npos) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos); + if (npos!=size-1) cimg_std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); + if (shared && img) { + new_data[npos].width = img.width; new_data[npos].height = img.height; new_data[npos].depth = img.depth; + new_data[npos].dim = img.dim; new_data[npos].is_shared = true; new_data[npos].data = img.data; + } else { + new_data[npos].width = new_data[npos].height = new_data[npos].depth = new_data[npos].dim = 0; new_data[npos].data = 0; + new_data[npos] = img; + } + cimg_std::memset(data,0,sizeof(CImg)*(size-1)); + delete[] data; + data = new_data; + } else { + if (npos!=size-1) cimg_std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); + if (shared && img) { + data[npos].width = img.width; data[npos].height = img.height; data[npos].depth = img.depth; data[npos].dim = img.dim; + data[npos].is_shared = true; data[npos].data = img.data; + } else { + data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; + data[npos] = img; + } + } + } + return *this; + } + + // The two functions below are necessary due to Visual C++ 6.0 function overloading bugs, when + // default parameters are used in function signatures. + template + CImgList& insert(const CImg& img, const unsigned int pos) { + return insert(img,pos,false); + } + + //! Insert a copy of the image \p img into the current image list, at position \p pos. + template + CImgList& insert(const CImg& img) { + return insert(img,~0U,false); + } + + template + CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { + return (+*this).insert(img,pos,shared); + } + + //! Insert n empty images img into the current image list, at position \p pos. + CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { + CImg foo; + if (!n) return *this; + const unsigned int npos = pos==~0U?size:pos; + for (unsigned int i=0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { + return (+*this).insert(n,pos); + } + + //! Insert n copies of the image \p img into the current image list, at position \p pos. + template + CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?size:pos; + insert(img,npos,shared); + for (unsigned int i=1; i + CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { + return (+*this).insert(n,img,pos,shared); + } + + //! Insert a copy of the image list \p list into the current image list, starting from position \p pos. + template + CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { + const unsigned int npos = pos==~0U?size:pos; + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,shared); + else insert(CImgList(list),npos,shared); + return *this; + } + + template + CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { + return (+*this).insert(list,pos,shared); + } + + //! Insert n copies of the list \p list at position \p pos of the current list. + template + CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?size:pos; + for (unsigned int i=0; i + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const bool shared=false) const { + return (+*this).insert(n,list,pos,shared); + } + + //! Insert a copy of the image \p img at the end of the current image list. + template + CImgList& operator<<(const CImg& img) { + return insert(img); + } + + //! Insert a copy of the image list \p list at the end of the current image list. + template + CImgList& operator<<(const CImgList& list) { + return insert(list); + } + + //! Return a copy of the current image list, where the image \p img has been inserted at the end. + template + CImgList& operator>>(CImg& img) const { + typedef typename cimg::superset::type Tt; + return CImgList(*this).insert(img); + } + + //! Insert a copy of the current image list at the beginning of the image list \p list. + template + CImgList& operator>>(CImgList& list) const { + return list.insert(*this,0); + } + + //! Remove the images at positions \p pos1 to \p pos2 from the image list. + CImgList& remove(const unsigned int pos1, const unsigned int pos2) { + const unsigned int + npos1 = pos1=size) + cimg::warn("CImgList<%s>::remove() : Cannot remove images from a list (%p,%u), at positions %u->%u.", + pixel_type(),data,size,npos1,tpos2); + else { + if (tpos2>=size) + cimg::warn("CImgList<%s>::remove() : Cannot remove all images from a list (%p,%u), at positions %u->%u.", + pixel_type(),data,size,npos1,tpos2); + for (unsigned int k = npos1; k<=npos2; ++k) data[k].assign(); + const unsigned int nb = 1 + npos2 - npos1; + if (!(size-=nb)) return assign(); + if (size>(allocsize>>2) || allocsize<=8) { // Removing items without reallocation. + if (npos1!=size) cimg_std::memmove(data+npos1,data+npos2+1,sizeof(CImg)*(size-npos1)); + cimg_std::memset(data+size,0,sizeof(CImg)*nb); + } else { // Removing items with reallocation. + allocsize>>=2; + while (allocsize>8 && size<(allocsize>>1)) allocsize>>=1; + CImg *new_data = new CImg[allocsize]; + if (npos1) cimg_std::memcpy(new_data,data,sizeof(CImg)*npos1); + if (npos1!=size) cimg_std::memcpy(new_data+npos1,data+npos2+1,sizeof(CImg)*(size-npos1)); + if (size!=allocsize) cimg_std::memset(new_data+size,0,sizeof(allocsize-size)); + cimg_std::memset(data,0,sizeof(CImg)*(size+nb)); + delete[] data; + data = new_data; + } + } + return *this; + } + + CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { + return (+*this).remove(pos1,pos2); + } + + //! Remove the image at position \p pos from the image list. + CImgList& remove(const unsigned int pos) { + return remove(pos,pos); + } + + CImgList get_remove(const unsigned int pos) const { + return (+*this).remove(pos); + } + + //! Remove the last image from the image list. + CImgList& remove() { + if (size) return remove(size-1); + else cimg::warn("CImgList<%s>::remove() : List is empty", + pixel_type()); + return *this; + } + + CImgList get_remove() const { + return (+*this).remove(); + } + + //! Reverse list order. + CImgList& reverse() { + for (unsigned int l=0; l get_reverse() const { + return (+*this).reverse(); + } + + //! Get a sub-list. + CImgList& crop(const unsigned int i0, const unsigned int i1, const bool shared=false) { + return get_crop(i0,i1,shared).transfer_to(*this); + } + + CImgList get_crop(const unsigned int i0, const unsigned int i1, const bool shared=false) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", + pixel_type(),i0,i1,size,data); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l].assign((*this)[i0+l],shared); + return res; + } + + //! Get sub-images of a sublist. + CImgList& crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, const int z0, const int v0, + const int x1, const int y1, const int z1, const int v1) { + return get_crop(i0,i1,x0,y0,z0,v0,x1,y1,z1,v1).transfer_to(*this); + } + + CImgList get_crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, const int z0, const int v0, + const int x1, const int y1, const int z1, const int v1) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", + pixel_type(),i0,i1,size,data); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,v0,x1,y1,z1,v1); + return res; + } + + //! Get sub-images of a sublist. + CImgList& crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1) { + return get_crop(i0,i1,x0,y0,z0,x1,y1,z1).transfer_to(*this); + } + + CImgList get_crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", + pixel_type(),i0,i1,size,data); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,x1,y1,z1); + return res; + } + + //! Get sub-images of a sublist. + CImgList& crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, + const int x1, const int y1) { + return get_crop(i0,i1,x0,y0,x1,y1).transfer_to(*this); + } + + CImgList get_crop(const unsigned int i0, const unsigned int i1, + const int x0, const int y0, + const int x1, const int y1) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", + pixel_type(),i0,i1,size,data); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,x1,y1); + return res; + } + + //! Get sub-images of a sublist. + CImgList& crop(const unsigned int i0, const unsigned int i1, + const int x0, const int x1) { + return get_crop(i0,i1,x0,x1).transfer_to(*this); + } + + CImgList get_crop(const unsigned int i0, const unsigned int i1, + const int x0, const int x1) const { + if (i0>i1 || i1>=size) + throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", + pixel_type(),i0,i1,size,data); + CImgList res(i1-i0+1); + cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,x1); + return res; + } + + //! Display an image list into a CImgDisplay. + const CImgList& operator>>(CImgDisplay& disp) const { + return display(disp); + } + + //! Insert image \p img at the end of the list. + template + CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image \p img at the front of the list. + template + CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list \p list at the end of the current list. + template + CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list \p list at the front of the current list. + template + CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Remove last element of the list. + CImgList& pop_back() { + return remove(size-1); + } + + //! Remove first element of the list. + CImgList& pop_front() { + return remove(0); + } + + //! Remove the element pointed by iterator \p iter. + CImgList& erase(const iterator iter) { + return remove(iter-data); + } + + //@} + //---------------------------- + // + //! \name Fourier Transforms + //@{ + //---------------------------- + + //! Compute the Fast Fourier Transform (along the specified axis). + CImgList& FFT(const char axis, const bool invert=false) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty", + pixel_type(),size,data); + if (!data[0]) + throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) is empty", + pixel_type(),data[0].width,data[0].height,data[0].depth,data[0].dim,data[0].data); + if (size>2) + cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images", + pixel_type(),size,data); + if (size==1) insert(CImg(data[0].width,data[0].height,data[0].depth,data[0].dim,0)); + CImg &Ir = data[0], &Ii = data[1]; + if (Ir.width!=Ii.width || Ir.height!=Ii.height || Ir.depth!=Ii.depth || Ir.dim!=Ii.dim) + throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p)" + "have different dimensions", + pixel_type(),Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data,Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); + +#ifdef cimg_use_fftw3 + fftw_complex *data_in; + fftw_plan data_plan; + + switch (cimg::uncase(axis)) { + case 'x' : { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*Ir.width); + data_plan = fftw_plan_dft_1d(Ir.width,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forYZV(Ir,y,z,k) { + T *ptrr = Ir.ptr(0,y,z,k), *ptri = Ii.ptr(0,y,z,k); + double *ptrd = (double*)data_in; + cimg_forX(Ir,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } + fftw_execute(data_plan); + const unsigned int fact = Ir.width; + if (invert) { cimg_forX(Ir,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }} + else { cimg_forX(Ir,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }} + } + } break; + + case 'y' : { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.height); + data_plan = fftw_plan_dft_1d(Ir.height,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned int off = Ir.width; + cimg_forXZV(Ir,x,z,k) { + T *ptrr = Ir.ptr(x,0,z,k), *ptri = Ii.ptr(x,0,z,k); + double *ptrd = (double*)data_in; + cimg_forY(Ir,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = Ir.height; + if (invert) { cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} + else { cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} + } + } break; + + case 'z' : { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.depth); + data_plan = fftw_plan_dft_1d(Ir.depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned int off = Ir.width*Ir.height; + cimg_forXYV(Ir,x,y,k) { + T *ptrr = Ir.ptr(x,y,0,k), *ptri = Ii.ptr(x,y,0,k); + double *ptrd = (double*)data_in; + cimg_forZ(Ir,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = Ir.depth; + if (invert) { cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} + else { cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} + } + } break; + + case 'v' : { + data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.dim); + data_plan = fftw_plan_dft_1d(Ir.dim,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + const unsigned int off = Ir.width*Ir.height*Ir.depth; + cimg_forXYZ(Ir,x,y,z) { + T *ptrr = Ir.ptr(x,y,z,0), *ptri = Ii.ptr(x,y,z,0); + double *ptrd = (double*)data_in; + cimg_forV(Ir,k) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } + fftw_execute(data_plan); + const unsigned int fact = Ir.dim; + if (invert) { cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }} + else { cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }} + } + } break; + } + + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#else + switch (cimg::uncase(axis)) { + case 'x' : { // Fourier along X + const unsigned int N = Ir.width, N2 = (N>>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image along 'x' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0, j=0; ii) cimg_forYZV(Ir,y,z,v) { cimg::swap(Ir(i,y,z,v),Ir(j,y,z,v)); cimg::swap(Ii(i,y,z,v),Ii(j,y,z,v)); + if (j=m; j-=m, m=n, n>>=1) {} + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'y' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0, j=0; ii) cimg_forXZV(Ir,x,z,v) { cimg::swap(Ir(x,i,z,v),Ir(x,j,z,v)); cimg::swap(Ii(x,i,z,v),Ii(x,j,z,v)); + if (j=m; j-=m, m=n, n>>=1) {} + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i>1); + if (((N-1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'z' is %d != 2^N", + pixel_type(),N); + for (unsigned int i=0, j=0; ii) cimg_forXYV(Ir,x,y,v) { cimg::swap(Ir(x,y,i,v),Ir(x,y,j,v)); cimg::swap(Ii(x,y,i,v),Ii(x,y,j,v)); + if (j=m; j-=m, m=n, n>>=1) {} + } + for (unsigned int delta=2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i=0; i::FFT() : Invalid axis '%c', must be 'x','y' or 'z'."); + } +#endif + return *this; + } + + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this).FFT(axis,invert); + } + + //! Compute the Fast Fourier Transform of a complex image. + CImgList& FFT(const bool invert=false) { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty", + pixel_type(),size,data); + if (size>2) + cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images", + pixel_type(),size,data); + if (size==1) insert(CImg(data->width,data->height,data->depth,data->dim,0)); + CImg &Ir = data[0], &Ii = data[1]; + if (Ii.width!=Ir.width || Ii.height!=Ir.height || Ii.depth!=Ir.depth || Ii.dim!=Ir.dim) + throw CImgInstanceException("CImgList<%s>::FFT() : Real (%u,%u,%u,%u,%p) and Imaginary (%u,%u,%u,%u,%p) parts " + "of the instance image have different dimensions", + pixel_type(),Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data, + Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); +#ifdef cimg_use_fftw3 + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width*Ir.height*Ir.depth); + fftw_plan data_plan; + const unsigned int w = Ir.width, wh = w*Ir.height, whd = wh*Ir.depth; + data_plan = fftw_plan_dft_3d(Ir.width,Ir.height,Ir.depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forV(Ir,k) { + T *ptrr = Ir.ptr(0,0,0,k), *ptri = Ii.ptr(0,0,0,k); + double *ptrd = (double*)data_in; + for (unsigned int x = 0; x1) FFT('z',invert); + if (Ir.height>1) FFT('y',invert); + if (Ir.width>1) FFT('x',invert); +#endif + return *this; + } + + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this).FFT(invert); + } + + // Return a list where each image has been split along the specified axis. + CImgList& split(const char axis) { + return get_split(axis).transfer_to(*this); + } + + CImgList get_split(const char axis) const { + CImgList res; + cimglist_for(*this,l) { + CImgList tmp = data[l].get_split(axis); + const unsigned int pos = res.size; + res.insert(tmp.size); + cimglist_for(tmp,i) tmp[i].transfer_to(data[pos+i]); + } + return res; + } + + //! Return a single image which is the concatenation of all images of the current CImgList instance. + /** + \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \return A CImg image corresponding to the concatenation is returned. + **/ + CImg get_append(const char axis, const char align='p') const { + if (is_empty()) return CImg(); + if (size==1) return +((*this)[0]); + unsigned int dx = 0, dy = 0, dz = 0, dv = 0, pos = 0; + CImg res; + switch (cimg::uncase(axis)) { + case 'x' : { + switch (cimg::uncase(align)) { + case 'x' : { dy = dz = dv = 1; cimglist_for(*this,l) dx+=(*this)[l].size(); } break; + case 'y' : { dx = size; dz = dv = 1; cimglist_for(*this,l) dy = cimg::max(dy,(unsigned int)(*this)[l].size()); } break; + case 'z' : { dx = size; dy = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; + case 'v' : { dx = size; dy = dz = 1; cimglist_for(*this,l) dv = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; + default : + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx += img.width; + dy = cimg::max(dy,img.height); + dz = cimg::max(dz,img.depth); + dv = cimg::max(dv,img.dim); + } + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'x' : { + cimglist_for(*this,l) { + res.draw_image(pos,CImg((*this)[l],true).unroll('x')); + pos+=(*this)[l].size(); + } + } break; + case 'y' : { + cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('y')); + } break; + case 'z' : { + cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('z')); + } break; + case 'v' : { + cimglist_for(*this,l) res.draw_image(pos++,CImg((*this)[l],true).unroll('v')); + } break; + case 'p' : { + cimglist_for(*this,l) { res.draw_image(pos,(*this)[l]); pos+=(*this)[l].width; } + } break; + case 'n' : { + cimglist_for(*this,l) { + res.draw_image(pos,dy-(*this)[l].height,dz-(*this)[l].depth,dv-(*this)[l].dim,(*this)[l]); + pos+=(*this)[l].width; + } + } break; + default : { + cimglist_for(*this,l) { + res.draw_image(pos,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2,(*this)[l]); + pos+=(*this)[l].width; + } + } break; + } + } break; + + case 'y' : { + switch (cimg::uncase(align)) { + case 'x' : { dy = size; dz = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; + case 'y' : { dx = dz = dv = 1; cimglist_for(*this,l) dy+=(*this)[l].size(); } break; + case 'z' : { dy = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; + case 'v' : { dy = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; + default : + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy += img.height; + dz = cimg::max(dz,img.depth); + dv = cimg::max(dv,img.dim); + } + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'x' : { + cimglist_for(*this,l) res.draw_image(0,++pos,CImg((*this)[l],true).unroll('x')); + } break; + case 'y' : { + cimglist_for(*this,l) { + res.draw_image(0,pos,CImg((*this)[l],true).unroll('y')); + pos+=(*this)[l].size(); + } + } break; + case 'z' : { + cimglist_for(*this,l) res.draw_image(0,pos++,CImg((*this)[l],true).unroll('z')); + } break; + case 'v' : { + cimglist_for(*this,l) res.draw_image(0,pos++,CImg((*this)[l],true).unroll('v')); + } break; + case 'p' : { + cimglist_for(*this,l) { res.draw_image(0,pos,(*this)[l]); pos+=(*this)[l].height; } + } break; + case 'n' : { + cimglist_for(*this,l) { + res.draw_image(dx-(*this)[l].width,pos,dz-(*this)[l].depth,dv-(*this)[l].dim,(*this)[l]); + pos+=(*this)[l].height; + } + } break; + default : { + cimglist_for(*this,l) { + res.draw_image((dx-(*this)[l].width)/2,pos,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2,(*this)[l]); + pos+=(*this)[l].height; + } + } break; + } + } break; + + case 'z' : { + switch (cimg::uncase(align)) { + case 'x' : { dz = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; + case 'y' : { dz = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; + case 'z' : { dx = dy = dv = 1; cimglist_for(*this,l) dz+=(*this)[l].size(); } break; + case 'v' : { dz = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; + default : + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy = cimg::max(dy,img.height); + dz += img.depth; + dv = cimg::max(dv,img.dim); + } + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'x' : { + cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('x')); + } break; + case 'y' : { + cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('y')); + } break; + case 'z' : { + cimglist_for(*this,l) { + res.draw_image(0,0,pos,CImg((*this)[l],true).unroll('z')); + pos+=(*this)[l].size(); + } + } break; + case 'v' : { + cimglist_for(*this,l) res.draw_image(0,0,pos++,CImg((*this)[l],true).unroll('v')); + } break; + case 'p' : { + cimglist_for(*this,l) { res.draw_image(0,0,pos,(*this)[l]); pos+=(*this)[l].depth; } + } break; + case 'n' : { + cimglist_for(*this,l) { + res.draw_image(dx-(*this)[l].width,dy-(*this)[l].height,pos,dv-(*this)[l].dim,(*this)[l]); + pos+=(*this)[l].depth; + } + } break; + case 'c' : { + cimglist_for(*this,l) { + res.draw_image((dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,pos,(dv-(*this)[l].dim)/2,(*this)[l]); + pos+=(*this)[l].depth; + } + } break; + } + } break; + + case 'v' : { + switch (cimg::uncase(align)) { + case 'x' : { dv = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; + case 'y' : { dv = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; + case 'z' : { dv = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; + case 'v' : { dx = dy = dz = 1; cimglist_for(*this,l) dv+=(*this)[l].size(); } break; + default : + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + dx = cimg::max(dx,img.width); + dy = cimg::max(dy,img.height); + dz = cimg::max(dz,img.depth); + dv += img.dim; + } + } + res.assign(dx,dy,dz,dv,0); + switch (cimg::uncase(align)) { + case 'x' : { + cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('x')); + } break; + case 'y' : { + cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('y')); + } break; + case 'z' : { + cimglist_for(*this,l) res.draw_image(0,0,0,pos++,CImg((*this)[l],true).unroll('v')); + } break; + case 'v' : { + cimglist_for(*this,l) { + res.draw_image(0,0,0,pos,CImg((*this)[l],true).unroll('z')); + pos+=(*this)[l].size(); + } + } break; + case 'p' : { + cimglist_for(*this,l) { res.draw_image(0,0,0,pos,(*this)[l]); pos+=(*this)[l].dim; } + } break; + case 'n' : { + cimglist_for(*this,l) { + res.draw_image(dx-(*this)[l].width,dy-(*this)[l].height,dz-(*this)[l].depth,pos,(*this)[l]); + pos+=(*this)[l].dim; + } + } break; + case 'c' : { + cimglist_for(*this,l) { + res.draw_image((dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,pos,(*this)[l]); + pos+=(*this)[l].dim; + } + } break; + } + } break; + default : + throw CImgArgumentException("CImgList<%s>::get_append() : unknow axis '%c', must be 'x','y','z' or 'v'", + pixel_type(),axis); + } + return res; + } + + //! Create an auto-cropped font (along the X axis) from a input font \p font. + CImgList& crop_font() { + return get_crop_font().transfer_to(*this); + } + + CImgList get_crop_font() const { + CImgList res; + cimglist_for(*this,l) { + const CImg& letter = (*this)[l]; + int xmin = letter.width, xmax = 0; + cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax=x; } + if (xmin>xmax) res.insert(CImg(letter.width,letter.height,1,letter.dim,0)); + else res.insert(letter.get_crop(xmin,0,xmax,letter.height-1)); + } + res[' '].resize(res['f'].width); + res[' '+256].resize(res['f'].width); + return res; + } + + //! Invert primitives orientation of a 3D object. + CImgList& invert_object3d() { + cimglist_for(*this,l) { + CImg& p = data[l]; + const unsigned int siz = p.size(); + if (siz==2 || siz==3 || siz==6 || siz==9) cimg::swap(p[0],p[1]); + else if (siz==4 || siz==12) cimg::swap(p[0],p[3],p[1],p[2]); + } + return *this; + } + + CImgList get_invert_object3d() const { + return (+*this).invert_object3d(); + } + + //! Return a CImg pre-defined font with desired size. + /** + \param font_height = height of the desired font (can be 11,13,24,38 or 57) + \param fixed_size = tell if the font has a fixed or variable width. + **/ + static CImgList font(const unsigned int font_width, const bool variable_size=true) { + if (font_width<=11) { + static CImgList font7x11, nfont7x11; + if (!variable_size && !font7x11) font7x11 = _font(cimg::font7x11,7,11,1,0,false); + if (variable_size && !nfont7x11) nfont7x11 = _font(cimg::font7x11,7,11,1,0,true); + return variable_size?nfont7x11:font7x11; + } + if (font_width<=13) { + static CImgList font10x13, nfont10x13; + if (!variable_size && !font10x13) font10x13 = _font(cimg::font10x13,10,13,1,0,false); + if (variable_size && !nfont10x13) nfont10x13 = _font(cimg::font10x13,10,13,1,0,true); + return variable_size?nfont10x13:font10x13; + } + if (font_width<=17) { + static CImgList font8x17, nfont8x17; + if (!variable_size && !font8x17) font8x17 = _font(cimg::font8x17,8,17,1,0,false); + if (variable_size && !nfont8x17) nfont8x17 = _font(cimg::font8x17,8,17,1,0,true); + return variable_size?nfont8x17:font8x17; + } + if (font_width<=19) { + static CImgList font10x19, nfont10x19; + if (!variable_size && !font10x19) font10x19 = _font(cimg::font10x19,10,19,2,0,false); + if (variable_size && !nfont10x19) nfont10x19 = _font(cimg::font10x19,10,19,2,0,true); + return variable_size?nfont10x19:font10x19; + } + if (font_width<=24) { + static CImgList font12x24, nfont12x24; + if (!variable_size && !font12x24) font12x24 = _font(cimg::font12x24,12,24,2,0,false); + if (variable_size && !nfont12x24) nfont12x24 = _font(cimg::font12x24,12,24,2,0,true); + return variable_size?nfont12x24:font12x24; + } + if (font_width<=32) { + static CImgList font16x32, nfont16x32; + if (!variable_size && !font16x32) font16x32 = _font(cimg::font16x32,16,32,2,0,false); + if (variable_size && !nfont16x32) nfont16x32 = _font(cimg::font16x32,16,32,2,0,true); + return variable_size?nfont16x32:font16x32; + } + if (font_width<=38) { + static CImgList font19x38, nfont19x38; + if (!variable_size && !font19x38) font19x38 = _font(cimg::font19x38,19,38,3,0,false); + if (variable_size && !nfont19x38) nfont19x38 = _font(cimg::font19x38,19,38,3,0,true); + return variable_size?nfont19x38:font19x38; + } + static CImgList font29x57, nfont29x57; + if (!variable_size && !font29x57) font29x57 = _font(cimg::font29x57,29,57,5,0,false); + if (variable_size && !nfont29x57) nfont29x57 = _font(cimg::font29x57,29,57,5,0,true); + return variable_size?nfont29x57:font29x57; + } + + static CImgList _font(const unsigned int *const font, const unsigned int w, const unsigned int h, + const unsigned int paddingx, const unsigned int paddingy, const bool variable_size=true) { + CImgList res = CImgList(256,w,h,1,3).insert(CImgList(256,w,h,1,1)); + const unsigned int *ptr = font; + unsigned int m = 0, val = 0; + for (unsigned int y=0; y>=1; if (!m) { m = 0x80000000; val = *(ptr++); } + CImg& img = res[x/w], &mask = res[x/w+256]; + unsigned int xm = x%w; + img(xm,y,0) = img(xm,y,1) = img(xm,y,2) = mask(xm,y,0) = (T)((val&m)?1:0); + } + if (variable_size) res.crop_font(); + if (paddingx || paddingy) cimglist_for(res,l) res[l].resize(res[l].dimx()+paddingx, res[l].dimy()+paddingy,1,-100,0); + return res; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + This function displays the list images of the current CImgList instance into an existing CImgDisplay window. + Images of the list are concatenated in a single temporarly image for visualization purposes. + The function returns immediately. + \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& display(CImgDisplay& disp, const char axis='x', const char align='p') const { + get_append(axis,align).display(disp); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + This function opens a new window with a specific title and displays the list images of the current CImgList instance into it. + Images of the list are concatenated in a single temporarly image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + \param title : specify the title of the opening display window. + \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'v'. + \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& display(CImgDisplay &disp, + const bool display_info, const char axis='x', const char align='p') const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::display() : Instance list (%u,%u) is empty.", + pixel_type(),size,data); + const CImg visu = get_append(axis,align); + if (display_info) print(disp.title); + visu.display(disp,false); + return *this; + } + + //! Display the current CImgList instance in a new display window. + const CImgList& display(const char *const title=0, + const bool display_info=true, const char axis='x', const char align='p') const { + const CImg visu = get_append(axis,align); + char ntitle[64] = { 0 }; + if (!title) cimg_std::sprintf(ntitle,"CImgList<%s>",pixel_type()); + if (display_info) print(title?title:ntitle); + visu.display(title?title:ntitle,false); + return *this; + } + + //@} + //---------------------------------- + // + //! \name Input-Output + //@{ + //---------------------------------- + + //! Return a C-string containing the values of all images in the instance list. + CImg value_string(const char separator=',', const unsigned int max_size=0) const { + if (is_empty()) return CImg(1,1,1,1,0); + CImgList items; + for (unsigned int l = 0; l item = data[l].value_string(separator,0); + item[item.size()-1] = separator; + items.insert(item); + } + items.insert(data[size-1].value_string(separator,0)); + CImg res = items.get_append('x'); + if (max_size) { res.crop(0,max_size); res(max_size) = 0; } + return res; + } + + //! Print informations about the list on the standard output. + const CImgList& print(const char* title=0, const bool display_stats=true) const { + unsigned long msiz = 0; + cimglist_for(*this,l) msiz += data[l].size(); + msiz*=sizeof(T); + const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2); + char ntitle[64] = { 0 }; + if (!title) cimg_std::sprintf(ntitle,"CImgList<%s>",pixel_type()); + cimg_std::fprintf(cimg_stdout,"%s: this = %p, size = %u [%lu %s], data = (CImg<%s>*)%p.\n", + title?title:ntitle,(void*)this,size, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kb":"Mb"), + pixel_type(),(void*)data); + char tmp[16] = { 0 }; + cimglist_for(*this,ll) { + cimg_std::sprintf(tmp,"[%d]",ll); + cimg_std::fprintf(cimg_stdout," "); + data[ll].print(tmp,display_stats); + if (ll==3 && size>8) { ll = size-5; cimg_std::fprintf(cimg_stdout," ...\n"); } + } + return *this; + } + + //! Load an image list from a file. + CImgList& load(const char *const filename) { + const char *ext = cimg::split_filename(filename); + const unsigned int odebug = cimg::exception_mode(); + cimg::exception_mode() = 0; + assign(); + try { +#ifdef cimglist_load_plugin + cimglist_load_plugin(filename); +#endif +#ifdef cimglist_load_plugin1 + cimglist_load_plugin1(filename); +#endif +#ifdef cimglist_load_plugin2 + cimglist_load_plugin2(filename); +#endif +#ifdef cimglist_load_plugin3 + cimglist_load_plugin3(filename); +#endif +#ifdef cimglist_load_plugin4 + cimglist_load_plugin4(filename); +#endif +#ifdef cimglist_load_plugin5 + cimglist_load_plugin5(filename); +#endif +#ifdef cimglist_load_plugin6 + cimglist_load_plugin6(filename); +#endif +#ifdef cimglist_load_plugin7 + cimglist_load_plugin7(filename); +#endif +#ifdef cimglist_load_plugin8 + cimglist_load_plugin8(filename); +#endif + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !ext[0]) load_cimg(filename); + if (!cimg::strcasecmp(ext,"rec") || + !cimg::strcasecmp(ext,"par")) load_parrec(filename); + if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename); + if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + if (is_empty()) throw CImgIOException("CImgList<%s>::load()",pixel_type()); + } catch (CImgIOException& e) { + if (!cimg::strncasecmp(e.message,"cimg::fopen()",13)) { + cimg::exception_mode() = odebug; + throw CImgIOException("CImgList<%s>::load() : File '%s' cannot be opened.",pixel_type(),filename); + } else try { + assign(1); + data->load(filename); + } catch (CImgException&) { + assign(); + } + } + cimg::exception_mode() = odebug; + if (is_empty()) + throw CImgIOException("CImgList<%s>::load() : File '%s', format not recognized.",pixel_type(),filename); + return *this; + } + + static CImgList get_load(const char *const filename) { + return CImgList().load(filename); + } + + //! Load an image list from a .cimg file. + CImgList& load_cimg(const char *const filename) { + return _load_cimg(0,filename); + } + + static CImgList get_load_cimg(const char *const filename) { + return CImgList().load_cimg(filename); + } + + //! Load an image list from a .cimg file. + CImgList& load_cimg(cimg_std::FILE *const file) { + return _load_cimg(file,0); + } + + static CImgList get_load_cimg(cimg_std::FILE *const file) { + return CImgList().load_cimg(file); + } + + CImgList& _load_cimg(cimg_std::FILE *const file, const char *const filename) { +#ifdef cimg_use_zlib +#define _cimgz_load_cimg_case(Tss) { \ + Bytef *const cbuf = new Bytef[csiz]; \ + cimg::fread(cbuf,csiz,nfile); \ + raw.assign(W,H,D,V); \ + unsigned long destlen = raw.size()*sizeof(T); \ + uncompress((Bytef*)raw.data,&destlen,cbuf,csiz); \ + delete[] cbuf; \ + const Tss *ptrs = raw.data; \ + for (unsigned int off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \ +} +#else +#define _cimgz_load_cimg_case(Tss) \ + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s' contains compressed data, zlib must be used",\ + pixel_type(),filename?filename:"(FILE*)"); +#endif + +#define _cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l=0) tmp[j++] = (char)i; tmp[j] = '\0'; \ + W = H = D = V = 0; csiz = 0; \ + if ((err = cimg_std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&V,&csiz))<4) \ + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ + pixel_type(),filename?filename:("(FILE*)"),W,H,D,V); \ + if (W*H*D*V>0) { \ + CImg raw; \ + CImg &img = data[l]; \ + img.assign(W,H,D,V); \ + T *ptrd = img.data; \ + if (err==5) _cimgz_load_cimg_case(Tss) \ + else for (int toread = (int)img.size(); toread>0; ) { \ + raw.assign(cimg::min(toread,cimg_iobuffer)); \ + cimg::fread(raw.data,raw.width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); \ + toread-=raw.width; \ + const Tss *ptrs = raw.data; \ + for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException("CImgList<%s>::load_cimg() : Cannot load (null) filename.", + pixel_type()); + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + const int cimg_iobuffer = 12*1024*1024; + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + char tmp[256], str_pixeltype[256], str_endian[256]; + unsigned int j, err, N = 0, W, H, D, V, csiz; + int i; + j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; + err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", + pixel_type(),filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + assign(N); + _cimg_load_cimg_case("bool",bool); + _cimg_load_cimg_case("unsigned_char",uchar); + _cimg_load_cimg_case("uchar",uchar); + _cimg_load_cimg_case("char",char); + _cimg_load_cimg_case("unsigned_short",ushort); + _cimg_load_cimg_case("ushort",ushort); + _cimg_load_cimg_case("short",short); + _cimg_load_cimg_case("unsigned_int",uint); + _cimg_load_cimg_case("uint",uint); + _cimg_load_cimg_case("int",int); + _cimg_load_cimg_case("unsigned_long",ulong); + _cimg_load_cimg_case("ulong",ulong); + _cimg_load_cimg_case("long",long); + _cimg_load_cimg_case("float",float); + _cimg_load_cimg_case("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", + pixel_type(),filename?filename:"(FILE*)",str_pixeltype); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a sub-image list from a non compressed .cimg file. + CImgList& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { + return _load_cimg(0,filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + } + + static CImgList get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { + return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + } + + //! Load a sub-image list from a non compressed .cimg file. + CImgList& load_cimg(cimg_std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { + return _load_cimg(file,0,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + } + + static CImgList get_load_cimg(cimg_std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { + return CImgList().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + } + + CImgList& _load_cimg(cimg_std::FILE *const file, const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, + const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { +#define _cimg_load_cimg_case2(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l<=nn1; ++l) { \ + j = 0; while ((i=cimg_std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = '\0'; \ + W = H = D = V = 0; \ + if (cimg_std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&V)!=4) \ + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ + pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ + if (W*H*D*V>0) { \ + if (l=W || y0>=H || z0>=D || v0>=D) cimg_std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ + else { \ + const unsigned int \ + nx1 = x1>=W?W-1:x1, \ + ny1 = y1>=H?H-1:y1, \ + nz1 = z1>=D?D-1:z1, \ + nv1 = v1>=V?V-1:v1; \ + CImg raw(1+nx1-x0); \ + CImg &img = data[l-n0]; \ + img.assign(1+nx1-x0,1+ny1-y0,1+nz1-z0,1+nv1-v0); \ + T *ptrd = img.data; \ + const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg_std::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v=1+nv1-v0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) cimg_std::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z=1+nz1-z0; z; --z) { \ + const unsigned int skipyb = y0*W*sizeof(Tss); \ + if (skipyb) cimg_std::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y=1+ny1-y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) cimg_std::fseek(nfile,skipxb,SEEK_CUR); \ + cimg::fread(raw.data,raw.width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw.data,raw.width); \ + const Tss *ptrs = raw.data; \ + for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ + if (skipxe) cimg_std::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ + if (skipye) cimg_std::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg_std::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg_std::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException("CImgList<%s>::load_cimg() : Cannot load (null) filename.", + pixel_type()); + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + if (n1::load_cimg() : File '%s', Bad sub-region coordinates [%u->%u] " + "(%u,%u,%u,%u)->(%u,%u,%u,%u).", + pixel_type(),filename?filename:"(FILE*)", + n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + char tmp[256], str_pixeltype[256], str_endian[256]; + unsigned int j, err, N, W, H, D, V; + int i; + j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; + err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", + pixel_type(),filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + const unsigned int nn1 = n1>=N?N-1:n1; + assign(1+nn1-n0); + _cimg_load_cimg_case2("bool",bool); + _cimg_load_cimg_case2("unsigned_char",uchar); + _cimg_load_cimg_case2("uchar",uchar); + _cimg_load_cimg_case2("char",char); + _cimg_load_cimg_case2("unsigned_short",ushort); + _cimg_load_cimg_case2("ushort",ushort); + _cimg_load_cimg_case2("short",short); + _cimg_load_cimg_case2("unsigned_int",uint); + _cimg_load_cimg_case2("uint",uint); + _cimg_load_cimg_case2("int",int); + _cimg_load_cimg_case2("unsigned_long",ulong); + _cimg_load_cimg_case2("ulong",ulong); + _cimg_load_cimg_case2("long",long); + _cimg_load_cimg_case2("float",float); + _cimg_load_cimg_case2("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", + pixel_type(),filename?filename:"(FILE*)",str_pixeltype); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image list from a PAR/REC (Philips) file. + CImgList& load_parrec(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImgList<%s>::load_parrec() : Cannot load (null) filename.", + pixel_type()); + char body[1024], filenamepar[1024], filenamerec[1024]; + const char *ext = cimg::split_filename(filename,body); + if (!cimg::strcmp(ext,"par")) { cimg_std::strcpy(filenamepar,filename); cimg_std::sprintf(filenamerec,"%s.rec",body); } + if (!cimg::strcmp(ext,"PAR")) { cimg_std::strcpy(filenamepar,filename); cimg_std::sprintf(filenamerec,"%s.REC",body); } + if (!cimg::strcmp(ext,"rec")) { cimg_std::strcpy(filenamerec,filename); cimg_std::sprintf(filenamepar,"%s.par",body); } + if (!cimg::strcmp(ext,"REC")) { cimg_std::strcpy(filenamerec,filename); cimg_std::sprintf(filenamepar,"%s.PAR",body); } + cimg_std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + int err; + char line[256] = { 0 }; + do { err=cimg_std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (line[0]=='#' || line[0]=='.')); + do { + unsigned int sn,sizex,sizey,pixsize; + float rs,ri,ss; + err = cimg_std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss); + if (err==7) { + st_slices.insert(CImg::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey, + ri,rs,ss,0)); + unsigned int i; for (i=0; i::vector(sizex,sizey,sn)); + else { + CImg &vec = st_global[i]; + if (sizex>vec[0]) vec[0] = sizex; + if (sizey>vec[1]) vec[1] = sizey; + vec[2] = sn; + } + st_slices[st_slices.size-1][7] = (float)i; + } + } while (err==7); + + // Read data + cimg_std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + { cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + insert(CImg(vec[0],vec[1],vec[2])); + }} + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0]-1, + pixsize = (unsigned int)vec[1], + sizex = (unsigned int)vec[2], + sizey = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8 : { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16 : { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32 : { + CImg buf(sizex,sizey); + cimg::fread(buf.data,sizex*sizey,file2); + if (cimg::endianness()) cimg::invert_endianness(buf.data,sizex*sizey); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default : + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException("CImg<%s>::load_parrec() : File '%s', cannot handle image with pixsize = %d bits.", + pixel_type(),filename,pixsize); + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!size) + throw CImgIOException("CImg<%s>::load_parrec() : File '%s' does not appear to be a valid PAR-REC file.", + pixel_type(),filename); + return *this; + } + + static CImgList get_load_parrec(const char *const filename) { + return CImgList().load_parrec(filename); + } + + //! Load an image sequence from a YUV file. + CImgList& load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(0,filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + } + + static CImgList get_load_yuv(const char *const filename, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load an image sequence from a YUV file. + CImgList& load_yuv(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(file,0,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + } + + static CImgList get_load_yuv(cimg_std::FILE *const file, + const unsigned int sizex, const unsigned int sizey=1, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb); + } + + CImgList& _load_yuv(cimg_std::FILE *const file, const char *const filename, + const unsigned int sizex, const unsigned int sizey, + const unsigned int first_frame, const unsigned int last_frame, + const unsigned int step_frame, const bool yuv2rgb) { + if (!filename && !file) + throw CImgArgumentException("CImgList<%s>::load_yuv() : Cannot load (null) filename.", + pixel_type()); + if (sizex%2 || sizey%2) + throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', image dimensions along X and Y must be " + "even numbers (given are %ux%u)\n", + pixel_type(),filename?filename:"(FILE*)",sizex,sizey); + if (!sizex || !sizey) + throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', given image sequence size (%u,%u) is invalid", + pixel_type(),filename?filename:"(FILE*)",sizex,sizey); + + const unsigned int + nfirst_frame = first_frame tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stopflag = false; + int err; + if (nfirst_frame) { + err = cimg_std::fseek(nfile,nfirst_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::load_yuv() : File '%s' doesn't contain frame number %u " + "(out of range error).", + pixel_type(),filename?filename:"(FILE*)",nfirst_frame); + } + } + unsigned int frame; + for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) { + tmp.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread ! + err = (int)cimg_std::fread((void*)(tmp.data),1,(size_t)(tmp.width*tmp.height),nfile); + if (err!=(int)(tmp.width*tmp.height)) { + stopflag = true; + if (err>0) + cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," + " or given image dimensions (%u,%u) are incorrect.", + pixel_type(),filename?filename:"(FILE*)",sizex,sizey); + } else { + UV.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread ! + err = (int)cimg_std::fread((void*)(UV.data),1,(size_t)(UV.size()),nfile); + if (err!=(int)(UV.size())) { + stopflag = true; + if (err>0) + cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," + " or given image dimensions (%u,%u) are incorrect.", + pixel_type(),filename?filename:"(FILE*)",sizex,sizey); + } else { + cimg_forXY(UV,x,y) { + const int x2 = x*2, y2 = y*2; + tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0); + tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1); + } + if (yuv2rgb) tmp.YCbCrtoRGB(); + insert(tmp); + if (nstep_frame>1) cimg_std::fseek(nfile,(nstep_frame-1)*(sizex*sizey + sizex*sizey/2),SEEK_CUR); + } + } + } + if (stopflag && nlast_frame!=~0U && frame!=nlast_frame) + cimg::warn("CImgList<%s>::load_yuv() : File '%s', frame %d not reached since only %u frames were found in the file.", + pixel_type(),filename?filename:"(FILE*)",nlast_frame,frame-1,filename); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a video file, using ffmpeg libraries. + // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net) + // I modified it afterwards for direct inclusion in the library core. + CImgList& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) { + if (!filename) + throw CImgArgumentException("CImgList<%s>::load_ffmpeg() : Cannot load (null) filename.", + pixel_type()); + const unsigned int + nfirst_frame = first_frame1) || (resume && (pixel_format || !pixel_format))) + throw CImgArgumentException("CImg<%s>::load_ffmpeg() : File '%s', reading sub-frames from a video file requires the use of ffmpeg.\n" + "('cimg_use_ffmpeg' must be defined).", + pixel_type(),filename); + return load_ffmpeg_external(filename); +#else + const unsigned int ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8; + avcodec_register_all(); + av_register_all(); + static AVFormatContext *format_ctx = 0; + static AVCodecContext *codec_ctx = 0; + static AVCodec *codec = 0; + static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame(); + static int vstream = 0; + + if (resume) { + if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame) + throw CImgArgumentException("CImgList<%s>::load_ffmpeg() : File '%s', cannot resume due to unallocated FFMPEG structures.", + pixel_type(),filename); + } else { + // Open video file, find main video stream and codec. + if (format_ctx) av_close_input_file(format_ctx); + if (av_open_input_file(&format_ctx,filename,0,0,0)!=0) + throw CImgIOException("CImgList<%s>::load_ffmpeg() : File '%s' cannot be opened.", + pixel_type(),filename); + if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) { + av_close_input_file(format_ctx); format_ctx = 0; + cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot retrieve stream information.\n" + "Trying with external ffmpeg executable.", + pixel_type(),filename); + return load_ffmpeg_external(filename); + } +#if cimg_debug>=3 + dump_format(format_ctx,0,0,0); +#endif + + // Special command : Return informations on main video stream. + // as a vector 1x4 containing : (nb_frames,width,height,fps). + if (!first_frame && !last_frame && !step_frame) { + for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream) + if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break; + if (vstream==(int)format_ctx->nb_streams) assign(); + else { + CImgList timestamps; + int nb_frames; + AVPacket packet; + // Count frames and store timestamps. + for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet)) + if (packet.stream_index==vstream) { + timestamps.insert(CImg::vector((double)packet.pts)); + ++nb_frames; + } + // Get frame with, height and fps. + const int + framew = format_ctx->streams[vstream]->codec->width, + frameh = format_ctx->streams[vstream]->codec->height; + const float + num = (float)(format_ctx->streams[vstream]->r_frame_rate).num, + den = (float)(format_ctx->streams[vstream]->r_frame_rate).den, + fps = num/den; + // Return infos as a list. + assign(2); + (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps); + (*this)[1] = timestamps.get_append('y'); + } + av_close_input_file(format_ctx); format_ctx = 0; + return *this; + } + + for (vstream = 0; vstream<(int)(format_ctx->nb_streams) && + format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream; + if (vstream==(int)format_ctx->nb_streams) { + cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot retrieve video stream.\n" + "Trying with external ffmpeg executable.", + pixel_type(),filename); + av_close_input_file(format_ctx); format_ctx = 0; + return load_ffmpeg_external(filename); + } + codec_ctx = format_ctx->streams[vstream]->codec; + codec = avcodec_find_decoder(codec_ctx->codec_id); + if (!codec) { + cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot find video codec.\n" + "Trying with external ffmpeg executable.", + pixel_type(),filename); + return load_ffmpeg_external(filename); + } + if (avcodec_open(codec_ctx,codec)<0) { // Open codec + cimg::warn("CImgList<%s>::load_ffmpeg() : File '%s', cannot open video codec.\n" + "Trying with external ffmpeg executable.", + pixel_type(),filename); + return load_ffmpeg_external(filename); + } + } + + // Read video frames + const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); + uint8_t *const buffer = new uint8_t[numBytes]; + avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height); + const T foo = (T)0; + AVPacket packet; + for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) { + if (packet.stream_index==(int)vstream) { + int decoded = 0; + avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data,packet.size); + if (decoded) { + if (frame==next_frame) { + SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width, + codec_ctx->height,ffmpeg_pixfmt,1,0,0,0); + sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize); + if (ffmpeg_pixfmt==PIX_FMT_RGB24) { + CImg next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true); + insert(next_image._get_permute_axes("yzvx",foo)); + } else { + CImg next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true); + insert(next_image._get_permute_axes("yzvx",foo)); + } + next_frame+=nstep_frame; + } + ++frame; + } + av_free_packet(&packet); + if (next_frame>nlast_frame) break; + } + } + delete[] buffer; +#endif + return *this; + } + + static CImgList get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool pixel_format=true) { + return CImgList().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format); + } + + //! Load an image from a video file (MPEG,AVI) using the external tool 'ffmpeg'. + CImgList& load_ffmpeg_external(const char *const filename) { + if (!filename) + throw CImgArgumentException("CImgList<%s>::load_ffmpeg_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512], filetmp2[512]; + cimg_std::FILE *file = 0; + do { + cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + cimg_std::sprintf(filetmp2,"%s_000001.ppm",filetmp); + if ((file=cimg_std::fopen(filetmp2,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(filetmp2,"%s_%%6d.ppm",filetmp); +#if cimg_OS!=2 + cimg_std::sprintf(command,"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2); +#else + cimg_std::sprintf(command,"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2); +#endif + cimg::system(command,0); + const unsigned int odebug = cimg::exception_mode(); + cimg::exception_mode() = 0; + assign(); + unsigned int i = 1; + for (bool stopflag = false; !stopflag; ++i) { + cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,i); + CImg img; + try { img.load_pnm(filetmp2); } + catch (CImgException&) { stopflag = true; } + if (img) { insert(img); cimg_std::remove(filetmp2); } + } + cimg::exception_mode() = odebug; + if (is_empty()) + throw CImgIOException("CImgList<%s>::load_ffmpeg_external() : Failed to open image sequence '%s'.\n" + "Check the filename and if the 'ffmpeg' tool is installed on your system.", + pixel_type(),filename); + return *this; + } + + static CImgList get_load_ffmpeg_external(const char *const filename) { + return CImgList().load_ffmpeg_external(filename); + } + + //! Load a gzipped list, using external tool 'gunzip'. + CImgList& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException("CImg<%s>::load_gzip_external() : Cannot load (null) filename.", + pixel_type()); + char command[1024], filetmp[512], body[512]; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + cimg_std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext2); + else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } else { + if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext); + else cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + cimg_std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp); + cimg::system(command); + if (!(file = cimg_std::fopen(filetmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException("CImg<%s>::load_gzip_external() : File '%s' cannot be opened.", + pixel_type(),filename); + } else cimg::fclose(file); + load(filetmp); + cimg_std::remove(filetmp); + return *this; + } + + static CImgList get_load_gzip_external(const char *const filename) { + return CImgList().load_gzip_external(filename); + } + + //! Load a 3D object from a .OFF file. + template + CImgList& load_off(const char *const filename, + CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return get_load_off(filename,primitives,colors,invert_faces).transfer_to(*this); + } + + template + static CImgList get_load_off(const char *const filename, + CImgList& primitives, CImgList& colors, + const bool invert_faces=false) { + return CImg().load_off(filename,primitives,colors,invert_faces).get_split('x'); + } + + //! Load a TIFF file. + CImgList& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + const unsigned int + nfirst_frame = first_frame::load_tiff() : File '%s', reading sub-images from a tiff file requires the use of libtiff.\n" + "('cimg_use_tiff' must be defined).", + pixel_type(),filename); + return assign(CImg::get_load_tiff(filename)); +#else + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn("CImgList<%s>::load_tiff() : File '%s' contains %u image(s), specified frame range is [%u,%u] (step %u).", + pixel_type(),filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images-1; + assign(1+(nlast_frame-nfirst_frame)/nstep_frame); + TIFFSetDirectory(tif,0); +#if cimg_debug>=3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + cimglist_for(*this,l) data[l]._load_tiff(tif,nfirst_frame+l*nstep_frame); + TIFFClose(tif); + } else throw CImgException("CImgList<%s>::load_tiff() : File '%s' cannot be opened.", + pixel_type(),filename); + return *this; +#endif + } + + static CImgList get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame); + } + + //! Save an image list into a file. + /** + Depending on the extension of the given filename, a file format is chosen for the output file. + **/ + const CImgList& save(const char *const filename, const int number=-1) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save() : File '%s, instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",size,data); + if (!filename) + throw CImgArgumentException("CImg<%s>::save() : Instance list (%u,%p), specified filename is (null).", + pixel_type(),size,data); + const char *ext = cimg::split_filename(filename); + char nfilename[1024]; + const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename; +#ifdef cimglist_save_plugin + cimglist_save_plugin(fn); +#endif +#ifdef cimglist_save_plugin1 + cimglist_save_plugin1(fn); +#endif +#ifdef cimglist_save_plugin2 + cimglist_save_plugin2(fn); +#endif +#ifdef cimglist_save_plugin3 + cimglist_save_plugin3(fn); +#endif +#ifdef cimglist_save_plugin4 + cimglist_save_plugin4(fn); +#endif +#ifdef cimglist_save_plugin5 + cimglist_save_plugin5(fn); +#endif +#ifdef cimglist_save_plugin6 + cimglist_save_plugin6(fn); +#endif +#ifdef cimglist_save_plugin7 + cimglist_save_plugin7(fn); +#endif +#ifdef cimglist_save_plugin8 + cimglist_save_plugin8(fn); +#endif +#ifdef cimg_use_tiff + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); +#endif + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + if (!cimg::strcasecmp(ext,"cimg") || !ext[0]) return save_cimg(fn,false); + if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true); + if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn); + if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + if (size==1) data[0].save(fn,-1); else cimglist_for(*this,l) data[l].save(fn,l); + return *this; + } + + //! Save an image sequence, using FFMPEG library. + // This piece of code has been originally written by David. G. Starkweather. + const CImgList& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int fps=25) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",size,data); + if (!filename) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : Instance list (%u,%p), specified filename is (null).", + pixel_type(),size,data); + if (!fps) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', specified framerate is 0.", + pixel_type(),filename); + const unsigned int nlast_frame = last_frame==~0U?size-1:last_frame; + if (first_frame>=size || nlast_frame>=size) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', specified frames [%u,%u] are out of list range (%u elements).", + pixel_type(),filename,first_frame,last_frame,size); + for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!data[ll].is_sameXYZ(data[0])) + throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', images of the sequence have different dimensions.", + pixel_type(),filename); + +#ifndef cimg_use_ffmpeg + return save_ffmpeg_external(filename,first_frame,last_frame); +#else + avcodec_register_all(); + av_register_all(); + const int + frame_dimx = data[first_frame].dimx(), + frame_dimy = data[first_frame].dimy(), + frame_dimv = data[first_frame].dimv(); + if (frame_dimv!=1 && frame_dimv!=3) + throw CImgInstanceException("CImgList<%s>::save_ffmpeg() : File '%s', image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels.", + pixel_type(),filename,data[0].width,data[0].height,data[0].depth,data[0].dim,data); + + PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P; + PixelFormat src_pxl_fmt = (frame_dimv == 3)?PIX_FMT_RGB24:PIX_FMT_GRAY8; + + int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now). + AVOutputFormat *fmt = 0; + fmt = guess_format(0,filename,0); + if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg". + if (!fmt) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg() : File '%s', could not determine file format from filename.", + pixel_type(),filename); + + AVFormatContext *oc = 0; + oc = av_alloc_format_context(); + if (!oc) // Failed to allocate format context. + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate structure for format context.", + pixel_type(),filename); + + AVCodec *codec = 0; + AVFrame *picture = 0; + AVFrame *tmp_pict = 0; + oc->oformat = fmt; + cimg_std::sprintf(oc->filename,"%s",filename); + + // Add video stream. + int stream_index = 0; + AVStream *video_str = 0; + if (fmt->video_codec!=CODEC_ID_NONE) { + video_str = av_new_stream(oc,stream_index); + if (!video_str) { // Failed to allocate stream. + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate video stream structure.", + pixel_type(),filename); + } + } else { // No codec identified. + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', no proper codec identified.", + pixel_type(),filename); + } + + AVCodecContext *c = video_str->codec; + c->codec_id = fmt->video_codec; + c->codec_type = CODEC_TYPE_VIDEO; + c->bit_rate = 400000; + c->width = frame_dimx; + c->height = frame_dimy; + c->time_base.num = 1; + c->time_base.den = fps; + c->gop_size = 12; + c->pix_fmt = dest_pxl_fmt; + if (c->codec_id == CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2; + if (c->codec_id == CODEC_ID_MPEG1VIDEO) c->mb_decision = 2; + + if (av_set_parameters(oc,0)<0) { // Parameters not properly set. + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', parameters for avcodec not properly set.", + pixel_type(),filename); + } + + // Open codecs and alloc buffers. + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { // Failed to find codec. + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', no codec found.", + pixel_type(),filename); + } + if (avcodec_open(c,codec)<0) // Failed to open codec. + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to open codec.", + pixel_type(),filename); + tmp_pict = avcodec_alloc_frame(); + if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame. + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for data buffer.", + pixel_type(),filename); + } + tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx; + tmp_pict->type = FF_BUFFER_TYPE_USER; + int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy); + uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size); + if (!tmp_buffer) { // Failed to allocate memory for tmp buffer. + av_free(tmp_pict); + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for data buffer.", + pixel_type(),filename); + } + + // Associate buffer with tmp_pict. + avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy); + picture = avcodec_alloc_frame(); + if (!picture) { // Failed to allocate picture frame. + av_free(tmp_pict->data[0]); + av_free(tmp_pict); + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for picture frame.", + pixel_type(),filename); + } + + int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy); + uint8_t *buffer = (uint8_t*)av_malloc(size); + if (!buffer) { // Failed to allocate picture frame buffer. + av_free(picture); + av_free(tmp_pict->data[0]); + av_free(tmp_pict); + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to allocate memory for picture frame buffer.", + pixel_type(),filename); + } + + // Associate the buffer with picture. + avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy); + + // Open file. + if (!(fmt->flags&AVFMT_NOFILE)) { + if (url_fopen(&oc->pb,filename,URL_WRONLY)<0) + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s' cannot be opened.", + pixel_type(),filename); + } + + if (av_write_header(oc)<0) + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', could not write header.", + pixel_type(),filename); + double video_pts; + SwsContext *img_convert_context = 0; + img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt, + c->width,c->height,c->pix_fmt,sws_flags,0,0,0); + if (!img_convert_context) { // Failed to get swscale context. + // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); + av_free(picture->data); + av_free(picture); + av_free(tmp_pict->data[0]); + av_free(tmp_pict); + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%', failed to get conversion context.", + pixel_type(),filename); + } + int ret = 0, out_size; + uint8_t *video_outbuf = 0; + int video_outbuf_size = 1000000; + video_outbuf = (uint8_t*)av_malloc(video_outbuf_size); + if (!video_outbuf) { + // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb); + av_free(picture->data); + av_free(picture); + av_free(tmp_pict->data[0]); + av_free(tmp_pict); + avcodec_close(video_str->codec); + av_free(oc); + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', memory allocation error.", + pixel_type(),filename); + } + + // Loop through each desired image in list. + for (unsigned int i = first_frame; i<=nlast_frame; ++i) { + CImg currentIm = data[i], red, green, blue, gray; + if (src_pxl_fmt == PIX_FMT_RGB24) { + red = currentIm.get_shared_channel(0); + green = currentIm.get_shared_channel(1); + blue = currentIm.get_shared_channel(2); + cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format. + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y); + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y); + tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y); + } + } else { + gray = currentIm.get_shared_channel(0); + cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y); + } + + if (video_str) video_pts = (video_str->pts.val * video_str->time_base.num)/(video_str->time_base.den); + else video_pts = 0.0; + if (!video_str) break; + if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break; + out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture); + if (out_size>0) { + AVPacket pkt; + av_init_packet(&pkt); + pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base); + if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY; + pkt.stream_index = video_str->index; + pkt.data = video_outbuf; + pkt.size = out_size; + ret = av_write_frame(oc,&pkt); + } else if (out_size<0) break; + if (ret) break; // Error occured in writing frame. + } + + // Close codec. + if (video_str) { + avcodec_close(video_str->codec); + av_free(picture->data[0]); + av_free(picture); + av_free(tmp_pict->data[0]); + av_free(tmp_pict); + } + if (av_write_trailer(oc)<0) + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to write trailer.", + pixel_type(),filename); + av_freep(&oc->streams[stream_index]->codec); + av_freep(&oc->streams[stream_index]); + if (!(fmt->flags&AVFMT_NOFILE)) { + /*if (url_fclose(oc->pb)<0) + throw CImgIOException("CImgList<%s>::save_ffmpeg() : File '%s', failed to close file.", + pixel_type(),filename); + */ + } + av_free(oc); + av_free(video_outbuf); +#endif + return *this; + } + + // Save an image sequence into a YUV file (internal). + const CImgList& _save_yuv(cimg_std::FILE *const file, const char *const filename, const bool rgb2yuv) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_yuv() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",size,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_yuv() : Instance list (%u,%p), specified file is (null).", + pixel_type(),size,data); + if ((*this)[0].dimx()%2 || (*this)[0].dimy()%2) + throw CImgInstanceException("CImgList<%s>::save_yuv() : File '%s', image dimensions must be even numbers (current are %ux%u).", + pixel_type(),filename?filename:"(FILE*)",(*this)[0].dimx(),(*this)[0].dimy()); + + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + CImg YCbCr((*this)[l]); + if (rgb2yuv) YCbCr.RGBtoYCbCr(); + cimg::fwrite(YCbCr.data,YCbCr.width*YCbCr.height,nfile); + cimg::fwrite(YCbCr.get_resize(YCbCr.width/2, YCbCr.height/2,1,3,3).ptr(0,0,0,1), + YCbCr.width*YCbCr.height/2,nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save an image sequence into a YUV file. + const CImgList& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const { + return _save_yuv(0,filename,rgb2yuv); + } + + //! Save an image sequence into a YUV file. + const CImgList& save_yuv(cimg_std::FILE *const file, const bool rgb2yuv=true) const { + return _save_yuv(file,0,rgb2yuv); + } + + //! Save an image list into a .cimg file. + /** + A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg images. + \param filename : name of the output file. + \return A reference to the current CImgList instance is returned. + **/ + const CImgList& _save_cimg(cimg_std::FILE *const file, const char *const filename, const bool compression) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_cimg() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",size,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", + pixel_type(),size,data); + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + if (cimg_std::strstr(ptype,"unsigned")==ptype) cimg_std::fprintf(nfile,"%u unsigned_%s %s_endian\n",size,ptype+9,etype); + else cimg_std::fprintf(nfile,"%u %s %s_endian\n",size,ptype,etype); + cimglist_for(*this,l) { + const CImg& img = data[l]; + cimg_std::fprintf(nfile,"%u %u %u %u",img.width,img.height,img.depth,img.dim); + if (img.data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp.data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool compressed = false; + if (compression) { +#ifdef cimg_use_zlib + const unsigned long siz = sizeof(T)*ref.size(); + unsigned long csiz = siz + siz/10 + 16; + Bytef *const cbuf = new Bytef[csiz]; + if (compress(cbuf,&csiz,(Bytef*)ref.data,siz)) { + cimg::warn("CImgList<%s>::save_cimg() : File '%s', failed to save compressed data.\n Data will be saved uncompressed.", + pixel_type(),filename?filename:"(FILE*)"); + compressed = false; + } else { + cimg_std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + delete[] cbuf; + compressed = true; + } +#else + cimg::warn("CImgList<%s>::save_cimg() : File '%s', cannot save compressed data unless zlib is used " + "('cimg_use_zlib' must be defined).\n Data will be saved uncompressed.", + pixel_type(),filename?filename:"(FILE*)"); + compressed = false; +#endif + } + if (!compressed) { + cimg_std::fputc('\n',nfile); + cimg::fwrite(ref.data,ref.size(),nfile); + } + } else cimg_std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save an image list into a CImg file (RAW binary file + simple header) + const CImgList& save_cimg(cimg_std::FILE *file, const bool compress=false) const { + return _save_cimg(file,0,compress); + } + + //! Save an image list into a CImg file (RAW binary file + simple header) + const CImgList& save_cimg(const char *const filename, const bool compress=false) const { + return _save_cimg(0,filename,compress); + } + + // Insert the instance image into into an existing .cimg file, at specified coordinates. + const CImgList& _save_cimg(cimg_std::FILE *const file, const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int v0) const { +#define _cimg_save_cimg_case(Ts,Tss) \ + if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l=0; l::save_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ + pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ + if (W*H*D*V>0) { \ + if (l=W || y0>=H || z0>=D || v0>=D) cimg_std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ + else { \ + const CImg& img = (*this)[l-n0]; \ + const T *ptrs = img.data; \ + const unsigned int \ + x1 = x0 + img.width - 1, \ + y1 = y0 + img.height - 1, \ + z1 = z0 + img.depth - 1, \ + v1 = v0 + img.dim - 1, \ + nx1 = x1>=W?W-1:x1, \ + ny1 = y1>=H?H-1:y1, \ + nz1 = z1>=D?D-1:z1, \ + nv1 = v1>=V?V-1:v1; \ + CImg raw(1+nx1-x0); \ + const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg_std::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v=1+nv1-v0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) cimg_std::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z=1+nz1-z0; z; --z) { \ + const unsigned int skipyb = y0*W*sizeof(Tss); \ + if (skipyb) cimg_std::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y=1+ny1-y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) cimg_std::fseek(nfile,skipxb,SEEK_CUR); \ + raw.assign(ptrs, raw.width); \ + ptrs+=img.width; \ + if (endian) cimg::invert_endianness(raw.data,raw.width); \ + cimg::fwrite(raw.data,raw.width,nfile); \ + const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ + if (skipxe) cimg_std::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ + if (skipye) cimg_std::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg_std::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg_std::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + saved = true; \ + } + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_cimg() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(FILE*)",size,data); + if (!file && !filename) + throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", + pixel_type(),size,data); + typedef unsigned char uchar; + typedef unsigned short ushort; + typedef unsigned int uint; + typedef unsigned long ulong; + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); + bool saved = false, endian = cimg::endianness(); + char tmp[256], str_pixeltype[256], str_endian[256]; + unsigned int j, err, N, W, H, D, V; + int i; + j = 0; while((i=cimg_std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; + err = cimg_std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', Unknow CImg RAW header.", + pixel_type(),filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + const unsigned int lmax = cimg::min(N,n0+size); + _cimg_save_cimg_case("bool",bool); + _cimg_save_cimg_case("unsigned_char",uchar); + _cimg_save_cimg_case("uchar",uchar); + _cimg_save_cimg_case("char",char); + _cimg_save_cimg_case("unsigned_short",ushort); + _cimg_save_cimg_case("ushort",ushort); + _cimg_save_cimg_case("short",short); + _cimg_save_cimg_case("unsigned_int",uint); + _cimg_save_cimg_case("uint",uint); + _cimg_save_cimg_case("int",int); + _cimg_save_cimg_case("unsigned_long",ulong); + _cimg_save_cimg_case("ulong",ulong); + _cimg_save_cimg_case("long",long); + _cimg_save_cimg_case("float",float); + _cimg_save_cimg_case("double",double); + if (!saved) { + if (!file) cimg::fclose(nfile); + throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', cannot save images of pixels coded as '%s'.", + pixel_type(),filename?filename:"(FILE*)",str_pixeltype); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Insert the instance image into into an existing .cimg file, at specified coordinates. + const CImgList& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int v0) const { + return _save_cimg(0,filename,n0,x0,y0,z0,v0); + } + + //! Insert the instance image into into an existing .cimg file, at specified coordinates. + const CImgList& save_cimg(cimg_std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int v0) const { + return _save_cimg(file,0,n0,x0,y0,z0,v0); + } + + // Create an empty .cimg file with specified dimensions (internal) + static void _save_empty_cimg(cimg_std::FILE *const file, const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dv) { + cimg_std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const unsigned int siz = dx*dy*dz*dv*sizeof(T); + cimg_std::fprintf(nfile,"%u %s\n",nb,pixel_type()); + for (unsigned int i=nb; i; --i) { + cimg_std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dv); + for (unsigned int off=siz; off; --off) cimg_std::fputc(0,nfile); + } + if (!file) cimg::fclose(nfile); + } + + //! Create an empty .cimg file with specified dimensions. + static void save_empty_cimg(const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + return _save_empty_cimg(0,filename,nb,dx,dy,dz,dv); + } + + //! Create an empty .cimg file with specified dimensions. + static void save_empty_cimg(cimg_std::FILE *const file, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dv=1) { + return _save_empty_cimg(file,0,nb,dx,dy,dz,dv); + } + + //! Save a file in TIFF format. +#ifdef cimg_use_tiff + const CImgList& save_tiff(const char *const filename) const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_tiff() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",size,data); + if (!filename) + throw CImgArgumentException("CImgList<%s>::save_tiff() : Specified filename is (null) for instance list (%u,%p).", + pixel_type(),size,data); + TIFF *tif = TIFFOpen(filename,"w"); + if (tif) { + for (unsigned int dir=0, l=0; l& img = (*this)[l]; + if (img) { + if (img.depth==1) img._save_tiff(tif,dir++); + else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++); + } + } + TIFFClose(tif); + } else + throw CImgException("CImgList<%s>::save_tiff() : File '%s', error while opening stream for tiff file.", + pixel_type(),filename); + return *this; + } +#endif + + //! Save an image list as a gzipped file, using external tool 'gzip'. + const CImgList& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException("CImg<%s>::save_gzip_external() : Cannot save (null) filename.", + pixel_type()); + char command[1024], filetmp[512], body[512]; + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + cimg_std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext2); + else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } else { + if (*ext) cimg_std::sprintf(filetmp,"%s%s%s.%s",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand(),ext); + else cimg_std::sprintf(filetmp,"%s%s%s.cimg",cimg::temporary_path(),cimg_OS==2?"\\":"/", + cimg::filenamerand()); + } + if ((file=cimg_std::fopen(filetmp,"rb"))!=0) cimg_std::fclose(file); + } while (file); + save(filetmp); + cimg_std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename); + cimg::system(command); + file = cimg_std::fopen(filename,"rb"); + if (!file) + throw CImgIOException("CImgList<%s>::save_gzip_external() : File '%s' cannot be saved.", + pixel_type(),filename); + else cimg::fclose(file); + cimg_std::remove(filetmp); + return *this; + } + + //! Save an image list into a OFF file. + template + const CImgList& save_off(const char *const filename, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + get_append('x','y').save_off(filename,primitives,colors,invert_faces); + return *this; + } + + //! Save an image list into a OFF file. + template + const CImgList& save_off(cimg_std::FILE *const file, + const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { + get_append('x','y').save_off(file,primitives,colors,invert_faces); + return *this; + } + + //! Save an image sequence using the external tool 'ffmpeg'. + const CImgList& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const char *const codec="mpeg2video") const { + if (is_empty()) + throw CImgInstanceException("CImgList<%s>::save_ffmpeg_external() : File '%s', instance list (%u,%p) is empty.", + pixel_type(),filename?filename:"(null)",size,data); + if (!filename) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg_external() : Instance list (%u,%p), specified filename is (null).", + pixel_type(),size,data); + char command[1024], filetmp[512], filetmp2[512]; + cimg_std::FILE *file = 0; + const unsigned int nlast_frame = last_frame==~0U?size-1:last_frame; + if (first_frame>=size || nlast_frame>=size) + throw CImgArgumentException("CImgList<%s>::save_ffmpeg_external() : File '%s', specified frames [%u,%u] are out of list range (%u elements).", + pixel_type(),filename,first_frame,last_frame,size); + for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!data[ll].is_sameXYZ(data[0])) + throw CImgInstanceException("CImgList<%s>::save_ffmpeg_external() : File '%s', all images of the sequence must be of the same dimension.", + pixel_type(),filename); + do { + cimg_std::sprintf(filetmp,"%s%s%s",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); + cimg_std::sprintf(filetmp2,"%s_000001.ppm",filetmp); + if ((file=cimg_std::fopen(filetmp2,"rb"))!=0) cimg_std::fclose(file); + } while (file); + for (unsigned int l = first_frame; l<=nlast_frame; ++l) { + cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,l+1); + if (data[l].depth>1 || data[l].dim!=3) data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2); + else data[l].save_pnm(filetmp2); + } +#if cimg_OS!=2 + cimg_std::sprintf(command,"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\" >/dev/null 2>&1",filetmp,codec,filename); +#else + cimg_std::sprintf(command,"\"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\"\" >NUL 2>&1",filetmp,codec,filename); +#endif + cimg::system(command); + file = cimg_std::fopen(filename,"rb"); + if (!file) + throw CImgIOException("CImg<%s>::save_ffmpeg_external() : Failed to save image sequence '%s'.\n\n", + pixel_type(),filename); + else cimg::fclose(file); + cimglist_for(*this,lll) { cimg_std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,lll+1); cimg_std::remove(filetmp2); } + return *this; + } + + }; + + /* + #--------------------------------------------- + # + # Completion of previously declared functions + # + #---------------------------------------------- + */ + +namespace cimg { + + //! Display a dialog box, where a user can click standard buttons. + /** + Up to 6 buttons can be defined in the dialog window. + This function returns when a user clicked one of the button or closed the dialog window. + \param title = Title of the dialog window. + \param msg = Main message displayed inside the dialog window. + \param button1_txt = Label of the 1st button. + \param button2_txt = Label of the 2nd button. + \param button3_txt = Label of the 3rd button. + \param button4_txt = Label of the 4th button. + \param button5_txt = Label of the 5th button. + \param button6_txt = Label of the 6th button. + \param logo = Logo image displayed at the left of the main message. This parameter is optional. + \param centering = Tell to center the dialog window on the screen. + \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user. + \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in + the dialog box. At least one button is necessary. + **/ + + template + inline int dialog(const char *title, const char *msg, + const char *button1_txt, const char *button2_txt, + const char *button3_txt, const char *button4_txt, + const char *button5_txt, const char *button6_txt, + const CImg& logo, const bool centering = false) { +#if cimg_display!=0 + const unsigned char + black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_txt) { buttons.insert(CImg().draw_text(0,0,button1_txt,black,gray,1,13)); + if (button2_txt) { buttons.insert(CImg().draw_text(0,0,button2_txt,black,gray,1,13)); + if (button3_txt) { buttons.insert(CImg().draw_text(0,0,button3_txt,black,gray,1,13)); + if (button4_txt) { buttons.insert(CImg().draw_text(0,0,button4_txt,black,gray,1,13)); + if (button5_txt) { buttons.insert(CImg().draw_text(0,0,button5_txt,black,gray,1,13)); + if (button6_txt) { buttons.insert(CImg().draw_text(0,0,button6_txt,black,gray,1,13)); + }}}}}} + if (!buttons.size) + throw CImgArgumentException("cimg::dialog() : No buttons have been defined. At least one is necessary"); + + unsigned int bw = 0, bh = 0; + cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l].width); bh = cimg::max(bh,buttons[l].height); } + bw+=8; bh+=8; + if (bw<64) bw=64; + if (bw>128) bw=128; + if (bh<24) bh=24; + if (bh>48) bh=48; + + CImg button(bw,bh,1,3); + button.draw_rectangle(0,0,bw-1,bh-1,gray); + button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white); + button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black); + button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2); + CImg sbutton(bw,bh,1,3); + sbutton.draw_rectangle(0,0,bw-1,bh-1,gray); + sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black); + sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black); + sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white); + sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black); + sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2); + sbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false); + sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false); + CImg cbutton(bw,bh,1,3); + cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray); + cbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false); + cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false); + + cimglist_for(buttons,ll) { + cbuttons.insert(CImg(cbutton).draw_image(1+(bw-buttons[ll].dimx())/2,1+(bh-buttons[ll].dimy())/2,buttons[ll])); + sbuttons.insert(CImg(sbutton).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll])); + buttons[ll] = CImg(button).draw_image((bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2,buttons[ll]); + } + + CImg canvas; + if (msg) canvas = CImg().draw_text(0,0,msg,black,gray,1,13); + const unsigned int + bwall = (buttons.size-1)*(12+bw) + bw, + w = cimg::max(196U,36+logo.width+canvas.width, 24+bwall), + h = cimg::max(96U,36+canvas.height+bh,36+logo.height+bh), + lx = 12 + (canvas.data?0:((w-24-logo.width)/2)), + ly = (h-12-bh-logo.height)/2, + tx = lx+logo.width+12, + ty = (h-12-bh-canvas.height)/2, + bx = (w-bwall)/2, + by = h-12-bh; + + if (canvas.data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black). + draw_image(tx,ty,canvas); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w-1,h-1,gray). + draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). + draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black); + if (logo.data) canvas.draw_image(lx,ly,logo); + + unsigned int xbuttons[6]; + cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); } + + // Open window and enter events loop + CImgDisplay disp(canvas,title?title:" ",0,false,centering?true:false); + if (centering) disp.move((CImgDisplay::screen_dimx()-disp.dimx())/2, + (CImgDisplay::screen_dimy()-disp.dimy())/2); + bool stopflag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed && !stopflag) { + if (refresh) { + if (clicked>=0) CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + else { + if (selected>=0) CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized) disp.resize(disp); + + if (disp.button&1) { + oclicked = clicked; + clicked = -1; + cimglist_for(buttons,l) + if (disp.mouse_y>=(int)by && disp.mouse_y<(int)(by+bh) && + disp.mouse_x>=(int)xbuttons[l] && disp.mouse_x<(int)(xbuttons[l]+bw)) { + clicked = selected = l; + refresh = true; + } + if (clicked!=oclicked) refresh = true; + } else if (clicked>=0) stopflag = true; + + if (disp.key) { + oselected = selected; + switch (disp.key) { + case cimg::keyESC : selected=-1; stopflag=true; break; + case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break; + case cimg::keyTAB : + case cimg::keyARROWRIGHT : + case cimg::keyARROWDOWN : selected = (selected+1)%buttons.size; break; + case cimg::keyARROWLEFT : + case cimg::keyARROWUP : selected = (selected+buttons.size-1)%buttons.size; break; + } + disp.key = 0; + if (selected!=oselected) refresh = true; + } + } + if (!disp) selected = -1; + return selected; +#else + cimg_std::fprintf(cimg_stdout,"<%s>\n\n%s\n\n",title,msg); + return -1+0*(int)(button1_txt-button2_txt+button3_txt-button4_txt+button5_txt-button6_txt+logo.width+(int)centering); +#endif + } + + inline int dialog(const char *title, const char *msg, + const char *button1_txt, const char *button2_txt, const char *button3_txt, + const char *button4_txt, const char *button5_txt, const char *button6_txt, + const bool centering) { + return dialog(title,msg,button1_txt,button2_txt,button3_txt,button4_txt,button5_txt,button6_txt, + CImg::logo40x38(),centering); + } + + // End of cimg:: namespace +} + + // End of cimg_library:: namespace +} + +#ifdef _cimg_redefine_min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +#endif diff --git a/src/libs/greycstoration/LICENSE.txt b/src/libs/greycstoration/LICENSE.txt new file mode 100644 index 00000000..a36324de --- /dev/null +++ b/src/libs/greycstoration/LICENSE.txt @@ -0,0 +1,505 @@ + + CeCILL FREE SOFTWARE LICENSE AGREEMENT + + + Notice + +This Agreement is a Free Software license agreement that is the result +of discussions between its authors in order to ensure compliance with +the two main principles guiding its drafting: + + * firstly, compliance with the principles governing the distribution + of Free Software: access to source code, broad rights granted to + users, + * secondly, the election of a governing law, French law, with which + it is conformant, both as regards the law of torts and + intellectual property law, and the protection that it offers to + both authors and holders of the economic rights over software. + +The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[logiciel] L[ibre]) +license are: + +Commissariat à l'Energie Atomique - CEA, a public scientific, technical +and industrial establishment, having its principal place of business at +31-33 rue de la Fédération, 75752 Paris cedex 15, France. + +Centre National de la Recherche Scientifique - CNRS, a public scientific +and technological establishment, having its principal place of business +at 3 rue Michel-Ange 75794 Paris cedex 16, France. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, a public scientific and technological establishment, having its +principal place of business at Domaine de Voluceau, Rocquencourt, BP +105, 78153 Le Chesnay cedex, France. + + + Preamble + +The purpose of this Free Software license agreement is to grant users +the right to modify and redistribute the software governed by this +license within the framework of an open source distribution model. + +The exercising of these rights is conditional upon certain obligations +for users so as to preserve this status for all subsequent redistributions. + +In consideration of access to the source code and the rights to copy, +modify and redistribute granted by the license, users are provided only +with a limited warranty and the software's author, the holder of the +economic rights, and the successive licensors only have limited liability. + +In this respect, the risks associated with loading, using, modifying +and/or developing or reproducing the software by the user are brought to +the user's attention, given its Free Software status, which may make it +complicated to use, with the result that its use is reserved for +developers and experienced professionals having in-depth computer +knowledge. Users are therefore encouraged to load and test the +Software's suitability as regards their requirements in conditions +enabling the security of their systems and/or data to be ensured and, +more generally, to use and operate it in the same conditions of +security. This Agreement may be freely reproduced and published, +provided it is not altered, and that no provisions are either added or +removed herefrom. + +This Agreement may apply to any or all software for which the holder of +the economic rights decides to submit the use thereof to its provisions. + + + Article 1 - DEFINITIONS + +For the purpose of this Agreement, when the following expressions +commence with a capital letter, they shall have the following meaning: + +Agreement: means this license agreement, and its possible subsequent +versions and annexes. + +Software: means the software in its Object Code and/or Source Code form +and, where applicable, its documentation, "as is" when the Licensee +accepts the Agreement. + +Initial Software: means the Software in its Source Code and possibly its +Object Code form and, where applicable, its documentation, "as is" when +it is first distributed under the terms and conditions of the Agreement. + +Modified Software: means the Software modified by at least one +Contribution. + +Source Code: means all the Software's instructions and program lines to +which access is required so as to modify the Software. + +Object Code: means the binary files originating from the compilation of +the Source Code. + +Holder: means the holder(s) of the economic rights over the Initial +Software. + +Licensee: means the Software user(s) having accepted the Agreement. + +Contributor: means a Licensee having made at least one Contribution. + +Licensor: means the Holder, or any other individual or legal entity, who +distributes the Software under the Agreement. + +Contribution: means any or all modifications, corrections, translations, +adaptations and/or new functions integrated into the Software by any or +all Contributors, as well as any or all Internal Modules. + +Module: means a set of sources files including their documentation that +enables supplementary functions or services in addition to those offered +by the Software. + +External Module: means any or all Modules, not derived from the +Software, so that this Module and the Software run in separate address +spaces, with one calling the other when they are run. + +Internal Module: means any or all Module, connected to the Software so +that they both execute in the same address space. + +GNU GPL: means the GNU General Public License version 2 or any +subsequent version, as published by the Free Software Foundation Inc. + +Parties: mean both the Licensee and the Licensor. + +These expressions may be used both in singular and plural form. + + + Article 2 - PURPOSE + +The purpose of the Agreement is the grant by the Licensor to the +Licensee of a non-exclusive, transferable and worldwide license for the +Software as set forth in Article 5 hereinafter for the whole term of the +protection granted by the rights over said Software. + + + Article 3 - ACCEPTANCE + +3.1 The Licensee shall be deemed as having accepted the terms and +conditions of this Agreement upon the occurrence of the first of the +following events: + + * (i) loading the Software by any or all means, notably, by + downloading from a remote server, or by loading from a physical + medium; + * (ii) the first time the Licensee exercises any of the rights + granted hereunder. + +3.2 One copy of the Agreement, containing a notice relating to the +characteristics of the Software, to the limited warranty, and to the +fact that its use is restricted to experienced users has been provided +to the Licensee prior to its acceptance as set forth in Article 3.1 +hereinabove, and the Licensee hereby acknowledges that it has read and +understood it. + + + Article 4 - EFFECTIVE DATE AND TERM + + + 4.1 EFFECTIVE DATE + +The Agreement shall become effective on the date when it is accepted by +the Licensee as set forth in Article 3.1. + + + 4.2 TERM + +The Agreement shall remain in force for the entire legal term of +protection of the economic rights over the Software. + + + Article 5 - SCOPE OF RIGHTS GRANTED + +The Licensor hereby grants to the Licensee, who accepts, the following +rights over the Software for any or all use, and for the term of the +Agreement, on the basis of the terms and conditions set forth hereinafter. + +Besides, if the Licensor owns or comes to own one or more patents +protecting all or part of the functions of the Software or of its +components, the Licensor undertakes not to enforce the rights granted by +these patents against successive Licensees using, exploiting or +modifying the Software. If these patents are transferred, the Licensor +undertakes to have the transferees subscribe to the obligations set +forth in this paragraph. + + + 5.1 RIGHT OF USE + +The Licensee is authorized to use the Software, without any limitation +as to its fields of application, with it being hereinafter specified +that this comprises: + + 1. permanent or temporary reproduction of all or part of the Software + by any or all means and in any or all form. + + 2. loading, displaying, running, or storing the Software on any or + all medium. + + 3. entitlement to observe, study or test its operation so as to + determine the ideas and principles behind any or all constituent + elements of said Software. This shall apply when the Licensee + carries out any or all loading, displaying, running, transmission + or storage operation as regards the Software, that it is entitled + to carry out hereunder. + + + 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS + +The right to make Contributions includes the right to translate, adapt, +arrange, or make any or all modifications to the Software, and the right +to reproduce the resulting Software. + +The Licensee is authorized to make any or all Contributions to the +Software provided that it includes an explicit notice that it is the +author of said Contribution and indicates the date of the creation thereof. + + + 5.3 RIGHT OF DISTRIBUTION + +In particular, the right of distribution includes the right to publish, +transmit and communicate the Software to the general public on any or +all medium, and by any or all means, and the right to market, either in +consideration of a fee, or free of charge, one or more copies of the +Software by any means. + +The Licensee is further authorized to distribute copies of the modified +or unmodified Software to third parties according to the terms and +conditions set forth hereinafter. + + + 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION + +The Licensee is authorized to distribute true copies of the Software in +Source Code or Object Code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the Object Code of the Software is +redistributed, the Licensee allows future Licensees unhindered access to +the full Source Code of the Software by indicating how to access it, it +being understood that the additional cost of acquiring the Source Code +shall not exceed the cost of transferring the data. + + + 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE + +When the Licensee makes a Contribution to the Software, the terms and +conditions for the distribution of the Modified Software become subject +to all the provisions of this Agreement. + +The Licensee is authorized to distribute the Modified Software, in +Source Code or Object Code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the Object Code of the Modified +Software is redistributed, the Licensee allows future Licensees +unhindered access to the full Source Code of the Modified Software by +indicating how to access it, it being understood that the additional +cost of acquiring the Source Code shall not exceed the cost of +transferring the data. + + + 5.3.3 DISTRIBUTION OF EXTERNAL MODULES + +When the Licensee has developed an External Module, the terms and +conditions of this Agreement do not apply to said External Module, that +may be distributed under a separate license agreement. + + + 5.3.4 COMPATIBILITY WITH THE GNU GPL + +The Licensee can include a code that is subject to the provisions of one +of the versions of the GNU GPL in the Modified or unmodified Software, +and distribute that entire code under the terms of the same version of +the GNU GPL. + +The Licensee can include the Modified or unmodified Software in a code +that is subject to the provisions of one of the versions of the GNU GPL, +and distribute that entire code under the terms of the same version of +the GNU GPL. + + + Article 6 - INTELLECTUAL PROPERTY + + + 6.1 OVER THE INITIAL SOFTWARE + +The Holder owns the economic rights over the Initial Software. Any or +all use of the Initial Software is subject to compliance with the terms +and conditions under which the Holder has elected to distribute its work +and no one shall be entitled to modify the terms and conditions for the +distribution of said Initial Software. + +The Holder undertakes that the Initial Software will remain ruled at +least by the current license, for the duration set forth in article 4.2. + + + 6.2 OVER THE CONTRIBUTIONS + +A Licensee who develops a Contribution is the owner of the intellectual +property rights over this Contribution as defined by applicable law. + + + 6.3 OVER THE EXTERNAL MODULES + +A Licensee who develops an External Module is the owner of the +intellectual property rights over this External Module as defined by +applicable law and is free to choose the type of agreement that shall +govern its distribution. + + + 6.4 JOINT PROVISIONS + +The Licensee expressly undertakes: + + 1. not to remove, or modify, in any manner, the intellectual property + notices attached to the Software; + + 2. to reproduce said notices, in an identical manner, in the copies + of the Software modified or not. + +The Licensee undertakes not to directly or indirectly infringe the +intellectual property rights of the Holder and/or Contributors on the +Software and to take, where applicable, vis-à-vis its staff, any and all +measures required to ensure respect of said intellectual property rights +of the Holder and/or Contributors. + + + Article 7 - RELATED SERVICES + +7.1 Under no circumstances shall the Agreement oblige the Licensor to +provide technical assistance or maintenance services for the Software. + +However, the Licensor is entitled to offer this type of services. The +terms and conditions of such technical assistance, and/or such +maintenance, shall be set forth in a separate instrument. Only the +Licensor offering said maintenance and/or technical assistance services +shall incur liability therefor. + +7.2 Similarly, any Licensor is entitled to offer to its licensees, under +its sole responsibility, a warranty, that shall only be binding upon +itself, for the redistribution of the Software and/or the Modified +Software, under terms and conditions that it is free to decide. Said +warranty, and the financial terms and conditions of its application, +shall be subject of a separate instrument executed between the Licensor +and the Licensee. + + + Article 8 - LIABILITY + +8.1 Subject to the provisions of Article 8.2, the Licensee shall be +entitled to claim compensation for any direct loss it may have suffered +from the Software as a result of a fault on the part of the relevant +Licensor, subject to providing evidence thereof. + +8.2 The Licensor's liability is limited to the commitments made under +this Agreement and shall not be incurred as a result of in particular: +(i) loss due the Licensee's total or partial failure to fulfill its +obligations, (ii) direct or consequential loss that is suffered by the +Licensee due to the use or performance of the Software, and (iii) more +generally, any consequential loss. In particular the Parties expressly +agree that any or all pecuniary or business loss (i.e. loss of data, +loss of profits, operating loss, loss of customers or orders, +opportunity cost, any disturbance to business activities) or any or all +legal proceedings instituted against the Licensee by a third party, +shall constitute consequential loss and shall not provide entitlement to +any or all compensation from the Licensor. + + + Article 9 - WARRANTY + +9.1 The Licensee acknowledges that the scientific and technical +state-of-the-art when the Software was distributed did not enable all +possible uses to be tested and verified, nor for the presence of +possible defects to be detected. In this respect, the Licensee's +attention has been drawn to the risks associated with loading, using, +modifying and/or developing and reproducing the Software which are +reserved for experienced users. + +The Licensee shall be responsible for verifying, by any or all means, +the product's suitability for its requirements, its good working order, +and for ensuring that it shall not cause damage to either persons or +properties. + +9.2 The Licensor hereby represents, in good faith, that it is entitled +to grant all the rights over the Software (including in particular the +rights set forth in Article 5). + +9.3 The Licensee acknowledges that the Software is supplied "as is" by +the Licensor without any other express or tacit warranty, other than +that provided for in Article 9.2 and, in particular, without any warranty +as to its commercial value, its secured, safe, innovative or relevant +nature. + +Specifically, the Licensor does not warrant that the Software is free +from any error, that it will operate without interruption, that it will +be compatible with the Licensee's own equipment and software +configuration, nor that it will meet the Licensee's requirements. + +9.4 The Licensor does not either expressly or tacitly warrant that the +Software does not infringe any third party intellectual property right +relating to a patent, software or any other property right. Therefore, +the Licensor disclaims any and all liability towards the Licensee +arising out of any or all proceedings for infringement that may be +instituted in respect of the use, modification and redistribution of the +Software. Nevertheless, should such proceedings be instituted against +the Licensee, the Licensor shall provide it with technical and legal +assistance for its defense. Such technical and legal assistance shall be +decided on a case-by-case basis between the relevant Licensor and the +Licensee pursuant to a memorandum of understanding. The Licensor +disclaims any and all liability as regards the Licensee's use of the +name of the Software. No warranty is given as regards the existence of +prior rights over the name of the Software or as regards the existence +of a trademark. + + + Article 10 - TERMINATION + +10.1 In the event of a breach by the Licensee of its obligations +hereunder, the Licensor may automatically terminate this Agreement +thirty (30) days after notice has been sent to the Licensee and has +remained ineffective. + +10.2 A Licensee whose Agreement is terminated shall no longer be +authorized to use, modify or distribute the Software. However, any +licenses that it may have granted prior to termination of the Agreement +shall remain valid subject to their having been granted in compliance +with the terms and conditions hereof. + + + Article 11 - MISCELLANEOUS + + + 11.1 EXCUSABLE EVENTS + +Neither Party shall be liable for any or all delay, or failure to +perform the Agreement, that may be attributable to an event of force +majeure, an act of God or an outside cause, such as defective +functioning or interruptions of the electricity or telecommunications +networks, network paralysis following a virus attack, intervention by +government authorities, natural disasters, water damage, earthquakes, +fire, explosions, strikes and labor unrest, war, etc. + +11.2 Any Failure by either Party, on one or more occasions, to invoke +one or more of the provisions hereof, shall under no circumstances be +interpreted as being a waiver by the interested Party of its right to +invoke said provision(s) subsequently. + +11.3 The Agreement cancels and replaces any or all previous agreements, +whether written or oral, between the Parties and having the same +purpose, and constitutes the entirety of the agreement between said +Parties concerning said purpose. No supplement or modification to the +terms and conditions hereof shall be effective as between the Parties +unless it is made in writing and signed by their duly authorized +representatives. + +11.4 In the event that one or more of the provisions hereof were to +conflict with a current or future applicable act or legislative text, +said act or legislative text shall prevail, and the Parties shall make +the necessary amendments so as to comply with said act or legislative +text. All other provisions shall remain effective. Similarly, invalidity +of a provision of the Agreement, for any reason whatsoever, shall not +cause the Agreement as a whole to be invalid. + + + 11.5 LANGUAGE + +The Agreement is drafted in both French and English and both versions +are deemed authentic. + + + Article 12 - NEW VERSIONS OF THE AGREEMENT + +12.1 Any person is authorized to duplicate and distribute copies of this +Agreement. + +12.2 So as to ensure coherence, the wording of this Agreement is +protected and may only be modified by the authors of the License, who +reserve the right to periodically publish updates or new versions of the +Agreement, each with a separate number. These subsequent versions may +address new issues encountered by Free Software. + +12.3 Any Software distributed under a given version of the Agreement may +only be subsequently distributed under the same version of the Agreement +or a subsequent version, subject to the provisions of Article 5.3.4. + + + Article 13 - GOVERNING LAW AND JURISDICTION + +13.1 The Agreement is governed by French law. The Parties agree to +endeavor to seek an amicable solution to any disagreements or disputes +that may arise during the performance of the Agreement. + +13.2 Failing an amicable solution within two (2) months as from their +occurrence, and unless emergency proceedings are necessary, the +disagreements or disputes shall be referred to the Paris Courts having +jurisdiction, by the more diligent Party. + + +Version 2.0 dated 2005-05-21. diff --git a/src/libs/greycstoration/Makefile.am b/src/libs/greycstoration/Makefile.am new file mode 100644 index 00000000..f7cc569f --- /dev/null +++ b/src/libs/greycstoration/Makefile.am @@ -0,0 +1,20 @@ +METASOURCES = AUTO + +KDE_CXXFLAGS = $(USE_EXCEPTIONS) -w + +noinst_LTLIBRARIES = libgreycstoration.la + +libgreycstoration_la_SOURCES = greycstorationiface.cpp greycstorationwidget.cpp + +libgreycstoration_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dimg/filters \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikaminclude_HEADERS = greycstorationiface.h greycstorationwidget.h greycstorationsettings.h + +digikamincludedir = $(includedir)/digikam + diff --git a/src/libs/greycstoration/greycstoration.h b/src/libs/greycstoration/greycstoration.h new file mode 100644 index 00000000..36b2c0e2 --- /dev/null +++ b/src/libs/greycstoration/greycstoration.h @@ -0,0 +1,481 @@ +/* + # + # File : greycstoration.h + # ( C++ header file - CImg plug-in ) + # + # Description : GREYCstoration plug-in allowing easy integration in + # third parties softwares. + # ( http://www.greyc.ensicaen.fr/~dtschump/greycstoration/ ) + # This file is a part of the CImg Library project. + # ( http://cimg.sourceforge.net ) + # + # THIS PLUG-IN IS INTENDED FOR DEVELOPERS ONLY. IT EASES THE INTEGRATION ALGORITHM IN + # THIRD PARTIES SOFTWARES. IF YOU ARE A USER OF GREYCSTORATION, PLEASE LOOK + # AT THE FILE 'greycstoration.cpp' WHICH IS THE SOURCE OF THE COMPLETE + # COMMAND LINE GREYCSTORATION TOOL. + # + # Copyright : David Tschumperle + # ( http://www.greyc.ensicaen.fr/~dtschump/ ) + # + # License : CeCILL v2.0 + # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) + # + # This software is governed by the CeCILL license under French law and + # abiding by the rules of distribution of free software. You can use, + # modify and/ or redistribute the software under the terms of the CeCILL + # license as circulated by CEA, CNRS and INRIA at the following URL + # "http://www.cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL license and that you accept its terms. + # +*/ + +#ifndef cimg_plugin_greycstoration +#define cimg_plugin_greycstoration + +//------------------------------------------------------------------------------ +// GREYCstoration parameter structure, storing important informations about +// algorithm parameters and computing threads. +// ** This structure has not to be manipulated by the API user, so please just +// ignore it if you want to ** +//------------------------------------------------------------------------------- +struct _greycstoration_params { + + // Tell if the patch-based algorithm is selected + bool patch_based; + + // Parameters specific to the non-patch regularization algorithm + float amplitude; + float sharpness; + float anisotropy; + float alpha; + float sigma; + float gfact; + float dl; + float da; + float gauss_prec; + unsigned int interpolation; + + // Parameters specific to the patch-based regularization algorithm + unsigned int patch_size; + float sigma_s; + float sigma_p; + unsigned int lookup_size; + + // Non-specific parameters of the algorithms. + CImg *source; + const CImg *mask; + CImg *temporary; + unsigned long *counter; + unsigned int tile; + unsigned int tile_border; + unsigned int thread; + unsigned int nb_threads; + bool fast_approx; + bool is_running; + bool *stop_request; +#if cimg_OS==1 && defined(_PTHREAD_H) + pthread_mutex_t + *mutex; +#elif cimg_OS==2 + HANDLE mutex; +#else + void *mutex; +#endif + + // Default constructor + _greycstoration_params():patch_based(false),amplitude(0),sharpness(0),anisotropy(0),alpha(0),sigma(0),gfact(1), + dl(0),da(0),gauss_prec(0),interpolation(0),patch_size(0), + sigma_s(0),sigma_p(0),lookup_size(0),source(0),mask(0),temporary(0),counter(0),tile(0), + tile_border(0),thread(0),nb_threads(0),fast_approx(false),is_running(false), stop_request(0), mutex(0) {} +}; + +_greycstoration_params greycstoration_params[16]; + +//---------------------------------------------------------- +// Public functions of the GREYCstoration API. +// Use the functions below for integrating GREYCstoration +// in your own C++ code. +//---------------------------------------------------------- + +//! Test if GREYCstoration threads are still running. +bool greycstoration_is_running() const { + return greycstoration_params->is_running; +} + +//! Force the GREYCstoration threads to stop. +CImg& greycstoration_stop() { + if (greycstoration_is_running()) { + *(greycstoration_params->stop_request) = true; + while (greycstoration_params->is_running) cimg::wait(50); + } + return *this; +} + +//! Return the GREYCstoration progress bar indice (between 0 and 100). +float greycstoration_progress() const { + if (!greycstoration_is_running()) return 0.0f; + const unsigned long counter = greycstoration_params->counter?*(greycstoration_params->counter):0; + const float + da = greycstoration_params->da, + factor = greycstoration_params->patch_based?1:(1+360/da); + float maxcounter = 0; + if (greycstoration_params->tile==0) maxcounter = width*height*depth*factor; + else { + const unsigned int + t = greycstoration_params->tile, + b = greycstoration_params->tile_border, + n = (1+(width-1)/t)*(1+(height-1)/t)*(1+(depth-1)/t); + maxcounter = (width*height*depth + n*4*b*(b + t))*factor; + } + return cimg::min(counter*99.9f/maxcounter,99.9f); +} + +//! Run the non-patch version of the GREYCstoration algorithm on the instance image, using a mask. +CImg& greycstoration_run(const CImg& mask, + const float amplitude=60, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f, + const float dl=0.8f, const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, + const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) { + + if (greycstoration_is_running()) + throw CImgInstanceException("CImg::greycstoration_run() : A GREYCstoration thread is already running on" + " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data); + + else { + if (!mask.is_empty() && !mask.is_sameXY(*this)) + throw CImgArgumentException("CImg<%s>::greycstoration_run() : Given mask (%u,%u,%u,%u,%p) and instance image " + "(%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data,width,height,depth,dim,data); + if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max."); + const unsigned int + ntile = (tile && (tile1 && tile *const temporary = ntile?new CImg(*this):0; + unsigned long *const counter = new unsigned long; + *counter = 0; + bool *const stop_request = new bool; + *stop_request = false; + + for (unsigned int k=0; k<(nthreads?nthreads:1); k++) { + greycstoration_params[k].patch_based = false; + greycstoration_params[k].amplitude = amplitude; + greycstoration_params[k].sharpness = sharpness; + greycstoration_params[k].anisotropy = anisotropy; + greycstoration_params[k].alpha = alpha; + greycstoration_params[k].sigma = sigma; + greycstoration_params[k].gfact = gfact; + greycstoration_params[k].dl = dl; + greycstoration_params[k].da = da; + greycstoration_params[k].gauss_prec = gauss_prec; + greycstoration_params[k].interpolation = interpolation; + greycstoration_params[k].fast_approx = fast_approx; + greycstoration_params[k].source = this; + greycstoration_params[k].mask = &mask; + greycstoration_params[k].temporary = temporary; + greycstoration_params[k].counter = counter; + greycstoration_params[k].tile = ntile; + greycstoration_params[k].tile_border = tile_border; + greycstoration_params[k].thread = k; + greycstoration_params[k].nb_threads = nthreads; + greycstoration_params[k].is_running = true; + greycstoration_params[k].stop_request = stop_request; + if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex; + else greycstoration_mutex_create(greycstoration_params[0]); + } + if (nthreads) { // Threaded version +#if cimg_OS==1 +#ifdef _PTHREAD_H + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for (unsigned int k=0; knb_threads; k++) { + pthread_t thread; + const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k)); + if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d", + pixel_type(), err); + } +#endif +#elif cimg_OS==2 + for (unsigned int k=0; knb_threads; k++) { + unsigned long ThreadID = 0; + CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID); + } +#else + throw CImgInstanceException("CImg::greycstoration_run() : Threads are not supported, please define cimg_OS first."); +#endif + } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version + } + return *this; +} + +//! Run the non-patch version of the GREYCstoration algorithm on the instance image. +CImg& greycstoration_run(const float amplitude=50, const float sharpness=0.7f, const float anisotropy=0.3f, + const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f, + const float dl=0.8f, const float da=30.0f, + const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, + const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) { + static const CImg empty_mask; + return greycstoration_run(empty_mask,amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec, + interpolation,fast_approx,tile,tile_border,nb_threads); +} + +//! Run the patch-based version of the GREYCstoration algorithm on the instance image. +CImg& greycstoration_patch_run(const unsigned int patch_size=5, const float sigma_p=10, const float sigma_s=100, + const unsigned int lookup_size=20, const bool fast_approx=true, + const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) { + + static const CImg empty_mask; + if (greycstoration_is_running()) + throw CImgInstanceException("CImg::greycstoration_run() : A GREYCstoration thread is already running on" + " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data); + + else { + if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max."); + const unsigned int + ntile = (tile && (tile1 && tile *const temporary = ntile?new CImg(*this):0; + unsigned long *const counter = new unsigned long; + *counter = 0; + bool *const stop_request = new bool; + *stop_request = false; + + for (unsigned int k=0; k<(nthreads?nthreads:1); k++) { + greycstoration_params[k].patch_based = true; + greycstoration_params[k].patch_size = patch_size; + greycstoration_params[k].sigma_s = sigma_s; + greycstoration_params[k].sigma_p = sigma_p; + greycstoration_params[k].lookup_size = lookup_size; + greycstoration_params[k].source = this; + greycstoration_params[k].mask = &empty_mask; + greycstoration_params[k].temporary = temporary; + greycstoration_params[k].counter = counter; + greycstoration_params[k].tile = ntile; + greycstoration_params[k].tile_border = tile_border; + greycstoration_params[k].thread = k; + greycstoration_params[k].nb_threads = nthreads; + greycstoration_params[k].fast_approx = fast_approx; + greycstoration_params[k].is_running = true; + greycstoration_params[k].stop_request = stop_request; + if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex; + else greycstoration_mutex_create(greycstoration_params[0]); + } + if (nthreads) { // Threaded version +#if cimg_OS==1 +#ifdef _PTHREAD_H + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for (unsigned int k=0; knb_threads; k++) { + pthread_t thread; + const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k)); + if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d", + pixel_type(), err); + } +#endif +#elif cimg_OS==2 + for (unsigned int k=0; knb_threads; k++) { + unsigned long ThreadID = 0; + CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID); + } +#else + throw CImgInstanceException("CImg::greycstoration_run() : Threads support have not been enabled in this version of GREYCstoration."); +#endif + } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version + } + return *this; +} + +//------------------------------------------------------------------------------ +// GREYCstoration private functions. +// Should not be used directly by the API user. +//------------------------------------------------------------------------------- + +static void greycstoration_mutex_create(_greycstoration_params &p) { + if (p.nb_threads>1) { +#if cimg_OS==1 && defined(_PTHREAD_H) + p.mutex = new pthread_mutex_t; + pthread_mutex_init(p.mutex,0); +#elif cimg_OS==2 + p.mutex = CreateMutex(0,FALSE,0); +#endif + } +} + +static void greycstoration_mutex_lock(_greycstoration_params &p) { + if (p.nb_threads>1) { +#if cimg_OS==1 && defined(_PTHREAD_H) + if (p.mutex) pthread_mutex_lock(p.mutex); +#elif cimg_OS==2 + WaitForSingleObject(p.mutex,INFINITE); +#endif + } +} + +static void greycstoration_mutex_unlock(_greycstoration_params &p) { + if (p.nb_threads>1) { +#if cimg_OS==1 && defined(_PTHREAD_H) + if (p.mutex) pthread_mutex_unlock(p.mutex); +#elif cimg_OS==2 + ReleaseMutex(p.mutex); +#endif + } +} + +static void greycstoration_mutex_destroy(_greycstoration_params &p) { + if (p.nb_threads>1) { +#if cimg_OS==1 && defined(_PTHREAD_H) + if (p.mutex) pthread_mutex_destroy(p.mutex); +#elif cimg_OS==2 + CloseHandle(p.mutex); +#endif + p.mutex = 0; + } +} + +#if cimg_OS==1 +static void* greycstoration_thread(void *arg) { +#elif cimg_OS==2 + static DWORD WINAPI greycstoration_thread(void *arg) { +#endif + _greycstoration_params &p = *(_greycstoration_params*)arg; + greycstoration_mutex_lock(p); + const CImg &mask = *(p.mask); + CImg &source = *(p.source); + + if (!p.tile) { + + // Non-tiled version + //------------------ + if (p.patch_based) source.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx); + else source.blur_anisotropic(mask,p.amplitude,p.sharpness,p.anisotropy,p.alpha,p.sigma,p.dl,p.da,p.gauss_prec, + p.interpolation,p.fast_approx,p.gfact); + + } else { + + // Tiled version + //--------------- + CImg &temporary = *(p.temporary); + const bool threed = (source.depth>1); + const unsigned int b = p.tile_border; + unsigned int ctile = 0; + if (threed) { + for (unsigned int z=0; z img = source.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true); + CImg mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true); + img.greycstoration_params[0] = p; + greycstoration_mutex_unlock(p); + if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx); + else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy, + p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact); + greycstoration_mutex_lock(p); + temporary.draw_image(x,y,z,img.crop(b,b,b,img.width-b,img.height-b,img.depth-b)); + } + } else { + for (unsigned int y=0; y img = source.get_crop(x-b,y-b,xe+b,ye+b,true); + CImg mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,xe+b,ye+b,true); + img.greycstoration_params[0] = p; + greycstoration_mutex_unlock(p); + if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx); + else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy, + p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact); + temporary.draw_image(x,y,img.crop(b,b,img.width-b,img.height-b)); + greycstoration_mutex_lock(p); + } + } + } + greycstoration_mutex_unlock(p); + + if (!p.thread) { + if (p.nb_threads>1) { + bool stopflag = true; + do { + stopflag = true; + for (unsigned int k=1; kstop_request)) ++(*greycstoration_params->counter); else return *this; +#define cimg_plugin_greycstoration_lock \ + greycstoration_mutex_lock(greycstoration_params[0]); +#define cimg_plugin_greycstoration_unlock \ + greycstoration_mutex_unlock(greycstoration_params[0]); + +#endif diff --git a/src/libs/greycstoration/greycstorationiface.cpp b/src/libs/greycstoration/greycstorationiface.cpp new file mode 100644 index 00000000..32f60514 --- /dev/null +++ b/src/libs/greycstoration/greycstorationiface.cpp @@ -0,0 +1,473 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-03 + * Description : Greycstoration interface. + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +/** Don't use CImg interface (keyboard/mouse interaction) */ +#define cimg_display 0 +/** Only print debug information on the console */ +#define cimg_debug 1 + +#ifdef HAVE_CONFIG_H +#include +#endif + +// C++ includes. + +#include + +// Local includes. + +#define cimg_plugin "greycstoration.h" +// Unix-like (Linux, Solaris, BSD, MacOSX, Irix,...). +#if defined(unix) || defined(__unix) || defined(__unix__) \ + || defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(sun) || defined(__sun) \ + || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined __DragonFly__ \ + || defined(__MACOSX__) || defined(__APPLE__) \ + || defined(sgi) || defined(__sgi) \ + || defined(__CYGWIN__) +#include +#endif + +/** Number of children threads used to run Greystoration algorithm + For the moment we use only one thread. See B.K.O #186642 for details. + Multithreading management need to be fixed into CImg. + */ +#define COMPUTATION_THREAD 2 + +/** Uncomment this line if you use future GreycStoration implementation with GFact parameter + */ +#define GREYSTORATION_USING_GFACT 1 + +// Local includes. + +#include "ddebug.h" +#include "greycstorationsettings.h" +#include "greycstorationiface.h" + +// CImg includes. + +#include "CImg.h" + +using namespace cimg_library; + +namespace Digikam +{ + +class GreycstorationIfacePriv +{ + +public: + + GreycstorationIfacePriv() + { + mode = GreycstorationIface::Restore; + gfact = 1.0; + } + + float gfact; + + int mode; // The interface running mode. + + TQImage inPaintingMask; // Mask for inpainting. + + GreycstorationSettings settings; // Current Greycstoraion algorithm settings. + + CImg<> img; // Main image. + CImg mask; // The mask used with inpaint or resize mode +}; + +GreycstorationIface::GreycstorationIface(DImg *orgImage, + GreycstorationSettings settings, + int mode, + int newWidth, int newHeight, + const TQImage& inPaintingMask, + TQObject *parent) + : DImgThreadedFilter(orgImage, parent) +{ + d = new GreycstorationIfacePriv; + d->settings = settings; + d->mode = mode; + d->inPaintingMask = inPaintingMask; + + if (m_orgImage.sixteenBit()) // 16 bits image. + d->gfact = 1.0/256.0; + + if (d->mode == Resize || d->mode == SimpleResize) + { + m_destImage = DImg(newWidth, newHeight, + m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + DDebug() << "GreycstorationIface::Resize: new size: (" + << newWidth << ", " << newHeight << ")" << endl; + } + else + { + m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), + m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); + } + + initFilter(); +} + +GreycstorationIface::~GreycstorationIface() +{ + delete d; +} + +// We need to re-implemente this method from DImgThreadedFilter class because +// target image size can be different from original if d->mode = Resize. + +void GreycstorationIface::initFilter() +{ + if (m_orgImage.width() && m_orgImage.height()) + { + if (m_parent) + start(); // m_parent is valide, start thread ==> run() + else + startComputation(); // no parent : no using thread. + } + else // No image data + { + if (m_parent) // If parent then send event about a problem. + { + postProgress(0, false, false); + DDebug() << m_name << "::No valid image data !!! ..." << endl; + } + } +} + +void GreycstorationIface::stopComputation() +{ + // Because Greycstoration algorithm run in a child thread, we need + // to stop it before to stop this thread. + if (d->img.greycstoration_is_running()) + { + // If the user abort, we stop the algorithm. + DDebug() << "Stop Greycstoration computation..." << endl; + d->img.greycstoration_stop(); + } + + // And now when stop main loop and clean up all + DImgThreadedFilter::stopComputation(); +} + +void GreycstorationIface::filterImage() +{ + int x, y; + + DDebug() << "GreycstorationIface::Initialization..." << endl; + + // Copy the src image data into a CImg type image with three channels and no alpha. + + uchar* imageData = m_orgImage.bits(); + int imageWidth = m_orgImage.width(); + int imageHeight = m_orgImage.height(); + d->img = CImg<>(imageWidth, imageHeight, 1, 4); + + if (!m_orgImage.sixteenBit()) // 8 bits image. + { + uchar *ptr = imageData; + + for (y = 0; y < imageHeight; y++) + { + for (x = 0; x < imageWidth; x++) + { + d->img(x, y, 0) = ptr[0]; // Blue. + d->img(x, y, 1) = ptr[1]; // Green. + d->img(x, y, 2) = ptr[2]; // Red. + d->img(x, y, 3) = ptr[3]; // Alpha. + ptr += 4; + } + } + } + else // 16 bits image. + { + unsigned short *ptr = (unsigned short *)imageData; + + for (y = 0; y < imageHeight; y++) + { + for (x = 0; x < imageWidth; x++) + { + d->img(x, y, 0) = ptr[0]; // Blue. + d->img(x, y, 1) = ptr[1]; // Green. + d->img(x, y, 2) = ptr[2]; // Red. + d->img(x, y, 3) = ptr[3]; // Alpha. + ptr += 4; + } + } + } + + DDebug() << "GreycstorationIface::Process Computation..." << endl; + + try + { + switch (d->mode) + { + case Restore: + restoration(); + break; + + case InPainting: + inpainting(); + break; + + case Resize: + resize(); + break; + + case SimpleResize: + simpleResize(); + break; + } + } + catch(...) // Everything went wrong. + { + DDebug() << "GreycstorationIface::Error during Greycstoration filter computation!" << endl; + + if (m_parent) + postProgress( 0, false, false ); + + return; + } + + if (m_cancel) + return; + + // Copy CImg onto destination. + + DDebug() << "GreycstorationIface::Finalization..." << endl; + + uchar* newData = m_destImage.bits(); + int newWidth = m_destImage.width(); + int newHeight = m_destImage.height(); + + if (!m_orgImage.sixteenBit()) // 8 bits image. + { + uchar *ptr = newData; + + for (y = 0; y < newHeight; y++) + { + for (x = 0; x < newWidth; x++) + { + // Overwrite RGB values to destination. + ptr[0] = static_cast(d->img(x, y, 0)); // Blue + ptr[1] = static_cast(d->img(x, y, 1)); // Green + ptr[2] = static_cast(d->img(x, y, 2)); // Red + ptr[3] = static_cast(d->img(x, y, 3)); // Alpha + ptr += 4; + } + } + } + else // 16 bits image. + { + unsigned short *ptr = (unsigned short *)newData; + + for (y = 0; y < newHeight; y++) + { + for (x = 0; x < newWidth; x++) + { + // Overwrite RGB values to destination. + ptr[0] = static_cast(d->img(x, y, 0)); // Blue + ptr[1] = static_cast(d->img(x, y, 1)); // Green + ptr[2] = static_cast(d->img(x, y, 2)); // Red + ptr[3] = static_cast(d->img(x, y, 3)); // Alpha + ptr += 4; + } + } + } +} + +void GreycstorationIface::restoration() +{ + for (uint iter = 0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++) + { + // This function will start a thread running one iteration of the GREYCstoration filter. + // It returns immediately, so you can do what you want after (update a progress bar for + // instance). + d->img.greycstoration_run(d->settings.amplitude, + d->settings.sharpness, + d->settings.anisotropy, + d->settings.alpha, + d->settings.sigma, +#ifdef GREYSTORATION_USING_GFACT + d->gfact, +#endif + d->settings.dl, + d->settings.da, + d->settings.gaussPrec, + d->settings.interp, + d->settings.fastApprox, + d->settings.tile, + d->settings.btile, + COMPUTATION_THREAD); + + iterationLoop(iter); + } +} + +void GreycstorationIface::inpainting() +{ + if (!d->inPaintingMask.isNull()) + { + // Copy the inpainting image data into a CImg type image with three channels and no alpha. + + int x, y; + + d->mask = CImg(d->inPaintingMask.width(), d->inPaintingMask.height(), 1, 3); + uchar *ptr = d->inPaintingMask.bits(); + + for (y = 0; y < d->inPaintingMask.height(); y++) + { + for (x = 0; x < d->inPaintingMask.width(); x++) + { + d->mask(x, y, 0) = ptr[2]; // blue. + d->mask(x, y, 1) = ptr[1]; // green. + d->mask(x, y, 2) = ptr[0]; // red. + ptr += 4; + } + } + } + else + { + DDebug() << "Inpainting image: mask is null!" << endl; + m_cancel = true; + return; + } + + for (uint iter=0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++) + { + // This function will start a thread running one iteration of the GREYCstoration filter. + // It returns immediately, so you can do what you want after (update a progress bar for + // instance). + d->img.greycstoration_run(d->mask, + d->settings.amplitude, + d->settings.sharpness, + d->settings.anisotropy, + d->settings.alpha, + d->settings.sigma, +#ifdef GREYSTORATION_USING_GFACT + d->gfact, +#endif + d->settings.dl, + d->settings.da, + d->settings.gaussPrec, + d->settings.interp, + d->settings.fastApprox, + d->settings.tile, + d->settings.btile, + COMPUTATION_THREAD); + + iterationLoop(iter); + } +} + +void GreycstorationIface::resize() +{ + const bool anchor = true; // Anchor original pixels. + const unsigned int init = 5; // Initial estimate (1=block, 3=linear, 5=bicubic). + + int w = m_destImage.width(); + int h = m_destImage.height(); + + d->mask.assign(d->img.dimx(), d->img.dimy(), 1, 1, 255); + + if (!anchor) + d->mask.resize(w, h, 1, 1, 1); + else + d->mask = !d->mask.resize(w, h, 1, 1, 4); + + d->img.resize(w, h, 1, -100, init); + + for (uint iter = 0 ; !m_cancel && (iter < d->settings.nbIter) ; iter++) + { + // This function will start a thread running one iteration of the GREYCstoration filter. + // It returns immediately, so you can do what you want after (update a progress bar for + // instance). + d->img.greycstoration_run(d->mask, + d->settings.amplitude, + d->settings.sharpness, + d->settings.anisotropy, + d->settings.alpha, + d->settings.sigma, +#ifdef GREYSTORATION_USING_GFACT + d->gfact, +#endif + d->settings.dl, + d->settings.da, + d->settings.gaussPrec, + d->settings.interp, + d->settings.fastApprox, + d->settings.tile, + d->settings.btile, + COMPUTATION_THREAD); + + iterationLoop(iter); + } +} + +void GreycstorationIface::simpleResize() +{ + const unsigned int method = 3; // Initial estimate (0, none, 1=block, 3=linear, 4=grid, 5=bicubic). + + int w = m_destImage.width(); + int h = m_destImage.height(); + + while (d->img.dimx() > 2*w && + d->img.dimy() > 2*h) + { + d->img.resize_halfXY(); + } + + d->img.resize(w, h, -100, -100, method); +} + +void GreycstorationIface::iterationLoop(uint iter) +{ + uint mp = 0; + uint p = 0; + + do + { + usleep(100000); + + if (m_parent && !m_cancel) + { + // Update the progress bar in dialog. We simply computes the global + // progression index (including all iterations). + + p = (uint)((iter*100 + d->img.greycstoration_progress())/d->settings.nbIter); + + if (p > mp) + { + postProgress(p); + mp = p; + } + } + } + while (d->img.greycstoration_is_running() && !m_cancel); + + // A delay is require here. I suspect a sync problem between threads + // used by GreycStoration algorithm. + usleep(100000); +} + +} // NameSpace Digikam diff --git a/src/libs/greycstoration/greycstorationiface.h b/src/libs/greycstoration/greycstorationiface.h new file mode 100644 index 00000000..fd7b248b --- /dev/null +++ b/src/libs/greycstoration/greycstorationiface.h @@ -0,0 +1,89 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-12-03 + * Description : Greycstoration interface. + * + * Copyright (C) 2007-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef GREYCSTORATIONIFACE_H +#define GREYCSTORATIONIFACE_H + +// TQt includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "dimgthreadedfilter.h" +#include "greycstorationsettings.h" +#include "digikam_export.h" + +class TQObject; + +namespace Digikam +{ + +class GreycstorationIfacePriv; + +class DIGIKAM_EXPORT GreycstorationIface : public DImgThreadedFilter +{ + +public: + + enum MODE + { + Restore = 0, + InPainting, + Resize, + SimpleResize // Mode to resize image without to use Greycstoration algorithm. + }; + +public: + + GreycstorationIface(DImg *orgImage, + GreycstorationSettings settings, + int mode=Restore, + int newWidth=0, int newHeight=0, + const TQImage& inPaintingMask=TQImage(), + TQObject *parent=0); + + ~GreycstorationIface(); + + void stopComputation(); + +private: + + void initFilter(); + void filterImage(); + + void restoration(); + void inpainting(); + void resize(); + void simpleResize(); + void iterationLoop(uint iter); + +private: + + GreycstorationIfacePriv *d; +}; + +} // NameSpace Digikam + +#endif /* GREYCSTORATIONIFACE_H */ diff --git a/src/libs/greycstoration/greycstorationsettings.h b/src/libs/greycstoration/greycstorationsettings.h new file mode 100644 index 00000000..e2686965 --- /dev/null +++ b/src/libs/greycstoration/greycstorationsettings.h @@ -0,0 +1,144 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-21-07 + * Description : Greycstoration settings container. + * + * Copyright (C) 2007 by Gilles Caulier + * + * For a full settings description, look at this url : + * http://www.greyc.ensicaen.fr/~dtschump/greycstoration/guide.html + * + * For demonstration of settings, look at this url : + * + * http://www.greyc.ensicaen.fr/~dtschump/greycstoration/demonstration.html + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef GREYCSTORATIONSETTINGS_H +#define GREYCSTORATIONSETTINGS_H + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class DIGIKAM_EXPORT GreycstorationSettings +{ + +public: + + enum INTERPOLATION + { + NearestNeighbor = 0, + Linear, + RungeKutta + }; + +public: + + GreycstorationSettings() + { + setRestorationDefaultSettings(); + }; + + ~GreycstorationSettings(){}; + + void setRestorationDefaultSettings() + { + fastApprox = true; + + tile = 256; + btile = 4; + + nbIter = 1; + interp = NearestNeighbor; + + amplitude = 60.0; + sharpness = 0.7; + anisotropy = 0.3; + alpha = 0.6; + sigma = 1.1; + gaussPrec = 2.0; + dl = 0.8; + da = 30.0; + }; + + void setInpaintingDefaultSettings() + { + fastApprox = true; + + tile = 256; + btile = 4; + + nbIter = 30; + interp = NearestNeighbor; + + amplitude = 20.0; + sharpness = 0.3; + anisotropy = 1.0; + alpha = 0.8; + sigma = 2.0; + gaussPrec = 2.0; + dl = 0.8; + da = 30.0; + }; + + void setResizeDefaultSettings() + { + fastApprox = true; + + tile = 256; + btile = 4; + + nbIter = 3; + interp = NearestNeighbor; + + amplitude = 20.0; + sharpness = 0.2; + anisotropy = 0.9; + alpha = 0.1; + sigma = 1.5; + gaussPrec = 2.0; + dl = 0.8; + da = 30.0; + }; + +public: + + bool fastApprox; + + int tile; + int btile; + + uint nbIter; + uint interp; + + float amplitude; + float sharpness; + float anisotropy; + float alpha; + float sigma; + float gaussPrec; + float dl; + float da; +}; + +} // namespace Digikam + +#endif // GREYCSTORATIONSETTINGS_H diff --git a/src/libs/greycstoration/greycstorationwidget.cpp b/src/libs/greycstoration/greycstorationwidget.cpp new file mode 100644 index 00000000..93f25799 --- /dev/null +++ b/src/libs/greycstoration/greycstorationwidget.cpp @@ -0,0 +1,377 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-09-13 + * Description : Greycstoration settings widgets + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include + +// LibKDcraw includes. + +#include +#include + +// Local includes. + +#include "greycstorationwidget.h" +#include "greycstorationwidget.moc" + +using namespace KDcrawIface; + +namespace Digikam +{ + +class GreycstorationWidgetPriv +{ + +public: + + GreycstorationWidgetPriv() + { + parent = 0; + + advancedPage = 0; + alphaInput = 0; + alphaLabel = 0; + amplitudeInput = 0; + amplitudeLabel = 0; + anisotropyInput = 0; + anisotropyLabel = 0; + btileInput = 0; + btileLabel = 0; + daInput = 0; + daLabel = 0; + dlInput = 0; + dlLabel = 0; + fastApproxCBox = 0; + gaussianPrecInput = 0; + gaussianPrecLabel = 0; + generalPage = 0; + interpolationBox = 0; + interpolationLabel = 0; + iterationInput = 0; + iterationLabel = 0; + sharpnessInput = 0; + sharpnessLabel = 0; + sigmaInput = 0; + sigmaLabel = 0; + tileInput = 0; + tileLabel = 0; + } + + TQLabel *alphaLabel; + TQLabel *amplitudeLabel; + TQLabel *anisotropyLabel; + TQLabel *btileLabel; + TQLabel *daLabel; + TQLabel *dlLabel; + TQLabel *gaussianPrecLabel; + TQLabel *interpolationLabel; + TQLabel *iterationLabel; + TQLabel *sharpnessLabel; + TQLabel *sigmaLabel; + TQLabel *tileLabel; + + TQWidget *advancedPage; + TQWidget *generalPage; + + TQCheckBox *fastApproxCBox; + + TQTabWidget *parent; + + RComboBox *interpolationBox; + + RDoubleNumInput *alphaInput; + RDoubleNumInput *amplitudeInput; + RDoubleNumInput *anisotropyInput; + RDoubleNumInput *daInput; + RDoubleNumInput *dlInput; + RDoubleNumInput *gaussianPrecInput; + RDoubleNumInput *sharpnessInput; + RDoubleNumInput *sigmaInput; + + RIntNumInput *btileInput; + RIntNumInput *iterationInput; + RIntNumInput *tileInput; +}; + +GreycstorationWidget::GreycstorationWidget(TQTabWidget *parent) + : TQObject(parent) +{ + d = new GreycstorationWidgetPriv; + d->parent = parent; + + // ------------------------------------------------------------- + + d->generalPage = new TQWidget( parent ); + TQGridLayout* grid1 = new TQGridLayout(d->generalPage, 6, 2, KDialog::spacingHint()); + parent->addTab( d->generalPage, i18n("General") ); + + d->sharpnessLabel = new TQLabel(i18n("Detail preservation:"), d->generalPage); + d->sharpnessInput = new RDoubleNumInput(d->generalPage); + d->sharpnessInput->setPrecision(2); + d->sharpnessInput->setRange(0.01, 1.0, 0.1); + TQWhatsThis::add( d->sharpnessInput, i18n("

    Preservation of details to set the sharpening level " + "of the small features in the target image. " + "Higher values leave details sharp.")); + grid1->addMultiCellWidget(d->sharpnessLabel, 0, 0, 0, 0); + grid1->addMultiCellWidget(d->sharpnessInput, 0, 0, 1, 1); + + d->anisotropyLabel = new TQLabel(i18n("Anisotropy:"), d->generalPage); + d->anisotropyInput = new RDoubleNumInput(d->generalPage); + d->anisotropyInput->setPrecision(2); + d->anisotropyInput->setRange(0.0, 1.0, 0.1); + TQWhatsThis::add( d->anisotropyInput, i18n("

    Anisotropic (directional) modifier of the details. " + "Keep it small for Gaussian noise.")); + grid1->addMultiCellWidget(d->anisotropyLabel, 1, 1, 0, 0); + grid1->addMultiCellWidget(d->anisotropyInput, 1, 1, 1, 1); + + d->amplitudeLabel = new TQLabel(i18n("Smoothing:"), d->generalPage); + d->amplitudeInput = new RDoubleNumInput(d->generalPage); + d->amplitudeInput->setPrecision(2); + d->amplitudeInput->setRange(0.01, 500.0, 0.1); + TQWhatsThis::add( d->amplitudeInput, i18n("

    Total smoothing power: if the Detail Factor sets the relative " + "smoothing and the Anisotropy Factor the direction, " + "the Smoothing Factor sets the overall effect.")); + grid1->addMultiCellWidget(d->amplitudeLabel, 2, 2, 0, 0); + grid1->addMultiCellWidget(d->amplitudeInput, 2, 2, 1, 1); + + d->sigmaLabel = new TQLabel(i18n("Regularity:"), d->generalPage); + d->sigmaInput = new RDoubleNumInput(d->generalPage); + d->sigmaInput->setPrecision(2); + d->sigmaInput->setRange(0.0, 10.0, 0.1); + TQWhatsThis::add( d->sigmaInput, i18n("

    This value controls the evenness of smoothing to the image. " + "Do not use a high value here, or the " + "target image will be completely blurred.")); + grid1->addMultiCellWidget(d->sigmaLabel, 3, 3, 0, 0); + grid1->addMultiCellWidget(d->sigmaInput, 3, 3, 1, 1); + + d->iterationLabel = new TQLabel(i18n("Iterations:"), d->generalPage); + d->iterationInput = new RIntNumInput(d->generalPage); + d->iterationInput->setRange(1, 5000, 1); + TQWhatsThis::add( d->iterationInput, i18n("

    Sets the number of times the filter is applied to " + "the image.")); + grid1->addMultiCellWidget(d->iterationLabel, 4, 4, 0, 0); + grid1->addMultiCellWidget(d->iterationInput, 4, 4, 1, 1); + + d->alphaLabel = new TQLabel(i18n("Noise:"), d->generalPage); + d->alphaInput = new RDoubleNumInput(d->generalPage); + d->alphaInput->setPrecision(2); + d->alphaInput->setRange(0.01, 1.0, 0.1); + TQWhatsThis::add( d->alphaInput, i18n("

    Sets the noise scale.")); + grid1->addMultiCellWidget(d->alphaLabel, 5, 5, 0, 0); + grid1->addMultiCellWidget(d->alphaInput, 5, 5, 1, 1); + grid1->setRowStretch(6, 10); + + // ------------------------------------------------------------- + + d->advancedPage = new TQWidget( parent ); + TQGridLayout* grid2 = new TQGridLayout(d->advancedPage, 6, 2, KDialog::spacingHint()); + parent->addTab( d->advancedPage, i18n("Advanced Settings") ); + + d->daLabel = new TQLabel(i18n("Angular step:"), d->advancedPage); + d->daInput = new RDoubleNumInput(d->advancedPage); + d->daInput->setPrecision(2); + d->daInput->setRange(0.0, 90.0, 1.0); + TQWhatsThis::add( d->daInput, i18n("

    Set here the angular integration step (in degrees) " + "analogous to anisotropy.")); + grid2->addMultiCellWidget(d->daLabel, 0, 0, 0, 0); + grid2->addMultiCellWidget(d->daInput, 0, 0, 1, 1); + + d->dlLabel = new TQLabel(i18n("Integral step:"), d->advancedPage); + d->dlInput = new RDoubleNumInput(d->advancedPage); + d->dlInput->setPrecision(2); + d->dlInput->setRange(0.0, 1.0, 0.1); + TQWhatsThis::add( d->dlInput, i18n("

    Set here the spatial integral step.")); + grid2->addMultiCellWidget(d->dlLabel, 1, 1, 0, 0); + grid2->addMultiCellWidget(d->dlInput, 1, 1, 1, 1); + + d->gaussianPrecLabel = new TQLabel(i18n("Gaussian:"), d->advancedPage); + d->gaussianPrecInput = new RDoubleNumInput(d->advancedPage); + d->gaussianPrecInput->setPrecision(2); + d->gaussianPrecInput->setRange(0.01, 20.0, 0.01); + TQWhatsThis::add( d->gaussianPrecInput, i18n("

    Set here the precision of the Gaussian function.")); + grid2->addMultiCellWidget(d->gaussianPrecLabel, 2, 2, 0, 0); + grid2->addMultiCellWidget(d->gaussianPrecInput, 2, 2, 1, 1); + + d->tileLabel = new TQLabel(i18n("Tile size:"), d->advancedPage); + d->tileInput = new RIntNumInput(d->advancedPage); + d->tileInput->setRange(0, 2000, 1); + TQWhatsThis::add( d->tileInput, i18n("

    Sets the tile size.")); + grid2->addMultiCellWidget(d->tileLabel, 3, 3, 0, 0); + grid2->addMultiCellWidget(d->tileInput, 3, 3, 1, 1); + + d->btileLabel = new TQLabel(i18n("Tile border:"), d->advancedPage); + d->btileInput = new RIntNumInput(d->advancedPage); + d->btileInput->setRange(1, 20, 1); + TQWhatsThis::add( d->btileInput, i18n("

    Sets the size of each tile border.")); + grid2->addMultiCellWidget(d->btileLabel, 4, 4, 0, 0); + grid2->addMultiCellWidget(d->btileInput, 4, 4, 1, 1); + + d->interpolationLabel = new TQLabel(i18n("Interpolation:"), d->advancedPage); + d->interpolationBox = new RComboBox(d->advancedPage); + d->interpolationBox->insertItem( i18n("Nearest Neighbor"), GreycstorationSettings::NearestNeighbor ); + d->interpolationBox->insertItem( i18n("Linear"), GreycstorationSettings::Linear ); + d->interpolationBox->insertItem( i18n("Runge-Kutta"), GreycstorationSettings::RungeKutta); + TQWhatsThis::add( d->interpolationBox, i18n("

    Select the right interpolation method for the " + "desired image quality.")); + grid2->addMultiCellWidget(d->interpolationLabel, 5, 5, 0, 0); + grid2->addMultiCellWidget(d->interpolationBox, 5, 5, 1, 1); + + d->fastApproxCBox = new TQCheckBox(i18n("Fast approximation"), d->advancedPage); + TQWhatsThis::add( d->fastApproxCBox, i18n("

    Enable fast approximation when rendering images.")); + grid2->addMultiCellWidget(d->fastApproxCBox, 6, 6, 0, 1); +} + +GreycstorationWidget::~GreycstorationWidget() +{ + delete d; +} + +void GreycstorationWidget::setEnabled(bool b) +{ + d->generalPage->setEnabled(b); + d->advancedPage->setEnabled(b); + d->parent->setTabEnabled(d->generalPage, b); + d->parent->setTabEnabled(d->advancedPage, b); +} + +void GreycstorationWidget::setSettings(GreycstorationSettings settings) +{ + blockSignals(true); + d->alphaInput->setValue(settings.alpha); + d->amplitudeInput->setValue(settings.amplitude); + d->anisotropyInput->setValue(settings.anisotropy); + d->btileInput->setValue(settings.btile); + d->daInput->setValue(settings.da); + d->dlInput->setValue(settings.dl); + d->fastApproxCBox->setChecked(settings.fastApprox); + d->gaussianPrecInput->setValue(settings.gaussPrec); + d->interpolationBox->setCurrentItem(settings.interp); + d->iterationInput->setValue(settings.nbIter); + d->sharpnessInput->setValue(settings.sharpness); + d->sigmaInput->setValue(settings.sigma); + d->tileInput->setValue(settings.tile); + blockSignals(false); +} + +void GreycstorationWidget::setDefaultSettings(GreycstorationSettings settings) +{ + blockSignals(true); + d->alphaInput->setDefaultValue(settings.alpha); + d->amplitudeInput->setDefaultValue(settings.amplitude); + d->anisotropyInput->setDefaultValue(settings.anisotropy); + d->btileInput->setDefaultValue(settings.btile); + d->daInput->setDefaultValue(settings.da); + d->dlInput->setDefaultValue(settings.dl); + d->fastApproxCBox->setChecked(settings.fastApprox); + d->gaussianPrecInput->setDefaultValue(settings.gaussPrec); + d->interpolationBox->setDefaultItem(settings.interp); + d->iterationInput->setDefaultValue(settings.nbIter); + d->sharpnessInput->setDefaultValue(settings.sharpness); + d->sigmaInput->setDefaultValue(settings.sigma); + d->tileInput->setDefaultValue(settings.tile); + blockSignals(false); +} + +GreycstorationSettings GreycstorationWidget::getSettings() +{ + GreycstorationSettings settings; + + settings.fastApprox = d->fastApproxCBox->isChecked(); + settings.interp = d->interpolationBox->currentItem(); + settings.amplitude = d->amplitudeInput->value(); + settings.sharpness = d->sharpnessInput->value(); + settings.anisotropy = d->anisotropyInput->value(); + settings.alpha = d->alphaInput->value(); + settings.sigma = d->sigmaInput->value(); + settings.gaussPrec = d->gaussianPrecInput->value(); + settings.dl = d->dlInput->value(); + settings.da = d->daInput->value(); + settings.nbIter = d->iterationInput->value(); + settings.tile = d->tileInput->value(); + settings.btile = d->btileInput->value(); + + return settings; +} + +bool GreycstorationWidget::loadSettings(TQFile& file, const TQString& header) +{ + TQTextStream stream( &file ); + + if (stream.readLine() != header) + return false; + + blockSignals(true); + + GreycstorationSettings settings; + settings.fastApprox = stream.readLine().toInt(); + settings.interp = stream.readLine().toInt(); + settings.amplitude = stream.readLine().toDouble(); + settings.sharpness = stream.readLine().toDouble(); + settings.anisotropy = stream.readLine().toDouble(); + settings.alpha = stream.readLine().toDouble(); + settings.sigma = stream.readLine().toDouble(); + settings.gaussPrec = stream.readLine().toDouble(); + settings.dl = stream.readLine().toDouble(); + settings.da = stream.readLine().toDouble(); + settings.nbIter = stream.readLine().toInt(); + settings.tile = stream.readLine().toInt(); + settings.btile = stream.readLine().toInt(); + setSettings(settings); + + blockSignals(false); + return true; +} + +void GreycstorationWidget::saveSettings(TQFile& file, const TQString& header) +{ + GreycstorationSettings settings = getSettings(); + TQTextStream stream( &file ); + stream << header << "\n"; + stream << settings.fastApprox << "\n"; + stream << settings.interp << "\n"; + stream << settings.amplitude << "\n"; + stream << settings.sharpness << "\n"; + stream << settings.anisotropy << "\n"; + stream << settings.alpha << "\n"; + stream << settings.sigma << "\n"; + stream << settings.gaussPrec << "\n"; + stream << settings.dl << "\n"; + stream << settings.da << "\n"; + stream << settings.nbIter << "\n"; + stream << settings.tile << "\n"; + stream << settings.btile << "\n"; +} + +} // NameSpace Digikam diff --git a/src/libs/greycstoration/greycstorationwidget.h b/src/libs/greycstoration/greycstorationwidget.h new file mode 100644 index 00000000..22797071 --- /dev/null +++ b/src/libs/greycstoration/greycstorationwidget.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-09-13 + * Description : Greycstoration settings widgets + * + * Copyright (C) 2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef GREYCSTORATION_WIDGET_H +#define GREYCSTORATION_WIDGET_H + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "digikam_export.h" +#include "greycstorationsettings.h" + +class TQTabWidget; + +namespace Digikam +{ + +class GreycstorationWidgetPriv; + +class DIGIKAM_EXPORT GreycstorationWidget : public TQObject +{ + TQ_OBJECT + + +public: + + GreycstorationWidget(TQTabWidget *parent); + ~GreycstorationWidget(); + + void setSettings(GreycstorationSettings settings); + void setDefaultSettings(GreycstorationSettings settings); + GreycstorationSettings getSettings(); + + bool loadSettings(TQFile& file, const TQString& header); + void saveSettings(TQFile& file, const TQString& header); + + void setEnabled(bool); + +private: + + GreycstorationWidgetPriv* d; +}; + +} // NameSpace Digikam + +#endif /* GREYCSTORATION_WIDGET_H */ diff --git a/src/libs/histogram/Makefile.am b/src/libs/histogram/Makefile.am new file mode 100644 index 00000000..56ea96f6 --- /dev/null +++ b/src/libs/histogram/Makefile.am @@ -0,0 +1,16 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libhistogram.la + +libhistogram_la_SOURCES = imagehistogram.cpp + +libhistogram_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + +digikaminclude_HEADERS = imagehistogram.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/histogram/imagehistogram.cpp b/src/libs/histogram/imagehistogram.cpp new file mode 100644 index 00000000..bee55624 --- /dev/null +++ b/src/libs/histogram/imagehistogram.cpp @@ -0,0 +1,548 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-21 + * Description : image histogram manipulation methods. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * Some code parts are inspired from gimp 2.0 + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include +#include +#include + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "imagehistogram.h" + +namespace Digikam +{ + +class ImageHistogramPriv +{ + +public: + + // Using a structure instead a class is more fast + // (access with memset() and bytes manipulation). + + struct double_packet + { + double value; + double red; + double green; + double blue; + double alpha; + }; + +public: + + ImageHistogramPriv() + { + parent = 0; + imageData = 0; + histogram = 0; + runningFlag = true; + } + + /** The histogram data.*/ + struct double_packet *histogram; + + /** Image information.*/ + uchar *imageData; + uint imageWidth; + uint imageHeight; + + /** Numbers of histogram segments dependaing of image bytes depth*/ + int histoSegments; + + /** To post event from thread to parent.*/ + TQObject *parent; + + /** Used to stop thread during calculations.*/ + bool runningFlag; +}; + +ImageHistogram::ImageHistogram(const DImg& image, TQObject *parent) + : TQThread() +{ + setup(image.bits(), image.width(), image.height(), image.sixteenBit(), parent); +} + +ImageHistogram::ImageHistogram(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent) + : TQThread() +{ + setup(i_data, i_w, i_h, i_sixteenBits, parent); +} + +void ImageHistogram::setup(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent) +{ + d = new ImageHistogramPriv; + d->imageData = i_data; + d->imageWidth = i_w; + d->imageHeight = i_h; + d->parent = parent; + d->histoSegments = i_sixteenBits ? 65536 : 256; + + if (d->imageData && d->imageWidth && d->imageHeight) + { + if (d->parent) + start(); + else + calcHistogramValues(); + } + else + { + if (d->parent) + postProgress(false, false); + } +} + +ImageHistogram::~ImageHistogram() +{ + stopCalcHistogramValues(); + + if (d->histogram) + delete [] d->histogram; + + delete d; +} + +int ImageHistogram::getHistogramSegment(void) +{ + return d->histoSegments; +} + +void ImageHistogram::postProgress(bool starting, bool success) +{ + EventData *eventData = new EventData(); + eventData->starting = starting; + eventData->success = success; + eventData->histogram = this; + TQApplication::postEvent(d->parent, new TQCustomEvent(TQEvent::User, eventData)); +} + +void ImageHistogram::stopCalcHistogramValues(void) +{ + d->runningFlag = false; + wait(); +} + +// List of threaded operations. + +void ImageHistogram::run() +{ + calcHistogramValues(); +} + +void ImageHistogram::calcHistogramValues() +{ + uint i; + int max; + + if (d->parent) + postProgress(true, false); + + d->histogram = new ImageHistogramPriv::double_packet[d->histoSegments]; + memset(d->histogram, 0, d->histoSegments*sizeof(ImageHistogramPriv::double_packet)); + + if ( !d->histogram ) + { + DWarning() << ("HistogramWidget::calcHistogramValues: Unable to allocate memory!") << endl; + + if (d->parent) + postProgress(false, false); + + return; + } + + memset(d->histogram, 0, d->histoSegments*sizeof(struct ImageHistogramPriv::double_packet)); + + if (d->histoSegments == 65536) // 16 bits image. + { + unsigned short blue, green, red, alpha; + unsigned short *data = (unsigned short*)d->imageData; + + for (i = 0 ; (i < d->imageHeight*d->imageWidth*4) && d->runningFlag ; i+=4) + { + blue = data[ i ]; + green = data[i+1]; + red = data[i+2]; + alpha = data[i+3]; + + d->histogram[blue].blue++; + d->histogram[green].green++; + d->histogram[red].red++; + d->histogram[alpha].alpha++; + + max = (blue > green) ? blue : green; + + if (red > max) + d->histogram[red].value++; + else + d->histogram[max].value++; + } + } + else // 8 bits images. + { + uchar blue, green, red, alpha; + uchar *data = d->imageData; + + for (i = 0 ; (i < d->imageHeight*d->imageWidth*4) && d->runningFlag ; i+=4) + { + blue = data[ i ]; + green = data[i+1]; + red = data[i+2]; + alpha = data[i+3]; + + d->histogram[blue].blue++; + d->histogram[green].green++; + d->histogram[red].red++; + d->histogram[alpha].alpha++; + + max = (blue > green) ? blue : green; + + if (red > max) + d->histogram[red].value++; + else + d->histogram[max].value++; + } + } + + if (d->parent && d->runningFlag) + postProgress(false, true); +} + +double ImageHistogram::getCount(int channel, int start, int end) +{ + int i; + double count = 0.0; + + if ( !d->histogram || start < 0 || + end > d->histoSegments-1 || start > end ) + return 0.0; + + switch(channel) + { + case ImageHistogram::ValueChannel: + for (i = start ; i <= end ; i++) + count += d->histogram[i].value; + break; + + case ImageHistogram::RedChannel: + for (i = start ; i <= end ; i++) + count += d->histogram[i].red; + break; + + case ImageHistogram::GreenChannel: + for (i = start ; i <= end ; i++) + count += d->histogram[i].green; + break; + + case ImageHistogram::BlueChannel: + for (i = start ; i <= end ; i++) + count += d->histogram[i].blue; + break; + + case ImageHistogram::AlphaChannel: + for (i = start ; i <= end ; i++) + count += d->histogram[i].alpha; + break; + + default: + return 0.0; + break; + } + + return count; +} + +double ImageHistogram::getPixels() +{ + if ( !d->histogram ) + return 0.0; + + return(d->imageWidth * d->imageHeight); +} + +double ImageHistogram::getMean(int channel, int start, int end) +{ + int i; + double mean = 0.0; + double count; + + if ( !d->histogram || start < 0 || + end > d->histoSegments-1 || start > end ) + return 0.0; + + switch(channel) + { + case ImageHistogram::ValueChannel: + for (i = start ; i <= end ; i++) + mean += i * d->histogram[i].value; + break; + + case ImageHistogram::RedChannel: + for (i = start ; i <= end ; i++) + mean += i * d->histogram[i].red; + break; + + case ImageHistogram::GreenChannel: + for (i = start ; i <= end ; i++) + mean += i * d->histogram[i].green; + break; + + case ImageHistogram::BlueChannel: + for (i = start ; i <= end ; i++) + mean += i * d->histogram[i].blue; + break; + + case ImageHistogram::AlphaChannel: + for (i = start ; i <= end ; i++) + mean += i * d->histogram[i].alpha; + break; + + default: + return 0.0; + break; + } + + count = getCount(channel, start, end); + + if (count > 0.0) + return mean / count; + + return mean; +} + +int ImageHistogram::getMedian(int channel, int start, int end) +{ + int i; + double sum = 0.0; + double count; + + if ( !d->histogram || start < 0 || + end > d->histoSegments-1 || start > end ) + return 0; + + count = getCount(channel, start, end); + + switch(channel) + { + case ImageHistogram::ValueChannel: + for (i = start ; i <= end ; i++) + { + sum += d->histogram[i].value; + if (sum * 2 > count) return i; + } + break; + + case ImageHistogram::RedChannel: + for (i = start ; i <= end ; i++) + { + sum += d->histogram[i].red; + if (sum * 2 > count) return i; + } + break; + + case ImageHistogram::GreenChannel: + for (i = start ; i <= end ; i++) + { + sum += d->histogram[i].green; + if (sum * 2 > count) return i; + } + break; + + case ImageHistogram::BlueChannel: + for (i = start ; i <= end ; i++) + { + sum += d->histogram[i].blue; + if (sum * 2 > count) return i; + } + break; + + case ImageHistogram::AlphaChannel: + for (i = start ; i <= end ; i++) + { + sum += d->histogram[i].alpha; + if (sum * 2 > count) return i; + } + break; + + default: + return 0; + break; + } + + return -1; +} + +double ImageHistogram::getStdDev(int channel, int start, int end) +{ + int i; + double dev = 0.0; + double count; + double mean; + + if ( !d->histogram || start < 0 || + end > d->histoSegments-1 || start > end ) + return 0.0; + + mean = getMean(channel, start, end); + count = getCount(channel, start, end); + + if (count == 0.0) + count = 1.0; + + switch(channel) + { + case ImageHistogram::ValueChannel: + for (i = start ; i <= end ; i++) + dev += (i - mean) * (i - mean) * d->histogram[i].value; + break; + + case ImageHistogram::RedChannel: + for (i = start ; i <= end ; i++) + dev += (i - mean) * (i - mean) * d->histogram[i].red; + break; + + case ImageHistogram::GreenChannel: + for (i = start ; i <= end ; i++) + dev += (i - mean) * (i - mean) * d->histogram[i].green; + break; + + case ImageHistogram::BlueChannel: + for (i = start ; i <= end ; i++) + dev += (i - mean) * (i - mean) * d->histogram[i].blue; + break; + + case ImageHistogram::AlphaChannel: + for (i = start ; i <= end ; i++) + dev += (i - mean) * (i - mean) * d->histogram[i].alpha; + break; + + default: + return 0.0; + break; + } + + return sqrt(dev / count); +} + +double ImageHistogram::getValue(int channel, int bin) +{ + double value; + + if ( !d->histogram || bin < 0 || bin > d->histoSegments-1 ) + return 0.0; + + switch(channel) + { + case ImageHistogram::ValueChannel: + value = d->histogram[bin].value; + break; + + case ImageHistogram::RedChannel: + value = d->histogram[bin].red; + break; + + case ImageHistogram::GreenChannel: + value = d->histogram[bin].green; + break; + + case ImageHistogram::BlueChannel: + value = d->histogram[bin].blue; + break; + + case ImageHistogram::AlphaChannel: + value = d->histogram[bin].alpha; + break; + + default: + return 0.0; + break; + } + + return value; +} + +double ImageHistogram::getMaximum(int channel) +{ + double max = 0.0; + int x; + + if ( !d->histogram ) + return 0.0; + + switch(channel) + { + case ImageHistogram::ValueChannel: + for (x = 0 ; x < d->histoSegments ; x++) + if (d->histogram[x].value > max) + max = d->histogram[x].value; + break; + + case ImageHistogram::RedChannel: + for (x = 0 ; x < d->histoSegments ; x++) + if (d->histogram[x].red > max) + max = d->histogram[x].red; + break; + + case ImageHistogram::GreenChannel: + for (x = 0 ; x < d->histoSegments ; x++) + if (d->histogram[x].green > max) + max = d->histogram[x].green; + break; + + case ImageHistogram::BlueChannel: + for (x = 0 ; x < d->histoSegments ; x++) + if (d->histogram[x].blue > max) + max = d->histogram[x].blue; + break; + + case ImageHistogram::AlphaChannel: + for (x = 0 ; x < d->histoSegments ; x++) + if (d->histogram[x].alpha > max) + max = d->histogram[x].alpha; + break; + + default: + return 0.0; + break; + } + + return max; +} + +} // NameSpace Digikam + diff --git a/src/libs/histogram/imagehistogram.h b/src/libs/histogram/imagehistogram.h new file mode 100644 index 00000000..6beb4919 --- /dev/null +++ b/src/libs/histogram/imagehistogram.h @@ -0,0 +1,113 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-21 + * Description : image histogram manipulation methods. + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + + +#ifndef IMAGEHISTOGRAM_H +#define IMAGEHISTOGRAM_H + +// TQt includes. + +#include + +// Local includes. + +#include "digikam_export.h" + +class TQObject; + +namespace Digikam +{ + +class ImageHistogramPriv; +class DImg; + +class DIGIKAM_EXPORT ImageHistogram : public TQThread +{ + +public: + +enum HistogramChannelType +{ + ValueChannel = 0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel +}; + +class EventData +{ +public: + + EventData() + { + starting = false; + success = false; + histogram = 0; + } + + bool starting; + bool success; + ImageHistogram *histogram; +}; + +public: + + ImageHistogram(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent=0); + + ImageHistogram(const DImg& image, TQObject *parent=0); + ~ImageHistogram(); + + void setup(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent); + + /** Method to stop threaded computations.*/ + void stopCalcHistogramValues(void); + + /** Methods for to manipulate the histogram data.*/ + double getCount(int channel, int start, int end); + double getMean(int channel, int start, int end); + double getPixels(); + double getStdDev(int channel, int start, int end); + double getValue(int channel, int bin); + double getMaximum(int channel); + + int getHistogramSegment(void); + int getMedian(int channel, int start, int end); + +private: + + ImageHistogramPriv* d; + +private: + + void calcHistogramValues(); + void postProgress(bool starting, bool success); + +protected: + + virtual void run(); +}; + +} // NameSpace Digikam + +#endif /* IMAGEHISTOGRAM_H */ diff --git a/src/libs/imageproperties/Makefile.am b/src/libs/imageproperties/Makefile.am new file mode 100644 index 00000000..2c4f021a --- /dev/null +++ b/src/libs/imageproperties/Makefile.am @@ -0,0 +1,51 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libimagepropertiesshowfoto.la libimagepropertiesdigikam.la \ + libimagepropertiescamgui.la + +# Image Properties SideBar for Camera GUI. + +libimagepropertiescamgui_la_SOURCES = imagepropertiessidebarcamgui.cpp cameraitempropertiestab.cpp + +libimagepropertiescamgui_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +# Image Properties SideBar for Showfoto (without digiKam database support). + +libimagepropertiesshowfoto_la_SOURCES = imagepropertiessidebar.cpp navigatebarwidget.cpp \ + imagepropertiesmetadatatab.cpp imagepropertiescolorstab.cpp \ + imagepropertiestab.cpp navigatebartab.cpp + +libimagepropertiesshowfoto_la_LIBADD = $(top_builddir)/src/libs/widgets/libwidgets.la \ + $(top_builddir)/src/libs/dmetadata/libdmetadata.la \ + $(top_builddir)/src/libs/dimg/libdimg.la \ + $(top_builddir)/src/libs/threadimageio/libthreadimageio.la \ + $(top_builddir)/src/libs/histogram/libhistogram.la + +libimagepropertiesshowfoto_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +# Image Properties SideBar for digiKam Main interface and Image Editor (digiKam database support). + +libimagepropertiesdigikam_la_SOURCES = imagedescedittab.cpp imagepropertiessidebar.cpp \ + imagepropertiessidebardb.cpp \ + talbumlistview.cpp imagepropertiesmetadatatab.cpp \ + imagepropertiescolorstab.cpp \ + navigatebarwidget.cpp imagepropertiestab.cpp navigatebartab.cpp + +libimagepropertiesdigikam_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/libs/themeengine \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/widgets/common \ + -I$(top_srcdir)/src/libs/widgets/iccprofiles \ + -I$(top_srcdir)/src/libs/widgets/metadata \ + -I$(top_srcdir)/src/libs/dialogs \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/threadimageio \ + -I$(top_srcdir)/src/utilities/cameragui \ + -I$(top_srcdir)/src/utilities/batch \ + -I$(top_srcdir)/src/digikam \ + $(LIBKEXIV2_CFLAGS) \ + $(LIBKDCRAW_CFLAGS) \ + $(all_includes) + diff --git a/src/libs/imageproperties/cameraitempropertiestab.cpp b/src/libs/imageproperties/cameraitempropertiestab.cpp new file mode 100644 index 00000000..8593e9c7 --- /dev/null +++ b/src/libs/imageproperties/cameraitempropertiestab.cpp @@ -0,0 +1,555 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-08 + * Description : A tab to display camera item information + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "gpiteminfo.h" +#include "navigatebarwidget.h" +#include "cameraitempropertiestab.h" +#include "cameraitempropertiestab.moc" + +namespace Digikam +{ + +class CameraItemPropertiesTabPriv +{ +public: + + CameraItemPropertiesTabPriv() + { + title = 0; + file = 0; + folder = 0; + date = 0; + size = 0; + isReadable = 0; + isWritable = 0; + mime = 0; + dimensions = 0; + newFileName = 0; + downloaded = 0; + settingsArea = 0; + title2 = 0; + make = 0; + model = 0; + photoDate = 0; + aperture = 0; + focalLength = 0; + exposureTime = 0; + sensitivity = 0; + exposureMode = 0; + flash = 0; + whiteBalance = 0; + labelFile = 0; + labelFolder = 0; + labelFileIsReadable = 0; + labelFileIsWritable = 0; + labelFileDate = 0; + labelFileSize = 0; + labelImageMime = 0; + labelImageDimensions = 0; + labelNewFileName = 0; + labelAlreadyDownloaded = 0; + labelPhotoMake = 0; + labelPhotoModel = 0; + labelPhotoDateTime = 0; + labelPhotoAperture = 0; + labelPhotoFocalLength = 0; + labelPhotoExposureTime = 0; + labelPhotoSensitivity = 0; + labelPhotoExposureMode = 0; + labelPhotoFlash = 0; + labelPhotoWhiteBalance = 0; + } + + TQLabel *title; + TQLabel *file; + TQLabel *folder; + TQLabel *date; + TQLabel *size; + TQLabel *isReadable; + TQLabel *isWritable; + TQLabel *mime; + TQLabel *dimensions; + TQLabel *newFileName; + TQLabel *downloaded; + + TQLabel *title2; + TQLabel *make; + TQLabel *model; + TQLabel *photoDate; + TQLabel *aperture; + TQLabel *focalLength; + TQLabel *exposureTime; + TQLabel *sensitivity; + TQLabel *exposureMode; + TQLabel *flash; + TQLabel *whiteBalance; + + TQFrame *settingsArea; + + KSqueezedTextLabel *labelFile; + KSqueezedTextLabel *labelFolder; + KSqueezedTextLabel *labelFileIsReadable; + KSqueezedTextLabel *labelFileIsWritable; + KSqueezedTextLabel *labelFileDate; + KSqueezedTextLabel *labelFileSize; + KSqueezedTextLabel *labelImageMime; + KSqueezedTextLabel *labelImageDimensions; + KSqueezedTextLabel *labelNewFileName; + KSqueezedTextLabel *labelAlreadyDownloaded; + + KSqueezedTextLabel *labelPhotoMake; + KSqueezedTextLabel *labelPhotoModel; + KSqueezedTextLabel *labelPhotoDateTime; + KSqueezedTextLabel *labelPhotoAperture; + KSqueezedTextLabel *labelPhotoFocalLength; + KSqueezedTextLabel *labelPhotoExposureTime; + KSqueezedTextLabel *labelPhotoSensitivity; + KSqueezedTextLabel *labelPhotoExposureMode; + KSqueezedTextLabel *labelPhotoFlash; + KSqueezedTextLabel *labelPhotoWhiteBalance; +}; + +CameraItemPropertiesTab::CameraItemPropertiesTab(TQWidget* parent, bool navBar) + : NavigateBarTab(parent) +{ + d = new CameraItemPropertiesTabPriv; + + setupNavigateBar(navBar); + + TQScrollView *sv = new TQScrollView(this); + sv->viewport()->setBackgroundMode(TQt::PaletteBackground); + sv->setResizePolicy(TQScrollView::AutoOneFit); + sv->setFrameStyle(TQFrame::NoFrame); + + d->settingsArea = new TQFrame(sv->viewport()); + d->settingsArea->setFrameStyle( TQFrame::StyledPanel | TQFrame::Sunken ); + d->settingsArea->setLineWidth( style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this) ); + + sv->addChild(d->settingsArea); + m_navigateBarLayout->addWidget(sv); + + // -------------------------------------------------- + + TQGridLayout *settingsLayout = new TQGridLayout(d->settingsArea, 27, 1, KDialog::spacingHint(), 0); + + d->title = new TQLabel(i18n("Camera File Properties"), d->settingsArea); + d->file = new TQLabel(i18n("File:"), d->settingsArea); + d->folder = new TQLabel(i18n("Folder:"), d->settingsArea); + d->date = new TQLabel(i18n("Date:"), d->settingsArea); + d->size = new TQLabel(i18n("Size:"), d->settingsArea); + d->isReadable = new TQLabel(i18n("Readable:"), d->settingsArea); + d->isWritable = new TQLabel(i18n("Writable:"), d->settingsArea); + d->mime = new TQLabel(i18n("Type:"), d->settingsArea); + d->dimensions = new TQLabel(i18n("Dimensions:"), d->settingsArea); + d->newFileName = new TQLabel(i18n("New Name:"), d->settingsArea); + d->downloaded = new TQLabel(i18n("Downloaded:"), d->settingsArea); + + KSeparator *line = new KSeparator(TQt::Horizontal, d->settingsArea); + d->title2 = new TQLabel(i18n("Photograph Properties"), d->settingsArea); + d->make = new TQLabel(i18n("Make:"), d->settingsArea); + d->model = new TQLabel(i18n("Model:"), d->settingsArea); + d->photoDate = new TQLabel(i18n("Created:"), d->settingsArea); + d->aperture = new TQLabel(i18n("Aperture:"), d->settingsArea); + d->focalLength = new TQLabel(i18n("Focal:"), d->settingsArea); + d->exposureTime = new TQLabel(i18n("Exposure:"), d->settingsArea); + d->sensitivity = new TQLabel(i18n("Sensitivity:"), d->settingsArea); + d->exposureMode = new TQLabel(i18n("Mode/Program:"), d->settingsArea); + d->flash = new TQLabel(i18n("Flash:"), d->settingsArea); + d->whiteBalance = new TQLabel(i18n("White balance:"), d->settingsArea); + + d->labelFile = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFolder = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileDate = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileSize = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileIsReadable = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileIsWritable = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageMime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageDimensions = new KSqueezedTextLabel(0, d->settingsArea); + d->labelNewFileName = new KSqueezedTextLabel(0, d->settingsArea); + d->labelAlreadyDownloaded = new KSqueezedTextLabel(0, d->settingsArea); + + d->labelPhotoMake = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoModel = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoDateTime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoAperture = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoFocalLength = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoExposureTime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoSensitivity = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoExposureMode = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoFlash = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoWhiteBalance = new KSqueezedTextLabel(0, d->settingsArea); + + int hgt = fontMetrics().height()-2; + d->title->setAlignment(TQt::AlignCenter); + d->file->setMaximumHeight(hgt); + d->folder->setMaximumHeight(hgt); + d->date->setMaximumHeight(hgt); + d->size->setMaximumHeight(hgt); + d->isReadable->setMaximumHeight(hgt); + d->isWritable->setMaximumHeight(hgt); + d->mime->setMaximumHeight(hgt); + d->dimensions->setMaximumHeight(hgt); + d->newFileName->setMaximumHeight(hgt); + d->downloaded->setMaximumHeight(hgt); + d->labelFile->setMaximumHeight(hgt); + d->labelFolder->setMaximumHeight(hgt); + d->labelFileDate->setMaximumHeight(hgt); + d->labelFileSize->setMaximumHeight(hgt); + d->labelFileIsReadable->setMaximumHeight(hgt); + d->labelFileIsWritable->setMaximumHeight(hgt); + d->labelImageMime->setMaximumHeight(hgt); + d->labelImageDimensions->setMaximumHeight(hgt); + d->labelNewFileName->setMaximumHeight(hgt); + d->labelAlreadyDownloaded->setMaximumHeight(hgt); + + d->title2->setAlignment(TQt::AlignCenter); + d->make->setMaximumHeight(hgt); + d->model->setMaximumHeight(hgt); + d->photoDate->setMaximumHeight(hgt); + d->aperture->setMaximumHeight(hgt); + d->focalLength->setMaximumHeight(hgt); + d->exposureTime->setMaximumHeight(hgt); + d->sensitivity->setMaximumHeight(hgt); + d->exposureMode->setMaximumHeight(hgt); + d->flash->setMaximumHeight(hgt); + d->whiteBalance->setMaximumHeight(hgt); + d->labelPhotoMake->setMaximumHeight(hgt); + d->labelPhotoModel->setMaximumHeight(hgt); + d->labelPhotoDateTime->setMaximumHeight(hgt); + d->labelPhotoAperture->setMaximumHeight(hgt); + d->labelPhotoFocalLength->setMaximumHeight(hgt); + d->labelPhotoExposureTime->setMaximumHeight(hgt); + d->labelPhotoSensitivity->setMaximumHeight(hgt); + d->labelPhotoExposureMode->setMaximumHeight(hgt); + d->labelPhotoFlash->setMaximumHeight(hgt); + d->labelPhotoWhiteBalance->setMaximumHeight(hgt); + + // -------------------------------------------------- + + settingsLayout->addMultiCellWidget(d->title, 0, 0, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 1, 1, 0, 1); + settingsLayout->addMultiCellWidget(d->file, 2, 2, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFile, 2, 2, 1, 1); + settingsLayout->addMultiCellWidget(d->folder, 3, 3, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFolder, 3, 3, 1, 1); + settingsLayout->addMultiCellWidget(d->date, 4, 4, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileDate, 4, 4, 1, 1); + settingsLayout->addMultiCellWidget(d->size, 5, 5, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileSize, 5, 5, 1, 1); + settingsLayout->addMultiCellWidget(d->isReadable, 6, 6, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileIsReadable, 6, 6, 1, 1); + settingsLayout->addMultiCellWidget(d->isWritable, 7, 7, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileIsWritable, 7, 7, 1, 1); + settingsLayout->addMultiCellWidget(d->mime, 8, 8, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageMime, 8, 8, 1, 1); + settingsLayout->addMultiCellWidget(d->dimensions, 9, 9, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageDimensions, 9, 9, 1, 1); + settingsLayout->addMultiCellWidget(d->newFileName, 10, 10, 0, 0); + settingsLayout->addMultiCellWidget(d->labelNewFileName, 10, 10, 1, 1); + settingsLayout->addMultiCellWidget(d->downloaded, 11, 11, 0, 0); + settingsLayout->addMultiCellWidget(d->labelAlreadyDownloaded, 11, 11, 1, 1); + + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 12, 12, 0, 1); + settingsLayout->addMultiCellWidget(line, 13, 13, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 14, 14, 0, 1); + + settingsLayout->addMultiCellWidget(d->title2, 15, 15, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 16, 16, 0, 1); + settingsLayout->addMultiCellWidget(d->make, 17, 17, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoMake, 17, 17, 1, 1); + settingsLayout->addMultiCellWidget(d->model, 18, 18, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoModel, 18, 18, 1, 1); + settingsLayout->addMultiCellWidget(d->photoDate, 19, 19, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoDateTime, 19, 19, 1, 1); + settingsLayout->addMultiCellWidget(d->aperture, 20, 20, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoAperture, 20, 20, 1, 1); + settingsLayout->addMultiCellWidget(d->focalLength, 21, 21, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoFocalLength, 21, 21, 1, 1); + settingsLayout->addMultiCellWidget(d->exposureTime, 22, 22, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoExposureTime, 22, 22, 1, 1); + settingsLayout->addMultiCellWidget(d->sensitivity, 23, 23, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoSensitivity, 23, 23, 1, 1); + settingsLayout->addMultiCellWidget(d->exposureMode, 24, 24, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoExposureMode, 24, 24, 1, 1); + settingsLayout->addMultiCellWidget(d->flash, 25, 25, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoFlash, 25, 25, 1, 1); + settingsLayout->addMultiCellWidget(d->whiteBalance, 26, 26, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoWhiteBalance, 26, 26, 1, 1); + settingsLayout->setRowStretch(27, 10); + settingsLayout->setColStretch(1, 10); +} + +CameraItemPropertiesTab::~CameraItemPropertiesTab() +{ + delete d; +} + +void CameraItemPropertiesTab::setCurrentItem(const GPItemInfo* itemInfo, + const TQString &newFileName, const TQByteArray& exifData, + const KURL ¤tURL) +{ + if (!itemInfo) + { + d->labelFile->setText(TQString()); + d->labelFolder->setText(TQString()); + d->labelFileIsReadable->setText(TQString()); + d->labelFileIsWritable->setText(TQString()); + d->labelFileDate->setText(TQString()); + d->labelFileSize->setText(TQString()); + d->labelImageMime->setText(TQString()); + d->labelImageDimensions->setText(TQString()); + d->labelNewFileName->setText(TQString()); + d->labelAlreadyDownloaded->setText(TQString()); + + d->labelPhotoMake->setText(TQString()); + d->labelPhotoModel->setText(TQString()); + d->labelPhotoDateTime->setText(TQString()); + d->labelPhotoAperture->setText(TQString()); + d->labelPhotoFocalLength->setText(TQString()); + d->labelPhotoExposureTime->setText(TQString()); + d->labelPhotoSensitivity->setText(TQString()); + d->labelPhotoExposureMode->setText(TQString()); + d->labelPhotoFlash->setText(TQString()); + d->labelPhotoWhiteBalance->setText(TQString()); + + setEnabled(false); + return; + } + + setEnabled(true); + + TQString str; + TQString unknown(i18n("unknown")); + + // -- Camera file system information ------------------------------------------ + + d->labelFile->setText(itemInfo->name); + d->labelFolder->setText(itemInfo->folder); + + if (itemInfo->readPermissions < 0) + str = unknown; + else if (itemInfo->readPermissions == 0) + str = i18n("No"); + else + str = i18n("Yes"); + + d->labelFileIsReadable->setText(str); + + if (itemInfo->writePermissions < 0) + str = unknown; + else if (itemInfo->writePermissions == 0) + str = i18n("No"); + else + str = i18n("Yes"); + + d->labelFileIsWritable->setText(str); + + TQDateTime date; + date.setTime_t(itemInfo->mtime); + d->labelFileDate->setText(TDEGlobal::locale()->formatDateTime(date, true, true)); + + str = i18n("%1 (%2)").arg(TDEIO::convertSize(itemInfo->size)) + .arg(TDEGlobal::locale()->formatNumber(itemInfo->size, 0)); + d->labelFileSize->setText(str); + + // -- Image Properties -------------------------------------------------- + + d->labelImageMime->setText( (itemInfo->mime == TQString("image/x-raw")) ? + i18n("RAW Image") : KMimeType::mimeType(itemInfo->mime)->comment() ); + + TQString mpixels; + TQSize dims; + if (itemInfo->width == -1 && itemInfo->height == -1 && !currentURL.isEmpty()) + { + // delayed loading to list faster from UMSCamera + if (itemInfo->mime == TQString("image/x-raw")) + { + DMetadata metaData(currentURL.path()); + dims = metaData.getImageDimensions(); + } + else + { + KFileMetaInfo meta(currentURL.path()); + if (meta.isValid()) + { + if (meta.containsGroup("Jpeg EXIF Data")) + dims = meta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + else if (meta.containsGroup("General")) + dims = meta.group("General").item("Dimensions").value().toSize(); + else if (meta.containsGroup("Technical")) + dims = meta.group("Technical").item("Dimensions").value().toSize(); + } + } + } + else + { + // if available (GPCamera), take dimensions directly from itemInfo + dims = TQSize(itemInfo->width, itemInfo->height); + } + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + str = (!dims.isValid()) ? unknown : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + d->labelImageDimensions->setText(str); + + // -- Download information ------------------------------------------ + + d->labelNewFileName->setText(newFileName.isEmpty() ? i18n("unchanged") : newFileName); + + if (itemInfo->downloaded == GPItemInfo::DownloadUnknow) + str = unknown; + else if (itemInfo->downloaded == GPItemInfo::DownloadedYes) + str = i18n("Yes"); + else + str = i18n("No"); + + d->labelAlreadyDownloaded->setText(str); + + // -- Photograph information ------------------------------------------ + // NOTA: If something is changed here, please updated albumfiletip section too. + + TQString unavailable(i18n("unavailable")); + DMetadata metaData; + metaData.setExif(exifData); + PhotoInfoContainer photoInfo = metaData.getPhotographInformations(); + + if (photoInfo.isEmpty()) + { + d->title2->hide(); + d->make->hide(); + d->model->hide(); + d->photoDate->hide(); + d->aperture->hide(); + d->focalLength->hide(); + d->exposureTime->hide(); + d->sensitivity->hide(); + d->exposureMode->hide(); + d->flash->hide(); + d->whiteBalance->hide(); + d->labelPhotoMake->hide(); + d->labelPhotoModel->hide(); + d->labelPhotoDateTime->hide(); + d->labelPhotoAperture->hide(); + d->labelPhotoFocalLength->hide(); + d->labelPhotoExposureTime->hide(); + d->labelPhotoSensitivity->hide(); + d->labelPhotoExposureMode->hide(); + d->labelPhotoFlash->hide(); + d->labelPhotoWhiteBalance->hide(); + } + else + { + d->title2->show(); + d->make->show(); + d->model->show(); + d->photoDate->show(); + d->aperture->show(); + d->focalLength->show(); + d->exposureTime->show(); + d->sensitivity->show(); + d->exposureMode->show(); + d->flash->show(); + d->whiteBalance->show(); + d->labelPhotoMake->show(); + d->labelPhotoModel->show(); + d->labelPhotoDateTime->show(); + d->labelPhotoAperture->show(); + d->labelPhotoFocalLength->show(); + d->labelPhotoExposureTime->show(); + d->labelPhotoSensitivity->show(); + d->labelPhotoExposureMode->show(); + d->labelPhotoFlash->show(); + d->labelPhotoWhiteBalance->show(); + } + + d->labelPhotoMake->setText(photoInfo.make.isEmpty() ? unavailable : photoInfo.make); + d->labelPhotoModel->setText(photoInfo.model.isEmpty() ? unavailable : photoInfo.model); + + if (photoInfo.dateTime.isValid()) + { + str = TDEGlobal::locale()->formatDateTime(photoInfo.dateTime, true, true); + d->labelPhotoDateTime->setText(str); + } + else + d->labelPhotoDateTime->setText(unavailable); + + d->labelPhotoAperture->setText(photoInfo.aperture.isEmpty() ? unavailable : photoInfo.aperture); + + if (photoInfo.focalLength35mm.isEmpty()) + d->labelPhotoFocalLength->setText(photoInfo.focalLength.isEmpty() ? unavailable : photoInfo.focalLength); + else + { + str = i18n("%1 (35mm: %2)").arg(photoInfo.focalLength).arg(photoInfo.focalLength35mm); + d->labelPhotoFocalLength->setText(str); + } + + d->labelPhotoExposureTime->setText(photoInfo.exposureTime.isEmpty() ? unavailable : photoInfo.exposureTime); + d->labelPhotoSensitivity->setText(photoInfo.sensitivity.isEmpty() ? unavailable : i18n("%1 ISO").arg(photoInfo.sensitivity)); + + if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(unavailable); + else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(photoInfo.exposureMode); + else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(photoInfo.exposureProgram); + else + { + str = TQString("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram); + d->labelPhotoExposureMode->setText(str); + } + + d->labelPhotoFlash->setText(photoInfo.flash.isEmpty() ? unavailable : photoInfo.flash); + d->labelPhotoWhiteBalance->setText(photoInfo.whiteBalance.isEmpty() ? unavailable : photoInfo.whiteBalance); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/cameraitempropertiestab.h b/src/libs/imageproperties/cameraitempropertiestab.h new file mode 100644 index 00000000..badf4a70 --- /dev/null +++ b/src/libs/imageproperties/cameraitempropertiestab.h @@ -0,0 +1,69 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-08 + * Description : A tab to display camera item information + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef CAMERAITEMPROPERTIESTAB_H +#define CAMERAITEMPROPERTIESTAB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" +#include "navigatebartab.h" + +namespace Digikam +{ + +class GPItemInfo; +class CameraItemPropertiesTabPriv; + +class DIGIKAM_EXPORT CameraItemPropertiesTab : public NavigateBarTab +{ + TQ_OBJECT + + +public: + + CameraItemPropertiesTab(TQWidget* parent, bool navBar=true); + ~CameraItemPropertiesTab(); + + void setCurrentItem(const GPItemInfo* itemInfo=0, + const TQString &newFileName=TQString(), + const TQByteArray& exifData=TQByteArray(), + const KURL ¤tURL = KURL()); + +private: + + CameraItemPropertiesTabPriv* d; +}; + +} // NameSpace Digikam + +#endif /* CAMERAITEMPROPERTIESTAB_H */ diff --git a/src/libs/imageproperties/imagedescedittab.cpp b/src/libs/imageproperties/imagedescedittab.cpp new file mode 100644 index 00000000..f79b1d7c --- /dev/null +++ b/src/libs/imageproperties/imagedescedittab.cpp @@ -0,0 +1,1763 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-03-09 + * Description : Captions, Tags, and Rating properties editor + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2003-2009 by Gilles Caulier + * Copyright (C) 2006-2009 by Marcel Wiesweg + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "kdatetimeedit.h" +#include "albumiconitem.h" +#include "albumdb.h" +#include "album.h" +#include "albumsettings.h" +#include "albumlister.h" +#include "albumthumbnailloader.h" +#include "tageditdlg.h" +#include "navigatebarwidget.h" +#include "ratingwidget.h" +#include "talbumlistview.h" +#include "tagfilterview.h" +#include "imageinfo.h" +#include "imageattributeswatch.h" +#include "metadatahub.h" +#include "statusprogressbar.h" +#include "searchtextbar.h" +#include "imagedescedittab.h" +#include "imagedescedittab.moc" + +namespace Digikam +{ + +class ImageDescEditTabPriv +{ + +public: + + ImageDescEditTabPriv() + { + modified = false; + ignoreImageAttributesWatch = false; + recentTagsBtn = 0; + commentsEdit = 0; + tagsSearchBar = 0; + dateTimeEdit = 0; + tagsView = 0; + ratingWidget = 0; + ABCMenu = 0; + assignedTagsBtn = 0; + applyBtn = 0; + revertBtn = 0; + newTagEdit = 0; + toggleAutoTags = TagFilterView::NoToggleAuto; + } + + bool modified; + bool ignoreImageAttributesWatch; + + TQToolButton *recentTagsBtn; + TQToolButton *assignedTagsBtn; + TQToolButton *revertBtn; + + TQPopupMenu *ABCMenu; + + TQPushButton *applyBtn; + + TQPushButton *moreButton; + TQPopupMenu *moreMenu; + + KTextEdit *commentsEdit; + + KDateTimeEdit *dateTimeEdit; + + SearchTextBar *tagsSearchBar; + SearchTextBar *newTagEdit; + + TQPtrList currInfos; + + TAlbumListView *tagsView; + + RatingWidget *ratingWidget; + + TagFilterView::ToggleAutoTags toggleAutoTags; + + MetadataHub hub; +}; + +ImageDescEditTab::ImageDescEditTab(TQWidget *parent, bool navBar) + : NavigateBarTab(parent) +{ + d = new ImageDescEditTabPriv; + + setupNavigateBar(navBar); + + TQScrollView *sv = new TQScrollView(this); + sv->viewport()->setBackgroundMode(TQt::PaletteBackground); + sv->setResizePolicy(TQScrollView::AutoOneFit); + sv->setFrameStyle(TQFrame::NoFrame); + + TQWidget *settingsArea = new TQWidget(sv->viewport()); + sv->addChild(settingsArea); + m_navigateBarLayout->addWidget(sv); + + TQGridLayout *settingsLayout = new TQGridLayout(settingsArea, 6, 1, + KDialog::spacingHint(), KDialog::spacingHint()); + + // Captions/Date/Rating view ----------------------------------- + + TQVBox *commentsBox = new TQVBox(settingsArea); + new TQLabel(i18n("Caption:"), commentsBox); + d->commentsEdit = new KTextEdit(commentsBox); + d->commentsEdit->setTextFormat(TQTextEdit::PlainText); + d->commentsEdit->setCheckSpellingEnabled(true); + d->commentsEdit->setFixedHeight(100); + + TQHBox *dateBox = new TQHBox(settingsArea); + new TQLabel(i18n("Date:"), dateBox); + d->dateTimeEdit = new KDateTimeEdit(dateBox, "datepicker"); + + TQHBox *ratingBox = new TQHBox(settingsArea); + new TQLabel(i18n("Rating:"), ratingBox); + d->ratingWidget = new RatingWidget(ratingBox); + + // Tags view --------------------------------------------------- + + d->newTagEdit = new SearchTextBar(settingsArea, "ImageDescEditTabNewTagEdit", i18n("Enter new tag here...")); + TQWhatsThis::add(d->newTagEdit, i18n("Enter here the text used to create new tags. " + "'/' can be used here to create a hierarchy of tags. " + "',' can be used here to create more than one hierarchy at the same time.")); + + d->tagsView = new TAlbumListView(settingsArea); + + TQHBox *tagsSearch = new TQHBox(settingsArea); + tagsSearch->setSpacing(KDialog::spacingHint()); + + d->tagsSearchBar = new SearchTextBar(tagsSearch, "ImageDescEditTabTagsSearchBar"); + + d->assignedTagsBtn = new TQToolButton(tagsSearch); + TQToolTip::add(d->assignedTagsBtn, i18n("Tags already assigned")); + d->assignedTagsBtn->setIconSet(kapp->iconLoader()->loadIcon("tag-assigned", + TDEIcon::NoGroup, TDEIcon::SizeSmall, + TDEIcon::DefaultState, 0, true)); + d->assignedTagsBtn->setToggleButton(true); + + d->recentTagsBtn = new TQToolButton(tagsSearch); + TQPopupMenu *popupMenu = new TQPopupMenu(d->recentTagsBtn); + TQToolTip::add(d->recentTagsBtn, i18n("Recent Tags")); + d->recentTagsBtn->setIconSet(kapp->iconLoader()->loadIcon("tag-recents", + TDEIcon::NoGroup, TDEIcon::SizeSmall, + TDEIcon::DefaultState, 0, true)); + d->recentTagsBtn->setUsesBigPixmap(false); + d->recentTagsBtn->setPopup(popupMenu); + d->recentTagsBtn->setPopupDelay(1); + + // Buttons ----------------------------------------- + + TQHBox *buttonsBox = new TQHBox(settingsArea); + buttonsBox->setSpacing(KDialog::spacingHint()); + + d->revertBtn = new TQToolButton(buttonsBox); + d->revertBtn->setIconSet(SmallIcon("reload_page")); + TQToolTip::add(d->revertBtn, i18n("Revert all changes")); + d->revertBtn->setEnabled(false); + + d->applyBtn = new TQPushButton(i18n("Apply"), buttonsBox); + d->applyBtn->setIconSet(SmallIcon("button_ok")); + d->applyBtn->setEnabled(false); + TQToolTip::add(d->applyBtn, i18n("Apply all changes to images")); + buttonsBox->setStretchFactor(d->applyBtn, 10); + + d->moreButton = new TQPushButton(i18n("More"), buttonsBox); + d->moreMenu = new TQPopupMenu(this); + d->moreButton->setPopup(d->moreMenu); + + // -------------------------------------------------- + + settingsLayout->addMultiCellWidget(commentsBox, 0, 0, 0, 1); + settingsLayout->addMultiCellWidget(dateBox, 1, 1, 0, 1); + settingsLayout->addMultiCellWidget(ratingBox, 2, 2, 0, 1); + settingsLayout->addMultiCellWidget(d->newTagEdit, 3, 3, 0, 1); + settingsLayout->addMultiCellWidget(d->tagsView, 4, 4, 0, 1); + settingsLayout->addMultiCellWidget(tagsSearch, 5, 5, 0, 1); + settingsLayout->addMultiCellWidget(buttonsBox, 6, 6, 0, 1); + settingsLayout->setRowStretch(4, 10); + + // -------------------------------------------------- + + connect(d->tagsView, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + this, TQ_SIGNAL(signalProgressBarMode(int, const TQString&))); + + connect(d->tagsView, TQ_SIGNAL(signalProgressValue(int)), + this, TQ_SIGNAL(signalProgressValue(int))); + + connect(popupMenu, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotRecentTagsMenuActivated(int))); + + connect(d->tagsView, TQ_SIGNAL(signalItemStateChanged(TAlbumCheckListItem *)), + this, TQ_SLOT(slotItemStateChanged(TAlbumCheckListItem *))); + + connect(d->commentsEdit, TQ_SIGNAL(textChanged()), + this, TQ_SLOT(slotCommentChanged())); + + connect(d->dateTimeEdit, TQ_SIGNAL(dateTimeChanged(const TQDateTime& )), + this, TQ_SLOT(slotDateTimeChanged(const TQDateTime&))); + + connect(d->ratingWidget, TQ_SIGNAL(signalRatingChanged(int)), + this, TQ_SLOT(slotRatingChanged(int))); + + connect(d->tagsView, TQ_SIGNAL(rightButtonClicked(TQListViewItem*, const TQPoint &, int)), + this, TQ_SLOT(slotRightButtonClicked(TQListViewItem*, const TQPoint&, int))); + + connect(d->tagsSearchBar, TQ_SIGNAL(signalTextChanged(const TQString&)), + this, TQ_SLOT(slotTagsSearchChanged(const TQString&))); + + connect(this, TQ_SIGNAL(signalTagFilterMatch(bool)), + d->tagsSearchBar, TQ_SLOT(slotSearchResult(bool))); + + connect(d->assignedTagsBtn, TQ_SIGNAL(toggled(bool)), + this, TQ_SLOT(slotAssignedTagsToggled(bool))); + + connect(d->newTagEdit->lineEdit(), TQ_SIGNAL(returnPressed(const TQString&)), + this, TQ_SLOT(slotCreateNewTag())); + + connect(d->applyBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotApplyAllChanges())); + + connect(d->revertBtn, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotRevertAllChanges())); + + connect(d->moreMenu, TQ_SIGNAL(aboutToShow()), + this, TQ_SLOT(slotMoreMenu())); + + // Initialize --------------------------------------------- + + d->commentsEdit->installEventFilter(this); + d->dateTimeEdit->installEventFilter(this); + d->ratingWidget->installEventFilter(this); + d->tagsView->installEventFilter(this); + updateRecentTags(); + + // Connect to album manager ----------------------------- + + AlbumManager* man = AlbumManager::instance(); + + connect(man, TQ_SIGNAL(signalAlbumAdded(Album*)), + this, TQ_SLOT(slotAlbumAdded(Album*))); + + connect(man, TQ_SIGNAL(signalAlbumDeleted(Album*)), + this, TQ_SLOT(slotAlbumDeleted(Album*))); + + connect(man, TQ_SIGNAL(signalAlbumRenamed(Album*)), + this, TQ_SLOT(slotAlbumRenamed(Album*))); + + connect(man, TQ_SIGNAL(signalAlbumsCleared()), + this, TQ_SLOT(slotAlbumsCleared())); + + connect(man, TQ_SIGNAL(signalAlbumIconChanged(Album*)), + this, TQ_SLOT(slotAlbumIconChanged(Album*))); + + connect(man, TQ_SIGNAL(signalTAlbumMoved(TAlbum*, TAlbum*)), + this, TQ_SLOT(slotAlbumMoved(TAlbum*, TAlbum*))); + + // Connect to thumbnail loader ----------------------------- + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + + connect(loader, TQ_SIGNAL(signalThumbnail(Album *, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&))); + + connect(loader, TQ_SIGNAL(signalFailed(Album *)), + this, TQ_SLOT(slotThumbnailLost(Album *))); + + connect(loader, TQ_SIGNAL(signalReloadThumbnails()), + this, TQ_SLOT(slotReloadThumbnails())); + + // Connect to attribute watch ------------------------------ + + ImageAttributesWatch *watch = ImageAttributesWatch::instance(); + + connect(watch, TQ_SIGNAL(signalImageTagsChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageTagsChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImagesChanged(int)), + this, TQ_SLOT(slotImagesChanged(int))); + + connect(watch, TQ_SIGNAL(signalImageRatingChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageRatingChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImageDateChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageDateChanged(TQ_LLONG))); + + connect(watch, TQ_SIGNAL(signalImageCaptionChanged(TQ_LLONG)), + this, TQ_SLOT(slotImageCaptionChanged(TQ_LLONG))); + + // -- read config --------------------------------------------------------- + + TDEConfig* config = kapp->config(); + config->setGroup("Tag List View"); + d->toggleAutoTags = (TagFilterView::ToggleAutoTags)(config->readNumEntry("Toggle Auto Tags", + TagFilterView::NoToggleAuto)); +} + +ImageDescEditTab::~ImageDescEditTab() +{ + slotChangingItems(); + + /* + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + (*it)->removeExtraData(this); + } + */ + + TDEConfig* config = kapp->config(); + config->setGroup("Tag List View"); + config->writeEntry("Toggle Auto Tags", (int)(d->toggleAutoTags)); + config->sync(); + + delete d; +} + +bool ImageDescEditTab::singleSelection() const +{ + return (d->currInfos.count() == 1); +} + +void ImageDescEditTab::slotChangingItems() +{ + if (!d->modified) + return; + + if (d->currInfos.isEmpty()) + return; + + if (!AlbumSettings::instance()->getApplySidebarChangesDirectly()) + { + KDialogBase *dialog = new KDialogBase(i18n("Apply changes?"), + KDialogBase::Yes | KDialogBase::No, + KDialogBase::Yes, KDialogBase::No, + this, "applyChanges", + true, true, + KStdGuiItem::yes(), KStdGuiItem::discard()); + + int changedFields = 0; + if (d->hub.commentChanged()) + changedFields++; + if (d->hub.dateTimeChanged()) + changedFields++; + if (d->hub.ratingChanged()) + changedFields++; + if (d->hub.tagsChanged()) + changedFields++; + + TQString text; + if (changedFields == 1) + { + if (d->hub.commentChanged()) + text = i18n("

    You have edited the comment of the image. ", + "

    You have edited the comment of %n images. ", + d->currInfos.count()); + else if (d->hub.dateTimeChanged()) + text = i18n("

    You have edited the date of the image. ", + "

    You have edited the date of %n images. ", + d->currInfos.count()); + else if (d->hub.ratingChanged()) + text = i18n("

    You have edited the rating of the image. ", + "

    You have edited the rating of %n images. ", + d->currInfos.count()); + else if (d->hub.tagsChanged()) + text = i18n("

    You have edited the tags of the image. ", + "

    You have edited the tags of %n images. ", + d->currInfos.count()); + + text += i18n("Do you want to apply your changes?

    "); + } + else + { + text = i18n("

    You have edited the metadata of the image:

      ", + "

      You have edited the metadata of %n images:

        ", + d->currInfos.count()); + + if (d->hub.commentChanged()) + text += i18n("
      • comment
      • "); + if (d->hub.dateTimeChanged()) + text += i18n("
      • date
      • "); + if (d->hub.ratingChanged()) + text += i18n("
      • rating
      • "); + if (d->hub.tagsChanged()) + text += i18n("
      • tags
      • "); + + text += "

      "; + + text += i18n("Do you want to apply your changes?

      "); + } + + bool alwaysApply = false; + int returnCode = KMessageBox::createKMessageBox + (dialog, TQMessageBox::Information, + text, TQStringList(), + i18n("Always apply changes without confirmation"), + &alwaysApply, KMessageBox::Notify); + + if (alwaysApply) + AlbumSettings::instance()->setApplySidebarChangesDirectly(true); + + if (returnCode == KDialogBase::User1) + return; + // otherwise apply + } + + slotApplyAllChanges(); +} + +void ImageDescEditTab::slotApplyAllChanges() +{ + if (!d->modified) + return; + + if (d->currInfos.isEmpty()) + return; + + bool progressInfo = (d->currInfos.count() > 1); + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Applying changes to images. Please wait...")); + MetadataWriteSettings writeSettings = MetadataHub::defaultWriteSettings(); + + // debugging - use this to indicate reentry from event loop (kapp->processEvents) + // remove before final release + if (d->ignoreImageAttributesWatch) + { + DWarning() << "ImageDescEditTab::slotApplyAllChanges(): re-entering from event loop!" << endl; + } + + // we are now changing attributes ourselves + d->ignoreImageAttributesWatch = true; + AlbumLister::instance()->blockSignals(true); + AlbumManager::instance()->albumDB()->beginTransaction(); + int i=0; + for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next()) + { + // apply to database + d->hub.write(info); + // apply to file metadata + d->hub.write(info->filePath(), MetadataHub::FullWrite, writeSettings); + + emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0)); + if (progressInfo) + kapp->processEvents(); + } + AlbumLister::instance()->blockSignals(false); + AlbumManager::instance()->albumDB()->commitTransaction(); + + d->ignoreImageAttributesWatch = false; + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + + d->modified = false; + d->hub.resetChanged(); + d->applyBtn->setEnabled(false); + d->revertBtn->setEnabled(false); + + updateRecentTags(); + updateTagsView(); +} + +void ImageDescEditTab::slotRevertAllChanges() +{ + if (!d->modified) + return; + + if (d->currInfos.isEmpty()) + return; + + setInfos(d->currInfos); +} + +void ImageDescEditTab::setItem(ImageInfo *info) +{ + slotChangingItems(); + TQPtrList list; + if (info) + list.append(info); + setInfos(list); +} + +void ImageDescEditTab::setItems(TQPtrList infos) +{ + slotChangingItems(); + setInfos(infos); +} + +void ImageDescEditTab::setInfos(TQPtrList infos) +{ + if (infos.isEmpty()) + { + d->hub = MetadataHub(); + d->commentsEdit->blockSignals(true); + d->commentsEdit->clear(); + d->commentsEdit->blockSignals(false); + d->currInfos.clear(); + setEnabled(false); + return; + } + + setEnabled(true); + d->currInfos = infos; + d->modified = false; + d->hub = MetadataHub(); + d->applyBtn->setEnabled(false); + d->revertBtn->setEnabled(false); + + for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next()) + { + d->hub.load(info); + } + + updateComments(); + updateRating(); + updateDate(); + updateTagsView(); +} + +void ImageDescEditTab::slotReadFromFileMetadataToDatabase() +{ + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Reading metadata from files. Please wait...")); + + d->ignoreImageAttributesWatch = true; + AlbumManager::instance()->albumDB()->beginTransaction(); + int i=0; + for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next()) + { + // A batch operation: a hub for each single file, not the common hub + MetadataHub fileHub(MetadataHub::NewTagsImport); + // read in from DMetadata + fileHub.load(info->filePath()); + // write out to database + fileHub.write(info); + + emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0)); + kapp->processEvents(); + } + AlbumManager::instance()->albumDB()->commitTransaction(); + d->ignoreImageAttributesWatch = false; + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + + // reload everything + setInfos(d->currInfos); +} + +void ImageDescEditTab::slotWriteToFileMetadataFromDatabase() +{ + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Writing metadata to files. Please wait...")); + MetadataWriteSettings writeSettings = MetadataHub::defaultWriteSettings(); + + int i=0; + for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next()) + { + MetadataHub fileHub; + // read in from database + fileHub.load(info); + // write out to file DMetadata + fileHub.write(info->filePath()); + + emit signalProgressValue((int)((i++/(float)d->currInfos.count())*100.0)); + kapp->processEvents(); + } + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); +} + +bool ImageDescEditTab::eventFilter(TQObject *, TQEvent *e) +{ + if ( e->type() == TQEvent::KeyPress ) + { + TQKeyEvent *k = (TQKeyEvent *)e; + if (k->state() == TQt::ControlButton && + (k->key() == TQt::Key_Enter || k->key() == TQt::Key_Return)) + { + emit signalNextItem(); + return true; + } + else if (k->state() == TQt::ShiftButton && + (k->key() == TQt::Key_Enter || k->key() == TQt::Key_Return)) + { + emit signalPrevItem(); + return true; + } + + return false; + } + + return false; +} + +void ImageDescEditTab::populateTags() +{ + d->tagsView->clear(); + + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum *tag = (TAlbum*)(*it); + slotAlbumAdded(tag); + } + + d->tagsView->loadViewState(); +} + +void ImageDescEditTab::slotItemStateChanged(TAlbumCheckListItem *item) +{ + TagFilterView::ToggleAutoTags oldAutoTags = d->toggleAutoTags; + + switch(d->toggleAutoTags) + { + case TagFilterView::Children: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item->album(), item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + case TagFilterView::Parents: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(item->album(), item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + case TagFilterView::ChildrenAndParents: + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(item->album(), item->isOn()); + toggleParentTags(item->album(), item->isOn()); + d->toggleAutoTags = oldAutoTags; + break; + default: + break; + } + + d->hub.setTag(item->album(), item->isOn()); + + d->tagsView->blockSignals(true); + item->setStatus(d->hub.tagStatus(item->album())); + d->tagsView->blockSignals(false); + + slotModified(); +} + +void ImageDescEditTab::slotCommentChanged() +{ + // we cannot trust that the text actually changed + // (there are bogus signals caused by spell checking, see bug 141663) + // so we have to check before marking the metadata as modified + if (d->hub.comment() == d->commentsEdit->text()) + return; + + d->hub.setComment(d->commentsEdit->text()); + setMetadataWidgetStatus(d->hub.commentStatus(), d->commentsEdit); + slotModified(); +} + +void ImageDescEditTab::slotDateTimeChanged(const TQDateTime& dateTime) +{ + d->hub.setDateTime(dateTime); + setMetadataWidgetStatus(d->hub.dateTimeStatus(), d->dateTimeEdit); + slotModified(); +} + +void ImageDescEditTab::slotRatingChanged(int rating) +{ + d->hub.setRating(rating); + // no handling for MetadataDisjoint needed for rating, + // we set it to 0 when disjoint, see below + slotModified(); +} + +void ImageDescEditTab::slotModified() +{ + d->modified = true; + d->applyBtn->setEnabled(true); + d->revertBtn->setEnabled(true); +} + +void ImageDescEditTab::assignRating(int rating) +{ + d->ratingWidget->setRating(rating); +} + +void ImageDescEditTab::updateTagsView() +{ + d->tagsView->blockSignals(true); + + TQListViewItemIterator it( d->tagsView); + while (it.current()) + { + TAlbumCheckListItem* tItem = dynamic_cast(it.current()); + if (tItem) + tItem->setStatus(d->hub.tagStatus(tItem->album())); + ++it; + } + + // The condition is a temporary fix not to destroy name filtering on image change. + // See comments in these methods. + if (d->assignedTagsBtn->isOn()) + slotAssignedTagsToggled(d->assignedTagsBtn->isOn()); + + d->tagsView->blockSignals(false); +} + +void ImageDescEditTab::updateComments() +{ + d->commentsEdit->blockSignals(true); + d->commentsEdit->setText(d->hub.comment()); + setMetadataWidgetStatus(d->hub.commentStatus(), d->commentsEdit); + d->commentsEdit->blockSignals(false); +} + +void ImageDescEditTab::updateRating() +{ + d->ratingWidget->blockSignals(true); + if (d->hub.ratingStatus() == MetadataHub::MetadataDisjoint) + d->ratingWidget->setRating(0); + else + d->ratingWidget->setRating(d->hub.rating()); + d->ratingWidget->blockSignals(false); +} + +void ImageDescEditTab::updateDate() +{ + d->dateTimeEdit->blockSignals(true); + d->dateTimeEdit->setDateTime(d->hub.dateTime()); + setMetadataWidgetStatus(d->hub.dateTimeStatus(), d->dateTimeEdit); + d->dateTimeEdit->blockSignals(false); +} + +void ImageDescEditTab::setMetadataWidgetStatus(int status, TQWidget *widget) +{ + if (status == MetadataHub::MetadataDisjoint) + { + // For text widgets: Set text color to color of disabled text + TQPalette palette = widget->palette(); + palette.setColor(TQColorGroup::Text, palette.color(TQPalette::Disabled, TQColorGroup::Text)); + widget->setPalette(palette); + } + else + { + widget->unsetPalette(); + } +} + +void ImageDescEditTab::slotRightButtonClicked(TQListViewItem *item, const TQPoint &, int ) +{ + TAlbum *album; + + if (!item) + { + album = AlbumManager::instance()->findTAlbum(0); + } + else + { + TAlbumCheckListItem* viewItem = dynamic_cast(item); + + if(!viewItem) + album = AlbumManager::instance()->findTAlbum(0); + else + album = viewItem->album(); + } + + if(!album) + return; + + d->ABCMenu = new TQPopupMenu; + + connect(d->ABCMenu, TQ_SIGNAL( aboutToShow() ), + this, TQ_SLOT( slotABCContextMenu() )); + + TDEPopupMenu popmenu(this); + popmenu.insertTitle(SmallIcon("digikam"), i18n("Tags")); + popmenu.insertItem(SmallIcon("tag-new"), i18n("New Tag..."), 10); + popmenu.insertItem(SmallIcon("tag-addressbook"), i18n("Create Tag From AddressBook"), d->ABCMenu); + + if (!album->isRoot()) + { + popmenu.insertItem(SmallIcon("tag-properties"), i18n("Edit Tag Properties..."), 11); + popmenu.insertItem(SmallIcon("tag-reset"), i18n("Reset Tag Icon"), 13); + popmenu.insertSeparator(-1); + popmenu.insertItem(SmallIcon("tag-delete"), i18n("Delete Tag"), 12); + } + + popmenu.insertSeparator(-1); + + TQPopupMenu selectTagsMenu; + selectTagsMenu.insertItem(i18n("All Tags"), 14); + if (!album->isRoot()) + { + selectTagsMenu.insertSeparator(-1); + selectTagsMenu.insertItem(i18n("Children"), 17); + selectTagsMenu.insertItem(i18n("Parents"), 19); + } + popmenu.insertItem(i18n("Select"), &selectTagsMenu); + + TQPopupMenu deselectTagsMenu; + deselectTagsMenu.insertItem(i18n("All Tags"), 15); + if (!album->isRoot()) + { + deselectTagsMenu.insertSeparator(-1); + deselectTagsMenu.insertItem(i18n("Children"), 18); + deselectTagsMenu.insertItem(i18n("Parents"), 20); + } + popmenu.insertItem(i18n("Deselect"), &deselectTagsMenu); + + popmenu.insertItem(i18n("Invert Selection"), 16); + popmenu.insertSeparator(-1); + + TQPopupMenu toggleAutoMenu; + toggleAutoMenu.setCheckable(true); + toggleAutoMenu.insertItem(i18n("None"), 21); + toggleAutoMenu.insertSeparator(-1); + toggleAutoMenu.insertItem(i18n("Children"), 22); + toggleAutoMenu.insertItem(i18n("Parents"), 23); + toggleAutoMenu.insertItem(i18n("Both"), 24); + toggleAutoMenu.setItemChecked(21 + d->toggleAutoTags, true); + popmenu.insertItem(i18n("Toggle Auto"), &toggleAutoMenu); + + TagFilterView::ToggleAutoTags oldAutoTags = d->toggleAutoTags; + + int choice = popmenu.exec((TQCursor::pos())); + switch( choice ) + { + case 10: // New Tag. + { + tagNew(album); + break; + } + case 11: // Edit Tag Properties. + { + if (!album->isRoot()) + tagEdit(album); + break; + } + case 12: // Delete Tag. + { + if (!album->isRoot()) + tagDelete(album); + break; + } + case 13: // Reset Tag Icon. + { + TQString errMsg; + AlbumManager::instance()->updateTAlbumIcon(album, TQString("tag"), 0, errMsg); + break; + } + case 14: // Select All Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(d->tagsView, TQListViewItemIterator::NotChecked); + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + if (item->isVisible()) + item->setOn(true); + ++it; + } + d->toggleAutoTags = oldAutoTags; + break; + } + case 15: // Deselect All Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(d->tagsView, TQListViewItemIterator::Checked); + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + if (item->isVisible()) + item->setOn(false); + ++it; + } + d->toggleAutoTags = oldAutoTags; + break; + } + case 16: // Invert All Tags Selection. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + TQListViewItemIterator it(d->tagsView); + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + if (item->isVisible()) + item->setOn(!item->isOn()); + ++it; + } + d->toggleAutoTags = oldAutoTags; + break; + } + case 17: // Select Child Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(album, true); + TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + item->setOn(true); + d->toggleAutoTags = oldAutoTags; + break; + } + case 18: // Deselect Child Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleChildTags(album, false); + TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + item->setOn(false); + d->toggleAutoTags = oldAutoTags; + break; + } + case 19: // Select Parent Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(album, true); + TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + item->setOn(true); + d->toggleAutoTags = oldAutoTags; + break; + } + case 20: // Deselect Parent Tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + toggleParentTags(album, false); + TAlbumCheckListItem *item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + item->setOn(false); + d->toggleAutoTags = oldAutoTags; + break; + } + case 21: // No toggle auto tags. + { + d->toggleAutoTags = TagFilterView::NoToggleAuto; + break; + } + case 22: // Toggle auto Children tags. + { + d->toggleAutoTags = TagFilterView::Children; + break; + } + case 23: // Toggle auto Parents tags. + { + d->toggleAutoTags = TagFilterView::Parents; + break; + } + case 24: // Toggle auto Children and Parents tags. + { + d->toggleAutoTags = TagFilterView::ChildrenAndParents; + break; + } + default: + break; + } + + if ( choice > 100 ) + { + tagNew(album, d->ABCMenu->text( choice ), "tag-people" ); + } + + delete d->ABCMenu; + d->ABCMenu = 0; +} + +void ImageDescEditTab::slotABCContextMenu() +{ + d->ABCMenu->clear(); + + int counter = 100; + TDEABC::AddressBook* ab = TDEABC::StdAddressBook::self(); + TQStringList names; + for ( TDEABC::AddressBook::Iterator it = ab->begin(); it != ab->end(); ++it ) + { + names.push_back(it->formattedName()); + } + + qHeapSort(names); + + for ( TQStringList::Iterator it = names.begin(); it != names.end(); ++it ) + { + TQString name = *it; + if ( !name.isNull() ) + d->ABCMenu->insertItem( name, ++counter ); + } + + if (counter == 100) + { + d->ABCMenu->insertItem( i18n("No AddressBook Entries Found"), ++counter ); + d->ABCMenu->setItemEnabled( counter, false ); + } +} + +void ImageDescEditTab::slotMoreMenu() +{ + d->moreMenu->clear(); + + if (singleSelection()) + { + d->moreMenu->insertItem(i18n("Read metadata from file to database"), this, TQ_SLOT(slotReadFromFileMetadataToDatabase())); + int writeActionId = d->moreMenu->insertItem(i18n("Write metadata to each file"), this, TQ_SLOT(slotWriteToFileMetadataFromDatabase())); + // we do not need a "Write to file" action here because the apply button will do just that + // if selection is a single file. + // Adding the option will confuse users: Does the apply button not write to file? + // Removing the option will confuse users: There is not option to write to file! (not visible in single selection) + // Disabling will confuse users: Why is it disabled? + d->moreMenu->setItemEnabled(writeActionId, false); + } + else + { + // We need to make clear that this action is different from the Apply button, + // which saves the same changes to all files. These batch operations operate on each single file. + d->moreMenu->insertItem(i18n("Read metadata from each file to database"), this, TQ_SLOT(slotReadFromFileMetadataToDatabase())); + d->moreMenu->insertItem(i18n("Write metadata to each file"), this, TQ_SLOT(slotWriteToFileMetadataFromDatabase())); + } +} + +void ImageDescEditTab::tagNew(TAlbum* parAlbum, const TQString& _title, const TQString& _icon) const +{ + if (!parAlbum) + return; + + TQString title = _title; + TQString icon = _icon; + + if (title.isNull()) + { + if (!TagEditDlg::tagCreate(kapp->activeWindow(), parAlbum, title, icon)) + return; + } + + TQMap errMap; + AlbumList tList = TagEditDlg::createTAlbum(parAlbum, title, icon, errMap); + TagEditDlg::showtagsListCreationError(kapp->activeWindow(), errMap); + + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbumCheckListItem* item = (TAlbumCheckListItem*)(*it)->extraData(d->tagsView); + if (item) + { + item->setOn(true); + d->tagsView->setSelected(item, true); + d->tagsView->ensureItemVisible(item); + } + } +} + +void ImageDescEditTab::tagDelete(TAlbum *album) +{ + if (!album || album->isRoot()) + return; + + AlbumManager *albumMan = AlbumManager::instance(); + + if (album == albumMan->currentAlbum() || + album->isAncestorOf(albumMan->currentAlbum())) + { + KMessageBox::error(this, i18n("You are currently viewing items in the " + "tag '%1' that you are about to delete. " + "You will need to apply change first " + "if you want to delete the tag." ) + .arg(album->title())); + return; + } + + // find number of subtags + int children = 0; + AlbumIterator iter(album); + while(iter.current()) + { + children++; + ++iter; + } + + if(children) + { + int result = KMessageBox::warningContinueCancel(this, + i18n("Tag '%1' has one subtag. " + "Deleting this will also delete " + "the subtag. " + "Do you want to continue?", + "Tag '%1' has %n subtags. " + "Deleting this will also delete " + "the subtags. " + "Do you want to continue?", + children).arg(album->title())); + + if(result != KMessageBox::Continue) + return; + } + + TQString message; + LLongList assignedItems = albumMan->albumDB()->getItemIDsInTag(album->id()); + if (!assignedItems.isEmpty()) + { + message = i18n("Tag '%1' is assigned to one item. " + "Do you want to continue?", + "Tag '%1' is assigned to %n items. " + "Do you want to continue?", + assignedItems.count()).arg(album->title()); + } + else + { + message = i18n("Delete '%1' tag?").arg(album->title()); + } + + int result = KMessageBox::warningContinueCancel(this, message, + i18n("Delete Tag"), + KGuiItem(i18n("Delete"), + "edit-delete")); + + if (result == KMessageBox::Continue) + { + TQString errMsg; + if (!albumMan->deleteTAlbum(album, errMsg)) + KMessageBox::error(this, errMsg); + } +} + +void ImageDescEditTab::tagEdit(TAlbum* album) +{ + if (!album || album->isRoot()) + return; + + TQString title; + TQString icon; + + if (!TagEditDlg::tagEdit(kapp->activeWindow(), album, title, icon)) + return; + + AlbumManager *albumMan = AlbumManager::instance(); + if (album->title() != title) + { + TQString errMsg; + if (!albumMan->renameTAlbum(album, title, errMsg)) + { + KMessageBox::error(this, errMsg); + return; + } + } + + if (album->icon() != icon) + { + TQString errMsg; + if (!albumMan->updateTAlbumIcon(album, icon, 0, errMsg)) + { + KMessageBox::error(this, errMsg); + } + } +} + +void ImageDescEditTab::slotAlbumAdded(Album* a) +{ + if (!a || a->type() != Album::TAG) + return; + + TAlbumCheckListItem* viewItem = 0; + + TAlbum* tag = dynamic_cast(a); + if (!tag) + return; + + if (tag->isRoot()) + { + viewItem = new TAlbumCheckListItem(d->tagsView, tag); + } + else + { + TAlbumCheckListItem* parent = (TAlbumCheckListItem*)(tag->parent()->extraData(d->tagsView)); + if (!parent) + { + DWarning() << k_funcinfo << "Failed to find parent for Tag " << tag->title() + << endl; + return; + } + + viewItem = new TAlbumCheckListItem(parent, tag); + d->tagsSearchBar->lineEdit()->completionObject()->addItem(tag->title()); + d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath()); + d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath().remove(0, 1)); // without root "/" + } + + if (viewItem) + { + // commenting this out due to the issues described in bug 148166. + // viewItem->setOpen(true); + setTagThumbnail(tag); + } +} + +void ImageDescEditTab::slotAlbumDeleted(Album* a) +{ + if (!a || a->isRoot() || a->type() != Album::TAG) + return; + + TAlbum* album = (TAlbum*)a; + + d->tagsSearchBar->lineEdit()->completionObject()->removeItem(album->title()); + d->newTagEdit->lineEdit()->completionObject()->removeItem(album->tagPath()); + d->newTagEdit->lineEdit()->completionObject()->removeItem(album->tagPath().remove(0, 1)); // without root "/" + TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)album->extraData(d->tagsView); + delete viewItem; + album->removeExtraData(this); + d->hub.setTag(album, false, MetadataHub::MetadataDisjoint); +} + +void ImageDescEditTab::slotAlbumsCleared() +{ + d->tagsView->clear(); + d->tagsSearchBar->lineEdit()->completionObject()->clear(); + d->newTagEdit->lineEdit()->completionObject()->clear(); +} + +void ImageDescEditTab::slotAlbumIconChanged(Album* a) +{ + if (!a || a->isRoot() || a->type() != Album::TAG) + return; + + setTagThumbnail((TAlbum *)a); +} + +void ImageDescEditTab::slotAlbumMoved(TAlbum* tag, TAlbum* newParent) +{ + if (!tag || !newParent) + return; + + TAlbumCheckListItem* item = (TAlbumCheckListItem*)tag->extraData(d->tagsView); + if (!item) + return; + + if (item->parent()) + { + TQListViewItem* oldPItem = item->parent(); + oldPItem->takeItem(item); + } + else + { + d->tagsView->takeItem(item); + } + + TAlbumCheckListItem* newPItem = (TAlbumCheckListItem*)newParent->extraData(d->tagsView); + if (newPItem) + newPItem->insertItem(item); + else + d->tagsView->insertItem(item); +} + +void ImageDescEditTab::slotAlbumRenamed(Album* album) +{ + if (!album || album->isRoot() || album->type() != Album::TAG) + return; + + TAlbum* tag = (TAlbum*)album; + d->tagsSearchBar->lineEdit()->completionObject()->addItem(tag->title()); + d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath()); + d->newTagEdit->lineEdit()->completionObject()->addItem(tag->tagPath().remove(0, 1)); // without root "/" + slotTagsSearchChanged(d->tagsSearchBar->lineEdit()->text()); + TAlbumCheckListItem* item = (TAlbumCheckListItem*)(tag->extraData(d->tagsView)); + if (item) + item->refresh(); +} + +void ImageDescEditTab::toggleChildTags(TAlbum *album, bool b) +{ + if (!album) + return; + + AlbumIterator it(album); + while ( it.current() ) + { + TAlbum *ta = (TAlbum*)it.current(); + TAlbumCheckListItem *item = (TAlbumCheckListItem*)(ta->extraData(d->tagsView)); + if (item) + if (item->isVisible()) + item->setOn(b); + ++it; + } +} + +void ImageDescEditTab::toggleParentTags(TAlbum *album, bool b) +{ + if (!album) + return; + + TQListViewItemIterator it(d->tagsView); + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + if (item->isVisible()) + { + if (!item->album()) + continue; + if (item->album() == album->parent()) + { + item->setOn(b); + toggleParentTags(item->album() , b); + } + } + ++it; + } +} + +void ImageDescEditTab::setTagThumbnail(TAlbum *album) +{ + if(!album) + return; + + TAlbumCheckListItem* item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap icon; + if (!loader->getTagThumbnail(album, icon)) + { + if (icon.isNull()) + { + item->setPixmap(0, loader->getStandardTagIcon(album)); + } + else + { + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), icon); + item->setPixmap(0, blendedIcon); + } + } +} + +void ImageDescEditTab::slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail) +{ + if(!album || album->type() != Album::TAG) + return; + + // update item in tags tree + TAlbumCheckListItem* item = (TAlbumCheckListItem*)album->extraData(d->tagsView); + if(!item) + return; + + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap blendedIcon = loader->blendIcons(loader->getStandardTagIcon(), thumbnail); + item->setPixmap(0, blendedIcon); + + // update item in recent tags popup menu, if found there in + TQPopupMenu *menu = d->recentTagsBtn->popup(); + if (menu->indexOf(album->id()) != -1) + { + menu->changeItem(album->id(), thumbnail, menu->text(album->id())); + } +} + +void ImageDescEditTab::slotThumbnailLost(Album *) +{ + // we already set the standard icon before loading +} + +void ImageDescEditTab::slotReloadThumbnails() +{ + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* tag = (TAlbum*)(*it); + setTagThumbnail(tag); + } +} + +void ImageDescEditTab::slotImageTagsChanged(TQ_LLONG imageId) +{ + // don't lose modifications + if (d->ignoreImageAttributesWatch || d->modified) + return; + + reloadForMetadataChange(imageId); +} + +void ImageDescEditTab::slotImagesChanged(int albumId) +{ + if (d->ignoreImageAttributesWatch || d->modified) + return; + + Album *a = AlbumManager::instance()->findAlbum(albumId); + if (d->currInfos.isEmpty() || !a || a->isRoot() || a->type() != Album::TAG) + return; + + setInfos(d->currInfos); +} + +void ImageDescEditTab::slotImageRatingChanged(TQ_LLONG imageId) +{ + if (d->ignoreImageAttributesWatch || d->modified) + return; + + reloadForMetadataChange(imageId); +} + +void ImageDescEditTab::slotImageCaptionChanged(TQ_LLONG imageId) +{ + if (d->ignoreImageAttributesWatch || d->modified) + return; + + reloadForMetadataChange(imageId); +} + +void ImageDescEditTab::slotImageDateChanged(TQ_LLONG imageId) +{ + if (d->ignoreImageAttributesWatch || d->modified) + return; + + reloadForMetadataChange(imageId); +} + +// private common code for above methods +void ImageDescEditTab::reloadForMetadataChange(TQ_LLONG imageId) +{ + if (d->currInfos.isEmpty()) + return; + + if (singleSelection()) + { + if (d->currInfos.first()->id() == imageId) + setInfos(d->currInfos); + } + else + { + // if image id is in our list, update + for (ImageInfo *info = d->currInfos.first(); info; info = d->currInfos.next()) + { + if (info->id() == imageId) + { + setInfos(d->currInfos); + return; + } + } + } +} + +void ImageDescEditTab::updateRecentTags() +{ + TQPopupMenu *menu = d->recentTagsBtn->popup(); + menu->clear(); + + AlbumManager* albumMan = AlbumManager::instance(); + IntList recentTags = albumMan->albumDB()->getRecentlyAssignedTags(); + + if (recentTags.isEmpty()) + { + menu->insertItem(i18n("No Recently Assigned Tags"), 0); + menu->setItemEnabled(0, false); + } + else + { + for (IntList::const_iterator it = recentTags.begin(); + it != recentTags.end(); ++it) + { + TAlbum* album = albumMan->findTAlbum(*it); + if (album) + { + AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance(); + TQPixmap icon; + if (!loader->getTagThumbnail(album, icon)) + { + if (icon.isNull()) + { + icon = loader->getStandardTagIcon(album, AlbumThumbnailLoader::SmallerSize); + } + } + TQString text = album->title() + " (" + ((TAlbum*)album->parent())->prettyURL() + ')'; + menu->insertItem(icon, text, album->id()); + } + } + } +} + +void ImageDescEditTab::slotRecentTagsMenuActivated(int id) +{ + AlbumManager* albumMan = AlbumManager::instance(); + + if (id > 0) + { + TAlbum* album = albumMan->findTAlbum(id); + if (album) + { + TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)album->extraData(d->tagsView); + if (viewItem) + { + viewItem->setOn(true); + d->tagsView->setSelected(viewItem, true); + d->tagsView->ensureItemVisible(viewItem); + } + } + } +} + +void ImageDescEditTab::slotTagsSearchChanged(const TQString& filter) +{ + if (filter.isEmpty()) + { + d->tagsView->collapseView(FolderView::OmitRoot); + return; + } + + //TODO: this will destroy assigned-tags filtering. Unify in one method. + TQString search = filter.lower(); + + bool atleastOneMatch = false; + + AlbumList tList = AlbumManager::instance()->allTAlbums(); + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbum* tag = (TAlbum*)(*it); + + // don't touch the root Tag + if (tag->isRoot()) + continue; + + bool match = tag->title().lower().contains(search); + bool doesExpand = false; + if (!match) + { + // check if any of the parents match the search + Album* parent = tag->parent(); + while (parent && !parent->isRoot()) + { + if (parent->title().lower().contains(search)) + { + match = true; + break; + } + + parent = parent->parent(); + } + } + + if (!match) + { + // check if any of the children match the search + AlbumIterator it(tag); + while (it.current()) + { + if ((*it)->title().lower().contains(search)) + { + match = true; + doesExpand = true; + break; + } + ++it; + } + } + + TAlbumCheckListItem* viewItem = (TAlbumCheckListItem*)(tag->extraData(d->tagsView)); + + if (match) + { + atleastOneMatch = true; + + if (viewItem) + { + viewItem->setVisible(true); + viewItem->setOpen(doesExpand); + } + } + else + { + if (viewItem) + { + viewItem->setVisible(false); + viewItem->setOpen(false); + } + } + } + + if (search.isEmpty()) + { + TAlbum* root = AlbumManager::instance()->findTAlbum(0); + TAlbumCheckListItem* rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView)); + if (rootItem) + rootItem->setText(0, root->title()); + } + else + { + TAlbum* root = AlbumManager::instance()->findTAlbum(0); + TAlbumCheckListItem* rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView)); + if (rootItem) + rootItem->setText(0, i18n("Found Tags")); + } + + emit signalTagFilterMatch(atleastOneMatch); +} + +void ImageDescEditTab::slotAssignedTagsToggled(bool t) +{ + //TODO: this will destroy name filtering. Unify in one method. + TQListViewItemIterator it(d->tagsView); + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + TAlbum *tag = item->album(); + if (tag) + { + if (!tag->isRoot()) + { + if (t) + { + MetadataHub::TagStatus status = d->hub.tagStatus(item->album()); + bool tagAssigned = (status == MetadataHub::MetadataAvailable && status.hasTag) + || status == MetadataHub::MetadataDisjoint; + item->setVisible(tagAssigned); + + if (tagAssigned) + { + Album* parent = tag->parent(); + while (parent && !parent->isRoot()) + { + TAlbumCheckListItem *pitem = (TAlbumCheckListItem*)parent->extraData(d->tagsView); + pitem->setVisible(true); + parent = parent->parent(); + } + } + } + else + { + item->setVisible(true); + } + } + } + ++it; + } + + // correct visibilities afterwards: + // As TQListViewItem::setVisible works recursively on all it's children + // we have to correct this + if (t) + { + it = d->tagsView; + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(it.current()); + TAlbum *tag = item->album(); + if (tag) + { + if (!tag->isRoot()) + { + // only if the current item is not marked as tagged, check all children + MetadataHub::TagStatus status = d->hub.tagStatus(item->album()); + bool tagAssigned = (status == MetadataHub::MetadataAvailable && status.hasTag) + || status == MetadataHub::MetadataDisjoint; + if (!tagAssigned) + { + bool somethingIsSet = false; + TQListViewItem* nextSibling = (*it)->nextSibling(); + TQListViewItemIterator tmpIt = it; + ++tmpIt; + while (*tmpIt != nextSibling ) + { + TAlbumCheckListItem* tmpItem = dynamic_cast(tmpIt.current()); + MetadataHub::TagStatus tmpStatus = d->hub.tagStatus(tmpItem->album()); + bool tmpTagAssigned = (tmpStatus == MetadataHub::MetadataAvailable && tmpStatus.hasTag) + || tmpStatus == MetadataHub::MetadataDisjoint; + if(tmpTagAssigned) + { + somethingIsSet = true; + } + ++tmpIt; + } + if (!somethingIsSet) + { + item->setVisible(false); + } + } + } + } + ++it; + } + } + + TAlbum *root = AlbumManager::instance()->findTAlbum(0); + TAlbumCheckListItem *rootItem = (TAlbumCheckListItem*)(root->extraData(d->tagsView)); + if (rootItem) + { + if (t) + rootItem->setText(0, i18n("Assigned Tags")); + else + rootItem->setText(0, root->title()); + } +} + +void ImageDescEditTab::refreshTagsView() +{ + d->tagsView->refresh(); +} + +void ImageDescEditTab::slotCreateNewTag() +{ + TQString tagStr = d->newTagEdit->text(); + if (tagStr.isEmpty()) return; + + TAlbum *mainRootAlbum = 0; + TAlbumCheckListItem* item = dynamic_cast(d->tagsView->selectedItem()); + if (item) + mainRootAlbum = item->album(); + + TQMap errMap; + AlbumList tList = TagEditDlg::createTAlbum(mainRootAlbum, tagStr, TQString("tag"), errMap); + + for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it) + { + TAlbumCheckListItem* item = (TAlbumCheckListItem*)(*it)->extraData(d->tagsView); + if (item) + { + item->setOn(true); + d->tagsView->ensureItemVisible(item); + } + } + + d->newTagEdit->lineEdit()->clear(); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/imagedescedittab.h b/src/libs/imageproperties/imagedescedittab.h new file mode 100644 index 00000000..617bd59b --- /dev/null +++ b/src/libs/imageproperties/imagedescedittab.h @@ -0,0 +1,145 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-03-09 + * Description : Captions, Tags, and Rating properties editor + * + * Copyright (C) 2003-2005 by Renchi Raju + * Copyright (C) 2003-2009 by Gilles Caulier + * Copyright (C) 2006-2009 by Marcel Wiesweg + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEDESCEDITTAB_H +#define IMAGEDESCEDITTAB_H + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "digikam_export.h" +#include "navigatebartab.h" +#include "albummanager.h" + +class TQListViewItem; + +namespace Digikam +{ +class TAlbumCheckListItem; +class ImageInfo; +class ImageDescEditTabPriv; + +class DIGIKAM_EXPORT ImageDescEditTab : public NavigateBarTab +{ + TQ_OBJECT + + +public: + + ImageDescEditTab(TQWidget *parent, bool navBar=true); + ~ImageDescEditTab(); + + void assignRating(int rating); + void setItem(ImageInfo *info=0); + void setItems(TQPtrList infos); + void populateTags(); + void refreshTagsView(); + +signals: + + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalTagFilterMatch(bool); + +protected: + + bool eventFilter(TQObject *o, TQEvent *e); + +private: + + void setInfos(TQPtrList infos); + + void updateTagsView(); + void updateComments(); + void updateRating(); + void updateDate(); + void updateRecentTags(); + + void tagNew(TAlbum* parAlbum, const TQString& _title=TQString(), const TQString& _icon=TQString()) const; + void tagEdit(TAlbum* album); + void tagDelete(TAlbum *album); + + void toggleChildTags(TAlbum *album, bool b); + void toggleParentTags(TAlbum *album, bool b); + + void setTagThumbnail(TAlbum *album); + + bool singleSelection() const; + void setMetadataWidgetStatus(int status, TQWidget *widget); + void reloadForMetadataChange(TQ_LLONG imageId); + +private slots: + + void slotApplyAllChanges(); + void slotCreateNewTag(); + void slotRevertAllChanges(); + void slotChangingItems(); + void slotItemStateChanged(TAlbumCheckListItem *); + void slotCommentChanged(); + void slotDateTimeChanged(const TQDateTime& dateTime); + void slotRatingChanged(int rating); + void slotModified(); + void slotRightButtonClicked(TQListViewItem *, const TQPoint &, int); + void slotTagsSearchChanged(const TQString&); + + void slotAlbumAdded(Album* a); + void slotAlbumDeleted(Album* a); + void slotAlbumIconChanged(Album* a); + void slotAlbumRenamed(Album* a); + void slotAlbumsCleared(); + void slotAlbumMoved(TAlbum* tag, TAlbum* newParent); + + void slotABCContextMenu(); + void slotGotThumbnailFromIcon(Album *album, const TQPixmap& thumbnail); + void slotThumbnailLost(Album *album); + void slotReloadThumbnails(); + + void slotImageTagsChanged(TQ_LLONG imageId); + void slotImagesChanged(int albumId); + void slotImageRatingChanged(TQ_LLONG imageId); + void slotImageDateChanged(TQ_LLONG imageId); + void slotImageCaptionChanged(TQ_LLONG imageId); + + void slotRecentTagsMenuActivated(int); + void slotAssignedTagsToggled(bool); + + void slotMoreMenu(); + void slotReadFromFileMetadataToDatabase(); + void slotWriteToFileMetadataFromDatabase(); + +private: + + ImageDescEditTabPriv* d; +}; + +} // NameSpace Digikam + +#endif // IMAGEDESCEDITTAB_H diff --git a/src/libs/imageproperties/imagepropertiescolorstab.cpp b/src/libs/imageproperties/imagepropertiescolorstab.cpp new file mode 100644 index 00000000..af85953b --- /dev/null +++ b/src/libs/imageproperties/imagepropertiescolorstab.cpp @@ -0,0 +1,799 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : a tab to display colors information of images + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// C++ includes. + +#include + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "imagehistogram.h" +#include "histogramwidget.h" +#include "colorgradientwidget.h" +#include "navigatebarwidget.h" +#include "sharedloadsavethread.h" +#include "iccprofilewidget.h" +#include "cietonguewidget.h" +#include "imagepropertiescolorstab.h" +#include "imagepropertiescolorstab.moc" + +namespace Digikam +{ + +class ImagePropertiesColorsTabPriv +{ +public: + + enum MetadataTab + { + HISTOGRAM=0, + ICCPROFILE + }; + + ImagePropertiesColorsTabPriv() + { + imageLoaderThread = 0; + tab = 0; + channelCB = 0; + colorsCB = 0; + renderingCB = 0; + scaleBG = 0; + regionBG = 0; + minInterv = 0; + maxInterv = 0; + labelMeanValue = 0; + labelPixelsValue = 0; + labelStdDevValue = 0; + labelCountValue = 0; + labelMedianValue = 0; + labelPercentileValue = 0; + labelColorDepth = 0; + labelAlphaChannel = 0; + + iccProfileWidget = 0; + hGradient = 0; + histogramWidget = 0; + imageLoaderThread = 0; + + inLoadingProcess = false; + } + + bool inLoadingProcess; + + TQComboBox *channelCB; + TQComboBox *colorsCB; + TQComboBox *renderingCB; + + TQHButtonGroup *scaleBG; + TQHButtonGroup *regionBG; + + TQSpinBox *minInterv; + TQSpinBox *maxInterv; + + TQLabel *labelMeanValue; + TQLabel *labelPixelsValue; + TQLabel *labelStdDevValue; + TQLabel *labelCountValue; + TQLabel *labelMedianValue; + TQLabel *labelPercentileValue; + TQLabel *labelColorDepth; + TQLabel *labelAlphaChannel; + + TQString currentFilePath; + LoadingDescription currentLoadingDescription; + + TQRect selectionArea; + + TQByteArray embedded_profile; + + KTabWidget *tab; + + DImg image; + DImg imageSelection; + + ICCProfileWidget *iccProfileWidget; + ColorGradientWidget *hGradient; + HistogramWidget *histogramWidget; + SharedLoadSaveThread *imageLoaderThread; +}; + +ImagePropertiesColorsTab::ImagePropertiesColorsTab(TQWidget* parent, bool navBar) + : NavigateBarTab(parent) +{ + d = new ImagePropertiesColorsTabPriv; + + setupNavigateBar(navBar); + d->tab = new KTabWidget(this); + m_navigateBarLayout->addWidget(d->tab); + + // Histogram tab area ----------------------------------------------------- + + TQScrollView *sv = new TQScrollView(d->tab); + sv->viewport()->setBackgroundMode(TQt::PaletteBackground); + sv->setResizePolicy(TQScrollView::AutoOneFit); + sv->setFrameStyle(TQFrame::NoFrame); + + TQWidget* histogramPage = new TQWidget(sv->viewport()); + TQGridLayout *topLayout = new TQGridLayout(histogramPage, 8, 3, + KDialog::spacingHint(), KDialog::spacingHint()); + sv->addChild(histogramPage); + + TQLabel *label1 = new TQLabel(i18n("Channel:"), histogramPage); + label1->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + d->channelCB = new TQComboBox( false, histogramPage ); + d->channelCB->insertItem( i18n("Luminosity") ); + d->channelCB->insertItem( i18n("Red") ); + d->channelCB->insertItem( i18n("Green") ); + d->channelCB->insertItem( i18n("Blue") ); + d->channelCB->insertItem( i18n("Alpha") ); + d->channelCB->insertItem( i18n("Colors") ); + TQWhatsThis::add( d->channelCB, i18n("

      Select the histogram channel to display here:

      " + "Luminosity: Display luminosity (perceived brightness) values.

      " + "Red: Display the red image channel.

      " + "Green: Display the green image channel.

      " + "Blue: Display the blue image channel.

      " + "Alpha: Display the alpha image channel. " + "This channel corresponds to the transparency value and " + "is supported by some image formats such as PNG or TIFF.

      " + "Colors: Display all color channel values at the same time.")); + + d->scaleBG = new TQHButtonGroup(histogramPage); + d->scaleBG->setExclusive(true); + d->scaleBG->setFrameShape(TQFrame::NoFrame); + d->scaleBG->setInsideMargin( 0 ); + TQWhatsThis::add( d->scaleBG, i18n("

      Select the histogram scale here.

      " + "If the image's maximal values are small, you can use the linear scale.

      " + "Logarithmic scale can be used when the maximal values are big; " + "if it is used, all values (small and large) will be visible on the " + "graph.")); + + TQPushButton *linHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( linHistoButton, i18n( "

      Linear" ) ); + d->scaleBG->insert(linHistoButton, HistogramWidget::LinScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-lin", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + TQString directory = TDEGlobal::dirs()->findResourceDir("histogram-lin", "histogram-lin.png"); + linHistoButton->setPixmap( TQPixmap( directory + "histogram-lin.png" ) ); + linHistoButton->setToggleButton(true); + + TQPushButton *logHistoButton = new TQPushButton( d->scaleBG ); + TQToolTip::add( logHistoButton, i18n( "

      Logarithmic" ) ); + d->scaleBG->insert(logHistoButton, HistogramWidget::LogScaleHistogram); + TDEGlobal::dirs()->addResourceType("histogram-log", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("histogram-log", "histogram-log.png"); + logHistoButton->setPixmap( TQPixmap( directory + "histogram-log.png" ) ); + logHistoButton->setToggleButton(true); + + TQLabel *label10 = new TQLabel(i18n("Colors:"), histogramPage); + label10->setAlignment ( TQt::AlignRight | TQt::AlignVCenter ); + d->colorsCB = new TQComboBox( false, histogramPage ); + d->colorsCB->insertItem( i18n("Red") ); + d->colorsCB->insertItem( i18n("Green") ); + d->colorsCB->insertItem( i18n("Blue") ); + d->colorsCB->setEnabled( false ); + TQWhatsThis::add( d->colorsCB, i18n("

      Select the main color displayed with Colors Channel mode here:

      " + "Red: Draw the red image channel in the foreground.

      " + "Green: Draw the green image channel in the foreground.

      " + "Blue: Draw the blue image channel in the foreground.

      ")); + + d->regionBG = new TQHButtonGroup(histogramPage); + d->regionBG->setExclusive(true); + d->regionBG->setFrameShape(TQFrame::NoFrame); + d->regionBG->setInsideMargin( 0 ); + d->regionBG->hide(); + TQWhatsThis::add( d->regionBG, i18n("

      Select from which region the histogram will be computed here:

      " + "Full Image: Compute histogram using the full image.

      " + "Selection: Compute histogram using the current image " + "selection.")); + + TQPushButton *fullImageButton = new TQPushButton( d->regionBG ); + TQToolTip::add( fullImageButton, i18n( "

      Full Image" ) ); + d->regionBG->insert(fullImageButton, HistogramWidget::FullImageHistogram); + TDEGlobal::dirs()->addResourceType("image-full", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("image-full", "image-full.png"); + fullImageButton->setPixmap( TQPixmap( directory + "image-full.png" ) ); + fullImageButton->setToggleButton(true); + + TQPushButton *SelectionImageButton = new TQPushButton( d->regionBG ); + TQToolTip::add( SelectionImageButton, i18n( "

      Selection" ) ); + d->regionBG->insert(SelectionImageButton, HistogramWidget::ImageSelectionHistogram); + TDEGlobal::dirs()->addResourceType("image-selection", TDEGlobal::dirs()->kde_default("data") + "digikam/data"); + directory = TDEGlobal::dirs()->findResourceDir("image-selection", "image-selection.png"); + SelectionImageButton->setPixmap( TQPixmap( directory + "image-selection.png" ) ); + SelectionImageButton->setToggleButton(true); + + // ------------------------------------------------------------- + + TQVBox *histoBox = new TQVBox(histogramPage); + d->histogramWidget = new HistogramWidget(256, 140, histoBox); + TQWhatsThis::add( d->histogramWidget, i18n("

      This is the histogram drawing of the " + "selected image channel")); + TQLabel *space = new TQLabel(histoBox); + space->setFixedHeight(1); + d->hGradient = new ColorGradientWidget(ColorGradientWidget::Horizontal, 10, histoBox); + d->hGradient->setColors(TQColor("black"), TQColor("white")); + + // ------------------------------------------------------------- + + TQHBoxLayout *hlay2 = new TQHBoxLayout(KDialog::spacingHint()); + TQLabel *label3 = new TQLabel(i18n("Range:"), histogramPage); + label3->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->minInterv = new TQSpinBox(0, 255, 1, histogramPage); + d->minInterv->setValue(0); + TQWhatsThis::add(d->minInterv, i18n("

      Select the minimal intensity " + "value of the histogram selection here.")); + d->maxInterv = new TQSpinBox(0, 255, 1, histogramPage); + d->maxInterv->setValue(255); + TQWhatsThis::add(d->minInterv, i18n("

      Select the maximal intensity value " + "of the histogram selection here.")); + hlay2->addWidget(label3); + hlay2->addWidget(d->minInterv); + hlay2->addWidget(d->maxInterv); + + // ------------------------------------------------------------- + + TQGroupBox *gbox = new TQGroupBox(2, TQt::Horizontal, i18n("Statistics"), histogramPage); + TQWhatsThis::add( gbox, i18n("

      Here you can see the statistical results calculated from the " + "selected histogram part. These values are available for all " + "channels.")); + + TQLabel *label5 = new TQLabel(i18n("Pixels:"), gbox); + label5->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelPixelsValue = new TQLabel(gbox); + d->labelPixelsValue->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label7 = new TQLabel(i18n("Count:"), gbox); + label7->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelCountValue = new TQLabel(gbox); + d->labelCountValue->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label4 = new TQLabel(i18n("Mean:"), gbox); + label4->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelMeanValue = new TQLabel(gbox); + d->labelMeanValue->setAlignment (TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label6 = new TQLabel(i18n("Std. deviation:"), gbox); + label6->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelStdDevValue = new TQLabel(gbox); + d->labelStdDevValue->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label8 = new TQLabel(i18n("Median:"), gbox); + label8->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelMedianValue = new TQLabel(gbox); + d->labelMedianValue->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label9 = new TQLabel(i18n("Percentile:"), gbox); + label9->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelPercentileValue = new TQLabel(gbox); + d->labelPercentileValue->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label11 = new TQLabel(i18n("Color depth:"), gbox); + label11->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelColorDepth = new TQLabel(gbox); + d->labelColorDepth->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + TQLabel *label12 = new TQLabel(i18n("Alpha Channel:"), gbox); + label12->setAlignment(TQt::AlignLeft | TQt::AlignVCenter); + d->labelAlphaChannel = new TQLabel(gbox); + d->labelAlphaChannel->setAlignment(TQt::AlignRight | TQt::AlignVCenter); + + topLayout->addMultiCellWidget(label1, 1, 1, 0, 0); + topLayout->addMultiCellWidget(d->channelCB, 1, 1, 1, 1); + topLayout->addMultiCellWidget(d->scaleBG, 1, 1, 3, 3); + topLayout->addMultiCellWidget(label10, 2, 2, 0, 0); + topLayout->addMultiCellWidget(d->colorsCB, 2, 2, 1, 1); + topLayout->addMultiCellWidget(d->regionBG, 2, 2, 3, 3); + topLayout->addMultiCellWidget(histoBox, 3, 4, 0, 3); + topLayout->addMultiCellLayout(hlay2, 5, 5, 0, 3); + topLayout->addMultiCellWidget(gbox, 6, 6, 0, 3); + topLayout->setColStretch(2, 10); + topLayout->setRowStretch(7, 10); + + d->tab->insertTab(sv, i18n("Histogram"), ImagePropertiesColorsTabPriv::HISTOGRAM ); + + // ICC Profiles tab area --------------------------------------- + + TQScrollView *sv2 = new TQScrollView(d->tab); + sv2->viewport()->setBackgroundMode(TQt::PaletteBackground); + sv2->setResizePolicy(TQScrollView::AutoOneFit); + sv2->setFrameStyle(TQFrame::NoFrame); + + d->iccProfileWidget = new ICCProfileWidget(sv2->viewport()); + sv2->addChild(d->iccProfileWidget); + d->tab->insertTab(sv2, i18n("ICC profile"), ImagePropertiesColorsTabPriv::ICCPROFILE); + + // ------------------------------------------------------------- + + connect(d->channelCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotChannelChanged(int))); + + connect(d->scaleBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotScaleChanged(int))); + + connect(d->colorsCB, TQ_SIGNAL(activated(int)), + this, TQ_SLOT(slotColorsChanged(int))); + + connect(d->regionBG, TQ_SIGNAL(released(int)), + this, TQ_SLOT(slotRenderingChanged(int))); + + connect(d->histogramWidget, TQ_SIGNAL(signalIntervalChanged( int, int )), + this, TQ_SLOT(slotUpdateInterval(int, int))); + + connect(d->histogramWidget, TQ_SIGNAL(signalMaximumValueChanged( int )), + this, TQ_SLOT(slotUpdateIntervRange(int))); + + connect(d->histogramWidget, TQ_SIGNAL(signalHistogramComputationDone(bool)), + this, TQ_SLOT(slotRefreshOptions(bool))); + + connect(d->histogramWidget, TQ_SIGNAL(signalHistogramComputationFailed(void)), + this, TQ_SLOT(slotHistogramComputationFailed(void))); + + connect(d->minInterv, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotMinValueChanged(int))); + + connect(d->maxInterv, TQ_SIGNAL(valueChanged (int)), + this, TQ_SLOT(slotMaxValueChanged(int))); + + // -- read config --------------------------------------------------------- + + TDEConfig* config = kapp->config(); + config->setGroup("Image Properties SideBar"); + d->tab->setCurrentPage(config->readNumEntry("ImagePropertiesColors Tab", + ImagePropertiesColorsTabPriv::HISTOGRAM)); + d->iccProfileWidget->setMode(config->readNumEntry("ICC Level", ICCProfileWidget::SIMPLE)); + d->iccProfileWidget->setCurrentItemByKey(config->readEntry("Current ICC Item", TQString())); + + d->channelCB->setCurrentItem(config->readNumEntry("Histogram Channel", 0)); // Luminosity. + d->scaleBG->setButton(config->readNumEntry("Histogram Scale", HistogramWidget::LogScaleHistogram)); + d->colorsCB->setCurrentItem(config->readNumEntry("Histogram Color", 0)); // Red. + d->regionBG->setButton(config->readNumEntry("Histogram Rendering", HistogramWidget::FullImageHistogram)); +} + +ImagePropertiesColorsTab::~ImagePropertiesColorsTab() +{ + // If there is a currently histogram computation when dialog is closed, + // stop it before the d->image data are deleted automatically! + d->histogramWidget->stopHistogramComputation(); + + TDEConfig* config = kapp->config(); + config->setGroup("Image Properties SideBar"); + config->writeEntry("ImagePropertiesColors Tab", d->tab->currentPageIndex()); + config->writeEntry("Histogram Channel", d->channelCB->currentItem()); + config->writeEntry("Histogram Scale", d->scaleBG->selectedId()); + config->writeEntry("Histogram Color", d->colorsCB->currentItem()); + config->writeEntry("Histogram Rendering", d->regionBG->selectedId()); + config->writeEntry("ICC Level", d->iccProfileWidget->getMode()); + config->writeEntry("Current ICC Item", d->iccProfileWidget->getCurrentItemKey()); + config->sync(); + + if (d->imageLoaderThread) + delete d->imageLoaderThread; + + if (d->histogramWidget) + delete d->histogramWidget; + + if (d->hGradient) + delete d->hGradient; + + delete d; +} + +void ImagePropertiesColorsTab::setData(const KURL& url, const TQRect &selectionArea, + DImg *img) +{ + // We might be getting duplicate events from AlbumIconView, + // which will cause all sorts of duplicate work. + // More importantly, while the loading thread can handle this pretty well, + // this will completely mess up the timing of progress info in the histogram widget. + // So filter here, before the stopHistogramComputation! + if (!img && url.path() == d->currentFilePath && d->inLoadingProcess) + return; + + // This is necessary to stop computation because d->image.bits() is currently used by + // threaded histogram algorithm. + d->histogramWidget->stopHistogramComputation(); + + d->currentFilePath = TQString(); + d->currentLoadingDescription = LoadingDescription(); + d->iccProfileWidget->loadFromURL(KURL()); + + // Clear information. + d->labelMeanValue->clear(); + d->labelPixelsValue->clear(); + d->labelStdDevValue->clear(); + d->labelCountValue->clear(); + d->labelMedianValue->clear(); + d->labelPercentileValue->clear(); + d->labelColorDepth->clear(); + d->labelAlphaChannel->clear(); + + if (url.isEmpty()) + { + setEnabled(false); + return; + } + + d->selectionArea = selectionArea; + d->image.reset(); + setEnabled(true); + + if (!img) + { + loadImageFromUrl(url); + } + else + { + d->image = img->copy(); + + if ( !d->image.isNull() ) + { + getICCData(); + + // If a selection area is done in Image Editor and if the current image is the same + // in Image Editor, then compute too the histogram for this selection. + if (d->selectionArea.isValid()) + { + d->imageSelection = d->image.copy(d->selectionArea); + d->histogramWidget->updateData(d->image.bits(), d->image.width(), d->image.height(), + d->image.sixteenBit(), d->imageSelection.bits(), + d->imageSelection.width(), d->imageSelection.height()); + d->regionBG->show(); + updateInformations(); + } + else + { + d->histogramWidget->updateData(d->image.bits(), d->image.width(), + d->image.height(), d->image.sixteenBit()); + d->regionBG->hide(); + updateInformations(); + } + } + else + { + d->histogramWidget->setLoadingFailed(); + d->iccProfileWidget->setLoadingFailed(); + slotHistogramComputationFailed(); + } + } +} + +void ImagePropertiesColorsTab::loadImageFromUrl(const KURL& url) +{ + // create thread on demand + if (!d->imageLoaderThread) + { + d->imageLoaderThread = new SharedLoadSaveThread(); + + connect(d->imageLoaderThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg&)), + this, TQ_SLOT(slotLoadImageFromUrlComplete(const LoadingDescription &, const DImg&))); + + connect(d->imageLoaderThread, TQ_SIGNAL(signalMoreCompleteLoadingAvailable(const LoadingDescription &, const LoadingDescription &)), + this, TQ_SLOT(slotMoreCompleteLoadingAvailable(const LoadingDescription &, const LoadingDescription &))); + } + + LoadingDescription desc = LoadingDescription(url.path()); + + if (DImg::fileFormat(desc.filePath) == DImg::RAW) + { + // use raw settings optimized for speed + + DRawDecoding rawDecodingSettings = DRawDecoding(); + rawDecodingSettings.optimizeTimeLoading(); + desc = LoadingDescription(desc.filePath, rawDecodingSettings); + } + + if (d->currentLoadingDescription.equalsOrBetterThan(desc)) + return; + + d->currentFilePath = desc.filePath; + d->currentLoadingDescription = desc; + d->inLoadingProcess = true; + + d->imageLoaderThread->load(d->currentLoadingDescription, + SharedLoadSaveThread::AccessModeRead, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + + d->histogramWidget->setDataLoading(); + d->iccProfileWidget->setDataLoading(); +} + +void ImagePropertiesColorsTab::slotLoadImageFromUrlComplete(const LoadingDescription &loadingDescription, const DImg& img) +{ + // Discard any leftover messages from previous, possibly aborted loads + if ( !loadingDescription.equalsOrBetterThan(d->currentLoadingDescription) ) + return; + + if ( !img.isNull() ) + { + d->histogramWidget->updateData(img.bits(), img.width(), img.height(), img.sixteenBit()); + + // As a safety precaution, this must be changed only after updateData is called, + // which stops computation because d->image.bits() is currently used by threaded histogram algorithm. + d->image = img; + d->regionBG->hide(); + updateInformations(); + getICCData(); + } + else + { + d->histogramWidget->setLoadingFailed(); + d->iccProfileWidget->setLoadingFailed(); + slotHistogramComputationFailed(); + } + d->inLoadingProcess = false; +} + +void ImagePropertiesColorsTab::slotMoreCompleteLoadingAvailable(const LoadingDescription &oldLoadingDescription, + const LoadingDescription &newLoadingDescription) +{ + if (oldLoadingDescription == d->currentLoadingDescription && + newLoadingDescription.equalsOrBetterThan(d->currentLoadingDescription)) + { + // Yes, we do want to stop our old time-optimized loading and chain to the current, more complete loading. + // Even the time-optimized raw loading takes significant time, and we must avoid two dcraw instances running + // at a time. + d->currentLoadingDescription = newLoadingDescription; + d->inLoadingProcess = true; + d->imageLoaderThread->load(newLoadingDescription, + SharedLoadSaveThread::AccessModeRead, + SharedLoadSaveThread::LoadingPolicyFirstRemovePrevious); + } +} + +void ImagePropertiesColorsTab::setSelection(const TQRect &selectionArea) +{ + // This is necessary to stop computation because d->image.bits() is currently used by + // threaded histogram algorithm. + + d->histogramWidget->stopHistogramComputation(); + d->selectionArea = selectionArea; + + if (d->selectionArea.isValid()) + { + d->imageSelection = d->image.copy(d->selectionArea); + d->histogramWidget->updateSelectionData(d->imageSelection.bits(), d->imageSelection.width(), + d->imageSelection.height(), d->imageSelection.sixteenBit()); + d->regionBG->show(); + } + else + { + d->regionBG->hide(); + slotRenderingChanged(HistogramWidget::FullImageHistogram); + } +} + +void ImagePropertiesColorsTab::slotRefreshOptions(bool /*sixteenBit*/) +{ + slotChannelChanged(d->channelCB->currentItem()); + slotScaleChanged(d->scaleBG->selectedId()); + slotColorsChanged(d->colorsCB->currentItem()); + + if (d->selectionArea.isValid()) + slotRenderingChanged(d->regionBG->selectedId()); +} + +void ImagePropertiesColorsTab::slotHistogramComputationFailed() +{ + d->imageSelection.reset(); + d->image.reset(); +} + +void ImagePropertiesColorsTab::slotChannelChanged(int channel) +{ + switch(channel) + { + case RedChannel: + d->histogramWidget->m_channelType = HistogramWidget::RedChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "red" ) ); + d->colorsCB->setEnabled(false); + break; + + case GreenChannel: + d->histogramWidget->m_channelType = HistogramWidget::GreenChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "green" ) ); + d->colorsCB->setEnabled(false); + break; + + case BlueChannel: + d->histogramWidget->m_channelType = HistogramWidget::BlueChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "blue" ) ); + d->colorsCB->setEnabled(false); + break; + + case AlphaChannel: + d->histogramWidget->m_channelType = HistogramWidget::AlphaChannelHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + d->colorsCB->setEnabled(false); + break; + + case ColorChannels: + d->histogramWidget->m_channelType = HistogramWidget::ColorChannelsHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + d->colorsCB->setEnabled(true); + break; + + default: // Luminosity. + d->histogramWidget->m_channelType = HistogramWidget::ValueHistogram; + d->hGradient->setColors( TQColor( "black" ), TQColor( "white" ) ); + d->colorsCB->setEnabled(false); + break; + } + + d->histogramWidget->repaint(false); + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotScaleChanged(int scale) +{ + d->histogramWidget->m_scaleType = scale; + d->histogramWidget->repaint(false); +} + +void ImagePropertiesColorsTab::slotColorsChanged(int color) +{ + switch(color) + { + case AllColorsGreen: + d->histogramWidget->m_colorType = HistogramWidget::GreenColor; + break; + + case AllColorsBlue: + d->histogramWidget->m_colorType = HistogramWidget::BlueColor; + break; + + default: // Red. + d->histogramWidget->m_colorType = HistogramWidget::RedColor; + break; + } + + d->histogramWidget->repaint(false); + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotRenderingChanged(int rendering) +{ + d->histogramWidget->m_renderingType = rendering; + d->histogramWidget->repaint(false); + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotMinValueChanged(int min) +{ + // Called when user changes values of spin box. + // Communicate the change to histogram widget. + + // make the one control "push" the other + if (min == d->maxInterv->value()+1) + d->maxInterv->setValue(min); + d->maxInterv->setMinValue(min-1); + d->histogramWidget->slotMinValueChanged(min); + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotMaxValueChanged(int max) +{ + if (max == d->minInterv->value()-1) + d->minInterv->setValue(max); + d->minInterv->setMaxValue(max+1); + d->histogramWidget->slotMaxValueChanged(max); + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotUpdateInterval(int min, int max) +{ + // Called when value is set from within histogram widget. + // Block signals to prevent slotMinValueChanged and + // slotMaxValueChanged being called. + d->minInterv->blockSignals(true); + d->minInterv->setMaxValue(max+1); + d->minInterv->setValue(min); + d->minInterv->blockSignals(false); + + d->maxInterv->blockSignals(true); + d->maxInterv->setMinValue(min-1); + d->maxInterv->setValue(max); + d->maxInterv->blockSignals(false); + + updateStatistiques(); +} + +void ImagePropertiesColorsTab::slotUpdateIntervRange(int range) +{ + d->maxInterv->setMaxValue( range ); +} + +void ImagePropertiesColorsTab::updateInformations() +{ + d->labelColorDepth->setText(d->image.sixteenBit() ? i18n("16 bits") : i18n("8 bits")); + d->labelAlphaChannel->setText(d->image.hasAlpha() ? i18n("Yes") : i18n("No")); +} + +void ImagePropertiesColorsTab::updateStatistiques() +{ + TQString value; + int min = d->minInterv->value(); + int max = d->maxInterv->value(); + int channel = d->channelCB->currentItem(); + + if ( channel == HistogramWidget::ColorChannelsHistogram ) + channel = d->colorsCB->currentItem()+1; + + double mean = d->histogramWidget->m_imageHistogram->getMean(channel, min, max); + d->labelMeanValue->setText(value.setNum(mean, 'f', 1)); + + double pixels = d->histogramWidget->m_imageHistogram->getPixels(); + d->labelPixelsValue->setText(value.setNum((float)pixels, 'f', 0)); + + double stddev = d->histogramWidget->m_imageHistogram->getStdDev(channel, min, max); + d->labelStdDevValue->setText(value.setNum(stddev, 'f', 1)); + + double counts = d->histogramWidget->m_imageHistogram->getCount(channel, min, max); + d->labelCountValue->setText(value.setNum((float)counts, 'f', 0)); + + double median = d->histogramWidget->m_imageHistogram->getMedian(channel, min, max); + d->labelMedianValue->setText(value.setNum(median, 'f', 1)); + + double percentile = (pixels > 0 ? (100.0 * counts / pixels) : 0.0); + d->labelPercentileValue->setText(value.setNum(percentile, 'f', 1)); +} + +void ImagePropertiesColorsTab::getICCData() +{ + if (d->image.getICCProfil().isNull()) + { + d->iccProfileWidget->setLoadingFailed(); + } + else + { + d->embedded_profile = d->image.getICCProfil(); + d->iccProfileWidget->loadFromData(d->currentFilePath, d->embedded_profile); + } +} + +} // NameSpace Digikam + diff --git a/src/libs/imageproperties/imagepropertiescolorstab.h b/src/libs/imageproperties/imagepropertiescolorstab.h new file mode 100644 index 00000000..0e01cad3 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiescolorstab.h @@ -0,0 +1,114 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : a tab to display colors information of images + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * ============================================================ */ + +#ifndef IMAGEPROPERTIESCOLORSTAB_H +#define IMAGEPROPERTIESCOLORSTAB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "dimg.h" +#include "digikam_export.h" +#include "navigatebartab.h" + +class TQRect; + +namespace Digikam +{ + +class DImg; +class LoadingDescription; +class ImagePropertiesColorsTabPriv; + +class DIGIKAM_EXPORT ImagePropertiesColorsTab : public NavigateBarTab +{ + TQ_OBJECT + + +public: + + ImagePropertiesColorsTab(TQWidget* parent, bool navBar=true); + ~ImagePropertiesColorsTab(); + + void setData(const KURL& url=KURL(), const TQRect &selectionArea = TQRect(), + DImg *img=0); + + void setSelection(const TQRect &selectionArea); + +private: + + void loadImageFromUrl(const KURL& url); + void updateInformations(); + void updateStatistiques(); + void getICCData(); + +private slots: + + void slotRefreshOptions(bool sixteenBit); + void slotHistogramComputationFailed(void); + void slotChannelChanged(int channel); + void slotScaleChanged(int scale); + void slotColorsChanged(int color); + void slotRenderingChanged(int rendering); + void slotMinValueChanged(int); + void slotMaxValueChanged(int); + + void slotUpdateInterval(int min, int max); + void slotUpdateIntervRange(int range); + + void slotLoadImageFromUrlComplete(const LoadingDescription &loadingDescription, const DImg& img); + void slotMoreCompleteLoadingAvailable(const LoadingDescription &oldLoadingDescription, + const LoadingDescription &newLoadingDescription); + +private: + + enum ColorChannel + { + LuminosityChannel=0, + RedChannel, + GreenChannel, + BlueChannel, + AlphaChannel, + ColorChannels + }; + + enum AllColorsColorType + { + AllColorsRed=0, + AllColorsGreen, + AllColorsBlue + }; + + ImagePropertiesColorsTabPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEPROPERTIESCOLORSTAB_H */ diff --git a/src/libs/imageproperties/imagepropertiesmetadatatab.cpp b/src/libs/imageproperties/imagepropertiesmetadatatab.cpp new file mode 100644 index 00000000..5a48aaec --- /dev/null +++ b/src/libs/imageproperties/imagepropertiesmetadatatab.cpp @@ -0,0 +1,201 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : a tab to display metadata information of images + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "exifwidget.h" +#include "makernotewidget.h" +#include "iptcwidget.h" +#include "gpswidget.h" +#include "navigatebarwidget.h" +#include "imagepropertiesmetadatatab.h" +#include "imagepropertiesmetadatatab.moc" + +namespace Digikam +{ + +class ImagePropertiesMetadataTabPriv +{ +public: + + enum MetadataTab + { + EXIF=0, + MAKERNOTE, + IPTC, + GPS + }; + + ImagePropertiesMetadataTabPriv() + { + exifWidget = 0; + makernoteWidget = 0; + iptcWidget = 0; + gpsWidget = 0; + tab = 0; + } + + KTabWidget *tab; + + ExifWidget *exifWidget; + + MakerNoteWidget *makernoteWidget; + + IptcWidget *iptcWidget; + + GPSWidget *gpsWidget; +}; + +ImagePropertiesMetaDataTab::ImagePropertiesMetaDataTab(TQWidget* parent, bool navBar) + : NavigateBarTab(parent) +{ + d = new ImagePropertiesMetadataTabPriv; + + setupNavigateBar(navBar); + d->tab = new KTabWidget(this); + m_navigateBarLayout->addWidget(d->tab); + + // Exif tab area ----------------------------------------------------- + + d->exifWidget = new ExifWidget(d->tab); + d->tab->insertTab(d->exifWidget, i18n("EXIF"), ImagePropertiesMetadataTabPriv::EXIF); + + // Makernote tab area ----------------------------------------------------- + + d->makernoteWidget = new MakerNoteWidget(d->tab); + d->tab->insertTab(d->makernoteWidget, i18n("Makernote"), ImagePropertiesMetadataTabPriv::MAKERNOTE); + + // IPTC tab area --------------------------------------- + + d->iptcWidget = new IptcWidget(d->tab); + d->tab->insertTab(d->iptcWidget, i18n("IPTC"), ImagePropertiesMetadataTabPriv::IPTC); + + // GPS tab area --------------------------------------- + + d->gpsWidget = new GPSWidget(d->tab); + d->tab->insertTab(d->gpsWidget, i18n("GPS"), ImagePropertiesMetadataTabPriv::GPS); + + // -- read config --------------------------------------------------------- + + TDEConfig* config = kapp->config(); + config->setGroup("Image Properties SideBar"); + d->tab->setCurrentPage(config->readNumEntry("ImagePropertiesMetaData Tab", + ImagePropertiesMetadataTabPriv::EXIF)); + d->exifWidget->setMode(config->readNumEntry("EXIF Level", ExifWidget::SIMPLE)); + d->makernoteWidget->setMode(config->readNumEntry("MAKERNOTE Level", MakerNoteWidget::SIMPLE)); + d->iptcWidget->setMode(config->readNumEntry("IPTC Level", IptcWidget::SIMPLE)); + d->gpsWidget->setMode(config->readNumEntry("GPS Level", GPSWidget::SIMPLE)); + d->exifWidget->setCurrentItemByKey(config->readEntry("Current EXIF Item", TQString())); + d->makernoteWidget->setCurrentItemByKey(config->readEntry("Current MAKERNOTE Item", TQString())); + d->iptcWidget->setCurrentItemByKey(config->readEntry("Current IPTC Item", TQString())); + d->gpsWidget->setCurrentItemByKey(config->readEntry("Current GPS Item", TQString())); + d->gpsWidget->setWebGPSLocator(config->readNumEntry("Current Web GPS Locator", GPSWidget::MapQuest)); +} + +ImagePropertiesMetaDataTab::~ImagePropertiesMetaDataTab() +{ + TDEConfig* config = kapp->config(); + config->setGroup("Image Properties SideBar"); + config->writeEntry("ImagePropertiesMetaData Tab", d->tab->currentPageIndex()); + config->writeEntry("EXIF Level", d->exifWidget->getMode()); + config->writeEntry("MAKERNOTE Level", d->makernoteWidget->getMode()); + config->writeEntry("IPTC Level", d->iptcWidget->getMode()); + config->writeEntry("GPS Level", d->gpsWidget->getMode()); + config->writeEntry("Current EXIF Item", d->exifWidget->getCurrentItemKey()); + config->writeEntry("Current MAKERNOTE Item", d->makernoteWidget->getCurrentItemKey()); + config->writeEntry("Current IPTC Item", d->iptcWidget->getCurrentItemKey()); + config->writeEntry("Current GPS Item", d->gpsWidget->getCurrentItemKey()); + config->writeEntry("Current Web GPS Locator", d->gpsWidget->getWebGPSLocator()); + config->sync(); + + delete d; +} + +void ImagePropertiesMetaDataTab::setCurrentURL(const KURL& url) +{ + if (url.isEmpty()) + { + d->exifWidget->loadFromURL(url); + d->makernoteWidget->loadFromURL(url); + d->iptcWidget->loadFromURL(url); + d->gpsWidget->loadFromURL(url); + setEnabled(false); + return; + } + + setEnabled(true); + DMetadata metadata(url.path()); + + TQByteArray exifData = metadata.getExif(); + TQByteArray iptcData = metadata.getIptc(); + + d->exifWidget->loadFromData(url.filename(), exifData); + d->makernoteWidget->loadFromData(url.filename(), exifData); + d->iptcWidget->loadFromData(url.filename(), iptcData); + d->gpsWidget->loadFromData(url.filename(), exifData); +} + +void ImagePropertiesMetaDataTab::setCurrentData(const TQByteArray& exifData, + const TQByteArray& iptcData, + const TQString& filename) +{ + if (exifData.isEmpty() && iptcData.isEmpty()) + { + d->exifWidget->loadFromData(filename, exifData); + d->makernoteWidget->loadFromData(filename, exifData); + d->iptcWidget->loadFromData(filename, iptcData); + d->gpsWidget->loadFromData(filename, exifData); + setEnabled(false); + return; + } + + setEnabled(true); + + d->exifWidget->loadFromData(filename, exifData); + d->makernoteWidget->loadFromData(filename, exifData); + d->iptcWidget->loadFromData(filename, iptcData); + d->gpsWidget->loadFromData(filename, exifData); +} + +} // NameSpace Digikam + diff --git a/src/libs/imageproperties/imagepropertiesmetadatatab.h b/src/libs/imageproperties/imagepropertiesmetadatatab.h new file mode 100644 index 00000000..95b8ecb8 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiesmetadatatab.h @@ -0,0 +1,68 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : a tab to display metadata information of images + * + * Copyright (C) 2004-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPROPERTIESMETADATATAB_H +#define IMAGEPROPERTIESMETADATATAB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" +#include "navigatebartab.h" + +namespace Digikam +{ + +class ImagePropertiesMetadataTabPriv; + +class DIGIKAM_EXPORT ImagePropertiesMetaDataTab : public NavigateBarTab +{ + TQ_OBJECT + + +public: + + ImagePropertiesMetaDataTab(TQWidget* parent, bool navBar=true); + ~ImagePropertiesMetaDataTab(); + + void setCurrentURL(const KURL& url=KURL()); + void setCurrentData(const TQByteArray& exifData=TQByteArray(), + const TQByteArray& iptcData=TQByteArray(), + const TQString& filename=TQString()); + +private: + + ImagePropertiesMetadataTabPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEPROPERTIESMETADATATAB_H */ diff --git a/src/libs/imageproperties/imagepropertiessidebar.cpp b/src/libs/imageproperties/imagepropertiessidebar.cpp new file mode 100644 index 00000000..67254388 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebar.cpp @@ -0,0 +1,150 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : simple image properties side bar (without support + * of digiKam database). + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "imagepropertiestab.h" +#include "imagepropertiesmetadatatab.h" +#include "imagepropertiescolorstab.h" +#include "imagepropertiessidebar.h" +#include "imagepropertiessidebar.moc" + +namespace Digikam +{ + +ImagePropertiesSideBar::ImagePropertiesSideBar(TQWidget *parent, const char *name, + TQSplitter *splitter, Side side, + bool mimimizedDefault, bool navBar) + : Sidebar(parent, name, side, mimimizedDefault) +{ + m_image = 0; + m_currentRect = TQRect(); + m_dirtyPropertiesTab = false; + m_dirtyMetadataTab = false; + m_dirtyColorTab = false; + + m_propertiesTab = new ImagePropertiesTab(parent, navBar); + m_metadataTab = new ImagePropertiesMetaDataTab(parent, navBar); + m_colorTab = new ImagePropertiesColorsTab(parent, navBar); + + setSplitter(splitter); + + appendTab(m_propertiesTab, SmallIcon("application-vnd.tde.info"), i18n("Properties")); + appendTab(m_metadataTab, SmallIcon("exifinfo"), i18n("Metadata")); + appendTab(m_colorTab, SmallIcon("blend"), i18n("Colors")); + + connect(this, TQ_SIGNAL(signalChangedTab(TQWidget*)), + this, TQ_SLOT(slotChangedTab(TQWidget*))); +} + +ImagePropertiesSideBar::~ImagePropertiesSideBar() +{ +} + +void ImagePropertiesSideBar::itemChanged(const KURL& url, const TQRect &rect, DImg *img) +{ + if (!url.isValid()) + return; + + m_currentURL = url; + m_currentRect = rect; + m_image = img; + m_dirtyPropertiesTab = false; + m_dirtyMetadataTab = false; + m_dirtyColorTab = false; + + slotChangedTab( getActiveTab() ); +} + +void ImagePropertiesSideBar::slotNoCurrentItem(void) +{ + m_currentURL = KURL(); + + m_propertiesTab->setCurrentURL(); + m_propertiesTab->setNavigateBarFileName(); + + m_metadataTab->setCurrentURL(); + m_metadataTab->setNavigateBarFileName(); + + m_colorTab->setData(); + m_colorTab->setNavigateBarFileName(); + + m_dirtyPropertiesTab = false; + m_dirtyMetadataTab = false; + m_dirtyColorTab = false; +} + +void ImagePropertiesSideBar::slotImageSelectionChanged(const TQRect &rect) +{ + m_currentRect = rect; + + if (m_dirtyColorTab) + m_colorTab->setSelection(rect); + else + slotChangedTab(m_colorTab); +} + +void ImagePropertiesSideBar::slotChangedTab(TQWidget* tab) +{ + if (!m_currentURL.isValid()) + return; + + setCursor(KCursor::waitCursor()); + + if (tab == m_propertiesTab && !m_dirtyPropertiesTab) + { + m_propertiesTab->setCurrentURL(m_currentURL); + m_dirtyPropertiesTab = true; + } + else if (tab == m_metadataTab && !m_dirtyMetadataTab) + { + m_metadataTab->setCurrentURL(m_currentURL); + m_dirtyMetadataTab = true; + } + else if (tab == m_colorTab && !m_dirtyColorTab) + { + m_colorTab->setData(m_currentURL, m_currentRect, m_image); + m_dirtyColorTab = true; + } + + unsetCursor(); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/imagepropertiessidebar.h b/src/libs/imageproperties/imagepropertiessidebar.h new file mode 100644 index 00000000..43e08df0 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebar.h @@ -0,0 +1,93 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : simple image properties side bar (without support + * of digiKam database). + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPROPERTIESSIDEBAR_H +#define IMAGEPROPERTIESSIDEBAR_H + +// KDE includes. + +#include + +// Local includes. + +#include "sidebar.h" +#include "digikam_export.h" + +class TQSplitter; +class TQWidget; +class TQRect; + +namespace Digikam +{ + +class DImg; +class ImagePropertiesTab; +class ImagePropertiesMetaDataTab; +class ImagePropertiesColorsTab; + +class DIGIKAM_EXPORT ImagePropertiesSideBar : public Sidebar +{ + TQ_OBJECT + + +public: + + ImagePropertiesSideBar(TQWidget* parent, const char *name, TQSplitter *splitter, + Side side=Left, bool mimimizedDefault=false, bool navBar=false); + + ~ImagePropertiesSideBar(); + + virtual void itemChanged(const KURL& url, const TQRect &rect = TQRect(), DImg *img = 0); + +public slots: + + void slotImageSelectionChanged(const TQRect &rect); + virtual void slotNoCurrentItem(void); + + +protected slots: + + virtual void slotChangedTab(TQWidget* tab); + +protected: + + bool m_dirtyPropertiesTab; + bool m_dirtyMetadataTab; + bool m_dirtyColorTab; + + TQRect m_currentRect; + + KURL m_currentURL; + + DImg *m_image; + + ImagePropertiesTab *m_propertiesTab; + ImagePropertiesMetaDataTab *m_metadataTab; + ImagePropertiesColorsTab *m_colorTab; + +}; + +} // NameSpace Digikam + +#endif // IMAGEPROPERTIESSIDEBAR_H diff --git a/src/libs/imageproperties/imagepropertiessidebarcamgui.cpp b/src/libs/imageproperties/imagepropertiessidebarcamgui.cpp new file mode 100644 index 00000000..942c33e5 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebarcamgui.cpp @@ -0,0 +1,209 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-08 + * Description : simple image properties side bar used by + * camera gui. + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "gpiteminfo.h" +#include "cameraiconview.h" +#include "cameraiconitem.h" +#include "cameraitempropertiestab.h" +#include "imagepropertiesmetadatatab.h" +#include "statusnavigatebar.h" +#include "navigatebarwidget.h" +#include "imagepropertiessidebarcamgui.h" +#include "imagepropertiessidebarcamgui.moc" + +namespace Digikam +{ + +class ImagePropertiesSideBarCamGuiPriv +{ +public: + + ImagePropertiesSideBarCamGuiPriv() + { + dirtyMetadataTab = false; + dirtyCameraItemTab = false; + metadataTab = 0; + cameraItemTab = 0; + itemInfo = 0; + cameraView = 0; + cameraItem = 0; + exifData = TQByteArray(); + currentURL = KURL(); + } + + bool dirtyMetadataTab; + bool dirtyCameraItemTab; + + TQByteArray exifData; + + KURL currentURL; + + GPItemInfo *itemInfo; + + ImagePropertiesMetaDataTab *metadataTab; + + CameraIconView *cameraView; + + CameraIconViewItem *cameraItem; + + CameraItemPropertiesTab *cameraItemTab; +}; + +ImagePropertiesSideBarCamGui::ImagePropertiesSideBarCamGui(TQWidget *parent, const char *name, + TQSplitter *splitter, Side side, + bool mimimizedDefault) + : Sidebar(parent, name, side, mimimizedDefault) +{ + d = new ImagePropertiesSideBarCamGuiPriv; + d->cameraItemTab = new CameraItemPropertiesTab(parent, true); + d->metadataTab = new ImagePropertiesMetaDataTab(parent, true); + + setSplitter(splitter); + + appendTab(d->cameraItemTab, SmallIcon("application-vnd.tde.info"), i18n("Properties")); + appendTab(d->metadataTab, SmallIcon("exifinfo"), i18n("Metadata")); + + // ---------------------------------------------------------- + + connectNavigateSignals(d->cameraItemTab); + connectNavigateSignals(d->metadataTab); + + connect(this, TQ_SIGNAL(signalChangedTab(TQWidget*)), + this, TQ_SLOT(slotChangedTab(TQWidget*))); +} + +ImagePropertiesSideBarCamGui::~ImagePropertiesSideBarCamGui() +{ + delete d; +} + +void ImagePropertiesSideBarCamGui::connectNavigateSignals(NavigateBarTab *tab) +{ + connect(tab, TQ_SIGNAL(signalFirstItem()), + this, TQ_SIGNAL(signalFirstItem())); + + connect(tab, TQ_SIGNAL(signalPrevItem()), + this, TQ_SIGNAL(signalPrevItem())); + + connect(tab, TQ_SIGNAL(signalNextItem()), + this, TQ_SIGNAL(signalNextItem())); + + connect(tab, TQ_SIGNAL(signalLastItem()), + this, TQ_SIGNAL(signalLastItem())); +} + +void ImagePropertiesSideBarCamGui::itemChanged(GPItemInfo* itemInfo, const KURL& url, + const TQByteArray& exifData, + CameraIconView* view, CameraIconViewItem* item) +{ + if (!itemInfo) + return; + + d->exifData = exifData; + d->itemInfo = itemInfo; + d->currentURL = url; + d->dirtyMetadataTab = false; + d->dirtyCameraItemTab = false; + d->cameraView = view; + d->cameraItem = item; + + if (d->exifData.isEmpty()) + { + DMetadata metaData(d->currentURL.path()); + d->exifData = metaData.getExif(); + } + + slotChangedTab( getActiveTab() ); +} + +void ImagePropertiesSideBarCamGui::slotNoCurrentItem(void) +{ + d->itemInfo = 0; + d->cameraItem = 0; + d->exifData = TQByteArray(); + d->currentURL = KURL(); + d->dirtyMetadataTab = false; + d->dirtyCameraItemTab = false; + + d->cameraItemTab->setCurrentItem(); + d->metadataTab->setCurrentURL(); +} + +void ImagePropertiesSideBarCamGui::slotChangedTab(TQWidget* tab) +{ + if (!d->itemInfo) + return; + + setCursor(KCursor::waitCursor()); + + if (tab == d->cameraItemTab && !d->dirtyCameraItemTab) + { + d->cameraItemTab->setCurrentItem(d->itemInfo, + d->cameraItem->getDownloadName(), d->exifData, + d->currentURL); + + d->dirtyCameraItemTab = true; + } + else if (tab == d->metadataTab && !d->dirtyMetadataTab) + { + d->metadataTab->setCurrentData(d->exifData, TQByteArray(), + d->itemInfo->name); + + d->dirtyMetadataTab = true; + } + + // setting of NavigateBar, common for all tabs + NavigateBarTab *navtab = dynamic_cast(tab); + if (navtab) + { + int currentItemType = StatusNavigateBar::ItemCurrent; + if (d->cameraView->firstItem() == d->cameraItem) + currentItemType = StatusNavigateBar::ItemFirst; + else if (d->cameraView->lastItem() == d->cameraItem) + currentItemType = StatusNavigateBar::ItemLast; + + navtab->setNavigateBarState(currentItemType); + navtab->setNavigateBarFileName(); + } + unsetCursor(); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/imagepropertiessidebarcamgui.h b/src/libs/imageproperties/imagepropertiessidebarcamgui.h new file mode 100644 index 00000000..3c2eafd0 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebarcamgui.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-02-08 + * Description : simple image properties side bar used by + * camera gui. + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPROPERTIESSIDEBARCAMGUI_H +#define IMAGEPROPERTIESSIDEBARCAMGUI_H + +// KDE includes. + +#include + +// Local includes. + +#include "sidebar.h" +#include "digikam_export.h" + +class TQSplitter; +class TQWidget; + +namespace Digikam +{ + +class GPItemInfo; +class CameraIconView; +class CameraIconViewItem; +class NavigateBarTab; +class ImagePropertiesSideBarCamGuiPriv; + +class DIGIKAM_EXPORT ImagePropertiesSideBarCamGui : public Sidebar +{ + TQ_OBJECT + + +public: + + ImagePropertiesSideBarCamGui(TQWidget* parent, const char *name, TQSplitter *splitter, + Side side=Left, bool mimimizedDefault=false); + + ~ImagePropertiesSideBarCamGui(); + + void itemChanged(GPItemInfo* itemInfo, const KURL& url, const TQByteArray& exifData=TQByteArray(), + CameraIconView* view=0, CameraIconViewItem* item=0); + +public slots: + + virtual void slotNoCurrentItem(void); + +signals: + + void signalFirstItem(void); + void signalPrevItem(void); + void signalNextItem(void); + void signalLastItem(void); + +private slots: + + virtual void slotChangedTab(TQWidget* tab); + +private: + + ImagePropertiesSideBarCamGuiPriv* d; + void connectNavigateSignals(NavigateBarTab *tab); +}; + +} // NameSpace Digikam + +#endif // IMAGEPROPERTIESSIDEBARCAMGUI_H diff --git a/src/libs/imageproperties/imagepropertiessidebardb.cpp b/src/libs/imageproperties/imagepropertiessidebardb.cpp new file mode 100644 index 00000000..f3dc5f7a --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebardb.cpp @@ -0,0 +1,368 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : image properties side bar using data from + * digiKam database. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dimg.h" +#include "imageinfo.h" +#include "imagedescedittab.h" +#include "imageattributeswatch.h" +#include "imagepropertiestab.h" +#include "imagepropertiesmetadatatab.h" +#include "imagepropertiescolorstab.h" +#include "imagepropertiessidebardb.h" +#include "imagepropertiessidebardb.moc" + +namespace Digikam +{ + +class ImagePropertiesSideBarDBPriv +{ +public: + + ImagePropertiesSideBarDBPriv() + { + desceditTab = 0; + dirtyDesceditTab = false; + hasPrevious = false; + hasNext = false; + hasImageInfoOwnership = false; + } + + bool dirtyDesceditTab; + + TQPtrList currentInfos; + + ImageDescEditTab *desceditTab; + + bool hasPrevious; + bool hasNext; + + bool hasImageInfoOwnership; +}; + +ImagePropertiesSideBarDB::ImagePropertiesSideBarDB(TQWidget *parent, const char *name, TQSplitter *splitter, + Side side, bool mimimizedDefault) + : ImagePropertiesSideBar(parent, name, splitter, side, mimimizedDefault, false) +{ + // Navigate bar is disabled by passing false to parent class constructor, and tab constructors + + d = new ImagePropertiesSideBarDBPriv; + d->desceditTab = new ImageDescEditTab(parent, false); + + appendTab(d->desceditTab, SmallIcon("imagecomment"), i18n("Captions/Tags")); + + // ---------------------------------------------------------- + + connect(this, TQ_SIGNAL(signalChangedTab(TQWidget*)), + this, TQ_SLOT(slotChangedTab(TQWidget*))); + + connect(d->desceditTab, TQ_SIGNAL(signalProgressBarMode(int, const TQString&)), + this, TQ_SIGNAL(signalProgressBarMode(int, const TQString&))); + + connect(d->desceditTab, TQ_SIGNAL(signalProgressValue(int)), + this, TQ_SIGNAL(signalProgressValue(int))); + + ImageAttributesWatch *watch = ImageAttributesWatch::instance(); + + connect(watch, TQ_SIGNAL(signalFileMetadataChanged(const KURL &)), + this, TQ_SLOT(slotFileMetadataChanged(const KURL &))); +} + +ImagePropertiesSideBarDB::~ImagePropertiesSideBarDB() +{ + delete d; +} + +void ImagePropertiesSideBarDB::itemChanged(ImageInfo *info, + const TQRect &rect, DImg *img) +{ + itemChanged(info->kurl(), info, rect, img); +} + +void ImagePropertiesSideBarDB::itemChanged(const KURL& url, const TQRect &rect, DImg *img) +{ + itemChanged(url, 0, rect, img); +} + +void ImagePropertiesSideBarDB::itemChanged(const KURL& url, ImageInfo *info, + const TQRect &rect, DImg *img) +{ + if ( !url.isValid() ) + return; + + m_currentURL = url; + + TQPtrList list; + if (info) + list.append(info); + + itemChanged(list, rect, img); +} + +void ImagePropertiesSideBarDB::itemChanged(TQPtrList infos) +{ + if (infos.isEmpty()) + return; + + m_currentURL = infos.first()->kurl(); + + itemChanged(infos, TQRect(), 0); +} + +void ImagePropertiesSideBarDB::itemChanged(TQPtrList infos, + const TQRect &rect, DImg *img) +{ + m_currentRect = rect; + m_image = img; + + // The list _may_ have autoDelete set to true. + // Keep old ImageInfo objects from being deleted + // until the tab has had the chance to save changes and clear lists. + TQPtrList temporaryList; + if (d->hasImageInfoOwnership) + { + temporaryList = d->currentInfos; + d->hasImageInfoOwnership = false; + } + + d->currentInfos = infos; + + m_dirtyPropertiesTab = false; + m_dirtyMetadataTab = false; + m_dirtyColorTab = false; + d->dirtyDesceditTab = false; + + // All tabs that store the ImageInfo list and access it after selection change + // must release the image info here. slotChangedTab only handles the active tab! + d->desceditTab->setItem(); + + slotChangedTab( getActiveTab() ); + + // now delete old objects, after slotChangedTab + for (ImageInfo *info = temporaryList.first(); info; info = temporaryList.next()) + { + delete info; + } +} + +void ImagePropertiesSideBarDB::takeImageInfoOwnership(bool takeOwnership) +{ + d->hasImageInfoOwnership = takeOwnership; +} + + +void ImagePropertiesSideBarDB::slotNoCurrentItem(void) +{ + ImagePropertiesSideBar::slotNoCurrentItem(); + + // All tabs that store the ImageInfo list and access it after selection change + // must release the image info here. slotChangedTab only handles the active tab! + d->desceditTab->setItem(); + + if (d->hasImageInfoOwnership) + { + for (ImageInfo *info = d->currentInfos.first(); info; info = d->currentInfos.next()) + { + delete info; + } + d->hasImageInfoOwnership = false; + } + d->currentInfos.clear(); + + d->desceditTab->setItem(); + d->dirtyDesceditTab = false; +} + +void ImagePropertiesSideBarDB::populateTags(void) +{ + d->desceditTab->populateTags(); +} + +void ImagePropertiesSideBarDB::slotChangedTab(TQWidget* tab) +{ + setCursor(KCursor::waitCursor()); + + // No database data available, for example in the case of image editor is + // started from camera GUI. + if (d->currentInfos.isEmpty()) + { + if (tab == m_propertiesTab && !m_dirtyPropertiesTab) + { + m_propertiesTab->setCurrentURL(m_currentURL); + m_dirtyPropertiesTab = true; + } + else if (tab == m_metadataTab && !m_dirtyMetadataTab) + { + if (m_image) + m_metadataTab->setCurrentData(m_image->getExif(), m_image->getIptc(), + m_currentURL.fileName()); + else + m_metadataTab->setCurrentURL(m_currentURL); + + m_dirtyMetadataTab = true; + } + else if (tab == m_colorTab && !m_dirtyColorTab) + { + m_colorTab->setData(m_currentURL, m_currentRect, m_image); + m_dirtyColorTab = true; + } + else if (tab == d->desceditTab && !d->dirtyDesceditTab) + { + // Do nothing here. We cannot get data from database ! + d->desceditTab->setItem(); + d->dirtyDesceditTab = true; + } + } + else if (d->currentInfos.count() == 1) // Data from database available... + { + if (tab == m_propertiesTab && !m_dirtyPropertiesTab) + { + m_propertiesTab->setCurrentURL(m_currentURL); + m_dirtyPropertiesTab = true; + } + else if (tab == m_metadataTab && !m_dirtyMetadataTab) + { + if (m_image) + m_metadataTab->setCurrentData(m_image->getExif(), m_image->getIptc(), + m_currentURL.fileName()); + else + m_metadataTab->setCurrentURL(m_currentURL); + + m_dirtyMetadataTab = true; + } + else if (tab == m_colorTab && !m_dirtyColorTab) + { + m_colorTab->setData(m_currentURL, m_currentRect, m_image); + m_dirtyColorTab = true; + } + else if (tab == d->desceditTab && !d->dirtyDesceditTab) + { + d->desceditTab->setItem(d->currentInfos.first()); + d->dirtyDesceditTab = true; + } + } + else // Data from database available, multiple selection + { + if (tab == m_propertiesTab && !m_dirtyPropertiesTab) + { + //TODO + m_propertiesTab->setCurrentURL(m_currentURL); + m_dirtyPropertiesTab = true; + } + else if (tab == m_metadataTab && !m_dirtyMetadataTab) + { + // any ideas? + m_metadataTab->setCurrentURL(); + m_dirtyMetadataTab = true; + } + else if (tab == m_colorTab && !m_dirtyColorTab) + { + // any ideas? + m_colorTab->setData(); + m_dirtyColorTab = true; + } + else if (tab == d->desceditTab && !d->dirtyDesceditTab) + { + d->desceditTab->setItems(d->currentInfos); + d->dirtyDesceditTab = true; + } + } + + unsetCursor(); +} + +void ImagePropertiesSideBarDB::slotFileMetadataChanged(const KURL &url) +{ + if (url == m_currentURL) + { + // trigger an update + m_dirtyMetadataTab = false; + + if (getActiveTab() == m_metadataTab) + { + // update now - reuse code form slotChangedTab + slotChangedTab( getActiveTab() ); + } + } +} + +void ImagePropertiesSideBarDB::slotAssignRating(int rating) +{ + d->desceditTab->assignRating(rating); +} + +void ImagePropertiesSideBarDB::slotAssignRatingNoStar() +{ + d->desceditTab->assignRating(0); +} + +void ImagePropertiesSideBarDB::slotAssignRatingOneStar() +{ + d->desceditTab->assignRating(1); +} + +void ImagePropertiesSideBarDB::slotAssignRatingTwoStar() +{ + d->desceditTab->assignRating(2); +} + +void ImagePropertiesSideBarDB::slotAssignRatingThreeStar() +{ + d->desceditTab->assignRating(3); +} + +void ImagePropertiesSideBarDB::slotAssignRatingFourStar() +{ + d->desceditTab->assignRating(4); +} + +void ImagePropertiesSideBarDB::slotAssignRatingFiveStar() +{ + d->desceditTab->assignRating(5); +} + +void ImagePropertiesSideBarDB::refreshTagsView() +{ + d->desceditTab->refreshTagsView(); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/imagepropertiessidebardb.h b/src/libs/imageproperties/imagepropertiessidebardb.h new file mode 100644 index 00000000..7037b648 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiessidebardb.h @@ -0,0 +1,117 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-17 + * Description : image properties side bar using data from + * digiKam database. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * Copyright (C) 2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPROPERTIESSIDEBARDB_H +#define IMAGEPROPERTIESSIDEBARDB_H + +// TQt includes. + +#include + +// KDE includes. + +#include + +// Local includes. + +#include "imagepropertiessidebar.h" +#include "digikam_export.h" + +class TQSplitter; +class TQWidget; +class TQRect; + +namespace Digikam +{ + +class DImg; +class AlbumIconView; +class AlbumIconItem; +class ImageInfo; +class NavigateBarTab; +class ImagePropertiesSideBarDBPriv; + +class DIGIKAM_EXPORT ImagePropertiesSideBarDB : public ImagePropertiesSideBar +{ + TQ_OBJECT + + +public: + + ImagePropertiesSideBarDB(TQWidget* parent, const char *name, TQSplitter *splitter, Side side=Left, + bool mimimizedDefault=false); + + ~ImagePropertiesSideBarDB(); + + virtual void itemChanged(const KURL& url, const TQRect &rect = TQRect(), DImg *img = 0); + + virtual void itemChanged(ImageInfo *info, const TQRect &rect = TQRect(), DImg *img = 0); + virtual void itemChanged(TQPtrList infos); + + void takeImageInfoOwnership(bool takeOwnership); + + void populateTags(void); + void refreshTagsView(); + +signals: + + void signalFirstItem(void); + void signalPrevItem(void); + void signalNextItem(void); + void signalLastItem(void); + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + +public slots: + + void slotAssignRating(int rating); + void slotAssignRatingNoStar(); + void slotAssignRatingOneStar(); + void slotAssignRatingTwoStar(); + void slotAssignRatingThreeStar(); + void slotAssignRatingFourStar(); + void slotAssignRatingFiveStar(); + + virtual void slotNoCurrentItem(void); + +private slots: + + void slotChangedTab(TQWidget* tab); + void slotFileMetadataChanged(const KURL &url); + +private: + + void itemChanged(const KURL& url, ImageInfo *info, + const TQRect &rect, DImg *img); + void itemChanged(TQPtrList infos, const TQRect &rect, DImg *img); + +private: + + ImagePropertiesSideBarDBPriv* d; +}; + +} // NameSpace Digikam + +#endif // IMAGEPROPERTIESSIDEBARDB_H diff --git a/src/libs/imageproperties/imagepropertiestab.cpp b/src/libs/imageproperties/imagepropertiestab.cpp new file mode 100644 index 00000000..7141c039 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiestab.cpp @@ -0,0 +1,601 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-19 + * Description : A tab to display general image information + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE includes. + +#include +#include +#include +#include +#include + +// LibKDcraw includes. + +#include +#include + +#if KDCRAW_VERSION < 0x000106 +#include +#endif + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "navigatebarwidget.h" +#include "imagepropertiestab.h" +#include "imagepropertiestab.moc" + +namespace Digikam +{ + +class ImagePropertiesTabPriv +{ +public: + + ImagePropertiesTabPriv() + { + settingsArea = 0; + title = 0; + file = 0; + folder = 0; + modifiedDate = 0; + size = 0; + owner = 0; + permissions = 0; + title2 = 0; + mime = 0; + dimensions = 0; + compression = 0; + bitDepth = 0; + colorMode = 0; + title3 = 0; + make = 0; + model = 0; + photoDate = 0; + aperture = 0; + focalLength = 0; + exposureTime = 0; + sensitivity = 0; + exposureMode = 0; + flash = 0; + whiteBalance = 0; + labelFile = 0; + labelFolder = 0; + labelFileModifiedDate = 0; + labelFileSize = 0; + labelFileOwner = 0; + labelFilePermissions = 0; + labelImageMime = 0; + labelImageDimensions = 0; + labelImageCompression = 0; + labelImageBitDepth = 0; + labelImageColorMode = 0; + labelPhotoMake = 0; + labelPhotoModel = 0; + labelPhotoDateTime = 0; + labelPhotoAperture = 0; + labelPhotoFocalLength = 0; + labelPhotoExposureTime = 0; + labelPhotoSensitivity = 0; + labelPhotoExposureMode = 0; + labelPhotoFlash = 0; + labelPhotoWhiteBalance = 0; + } + + TQLabel *title; + TQLabel *file; + TQLabel *folder; + TQLabel *modifiedDate; + TQLabel *size; + TQLabel *owner; + TQLabel *permissions; + + TQLabel *title2; + TQLabel *mime; + TQLabel *dimensions; + TQLabel *compression; + TQLabel *bitDepth; + TQLabel *colorMode; + + TQLabel *title3; + TQLabel *make; + TQLabel *model; + TQLabel *photoDate; + TQLabel *aperture; + TQLabel *focalLength; + TQLabel *exposureTime; + TQLabel *sensitivity; + TQLabel *exposureMode; + TQLabel *flash; + TQLabel *whiteBalance; + + TQFrame *settingsArea; + + KSqueezedTextLabel *labelFile; + KSqueezedTextLabel *labelFolder; + KSqueezedTextLabel *labelFileModifiedDate; + KSqueezedTextLabel *labelFileSize; + KSqueezedTextLabel *labelFileOwner; + KSqueezedTextLabel *labelFilePermissions; + + KSqueezedTextLabel *labelImageMime; + KSqueezedTextLabel *labelImageDimensions; + KSqueezedTextLabel *labelImageCompression; + KSqueezedTextLabel *labelImageBitDepth; + KSqueezedTextLabel *labelImageColorMode; + + KSqueezedTextLabel *labelPhotoMake; + KSqueezedTextLabel *labelPhotoModel; + KSqueezedTextLabel *labelPhotoDateTime; + KSqueezedTextLabel *labelPhotoAperture; + KSqueezedTextLabel *labelPhotoFocalLength; + KSqueezedTextLabel *labelPhotoExposureTime; + KSqueezedTextLabel *labelPhotoSensitivity; + KSqueezedTextLabel *labelPhotoExposureMode; + KSqueezedTextLabel *labelPhotoFlash; + KSqueezedTextLabel *labelPhotoWhiteBalance; +}; + +ImagePropertiesTab::ImagePropertiesTab(TQWidget* parent, bool navBar) + : NavigateBarTab(parent) +{ + d = new ImagePropertiesTabPriv; + + setupNavigateBar(navBar); + + TQScrollView *sv = new TQScrollView(this); + sv->viewport()->setBackgroundMode(TQt::PaletteBackground); + sv->setResizePolicy(TQScrollView::AutoOneFit); + sv->setFrameStyle(TQFrame::NoFrame); + + d->settingsArea = new TQFrame(sv->viewport()); + d->settingsArea->setFrameStyle( TQFrame::StyledPanel | TQFrame::Sunken ); + d->settingsArea->setLineWidth( style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this) ); + + sv->addChild(d->settingsArea); + m_navigateBarLayout->addWidget(sv); + + // -------------------------------------------------- + + TQGridLayout *settingsLayout = new TQGridLayout(d->settingsArea, 33, 1, KDialog::spacingHint(), 0); + + // -------------------------------------------------- + + d->title = new TQLabel(i18n("File Properties"), d->settingsArea); + d->file = new TQLabel(i18n("File:"), d->settingsArea); + d->folder = new TQLabel(i18n("Folder:"), d->settingsArea); + d->modifiedDate = new TQLabel(i18n("Modified:"), d->settingsArea); + d->size = new TQLabel(i18n("Size:"), d->settingsArea); + d->owner = new TQLabel(i18n("Owner:"), d->settingsArea); + d->permissions = new TQLabel(i18n("Permissions:"), d->settingsArea); + + KSeparator *line = new KSeparator(TQt::Horizontal, d->settingsArea); + d->title2 = new TQLabel(i18n("Image Properties"), d->settingsArea); + d->mime = new TQLabel(i18n("Type:"), d->settingsArea); + d->dimensions = new TQLabel(i18n("Dimensions:"), d->settingsArea); + d->compression = new TQLabel(i18n("Compression:"), d->settingsArea); + d->bitDepth = new TQLabel(i18n("Bit depth:"), d->settingsArea); + d->colorMode = new TQLabel(i18n("Color mode:"), d->settingsArea); + + KSeparator *line2 = new KSeparator(TQt::Horizontal, d->settingsArea); + d->title3 = new TQLabel(i18n("Photograph Properties"), d->settingsArea); + d->make = new TQLabel(i18n("Make:"), d->settingsArea); + d->model = new TQLabel(i18n("Model:"), d->settingsArea); + d->photoDate = new TQLabel(i18n("Created:"), d->settingsArea); + d->aperture = new TQLabel(i18n("Aperture:"), d->settingsArea); + d->focalLength = new TQLabel(i18n("Focal:"), d->settingsArea); + d->exposureTime = new TQLabel(i18n("Exposure:"), d->settingsArea); + d->sensitivity = new TQLabel(i18n("Sensitivity:"), d->settingsArea); + d->exposureMode = new TQLabel(i18n("Mode/Program:"), d->settingsArea); + d->flash = new TQLabel(i18n("Flash:"), d->settingsArea); + d->whiteBalance = new TQLabel(i18n("White balance:"), d->settingsArea); + + d->labelFile = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFolder = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileModifiedDate = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileSize = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFileOwner = new KSqueezedTextLabel(0, d->settingsArea); + d->labelFilePermissions = new KSqueezedTextLabel(0, d->settingsArea); + + d->labelImageMime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageDimensions = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageCompression = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageBitDepth = new KSqueezedTextLabel(0, d->settingsArea); + d->labelImageColorMode = new KSqueezedTextLabel(0, d->settingsArea); + + d->labelPhotoMake = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoModel = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoDateTime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoAperture = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoFocalLength = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoExposureTime = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoSensitivity = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoExposureMode = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoFlash = new KSqueezedTextLabel(0, d->settingsArea); + d->labelPhotoWhiteBalance = new KSqueezedTextLabel(0, d->settingsArea); + + int hgt = fontMetrics().height()-2; + d->title->setAlignment(TQt::AlignCenter); + d->file->setMaximumHeight(hgt); + d->folder->setMaximumHeight(hgt); + d->modifiedDate->setMaximumHeight(hgt); + d->size->setMaximumHeight(hgt); + d->owner->setMaximumHeight(hgt); + d->permissions->setMaximumHeight(hgt); + d->labelFile->setMaximumHeight(hgt); + d->labelFolder->setMaximumHeight(hgt); + d->labelFileModifiedDate->setMaximumHeight(hgt); + d->labelFileSize->setMaximumHeight(hgt); + d->labelFileOwner->setMaximumHeight(hgt); + d->labelFilePermissions->setMaximumHeight(hgt); + + d->title2->setAlignment(TQt::AlignCenter); + d->mime->setMaximumHeight(hgt); + d->dimensions->setMaximumHeight(hgt); + d->compression->setMaximumHeight(hgt); + d->bitDepth->setMaximumHeight(hgt); + d->colorMode->setMaximumHeight(hgt); + d->labelImageMime->setMaximumHeight(hgt); + d->labelImageDimensions->setMaximumHeight(hgt); + d->labelImageCompression->setMaximumHeight(hgt); + d->labelImageBitDepth->setMaximumHeight(hgt); + d->labelImageColorMode->setMaximumHeight(hgt); + + d->title3->setAlignment(TQt::AlignCenter); + d->make->setMaximumHeight(hgt); + d->model->setMaximumHeight(hgt); + d->photoDate->setMaximumHeight(hgt); + d->aperture->setMaximumHeight(hgt); + d->focalLength->setMaximumHeight(hgt); + d->exposureTime->setMaximumHeight(hgt); + d->sensitivity->setMaximumHeight(hgt); + d->exposureMode->setMaximumHeight(hgt); + d->flash->setMaximumHeight(hgt); + d->whiteBalance->setMaximumHeight(hgt); + d->labelPhotoMake->setMaximumHeight(hgt); + d->labelPhotoModel->setMaximumHeight(hgt); + d->labelPhotoDateTime->setMaximumHeight(hgt); + d->labelPhotoAperture->setMaximumHeight(hgt); + d->labelPhotoFocalLength->setMaximumHeight(hgt); + d->labelPhotoExposureTime->setMaximumHeight(hgt); + d->labelPhotoSensitivity->setMaximumHeight(hgt); + d->labelPhotoExposureMode->setMaximumHeight(hgt); + d->labelPhotoFlash->setMaximumHeight(hgt); + d->labelPhotoWhiteBalance->setMaximumHeight(hgt); + + // -------------------------------------------------- + + settingsLayout->addMultiCellWidget(d->title, 0, 0, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 1, 1, 0, 1); + settingsLayout->addMultiCellWidget(d->file, 2, 2, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFile, 2, 2, 1, 1); + settingsLayout->addMultiCellWidget(d->folder, 3, 3, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFolder, 3, 3, 1, 1); + settingsLayout->addMultiCellWidget(d->modifiedDate, 4, 4, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileModifiedDate, 4, 4, 1, 1); + settingsLayout->addMultiCellWidget(d->size, 5, 5, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileSize, 5, 5, 1, 1); + settingsLayout->addMultiCellWidget(d->owner, 6, 6, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFileOwner, 6, 6, 1, 1); + settingsLayout->addMultiCellWidget(d->permissions, 7, 7, 0, 0); + settingsLayout->addMultiCellWidget(d->labelFilePermissions, 7, 7, 1, 1); + + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 8, 8, 0, 1); + settingsLayout->addMultiCellWidget(line, 9, 9, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 10, 10, 0, 1); + + settingsLayout->addMultiCellWidget(d->title2, 11, 11, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 12, 12, 0, 1); + settingsLayout->addMultiCellWidget(d->mime, 13, 13, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageMime, 13, 13, 1, 1); + settingsLayout->addMultiCellWidget(d->dimensions, 14, 14, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageDimensions, 14, 14, 1, 1); + settingsLayout->addMultiCellWidget(d->compression, 15, 15, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageCompression, 15, 15, 1, 1); + settingsLayout->addMultiCellWidget(d->bitDepth, 16, 16, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageBitDepth, 16, 16, 1, 1); + settingsLayout->addMultiCellWidget(d->colorMode, 17, 17, 0, 0); + settingsLayout->addMultiCellWidget(d->labelImageColorMode, 17, 17, 1, 1); + + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 18, 18, 0, 1); + settingsLayout->addMultiCellWidget(line2, 19, 19, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 20, 20, 0, 1); + + settingsLayout->addMultiCellWidget(d->title3, 21, 21, 0, 1); + settingsLayout->addMultiCell(new TQSpacerItem(KDialog::spacingHint(), KDialog::spacingHint(), + TQSizePolicy::Minimum, TQSizePolicy::MinimumExpanding), 22, 22, 0, 1); + settingsLayout->addMultiCellWidget(d->make, 23, 23, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoMake, 23, 23, 1, 1); + settingsLayout->addMultiCellWidget(d->model, 24, 24, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoModel, 24, 24, 1, 1); + settingsLayout->addMultiCellWidget(d->photoDate, 25, 25, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoDateTime, 25, 25, 1, 1); + settingsLayout->addMultiCellWidget(d->aperture, 26, 26, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoAperture, 26, 26, 1, 1); + settingsLayout->addMultiCellWidget(d->focalLength, 27, 27, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoFocalLength, 27, 27, 1, 1); + settingsLayout->addMultiCellWidget(d->exposureTime, 28, 28, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoExposureTime, 28, 28, 1, 1); + settingsLayout->addMultiCellWidget(d->sensitivity, 29, 29, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoSensitivity, 29, 29, 1, 1); + settingsLayout->addMultiCellWidget(d->exposureMode, 30, 30, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoExposureMode, 30, 30, 1, 1); + settingsLayout->addMultiCellWidget(d->flash, 31, 31, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoFlash, 31, 31, 1, 1); + settingsLayout->addMultiCellWidget(d->whiteBalance, 32, 32, 0, 0); + settingsLayout->addMultiCellWidget(d->labelPhotoWhiteBalance, 32, 32, 1, 1); + + settingsLayout->setRowStretch(33, 10); + settingsLayout->setColStretch(1, 10); +} + +ImagePropertiesTab::~ImagePropertiesTab() +{ + delete d; +} + +void ImagePropertiesTab::setCurrentURL(const KURL& url) +{ + if (url.isEmpty()) + { + setNavigateBarFileName(); + + d->labelFile->setText(TQString()); + d->labelFolder->setText(TQString()); + d->labelFileModifiedDate->setText(TQString()); + d->labelFileSize->setText(TQString()); + d->labelFileOwner->setText(TQString()); + d->labelFilePermissions->setText(TQString()); + + d->labelImageMime->setText(TQString()); + d->labelImageDimensions->setText(TQString()); + d->labelImageCompression->setText(TQString()); + d->labelImageBitDepth->setText(TQString()); + d->labelImageColorMode->setText(TQString()); + + d->labelPhotoMake->setText(TQString()); + d->labelPhotoModel->setText(TQString()); + d->labelPhotoDateTime->setText(TQString()); + d->labelPhotoAperture->setText(TQString()); + d->labelPhotoFocalLength->setText(TQString()); + d->labelPhotoExposureTime->setText(TQString()); + d->labelPhotoSensitivity->setText(TQString()); + d->labelPhotoExposureMode->setText(TQString()); + d->labelPhotoFlash->setText(TQString()); + d->labelPhotoWhiteBalance->setText(TQString()); + + setEnabled(false); + return; + } + + setEnabled(true); + + TQString str; + TQString unavailable(i18n("unavailable")); + + KFileItem fi(KFileItem::Unknown, KFileItem::Unknown, url); + TQFileInfo fileInfo(url.path()); + DMetadata metaData(url.path()); + + // -- File system information ------------------------------------------ + + d->labelFile->setText(url.fileName()); + d->labelFolder->setText(url.directory()); + + TQDateTime modifiedDate = fileInfo.lastModified(); + str = TDEGlobal::locale()->formatDateTime(modifiedDate, true, true); + d->labelFileModifiedDate->setText(str); + + str = TQString("%1 (%2)").arg(TDEIO::convertSize(fi.size())) + .arg(TDEGlobal::locale()->formatNumber(fi.size(), 0)); + d->labelFileSize->setText(str); + + d->labelFileOwner->setText( TQString("%1 - %2").arg(fi.user()).arg(fi.group()) ); + d->labelFilePermissions->setText( fi.permissionsString() ); + + // -- Image Properties -------------------------------------------------- + + TQSize dims; + TQString compression, bitDepth, colorMode; +#if KDCRAW_VERSION < 0x000106 + TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + TQString ext = fileInfo.extension(false).upper(); + + if (!ext.isEmpty() && rawFilesExt.upper().contains(ext)) + { + d->labelImageMime->setText(i18n("RAW Image")); + compression = i18n("None"); + bitDepth = "48"; + dims = metaData.getImageDimensions(); + colorMode = i18n("Uncalibrated"); + } + else + { + d->labelImageMime->setText(fi.mimeComment()); + + KFileMetaInfo meta = fi.metaInfo(); + if (meta.isValid()) + { + if (meta.containsGroup("Jpeg EXIF Data")) // JPEG image ? + { + dims = meta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + + TQString quality = meta.group("Jpeg EXIF Data").item("JPEG quality").value().toString(); + quality.isEmpty() ? compression = unavailable : + compression = i18n("JPEG quality %1").arg(quality); + bitDepth = meta.group("Jpeg EXIF Data").item("BitDepth").value().toString(); + colorMode = meta.group("Jpeg EXIF Data").item("ColorMode").value().toString(); + } + + if (meta.containsGroup("General")) + { + if (dims.isEmpty() ) + dims = meta.group("General").item("Dimensions").value().toSize(); + if (compression.isEmpty()) + compression = meta.group("General").item("Compression").value().toString(); + if (bitDepth.isEmpty()) + bitDepth = meta.group("General").item("BitDepth").value().toString(); + if (colorMode.isEmpty()) + colorMode = meta.group("General").item("ColorMode").value().toString(); + } + + if (meta.containsGroup("Technical")) + { + if (dims.isEmpty()) + dims = meta.group("Technical").item("Dimensions").value().toSize(); + if (compression.isEmpty()) + compression = meta.group("Technical").item("Compression").value().toString(); + if (bitDepth.isEmpty()) + bitDepth = meta.group("Technical").item("BitDepth").value().toString(); + if (colorMode.isEmpty()) + colorMode = meta.group("Technical").item("ColorMode").value().toString(); + } + } + } + + TQString mpixels; + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + str = (!dims.isValid()) ? i18n("Unknown") : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + d->labelImageDimensions->setText(str); + d->labelImageCompression->setText(compression.isEmpty() ? unavailable : compression); + d->labelImageBitDepth->setText(bitDepth.isEmpty() ? unavailable : i18n("%1 bpp").arg(bitDepth)); + d->labelImageColorMode->setText(colorMode.isEmpty() ? unavailable : colorMode); + + // -- Photograph information ------------------------------------------ + // NOTA: If something is changed here, please updated albumfiletip section too. + + PhotoInfoContainer photoInfo = metaData.getPhotographInformations(); + + if (photoInfo.isEmpty()) + { + d->title3->hide(); + d->make->hide(); + d->model->hide(); + d->photoDate->hide(); + d->aperture->hide(); + d->focalLength->hide(); + d->exposureTime->hide(); + d->sensitivity->hide(); + d->exposureMode->hide(); + d->flash->hide(); + d->whiteBalance->hide(); + d->labelPhotoMake->hide(); + d->labelPhotoModel->hide(); + d->labelPhotoDateTime->hide(); + d->labelPhotoAperture->hide(); + d->labelPhotoFocalLength->hide(); + d->labelPhotoExposureTime->hide(); + d->labelPhotoSensitivity->hide(); + d->labelPhotoExposureMode->hide(); + d->labelPhotoFlash->hide(); + d->labelPhotoWhiteBalance->hide(); + } + else + { + d->title3->show(); + d->make->show(); + d->model->show(); + d->photoDate->show(); + d->aperture->show(); + d->focalLength->show(); + d->exposureTime->show(); + d->sensitivity->show(); + d->exposureMode->show(); + d->flash->show(); + d->whiteBalance->show(); + d->labelPhotoMake->show(); + d->labelPhotoModel->show(); + d->labelPhotoDateTime->show(); + d->labelPhotoAperture->show(); + d->labelPhotoFocalLength->show(); + d->labelPhotoExposureTime->show(); + d->labelPhotoSensitivity->show(); + d->labelPhotoExposureMode->show(); + d->labelPhotoFlash->show(); + d->labelPhotoWhiteBalance->show(); + } + + d->labelPhotoMake->setText(photoInfo.make.isEmpty() ? unavailable : photoInfo.make); + d->labelPhotoModel->setText(photoInfo.model.isEmpty() ? unavailable : photoInfo.model); + + if (photoInfo.dateTime.isValid()) + { + str = TDEGlobal::locale()->formatDateTime(photoInfo.dateTime, true, true); + d->labelPhotoDateTime->setText(str); + } + else + d->labelPhotoDateTime->setText(unavailable); + + d->labelPhotoAperture->setText(photoInfo.aperture.isEmpty() ? unavailable : photoInfo.aperture); + + if (photoInfo.focalLength35mm.isEmpty()) + d->labelPhotoFocalLength->setText(photoInfo.focalLength.isEmpty() ? unavailable : photoInfo.focalLength); + else + { + str = i18n("%1 (35mm: %2)").arg(photoInfo.focalLength).arg(photoInfo.focalLength35mm); + d->labelPhotoFocalLength->setText(str); + } + + d->labelPhotoExposureTime->setText(photoInfo.exposureTime.isEmpty() ? unavailable : photoInfo.exposureTime); + d->labelPhotoSensitivity->setText(photoInfo.sensitivity.isEmpty() ? unavailable : i18n("%1 ISO").arg(photoInfo.sensitivity)); + + if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(unavailable); + else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(photoInfo.exposureMode); + else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty()) + d->labelPhotoExposureMode->setText(photoInfo.exposureProgram); + else + { + str = TQString("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram); + d->labelPhotoExposureMode->setText(str); + } + + d->labelPhotoFlash->setText(photoInfo.flash.isEmpty() ? unavailable : photoInfo.flash); + d->labelPhotoWhiteBalance->setText(photoInfo.whiteBalance.isEmpty() ? unavailable : photoInfo.whiteBalance); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/imagepropertiestab.h b/src/libs/imageproperties/imagepropertiestab.h new file mode 100644 index 00000000..2a69ce97 --- /dev/null +++ b/src/libs/imageproperties/imagepropertiestab.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-04-19 + * Description : A tab to display general image information + * + * Copyright (C) 2006-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGEPROPERTIESTAB_H +#define IMAGEPROPERTIESTAB_H + +// TQt includes. + +#include +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" +#include "navigatebartab.h" + +namespace Digikam +{ + +class ImagePropertiesTabPriv; + +class DIGIKAM_EXPORT ImagePropertiesTab : public NavigateBarTab +{ + TQ_OBJECT + + +public: + + ImagePropertiesTab(TQWidget* parent, bool navBar=true); + ~ImagePropertiesTab(); + + void setCurrentURL(const KURL& url=KURL()); + +private: + + ImagePropertiesTabPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGEPROPERTIESTAB_H */ diff --git a/src/libs/imageproperties/navigatebartab.cpp b/src/libs/imageproperties/navigatebartab.cpp new file mode 100644 index 00000000..d40ae134 --- /dev/null +++ b/src/libs/imageproperties/navigatebartab.cpp @@ -0,0 +1,146 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-04 + * Description : A parent tab class with a navigation bar + * + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "statusnavigatebar.h" +#include "navigatebarwidget.h" +#include "navigatebartab.h" +#include "navigatebartab.moc" + +namespace Digikam +{ + +class NavigateBarTabPriv +{ +public: + + NavigateBarTabPriv() + { + stack = 0; + navigateBar = 0; + label = 0; + } + + TQWidgetStack *stack; + + TQLabel *label; + + NavigateBarWidget *navigateBar; +}; + +NavigateBarTab::NavigateBarTab(TQWidget* parent) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new NavigateBarTabPriv; + m_navigateBarLayout = 0; +} + +NavigateBarTab::~NavigateBarTab() +{ + delete d; +} + +void NavigateBarTab::setupNavigateBar(bool withBar) +{ + m_navigateBarLayout = new TQVBoxLayout(this); + + if (withBar) + { + d->stack = new TQWidgetStack(this); + m_navigateBarLayout->addWidget(d->stack); + + d->navigateBar = new NavigateBarWidget(d->stack, withBar); + d->stack->addWidget(d->navigateBar); + + connect(d->navigateBar, TQ_SIGNAL(signalFirstItem()), + this, TQ_SIGNAL(signalFirstItem())); + + connect(d->navigateBar, TQ_SIGNAL(signalPrevItem()), + this, TQ_SIGNAL(signalPrevItem())); + + connect(d->navigateBar, TQ_SIGNAL(signalNextItem()), + this, TQ_SIGNAL(signalNextItem())); + + connect(d->navigateBar, TQ_SIGNAL(signalLastItem()), + this, TQ_SIGNAL(signalLastItem())); + + d->label = new TQLabel(d->stack); + d->label->setAlignment(TQt::AlignCenter); + d->stack->addWidget(d->label); + } +} + +void NavigateBarTab::setNavigateBarState(bool hasPrevious, bool hasNext) +{ + if (!d->navigateBar) + return; + + d->stack->raiseWidget(d->navigateBar); + + if (hasPrevious && hasNext) + d->navigateBar->setButtonsState(StatusNavigateBar::ItemCurrent); + else if (!hasPrevious && hasNext) + d->navigateBar->setButtonsState(StatusNavigateBar::ItemFirst); + else if (hasPrevious && !hasNext) + d->navigateBar->setButtonsState(StatusNavigateBar::ItemLast); + else + d->navigateBar->setButtonsState(StatusNavigateBar::NoNavigation); +} + +void NavigateBarTab::setNavigateBarState(int itemType) +{ + if (!d->navigateBar) + return; + + d->stack->raiseWidget(d->navigateBar); + d->navigateBar->setButtonsState(itemType); +} + +void NavigateBarTab::setNavigateBarFileName(const TQString &name) +{ + if (!d->navigateBar) + return; + + d->stack->raiseWidget(d->navigateBar); + d->navigateBar->setFileName(name); +} + +void NavigateBarTab::setLabelText(const TQString &text) +{ + if (!d->label) + return; + + d->stack->raiseWidget(d->label); + d->label->setText(text); +} + +} // NameSpace Digikam + diff --git a/src/libs/imageproperties/navigatebartab.h b/src/libs/imageproperties/navigatebartab.h new file mode 100644 index 00000000..a93090e0 --- /dev/null +++ b/src/libs/imageproperties/navigatebartab.h @@ -0,0 +1,85 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2007-01-04 + * Description : A parent tab class with a navigation bar + * + * Copyright (C) 2006-2007 by Gilles Caulier + * Copyright (C) 2007 by Marcel Wiesweg + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef NAVIGATEBARTAB_H +#define NAVIGATEBARTAB_H + +// TQt includes. + +#include +#include + +// KDE includes. + +#include + +// Local includes. + +#include "digikam_export.h" +#include "imagepropertiessidebar.h" + +class TQVBoxLayout; + +namespace Digikam +{ + +class NavigateBarWidget; +class NavigateBarTabPriv; + +class DIGIKAM_EXPORT NavigateBarTab : public TQWidget +{ + TQ_OBJECT + + +public: + + NavigateBarTab(TQWidget* parent); + ~NavigateBarTab(); + + void setNavigateBarState(bool hasPrevious, bool hasNext); + void setNavigateBarState(int itemType); + void setNavigateBarFileName(const TQString &name = TQString()); + void setLabelText(const TQString &text); + +signals: + + void signalFirstItem(void); + void signalPrevItem(void); + void signalNextItem(void); + void signalLastItem(void); + +protected: + + void setupNavigateBar(bool withBar); + +protected: + + TQVBoxLayout *m_navigateBarLayout; + NavigateBarTabPriv *d; + +}; + +} // NameSpace Digikam + +#endif /* NAVIGATEBARTAB_H */ diff --git a/src/libs/imageproperties/navigatebarwidget.cpp b/src/libs/imageproperties/navigatebarwidget.cpp new file mode 100644 index 00000000..9232c55e --- /dev/null +++ b/src/libs/imageproperties/navigatebarwidget.cpp @@ -0,0 +1,112 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-07 + * Description : a navigate bar with text + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include + +// Local includes. + +#include "statusnavigatebar.h" +#include "navigatebarwidget.h" +#include "navigatebarwidget.moc" + +namespace Digikam +{ + +class NavigateBarWidgetPriv +{ +public: + + NavigateBarWidgetPriv() + { + filename = 0; + navBar = 0; + } + + KSqueezedTextLabel *filename; + + StatusNavigateBar *navBar; +}; + +NavigateBarWidget::NavigateBarWidget(TQWidget *parent, bool show) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new NavigateBarWidgetPriv; + + TQHBoxLayout *lay = new TQHBoxLayout(this); + d->navBar = new StatusNavigateBar(this); + d->filename = new KSqueezedTextLabel(this); + + lay->addWidget(d->navBar); + lay->addSpacing( KDialog::spacingHint() ); + lay->addWidget(d->filename); + + if (!show) hide(); + + connect(d->navBar, TQ_SIGNAL(signalFirstItem()), + this, TQ_SIGNAL(signalFirstItem())); + + connect(d->navBar, TQ_SIGNAL(signalPrevItem()), + this, TQ_SIGNAL(signalPrevItem())); + + connect(d->navBar, TQ_SIGNAL(signalNextItem()), + this, TQ_SIGNAL(signalNextItem())); + + connect(d->navBar, TQ_SIGNAL(signalLastItem()), + this, TQ_SIGNAL(signalLastItem())); +} + +NavigateBarWidget::~NavigateBarWidget() +{ + delete d; +} + +void NavigateBarWidget::setFileName(TQString filename) +{ + d->filename->setText(filename); +} + +TQString NavigateBarWidget::getFileName() +{ + return (d->filename->text()); +} + +void NavigateBarWidget::setButtonsState(int itemType) +{ + d->navBar->setButtonsState(itemType); +} + +int NavigateBarWidget::getButtonsState() +{ + return (d->navBar->getButtonsState()); +} + +} // namespace Digikam + diff --git a/src/libs/imageproperties/navigatebarwidget.h b/src/libs/imageproperties/navigatebarwidget.h new file mode 100644 index 00000000..7be088ed --- /dev/null +++ b/src/libs/imageproperties/navigatebarwidget.h @@ -0,0 +1,70 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2005-07-07 + * Description : a navigate bar with text + * + * Copyright (C) 2005-2007 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef NAVIGATEBARWIDGET_H +#define NAVIGATEBARWIDGET_H + +// TQt includes. + +#include +#include + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class NavigateBarWidgetPriv; + +class DIGIKAM_EXPORT NavigateBarWidget : public TQWidget +{ +TQ_OBJECT + + +public: + + NavigateBarWidget(TQWidget *parent=0, bool show=true); + ~NavigateBarWidget(); + + void setFileName(TQString filename=TQString()); + TQString getFileName(); + void setButtonsState(int itemType); + int getButtonsState(); + +signals: + + void signalFirstItem(void); + void signalPrevItem(void); + void signalNextItem(void); + void signalLastItem(void); + +private : + + NavigateBarWidgetPriv* d; +}; + +} // namespace Digikam + +#endif /* NAVIGATEBARWIDGET_H */ diff --git a/src/libs/imageproperties/talbumlistview.cpp b/src/libs/imageproperties/talbumlistview.cpp new file mode 100644 index 00000000..1369d228 --- /dev/null +++ b/src/libs/imageproperties/talbumlistview.cpp @@ -0,0 +1,528 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-18-12 + * Description : A list view to display digiKam Tags. + * + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// KDE includes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "albumiconitem.h" +#include "albumlister.h" +#include "albummanager.h" +#include "albumdb.h" +#include "album.h" +#include "albumsettings.h" +#include "imageinfo.h" +#include "navigatebarwidget.h" +#include "dragobjects.h" +#include "imageattributeswatch.h" +#include "albumthumbnailloader.h" +#include "statusprogressbar.h" +#include "talbumlistview.h" +#include "talbumlistview.moc" + +// X11 includes. + +extern "C" +{ +#include +} + +namespace Digikam +{ + +TAlbumCheckListItem::TAlbumCheckListItem(TQListView* parent, TAlbum* album) + : FolderCheckListItem(parent, album->title(), TQCheckListItem::RadioButtonController) +{ + setDragEnabled(true); + m_album = album; + m_count = 0; + + if (m_album) + m_album->setExtraData(listView(), this); +} + +TAlbumCheckListItem::TAlbumCheckListItem(TQCheckListItem* parent, TAlbum* album) + : FolderCheckListItem(parent, album->title(), TQCheckListItem::CheckBox) +{ + setDragEnabled(true); + m_album = album; + m_count = 0; + + if (m_album) + m_album->setExtraData(listView(), this); +} + +void TAlbumCheckListItem::refresh() +{ + if (!m_album) return; + + if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount() && + dynamic_cast(parent())) + { + if (isOpen()) + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count)); + else + { + int countRecursive = m_count; + AlbumIterator it(m_album); + while ( it.current() ) + { + TAlbumCheckListItem *item = (TAlbumCheckListItem*)it.current()->extraData(listView()); + if (item) + countRecursive += item->count(); + ++it; + } + setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive)); + } + } + else + { + setText(0, m_album->title()); + } +} + +void TAlbumCheckListItem::stateChange(bool val) +{ + TQCheckListItem::stateChange(val); + ((TAlbumListView*)listView())->stateChanged(this); +} + +void TAlbumCheckListItem::setOpen(bool o) +{ + TQListViewItem::setOpen(o); + refresh(); +} + +TAlbum* TAlbumCheckListItem::album() const +{ + return m_album; +} + +int TAlbumCheckListItem::id() const +{ + return m_album ? m_album->id() : 0; +} + +void TAlbumCheckListItem::setCount(int count) +{ + m_count = count; + refresh(); +} + +int TAlbumCheckListItem::count() +{ + return m_count; +} + +void TAlbumCheckListItem::setStatus(MetadataHub::TagStatus status) +{ + if (status == MetadataHub::MetadataDisjoint) + { + if (type() != TQCheckListItem::RadioButtonController) setTristate(true); + setState(TQCheckListItem::NoChange); + } + else + { + if (type() != TQCheckListItem::RadioButtonController) setTristate(false); + setOn(status.hasTag); + } +} + +// ------------------------------------------------------------------------ + +TAlbumListView::TAlbumListView(TQWidget* parent) + : FolderView(parent, "TAlbumListView") +{ + addColumn(i18n("Tags")); + header()->hide(); + setResizeMode(TQListView::LastColumn); + setRootIsDecorated(true); + + setAcceptDrops(true); + viewport()->setAcceptDrops(true); + + connect(AlbumManager::instance(), TQ_SIGNAL(signalTAlbumsDirty(const TQMap&)), + this, TQ_SLOT(slotRefresh(const TQMap&))); +} + +TAlbumListView::~TAlbumListView() +{ + saveViewState(); +} + +void TAlbumListView::stateChanged(TAlbumCheckListItem *item) +{ + emit signalItemStateChanged(item); +} + +TQDragObject* TAlbumListView::dragObject() +{ + TAlbumCheckListItem *item = dynamic_cast(dragItem()); + if(!item) + return 0; + + if(!item->parent()) + return 0; + + TagDrag *t = new TagDrag(item->id(), this); + t->setPixmap(*item->pixmap(0)); + + return t; +} + +bool TAlbumListView::acceptDrop(const TQDropEvent *e) const +{ + TQPoint vp = contentsToViewport(e->pos()); + TAlbumCheckListItem *itemDrop = dynamic_cast(itemAt(vp)); + TAlbumCheckListItem *itemDrag = dynamic_cast(dragItem()); + + if(TagDrag::canDecode(e) || TagListDrag::canDecode(e)) + { + // Allow dragging at the root, to move the tag to the root + if(!itemDrop) + return true; + + // Dragging an item on itself makes no sense + if(itemDrag == itemDrop) + return false; + + // Dragging a parent on its child makes no sense + if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album())) + return false; + + return true; + } + + if (ItemDrag::canDecode(e) && itemDrop && itemDrop->album()->parent()) + { + // Only other possibility is image items being dropped + // And allow this only if there is a Tag to be dropped + // on and also the Tag is not root. + return true; + } + + return false; +} + +void TAlbumListView::contentsDropEvent(TQDropEvent *e) +{ + TQListView::contentsDropEvent(e); + + if(!acceptDrop(e)) + return; + + TQPoint vp = contentsToViewport(e->pos()); + TAlbumCheckListItem *itemDrop = dynamic_cast(itemAt(vp)); + + if(TagDrag::canDecode(e)) + { + TQByteArray ba = e->encodedData("digikam/tag-id"); + TQDataStream ds(ba, IO_ReadOnly); + int tagID; + ds >> tagID; + + AlbumManager* man = AlbumManager::instance(); + TAlbum* talbum = man->findTAlbum(tagID); + + if(!talbum) + return; + + if (talbum == itemDrop->album()) + return; + + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("Tags")); + popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10); + popMenu.insertSeparator(-1); + popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20); + popMenu.setMouseTracking(true); + int id = popMenu.exec(TQCursor::pos()); + + if(id == 10) + { + TAlbum *newParentTag = 0; + + if (!itemDrop) + { + // move dragItem to the root + newParentTag = AlbumManager::instance()->findTAlbum(0); + } + else + { + // move dragItem as child of dropItem + newParentTag = itemDrop->album(); + } + + TQString errMsg; + if (!AlbumManager::instance()->moveTAlbum(talbum, newParentTag, errMsg)) + { + KMessageBox::error(this, errMsg); + } + + if(itemDrop && !itemDrop->isOpen()) + itemDrop->setOpen(true); + } + + return; + } + + if (ItemDrag::canDecode(e)) + { + TAlbum *destAlbum = itemDrop->album(); + TAlbum *srcAlbum; + + KURL::List urls; + KURL::List kioURLs; + TQValueList albumIDs; + TQValueList imageIDs; + + if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) + return; + + if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty()) + return; + + // all the albumids will be the same + int albumID = albumIDs.first(); + srcAlbum = AlbumManager::instance()->findTAlbum(albumID); + if (!srcAlbum) + { + DWarning() << "Could not find source album of drag" + << endl; + return; + } + + int id = 0; + char keys_return[32]; + XQueryKeymap(x11Display(), keys_return); + int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3); + int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4); + + if(srcAlbum == destAlbum) + { + // Setting the dropped image as the album thumbnail + // If the ctrl key is pressed, when dropping the image, the + // thumbnail is set without a popup menu + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 12; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("Tags")); + popMenu.insertItem(i18n("Set as Tag Thumbnail"), 12); + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if(id == 12) + { + TQString errMsg; + AlbumManager::instance()->updateTAlbumIcon(destAlbum, TQString(), + imageIDs.first(), errMsg); + } + return; + } + + // If a ctrl key is pressed while dropping the drag object, + // the tag is assigned to the images without showing a + // popup menu. + if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) || + ((keys_return[key_2 / 8]) && (1 << (key_2 % 8)))) + { + id = 10; + } + else + { + TDEPopupMenu popMenu(this); + popMenu.insertTitle(SmallIcon("digikam"), i18n("Tags")); + popMenu.insertItem( SmallIcon("tag"), i18n("Assign Tag '%1' to Items") + .arg(destAlbum->prettyURL()), 10) ; + popMenu.insertSeparator(-1); + popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") ); + + popMenu.setMouseTracking(true); + id = popMenu.exec(TQCursor::pos()); + } + + if (id == 10) + { + emit signalProgressBarMode(StatusProgressBar::ProgressBarMode, + i18n("Assign tag to images. Please wait...")); + + AlbumLister::instance()->blockSignals(true); + AlbumManager::instance()->albumDB()->beginTransaction(); + int i=0; + for (TQValueList::const_iterator it = imageIDs.begin(); + it != imageIDs.end(); ++it) + { + // create temporary ImageInfo object + ImageInfo info(*it); + + MetadataHub hub; + hub.load(&info); + hub.setTag(destAlbum, true); + hub.write(&info, MetadataHub::PartialWrite); + hub.write(info.filePath(), MetadataHub::FullWriteIfChanged); + + emit signalProgressValue((int)((i++/(float)imageIDs.count())*100.0)); + kapp->processEvents(); + } + AlbumLister::instance()->blockSignals(false); + AlbumManager::instance()->albumDB()->commitTransaction(); + + ImageAttributesWatch::instance()->imagesChanged(destAlbum->id()); + + emit signalProgressBarMode(StatusProgressBar::TextMode, TQString()); + } + } +} + +void TAlbumListView::refresh() +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(*it); + if (item) + item->refresh(); + ++it; + } +} + +void TAlbumListView::slotRefresh(const TQMap& tagsStatMap) +{ + TQListViewItemIterator it(this); + + while (it.current()) + { + TAlbumCheckListItem* item = dynamic_cast(*it); + if (item) + { + if (item->album()) + { + int id = item->id(); + TQMap::const_iterator it2 = tagsStatMap.find(id); + if ( it2 != tagsStatMap.end() ) + item->setCount(it2.data()); + } + } + ++it; + } + + refresh(); +} + +void TAlbumListView::loadViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + int selectedItem = config->readNumEntry("LastSelectedItem", 0); + + TQValueList openFolders; + if(config->hasKey("OpenFolders")) + { + openFolders = config->readIntListEntry("OpenFolders"); + } + + TAlbumCheckListItem *item = 0; + TAlbumCheckListItem *foundItem = 0; + TQListViewItemIterator it(this->lastItem()); + + for( ; it.current(); --it) + { + item = dynamic_cast(it.current()); + if(!item) + continue; + + // Start the album root always open + if(openFolders.contains(item->id()) || item->id() == 0) + setOpen(item, true); + else + setOpen(item, false); + + if(item->id() == selectedItem) + { + // Save the found selected item so that it can be made visible. + foundItem = item; + } + } + + // Important note: this cannot be done inside the previous loop + // because opening folders prevents the visibility. + // Fixes bug #144815. + // (Looks a bit like a bug in TQt to me ...) + if (foundItem) + { + setSelected(foundItem, true); + ensureItemVisible(foundItem); + } +} + +void TAlbumListView::saveViewState() +{ + TDEConfig *config = kapp->config(); + config->setGroup(name()); + + TAlbumCheckListItem *item = dynamic_cast(selectedItem()); + if(item) + config->writeEntry("LastSelectedItem", item->id()); + else + config->writeEntry("LastSelectedItem", 0); + + TQValueList openFolders; + TQListViewItemIterator it(this); + for( ; it.current(); ++it) + { + item = dynamic_cast(it.current()); + if(item && isOpen(item)) + openFolders.push_back(item->id()); + } + config->writeEntry("OpenFolders", openFolders); +} + +} // NameSpace Digikam diff --git a/src/libs/imageproperties/talbumlistview.h b/src/libs/imageproperties/talbumlistview.h new file mode 100644 index 00000000..b483706c --- /dev/null +++ b/src/libs/imageproperties/talbumlistview.h @@ -0,0 +1,109 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-18-12 + * Description : A list view to display digiKam Tags. + * + * Copyright (C) 2006-2009 by Gilles Caulier + * Copyright (C) 2009 by Andi Clemens + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef TALBUMLISTVIEW_H +#define TALBUMLISTVIEW_H + +// Local includes. + +#include "digikam_export.h" +#include "metadatahub.h" +#include "folderitem.h" +#include "folderview.h" + +class TQDropEvent; +class TQMouseEvent; + +namespace Digikam +{ +class TAlbum; + +class DIGIKAM_EXPORT TAlbumCheckListItem : public FolderCheckListItem +{ +public: + + TAlbumCheckListItem(TQListView* parent, TAlbum* album); + + TAlbumCheckListItem(TQCheckListItem* parent, TAlbum* album); + + void setStatus(MetadataHub::TagStatus status); + void refresh(); + void setOpen(bool o); + TAlbum* album() const; + int id() const; + void setCount(int count); + int count(); + +private : + + void stateChange(bool val); + +private : + + int m_count; + + TAlbum *m_album; +}; + +// ------------------------------------------------------------------------ + +class DIGIKAM_EXPORT TAlbumListView : public FolderView +{ + TQ_OBJECT + + +public: + + TAlbumListView(TQWidget* parent); + ~TAlbumListView(); + + void stateChanged(TAlbumCheckListItem *item); + void refresh(); + void loadViewState(); + +signals: + + void signalProgressBarMode(int, const TQString&); + void signalProgressValue(int); + void signalItemStateChanged(TAlbumCheckListItem *item); + +protected: + + bool acceptDrop(const TQDropEvent *e) const; + void contentsDropEvent(TQDropEvent *e); + + TQDragObject* dragObject(); + +private slots: + + void slotRefresh(const TQMap&); + +private: + + void saveViewState(); +}; + +} // NameSpace Digikam + +#endif // TALBUMLISTVIEW_H diff --git a/src/libs/jpegutils/Makefile.am b/src/libs/jpegutils/Makefile.am new file mode 100644 index 00000000..3c07aa5a --- /dev/null +++ b/src/libs/jpegutils/Makefile.am @@ -0,0 +1,22 @@ +METASOURCES = AUTO + +# --enable-final triggers: http://bugs.kde.org/show_bug.cgi?id=126326 +# digikam: camera download: auto-rotated images lose EXIF info ... +# So make sure nofinal is always used here! +KDE_OPTIONS = nofinal + +INCLUDES = $(all_includes) \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(LIBKEXIV2_CFLAGS) + + +noinst_LTLIBRARIES = libjpegutils.la + +libjpegutils_la_SOURCES = jpegutils.cpp transupp.cpp + +libjpegutils_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +libjpegutils_la_LIBADD = $(LIBJPEG) diff --git a/src/libs/jpegutils/jinclude.h b/src/libs/jpegutils/jinclude.h new file mode 100644 index 00000000..adee51e0 --- /dev/null +++ b/src/libs/jpegutils/jinclude.h @@ -0,0 +1,90 @@ +/* + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/src/libs/jpegutils/jpegint.h b/src/libs/jpegutils/jpegint.h new file mode 100644 index 00000000..bf01aa3e --- /dev/null +++ b/src/libs/jpegutils/jpegint.h @@ -0,0 +1,811 @@ +#if JPEG_LIB_VERSION >= 80 + +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Ensuring definition INT32 */ +#ifndef INT32 +#define INT32 TQ_INT32 +#endif + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +typedef JMETHOD(void, forward_DCT_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); + +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* It is useful to allow each component to have a separate FDCT method. */ + forward_DCT_ptr forward_DCT[MAX_COMPONENTS]; +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_arith_encoder jIAEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_arith_decoder jIADecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#define jpeg_natural_order7 jZAGTable7 +#define jpeg_natural_order6 jZAGTable6 +#define jpeg_natural_order5 jZAGTable5 +#define jpeg_natural_order4 jZAGTable4 +#define jpeg_natural_order3 jZAGTable3 +#define jpeg_natural_order2 jZAGTable2 +#define jpeg_aritab jAriTab +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_arith_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_arith_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ +extern const int jpeg_natural_order7[]; /* zz to natural order for 7x7 block */ +extern const int jpeg_natural_order6[]; /* zz to natural order for 6x6 block */ +extern const int jpeg_natural_order5[]; /* zz to natural order for 5x5 block */ +extern const int jpeg_natural_order4[]; /* zz to natural order for 4x4 block */ +extern const int jpeg_natural_order3[]; /* zz to natural order for 3x3 block */ +extern const int jpeg_natural_order2[]; /* zz to natural order for 2x2 block */ + +/* Arithmetic coding probability estimation tables in jaricom.c */ +extern const INT32 jpeg_aritab[]; + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +#else // JPEG_LIB_VERSION >= 80 + +/* + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ + +#endif // JPEG_LIB_VERSION >= 80 diff --git a/src/libs/jpegutils/jpegutils.cpp b/src/libs/jpegutils/jpegutils.cpp new file mode 100644 index 00000000..2f2aabb6 --- /dev/null +++ b/src/libs/jpegutils/jpegutils.cpp @@ -0,0 +1,532 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-29 + * Description : perform lossless rotation/flip to JPEG file + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#define XMD_H + +// C++ includes. + +#include +#include + +// C Ansi includes. + +extern "C" +{ +#include +#include +#include +#include +#include +#include +} + +// TQt includes. + +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "dmetadata.h" +#include "transupp.h" +#include "jpegutils.h" + +namespace Digikam +{ + +// To manage Errors/Warnings handling provide by libjpeg + +//#define ENABLE_DEBUG_MESSAGES + +struct jpegutils_jpeg_error_mgr : public jpeg_error_mgr +{ + jmp_buf setjmp_buffer; +}; + +static void jpegutils_jpeg_error_exit(j_common_ptr cinfo); +static void jpegutils_jpeg_emit_message(j_common_ptr cinfo, int msg_level); +static void jpegutils_jpeg_output_message(j_common_ptr cinfo); + +static void jpegutils_jpeg_error_exit(j_common_ptr cinfo) +{ + jpegutils_jpeg_error_mgr* myerr = (jpegutils_jpeg_error_mgr*) cinfo->err; + + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << endl; +#endif + + longjmp(myerr->setjmp_buffer, 1); +} + +static void jpegutils_jpeg_emit_message(j_common_ptr cinfo, int msg_level) +{ + Q_UNUSED(msg_level) + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << " (" << msg_level << ")" << endl; +#endif +} + +static void jpegutils_jpeg_output_message(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo, buffer); + +#ifdef ENABLE_DEBUG_MESSAGES + DDebug() << k_funcinfo << buffer << endl; +#endif +} + +bool loadJPEGScaled(TQImage& image, const TQString& path, int maximumSize) +{ + TQString format = TQImageIO::imageFormat(path); + if (format !="JPEG") return false; + + FILE* inputFile=fopen(TQFile::encodeName(path), "rb"); + if(!inputFile) + return false; + + struct jpeg_decompress_struct cinfo; + struct jpegutils_jpeg_error_mgr jerr; + + // JPEG error handling - thanks to Marcus Meissner + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = jpegutils_jpeg_error_exit; + cinfo.err->emit_message = jpegutils_jpeg_emit_message; + cinfo.err->output_message = jpegutils_jpeg_output_message; + + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + fclose(inputFile); + return false; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, inputFile); + jpeg_read_header(&cinfo, true); + + int imgSize = TQMAX(cinfo.image_width, cinfo.image_height); + + // libjpeg supports 1/1, 1/2, 1/4, 1/8 + int scale=1; + while(maximumSize*scale*2<=imgSize) + { + scale*=2; + } + if(scale>8) scale=8; + + cinfo.scale_num=1; + cinfo.scale_denom=scale; + + switch (cinfo.jpeg_color_space) + { + case JCS_UNKNOWN: + break; + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo.out_color_space = JCS_CMYK; + break; + } + + jpeg_start_decompress(&cinfo); + + TQImage img; + + // We only take RGB with 1 or 3 components, or CMYK with 4 components + if (!( + (cinfo.out_color_space == JCS_RGB && (cinfo.output_components == 3 || cinfo.output_components == 1)) + || (cinfo.out_color_space == JCS_CMYK && cinfo.output_components == 4) + )) + { + jpeg_destroy_decompress(&cinfo); + fclose(inputFile); + return false; + } + + switch(cinfo.output_components) + { + case 3: + case 4: + img.create( cinfo.output_width, cinfo.output_height, 32 ); + break; + case 1: // B&W image + img.create( cinfo.output_width, cinfo.output_height, 8, 256 ); + for (int i = 0 ; i < 256 ; i++) + img.setColor(i, tqRgb(i, i, i)); + break; + } + + uchar** lines = img.jumpTable(); + while (cinfo.output_scanline < cinfo.output_height) + jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height); + + jpeg_finish_decompress(&cinfo); + + // Expand 24->32 bpp + if ( cinfo.output_components == 3 ) + { + for (uint j=0; jerror_exit = jpegutils_jpeg_error_exit; + srcinfo.err->emit_message = jpegutils_jpeg_emit_message; + srcinfo.err->output_message = jpegutils_jpeg_output_message; + + // Initialize the JPEG compression object with default error handling + dstinfo.err = jpeg_std_error(&jdsterr); + dstinfo.err->error_exit = jpegutils_jpeg_error_exit; + dstinfo.err->emit_message = jpegutils_jpeg_emit_message; + dstinfo.err->output_message = jpegutils_jpeg_output_message; + + FILE *input_file; + FILE *output_file; + + input_file = fopen(in, "rb"); + if (!input_file) + { + DWarning() << "ExifRotate: Error in opening input file: " << input_file << endl; + return false; + } + + output_file = fopen(out, "wb"); + if (!output_file) + { + fclose(input_file); + DWarning() << "ExifRotate: Error in opening output file: " << output_file << endl; + return false; + } + + if (setjmp(jsrcerr.setjmp_buffer) || setjmp(jdsterr.setjmp_buffer)) + { + jpeg_destroy_decompress(&srcinfo); + jpeg_destroy_compress(&dstinfo); + fclose(input_file); + fclose(output_file); + return false; + } + + jpeg_create_decompress(&srcinfo); + jpeg_create_compress(&dstinfo); + + jpeg_stdio_src(&srcinfo, input_file); + jcopy_markers_setup(&srcinfo, copyoption); + + (void) jpeg_read_header(&srcinfo, true); + + jtransform_request_workspace(&srcinfo, &transformoption); + + // Read source file as DCT coefficients + src_coef_arrays = jpeg_read_coefficients(&srcinfo); + + // Initialize destination compression parameters from source values + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, + src_coef_arrays, &transformoption); + + // Specify data destination for compression + jpeg_stdio_dest(&dstinfo, output_file); + + // Start compressor (note no image data is actually written here) + jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + // Copy to the output file any extra markers that we want to preserve + jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); + + jtransform_execute_transformation(&srcinfo, &dstinfo, + src_coef_arrays, &transformoption); + + // Finish compression and release memory + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); + (void) jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + fclose(input_file); + fclose(output_file); + + // -- Metadata operations ------------------------------------------------------ + + // Reset the Exif orientation tag of the temp image to normal + DDebug() << "ExifRotate: set Orientation tag to normal: " << file << endl; + + metaData.load(temp); + metaData.setImageOrientation(DMetadata::ORIENTATION_NORMAL); + TQImage img(temp); + + // Get the new image dimension of the temp image. Using a dummy TQImage objet here + // has a sense because the Exif dimension information can be missing from original image. + // Get new dimensions with TQImage will always work... + metaData.setImageDimensions(img.size()); + + // Update the image thumbnail. + TQImage thumb = img.scale(160, 120, TQImage::ScaleMin); + metaData.setExifThumbnail(thumb); + + // Update Exif Document Name tag (the orinal file name from camera for example). + metaData.setExifTagString("Exif.Image.DocumentName", documentName); + + // We update all new metadata now... + metaData.applyChanges(); + + // ----------------------------------------------------------------------------- + // set the file modification time of the temp file to that + // of the original file + struct stat st; + stat(in, &st); + + struct utimbuf ut; + ut.modtime = st.st_mtime; + ut.actime = st.st_atime; + + utime(out, &ut); + + // now overwrite the original file + if (rename(out, in) == 0) + { + return true; + } + else + { + // moving failed. unlink the temp file + unlink(out); + return false; + } + } + + // Not a jpeg image. + DDebug() << "ExifRotate: not a JPEG file: " << file << endl; + return false; +} + +bool jpegConvert(const TQString& src, const TQString& dest, const TQString& documentName, const TQString& format) +{ + TQFileInfo fi(src); + if (!fi.exists()) + { + DDebug() << "JpegConvert: file do not exist: " << src << endl; + return false; + } + + if (isJpegImage(src)) + { + DImg image(src); + + // Get image Exif/Iptc data. + DMetadata meta; + meta.setExif(image.getExif()); + meta.setIptc(image.getIptc()); + + // Update Iptc preview. + TQImage preview = image.smoothScale(1280, 1024, TQSize::ScaleMin).copyTQImage(); + + // TODO: see B.K.O #130525. a JPEG segment is limited to 64K. If the IPTC byte array is + // bigger than 64K duing of image preview tag size, the target JPEG image will be + // broken. Note that IPTC image preview tag is limited to 256K!!! + // Temp. solution to disable IPTC preview record in JPEG file until a right solution + // will be found into Exiv2. + // Note : There is no limitation with TIFF and PNG about IPTC byte array size. + + if (format.upper() != TQString("JPG") && format.upper() != TQString("JPEG") && + format.upper() != TQString("JPE")) + meta.setImagePreview(preview); + + // Update Exif thumbnail. + TQImage thumb = preview.smoothScale(160, 120, TQImage::ScaleMin); + meta.setExifThumbnail(thumb); + + // Update Exif Document Name tag (the orinal file name from camera for example). + meta.setExifTagString("Exif.Image.DocumentName", documentName); + + // Store new Exif/Iptc data into image. + image.setExif(meta.getExif()); + image.setIptc(meta.getIptc()); + + // And now save the image to a new file format. + + if ( format.upper() == TQString("PNG") ) + image.setAttribute("quality", 9); + + if ( format.upper() == TQString("TIFF") || format.upper() == TQString("TIF") ) + image.setAttribute("compress", true); + + return (image.save(dest, format)); + } + + return false; +} + +bool isJpegImage(const TQString& file) +{ + // Check if the file is an JPEG image + TQString format = TQString(TQImage::imageFormat(file)).upper(); + DDebug() << "mimetype = " << format << endl; + if (format !="JPEG") return false; + + return true; +} + +} // Namespace Digikam diff --git a/src/libs/jpegutils/jpegutils.h b/src/libs/jpegutils/jpegutils.h new file mode 100644 index 00000000..ad384770 --- /dev/null +++ b/src/libs/jpegutils/jpegutils.h @@ -0,0 +1,44 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-29 + * Description : perform lossless rotation/flip to JPEG file + * + * Copyright (C) 2004-2005 by Renchi Raju + * Copyright (C) 2006-2009 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef JPEGUTILS_H +#define JPEGUTILS_H + +// TQt includes. + +#include +#include + +namespace Digikam +{ + +bool loadJPEGScaled(TQImage& image, const TQString& path, int maximumSize); +bool exifRotate(const TQString& file, const TQString& documentName); +bool jpegConvert(const TQString& src, const TQString& dest, const TQString& documentName, + const TQString& format=TQString("PNG")); +bool isJpegImage(const TQString& file); + +} + +#endif /* JPEGUTILS_H */ diff --git a/src/libs/jpegutils/libjpeg62.README b/src/libs/jpegutils/libjpeg62.README new file mode 100644 index 00000000..a18979ac --- /dev/null +++ b/src/libs/jpegutils/libjpeg62.README @@ -0,0 +1,385 @@ +The Independent JPEG Group's JPEG software +========================================== + +README for release 6b of 27-Mar-1998 +==================================== + +This distribution contains the sixth public release of the Independent JPEG +Group's free JPEG software. You are welcome to redistribute this software and +to use it for any purpose, subject to the conditions under LEGAL ISSUES, below. + +Serious users of this software (particularly those incorporating it into +larger programs) should contact IJG at jpeg-info@uunet.uu.net to be added to +our electronic mailing list. Mailing list members are notified of updates +and have a chance to participate in technical discussions, etc. + +This software is the work of Tom Lane, Philip Gladstone, Jim Boucher, +Lee Crocker, Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, +Guido Vollbeding, Ge' Weijers, and other members of the Independent JPEG +Group. + +IJG is not affiliated with the official ISO JPEG standards committee. + + +DOCUMENTATION ROADMAP +===================== + +This file contains the following sections: + +OVERVIEW General description of JPEG and the IJG software. +LEGAL ISSUES Copyright, lack of warranty, terms of distribution. +REFERENCES Where to learn more about JPEG. +ARCHIVE LOCATIONS Where to find newer versions of this software. +RELATED SOFTWARE Other stuff you should get. +FILE FORMAT WARS Software *not* to get. +TO DO Plans for future IJG releases. + +Other documentation files in the distribution are: + +User documentation: + install.doc How to configure and install the IJG software. + usage.doc Usage instructions for cjpeg, djpeg, jpegtran, + rdjpgcom, and wrjpgcom. + *.1 Unix-style man pages for programs (same info as usage.doc). + wizard.doc Advanced usage instructions for JPEG wizards only. + change.log Version-to-version change highlights. +Programmer and internal documentation: + libjpeg.doc How to use the JPEG library in your own programs. + example.c Sample code for calling the JPEG library. + structure.doc Overview of the JPEG library's internal structure. + filelist.doc Road map of IJG files. + coderules.doc Coding style rules --- please read if you contribute code. + +Please read at least the files install.doc and usage.doc. Useful information +can also be found in the JPEG FAQ (Frequently Asked Questions) article. See +ARCHIVE LOCATIONS below to find out where to obtain the FAQ article. + +If you want to understand how the JPEG code works, we suggest reading one or +more of the REFERENCES, then looking at the documentation files (in roughly +the order listed) before diving into the code. + + +OVERVIEW +======== + +This package contains C software to implement JPEG image compression and +decompression. JPEG (pronounced "jay-peg") is a standardized compression +method for full-color and gray-scale images. JPEG is intended for compressing +"real-world" scenes; line drawings, cartoons and other non-realistic images +are not its strong suit. JPEG is lossy, meaning that the output image is not +exactly identical to the input image. Hence you must not use JPEG if you +have to have identical output bits. However, on typical photographic images, +very good compression levels can be obtained with no visible change, and +remarkably high compression levels are possible if you can tolerate a +low-quality image. For more details, see the references, or just experiment +with various compression settings. + +This software implements JPEG baseline, extended-sequential, and progressive +compression processes. Provision is made for supporting all variants of these +processes, although some uncommon parameter settings aren't implemented yet. +For legal reasons, we are not distributing code for the arithmetic-coding +variants of JPEG; see LEGAL ISSUES. We have made no provision for supporting +the hierarchical or lossless processes defined in the standard. + +We provide a set of library routines for reading and writing JPEG image files, +plus two sample applications "cjpeg" and "djpeg", which use the library to +perform conversion between JPEG and some other popular image file formats. +The library is intended to be reused in other applications. + +In order to support file conversion and viewing software, we have included +considerable functionality beyond the bare JPEG coding/decoding capability; +for example, the color quantization modules are not strictly part of JPEG +decoding, but they are essential for output to colormapped file formats or +colormapped displays. These extra functions can be compiled out of the +library if not required for a particular application. We have also included +"jpegtran", a utility for lossless transcoding between different JPEG +processes, and "rdjpgcom" and "wrjpgcom", two simple applications for +inserting and extracting textual comments in JFIF files. + +The emphasis in designing this software has been on achieving portability and +flexibility, while also making it fast enough to be useful. In particular, +the software is not intended to be read as a tutorial on JPEG. (See the +REFERENCES section for introductory material.) Rather, it is intended to +be reliable, portable, industrial-strength code. We do not claim to have +achieved that goal in every aspect of the software, but we strive for it. + +We welcome the use of this software as a component of commercial products. +No royalty is required, but we do ask for an acknowledgement in product +documentation, as described under LEGAL ISSUES. + + +LEGAL ISSUES +============ + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, +with respect to this software, its quality, accuracy, merchantability, or +fitness for a particular purpose. This software is provided "AS IS", and you, +its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-1998, Thomas G. Lane. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + + +ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, +sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. +ansi2knr.c is NOT covered by the above copyright and conditions, but instead +by the usual distribution terms of the Free Software Foundation; principally, +that you must include source code if you redistribute it. (See the file +ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part +of any program generated from the IJG code, this does not limit you more than +the foregoing paragraphs do. + +The Unix configuration script "configure" was produced with GNU Autoconf. +It is copyright by the Free Software Foundation but is freely distributable. +The same holds for its supporting scripts (config.guess, config.sub, +ltconfig, ltmain.sh). Another support script, install-sh, is copyright +by M.I.T. but is also freely distributable. + +It appears that the arithmetic coding option of the JPEG spec is covered by +patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot +legally be used without obtaining one or more licenses. For this reason, +support for arithmetic coding has been removed from the free JPEG software. +(Since arithmetic coding provides only a marginal gain over the unpatented +Huffman mode, it is unlikely that very many implementations will support it.) +So far as we are aware, there are no patent restrictions on the remaining +code. + +The IJG distribution formerly included code to read and write GIF files. +To avoid entanglement with the Unisys LZW patent, GIF reading support has +been removed altogether, and the GIF writer has been simplified to produce +"uncompressed GIFs". This technique does not use the LZW algorithm; the +resulting GIF files are larger than usual, but are readable by all standard +GIF decoders. + +We are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + + +REFERENCES +========== + +We highly recommend reading one or more of these references before trying to +understand the innards of the JPEG software. + +The best short technical introduction to the JPEG compression algorithm is + Wallace, Gregory K. "The JPEG Still Image Compression Standard", + Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44. +(Adjacent articles in that issue discuss MPEG motion picture compression, +applications of JPEG, and related topics.) If you don't have the CACM issue +handy, a PostScript file containing a revised version of Wallace's article is +available at ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz. The file (actually +a preprint for an article that appeared in IEEE Trans. Consumer Electronics) +omits the sample images that appeared in CACM, but it includes corrections +and some added material. Note: the Wallace article is copyright ACM and IEEE, +and it may not be used for commercial purposes. + +A somewhat less technical, more leisurely introduction to JPEG can be found in +"The Data Compression Book" by Mark Nelson and Jean-loup Gailly, published by +M&T Books (New York), 2nd ed. 1996, ISBN 1-55851-434-1. This book provides +good explanations and example C code for a multitude of compression methods +including JPEG. It is an excellent source if you are comfortable reading C +code but don't know much about data compression in general. The book's JPEG +sample code is far from industrial-strength, but when you are ready to look +at a full implementation, you've got one here... + +The best full description of JPEG is the textbook "JPEG Still Image Data +Compression Standard" by William B. Pennebaker and Joan L. Mitchell, published +by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1. Price US$59.95, 638 pp. +The book includes the complete text of the ISO JPEG standards (DIS 10918-1 +and draft DIS 10918-2). This is by far the most complete exposition of JPEG +in existence, and we highly recommend it. + +The JPEG standard itself is not available electronically; you must order a +paper copy through ISO or ITU. (Unless you feel a need to own a certified +official copy, we recommend buying the Pennebaker and Mitchell book instead; +it's much cheaper and includes a great deal of useful explanatory material.) +In the USA, copies of the standard may be ordered from ANSI Sales at (212) +642-4900, or from Global Engineering Documents at (800) 854-7179. (ANSI +doesn't take credit card orders, but Global does.) It's not cheap: as of +1992, ANSI was charging $95 for Part 1 and $47 for Part 2, plus 7% +shipping/handling. The standard is divided into two parts, Part 1 being the +actual specification, while Part 2 covers compliance testing methods. Part 1 +is titled "Digital Compression and Coding of Continuous-tone Still Images, +Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS +10918-1, ITU-T T.81. Part 2 is titled "Digital Compression and Coding of +Continuous-tone Still Images, Part 2: Compliance testing" and has document +numbers ISO/IEC IS 10918-2, ITU-T T.83. + +Some extensions to the original JPEG standard are defined in JPEG Part 3, +a newer ISO standard numbered ISO/IEC IS 10918-3 and ITU-T T.84. IJG +currently does not support any Part 3 extensions. + +The JPEG standard does not specify all details of an interchangeable file +format. For the omitted details we follow the "JFIF" conventions, revision +1.02. A copy of the JFIF spec is available from: + Literature Department + C-Cube Microsystems, Inc. + 1778 McCarthy Blvd. + Milpitas, CA 95035 + phone (408) 944-6300, fax (408) 944-6314 +A PostScript version of this document is available by FTP at +ftp://ftp.uu.net/graphics/jpeg/jfif.ps.gz. There is also a plain text +version at ftp://ftp.uu.net/graphics/jpeg/jfif.txt.gz, but it is missing +the figures. + +The TIFF 6.0 file format specification can be obtained by FTP from +ftp://ftp.sgi.com/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation scheme +found in the TIFF 6.0 spec of 3-June-92 has a number of serious problems. +IJG does not recommend use of the TIFF 6.0 design (TIFF Compression tag 6). +Instead, we recommend the JPEG design proposed by TIFF Technical Note #2 +(Compression tag 7). Copies of this Note can be obtained from ftp.sgi.com or +from ftp://ftp.uu.net/graphics/jpeg/. It is expected that the next revision +of the TIFF spec will replace the 6.0 JPEG design with the Note's design. +Although IJG's own code does not support TIFF/JPEG, the free libtiff library +uses our library to implement TIFF/JPEG per the Note. libtiff is available +from ftp://ftp.sgi.com/graphics/tiff/. + + +ARCHIVE LOCATIONS +================= + +The "official" archive site for this software is ftp.uu.net (Internet +address 192.48.96.9). The most recent released version can always be found +there in directory graphics/jpeg. This particular version will be archived +as ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz. If you don't have +direct Internet access, UUNET's archives are also available via UUCP; contact +help@uunet.uu.net for information on retrieving files that way. + +Numerous Internet sites maintain copies of the UUNET files. However, only +ftp.uu.net is guaranteed to have the latest official version. + +You can also obtain this software in DOS-compatible "zip" archive format from +the SimTel archives (ftp://ftp.simtel.net/pub/simtelnet/msdos/graphics/), or +on CompuServe in the Graphics Support forum (GO CIS:GRAPHSUP), library 12 +"JPEG Tools". Again, these versions may sometimes lag behind the ftp.uu.net +release. + +The JPEG FAQ (Frequently Asked Questions) article is a useful source of +general information about JPEG. It is updated constantly and therefore is +not included in this distribution. The FAQ is posted every two weeks to +Usenet newsgroups comp.graphics.misc, news.answers, and other groups. +It is available on the World Wide Web at http://www.faqs.org/faqs/jpeg-faq/ +and other news.answers archive sites, including the official news.answers +archive at rtfm.mit.edu: ftp://rtfm.mit.edu/pub/usenet/news.answers/jpeg-faq/. +If you don't have Web or FTP access, send e-mail to mail-server@rtfm.mit.edu +with body + send usenet/news.answers/jpeg-faq/part1 + send usenet/news.answers/jpeg-faq/part2 + + +RELATED SOFTWARE +================ + +Numerous viewing and image manipulation programs now support JPEG. (Quite a +few of them use this library to do so.) The JPEG FAQ described above lists +some of the more popular free and shareware viewers, and tells where to +obtain them on Internet. + +If you are on a Unix machine, we highly recommend Jef Poskanzer's free +PBMPLUS software, which provides many useful operations on PPM-format image +files. In particular, it can convert PPM images to and from a wide range of +other formats, thus making cjpeg/djpeg considerably more useful. The latest +version is distributed by the NetPBM group, and is available from numerous +sites, notably ftp://wuarchive.wustl.edu/graphics/graphics/packages/NetPBM/. +Unfortunately PBMPLUS/NETPBM is not nearly as portable as the IJG software is; +you are likely to have difficulty making it work on any non-Unix machine. + +A different free JPEG implementation, written by the PVRG group at Stanford, +is available from ftp://havefun.stanford.edu/pub/jpeg/. This program +is designed for research and experimentation rather than production use; +it is slower, harder to use, and less portable than the IJG code, but it +is easier to read and modify. Also, the PVRG code supports lossless JPEG, +which we do not. (On the other hand, it doesn't do progressive JPEG.) + + +FILE FORMAT WARS +================ + +Some JPEG programs produce files that are not compatible with our library. +The root of the problem is that the ISO JPEG committee failed to specify a +concrete file format. Some vendors "filled in the blanks" on their own, +creating proprietary formats that no one else could read. (For example, none +of the early commercial JPEG implementations for the Macintosh were able to +exchange compressed files.) + +The file format we have adopted is called JFIF (see REFERENCES). This format +has been agreed to by a number of major commercial JPEG vendors, and it has +become the de facto standard. JFIF is a minimal or "low end" representation. +We recommend the use of TIFF/JPEG (TIFF revision 6.0 as modified by TIFF +Technical Note #2) for "high end" applications that need to record a lot of +additional data about an image. TIFF/JPEG is fairly new and not yet widely +supported, unfortunately. + +The upcoming JPEG Part 3 standard defines a file format called SPIFF. +SPIFF is interoperable with JFIF, in the sense that most JFIF decoders should +be able to read the most common variant of SPIFF. SPIFF has some technical +advantages over JFIF, but its major claim to fame is simply that it is an +official standard rather than an informal one. At this point it is unclear +whether SPIFF will supersede JFIF or whether JFIF will remain the de-facto +standard. IJG intends to support SPIFF once the standard is frozen, but we +have not decided whether it should become our default output format or not. +(In any case, our decoder will remain capable of reading JFIF indefinitely.) + +Various proprietary file formats incorporating JPEG compression also exist. +We have little or no sympathy for the existence of these formats. Indeed, +one of the original reasons for developing this free software was to help +force convergence on common, open format standards for JPEG files. Don't +use a proprietary file format! + + +TO DO +===== + +The major thrust for v7 will probably be improvement of visual quality. +The current method for scaling the quantization tables is known not to be +very good at low Q values. We also intend to investigate block boundary +smoothing, "poor man's variable quantization", and other means of improving +quality-vs-file-size performance without sacrificing compatibility. + +In future versions, we are considering supporting some of the upcoming JPEG +Part 3 extensions --- principally, variable quantization and the SPIFF file +format. + +As always, speeding things up is of great interest. + +Please send bug reports, offers of help, etc. to jpeg-info@uunet.uu.net. diff --git a/src/libs/jpegutils/transupp.cpp b/src/libs/jpegutils/transupp.cpp new file mode 100644 index 00000000..47a9aa8b --- /dev/null +++ b/src/libs/jpegutils/transupp.cpp @@ -0,0 +1,2527 @@ +/* Although this file really shouldn't have access to the library internals, + * it's helpful to let it call jround_up() and jcopy_block_row(). + */ +#define JPEG_INTERNALS + +// LibJPEG includes. + +extern "C" +{ +#include "jinclude.h" +#include "jpeglib.h" +} + +#if JPEG_LIB_VERSION >= 80 + +/* + * transupp.c + * + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +#include "transupp.h" /* My own external interface */ +#include /* to declare isdigit() */ + +namespace Digikam +{ + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature, + * and to Ben Jackson for introducing the cropping feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * If cropping or trimming is involved, the destination arrays may be smaller + * than the source arrays. Note it is not possible to do horizontal flip + * in-place when a nonzero Y crop offset is specified, since we'd have to move + * data from one block row to another but the virtual array manager doesn't + * guarantee we can touch more than one row at a time. So in that case, + * we have to use a separate destination array. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. When "crop" is in effect, the destination's dimensions will be the + * cropped values but the source's will be uncropped. Each transform + * routine is responsible for picking up source data starting at the + * correct X and Y offset for the crop region. (The X and Y offsets + * passed to the transform routines are measured in iMCU blocks of the + * destination.) + * 6. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + */ + + +LOCAL(void) +do_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Crop. This is only used when no rotate/flip is requested with the crop. */ +{ + JDIMENSION dst_blk_y, x_crop_blocks, y_crop_blocks; + int ci, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + jpeg_component_info *compptr; + + /* We simply have to copy the right amount of data (the destination's + * image size) starting at the given X and Y offsets in the source. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, + dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } +} + + +LOCAL(void) +do_flip_h_no_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required. + * NB: this only works when y_crop_offset is zero. + */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y, x_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + /* Do the mirroring */ + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + if (x_crop_blocks > 0) { + /* Now left-justify the portion of the data to be kept. + * We can't use a single jcopy_block_row() call because that routine + * depends on memcpy(), whose behavior is unspecified for overlapping + * source and destination areas. Sigh. + */ + for (blk_x = 0; blk_x < compptr->width_in_blocks; blk_x++) { + jcopy_block_row(buffer[offset_y] + blk_x + x_crop_blocks, + buffer[offset_y] + blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Horizontal flip in general cropping case */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, k, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Here we must output into a separate array because we can't touch + * different rows of a single virtual array simultaneously. Otherwise, + * this is essentially the same as the routine above. + */ + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Do the mirrorable blocks */ + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + *dst_ptr++ = *src_ptr++; /* copy even column */ + *dst_ptr++ = - *src_ptr++; /* copy odd column with sign change */ + } + } else { + /* Copy last partial block(s) verbatim */ + jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, + dst_row_ptr + dst_blk_x, + (JDIMENSION) 1); + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = srcinfo->output_height / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - y_crop_blocks - dst_blk_y - + (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + src_row_ptr += x_crop_blocks; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y] + x_crop_blocks, + dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y, x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = srcinfo->output_height / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_width - x_crop_blocks - dst_blk_x - + (JDIMENSION) compptr->h_samp_factor, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } else { + /* Edge blocks are transposed but not mirrored. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = srcinfo->output_width / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = srcinfo->output_width / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_height / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - y_crop_blocks - dst_blk_y - + (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + dst_row_ptr = dst_buffer[offset_y]; + if (y_crop_blocks + dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Process the blocks that can be mirrored both ways. */ + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } else { + /* Any remaining right-edge blocks are only mirrored vertically. */ + src_ptr = src_row_ptr[x_crop_blocks + dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + src_row_ptr = src_buffer[offset_y]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Process the blocks that can be mirrored. */ + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } else { + /* Any remaining right-edge blocks are only copied. */ + jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks, + dst_row_ptr + dst_blk_x, + (JDIMENSION) 1); + } + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JDIMENSION x_crop_offset, JDIMENSION y_crop_offset, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + JDIMENSION x_crop_blocks, y_crop_blocks; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = srcinfo->output_height / + (dstinfo->max_h_samp_factor * dstinfo->min_DCT_h_scaled_size); + MCU_rows = srcinfo->output_width / + (dstinfo->max_v_samp_factor * dstinfo->min_DCT_v_scaled_size); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_width - x_crop_blocks - dst_blk_x - + (JDIMENSION) compptr->h_samp_factor, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } else { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + dst_blk_x + x_crop_blocks, + (JDIMENSION) compptr->h_samp_factor, FALSE); + } + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (y_crop_blocks + dst_blk_y < comp_height) { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + src_ptr = src_buffer[offset_x] + [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + if (x_crop_blocks + dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + src_ptr = src_buffer[offset_x] + [dst_blk_y + offset_y + y_crop_blocks]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Parse an unsigned integer: subroutine for jtransform_parse_crop_spec. + * Returns TRUE if valid integer found, FALSE if not. + * *strptr is advanced over the digit string, and *result is set to its value. + */ + +LOCAL(boolean) +jt_read_integer (const char ** strptr, JDIMENSION * result) +{ + const char * ptr = *strptr; + JDIMENSION val = 0; + + for (; isdigit(*ptr); ptr++) { + val = val * 10 + (JDIMENSION) (*ptr - '0'); + } + *result = val; + if (ptr == *strptr) + return FALSE; /* oops, no digits */ + *strptr = ptr; + return TRUE; +} + + +/* Parse a crop specification (written in X11 geometry style). + * The routine returns TRUE if the spec string is valid, FALSE if not. + * + * The crop spec string should have the format + * x{+-}{+-} + * where width, height, xoffset, and yoffset are unsigned integers. + * Each of the elements can be omitted to indicate a default value. + * (A weakness of this style is that it is not possible to omit xoffset + * while specifying yoffset, since they look alike.) + * + * This code is loosely based on XParseGeometry from the X11 distribution. + */ + +GLOBAL(boolean) +jtransform_parse_crop_spec (jpeg_transform_info *info, const char *spec) +{ + info->crop = FALSE; + info->crop_width_set = JCROP_UNSET; + info->crop_height_set = JCROP_UNSET; + info->crop_xoffset_set = JCROP_UNSET; + info->crop_yoffset_set = JCROP_UNSET; + + if (isdigit(*spec)) { + /* fetch width */ + if (! jt_read_integer(&spec, &info->crop_width)) + return FALSE; + info->crop_width_set = JCROP_POS; + } + if (*spec == 'x' || *spec == 'X') { + /* fetch height */ + spec++; + if (! jt_read_integer(&spec, &info->crop_height)) + return FALSE; + info->crop_height_set = JCROP_POS; + } + if (*spec == '+' || *spec == '-') { + /* fetch xoffset */ + info->crop_xoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; + spec++; + if (! jt_read_integer(&spec, &info->crop_xoffset)) + return FALSE; + } + if (*spec == '+' || *spec == '-') { + /* fetch yoffset */ + info->crop_yoffset_set = (*spec == '-') ? JCROP_NEG : JCROP_POS; + spec++; + if (! jt_read_integer(&spec, &info->crop_yoffset)) + return FALSE; + } + /* We had better have gotten to the end of the string. */ + if (*spec != '\0') + return FALSE; + info->crop = TRUE; + return TRUE; +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (jpeg_transform_info *info, JDIMENSION full_width) +{ + JDIMENSION MCU_cols; + + MCU_cols = info->output_width / info->iMCU_sample_width; + if (MCU_cols > 0 && info->x_crop_offset + MCU_cols == + full_width / info->iMCU_sample_width) + info->output_width = MCU_cols * info->iMCU_sample_width; +} + +LOCAL(void) +trim_bottom_edge (jpeg_transform_info *info, JDIMENSION full_height) +{ + JDIMENSION MCU_rows; + + MCU_rows = info->output_height / info->iMCU_sample_height; + if (MCU_rows > 0 && info->y_crop_offset + MCU_rows == + full_height / info->iMCU_sample_height) + info->output_height = MCU_rows * info->iMCU_sample_height; +} + + +/* Request any required workspace. + * + * This routine figures out the size that the output image will be + * (which implies that all the transform parameters must be set before + * it is called). + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + * + * This function returns FALSE right away if -perfect is given + * and transformation is not perfect. Otherwise returns TRUE. + */ + +GLOBAL(boolean) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays; + boolean need_workspace, transpose_it; + jpeg_component_info *compptr; + JDIMENSION xoffset, yoffset; + JDIMENSION width_in_iMCUs, height_in_iMCUs; + JDIMENSION width_in_blocks, height_in_blocks; + int ci, h_samp_factor, v_samp_factor; + + /* Determine number of components in output image */ + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) + /* We'll only process the first component */ + info->num_components = 1; + else + /* Process all the components */ + info->num_components = srcinfo->num_components; + + /* Compute output image dimensions and related values. */ + jpeg_core_output_dimensions(srcinfo); + + /* Return right away if -perfect is given and transformation is not perfect. + */ + if (info->perfect) { + if (info->num_components == 1) { + if (!jtransform_perfect_transform(srcinfo->output_width, + srcinfo->output_height, + srcinfo->min_DCT_h_scaled_size, + srcinfo->min_DCT_v_scaled_size, + info->transform)) + return FALSE; + } else { + if (!jtransform_perfect_transform(srcinfo->output_width, + srcinfo->output_height, + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size, + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size, + info->transform)) + return FALSE; + } + } + + /* If there is only one output component, force the iMCU size to be 1; + * else use the source iMCU size. (This allows us to do the right thing + * when reducing color to grayscale, and also provides a handy way of + * cleaning up "funny" grayscale images whose sampling factors are not 1x1.) + */ + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + info->output_width = srcinfo->output_height; + info->output_height = srcinfo->output_width; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_h_scaled_size; + } else { + info->iMCU_sample_width = + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + info->iMCU_sample_height = + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + } + break; + default: + info->output_width = srcinfo->output_width; + info->output_height = srcinfo->output_height; + if (info->num_components == 1) { + info->iMCU_sample_width = srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = srcinfo->min_DCT_v_scaled_size; + } else { + info->iMCU_sample_width = + srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size; + info->iMCU_sample_height = + srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size; + } + break; + } + + /* If cropping has been requested, compute the crop area's position and + * dimensions, ensuring that its upper left corner falls at an iMCU boundary. + */ + if (info->crop) { + /* Insert default values for unset crop parameters */ + if (info->crop_xoffset_set == JCROP_UNSET) + info->crop_xoffset = 0; /* default to +0 */ + if (info->crop_yoffset_set == JCROP_UNSET) + info->crop_yoffset = 0; /* default to +0 */ + if (info->crop_xoffset >= info->output_width || + info->crop_yoffset >= info->output_height) + ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); + if (info->crop_width_set == JCROP_UNSET) + info->crop_width = info->output_width - info->crop_xoffset; + if (info->crop_height_set == JCROP_UNSET) + info->crop_height = info->output_height - info->crop_yoffset; + /* Ensure parameters are valid */ + if (info->crop_width <= 0 || info->crop_width > info->output_width || + info->crop_height <= 0 || info->crop_height > info->output_height || + info->crop_xoffset > info->output_width - info->crop_width || + info->crop_yoffset > info->output_height - info->crop_height) + ERREXIT(srcinfo, JERR_BAD_CROP_SPEC); + /* Convert negative crop offsets into regular offsets */ + if (info->crop_xoffset_set == JCROP_NEG) + xoffset = info->output_width - info->crop_width - info->crop_xoffset; + else + xoffset = info->crop_xoffset; + if (info->crop_yoffset_set == JCROP_NEG) + yoffset = info->output_height - info->crop_height - info->crop_yoffset; + else + yoffset = info->crop_yoffset; + /* Now adjust so that upper left corner falls at an iMCU boundary */ + info->output_width = + info->crop_width + (xoffset % info->iMCU_sample_width); + info->output_height = + info->crop_height + (yoffset % info->iMCU_sample_height); + /* Save x/y offsets measured in iMCUs */ + info->x_crop_offset = xoffset / info->iMCU_sample_width; + info->y_crop_offset = yoffset / info->iMCU_sample_height; + } else { + info->x_crop_offset = 0; + info->y_crop_offset = 0; + } + + /* Figure out whether we need workspace arrays, + * and if so whether they are transposed relative to the source. + */ + need_workspace = FALSE; + transpose_it = FALSE; + switch (info->transform) { + case JXFORM_NONE: + if (info->x_crop_offset != 0 || info->y_crop_offset != 0) + need_workspace = TRUE; + /* No workspace needed if neither cropping nor transforming */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(info, srcinfo->output_width); + if (info->y_crop_offset != 0) + need_workspace = TRUE; + /* do_flip_h_no_crop doesn't need a workspace array */ + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(info, srcinfo->output_height); + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_TRANSPOSE: + /* transpose does NOT have to trim anything */ + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_TRANSVERSE: + if (info->trim) { + trim_right_edge(info, srcinfo->output_height); + trim_bottom_edge(info, srcinfo->output_width); + } + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_90: + if (info->trim) + trim_right_edge(info, srcinfo->output_height); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(info, srcinfo->output_width); + trim_bottom_edge(info, srcinfo->output_height); + } + /* Need workspace arrays having same dimensions as source image. */ + need_workspace = TRUE; + break; + case JXFORM_ROT_270: + if (info->trim) + trim_bottom_edge(info, srcinfo->output_width); + /* Need workspace arrays having transposed dimensions. */ + need_workspace = TRUE; + transpose_it = TRUE; + break; + } + + /* Allocate workspace if needed. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + if (need_workspace) { + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + width_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_width, + (long) info->iMCU_sample_width); + height_in_iMCUs = (JDIMENSION) + jdiv_round_up((long) info->output_height, + (long) info->iMCU_sample_height); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + if (info->num_components == 1) { + /* we're going to force samp factors to 1x1 in this case */ + h_samp_factor = v_samp_factor = 1; + } else if (transpose_it) { + h_samp_factor = compptr->v_samp_factor; + v_samp_factor = compptr->h_samp_factor; + } else { + h_samp_factor = compptr->h_samp_factor; + v_samp_factor = compptr->v_samp_factor; + } + width_in_blocks = width_in_iMCUs * h_samp_factor; + height_in_blocks = height_in_iMCUs * v_samp_factor; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor); + } + info->workspace_coef_arrays = coef_arrays; + } else + info->workspace_coef_arrays = NULL; + + return TRUE; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION jtemp; + UINT16 qtemp; + + /* Transpose image dimensions */ + jtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = jtemp; + itemp = dstinfo->min_DCT_h_scaled_size; + dstinfo->min_DCT_h_scaled_size = dstinfo->min_DCT_v_scaled_size; + dstinfo->min_DCT_v_scaled_size = itemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Adjust Exif image parameters. + * + * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. + */ + +LOCAL(void) +adjust_exif_parameters (JOCTET FAR * data, unsigned int length, + JDIMENSION new_width, JDIMENSION new_height) +{ + boolean is_motorola; /* Flag for byte order */ + unsigned int number_of_tags, tagnum; + unsigned int firstoffset, offset; + JDIMENSION new_value; + + if (length < 12) return; /* Length of an IFD entry */ + + /* Discover byte order */ + if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) + is_motorola = FALSE; + else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) + is_motorola = TRUE; + else + return; + + /* Check Tag Mark */ + if (is_motorola) { + if (GETJOCTET(data[2]) != 0) return; + if (GETJOCTET(data[3]) != 0x2A) return; + } else { + if (GETJOCTET(data[3]) != 0) return; + if (GETJOCTET(data[2]) != 0x2A) return; + } + + /* Get first IFD offset (offset to IFD0) */ + if (is_motorola) { + if (GETJOCTET(data[4]) != 0) return; + if (GETJOCTET(data[5]) != 0) return; + firstoffset = GETJOCTET(data[6]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[7]); + } else { + if (GETJOCTET(data[7]) != 0) return; + if (GETJOCTET(data[6]) != 0) return; + firstoffset = GETJOCTET(data[5]); + firstoffset <<= 8; + firstoffset += GETJOCTET(data[4]); + } + if (firstoffset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this IFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[firstoffset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset+1]); + } else { + number_of_tags = GETJOCTET(data[firstoffset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[firstoffset]); + } + if (number_of_tags == 0) return; + firstoffset += 2; + + /* Search for ExifSubIFD offset Tag in IFD0 */ + for (;;) { + if (firstoffset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[firstoffset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset+1]); + } else { + tagnum = GETJOCTET(data[firstoffset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[firstoffset]); + } + if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */ + if (--number_of_tags == 0) return; + firstoffset += 12; + } + + /* Get the ExifSubIFD offset */ + if (is_motorola) { + if (GETJOCTET(data[firstoffset+8]) != 0) return; + if (GETJOCTET(data[firstoffset+9]) != 0) return; + offset = GETJOCTET(data[firstoffset+10]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+11]); + } else { + if (GETJOCTET(data[firstoffset+11]) != 0) return; + if (GETJOCTET(data[firstoffset+10]) != 0) return; + offset = GETJOCTET(data[firstoffset+9]); + offset <<= 8; + offset += GETJOCTET(data[firstoffset+8]); + } + if (offset > length - 2) return; /* check end of data segment */ + + /* Get the number of directory entries contained in this SubIFD */ + if (is_motorola) { + number_of_tags = GETJOCTET(data[offset]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset+1]); + } else { + number_of_tags = GETJOCTET(data[offset+1]); + number_of_tags <<= 8; + number_of_tags += GETJOCTET(data[offset]); + } + if (number_of_tags < 2) return; + offset += 2; + + /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */ + do { + if (offset > length - 12) return; /* check end of data segment */ + /* Get Tag number */ + if (is_motorola) { + tagnum = GETJOCTET(data[offset]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset+1]); + } else { + tagnum = GETJOCTET(data[offset+1]); + tagnum <<= 8; + tagnum += GETJOCTET(data[offset]); + } + if (tagnum == 0xA002 || tagnum == 0xA003) { + if (tagnum == 0xA002) + new_value = new_width; /* ExifImageWidth Tag */ + else + new_value = new_height; /* ExifImageHeight Tag */ + if (is_motorola) { + data[offset+2] = 0; /* Format = unsigned long (4 octets) */ + data[offset+3] = 4; + data[offset+4] = 0; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 1; + data[offset+8] = 0; + data[offset+9] = 0; + data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+11] = (JOCTET)(new_value & 0xFF); + } else { + data[offset+2] = 4; /* Format = unsigned long (4 octets) */ + data[offset+3] = 0; + data[offset+4] = 1; /* Number Of Components = 1 */ + data[offset+5] = 0; + data[offset+6] = 0; + data[offset+7] = 0; + data[offset+8] = (JOCTET)(new_value & 0xFF); + data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF); + data[offset+10] = 0; + data[offset+11] = 0; + } + } + offset += 12; + } while (--number_of_tags); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* First, ensure we have YCbCr or grayscale data, and that the source's + * Y channel is full resolution. (No reasonable person would make Y + * be less than full resolution, so actually coping with that case + * isn't worth extra code space. But we check it to avoid crashing.) + */ + if (((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) && + srcinfo->comp_info[0].h_samp_factor == srcinfo->max_h_samp_factor && + srcinfo->comp_info[0].v_samp_factor == srcinfo->max_v_samp_factor) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, it sets the target h_samp_factor & + * v_samp_factor to 1, which typically won't match the source. + * We have to preserve the source's quantization table number, however. + */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } else if (info->num_components == 1) { + /* For a single-component source, we force the destination sampling factors + * to 1x1, with or without force_grayscale. This is useful because some + * decoders choke on grayscale images with other sampling factors. + */ + dstinfo->comp_info[0].h_samp_factor = 1; + dstinfo->comp_info[0].v_samp_factor = 1; + } + + /* Correct the destination's image dimensions as necessary + * for rotate/flip, resize, and crop operations. + */ + dstinfo->jpeg_width = info->output_width; + dstinfo->jpeg_height = info->output_height; + + /* Transpose destination image parameters */ + switch (info->transform) { + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + break; + default: + break; + } + + /* Adjust Exif properties */ + if (srcinfo->marker_list != NULL && + srcinfo->marker_list->marker == JPEG_APP0+1 && + srcinfo->marker_list->data_length >= 6 && + GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && + GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && + GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && + GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && + GETJOCTET(srcinfo->marker_list->data[4]) == 0 && + GETJOCTET(srcinfo->marker_list->data[5]) == 0) { + /* Suppress output of JFIF marker */ + dstinfo->write_JFIF_header = FALSE; + /* Adjust Exif image parameters */ + if (dstinfo->jpeg_width != srcinfo->image_width || + dstinfo->jpeg_height != srcinfo->image_height) + /* Align data segment to start of TIFF structure for parsing */ + adjust_exif_parameters(srcinfo->marker_list->data + 6, + srcinfo->marker_list->data_length - 6, + dstinfo->jpeg_width, dstinfo->jpeg_height); + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transform (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + /* Note: conditions tested here should match those in switch statement + * in jtransform_request_workspace() + */ + switch (info->transform) { + case JXFORM_NONE: + if (info->x_crop_offset != 0 || info->y_crop_offset != 0) + do_crop(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_FLIP_H: + if (info->y_crop_offset != 0) + do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + else + do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset, + src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset, + src_coef_arrays, dst_coef_arrays); + break; + } +} + +/* jtransform_perfect_transform + * + * Determine whether lossless transformation is perfectly + * possible for a specified image and transformation. + * + * Inputs: + * image_width, image_height: source image dimensions. + * MCU_width, MCU_height: pixel dimensions of MCU. + * transform: transformation identifier. + * Parameter sources from initialized jpeg_struct + * (after reading source header): + * image_width = cinfo.image_width + * image_height = cinfo.image_height + * MCU_width = cinfo.max_h_samp_factor * cinfo.block_size + * MCU_height = cinfo.max_v_samp_factor * cinfo.block_size + * Result: + * TRUE = perfect transformation possible + * FALSE = perfect transformation not possible + * (may use custom action then) + */ + +GLOBAL(boolean) +jtransform_perfect_transform(JDIMENSION image_width, JDIMENSION image_height, + int MCU_width, int MCU_height, + JXFORM_CODE transform) +{ + boolean result = TRUE; /* initialize TRUE */ + + switch (transform) { + case JXFORM_FLIP_H: + case JXFORM_ROT_270: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_90: + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + case JXFORM_TRANSVERSE: + case JXFORM_ROT_180: + if (image_width % (JDIMENSION) MCU_width) + result = FALSE; + if (image_height % (JDIMENSION) MCU_height) + result = FALSE; + break; + default: + break; + } + + return result; +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} + +} // namespace Digikam + +#else // JPEG_LIB_VERSION >= 80 + +/* + * transupp.c + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains image transformation routines and other utility code + * used by the jpegtran sample application. These are NOT part of the core + * JPEG library. But we keep these routines separate from jpegtran.c to + * ease the task of maintaining jpegtran-like programs that have other user + * interfaces. + */ + +// Local includes. + +#include "transupp.h" /* My own external interface */ + +namespace Digikam +{ + +#if TRANSFORMS_SUPPORTED + +/* + * Lossless image transformation routines. These routines work on DCT + * coefficient arrays and thus do not require any lossy decompression + * or recompression of the image. + * Thanks to Guido Vollbeding for the initial design and code of this feature. + * + * Horizontal flipping is done in-place, using a single top-to-bottom + * pass through the virtual source array. It will thus be much the + * fastest option for images larger than main memory. + * + * The other routines require a set of destination virtual arrays, so they + * need twice as much memory as jpegtran normally does. The destination + * arrays are always written in normal scan order (top to bottom) because + * the virtual array manager expects this. The source arrays will be scanned + * in the corresponding order, which means multiple passes through the source + * arrays for most of the transforms. That could result in much thrashing + * if the image is larger than main memory. + * + * Some notes about the operating environment of the individual transform + * routines: + * 1. Both the source and destination virtual arrays are allocated from the + * source JPEG object, and therefore should be manipulated by calling the + * source's memory manager. + * 2. The destination's component count should be used. It may be smaller + * than the source's when forcing to grayscale. + * 3. Likewise the destination's sampling factors should be used. When + * forcing to grayscale the destination's sampling factors will be all 1, + * and we may as well take that as the effective iMCU size. + * 4. When "trim" is in effect, the destination's dimensions will be the + * trimmed values but the source's will be untrimmed. + * 5. All the routines assume that the source and destination buffers are + * padded out to a full iMCU boundary. This is true, although for the + * source buffer it is an undocumented property of jdcoefct.c. + * Notes 2,3,4 boil down to this: generally we should use the destination's + * dimensions and ignore the source's. + */ + + +LOCAL(void) +do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays) +/* Horizontal flip; done in-place, so no separate dest array is required */ +{ + JDIMENSION MCU_cols, comp_width, blk_x, blk_y; + int ci, k, offset_y; + JBLOCKARRAY buffer; + JCOEFPTR ptr1, ptr2; + JCOEF temp1, temp2; + jpeg_component_info *compptr; + + /* Horizontal mirroring of DCT blocks is accomplished by swapping + * pairs of blocks in-place. Within a DCT block, we perform horizontal + * mirroring by changing the signs of odd-numbered columns. + * Partial iMCUs at the right edge are left untouched. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, false); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, false); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, false); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, false); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, false); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + if (dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, false); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, false); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + /* Process the blocks that can be mirrored both ways. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } + /* Any remaining right-edge blocks are only mirrored vertically. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + /* Process the blocks that can be mirrored. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } + /* Any remaining right-edge blocks are only copied. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE2; i++) + *dst_ptr++ = *src_ptr++; + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, true); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, false); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + if (dst_blk_y < comp_height) { + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Request any required workspace. + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + */ + +GLOBAL(void) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don't need a workspace array */ + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_180: + /* Need workspace arrays having same dimensions as source image. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, false, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + /* Need workspace arrays having transposed dimensions. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, false, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->h_samp_factor); + } + break; + } + info->workspace_coef_arrays = coef_arrays; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (j_compress_ptr dstinfo) +{ + int ci, max_h_samp_factor; + JDIMENSION MCU_cols; + + /* We have to compute max_h_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_h_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; + max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); + } + MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); + if (MCU_cols > 0) /* can't trim to 0 pixels */ + dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); +} + +LOCAL(void) +trim_bottom_edge (j_compress_ptr dstinfo) +{ + int ci, max_v_samp_factor; + JDIMENSION MCU_rows; + + /* We have to compute max_v_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_v_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; + max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); + } + MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); + if (MCU_rows > 0) /* can't trim to 0 pixels */ + dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr /*srcinfo*/, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, the target h_samp_factor & v_samp_factor + * will get set to 1, which typically won't match the source. + * In fact we do this even if the source is already grayscale; that + * provides an easy way of coercing a grayscale JPEG with funny sampling + * factors to the customary 1,1. (Some decoders fail on other factors.) + */ + if ((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) { + /* We have to preserve the source's quantization table number. */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + switch (info->transform) { + case JXFORM_NONE: + /* Nothing to do */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(dstinfo); + break; + case JXFORM_TRANSPOSE: + transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ + break; + case JXFORM_TRANSVERSE: + transpose_critical_parameters(dstinfo); + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_90: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_bottom_edge(dstinfo); + break; + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transformation (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + } +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION /*option*/) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} + +} // namespace Digikam + +#endif // JPEG_LIB_VERSION >= 80 diff --git a/src/libs/jpegutils/transupp.h b/src/libs/jpegutils/transupp.h new file mode 100644 index 00000000..7dd3fad6 --- /dev/null +++ b/src/libs/jpegutils/transupp.h @@ -0,0 +1,373 @@ +#if JPEG_LIB_VERSION >= 80 + +/* + * transupp.h + * + * Copyright (C) 1997-2009, Thomas G. Lane, Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +#ifndef TRANSUPP_H +#define TRANSUPP_H + +namespace Digikam +{ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a lossless-crop option, which discards data outside a given + * image region but losslessly preserves what is inside. Like the rotate and + * flip transforms, lossless crop is restricted by the JPEG format: the upper + * left corner of the selected region must fall on an iMCU boundary. If this + * does not hold for the given crop parameters, we silently move the upper left + * corner up and/or left to make it so, simultaneously increasing the region + * dimensions to keep the lower right crop corner unchanged. (Thus, the + * output image covers at least the requested region, but may cover more.) + * + * We also provide a lossless-resize option, which is kind of a lossless-crop + * operation in the DCT coefficient block domain - it discards higher-order + * coefficients and losslessly preserves lower-order coefficients of a + * sub-block. + * + * Rotate/flip transform, resize, and crop can be requested together in a + * single invocation. The crop is applied last --- that is, the crop region + * is specified in terms of the destination image after transform/resize. + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_parse_crop_spec jTrParCrop +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transform jTrExec +#define jtransform_perfect_transform jTrPerfect +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Codes for crop parameters, which can individually be unspecified, + * positive, or negative. (Negative width or height makes no sense, though.) + */ + +typedef enum { + JCROP_UNSET, + JCROP_POS, + JCROP_NEG +} JCROP_CODE; + +/* + * Transform parameters struct. + * NB: application must not change any elements of this struct after + * calling jtransform_request_workspace. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean perfect; /* if TRUE, fail if partial MCUs are requested */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + boolean crop; /* if TRUE, crop source image */ + + /* Crop parameters: application need not set these unless crop is TRUE. + * These can be filled in by jtransform_parse_crop_spec(). + */ + JDIMENSION crop_width; /* Width of selected region */ + JCROP_CODE crop_width_set; + JDIMENSION crop_height; /* Height of selected region */ + JCROP_CODE crop_height_set; + JDIMENSION crop_xoffset; /* X offset of selected region */ + JCROP_CODE crop_xoffset_set; /* (negative measures from right edge) */ + JDIMENSION crop_yoffset; /* Y offset of selected region */ + JCROP_CODE crop_yoffset_set; /* (negative measures from bottom edge) */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ + JDIMENSION output_width; /* cropped destination dimensions */ + JDIMENSION output_height; + JDIMENSION x_crop_offset; /* destination crop offsets measured in iMCUs */ + JDIMENSION y_crop_offset; + int iMCU_sample_width; /* destination iMCU size */ + int iMCU_sample_height; +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Parse a crop specification (written in X11 geometry style) */ +EXTERN(boolean) jtransform_parse_crop_spec + JPP((jpeg_transform_info *info, const char *spec)); +/* Request any required workspace */ +EXTERN(boolean) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transform + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Determine whether lossless transformation is perfectly + * possible for a specified image and transformation. + */ +EXTERN(boolean) jtransform_perfect_transform + JPP((JDIMENSION image_width, JDIMENSION image_height, + int MCU_width, int MCU_height, + JXFORM_CODE transform)); + +/* jtransform_execute_transform used to be called + * jtransform_execute_transformation, but some compilers complain about + * routine names that long. This macro is here to avoid breaking any + * old source code that uses the original name... + */ +#define jtransform_execute_transformation jtransform_execute_transform + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); + + +} // namespace DigiKam + +#endif // TRANSUPP_H + +#else // JPEG_LIB_VERSION >= 80 + +/* + * transupp.h + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +#ifndef TRANSUPP_H +#define TRANSUPP_H + +namespace Digikam +{ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transformation jTrExec +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Request any required workspace */ +EXTERN(void) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transformation + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); + +} // namespace DigiKam + +#endif // TRANSUPP_H + +#endif // JPEG_LIB_VERSION >= 80 diff --git a/src/libs/levels/Makefile.am b/src/libs/levels/Makefile.am new file mode 100644 index 00000000..d86db7c3 --- /dev/null +++ b/src/libs/levels/Makefile.am @@ -0,0 +1,17 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = liblevels.la + +liblevels_la_SOURCES = imagelevels.cpp + +liblevels_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/histogram \ + -I$(top_srcdir)/src/ \ + -I$(top_srcdir)/src/digikam \ + $(all_includes) + +digikaminclude_HEADERS = imagelevels.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/levels/imagelevels.cpp b/src/libs/levels/imagelevels.cpp new file mode 100644 index 00000000..ac995927 --- /dev/null +++ b/src/libs/levels/imagelevels.cpp @@ -0,0 +1,715 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-29 + * Description : image levels manipulation methods. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * Some code parts are inspired from gimp 2.0 + * app/base/levels.c, gimplut.c, and app/base/gimpleveltool.c + * source files. + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +// TQt includes. + +#include + +// C++ includes. + +#include +#include +#include +#include +#include + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagelevels.h" + +namespace Digikam +{ + +class ImageLevelsPriv +{ + +public: + + enum PixelType + { + RedPixel = 0, + GreenPixel, + BluePixel, + AlphaPixel + }; + + struct _Levels + { + double gamma[5]; + + int low_input[5]; + int high_input[5]; + + int low_output[5]; + int high_output[5]; + }; + + struct _Lut + { + unsigned short **luts; + int nchannels; + }; + +public: + + ImageLevelsPriv() + { + levels = 0; + lut = 0; + dirty = false; + } + + // Levels data. + struct _Levels *levels; + + // Lut data. + struct _Lut *lut; + + bool sixteenBit; + bool dirty; +}; + +ImageLevels::ImageLevels(bool sixteenBit) +{ + d = new ImageLevelsPriv; + d->lut = new ImageLevelsPriv::_Lut; + d->levels = new ImageLevelsPriv::_Levels; + d->sixteenBit = sixteenBit; + + memset(d->levels, 0, sizeof(struct ImageLevelsPriv::_Levels)); + d->lut->luts = NULL; + d->lut->nchannels = 0; + + reset(); +} + +ImageLevels::~ImageLevels() +{ + if (d->lut) + { + if (d->lut->luts) + { + for (int i = 0 ; i < d->lut->nchannels ; i++) + delete [] d->lut->luts[i]; + + delete [] d->lut->luts; + } + + delete d->lut; + } + + if (d->levels) + delete d->levels; + + delete d; +} + +bool ImageLevels::isDirty() +{ + return d->dirty; +} + +bool ImageLevels::isSixteenBits() +{ + return d->sixteenBit; +} + +void ImageLevels::reset() +{ + for (int channel = 0 ; channel < 5 ; channel++) + levelsChannelReset(channel); +} + +void ImageLevels::levelsChannelReset(int channel) +{ + if (!d->levels) return; + + d->levels->gamma[channel] = 1.0; + d->levels->low_input[channel] = 0; + d->levels->high_input[channel] = d->sixteenBit ? 65535 : 255; + d->levels->low_output[channel] = 0; + d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255; + d->dirty = false; +} + +void ImageLevels::levelsAuto(ImageHistogram *hist) +{ + if (!d->levels || !hist) return; + + levelsChannelReset(ImageHistogram::ValueChannel); + + for (int channel = ImageHistogram::RedChannel ; + channel <= ImageHistogram::BlueChannel ; + channel++) + { + levelsChannelAuto(hist, channel); + } + d->dirty = true; +} + +void ImageLevels::levelsChannelAuto(ImageHistogram *hist, int channel) +{ + int i; + double count, new_count, percentage, next_percentage; + + if (!d->levels || !hist) return; + + d->levels->gamma[channel] = 1.0; + d->levels->low_output[channel] = 0; + d->levels->high_output[channel] = d->sixteenBit ? 65535 : 255; + + count = hist->getCount(channel, 0, d->sixteenBit ? 65535 : 255); + + if (count == 0.0) + { + d->levels->low_input[channel] = 0; + d->levels->high_input[channel] = 0; + } + else + { + // Set the low input + + new_count = 0.0; + + for (i = 0 ; i < (d->sixteenBit ? 65535 : 255) ; i++) + { + new_count += hist->getValue(channel, i); + percentage = new_count / count; + next_percentage = (new_count + hist->getValue(channel, i + 1)) / count; + + if (fabs (percentage - 0.006) < fabs (next_percentage - 0.006)) + { + d->levels->low_input[channel] = i + 1; + break; + } + } + + // Set the high input + + new_count = 0.0; + + for (i = (d->sixteenBit ? 65535 : 255) ; i > 0 ; i--) + { + new_count += hist->getValue(channel, i); + percentage = new_count / count; + next_percentage = (new_count + hist->getValue(channel, i - 1)) / count; + + if (fabs (percentage - 0.006) < fabs (next_percentage - 0.006)) + { + d->levels->high_input[channel] = i - 1; + break; + } + } + } + d->dirty = true; +} + +int ImageLevels::levelsInputFromColor(int channel, const DColor& color) +{ + switch (channel) + { + case ImageHistogram::ValueChannel: + return TQMAX (TQMAX (color.red(), color.green()), color.blue()); + + case ImageHistogram::RedChannel: + return color.red(); + + case ImageHistogram::GreenChannel: + return color.green(); + + case ImageHistogram::BlueChannel: + return color.blue(); + } + + return 0; // just to please the compiler. +} + +void ImageLevels::levelsBlackToneAdjustByColors(int channel, const DColor& color) +{ + if (!d->levels) return; + + d->levels->low_input[channel] = levelsInputFromColor(channel, color); + d->dirty = true; +} + +void ImageLevels::levelsWhiteToneAdjustByColors(int channel, const DColor& color) +{ + if (!d->levels) return; + + d->levels->high_input[channel] = levelsInputFromColor(channel, color); + d->dirty = true; +} + +void ImageLevels::levelsGrayToneAdjustByColors(int channel, const DColor& color) +{ + if (!d->levels) return; + + int input; + int range; + double inten; + double out_light; + unsigned short lightness; + + // Calculate lightness value. + + lightness = (unsigned short)LEVELS_RGB_INTENSITY (color.red(), color.green(), color.blue()); + + input = levelsInputFromColor(channel, color); + + range = d->levels->high_input[channel] - d->levels->low_input[channel]; + + if (range <= 0) + return; + + input -= d->levels->low_input[channel]; + + if (input < 0) + return; + + // Normalize input and lightness. + + inten = (double) input / (double) range; + out_light = (double) lightness/ (double) range; + + if (out_light <= 0) + return; + + // Map selected color to corresponding lightness. + + d->levels->gamma[channel] = log (inten) / log (out_light); + d->dirty = true; +} + +void ImageLevels::levelsCalculateTransfers() +{ + double inten; + int i, j; + + if (!d->levels) return; + + // Recalculate the levels arrays. + + for (j = 0 ; j < 5 ; j++) + { + for (i = 0; i <= (d->sixteenBit ? 65535 : 255); i++) + { + // determine input intensity. + + if (d->levels->high_input[j] != d->levels->low_input[j]) + { + inten = ((double) (i - d->levels->low_input[j]) / + (double) (d->levels->high_input[j] - d->levels->low_input[j])); + } + else + { + inten = (double) (i - d->levels->low_input[j]); + } + + inten = CLAMP (inten, 0.0, 1.0); + + if (d->levels->gamma[j] != 0.0) + inten = pow (inten, (1.0 / d->levels->gamma[j])); + } + } +} + +float ImageLevels::levelsLutFunc(int n_channels, int channel, float value) +{ + double inten; + int j; + + if (!d->levels) return 0.0; + + if (n_channels == 1) + j = 0; + else + j = channel + 1; + + inten = value; + + // For color images this runs through the loop with j = channel +1 + // the first time and j = 0 the second time. + // + // For bw images this runs through the loop with j = 0 the first and + // only time. + + for ( ; j >= 0 ; j -= (channel + 1) ) + { + // Don't apply the overall curve to the alpha channel. + + if (j == 0 && (n_channels == 2 || n_channels == 4) + && channel == n_channels -1) + return inten; + + // Determine input intensity. + + if (d->levels->high_input[j] != d->levels->low_input[j]) + inten = ((double) ((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]) / + (double) (d->levels->high_input[j] - d->levels->low_input[j])); + else + inten = (double) ((float)(d->sixteenBit ? 65535 : 255) * inten - d->levels->low_input[j]); + + if (d->levels->gamma[j] != 0.0) + { + if (inten >= 0.0) + inten = pow ( inten, (1.0 / d->levels->gamma[j])); + else + inten = -pow (-inten, (1.0 / d->levels->gamma[j])); + } + + // determine the output intensity. + + if (d->levels->high_output[j] >= d->levels->low_output[j]) + inten = (double) (inten * (d->levels->high_output[j] - + d->levels->low_output[j]) + d->levels->low_output[j]); + + else if (d->levels->high_output[j] < d->levels->low_output[j]) + inten = (double) (d->levels->low_output[j] - inten * + (d->levels->low_output[j] - d->levels->high_output[j])); + + inten /= (float)(d->sixteenBit ? 65535 : 255); + } + + return inten; +} + +void ImageLevels::levelsLutSetup(int nchannels) +{ + int i; + uint v; + double val; + + if (d->lut->luts) + { + for (i = 0 ; i < d->lut->nchannels ; i++) + delete [] d->lut->luts[i]; + + delete [] d->lut->luts; + } + + d->lut->nchannels = nchannels; + d->lut->luts = new unsigned short*[d->lut->nchannels]; + + for (i = 0 ; i < d->lut->nchannels ; i++) + { + d->lut->luts[i] = new unsigned short[(d->sixteenBit ? 65535 : 255) + 1]; + + for (v = 0 ; v <= (d->sixteenBit ? 65535 : 255) ; v++) + { + // to add gamma correction use func(v ^ g) ^ 1/g instead. + + val = (float)(d->sixteenBit ? 65535 : 255) * + levelsLutFunc( d->lut->nchannels, i, v/(float)(d->sixteenBit ? 65535 : 255)) + 0.5; + + d->lut->luts[i][v] = (unsigned short)CLAMP (val, 0, (d->sixteenBit ? 65535 : 255)); + } + } +} + +void ImageLevels::levelsLutProcess(uchar *srcPR, uchar *destPR, int w, int h) +{ + unsigned short *lut0 = NULL, *lut1 = NULL, *lut2 = NULL, *lut3 = NULL; + + int i; + + if (d->lut->nchannels > 0) + lut0 = d->lut->luts[0]; + if (d->lut->nchannels > 1) + lut1 = d->lut->luts[1]; + if (d->lut->nchannels > 2) + lut2 = d->lut->luts[2]; + if (d->lut->nchannels > 3) + lut3 = d->lut->luts[3]; + + if (!d->sixteenBit) // 8 bits image. + { + uchar red, green, blue, alpha; + uchar *ptr = srcPR; + uchar *dst = destPR; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if ( d->lut->nchannels > 0 ) + red = lut0[red]; + + if ( d->lut->nchannels > 1 ) + green = lut1[green]; + + if ( d->lut->nchannels > 2 ) + blue = lut2[blue]; + + if ( d->lut->nchannels > 3 ) + alpha = lut3[alpha]; + + dst[0] = blue; + dst[1] = green; + dst[2] = red; + dst[3] = alpha; + + ptr += 4; + dst += 4; + } + } + else // 16 bits image. + { + unsigned short red, green, blue, alpha; + unsigned short *ptr = (unsigned short *)srcPR; + unsigned short *dst = (unsigned short *)destPR; + + for (i = 0 ; i < w*h ; i++) + { + blue = ptr[0]; + green = ptr[1]; + red = ptr[2]; + alpha = ptr[3]; + + if ( d->lut->nchannels > 0 ) + red = lut0[red]; + + if ( d->lut->nchannels > 1 ) + green = lut1[green]; + + if ( d->lut->nchannels > 2 ) + blue = lut2[blue]; + + if ( d->lut->nchannels > 3 ) + alpha = lut3[alpha]; + + dst[0] = blue; + dst[1] = green; + dst[2] = red; + dst[3] = alpha; + + ptr += 4; + dst += 4; + } + } +} + +void ImageLevels::setLevelGammaValue(int Channel, double val) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + { + d->levels->gamma[Channel] = val; + d->dirty = true; + } +} + +void ImageLevels::setLevelLowInputValue(int Channel, int val) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + { + d->levels->low_input[Channel] = val; + d->dirty = true; + } +} + +void ImageLevels::setLevelHighInputValue(int Channel, int val) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + { + d->levels->high_input[Channel] = val; + d->dirty = true; + } +} + +void ImageLevels::setLevelLowOutputValue(int Channel, int val) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + { + d->levels->low_output[Channel] = val; + d->dirty = true; + } +} + +void ImageLevels::setLevelHighOutputValue(int Channel, int val) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + { + d->levels->high_output[Channel] = val; + d->dirty = true; + } +} + +double ImageLevels::getLevelGammaValue(int Channel) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + return (d->levels->gamma[Channel]); + + return 0.0; +} + +int ImageLevels::getLevelLowInputValue(int Channel) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + return (d->levels->low_input[Channel]); + + return 0; +} + +int ImageLevels::getLevelHighInputValue(int Channel) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + return (d->levels->high_input[Channel]); + + return 0; +} + +int ImageLevels::getLevelLowOutputValue(int Channel) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + return (d->levels->low_output[Channel]); + + return 0; +} + +int ImageLevels::getLevelHighOutputValue(int Channel) +{ + if ( d->levels && Channel>=0 && Channel<5 ) + return (d->levels->high_output[Channel]); + + return 0; +} + +bool ImageLevels::loadLevelsFromGimpLevelsFile(const KURL& fileUrl) +{ + // TODO : support KURL ! + + FILE *file; + int low_input[5]; + int high_input[5]; + int low_output[5]; + int high_output[5]; + double gamma[5]; + int i, fields; + char buf[50]; + char *nptr; + + file = fopen(TQFile::encodeName(fileUrl.path()), "r"); + + if (!file) + return false; + + if (! fgets (buf, sizeof (buf), file)) + { + fclose(file); + return false; + } + + if (strcmp (buf, "# GIMP Levels File\n") != 0) + { + fclose(file); + return false; + } + + for (i = 0 ; i < 5 ; i++) + { + fields = fscanf (file, "%d %d %d %d ", + &low_input[i], + &high_input[i], + &low_output[i], + &high_output[i]); + + if (fields != 4) + { + DWarning() << "Invalid Gimp levels file!" << endl; + fclose(file); + return false; + } + + if (!fgets (buf, 50, file)) + { + DWarning() << "Invalid Gimp levels file!" << endl; + fclose(file); + return false; + } + + gamma[i] = strtod (buf, &nptr); + + if (buf == nptr || errno == ERANGE) + { + DWarning() << "Invalid Gimp levels file!" << endl; + fclose(file); + return false; + } + } + + for (i = 0 ; i < 5 ; i++) + { + setLevelGammaValue(i, gamma[i]); + setLevelLowInputValue(i, d->sixteenBit ? low_input[i]*255 : low_input[i]); + setLevelHighInputValue(i, d->sixteenBit ? high_input[i]*255 : high_input[i]); + setLevelLowOutputValue(i, d->sixteenBit ? low_output[i]*255 : low_output[i]); + setLevelHighOutputValue(i, d->sixteenBit ? high_output[i]*255 : high_output[i]); + } + + fclose(file); + return true; +} + +bool ImageLevels::saveLevelsToGimpLevelsFile(const KURL& fileUrl) +{ + // TODO : support KURL ! + + FILE *file; + int i; + + file = fopen(TQFile::encodeName(fileUrl.path()), "w"); + + if (!file) + return false; + + fprintf (file, "# GIMP Levels File\n"); + + for (i = 0 ; i < 5 ; i++) + { + char buf[256]; + sprintf (buf, "%f", getLevelGammaValue(i)); + + fprintf (file, "%d %d %d %d %s\n", + d->sixteenBit ? getLevelLowInputValue(i)/255 : getLevelLowInputValue(i), + d->sixteenBit ? getLevelHighInputValue(i)/255 : getLevelHighInputValue(i), + d->sixteenBit ? getLevelLowOutputValue(i)/255 : getLevelLowOutputValue(i), + d->sixteenBit ? getLevelHighInputValue(i)/255 : getLevelHighInputValue(i), + buf); + } + + fflush(file); + fclose(file); + + return true; +} + +} // NameSpace Digikam diff --git a/src/libs/levels/imagelevels.h b/src/libs/levels/imagelevels.h new file mode 100644 index 00000000..04d10d97 --- /dev/null +++ b/src/libs/levels/imagelevels.h @@ -0,0 +1,105 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-07-29 + * Description : image levels manipulation methods. + * + * Copyright (C) 2004-2008 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#ifndef IMAGELEVELS_H +#define IMAGELEVELS_H + +#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x))) + +/* Map RGB to intensity */ + +#define LEVELS_RGB_INTENSITY_RED 0.30 +#define LEVELS_RGB_INTENSITY_GREEN 0.59 +#define LEVELS_RGB_INTENSITY_BLUE 0.11 +#define LEVELS_RGB_INTENSITY(r,g,b) ((r) * LEVELS_RGB_INTENSITY_RED + \ + (g) * LEVELS_RGB_INTENSITY_GREEN + \ + (b) * LEVELS_RGB_INTENSITY_BLUE) + +// KDE includes. + +#include + +// Local includes. + +#include "dcolor.h" +#include "digikam_export.h" + +namespace Digikam +{ + +class ImageLevelsPriv; +class ImageHistogram; + +class DIGIKAM_EXPORT ImageLevels +{ + +public: + + ImageLevels(bool sixteenBit); + ~ImageLevels(); + + bool isDirty(); + bool isSixteenBits(); + void reset(); + + // Methods for to manipulate the levels data. + + void levelsChannelReset(int channel); + void levelsAuto(ImageHistogram *hist); + void levelsChannelAuto(ImageHistogram *hist, int channel); + int levelsInputFromColor(int channel, const DColor& color); + void levelsBlackToneAdjustByColors(int channel, const DColor& color); + void levelsGrayToneAdjustByColors(int channel, const DColor& color); + void levelsWhiteToneAdjustByColors(int channel, const DColor& color); + void levelsCalculateTransfers(); + float levelsLutFunc(int n_channels, int channel, float value); + void levelsLutSetup(int nchannels); + void levelsLutProcess(uchar *srcPR, uchar *destPR, int w, int h); + + // Methods for to set manually the levels values. + + void setLevelGammaValue(int Channel, double val); + void setLevelLowInputValue(int Channel, int val); + void setLevelHighInputValue(int Channel, int val); + void setLevelLowOutputValue(int Channel, int val); + void setLevelHighOutputValue(int Channel, int val); + + double getLevelGammaValue(int Channel); + int getLevelLowInputValue(int Channel); + int getLevelHighInputValue(int Channel); + int getLevelLowOutputValue(int Channel); + int getLevelHighOutputValue(int Channel); + + // Methods for to save/load the levels values to/from a Gimp levels text file. + + bool saveLevelsToGimpLevelsFile(const KURL& fileUrl); + bool loadLevelsFromGimpLevelsFile(const KURL& fileUrl); + +private: + + ImageLevelsPriv* d; +}; + +} // NameSpace Digikam + +#endif /* IMAGELEVELS_H */ diff --git a/src/libs/lprof/Makefile.am b/src/libs/lprof/Makefile.am new file mode 100644 index 00000000..e7251d2c --- /dev/null +++ b/src/libs/lprof/Makefile.am @@ -0,0 +1,15 @@ +# Gilles Caulier 12/01/06: lprof implementation is in C not C++ and do not +# support 'nofinal' compilation option. +KDE_OPTIONS = nofinal + +INCLUDES = $(all_includes) + +noinst_LTLIBRARIES = liblprof.la + +noinst_HEADERS = lcmsprf.h + +liblprof_la_CXXFLAGS = -w -fomit-frame-pointer + +liblprof_la_SOURCES = cmshull.cpp cmslm.cpp cmslnr.cpp cmsmatn.cpp \ + cmsmkmsh.cpp cmsmntr.cpp cmsoutl.cpp cmspcoll.cpp \ + cmsprf.cpp cmsreg.cpp cmsscn.cpp cmssheet.cpp diff --git a/src/libs/lprof/cmshull.cpp b/src/libs/lprof/cmshull.cpp new file mode 100644 index 00000000..05b8dfa3 --- /dev/null +++ b/src/libs/lprof/cmshull.cpp @@ -0,0 +1,1480 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + + +#include "lcmsprf.h" + +/* Convex hull management */ + +LCMSHANDLE cdecl cmsxHullInit(void); +void cdecl cmsxHullDone(LCMSHANDLE hHull); +BOOL cdecl cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z); +BOOL cdecl cmsxHullComputeHull(LCMSHANDLE hHull); +char cdecl cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z); +BOOL cdecl cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname); + +/* --------------------------------------------------------------------- */ + + + +/* This method is described in "Computational Geometry in C" Chapter 4. */ +/* */ +/* -------------------------------------------------------------------- */ +/* This code is Copyright 1998 by Joseph O'Rourke. It may be freely */ +/* redistributed in its entirety provided that this copyright notice is */ +/* not removed. */ +/* -------------------------------------------------------------------- */ + +#define SWAP(t,x,y) { t = x; x = y; y = t; } + +#define XFREE(p) +/* if (p) { free ((char *) p); p = NULL; } */ + + +#define ADD( head, p ) if ( head ) { \ + p->Next = head; \ + p->Prev = head->Prev; \ + head->Prev = p; \ + p->Prev->Next = p; \ + } \ + else { \ + head = p; \ + head->Next = head->Prev = p; \ + } + +#define XDELETE( head, p ) if ( head ) { \ + if ( head == head->Next ) \ + head = NULL; \ + else if ( p == head ) \ + head = head->Next; \ + p->Next->Prev = p->Prev; \ + p->Prev->Next = p->Next; \ + XFREE( p ); \ + } + +/* Define Vertex indices. */ +#define X 0 +#define Y 1 +#define Z 2 + +/* Define structures for vertices, edges and faces */ + +typedef struct _vertex_struct VERTEX,FAR *LPVERTEX; +typedef struct _edge_struct EDGE, FAR *LPEDGE; +typedef struct _face_struct FACE, FAR *LPFACE; + + +struct _edge_struct { + + LPFACE AdjFace[2]; + LPVERTEX EndPts[2]; + LPFACE NewFace; /* pointer to incident cone face. */ + BOOL DoDelete; /* T iff Edge should be delete. */ + + LPEDGE Next, Prev; +}; + +struct _face_struct { + + LPEDGE Edge[3]; + LPVERTEX Vertex[3]; + BOOL Visible; /* T iff face Visible from new point. */ + + LPFACE Next, Prev; +}; + +struct _vertex_struct { + + int v[3]; + int vnum; + LPEDGE duplicate; /* pointer to incident cone Edge (or NULL) */ + BOOL onhull; /* T iff point on hull. */ + BOOL mark; /* T iff point already processed. */ + + LPVERTEX Next, Prev; +}; + +/* Define flags */ + +#define ONHULL true +#define REMOVED true +#define VISIBLE true +#define PROCESSED true +#define SAFE 1000000 /* Range of safe coord values. */ + +#define DIM 3 /* Dimension of points */ +typedef int VEC3I[DIM]; /* Type integer point */ + +#define PMAX 10000 /* Max # of pts */ + +typedef struct { + + /* Global variable definitions */ + LPVERTEX vertices; + LPEDGE edges; + LPFACE faces; + + VEC3I Vertices[PMAX]; /* All the points */ + VEC3I Faces[PMAX]; /* Each triangle face is 3 indices */ + VEC3I Box[PMAX][2]; /* Box around each face */ + + + VEC3I bmin, bmax; + int radius; + int vnumCounter; + + int nfaces; + int nvertex; + +} HULL, FAR* LPHULL; + +/* static HULL Global; */ + + +/*--------------------------------------------------------------------- +MakeNullVertex: Makes a Vertex, nulls out fields. +---------------------------------------------------------------------*/ + +static +LPVERTEX MakeNullVertex(LPHULL hull) +{ + LPVERTEX v; + + v = (LPVERTEX) malloc(sizeof(VERTEX)); + if (!v) return NULL; + + v->duplicate = NULL; + v->onhull = !ONHULL; + v->mark = !PROCESSED; + ADD( hull->vertices, v ); + + return v; +} + + + +/*--------------------------------------------------------------------- +MakeNullEdge creates a new cell and initializes all pointers to NULL +and sets all flags to off. It returns a pointer to the empty cell. +---------------------------------------------------------------------*/ +static +LPEDGE MakeNullEdge(LPHULL hull) +{ + LPEDGE e; + + e = (LPEDGE) malloc(sizeof(EDGE)); + if (!e) return NULL; + + e->AdjFace[0] = e->AdjFace[1] = e->NewFace = NULL; + e->EndPts[0] = e->EndPts[1] = NULL; + e->DoDelete = !REMOVED; + ADD( hull->edges, e ); + return e; +} + +/*-------------------------------------------------------------------- +MakeNullFace creates a new face structure and initializes all of its +flags to NULL and sets all the flags to off. It returns a pointer +to the empty cell. +---------------------------------------------------------------------*/ +static +LPFACE MakeNullFace(LPHULL hull) +{ + LPFACE f; + int i; + + f = (LPFACE) malloc(sizeof(FACE)); + if (!f) return NULL; + + for ( i=0; i < 3; ++i ) { + f->Edge[i] = NULL; + f->Vertex[i] = NULL; + } + f->Visible = !VISIBLE; + ADD( hull->faces, f ); + return f; +} + + + +/*--------------------------------------------------------------------- +MakeFace creates a new face structure from three vertices (in ccw +order). It returns a pointer to the face. +---------------------------------------------------------------------*/ +static +LPFACE MakeFace(LPHULL hull, LPVERTEX v0, LPVERTEX v1, LPVERTEX v2, LPFACE fold) +{ + LPFACE f; + LPEDGE e0, e1, e2; + + /* Create edges of the initial triangle. */ + if( !fold ) { + e0 = MakeNullEdge(hull); + e1 = MakeNullEdge(hull); + e2 = MakeNullEdge(hull); + } + else { /* Copy from fold, in reverse order. */ + e0 = fold->Edge[2]; + e1 = fold->Edge[1]; + e2 = fold->Edge[0]; + } + e0->EndPts[0] = v0; e0->EndPts[1] = v1; + e1->EndPts[0] = v1; e1->EndPts[1] = v2; + e2->EndPts[0] = v2; e2->EndPts[1] = v0; + + /* Create face for triangle. */ + f = MakeNullFace(hull); + f->Edge[0] = e0; f->Edge[1] = e1; f->Edge[2] = e2; + f->Vertex[0] = v0; f->Vertex[1] = v1; f->Vertex[2] = v2; + + /* Link edges to face. */ + e0->AdjFace[0] = e1->AdjFace[0] = e2->AdjFace[0] = f; + + return f; +} + +/*--------------------------------------------------------------------- +Collinear checks to see if the three points given are collinear, +by checking to see if each element of the cross product is zero. +---------------------------------------------------------------------*/ +static +BOOL Collinear( LPVERTEX a, LPVERTEX b, LPVERTEX c ) +{ + return + ( c->v[Z] - a->v[Z] ) * ( b->v[Y] - a->v[Y] ) - + ( b->v[Z] - a->v[Z] ) * ( c->v[Y] - a->v[Y] ) == 0 + && ( b->v[Z] - a->v[Z] ) * ( c->v[X] - a->v[X] ) - + ( b->v[X] - a->v[X] ) * ( c->v[Z] - a->v[Z] ) == 0 + && ( b->v[X] - a->v[X] ) * ( c->v[Y] - a->v[Y] ) - + ( b->v[Y] - a->v[Y] ) * ( c->v[X] - a->v[X] ) == 0 ; +} + +/*--------------------------------------------------------------------- +VolumeSign returns the sign of the volume of the tetrahedron determined by f +and p. VolumeSign is +1 iff p is on the negative side of f, +where the positive side is determined by the rh-rule. So the volume +is positive if the ccw normal to f points outside the tetrahedron. +The final fewer-multiplications form is due to Bob Williamson. +---------------------------------------------------------------------*/ +int VolumeSign( LPFACE f, LPVERTEX p ) +{ + double vol; + double ax, ay, az, bx, by, bz, cx, cy, cz; + + ax = f->Vertex[0]->v[X] - p->v[X]; + ay = f->Vertex[0]->v[Y] - p->v[Y]; + az = f->Vertex[0]->v[Z] - p->v[Z]; + bx = f->Vertex[1]->v[X] - p->v[X]; + by = f->Vertex[1]->v[Y] - p->v[Y]; + bz = f->Vertex[1]->v[Z] - p->v[Z]; + cx = f->Vertex[2]->v[X] - p->v[X]; + cy = f->Vertex[2]->v[Y] - p->v[Y]; + cz = f->Vertex[2]->v[Z] - p->v[Z]; + + vol = ax * (by*cz - bz*cy) + + ay * (bz*cx - bx*cz) + + az * (bx*cy - by*cx); + + + /* The volume should be an integer. */ + if ( vol > 0.5 ) return 1; + else if ( vol < -0.5 ) return -1; + else return 0; +} + + + +/*--------------------------------------------------------------------- +CleanEdges runs through the Edge list and cleans up the structure. +If there is a NewFace then it will put that face in place of the +Visible face and NULL out NewFace. It also deletes so marked edges. +---------------------------------------------------------------------*/ +static +void CleanEdges(LPHULL hull) +{ + LPEDGE e; /* Primary index into Edge list. */ + LPEDGE t; /* Temporary Edge pointer. */ + + /* Integrate the NewFace's into the data structure. */ + /* Check every Edge. */ + + e = hull ->edges; + do { + if ( e->NewFace ) { + + if ( e->AdjFace[0]->Visible ) + e->AdjFace[0] = e->NewFace; + else + e->AdjFace[1] = e->NewFace; + + e->NewFace = NULL; + } + + e = e->Next; + + } while ( e != hull ->edges ); + + /* Delete any edges marked for deletion. */ + while ( hull ->edges && hull ->edges->DoDelete ) { + + e = hull ->edges; + + XDELETE( hull ->edges, e ); + } + + e = hull ->edges->Next; + + do { + if ( e->DoDelete ) { + + t = e; + e = e->Next; + XDELETE( hull ->edges, t ); + } + else e = e->Next; + + } while ( e != hull ->edges ); +} + +/*--------------------------------------------------------------------- +CleanFaces runs through the face list and deletes any face marked Visible. +---------------------------------------------------------------------*/ +static +void CleanFaces(LPHULL hull) +{ + LPFACE f; /* Primary pointer into face list. */ + LPFACE t; /* Temporary pointer, for deleting. */ + + + while ( hull ->faces && hull ->faces->Visible ) { + + f = hull ->faces; + XDELETE( hull ->faces, f ); + } + + f = hull ->faces->Next; + + do { + if ( f->Visible ) { + + t = f; + f = f->Next; + XDELETE( hull ->faces, t ); + } + else f = f->Next; + + } while ( f != hull ->faces ); +} + + + +/*--------------------------------------------------------------------- +CleanVertices runs through the Vertex list and deletes the +vertices that are marked as processed but are not incident to any +undeleted edges. +---------------------------------------------------------------------*/ +static +void CleanVertices(LPHULL hull) +{ + LPEDGE e; + LPVERTEX v, t; + + /* Mark all vertices incident to some undeleted Edge as on the hull. */ + + e = hull ->edges; + do { + e->EndPts[0]->onhull = e->EndPts[1]->onhull = ONHULL; + e = e->Next; + + } while (e != hull ->edges); + + + /* Delete all vertices that have been processed but + are not on the hull. */ + + while ( hull ->vertices && hull->vertices->mark && !hull ->vertices->onhull ) { + + v = hull ->vertices; + XDELETE(hull ->vertices, v ); + } + + + v = hull ->vertices->Next; + do { + if (v->mark && !v->onhull ) { + t = v; + v = v->Next; + XDELETE(hull ->vertices, t ) + } + else + v = v->Next; + + } while ( v != hull ->vertices ); + + + /* Reset flags. */ + + v = hull ->vertices; + do { + v->duplicate = NULL; + v->onhull = !ONHULL; + v = v->Next; + + } while (v != hull->vertices ); + +} + + + + +/*--------------------------------------------------------------------- +MakeCcw puts the vertices in the face structure in counterclock wise +order. We want to store the vertices in the same +order as in the Visible face. The third Vertex is always p. + +Although no specific ordering of the edges of a face are used +by the code, the following condition is maintained for each face f: +one of the two endpoints of f->Edge[i] matches f->Vertex[i]. +But note that this does not imply that f->Edge[i] is between +f->Vertex[i] and f->Vertex[(i+1)%3]. (Thanks to Bob Williamson.) +---------------------------------------------------------------------*/ + +static +void MakeCcw(LPFACE f, LPEDGE e, LPVERTEX p) +{ + LPFACE fv; /* The Visible face adjacent to e */ + int i; /* Index of e->endpoint[0] in fv. */ + LPEDGE s; /* Temporary, for swapping */ + + if (e->AdjFace[0]->Visible) + + fv = e->AdjFace[0]; + else + fv = e->AdjFace[1]; + + /* Set Vertex[0] & [1] of f to have the same orientation + as do the corresponding vertices of fv. */ + + for ( i=0; fv->Vertex[i] != e->EndPts[0]; ++i ) + ; + + /* Orient f the same as fv. */ + + if ( fv->Vertex[ (i+1) % 3 ] != e->EndPts[1] ) { + + f->Vertex[0] = e->EndPts[1]; + f->Vertex[1] = e->EndPts[0]; + } + else { + f->Vertex[0] = e->EndPts[0]; + f->Vertex[1] = e->EndPts[1]; + SWAP( s, f->Edge[1], f->Edge[2] ); + } + + /* This swap is tricky. e is Edge[0]. Edge[1] is based on endpt[0], + Edge[2] on endpt[1]. So if e is oriented "forwards," we + need to move Edge[1] to follow [0], because it precedes. */ + + f->Vertex[2] = p; +} + +/*--------------------------------------------------------------------- +MakeConeFace makes a new face and two new edges between the +Edge and the point that are passed to it. It returns a pointer to +the new face. +---------------------------------------------------------------------*/ + +static +LPFACE MakeConeFace(LPHULL hull, LPEDGE e, LPVERTEX p) +{ + LPEDGE new_edge[2]; + LPFACE new_face; + int i, j; + + /* Make two new edges (if don't already exist). */ + + for ( i=0; i < 2; ++i ) + /* If the Edge exists, copy it into new_edge. */ + if ( !( new_edge[i] = e->EndPts[i]->duplicate) ) { + + /* Otherwise (duplicate is NULL), MakeNullEdge. */ + new_edge[i] = MakeNullEdge(hull); + new_edge[i]->EndPts[0] = e->EndPts[i]; + new_edge[i]->EndPts[1] = p; + e->EndPts[i]->duplicate = new_edge[i]; + } + + /* Make the new face. */ + new_face = MakeNullFace(hull); + new_face->Edge[0] = e; + new_face->Edge[1] = new_edge[0]; + new_face->Edge[2] = new_edge[1]; + MakeCcw( new_face, e, p ); + + /* Set the adjacent face pointers. */ + for ( i=0; i < 2; ++i ) + for ( j=0; j < 2; ++j ) + /* Only one NULL link should be set to new_face. */ + if ( !new_edge[i]->AdjFace[j] ) { + new_edge[i]->AdjFace[j] = new_face; + break; + } + + return new_face; +} + + +/*--------------------------------------------------------------------- +AddOne is passed a Vertex. It first determines all faces Visible from +that point. If none are Visible then the point is marked as not +onhull. Next is a loop over edges. If both faces adjacent to an Edge +are Visible, then the Edge is marked for deletion. If just one of the +adjacent faces is Visible then a new face is constructed. +---------------------------------------------------------------------*/ +static +BOOL AddOne(LPHULL hull, LPVERTEX p) +{ + LPFACE f; + LPEDGE e, temp; + int vol; + BOOL vis = false; + + + /* Mark faces Visible from p. */ + f = hull -> faces; + + do { + + vol = VolumeSign(f, p); + + if ( vol < 0 ) { + f->Visible = VISIBLE; + vis = true; + } + + f = f->Next; + + } while ( f != hull ->faces ); + + /* If no faces are Visible from p, then p is inside the hull. */ + + if ( !vis ) { + + p->onhull = !ONHULL; + return false; + } + + /* Mark edges in interior of Visible region for deletion. + Erect a NewFace based on each border Edge. */ + + e = hull ->edges; + + do { + + temp = e->Next; + + if ( e->AdjFace[0]->Visible && e->AdjFace[1]->Visible ) + /* e interior: mark for deletion. */ + e->DoDelete = REMOVED; + + else + if ( e->AdjFace[0]->Visible || e->AdjFace[1]->Visible ) + /* e border: make a new face. */ + e->NewFace = MakeConeFace(hull, e, p ); + + e = temp; + + } while ( e != hull ->edges ); + + return true; +} + + +/*--------------------------------------------------------------------- + DoubleTriangle builds the initial double triangle. It first finds 3 + noncollinear points and makes two faces out of them, in opposite order. + It then finds a fourth point that is not coplanar with that face. The + vertices are stored in the face structure in counterclockwise order so + that the volume between the face and the point is negative. Lastly, the + 3 newfaces to the fourth point are constructed and the data structures + are cleaned up. +---------------------------------------------------------------------*/ + +static +BOOL DoubleTriangle(LPHULL hull) +{ + LPVERTEX v0, v1, v2, v3; + LPFACE f0, f1 = NULL; + int vol; + + /* Find 3 noncollinear points. */ + v0 = hull ->vertices; + while ( Collinear( v0, v0->Next, v0->Next->Next ) ) + if ( ( v0 = v0->Next ) == hull->vertices ) + return false; /* All points are Collinear! */ + + v1 = v0->Next; + v2 = v1->Next; + + /* Mark the vertices as processed. */ + v0->mark = PROCESSED; + v1->mark = PROCESSED; + v2->mark = PROCESSED; + + /* Create the two "twin" faces. */ + f0 = MakeFace(hull, v0, v1, v2, f1 ); + f1 = MakeFace(hull, v2, v1, v0, f0 ); + + /* Link adjacent face fields. */ + f0->Edge[0]->AdjFace[1] = f1; + f0->Edge[1]->AdjFace[1] = f1; + f0->Edge[2]->AdjFace[1] = f1; + f1->Edge[0]->AdjFace[1] = f0; + f1->Edge[1]->AdjFace[1] = f0; + f1->Edge[2]->AdjFace[1] = f0; + + /* Find a fourth, noncoplanar point to form tetrahedron. */ + v3 = v2->Next; + vol = VolumeSign( f0, v3 ); + + while ( !vol ) { + + if ( ( v3 = v3->Next ) == v0 ) + return false; /* All points are coplanar! */ + + vol = VolumeSign( f0, v3 ); + } + + /* Insure that v3 will be the first added. */ + hull ->vertices = v3; + return true; +} + + + +/*--------------------------------------------------------------------- +ConstructHull adds the vertices to the hull one at a time. The hull +vertices are those in the list marked as onhull. +---------------------------------------------------------------------*/ +static +void ConstructHull(LPHULL hull) +{ + LPVERTEX v, vnext; + BOOL changed; /* T if addition changes hull; not used. */ + + v = hull->vertices; + + do { + vnext = v->Next; + + changed = false; + + if (!v->mark ) { + + v->mark = PROCESSED; + changed = AddOne(hull, v ); + + CleanEdges(hull); + CleanFaces(hull); + CleanVertices(hull); + } + + v = vnext; + + } while (v != hull->vertices ); + +} + + + +/*-------------------------------------------------------------------*/ + + +static +void AddVec( VEC3I q, VEC3I ray ) +{ + int i; + + for( i = 0; i < DIM; i++ ) + ray[i] = q[i] + ray[i]; +} + +/*--------------------------------------------------------------------- +a - b ==> c. +---------------------------------------------------------------------*/ +static +void SubVec( VEC3I a, VEC3I b, VEC3I c ) +{ + int i; + + for( i = 0; i < DIM; i++ ) + c[i] = a[i] - b[i]; +} + + +/*--------------------------------------------------------------------- +Returns the dot product of the two input vectors. +---------------------------------------------------------------------*/ +static +double Dot( VEC3I a, LPVEC3 b ) +{ + int i; + double sum = 0.0; + + for( i = 0; i < DIM; i++ ) + sum += a[i] * b->n[i]; + + return sum; +} + +/*--------------------------------------------------------------------- +Compute the cross product of (b-a)x(c-a) and place into N. +---------------------------------------------------------------------*/ +static +void NormalVec( VEC3I a, VEC3I b, VEC3I c, LPVEC3 N ) +{ + N->n[X] = ( c[Z] - a[Z] ) * ( b[Y] - a[Y] ) - + ( b[Z] - a[Z] ) * ( c[Y] - a[Y] ); + N->n[Y] = ( b[Z] - a[Z] ) * ( c[X] - a[X] ) - + ( b[X] - a[X] ) * ( c[Z] - a[Z] ); + N->n[Z] = ( b[X] - a[X] ) * ( c[Y] - a[Y] ) - + ( b[Y] - a[Y] ) * ( c[X] - a[X] ); +} + + + + +static +int InBox( VEC3I q, VEC3I bmin, VEC3I bmax ) +{ + + if( ( bmin[X] <= q[X] ) && ( q[X] <= bmax[X] ) && + ( bmin[Y] <= q[Y] ) && ( q[Y] <= bmax[Y] ) && + ( bmin[Z] <= q[Z] ) && ( q[Z] <= bmax[Z] ) ) + return true; + + return false; +} + + + +/* + This function returns a char: + '0': the segment [ab] does not intersect (completely misses) the + bounding box surrounding the n-th triangle T. It lies + strictly to one side of one of the six supporting planes. + '?': status unknown: the segment may or may not intersect T. +*/ + +static +char BoxTest(LPHULL hull, int n, VEC3I a, VEC3I b) +{ + int i; /* Coordinate index */ + int w; + + for ( i=0; i < DIM; i++ ) { + + w = hull ->Box[ n ][0][i]; /* min: lower left */ + + if ( (a[i] < w) && (b[i] < w) ) return '0'; + + w = hull ->Box[ n ][1][i]; /* max: upper right */ + + if ( (a[i] > w) && (b[i] > w) ) return '0'; + } + + return '?'; +} + + + +/* Return a random ray endpoint */ + +static +void RandomRay( VEC3I ray, int radius ) +{ + double x, y, z, w, t; + + /* Generate a random point on a sphere of radius 1. */ + /* the sphere is sliced at z, and a random point at angle t + generated on the circle of intersection. */ + + z = 2.0 * (double) rand() / RAND_MAX - 1.0; + t = 2.0 * M_PI * (double) rand() / RAND_MAX; + w = sqrt( 1 - z*z ); + x = w * cos( t ); + y = w * sin( t ); + + ray[X] = (int) ( radius * x ); + ray[Y] = (int) ( radius * y ); + ray[Z] = (int) ( radius * z ); +} + + + +static +int ComputeBox(LPHULL hull, int F, VEC3I bmin, VEC3I bmax ) +{ + int i, j; + double radius; + + for( i = 0; i < F; i++ ) + for( j = 0; j < DIM; j++ ) { + + if( hull ->Vertices[i][j] < bmin[j] ) + bmin[j] = hull ->Vertices[i][j]; + + if( hull ->Vertices[i][j] > bmax[j] ) + bmax[j] = hull ->Vertices[i][j]; + } + + radius = sqrt( pow( (double)(bmax[X] - bmin[X]), 2.0 ) + + pow( (double)(bmax[Y] - bmin[Y]), 2.0 ) + + pow( (double)(bmax[Z] - bmin[Z]), 2.0 ) ); + + return (int)( radius +1 ) + 1; +} + + +/*--------------------------------------------------------------------- +Computes N & D and returns index m of largest component. +---------------------------------------------------------------------*/ +static +int PlaneCoeff(LPHULL hull, VEC3I T, LPVEC3 N, double *D ) +{ + int i; + double t; /* Temp storage */ + double biggest = 0.0; /* Largest component of normal vector. */ + int m = 0; /* Index of largest component. */ + + NormalVec(hull ->Vertices[T[0]], hull ->Vertices[T[1]], hull ->Vertices[T[2]], N ); + *D = Dot( hull ->Vertices[T[0]], N ); + + /* Find the largest component of N. */ + for ( i = 0; i < DIM; i++ ) { + t = fabs( N->n[i] ); + if ( t > biggest ) { + biggest = t; + m = i; + } + } + return m; +} + +/*--------------------------------------------------------------------- + 'p': The segment lies wholly within the plane. + 'q': The q endpoint is on the plane (but not 'p'). + 'r': The r endpoint is on the plane (but not 'p'). + '0': The segment lies strictly to one side or the other of the plane. + '1': The segement intersects the plane, and 'p' does not hold. +---------------------------------------------------------------------*/ +static +char SegPlaneInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p, int *m) +{ + VEC3 N; double D; + VEC3I rq; + double num, denom, t; + int i; + + *m = PlaneCoeff(hull, T, &N, &D ); + num = D - Dot( q, &N ); + SubVec( r, q, rq ); + denom = Dot( rq, &N ); + + if ( denom == 0.0 ) { /* Segment is parallel to plane. */ + if ( num == 0.0 ) /* q is on plane. */ + return 'p'; + else + return '0'; + } + else + t = num / denom; + + for( i = 0; i < DIM; i++ ) + p->n[i] = q[i] + t * ( r[i] - q[i] ); + + if ( (0.0 < t) && (t < 1.0) ) + return '1'; + else if ( num == 0.0 ) /* t == 0 */ + return 'q'; + else if ( num == denom ) /* t == 1 */ + return 'r'; + else return '0'; +} + + + +static +int AreaSign( VEC3I a, VEC3I b, VEC3I c ) +{ + double area2; + + area2 = ( b[0] - a[0] ) * (double)( c[1] - a[1] ) - + ( c[0] - a[0] ) * (double)( b[1] - a[1] ); + + /* The area should be an integer. */ + if ( area2 > 0.5 ) return 1; + else if ( area2 < -0.5 ) return -1; + else return 0; +} + + +static +char InTri2D( VEC3I Tp[3], VEC3I pp ) +{ + int area0, area1, area2; + + /* compute three AreaSign() values for pp w.r.t. each Edge of the face in 2D */ + area0 = AreaSign( pp, Tp[0], Tp[1] ); + area1 = AreaSign( pp, Tp[1], Tp[2] ); + area2 = AreaSign( pp, Tp[2], Tp[0] ); + + if ( (( area0 == 0 ) && ( area1 > 0 ) && ( area2 > 0 )) || + (( area1 == 0 ) && ( area0 > 0 ) && ( area2 > 0 )) || + (( area2 == 0 ) && ( area0 > 0 ) && ( area1 > 0 )) ) + return 'E'; + + if ( (( area0 == 0 ) && ( area1 < 0 ) && ( area2 < 0 )) || + (( area1 == 0 ) && ( area0 < 0 ) && ( area2 < 0 )) || + (( area2 == 0 ) && ( area0 < 0 ) && ( area1 < 0 ))) + return 'E'; + + if ( (( area0 > 0 ) && ( area1 > 0 ) && ( area2 > 0 )) || + (( area0 < 0 ) && ( area1 < 0 ) && ( area2 < 0 ))) + return 'F'; + + if ( ( area0 == 0 ) && ( area1 == 0 ) && ( area2 == 0 ) ) + return '?'; /* Error in InTriD */ + + if ( (( area0 == 0 ) && ( area1 == 0 )) || + (( area0 == 0 ) && ( area2 == 0 )) || + (( area1 == 0 ) && ( area2 == 0 )) ) + return 'V'; + + else + return '0'; +} + +/* Assumption: p lies in the plane containing T. + Returns a char: + 'V': the query point p coincides with a Vertex of triangle T. + 'E': the query point p is in the relative interior of an Edge of triangle T. + 'F': the query point p is in the relative interior of a Face of triangle T. + '0': the query point p does not intersect (misses) triangle T. +*/ + +static +char InTri3D(LPHULL hull, VEC3I T, int m, VEC3I p ) +{ + int i; /* Index for X,Y,Z */ + int j; /* Index for X,Y */ + int k; /* Index for triangle Vertex */ + VEC3I pp; /* projected p */ + VEC3I Tp[3]; /* projected T: three new vertices */ + + /* Project out coordinate m in both p and the triangular face */ + j = 0; + for ( i = 0; i < DIM; i++ ) { + if ( i != m ) { /* skip largest coordinate */ + pp[j] = p[i]; + for ( k = 0; k < 3; k++ ) + Tp[k][j] = hull->Vertices[T[k]][i]; + j++; + } + } + return( InTri2D( Tp, pp ) ); +} + + + +static +int VolumeSign2( VEC3I a, VEC3I b, VEC3I c, VEC3I d ) +{ + double vol; + double ax, ay, az, bx, by, bz, cx, cy, cz, dx, dy, dz; + double bxdx, bydy, bzdz, cxdx, cydy, czdz; + + ax = a[X]; + ay = a[Y]; + az = a[Z]; + bx = b[X]; + by = b[Y]; + bz = b[Z]; + cx = c[X]; + cy = c[Y]; + cz = c[Z]; + dx = d[X]; + dy = d[Y]; + dz = d[Z]; + + bxdx=bx-dx; + bydy=by-dy; + bzdz=bz-dz; + cxdx=cx-dx; + cydy=cy-dy; + czdz=cz-dz; + vol = (az-dz) * (bxdx*cydy - bydy*cxdx) + + (ay-dy) * (bzdz*cxdx - bxdx*czdz) + + (ax-dx) * (bydy*czdz - bzdz*cydy); + + + /* The volume should be an integer. */ + if ( vol > 0.5 ) return 1; + else if ( vol < -0.5 ) return -1; + else return 0; +} + + + + +/*--------------------------------------------------------------------- +The signed volumes of three tetrahedra are computed, determined +by the segment qr, and each Edge of the triangle. +Returns a char: + 'v': the open segment includes a Vertex of T. + 'e': the open segment includes a point in the relative interior of an Edge + of T. + 'f': the open segment includes a point in the relative interior of a face + of T. + '0': the open segment does not intersect triangle T. +---------------------------------------------------------------------*/ + +static +char SegTriCross(LPHULL hull, VEC3I T, VEC3I q, VEC3I r ) +{ + int vol0, vol1, vol2; + + vol0 = VolumeSign2( q, hull->Vertices[ T[0] ], hull->Vertices[ T[1] ], r ); + vol1 = VolumeSign2( q, hull->Vertices[ T[1] ], hull->Vertices[ T[2] ], r ); + vol2 = VolumeSign2( q, hull->Vertices[ T[2] ], hull->Vertices[ T[0] ], r ); + + + /* Same sign: segment intersects interior of triangle. */ + if ( ( ( vol0 > 0 ) && ( vol1 > 0 ) && ( vol2 > 0 ) ) || + ( ( vol0 < 0 ) && ( vol1 < 0 ) && ( vol2 < 0 ) ) ) + return 'f'; + + /* Opposite sign: no intersection between segment and triangle */ + if ( ( ( vol0 > 0 ) || ( vol1 > 0 ) || ( vol2 > 0 ) ) && + ( ( vol0 < 0 ) || ( vol1 < 0 ) || ( vol2 < 0 ) ) ) + return '0'; + + else if ( ( vol0 == 0 ) && ( vol1 == 0 ) && ( vol2 == 0 ) ) + return '?'; /* Error 1 in SegTriCross */ + + /* Two zeros: segment intersects Vertex. */ + else if ( ( ( vol0 == 0 ) && ( vol1 == 0 ) ) || + ( ( vol0 == 0 ) && ( vol2 == 0 ) ) || + ( ( vol1 == 0 ) && ( vol2 == 0 ) ) ) + return 'v'; + + /* One zero: segment intersects Edge. */ + else if ( ( vol0 == 0 ) || ( vol1 == 0 ) || ( vol2 == 0 ) ) + return 'e'; + + else + return '?'; /* Error 2 in SegTriCross */ +} + + + +static +char SegTriInt(LPHULL hull, VEC3I T, VEC3I q, VEC3I r, LPVEC3 p ) +{ + int code; + int m = -1; + + code = SegPlaneInt(hull, T, q, r, p, &m ); + + if ( code == '0') return '0'; + else if ( code == 'q') return InTri3D(hull, T, m, q ); + else if ( code == 'r') return InTri3D(hull, T, m, r ); + else if ( code == 'p') return 'p'; + else if ( code == '1' ) return SegTriCross(hull, T, q, r ); + else + return code; /* Error */ +} + + + + +/* + This function returns a char: + 'i': the query point a is strictly interior to polyhedron P. + 'o': the query point a is strictly exterior to( or outside of) polyhedron P. +*/ +char InPolyhedron(LPHULL hull, VEC3I q) +{ + int F = hull->nfaces; + VEC3I Ray; /* Ray endpoint. */ + VEC3 p; /* Intersection point; not used. */ + int f, k = 0, crossings = 0; + char code = '?'; + + + /* If query point is outside bounding box, finished. */ + if ( !InBox( q, hull->bmin, hull->bmax ) ) + return 'o'; + + LOOP: + while( k++ < F ) { + + crossings = 0; + + RandomRay(Ray, hull->radius ); + + AddVec( q, Ray ); + + + for ( f = 0; f < F; f++ ) { /* Begin check each face */ + + if ( BoxTest(hull, f, q, Ray ) == '0' ) { + code = '0'; + + } + else code = SegTriInt(hull, hull->Faces[f], q, Ray, &p ); + + + /* If ray is degenerate, then goto outer while to generate another. */ + if ( code == 'p' || code == 'v' || code == 'e' ) { + + goto LOOP; + } + + /* If ray hits face at interior point, increment crossings. */ + else if ( code == 'f' ) { + crossings++; + + } + + /* If query endpoint q sits on a V/E/F, return inside. */ + else if ( code == 'V' || code == 'E' || code == 'F' ) + return code; /* 'i'; MM2 */ + + /* If ray misses triangle, do nothing. */ + else if ( code == '0' ) + ; + + else + return '?'; /* Error */ + + } + /* No degeneracies encountered: ray is generic, so finished. */ + break; + + } /* End while loop */ + + + /* q strictly interior to polyhedron iff an odd number of crossings. */ + if( ( crossings % 2 ) == 1 ) + return 'i'; + + else return 'o'; +} + + +/*/ ---------------------------------------------------------------------------------- */ + + + + +static +void StoreResults(LPHULL hull) +{ + + int i, w; + LPVERTEX v; + LPFACE f; + int V = 0, F = 0; + int j, k; + + /* Vertices */ + + v = hull ->vertices; + V = 0; + do { + + v -> vnum = V; + hull ->Vertices[V][X] = v -> v[X]; + hull ->Vertices[V][Y] = v -> v[Y]; + hull ->Vertices[V][Z] = v -> v[Z]; + + v = v->Next; + V++; + + } while ( v != hull ->vertices ); + + hull ->nvertex = V; + + /* Faces */ + f = hull ->faces; + F = 0; + do { + + hull ->Faces[F][0] = f->Vertex[0]->vnum; + hull ->Faces[F][1] = f->Vertex[1]->vnum; + hull ->Faces[F][2] = f->Vertex[2]->vnum; + + for ( j=0; j < 3; j++ ) { + + hull ->Box[F][0][j] = hull ->Vertices[ hull ->Faces[F][0] ][j]; + hull ->Box[F][1][j] = hull ->Vertices[ hull ->Faces[F][0] ][j]; + } + + /* Check k=1,2 vertices of face. */ + for ( k=1; k < 3; k++ ) + for ( j=0; j < 3; j++ ) { + + w = hull ->Vertices[ hull ->Faces[F][k] ][j]; + if ( w < hull ->Box[F][0][j] ) hull ->Box[F][0][j] = w; + if ( w > hull ->Box[F][1][j] ) hull ->Box[F][1][j] = w; + } + + + f = f->Next; F++; + + } while ( f != hull ->faces ); + + + hull ->nfaces = F; + + + /* Initialize the bounding box */ + for ( i = 0; i < DIM; i++ ) + hull ->bmin[i] = hull ->bmax[i] = hull ->Vertices[0][i]; + + hull ->radius = ComputeBox(hull, V, hull ->bmin, hull ->bmax ); + + +} + + +LCMSHANDLE cmsxHullInit(void) +{ + LPHULL hull = (LPHULL) malloc(sizeof(HULL)); + + ZeroMemory(hull, sizeof(HULL)); + + hull->vnumCounter = 0; + hull->vertices = NULL; + hull->edges = NULL; + hull->faces = NULL; + hull->nfaces = 0; + hull->nvertex = 0; + + return (LCMSHANDLE) (LPSTR) hull; +} + + +void cmsxHullDone(LCMSHANDLE hHull) +{ + LPHULL hull = (LPHULL) (LPSTR) hHull; + + if (hull) + free((LPVOID) hull); +} + + +BOOL cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z) +{ + LPVERTEX v; + LPHULL hull = (LPHULL) (LPSTR) hHull; + + + v = MakeNullVertex(hull); + v->v[X] = x; + v->v[Y] = y; + v->v[Z] = z; + v->vnum = hull->vnumCounter++; + + return true; +} + +BOOL cmsxHullComputeHull(LCMSHANDLE hHull) +{ + + LPHULL hull = (LPHULL) (LPSTR) hHull; + + if (!DoubleTriangle(hull)) return false; + + ConstructHull(hull); + StoreResults(hull); + + return true; +} + + +char cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z) +{ + VEC3I q; + LPHULL hull = (LPHULL) (LPSTR) hHull; + + q[X] = x; q[Y] = y; q[Z] = z; + + return InPolyhedron(hull, q ) ; +} + + +BOOL cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname) +{ + FILE* fp; + int i; + LPHULL hull = (LPHULL) (LPSTR) hHull; + + fp = fopen (fname, "wt"); + if (fp == NULL) + return false; + + fprintf (fp, "#VRML V2.0 utf8\n"); + + /* set the viewing orientation and distance */ + fprintf (fp, "DEF CamTest Group {\n"); + fprintf (fp, "\tchildren [\n"); + fprintf (fp, "\t\tDEF Cameras Group {\n"); + fprintf (fp, "\t\t\tchildren [\n"); + fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); + fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); + fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); + fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t]\n"); + fprintf (fp, "\t\t},\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + /* Output the background stuff */ + fprintf (fp, "Background {\n"); + fprintf (fp, "\tskyColor [\n"); + fprintf (fp, "\t\t.5 .5 .5\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + /* Output the shape stuff */ + fprintf (fp, "Transform {\n"); + fprintf (fp, "\tscale 8 8 8\n"); + fprintf (fp, "\tchildren [\n"); + + /* Draw the axes as a shape: */ + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); + fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t\tcoordIndex [\n"); + fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + + + /* Draw the triangles as a shape: */ + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 0 0 0\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry IndexedFaceSet {\n"); + fprintf (fp, "\t\t\t\tsolid false\n"); + + /* fill in the points here */ + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + + for (i = 0; i < hull->nvertex; ++i) + { + fprintf (fp, "\t\t\t\t\t%g %g %g%c\n", + (double) hull->Vertices[i][X], (double) hull->Vertices[i][Y], (double) hull->Vertices[i][Z], + i == hull->nvertex-1? ']': ','); + } + fprintf (fp, "\t\t\t\t}\n"); + + /* fill in the Vertex indices (followed by -1) */ + + + fprintf (fp, "\t\t\t\tcoordIndex [\n"); + for (i = 0; i < hull->nfaces; ++i) + { + fprintf (fp, "\t\t\t\t\t%d, %d, %d, -1\n", + hull->Faces[i][0], hull->Faces[i][1], hull->Faces[i][2]); + + } + fprintf (fp, "]\n"); + + + /* fill in the face colors */ + fprintf (fp, "\t\t\t\tcolor Color {\n"); + fprintf (fp, "\t\t\t\t\tcolor [\n"); + for (i = 0; i < hull->nfaces; ++i) + { + int vx, vy, vz; + double r, g, b; + + vx = hull->Faces[i][0]; vy = hull->Faces[i][1]; vz = hull->Faces[i][2]; + r = (double) (hull->Vertices[vx][X] + hull->Vertices[vy][X] + hull->Vertices[vz][X]) / (3* 255); + g = (double) (hull->Vertices[vx][Y] + hull->Vertices[vy][Y] + hull->Vertices[vz][Y]) / (3* 255); + b = (double) (hull->Vertices[vx][Z] + hull->Vertices[vy][Z] + hull->Vertices[vz][Z]) / (3* 255); + + fprintf (fp, "\t\t\t\t\t%g %g %g%c\n", r, g, b, + i == hull->nfaces-1? ']': ','); + } + fprintf (fp, "\t\t\t}\n"); + + fprintf (fp, "\t\t\tcolorPerVertex false\n"); + + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + fclose (fp); + + return true; +} diff --git a/src/libs/lprof/cmslm.cpp b/src/libs/lprof/cmslm.cpp new file mode 100644 index 00000000..81d86ba6 --- /dev/null +++ b/src/libs/lprof/cmslm.cpp @@ -0,0 +1,288 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ma 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + +#include "lcmsprf.h" + + +/* From "numerical recipes in C" */ +/* */ +/* Levenberg-Marquardt method, attempting to reduce the value X2 of a */ +/* fit between a set of data points x[1..ndata], y[1..ndata] with individual */ +/* standard deviations sig[1..ndata], and a nonlinear function dependent */ +/* on ma coefficients a[1..ma]. The input array ia[1..ma] */ +/* indicates by nonzero entries those components of a that should be */ +/* fitted for, and by zero entries those components that should be held */ +/* fixed at their input values. The program returns current best-fitt */ +/* values for the parameters a[1..ma], and chisq. The arrays */ +/* covar[1..ma][1..ma], alpha[1..ma][1..ma] are used as */ +/* working space during most iterations. Supply a routine */ +/* funcs(x, a, yfit, dyda, ma) */ +/* that evaluates the fitting function yfit, and its derivatives dyda[1..ma] */ +/* with respect to the fitting parameters a at x. On the first call provide */ +/* an initial guess for the parameters a, and set alamda<0 for initialization */ +/* (which then sets alamda=.001). If a step succeeds chisq becomes smaller */ +/* and alamda decreases by a factor of 10. If a step fails alamda grows by */ +/* a factor of 10. You must call this routine repeatedly until convergence */ +/* is achieved. Then, make one final call with alamda=0, so that */ +/* covar[1..ma][1..ma] returns the covar matrix, and alpha the */ +/* alpha matrix. (Parameters held fixed will return zero covariances.) */ + + +LCMSHANDLE cdecl cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig, + double a[], + int ma, + void (*funcs)(double, double[], double*, double[], int) + ); + +double cdecl cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ); +double cdecl cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ); +BOOL cdecl cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ); +BOOL cdecl cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ); + +/* ---------------------------------------------------------------------------- */ + + + +typedef struct { + + LPSAMPLEDCURVE x; + LPSAMPLEDCURVE y; + int ndata; + double* a; + int ma; + LPMATN covar; + LPMATN alpha; + double* atry; + LPMATN beta; + LPMATN oneda; + double* dyda; + double ochisq; + double sig; + + + void (*funcs)(double, double[], double*, double[], int); + + double alamda; + double chisq; + +} LMRTQMIN, FAR* LPLMRTQMIN; + + + + +static +void mrqcof(LPLMRTQMIN pLM, double *a, LPMATN alpha, LPMATN beta, double *chisq) +{ + int i, j, k; + double ymod, wt, sig2i, dy; + + for(j = 0; j < pLM->ma; j++) + { + for(k = 0; k <= j; k++) + alpha->Values[j][k] = 0.0; + + beta->Values[j][0] = 0.0; + } + + *chisq = 0.0; + sig2i = 1.0 / (pLM->sig * pLM->sig); + + for(i = 0; i < pLM->ndata; i++) + { + (*(pLM->funcs))(pLM->x ->Values[i], a, &ymod, pLM->dyda, pLM->ma); + + dy = pLM->y->Values[i] - ymod; + + for(j = 0; j < pLM->ma; j++) + { + wt = pLM->dyda[j] * sig2i; + + for(k = 0; k <= j; k++) + alpha->Values[j][k] += wt * pLM->dyda[k]; + + beta->Values[j][0] += dy * wt; + } + + *chisq += dy * dy * sig2i; + } + + for(j = 1; j < pLM->ma; j++) /* Fill in the symmetric side. */ + for(k = 0; k < j; k++) + alpha->Values[k][j] = alpha->Values[j][k]; +} + + + +static +void FreeStruct(LPLMRTQMIN pLM) +{ + if(pLM == NULL) return; + + if(pLM->covar) MATNfree (pLM->covar); + if(pLM->alpha) MATNfree (pLM->alpha); + if(pLM->atry) free(pLM->atry); + if(pLM->beta) MATNfree (pLM->beta); + if(pLM->oneda) MATNfree (pLM->oneda); + if(pLM->dyda) free(pLM->dyda); + free(pLM); +} + + + +LCMSHANDLE cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig, + double a[], + int ma, + void (*funcs)(double, double[], double*, double[], int)) + +{ + int i; + LPLMRTQMIN pLM; + + if (x ->nItems != y ->nItems) return NULL; + + pLM = (LPLMRTQMIN) malloc(sizeof(LMRTQMIN)); + if(!pLM) + return NULL; + + ZeroMemory(pLM, sizeof(LMRTQMIN)); + + if((pLM->atry = (double*)malloc(ma * sizeof(double))) == NULL) goto failed; + if((pLM->beta = MATNalloc (ma, 1)) == NULL) goto failed; + if((pLM->oneda = MATNalloc (ma, 1)) == NULL) goto failed; + + + + if((pLM->covar = MATNalloc(ma, ma)) == NULL) goto failed; + if((pLM->alpha = MATNalloc(ma, ma)) == NULL) goto failed; + if((pLM->dyda = (double*)malloc(ma * sizeof(double))) == NULL) goto failed; + + pLM->alamda = 0.001; + + pLM->ndata = x ->nItems; + pLM->x = x; + pLM->y = y; + pLM->ma = ma; + pLM->a = a; + pLM->funcs = funcs; + pLM->sig = sig; + + mrqcof(pLM, a, pLM->alpha, pLM->beta, &pLM->chisq); + pLM->ochisq = (pLM->chisq); + + for(i = 0; i < ma; i++) pLM->atry[i] = a[i]; + + return (LCMSHANDLE) pLM; + +failed: + FreeStruct(pLM); + return NULL; +} + + +BOOL cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ) +{ + LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ; + if(!pLM) + return false; + + FreeStruct(pLM); + return true; +} + + +BOOL cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ) +{ + int j, k; + BOOL sts; + LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ; + if(!pLM) + return false; + + for(j = 0; j < pLM->ma; j++) /* Alter linearized fitting matrix, by augmenting diagonal elements. */ + { + for(k = 0; k < pLM->ma; k++) + pLM->covar->Values[j][k] = pLM->alpha->Values[j][k]; + + pLM->covar->Values[j][j] = pLM->alpha->Values[j][j] * (1.0 + pLM ->alamda); + pLM->oneda->Values[j][0] = pLM->beta->Values[j][0]; + } + + if((sts = MATNsolve (pLM->covar, pLM->oneda)) != true) /* Matrix solution. */ + return sts; + + for(j = 0; j < pLM->ma; j++) /* Did the trial succeed? */ + pLM->atry[j] = pLM->a[j] + pLM->oneda->Values[j][0]; + + mrqcof(pLM, pLM->atry, pLM->covar, pLM->oneda, &pLM -> chisq); + + if (pLM->chisq < pLM->ochisq) { /* Success, accept the new solution. */ + + pLM->alamda *= 0.1; + pLM->ochisq = pLM->chisq; + + for(j = 0; j < pLM->ma; j++) + { + for(k = 0; k < pLM->ma; k++) + pLM->alpha->Values[j][k] = pLM->covar->Values[j][k]; + + pLM->beta->Values[j][0] = pLM->oneda->Values[j][0]; + } + + for (j=0; j < pLM ->ma; j++) pLM->a[j] = pLM->atry[j]; + } + else /* Failure, increase alamda and return. */ + { + pLM -> alamda *= 10.0; + pLM->chisq = pLM->ochisq; + } + + return true; +} + + +double cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ) +{ + LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ; + + return pLM ->alamda; +} + +double cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ) +{ + LPLMRTQMIN pLM = (LPLMRTQMIN)hMRQ; + + return pLM ->chisq; +} diff --git a/src/libs/lprof/cmslnr.cpp b/src/libs/lprof/cmslnr.cpp new file mode 100644 index 00000000..dddd8e38 --- /dev/null +++ b/src/libs/lprof/cmslnr.cpp @@ -0,0 +1,560 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + + +#include "lcmsprf.h" + + +LPGAMMATABLE cdecl cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints); +void cdecl cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium); + +void cdecl cmsxComputeLinearizationTables(LPMEASUREMENT m, + int ColorSpace, + LPGAMMATABLE Lin[3], + int nResultingPoints, + int Medium); + + +void cdecl cmsxApplyLinearizationTable(double In[3], + LPGAMMATABLE Gamma[3], + double Out[3]); + +void cdecl cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3]); + + + +/* ------------------------------------------------------------- Implementation */ + + +#define EPSILON 0.00005 +#define LEVENBERG_MARQUARDT_ITERATE_MAX 150 + +/* In order to track linearization tables, we use following procedure */ +/* */ +/* We first assume R', G' and B' does exhibit a non-linear behaviour */ +/* that can be separated for each channel as Yr(R'), Yg(G'), Yb(B') */ +/* This is the shaper step */ +/* */ +/* R = Lr(R') */ +/* G = Lg(G') */ +/* B = Lb(B') (0.0) */ +/* */ +/* After this step, RGB is converted to XYZ by a matrix multiplication */ +/* */ +/* |X| |R| */ +/* |Y| = [M]·|G| */ +/* |Z| |B| (1.0) */ +/* */ +/* In order to extract Lr,Lg,Lb tables, we are interested only on Y part */ +/* */ +/* Y = (m1 * R + m2 * G + m3 * B) (1.1) */ +/* */ +/* The total intensity for maximum RGB = (1, 1, 1) should be 1, */ +/* */ +/* 1 = m1 * 1 + m2 * 1 + m3 * 1, so */ +/* */ +/* m1 + m2 + m3 = 1.0 (1.2) */ +/* */ +/* We now impose that for neutral (gray) patches, RGB components must be equal */ +/* */ +/* R = G = B = Gray */ +/* */ +/* So, substituting in (1.1): */ +/* */ +/* Y = (m1 + m2 + m3) Gray */ +/* */ +/* and for (1.2), (m1+m2+m3) = 1, so */ +/* */ +/* Y = Gray = Lr(R') = Lg(G') = Lb(B') */ +/* */ +/* That is, after prelinearization, RGB of gray patches should give */ +/* same values for R, G and B. And this value is Y. */ +/* */ +/* */ + + +static +LPSAMPLEDCURVE NormalizeTo(LPSAMPLEDCURVE X, double N, BOOL lAddEndPoint) +{ + int i, nItems; + LPSAMPLEDCURVE XNorm; + + nItems = X ->nItems; + if (lAddEndPoint) nItems++; + + XNorm = cmsAllocSampledCurve(nItems); + + for (i=0; i < X ->nItems; i++) { + + XNorm ->Values[i] = X ->Values[i] / N; + } + + if (lAddEndPoint) + XNorm -> Values[X ->nItems] = 1.0; + + return XNorm; +} + + +/* */ +/* ------------------------------------------------------------------------------ */ +/* */ +/* Our Monitor model. We assume gamma has a general expression of */ +/* */ +/* Fn(x) = (Gain * x + offset) ^ gamma | for x >= 0 */ +/* Fn(x) = 0 | for x < 0 */ +/* */ +/* First partial derivatives are */ +/* */ +/* dFn/dGamma = Fn * ln(Base) */ +/* dFn/dGain = gamma * x * ((Gain * x + Offset) ^ (gamma -1)) */ +/* dFn/dOffset = gamma * ((Gain * x + Offset) ^ (gamma -1)) */ +/* */ + +static +void GammaGainOffsetFn(double x, double *a, double *y, double *dyda, int na) +{ + double Gamma,Gain,Offset; + double Base; + + Gamma = a[0]; + Gain = a[1]; + Offset = a[2]; + + Base = Gain * x + Offset; + + if (Base < 0) { + + Base = 0.0; + *y = 0.0; + dyda[0] = 0.0; + dyda[1] = 0.0; + dyda[2] = 0.0; + + + } else { + + + /* The function itself */ + *y = pow(Base, Gamma); + + /* dyda[0] is partial derivative across Gamma */ + dyda[0] = *y * log(Base); + + /* dyda[1] is partial derivative across gain */ + dyda[1] = (x * Gamma) * pow(Base, Gamma-1.0); + + /* dyda[2] is partial derivative across offset */ + dyda[2] = Gamma * pow(Base, Gamma-1.0); + } +} + + +/* Fit curve to our gamma-gain-offset model. */ + +static +BOOL OneTry(LPSAMPLEDCURVE XNorm, LPSAMPLEDCURVE YNorm, double a[]) +{ + LCMSHANDLE h; + double ChiSq, OldChiSq; + int i; + BOOL Status = true; + + /* initial guesses */ + + a[0] = 3.0; /* gamma */ + a[1] = 4.0; /* gain */ + a[2] = 6.0; /* offset */ + a[3] = 0.0; /* Thereshold */ + a[4] = 0.0; /* Black */ + + + /* Significance = 0.02 gives good results */ + + h = cmsxLevenbergMarquardtInit(XNorm, YNorm, 0.02, a, 3, GammaGainOffsetFn); + if (h == NULL) return false; + + + OldChiSq = cmsxLevenbergMarquardtChiSq(h); + + for(i = 0; i < LEVENBERG_MARQUARDT_ITERATE_MAX; i++) { + + if (!cmsxLevenbergMarquardtIterate(h)) { + Status = false; + break; + } + + ChiSq = cmsxLevenbergMarquardtChiSq(h); + + if(OldChiSq != ChiSq && (OldChiSq - ChiSq) < EPSILON) + break; + + OldChiSq = ChiSq; + } + + cmsxLevenbergMarquardtFree(h); + + return Status; +} + +/* Tries to fit gamma as per IEC 61966-2.1 using Levenberg-Marquardt method */ +/* */ +/* Y = (aX + b)^Gamma | X >= d */ +/* Y = cX | X < d */ + +LPGAMMATABLE cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints) +{ + double a[5]; + LPSAMPLEDCURVE XNorm, YNorm; + double e, Max; + + + /* Coarse approximation, to find maximum. */ + /* We have only a portion of curve. It is likely */ + /* maximum will not fall on exactly 100. */ + + if (!OneTry(X, Y, a)) + return 0; + + /* Got parameters. Compute maximum. */ + e = a[1]* 255.0 + a[2]; + if (e < 0) return 0; + Max = pow(e, a[0]); + + + /* Normalize values to maximum */ + XNorm = NormalizeTo(X, 255.0, false); + YNorm = NormalizeTo(Y, Max, false); + + /* Do the final fitting */ + if (!OneTry(XNorm, YNorm, a)) + return 0; + + /* Type 3 = IEC 61966-2.1 (sRGB) */ + /* Y = (aX + b)^Gamma | X >= d */ + /* Y = cX | X < d */ + return cmsBuildParametricGamma(nResultingPoints, 3, a); +} + + + + + +/* A dumb bubble sort */ + +static +void Bubble(LPSAMPLEDCURVE C, LPSAMPLEDCURVE L) +{ +#define SWAP(a, b) { tmp = (a); (a) = (b); (b) = tmp; } + + BOOL lSwapped; + int i, nItems; + double tmp; + + nItems = C -> nItems; + do { + lSwapped = false; + + for (i= 0; i < nItems - 1; i++) { + + if (C->Values[i] > C->Values[i+1]) { + + SWAP(C->Values[i], C->Values[i+1]); + SWAP(L->Values[i], L->Values[i+1]); + lSwapped = true; + } + } + + } while (lSwapped); + +#undef SWAP +} + + + +/* Check for monotonicity. Force it if is not the case. */ + +static +void CheckForMonotonicSampledCurve(LPSAMPLEDCURVE t) +{ + int n = t ->nItems; + int i; + double last; + + last = t ->Values[n-1]; + for (i = n-2; i >= 0; --i) { + + if (t ->Values[i] > last) + + t ->Values[i] = last; + else + last = t ->Values[i]; + + } + +} + +/* The main gamma inferer. Tries first by gamma-gain-offset, */ +/* if not proper reverts to curve guessing. */ + +static +LPGAMMATABLE BuildGammaTable(LPSAMPLEDCURVE C, LPSAMPLEDCURVE L, int nResultingPoints) +{ + LPSAMPLEDCURVE Cw, Lw, Cn, Ln; + LPSAMPLEDCURVE out; + LPGAMMATABLE Result; + double Lmax, Lend, Cmax; + + /* Try to see if it can be fitted */ + Result = cmsxEstimateGamma(C, L, nResultingPoints); + if (Result) + return Result; + + + /* No... build curve from scratch. Since we have not */ + /* endpoints, a coarse linear extrapolation should be */ + /* applied in order to get the expected maximum. */ + + Cw = cmsDupSampledCurve(C); + Lw = cmsDupSampledCurve(L); + + Bubble(Cw, Lw); + + /* Get endpoint */ + Lmax = Lw->Values[Lw ->nItems - 1]; + Cmax = Cw->Values[Cw ->nItems - 1]; + + /* Linearly extrapolate */ + Lend = (255 * Lmax) / Cmax; + + Ln = NormalizeTo(Lw, Lend, true); + Cn = NormalizeTo(Cw, 255.0, true); + + cmsFreeSampledCurve(Cw); + cmsFreeSampledCurve(Lw); + + /* Add endpoint */ + out = cmsJoinSampledCurves(Cn, Ln, nResultingPoints); + + cmsFreeSampledCurve(Cn); + cmsFreeSampledCurve(Ln); + + CheckForMonotonicSampledCurve(out); + + cmsSmoothSampledCurve(out, nResultingPoints*4.); + cmsClampSampledCurve(out, 0, 1.0); + + Result = cmsConvertSampledCurveToGamma(out, 1.0); + + cmsFreeSampledCurve(out); + return Result; +} + + + + +void cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium) +{ + LPPATCH White; + cmsCIEXYZ WhiteXYZ; + int i; + + if (Medium == MEDIUM_REFLECTIVE_D50) + { + WhiteXYZ.X = D50X * 100.; + WhiteXYZ.Y = D50Y * 100.; + WhiteXYZ.Z = D50Z * 100.; + } + else { + + White = cmsxPCollFindWhite(m, Valids, NULL); + if (!White) return; + + WhiteXYZ = White ->XYZ; + } + + /* For all patches with XYZ and without Lab, add Lab values. */ + /* Transmissive profiles does need to locate its own white */ + /* point for device gray. Reflective does use D50 */ + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + if ((p ->dwFlags & PATCH_HAS_XYZ) && + (!(p ->dwFlags & PATCH_HAS_Lab) || (Medium == MEDIUM_TRANSMISSIVE))) { + + cmsXYZ2Lab(&WhiteXYZ, &p->Lab, &p->XYZ); + p -> dwFlags |= PATCH_HAS_Lab; + } + } + } +} + + +/* Compute linearization tables, trying to fit in a pure */ +/* exponential gamma. If gamma cannot be accurately infered, */ +/* then does build a smooth, monotonic curve that does the job. */ + +void cmsxComputeLinearizationTables(LPMEASUREMENT m, + int ColorSpace, + LPGAMMATABLE Lin[3], + int nResultingPoints, + int Medium) + +{ + LPSAMPLEDCURVE R, G, B, L; + LPGAMMATABLE gr, gg, gb; + SETOFPATCHES Neutrals; + int nGrays; + int i; + + /* We need Lab for grays. */ + cmsxCompleteLabOfPatches(m, m->Allowed, Medium); + + /* Add neutrals, normalize to max */ + Neutrals = cmsxPCollBuildSet(m, false); + cmsxPCollPatchesNearNeutral(m, m ->Allowed, 15, Neutrals); + + nGrays = cmsxPCollCountSet(m, Neutrals); + + R = cmsAllocSampledCurve(nGrays); + G = cmsAllocSampledCurve(nGrays); + B = cmsAllocSampledCurve(nGrays); + L = cmsAllocSampledCurve(nGrays); + + nGrays = 0; + + /* Collect patches */ + for (i=0; i < m -> nPatches; i++) { + + if (Neutrals[i]) { + + LPPATCH gr = m -> Patches + i; + + + R -> Values[nGrays] = gr -> Colorant.RGB[0]; + G -> Values[nGrays] = gr -> Colorant.RGB[1]; + B -> Values[nGrays] = gr -> Colorant.RGB[2]; + L -> Values[nGrays] = gr -> XYZ.Y; + + nGrays++; + } + + } + + + gr = BuildGammaTable(R, L, nResultingPoints); + gg = BuildGammaTable(G, L, nResultingPoints); + gb = BuildGammaTable(B, L, nResultingPoints); + + cmsFreeSampledCurve(R); + cmsFreeSampledCurve(G); + cmsFreeSampledCurve(B); + cmsFreeSampledCurve(L); + + if (ColorSpace == PT_Lab) { + + LPGAMMATABLE Gamma3 = cmsBuildGamma(nResultingPoints, 3.0); + + Lin[0] = cmsJoinGammaEx(gr, Gamma3, nResultingPoints); + Lin[1] = cmsJoinGammaEx(gg, Gamma3, nResultingPoints); + Lin[2] = cmsJoinGammaEx(gb, Gamma3, nResultingPoints); + + cmsFreeGamma(gr); cmsFreeGamma(gg); cmsFreeGamma(gb); + cmsFreeGamma(Gamma3); + } + else { + + + LPGAMMATABLE Gamma1 = cmsBuildGamma(nResultingPoints, 1.0); + + Lin[0] = cmsJoinGammaEx(gr, Gamma1, nResultingPoints); + Lin[1] = cmsJoinGammaEx(gg, Gamma1, nResultingPoints); + Lin[2] = cmsJoinGammaEx(gb, Gamma1, nResultingPoints); + + cmsFreeGamma(gr); cmsFreeGamma(gg); cmsFreeGamma(gb); + cmsFreeGamma(Gamma1); + + } + +} + + + +/* Apply linearization. WORD encoded version */ + +void cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3]) +{ + L16PARAMS Lut16; + + cmsCalcL16Params(Gamma[0] -> nEntries, &Lut16); + + Out[0] = cmsLinearInterpLUT16(In[0], Gamma[0] -> GammaTable, &Lut16); + Out[1] = cmsLinearInterpLUT16(In[1], Gamma[1] -> GammaTable, &Lut16); + Out[2] = cmsLinearInterpLUT16(In[2], Gamma[2] -> GammaTable, &Lut16); + + +} + + + +/* Apply linearization. double version */ + +void cmsxApplyLinearizationTable(double In[3], LPGAMMATABLE Gamma[3], double Out[3]) +{ + WORD rw, gw, bw; + double rd, gd, bd; + L16PARAMS Lut16; + + + cmsCalcL16Params(Gamma[0] -> nEntries, &Lut16); + + rw = (WORD) floor(_cmsxSaturate255To65535(In[0]) + .5); + gw = (WORD) floor(_cmsxSaturate255To65535(In[1]) + .5); + bw = (WORD) floor(_cmsxSaturate255To65535(In[2]) + .5); + + rd = cmsLinearInterpLUT16(rw , Gamma[0] -> GammaTable, &Lut16); + gd = cmsLinearInterpLUT16(gw, Gamma[1] -> GammaTable, &Lut16); + bd = cmsLinearInterpLUT16(bw, Gamma[2] -> GammaTable, &Lut16); + + Out[0] = _cmsxSaturate65535To255(rd); /* back to 0..255 */ + Out[1] = _cmsxSaturate65535To255(gd); + Out[2] = _cmsxSaturate65535To255(bd); +} + diff --git a/src/libs/lprof/cmsmatn.cpp b/src/libs/lprof/cmsmatn.cpp new file mode 100644 index 00000000..bca52717 --- /dev/null +++ b/src/libs/lprof/cmsmatn.cpp @@ -0,0 +1,323 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + + +#include "lcmsprf.h" + + +LPMATN cdecl MATNalloc(int Rows, int Cols); +void cdecl MATNfree (LPMATN mat); +LPMATN cdecl MATNmult(LPMATN a1, LPMATN a2); +double cdecl MATNcross(LPMATN a); +void cdecl MATNscalar (LPMATN a, double scl, LPMATN b); +LPMATN cdecl MATNtranspose (LPMATN a); +BOOL cdecl MATNsolve(LPMATN a, LPMATN b); + + +/* ------------------------------------------------------------ Implementation */ + +/* Free matrix */ + +void MATNfree(LPMATN mat) +{ + int i; + + if (mat == NULL) return; + + for (i = 0; i < mat->Rows; i++) + { + if (mat -> Values[i] != NULL) + free (mat->Values[i]); + } + + free(mat->Values); + free(mat); +} + + +/* Allocate (and Zero) a new matrix */ + +LPMATN MATNalloc(int Rows, int Cols) +{ + int i; + + LPMATN mat = (LPMATN) malloc (sizeof (MATN)); + if (mat == NULL) return mat; + + ZeroMemory(mat, sizeof(MATN)); + + mat->Rows = Rows; + mat->Cols = Cols; + mat->Values = (double**) malloc(Rows * sizeof (double*)); + + if (mat->Values == NULL) { + free(mat); + return NULL; + } + + ZeroMemory(mat -> Values, Rows * sizeof (double*)); + + for (i = 0; i < Rows; i++) + { + mat-> Values [i] = (double*) malloc(Cols * sizeof (double)); + if (mat -> Values[i] == NULL) { + MATNfree(mat); + return NULL; + } + + } + + return mat; +} + +#define DO_SWAP(a, b, tmp) { tmp = (a); (a) = (b); (b) = tmp; } + +/* Gauss-Jordan elimination. There is also a more */ +/* exahustive non-singular matrix checking part. */ + +BOOL MATNsolve(LPMATN a, LPMATN b) +{ + BOOL status; + int n = a->Rows; + int i, iCol=0, iRow=0, j, k; + double fMax, fAbs, fSave, fInf, temp; + int* aiColIndex; + int* aiRowIndex=0; + int* aiPivoted=0; + + + if (a->Rows != a->Cols) return false; + + status = false; + if((aiColIndex = (int*) malloc(n * sizeof(int))) == NULL) + goto GotError; + + if((aiRowIndex = (int*) malloc(n * sizeof(int))) == NULL) + goto GotError; + + if((aiPivoted = (int*) malloc(n * sizeof(int))) == NULL) + goto GotError; + + ZeroMemory(aiPivoted, n * sizeof(int)); + + + for(i = 0; i < n; i++) { + + /* search matrix (excluding pivoted rows) for maximum absolute entry */ + + fMax = 0.0; + for (j = 0; j < n; j++) + if (aiPivoted[j] != 1) + for (k = 0; k < n; k++) + { + fAbs = fabs(a->Values[j][k]); + if (fAbs >= fMax) { + + fMax = fAbs; + iRow = j; + iCol = k; + } + else + if (aiPivoted[k] > 1) { + + status = false; + goto GotError; + } + } + + aiPivoted[iCol]++; + + /* swap rows so that A[iCol][iCol] contains the pivot entry */ + + if (iRow != iCol) { + + for(j = 0; j < n; j++) + DO_SWAP(a->Values[iRow][j], a->Values[iCol][j], temp) + + DO_SWAP(b->Values[iRow][0], b->Values[iCol][0], temp) + } + + /* keep track of the permutations of the rows */ + + aiRowIndex[i] = iRow; + aiColIndex[i] = iCol; + + if (a->Values[iCol][iCol] == 0.0) + { + status = false; + goto GotError; + } + + /* scale the row so that the pivot entry is 1 */ + + fInf = 1.0 / a->Values[iCol][iCol]; + a->Values[iCol][iCol] = 1.0; + + for(j = 0; j < n; j++) + a->Values[iCol][j] *= fInf; + + b->Values[iCol][0] *= fInf; + + /* zero out the pivot column locations in the other rows */ + + for(j = 0; j < n; j++) + if (j != iCol) { + + fSave = a->Values[j][iCol]; + a->Values[j][iCol] = 0.0; + + for(k = 0; k < n; k++) + a->Values[j][k] -= a->Values[iCol][k] * fSave; + + b->Values[j][0] -= b->Values[iCol][0] * fSave; + } + } + + /* reorder rows so that A[][] stores the inverse of the original matrix */ + + for(i = n - 1; i >= 0; i--) { + + if(aiRowIndex[i] != aiColIndex[i]) + for(j = 0; j < n; j++) + DO_SWAP(a->Values[j][aiRowIndex[i]], a->Values[j][aiColIndex[i]], temp) + } + + status = true; + +GotError: + if(aiColIndex) free(aiColIndex); + if(aiRowIndex) free(aiRowIndex); + if(aiPivoted) free(aiPivoted); + return status; + +} + +#undef DO_SWAP + + +LPMATN MATNmult(LPMATN a1, LPMATN a2) +{ + int i, j, k; + LPMATN b; + + if (a1->Cols != a2->Rows) + return NULL; + + b = MATNalloc (a1->Rows, a2->Cols); + if (b == NULL) + return NULL; + + for (i = 0; i < b->Rows; i++) { + + for (j = 0; j < b->Cols; j++) { + + b->Values[i][j] = 0.0; + + for (k = 0; k < a1->Cols; k++) { + + b->Values[i][j] += a1->Values[i][k] * a2->Values[k][j]; + } + } + } + + return b; +} + + +double MATNcross(LPMATN a) +{ + int i; + double prod = 0.0; + + for (i = 0; i < a->Rows; i++) { + + prod += a->Values[i][0]*a->Values[i][0]; + } + return prod; +} + + +void MATNscalar(LPMATN a, double scl, LPMATN b) +{ + int i, j; + + if (a->Rows != b->Rows || a->Cols != b->Cols) + return; + + for (i = 0; i < a->Rows; i++) { + + for (j = 0; j < a->Cols; j++) + b->Values[i][j] = a->Values[i][j] * scl; + } +} + + +LPMATN MATNtranspose(LPMATN a) +{ + LPMATN b = MATNalloc(a->Cols, a->Rows); + if (b != NULL) { + + int i, j; + + for (i = 0; i < a->Rows; i++) + { + for (j = 0; j < a->Cols; j++) + b->Values[j][i] = a->Values [i][j]; + } + } + return b; +} + + + +/* Used for debug purposes */ +#ifdef DEBUG +void MATNprintf(char* name, LPMATN mat) +{ + int i, j; + + printf ("%s:\n", name); + for (i= 0; i < mat->Rows; i++) { + + printf ("%3d", i); + for (j = 0; j < mat->Cols; j++) + printf (" %.5f", mat->Values[i][j]); + printf ("\n"); + } +} +#endif + + diff --git a/src/libs/lprof/cmsmkmsh.cpp b/src/libs/lprof/cmsmkmsh.cpp new file mode 100644 index 00000000..c0e3d4c3 --- /dev/null +++ b/src/libs/lprof/cmsmkmsh.cpp @@ -0,0 +1,346 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.08a */ + + +#include "lcmsprf.h" + + +BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet, + const char* MeasurementSheet, + int Medium, + LPGAMMATABLE TransferCurves[3], + LPcmsCIEXYZ WhitePoint, + LPcmsCIEXYZ BlackPoint, + LPcmsCIExyYTRIPLE Primaries); + + + +/* ------------------------------------------------------------- Implementation */ + + +static +void Div100(LPcmsCIEXYZ xyz) +{ + xyz -> X /= 100; xyz -> Y /= 100; xyz -> Z /= 100; +} + + + +/* Compute endpoints */ + +static +BOOL ComputeWhiteAndBlackPoints(LPMEASUREMENT Linearized, + LPGAMMATABLE TransferCurves[3], + LPcmsCIEXYZ Black, LPcmsCIEXYZ White) +{ + + double Zeroes[3], Ones[3], lmin[3], lmax[3]; + + SETOFPATCHES Neutrals = cmsxPCollBuildSet(Linearized, false); + + cmsxPCollPatchesNearNeutral(Linearized, Linearized->Allowed, + 15, Neutrals); + + Zeroes[0] = Zeroes[1] = Zeroes[2] = 0.0; + Ones[0] = Ones[1] = Ones[2] = 255.0; + + + cmsxApplyLinearizationTable(Zeroes, TransferCurves, lmin); + cmsxApplyLinearizationTable(Ones, TransferCurves, lmax); + + + /* Global regression to find White & Black points */ + if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, + 4, + true, + 12, + lmin[0], lmin[1], lmin[2], + Black)) return false; + + if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, + 4, + true, + 12, + lmax[0], lmax[1], lmax[2], + White)) return false; + + _cmsxClampXYZ100(White); + _cmsxClampXYZ100(Black); + + return true; + +} + + +/* Study convergence of primary axis */ + +static +BOOL ComputePrimary(LPMEASUREMENT Linearized, + LPGAMMATABLE TransferCurves[3], + int n, + LPcmsCIExyY Primary) +{ + + double Ones[3], lmax[3]; + cmsCIEXYZ PrimXYZ; + SETOFPATCHES SetPrimary; + int nR; + + + /* At first, try to see if primaries are already in measurement */ + + SetPrimary = cmsxPCollBuildSet(Linearized, false); + nR = cmsxPCollPatchesNearPrimary(Linearized, Linearized->Allowed, + n, 32, SetPrimary); + + Ones[0] = Ones[1] = Ones[2] = 0; + Ones[n] = 255.0; + + cmsxApplyLinearizationTable(Ones, TransferCurves, lmax); + + /* Do incremental regression to find primaries */ + if (!cmsxRegressionInterpolatorRGB(Linearized, PT_XYZ, + 4, + false, + 12, + lmax[0], lmax[1], lmax[2], + &PrimXYZ)) return false; + + _cmsxClampXYZ100(&PrimXYZ); + cmsXYZ2xyY(Primary, &PrimXYZ); + return true; + + +} + + + +/* Does compute a matrix-shaper based on patches. */ + +static +double Clip(double d) +{ + return d > 0 ? d: 0; +} + + +BOOL cmsxComputeMatrixShaper(const char* ReferenceSheet, + const char* MeasurementSheet, + int Medium, + LPGAMMATABLE TransferCurves[3], + LPcmsCIEXYZ WhitePoint, + LPcmsCIEXYZ BlackPoint, + LPcmsCIExyYTRIPLE Primaries) +{ + + MEASUREMENT Linearized; + cmsCIEXYZ Black, White; + cmsCIExyYTRIPLE PrimarySet; + LPPATCH PatchWhite, PatchBlack; + LPPATCH PatchRed, PatchGreen, PatchBlue; + double Distance; + + /* Load sheets */ + + if (!cmsxPCollBuildMeasurement(&Linearized, + ReferenceSheet, + MeasurementSheet, + PATCH_HAS_XYZ|PATCH_HAS_RGB)) return false; + + + + /* Any patch to deal of? */ + if (cmsxPCollCountSet(&Linearized, Linearized.Allowed) <= 0) return false; + + + /* Try to see if proper primaries, white and black already present */ + PatchWhite = cmsxPCollFindWhite(&Linearized, Linearized.Allowed, &Distance); + if (Distance != 0) + PatchWhite = NULL; + + PatchBlack = cmsxPCollFindBlack(&Linearized, Linearized.Allowed, &Distance); + if (Distance != 0) + PatchBlack = NULL; + + PatchRed = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 0, &Distance); + if (Distance != 0) + PatchRed = NULL; + + PatchGreen = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 1, &Distance); + if (Distance != 0) + PatchGreen = NULL; + + PatchBlue = cmsxPCollFindPrimary(&Linearized, Linearized.Allowed, 2, &Distance); + if (Distance != 0) + PatchBlue= NULL; + + /* If we got primaries, then we can also get prelinearization */ + /* by Levenberg-Marquardt. This applies on monitor profiles */ + + if (PatchWhite && PatchRed && PatchGreen && PatchBlue) { + + /* Build matrix with primaries */ + + MAT3 Mat, MatInv; + LPSAMPLEDCURVE Xr,Yr, Xg, Yg, Xb, Yb; + int i, nRes, cnt; + + VEC3init(&Mat.v[0], PatchRed->XYZ.X, PatchGreen->XYZ.X, PatchBlue->XYZ.X); + VEC3init(&Mat.v[1], PatchRed->XYZ.Y, PatchGreen->XYZ.Y, PatchBlue->XYZ.Y); + VEC3init(&Mat.v[2], PatchRed->XYZ.Z, PatchGreen->XYZ.Z, PatchBlue->XYZ.Z); + + /* Invert matrix */ + MAT3inverse(&Mat, &MatInv); + + nRes = cmsxPCollCountSet(&Linearized, Linearized.Allowed); + + Xr = cmsAllocSampledCurve(nRes); + Yr = cmsAllocSampledCurve(nRes); + Xg = cmsAllocSampledCurve(nRes); + Yg = cmsAllocSampledCurve(nRes); + Xb = cmsAllocSampledCurve(nRes); + Yb = cmsAllocSampledCurve(nRes); + + /* Convert XYZ of all patches to RGB */ + cnt = 0; + for (i=0; i < Linearized.nPatches; i++) { + + if (Linearized.Allowed[i]) { + + VEC3 RGBprime, XYZ; + LPPATCH p; + + p = Linearized.Patches + i; + XYZ.n[0] = p -> XYZ.X; + XYZ.n[1] = p -> XYZ.Y; + XYZ.n[2] = p -> XYZ.Z; + + MAT3eval(&RGBprime, &MatInv, &XYZ); + + Xr ->Values[cnt] = p ->Colorant.RGB[0]; + Yr ->Values[cnt] = Clip(RGBprime.n[0]); + + Xg ->Values[cnt] = p ->Colorant.RGB[1]; + Yg ->Values[cnt] = Clip(RGBprime.n[1]); + + Xb ->Values[cnt] = p ->Colorant.RGB[2]; + Yb ->Values[cnt] = Clip(RGBprime.n[2]); + + cnt++; + + } + } + + TransferCurves[0] = cmsxEstimateGamma(Xr, Yr, 1024); + TransferCurves[1] = cmsxEstimateGamma(Xg, Yg, 1024); + TransferCurves[2] = cmsxEstimateGamma(Xb, Yb, 1024); + + if (WhitePoint) { + + WhitePoint->X = PatchWhite->XYZ.X; + WhitePoint->Y= PatchWhite ->XYZ.Y; + WhitePoint->Z= PatchWhite ->XYZ.Z; + } + + if (BlackPoint && PatchBlack) { + + BlackPoint->X = PatchBlack ->XYZ.X; + BlackPoint->Y = PatchBlack ->XYZ.Y; + BlackPoint->Z = PatchBlack ->XYZ.Z; + } + + if (Primaries) { + + cmsXYZ2xyY(&Primaries->Red, &PatchRed ->XYZ); + cmsXYZ2xyY(&Primaries->Green, &PatchGreen ->XYZ); + cmsXYZ2xyY(&Primaries->Blue, &PatchBlue ->XYZ); + + } + + + cmsFreeSampledCurve(Xr); + cmsFreeSampledCurve(Yr); + cmsFreeSampledCurve(Xg); + cmsFreeSampledCurve(Yg); + cmsFreeSampledCurve(Xb); + cmsFreeSampledCurve(Yb); + + cmsxPCollFreeMeasurements(&Linearized); + + return true; + } + + + + + /* Compute prelinearization */ + cmsxComputeLinearizationTables(&Linearized, PT_XYZ, TransferCurves, 1024, Medium); + + /* Linearize measurements */ + cmsxPCollLinearizePatches(&Linearized, Linearized.Allowed, TransferCurves); + + + /* Endpoints */ + ComputeWhiteAndBlackPoints(&Linearized, TransferCurves, &Black, &White); + + /* Primaries */ + ComputePrimary(&Linearized, TransferCurves, 0, &PrimarySet.Red); + ComputePrimary(&Linearized, TransferCurves, 1, &PrimarySet.Green); + ComputePrimary(&Linearized, TransferCurves, 2, &PrimarySet.Blue); + + + if (BlackPoint) { + *BlackPoint = Black; + Div100(BlackPoint); + } + + if (WhitePoint) { + *WhitePoint = White; + Div100(WhitePoint); + } + + + if (Primaries) { + *Primaries = PrimarySet; + } + + cmsxPCollFreeMeasurements(&Linearized); + + return true; +} + + + diff --git a/src/libs/lprof/cmsmntr.cpp b/src/libs/lprof/cmsmntr.cpp new file mode 100644 index 00000000..ed14ed50 --- /dev/null +++ b/src/libs/lprof/cmsmntr.cpp @@ -0,0 +1,371 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + + +#include "lcmsprf.h" + + +static +void ClampRGB(LPVEC3 RGB) +{ + int i; + + for (i=0; i < 3; i++) { + + if (RGB->n[i] > 1.0) + RGB->n[i] = 1.0; + if (RGB->n[i] < 0) + RGB->n[i] = 0; + } +} + + +static +int RegressionSamplerA2B(WORD In[], WORD Out[], LPVOID Cargo) +{ + cmsCIEXYZ xyz; + cmsCIELab Lab; + VEC3 RGB, RGBlinear, vxyz; + LPMONITORPROFILERDATA sys = (LPMONITORPROFILERDATA) Cargo; + + + RGB.n[0] = _cmsxSaturate65535To255(In[0]); + RGB.n[1] = _cmsxSaturate65535To255(In[1]); + RGB.n[2] = _cmsxSaturate65535To255(In[2]); + + cmsxApplyLinearizationTable(RGB.n, sys->PreLab, RGBlinear.n); + cmsxApplyLinearizationTable(RGBlinear.n, sys->Prelinearization, RGBlinear.n); + + RGBlinear.n[0] /= 255.; + RGBlinear.n[1] /= 255.; + RGBlinear.n[2] /= 255.; + + MAT3eval(&vxyz, &sys->PrimariesMatrix, &RGBlinear); + + xyz.X = vxyz.n[0]; + xyz.Y = vxyz.n[1]; + xyz.Z = vxyz.n[2]; + + cmsxChromaticAdaptationAndNormalization(&sys ->hdr, &xyz, false); + + + /* To PCS encoding */ + + cmsXYZ2Lab(NULL, &Lab, &xyz); + cmsFloat2LabEncoded(Out, &Lab); + + + return true; /* And done witch success */ +} + + + + +static +int RegressionSamplerB2A(WORD In[], WORD Out[], LPVOID Cargo) +{ + cmsCIELab Lab; + cmsCIEXYZ xyz; + VEC3 vxyz, RGB; + /* cmsJCh JCh; */ + WORD Lin[3], Llab[3]; + LPMONITORPROFILERDATA sys = (LPMONITORPROFILERDATA) Cargo; + double L; + + + /* Pass L back to 0..0xff00 domain */ + + L = (double) (In[0] * 65280.0) / 65535.0; + In[0] = (WORD) floor(L + .5); + + + /* To float values */ + cmsLabEncoded2Float(&Lab, In); + cmsLab2XYZ(NULL, &xyz, &Lab); + + + cmsxChromaticAdaptationAndNormalization(&sys ->hdr, &xyz, true); + vxyz.n[0] = xyz.X; + vxyz.n[1] = xyz.Y; + vxyz.n[2] = xyz.Z; + + MAT3eval(&RGB, &sys-> PrimariesMatrixRev, &vxyz); + + /* Clamp RGB */ + ClampRGB(&RGB); + + /* Encode output */ + Lin[0] = (WORD) ((double) RGB.n[0] * 65535. + .5); + Lin[1] = (WORD) ((double) RGB.n[1] * 65535. + .5); + Lin[2] = (WORD) ((double) RGB.n[2] * 65535. + .5); + + cmsxApplyLinearizationGamma(Lin, sys ->ReverseTables, Llab); + cmsxApplyLinearizationGamma(Llab, sys ->PreLabRev, Out); + + + return true; /* And done witch success */ +} + + +BOOL cmsxMonitorProfilerInit(LPMONITORPROFILERDATA sys) +{ + + + if (sys == NULL) return false; + ZeroMemory(sys, sizeof(MONITORPROFILERDATA)); + + sys->hdr.DeviceClass = icSigDisplayClass; + sys->hdr.ColorSpace = icSigRgbData; + sys->hdr.PCSType = PT_Lab; + sys->hdr.Medium = MEDIUM_TRANSMISSIVE; + + + /* Default values for generation */ + + sys -> hdr.lUseCIECAM97s = false; + sys -> hdr.CLUTPoints = 16; + + /* Default viewing conditions */ + + sys -> hdr.device.Yb = 20; + sys -> hdr.device.La = 20; + sys -> hdr.device.surround = AVG_SURROUND; + sys -> hdr.device.D_value = 1; /* Complete adaptation */ + + + /* Viewing conditions of PCS */ + cmsxInitPCSViewingConditions(&sys ->hdr); + + strcpy(sys -> hdr.Description, "unknown monitor"); + strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set"); + strcpy(sys -> hdr.Copyright, "No copyright, use freely"); + strcpy(sys -> hdr.Model, "(unknown)"); + + sys -> hdr.ProfileVerbosityLevel = 0; + + return true; +} + + +static +void CreatePrimaryMatrices(LPMONITORPROFILERDATA sys) +{ + cmsCIExyY White; + MAT3 tmp; + + + cmsXYZ2xyY(&White, &sys->hdr.WhitePoint); + cmsBuildRGB2XYZtransferMatrix(&sys -> PrimariesMatrix, &White, &sys->hdr.Primaries); + + CopyMemory(&tmp, &sys -> PrimariesMatrix, sizeof(MAT3)); + MAT3inverse(&tmp, &sys->PrimariesMatrixRev); + +} + + +static +BOOL CreateLUTS(LPMONITORPROFILERDATA sys, LPLUT* A2B, LPLUT* B2A) +{ + LPLUT AToB0 = cmsAllocLUT(); + LPLUT BToA0 = cmsAllocLUT(); + LPGAMMATABLE LabG; + cmsCIExyY xyY; + + + cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3); + cmsAlloc3DGrid(BToA0, sys->hdr.CLUTPoints, 3, 3); + + /* cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1); */ + + sys->ReverseTables[0] = cmsReverseGamma(4096, sys ->Prelinearization[0]); + sys->ReverseTables[1] = cmsReverseGamma(4096, sys ->Prelinearization[1]); + sys->ReverseTables[2] = cmsReverseGamma(4096, sys ->Prelinearization[2]); + + /* Prelinearization */ + + LabG = cmsBuildGamma(4096, 3.0); + + sys -> PreLab[0] = cmsJoinGammaEx(LabG, sys ->Prelinearization[0], 4096); + sys -> PreLab[1] = cmsJoinGammaEx(LabG, sys ->Prelinearization[1], 4096); + sys -> PreLab[2] = cmsJoinGammaEx(LabG, sys ->Prelinearization[2], 4096); + + sys -> PreLabRev[0] = cmsJoinGammaEx(sys ->Prelinearization[0], LabG, 4096); + sys -> PreLabRev[1] = cmsJoinGammaEx(sys ->Prelinearization[1], LabG, 4096); + sys -> PreLabRev[2] = cmsJoinGammaEx(sys ->Prelinearization[2], LabG, 4096); + + + cmsFreeGamma(LabG); + + + cmsAllocLinearTable(AToB0, sys->PreLabRev, 1); + cmsAllocLinearTable(BToA0, sys->PreLab, 2); + + + /* Set CIECAM97s parameters */ + + sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.; + sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.; + sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.; + + + /* Normalize White point for CIECAM97s model */ + cmsXYZ2xyY(&xyY, &sys -> hdr.device.whitePoint); + xyY.Y = 100.; + cmsxyY2XYZ(&sys -> hdr.device.whitePoint, &xyY); + + + sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device); + sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS); + + + cmsSample3DGrid(AToB0, RegressionSamplerA2B, sys, 0); + cmsSample3DGrid(BToA0, RegressionSamplerB2A, sys, 0); + + cmsCIECAM97sDone(sys->hdr.hDevice); + cmsCIECAM97sDone(sys->hdr.hPCS); + + cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0); + cmsAddTag(sys->hdr.hProfile, icSigBToA0Tag, BToA0); + + /* This is the 0xff00 trick to map white at lattice point */ + BToA0 ->Matrix.v[0].n[0] = DOUBLE_TO_FIXED((65535.0 / 65280.0)); + + *A2B = AToB0; + *B2A = BToA0; + + cmsFreeGammaTriple(sys->ReverseTables); + cmsFreeGammaTriple(sys->PreLab); + cmsFreeGammaTriple(sys->PreLabRev); + return true; +} + + + +BOOL cmsxMonitorProfilerDo(LPMONITORPROFILERDATA sys) +{ + + cmsCIExyY White; + LPLUT AToB0, BToA0; + + AToB0 = BToA0 = NULL; + + if (!*sys -> hdr.OutputProfileFile) + return false; + + + if (sys->hdr.ReferenceSheet[0] || sys->hdr.MeasurementSheet[0]) { + + if (sys->hdr.printf) { + + sys->hdr.printf("Loading sheets..."); + + if (sys->hdr.ReferenceSheet[0]) + sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet); + if (sys->hdr.MeasurementSheet[0]) + sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet); + } + + + if (!cmsxComputeMatrixShaper(sys -> hdr.ReferenceSheet, + sys -> hdr.MeasurementSheet, + MEDIUM_TRANSMISSIVE, + sys -> Prelinearization, + &sys -> hdr.WhitePoint, + &sys -> hdr.BlackPoint, + &sys -> hdr.Primaries)) return false; + + if (sys->hdr.printf) { + + char Buffer[1024]; + _cmsIdentifyWhitePoint(Buffer, &sys ->hdr.WhitePoint); + sys->hdr.printf("%s", Buffer); + + sys->hdr.printf("Primaries: R:%1.2g, %1.2g G:%1.2g, %1.2g B:%1.2g, %1.2g", + sys->hdr.Primaries.Red.x,sys->hdr.Primaries.Red.y, + sys->hdr.Primaries.Green.x, sys->hdr.Primaries.Green.y, + sys->hdr.Primaries.Blue.x, sys->hdr.Primaries.Blue.y); + } + + } + + + CreatePrimaryMatrices(sys); + + + cmsXYZ2xyY(&White, &sys->hdr.WhitePoint); + + sys->hdr.hProfile = cmsCreateRGBProfile(&White, + &sys-> hdr.Primaries, + sys -> Prelinearization); + + cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass); + + if (sys -> hdr.lUseCIECAM97s) + sys->hdr.PCSType = PT_Lab; + else + sys->hdr.PCSType = PT_XYZ; + + cmsSetPCS(sys->hdr.hProfile, _cmsICCcolorSpace(sys->hdr.PCSType)); + + if (sys -> hdr.lUseCIECAM97s) + CreateLUTS(sys, &AToB0, &BToA0); + + + cmsxEmbedTextualInfo(&sys ->hdr); + + cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint); + cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint); + + + if (sys->hdr.ProfileVerbosityLevel >= 2) { + + cmsxEmbedCharTarget(&sys ->hdr); + } + + + _cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile); + cmsCloseProfile(sys->hdr.hProfile); + sys->hdr.hProfile = NULL; + + + if (AToB0) cmsFreeLUT(AToB0); + if (BToA0) cmsFreeLUT(BToA0); + + if (sys ->Prelinearization[0]) + cmsFreeGammaTriple(sys -> Prelinearization); + + return true; +} diff --git a/src/libs/lprof/cmsoutl.cpp b/src/libs/lprof/cmsoutl.cpp new file mode 100644 index 00000000..248aaa04 --- /dev/null +++ b/src/libs/lprof/cmsoutl.cpp @@ -0,0 +1,284 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ +/* */ +/* Incremental Interpolator */ + +#include "lcmsprf.h" + + +/* Res points to a result in XYZ or Lab */ + +BOOL cdecl cmsxRegressionInterpolatorRGB(LPMEASUREMENT m, + int ColorSpace, + int RegressionTerms, + BOOL lUseLocalPatches, + int MinPatchesToCollect, + double r, double g, double b, + void* Res); + + +/* -------------------------------------------------------------- Implementation */ + +/* #define DEBUG 1 */ + +/* Estimate regression matrix */ +static +void EstimateRegression(LPMEASUREMENT m, double r, double g, double b, + int ColorSpace, + LPMATN* ptfm, + int nterms, + BOOL lIncludeAllPatches, + int MinPatchesToCollect) +{ + int nCollected; + MLRSTATISTICS maxAns; + int ToCollect; + SETOFPATCHES collected = cmsxPCollBuildSet(m, false); + SETOFPATCHES allowed = cmsxPCollBuildSet(m, true); + BOOL rc; + BOOL lPatchesExhausted = false; + + + CopyMemory(allowed, m -> Allowed, m->nPatches*sizeof(BOOL)); + + *ptfm = NULL; + + ToCollect = max(MinPatchesToCollect, (nterms + 1)); + + do { + + if (lIncludeAllPatches) { + + CopyMemory(collected, allowed, m->nPatches*sizeof(BOOL)); + lPatchesExhausted = true; + ToCollect = nCollected = m->nPatches; + } + else + { + + nCollected = cmsxPCollPatchesNearRGB(m, m -> Allowed, + r, g, b, + ToCollect, collected); + + if (nCollected < ToCollect) { /* No more patches available */ + lPatchesExhausted = true; + } + else { + ToCollect = nCollected + 1; /* Start from here in next iteration */ + } + } + + /* We are going always 3 -> 3 for now.... */ + rc = cmsxRegressionCreateMatrix(m, collected, nterms, ColorSpace, ptfm, &maxAns); + + + /* Does fit? */ + if ((rc == false) || maxAns.R2adj < 0.95 || maxAns.R2adj > 1.0) { + + maxAns.R2adj = -100; /* No, repeat */ + } + + + } while (!lPatchesExhausted && maxAns.R2adj < 0.95); + +#ifdef DEBUG + printf("R2adj: %g, F: %g\n", maxAns.R2adj, maxAns.F); +#endif + + free(collected); + free(allowed); +} + + + +BOOL cmsxRegressionInterpolatorRGB(LPMEASUREMENT m, + int ColorSpace, + int RegressionTerms, + BOOL lUseLocalPatches, + int MinPatchesToCollect, + double r, double g, double b, + void* Res) +{ + LPMATN tfm = NULL; + + + EstimateRegression(m, r, g, b, ColorSpace, &tfm, RegressionTerms, + !lUseLocalPatches, MinPatchesToCollect); + + if (tfm == NULL) return false; + + switch (ColorSpace) { + + case PT_Lab: + + if (!cmsxRegressionRGB2Lab(r, g, b, tfm, (LPcmsCIELab) Res)) return false; + break; + + case PT_XYZ: + if (!cmsxRegressionRGB2XYZ(r, g, b, tfm, (LPcmsCIEXYZ) Res)) return false; + break; + + default: + return false; + } + + MATNfree(tfm); + + +#ifdef DEBUG + printf("INTERPOLATED RGB %g,%g,%g Lab %g, %g, %g \n", r , g, b, + Lab->L, Lab->a, Lab->b); + +#endif + return true; +} + + +/* Check the results of a given regression matrix */ + +static +void CheckOneRegressionMatrix(LPPROFILERCOMMONDATA hdr, LPMATN Matrix, + double* Mean, double* Std, double* Max) +{ + + cmsCIELab Lab; + cmsCIEXYZ XYZ; + double Hit, sum, sum2, n, dE; + int i; + cmsCIEXYZ D50; + + + D50.X = cmsD50_XYZ() -> X* 100.; + D50.Y = cmsD50_XYZ() -> Y* 100.; + D50.Z = cmsD50_XYZ() -> Z* 100.; + + Hit = sum = sum2 = n = 0; + for (i=0; i < hdr -> m.nPatches; i++) { + + if (hdr -> m.Allowed[i]) { + + LPPATCH p = hdr -> m.Patches + i; + + if (hdr -> PCSType == PT_Lab) { + + WORD ProfileLabEncoded[3]; + + cmsxRegressionRGB2Lab(p -> Colorant.RGB[0], + p -> Colorant.RGB[1], + p -> Colorant.RGB[2], + Matrix, &Lab); + + cmsFloat2LabEncoded(ProfileLabEncoded, &Lab); + cmsLabEncoded2Float(&Lab, ProfileLabEncoded); + + dE = cmsDeltaE(&Lab, &p ->Lab); + } + else { + cmsCIELab Lab2; + + cmsxRegressionRGB2XYZ(p -> Colorant.RGB[0], + p -> Colorant.RGB[1], + p -> Colorant.RGB[2], + Matrix, &XYZ); + _cmsxClampXYZ100(&XYZ); + + cmsXYZ2Lab(&D50, &Lab, &XYZ); + cmsXYZ2Lab(&D50, &Lab2, &p ->XYZ); + + dE = cmsDeltaE(&Lab, &Lab2); + } + + + if (dE > Hit) + Hit = dE; + + sum += dE; + sum2 += dE * dE; + n = n + 1; + + } + } + + *Mean = sum / n; + *Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); + *Max = Hit; + +} + + +/* Trial-and-error in order to get best number of terms. */ + +int cmsxFindOptimumNumOfTerms(LPPROFILERCOMMONDATA hdr, int nMaxTerms, BOOL* lAllOk) +{ + int i, BestTerms; + BOOL rc; + LPMATN Matrix = NULL; + MLRSTATISTICS Stat; + double dEmean, dEStd, dEHit, Best; + BOOL lOneFound; + + + BestTerms = 4; + Best = 1000.; + lOneFound = false; + + for (i=4; i <= nMaxTerms; i++) { /* 55 */ + + rc = cmsxRegressionCreateMatrix(&hdr -> m, hdr -> m.Allowed, + i, hdr -> PCSType, &Matrix, &Stat); + + if (rc && Stat.R2adj < 1 && Stat.R2adj > 0.6) { + + CheckOneRegressionMatrix(hdr, Matrix, &dEmean, &dEStd, &dEHit); + + if (dEStd < Best && dEHit < 50.) { + + Best = dEStd; + BestTerms = i; + lOneFound = true; + } + + } + MATNfree(Matrix); + Matrix = NULL; + } + + *lAllOk = lOneFound; + + return BestTerms; +} + + diff --git a/src/libs/lprof/cmspcoll.cpp b/src/libs/lprof/cmspcoll.cpp new file mode 100644 index 00000000..0b03fd5a --- /dev/null +++ b/src/libs/lprof/cmspcoll.cpp @@ -0,0 +1,1045 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.08a */ + + +#include "lcmsprf.h" + + +/* ----------------------------------------------------------------- Patch collections */ + +BOOL cdecl cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet); + +BOOL cdecl cmsxPCollBuildMeasurement(LPMEASUREMENT m, + const char *ReferenceSheet, + const char *MeasurementSheet, + DWORD dwNeededSamplesType); + +LPPATCH cdecl cmsxPCollGetPatch(LPMEASUREMENT m, int n); + +LPPATCH cdecl cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* Name, int* lpPos); +LPPATCH cdecl cmsxPCollGetPatchByPos(LPMEASUREMENT m, int row, int col); +LPPATCH cdecl cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name, + double r, double g, double b, + LPcmsCIEXYZ XYZ, LPcmsCIELab Lab); + +/* Sets of patches */ + +SETOFPATCHES cdecl cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault); +int cdecl cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set); +BOOL cdecl cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags); + + +/* Collect "need" patches of the specific kind, return the number of collected (that */ +/* could be less if set of patches is exhausted) */ + + +void cdecl cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids, + double r, double g, double b, int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids, + int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, + int nChannel, int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids, + double Lmin, double LMax, double a, double b, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids, + LPLUT Gamut, SETOFPATCHES Result); + +LPPATCH cdecl cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance); +LPPATCH cdecl cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance); +LPPATCH cdecl cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* Distance); + + +/* ------------------------------------------------------------- Implementation */ + +#define IS(x) EqualsTo(c, x) + +/* A wrapper on stricmp() */ + +static +BOOL EqualsTo(const char* a, const char *b) +{ + return (stricmp(a, b) == 0); +} + + +/* Does return a bitwise mask holding the measurements contained in a Sheet */ + +static +DWORD MaskOfDataSet(LCMSHANDLE hSheet) +{ + char** Names; + int i, n; + DWORD dwMask = 0; + + n = cmsxIT8EnumDataFormat(hSheet, &Names); + + for (i=0; i < n; i++) { + + char *c = Names[i]; + + if (IS("RGB_R") || IS("RGB_G") || IS("RGB_B")) + dwMask |= PATCH_HAS_RGB; + else + if (IS("XYZ_X") || IS("XYZ_Y") || IS("XYZ_Z")) + dwMask |= PATCH_HAS_XYZ; + else + if (IS("LAB_L") || IS("LAB_A") ||IS("LAB_B")) + dwMask |= PATCH_HAS_Lab; + else + if (IS("STDEV_DE")) + dwMask |= PATCH_HAS_STD_DE; + } + + return dwMask; +} + + +/* Addition of a patch programatically */ + +LPPATCH cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name, + double r, double g, double b, + LPcmsCIEXYZ XYZ, LPcmsCIELab Lab) +{ + LPPATCH p; + + p = m->Patches + m->nPatches++; + + strcpy(p -> Name, Name); + + p -> Colorant.RGB[0] = r; + p -> Colorant.RGB[1] = g; + p -> Colorant.RGB[2] = b; + p -> dwFlags = PATCH_HAS_RGB; + + if (XYZ) { + + p -> XYZ = *XYZ; + p -> dwFlags |= PATCH_HAS_XYZ; + } + + if (Lab) { + p -> Lab = *Lab; + p -> dwFlags |= PATCH_HAS_Lab; + } + + + return p; +} + +/* Some vendors does store colorant data in a non-standard way, */ +/* i.e, from 0.0..1.0 or from 0.0..100.0 This routine tries to */ +/* detect such situations */ + +static +void NormalizeColorant(LPMEASUREMENT m) +{ + int i, j; + double MaxColorant=0; + double Normalize; + + + for (i=0; i < m -> nPatches; i++) { + + + LPPATCH p = m -> Patches + i; + + for (j=0; j < MAXCHANNELS; j++) { + if (p ->Colorant.Hexa[j] > MaxColorant) + MaxColorant = p ->Colorant.Hexa[j]; + } + } + + /* Ok, some heuristics */ + + if (MaxColorant < 2) + Normalize = 255.0; /* goes 0..1 */ + else + if (MaxColorant < 102) + Normalize = 2.55; /* goes 0..100 */ + else + if (MaxColorant > 300) + Normalize = (255.0 / 65535.0); /* Goes 0..65535.0 */ + else + return; /* Is ok */ + + + /* Rearrange patches */ + for (i=0; i < m -> nPatches; i++) { + + + LPPATCH p = m -> Patches + i; + for (j=0; j < MAXCHANNELS; j++) + p ->Colorant.Hexa[j] *= Normalize; + } + +} + + +/* Load a collection from a Sheet */ + +BOOL cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet) +{ + int i; + DWORD dwMask; + + + if (m -> nPatches == 0) { + + m -> nPatches = (int) cmsxIT8GetPropertyDbl(hSheet, "NUMBER_OF_SETS"); + m -> Patches = (PATCH*)calloc(m -> nPatches, sizeof(PATCH)); // C->C++ : cast + + if (m -> Patches == NULL) { + cmsxIT8Free(hSheet); + return false; + } + + for (i=0; i < m -> nPatches; i++) { + + LPPATCH p = m -> Patches + i; + p -> dwFlags = 0; + cmsxIT8GetPatchName(hSheet, i, p ->Name); + + } + + } + + + /* Build mask according to data format */ + + dwMask = MaskOfDataSet(hSheet); + + + /* Read items. Set flags accordly. */ + for (i = 0; i < m->nPatches; i++) { + + LPPATCH Patch = m -> Patches + i; + + /* Fill in data according to mask */ + + if (dwMask & PATCH_HAS_Lab) { + + if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_L", &Patch -> Lab.L) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_A", &Patch -> Lab.a) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "LAB_B", &Patch -> Lab.b)) + + Patch -> dwFlags |= PATCH_HAS_Lab; + } + + if (dwMask & PATCH_HAS_XYZ) { + + if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_X", &Patch -> XYZ.X) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Y", &Patch -> XYZ.Y) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "XYZ_Z", &Patch -> XYZ.Z)) + + Patch -> dwFlags |= PATCH_HAS_XYZ; + + } + + if (dwMask & PATCH_HAS_RGB) { + + if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_R", &Patch -> Colorant.RGB[0]) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_G", &Patch -> Colorant.RGB[1]) && + cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "RGB_B", &Patch -> Colorant.RGB[2])) + + Patch -> dwFlags |= PATCH_HAS_RGB; + } + + if (dwMask & PATCH_HAS_STD_DE) { + + if (cmsxIT8GetDataSetDbl(hSheet, Patch->Name, "STDEV_DE", &Patch -> dEStd)) + + Patch -> dwFlags |= PATCH_HAS_STD_DE; + + } + + } + + NormalizeColorant(m); + return true; +} + + +/* Does save parameters to a empty sheet */ + +BOOL cmsxPCollSaveToSheet(LPMEASUREMENT m, LCMSHANDLE it8) +{ + int nNumberOfSets = cmsxPCollCountSet(m, m->Allowed); + int nNumberOfFields = 0; + DWORD dwMask = 0; + int i; + + /* Find mask of fields */ + for (i=0; i < m ->nPatches; i++) { + if (m ->Allowed[i]) { + + LPPATCH p = m ->Patches + i; + dwMask |= p ->dwFlags; + } + } + + nNumberOfFields = 1; /* SampleID */ + + if (dwMask & PATCH_HAS_RGB) + nNumberOfFields += 3; + + if (dwMask & PATCH_HAS_XYZ) + nNumberOfFields += 3; + + if (dwMask & PATCH_HAS_Lab) + nNumberOfFields += 3; + + + cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", nNumberOfSets); + cmsxIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", nNumberOfFields); + + nNumberOfFields = 0; + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "SAMPLE_ID"); + + if (dwMask & PATCH_HAS_RGB) { + + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_R"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_G"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "RGB_B"); + } + + if (dwMask & PATCH_HAS_XYZ) { + + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_X"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Y"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "XYZ_Z"); + + } + + + if (dwMask & PATCH_HAS_XYZ) { + + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_L"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_A"); + cmsxIT8SetDataFormat(it8, nNumberOfFields++, "LAB_B"); + + } + + for (i=0; i < m ->nPatches; i++) { + if (m ->Allowed[i]) { + + LPPATCH Patch = m ->Patches + i; + + cmsxIT8SetDataSet(it8, Patch->Name, "SAMPLE_ID", Patch->Name); + + if (dwMask & PATCH_HAS_RGB) { + cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_R", Patch ->Colorant.RGB[0]); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_G", Patch ->Colorant.RGB[1]); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "RGB_B", Patch ->Colorant.RGB[2]); + } + + if (dwMask & PATCH_HAS_XYZ) { + cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_X", Patch ->XYZ.X); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Y", Patch ->XYZ.Y); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "XYZ_Z", Patch ->XYZ.Z); + } + + if (dwMask & PATCH_HAS_Lab) { + cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_L", Patch ->Lab.L); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_A", Patch ->Lab.a); + cmsxIT8SetDataSetDbl(it8, Patch->Name, "LAB_B", Patch ->Lab.b); + + } + } + } + + return true; +} + +static +void FixLabOnly(LPMEASUREMENT m) +{ + int i; + + for (i=0; i < m ->nPatches; i++) { + + LPPATCH p = m ->Patches + i; + if ((p ->dwFlags & PATCH_HAS_Lab) && + !(p ->dwFlags & PATCH_HAS_XYZ)) + { + cmsLab2XYZ(cmsD50_XYZ(), &p->XYZ, &p ->Lab); + + p ->XYZ.X *= 100.; + p ->XYZ.Y *= 100.; + p ->XYZ.Z *= 100.; + + p ->dwFlags |= PATCH_HAS_XYZ; + } + + } + +} + + +/* Higher level function. Does merge reference and measurement sheet into */ +/* a MEASUREMENT struct. Data to keep is described in dwNeededSamplesType */ +/* mask as follows: */ +/* */ +/* PATCH_HAS_Lab 0x00000001 */ +/* PATCH_HAS_XYZ 0x00000002 */ +/* PATCH_HAS_RGB 0x00000004 */ +/* PATCH_HAS_CMY 0x00000008 */ +/* PATCH_HAS_CMYK 0x00000010 */ +/* PATCH_HAS_HEXACRM 0x00000020 */ +/* PATCH_HAS_STD_Lab 0x00010000 */ +/* PATCH_HAS_STD_XYZ 0x00020000 */ +/* PATCH_HAS_STD_RGB 0x00040000 */ +/* PATCH_HAS_STD_CMY 0x00080000 */ +/* PATCH_HAS_STD_CMYK 0x00100000 */ +/* PATCH_HAS_STD_HEXACRM 0x00100000 */ +/* PATCH_HAS_MEAN_DE 0x01000000 */ +/* PATCH_HAS_STD_DE 0x02000000 */ +/* PATCH_HAS_CHISQ 0x04000000 */ +/* */ +/* See lprof.h for further info */ + + +BOOL cmsxPCollBuildMeasurement(LPMEASUREMENT m, + const char *ReferenceSheet, + const char *MeasurementSheet, + DWORD dwNeededSamplesType) +{ + LCMSHANDLE hSheet; + BOOL rc = true; + + ZeroMemory(m, sizeof(MEASUREMENT)); + + + if (ReferenceSheet != NULL && *ReferenceSheet) { + + hSheet = cmsxIT8LoadFromFile(ReferenceSheet); + if (hSheet == NULL) return false; + + rc = cmsxPCollLoadFromSheet(m, hSheet); + cmsxIT8Free(hSheet); + } + + if (!rc) return false; + + if (MeasurementSheet != NULL && *MeasurementSheet) { + + hSheet = cmsxIT8LoadFromFile(MeasurementSheet); + if (hSheet == NULL) return false; + + rc = cmsxPCollLoadFromSheet(m, hSheet); + cmsxIT8Free(hSheet); + } + + if (!rc) return false; + + + /* Fix up -- If only Lab is present, then compute */ + /* XYZ based on D50 */ + + FixLabOnly(m); + + cmsxPCollValidatePatches(m, dwNeededSamplesType); + return true; +} + + + +void cmsxPCollFreeMeasurements(LPMEASUREMENT m) +{ + if (m->Patches) + free(m->Patches); + + m->Patches = NULL; + m->nPatches = 0; + + if (m -> Allowed) + free(m -> Allowed); + +} + +/* Retrieval functions */ + +LPPATCH cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* name, int* lpPos) +{ + int i; + for (i=0; i < m->nPatches; i++) + { + if (m -> Allowed) + if (!m -> Allowed[i]) + continue; + + if (EqualsTo(m->Patches[i].Name, name)) { + if (lpPos) *lpPos = i; + return m->Patches + i; + } + } + + return NULL; +} + + + + +/* -------------------------------------------------------------------- Sets */ + + +SETOFPATCHES cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault) +{ + SETOFPATCHES Full = (SETOFPATCHES) malloc(m -> nPatches * sizeof(BOOL)); + int i; + + for (i=0; i < m -> nPatches; i++) + Full[i] = lDefault; + + return Full; +} + +int cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set) +{ + int i, Count = 0; + + for (i = 0; i < m -> nPatches; i++) { + + if (Set[i]) + Count++; + } + + return Count; +} + + +/* Validate patches */ + +BOOL cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags) +{ + int i, n; + + if (m->Allowed) + free(m->Allowed); + + m -> Allowed = cmsxPCollBuildSet(m, true); + + /* Check for flags */ + for (i=n=0; i < m -> nPatches; i++) { + + LPPATCH p = m -> Patches + i; + m -> Allowed[i] = ((p -> dwFlags & dwFlags) == dwFlags); + + } + + return true; +} + + +/* Several filters */ + + +/* This filter does validate patches placed on 'radius' distance of a */ +/* device-color space. Currently only RGB is supported */ + +static +void PatchesByRGB(LPMEASUREMENT m, SETOFPATCHES Valids, + double R, double G, double B, double radius, SETOFPATCHES Result) +{ + int i; + double ra, rmax = sqrt(radius / 255.); + double dR, dG, dB; + LPPATCH p; + + for (i=0; i < m->nPatches; i++) { + + + if (Valids[i]) { + + p = m->Patches + i; + + dR = fabs(R - p -> Colorant.RGB[0]) / 255.; + dG = fabs(G - p -> Colorant.RGB[1]) / 255.; + dB = fabs(B - p -> Colorant.RGB[2]) / 255.; + + ra = sqrt(dR*dR + dG*dG + dB*dB); + + if (ra <= rmax) + Result[i] = true; + else + Result[i] = false; + + } + } + +} + + +/* This filter does validate patches placed at dEmax radius */ +/* in the device-independent side. */ + +static +void PatchesByLab(LPMEASUREMENT m, SETOFPATCHES Valids, + double L, double a, double b, double dEmax, SETOFPATCHES Result) +{ + int i; + double dE, dEMaxSQR = sqrt(dEmax); + double dL, da, db; + LPPATCH p; + + + for (i=0; i < m->nPatches; i++) { + + + if (Valids[i]) { + + p = m->Patches + i; + + dL = fabs(L - p -> Lab.L); + da = fabs(a - p -> Lab.a); + db = fabs(b - p -> Lab.b); + + dE = sqrt(dL*dL + da*da + db*db); + + if (dE <= dEMaxSQR) + Result[i] = true; + else + Result[i] = false; + } + } +} + + +/* Restrict Lab in a cube of variable sides. Quick and dirty out-of-gamut */ +/* stripper used in estimations. */ + +static +void PatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids, + double Lmin, double Lmax, double da, double db, SETOFPATCHES Result) +{ + int i; + + for (i=0; i < m -> nPatches; i++) { + + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + if ((p->Lab.L >= Lmin && p->Lab.L <= Lmax) && + (fabs(p -> Lab.a) < da) && + (fabs(p -> Lab.b) < db)) + + Result[i] = true; + else + Result[i] = false; + } + } + +} + +/* Restrict to low colorfullness */ + +static +void PatchesOfLowC(LPMEASUREMENT m, SETOFPATCHES Valids, + double Cmax, SETOFPATCHES Result) +{ + int i; + cmsCIELCh LCh; + + for (i=0; i < m -> nPatches; i++) { + + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + cmsLab2LCh(&LCh, &p->Lab); + + + if (LCh.C < Cmax) + Result[i] = true; + else + Result[i] = false; + } + } + +} + + + +/* Primary can be -1 for specifying device gray. Does return patches */ +/* on device-space Colorants. dEMax is the maximum allowed ratio */ + +static +void PatchesPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, + int nColorant, double dEMax, SETOFPATCHES Result) +{ + int i, j; + double n, dE; + + for (i=0; i < m -> nPatches; i++) { + + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + + if (nColorant < 0) /* device-grey? */ + { + /* cross. */ + + double drg = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[1]) / 255.; + double drb = fabs(p -> Colorant.RGB[0] - p -> Colorant.RGB[2]) / 255.; + double dbg = fabs(p -> Colorant.RGB[1] - p -> Colorant.RGB[2]) / 255.; + + dE = (drg*drg + drb*drb + dbg*dbg); + + + } + else { + dE = 0.; + for (j=0; j < 3; j++) { + + if (j != nColorant) { + + n = p -> Colorant.RGB[j] / 255.; + dE += (n * n); + + + } + } + } + + + + if (sqrt(dE) < dEMax) + Result[i] = true; + else + Result[i] = false; + } + } + +} + + +/* The high level extractors ----------------------------------------------------- */ + +int cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids, + double r, double g, double b, + int need, SETOFPATCHES Result) +{ + double radius; + int nCollected; + + /* Collect points inside of a sphere or radius 'radius' by RGB */ + + radius = 1; + do { + PatchesByRGB(m, Valids, r, g, b, radius, Result); + + nCollected = cmsxPCollCountSet(m, Result); + if (nCollected <= need) { + + radius += 1.0; + } + + } while (nCollected <= need && radius < 256.); + + return nCollected; /* Can be less than needed! */ +} + + +int cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids, + int need, SETOFPATCHES Result) +{ + int nGrays; + double Cmax; + + Cmax = 1.; + do { + + + PatchesOfLowC(m, Valids, Cmax, Result); + + nGrays = cmsxPCollCountSet(m, Result); + if (nGrays <= need) { + + Cmax += .2; + } + + } while (nGrays <= need && Cmax < 10.); + + return nGrays; +} + + +int cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids, + double Lmin, double Lmax, double a, double b, + SETOFPATCHES Result) + + +{ + PatchesInLabCube(m, Valids, Lmin, Lmax, a, b, Result); + return cmsxPCollCountSet(m, Result); +} + + + + +int cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, + SETOFPATCHES Valids, + int nChannel, + int need, + SETOFPATCHES Result) +{ + double radius; + int nCollected; + + /* Collect points inside of a sphere or radius 'radius' by RGB */ + + radius = 0.05; + do { + PatchesPrimary(m, Valids, nChannel, radius, Result); + + nCollected = cmsxPCollCountSet(m, Result); + if (nCollected <= need) { + + radius += 0.01; + } + + } while (nCollected <= need && radius < 256.); + + return nCollected; + +} + + +static +void AddOneGray(LPMEASUREMENT m, int n, SETOFPATCHES Grays) +{ + LPPATCH p; + char Buffer[cmsxIT8_GRAYCOLS]; + int pos; + + if (n == 0) strcpy(Buffer, "DMIN"); + else + if (n == cmsxIT8_GRAYCOLS - 1) strcpy(Buffer, "DMAX"); + else + sprintf(Buffer, "GS%d", n); + + p = cmsxPCollGetPatchByName(m, Buffer, &pos); + + if (p) + Grays[pos] = true; +} + + + +void cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result) +{ + + int i; + + for (i=0; i < cmsxIT8_GRAYCOLS; i++) + AddOneGray(m, i, Result); +} + + + +/* Refresh RGB of all patches after prelinearization */ + +void cmsxPCollLinearizePatches(LPMEASUREMENT m, SETOFPATCHES Valids, LPGAMMATABLE Gamma[3]) +{ + int i; + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + cmsxApplyLinearizationTable(p -> Colorant.RGB, Gamma, p -> Colorant.RGB); + } + } + +} + + +int cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids, + LPLUT Gamut, SETOFPATCHES Result) +{ + int i; + int nCollected = 0; + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + WORD EncodedLab[3]; + WORD dE; + + cmsFloat2LabEncoded(EncodedLab, &p->Lab); + cmsEvalLUT(Gamut, EncodedLab, &dE); + Result[i] = (dE < 2) ? true : false; + if (Result[i]) nCollected++; + } + } + + return nCollected; +} + +LPPATCH cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance) +{ + int i; + LPPATCH Candidate = NULL; + double Distance, CandidateDistance = 255; + double dR, dG, dB; + + Candidate = cmsxPCollGetPatchByName(m, "DMIN", NULL); + if (Candidate) { + + if (TheDistance) *TheDistance = 0.0; + return Candidate; + } + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + dR = fabs(255.0 - p -> Colorant.RGB[0]) / 255.0; + dG = fabs(255.0 - p -> Colorant.RGB[1]) / 255.0; + dB = fabs(255.0 - p -> Colorant.RGB[2]) / 255.0; + + Distance = sqrt(dR*dR + dG*dG + dB*dB); + + if (Distance < CandidateDistance) { + Candidate = p; + CandidateDistance = Distance; + } + } + } + + if (TheDistance) + *TheDistance = floor(CandidateDistance * 255.0 + .5); + + return Candidate; +} + +LPPATCH cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* TheDistance) +{ + int i; + LPPATCH Candidate = NULL; + double Distance, CandidateDistance = 255; + double dR, dG, dB; + + + Candidate = cmsxPCollGetPatchByName(m, "DMAX", NULL); + if (Candidate) { + + if (TheDistance) *TheDistance = 0.0; + return Candidate; + } + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + dR = (p -> Colorant.RGB[0]) / 255.0; + dG = (p -> Colorant.RGB[1]) / 255.0; + dB = (p -> Colorant.RGB[2]) / 255.0; + + Distance = sqrt(dR*dR + dG*dG + dB*dB); + + if (Distance < CandidateDistance) { + Candidate = p; + CandidateDistance = Distance; + } + } + } + + if (TheDistance) + *TheDistance = floor(CandidateDistance * 255.0 + .5); + + return Candidate; +} + + +LPPATCH cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* TheDistance) +{ + int i; + LPPATCH Candidate = NULL; + double Distance, CandidateDistance = 255; + double dR, dG, dB; + const struct { + double r, g, b; + + } RGBPrimaries[3] = { + { 255.0, 0, 0}, + { 0, 255.0, 0}, + { 0, 0, 255 }}; + + + for (i=0; i < m -> nPatches; i++) { + + if (Valids[i]) { + + LPPATCH p = m -> Patches + i; + + dR = fabs(RGBPrimaries[Channel].r - p -> Colorant.RGB[0]) / 255.0; + dG = fabs(RGBPrimaries[Channel].g - p -> Colorant.RGB[1]) / 255.0; + dB = fabs(RGBPrimaries[Channel].b - p -> Colorant.RGB[2]) / 255.0; + + Distance = sqrt(dR*dR + dG*dG + dB*dB); + + if (Distance < CandidateDistance) { + Candidate = p; + CandidateDistance = Distance; + } + } + } + + if (TheDistance) + *TheDistance = floor(CandidateDistance * 255.0 + .5); + + return Candidate; +} diff --git a/src/libs/lprof/cmsprf.cpp b/src/libs/lprof/cmsprf.cpp new file mode 100644 index 00000000..527fc8e8 --- /dev/null +++ b/src/libs/lprof/cmsprf.cpp @@ -0,0 +1,439 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library 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 */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "lcmsprf.h" + + +double cdecl _cmsxSaturate65535To255(double d); +double cdecl _cmsxSaturate255To65535(double d); + + +void cdecl _cmsxClampXYZ100(LPcmsCIEXYZ xyz); + + +BOOL cdecl cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr); +BOOL cdecl cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr); +BOOL cdecl cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr); + +/* ----------------------------------------------------------------- Implementation */ + + +/* Convert from 0.0..65535.0 to 0.0..255.0 */ + +double _cmsxSaturate65535To255(double d) +{ + double v; + + v = d / 257.0; + + if (v < 0) return 0; + if (v > 255.0) return 255.0; + + return v; +} + + +double _cmsxSaturate255To65535(double d) +{ + double v; + + v = d * 257.0; + + if (v < 0) return 0; + if (v > 65535.0) return 65535.0; + + return v; +} + + + +/* Cut off absurd values */ + +void _cmsxClampXYZ100(LPcmsCIEXYZ xyz) +{ + + if (xyz->X > 199.996) + xyz->X = 199.996; + + if (xyz->Y > 199.996) + xyz->Y = 199.996; + + if (xyz->Z > 199.996) + xyz->Z = 199.996; + + if (xyz->Y < 0) + xyz->Y = 0; + + if (xyz->X < 0) + xyz->X = 0; + + if (xyz->Z < 0) + xyz->Z = 0; + +} + +static +int xfilelength(int fd) +{ +#ifdef _MSC_VER + return _filelength(fd); +#else + struct stat sb; + if (fstat(fd, &sb) < 0) + return(-1); + return(sb.st_size); +#endif + + +} + + +BOOL cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr) +{ + LCMSHANDLE it8 = cmsxIT8Alloc(); + LPBYTE mem; + size_t size, readed; + FILE* f; + BOOL lFreeOnExit = false; + + + if (!hdr->m.Patches) { + + if (!hdr ->ReferenceSheet[0] && !hdr->MeasurementSheet[0]) return false; + + if (cmsxPCollBuildMeasurement(&hdr ->m, + hdr->ReferenceSheet, + hdr->MeasurementSheet, + PATCH_HAS_RGB|PATCH_HAS_XYZ) == false) return false; + lFreeOnExit = true; + + } + + cmsxIT8SetSheetType(it8,"LCMSEMBED"); + cmsxIT8SetProperty(it8, "ORIGINATOR", (const char *) "Little cms"); + cmsxIT8SetProperty(it8, "DESCRIPTOR", (const char *) hdr -> Description); + cmsxIT8SetProperty(it8, "MANUFACTURER", (const char *) hdr ->Manufacturer); + + cmsxPCollSaveToSheet(&hdr->m, it8); + cmsxIT8SaveToFile(it8, "TMP00.IT8"); + cmsxIT8Free(it8); + + f = fopen("TMP00.IT8", "rb"); + size = xfilelength(fileno(f)); + mem = (unsigned char*) malloc(size + 1); // C->C++ : fixed cast + readed = fread(mem, 1, size, f); + fclose(f); + + mem[readed] = 0; + unlink("TMP00.IT8"); + + cmsAddTag(hdr->hProfile, icSigCharTargetTag, mem); + free(mem); + + if (lFreeOnExit) { + + cmsxPCollFreeMeasurements(&hdr->m); + } + + return true; +} + + +static +BOOL ComputeColorantMatrix(LPcmsCIEXYZTRIPLE Colorants, + LPcmsCIExyY WhitePoint, + LPcmsCIExyYTRIPLE Primaries) +{ + MAT3 MColorants; + + if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, WhitePoint, Primaries)) + { + return false; + } + + + cmsAdaptMatrixToD50(&MColorants, WhitePoint); + + Colorants->Red.X = MColorants.v[0].n[0]; + Colorants->Red.Y = MColorants.v[1].n[0]; + Colorants->Red.Z = MColorants.v[2].n[0]; + + Colorants->Green.X = MColorants.v[0].n[1]; + Colorants->Green.Y = MColorants.v[1].n[1]; + Colorants->Green.Z = MColorants.v[2].n[1]; + + Colorants->Blue.X = MColorants.v[0].n[2]; + Colorants->Blue.Y = MColorants.v[1].n[2]; + Colorants->Blue.Z = MColorants.v[2].n[2]; + + return true; + +} + + +BOOL cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr) +{ + cmsCIEXYZTRIPLE Colorant; + cmsCIExyY MediaWhite; + + cmsXYZ2xyY(&MediaWhite, &hdr ->WhitePoint); + + if (ComputeColorantMatrix(&Colorant, &MediaWhite, &hdr ->Primaries)) { + + cmsAddTag(hdr ->hProfile, icSigRedColorantTag, &Colorant.Red); + cmsAddTag(hdr ->hProfile, icSigGreenColorantTag, &Colorant.Green); + cmsAddTag(hdr ->hProfile, icSigBlueColorantTag, &Colorant.Blue); + } + + cmsAddTag(hdr ->hProfile, icSigRedTRCTag, hdr ->Gamma[0]); + cmsAddTag(hdr ->hProfile, icSigGreenTRCTag, hdr ->Gamma[1]); + cmsAddTag(hdr ->hProfile, icSigBlueTRCTag, hdr ->Gamma[2]); + + return true; +} + + +BOOL cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr) +{ + if (*hdr ->Description) + cmsAddTag(hdr ->hProfile, icSigProfileDescriptionTag, hdr ->Description); + + if (*hdr ->Copyright) + cmsAddTag(hdr ->hProfile, icSigCopyrightTag, hdr ->Copyright); + + if (*hdr ->Manufacturer) + cmsAddTag(hdr ->hProfile, icSigDeviceMfgDescTag, hdr ->Manufacturer); + + if (*hdr ->Model) + cmsAddTag(hdr ->hProfile, icSigDeviceModelDescTag, hdr ->Model); + + return true; +} + + + +void cmsxChromaticAdaptationAndNormalization(LPPROFILERCOMMONDATA hdr, LPcmsCIEXYZ xyz, BOOL lReverse) +{ + + if (hdr->lUseCIECAM97s) { + + cmsJCh JCh; + + /* Let's CIECAM97s to do the adaptation to D50 */ + + xyz->X *= 100.; + xyz->Y *= 100.; + xyz->Z *= 100.; + + _cmsxClampXYZ100(xyz); + + if (lReverse) { + cmsCIECAM97sForward(hdr->hPCS, xyz, &JCh); + cmsCIECAM97sReverse(hdr->hDevice, &JCh, xyz); + } + else { + + cmsCIECAM97sForward(hdr->hDevice, xyz, &JCh); + cmsCIECAM97sReverse(hdr->hPCS, &JCh, xyz); + } + + _cmsxClampXYZ100(xyz); + + xyz -> X /= 100.; + xyz -> Y /= 100.; + xyz -> Z /= 100.; + + } + else { + + /* Else, use Bradford */ + + if (lReverse) { + cmsAdaptToIlluminant(xyz, cmsD50_XYZ(), &hdr->WhitePoint, xyz); + } + else { + cmsAdaptToIlluminant(xyz, &hdr->WhitePoint, cmsD50_XYZ(), xyz); + } + + } + +} + + +void cmsxInitPCSViewingConditions(LPPROFILERCOMMONDATA hdr) +{ + + hdr->PCS.whitePoint.X = cmsD50_XYZ()->X * 100.; + hdr->PCS.whitePoint.Y = cmsD50_XYZ()->Y * 100.; + hdr->PCS.whitePoint.Z = cmsD50_XYZ()->Z * 100.; + + + hdr->PCS.Yb = 20; /* 20% of surround */ + hdr->PCS.La = 20; /* Adapting field luminance */ + hdr->PCS.surround = AVG_SURROUND; + hdr->PCS.D_value = 1.0; /* Complete adaptation */ + +} + + +/* Build gamut hull by geometric means */ +void cmsxComputeGamutHull(LPPROFILERCOMMONDATA hdr) +{ + int i; + int x0, y0, z0; + int Inside, Outside, Boundaries; + char code; + + + hdr -> hRGBHull = cmsxHullInit(); + + /* For all valid patches, mark RGB knots as 0 */ + for (i=0; i < hdr ->m.nPatches; i++) { + + if (hdr ->m.Allowed[i]) { + + LPPATCH p = hdr ->m.Patches + i; + + + x0 = (int) floor(p->Colorant.RGB[0] + .5); + y0 = (int) floor(p->Colorant.RGB[1] + .5); + z0 = (int) floor(p->Colorant.RGB[2] + .5); + + cmsxHullAddPoint(hdr->hRGBHull, x0, y0, z0); + } + } + + cmsxHullComputeHull(hdr ->hRGBHull); + +/* #ifdef DEBUG */ + cmsxHullDumpVRML(hdr -> hRGBHull, "rgbhull.wrl"); +/* #endif */ + + + + /* A check */ + + Inside = Outside = Boundaries = 0; + /* For all valid patches, mark RGB knots as 0 */ + for (i=0; i < hdr ->m.nPatches; i++) { + + if (hdr ->m.Allowed[i]) { + + LPPATCH p = hdr ->m.Patches + i; + + x0 = (int) floor(p->Colorant.RGB[0] + .5); + y0 = (int) floor(p->Colorant.RGB[1] + .5); + z0 = (int) floor(p->Colorant.RGB[2] + .5); + + code = cmsxHullCheckpoint(hdr -> hRGBHull, x0, y0, z0); + + switch (code) { + + case 'i': Inside++; break; + case 'o': Outside++; break; + default: Boundaries++; + } + + } + } + + if (hdr ->printf) + hdr ->printf("Gamut hull: %d inside, %d outside, %d on boundaries", Inside, Outside, Boundaries); + +} + +BOOL cmsxChoosePCS(LPPROFILERCOMMONDATA hdr) +{ + + double gamma_r, gamma_g, gamma_b; + cmsCIExyY SourceWhite; + + /* At first, compute aproximation on matrix-shaper */ + if (!cmsxComputeMatrixShaper(hdr ->ReferenceSheet, + hdr ->MeasurementSheet, + hdr -> Medium, + hdr ->Gamma, + &hdr ->WhitePoint, + &hdr ->BlackPoint, + &hdr ->Primaries)) return false; + + + + cmsXYZ2xyY(&SourceWhite, &hdr ->WhitePoint); + + gamma_r = cmsEstimateGamma(hdr ->Gamma[0]); + gamma_g = cmsEstimateGamma(hdr ->Gamma[1]); + gamma_b = cmsEstimateGamma(hdr ->Gamma[2]); + + + + if (gamma_r > 1.8 || gamma_g > 1.8 || gamma_b > 1.8 || + gamma_r == -1 || gamma_g == -1 || gamma_b == -1) { + + hdr ->PCSType = PT_Lab; + + if (hdr ->printf) + hdr ->printf("I have chosen Lab as PCS"); + + } + else { + + hdr ->PCSType = PT_XYZ; + + if (hdr ->printf) + hdr ->printf("I have chosen XYZ as PCS"); + } + + + + if (hdr ->printf) { + + char Buffer[256] = "Infered "; + + _cmsIdentifyWhitePoint(Buffer, &hdr ->WhitePoint); + hdr ->printf("%s", Buffer); + hdr ->printf("Primaries (x-y): [Red: %2.2f, %2.2f] [Green: %2.2f, %2.2f] [Blue: %2.2f, %2.2f]", + hdr ->Primaries.Red.x, hdr ->Primaries.Red.y, + hdr ->Primaries.Green.x, hdr ->Primaries.Green.y, + hdr ->Primaries.Blue.x, hdr ->Primaries.Blue.y); + + if ((gamma_r != -1) && (gamma_g != -1) && (gamma_b != -1)) { + + hdr ->printf("Estimated gamma: [Red: %2.2f] [Green: %2.2f] [Blue: %2.2f]", + gamma_r, gamma_g, gamma_b); + } + + + } + + + + return true; +} diff --git a/src/libs/lprof/cmsreg.cpp b/src/libs/lprof/cmsreg.cpp new file mode 100644 index 00000000..4e66a9ef --- /dev/null +++ b/src/libs/lprof/cmsreg.cpp @@ -0,0 +1,558 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.09a */ + + +#include "lcmsprf.h" + + +/* There are three kinds of lies: */ +/* */ +/* * lies */ +/* * damn lies */ +/* * statistics */ +/* */ +/* -Some Wag */ +/* */ +/* */ +/* This module handles multiple linear regression stuff */ + + + +/* A measurement of error + +typedef struct { + + double SSE; // The error sum of squares + double MSE; // The error mean sum of squares + double SSR; // The regression sum of squares + double MSR; // The regression mean sum of squares + double SSTO; // Total sum of squares + double F; // The Fisher-F value (MSR / MSE) + double R2; // Proportion of variability explained by the regression + // (root is Pearson correlation coefficient) + + double R2adj; // The adjusted coefficient of multiple determination. + // R2-adjusted or R2adj. This is calculated as + // R2adj = 1 - (1-R2)(N-n-1)/(N-1) + // and used as multiple correlation coefficient + // (really, it should be square root) + + } MLRSTATISTICS, FAR* LPMLRSTATISTICS; + +*/ + + +int cdecl cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms, + int ColorSpace, + LPMATN* lpMat, LPMLRSTATISTICS Stat); + +BOOL cdecl cmsxRegressionRGB2Lab(double r, double g, double b, + LPMATN tfm, LPcmsCIELab Lab); + +BOOL cdecl cmsxRegressionRGB2XYZ(double r, double g, double b, + LPMATN tfm, LPcmsCIEXYZ XYZ); + + +/* -------------------------------------------------------------- Implementation */ + +/* #define DEBUG 1 */ + + +/* Multiple linear regression. Also keep track of error. */ +/* Returns false if something goes wrong, or true if all Ok. */ + +static +BOOL MultipleLinearRegression(const LPMATN xi, /* Dependent variable */ + const LPMATN y, /* Independent variable */ + int nvar, /* Number of samples */ + int npar, /* Number of parameters (terms) */ + double* coeff, /* Returned coefficients */ + LPMATN vcb, /* Variance-covariance array */ + double *tvl, /* T-Values */ + LPMLRSTATISTICS ans) /* The returned statistics */ +{ + LPMATN bt, xt, a, xy, yt, b; + double sum; + LPMATN temp1, temp2; + int i; + + + /* |xt| = |xi| T */ + xt = MATNtranspose(xi); + if (xt == NULL) return false; + + + /* |a| = |xt|* |xi| */ + a = MATNmult(xt, xi); + if (a == NULL) return false; + + + /* |xy| = |xy| * |y| */ + xy = MATNmult (xt, y); + if (xy == NULL) return false; + + + /* solve system |a|*|xy| = 0 */ + if (!MATNsolve(a, xy)) return false; + + /* b will hold coefficients */ + b = MATNalloc (xy->Rows, 1); + if (b == NULL) return false; + + for (i = 0; i < npar; i++) + b->Values[i][0] = xy->Values[i][0]; + + /* Store a copy for later user */ + for (i = 0; i < npar; i++) + coeff[i] = b->Values[i][0]; + + /* Error analysis. */ + + /* SSE and MSE. */ + temp1 = MATNalloc (1,1); + if ((temp1->Values[0][0] = MATNcross(y)) == 0) return false; + + /* |bt| = |b| T */ + bt = MATNtranspose (b); + if (bt == NULL) return false; + + /* |yt| = |bt| * |xt| */ + yt = MATNmult (bt, xt); + if (yt == NULL) return false; + + + /* |temp2| = |yt|* |y| */ + temp2 = MATNmult (yt, y); + if (temp2 == NULL) return false; + + /* SSE, MSE */ + ans->SSE = temp1 -> Values[0][0] - temp2 -> Values[0][0]; + ans->MSE = ans->SSE / (double) (nvar - npar); + + /* SSTO */ + sum = 0; + for (i=0; i < nvar; i++) + sum += y->Values[i][0]; + + sum *= sum / (double) nvar; + ans->SSTO = temp1->Values[0][0] - sum; + + /* SSR, MSR, and Fisher-F */ + ans->SSR = temp2->Values[0][0] - sum; + ans->MSR = ans->SSR / (double) (npar - 1); + ans->F = ans->MSR / ans->MSE; + + /* Correlation coefficients. */ + ans->R2 = ans->SSR/ans->SSTO; + ans->R2adj = 1.0 - (ans->SSE/ans->SSTO)*((nvar-1.)/(nvar-npar)); + + /* Variance-covariance matrix */ + /* */ + /* In RGB->Lab, for example: */ + /* */ + /* Var(R) Cov(R,G) Cov(R,B) */ + /* |vcb| = Cov(R,G) Var(G) Cov(G,B) */ + /* Cov(R,B) Cov(G,B) Var(B) */ + /* */ + + MATNscalar(a, ans->MSE, vcb); + + /* Determine the T-values */ + + for (i=0; i < npar; i++) { + + temp1->Values[0][0] = fabs(vcb->Values[i][0]); + if ( temp1->Values[0][0] == 0) + tvl[i] = 0; /* This should never happen */ + else + tvl[i] = b->Values[i][0] / sqrt(temp1->Values[0][0]); + } + + + /* Ok, done */ + + MATNfree(a); MATNfree(xy); MATNfree(yt); MATNfree(b); + MATNfree(temp1); MATNfree(temp2); MATNfree(bt); MATNfree(xt); + + + return true; +} + + + +/* Does create (so, it allocates) the regression matrix, */ +/* keeping track of error as well. */ + +static +BOOL CreateRegressionMatrix(const LPMATN Input, const LPMATN Output, + LPMATN* ptrMatrix, LPMLRSTATISTICS maxErrorMeas) +{ + double* coef; + double* tval; + LPMATN ivar, dvar, vcov; + MLRSTATISTICS ErrorMeas, PeakErrorMeas; + int i, j, nIn, nOut, NumOfPatches; + + nIn = Input -> Cols; + nOut = Output -> Cols; + NumOfPatches = Input -> Rows; + + /* Checkpoint */ + if (Output -> Rows != NumOfPatches) { + + cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Regression matrix mismatch"); + return false; + } + + coef = (double*) malloc(nIn * sizeof(double)); + if (coef == NULL) return false; + + tval = (double*) malloc(nIn * sizeof(double)); + if (tval == NULL) { + free(coef); + return false; + } + + ivar = MATNalloc(NumOfPatches, nIn); + dvar = MATNalloc(NumOfPatches, 1); + + /* Copy In to ivar, */ + for (i = 0; i < NumOfPatches; i++) { + + for (j = 0; j < nIn; j++) + ivar->Values[i][j] = Input->Values[i][j]; + } + + /* This is the (symmetric) Covariance matrix */ + vcov = MATNalloc(nIn, nIn); + + /* This is the regression matrix */ + *ptrMatrix = MATNalloc(nIn, nOut); + + PeakErrorMeas.R2adj = 0; + for (j = 0; j < nOut; ++j) + { + for (i = 0; i < NumOfPatches; ++i) + dvar->Values[i][0] = Output->Values[i][j]; + + if (MultipleLinearRegression(ivar, dvar, NumOfPatches, nIn, coef, vcov, tval, &ErrorMeas)) { + + /* Ok so far... store values */ + for (i = 0; i < nIn; i++) + (*ptrMatrix)->Values[i][j] = coef[i]; + } + else { + /* Boo... got error. Discard whole point. */ + MATNfree(ivar); MATNfree(dvar); MATNfree(vcov); + if (coef) free(coef); + if (tval) free(tval); + MATNfree(*ptrMatrix); *ptrMatrix = NULL; + return false; + } + + /* Did this colorant got higer error? If so, this is */ + /* the peak of all pixel */ + + if(fabs(ErrorMeas.R2adj) > fabs(PeakErrorMeas.R2adj)) + PeakErrorMeas = ErrorMeas; + } + + /* This is the peak error on all components */ + *maxErrorMeas = PeakErrorMeas; + + +#ifdef DEBUG + MATNprintf("Variance-Covariance", vcov); + printf("R2adj: %g, F: %g\n", PeakErrorMeas.R2adj, PeakErrorMeas.F); +#endif + + /* Free stuff. */ + MATNfree(ivar); MATNfree(dvar); MATNfree(vcov); + if (coef) free(coef); + if (tval) free(tval); + + return true; +} + + +/* Does compute the term of regression based on inputs. */ + +static +double Term(int n, double r, double g, double b) +{ + + switch (n) { + + /* 0 */ + case 0 : return 255.0; /* 0 0 0 */ + + /* 1 */ + case 1 : return r; /* 1 0 0 */ + case 2 : return g; /* 0 1 0 */ + case 3 : return b; /* 0 0 1 */ + + /* 2 */ + case 4 : return r * g; /* 1 1 0 */ + case 5 : return r * b; /* 1 0 1 */ + case 6 : return g * b; /* 0 1 1 */ + case 7 : return r * r; /* 2 0 0 */ + case 8 : return g * g; /* 0 2 0 */ + case 9 : return b * b; /* 0 0 2 */ + + /* 3 */ + case 10: return r * g * b; /* 1 1 1 */ + case 11: return r * r * r; /* 3 0 0 */ + case 12: return g * g * g; /* 0 3 0 */ + case 13: return b * b * b; /* 0 0 3 */ + case 14: return r * g * g; /* 1 2 0 */ + case 15: return r * r * g; /* 2 1 0 */ + case 16: return g * g * b; /* 0 2 1 */ + case 17: return b * r * r; /* 2 0 1 */ + case 18: return b * b * r; /* 1 0 2 */ + + /* 4 */ + + case 19: return r * r * g * g; /* 2 2 0 */ + case 20: return g * g * b * b; /* 0 2 2 */ + case 21: return r * r * b * b; /* 2 0 2 */ + case 22: return r * r * g * b; /* 2 1 1 */ + case 23: return r * g * g * b; /* 1 2 1 */ + case 24: return r * g * b * b; /* 1 1 2 */ + case 25: return r * r * r * g; /* 3 1 0 */ + case 26: return r * r * r * b; /* 3 0 1 */ + case 27: return r * g * g * g; /* 1 3 0 */ + case 28: return g * g * g * b; /* 0 3 1 */ + case 29: return r * b * b * b; /* 1 0 3 */ + case 30: return g * b * b * b; /* 0 1 3 */ + case 31: return r * r * r * r; /* 4 0 0 */ + case 32: return g * g * g * g; /* 0 4 0 */ + case 33: return b * b * b * b; /* 0 0 4 */ + + /* 5 */ + + case 34: return r * r * g * g * b; /* 2 2 1 */ + case 35: return r * g * g * b * b; /* 1 2 2 */ + case 36: return r * r * g * b * b; /* 2 1 2 */ + case 37: return r * r * r * g * g; /* 3 2 0 */ + case 38: return r * r * r * g * b; /* 3 1 1 */ + case 39: return r * r * r * b * b; /* 3 0 2 */ + case 40: return g * g * g * b * b; /* 0 3 2 */ + case 41: return r * r * g * g * g; /* 2 3 0 */ + case 42: return r * g * g * g * b; /* 1 3 1 */ + case 43: return r * r * b * b * b; /* 2 0 3 */ + case 44: return g * g * b * b * b; /* 0 2 3 */ + case 45: return r * g * b * b * b; /* 1 1 3 */ + case 46: return r * r * r * r * g; /* 4 1 0 */ + case 47: return r * r * r * r * b; /* 4 0 1 */ + case 48: return r * g * g * g * g; /* 1 4 0 */ + case 49: return g * g * g * g * b; /* 0 4 1 */ + case 50: return r * b * b * b * b; /* 1 0 4 */ + case 51: return g * b * b * b * b; /* 0 1 4 */ + case 52: return r * r * r * r * r; /* 5 0 0 */ + case 53: return g * g * g * g * g; /* 0 5 0 */ + case 54: return b * b * b * b * b; /* 0 0 5 */ + + + default: return 0; + } +} + + + +int cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms, + int ColorSpace, + LPMATN* lpMat, LPMLRSTATISTICS Stat) +{ + LPMATN Input, Output; + int nCollected = cmsxPCollCountSet(m, Allowed); + int i, j, n, rc; + + /* We are going always 3 -> 3 for now.... */ + + Input = MATNalloc(nCollected, nterms); + Output = MATNalloc(nCollected, 3); + + /* Set independent terms */ + + for (n = i = 0; i < m -> nPatches; i++) + { + if (Allowed[i]) { + + LPPATCH p = m -> Patches + i; + + for (j=0; j < nterms; j++) + Input -> Values[n][j] = Term(j, p -> Colorant.RGB[0], p -> Colorant.RGB[1], p->Colorant.RGB[2]); + + switch (ColorSpace) { + + case PT_Lab: + + Output-> Values[n][0] = p -> Lab.L; + Output-> Values[n][1] = p -> Lab.a; + Output-> Values[n][2] = p -> Lab.b; + break; + + case PT_XYZ: + Output-> Values[n][0] = p -> XYZ.X; + Output-> Values[n][1] = p -> XYZ.Y; + Output-> Values[n][2] = p -> XYZ.Z; + break; + + + default: + cmsSignalError(LCMS_ERRC_ABORTED, "Invalid colorspace"); + } + + n++; + } + } + + + /* Apply multiple linear regression */ + + if (*lpMat) MATNfree(*lpMat); + rc = CreateRegressionMatrix(Input, Output, lpMat, Stat); + + /* Free variables */ + + MATNfree(Input); + MATNfree(Output); + + +#ifdef DEBUG + if (rc == true) + MATNprintf("tfm", *lpMat); +#endif + + return rc; +} + + +/* Convert a RGB triplet to Lab by using regression matrix */ + +BOOL cmsxRegressionRGB2Lab(double r, double g, double b, LPMATN tfm, LPcmsCIELab Lab) +{ + LPMATN inVec, outVec; + int i; + + inVec = MATNalloc(1, tfm->Rows); + if (inVec == NULL) + return false; + + /* Put terms */ + for (i=0; i < tfm->Rows; i++) + inVec -> Values[0][i] = Term(i, r, g, b); + + /* Across regression matrix */ + outVec = MATNmult(inVec, tfm); + + /* Store result */ + if (outVec != NULL) { + + Lab->L = outVec->Values[0][0]; + Lab->a = outVec->Values[0][1]; + Lab->b = outVec->Values[0][2]; + MATNfree(outVec); + } + + MATNfree(inVec); + return true; +} + + +/* Convert a RGB triplet to XYX by using regression matrix */ + +BOOL cmsxRegressionRGB2XYZ(double r, double g, double b, LPMATN tfm, LPcmsCIEXYZ XYZ) +{ + LPMATN inVec, outVec; + int i; + + inVec = MATNalloc(1, tfm->Rows); + if (inVec == NULL) + return false; + + /* Put terms */ + for (i=0; i < tfm->Rows; i++) + inVec -> Values[0][i] = Term(i, r, g, b); + + /* Across regression matrix */ + outVec = MATNmult(inVec, tfm); + + /* Store result */ + if (outVec != NULL) { + + XYZ->X = outVec->Values[0][0]; + XYZ->Y = outVec->Values[0][1]; + XYZ->Z = outVec->Values[0][2]; + MATNfree(outVec); + } + + MATNfree(inVec); + return true; +} + + +/* Convert a RGB triplet to XYX by using regression matrix */ + +BOOL cmsxRegressionXYZ2RGB(LPcmsCIEXYZ XYZ, LPMATN tfm, double RGB[3]) +{ + LPMATN inVec, outVec; + int i; + + inVec = MATNalloc(1, tfm->Rows); + if (inVec == NULL) + return false; + + /* Put terms */ + for (i=0; i < tfm->Rows; i++) + inVec -> Values[0][i] = Term(i, XYZ->X, XYZ->Y, XYZ->Z); + + /* Across regression matrix */ + outVec = MATNmult(inVec, tfm); + + /* Store result */ + if (outVec != NULL) { + + RGB[0] = outVec->Values[0][0]; + RGB[1] = outVec->Values[0][1]; + RGB[2] = outVec->Values[0][2]; + MATNfree(outVec); + } + + MATNfree(inVec); + return true; +} + diff --git a/src/libs/lprof/cmsscn.cpp b/src/libs/lprof/cmsscn.cpp new file mode 100644 index 00000000..b99fed87 --- /dev/null +++ b/src/libs/lprof/cmsscn.cpp @@ -0,0 +1,422 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.08a */ + + +#include "lcmsprf.h" +#include + +/* The scanner profiler */ + + +BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys); +BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys); + +/* ------------------------------------------------------------ Implementation */ + + + +/* Does create regression matrix */ + +static +void ComputeGlobalRegression(LPSCANNERPROFILERDATA sys) +{ + BOOL lAllOk; + int nTerms; + MLRSTATISTICS Stat; + + nTerms = cmsxFindOptimumNumOfTerms(&sys ->hdr, 55, &lAllOk); + + + if (!lAllOk) { + if (sys -> hdr.printf) + sys -> hdr.printf("*** WARNING: Inconsistence found, profile may be wrong. Check the target!"); + nTerms = 4; + } + + /* Create high terms matrix used by interpolation */ + cmsxRegressionCreateMatrix(&sys -> hdr.m, + sys -> hdr.m.Allowed, + nTerms, + sys -> hdr.PCSType, + &sys -> HiTerms, + &Stat); + + if (sys -> hdr.printf) + sys -> hdr.printf("Global regression: %d terms, R2Adj = %g", nTerms, Stat.R2adj); + + /* Create low terms matrix used by extrapolation */ + cmsxRegressionCreateMatrix(&sys -> hdr.m, + sys -> hdr.m.Allowed, + (nTerms > 10 ? 10 : nTerms), + sys -> hdr.PCSType, + &sys -> LoTerms, + &Stat); + if (sys -> hdr.printf) + sys -> hdr.printf("Extrapolation: R2Adj = %g", Stat.R2adj); + +} + + +/* Fill struct with default values */ + +BOOL cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys) +{ + + + if (sys == NULL) return false; + ZeroMemory(sys, sizeof(SCANNERPROFILERDATA)); + + sys->hdr.DeviceClass = icSigInputClass; + sys->hdr.ColorSpace = icSigRgbData; + sys->hdr.PCSType = PT_Lab; + sys->hdr.Medium = MEDIUM_REFLECTIVE_D50; + + /* Default values for generation */ + + sys -> hdr.lUseCIECAM97s = false; + sys -> hdr.CLUTPoints = 16; + + + /* Default viewing conditions for scanner */ + + sys -> hdr.device.Yb = 20; + sys -> hdr.device.La = 20; + sys -> hdr.device.surround = AVG_SURROUND; + sys -> hdr.device.D_value = 1.0; /* Complete adaptation */ + + + /* Viewing conditions of PCS */ + cmsxInitPCSViewingConditions(&sys -> hdr); + + + sys -> HiTerms = NULL; + sys -> LoTerms = NULL; + + strcpy(sys -> hdr.Description, "no description"); + strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set"); + strcpy(sys -> hdr.Copyright, "No copyright, use freely"); + strcpy(sys -> hdr.Model, "(unknown)"); + + sys ->lLocalConvergenceExtrapolation = false; + sys ->hdr.ProfileVerbosityLevel = 0; + + + return true; +} + +/* Auxiliar: take RGB and update gauge */ +static +void GetRGB(LPPROFILERCOMMONDATA hdr, WORD In[], double* r, double* g, double* b) +{ + static int Count = 0, n_old = 0; + double R, G, B; + int n; + + + R = _cmsxSaturate65535To255(In[0]); /* Convert from the sheet notation */ + G = _cmsxSaturate65535To255(In[1]); /* 0..255.0, to our notation */ + B = _cmsxSaturate65535To255(In[2]); /* of 0..0xffff, 0xffff/255 = 257 */ + + if (R == 0 && G == 0 && B == 0) { + Count = 0; n_old = -1; + } + + n = (int) (double) (100. * Count) / (hdr->CLUTPoints * hdr->CLUTPoints * hdr->CLUTPoints); + Count++; + + if (n > n_old) { + if (hdr->Gauger) hdr->Gauger("", 0, 100, (int) n); + } + + n_old = n; + *r = R; *g = G; *b = B; + +} + + + + + +/* The sampler for Lab */ +static +int RegressionSamplerLab(WORD In[], WORD Out[], LPVOID Cargo) +{ + cmsCIEXYZ xyz; + cmsCIELab Lab; + double r, g, b; + LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo; + char code; + + + GetRGB(&sys->hdr, In, &r, &g, &b); + + + code = cmsxHullCheckpoint(sys->hdr.hRGBHull, + (int) floor(r + .5), + (int) floor(g + .5), + (int) floor(b + .5)); + + + if (code == 'i') { /* Inside gamut */ + + if (!cmsxRegressionRGB2Lab(r, g, b, sys -> HiTerms, &Lab)) return false; + } + else + if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */ + + if (!cmsxRegressionRGB2Lab(r, g, b, sys -> LoTerms, &Lab)) return false; + } + else { /* At gamut hull boundaries */ + + if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m, + PT_Lab, + 10, + true, + 30, + r, g, b, + &Lab)) return false; + } + + + /* Regression CAN deliver wrong values. Clamp these. */ + cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128); + + /* Normalize */ + cmsLab2XYZ(cmsD50_XYZ(), &xyz, &Lab); + cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false); + cmsXYZ2Lab(cmsD50_XYZ(), &Lab, &xyz); + + /* Clamping again, adaptation could move slightly values */ + cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128); + + /* To PCS encoding */ + cmsFloat2LabEncoded(Out, &Lab); + + + return true; /* And done with success */ +} + + + + + +/* The sampler for XYZ */ +static +int RegressionSamplerXYZ(WORD In[], WORD Out[], LPVOID Cargo) +{ + cmsCIEXYZ xyz; + double r, g, b; + LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo; + char code; + + GetRGB(&sys -> hdr, In, &r, &g, &b); + + code = cmsxHullCheckpoint(sys ->hdr.hRGBHull, + (int) floor(r + .5), + (int) floor(g + .5), + (int) floor(b + .5)); + + if (code == 'i') { /* Inside gamut */ + + if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> HiTerms, &xyz)) return false; + } + else + if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */ + + if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> LoTerms, &xyz)) return false; + } + + else { /* At gamut hull boundaries */ + + if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m, + PT_XYZ, + 10, + true, + 30, + r, g, b, + &xyz)) return false; + } + + + xyz.X /= 100.; + xyz.Y /= 100.; + xyz.Z /= 100.; + + cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false); + + /* To PCS encoding. It also claps bad values */ + cmsFloat2XYZEncoded(Out, &xyz); + + return true; /* And done witch success */ +} + + + +/* The main scanner profiler */ +BOOL cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys) +{ + + LPLUT AToB0; + DWORD dwNeedSamples; + + + if (!*sys -> hdr.OutputProfileFile) + return false; + + + if (!cmsxChoosePCS(&sys->hdr)) + return false; + + dwNeedSamples = PATCH_HAS_RGB; + if (sys ->hdr.PCSType == PT_Lab) + dwNeedSamples |= PATCH_HAS_Lab; + else + dwNeedSamples |= PATCH_HAS_XYZ; + + + if (sys->hdr.printf) { + + sys->hdr.printf("Loading sheets..."); + + if (sys->hdr.ReferenceSheet[0]) + sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet); + if (sys->hdr.MeasurementSheet[0]) + sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet); + } + + + if (!cmsxPCollBuildMeasurement(&sys->hdr.m, + sys->hdr.ReferenceSheet, + sys->hdr.MeasurementSheet, + dwNeedSamples)) return false; + + + + sys->hdr.hProfile = cmsCreateRGBProfile(NULL, NULL, NULL); + + + cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass); + cmsSetColorSpace(sys->hdr.hProfile, sys->hdr.ColorSpace); + cmsSetPCS(sys->hdr. hProfile, _cmsICCcolorSpace(sys->hdr.PCSType)); + + /* Save char target tag */ + if (sys->hdr.ProfileVerbosityLevel >= 2) { + + cmsxEmbedCharTarget(&sys ->hdr); + } + + AToB0 = cmsAllocLUT(); + + cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3); + + cmsxComputeLinearizationTables(&sys-> hdr.m, + sys -> hdr.PCSType, + sys -> Prelinearization, + 1024, + MEDIUM_REFLECTIVE_D50); + + /* Refresh RGB of all patches. This converts all regression into */ + /* near linear RGB->Lab or XYZ */ + + cmsxPCollLinearizePatches(&sys->hdr.m, sys -> hdr.m.Allowed, sys -> Prelinearization); + + cmsxComputeGamutHull(&sys->hdr); + ComputeGlobalRegression(sys); + + cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1); + + /* Set CIECAM97s parameters */ + + sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.; + sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.; + sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.; + + + sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device); + sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS); + + + if (sys -> hdr.PCSType == PT_Lab) + cmsSample3DGrid(AToB0, RegressionSamplerLab, sys, 0); + else + cmsSample3DGrid(AToB0, RegressionSamplerXYZ, sys, 0); + + cmsCIECAM97sDone(sys->hdr.hDevice); + cmsCIECAM97sDone(sys->hdr.hPCS); + + cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0); + + + cmsxEmbedTextualInfo(&sys -> hdr); + + cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint); + cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint); + + + /* Save primaries & gamma curves */ + if (sys->hdr.ProfileVerbosityLevel >= 1) { + + cmsxEmbedMatrixShaper(&sys ->hdr); + } + + _cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile); + + cmsCloseProfile(sys->hdr.hProfile); + sys->hdr.hProfile = NULL; + + cmsxPCollFreeMeasurements(&sys->hdr.m); + + cmsFreeLUT(AToB0); + + if (sys -> HiTerms) + MATNfree(sys -> HiTerms); + sys -> HiTerms = NULL; + + + if (sys -> LoTerms) + MATNfree(sys -> LoTerms); + sys -> LoTerms = NULL; + + + + if (sys ->Prelinearization[0]) + cmsFreeGammaTriple(sys -> Prelinearization); + + if (sys ->hdr.Gamma) + cmsFreeGammaTriple(sys->hdr.Gamma); + + return true; +} diff --git a/src/libs/lprof/cmssheet.cpp b/src/libs/lprof/cmssheet.cpp new file mode 100644 index 00000000..cf8c569e --- /dev/null +++ b/src/libs/lprof/cmssheet.cpp @@ -0,0 +1,1746 @@ +/* */ +/* Little cms - profiler construction set */ +/* Copyright (C) 1998-2001 Marti Maria */ +/* */ +/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */ +/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */ +/* */ +/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */ +/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */ +/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */ +/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */ +/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */ +/* OF THIS SOFTWARE. */ +/* */ +/* This file is free software; you can redistribute it and/or modify it */ +/* under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* */ +/* As a special exception to the GNU General Public License, if you */ +/* distribute this file as part of a program that contains a */ +/* configuration script generated by Autoconf, you may include it under */ +/* the same distribution terms that you use for the rest of that program. */ +/* */ +/* Version 1.08a */ + + +#include "lcmsprf.h" + +/* #define _DEBUG 1 */ + + + +/* IT8.7 / CGATS.17-200x handling */ +LCMSHANDLE cdecl cmsxIT8Alloc(void); +void cdecl cmsxIT8Free(LCMSHANDLE IT8); + +/* Persistence */ +LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName); +LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len); +BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName); + +/* Properties */ +const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8); +BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type); + +BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* cProp, const char *Str); +BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val); + +const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* cProp); +double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp); +int cdecl cmsxIT8EnumProperties(LCMSHANDLE IT8, char ***PropertyNames); + + +/* Datasets */ + +BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen); + +BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE IT8, const char* cPatch, + const char* cSample, + char* Val, int ValBufferLen); + + +BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample, double* d); + +BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE IT8, const char* cPatch, + const char* cSample, + char *Val); + +BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample); +int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames); + +const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer); +int cdecl cmsxIT8GenericPatchNum(const char *Name); + + +/* ------------------------------------------------------------- Implementation */ + + +#define MAXID 128 /* Max length of identifier */ +#define MAXSTR 255 /* Max length of string */ + +#ifndef NON_WINDOWS +#include +#endif + +/* Symbols */ + +typedef enum { SNONE, + SINUM, /* Integer */ + SDNUM, /* Real */ + SIDENT, /* Identifier */ + SSTRING, /* string */ + SCOMMENT, /* comment */ + SEOLN, /* End of line */ + SEOF, /* End of stream */ + SSYNERROR, /* Syntax error found on stream */ + + /* Keywords */ + + SBEGIN_DATA, + SBEGIN_DATA_FORMAT, + SEND_DATA, + SEND_DATA_FORMAT, + SKEYWORD, + SSTRING_SY + + } SYMBOL; + + +/* Linked list of variable names */ + +typedef struct _KeyVal { + + struct _KeyVal* Next; + char* Keyword; /* Name of variable */ + char* Value; /* Points to value */ + + } KEYVALUE, FAR* LPKEYVALUE; + +/* Linked list of values (Memory sink) */ + +typedef struct _OwnedMem { + + struct _OwnedMem* Next; + void * Ptr; /* Point to value */ + + } OWNEDMEM, FAR* LPOWNEDMEM; + + +/* This struct hold all information about an openened */ +/* IT8 handler. Only one dataset is allowed. */ + +typedef struct { + + int nSamples, nPatches; /* Rows, Cols */ + int SampleID; /* Pos of ID */ + LPKEYVALUE HeaderList; /* The properties */ + char* FileBuffer; /* The ASCII stream */ + char** DataFormat; /* The binary stream descriptor */ + char** Data; /* The binary stream */ + LPOWNEDMEM MemorySink; /* The storage bakend */ + + /* Parser state machine */ + + SYMBOL sy; /* Current symbol */ + int ch; /* Current character */ + char* Source; /* Points to loc. being parsed */ + int inum; /* integer value */ + double dnum; /* real value */ + char id[MAXID]; /* identifier */ + char str[MAXSTR]; /* string */ + + /* Allowed keywords & datasets */ + + LPKEYVALUE ValidKeywords; + LPKEYVALUE ValidSampleID; + + char FileName[MAX_PATH]; + int lineno; /* line counter for error reporting */ + + char SheetType[MAXSTR]; /* New 1.09 */ + + } IT8, FAR* LPIT8; + + + +/* ------------------------------------------------------ IT8 parsing routines */ + + +/* A keyword */ +typedef struct { + const char *id; + SYMBOL sy; + + } KEYWORD; + +/* The keyword->symbol translation table. Sorting is required. */ +static const KEYWORD TabKeys[] = { + + {"BEGIN_DATA", SBEGIN_DATA }, + {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, + {"END_DATA", SEND_DATA}, + {"END_DATA_FORMAT", SEND_DATA_FORMAT}, + {"KEYWORD", SKEYWORD}, + {"STRING", SSTRING_SY}}; + +#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) + +/* Predefined properties */ +static char* PredefinedProperties[] = { + + "NUMBER_OF_FIELDS", /* Required - NUMBER OF FIELDS */ + "NUMBER_OF_SETS", /* Required - NUMBER OF SETS */ + "ORIGINATOR", /* Required - Identifies the specific system, organization or individual that created the data file. */ + "CREATED", /* Required - Indicates date of creation of the data file. */ + "DESCRIPTOR", /* Required - Describes the purpose or contents of the data file. */ + "DIFFUSE_GEOMETRY", /* The diffuse geometry used. Allowed values are "sphere" or "opal". */ + "MANUFACTURER", + "MANUFACTURE", /* Some broken Fuji targets does store this value */ + "PROD_DATE", /* Identifies year and month of production of the target in the form yyyy:mm. */ + "SERIAL", /* Uniquely identifies individual physical target. */ + + "MATERIAL", /* Identifies the material on which the target was produced using a code */ + /* uniquely identifying th e material. Th is is intend ed to be used for IT8.7 */ + /* physical targets only (i.e . IT8.7/1 a nd IT8.7/2). */ + + "INSTRUMENTATION", /* Used to report the specific instrumentation used (manufacturer and */ + /* model number) to generate the data reported. This data will often */ + /* provide more information about the particular data collected than an */ + /* extensive list of specific details. This is particularly important for */ + /* spectral data or data derived from spectrophotometry. */ + + "MEASUREMENT_SOURCE", /* Illumination used for spectral measurements. This data helps provide */ + /* a guide to the potential for issues of paper fluorescence, etc. */ + + "PRINT_CONDITIONS", /* Used to define the ch aracteristics of the printed sheet being reported. */ + /* Where standard conditions have been defined (e.g., SW OP at nominal) */ + /* named conditions may suffice. Otherwise, detailed in formation is */ + /* needed. */ + + "SAMPLE_BACKING", /* Identifies the backing material used behind the sample during */ + /* measurement. Allowed values are “black”, “white”, or "na". */ + + "CHISQ_DOF" /* Degrees of freedom associated with the Chi squared statistic */ + }; + +#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(char *)) + + +/* Predefined sample types on dataset */ +static char* PredefinedSampleID[] = { + + "CMYK_C", /* Cyan component of CMYK data expressed as a percentage */ + "CMYK_M", /* Magenta component of CMYK data expressed as a percentage */ + "CMYK_Y", /* Yellow component of CMYK data expressed as a percentage */ + "CMYK_K", /* Black component of CMYK data expressed as a percentage */ + "D_RED", /* Red filter density */ + "D_GREEN", /* Green filter density */ + "D_BLUE", /* Blue filter density */ + "D_VIS", /* Visual filter density */ + "D_MAJOR_FILTER", /* Major filter d ensity */ + "RGB_R", /* Red component of RGB data */ + "RGB_G", /* Green component of RGB data */ + "RGB_B", /* Blue com ponent of RGB data */ + "SPECTRAL_NM", /* Wavelength of measurement expressed in nanometers */ + "SPECTRAL_PCT", /* Percentage reflectance/transmittance */ + "SPECTRAL_DEC", /* Reflectance/transmittance */ + "XYZ_X", /* X component of tristimulus data */ + "XYZ_Y", /* Y component of tristimulus data */ + "XYZ_Z", /* Z component of tristimulus data */ + "XYY_X" /* x component of chromaticity data */ + "XYY_Y", /* y component of chromaticity data */ + "XYY_CAPY", /* Y component of tristimulus data */ + "LAB_L", /* L* component of Lab data */ + "LAB_A", /* a* component of Lab data */ + "LAB_B", /* b* component of Lab data */ + "LAB_C", /* C*ab component of Lab data */ + "LAB_H", /* hab component of Lab data */ + "LAB_DE" /* CIE dE */ + "LAB_DE_94", /* CIE dE using CIE 94 */ + "LAB_DE_CMC", /* dE using CMC */ + "LAB_DE_2000", /* CIE dE using CIE DE 2000 */ + "MEAN_DE", /* Mean Delta E (LAB_DE) of samples compared to batch average */ + /* (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) */ + "STDEV_X", /* Standard deviation of X (tristimulus data) */ + "STDEV_Y", /* Standard deviation of Y (tristimulus data) */ + "STDEV_Z", /* Standard deviation of Z (tristimulus data) */ + "STDEV_L", /* Standard deviation of L* */ + "STDEV_A" /* Standard deviation of a* */ + "STDEV_B", /* Standard deviation of b* */ + "STDEV_DE", /* Standard deviation of CIE dE */ + "CHI_STQD_PAR"}; /* The average of the standard deviations of L*, a* and b*. It is */ + /* used to derive an estimate of the chi-squared parameter which is */ + /* recommended as the predictor of the variability of dE */ + +#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) + + +/* Checks whatsever if c is a valid identifier middle char. */ +static +BOOL isidchar(int c) +{ + return (isalnum(c) || c == '$' || c == '%' || c == '&' || c == '/' || c == '.' || c == '_'); + +} + +/* Checks whatsever if c is a valid identifier first char. */ +static +BOOL isfirstidchar(int c) +{ + return !isdigit(c) && isidchar(c); +} + +/* Checks if c is a separator */ +static +BOOL isseparator(int c) +{ + return (c == ' ' || c == '\t' || c == '\r'); +} + +/* a replacement for strupr(), just for compatibility sake */ + +static +void xstrupr(char *cp) +{ + for (;*cp;cp++) + if (*cp >= 'a' && *cp <= 'z') + *cp += 'A'-'a'; +} + +/* A replacement for (the nonstandard) filelength */ + +static +int xfilelength(int fd) +{ +#ifdef _MSC_VER + return _filelength(fd); +#else + struct stat sb; + if (fstat(fd, &sb) < 0) + return(-1); + return(sb.st_size); +#endif + + +} + +static +BOOL SynError(LPIT8 it8, const char *Txt, ...) +{ + char Buffer[256], ErrMsg[1024]; + va_list args; + + va_start(args, Txt); + vsprintf(Buffer, Txt, args); + va_end(args); + + sprintf(ErrMsg, "%s: Line %d, %s", it8->FileName, it8->lineno, Buffer); + it8->sy = SSYNERROR; + cmsSignalError(LCMS_ERRC_ABORTED, ErrMsg); + return false; +} + +static +BOOL Check(LPIT8 it8, SYMBOL sy, const char* Err) +{ + if (it8 -> sy != sy) + return SynError(it8, Err); + return true; +} + + + +/* Read Next character from stream */ +static +void NextCh(LPIT8 it8) +{ + it8->ch = *it8->Source; + if (it8->ch) it8->Source++; +} + + +/* Try to see if current identifier is a keyword, if so return the referred symbol */ +static +SYMBOL BinSrchKey(const char *id) +{ + int l = 1; + int r = NUMKEYS; + int x, res; + + while (r >= l) + { + x = (l+r)/2; + res = strcmp(id, TabKeys[x-1].id); + if (res == 0) return TabKeys[x-1].sy; + if (res < 0) r = x - 1; + else l = x + 1; + } + + return SNONE; +} + + +/* 10 ^n */ +static +double pow10(int n) +{ + return pow(10, n); +} + + +/* Reads a Real number, tries to follow from integer number */ +static +void ReadReal(LPIT8 it8, int inum) +{ + it8->dnum = (double) inum; + + while (isdigit(it8->ch)) { + + it8->dnum = it8->dnum * 10.0 + (it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { /* Decimal point */ + + double frac = 0.0; /* fraction */ + int prec = 0; /* precission */ + + NextCh(it8); /* Eats dec. point */ + + while (isdigit(it8->ch)) { + + frac = frac * 10.0 + (it8->ch - '0'); + prec++; + NextCh(it8); + } + + it8->dnum = it8->dnum + (frac / pow10(prec)); + } + + /* Exponent, example 34.00E+20 */ + if (toupper(it8->ch) == 'E') { + + int e; + int sgn; + + NextCh(it8); sgn = 1; + + if (it8->ch == '-') { + + sgn = -1; NextCh(it8); + } + else + if (it8->ch == '+') { + + sgn = +1; + NextCh(it8); + } + + + e = 0; + while (isdigit(it8->ch)) { + + if ((double) e * 10L < INT_MAX) + e = e * 10 + (it8->ch - '0'); + + NextCh(it8); + } + + e = sgn*e; + + it8 -> dnum = it8 -> dnum * pow10(e); + } +} + + + +/* Reads next symbol */ +static +void InSymbol(LPIT8 it8) +{ + char *idptr; + int k; + SYMBOL key; + int sng; + + do { + + while (isseparator(it8->ch)) + NextCh(it8); + + if (isfirstidchar(it8->ch)) { /* Identifier */ + + + k = 0; + idptr = it8->id; + + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + xstrupr(it8->id); + + key = BinSrchKey(it8->id); + if (key == SNONE) it8->sy = SIDENT; + else it8->sy = key; + + } + else /* Is a number? */ + if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+') + { + int sign = 1; + + if (it8->ch == '-') { + sign = -1; + NextCh(it8); + } + + it8->inum = 0; + it8->sy = SINUM; + + while (isdigit(it8->ch)) + { + if ((long) it8->inum * 10L > (long) INT_MAX) + { + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8->inum = it8->inum * 10 + (it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { + + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8 -> inum *= sign; + return; + + } + else + switch ((int) it8->ch) { + + case '\0': + case '\x1a': + it8->sy = SEOF; + break; + + + + case '\n': + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + /* Comment */ + + case '#': + NextCh(it8); + while (it8->ch && it8->ch != '\n') + NextCh(it8); + + it8->sy = SCOMMENT; + break; + + /* String. I will support \", \n, \t and \\. */ + /* But otherwise I hardly doubt these will be used ... */ + + case '\'': + case '\"': + idptr = it8->str; + sng = it8->ch; + k = 0; + NextCh(it8); + + while (k < MAXSTR && it8->ch != sng) { + + if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; + else { + + if (it8->ch == '\\') + { + NextCh(it8); + + switch (it8->ch) { + + case 'n': *idptr++ = '\n'; break; + case 'r': *idptr++ = '\r'; break; + case 't': *idptr++ = '\t'; break; + case '\\': *idptr++ = '\\'; break; + + default: + *idptr++ = (char) it8->ch; + } + + NextCh(it8); + } + else + { + *idptr++ = (char) it8->ch; + NextCh(it8); + } + + k++; + } + } + + it8->sy = SSTRING; + *idptr = '\0'; + NextCh(it8); + break; + + + default: + it8->sy = SSYNERROR; + NextCh(it8); + + } + + } while (it8->sy == SCOMMENT); +} + +/* Checks end of line separator */ +static +BOOL CheckEOLN(LPIT8 it8) +{ + if (!Check(it8, SEOLN, "Expected separator")) return false; + while (it8 -> sy == SEOLN) + InSymbol(it8); + return true; + +} + +/* Skip a symbol */ + +static +void Skip(LPIT8 it8, SYMBOL sy) +{ + if (it8->sy == sy && it8->sy != SEOF) + InSymbol(it8); +} + +/* Returns a string holding current value */ +static +BOOL GetVal(LPIT8 it8, char* Buffer) +{ + switch (it8->sy) { + + case SIDENT: strncpy(Buffer, it8->id, MAXID-1); break; + case SINUM: sprintf(Buffer, "%d", it8 -> inum); break; + case SDNUM: sprintf(Buffer, "%g", it8 -> dnum); break; + case SSTRING: strncpy(Buffer, it8->str, MAXSTR-1); break; + + + default: + return SynError(it8, "Sample data expected"); + } + + return true; +} + +/* ---------------------------------------------------------- Memory management */ + + + +/* Frees an allocator and owned memory */ +void cmsxIT8Free(LCMSHANDLE hIT8) +{ + LPIT8 it8 = (LPIT8) hIT8; + + if (it8 == NULL) + return; + + if (it8->MemorySink) { + + LPOWNEDMEM p; + LPOWNEDMEM n; + + for (p = it8->MemorySink; p != NULL; p = n) { + + n = p->Next; + if (p->Ptr) free(p->Ptr); + free(p); + } + } + + if (it8->FileBuffer) + free(it8->FileBuffer); + + free(it8); +} + + +/* Allocates a chunk of data, keep linked list */ +static +void* AllocChunk(LPIT8 it8, size_t size) +{ + LPOWNEDMEM ptr1; + void* ptr = malloc(size); + + if (ptr) { + + ZeroMemory(ptr, size); + ptr1 = (LPOWNEDMEM) malloc(sizeof(OWNEDMEM)); + + if (ptr1 == NULL) { + + free(ptr); + return NULL; + } + + ZeroMemory(ptr1, sizeof(OWNEDMEM)); + + ptr1-> Ptr = ptr; + ptr1-> Next = it8 -> MemorySink; + it8 -> MemorySink = ptr1; + } + + return ptr; +} + + +/* Allocates a string */ +static +char *AllocString(LPIT8 it8, const char* str) +{ + int Size = strlen(str)+1; + char *ptr; + ptr = (char *) AllocChunk(it8, Size); + if (ptr) strncpy (ptr, str, Size); + return ptr; +} + +/* Searches through linked list */ + +static +BOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, LPKEYVALUE* LastPtr) +{ + for (; p != NULL; p = p->Next) { + + if (LastPtr) *LastPtr = p; + if (stricmp(Key, p->Keyword) == 0) + return true; + } + + return false; +} + + + +/* Add a property into a linked list */ +static +BOOL AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char* Value) +{ + LPKEYVALUE p; + LPKEYVALUE last; + + + /* Check if property is already in list (this is an error) */ + + if (IsAvailableOnList(*Head, Key, &last)) { + cmsSignalError(LCMS_ERRC_ABORTED, "duplicate key <%s>", Key); + return false; + } + + /* Allocate the container */ + p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE)); + if (p == NULL) + { + cmsSignalError(LCMS_ERRC_ABORTED, "AddToList: out of memory"); + return false; + } + + /* Store name and value */ + p->Keyword = AllocString(it8, Key); + + if (Value) + p->Value = AllocString(it8, Value); + else + p->Value = NULL; + + p->Next = NULL; + + /* Keep the container in our list */ + if (*Head == NULL) + *Head = p; + else + last->Next = p; + + return true; +} + +static +BOOL AddAvailableProperty(LPIT8 it8, const char* Key) +{ + return AddToList(it8, &it8->ValidKeywords, Key, NULL); +} + + +static +BOOL AddAvailableSampleID(LPIT8 it8, const char* Key) +{ + return AddToList(it8, &it8->ValidSampleID, Key, NULL); +} + + + +/* Init an empty container */ +LCMSHANDLE cmsxIT8Alloc(void) +{ + LPIT8 it8; + int i; + + it8 = (LPIT8) malloc(sizeof(IT8)); + if (it8 == NULL) return NULL; + + ZeroMemory(it8, sizeof(IT8)); + + it8->HeaderList = NULL; + it8->FileBuffer = NULL; + it8->DataFormat = NULL; + it8->Data = NULL; + it8->MemorySink = NULL; + it8->ValidKeywords = NULL; + it8->ValidSampleID = NULL; + + it8 -> sy = SNONE; + it8 -> ch = ' '; + it8 -> Source = NULL; + it8 -> inum = 0; + it8 -> dnum = 0.0; + + it8 -> lineno = 1; + + strcpy(it8->SheetType, "IT8.7/2"); + + /* Initialize predefined properties & data */ + + for (i=0; i < NUMPREDEFINEDPROPS; i++) + AddAvailableProperty(it8, PredefinedProperties[i]); + + for (i=0; i < NUMPREDEFINEDSAMPLEID; i++) + AddAvailableSampleID(it8, PredefinedSampleID[i]); + + + return (LCMSHANDLE) it8; +} + + +const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8) +{ + LPIT8 it8 = (LPIT8) hIT8; + + return it8 ->SheetType; + +} + +BOOL cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type) +{ + LPIT8 it8 = (LPIT8) hIT8; + + strncpy(it8 ->SheetType, Type, MAXSTR-1); + return true; +} + + + +/* Sets a property */ +BOOL cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* Key, const char *Val) +{ + LPIT8 it8 = (LPIT8) hIT8; + + if (!Val) return false; + if (!*Val) return false; + + return AddToList(it8, &it8 -> HeaderList, Key, Val); +} + + +BOOL cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val) +{ + char Buffer[256]; + + sprintf(Buffer, "%g", Val); + return cmsxIT8SetProperty(hIT8, cProp, Buffer); +} + +/* Gets a property */ +const char* cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* Key) +{ + LPIT8 it8 = (LPIT8) hIT8; + LPKEYVALUE p; + + if (IsAvailableOnList(it8 -> HeaderList, Key, &p)) + { + return p -> Value; + } + return NULL; +} + + +double cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp) +{ + const char *v = cmsxIT8GetProperty(hIT8, cProp); + if (v) return atof(v); + else return 0.0; +} + +/* ----------------------------------------------------------------- Datasets */ + + +static +void AllocateDataFormat(LPIT8 it8) +{ + if (it8 -> DataFormat) return; /* Already allocated */ + + it8 -> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + + if (it8 -> nSamples <= 0) { + + cmsSignalError(LCMS_ERRC_WARNING, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS, assuming 10"); + it8 -> nSamples = 10; + } + + it8 -> DataFormat = (char**) AllocChunk (it8, (it8->nSamples + 1) * sizeof(char *)); + if (it8->DataFormat == NULL) + { + cmsSignalError(LCMS_ERRC_ABORTED, "AllocateDataFormat: Unable to allocate dataFormat array"); + } + +} + +static +const char *GetDataFormat(LPIT8 it8, int n) +{ + if (it8->DataFormat) + return it8->DataFormat[n]; + + return NULL; +} + +static +BOOL SetDataFormat(LPIT8 it8, int n, const char *label) +{ + if (n > it8 -> nSamples) return false; + + if (!it8->DataFormat) + AllocateDataFormat(it8); + + if (it8->DataFormat) { + + it8->DataFormat[n] = AllocString(it8, label); + } + + return true; +} + + +BOOL cmsxIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample) +{ + LPIT8 it8 = (LPIT8) h; + return SetDataFormat(it8, n, Sample); +} + +static +void AllocateDataSet(LPIT8 it8) +{ + if (it8 -> Data) return; /* Already allocated */ + + it8-> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + it8-> nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS")); + it8-> Data = (char**)AllocChunk (it8, (it8->nSamples + 1) * (it8->nPatches + 1) *sizeof (char*)); + if (it8->Data == NULL) + { + cmsSignalError(-1, "AllocateDataSet: Unable to allocate data array"); + } + +} + +static +char* GetData(LPIT8 it8, int nSet, int nField) +{ + int nSamples = it8 -> nSamples; + int nPatches = it8 -> nPatches; + + if (nSet >= nPatches || nField >= nSamples) + return NULL; + + if (!it8->Data) return NULL; + return it8->Data [nSet * nSamples + nField]; +} + +static +BOOL SetData(LPIT8 it8, int nSet, int nField, char *Val) +{ + if (!it8->Data) + AllocateDataSet(it8); + + if (!it8->Data) return false; + + + if (nSet > it8 -> nPatches) { + + SynError(it8, "Patch %d out of range, there are %d datasets", nSet, it8 -> nPatches); + return false; + } + + if (nField > it8 ->nSamples) { + SynError(it8, "Sample %d out of range, there are %d datasets", nField, it8 ->nSamples); + return false; + } + + + it8->Data [nSet * it8 -> nSamples + nField] = AllocString(it8, Val); + return true; +} + + +/* --------------------------------------------------------------- File I/O */ + + +/* Writes a string to file */ +static +void WriteStr(FILE *f, char *str) +{ + if (str == NULL) + fwrite(" ", 1, 1, f); + else + fwrite(str, 1, strlen(str), f); +} + + +/* Writes full header */ +static +void WriteHeader(LPIT8 it8, FILE *fp) +{ + LPKEYVALUE p; + + WriteStr(fp, it8->SheetType); + WriteStr(fp, "\n"); + for (p = it8->HeaderList; (p != NULL); p = p->Next) + { + if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL)) { + + WriteStr(fp, "KEYWORD\t\""); + WriteStr(fp, p->Keyword); + WriteStr(fp, "\"\n"); + + } + + WriteStr(fp, p->Keyword); + if (p->Value) { + + WriteStr(fp, "\t\""); + WriteStr(fp, p->Value); + WriteStr(fp, "\""); + } + WriteStr (fp, "\n"); + } + +} + + +/* Writes the data format */ +static +void WriteDataFormat(FILE *fp, LPIT8 it8) +{ + int i, nSamples; + + if (!it8 -> DataFormat) return; + + WriteStr(fp, "BEGIN_DATA_FORMAT\n"); + nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + + for (i = 0; i < nSamples; i++) { + + WriteStr(fp, it8->DataFormat[i]); + WriteStr(fp, (char*)((i == (nSamples-1)) ? "\n" : "\t")); // C->C++ : cast + } + + WriteStr (fp, "END_DATA_FORMAT\n"); +} + + +/* Writes data array */ +static +void WriteData(FILE *fp, LPIT8 it8) +{ + int i, j; + + if (!it8->Data) return; + + WriteStr (fp, "BEGIN_DATA\n"); + + it8->nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS")); + + for (i = 0; i < it8-> nPatches; i++) { + + for (j = 0; j < it8->nSamples; j++) { + + char *ptr = it8->Data[i*it8->nSamples+j]; + + WriteStr(fp, (char*)((ptr == NULL) ? "0.00" : ptr)); // C->C++ : cast + WriteStr(fp, (char*)((j == (it8->nSamples-1)) ? "\n" : "\t")); // C->C++ : cast + } + } + WriteStr (fp, "END_DATA\n"); +} + + + +/* Saves whole file */ +BOOL cmsxIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName) +{ + FILE *fp; + LPIT8 it8 = (LPIT8) hIT8; + + fp = fopen(cFileName, "wt"); + if (!fp) return false; + WriteHeader(it8, fp); + WriteDataFormat(fp, it8); + WriteData(fp, it8); + fclose(fp); + + return true; +} + + +/* Reads whole file in a memory block */ +static +BOOL ReadFileInMemory(const char *cFileName, char **Buffer, size_t *Len) +{ + FILE *fp; + size_t Size; + char *Ptr; + + fp = fopen(cFileName, "rt"); + if (!fp) return false; + + Size = xfilelength(fileno(fp)); + if (Size <= 0) { + fclose(fp); + return false; + } + + Ptr = (char*)malloc(Size+1); // C->C++ : cast + + Size = fread(Ptr, 1, Size, fp); + fclose(fp); + Ptr[Size] = '\0'; + + *Buffer = Ptr; + *Len = Size; + return true; +} + + +/* -------------------------------------------------------------- Higer lever parsing */ + +static +BOOL DataFormatSection(LPIT8 it8) +{ + int iField = 0; + BOOL Ignoring = false; + + InSymbol(it8); /* Eats "BEGIN_DATA_FORMAT" */ + CheckEOLN(it8); + + while (it8->sy != SEND_DATA_FORMAT && + it8->sy != SEOLN && + it8->sy != SEOF && + it8->sy != SSYNERROR) + { + + if (it8->sy != SIDENT) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Sample type expected"); + it8->sy = SSYNERROR; + return false; + } + + if (!Ignoring && iField > it8->nSamples) { + cmsSignalError(LCMS_ERRC_WARNING, "More than NUMBER_OF_FIELDS fields. Extra is ignored\n"); + Ignoring = true; + } + else { + if (!SetDataFormat(it8, iField, it8->id)) return false; + iField++; + } + + InSymbol(it8); + Skip(it8, SEOLN); + } + + Skip(it8, SEOLN); + Skip(it8, SEND_DATA_FORMAT); + Skip(it8, SEOLN); + return true; +} + + + +static +BOOL DataSection (LPIT8 it8) +{ + int iField = 0; + int iSet = 0; + char Buffer[256]; + + InSymbol(it8); /* Eats "BEGIN_DATA" */ + CheckEOLN(it8); + + while (it8->sy != SEND_DATA && it8->sy != SEOF) + { + if (iField >= it8 -> nSamples) { + iField = 0; + iSet++; + if (!CheckEOLN(it8)) + return false; + } + + if (it8->sy != SEND_DATA && it8->sy != SEOF) { + + if (!GetVal(it8, Buffer)) + return false; + + if (!SetData(it8, iSet, iField, Buffer)) + return false; + + iField++; + + Skip(it8, SEOLN); + InSymbol(it8); + } + } + + Skip(it8, SEOLN); + Skip(it8, SEND_DATA); + Skip(it8, SEOLN); + return true; +} + + + + + + +static +BOOL HeaderSection (LPIT8 it8) +{ + char VarName[MAXID]; + char Buffer[MAXSTR]; + + while (it8->sy != SEOF && + it8->sy != SSYNERROR && + it8->sy != SBEGIN_DATA_FORMAT && + it8->sy != SBEGIN_DATA) { + + + switch (it8 -> sy) { + + case SKEYWORD: + InSymbol(it8); + if (!Check(it8, SSTRING, "Keyword expected")) return false; + if (!AddAvailableProperty(it8, it8 -> str)) return false; + InSymbol(it8); + break; + + + case SIDENT: + strncpy(VarName, it8->id, MAXID-1); + if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL)) + return SynError(it8, "Undefined keyword '%s'", VarName); + + InSymbol(it8); + GetVal(it8, Buffer); + cmsxIT8SetProperty((LCMSHANDLE) it8, VarName, Buffer); + InSymbol(it8); + break; + + + case SEOLN: break; + + default: + return SynError(it8, "expected keyword or identifier"); + } + + Skip(it8, SEOLN); + } + + return true; + +} + + +static +BOOL ParseIT8(LPIT8 it8) +{ + + InSymbol(it8); + + if (it8->sy == SIDENT) { + + strncpy(it8->SheetType, it8->id, MAXSTR-1); + InSymbol(it8); + + /* if (!AddAvailableProperty(it8, it8 -> id)) return false; */ + /* cmsxIT8SetProperty((LCMSHANDLE) it8, it8->id, NULL); */ + } + + Skip(it8, SEOLN); + + while (it8-> sy != SEOF && + it8-> sy != SSYNERROR) { + + switch (it8 -> sy) { + + case SBEGIN_DATA_FORMAT: + if (!DataFormatSection(it8)) return false; + break; + + case SBEGIN_DATA: + if (!DataSection(it8)) return false; + break; + + case SEOLN: + Skip(it8, SEOLN); + break; + + default: + if (!HeaderSection(it8)) return false; + } + + } + + return true; +} + + +static +void CleanPatchName(char *cell) +{ + char cleaned[256], Buffer[256], ident[256]; + char *orig = cell, *id; + int n, lOneNum; + + + id = ident; + while (*cell && isalpha(*cell)) + { + *id++ = (char) toupper(*cell); + cell++; + } + *id = 0; + strcpy(cleaned, ident); + + + n = 0; + lOneNum = false; + while (*cell && isdigit(*cell)) + { + n = n * 10 + (*cell -'0'); + cell++; + lOneNum = true; + } + + if (lOneNum) { + + sprintf(Buffer, "%d", n); + strcat(cleaned, Buffer); + } + + if (strcmp(cleaned, "GS0") == 0) + strcpy(orig, "DMIN"); + else + if (strcmp(cleaned, "GS23") == 0) + strcpy(orig, "DMAX"); + else + strcpy(orig, cleaned); +} + + +/* Init useful pointers */ + +static +void CookPointers(LPIT8 it8) +{ + int idField, i; + char* Fld; + + it8 -> SampleID = 0; + for (idField = 0; idField < it8 -> nSamples; idField++) + { + Fld = it8->DataFormat[idField]; + if (!Fld) continue; + + if (strcmp(Fld, "SAMPLE_ID") == 0) { + it8 -> SampleID = idField; + + + for (i=0; i < it8 -> nPatches; i++) { + + char *Data = GetData(it8, i, idField); + if (Data) { + char Buffer[256]; + + strncpy(Buffer, Data, 255); + CleanPatchName(Buffer); + + if (strlen(Buffer) <= strlen(Data)) + strcpy(Data, Buffer); + else + SetData(it8, i, idField, Buffer); + + } + } + + } + } + +} + + +/* ---------------------------------------------------------- Exported routines */ + + +LCMSHANDLE cmsxIT8LoadFromMem(void *Ptr, size_t len) +{ + LCMSHANDLE hIT8 = cmsxIT8Alloc(); + LPIT8 it8 = (LPIT8) hIT8; + + if (!hIT8) return NULL; + it8 ->FileBuffer = (char*) malloc(len + 1); + + strncpy(it8 ->FileBuffer, (const char*)Ptr, len); // C->C++ : cast + strncpy(it8->FileName, "", MAX_PATH-1); + it8-> Source = it8 -> FileBuffer; + + ParseIT8(it8); + CookPointers(it8); + + free(it8->FileBuffer); + it8 -> FileBuffer = NULL; + + return hIT8; + + +} + + +LCMSHANDLE cmsxIT8LoadFromFile(const char* cFileName) +{ + + LCMSHANDLE hIT8 = cmsxIT8Alloc(); + LPIT8 it8 = (LPIT8) hIT8; + size_t Len; + + if (!hIT8) return NULL; + if (!ReadFileInMemory(cFileName, &it8->FileBuffer, &Len)) return NULL; + + strncpy(it8->FileName, cFileName, MAX_PATH-1); + it8-> Source = it8 -> FileBuffer; + + ParseIT8(it8); + CookPointers(it8); + + free(it8->FileBuffer); + it8 -> FileBuffer = NULL; + + return hIT8; + +} + +int cmsxIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames) +{ + LPIT8 it8 = (LPIT8) hIT8; + + *SampleNames = it8 -> DataFormat; + return it8 -> nSamples; +} + + +int cmsxIT8EnumProperties(LCMSHANDLE hIT8, char ***PropertyNames) +{ + LPIT8 it8 = (LPIT8) hIT8; + LPKEYVALUE p; + int n; + char **Props; + + /* Pass#1 - count properties */ + + n = 0; + for (p = it8 -> HeaderList; p != NULL; p = p->Next) { + n++; + } + + + Props = (char **) malloc(sizeof(char *) * n); + + /* Pass#2 - Fill pointers */ + n = 0; + for (p = it8 -> HeaderList; p != NULL; p = p->Next) { + Props[n++] = p -> Keyword; + } + + *PropertyNames = Props; + return n; +} + +static +int LocatePatch(LPIT8 it8, const char* cPatch) +{ + int i; + const char *data; + + for (i=0; i < it8-> nPatches; i++) { + + data = GetData(it8, i, it8->SampleID); + + if (data != NULL) { + + if (stricmp(data, cPatch) == 0) + return i; + } + } + + return -1; +} + + +static +int LocateEmptyPatch(LPIT8 it8, const char* cPatch) +{ + int i; + const char *data; + + for (i=0; i < it8-> nPatches; i++) { + + data = GetData(it8, i, it8->SampleID); + + if (data == NULL) + return i; + + } + + return -1; +} + +static +int LocateSample(LPIT8 it8, const char* cSample) +{ + int i; + const char *fld; + + for (i=0; i < it8->nSamples; i++) { + + fld = GetDataFormat(it8, i); + if (stricmp(fld, cSample) == 0) + return i; + } + + return -1; +} + + +BOOL cmsxIT8GetDataSetByPos(LCMSHANDLE hIT8, int col, int row, char* Val, int ValBufferLen) +{ + LPIT8 it8 = (LPIT8) hIT8; + const char *data = GetData(it8, row, col); + + if (!data) + { + *Val = '\0'; + return false; + } + + strncpy(Val, data, ValBufferLen-1); + return true; +} + + + +BOOL cmsxIT8GetDataSet(LCMSHANDLE hIT8, const char* cPatch, + const char* cSample, + char* Val, int ValBuffLen) +{ + LPIT8 it8 = (LPIT8) hIT8; + int iField, iSet; + + + iField = LocateSample(it8, cSample); + if (iField < 0) { + /* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample); */ + return false; + } + + + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + + /* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch); */ + return false; + } + + strncpy(Val, GetData(it8, iSet, iField), ValBuffLen-1); + return true; +} + + +BOOL cmsxIT8GetDataSetDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample, double* v) +{ + char Buffer[20]; + + if (cmsxIT8GetDataSet(it8, cPatch, cSample, Buffer, 20)) { + + *v = atof(Buffer); + return true; + } else + return false; +} + + + +BOOL cmsxIT8SetDataSet(LCMSHANDLE hIT8, const char* cPatch, + const char* cSample, + char *Val) +{ + LPIT8 it8 = (LPIT8) hIT8; + int iField, iSet; + + + iField = LocateSample(it8, cSample); + + if (iField < 0) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample); + return false; + } + + + if (it8-> nPatches == 0) { + + AllocateDataFormat(it8); + AllocateDataSet(it8); + CookPointers(it8); + } + + + if (stricmp(cSample, "SAMPLE_ID") == 0) + { + + iSet = LocateEmptyPatch(it8, cPatch); + if (iSet < 0) { + cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't add more patches '%s'\n", cPatch); + return false; + } + iField = it8 -> SampleID; + } + else { + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + + cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch); + return false; + } + } + + return SetData(it8, iSet, iField, Val); +} + + +BOOL cmsxIT8SetDataSetDbl(LCMSHANDLE hIT8, const char* cPatch, + const char* cSample, + double Val) +{ + char Buff[256]; + + sprintf(Buff, "%g", Val); + return cmsxIT8SetDataSet(hIT8, cPatch, cSample, Buff); + +} + + +const char* cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer) +{ + LPIT8 it8 = (LPIT8) hIT8; + char* Data = GetData(it8, nPatch, it8->SampleID); + if (!Data) return NULL; + + strcpy(buffer, Data); + return buffer; +} + + +const char* cmsxIT8GenericPatchName(int nPatch, char* buffer) +{ + int row, col; + + if (nPatch >= cmsxIT8_NORMAL_PATCHES) + return "$CUSTOM"; + + if (nPatch >= (cmsxIT8_ROWS * cmsxIT8_COLS)) { + + nPatch -= cmsxIT8_ROWS * cmsxIT8_COLS; + if (nPatch == 0) + return "DMIN"; + else + if (nPatch == cmsxIT8_GRAYCOLS - 1) + return "DMAX"; + else + sprintf(buffer, "GS%d", nPatch); + return buffer; + } + + + row = nPatch / cmsxIT8_COLS; + col = nPatch % cmsxIT8_COLS; + + sprintf (buffer, "%c%d", 'A'+row, col+1); + return buffer; +} + + + + +int cmsxIT8GenericPatchNum(const char *name) +{ + int i; + char Buff[256]; + + + for (i=0; i < cmsxIT8_TOTAL_PATCHES; i++) + if (stricmp(cmsxIT8GenericPatchName(i, Buff), name) == 0) + return i; + + return -1; +} + + + + + diff --git a/src/libs/lprof/lcmsprf.h b/src/libs/lprof/lcmsprf.h new file mode 100644 index 00000000..00c7ac40 --- /dev/null +++ b/src/libs/lprof/lcmsprf.h @@ -0,0 +1,485 @@ +/* +Little cms - profiler construction set +Copyright (C) 1998-2001 Marti Maria + +THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF +LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +As a special exception to the GNU General Public License, if you +distribute this file as part of a program that contains a +configuration script generated by Autoconf, you may include it under +the same distribution terms that you use for the rest of that program. +*/ + +/* Version 1.09a */ + +#ifndef __cmsprf_H + +#include +#include LCMS_HEADER + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef NON_WINDOWS +# ifndef stricmp +# define stricmp strcasecmp +# endif +#endif + +#ifndef max +#define max(a,b) ((a) > (b)?(a):(b)) +#endif + + +/* Matrix operations - arbitrary size ----------------------------------------------------- */ + +typedef struct { + + int Cols, Rows; + double** Values; + + } MATN,FAR* LPMATN; + +// See B.K.O #148930: compile with lcms v.1.17 +#if (LCMS_VERSION > 116) +typedef LCMSBOOL BOOL; +#endif + +LPMATN cdecl MATNalloc(int Rows, int Cols); +void cdecl MATNfree (LPMATN mat); +LPMATN cdecl MATNmult(LPMATN a1, LPMATN a2); +double cdecl MATNcross(LPMATN a); +void cdecl MATNscalar (LPMATN a, double scl, LPMATN b); +LPMATN cdecl MATNtranspose (LPMATN a); +BOOL cdecl MATNsolve(LPMATN a, LPMATN b); + + +/* IT8.7 / CGATS.17-200x handling -------------------------------------------------------- */ + +#define cmsxIT8_ROWS 12 +#define cmsxIT8_COLS 22 +#define cmsxIT8_GRAYCOLS 24 +#define cmsxIT8_NORMAL_PATCHES (cmsxIT8_ROWS*cmsxIT8_COLS + cmsxIT8_GRAYCOLS) +#define cmsxIT8_CUSTOM_PATCHES 10 +#define cmsxIT8_TOTAL_PATCHES (cmsxIT8_NORMAL_PATCHES + cmsxIT8_CUSTOM_PATCHES) + + +LCMSHANDLE cdecl cmsxIT8Alloc(void); +void cdecl cmsxIT8Free(LCMSHANDLE cmsxIT8); +LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName); +LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len); +BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE cmsxIT8, const char* cFileName); +const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8); +BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type); +const char* cdecl cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer); +BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hcmsxIT8, const char* cProp, const char *Str); +BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hcmsxIT8, const char* cProp, double Val); +const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hcmsxIT8, const char* cProp); +double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hcmsxIT8, const char* cProp); +int cdecl cmsxIT8EnumProperties(LCMSHANDLE cmsxIT8, char ***PropertyNames); +int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE cmsxIT8, char ***SampleNames); +BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE cmsxIT8, int n, const char *Sample); + +BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen); + +BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE cmsxIT8, const char* cPatch, + const char* cSample, + char* Val, int ValBuffLen); + +BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE cmsxIT8, const char* cPatch, const char* cSample, double* v); + +BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE cmsxIT8, const char* cPatch, + const char* cSample, + char *Val); + +BOOL cdecl cmsxIT8SetDataSetDbl(LCMSHANDLE cmsxIT8, const char* cPatch, const char* cSample, double Val); + +const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer); + + + +/* Patch collections (measurement lists) -------------------------------------------------- */ + +#define PATCH_HAS_Lab 0x00000001 +#define PATCH_HAS_XYZ 0x00000002 +#define PATCH_HAS_RGB 0x00000004 +#define PATCH_HAS_CMY 0x00000008 +#define PATCH_HAS_CMYK 0x00000010 +#define PATCH_HAS_HEXACRM 0x00000020 +#define PATCH_HAS_STD_Lab 0x00010000 +#define PATCH_HAS_STD_XYZ 0x00020000 +#define PATCH_HAS_XYZ_PROOF 0x00100000 +#define PATCH_HAS_MEAN_DE 0x01000000 +#define PATCH_HAS_STD_DE 0x02000000 +#define PATCH_HAS_CHISQ 0x04000000 + + +#define MAXPATCHNAMELEN 20 +/* A patch in memory */ + +typedef struct { + + DWORD dwFlags; /* Is quite possible to have colorant in only */ + /* some patches of sheet, so mark each entry with */ + /* the values it has. */ + + char Name[MAXPATCHNAMELEN]; + + cmsCIELab Lab; /* The tristimulus values of target */ + cmsCIEXYZ XYZ; + + cmsCIEXYZ XYZProof; /* The absolute XYZ value returned by profile */ + /* (gamut constrained to device) */ + + union { /* The possible colorants. Only one space is */ + /* allowed...obviously only one set of */ + /* device-dependent values per patch does make sense. */ + double RGB[3]; + double CMY[3]; + double CMYK[4]; + double Hexa[MAXCHANNELS]; + + } Colorant; + + double dEStd; /* Standard deviation */ + double ChiSq; /* Chi-square parameter (mean of STD of colorants) */ + double dEMean; /* Mean dE */ + + } PATCH, FAR* LPPATCH; + + + +/* A set of patches is simply an array of bools, TRUE if the patch */ +/* belong to the set, false otherwise. */ + +typedef BOOL* SETOFPATCHES; + +/* This struct holds whole Patches collection */ + +typedef struct _measurement { + + int nPatches; + LPPATCH Patches; + SETOFPATCHES Allowed; + + } MEASUREMENT,FAR *LPMEASUREMENT; + + +void cdecl cmsxPCollFreeMeasurements(LPMEASUREMENT m); +SETOFPATCHES cdecl cmsxPCollBuildSet(LPMEASUREMENT m, BOOL lDefault); + +BOOL cdecl cmsxPCollBuildMeasurement(LPMEASUREMENT m, + const char *ReferenceSheet, + const char *MeasurementSheet, + DWORD dwNeededSamplesType); + +int cdecl cmsxPCollCountSet(LPMEASUREMENT m, SETOFPATCHES Set); +BOOL cdecl cmsxPCollValidatePatches(LPMEASUREMENT m, DWORD dwFlags); + +BOOL cdecl cmsxPCollLoadFromSheet(LPMEASUREMENT m, LCMSHANDLE hSheet); +BOOL cdecl cmsxPCollSaveToSheet(LPMEASUREMENT m, LCMSHANDLE it8); + +LPPATCH cdecl cmsxPCollGetPatch(LPMEASUREMENT m, int n); +LPPATCH cdecl cmsxPCollGetPatchByName(LPMEASUREMENT m, const char* Name, int* lpPos); +LPPATCH cdecl cmsxPCollGetPatchByPos(LPMEASUREMENT m, int row, int col); +LPPATCH cdecl cmsxPCollAddPatchRGB(LPMEASUREMENT m, const char *Name, + double r, double g, double b, + LPcmsCIEXYZ XYZ, LPcmsCIELab Lab); + +void cdecl cmsxPCollLinearizePatches(LPMEASUREMENT m, SETOFPATCHES Valids, + LPGAMMATABLE Gamma[3]); + +/* Extraction utilities */ + +/* Collect "need" patches of the specific kind, return the number of collected (that */ +/* could be less if set of patches is exhausted) */ + +void cdecl cmsxPCollPatchesGS(LPMEASUREMENT m, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearRGB(LPMEASUREMENT m, SETOFPATCHES Valids, + double r, double g, double b, int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearNeutral(LPMEASUREMENT m, SETOFPATCHES Valids, + int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesNearPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, + int nChannel, int need, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesInLabCube(LPMEASUREMENT m, SETOFPATCHES Valids, + double Lmin, double LMax, double a, double b, SETOFPATCHES Result); + +int cdecl cmsxPCollPatchesInGamutLUT(LPMEASUREMENT m, SETOFPATCHES Valids, + LPLUT Gamut, SETOFPATCHES Result); + +/* Find important values */ + +LPPATCH cdecl cmsxPCollFindWhite(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance); +LPPATCH cdecl cmsxPCollFindBlack(LPMEASUREMENT m, SETOFPATCHES Valids, double* Distance); +LPPATCH cdecl cmsxPCollFindPrimary(LPMEASUREMENT m, SETOFPATCHES Valids, int Channel, double* Distance); + +/* Multiple linear regression stuff ---------------------------------------- */ + + +/* A measurement of error */ + +typedef struct { + + double SSE; /* The error sum of squares */ + double MSE; /* The error mean sum of squares */ + double SSR; /* The regression sum of squares */ + double MSR; /* The regression mean sum of squares */ + double SSTO; /* Total sum of squares */ + double F; /* The Fisher-F value (MSR / MSE) */ + double R2; /* Proportion of variability explained by the regression */ + /* (root is Pearson correlation coefficient) */ + + double R2adj; /* The adjusted coefficient of multiple determination. */ + /* R2-adjusted or R2adj. This is calculated as */ + /* R2adj = 1 - (1-R2)(N-n-1)/(N-1) */ + /* and used as multiple correlation coefficient */ + /* (really, it should be square root) */ + + } MLRSTATISTICS, FAR* LPMLRSTATISTICS; + + +int cdecl cmsxRegressionCreateMatrix(LPMEASUREMENT m, SETOFPATCHES Allowed, int nterms, + int ColorSpace, + LPMATN* lpMat, LPMLRSTATISTICS Stat); + +BOOL cdecl cmsxRegressionRGB2Lab(double r, double g, double b, + LPMATN tfm, LPcmsCIELab Lab); + +BOOL cdecl cmsxRegressionRGB2XYZ(double r, double g, double b, + LPMATN tfm, LPcmsCIEXYZ XYZ); + +BOOL cdecl cmsxRegressionInterpolatorRGB(LPMEASUREMENT m, + int ColorSpace, + int RegressionTerms, + BOOL lUseLocalPatches, + int MinPatchesToCollect, + double r, double g, double b, + void* Res); + + + +/* Levenberg-Marquardt ---------------------------------------------------------------------- */ + +LCMSHANDLE cdecl cmsxLevenbergMarquardtInit(LPSAMPLEDCURVE x, LPSAMPLEDCURVE y, double sig, + double a[], + int ma, + void (*funcs)(double, double[], double*, double[], int) + ); + +double cdecl cmsxLevenbergMarquardtAlamda(LCMSHANDLE hMRQ); +double cdecl cmsxLevenbergMarquardtChiSq(LCMSHANDLE hMRQ); +BOOL cdecl cmsxLevenbergMarquardtIterate(LCMSHANDLE hMRQ); +BOOL cdecl cmsxLevenbergMarquardtFree(LCMSHANDLE hMRQ); + + +/* Convex hull geometric routines ------------------------------------------------------------ */ + +LCMSHANDLE cdecl cmsxHullInit(void); +void cdecl cmsxHullDone(LCMSHANDLE hHull); +BOOL cdecl cmsxHullAddPoint(LCMSHANDLE hHull, int x, int y, int z); +BOOL cdecl cmsxHullComputeHull(LCMSHANDLE hHull); +char cdecl cmsxHullCheckpoint(LCMSHANDLE hHull, int x, int y, int z); +BOOL cdecl cmsxHullDumpVRML(LCMSHANDLE hHull, const char* fname); + + +/* Linearization ---------------------------------------------------------------------------- */ + + +#define MEDIUM_REFLECTIVE_D50 0 /* Used for scanner targets */ +#define MEDIUM_TRANSMISSIVE 1 /* Used for monitors & projectors */ + +void cdecl cmsxComputeLinearizationTables(LPMEASUREMENT m, + int ColorSpace, + LPGAMMATABLE Lin[3], + int nResultingPoints, + int Medium); + + +void cdecl cmsxCompleteLabOfPatches(LPMEASUREMENT m, SETOFPATCHES Valids, int Medium); +LPGAMMATABLE cdecl cmsxEstimateGamma(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints); + +void cdecl cmsxApplyLinearizationTable(double In[3], LPGAMMATABLE Gamma[3], double Out[3]); +void cdecl cmsxApplyLinearizationGamma(WORD In[3], LPGAMMATABLE Gamma[3], WORD Out[3]); + +/* Support routines ---------------------------------------------------------------------- */ + +double cdecl _cmsxSaturate65535To255(double d); +double cdecl _cmsxSaturate255To65535(double d); +void cdecl _cmsxClampXYZ100(LPcmsCIEXYZ xyz); + +/* Matrix shaper profiler API ------------------------------------------------------------- */ + + +BOOL cdecl cmsxComputeMatrixShaper(const char* ReferenceSheet, + const char* MeasurementSheet, + int Medium, + LPGAMMATABLE TransferCurves[3], + LPcmsCIEXYZ WhitePoint, + LPcmsCIEXYZ BlackPoint, + LPcmsCIExyYTRIPLE Primaries); + + +/* Common to all profilers ------------------------------------------------------------------- */ + +#define MAX_STR 256 + +typedef int (* cmsxGAUGER)(const char *Label, int nMin, int nMax, int Pos); +typedef int (* cmsxPRINTF)(const char *Frm, ...); + +typedef struct { + + /* Files */ + char ReferenceSheet[MAX_PATH]; + char MeasurementSheet[MAX_PATH]; + char OutputProfileFile[MAX_PATH]; + + /* Some infos */ + char Description[MAX_STR]; + char Manufacturer[MAX_STR]; + char Model[MAX_STR]; + char Copyright[MAX_STR]; + + /* Callbacks */ + cmsxGAUGER Gauger; + cmsxPRINTF printf; + + /* EndPoints */ + cmsCIEXYZ WhitePoint; /* Black point in 0.xxx notation */ + cmsCIEXYZ BlackPoint; /* Black point in 0.xxx notation */ + cmsCIExyYTRIPLE Primaries; /* The primaries */ + LPGAMMATABLE Gamma[3]; /* Gamma curves */ + + /* Profile */ + cmsHPROFILE hProfile; /* handle to profile */ + + icProfileClassSignature DeviceClass; + icColorSpaceSignature ColorSpace; + + int PCSType; /* PT_XYZ or PT_Lab */ + int CLUTPoints; /* Final CLUT resolution */ + int ProfileVerbosityLevel; /* 0=minimum, 1=additional, 2=Verbose, 3=Any suitable */ + + + /* Measurement */ + MEASUREMENT m; /* Contains list of available patches */ + int Medium; + + + /* RGB Gamut hull */ + LCMSHANDLE hRGBHull; /* Contains bobbin of valid RGB values */ + + /* CIECAM97s */ + BOOL lUseCIECAM97s; /* Use CIECAM97s for chromatic adaptation? */ + + cmsViewingConditions device; /* Viewing condition of source */ + cmsViewingConditions PCS; /* Viewing condition of PCS */ + + LCMSHANDLE hDevice; /* CIECAM97s models used for adaptation */ + LCMSHANDLE hPCS; /* and viewing conditions */ + + + } PROFILERCOMMONDATA,FAR* LPPROFILERCOMMONDATA; + + +/* Shared routines */ + +BOOL cdecl cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr); +BOOL cdecl cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr); +BOOL cdecl cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr); + +int cdecl cmsxFindOptimumNumOfTerms(LPPROFILERCOMMONDATA hdr, int nMaxTerms, BOOL* lAllOk); +void cdecl cmsxChromaticAdaptationAndNormalization(LPPROFILERCOMMONDATA hdr, LPcmsCIEXYZ xyz, BOOL lReverse); +void cdecl cmsxInitPCSViewingConditions(LPPROFILERCOMMONDATA hdr); +void cdecl cmsxComputeGamutHull(LPPROFILERCOMMONDATA hdr); +BOOL cdecl cmsxChoosePCS(LPPROFILERCOMMONDATA hdr); + +/* Monitor profiler API ------------------------------------------------------------------- */ + +typedef struct { + + PROFILERCOMMONDATA hdr; + + + LPGAMMATABLE Prelinearization[3]; /* Canonic gamma */ + LPGAMMATABLE ReverseTables[3]; /* Reverse (direct) gamma */ + LPGAMMATABLE PreLab[3]; + LPGAMMATABLE PreLabRev[3]; + + + MAT3 PrimariesMatrix; + MAT3 PrimariesMatrixRev; + + + } MONITORPROFILERDATA,FAR* LPMONITORPROFILERDATA; + + + +BOOL cdecl cmsxMonitorProfilerInit(LPMONITORPROFILERDATA sys); +BOOL cdecl cmsxMonitorProfilerDo(LPMONITORPROFILERDATA sys); + + +/* Scanner profiler API ------------------------------------------------------------------- */ + + +typedef struct { + + PROFILERCOMMONDATA hdr; + + LPGAMMATABLE Prelinearization[3]; + + LPMATN HiTerms; /* Regression matrix of many terms */ + LPMATN LoTerms; /* Low order regression matrix used for extrapolation */ + + BOOL lLocalConvergenceExtrapolation; + + + } SCANNERPROFILERDATA,FAR* LPSCANNERPROFILERDATA; + + + + +BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys); +BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys); + +/* ----------------------------------------------------------- end of profilers */ + + +#ifdef __cplusplus +} +#endif + +#define __cmsprf_H +#endif diff --git a/src/libs/sqlite2/Makefile.am b/src/libs/sqlite2/Makefile.am new file mode 100644 index 00000000..dca86fe0 --- /dev/null +++ b/src/libs/sqlite2/Makefile.am @@ -0,0 +1,43 @@ +#stolen Makefile.am from amarok + +noinst_LTLIBRARIES = libsqlite2.la + +INCLUDES = $(all_includes) + +libsqlite2_la_CFLAGS = -w + +libsqlite2_la_LDFLAGS = $(LIBPTHREAD) + +libsqlite2_la_SOURCES = \ + attach.c \ + auth.c \ + btree.c \ + btree_rb.c \ + build.c \ + copy.c \ + date.c \ + delete.c \ + encode.c \ + expr.c \ + func.c \ + hash.c \ + insert.c \ + main.c \ + opcodes.c \ + os.c \ + pager.c \ + parse.c \ + pragma.c \ + printf.c \ + random.c \ + select.c \ + shell.c \ + table.c \ + tokenize.c \ + trigger.c \ + update.c \ + util.c \ + vacuum.c \ + vdbe.c \ + vdbeaux.c \ + where.c diff --git a/src/libs/sqlite2/README b/src/libs/sqlite2/README new file mode 100644 index 00000000..eefbd49e --- /dev/null +++ b/src/libs/sqlite2/README @@ -0,0 +1,2 @@ +This folder contents sqlite version 2 source code used to backport old +digiKam database < 0.8.0 to new database based on sqlite version 3 \ No newline at end of file diff --git a/src/libs/sqlite2/attach.c b/src/libs/sqlite2/attach.c new file mode 100644 index 00000000..316d0d2a --- /dev/null +++ b/src/libs/sqlite2/attach.c @@ -0,0 +1,311 @@ +/* +** 2003 April 6 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the ATTACH and DETACH commands. +** +** $Id: attach.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" + +/* +** This routine is called by the parser to process an ATTACH statement: +** +** ATTACH DATABASE filename AS dbname +** +** The pFilename and pDbname arguments are the tokens that define the +** filename and dbname in the ATTACH statement. +*/ +void sqliteAttach(Parse *pParse, Token *pFilename, Token *pDbname, Token *pKey){ + Db *aNew; + int rc, i; + char *zFile, *zName; + sqlite *db; + Vdbe *v; + + v = sqliteGetVdbe(pParse); + sqliteVdbeAddOp(v, OP_Halt, 0, 0); + if( pParse->explain ) return; + db = pParse->db; + if( db->file_format<4 ){ + sqliteErrorMsg(pParse, "cannot attach auxiliary databases to an " + "older format master database", 0); + pParse->rc = SQLITE_ERROR; + return; + } + if( db->nDb>=MAX_ATTACHED+2 ){ + sqliteErrorMsg(pParse, "too many attached databases - max %d", + MAX_ATTACHED); + pParse->rc = SQLITE_ERROR; + return; + } + + zFile = 0; + sqliteSetNString(&zFile, pFilename->z, pFilename->n, 0); + if( zFile==0 ) return; + sqliteDequote(zFile); +#ifndef SQLITE_OMIT_AUTHORIZATION + if( sqliteAuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){ + sqliteFree(zFile); + return; + } +#endif /* SQLITE_OMIT_AUTHORIZATION */ + + zName = 0; + sqliteSetNString(&zName, pDbname->z, pDbname->n, 0); + if( zName==0 ) return; + sqliteDequote(zName); + for(i=0; inDb; i++){ + if( db->aDb[i].zName && sqliteStrICmp(db->aDb[i].zName, zName)==0 ){ + sqliteErrorMsg(pParse, "database %z is already in use", zName); + pParse->rc = SQLITE_ERROR; + sqliteFree(zFile); + return; + } + } + + if( db->aDb==db->aDbStatic ){ + aNew = sqliteMalloc( sizeof(db->aDb[0])*3 ); + if( aNew==0 ) return; + memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2); + }else{ + aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) ); + if( aNew==0 ) return; + } + db->aDb = aNew; + aNew = &db->aDb[db->nDb++]; + memset(aNew, 0, sizeof(*aNew)); + sqliteHashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1); + aNew->zName = zName; + rc = sqliteBtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt); + if( rc ){ + sqliteErrorMsg(pParse, "unable to open database: %s", zFile); + } +#if SQLITE_HAS_CODEC + { + extern int sqliteCodecAttach(sqlite*, int, void*, int); + char *zKey = 0; + int nKey; + if( pKey && pKey->z && pKey->n ){ + sqliteSetNString(&zKey, pKey->z, pKey->n, 0); + sqliteDequote(zKey); + nKey = strlen(zKey); + }else{ + zKey = 0; + nKey = 0; + } + sqliteCodecAttach(db, db->nDb-1, zKey, nKey); + } +#endif + sqliteFree(zFile); + db->flags &= ~SQLITE_Initialized; + if( pParse->nErr ) return; + if( rc==SQLITE_OK ){ + rc = sqliteInit(pParse->db, &pParse->zErrMsg); + } + if( rc ){ + int i = db->nDb - 1; + assert( i>=2 ); + if( db->aDb[i].pBt ){ + sqliteBtreeClose(db->aDb[i].pBt); + db->aDb[i].pBt = 0; + } + sqliteResetInternalSchema(db, 0); + pParse->nErr++; + pParse->rc = SQLITE_ERROR; + } +} + +/* +** This routine is called by the parser to process a DETACH statement: +** +** DETACH DATABASE dbname +** +** The pDbname argument is the name of the database in the DETACH statement. +*/ +void sqliteDetach(Parse *pParse, Token *pDbname){ + int i; + sqlite *db; + Vdbe *v; + Db *pDb; + + v = sqliteGetVdbe(pParse); + sqliteVdbeAddOp(v, OP_Halt, 0, 0); + if( pParse->explain ) return; + db = pParse->db; + for(i=0; inDb; i++){ + pDb = &db->aDb[i]; + if( pDb->pBt==0 || pDb->zName==0 ) continue; + if( strlen(pDb->zName)!=pDbname->n ) continue; + if( sqliteStrNICmp(pDb->zName, pDbname->z, pDbname->n)==0 ) break; + } + if( i>=db->nDb ){ + sqliteErrorMsg(pParse, "no such database: %T", pDbname); + return; + } + if( i<2 ){ + sqliteErrorMsg(pParse, "cannot detach database %T", pDbname); + return; + } +#ifndef SQLITE_OMIT_AUTHORIZATION + if( sqliteAuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){ + return; + } +#endif /* SQLITE_OMIT_AUTHORIZATION */ + sqliteBtreeClose(pDb->pBt); + pDb->pBt = 0; + sqliteFree(pDb->zName); + sqliteResetInternalSchema(db, i); + if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux); + db->nDb--; + if( inDb ){ + db->aDb[i] = db->aDb[db->nDb]; + memset(&db->aDb[db->nDb], 0, sizeof(db->aDb[0])); + sqliteResetInternalSchema(db, i); + } +} + +/* +** Initialize a DbFixer structure. This routine must be called prior +** to passing the structure to one of the sqliteFixAAAA() routines below. +** +** The return value indicates whether or not fixation is required. TRUE +** means we do need to fix the database references, FALSE means we do not. +*/ +int sqliteFixInit( + DbFixer *pFix, /* The fixer to be initialized */ + Parse *pParse, /* Error messages will be written here */ + int iDb, /* This is the database that must must be used */ + const char *zType, /* "view", "trigger", or "index" */ + const Token *pName /* Name of the view, trigger, or index */ +){ + sqlite *db; + + if( iDb<0 || iDb==1 ) return 0; + db = pParse->db; + assert( db->nDb>iDb ); + pFix->pParse = pParse; + pFix->zDb = db->aDb[iDb].zName; + pFix->zType = zType; + pFix->pName = pName; + return 1; +} + +/* +** The following set of routines walk through the parse tree and assign +** a specific database to all table references where the database name +** was left unspecified in the original SQL statement. The pFix structure +** must have been initialized by a prior call to sqliteFixInit(). +** +** These routines are used to make sure that an index, trigger, or +** view in one database does not refer to objects in a different database. +** (Exception: indices, triggers, and views in the TEMP database are +** allowed to refer to anything.) If a reference is explicitly made +** to an object in a different database, an error message is added to +** pParse->zErrMsg and these routines return non-zero. If everything +** checks out, these routines return 0. +*/ +int sqliteFixSrcList( + DbFixer *pFix, /* Context of the fixation */ + SrcList *pList /* The Source list to check and modify */ +){ + int i; + const char *zDb; + + if( pList==0 ) return 0; + zDb = pFix->zDb; + for(i=0; inSrc; i++){ + if( pList->a[i].zDatabase==0 ){ + pList->a[i].zDatabase = sqliteStrDup(zDb); + }else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){ + sqliteErrorMsg(pFix->pParse, + "%s %z cannot reference objects in database %s", + pFix->zType, sqliteStrNDup(pFix->pName->z, pFix->pName->n), + pList->a[i].zDatabase); + return 1; + } + if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1; + if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1; + } + return 0; +} +int sqliteFixSelect( + DbFixer *pFix, /* Context of the fixation */ + Select *pSelect /* The SELECT statement to be fixed to one database */ +){ + while( pSelect ){ + if( sqliteFixExprList(pFix, pSelect->pEList) ){ + return 1; + } + if( sqliteFixSrcList(pFix, pSelect->pSrc) ){ + return 1; + } + if( sqliteFixExpr(pFix, pSelect->pWhere) ){ + return 1; + } + if( sqliteFixExpr(pFix, pSelect->pHaving) ){ + return 1; + } + pSelect = pSelect->pPrior; + } + return 0; +} +int sqliteFixExpr( + DbFixer *pFix, /* Context of the fixation */ + Expr *pExpr /* The expression to be fixed to one database */ +){ + while( pExpr ){ + if( sqliteFixSelect(pFix, pExpr->pSelect) ){ + return 1; + } + if( sqliteFixExprList(pFix, pExpr->pList) ){ + return 1; + } + if( sqliteFixExpr(pFix, pExpr->pRight) ){ + return 1; + } + pExpr = pExpr->pLeft; + } + return 0; +} +int sqliteFixExprList( + DbFixer *pFix, /* Context of the fixation */ + ExprList *pList /* The expression to be fixed to one database */ +){ + int i; + if( pList==0 ) return 0; + for(i=0; inExpr; i++){ + if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){ + return 1; + } + } + return 0; +} +int sqliteFixTriggerStep( + DbFixer *pFix, /* Context of the fixation */ + TriggerStep *pStep /* The trigger step be fixed to one database */ +){ + while( pStep ){ + if( sqliteFixSelect(pFix, pStep->pSelect) ){ + return 1; + } + if( sqliteFixExpr(pFix, pStep->pWhere) ){ + return 1; + } + if( sqliteFixExprList(pFix, pStep->pExprList) ){ + return 1; + } + pStep = pStep->pNext; + } + return 0; +} diff --git a/src/libs/sqlite2/auth.c b/src/libs/sqlite2/auth.c new file mode 100644 index 00000000..9147f148 --- /dev/null +++ b/src/libs/sqlite2/auth.c @@ -0,0 +1,219 @@ +/* +** 2003 January 11 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the sqlite_set_authorizer() +** API. This facility is an optional feature of the library. Embedded +** systems that do not need this facility may omit it by recompiling +** the library with -DSQLITE_OMIT_AUTHORIZATION=1 +** +** $Id: auth.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "sqliteInt.h" + +/* +** All of the code in this file may be omitted by defining a single +** macro. +*/ +#ifndef SQLITE_OMIT_AUTHORIZATION + +/* +** Set or clear the access authorization function. +** +** The access authorization function is be called during the compilation +** phase to verify that the user has read and/or write access permission on +** various fields of the database. The first argument to the auth function +** is a copy of the 3rd argument to this routine. The second argument +** to the auth function is one of these constants: +** +** SQLITE_COPY +** SQLITE_CREATE_INDEX +** SQLITE_CREATE_TABLE +** SQLITE_CREATE_TEMP_INDEX +** SQLITE_CREATE_TEMP_TABLE +** SQLITE_CREATE_TEMP_TRIGGER +** SQLITE_CREATE_TEMP_VIEW +** SQLITE_CREATE_TRIGGER +** SQLITE_CREATE_VIEW +** SQLITE_DELETE +** SQLITE_DROP_INDEX +** SQLITE_DROP_TABLE +** SQLITE_DROP_TEMP_INDEX +** SQLITE_DROP_TEMP_TABLE +** SQLITE_DROP_TEMP_TRIGGER +** SQLITE_DROP_TEMP_VIEW +** SQLITE_DROP_TRIGGER +** SQLITE_DROP_VIEW +** SQLITE_INSERT +** SQLITE_PRAGMA +** SQLITE_READ +** SQLITE_SELECT +** SQLITE_TRANSACTION +** SQLITE_UPDATE +** +** The third and fourth arguments to the auth function are the name of +** the table and the column that are being accessed. The auth function +** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If +** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY +** means that the SQL statement will never-run - the sqlite_exec() call +** will return with an error. SQLITE_IGNORE means that the SQL statement +** should run but attempts to read the specified column will return NULL +** and attempts to write the column will be ignored. +** +** Setting the auth function to NULL disables this hook. The default +** setting of the auth function is NULL. +*/ +int sqlite_set_authorizer( + sqlite *db, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pArg +){ + db->xAuth = xAuth; + db->pAuthArg = pArg; + return SQLITE_OK; +} + +/* +** Write an error message into pParse->zErrMsg that explains that the +** user-supplied authorization function returned an illegal value. +*/ +static void sqliteAuthBadReturnCode(Parse *pParse, int rc){ + sqliteErrorMsg(pParse, "illegal return value (%d) from the " + "authorization function - should be SQLITE_OK, SQLITE_IGNORE, " + "or SQLITE_DENY", rc); + pParse->rc = SQLITE_MISUSE; +} + +/* +** The pExpr should be a TK_COLUMN expression. The table referred to +** is in pTabList or else it is the NEW or OLD table of a trigger. +** Check to see if it is OK to read this particular column. +** +** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN +** instruction into a TK_NULL. If the auth function returns SQLITE_DENY, +** then generate an error. +*/ +void sqliteAuthRead( + Parse *pParse, /* The parser context */ + Expr *pExpr, /* The expression to check authorization on */ + SrcList *pTabList /* All table that pExpr might refer to */ +){ + sqlite *db = pParse->db; + int rc; + Table *pTab; /* The table being read */ + const char *zCol; /* Name of the column of the table */ + int iSrc; /* Index in pTabList->a[] of table being read */ + const char *zDBase; /* Name of database being accessed */ + TriggerStack *pStack; /* The stack of current triggers */ + + if( db->xAuth==0 ) return; + assert( pExpr->op==TK_COLUMN ); + for(iSrc=0; iSrcnSrc; iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; + } + if( iSrc>=0 && iSrcnSrc ){ + pTab = pTabList->a[iSrc].pTab; + }else if( (pStack = pParse->trigStack)!=0 ){ + /* This must be an attempt to read the NEW or OLD pseudo-tables + ** of a trigger. + */ + assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); + pTab = pStack->pTab; + }else{ + return; + } + if( pTab==0 ) return; + if( pExpr->iColumn>=0 ){ + assert( pExpr->iColumnnCol ); + zCol = pTab->aCol[pExpr->iColumn].zName; + }else if( pTab->iPKey>=0 ){ + assert( pTab->iPKeynCol ); + zCol = pTab->aCol[pTab->iPKey].zName; + }else{ + zCol = "ROWID"; + } + assert( pExpr->iDbnDb ); + zDBase = db->aDb[pExpr->iDb].zName; + rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, + pParse->zAuthContext); + if( rc==SQLITE_IGNORE ){ + pExpr->op = TK_NULL; + }else if( rc==SQLITE_DENY ){ + if( db->nDb>2 || pExpr->iDb!=0 ){ + sqliteErrorMsg(pParse, "access to %s.%s.%s is prohibited", + zDBase, pTab->zName, zCol); + }else{ + sqliteErrorMsg(pParse, "access to %s.%s is prohibited", pTab->zName,zCol); + } + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_OK ){ + sqliteAuthBadReturnCode(pParse, rc); + } +} + +/* +** Do an authorization check using the code and arguments given. Return +** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY +** is returned, then the error count and error message in pParse are +** modified appropriately. +*/ +int sqliteAuthCheck( + Parse *pParse, + int code, + const char *zArg1, + const char *zArg2, + const char *zArg3 +){ + sqlite *db = pParse->db; + int rc; + + if( db->init.busy || db->xAuth==0 ){ + return SQLITE_OK; + } + rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + sqliteErrorMsg(pParse, "not authorized"); + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){ + rc = SQLITE_DENY; + sqliteAuthBadReturnCode(pParse, rc); + } + return rc; +} + +/* +** Push an authorization context. After this routine is called, the +** zArg3 argument to authorization callbacks will be zContext until +** popped. Or if pParse==0, this routine is a no-op. +*/ +void sqliteAuthContextPush( + Parse *pParse, + AuthContext *pContext, + const char *zContext +){ + pContext->pParse = pParse; + if( pParse ){ + pContext->zAuthContext = pParse->zAuthContext; + pParse->zAuthContext = zContext; + } +} + +/* +** Pop an authorization context that was previously pushed +** by sqliteAuthContextPush +*/ +void sqliteAuthContextPop(AuthContext *pContext){ + if( pContext->pParse ){ + pContext->pParse->zAuthContext = pContext->zAuthContext; + pContext->pParse = 0; + } +} + +#endif /* SQLITE_OMIT_AUTHORIZATION */ diff --git a/src/libs/sqlite2/btree.c b/src/libs/sqlite2/btree.c new file mode 100644 index 00000000..745bdda2 --- /dev/null +++ b/src/libs/sqlite2/btree.c @@ -0,0 +1,3584 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** $Id: btree.c 875429 2008-10-24 12:20:41Z cgilles $ +** +** This file implements a external (disk-based) database using BTrees. +** For a detailed discussion of BTrees, refer to +** +** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: +** "Sorting And Searching", pages 473-480. Addison-Wesley +** Publishing Company, Reading, Massachusetts. +** +** The basic idea is that each page of the file contains N database +** entries and N+1 pointers to subpages. +** +** ---------------------------------------------------------------- +** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) | +** ---------------------------------------------------------------- +** +** All of the keys on the page that Ptr(0) points to have values less +** than Key(0). All of the keys on page Ptr(1) and its subpages have +** values greater than Key(0) and less than Key(1). All of the keys +** on Ptr(N+1) and its subpages have values greater than Key(N). And +** so forth. +** +** Finding a particular key requires reading O(log(M)) pages from the +** disk where M is the number of entries in the tree. +** +** In this implementation, a single file can hold one or more separate +** BTrees. Each BTree is identified by the index of its root page. The +** key and data for any entry are combined to form the "payload". Up to +** MX_LOCAL_PAYLOAD bytes of payload can be carried directly on the +** database page. If the payload is larger than MX_LOCAL_PAYLOAD bytes +** then surplus bytes are stored on overflow pages. The payload for an +** entry and the preceding pointer are combined to form a "Cell". Each +** page has a small header which contains the Ptr(N+1) pointer. +** +** The first page of the file contains a magic string used to verify that +** the file really is a valid BTree database, a pointer to a list of unused +** pages in the file, and some meta information. The root of the first +** BTree begins on page 2 of the file. (Pages are numbered beginning with +** 1, not 0.) Thus a minimum database contains 2 pages. +*/ +#include "sqliteInt.h" +#include "pager.h" +#include "btree.h" +#include + +/* Forward declarations */ +static BtOps sqliteBtreeOps; +static BtCursorOps sqliteBtreeCursorOps; + +/* +** Macros used for byteswapping. B is a pointer to the Btree +** structure. This is needed to access the Btree.needSwab boolean +** in order to tell if byte swapping is needed or not. +** X is an unsigned integer. SWAB16 byte swaps a 16-bit integer. +** SWAB32 byteswaps a 32-bit integer. +*/ +#define SWAB16(B,X) ((B)->needSwab? swab16((u16)X) : ((u16)X)) +#define SWAB32(B,X) ((B)->needSwab? swab32(X) : (X)) +#define SWAB_ADD(B,X,A) \ + if((B)->needSwab){ X=swab32(swab32(X)+A); }else{ X += (A); } + +/* +** The following global variable - available only if SQLITE_TEST is +** defined - is used to determine whether new databases are created in +** native byte order or in non-native byte order. Non-native byte order +** databases are created for testing purposes only. Under normal operation, +** only native byte-order databases should be created, but we should be +** able to read or write existing databases regardless of the byteorder. +*/ +#ifdef SQLITE_TEST +int btree_native_byte_order = 1; +#else +# define btree_native_byte_order 1 +#endif + +/* +** Forward declarations of structures used only in this file. +*/ +typedef struct PageOne PageOne; +typedef struct MemPage MemPage; +typedef struct PageHdr PageHdr; +typedef struct Cell Cell; +typedef struct CellHdr CellHdr; +typedef struct FreeBlk FreeBlk; +typedef struct OverflowPage OverflowPage; +typedef struct FreelistInfo FreelistInfo; + +/* +** All structures on a database page are aligned to 4-byte boundries. +** This routine rounds up a number of bytes to the next multiple of 4. +** +** This might need to change for computer architectures that require +** and 8-byte alignment boundry for structures. +*/ +#define ROUNDUP(X) ((X+3) & ~3) + +/* +** This is a magic string that appears at the beginning of every +** SQLite database in order to identify the file as a real database. +*/ +static const char zMagicHeader[] = + "** This file contains an SQLite 2.1 database **"; +#define MAGIC_SIZE (sizeof(zMagicHeader)) + +/* +** This is a magic integer also used to test the integrity of the database +** file. This integer is used in addition to the string above so that +** if the file is written on a little-endian architecture and read +** on a big-endian architectures (or vice versa) we can detect the +** problem. +** +** The number used was obtained at random and has no special +** significance other than the fact that it represents a different +** integer on little-endian and big-endian machines. +*/ +#define MAGIC 0xdae37528 + +/* +** The first page of the database file contains a magic header string +** to identify the file as an SQLite database file. It also contains +** a pointer to the first free page of the file. Page 2 contains the +** root of the principle BTree. The file might contain other BTrees +** rooted on pages above 2. +** +** The first page also contains SQLITE_N_BTREE_META integers that +** can be used by higher-level routines. +** +** Remember that pages are numbered beginning with 1. (See pager.c +** for additional information.) Page 0 does not exist and a page +** number of 0 is used to mean "no such page". +*/ +struct PageOne { + char zMagic[MAGIC_SIZE]; /* String that identifies the file as a database */ + int iMagic; /* Integer to verify correct byte order */ + Pgno freeList; /* First free page in a list of all free pages */ + int nFree; /* Number of pages on the free list */ + int aMeta[SQLITE_N_BTREE_META-1]; /* User defined integers */ +}; + +/* +** Each database page has a header that is an instance of this +** structure. +** +** PageHdr.firstFree is 0 if there is no free space on this page. +** Otherwise, PageHdr.firstFree is the index in MemPage.u.aDisk[] of a +** FreeBlk structure that describes the first block of free space. +** All free space is defined by a linked list of FreeBlk structures. +** +** Data is stored in a linked list of Cell structures. PageHdr.firstCell +** is the index into MemPage.u.aDisk[] of the first cell on the page. The +** Cells are kept in sorted order. +** +** A Cell contains all information about a database entry and a pointer +** to a child page that contains other entries less than itself. In +** other words, the i-th Cell contains both Ptr(i) and Key(i). The +** right-most pointer of the page is contained in PageHdr.rightChild. +*/ +struct PageHdr { + Pgno rightChild; /* Child page that comes after all cells on this page */ + u16 firstCell; /* Index in MemPage.u.aDisk[] of the first cell */ + u16 firstFree; /* Index in MemPage.u.aDisk[] of the first free block */ +}; + +/* +** Entries on a page of the database are called "Cells". Each Cell +** has a header and data. This structure defines the header. The +** key and data (collectively the "payload") follow this header on +** the database page. +** +** A definition of the complete Cell structure is given below. The +** header for the cell must be defined first in order to do some +** of the sizing #defines that follow. +*/ +struct CellHdr { + Pgno leftChild; /* Child page that comes before this cell */ + u16 nKey; /* Number of bytes in the key */ + u16 iNext; /* Index in MemPage.u.aDisk[] of next cell in sorted order */ + u8 nKeyHi; /* Upper 8 bits of key size for keys larger than 64K bytes */ + u8 nDataHi; /* Upper 8 bits of data size when the size is more than 64K */ + u16 nData; /* Number of bytes of data */ +}; + +/* +** The key and data size are split into a lower 16-bit segment and an +** upper 8-bit segment in order to pack them together into a smaller +** space. The following macros reassembly a key or data size back +** into an integer. +*/ +#define NKEY(b,h) (SWAB16(b,h.nKey) + h.nKeyHi*65536) +#define NDATA(b,h) (SWAB16(b,h.nData) + h.nDataHi*65536) + +/* +** The minimum size of a complete Cell. The Cell must contain a header +** and at least 4 bytes of payload. +*/ +#define MIN_CELL_SIZE (sizeof(CellHdr)+4) + +/* +** The maximum number of database entries that can be held in a single +** page of the database. +*/ +#define MX_CELL ((SQLITE_USABLE_SIZE-sizeof(PageHdr))/MIN_CELL_SIZE) + +/* +** The amount of usable space on a single page of the BTree. This is the +** page size minus the overhead of the page header. +*/ +#define USABLE_SPACE (SQLITE_USABLE_SIZE - sizeof(PageHdr)) + +/* +** The maximum amount of payload (in bytes) that can be stored locally for +** a database entry. If the entry contains more data than this, the +** extra goes onto overflow pages. +** +** This number is chosen so that at least 4 cells will fit on every page. +*/ +#define MX_LOCAL_PAYLOAD ((USABLE_SPACE/4-(sizeof(CellHdr)+sizeof(Pgno)))&~3) + +/* +** Data on a database page is stored as a linked list of Cell structures. +** Both the key and the data are stored in aPayload[]. The key always comes +** first. The aPayload[] field grows as necessary to hold the key and data, +** up to a maximum of MX_LOCAL_PAYLOAD bytes. If the size of the key and +** data combined exceeds MX_LOCAL_PAYLOAD bytes, then Cell.ovfl is the +** page number of the first overflow page. +** +** Though this structure is fixed in size, the Cell on the database +** page varies in size. Every cell has a CellHdr and at least 4 bytes +** of payload space. Additional payload bytes (up to the maximum of +** MX_LOCAL_PAYLOAD) and the Cell.ovfl value are allocated only as +** needed. +*/ +struct Cell { + CellHdr h; /* The cell header */ + char aPayload[MX_LOCAL_PAYLOAD]; /* Key and data */ + Pgno ovfl; /* The first overflow page */ +}; + +/* +** Free space on a page is remembered using a linked list of the FreeBlk +** structures. Space on a database page is allocated in increments of +** at least 4 bytes and is always aligned to a 4-byte boundry. The +** linked list of FreeBlks is always kept in order by address. +*/ +struct FreeBlk { + u16 iSize; /* Number of bytes in this block of free space */ + u16 iNext; /* Index in MemPage.u.aDisk[] of the next free block */ +}; + +/* +** The number of bytes of payload that will fit on a single overflow page. +*/ +#define OVERFLOW_SIZE (SQLITE_USABLE_SIZE-sizeof(Pgno)) + +/* +** When the key and data for a single entry in the BTree will not fit in +** the MX_LOCAL_PAYLOAD bytes of space available on the database page, +** then all extra bytes are written to a linked list of overflow pages. +** Each overflow page is an instance of the following structure. +** +** Unused pages in the database are also represented by instances of +** the OverflowPage structure. The PageOne.freeList field is the +** page number of the first page in a linked list of unused database +** pages. +*/ +struct OverflowPage { + Pgno iNext; + char aPayload[OVERFLOW_SIZE]; +}; + +/* +** The PageOne.freeList field points to a linked list of overflow pages +** hold information about free pages. The aPayload section of each +** overflow page contains an instance of the following structure. The +** aFree[] array holds the page number of nFree unused pages in the disk +** file. +*/ +struct FreelistInfo { + int nFree; + Pgno aFree[(OVERFLOW_SIZE-sizeof(int))/sizeof(Pgno)]; +}; + +/* +** For every page in the database file, an instance of the following structure +** is stored in memory. The u.aDisk[] array contains the raw bits read from +** the disk. The rest is auxiliary information held in memory only. The +** auxiliary info is only valid for regular database pages - it is not +** used for overflow pages and pages on the freelist. +** +** Of particular interest in the auxiliary info is the apCell[] entry. Each +** apCell[] entry is a pointer to a Cell structure in u.aDisk[]. The cells are +** put in this array so that they can be accessed in constant time, rather +** than in linear time which would be needed if we had to walk the linked +** list on every access. +** +** Note that apCell[] contains enough space to hold up to two more Cells +** than can possibly fit on one page. In the steady state, every apCell[] +** points to memory inside u.aDisk[]. But in the middle of an insert +** operation, some apCell[] entries may temporarily point to data space +** outside of u.aDisk[]. This is a transient situation that is quickly +** resolved. But while it is happening, it is possible for a database +** page to hold as many as two more cells than it might otherwise hold. +** The extra two entries in apCell[] are an allowance for this situation. +** +** The pParent field points back to the parent page. This allows us to +** walk up the BTree from any leaf to the root. Care must be taken to +** unref() the parent page pointer when this page is no longer referenced. +** The pageDestructor() routine handles that chore. +*/ +struct MemPage { + union u_page_data { + char aDisk[SQLITE_PAGE_SIZE]; /* Page data stored on disk */ + PageHdr hdr; /* Overlay page header */ + } u; + u8 isInit; /* True if auxiliary data is initialized */ + u8 idxShift; /* True if apCell[] indices have changed */ + u8 isOverfull; /* Some apCell[] points outside u.aDisk[] */ + MemPage *pParent; /* The parent of this page. NULL for root */ + int idxParent; /* Index in pParent->apCell[] of this node */ + int nFree; /* Number of free bytes in u.aDisk[] */ + int nCell; /* Number of entries on this page */ + Cell *apCell[MX_CELL+2]; /* All data entires in sorted order */ +}; + +/* +** The in-memory image of a disk page has the auxiliary information appended +** to the end. EXTRA_SIZE is the number of bytes of space needed to hold +** that extra information. +*/ +#define EXTRA_SIZE (sizeof(MemPage)-sizeof(union u_page_data)) + +/* +** Everything we need to know about an open database +*/ +struct Btree { + BtOps *pOps; /* Function table */ + Pager *pPager; /* The page cache */ + BtCursor *pCursor; /* A list of all open cursors */ + PageOne *page1; /* First page of the database */ + u8 inTrans; /* True if a transaction is in progress */ + u8 inCkpt; /* True if there is a checkpoint on the transaction */ + u8 readOnly; /* True if the underlying file is readonly */ + u8 needSwab; /* Need to byte-swapping */ +}; +typedef Btree Bt; + +/* +** A cursor is a pointer to a particular entry in the BTree. +** The entry is identified by its MemPage and the index in +** MemPage.apCell[] of the entry. +*/ +struct BtCursor { + BtCursorOps *pOps; /* Function table */ + Btree *pBt; /* The Btree to which this cursor belongs */ + BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ + BtCursor *pShared; /* Loop of cursors with the same root page */ + Pgno pgnoRoot; /* The root page of this tree */ + MemPage *pPage; /* Page that contains the entry */ + int idx; /* Index of the entry in pPage->apCell[] */ + u8 wrFlag; /* True if writable */ + u8 eSkip; /* Determines if next step operation is a no-op */ + u8 iMatch; /* compare result from last sqliteBtreeMoveto() */ +}; + +/* +** Legal values for BtCursor.eSkip. +*/ +#define SKIP_NONE 0 /* Always step the cursor */ +#define SKIP_NEXT 1 /* The next sqliteBtreeNext() is a no-op */ +#define SKIP_PREV 2 /* The next sqliteBtreePrevious() is a no-op */ +#define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */ + +/* Forward declarations */ +static int fileBtreeCloseCursor(BtCursor *pCur); + +/* +** Routines for byte swapping. +*/ +u16 swab16(u16 x){ + return ((x & 0xff)<<8) | ((x>>8)&0xff); +} +u32 swab32(u32 x){ + return ((x & 0xff)<<24) | ((x & 0xff00)<<8) | + ((x>>8) & 0xff00) | ((x>>24)&0xff); +} + +/* +** Compute the total number of bytes that a Cell needs on the main +** database page. The number returned includes the Cell header, +** local payload storage, and the pointer to overflow pages (if +** applicable). Additional space allocated on overflow pages +** is NOT included in the value returned from this routine. +*/ +static int cellSize(Btree *pBt, Cell *pCell){ + int n = NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h); + if( n>MX_LOCAL_PAYLOAD ){ + n = MX_LOCAL_PAYLOAD + sizeof(Pgno); + }else{ + n = ROUNDUP(n); + } + n += sizeof(CellHdr); + return n; +} + +/* +** Defragment the page given. All Cells are moved to the +** beginning of the page and all free space is collected +** into one big FreeBlk at the end of the page. +*/ +static void defragmentPage(Btree *pBt, MemPage *pPage){ + int pc, i, n; + FreeBlk *pFBlk; + char newPage[SQLITE_USABLE_SIZE]; + + assert( sqlitepager_iswriteable(pPage) ); + assert( pPage->isInit ); + pc = sizeof(PageHdr); + pPage->u.hdr.firstCell = SWAB16(pBt, pc); + memcpy(newPage, pPage->u.aDisk, pc); + for(i=0; inCell; i++){ + Cell *pCell = pPage->apCell[i]; + + /* This routine should never be called on an overfull page. The + ** following asserts verify that constraint. */ + assert( Addr(pCell) > Addr(pPage) ); + assert( Addr(pCell) < Addr(pPage) + SQLITE_USABLE_SIZE ); + + n = cellSize(pBt, pCell); + pCell->h.iNext = SWAB16(pBt, pc + n); + memcpy(&newPage[pc], pCell, n); + pPage->apCell[i] = (Cell*)&pPage->u.aDisk[pc]; + pc += n; + } + assert( pPage->nFree==SQLITE_USABLE_SIZE-pc ); + memcpy(pPage->u.aDisk, newPage, pc); + if( pPage->nCell>0 ){ + pPage->apCell[pPage->nCell-1]->h.iNext = 0; + } + pFBlk = (FreeBlk*)&pPage->u.aDisk[pc]; + pFBlk->iSize = SWAB16(pBt, SQLITE_USABLE_SIZE - pc); + pFBlk->iNext = 0; + pPage->u.hdr.firstFree = SWAB16(pBt, pc); + memset(&pFBlk[1], 0, SQLITE_USABLE_SIZE - pc - sizeof(FreeBlk)); +} + +/* +** Allocate nByte bytes of space on a page. nByte must be a +** multiple of 4. +** +** Return the index into pPage->u.aDisk[] of the first byte of +** the new allocation. Or return 0 if there is not enough free +** space on the page to satisfy the allocation request. +** +** If the page contains nBytes of free space but does not contain +** nBytes of contiguous free space, then this routine automatically +** calls defragementPage() to consolidate all free space before +** allocating the new chunk. +*/ +static int allocateSpace(Btree *pBt, MemPage *pPage, int nByte){ + FreeBlk *p; + u16 *pIdx; + int start; + int iSize; +#ifndef NDEBUG + int cnt = 0; +#endif + + assert( sqlitepager_iswriteable(pPage) ); + assert( nByte==ROUNDUP(nByte) ); + assert( pPage->isInit ); + if( pPage->nFreeisOverfull ) return 0; + pIdx = &pPage->u.hdr.firstFree; + p = (FreeBlk*)&pPage->u.aDisk[SWAB16(pBt, *pIdx)]; + while( (iSize = SWAB16(pBt, p->iSize))iNext==0 ){ + defragmentPage(pBt, pPage); + pIdx = &pPage->u.hdr.firstFree; + }else{ + pIdx = &p->iNext; + } + p = (FreeBlk*)&pPage->u.aDisk[SWAB16(pBt, *pIdx)]; + } + if( iSize==nByte ){ + start = SWAB16(pBt, *pIdx); + *pIdx = p->iNext; + }else{ + FreeBlk *pNew; + start = SWAB16(pBt, *pIdx); + pNew = (FreeBlk*)&pPage->u.aDisk[start + nByte]; + pNew->iNext = p->iNext; + pNew->iSize = SWAB16(pBt, iSize - nByte); + *pIdx = SWAB16(pBt, start + nByte); + } + pPage->nFree -= nByte; + return start; +} + +/* +** Return a section of the MemPage.u.aDisk[] to the freelist. +** The first byte of the new free block is pPage->u.aDisk[start] +** and the size of the block is "size" bytes. Size must be +** a multiple of 4. +** +** Most of the effort here is involved in coalesing adjacent +** free blocks into a single big free block. +*/ +static void freeSpace(Btree *pBt, MemPage *pPage, int start, int size){ + int end = start + size; + u16 *pIdx, idx; + FreeBlk *pFBlk; + FreeBlk *pNew; + FreeBlk *pNext; + int iSize; + + assert( sqlitepager_iswriteable(pPage) ); + assert( size == ROUNDUP(size) ); + assert( start == ROUNDUP(start) ); + assert( pPage->isInit ); + pIdx = &pPage->u.hdr.firstFree; + idx = SWAB16(pBt, *pIdx); + while( idx!=0 && idxu.aDisk[idx]; + iSize = SWAB16(pBt, pFBlk->iSize); + if( idx + iSize == start ){ + pFBlk->iSize = SWAB16(pBt, iSize + size); + if( idx + iSize + size == SWAB16(pBt, pFBlk->iNext) ){ + pNext = (FreeBlk*)&pPage->u.aDisk[idx + iSize + size]; + if( pBt->needSwab ){ + pFBlk->iSize = swab16((u16)swab16(pNext->iSize)+iSize+size); + }else{ + pFBlk->iSize += pNext->iSize; + } + pFBlk->iNext = pNext->iNext; + } + pPage->nFree += size; + return; + } + pIdx = &pFBlk->iNext; + idx = SWAB16(pBt, *pIdx); + } + pNew = (FreeBlk*)&pPage->u.aDisk[start]; + if( idx != end ){ + pNew->iSize = SWAB16(pBt, size); + pNew->iNext = SWAB16(pBt, idx); + }else{ + pNext = (FreeBlk*)&pPage->u.aDisk[idx]; + pNew->iSize = SWAB16(pBt, size + SWAB16(pBt, pNext->iSize)); + pNew->iNext = pNext->iNext; + } + *pIdx = SWAB16(pBt, start); + pPage->nFree += size; +} + +/* +** Initialize the auxiliary information for a disk block. +** +** The pParent parameter must be a pointer to the MemPage which +** is the parent of the page being initialized. The root of the +** BTree (usually page 2) has no parent and so for that page, +** pParent==NULL. +** +** Return SQLITE_OK on success. If we see that the page does +** not contain a well-formed database page, then return +** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not +** guarantee that the page is well-formed. It only shows that +** we failed to detect any corruption. +*/ +static int initPage(Bt *pBt, MemPage *pPage, Pgno pgnoThis, MemPage *pParent){ + int idx; /* An index into pPage->u.aDisk[] */ + Cell *pCell; /* A pointer to a Cell in pPage->u.aDisk[] */ + FreeBlk *pFBlk; /* A pointer to a free block in pPage->u.aDisk[] */ + int sz; /* The size of a Cell in bytes */ + int freeSpace; /* Amount of free space on the page */ + + if( pPage->pParent ){ + assert( pPage->pParent==pParent ); + return SQLITE_OK; + } + if( pParent ){ + pPage->pParent = pParent; + sqlitepager_ref(pParent); + } + if( pPage->isInit ) return SQLITE_OK; + pPage->isInit = 1; + pPage->nCell = 0; + freeSpace = USABLE_SPACE; + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx!=0 ){ + if( idx>SQLITE_USABLE_SIZE-MIN_CELL_SIZE ) goto page_format_error; + if( idxu.aDisk[idx]; + sz = cellSize(pBt, pCell); + if( idx+sz > SQLITE_USABLE_SIZE ) goto page_format_error; + freeSpace -= sz; + pPage->apCell[pPage->nCell++] = pCell; + idx = SWAB16(pBt, pCell->h.iNext); + } + pPage->nFree = 0; + idx = SWAB16(pBt, pPage->u.hdr.firstFree); + while( idx!=0 ){ + int iNext; + if( idx>SQLITE_USABLE_SIZE-sizeof(FreeBlk) ) goto page_format_error; + if( idxu.aDisk[idx]; + pPage->nFree += SWAB16(pBt, pFBlk->iSize); + iNext = SWAB16(pBt, pFBlk->iNext); + if( iNext>0 && iNext <= idx ) goto page_format_error; + idx = iNext; + } + if( pPage->nCell==0 && pPage->nFree==0 ){ + /* As a special case, an uninitialized root page appears to be + ** an empty database */ + return SQLITE_OK; + } + if( pPage->nFree!=freeSpace ) goto page_format_error; + return SQLITE_OK; + +page_format_error: + return SQLITE_CORRUPT; +} + +/* +** Set up a raw page so that it looks like a database page holding +** no entries. +*/ +static void zeroPage(Btree *pBt, MemPage *pPage){ + PageHdr *pHdr; + FreeBlk *pFBlk; + assert( sqlitepager_iswriteable(pPage) ); + memset(pPage, 0, SQLITE_USABLE_SIZE); + pHdr = &pPage->u.hdr; + pHdr->firstCell = 0; + pHdr->firstFree = SWAB16(pBt, sizeof(*pHdr)); + pFBlk = (FreeBlk*)&pHdr[1]; + pFBlk->iNext = 0; + pPage->nFree = SQLITE_USABLE_SIZE - sizeof(*pHdr); + pFBlk->iSize = SWAB16(pBt, pPage->nFree); + pPage->nCell = 0; + pPage->isOverfull = 0; +} + +/* +** This routine is called when the reference count for a page +** reaches zero. We need to unref the pParent pointer when that +** happens. +*/ +static void pageDestructor(void *pData){ + MemPage *pPage = (MemPage*)pData; + if( pPage->pParent ){ + MemPage *pParent = pPage->pParent; + pPage->pParent = 0; + sqlitepager_unref(pParent); + } +} + +/* +** Open a new database. +** +** Actually, this routine just sets up the internal data structures +** for accessing the database. We do not open the database file +** until the first page is loaded. +** +** zFilename is the name of the database file. If zFilename is NULL +** a new database with a random name is created. This randomly named +** database file will be deleted when sqliteBtreeClose() is called. +*/ +int sqliteBtreeOpen( + const char *zFilename, /* Name of the file containing the BTree database */ + int omitJournal, /* if TRUE then do not journal this file */ + int nCache, /* How many pages in the page cache */ + Btree **ppBtree /* Pointer to new Btree object written here */ +){ + Btree *pBt; + int rc; + + /* + ** The following asserts make sure that structures used by the btree are + ** the right size. This is to guard against size changes that result + ** when compiling on a different architecture. + */ + assert( sizeof(u32)==4 ); + assert( sizeof(u16)==2 ); + assert( sizeof(Pgno)==4 ); + assert( sizeof(PageHdr)==8 ); + assert( sizeof(CellHdr)==12 ); + assert( sizeof(FreeBlk)==4 ); + assert( sizeof(OverflowPage)==SQLITE_USABLE_SIZE ); + assert( sizeof(FreelistInfo)==OVERFLOW_SIZE ); + assert( sizeof(ptr)==sizeof(char*) ); + assert( sizeof(uptr)==sizeof(ptr) ); + + pBt = sqliteMalloc( sizeof(*pBt) ); + if( pBt==0 ){ + *ppBtree = 0; + return SQLITE_NOMEM; + } + if( nCache<10 ) nCache = 10; + rc = sqlitepager_open(&pBt->pPager, zFilename, nCache, EXTRA_SIZE, + !omitJournal); + if( rc!=SQLITE_OK ){ + if( pBt->pPager ) sqlitepager_close(pBt->pPager); + sqliteFree(pBt); + *ppBtree = 0; + return rc; + } + sqlitepager_set_destructor(pBt->pPager, pageDestructor); + pBt->pCursor = 0; + pBt->page1 = 0; + pBt->readOnly = sqlitepager_isreadonly(pBt->pPager); + pBt->pOps = &sqliteBtreeOps; + *ppBtree = pBt; + return SQLITE_OK; +} + +/* +** Close an open database and invalidate all cursors. +*/ +static int fileBtreeClose(Btree *pBt){ + while( pBt->pCursor ){ + fileBtreeCloseCursor(pBt->pCursor); + } + sqlitepager_close(pBt->pPager); + sqliteFree(pBt); + return SQLITE_OK; +} + +/* +** Change the limit on the number of pages allowed in the cache. +** +** The maximum number of cache pages is set to the absolute +** value of mxPage. If mxPage is negative, the pager will +** operate asynchronously - it will not stop to do fsync()s +** to insure data is written to the disk surface before +** continuing. Transactions still work if synchronous is off, +** and the database cannot be corrupted if this program +** crashes. But if the operating system crashes or there is +** an abrupt power failure when synchronous is off, the database +** could be left in an inconsistent and unrecoverable state. +** Synchronous is on by default so database corruption is not +** normally a worry. +*/ +static int fileBtreeSetCacheSize(Btree *pBt, int mxPage){ + sqlitepager_set_cachesize(pBt->pPager, mxPage); + return SQLITE_OK; +} + +/* +** Change the way data is synced to disk in order to increase or decrease +** how well the database resists damage due to OS crashes and power +** failures. Level 1 is the same as asynchronous (no syncs() occur and +** there is a high probability of damage) Level 2 is the default. There +** is a very low but non-zero probability of damage. Level 3 reduces the +** probability of damage to near zero but with a write performance reduction. +*/ +static int fileBtreeSetSafetyLevel(Btree *pBt, int level){ + sqlitepager_set_safety_level(pBt->pPager, level); + return SQLITE_OK; +} + +/* +** Get a reference to page1 of the database file. This will +** also acquire a readlock on that file. +** +** SQLITE_OK is returned on success. If the file is not a +** well-formed database file, then SQLITE_CORRUPT is returned. +** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM +** is returned if we run out of memory. SQLITE_PROTOCOL is returned +** if there is a locking protocol violation. +*/ +static int lockBtree(Btree *pBt){ + int rc; + if( pBt->page1 ) return SQLITE_OK; + rc = sqlitepager_get(pBt->pPager, 1, (void**)&pBt->page1); + if( rc!=SQLITE_OK ) return rc; + + /* Do some checking to help insure the file we opened really is + ** a valid database file. + */ + if( sqlitepager_pagecount(pBt->pPager)>0 ){ + PageOne *pP1 = pBt->page1; + if( strcmp(pP1->zMagic,zMagicHeader)!=0 || + (pP1->iMagic!=MAGIC && swab32(pP1->iMagic)!=MAGIC) ){ + rc = SQLITE_NOTADB; + goto page1_init_failed; + } + pBt->needSwab = pP1->iMagic!=MAGIC; + } + return rc; + +page1_init_failed: + sqlitepager_unref(pBt->page1); + pBt->page1 = 0; + return rc; +} + +/* +** If there are no outstanding cursors and we are not in the middle +** of a transaction but there is a read lock on the database, then +** this routine unrefs the first page of the database file which +** has the effect of releasing the read lock. +** +** If there are any outstanding cursors, this routine is a no-op. +** +** If there is a transaction in progress, this routine is a no-op. +*/ +static void unlockBtreeIfUnused(Btree *pBt){ + if( pBt->inTrans==0 && pBt->pCursor==0 && pBt->page1!=0 ){ + sqlitepager_unref(pBt->page1); + pBt->page1 = 0; + pBt->inTrans = 0; + pBt->inCkpt = 0; + } +} + +/* +** Create a new database by initializing the first two pages of the +** file. +*/ +static int newDatabase(Btree *pBt){ + MemPage *pRoot; + PageOne *pP1; + int rc; + if( sqlitepager_pagecount(pBt->pPager)>1 ) return SQLITE_OK; + pP1 = pBt->page1; + rc = sqlitepager_write(pBt->page1); + if( rc ) return rc; + rc = sqlitepager_get(pBt->pPager, 2, (void**)&pRoot); + if( rc ) return rc; + rc = sqlitepager_write(pRoot); + if( rc ){ + sqlitepager_unref(pRoot); + return rc; + } + strcpy(pP1->zMagic, zMagicHeader); + if( btree_native_byte_order ){ + pP1->iMagic = MAGIC; + pBt->needSwab = 0; + }else{ + pP1->iMagic = swab32(MAGIC); + pBt->needSwab = 1; + } + zeroPage(pBt, pRoot); + sqlitepager_unref(pRoot); + return SQLITE_OK; +} + +/* +** Attempt to start a new transaction. +** +** A transaction must be started before attempting any changes +** to the database. None of the following routines will work +** unless a transaction is started first: +** +** sqliteBtreeCreateTable() +** sqliteBtreeCreateIndex() +** sqliteBtreeClearTable() +** sqliteBtreeDropTable() +** sqliteBtreeInsert() +** sqliteBtreeDelete() +** sqliteBtreeUpdateMeta() +*/ +static int fileBtreeBeginTrans(Btree *pBt){ + int rc; + if( pBt->inTrans ) return SQLITE_ERROR; + if( pBt->readOnly ) return SQLITE_READONLY; + if( pBt->page1==0 ){ + rc = lockBtree(pBt); + if( rc!=SQLITE_OK ){ + return rc; + } + } + rc = sqlitepager_begin(pBt->page1); + if( rc==SQLITE_OK ){ + rc = newDatabase(pBt); + } + if( rc==SQLITE_OK ){ + pBt->inTrans = 1; + pBt->inCkpt = 0; + }else{ + unlockBtreeIfUnused(pBt); + } + return rc; +} + +/* +** Commit the transaction currently in progress. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. +*/ +static int fileBtreeCommit(Btree *pBt){ + int rc; + rc = pBt->readOnly ? SQLITE_OK : sqlitepager_commit(pBt->pPager); + pBt->inTrans = 0; + pBt->inCkpt = 0; + unlockBtreeIfUnused(pBt); + return rc; +} + +/* +** Rollback the transaction in progress. All cursors will be +** invalided by this operation. Any attempt to use a cursor +** that was open at the beginning of this operation will result +** in an error. +** +** This will release the write lock on the database file. If there +** are no active cursors, it also releases the read lock. +*/ +static int fileBtreeRollback(Btree *pBt){ + int rc; + BtCursor *pCur; + if( pBt->inTrans==0 ) return SQLITE_OK; + pBt->inTrans = 0; + pBt->inCkpt = 0; + rc = pBt->readOnly ? SQLITE_OK : sqlitepager_rollback(pBt->pPager); + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pPage && pCur->pPage->isInit==0 ){ + sqlitepager_unref(pCur->pPage); + pCur->pPage = 0; + } + } + unlockBtreeIfUnused(pBt); + return rc; +} + +/* +** Set the checkpoint for the current transaction. The checkpoint serves +** as a sub-transaction that can be rolled back independently of the +** main transaction. You must start a transaction before starting a +** checkpoint. The checkpoint is ended automatically if the transaction +** commits or rolls back. +** +** Only one checkpoint may be active at a time. It is an error to try +** to start a new checkpoint if another checkpoint is already active. +*/ +static int fileBtreeBeginCkpt(Btree *pBt){ + int rc; + if( !pBt->inTrans || pBt->inCkpt ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + rc = pBt->readOnly ? SQLITE_OK : sqlitepager_ckpt_begin(pBt->pPager); + pBt->inCkpt = 1; + return rc; +} + + +/* +** Commit a checkpoint to transaction currently in progress. If no +** checkpoint is active, this is a no-op. +*/ +static int fileBtreeCommitCkpt(Btree *pBt){ + int rc; + if( pBt->inCkpt && !pBt->readOnly ){ + rc = sqlitepager_ckpt_commit(pBt->pPager); + }else{ + rc = SQLITE_OK; + } + pBt->inCkpt = 0; + return rc; +} + +/* +** Rollback the checkpoint to the current transaction. If there +** is no active checkpoint or transaction, this routine is a no-op. +** +** All cursors will be invalided by this operation. Any attempt +** to use a cursor that was open at the beginning of this operation +** will result in an error. +*/ +static int fileBtreeRollbackCkpt(Btree *pBt){ + int rc; + BtCursor *pCur; + if( pBt->inCkpt==0 || pBt->readOnly ) return SQLITE_OK; + rc = sqlitepager_ckpt_rollback(pBt->pPager); + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pPage && pCur->pPage->isInit==0 ){ + sqlitepager_unref(pCur->pPage); + pCur->pPage = 0; + } + } + pBt->inCkpt = 0; + return rc; +} + +/* +** Create a new cursor for the BTree whose root is on the page +** iTable. The act of acquiring a cursor gets a read lock on +** the database file. +** +** If wrFlag==0, then the cursor can only be used for reading. +** If wrFlag==1, then the cursor can be used for reading or for +** writing if other conditions for writing are also met. These +** are the conditions that must be met in order for writing to +** be allowed: +** +** 1: The cursor must have been opened with wrFlag==1 +** +** 2: No other cursors may be open with wrFlag==0 on the same table +** +** 3: The database must be writable (not on read-only media) +** +** 4: There must be an active transaction. +** +** Condition 2 warrants further discussion. If any cursor is opened +** on a table with wrFlag==0, that prevents all other cursors from +** writing to that table. This is a kind of "read-lock". When a cursor +** is opened with wrFlag==0 it is guaranteed that the table will not +** change as long as the cursor is open. This allows the cursor to +** do a sequential scan of the table without having to worry about +** entries being inserted or deleted during the scan. Cursors should +** be opened with wrFlag==0 only if this read-lock property is needed. +** That is to say, cursors should be opened with wrFlag==0 only if they +** intend to use the sqliteBtreeNext() system call. All other cursors +** should be opened with wrFlag==1 even if they never really intend +** to write. +** +** No checking is done to make sure that page iTable really is the +** root page of a b-tree. If it is not, then the cursor acquired +** will not work correctly. +*/ +static +int fileBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ + int rc; + BtCursor *pCur, *pRing; + + if( pBt->readOnly && wrFlag ){ + *ppCur = 0; + return SQLITE_READONLY; + } + if( pBt->page1==0 ){ + rc = lockBtree(pBt); + if( rc!=SQLITE_OK ){ + *ppCur = 0; + return rc; + } + } + pCur = sqliteMalloc( sizeof(*pCur) ); + if( pCur==0 ){ + rc = SQLITE_NOMEM; + goto create_cursor_exception; + } + pCur->pgnoRoot = (Pgno)iTable; + rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage); + if( rc!=SQLITE_OK ){ + goto create_cursor_exception; + } + rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0); + if( rc!=SQLITE_OK ){ + goto create_cursor_exception; + } + pCur->pOps = &sqliteBtreeCursorOps; + pCur->pBt = pBt; + pCur->wrFlag = wrFlag; + pCur->idx = 0; + pCur->eSkip = SKIP_INVALID; + pCur->pNext = pBt->pCursor; + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur; + } + pCur->pPrev = 0; + pRing = pBt->pCursor; + while( pRing && pRing->pgnoRoot!=pCur->pgnoRoot ){ pRing = pRing->pNext; } + if( pRing ){ + pCur->pShared = pRing->pShared; + pRing->pShared = pCur; + }else{ + pCur->pShared = pCur; + } + pBt->pCursor = pCur; + *ppCur = pCur; + return SQLITE_OK; + +create_cursor_exception: + *ppCur = 0; + if( pCur ){ + if( pCur->pPage ) sqlitepager_unref(pCur->pPage); + sqliteFree(pCur); + } + unlockBtreeIfUnused(pBt); + return rc; +} + +/* +** Close a cursor. The read lock on the database file is released +** when the last cursor is closed. +*/ +static int fileBtreeCloseCursor(BtCursor *pCur){ + Btree *pBt = pCur->pBt; + if( pCur->pPrev ){ + pCur->pPrev->pNext = pCur->pNext; + }else{ + pBt->pCursor = pCur->pNext; + } + if( pCur->pNext ){ + pCur->pNext->pPrev = pCur->pPrev; + } + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } + if( pCur->pShared!=pCur ){ + BtCursor *pRing = pCur->pShared; + while( pRing->pShared!=pCur ){ pRing = pRing->pShared; } + pRing->pShared = pCur->pShared; + } + unlockBtreeIfUnused(pBt); + sqliteFree(pCur); + return SQLITE_OK; +} + +/* +** Make a temporary cursor by filling in the fields of pTempCur. +** The temporary cursor is not on the cursor list for the Btree. +*/ +static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ + memcpy(pTempCur, pCur, sizeof(*pCur)); + pTempCur->pNext = 0; + pTempCur->pPrev = 0; + if( pTempCur->pPage ){ + sqlitepager_ref(pTempCur->pPage); + } +} + +/* +** Delete a temporary cursor such as was made by the CreateTemporaryCursor() +** function above. +*/ +static void releaseTempCursor(BtCursor *pCur){ + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } +} + +/* +** Set *pSize to the number of bytes of key in the entry the +** cursor currently points to. Always return SQLITE_OK. +** Failure is not possible. If the cursor is not currently +** pointing to an entry (which can happen, for example, if +** the database is empty) then *pSize is set to 0. +*/ +static int fileBtreeKeySize(BtCursor *pCur, int *pSize){ + Cell *pCell; + MemPage *pPage; + + pPage = pCur->pPage; + assert( pPage!=0 ); + if( pCur->idx >= pPage->nCell ){ + *pSize = 0; + }else{ + pCell = pPage->apCell[pCur->idx]; + *pSize = NKEY(pCur->pBt, pCell->h); + } + return SQLITE_OK; +} + +/* +** Read payload information from the entry that the pCur cursor is +** pointing to. Begin reading the payload at "offset" and read +** a total of "amt" bytes. Put the result in zBuf. +** +** This routine does not make a distinction between key and data. +** It just reads bytes from the payload area. +*/ +static int getPayload(BtCursor *pCur, int offset, int amt, char *zBuf){ + char *aPayload; + Pgno nextPage; + int rc; + Btree *pBt = pCur->pBt; + assert( pCur!=0 && pCur->pPage!=0 ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); + aPayload = pCur->pPage->apCell[pCur->idx]->aPayload; + if( offsetMX_LOCAL_PAYLOAD ){ + a = MX_LOCAL_PAYLOAD - offset; + } + memcpy(zBuf, &aPayload[offset], a); + if( a==amt ){ + return SQLITE_OK; + } + offset = 0; + zBuf += a; + amt -= a; + }else{ + offset -= MX_LOCAL_PAYLOAD; + } + if( amt>0 ){ + nextPage = SWAB32(pBt, pCur->pPage->apCell[pCur->idx]->ovfl); + } + while( amt>0 && nextPage ){ + OverflowPage *pOvfl; + rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl); + if( rc!=0 ){ + return rc; + } + nextPage = SWAB32(pBt, pOvfl->iNext); + if( offset OVERFLOW_SIZE ){ + a = OVERFLOW_SIZE - offset; + } + memcpy(zBuf, &pOvfl->aPayload[offset], a); + offset = 0; + amt -= a; + zBuf += a; + }else{ + offset -= OVERFLOW_SIZE; + } + sqlitepager_unref(pOvfl); + } + if( amt>0 ){ + return SQLITE_CORRUPT; + } + return SQLITE_OK; +} + +/* +** Read part of the key associated with cursor pCur. A maximum +** of "amt" bytes will be transfered into zBuf[]. The transfer +** begins at "offset". The number of bytes actually read is +** returned. +** +** Change: It used to be that the amount returned will be smaller +** than the amount requested if there are not enough bytes in the key +** to satisfy the request. But now, it must be the case that there +** is enough data available to satisfy the request. If not, an exception +** is raised. The change was made in an effort to boost performance +** by eliminating unneeded tests. +*/ +static int fileBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ + MemPage *pPage; + + assert( amt>=0 ); + assert( offset>=0 ); + assert( pCur->pPage!=0 ); + pPage = pCur->pPage; + if( pCur->idx >= pPage->nCell ){ + return 0; + } + assert( amt+offset <= NKEY(pCur->pBt, pPage->apCell[pCur->idx]->h) ); + getPayload(pCur, offset, amt, zBuf); + return amt; +} + +/* +** Set *pSize to the number of bytes of data in the entry the +** cursor currently points to. Always return SQLITE_OK. +** Failure is not possible. If the cursor is not currently +** pointing to an entry (which can happen, for example, if +** the database is empty) then *pSize is set to 0. +*/ +static int fileBtreeDataSize(BtCursor *pCur, int *pSize){ + Cell *pCell; + MemPage *pPage; + + pPage = pCur->pPage; + assert( pPage!=0 ); + if( pCur->idx >= pPage->nCell ){ + *pSize = 0; + }else{ + pCell = pPage->apCell[pCur->idx]; + *pSize = NDATA(pCur->pBt, pCell->h); + } + return SQLITE_OK; +} + +/* +** Read part of the data associated with cursor pCur. A maximum +** of "amt" bytes will be transfered into zBuf[]. The transfer +** begins at "offset". The number of bytes actually read is +** returned. The amount returned will be smaller than the +** amount requested if there are not enough bytes in the data +** to satisfy the request. +*/ +static int fileBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ + Cell *pCell; + MemPage *pPage; + + assert( amt>=0 ); + assert( offset>=0 ); + assert( pCur->pPage!=0 ); + pPage = pCur->pPage; + if( pCur->idx >= pPage->nCell ){ + return 0; + } + pCell = pPage->apCell[pCur->idx]; + assert( amt+offset <= NDATA(pCur->pBt, pCell->h) ); + getPayload(pCur, offset + NKEY(pCur->pBt, pCell->h), amt, zBuf); + return amt; +} + +/* +** Compare an external key against the key on the entry that pCur points to. +** +** The external key is pKey and is nKey bytes long. The last nIgnore bytes +** of the key associated with pCur are ignored, as if they do not exist. +** (The normal case is for nIgnore to be zero in which case the entire +** internal key is used in the comparison.) +** +** The comparison result is written to *pRes as follows: +** +** *pRes<0 This means pCur0 This means pCur>pKey +** +** When one key is an exact prefix of the other, the shorter key is +** considered less than the longer one. In order to be equal the +** keys must be exactly the same length. (The length of the pCur key +** is the actual key length minus nIgnore bytes.) +*/ +static int fileBtreeKeyCompare( + BtCursor *pCur, /* Pointer to entry to compare against */ + const void *pKey, /* Key to compare against entry that pCur points to */ + int nKey, /* Number of bytes in pKey */ + int nIgnore, /* Ignore this many bytes at the end of pCur */ + int *pResult /* Write the result here */ +){ + Pgno nextPage; + int n, c, rc, nLocal; + Cell *pCell; + Btree *pBt = pCur->pBt; + const char *zKey = (const char*)pKey; + + assert( pCur->pPage ); + assert( pCur->idx>=0 && pCur->idxpPage->nCell ); + pCell = pCur->pPage->apCell[pCur->idx]; + nLocal = NKEY(pBt, pCell->h) - nIgnore; + if( nLocal<0 ) nLocal = 0; + n = nKeyMX_LOCAL_PAYLOAD ){ + n = MX_LOCAL_PAYLOAD; + } + c = memcmp(pCell->aPayload, zKey, n); + if( c!=0 ){ + *pResult = c; + return SQLITE_OK; + } + zKey += n; + nKey -= n; + nLocal -= n; + nextPage = SWAB32(pBt, pCell->ovfl); + while( nKey>0 && nLocal>0 ){ + OverflowPage *pOvfl; + if( nextPage==0 ){ + return SQLITE_CORRUPT; + } + rc = sqlitepager_get(pBt->pPager, nextPage, (void**)&pOvfl); + if( rc ){ + return rc; + } + nextPage = SWAB32(pBt, pOvfl->iNext); + n = nKeyOVERFLOW_SIZE ){ + n = OVERFLOW_SIZE; + } + c = memcmp(pOvfl->aPayload, zKey, n); + sqlitepager_unref(pOvfl); + if( c!=0 ){ + *pResult = c; + return SQLITE_OK; + } + nKey -= n; + nLocal -= n; + zKey += n; + } + if( c==0 ){ + c = nLocal - nKey; + } + *pResult = c; + return SQLITE_OK; +} + +/* +** Move the cursor down to a new child page. The newPgno argument is the +** page number of the child page in the byte order of the disk image. +*/ +static int moveToChild(BtCursor *pCur, int newPgno){ + int rc; + MemPage *pNewPage; + Btree *pBt = pCur->pBt; + + newPgno = SWAB32(pBt, newPgno); + rc = sqlitepager_get(pBt->pPager, newPgno, (void**)&pNewPage); + if( rc ) return rc; + rc = initPage(pBt, pNewPage, newPgno, pCur->pPage); + if( rc ) return rc; + assert( pCur->idx>=pCur->pPage->nCell + || pCur->pPage->apCell[pCur->idx]->h.leftChild==SWAB32(pBt,newPgno) ); + assert( pCur->idxpPage->nCell + || pCur->pPage->u.hdr.rightChild==SWAB32(pBt,newPgno) ); + pNewPage->idxParent = pCur->idx; + pCur->pPage->idxShift = 0; + sqlitepager_unref(pCur->pPage); + pCur->pPage = pNewPage; + pCur->idx = 0; + if( pNewPage->nCell<1 ){ + return SQLITE_CORRUPT; + } + return SQLITE_OK; +} + +/* +** Move the cursor up to the parent page. +** +** pCur->idx is set to the cell index that contains the pointer +** to the page we are coming from. If we are coming from the +** right-most child page then pCur->idx is set to one more than +** the largest cell index. +*/ +static void moveToParent(BtCursor *pCur){ + Pgno oldPgno; + MemPage *pParent; + MemPage *pPage; + int idxParent; + pPage = pCur->pPage; + assert( pPage!=0 ); + pParent = pPage->pParent; + assert( pParent!=0 ); + idxParent = pPage->idxParent; + sqlitepager_ref(pParent); + sqlitepager_unref(pPage); + pCur->pPage = pParent; + assert( pParent->idxShift==0 ); + if( pParent->idxShift==0 ){ + pCur->idx = idxParent; +#ifndef NDEBUG + /* Verify that pCur->idx is the correct index to point back to the child + ** page we just came from + */ + oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage)); + if( pCur->idxnCell ){ + assert( pParent->apCell[idxParent]->h.leftChild==oldPgno ); + }else{ + assert( pParent->u.hdr.rightChild==oldPgno ); + } +#endif + }else{ + /* The MemPage.idxShift flag indicates that cell indices might have + ** changed since idxParent was set and hence idxParent might be out + ** of date. So recompute the parent cell index by scanning all cells + ** and locating the one that points to the child we just came from. + */ + int i; + pCur->idx = pParent->nCell; + oldPgno = SWAB32(pCur->pBt, sqlitepager_pagenumber(pPage)); + for(i=0; inCell; i++){ + if( pParent->apCell[i]->h.leftChild==oldPgno ){ + pCur->idx = i; + break; + } + } + } +} + +/* +** Move the cursor to the root page +*/ +static int moveToRoot(BtCursor *pCur){ + MemPage *pNew; + int rc; + Btree *pBt = pCur->pBt; + + rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pNew); + if( rc ) return rc; + rc = initPage(pBt, pNew, pCur->pgnoRoot, 0); + if( rc ) return rc; + sqlitepager_unref(pCur->pPage); + pCur->pPage = pNew; + pCur->idx = 0; + return SQLITE_OK; +} + +/* +** Move the cursor down to the left-most leaf entry beneath the +** entry to which it is currently pointing. +*/ +static int moveToLeftmost(BtCursor *pCur){ + Pgno pgno; + int rc; + + while( (pgno = pCur->pPage->apCell[pCur->idx]->h.leftChild)!=0 ){ + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + return SQLITE_OK; +} + +/* +** Move the cursor down to the right-most leaf entry beneath the +** page to which it is currently pointing. Notice the difference +** between moveToLeftmost() and moveToRightmost(). moveToLeftmost() +** finds the left-most entry beneath the *entry* whereas moveToRightmost() +** finds the right-most entry beneath the *page*. +*/ +static int moveToRightmost(BtCursor *pCur){ + Pgno pgno; + int rc; + + while( (pgno = pCur->pPage->u.hdr.rightChild)!=0 ){ + pCur->idx = pCur->pPage->nCell; + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + } + pCur->idx = pCur->pPage->nCell - 1; + return SQLITE_OK; +} + +/* Move the cursor to the first entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. +*/ +static int fileBtreeFirst(BtCursor *pCur, int *pRes){ + int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; + rc = moveToRoot(pCur); + if( rc ) return rc; + if( pCur->pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + *pRes = 0; + rc = moveToLeftmost(pCur); + pCur->eSkip = SKIP_NONE; + return rc; +} + +/* Move the cursor to the last entry in the table. Return SQLITE_OK +** on success. Set *pRes to 0 if the cursor actually points to something +** or set *pRes to 1 if the table is empty. +*/ +static int fileBtreeLast(BtCursor *pCur, int *pRes){ + int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; + rc = moveToRoot(pCur); + if( rc ) return rc; + assert( pCur->pPage->isInit ); + if( pCur->pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + *pRes = 0; + rc = moveToRightmost(pCur); + pCur->eSkip = SKIP_NONE; + return rc; +} + +/* Move the cursor so that it points to an entry near pKey. +** Return a success code. +** +** If an exact match is not found, then the cursor is always +** left pointing at a leaf page which would hold the entry if it +** were present. The cursor might point to an entry that comes +** before or after the key. +** +** The result of comparing the key with the entry to which the +** cursor is left pointing is stored in pCur->iMatch. The same +** value is also written to *pRes if pRes!=NULL. The meaning of +** this value is as follows: +** +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than pKey or if the table is empty +** and the cursor is therefore left point to nothing. +** +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches pKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pKey. +*/ +static +int fileBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ + int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; + pCur->eSkip = SKIP_NONE; + rc = moveToRoot(pCur); + if( rc ) return rc; + for(;;){ + int lwr, upr; + Pgno chldPg; + MemPage *pPage = pCur->pPage; + int c = -1; /* pRes return if table is empty must be -1 */ + lwr = 0; + upr = pPage->nCell-1; + while( lwr<=upr ){ + pCur->idx = (lwr+upr)/2; + rc = fileBtreeKeyCompare(pCur, pKey, nKey, 0, &c); + if( rc ) return rc; + if( c==0 ){ + pCur->iMatch = c; + if( pRes ) *pRes = 0; + return SQLITE_OK; + } + if( c<0 ){ + lwr = pCur->idx+1; + }else{ + upr = pCur->idx-1; + } + } + assert( lwr==upr+1 ); + assert( pPage->isInit ); + if( lwr>=pPage->nCell ){ + chldPg = pPage->u.hdr.rightChild; + }else{ + chldPg = pPage->apCell[lwr]->h.leftChild; + } + if( chldPg==0 ){ + pCur->iMatch = c; + if( pRes ) *pRes = c; + return SQLITE_OK; + } + pCur->idx = lwr; + rc = moveToChild(pCur, chldPg); + if( rc ) return rc; + } + /* NOT REACHED */ +} + +/* +** Advance the cursor to the next entry in the database. If +** successful then set *pRes=0. If the cursor +** was already pointing to the last entry in the database before +** this routine was called, then set *pRes=1. +*/ +static int fileBtreeNext(BtCursor *pCur, int *pRes){ + int rc; + MemPage *pPage = pCur->pPage; + assert( pRes!=0 ); + if( pPage==0 ){ + *pRes = 1; + return SQLITE_ABORT; + } + assert( pPage->isInit ); + assert( pCur->eSkip!=SKIP_INVALID ); + if( pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + assert( pCur->idxnCell ); + if( pCur->eSkip==SKIP_NEXT ){ + pCur->eSkip = SKIP_NONE; + *pRes = 0; + return SQLITE_OK; + } + pCur->eSkip = SKIP_NONE; + pCur->idx++; + if( pCur->idx>=pPage->nCell ){ + if( pPage->u.hdr.rightChild ){ + rc = moveToChild(pCur, pPage->u.hdr.rightChild); + if( rc ) return rc; + rc = moveToLeftmost(pCur); + *pRes = 0; + return rc; + } + do{ + if( pPage->pParent==0 ){ + *pRes = 1; + return SQLITE_OK; + } + moveToParent(pCur); + pPage = pCur->pPage; + }while( pCur->idx>=pPage->nCell ); + *pRes = 0; + return SQLITE_OK; + } + *pRes = 0; + if( pPage->u.hdr.rightChild==0 ){ + return SQLITE_OK; + } + rc = moveToLeftmost(pCur); + return rc; +} + +/* +** Step the cursor to the back to the previous entry in the database. If +** successful then set *pRes=0. If the cursor +** was already pointing to the first entry in the database before +** this routine was called, then set *pRes=1. +*/ +static int fileBtreePrevious(BtCursor *pCur, int *pRes){ + int rc; + Pgno pgno; + MemPage *pPage; + pPage = pCur->pPage; + if( pPage==0 ){ + *pRes = 1; + return SQLITE_ABORT; + } + assert( pPage->isInit ); + assert( pCur->eSkip!=SKIP_INVALID ); + if( pPage->nCell==0 ){ + *pRes = 1; + return SQLITE_OK; + } + if( pCur->eSkip==SKIP_PREV ){ + pCur->eSkip = SKIP_NONE; + *pRes = 0; + return SQLITE_OK; + } + pCur->eSkip = SKIP_NONE; + assert( pCur->idx>=0 ); + if( (pgno = pPage->apCell[pCur->idx]->h.leftChild)!=0 ){ + rc = moveToChild(pCur, pgno); + if( rc ) return rc; + rc = moveToRightmost(pCur); + }else{ + while( pCur->idx==0 ){ + if( pPage->pParent==0 ){ + if( pRes ) *pRes = 1; + return SQLITE_OK; + } + moveToParent(pCur); + pPage = pCur->pPage; + } + pCur->idx--; + rc = SQLITE_OK; + } + *pRes = 0; + return rc; +} + +/* +** Allocate a new page from the database file. +** +** The new page is marked as dirty. (In other words, sqlitepager_write() +** has already been called on the new page.) The new page has also +** been referenced and the calling routine is responsible for calling +** sqlitepager_unref() on the new page when it is done. +** +** SQLITE_OK is returned on success. Any other return value indicates +** an error. *ppPage and *pPgno are undefined in the event of an error. +** Do not invoke sqlitepager_unref() on *ppPage if an error is returned. +** +** If the "nearby" parameter is not 0, then a (feeble) effort is made to +** locate a page close to the page number "nearby". This can be used in an +** attempt to keep related pages close to each other in the database file, +** which in turn can make database access faster. +*/ +static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){ + PageOne *pPage1 = pBt->page1; + int rc; + if( pPage1->freeList ){ + OverflowPage *pOvfl; + FreelistInfo *pInfo; + + rc = sqlitepager_write(pPage1); + if( rc ) return rc; + SWAB_ADD(pBt, pPage1->nFree, -1); + rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList), + (void**)&pOvfl); + if( rc ) return rc; + rc = sqlitepager_write(pOvfl); + if( rc ){ + sqlitepager_unref(pOvfl); + return rc; + } + pInfo = (FreelistInfo*)pOvfl->aPayload; + if( pInfo->nFree==0 ){ + *pPgno = SWAB32(pBt, pPage1->freeList); + pPage1->freeList = pOvfl->iNext; + *ppPage = (MemPage*)pOvfl; + }else{ + int closest, n; + n = SWAB32(pBt, pInfo->nFree); + if( n>1 && nearby>0 ){ + int i, dist; + closest = 0; + dist = SWAB32(pBt, pInfo->aFree[0]) - nearby; + if( dist<0 ) dist = -dist; + for(i=1; iaFree[i]) - nearby; + if( d2<0 ) d2 = -d2; + if( d2nFree, -1); + *pPgno = SWAB32(pBt, pInfo->aFree[closest]); + pInfo->aFree[closest] = pInfo->aFree[n-1]; + rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage); + sqlitepager_unref(pOvfl); + if( rc==SQLITE_OK ){ + sqlitepager_dont_rollback(*ppPage); + rc = sqlitepager_write(*ppPage); + } + } + }else{ + *pPgno = sqlitepager_pagecount(pBt->pPager) + 1; + rc = sqlitepager_get(pBt->pPager, *pPgno, (void**)ppPage); + if( rc ) return rc; + rc = sqlitepager_write(*ppPage); + } + return rc; +} + +/* +** Add a page of the database file to the freelist. Either pgno or +** pPage but not both may be 0. +** +** sqlitepager_unref() is NOT called for pPage. +*/ +static int freePage(Btree *pBt, void *pPage, Pgno pgno){ + PageOne *pPage1 = pBt->page1; + OverflowPage *pOvfl = (OverflowPage*)pPage; + int rc; + int needUnref = 0; + MemPage *pMemPage; + + if( pgno==0 ){ + assert( pOvfl!=0 ); + pgno = sqlitepager_pagenumber(pOvfl); + } + assert( pgno>2 ); + assert( sqlitepager_pagenumber(pOvfl)==pgno ); + pMemPage = (MemPage*)pPage; + pMemPage->isInit = 0; + if( pMemPage->pParent ){ + sqlitepager_unref(pMemPage->pParent); + pMemPage->pParent = 0; + } + rc = sqlitepager_write(pPage1); + if( rc ){ + return rc; + } + SWAB_ADD(pBt, pPage1->nFree, 1); + if( pPage1->nFree!=0 && pPage1->freeList!=0 ){ + OverflowPage *pFreeIdx; + rc = sqlitepager_get(pBt->pPager, SWAB32(pBt, pPage1->freeList), + (void**)&pFreeIdx); + if( rc==SQLITE_OK ){ + FreelistInfo *pInfo = (FreelistInfo*)pFreeIdx->aPayload; + int n = SWAB32(pBt, pInfo->nFree); + if( n<(sizeof(pInfo->aFree)/sizeof(pInfo->aFree[0])) ){ + rc = sqlitepager_write(pFreeIdx); + if( rc==SQLITE_OK ){ + pInfo->aFree[n] = SWAB32(pBt, pgno); + SWAB_ADD(pBt, pInfo->nFree, 1); + sqlitepager_unref(pFreeIdx); + sqlitepager_dont_write(pBt->pPager, pgno); + return rc; + } + } + sqlitepager_unref(pFreeIdx); + } + } + if( pOvfl==0 ){ + assert( pgno>0 ); + rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pOvfl); + if( rc ) return rc; + needUnref = 1; + } + rc = sqlitepager_write(pOvfl); + if( rc ){ + if( needUnref ) sqlitepager_unref(pOvfl); + return rc; + } + pOvfl->iNext = pPage1->freeList; + pPage1->freeList = SWAB32(pBt, pgno); + memset(pOvfl->aPayload, 0, OVERFLOW_SIZE); + if( needUnref ) rc = sqlitepager_unref(pOvfl); + return rc; +} + +/* +** Erase all the data out of a cell. This involves returning overflow +** pages back the freelist. +*/ +static int clearCell(Btree *pBt, Cell *pCell){ + Pager *pPager = pBt->pPager; + OverflowPage *pOvfl; + Pgno ovfl, nextOvfl; + int rc; + + if( NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h) <= MX_LOCAL_PAYLOAD ){ + return SQLITE_OK; + } + ovfl = SWAB32(pBt, pCell->ovfl); + pCell->ovfl = 0; + while( ovfl ){ + rc = sqlitepager_get(pPager, ovfl, (void**)&pOvfl); + if( rc ) return rc; + nextOvfl = SWAB32(pBt, pOvfl->iNext); + rc = freePage(pBt, pOvfl, ovfl); + if( rc ) return rc; + sqlitepager_unref(pOvfl); + ovfl = nextOvfl; + } + return SQLITE_OK; +} + +/* +** Create a new cell from key and data. Overflow pages are allocated as +** necessary and linked to this cell. +*/ +static int fillInCell( + Btree *pBt, /* The whole Btree. Needed to allocate pages */ + Cell *pCell, /* Populate this Cell structure */ + const void *pKey, int nKey, /* The key */ + const void *pData,int nData /* The data */ +){ + OverflowPage *pOvfl, *pPrior; + Pgno *pNext; + int spaceLeft; + int n, rc; + int nPayload; + const char *pPayload; + char *pSpace; + Pgno nearby = 0; + + pCell->h.leftChild = 0; + pCell->h.nKey = SWAB16(pBt, nKey & 0xffff); + pCell->h.nKeyHi = nKey >> 16; + pCell->h.nData = SWAB16(pBt, nData & 0xffff); + pCell->h.nDataHi = nData >> 16; + pCell->h.iNext = 0; + + pNext = &pCell->ovfl; + pSpace = pCell->aPayload; + spaceLeft = MX_LOCAL_PAYLOAD; + pPayload = pKey; + pKey = 0; + nPayload = nKey; + pPrior = 0; + while( nPayload>0 ){ + if( spaceLeft==0 ){ + rc = allocatePage(pBt, (MemPage**)&pOvfl, pNext, nearby); + if( rc ){ + *pNext = 0; + }else{ + nearby = *pNext; + } + if( pPrior ) sqlitepager_unref(pPrior); + if( rc ){ + clearCell(pBt, pCell); + return rc; + } + if( pBt->needSwab ) *pNext = swab32(*pNext); + pPrior = pOvfl; + spaceLeft = OVERFLOW_SIZE; + pSpace = pOvfl->aPayload; + pNext = &pOvfl->iNext; + } + n = nPayload; + if( n>spaceLeft ) n = spaceLeft; + memcpy(pSpace, pPayload, n); + nPayload -= n; + if( nPayload==0 && pData ){ + pPayload = pData; + nPayload = nData; + pData = 0; + }else{ + pPayload += n; + } + spaceLeft -= n; + pSpace += n; + } + *pNext = 0; + if( pPrior ){ + sqlitepager_unref(pPrior); + } + return SQLITE_OK; +} + +/* +** Change the MemPage.pParent pointer on the page whose number is +** given in the second argument so that MemPage.pParent holds the +** pointer in the third argument. +*/ +static void reparentPage(Pager *pPager, Pgno pgno, MemPage *pNewParent,int idx){ + MemPage *pThis; + + if( pgno==0 ) return; + assert( pPager!=0 ); + pThis = sqlitepager_lookup(pPager, pgno); + if( pThis && pThis->isInit ){ + if( pThis->pParent!=pNewParent ){ + if( pThis->pParent ) sqlitepager_unref(pThis->pParent); + pThis->pParent = pNewParent; + if( pNewParent ) sqlitepager_ref(pNewParent); + } + pThis->idxParent = idx; + sqlitepager_unref(pThis); + } +} + +/* +** Reparent all children of the given page to be the given page. +** In other words, for every child of pPage, invoke reparentPage() +** to make sure that each child knows that pPage is its parent. +** +** This routine gets called after you memcpy() one page into +** another. +*/ +static void reparentChildPages(Btree *pBt, MemPage *pPage){ + int i; + Pager *pPager = pBt->pPager; + for(i=0; inCell; i++){ + reparentPage(pPager, SWAB32(pBt, pPage->apCell[i]->h.leftChild), pPage, i); + } + reparentPage(pPager, SWAB32(pBt, pPage->u.hdr.rightChild), pPage, i); + pPage->idxShift = 0; +} + +/* +** Remove the i-th cell from pPage. This routine effects pPage only. +** The cell content is not freed or deallocated. It is assumed that +** the cell content has been copied someplace else. This routine just +** removes the reference to the cell from pPage. +** +** "sz" must be the number of bytes in the cell. +** +** Do not bother maintaining the integrity of the linked list of Cells. +** Only the pPage->apCell[] array is important. The relinkCellList() +** routine will be called soon after this routine in order to rebuild +** the linked list. +*/ +static void dropCell(Btree *pBt, MemPage *pPage, int idx, int sz){ + int j; + assert( idx>=0 && idxnCell ); + assert( sz==cellSize(pBt, pPage->apCell[idx]) ); + assert( sqlitepager_iswriteable(pPage) ); + freeSpace(pBt, pPage, Addr(pPage->apCell[idx]) - Addr(pPage), sz); + for(j=idx; jnCell-1; j++){ + pPage->apCell[j] = pPage->apCell[j+1]; + } + pPage->nCell--; + pPage->idxShift = 1; +} + +/* +** Insert a new cell on pPage at cell index "i". pCell points to the +** content of the cell. +** +** If the cell content will fit on the page, then put it there. If it +** will not fit, then just make pPage->apCell[i] point to the content +** and set pPage->isOverfull. +** +** Do not bother maintaining the integrity of the linked list of Cells. +** Only the pPage->apCell[] array is important. The relinkCellList() +** routine will be called soon after this routine in order to rebuild +** the linked list. +*/ +static void insertCell(Btree *pBt, MemPage *pPage, int i, Cell *pCell, int sz){ + int idx, j; + assert( i>=0 && i<=pPage->nCell ); + assert( sz==cellSize(pBt, pCell) ); + assert( sqlitepager_iswriteable(pPage) ); + idx = allocateSpace(pBt, pPage, sz); + for(j=pPage->nCell; j>i; j--){ + pPage->apCell[j] = pPage->apCell[j-1]; + } + pPage->nCell++; + if( idx<=0 ){ + pPage->isOverfull = 1; + pPage->apCell[i] = pCell; + }else{ + memcpy(&pPage->u.aDisk[idx], pCell, sz); + pPage->apCell[i] = (Cell*)&pPage->u.aDisk[idx]; + } + pPage->idxShift = 1; +} + +/* +** Rebuild the linked list of cells on a page so that the cells +** occur in the order specified by the pPage->apCell[] array. +** Invoke this routine once to repair damage after one or more +** invocations of either insertCell() or dropCell(). +*/ +static void relinkCellList(Btree *pBt, MemPage *pPage){ + int i; + u16 *pIdx; + assert( sqlitepager_iswriteable(pPage) ); + pIdx = &pPage->u.hdr.firstCell; + for(i=0; inCell; i++){ + int idx = Addr(pPage->apCell[i]) - Addr(pPage); + assert( idx>0 && idxapCell[i]->h.iNext; + } + *pIdx = 0; +} + +/* +** Make a copy of the contents of pFrom into pTo. The pFrom->apCell[] +** pointers that point into pFrom->u.aDisk[] must be adjusted to point +** into pTo->u.aDisk[] instead. But some pFrom->apCell[] entries might +** not point to pFrom->u.aDisk[]. Those are unchanged. +*/ +static void copyPage(MemPage *pTo, MemPage *pFrom){ + uptr from, to; + int i; + memcpy(pTo->u.aDisk, pFrom->u.aDisk, SQLITE_USABLE_SIZE); + pTo->pParent = 0; + pTo->isInit = 1; + pTo->nCell = pFrom->nCell; + pTo->nFree = pFrom->nFree; + pTo->isOverfull = pFrom->isOverfull; + to = Addr(pTo); + from = Addr(pFrom); + for(i=0; inCell; i++){ + uptr x = Addr(pFrom->apCell[i]); + if( x>from && xapCell[i]) = x + to - from; + }else{ + pTo->apCell[i] = pFrom->apCell[i]; + } + } +} + +/* +** The following parameters determine how many adjacent pages get involved +** in a balancing operation. NN is the number of neighbors on either side +** of the page that participate in the balancing operation. NB is the +** total number of pages that participate, including the target page and +** NN neighbors on either side. +** +** The minimum value of NN is 1 (of course). Increasing NN above 1 +** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance +** in exchange for a larger degradation in INSERT and UPDATE performance. +** The value of NN appears to give the best results overall. +*/ +#define NN 1 /* Number of neighbors on either side of pPage */ +#define NB (NN*2+1) /* Total pages involved in the balance */ + +/* +** This routine redistributes Cells on pPage and up to two siblings +** of pPage so that all pages have about the same amount of free space. +** Usually one sibling on either side of pPage is used in the balancing, +** though both siblings might come from one side if pPage is the first +** or last child of its parent. If pPage has fewer than two siblings +** (something which can only happen if pPage is the root page or a +** child of root) then all available siblings participate in the balancing. +** +** The number of siblings of pPage might be increased or decreased by +** one in an effort to keep pages between 66% and 100% full. The root page +** is special and is allowed to be less than 66% full. If pPage is +** the root page, then the depth of the tree might be increased +** or decreased by one, as necessary, to keep the root page from being +** overfull or empty. +** +** This routine calls relinkCellList() on its input page regardless of +** whether or not it does any real balancing. Client routines will typically +** invoke insertCell() or dropCell() before calling this routine, so we +** need to call relinkCellList() to clean up the mess that those other +** routines left behind. +** +** pCur is left pointing to the same cell as when this routine was called +** even if that cell gets moved to a different page. pCur may be NULL. +** Set the pCur parameter to NULL if you do not care about keeping track +** of a cell as that will save this routine the work of keeping track of it. +** +** Note that when this routine is called, some of the Cells on pPage +** might not actually be stored in pPage->u.aDisk[]. This can happen +** if the page is overfull. Part of the job of this routine is to +** make sure all Cells for pPage once again fit in pPage->u.aDisk[]. +** +** In the course of balancing the siblings of pPage, the parent of pPage +** might become overfull or underfull. If that happens, then this routine +** is called recursively on the parent. +** +** If this routine fails for any reason, it might leave the database +** in a corrupted state. So if this routine fails, the database should +** be rolled back. +*/ +static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ + MemPage *pParent; /* The parent of pPage */ + int nCell; /* Number of cells in apCell[] */ + int nOld; /* Number of pages in apOld[] */ + int nNew; /* Number of pages in apNew[] */ + int nDiv; /* Number of cells in apDiv[] */ + int i, j, k; /* Loop counters */ + int idx; /* Index of pPage in pParent->apCell[] */ + int nxDiv; /* Next divider slot in pParent->apCell[] */ + int rc; /* The return code */ + int iCur; /* apCell[iCur] is the cell of the cursor */ + MemPage *pOldCurPage; /* The cursor originally points to this page */ + int subtotal; /* Subtotal of bytes in cells on one page */ + MemPage *extraUnref = 0; /* A page that needs to be unref-ed */ + MemPage *apOld[NB]; /* pPage and up to two siblings */ + Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */ + MemPage *apNew[NB+1]; /* pPage and up to NB siblings after balancing */ + Pgno pgnoNew[NB+1]; /* Page numbers for each page in apNew[] */ + int idxDiv[NB]; /* Indices of divider cells in pParent */ + Cell *apDiv[NB]; /* Divider cells in pParent */ + Cell aTemp[NB]; /* Temporary holding area for apDiv[] */ + int cntNew[NB+1]; /* Index in apCell[] of cell after i-th page */ + int szNew[NB+1]; /* Combined size of cells place on i-th page */ + MemPage aOld[NB]; /* Temporary copies of pPage and its siblings */ + Cell *apCell[(MX_CELL+2)*NB]; /* All cells from pages being balanced */ + int szCell[(MX_CELL+2)*NB]; /* Local size of all cells */ + + /* + ** Return without doing any work if pPage is neither overfull nor + ** underfull. + */ + assert( sqlitepager_iswriteable(pPage) ); + if( !pPage->isOverfull && pPage->nFreenCell>=2){ + relinkCellList(pBt, pPage); + return SQLITE_OK; + } + + /* + ** Find the parent of the page to be balanceed. + ** If there is no parent, it means this page is the root page and + ** special rules apply. + */ + pParent = pPage->pParent; + if( pParent==0 ){ + Pgno pgnoChild; + MemPage *pChild; + assert( pPage->isInit ); + if( pPage->nCell==0 ){ + if( pPage->u.hdr.rightChild ){ + /* + ** The root page is empty. Copy the one child page + ** into the root page and return. This reduces the depth + ** of the BTree by one. + */ + pgnoChild = SWAB32(pBt, pPage->u.hdr.rightChild); + rc = sqlitepager_get(pBt->pPager, pgnoChild, (void**)&pChild); + if( rc ) return rc; + memcpy(pPage, pChild, SQLITE_USABLE_SIZE); + pPage->isInit = 0; + rc = initPage(pBt, pPage, sqlitepager_pagenumber(pPage), 0); + assert( rc==SQLITE_OK ); + reparentChildPages(pBt, pPage); + if( pCur && pCur->pPage==pChild ){ + sqlitepager_unref(pChild); + pCur->pPage = pPage; + sqlitepager_ref(pPage); + } + freePage(pBt, pChild, pgnoChild); + sqlitepager_unref(pChild); + }else{ + relinkCellList(pBt, pPage); + } + return SQLITE_OK; + } + if( !pPage->isOverfull ){ + /* It is OK for the root page to be less than half full. + */ + relinkCellList(pBt, pPage); + return SQLITE_OK; + } + /* + ** If we get to here, it means the root page is overfull. + ** When this happens, Create a new child page and copy the + ** contents of the root into the child. Then make the root + ** page an empty page with rightChild pointing to the new + ** child. Then fall thru to the code below which will cause + ** the overfull child page to be split. + */ + rc = sqlitepager_write(pPage); + if( rc ) return rc; + rc = allocatePage(pBt, &pChild, &pgnoChild, sqlitepager_pagenumber(pPage)); + if( rc ) return rc; + assert( sqlitepager_iswriteable(pChild) ); + copyPage(pChild, pPage); + pChild->pParent = pPage; + pChild->idxParent = 0; + sqlitepager_ref(pPage); + pChild->isOverfull = 1; + if( pCur && pCur->pPage==pPage ){ + sqlitepager_unref(pPage); + pCur->pPage = pChild; + }else{ + extraUnref = pChild; + } + zeroPage(pBt, pPage); + pPage->u.hdr.rightChild = SWAB32(pBt, pgnoChild); + pParent = pPage; + pPage = pChild; + } + rc = sqlitepager_write(pParent); + if( rc ) return rc; + assert( pParent->isInit ); + + /* + ** Find the Cell in the parent page whose h.leftChild points back + ** to pPage. The "idx" variable is the index of that cell. If pPage + ** is the rightmost child of pParent then set idx to pParent->nCell + */ + if( pParent->idxShift ){ + Pgno pgno, swabPgno; + pgno = sqlitepager_pagenumber(pPage); + swabPgno = SWAB32(pBt, pgno); + for(idx=0; idxnCell; idx++){ + if( pParent->apCell[idx]->h.leftChild==swabPgno ){ + break; + } + } + assert( idxnCell || pParent->u.hdr.rightChild==swabPgno ); + }else{ + idx = pPage->idxParent; + } + + /* + ** Initialize variables so that it will be safe to jump + ** directly to balance_cleanup at any moment. + */ + nOld = nNew = 0; + sqlitepager_ref(pParent); + + /* + ** Find sibling pages to pPage and the Cells in pParent that divide + ** the siblings. An attempt is made to find NN siblings on either + ** side of pPage. More siblings are taken from one side, however, if + ** pPage there are fewer than NN siblings on the other side. If pParent + ** has NB or fewer children then all children of pParent are taken. + */ + nxDiv = idx - NN; + if( nxDiv + NB > pParent->nCell ){ + nxDiv = pParent->nCell - NB + 1; + } + if( nxDiv<0 ){ + nxDiv = 0; + } + nDiv = 0; + for(i=0, k=nxDiv; inCell ){ + idxDiv[i] = k; + apDiv[i] = pParent->apCell[k]; + nDiv++; + pgnoOld[i] = SWAB32(pBt, apDiv[i]->h.leftChild); + }else if( k==pParent->nCell ){ + pgnoOld[i] = SWAB32(pBt, pParent->u.hdr.rightChild); + }else{ + break; + } + rc = sqlitepager_get(pBt->pPager, pgnoOld[i], (void**)&apOld[i]); + if( rc ) goto balance_cleanup; + rc = initPage(pBt, apOld[i], pgnoOld[i], pParent); + if( rc ) goto balance_cleanup; + apOld[i]->idxParent = k; + nOld++; + } + + /* + ** Set iCur to be the index in apCell[] of the cell that the cursor + ** is pointing to. We will need this later on in order to keep the + ** cursor pointing at the same cell. If pCur points to a page that + ** has no involvement with this rebalancing, then set iCur to a large + ** number so that the iCur==j tests always fail in the main cell + ** distribution loop below. + */ + if( pCur ){ + iCur = 0; + for(i=0; ipPage==apOld[i] ){ + iCur += pCur->idx; + break; + } + iCur += apOld[i]->nCell; + if( ipPage==pParent && pCur->idx==idxDiv[i] ){ + break; + } + iCur++; + } + pOldCurPage = pCur->pPage; + } + + /* + ** Make copies of the content of pPage and its siblings into aOld[]. + ** The rest of this function will use data from the copies rather + ** that the original pages since the original pages will be in the + ** process of being overwritten. + */ + for(i=0; inCell; j++){ + apCell[nCell] = pOld->apCell[j]; + szCell[nCell] = cellSize(pBt, apCell[nCell]); + nCell++; + } + if( ih.leftChild)==pgnoOld[i] ); + apCell[nCell]->h.leftChild = pOld->u.hdr.rightChild; + nCell++; + } + } + + /* + ** Figure out the number of pages needed to hold all nCell cells. + ** Store this number in "k". Also compute szNew[] which is the total + ** size of all cells on the i-th page and cntNew[] which is the index + ** in apCell[] of the cell that divides path i from path i+1. + ** cntNew[k] should equal nCell. + ** + ** This little patch of code is critical for keeping the tree + ** balanced. + */ + for(subtotal=k=i=0; i USABLE_SPACE ){ + szNew[k] = subtotal - szCell[i]; + cntNew[k] = i; + subtotal = 0; + k++; + } + } + szNew[k] = subtotal; + cntNew[k] = nCell; + k++; + for(i=k-1; i>0; i--){ + while( szNew[i]0 ); + szNew[i] += szCell[cntNew[i-1]]; + szNew[i-1] -= szCell[cntNew[i-1]-1]; + } + } + assert( cntNew[0]>0 ); + + /* + ** Allocate k new pages. Reuse old pages where possible. + */ + for(i=0; iisInit = 1; + } + + /* Free any old pages that were not reused as new pages. + */ + while( ii ){ + int t; + MemPage *pT; + t = pgnoNew[i]; + pT = apNew[i]; + pgnoNew[i] = pgnoNew[minI]; + apNew[i] = apNew[minI]; + pgnoNew[minI] = t; + apNew[minI] = pT; + } + } + + /* + ** Evenly distribute the data in apCell[] across the new pages. + ** Insert divider cells into pParent as necessary. + */ + j = 0; + for(i=0; inFree>=szCell[j] ); + if( pCur && iCur==j ){ pCur->pPage = pNew; pCur->idx = pNew->nCell; } + insertCell(pBt, pNew, pNew->nCell, apCell[j], szCell[j]); + j++; + } + assert( pNew->nCell>0 ); + assert( !pNew->isOverfull ); + relinkCellList(pBt, pNew); + if( iu.hdr.rightChild = apCell[j]->h.leftChild; + apCell[j]->h.leftChild = SWAB32(pBt, pgnoNew[i]); + if( pCur && iCur==j ){ pCur->pPage = pParent; pCur->idx = nxDiv; } + insertCell(pBt, pParent, nxDiv, apCell[j], szCell[j]); + j++; + nxDiv++; + } + } + assert( j==nCell ); + apNew[nNew-1]->u.hdr.rightChild = aOld[nOld-1].u.hdr.rightChild; + if( nxDiv==pParent->nCell ){ + pParent->u.hdr.rightChild = SWAB32(pBt, pgnoNew[nNew-1]); + }else{ + pParent->apCell[nxDiv]->h.leftChild = SWAB32(pBt, pgnoNew[nNew-1]); + } + if( pCur ){ + if( j<=iCur && pCur->pPage==pParent && pCur->idx>idxDiv[nOld-1] ){ + assert( pCur->pPage==pOldCurPage ); + pCur->idx += nNew - nOld; + }else{ + assert( pOldCurPage!=0 ); + sqlitepager_ref(pCur->pPage); + sqlitepager_unref(pOldCurPage); + } + } + + /* + ** Reparent children of all cells. + */ + for(i=0; ipPage==0 ){ + pCur->pPage = pParent; + pCur->idx = 0; + }else{ + sqlitepager_unref(pParent); + } + return rc; +} + +/* +** This routine checks all cursors that point to the same table +** as pCur points to. If any of those cursors were opened with +** wrFlag==0 then this routine returns SQLITE_LOCKED. If all +** cursors point to the same table were opened with wrFlag==1 +** then this routine returns SQLITE_OK. +** +** In addition to checking for read-locks (where a read-lock +** means a cursor opened with wrFlag==0) this routine also moves +** all cursors other than pCur so that they are pointing to the +** first Cell on root page. This is necessary because an insert +** or delete might change the number of cells on a page or delete +** a page entirely and we do not want to leave any cursors +** pointing to non-existant pages or cells. +*/ +static int checkReadLocks(BtCursor *pCur){ + BtCursor *p; + assert( pCur->wrFlag ); + for(p=pCur->pShared; p!=pCur; p=p->pShared){ + assert( p ); + assert( p->pgnoRoot==pCur->pgnoRoot ); + if( p->wrFlag==0 ) return SQLITE_LOCKED; + if( sqlitepager_pagenumber(p->pPage)!=p->pgnoRoot ){ + moveToRoot(p); + } + } + return SQLITE_OK; +} + +/* +** Insert a new record into the BTree. The key is given by (pKey,nKey) +** and the data is given by (pData,nData). The cursor is used only to +** define what database the record should be inserted into. The cursor +** is left pointing at the new record. +*/ +static int fileBtreeInsert( + BtCursor *pCur, /* Insert data into the table of this cursor */ + const void *pKey, int nKey, /* The key of the new record */ + const void *pData, int nData /* The data of the new record */ +){ + Cell newCell; + int rc; + int loc; + int szNew; + MemPage *pPage; + Btree *pBt = pCur->pBt; + + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } + if( !pBt->inTrans || nKey+nData==0 ){ + /* Must start a transaction before doing an insert */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( !pBt->readOnly ); + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Cursor not open for writing */ + } + if( checkReadLocks(pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + rc = fileBtreeMoveto(pCur, pKey, nKey, &loc); + if( rc ) return rc; + pPage = pCur->pPage; + assert( pPage->isInit ); + rc = sqlitepager_write(pPage); + if( rc ) return rc; + rc = fillInCell(pBt, &newCell, pKey, nKey, pData, nData); + if( rc ) return rc; + szNew = cellSize(pBt, &newCell); + if( loc==0 ){ + newCell.h.leftChild = pPage->apCell[pCur->idx]->h.leftChild; + rc = clearCell(pBt, pPage->apCell[pCur->idx]); + if( rc ) return rc; + dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pPage->apCell[pCur->idx])); + }else if( loc<0 && pPage->nCell>0 ){ + assert( pPage->u.hdr.rightChild==0 ); /* Must be a leaf page */ + pCur->idx++; + }else{ + assert( pPage->u.hdr.rightChild==0 ); /* Must be a leaf page */ + } + insertCell(pBt, pPage, pCur->idx, &newCell, szNew); + rc = balance(pCur->pBt, pPage, pCur); + /* sqliteBtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */ + /* fflush(stdout); */ + pCur->eSkip = SKIP_INVALID; + return rc; +} + +/* +** Delete the entry that the cursor is pointing to. +** +** The cursor is left pointing at either the next or the previous +** entry. If the cursor is left pointing to the next entry, then +** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to +** sqliteBtreeNext() to be a no-op. That way, you can always call +** sqliteBtreeNext() after a delete and the cursor will be left +** pointing to the first entry after the deleted entry. Similarly, +** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to +** the entry prior to the deleted entry so that a subsequent call to +** sqliteBtreePrevious() will always leave the cursor pointing at the +** entry immediately before the one that was deleted. +*/ +static int fileBtreeDelete(BtCursor *pCur){ + MemPage *pPage = pCur->pPage; + Cell *pCell; + int rc; + Pgno pgnoChild; + Btree *pBt = pCur->pBt; + + assert( pPage->isInit ); + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } + if( !pBt->inTrans ){ + /* Must start a transaction before doing a delete */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + assert( !pBt->readOnly ); + if( pCur->idx >= pPage->nCell ){ + return SQLITE_ERROR; /* The cursor is not pointing to anything */ + } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Did not open this cursor for writing */ + } + if( checkReadLocks(pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + rc = sqlitepager_write(pPage); + if( rc ) return rc; + pCell = pPage->apCell[pCur->idx]; + pgnoChild = SWAB32(pBt, pCell->h.leftChild); + clearCell(pBt, pCell); + if( pgnoChild ){ + /* + ** The entry we are about to delete is not a leaf so if we do not + ** do something we will leave a hole on an internal page. + ** We have to fill the hole by moving in a cell from a leaf. The + ** next Cell after the one to be deleted is guaranteed to exist and + ** to be a leaf so we can use it. + */ + BtCursor leafCur; + Cell *pNext; + int szNext; + int notUsed; + getTempCursor(pCur, &leafCur); + rc = fileBtreeNext(&leafCur, ¬Used); + if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_NOMEM ) rc = SQLITE_CORRUPT; + return rc; + } + rc = sqlitepager_write(leafCur.pPage); + if( rc ) return rc; + dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pCell)); + pNext = leafCur.pPage->apCell[leafCur.idx]; + szNext = cellSize(pBt, pNext); + pNext->h.leftChild = SWAB32(pBt, pgnoChild); + insertCell(pBt, pPage, pCur->idx, pNext, szNext); + rc = balance(pBt, pPage, pCur); + if( rc ) return rc; + pCur->eSkip = SKIP_NEXT; + dropCell(pBt, leafCur.pPage, leafCur.idx, szNext); + rc = balance(pBt, leafCur.pPage, pCur); + releaseTempCursor(&leafCur); + }else{ + dropCell(pBt, pPage, pCur->idx, cellSize(pBt, pCell)); + if( pCur->idx>=pPage->nCell ){ + pCur->idx = pPage->nCell-1; + if( pCur->idx<0 ){ + pCur->idx = 0; + pCur->eSkip = SKIP_NEXT; + }else{ + pCur->eSkip = SKIP_PREV; + } + }else{ + pCur->eSkip = SKIP_NEXT; + } + rc = balance(pBt, pPage, pCur); + } + return rc; +} + +/* +** Create a new BTree table. Write into *piTable the page +** number for the root page of the new table. +** +** In the current implementation, BTree tables and BTree indices are the +** the same. In the future, we may change this so that BTree tables +** are restricted to having a 4-byte integer key and arbitrary data and +** BTree indices are restricted to having an arbitrary key and no data. +** But for now, this routine also serves to create indices. +*/ +static int fileBtreeCreateTable(Btree *pBt, int *piTable){ + MemPage *pRoot; + Pgno pgnoRoot; + int rc; + if( !pBt->inTrans ){ + /* Must start a transaction first */ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + if( pBt->readOnly ){ + return SQLITE_READONLY; + } + rc = allocatePage(pBt, &pRoot, &pgnoRoot, 0); + if( rc ) return rc; + assert( sqlitepager_iswriteable(pRoot) ); + zeroPage(pBt, pRoot); + sqlitepager_unref(pRoot); + *piTable = (int)pgnoRoot; + return SQLITE_OK; +} + +/* +** Erase the given database page and all its children. Return +** the page to the freelist. +*/ +static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){ + MemPage *pPage; + int rc; + Cell *pCell; + int idx; + + rc = sqlitepager_get(pBt->pPager, pgno, (void**)&pPage); + if( rc ) return rc; + rc = sqlitepager_write(pPage); + if( rc ) return rc; + rc = initPage(pBt, pPage, pgno, 0); + if( rc ) return rc; + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx>0 ){ + pCell = (Cell*)&pPage->u.aDisk[idx]; + idx = SWAB16(pBt, pCell->h.iNext); + if( pCell->h.leftChild ){ + rc = clearDatabasePage(pBt, SWAB32(pBt, pCell->h.leftChild), 1); + if( rc ) return rc; + } + rc = clearCell(pBt, pCell); + if( rc ) return rc; + } + if( pPage->u.hdr.rightChild ){ + rc = clearDatabasePage(pBt, SWAB32(pBt, pPage->u.hdr.rightChild), 1); + if( rc ) return rc; + } + if( freePageFlag ){ + rc = freePage(pBt, pPage, pgno); + }else{ + zeroPage(pBt, pPage); + } + sqlitepager_unref(pPage); + return rc; +} + +/* +** Delete all information from a single table in the database. +*/ +static int fileBtreeClearTable(Btree *pBt, int iTable){ + int rc; + BtCursor *pCur; + if( !pBt->inTrans ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pgnoRoot==(Pgno)iTable ){ + if( pCur->wrFlag==0 ) return SQLITE_LOCKED; + moveToRoot(pCur); + } + } + rc = clearDatabasePage(pBt, (Pgno)iTable, 0); + if( rc ){ + fileBtreeRollback(pBt); + } + return rc; +} + +/* +** Erase all information in a table and add the root of the table to +** the freelist. Except, the root of the principle table (the one on +** page 2) is never added to the freelist. +*/ +static int fileBtreeDropTable(Btree *pBt, int iTable){ + int rc; + MemPage *pPage; + BtCursor *pCur; + if( !pBt->inTrans ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pgnoRoot==(Pgno)iTable ){ + return SQLITE_LOCKED; /* Cannot drop a table that has a cursor */ + } + } + rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage); + if( rc ) return rc; + rc = fileBtreeClearTable(pBt, iTable); + if( rc ) return rc; + if( iTable>2 ){ + rc = freePage(pBt, pPage, iTable); + }else{ + zeroPage(pBt, pPage); + } + sqlitepager_unref(pPage); + return rc; +} + +#if 0 /* UNTESTED */ +/* +** Copy all cell data from one database file into another. +** pages back the freelist. +*/ +static int copyCell(Btree *pBtFrom, BTree *pBtTo, Cell *pCell){ + Pager *pFromPager = pBtFrom->pPager; + OverflowPage *pOvfl; + Pgno ovfl, nextOvfl; + Pgno *pPrev; + int rc = SQLITE_OK; + MemPage *pNew, *pPrevPg; + Pgno new; + + if( NKEY(pBtTo, pCell->h) + NDATA(pBtTo, pCell->h) <= MX_LOCAL_PAYLOAD ){ + return SQLITE_OK; + } + pPrev = &pCell->ovfl; + pPrevPg = 0; + ovfl = SWAB32(pBtTo, pCell->ovfl); + while( ovfl && rc==SQLITE_OK ){ + rc = sqlitepager_get(pFromPager, ovfl, (void**)&pOvfl); + if( rc ) return rc; + nextOvfl = SWAB32(pBtFrom, pOvfl->iNext); + rc = allocatePage(pBtTo, &pNew, &new, 0); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pNew); + if( rc==SQLITE_OK ){ + memcpy(pNew, pOvfl, SQLITE_USABLE_SIZE); + *pPrev = SWAB32(pBtTo, new); + if( pPrevPg ){ + sqlitepager_unref(pPrevPg); + } + pPrev = &pOvfl->iNext; + pPrevPg = pNew; + } + } + sqlitepager_unref(pOvfl); + ovfl = nextOvfl; + } + if( pPrevPg ){ + sqlitepager_unref(pPrevPg); + } + return rc; +} +#endif + + +#if 0 /* UNTESTED */ +/* +** Copy a page of data from one database over to another. +*/ +static int copyDatabasePage( + Btree *pBtFrom, + Pgno pgnoFrom, + Btree *pBtTo, + Pgno *pTo +){ + MemPage *pPageFrom, *pPage; + Pgno to; + int rc; + Cell *pCell; + int idx; + + rc = sqlitepager_get(pBtFrom->pPager, pgno, (void**)&pPageFrom); + if( rc ) return rc; + rc = allocatePage(pBt, &pPage, pTo, 0); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pPage); + } + if( rc==SQLITE_OK ){ + memcpy(pPage, pPageFrom, SQLITE_USABLE_SIZE); + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx>0 ){ + pCell = (Cell*)&pPage->u.aDisk[idx]; + idx = SWAB16(pBt, pCell->h.iNext); + if( pCell->h.leftChild ){ + Pgno newChld; + rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pCell->h.leftChild), + pBtTo, &newChld); + if( rc ) return rc; + pCell->h.leftChild = SWAB32(pBtFrom, newChld); + } + rc = copyCell(pBtFrom, pBtTo, pCell); + if( rc ) return rc; + } + if( pPage->u.hdr.rightChild ){ + Pgno newChld; + rc = copyDatabasePage(pBtFrom, SWAB32(pBtFrom, pPage->u.hdr.rightChild), + pBtTo, &newChld); + if( rc ) return rc; + pPage->u.hdr.rightChild = SWAB32(pBtTo, newChild); + } + } + sqlitepager_unref(pPage); + return rc; +} +#endif + +/* +** Read the meta-information out of a database file. +*/ +static int fileBtreeGetMeta(Btree *pBt, int *aMeta){ + PageOne *pP1; + int rc; + int i; + + rc = sqlitepager_get(pBt->pPager, 1, (void**)&pP1); + if( rc ) return rc; + aMeta[0] = SWAB32(pBt, pP1->nFree); + for(i=0; iaMeta)/sizeof(pP1->aMeta[0]); i++){ + aMeta[i+1] = SWAB32(pBt, pP1->aMeta[i]); + } + sqlitepager_unref(pP1); + return SQLITE_OK; +} + +/* +** Write meta-information back into the database. +*/ +static int fileBtreeUpdateMeta(Btree *pBt, int *aMeta){ + PageOne *pP1; + int rc, i; + if( !pBt->inTrans ){ + return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; + } + pP1 = pBt->page1; + rc = sqlitepager_write(pP1); + if( rc ) return rc; + for(i=0; iaMeta)/sizeof(pP1->aMeta[0]); i++){ + pP1->aMeta[i] = SWAB32(pBt, aMeta[i+1]); + } + return SQLITE_OK; +} + +/****************************************************************************** +** The complete implementation of the BTree subsystem is above this line. +** All the code the follows is for testing and troubleshooting the BTree +** subsystem. None of the code that follows is used during normal operation. +******************************************************************************/ + +/* +** Print a disassembly of the given page on standard output. This routine +** is used for debugging and testing only. +*/ +#ifdef SQLITE_TEST +static int fileBtreePageDump(Btree *pBt, int pgno, int recursive){ + int rc; + MemPage *pPage; + int i, j; + int nFree; + u16 idx; + char range[20]; + unsigned char payload[20]; + rc = sqlitepager_get(pBt->pPager, (Pgno)pgno, (void**)&pPage); + if( rc ){ + return rc; + } + if( recursive ) printf("PAGE %d:\n", pgno); + i = 0; + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx>0 && idx<=SQLITE_USABLE_SIZE-MIN_CELL_SIZE ){ + Cell *pCell = (Cell*)&pPage->u.aDisk[idx]; + int sz = cellSize(pBt, pCell); + sprintf(range,"%d..%d", idx, idx+sz-1); + sz = NKEY(pBt, pCell->h) + NDATA(pBt, pCell->h); + if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1; + memcpy(payload, pCell->aPayload, sz); + for(j=0; j0x7f ) payload[j] = '.'; + } + payload[sz] = 0; + printf( + "cell %2d: i=%-10s chld=%-4d nk=%-4d nd=%-4d payload=%s\n", + i, range, (int)pCell->h.leftChild, + NKEY(pBt, pCell->h), NDATA(pBt, pCell->h), + payload + ); + if( pPage->isInit && pPage->apCell[i]!=pCell ){ + printf("**** apCell[%d] does not match on prior entry ****\n", i); + } + i++; + idx = SWAB16(pBt, pCell->h.iNext); + } + if( idx!=0 ){ + printf("ERROR: next cell index out of range: %d\n", idx); + } + printf("right_child: %d\n", SWAB32(pBt, pPage->u.hdr.rightChild)); + nFree = 0; + i = 0; + idx = SWAB16(pBt, pPage->u.hdr.firstFree); + while( idx>0 && idxu.aDisk[idx]; + sprintf(range,"%d..%d", idx, idx+p->iSize-1); + nFree += SWAB16(pBt, p->iSize); + printf("freeblock %2d: i=%-10s size=%-4d total=%d\n", + i, range, SWAB16(pBt, p->iSize), nFree); + idx = SWAB16(pBt, p->iNext); + i++; + } + if( idx!=0 ){ + printf("ERROR: next freeblock index out of range: %d\n", idx); + } + if( recursive && pPage->u.hdr.rightChild!=0 ){ + idx = SWAB16(pBt, pPage->u.hdr.firstCell); + while( idx>0 && idxu.aDisk[idx]; + fileBtreePageDump(pBt, SWAB32(pBt, pCell->h.leftChild), 1); + idx = SWAB16(pBt, pCell->h.iNext); + } + fileBtreePageDump(pBt, SWAB32(pBt, pPage->u.hdr.rightChild), 1); + } + sqlitepager_unref(pPage); + return SQLITE_OK; +} +#endif + +#ifdef SQLITE_TEST +/* +** Fill aResult[] with information about the entry and page that the +** cursor is pointing to. +** +** aResult[0] = The page number +** aResult[1] = The entry number +** aResult[2] = Total number of entries on this page +** aResult[3] = Size of this entry +** aResult[4] = Number of free bytes on this page +** aResult[5] = Number of free blocks on the page +** aResult[6] = Page number of the left child of this entry +** aResult[7] = Page number of the right child for the whole page +** +** This routine is used for testing and debugging only. +*/ +static int fileBtreeCursorDump(BtCursor *pCur, int *aResult){ + int cnt, idx; + MemPage *pPage = pCur->pPage; + Btree *pBt = pCur->pBt; + aResult[0] = sqlitepager_pagenumber(pPage); + aResult[1] = pCur->idx; + aResult[2] = pPage->nCell; + if( pCur->idx>=0 && pCur->idxnCell ){ + aResult[3] = cellSize(pBt, pPage->apCell[pCur->idx]); + aResult[6] = SWAB32(pBt, pPage->apCell[pCur->idx]->h.leftChild); + }else{ + aResult[3] = 0; + aResult[6] = 0; + } + aResult[4] = pPage->nFree; + cnt = 0; + idx = SWAB16(pBt, pPage->u.hdr.firstFree); + while( idx>0 && idxu.aDisk[idx])->iNext); + } + aResult[5] = cnt; + aResult[7] = SWAB32(pBt, pPage->u.hdr.rightChild); + return SQLITE_OK; +} +#endif + +/* +** Return the pager associated with a BTree. This routine is used for +** testing and debugging only. +*/ +static Pager *fileBtreePager(Btree *pBt){ + return pBt->pPager; +} + +/* +** This structure is passed around through all the sanity checking routines +** in order to keep track of some global state information. +*/ +typedef struct IntegrityCk IntegrityCk; +struct IntegrityCk { + Btree *pBt; /* The tree being checked out */ + Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ + int nPage; /* Number of pages in the database */ + int *anRef; /* Number of times each page is referenced */ + char *zErrMsg; /* An error message. NULL of no errors seen. */ +}; + +/* +** Append a message to the error message string. +*/ +static void checkAppendMsg(IntegrityCk *pCheck, char *zMsg1, char *zMsg2){ + if( pCheck->zErrMsg ){ + char *zOld = pCheck->zErrMsg; + pCheck->zErrMsg = 0; + sqliteSetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0); + sqliteFree(zOld); + }else{ + sqliteSetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0); + } +} + +/* +** Add 1 to the reference count for page iPage. If this is the second +** reference to the page, add an error message to pCheck->zErrMsg. +** Return 1 if there are 2 ore more references to the page and 0 if +** if this is the first reference to the page. +** +** Also check that the page number is in bounds. +*/ +static int checkRef(IntegrityCk *pCheck, int iPage, char *zContext){ + if( iPage==0 ) return 1; + if( iPage>pCheck->nPage || iPage<0 ){ + char zBuf[100]; + sprintf(zBuf, "invalid page number %d", iPage); + checkAppendMsg(pCheck, zContext, zBuf); + return 1; + } + if( pCheck->anRef[iPage]==1 ){ + char zBuf[100]; + sprintf(zBuf, "2nd reference to page %d", iPage); + checkAppendMsg(pCheck, zContext, zBuf); + return 1; + } + return (pCheck->anRef[iPage]++)>1; +} + +/* +** Check the integrity of the freelist or of an overflow page list. +** Verify that the number of pages on the list is N. +*/ +static void checkList( + IntegrityCk *pCheck, /* Integrity checking context */ + int isFreeList, /* True for a freelist. False for overflow page list */ + int iPage, /* Page number for first page in the list */ + int N, /* Expected number of pages in the list */ + char *zContext /* Context for error messages */ +){ + int i; + char zMsg[100]; + while( N-- > 0 ){ + OverflowPage *pOvfl; + if( iPage<1 ){ + sprintf(zMsg, "%d pages missing from overflow list", N+1); + checkAppendMsg(pCheck, zContext, zMsg); + break; + } + if( checkRef(pCheck, iPage, zContext) ) break; + if( sqlitepager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){ + sprintf(zMsg, "failed to get page %d", iPage); + checkAppendMsg(pCheck, zContext, zMsg); + break; + } + if( isFreeList ){ + FreelistInfo *pInfo = (FreelistInfo*)pOvfl->aPayload; + int n = SWAB32(pCheck->pBt, pInfo->nFree); + for(i=0; ipBt, pInfo->aFree[i]), zContext); + } + N -= n; + } + iPage = SWAB32(pCheck->pBt, pOvfl->iNext); + sqlitepager_unref(pOvfl); + } +} + +/* +** Return negative if zKey1zKey2. +*/ +static int keyCompare( + const char *zKey1, int nKey1, + const char *zKey2, int nKey2 +){ + int min = nKey1>nKey2 ? nKey2 : nKey1; + int c = memcmp(zKey1, zKey2, min); + if( c==0 ){ + c = nKey1 - nKey2; + } + return c; +} + +/* +** Do various sanity checks on a single page of a tree. Return +** the tree depth. Root pages return 0. Parents of root pages +** return 1, and so forth. +** +** These checks are done: +** +** 1. Make sure that cells and freeblocks do not overlap +** but combine to completely cover the page. +** 2. Make sure cell keys are in order. +** 3. Make sure no key is less than or equal to zLowerBound. +** 4. Make sure no key is greater than or equal to zUpperBound. +** 5. Check the integrity of overflow pages. +** 6. Recursively call checkTreePage on all children. +** 7. Verify that the depth of all children is the same. +** 8. Make sure this page is at least 33% full or else it is +** the root of the tree. +*/ +static int checkTreePage( + IntegrityCk *pCheck, /* Context for the sanity check */ + int iPage, /* Page number of the page to check */ + MemPage *pParent, /* Parent page */ + char *zParentContext, /* Parent context */ + char *zLowerBound, /* All keys should be greater than this, if not NULL */ + int nLower, /* Number of characters in zLowerBound */ + char *zUpperBound, /* All keys should be less than this, if not NULL */ + int nUpper /* Number of characters in zUpperBound */ +){ + MemPage *pPage; + int i, rc, depth, d2, pgno; + char *zKey1, *zKey2; + int nKey1, nKey2; + BtCursor cur; + Btree *pBt; + char zMsg[100]; + char zContext[100]; + char hit[SQLITE_USABLE_SIZE]; + + /* Check that the page exists + */ + cur.pBt = pBt = pCheck->pBt; + if( iPage==0 ) return 0; + if( checkRef(pCheck, iPage, zParentContext) ) return 0; + sprintf(zContext, "On tree page %d: ", iPage); + if( (rc = sqlitepager_get(pCheck->pPager, (Pgno)iPage, (void**)&pPage))!=0 ){ + sprintf(zMsg, "unable to get the page. error code=%d", rc); + checkAppendMsg(pCheck, zContext, zMsg); + return 0; + } + if( (rc = initPage(pBt, pPage, (Pgno)iPage, pParent))!=0 ){ + sprintf(zMsg, "initPage() returns error code %d", rc); + checkAppendMsg(pCheck, zContext, zMsg); + sqlitepager_unref(pPage); + return 0; + } + + /* Check out all the cells. + */ + depth = 0; + if( zLowerBound ){ + zKey1 = sqliteMalloc( nLower+1 ); + memcpy(zKey1, zLowerBound, nLower); + zKey1[nLower] = 0; + }else{ + zKey1 = 0; + } + nKey1 = nLower; + cur.pPage = pPage; + for(i=0; inCell; i++){ + Cell *pCell = pPage->apCell[i]; + int sz; + + /* Check payload overflow pages + */ + nKey2 = NKEY(pBt, pCell->h); + sz = nKey2 + NDATA(pBt, pCell->h); + sprintf(zContext, "On page %d cell %d: ", iPage, i); + if( sz>MX_LOCAL_PAYLOAD ){ + int nPage = (sz - MX_LOCAL_PAYLOAD + OVERFLOW_SIZE - 1)/OVERFLOW_SIZE; + checkList(pCheck, 0, SWAB32(pBt, pCell->ovfl), nPage, zContext); + } + + /* Check that keys are in the right order + */ + cur.idx = i; + zKey2 = sqliteMallocRaw( nKey2+1 ); + getPayload(&cur, 0, nKey2, zKey2); + if( zKey1 && keyCompare(zKey1, nKey1, zKey2, nKey2)>=0 ){ + checkAppendMsg(pCheck, zContext, "Key is out of order"); + } + + /* Check sanity of left child page. + */ + pgno = SWAB32(pBt, pCell->h.leftChild); + d2 = checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zKey2,nKey2); + if( i>0 && d2!=depth ){ + checkAppendMsg(pCheck, zContext, "Child page depth differs"); + } + depth = d2; + sqliteFree(zKey1); + zKey1 = zKey2; + nKey1 = nKey2; + } + pgno = SWAB32(pBt, pPage->u.hdr.rightChild); + sprintf(zContext, "On page %d at right child: ", iPage); + checkTreePage(pCheck, pgno, pPage, zContext, zKey1,nKey1,zUpperBound,nUpper); + sqliteFree(zKey1); + + /* Check for complete coverage of the page + */ + memset(hit, 0, sizeof(hit)); + memset(hit, 1, sizeof(PageHdr)); + for(i=SWAB16(pBt, pPage->u.hdr.firstCell); i>0 && iu.aDisk[i]; + int j; + for(j=i+cellSize(pBt, pCell)-1; j>=i; j--) hit[j]++; + i = SWAB16(pBt, pCell->h.iNext); + } + for(i=SWAB16(pBt,pPage->u.hdr.firstFree); i>0 && iu.aDisk[i]; + int j; + for(j=i+SWAB16(pBt,pFBlk->iSize)-1; j>=i; j--) hit[j]++; + i = SWAB16(pBt,pFBlk->iNext); + } + for(i=0; i1 ){ + sprintf(zMsg, "Multiple uses for byte %d of page %d", i, iPage); + checkAppendMsg(pCheck, zMsg, 0); + break; + } + } + + /* Check that free space is kept to a minimum + */ +#if 0 + if( pParent && pParent->nCell>2 && pPage->nFree>3*SQLITE_USABLE_SIZE/4 ){ + sprintf(zMsg, "free space (%d) greater than max (%d)", pPage->nFree, + SQLITE_USABLE_SIZE/3); + checkAppendMsg(pCheck, zContext, zMsg); + } +#endif + + sqlitepager_unref(pPage); + return depth; +} + +/* +** This routine does a complete check of the given BTree file. aRoot[] is +** an array of pages numbers were each page number is the root page of +** a table. nRoot is the number of entries in aRoot. +** +** If everything checks out, this routine returns NULL. If something is +** amiss, an error message is written into memory obtained from malloc() +** and a pointer to that error message is returned. The calling function +** is responsible for freeing the error message when it is done. +*/ +char *fileBtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){ + int i; + int nRef; + IntegrityCk sCheck; + + nRef = *sqlitepager_stats(pBt->pPager); + if( lockBtree(pBt)!=SQLITE_OK ){ + return sqliteStrDup("Unable to acquire a read lock on the database"); + } + sCheck.pBt = pBt; + sCheck.pPager = pBt->pPager; + sCheck.nPage = sqlitepager_pagecount(sCheck.pPager); + if( sCheck.nPage==0 ){ + unlockBtreeIfUnused(pBt); + return 0; + } + sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); + sCheck.anRef[1] = 1; + for(i=2; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; } + sCheck.zErrMsg = 0; + + /* Check the integrity of the freelist + */ + checkList(&sCheck, 1, SWAB32(pBt, pBt->page1->freeList), + SWAB32(pBt, pBt->page1->nFree), "Main freelist: "); + + /* Check all the tables. + */ + for(i=0; ipPager) ){ + char zBuf[100]; + sprintf(zBuf, + "Outstanding page count goes from %d to %d during this analysis", + nRef, *sqlitepager_stats(pBt->pPager) + ); + checkAppendMsg(&sCheck, zBuf, 0); + } + + /* Clean up and report errors. + */ + sqliteFree(sCheck.anRef); + return sCheck.zErrMsg; +} + +/* +** Return the full pathname of the underlying database file. +*/ +static const char *fileBtreeGetFilename(Btree *pBt){ + assert( pBt->pPager!=0 ); + return sqlitepager_filename(pBt->pPager); +} + +/* +** Copy the complete content of pBtFrom into pBtTo. A transaction +** must be active for both files. +** +** The size of file pBtFrom may be reduced by this operation. +** If anything goes wrong, the transaction on pBtFrom is rolled back. +*/ +static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){ + int rc = SQLITE_OK; + Pgno i, nPage, nToPage; + + if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR; + if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR; + if( pBtTo->pCursor ) return SQLITE_BUSY; + memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_USABLE_SIZE); + rc = sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1); + nToPage = sqlitepager_pagecount(pBtTo->pPager); + nPage = sqlitepager_pagecount(pBtFrom->pPager); + for(i=2; rc==SQLITE_OK && i<=nPage; i++){ + void *pPage; + rc = sqlitepager_get(pBtFrom->pPager, i, &pPage); + if( rc ) break; + rc = sqlitepager_overwrite(pBtTo->pPager, i, pPage); + if( rc ) break; + sqlitepager_unref(pPage); + } + for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){ + void *pPage; + rc = sqlitepager_get(pBtTo->pPager, i, &pPage); + if( rc ) break; + rc = sqlitepager_write(pPage); + sqlitepager_unref(pPage); + sqlitepager_dont_write(pBtTo->pPager, i); + } + if( !rc && nPagepPager, nPage); + } + if( rc ){ + fileBtreeRollback(pBtTo); + } + return rc; +} + +/* +** The following tables contain pointers to all of the interface +** routines for this implementation of the B*Tree backend. To +** substitute a different implemention of the backend, one has merely +** to provide pointers to alternative functions in similar tables. +*/ +static BtOps sqliteBtreeOps = { + fileBtreeClose, + fileBtreeSetCacheSize, + fileBtreeSetSafetyLevel, + fileBtreeBeginTrans, + fileBtreeCommit, + fileBtreeRollback, + fileBtreeBeginCkpt, + fileBtreeCommitCkpt, + fileBtreeRollbackCkpt, + fileBtreeCreateTable, + fileBtreeCreateTable, /* Really sqliteBtreeCreateIndex() */ + fileBtreeDropTable, + fileBtreeClearTable, + fileBtreeCursor, + fileBtreeGetMeta, + fileBtreeUpdateMeta, + fileBtreeIntegrityCheck, + fileBtreeGetFilename, + fileBtreeCopyFile, + fileBtreePager, +#ifdef SQLITE_TEST + fileBtreePageDump, +#endif +}; +static BtCursorOps sqliteBtreeCursorOps = { + fileBtreeMoveto, + fileBtreeDelete, + fileBtreeInsert, + fileBtreeFirst, + fileBtreeLast, + fileBtreeNext, + fileBtreePrevious, + fileBtreeKeySize, + fileBtreeKey, + fileBtreeKeyCompare, + fileBtreeDataSize, + fileBtreeData, + fileBtreeCloseCursor, +#ifdef SQLITE_TEST + fileBtreeCursorDump, +#endif +}; diff --git a/src/libs/sqlite2/btree.h b/src/libs/sqlite2/btree.h new file mode 100644 index 00000000..5a11b60e --- /dev/null +++ b/src/libs/sqlite2/btree.h @@ -0,0 +1,156 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +** +** @(#) $Id: btree.h 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtOps BtOps; +typedef struct BtCursorOps BtCursorOps; + + +/* +** An instance of the following structure contains pointers to all +** methods against an open BTree. Alternative BTree implementations +** (examples: file based versus in-memory) can be created by substituting +** different methods. Users of the BTree cannot tell the difference. +** +** In C++ we could do this by defining a virtual base class and then +** creating subclasses for each different implementation. But this is +** C not C++ so we have to be a little more explicit. +*/ +struct BtOps { + int (*Close)(Btree*); + int (*SetCacheSize)(Btree*, int); + int (*SetSafetyLevel)(Btree*, int); + int (*BeginTrans)(Btree*); + int (*Commit)(Btree*); + int (*Rollback)(Btree*); + int (*BeginCkpt)(Btree*); + int (*CommitCkpt)(Btree*); + int (*RollbackCkpt)(Btree*); + int (*CreateTable)(Btree*, int*); + int (*CreateIndex)(Btree*, int*); + int (*DropTable)(Btree*, int); + int (*ClearTable)(Btree*, int); + int (*Cursor)(Btree*, int iTable, int wrFlag, BtCursor **ppCur); + int (*GetMeta)(Btree*, int*); + int (*UpdateMeta)(Btree*, int*); + char *(*IntegrityCheck)(Btree*, int*, int); + const char *(*GetFilename)(Btree*); + int (*Copyfile)(Btree*,Btree*); + struct Pager *(*Pager)(Btree*); +#ifdef SQLITE_TEST + int (*PageDump)(Btree*, int, int); +#endif +}; + +/* +** An instance of this structure defines all of the methods that can +** be executed against a cursor. +*/ +struct BtCursorOps { + int (*Moveto)(BtCursor*, const void *pKey, int nKey, int *pRes); + int (*Delete)(BtCursor*); + int (*Insert)(BtCursor*, const void *pKey, int nKey, + const void *pData, int nData); + int (*First)(BtCursor*, int *pRes); + int (*Last)(BtCursor*, int *pRes); + int (*Next)(BtCursor*, int *pRes); + int (*Previous)(BtCursor*, int *pRes); + int (*KeySize)(BtCursor*, int *pSize); + int (*Key)(BtCursor*, int offset, int amt, char *zBuf); + int (*KeyCompare)(BtCursor*, const void *pKey, int nKey, + int nIgnore, int *pRes); + int (*DataSize)(BtCursor*, int *pSize); + int (*Data)(BtCursor*, int offset, int amt, char *zBuf); + int (*CloseCursor)(BtCursor*); +#ifdef SQLITE_TEST + int (*CursorDump)(BtCursor*, int*); +#endif +}; + +/* +** The number of 4-byte "meta" values contained on the first page of each +** database file. +*/ +#define SQLITE_N_BTREE_META 10 + +int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree); +int sqliteRbtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree); + +#define btOps(pBt) (*((BtOps **)(pBt))) +#define btCOps(pCur) (*((BtCursorOps **)(pCur))) + +#define sqliteBtreeClose(pBt) (btOps(pBt)->Close(pBt)) +#define sqliteBtreeSetCacheSize(pBt, sz) (btOps(pBt)->SetCacheSize(pBt, sz)) +#define sqliteBtreeSetSafetyLevel(pBt, sl) (btOps(pBt)->SetSafetyLevel(pBt, sl)) +#define sqliteBtreeBeginTrans(pBt) (btOps(pBt)->BeginTrans(pBt)) +#define sqliteBtreeCommit(pBt) (btOps(pBt)->Commit(pBt)) +#define sqliteBtreeRollback(pBt) (btOps(pBt)->Rollback(pBt)) +#define sqliteBtreeBeginCkpt(pBt) (btOps(pBt)->BeginCkpt(pBt)) +#define sqliteBtreeCommitCkpt(pBt) (btOps(pBt)->CommitCkpt(pBt)) +#define sqliteBtreeRollbackCkpt(pBt) (btOps(pBt)->RollbackCkpt(pBt)) +#define sqliteBtreeCreateTable(pBt,piTable)\ + (btOps(pBt)->CreateTable(pBt,piTable)) +#define sqliteBtreeCreateIndex(pBt, piIndex)\ + (btOps(pBt)->CreateIndex(pBt, piIndex)) +#define sqliteBtreeDropTable(pBt, iTable) (btOps(pBt)->DropTable(pBt, iTable)) +#define sqliteBtreeClearTable(pBt, iTable)\ + (btOps(pBt)->ClearTable(pBt, iTable)) +#define sqliteBtreeCursor(pBt, iTable, wrFlag, ppCur)\ + (btOps(pBt)->Cursor(pBt, iTable, wrFlag, ppCur)) +#define sqliteBtreeMoveto(pCur, pKey, nKey, pRes)\ + (btCOps(pCur)->Moveto(pCur, pKey, nKey, pRes)) +#define sqliteBtreeDelete(pCur) (btCOps(pCur)->Delete(pCur)) +#define sqliteBtreeInsert(pCur, pKey, nKey, pData, nData) \ + (btCOps(pCur)->Insert(pCur, pKey, nKey, pData, nData)) +#define sqliteBtreeFirst(pCur, pRes) (btCOps(pCur)->First(pCur, pRes)) +#define sqliteBtreeLast(pCur, pRes) (btCOps(pCur)->Last(pCur, pRes)) +#define sqliteBtreeNext(pCur, pRes) (btCOps(pCur)->Next(pCur, pRes)) +#define sqliteBtreePrevious(pCur, pRes) (btCOps(pCur)->Previous(pCur, pRes)) +#define sqliteBtreeKeySize(pCur, pSize) (btCOps(pCur)->KeySize(pCur, pSize) ) +#define sqliteBtreeKey(pCur, offset, amt, zBuf)\ + (btCOps(pCur)->Key(pCur, offset, amt, zBuf)) +#define sqliteBtreeKeyCompare(pCur, pKey, nKey, nIgnore, pRes)\ + (btCOps(pCur)->KeyCompare(pCur, pKey, nKey, nIgnore, pRes)) +#define sqliteBtreeDataSize(pCur, pSize) (btCOps(pCur)->DataSize(pCur, pSize)) +#define sqliteBtreeData(pCur, offset, amt, zBuf)\ + (btCOps(pCur)->Data(pCur, offset, amt, zBuf)) +#define sqliteBtreeCloseCursor(pCur) (btCOps(pCur)->CloseCursor(pCur)) +#define sqliteBtreeGetMeta(pBt, aMeta) (btOps(pBt)->GetMeta(pBt, aMeta)) +#define sqliteBtreeUpdateMeta(pBt, aMeta) (btOps(pBt)->UpdateMeta(pBt, aMeta)) +#define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\ + (btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot)) +#define sqliteBtreeGetFilename(pBt) (btOps(pBt)->GetFilename(pBt)) +#define sqliteBtreeCopyFile(pBt1, pBt2) (btOps(pBt1)->Copyfile(pBt1, pBt2)) +#define sqliteBtreePager(pBt) (btOps(pBt)->Pager(pBt)) + +#ifdef SQLITE_TEST +#define sqliteBtreePageDump(pBt, pgno, recursive)\ + (btOps(pBt)->PageDump(pBt, pgno, recursive)) +#define sqliteBtreeCursorDump(pCur, aResult)\ + (btCOps(pCur)->CursorDump(pCur, aResult)) +int btree_native_byte_order; +#endif /* SQLITE_TEST */ + + +#endif /* _BTREE_H_ */ diff --git a/src/libs/sqlite2/btree_rb.c b/src/libs/sqlite2/btree_rb.c new file mode 100644 index 00000000..18e49b81 --- /dev/null +++ b/src/libs/sqlite2/btree_rb.c @@ -0,0 +1,1488 @@ +/* +** 2003 Feb 4 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** $Id: btree_rb.c 875429 2008-10-24 12:20:41Z cgilles $ +** +** This file implements an in-core database using Red-Black balanced +** binary trees. +** +** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC. +*/ +#include "btree.h" +#include "sqliteInt.h" +#include + +/* +** Omit this whole file if the SQLITE_OMIT_INMEMORYDB macro is +** defined. This allows a lot of code to be omitted for installations +** that do not need it. +*/ +#ifndef SQLITE_OMIT_INMEMORYDB + + +typedef struct BtRbTree BtRbTree; +typedef struct BtRbNode BtRbNode; +typedef struct BtRollbackOp BtRollbackOp; +typedef struct Rbtree Rbtree; +typedef struct RbtCursor RbtCursor; + +/* Forward declarations */ +static BtOps sqliteRbtreeOps; +static BtCursorOps sqliteRbtreeCursorOps; + +/* + * During each transaction (or checkpoint), a linked-list of + * "rollback-operations" is accumulated. If the transaction is rolled back, + * then the list of operations must be executed (to restore the database to + * it's state before the transaction started). If the transaction is to be + * committed, just delete the list. + * + * Each operation is represented as follows, depending on the value of eOp: + * + * ROLLBACK_INSERT -> Need to insert (pKey, pData) into table iTab. + * ROLLBACK_DELETE -> Need to delete the record (pKey) into table iTab. + * ROLLBACK_CREATE -> Need to create table iTab. + * ROLLBACK_DROP -> Need to drop table iTab. + */ +struct BtRollbackOp { + u8 eOp; + int iTab; + int nKey; + void *pKey; + int nData; + void *pData; + BtRollbackOp *pNext; +}; + +/* +** Legal values for BtRollbackOp.eOp: +*/ +#define ROLLBACK_INSERT 1 /* Insert a record */ +#define ROLLBACK_DELETE 2 /* Delete a record */ +#define ROLLBACK_CREATE 3 /* Create a table */ +#define ROLLBACK_DROP 4 /* Drop a table */ + +struct Rbtree { + BtOps *pOps; /* Function table */ + int aMetaData[SQLITE_N_BTREE_META]; + + int next_idx; /* next available table index */ + Hash tblHash; /* All created tables, by index */ + u8 isAnonymous; /* True if this Rbtree is to be deleted when closed */ + u8 eTransState; /* State of this Rbtree wrt transactions */ + + BtRollbackOp *pTransRollback; + BtRollbackOp *pCheckRollback; + BtRollbackOp *pCheckRollbackTail; +}; + +/* +** Legal values for Rbtree.eTransState. +*/ +#define TRANS_NONE 0 /* No transaction is in progress */ +#define TRANS_INTRANSACTION 1 /* A transaction is in progress */ +#define TRANS_INCHECKPOINT 2 /* A checkpoint is in progress */ +#define TRANS_ROLLBACK 3 /* We are currently rolling back a checkpoint or + * transaction. */ + +struct RbtCursor { + BtCursorOps *pOps; /* Function table */ + Rbtree *pRbtree; + BtRbTree *pTree; + int iTree; /* Index of pTree in pRbtree */ + BtRbNode *pNode; + RbtCursor *pShared; /* List of all cursors on the same Rbtree */ + u8 eSkip; /* Determines if next step operation is a no-op */ + u8 wrFlag; /* True if this cursor is open for writing */ +}; + +/* +** Legal values for RbtCursor.eSkip. +*/ +#define SKIP_NONE 0 /* Always step the cursor */ +#define SKIP_NEXT 1 /* The next sqliteRbtreeNext() is a no-op */ +#define SKIP_PREV 2 /* The next sqliteRbtreePrevious() is a no-op */ +#define SKIP_INVALID 3 /* Calls to Next() and Previous() are invalid */ + +struct BtRbTree { + RbtCursor *pCursors; /* All cursors pointing to this tree */ + BtRbNode *pHead; /* Head of the tree, or NULL */ +}; + +struct BtRbNode { + int nKey; + void *pKey; + int nData; + void *pData; + u8 isBlack; /* true for a black node, 0 for a red node */ + BtRbNode *pParent; /* Nodes parent node, NULL for the tree head */ + BtRbNode *pLeft; /* Nodes left child, or NULL */ + BtRbNode *pRight; /* Nodes right child, or NULL */ + + int nBlackHeight; /* Only used during the red-black integrity check */ +}; + +/* Forward declarations */ +static int memRbtreeMoveto( + RbtCursor* pCur, + const void *pKey, + int nKey, + int *pRes +); +static int memRbtreeClearTable(Rbtree* tree, int n); +static int memRbtreeNext(RbtCursor* pCur, int *pRes); +static int memRbtreeLast(RbtCursor* pCur, int *pRes); +static int memRbtreePrevious(RbtCursor* pCur, int *pRes); + + +/* +** This routine checks all cursors that point to the same table +** as pCur points to. If any of those cursors were opened with +** wrFlag==0 then this routine returns SQLITE_LOCKED. If all +** cursors point to the same table were opened with wrFlag==1 +** then this routine returns SQLITE_OK. +** +** In addition to checking for read-locks (where a read-lock +** means a cursor opened with wrFlag==0) this routine also NULLs +** out the pNode field of all other cursors. +** This is necessary because an insert +** or delete might change erase the node out from under +** another cursor. +*/ +static int checkReadLocks(RbtCursor *pCur){ + RbtCursor *p; + assert( pCur->wrFlag ); + for(p=pCur->pTree->pCursors; p; p=p->pShared){ + if( p!=pCur ){ + if( p->wrFlag==0 ) return SQLITE_LOCKED; + p->pNode = 0; + } + } + return SQLITE_OK; +} + +/* + * The key-compare function for the red-black trees. Returns as follows: + * + * (key1 < key2) -1 + * (key1 == key2) 0 + * (key1 > key2) 1 + * + * Keys are compared using memcmp(). If one key is an exact prefix of the + * other, then the shorter key is less than the longer key. + */ +static int key_compare(void const*pKey1, int nKey1, void const*pKey2, int nKey2) +{ + int mcmp = memcmp(pKey1, pKey2, (nKey1 <= nKey2)?nKey1:nKey2); + if( mcmp == 0){ + if( nKey1 == nKey2 ) return 0; + return ((nKey1 < nKey2)?-1:1); + } + return ((mcmp>0)?1:-1); +} + +/* + * Perform the LEFT-rotate transformation on node X of tree pTree. This + * transform is part of the red-black balancing code. + * + * | | + * X Y + * / \ / \ + * a Y X c + * / \ / \ + * b c a b + * + * BEFORE AFTER + */ +static void leftRotate(BtRbTree *pTree, BtRbNode *pX) +{ + BtRbNode *pY; + BtRbNode *pb; + pY = pX->pRight; + pb = pY->pLeft; + + pY->pParent = pX->pParent; + if( pX->pParent ){ + if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY; + else pX->pParent->pRight = pY; + } + pY->pLeft = pX; + pX->pParent = pY; + pX->pRight = pb; + if( pb ) pb->pParent = pX; + if( pTree->pHead == pX ) pTree->pHead = pY; +} + +/* + * Perform the RIGHT-rotate transformation on node X of tree pTree. This + * transform is part of the red-black balancing code. + * + * | | + * X Y + * / \ / \ + * Y c a X + * / \ / \ + * a b b c + * + * BEFORE AFTER + */ +static void rightRotate(BtRbTree *pTree, BtRbNode *pX) +{ + BtRbNode *pY; + BtRbNode *pb; + pY = pX->pLeft; + pb = pY->pRight; + + pY->pParent = pX->pParent; + if( pX->pParent ){ + if( pX->pParent->pLeft == pX ) pX->pParent->pLeft = pY; + else pX->pParent->pRight = pY; + } + pY->pRight = pX; + pX->pParent = pY; + pX->pLeft = pb; + if( pb ) pb->pParent = pX; + if( pTree->pHead == pX ) pTree->pHead = pY; +} + +/* + * A string-manipulation helper function for check_redblack_tree(). If (orig == + * NULL) a copy of val is returned. If (orig != NULL) then a copy of the * + * concatenation of orig and val is returned. The original orig is deleted + * (using sqliteFree()). + */ +static char *append_val(char * orig, char const * val){ + char *z; + if( !orig ){ + z = sqliteStrDup( val ); + } else{ + z = 0; + sqliteSetString(&z, orig, val, (char*)0); + sqliteFree( orig ); + } + return z; +} + +/* + * Append a string representation of the entire node to orig and return it. + * This is used to produce debugging information if check_redblack_tree() finds + * a problem with a red-black binary tree. + */ +static char *append_node(char * orig, BtRbNode *pNode, int indent) +{ + char buf[128]; + int i; + + for( i=0; iisBlack ){ + orig = append_val(orig, " B \n"); + }else{ + orig = append_val(orig, " R \n"); + } + orig = append_node( orig, pNode->pLeft, indent ); + orig = append_node( orig, pNode->pRight, indent ); + }else{ + orig = append_val(orig, "\n"); + } + return orig; +} + +/* + * Print a representation of a node to stdout. This function is only included + * so you can call it from within a debugger if things get really bad. It + * is not called from anyplace in the code. + */ +static void print_node(BtRbNode *pNode) +{ + char * str = append_node(0, pNode, 0); + printf("%s", str); + + /* Suppress a warning message about print_node() being unused */ + (void)print_node; +} + +/* + * Check the following properties of the red-black tree: + * (1) - If a node is red, both of it's children are black + * (2) - Each path from a given node to a leaf (NULL) node passes thru the + * same number of black nodes + * + * If there is a problem, append a description (using append_val() ) to *msg. + */ +static void check_redblack_tree(BtRbTree * tree, char ** msg) +{ + BtRbNode *pNode; + + /* 0 -> came from parent + * 1 -> came from left + * 2 -> came from right */ + int prev_step = 0; + + pNode = tree->pHead; + while( pNode ){ + switch( prev_step ){ + case 0: + if( pNode->pLeft ){ + pNode = pNode->pLeft; + }else{ + prev_step = 1; + } + break; + case 1: + if( pNode->pRight ){ + pNode = pNode->pRight; + prev_step = 0; + }else{ + prev_step = 2; + } + break; + case 2: + /* Check red-black property (1) */ + if( !pNode->isBlack && + ( (pNode->pLeft && !pNode->pLeft->isBlack) || + (pNode->pRight && !pNode->pRight->isBlack) ) + ){ + char buf[128]; + sprintf(buf, "Red node with red child at %p\n", pNode); + *msg = append_val(*msg, buf); + *msg = append_node(*msg, tree->pHead, 0); + *msg = append_val(*msg, "\n"); + } + + /* Check red-black property (2) */ + { + int leftHeight = 0; + int rightHeight = 0; + if( pNode->pLeft ){ + leftHeight += pNode->pLeft->nBlackHeight; + leftHeight += (pNode->pLeft->isBlack?1:0); + } + if( pNode->pRight ){ + rightHeight += pNode->pRight->nBlackHeight; + rightHeight += (pNode->pRight->isBlack?1:0); + } + if( leftHeight != rightHeight ){ + char buf[128]; + sprintf(buf, "Different black-heights at %p\n", pNode); + *msg = append_val(*msg, buf); + *msg = append_node(*msg, tree->pHead, 0); + *msg = append_val(*msg, "\n"); + } + pNode->nBlackHeight = leftHeight; + } + + if( pNode->pParent ){ + if( pNode == pNode->pParent->pLeft ) prev_step = 1; + else prev_step = 2; + } + pNode = pNode->pParent; + break; + default: assert(0); + } + } +} + +/* + * Node pX has just been inserted into pTree (by code in sqliteRbtreeInsert()). + * It is possible that pX is a red node with a red parent, which is a violation + * of the red-black tree properties. This function performs rotations and + * color changes to rebalance the tree + */ +static void do_insert_balancing(BtRbTree *pTree, BtRbNode *pX) +{ + /* In the first iteration of this loop, pX points to the red node just + * inserted in the tree. If the parent of pX exists (pX is not the root + * node) and is red, then the properties of the red-black tree are + * violated. + * + * At the start of any subsequent iterations, pX points to a red node + * with a red parent. In all other respects the tree is a legal red-black + * binary tree. */ + while( pX != pTree->pHead && !pX->pParent->isBlack ){ + BtRbNode *pUncle; + BtRbNode *pGrandparent; + + /* Grandparent of pX must exist and must be black. */ + pGrandparent = pX->pParent->pParent; + assert( pGrandparent ); + assert( pGrandparent->isBlack ); + + /* Uncle of pX may or may not exist. */ + if( pX->pParent == pGrandparent->pLeft ) + pUncle = pGrandparent->pRight; + else + pUncle = pGrandparent->pLeft; + + /* If the uncle of pX exists and is red, we do the following: + * | | + * G(b) G(r) + * / \ / \ + * U(r) P(r) U(b) P(b) + * \ \ + * X(r) X(r) + * + * BEFORE AFTER + * pX is then set to G. If the parent of G is red, then the while loop + * will run again. */ + if( pUncle && !pUncle->isBlack ){ + pGrandparent->isBlack = 0; + pUncle->isBlack = 1; + pX->pParent->isBlack = 1; + pX = pGrandparent; + }else{ + + if( pX->pParent == pGrandparent->pLeft ){ + if( pX == pX->pParent->pRight ){ + /* If pX is a right-child, do the following transform, essentially + * to change pX into a left-child: + * | | + * G(b) G(b) + * / \ / \ + * P(r) U(b) X(r) U(b) + * \ / + * X(r) P(r) <-- new X + * + * BEFORE AFTER + */ + pX = pX->pParent; + leftRotate(pTree, pX); + } + + /* Do the following transform, which balances the tree :) + * | | + * G(b) P(b) + * / \ / \ + * P(r) U(b) X(r) G(r) + * / \ + * X(r) U(b) + * + * BEFORE AFTER + */ + assert( pGrandparent == pX->pParent->pParent ); + pGrandparent->isBlack = 0; + pX->pParent->isBlack = 1; + rightRotate( pTree, pGrandparent ); + + }else{ + /* This code is symetric to the illustrated case above. */ + if( pX == pX->pParent->pLeft ){ + pX = pX->pParent; + rightRotate(pTree, pX); + } + assert( pGrandparent == pX->pParent->pParent ); + pGrandparent->isBlack = 0; + pX->pParent->isBlack = 1; + leftRotate( pTree, pGrandparent ); + } + } + } + pTree->pHead->isBlack = 1; +} + +/* + * A child of pParent, which in turn had child pX, has just been removed from + * pTree (the figure below depicts the operation, Z is being removed). pParent + * or pX, or both may be NULL. + * | | + * P P + * / \ / \ + * Z X + * / \ + * X nil + * + * This function is only called if Z was black. In this case the red-black tree + * properties have been violated, and pX has an "extra black". This function + * performs rotations and color-changes to re-balance the tree. + */ +static +void do_delete_balancing(BtRbTree *pTree, BtRbNode *pX, BtRbNode *pParent) +{ + BtRbNode *pSib; + + /* TODO: Comment this code! */ + while( pX != pTree->pHead && (!pX || pX->isBlack) ){ + if( pX == pParent->pLeft ){ + pSib = pParent->pRight; + if( pSib && !(pSib->isBlack) ){ + pSib->isBlack = 1; + pParent->isBlack = 0; + leftRotate(pTree, pParent); + pSib = pParent->pRight; + } + if( !pSib ){ + pX = pParent; + }else if( + (!pSib->pLeft || pSib->pLeft->isBlack) && + (!pSib->pRight || pSib->pRight->isBlack) ) { + pSib->isBlack = 0; + pX = pParent; + }else{ + if( (!pSib->pRight || pSib->pRight->isBlack) ){ + if( pSib->pLeft ) pSib->pLeft->isBlack = 1; + pSib->isBlack = 0; + rightRotate( pTree, pSib ); + pSib = pParent->pRight; + } + pSib->isBlack = pParent->isBlack; + pParent->isBlack = 1; + if( pSib->pRight ) pSib->pRight->isBlack = 1; + leftRotate(pTree, pParent); + pX = pTree->pHead; + } + }else{ + pSib = pParent->pLeft; + if( pSib && !(pSib->isBlack) ){ + pSib->isBlack = 1; + pParent->isBlack = 0; + rightRotate(pTree, pParent); + pSib = pParent->pLeft; + } + if( !pSib ){ + pX = pParent; + }else if( + (!pSib->pLeft || pSib->pLeft->isBlack) && + (!pSib->pRight || pSib->pRight->isBlack) ){ + pSib->isBlack = 0; + pX = pParent; + }else{ + if( (!pSib->pLeft || pSib->pLeft->isBlack) ){ + if( pSib->pRight ) pSib->pRight->isBlack = 1; + pSib->isBlack = 0; + leftRotate( pTree, pSib ); + pSib = pParent->pLeft; + } + pSib->isBlack = pParent->isBlack; + pParent->isBlack = 1; + if( pSib->pLeft ) pSib->pLeft->isBlack = 1; + rightRotate(pTree, pParent); + pX = pTree->pHead; + } + } + pParent = pX->pParent; + } + if( pX ) pX->isBlack = 1; +} + +/* + * Create table n in tree pRbtree. Table n must not exist. + */ +static void btreeCreateTable(Rbtree* pRbtree, int n) +{ + BtRbTree *pNewTbl = sqliteMalloc(sizeof(BtRbTree)); + sqliteHashInsert(&pRbtree->tblHash, 0, n, pNewTbl); +} + +/* + * Log a single "rollback-op" for the given Rbtree. See comments for struct + * BtRollbackOp. + */ +static void btreeLogRollbackOp(Rbtree* pRbtree, BtRollbackOp *pRollbackOp) +{ + assert( pRbtree->eTransState == TRANS_INCHECKPOINT || + pRbtree->eTransState == TRANS_INTRANSACTION ); + if( pRbtree->eTransState == TRANS_INTRANSACTION ){ + pRollbackOp->pNext = pRbtree->pTransRollback; + pRbtree->pTransRollback = pRollbackOp; + } + if( pRbtree->eTransState == TRANS_INCHECKPOINT ){ + if( !pRbtree->pCheckRollback ){ + pRbtree->pCheckRollbackTail = pRollbackOp; + } + pRollbackOp->pNext = pRbtree->pCheckRollback; + pRbtree->pCheckRollback = pRollbackOp; + } +} + +int sqliteRbtreeOpen( + const char *zFilename, + int mode, + int nPg, + Btree **ppBtree +){ + Rbtree **ppRbtree = (Rbtree**)ppBtree; + *ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree)); + if( sqlite_malloc_failed ) goto open_no_mem; + sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0); + + /* Create a binary tree for the SQLITE_MASTER table at location 2 */ + btreeCreateTable(*ppRbtree, 2); + if( sqlite_malloc_failed ) goto open_no_mem; + (*ppRbtree)->next_idx = 3; + (*ppRbtree)->pOps = &sqliteRbtreeOps; + /* Set file type to 4; this is so that "attach ':memory:' as ...." does not + ** think that the database in uninitialised and refuse to attach + */ + (*ppRbtree)->aMetaData[2] = 4; + + return SQLITE_OK; + +open_no_mem: + *ppBtree = 0; + return SQLITE_NOMEM; +} + +/* + * Create a new table in the supplied Rbtree. Set *n to the new table number. + * Return SQLITE_OK if the operation is a success. + */ +static int memRbtreeCreateTable(Rbtree* tree, int* n) +{ + assert( tree->eTransState != TRANS_NONE ); + + *n = tree->next_idx++; + btreeCreateTable(tree, *n); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + + /* Set up the rollback structure (if we are not doing this as part of a + * rollback) */ + if( tree->eTransState != TRANS_ROLLBACK ){ + BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp)); + if( pRollbackOp==0 ) return SQLITE_NOMEM; + pRollbackOp->eOp = ROLLBACK_DROP; + pRollbackOp->iTab = *n; + btreeLogRollbackOp(tree, pRollbackOp); + } + + return SQLITE_OK; +} + +/* + * Delete table n from the supplied Rbtree. + */ +static int memRbtreeDropTable(Rbtree* tree, int n) +{ + BtRbTree *pTree; + assert( tree->eTransState != TRANS_NONE ); + + memRbtreeClearTable(tree, n); + pTree = sqliteHashInsert(&tree->tblHash, 0, n, 0); + assert(pTree); + assert( pTree->pCursors==0 ); + sqliteFree(pTree); + + if( tree->eTransState != TRANS_ROLLBACK ){ + BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp)); + if( pRollbackOp==0 ) return SQLITE_NOMEM; + pRollbackOp->eOp = ROLLBACK_CREATE; + pRollbackOp->iTab = n; + btreeLogRollbackOp(tree, pRollbackOp); + } + + return SQLITE_OK; +} + +static int memRbtreeKeyCompare(RbtCursor* pCur, const void *pKey, int nKey, + int nIgnore, int *pRes) +{ + assert(pCur); + + if( !pCur->pNode ) { + *pRes = -1; + } else { + if( (pCur->pNode->nKey - nIgnore) < 0 ){ + *pRes = -1; + }else{ + *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey-nIgnore, + pKey, nKey); + } + } + return SQLITE_OK; +} + +/* + * Get a new cursor for table iTable of the supplied Rbtree. The wrFlag + * parameter indicates that the cursor is open for writing. + * + * Note that RbtCursor.eSkip and RbtCursor.pNode both initialize to 0. + */ +static int memRbtreeCursor( + Rbtree* tree, + int iTable, + int wrFlag, + RbtCursor **ppCur +){ + RbtCursor *pCur; + assert(tree); + pCur = *ppCur = sqliteMalloc(sizeof(RbtCursor)); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + pCur->pTree = sqliteHashFind(&tree->tblHash, 0, iTable); + assert( pCur->pTree ); + pCur->pRbtree = tree; + pCur->iTree = iTable; + pCur->pOps = &sqliteRbtreeCursorOps; + pCur->wrFlag = wrFlag; + pCur->pShared = pCur->pTree->pCursors; + pCur->pTree->pCursors = pCur; + + assert( (*ppCur)->pTree ); + return SQLITE_OK; +} + +/* + * Insert a new record into the Rbtree. The key is given by (pKey,nKey) + * and the data is given by (pData,nData). The cursor is used only to + * define what database the record should be inserted into. The cursor + * is left pointing at the new record. + * + * If the key exists already in the tree, just replace the data. + */ +static int memRbtreeInsert( + RbtCursor* pCur, + const void *pKey, + int nKey, + const void *pDataInput, + int nData +){ + void * pData; + int match; + + /* It is illegal to call sqliteRbtreeInsert() if we are + ** not in a transaction */ + assert( pCur->pRbtree->eTransState != TRANS_NONE ); + + /* Make sure some other cursor isn't trying to read this same table */ + if( checkReadLocks(pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + + /* Take a copy of the input data now, in case we need it for the + * replace case */ + pData = sqliteMallocRaw(nData); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + memcpy(pData, pDataInput, nData); + + /* Move the cursor to a node near the key to be inserted. If the key already + * exists in the table, then (match == 0). In this case we can just replace + * the data associated with the entry, we don't need to manipulate the tree. + * + * If there is no exact match, then the cursor points at what would be either + * the predecessor (match == -1) or successor (match == 1) of the + * searched-for key, were it to be inserted. The new node becomes a child of + * this node. + * + * The new node is initially red. + */ + memRbtreeMoveto( pCur, pKey, nKey, &match); + if( match ){ + BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode)); + if( pNode==0 ) return SQLITE_NOMEM; + pNode->nKey = nKey; + pNode->pKey = sqliteMallocRaw(nKey); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + memcpy(pNode->pKey, pKey, nKey); + pNode->nData = nData; + pNode->pData = pData; + if( pCur->pNode ){ + switch( match ){ + case -1: + assert( !pCur->pNode->pRight ); + pNode->pParent = pCur->pNode; + pCur->pNode->pRight = pNode; + break; + case 1: + assert( !pCur->pNode->pLeft ); + pNode->pParent = pCur->pNode; + pCur->pNode->pLeft = pNode; + break; + default: + assert(0); + } + }else{ + pCur->pTree->pHead = pNode; + } + + /* Point the cursor at the node just inserted, as per SQLite requirements */ + pCur->pNode = pNode; + + /* A new node has just been inserted, so run the balancing code */ + do_insert_balancing(pCur->pTree, pNode); + + /* Set up a rollback-op in case we have to roll this operation back */ + if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ + BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); + if( pOp==0 ) return SQLITE_NOMEM; + pOp->eOp = ROLLBACK_DELETE; + pOp->iTab = pCur->iTree; + pOp->nKey = pNode->nKey; + pOp->pKey = sqliteMallocRaw( pOp->nKey ); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + memcpy( pOp->pKey, pNode->pKey, pOp->nKey ); + btreeLogRollbackOp(pCur->pRbtree, pOp); + } + + }else{ + /* No need to insert a new node in the tree, as the key already exists. + * Just clobber the current nodes data. */ + + /* Set up a rollback-op in case we have to roll this operation back */ + if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ + BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); + if( pOp==0 ) return SQLITE_NOMEM; + pOp->iTab = pCur->iTree; + pOp->nKey = pCur->pNode->nKey; + pOp->pKey = sqliteMallocRaw( pOp->nKey ); + if( sqlite_malloc_failed ) return SQLITE_NOMEM; + memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey ); + pOp->nData = pCur->pNode->nData; + pOp->pData = pCur->pNode->pData; + pOp->eOp = ROLLBACK_INSERT; + btreeLogRollbackOp(pCur->pRbtree, pOp); + }else{ + sqliteFree( pCur->pNode->pData ); + } + + /* Actually clobber the nodes data */ + pCur->pNode->pData = pData; + pCur->pNode->nData = nData; + } + + return SQLITE_OK; +} + +/* Move the cursor so that it points to an entry near pKey. +** Return a success code. +** +** *pRes<0 The cursor is left pointing at an entry that +** is smaller than pKey or if the table is empty +** and the cursor is therefore left point to nothing. +** +** *pRes==0 The cursor is left pointing at an entry that +** exactly matches pKey. +** +** *pRes>0 The cursor is left pointing at an entry that +** is larger than pKey. +*/ +static int memRbtreeMoveto( + RbtCursor* pCur, + const void *pKey, + int nKey, + int *pRes +){ + BtRbNode *pTmp = 0; + + pCur->pNode = pCur->pTree->pHead; + *pRes = -1; + while( pCur->pNode && *pRes ) { + *pRes = key_compare(pCur->pNode->pKey, pCur->pNode->nKey, pKey, nKey); + pTmp = pCur->pNode; + switch( *pRes ){ + case 1: /* cursor > key */ + pCur->pNode = pCur->pNode->pLeft; + break; + case -1: /* cursor < key */ + pCur->pNode = pCur->pNode->pRight; + break; + } + } + + /* If (pCur->pNode == NULL), then we have failed to find a match. Set + * pCur->pNode to pTmp, which is either NULL (if the tree is empty) or the + * last node traversed in the search. In either case the relation ship + * between pTmp and the searched for key is already stored in *pRes. pTmp is + * either the successor or predecessor of the key we tried to move to. */ + if( !pCur->pNode ) pCur->pNode = pTmp; + pCur->eSkip = SKIP_NONE; + + return SQLITE_OK; +} + + +/* +** Delete the entry that the cursor is pointing to. +** +** The cursor is left pointing at either the next or the previous +** entry. If the cursor is left pointing to the next entry, then +** the pCur->eSkip flag is set to SKIP_NEXT which forces the next call to +** sqliteRbtreeNext() to be a no-op. That way, you can always call +** sqliteRbtreeNext() after a delete and the cursor will be left +** pointing to the first entry after the deleted entry. Similarly, +** pCur->eSkip is set to SKIP_PREV is the cursor is left pointing to +** the entry prior to the deleted entry so that a subsequent call to +** sqliteRbtreePrevious() will always leave the cursor pointing at the +** entry immediately before the one that was deleted. +*/ +static int memRbtreeDelete(RbtCursor* pCur) +{ + BtRbNode *pZ; /* The one being deleted */ + BtRbNode *pChild; /* The child of the spliced out node */ + + /* It is illegal to call sqliteRbtreeDelete() if we are + ** not in a transaction */ + assert( pCur->pRbtree->eTransState != TRANS_NONE ); + + /* Make sure some other cursor isn't trying to read this same table */ + if( checkReadLocks(pCur) ){ + return SQLITE_LOCKED; /* The table pCur points to has a read lock */ + } + + pZ = pCur->pNode; + if( !pZ ){ + return SQLITE_OK; + } + + /* If we are not currently doing a rollback, set up a rollback op for this + * deletion */ + if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){ + BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) ); + if( pOp==0 ) return SQLITE_NOMEM; + pOp->iTab = pCur->iTree; + pOp->nKey = pZ->nKey; + pOp->pKey = pZ->pKey; + pOp->nData = pZ->nData; + pOp->pData = pZ->pData; + pOp->eOp = ROLLBACK_INSERT; + btreeLogRollbackOp(pCur->pRbtree, pOp); + } + + /* First do a standard binary-tree delete (node pZ is to be deleted). How + * to do this depends on how many children pZ has: + * + * If pZ has no children or one child, then splice out pZ. If pZ has two + * children, splice out the successor of pZ and replace the key and data of + * pZ with the key and data of the spliced out successor. */ + if( pZ->pLeft && pZ->pRight ){ + BtRbNode *pTmp; + int dummy; + pCur->eSkip = SKIP_NONE; + memRbtreeNext(pCur, &dummy); + assert( dummy == 0 ); + if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){ + sqliteFree(pZ->pKey); + sqliteFree(pZ->pData); + } + pZ->pData = pCur->pNode->pData; + pZ->nData = pCur->pNode->nData; + pZ->pKey = pCur->pNode->pKey; + pZ->nKey = pCur->pNode->nKey; + pTmp = pZ; + pZ = pCur->pNode; + pCur->pNode = pTmp; + pCur->eSkip = SKIP_NEXT; + }else{ + int res; + pCur->eSkip = SKIP_NONE; + memRbtreeNext(pCur, &res); + pCur->eSkip = SKIP_NEXT; + if( res ){ + memRbtreeLast(pCur, &res); + memRbtreePrevious(pCur, &res); + pCur->eSkip = SKIP_PREV; + } + if( pCur->pRbtree->eTransState == TRANS_ROLLBACK ){ + sqliteFree(pZ->pKey); + sqliteFree(pZ->pData); + } + } + + /* pZ now points at the node to be spliced out. This block does the + * splicing. */ + { + BtRbNode **ppParentSlot = 0; + assert( !pZ->pLeft || !pZ->pRight ); /* pZ has at most one child */ + pChild = ((pZ->pLeft)?pZ->pLeft:pZ->pRight); + if( pZ->pParent ){ + assert( pZ == pZ->pParent->pLeft || pZ == pZ->pParent->pRight ); + ppParentSlot = ((pZ == pZ->pParent->pLeft) + ?&pZ->pParent->pLeft:&pZ->pParent->pRight); + *ppParentSlot = pChild; + }else{ + pCur->pTree->pHead = pChild; + } + if( pChild ) pChild->pParent = pZ->pParent; + } + + /* pZ now points at the spliced out node. pChild is the only child of pZ, or + * NULL if pZ has no children. If pZ is black, and not the tree root, then we + * will have violated the "same number of black nodes in every path to a + * leaf" property of the red-black tree. The code in do_delete_balancing() + * repairs this. */ + if( pZ->isBlack ){ + do_delete_balancing(pCur->pTree, pChild, pZ->pParent); + } + + sqliteFree(pZ); + return SQLITE_OK; +} + +/* + * Empty table n of the Rbtree. + */ +static int memRbtreeClearTable(Rbtree* tree, int n) +{ + BtRbTree *pTree; + BtRbNode *pNode; + + pTree = sqliteHashFind(&tree->tblHash, 0, n); + assert(pTree); + + pNode = pTree->pHead; + while( pNode ){ + if( pNode->pLeft ){ + pNode = pNode->pLeft; + } + else if( pNode->pRight ){ + pNode = pNode->pRight; + } + else { + BtRbNode *pTmp = pNode->pParent; + if( tree->eTransState == TRANS_ROLLBACK ){ + sqliteFree( pNode->pKey ); + sqliteFree( pNode->pData ); + }else{ + BtRollbackOp *pRollbackOp = sqliteMallocRaw(sizeof(BtRollbackOp)); + if( pRollbackOp==0 ) return SQLITE_NOMEM; + pRollbackOp->eOp = ROLLBACK_INSERT; + pRollbackOp->iTab = n; + pRollbackOp->nKey = pNode->nKey; + pRollbackOp->pKey = pNode->pKey; + pRollbackOp->nData = pNode->nData; + pRollbackOp->pData = pNode->pData; + btreeLogRollbackOp(tree, pRollbackOp); + } + sqliteFree( pNode ); + if( pTmp ){ + if( pTmp->pLeft == pNode ) pTmp->pLeft = 0; + else if( pTmp->pRight == pNode ) pTmp->pRight = 0; + } + pNode = pTmp; + } + } + + pTree->pHead = 0; + return SQLITE_OK; +} + +static int memRbtreeFirst(RbtCursor* pCur, int *pRes) +{ + if( pCur->pTree->pHead ){ + pCur->pNode = pCur->pTree->pHead; + while( pCur->pNode->pLeft ){ + pCur->pNode = pCur->pNode->pLeft; + } + } + if( pCur->pNode ){ + *pRes = 0; + }else{ + *pRes = 1; + } + pCur->eSkip = SKIP_NONE; + return SQLITE_OK; +} + +static int memRbtreeLast(RbtCursor* pCur, int *pRes) +{ + if( pCur->pTree->pHead ){ + pCur->pNode = pCur->pTree->pHead; + while( pCur->pNode->pRight ){ + pCur->pNode = pCur->pNode->pRight; + } + } + if( pCur->pNode ){ + *pRes = 0; + }else{ + *pRes = 1; + } + pCur->eSkip = SKIP_NONE; + return SQLITE_OK; +} + +/* +** Advance the cursor to the next entry in the database. If +** successful then set *pRes=0. If the cursor +** was already pointing to the last entry in the database before +** this routine was called, then set *pRes=1. +*/ +static int memRbtreeNext(RbtCursor* pCur, int *pRes) +{ + if( pCur->pNode && pCur->eSkip != SKIP_NEXT ){ + if( pCur->pNode->pRight ){ + pCur->pNode = pCur->pNode->pRight; + while( pCur->pNode->pLeft ) + pCur->pNode = pCur->pNode->pLeft; + }else{ + BtRbNode * pX = pCur->pNode; + pCur->pNode = pX->pParent; + while( pCur->pNode && (pCur->pNode->pRight == pX) ){ + pX = pCur->pNode; + pCur->pNode = pX->pParent; + } + } + } + pCur->eSkip = SKIP_NONE; + + if( !pCur->pNode ){ + *pRes = 1; + }else{ + *pRes = 0; + } + + return SQLITE_OK; +} + +static int memRbtreePrevious(RbtCursor* pCur, int *pRes) +{ + if( pCur->pNode && pCur->eSkip != SKIP_PREV ){ + if( pCur->pNode->pLeft ){ + pCur->pNode = pCur->pNode->pLeft; + while( pCur->pNode->pRight ) + pCur->pNode = pCur->pNode->pRight; + }else{ + BtRbNode * pX = pCur->pNode; + pCur->pNode = pX->pParent; + while( pCur->pNode && (pCur->pNode->pLeft == pX) ){ + pX = pCur->pNode; + pCur->pNode = pX->pParent; + } + } + } + pCur->eSkip = SKIP_NONE; + + if( !pCur->pNode ){ + *pRes = 1; + }else{ + *pRes = 0; + } + + return SQLITE_OK; +} + +static int memRbtreeKeySize(RbtCursor* pCur, int *pSize) +{ + if( pCur->pNode ){ + *pSize = pCur->pNode->nKey; + }else{ + *pSize = 0; + } + return SQLITE_OK; +} + +static int memRbtreeKey(RbtCursor* pCur, int offset, int amt, char *zBuf) +{ + if( !pCur->pNode ) return 0; + if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){ + memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt); + }else{ + memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset); + amt = pCur->pNode->nKey-offset; + } + return amt; +} + +static int memRbtreeDataSize(RbtCursor* pCur, int *pSize) +{ + if( pCur->pNode ){ + *pSize = pCur->pNode->nData; + }else{ + *pSize = 0; + } + return SQLITE_OK; +} + +static int memRbtreeData(RbtCursor *pCur, int offset, int amt, char *zBuf) +{ + if( !pCur->pNode ) return 0; + if( (amt + offset) <= pCur->pNode->nData ){ + memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt); + }else{ + memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset); + amt = pCur->pNode->nData-offset; + } + return amt; +} + +static int memRbtreeCloseCursor(RbtCursor* pCur) +{ + if( pCur->pTree->pCursors==pCur ){ + pCur->pTree->pCursors = pCur->pShared; + }else{ + RbtCursor *p = pCur->pTree->pCursors; + while( p && p->pShared!=pCur ){ p = p->pShared; } + assert( p!=0 ); + if( p ){ + p->pShared = pCur->pShared; + } + } + sqliteFree(pCur); + return SQLITE_OK; +} + +static int memRbtreeGetMeta(Rbtree* tree, int* aMeta) +{ + memcpy( aMeta, tree->aMetaData, sizeof(int) * SQLITE_N_BTREE_META ); + return SQLITE_OK; +} + +static int memRbtreeUpdateMeta(Rbtree* tree, int* aMeta) +{ + memcpy( tree->aMetaData, aMeta, sizeof(int) * SQLITE_N_BTREE_META ); + return SQLITE_OK; +} + +/* + * Check that each table in the Rbtree meets the requirements for a red-black + * binary tree. If an error is found, return an explanation of the problem in + * memory obtained from sqliteMalloc(). Parameters aRoot and nRoot are ignored. + */ +static char *memRbtreeIntegrityCheck(Rbtree* tree, int* aRoot, int nRoot) +{ + char * msg = 0; + HashElem *p; + + for(p=sqliteHashFirst(&tree->tblHash); p; p=sqliteHashNext(p)){ + BtRbTree *pTree = sqliteHashData(p); + check_redblack_tree(pTree, &msg); + } + + return msg; +} + +static int memRbtreeSetCacheSize(Rbtree* tree, int sz) +{ + return SQLITE_OK; +} + +static int memRbtreeSetSafetyLevel(Rbtree *pBt, int level){ + return SQLITE_OK; +} + +static int memRbtreeBeginTrans(Rbtree* tree) +{ + if( tree->eTransState != TRANS_NONE ) + return SQLITE_ERROR; + + assert( tree->pTransRollback == 0 ); + tree->eTransState = TRANS_INTRANSACTION; + return SQLITE_OK; +} + +/* +** Delete a linked list of BtRollbackOp structures. +*/ +static void deleteRollbackList(BtRollbackOp *pOp){ + while( pOp ){ + BtRollbackOp *pTmp = pOp->pNext; + sqliteFree(pOp->pData); + sqliteFree(pOp->pKey); + sqliteFree(pOp); + pOp = pTmp; + } +} + +static int memRbtreeCommit(Rbtree* tree){ + /* Just delete pTransRollback and pCheckRollback */ + deleteRollbackList(tree->pCheckRollback); + deleteRollbackList(tree->pTransRollback); + tree->pTransRollback = 0; + tree->pCheckRollback = 0; + tree->pCheckRollbackTail = 0; + tree->eTransState = TRANS_NONE; + return SQLITE_OK; +} + +/* + * Close the supplied Rbtree. Delete everything associated with it. + */ +static int memRbtreeClose(Rbtree* tree) +{ + HashElem *p; + memRbtreeCommit(tree); + while( (p=sqliteHashFirst(&tree->tblHash))!=0 ){ + tree->eTransState = TRANS_ROLLBACK; + memRbtreeDropTable(tree, sqliteHashKeysize(p)); + } + sqliteHashClear(&tree->tblHash); + sqliteFree(tree); + return SQLITE_OK; +} + +/* + * Execute and delete the supplied rollback-list on pRbtree. + */ +static void execute_rollback_list(Rbtree *pRbtree, BtRollbackOp *pList) +{ + BtRollbackOp *pTmp; + RbtCursor cur; + int res; + + cur.pRbtree = pRbtree; + cur.wrFlag = 1; + while( pList ){ + switch( pList->eOp ){ + case ROLLBACK_INSERT: + cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab ); + assert(cur.pTree); + cur.iTree = pList->iTab; + cur.eSkip = SKIP_NONE; + memRbtreeInsert( &cur, pList->pKey, + pList->nKey, pList->pData, pList->nData ); + break; + case ROLLBACK_DELETE: + cur.pTree = sqliteHashFind( &pRbtree->tblHash, 0, pList->iTab ); + assert(cur.pTree); + cur.iTree = pList->iTab; + cur.eSkip = SKIP_NONE; + memRbtreeMoveto(&cur, pList->pKey, pList->nKey, &res); + assert(res == 0); + memRbtreeDelete( &cur ); + break; + case ROLLBACK_CREATE: + btreeCreateTable(pRbtree, pList->iTab); + break; + case ROLLBACK_DROP: + memRbtreeDropTable(pRbtree, pList->iTab); + break; + default: + assert(0); + } + sqliteFree(pList->pKey); + sqliteFree(pList->pData); + pTmp = pList->pNext; + sqliteFree(pList); + pList = pTmp; + } +} + +static int memRbtreeRollback(Rbtree* tree) +{ + tree->eTransState = TRANS_ROLLBACK; + execute_rollback_list(tree, tree->pCheckRollback); + execute_rollback_list(tree, tree->pTransRollback); + tree->pTransRollback = 0; + tree->pCheckRollback = 0; + tree->pCheckRollbackTail = 0; + tree->eTransState = TRANS_NONE; + return SQLITE_OK; +} + +static int memRbtreeBeginCkpt(Rbtree* tree) +{ + if( tree->eTransState != TRANS_INTRANSACTION ) + return SQLITE_ERROR; + + assert( tree->pCheckRollback == 0 ); + assert( tree->pCheckRollbackTail == 0 ); + tree->eTransState = TRANS_INCHECKPOINT; + return SQLITE_OK; +} + +static int memRbtreeCommitCkpt(Rbtree* tree) +{ + if( tree->eTransState == TRANS_INCHECKPOINT ){ + if( tree->pCheckRollback ){ + tree->pCheckRollbackTail->pNext = tree->pTransRollback; + tree->pTransRollback = tree->pCheckRollback; + tree->pCheckRollback = 0; + tree->pCheckRollbackTail = 0; + } + tree->eTransState = TRANS_INTRANSACTION; + } + return SQLITE_OK; +} + +static int memRbtreeRollbackCkpt(Rbtree* tree) +{ + if( tree->eTransState != TRANS_INCHECKPOINT ) return SQLITE_OK; + tree->eTransState = TRANS_ROLLBACK; + execute_rollback_list(tree, tree->pCheckRollback); + tree->pCheckRollback = 0; + tree->pCheckRollbackTail = 0; + tree->eTransState = TRANS_INTRANSACTION; + return SQLITE_OK; +} + +#ifdef SQLITE_TEST +static int memRbtreePageDump(Rbtree* tree, int pgno, int rec) +{ + assert(!"Cannot call sqliteRbtreePageDump"); + return SQLITE_OK; +} + +static int memRbtreeCursorDump(RbtCursor* pCur, int* aRes) +{ + assert(!"Cannot call sqliteRbtreeCursorDump"); + return SQLITE_OK; +} +#endif + +static struct Pager *memRbtreePager(Rbtree* tree) +{ + return 0; +} + +/* +** Return the full pathname of the underlying database file. +*/ +static const char *memRbtreeGetFilename(Rbtree *pBt){ + return 0; /* A NULL return indicates there is no underlying file */ +} + +/* +** The copy file function is not implemented for the in-memory database +*/ +static int memRbtreeCopyFile(Rbtree *pBt, Rbtree *pBt2){ + return SQLITE_INTERNAL; /* Not implemented */ +} + +static BtOps sqliteRbtreeOps = { + (int(*)(Btree*)) memRbtreeClose, + (int(*)(Btree*,int)) memRbtreeSetCacheSize, + (int(*)(Btree*,int)) memRbtreeSetSafetyLevel, + (int(*)(Btree*)) memRbtreeBeginTrans, + (int(*)(Btree*)) memRbtreeCommit, + (int(*)(Btree*)) memRbtreeRollback, + (int(*)(Btree*)) memRbtreeBeginCkpt, + (int(*)(Btree*)) memRbtreeCommitCkpt, + (int(*)(Btree*)) memRbtreeRollbackCkpt, + (int(*)(Btree*,int*)) memRbtreeCreateTable, + (int(*)(Btree*,int*)) memRbtreeCreateTable, + (int(*)(Btree*,int)) memRbtreeDropTable, + (int(*)(Btree*,int)) memRbtreeClearTable, + (int(*)(Btree*,int,int,BtCursor**)) memRbtreeCursor, + (int(*)(Btree*,int*)) memRbtreeGetMeta, + (int(*)(Btree*,int*)) memRbtreeUpdateMeta, + (char*(*)(Btree*,int*,int)) memRbtreeIntegrityCheck, + (const char*(*)(Btree*)) memRbtreeGetFilename, + (int(*)(Btree*,Btree*)) memRbtreeCopyFile, + (struct Pager*(*)(Btree*)) memRbtreePager, +#ifdef SQLITE_TEST + (int(*)(Btree*,int,int)) memRbtreePageDump, +#endif +}; + +static BtCursorOps sqliteRbtreeCursorOps = { + (int(*)(BtCursor*,const void*,int,int*)) memRbtreeMoveto, + (int(*)(BtCursor*)) memRbtreeDelete, + (int(*)(BtCursor*,const void*,int,const void*,int)) memRbtreeInsert, + (int(*)(BtCursor*,int*)) memRbtreeFirst, + (int(*)(BtCursor*,int*)) memRbtreeLast, + (int(*)(BtCursor*,int*)) memRbtreeNext, + (int(*)(BtCursor*,int*)) memRbtreePrevious, + (int(*)(BtCursor*,int*)) memRbtreeKeySize, + (int(*)(BtCursor*,int,int,char*)) memRbtreeKey, + (int(*)(BtCursor*,const void*,int,int,int*)) memRbtreeKeyCompare, + (int(*)(BtCursor*,int*)) memRbtreeDataSize, + (int(*)(BtCursor*,int,int,char*)) memRbtreeData, + (int(*)(BtCursor*)) memRbtreeCloseCursor, +#ifdef SQLITE_TEST + (int(*)(BtCursor*,int*)) memRbtreeCursorDump, +#endif + +}; + +#endif /* SQLITE_OMIT_INMEMORYDB */ diff --git a/src/libs/sqlite2/build.c b/src/libs/sqlite2/build.c new file mode 100644 index 00000000..6c17f140 --- /dev/null +++ b/src/libs/sqlite2/build.c @@ -0,0 +1,2156 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the SQLite parser +** when syntax rules are reduced. The routines in this file handle the +** following kinds of SQL syntax: +** +** CREATE TABLE +** DROP TABLE +** CREATE INDEX +** DROP INDEX +** creating ID lists +** BEGIN TRANSACTION +** COMMIT +** ROLLBACK +** PRAGMA +** +** $Id: build.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "sqliteInt.h" +#include + +/* +** This routine is called when a new SQL statement is beginning to +** be parsed. Check to see if the schema for the database needs +** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables. +** If it does, then read it. +*/ +void sqliteBeginParse(Parse *pParse, int explainFlag){ + sqlite *db = pParse->db; + int i; + pParse->explain = explainFlag; + if((db->flags & SQLITE_Initialized)==0 && db->init.busy==0 ){ + int rc = sqliteInit(db, &pParse->zErrMsg); + if( rc!=SQLITE_OK ){ + pParse->rc = rc; + pParse->nErr++; + } + } + for(i=0; inDb; i++){ + DbClearProperty(db, i, DB_Locked); + if( !db->aDb[i].inTrans ){ + DbClearProperty(db, i, DB_Cookie); + } + } + pParse->nVar = 0; +} + +/* +** This routine is called after a single SQL statement has been +** parsed and we want to execute the VDBE code to implement +** that statement. Prior action routines should have already +** constructed VDBE code to do the work of the SQL statement. +** This routine just has to execute the VDBE code. +** +** Note that if an error occurred, it might be the case that +** no VDBE code was generated. +*/ +void sqliteExec(Parse *pParse){ + sqlite *db = pParse->db; + Vdbe *v = pParse->pVdbe; + + if( v==0 && (v = sqliteGetVdbe(pParse))!=0 ){ + sqliteVdbeAddOp(v, OP_Halt, 0, 0); + } + if( sqlite_malloc_failed ) return; + if( v && pParse->nErr==0 ){ + FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; + sqliteVdbeTrace(v, trace); + sqliteVdbeMakeReady(v, pParse->nVar, pParse->explain); + pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE; + pParse->colNamesSet = 0; + }else if( pParse->rc==SQLITE_OK ){ + pParse->rc = SQLITE_ERROR; + } + pParse->nTab = 0; + pParse->nMem = 0; + pParse->nSet = 0; + pParse->nAgg = 0; + pParse->nVar = 0; +} + +/* +** Locate the in-memory structure that describes +** a particular database table given the name +** of that table and (optionally) the name of the database +** containing the table. Return NULL if not found. +** +** If zDatabase is 0, all databases are searched for the +** table and the first matching table is returned. (No checking +** for duplicate table names is done.) The search order is +** TEMP first, then MAIN, then any auxiliary databases added +** using the ATTACH command. +** +** See also sqliteLocateTable(). +*/ +Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ + Table *p = 0; + int i; + for(i=0; inDb; i++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + if( zDatabase!=0 && sqliteStrICmp(zDatabase, db->aDb[j].zName) ) continue; + p = sqliteHashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1); + if( p ) break; + } + return p; +} + +/* +** Locate the in-memory structure that describes +** a particular database table given the name +** of that table and (optionally) the name of the database +** containing the table. Return NULL if not found. +** Also leave an error message in pParse->zErrMsg. +** +** The difference between this routine and sqliteFindTable() +** is that this routine leaves an error message in pParse->zErrMsg +** where sqliteFindTable() does not. +*/ +Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ + Table *p; + + p = sqliteFindTable(pParse->db, zName, zDbase); + if( p==0 ){ + if( zDbase ){ + sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); + }else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){ + sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"", + zName, zDbase); + }else{ + sqliteErrorMsg(pParse, "no such table: %s", zName); + } + } + return p; +} + +/* +** Locate the in-memory structure that describes +** a particular index given the name of that index +** and the name of the database that contains the index. +** Return NULL if not found. +** +** If zDatabase is 0, all databases are searched for the +** table and the first matching index is returned. (No checking +** for duplicate index names is done.) The search order is +** TEMP first, then MAIN, then any auxiliary databases added +** using the ATTACH command. +*/ +Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){ + Index *p = 0; + int i; + for(i=0; inDb; i++){ + int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ + if( zDb && sqliteStrICmp(zDb, db->aDb[j].zName) ) continue; + p = sqliteHashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1); + if( p ) break; + } + return p; +} + +/* +** Remove the given index from the index hash table, and free +** its memory structures. +** +** The index is removed from the database hash tables but +** it is not unlinked from the Table that it indexes. +** Unlinking from the Table must be done by the calling function. +*/ +static void sqliteDeleteIndex(sqlite *db, Index *p){ + Index *pOld; + + assert( db!=0 && p->zName!=0 ); + pOld = sqliteHashInsert(&db->aDb[p->iDb].idxHash, p->zName, + strlen(p->zName)+1, 0); + if( pOld!=0 && pOld!=p ){ + sqliteHashInsert(&db->aDb[p->iDb].idxHash, pOld->zName, + strlen(pOld->zName)+1, pOld); + } + sqliteFree(p); +} + +/* +** Unlink the given index from its table, then remove +** the index from the index hash table and free its memory +** structures. +*/ +void sqliteUnlinkAndDeleteIndex(sqlite *db, Index *pIndex){ + if( pIndex->pTable->pIndex==pIndex ){ + pIndex->pTable->pIndex = pIndex->pNext; + }else{ + Index *p; + for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){} + if( p && p->pNext==pIndex ){ + p->pNext = pIndex->pNext; + } + } + sqliteDeleteIndex(db, pIndex); +} + +/* +** Erase all schema information from the in-memory hash tables of +** database connection. This routine is called to reclaim memory +** before the connection closes. It is also called during a rollback +** if there were schema changes during the transaction. +** +** If iDb<=0 then reset the internal schema tables for all database +** files. If iDb>=2 then reset the internal schema for only the +** single file indicated. +*/ +void sqliteResetInternalSchema(sqlite *db, int iDb){ + HashElem *pElem; + Hash temp1; + Hash temp2; + int i, j; + + assert( iDb>=0 && iDbnDb ); + db->flags &= ~SQLITE_Initialized; + for(i=iDb; inDb; i++){ + Db *pDb = &db->aDb[i]; + temp1 = pDb->tblHash; + temp2 = pDb->trigHash; + sqliteHashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0); + sqliteHashClear(&pDb->aFKey); + sqliteHashClear(&pDb->idxHash); + for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){ + Trigger *pTrigger = sqliteHashData(pElem); + sqliteDeleteTrigger(pTrigger); + } + sqliteHashClear(&temp2); + sqliteHashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0); + for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ + Table *pTab = sqliteHashData(pElem); + sqliteDeleteTable(db, pTab); + } + sqliteHashClear(&temp1); + DbClearProperty(db, i, DB_SchemaLoaded); + if( iDb>0 ) return; + } + assert( iDb==0 ); + db->flags &= ~SQLITE_InternChanges; + + /* If one or more of the auxiliary database files has been closed, + ** then remove then from the auxiliary database list. We take the + ** opportunity to do this here since we have just deleted all of the + ** schema hash tables and therefore do not have to make any changes + ** to any of those tables. + */ + for(i=0; inDb; i++){ + struct Db *pDb = &db->aDb[i]; + if( pDb->pBt==0 ){ + if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux); + pDb->pAux = 0; + } + } + for(i=j=2; inDb; i++){ + struct Db *pDb = &db->aDb[i]; + if( pDb->pBt==0 ){ + sqliteFree(pDb->zName); + pDb->zName = 0; + continue; + } + if( jaDb[j] = db->aDb[i]; + } + j++; + } + memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j])); + db->nDb = j; + if( db->nDb<=2 && db->aDb!=db->aDbStatic ){ + memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0])); + sqliteFree(db->aDb); + db->aDb = db->aDbStatic; + } +} + +/* +** This routine is called whenever a rollback occurs. If there were +** schema changes during the transaction, then we have to reset the +** internal hash tables and reload them from disk. +*/ +void sqliteRollbackInternalChanges(sqlite *db){ + if( db->flags & SQLITE_InternChanges ){ + sqliteResetInternalSchema(db, 0); + } +} + +/* +** This routine is called when a commit occurs. +*/ +void sqliteCommitInternalChanges(sqlite *db){ + db->aDb[0].schema_cookie = db->next_cookie; + db->flags &= ~SQLITE_InternChanges; +} + +/* +** Remove the memory data structures associated with the given +** Table. No changes are made to disk by this routine. +** +** This routine just deletes the data structure. It does not unlink +** the table data structure from the hash table. Nor does it remove +** foreign keys from the sqlite.aFKey hash table. But it does destroy +** memory structures of the indices and foreign keys associated with +** the table. +** +** Indices associated with the table are unlinked from the "db" +** data structure if db!=NULL. If db==NULL, indices attached to +** the table are deleted, but it is assumed they have already been +** unlinked. +*/ +void sqliteDeleteTable(sqlite *db, Table *pTable){ + int i; + Index *pIndex, *pNext; + FKey *pFKey, *pNextFKey; + + if( pTable==0 ) return; + + /* Delete all indices associated with this table + */ + for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ + pNext = pIndex->pNext; + assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) ); + sqliteDeleteIndex(db, pIndex); + } + + /* Delete all foreign keys associated with this table. The keys + ** should have already been unlinked from the db->aFKey hash table + */ + for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ + pNextFKey = pFKey->pNextFrom; + assert( pTable->iDbnDb ); + assert( sqliteHashFind(&db->aDb[pTable->iDb].aFKey, + pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey ); + sqliteFree(pFKey); + } + + /* Delete the Table structure itself. + */ + for(i=0; inCol; i++){ + sqliteFree(pTable->aCol[i].zName); + sqliteFree(pTable->aCol[i].zDflt); + sqliteFree(pTable->aCol[i].zType); + } + sqliteFree(pTable->zName); + sqliteFree(pTable->aCol); + sqliteSelectDelete(pTable->pSelect); + sqliteFree(pTable); +} + +/* +** Unlink the given table from the hash tables and the delete the +** table structure with all its indices and foreign keys. +*/ +static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *p){ + Table *pOld; + FKey *pF1, *pF2; + int i = p->iDb; + assert( db!=0 ); + pOld = sqliteHashInsert(&db->aDb[i].tblHash, p->zName, strlen(p->zName)+1, 0); + assert( pOld==0 || pOld==p ); + for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){ + int nTo = strlen(pF1->zTo) + 1; + pF2 = sqliteHashFind(&db->aDb[i].aFKey, pF1->zTo, nTo); + if( pF2==pF1 ){ + sqliteHashInsert(&db->aDb[i].aFKey, pF1->zTo, nTo, pF1->pNextTo); + }else{ + while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; } + if( pF2 ){ + pF2->pNextTo = pF1->pNextTo; + } + } + } + sqliteDeleteTable(db, p); +} + +/* +** Construct the name of a user table or index from a token. +** +** Space to hold the name is obtained from sqliteMalloc() and must +** be freed by the calling function. +*/ +char *sqliteTableNameFromToken(Token *pName){ + char *zName = sqliteStrNDup(pName->z, pName->n); + sqliteDequote(zName); + return zName; +} + +/* +** Generate code to open the appropriate master table. The table +** opened will be SQLITE_MASTER for persistent tables and +** SQLITE_TEMP_MASTER for temporary tables. The table is opened +** on cursor 0. +*/ +void sqliteOpenMasterTable(Vdbe *v, int isTemp){ + sqliteVdbeAddOp(v, OP_Integer, isTemp, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); +} + +/* +** Begin constructing a new table representation in memory. This is +** the first of several action routines that get called in response +** to a CREATE TABLE statement. In particular, this routine is called +** after seeing tokens "CREATE" and "TABLE" and the table name. The +** pStart token is the CREATE and pName is the table name. The isTemp +** flag is true if the table should be stored in the auxiliary database +** file instead of in the main database file. This is normally the case +** when the "TEMP" or "TEMPORARY" keyword occurs in between +** CREATE and TABLE. +** +** The new table record is initialized and put in pParse->pNewTable. +** As more of the CREATE TABLE statement is parsed, additional action +** routines will be called to add more information to this record. +** At the end of the CREATE TABLE statement, the sqliteEndTable() routine +** is called to complete the construction of the new table record. +*/ +void sqliteStartTable( + Parse *pParse, /* Parser context */ + Token *pStart, /* The "CREATE" token */ + Token *pName, /* Name of table or view to create */ + int isTemp, /* True if this is a TEMP table */ + int isView /* True if this is a VIEW */ +){ + Table *pTable; + Index *pIdx; + char *zName; + sqlite *db = pParse->db; + Vdbe *v; + int iDb; + + pParse->sFirstToken = *pStart; + zName = sqliteTableNameFromToken(pName); + if( zName==0 ) return; + if( db->init.iDb==1 ) isTemp = 1; +#ifndef SQLITE_OMIT_AUTHORIZATION + assert( (isTemp & 1)==isTemp ); + { + int code; + char *zDb = isTemp ? "temp" : "main"; + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ + sqliteFree(zName); + return; + } + if( isView ){ + if( isTemp ){ + code = SQLITE_CREATE_TEMP_VIEW; + }else{ + code = SQLITE_CREATE_VIEW; + } + }else{ + if( isTemp ){ + code = SQLITE_CREATE_TEMP_TABLE; + }else{ + code = SQLITE_CREATE_TABLE; + } + } + if( sqliteAuthCheck(pParse, code, zName, 0, zDb) ){ + sqliteFree(zName); + return; + } + } +#endif + + + /* Before trying to create a temporary table, make sure the Btree for + ** holding temporary tables is open. + */ + if( isTemp && db->aDb[1].pBt==0 && !pParse->explain ){ + int rc = sqliteBtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt); + if( rc!=SQLITE_OK ){ + sqliteErrorMsg(pParse, "unable to open a temporary database " + "file for storing temporary tables"); + pParse->nErr++; + return; + } + if( db->flags & SQLITE_InTrans ){ + rc = sqliteBtreeBeginTrans(db->aDb[1].pBt); + if( rc!=SQLITE_OK ){ + sqliteErrorMsg(pParse, "unable to get a write lock on " + "the temporary database file"); + return; + } + } + } + + /* Make sure the new table name does not collide with an existing + ** index or table name. Issue an error message if it does. + ** + ** If we are re-reading the sqlite_master table because of a schema + ** change and a new permanent table is found whose name collides with + ** an existing temporary table, that is not an error. + */ + pTable = sqliteFindTable(db, zName, 0); + iDb = isTemp ? 1 : db->init.iDb; + if( pTable!=0 && (pTable->iDb==iDb || !db->init.busy) ){ + sqliteErrorMsg(pParse, "table %T already exists", pName); + sqliteFree(zName); + return; + } + if( (pIdx = sqliteFindIndex(db, zName, 0))!=0 && + (pIdx->iDb==0 || !db->init.busy) ){ + sqliteErrorMsg(pParse, "there is already an index named %s", zName); + sqliteFree(zName); + return; + } + pTable = sqliteMalloc( sizeof(Table) ); + if( pTable==0 ){ + sqliteFree(zName); + return; + } + pTable->zName = zName; + pTable->nCol = 0; + pTable->aCol = 0; + pTable->iPKey = -1; + pTable->pIndex = 0; + pTable->iDb = iDb; + if( pParse->pNewTable ) sqliteDeleteTable(db, pParse->pNewTable); + pParse->pNewTable = pTable; + + /* Begin generating the code that will insert the table record into + ** the SQLITE_MASTER table. Note in particular that we must go ahead + ** and allocate the record number for the table entry now. Before any + ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause + ** indices to be created and the table record must come before the + ** indices. Hence, the record number for the table must be allocated + ** now. + */ + if( !db->init.busy && (v = sqliteGetVdbe(pParse))!=0 ){ + sqliteBeginWriteOperation(pParse, 0, isTemp); + if( !isTemp ){ + sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0); + sqliteVdbeAddOp(v, OP_SetCookie, 0, 1); + } + sqliteOpenMasterTable(v, isTemp); + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); + } +} + +/* +** Add a new column to the table currently being constructed. +** +** The parser calls this routine once for each column declaration +** in a CREATE TABLE statement. sqliteStartTable() gets called +** first to get things going. Then this routine is called for each +** column. +*/ +void sqliteAddColumn(Parse *pParse, Token *pName){ + Table *p; + int i; + char *z = 0; + Column *pCol; + if( (p = pParse->pNewTable)==0 ) return; + sqliteSetNString(&z, pName->z, pName->n, 0); + if( z==0 ) return; + sqliteDequote(z); + for(i=0; inCol; i++){ + if( sqliteStrICmp(z, p->aCol[i].zName)==0 ){ + sqliteErrorMsg(pParse, "duplicate column name: %s", z); + sqliteFree(z); + return; + } + } + if( (p->nCol & 0x7)==0 ){ + Column *aNew; + aNew = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0])); + if( aNew==0 ) return; + p->aCol = aNew; + } + pCol = &p->aCol[p->nCol]; + memset(pCol, 0, sizeof(p->aCol[0])); + pCol->zName = z; + pCol->sortOrder = SQLITE_SO_NUM; + p->nCol++; +} + +/* +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. A "NOT NULL" constraint has +** been seen on a column. This routine sets the notNull flag on +** the column currently under construction. +*/ +void sqliteAddNotNull(Parse *pParse, int onError){ + Table *p; + int i; + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + if( i>=0 ) p->aCol[i].notNull = onError; +} + +/* +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. The pFirst token is the first +** token in the sequence of tokens that describe the type of the +** column currently under construction. pLast is the last token +** in the sequence. Use this information to construct a string +** that contains the typename of the column and store that string +** in zType. +*/ +void sqliteAddColumnType(Parse *pParse, Token *pFirst, Token *pLast){ + Table *p; + int i, j; + int n; + char *z, **pz; + Column *pCol; + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + if( i<0 ) return; + pCol = &p->aCol[i]; + pz = &pCol->zType; + n = pLast->n + Addr(pLast->z) - Addr(pFirst->z); + sqliteSetNString(pz, pFirst->z, n, 0); + z = *pz; + if( z==0 ) return; + for(i=j=0; z[i]; i++){ + int c = z[i]; + if( isspace(c) ) continue; + z[j++] = c; + } + z[j] = 0; + if( pParse->db->file_format>=4 ){ + pCol->sortOrder = sqliteCollateType(z, n); + }else{ + pCol->sortOrder = SQLITE_SO_NUM; + } +} + +/* +** The given token is the default value for the last column added to +** the table currently under construction. If "minusFlag" is true, it +** means the value token was preceded by a minus sign. +** +** This routine is called by the parser while in the middle of +** parsing a CREATE TABLE statement. +*/ +void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){ + Table *p; + int i; + char **pz; + if( (p = pParse->pNewTable)==0 ) return; + i = p->nCol-1; + if( i<0 ) return; + pz = &p->aCol[i].zDflt; + if( minusFlag ){ + sqliteSetNString(pz, "-", 1, pVal->z, pVal->n, 0); + }else{ + sqliteSetNString(pz, pVal->z, pVal->n, 0); + } + sqliteDequote(*pz); +} + +/* +** Designate the PRIMARY KEY for the table. pList is a list of names +** of columns that form the primary key. If pList is NULL, then the +** most recently added column of the table is the primary key. +** +** A table can have at most one primary key. If the table already has +** a primary key (and this is the second primary key) then create an +** error. +** +** If the PRIMARY KEY is on a single column whose datatype is INTEGER, +** then we will try to use that column as the row id. (Exception: +** For backwards compatibility with older databases, do not do this +** if the file format version number is less than 1.) Set the Table.iPKey +** field of the table under construction to be the index of the +** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is +** no INTEGER PRIMARY KEY. +** +** If the key is not an INTEGER PRIMARY KEY, then create a unique +** index for the key. No index is created for INTEGER PRIMARY KEYs. +*/ +void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){ + Table *pTab = pParse->pNewTable; + char *zType = 0; + int iCol = -1, i; + if( pTab==0 ) goto primary_key_exit; + if( pTab->hasPrimKey ){ + sqliteErrorMsg(pParse, + "table \"%s\" has more than one primary key", pTab->zName); + goto primary_key_exit; + } + pTab->hasPrimKey = 1; + if( pList==0 ){ + iCol = pTab->nCol - 1; + pTab->aCol[iCol].isPrimKey = 1; + }else{ + for(i=0; inId; i++){ + for(iCol=0; iColnCol; iCol++){ + if( sqliteStrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ) break; + } + if( iColnCol ) pTab->aCol[iCol].isPrimKey = 1; + } + if( pList->nId>1 ) iCol = -1; + } + if( iCol>=0 && iColnCol ){ + zType = pTab->aCol[iCol].zType; + } + if( pParse->db->file_format>=1 && + zType && sqliteStrICmp(zType, "INTEGER")==0 ){ + pTab->iPKey = iCol; + pTab->keyConf = onError; + }else{ + sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0); + pList = 0; + } + +primary_key_exit: + sqliteIdListDelete(pList); + return; +} + +/* +** Return the appropriate collating type given a type name. +** +** The collation type is text (SQLITE_SO_TEXT) if the type +** name contains the character stream "text" or "blob" or +** "clob". Any other type name is collated as numeric +** (SQLITE_SO_NUM). +*/ +int sqliteCollateType(const char *zType, int nType){ + int i; + for(i=0; ipNewTable)==0 ) return; + i = p->nCol-1; + if( i>=0 ) p->aCol[i].sortOrder = collType; +} + +/* +** Come up with a new random value for the schema cookie. Make sure +** the new value is different from the old. +** +** The schema cookie is used to determine when the schema for the +** database changes. After each schema change, the cookie value +** changes. When a process first reads the schema it records the +** cookie. Thereafter, whenever it goes to access the database, +** it checks the cookie to make sure the schema has not changed +** since it was last read. +** +** This plan is not completely bullet-proof. It is possible for +** the schema to change multiple times and for the cookie to be +** set back to prior value. But schema changes are infrequent +** and the probability of hitting the same cookie value is only +** 1 chance in 2^32. So we're safe enough. +*/ +void sqliteChangeCookie(sqlite *db, Vdbe *v){ + if( db->next_cookie==db->aDb[0].schema_cookie ){ + unsigned char r; + sqliteRandomness(1, &r); + db->next_cookie = db->aDb[0].schema_cookie + r + 1; + db->flags |= SQLITE_InternChanges; + sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0); + sqliteVdbeAddOp(v, OP_SetCookie, 0, 0); + } +} + +/* +** Measure the number of characters needed to output the given +** identifier. The number returned includes any quotes used +** but does not include the null terminator. +*/ +static int identLength(const char *z){ + int n; + int needQuote = 0; + for(n=0; *z; n++, z++){ + if( *z=='\'' ){ n++; needQuote=1; } + } + return n + needQuote*2; +} + +/* +** Write an identifier onto the end of the given string. Add +** quote characters as needed. +*/ +static void identPut(char *z, int *pIdx, char *zIdent){ + int i, j, needQuote; + i = *pIdx; + for(j=0; zIdent[j]; j++){ + if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break; + } + needQuote = zIdent[j]!=0 || isdigit(zIdent[0]) + || sqliteKeywordCode(zIdent, j)!=TK_ID; + if( needQuote ) z[i++] = '\''; + for(j=0; zIdent[j]; j++){ + z[i++] = zIdent[j]; + if( zIdent[j]=='\'' ) z[i++] = '\''; + } + if( needQuote ) z[i++] = '\''; + z[i] = 0; + *pIdx = i; +} + +/* +** Generate a CREATE TABLE statement appropriate for the given +** table. Memory to hold the text of the statement is obtained +** from sqliteMalloc() and must be freed by the calling function. +*/ +static char *createTableStmt(Table *p){ + int i, k, n; + char *zStmt; + char *zSep, *zSep2, *zEnd; + n = 0; + for(i=0; inCol; i++){ + n += identLength(p->aCol[i].zName); + } + n += identLength(p->zName); + if( n<40 ){ + zSep = ""; + zSep2 = ","; + zEnd = ")"; + }else{ + zSep = "\n "; + zSep2 = ",\n "; + zEnd = "\n)"; + } + n += 35 + 6*p->nCol; + zStmt = sqliteMallocRaw( n ); + if( zStmt==0 ) return 0; + strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE "); + k = strlen(zStmt); + identPut(zStmt, &k, p->zName); + zStmt[k++] = '('; + for(i=0; inCol; i++){ + strcpy(&zStmt[k], zSep); + k += strlen(&zStmt[k]); + zSep = zSep2; + identPut(zStmt, &k, p->aCol[i].zName); + } + strcpy(&zStmt[k], zEnd); + return zStmt; +} + +/* +** This routine is called to report the final ")" that terminates +** a CREATE TABLE statement. +** +** The table structure that other action routines have been building +** is added to the internal hash tables, assuming no errors have +** occurred. +** +** An entry for the table is made in the master table on disk, unless +** this is a temporary table or db->init.busy==1. When db->init.busy==1 +** it means we are reading the sqlite_master table because we just +** connected to the database or because the sqlite_master table has +** recently changes, so the entry for this table already exists in +** the sqlite_master table. We do not want to create it again. +** +** If the pSelect argument is not NULL, it means that this routine +** was called to create a table generated from a +** "CREATE TABLE ... AS SELECT ..." statement. The column names of +** the new table will match the result set of the SELECT. +*/ +void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ + Table *p; + sqlite *db = pParse->db; + + if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite_malloc_failed ) return; + p = pParse->pNewTable; + if( p==0 ) return; + + /* If the table is generated from a SELECT, then construct the + ** list of columns and the text of the table. + */ + if( pSelect ){ + Table *pSelTab = sqliteResultSetOfSelect(pParse, 0, pSelect); + if( pSelTab==0 ) return; + assert( p->aCol==0 ); + p->nCol = pSelTab->nCol; + p->aCol = pSelTab->aCol; + pSelTab->nCol = 0; + pSelTab->aCol = 0; + sqliteDeleteTable(0, pSelTab); + } + + /* If the db->init.busy is 1 it means we are reading the SQL off the + ** "sqlite_master" or "sqlite_temp_master" table on the disk. + ** So do not write to the disk again. Extract the root page number + ** for the table from the db->init.newTnum field. (The page number + ** should have been put there by the sqliteOpenCb routine.) + */ + if( db->init.busy ){ + p->tnum = db->init.newTnum; + } + + /* If not initializing, then create a record for the new table + ** in the SQLITE_MASTER table of the database. The record number + ** for the new table entry should already be on the stack. + ** + ** If this is a TEMPORARY table, write the entry into the auxiliary + ** file instead of into the main database file. + */ + if( !db->init.busy ){ + int n; + Vdbe *v; + + v = sqliteGetVdbe(pParse); + if( v==0 ) return; + if( p->pSelect==0 ){ + /* A regular table */ + sqliteVdbeOp3(v, OP_CreateTable, 0, p->iDb, (char*)&p->tnum, P3_POINTER); + }else{ + /* A view */ + sqliteVdbeAddOp(v, OP_Integer, 0, 0); + } + p->tnum = 0; + sqliteVdbeAddOp(v, OP_Pull, 1, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, p->pSelect==0?"table":"view", P3_STATIC); + sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, p->zName, 0); + sqliteVdbeAddOp(v, OP_Dup, 4, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0); + if( pSelect ){ + char *z = createTableStmt(p); + n = z ? strlen(z) : 0; + sqliteVdbeChangeP3(v, -1, z, n); + sqliteFree(z); + }else{ + assert( pEnd!=0 ); + n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1; + sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n); + } + sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); + if( !p->iDb ){ + sqliteChangeCookie(db, v); + } + sqliteVdbeAddOp(v, OP_Close, 0, 0); + if( pSelect ){ + sqliteVdbeAddOp(v, OP_Integer, p->iDb, 0); + sqliteVdbeAddOp(v, OP_OpenWrite, 1, 0); + pParse->nTab = 2; + sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0); + } + sqliteEndWriteOperation(pParse); + } + + /* Add the table to the in-memory representation of the database. + */ + if( pParse->explain==0 && pParse->nErr==0 ){ + Table *pOld; + FKey *pFKey; + pOld = sqliteHashInsert(&db->aDb[p->iDb].tblHash, + p->zName, strlen(p->zName)+1, p); + if( pOld ){ + assert( p==pOld ); /* Malloc must have failed inside HashInsert() */ + return; + } + for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int nTo = strlen(pFKey->zTo) + 1; + pFKey->pNextTo = sqliteHashFind(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo); + sqliteHashInsert(&db->aDb[p->iDb].aFKey, pFKey->zTo, nTo, pFKey); + } + pParse->pNewTable = 0; + db->nTable++; + db->flags |= SQLITE_InternChanges; + } +} + +/* +** The parser calls this routine in order to create a new VIEW +*/ +void sqliteCreateView( + Parse *pParse, /* The parsing context */ + Token *pBegin, /* The CREATE token that begins the statement */ + Token *pName, /* The token that holds the name of the view */ + Select *pSelect, /* A SELECT statement that will become the new view */ + int isTemp /* TRUE for a TEMPORARY view */ +){ + Table *p; + int n; + const char *z; + Token sEnd; + DbFixer sFix; + + sqliteStartTable(pParse, pBegin, pName, isTemp, 1); + p = pParse->pNewTable; + if( p==0 || pParse->nErr ){ + sqliteSelectDelete(pSelect); + return; + } + if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName) + && sqliteFixSelect(&sFix, pSelect) + ){ + sqliteSelectDelete(pSelect); + return; + } + + /* Make a copy of the entire SELECT statement that defines the view. + ** This will force all the Expr.token.z values to be dynamically + ** allocated rather than point to the input string - which means that + ** they will persist after the current sqlite_exec() call returns. + */ + p->pSelect = sqliteSelectDup(pSelect); + sqliteSelectDelete(pSelect); + if( !pParse->db->init.busy ){ + sqliteViewGetColumnNames(pParse, p); + } + + /* Locate the end of the CREATE VIEW statement. Make sEnd point to + ** the end. + */ + sEnd = pParse->sLastToken; + if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){ + sEnd.z += sEnd.n; + } + sEnd.n = 0; + n = sEnd.z - pBegin->z; + z = pBegin->z; + while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; } + sEnd.z = &z[n-1]; + sEnd.n = 1; + + /* Use sqliteEndTable() to add the view to the SQLITE_MASTER table */ + sqliteEndTable(pParse, &sEnd, 0); + return; +} + +/* +** The Table structure pTable is really a VIEW. Fill in the names of +** the columns of the view in the pTable structure. Return the number +** of errors. If an error is seen leave an error message in pParse->zErrMsg. +*/ +int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){ + ExprList *pEList; + Select *pSel; + Table *pSelTab; + int nErr = 0; + + assert( pTable ); + + /* A positive nCol means the columns names for this view are + ** already known. + */ + if( pTable->nCol>0 ) return 0; + + /* A negative nCol is a special marker meaning that we are currently + ** trying to compute the column names. If we enter this routine with + ** a negative nCol, it means two or more views form a loop, like this: + ** + ** CREATE VIEW one AS SELECT * FROM two; + ** CREATE VIEW two AS SELECT * FROM one; + ** + ** Actually, this error is caught previously and so the following test + ** should always fail. But we will leave it in place just to be safe. + */ + if( pTable->nCol<0 ){ + sqliteErrorMsg(pParse, "view %s is circularly defined", pTable->zName); + return 1; + } + + /* If we get this far, it means we need to compute the table names. + */ + assert( pTable->pSelect ); /* If nCol==0, then pTable must be a VIEW */ + pSel = pTable->pSelect; + + /* Note that the call to sqliteResultSetOfSelect() will expand any + ** "*" elements in this list. But we will need to restore the list + ** back to its original configuration afterwards, so we save a copy of + ** the original in pEList. + */ + pEList = pSel->pEList; + pSel->pEList = sqliteExprListDup(pEList); + if( pSel->pEList==0 ){ + pSel->pEList = pEList; + return 1; /* Malloc failed */ + } + pTable->nCol = -1; + pSelTab = sqliteResultSetOfSelect(pParse, 0, pSel); + if( pSelTab ){ + assert( pTable->aCol==0 ); + pTable->nCol = pSelTab->nCol; + pTable->aCol = pSelTab->aCol; + pSelTab->nCol = 0; + pSelTab->aCol = 0; + sqliteDeleteTable(0, pSelTab); + DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews); + }else{ + pTable->nCol = 0; + nErr++; + } + sqliteSelectUnbind(pSel); + sqliteExprListDelete(pSel->pEList); + pSel->pEList = pEList; + return nErr; +} + +/* +** Clear the column names from the VIEW pTable. +** +** This routine is called whenever any other table or view is modified. +** The view passed into this routine might depend directly or indirectly +** on the modified or deleted table so we need to clear the old column +** names so that they will be recomputed. +*/ +static void sqliteViewResetColumnNames(Table *pTable){ + int i; + Column *pCol; + assert( pTable!=0 && pTable->pSelect!=0 ); + for(i=0, pCol=pTable->aCol; inCol; i++, pCol++){ + sqliteFree(pCol->zName); + sqliteFree(pCol->zDflt); + sqliteFree(pCol->zType); + } + sqliteFree(pTable->aCol); + pTable->aCol = 0; + pTable->nCol = 0; +} + +/* +** Clear the column names from every VIEW in database idx. +*/ +static void sqliteViewResetAll(sqlite *db, int idx){ + HashElem *i; + if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; + for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){ + Table *pTab = sqliteHashData(i); + if( pTab->pSelect ){ + sqliteViewResetColumnNames(pTab); + } + } + DbClearProperty(db, idx, DB_UnresetViews); +} + +/* +** Given a token, look up a table with that name. If not found, leave +** an error for the parser to find and return NULL. +*/ +Table *sqliteTableFromToken(Parse *pParse, Token *pTok){ + char *zName; + Table *pTab; + zName = sqliteTableNameFromToken(pTok); + if( zName==0 ) return 0; + pTab = sqliteFindTable(pParse->db, zName, 0); + sqliteFree(zName); + if( pTab==0 ){ + sqliteErrorMsg(pParse, "no such table: %T", pTok); + } + return pTab; +} + +/* +** This routine is called to do the work of a DROP TABLE statement. +** pName is the name of the table to be dropped. +*/ +void sqliteDropTable(Parse *pParse, Token *pName, int isView){ + Table *pTable; + Vdbe *v; + int base; + sqlite *db = pParse->db; + int iDb; + + if( pParse->nErr || sqlite_malloc_failed ) return; + pTable = sqliteTableFromToken(pParse, pName); + if( pTable==0 ) return; + iDb = pTable->iDb; + assert( iDb>=0 && iDbnDb ); +#ifndef SQLITE_OMIT_AUTHORIZATION + { + int code; + const char *zTab = SCHEMA_TABLE(pTable->iDb); + const char *zDb = db->aDb[pTable->iDb].zName; + if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){ + return; + } + if( isView ){ + if( iDb==1 ){ + code = SQLITE_DROP_TEMP_VIEW; + }else{ + code = SQLITE_DROP_VIEW; + } + }else{ + if( iDb==1 ){ + code = SQLITE_DROP_TEMP_TABLE; + }else{ + code = SQLITE_DROP_TABLE; + } + } + if( sqliteAuthCheck(pParse, code, pTable->zName, 0, zDb) ){ + return; + } + if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTable->zName, 0, zDb) ){ + return; + } + } +#endif + if( pTable->readOnly ){ + sqliteErrorMsg(pParse, "table %s may not be dropped", pTable->zName); + pParse->nErr++; + return; + } + if( isView && pTable->pSelect==0 ){ + sqliteErrorMsg(pParse, "use DROP TABLE to delete table %s", pTable->zName); + return; + } + if( !isView && pTable->pSelect ){ + sqliteErrorMsg(pParse, "use DROP VIEW to delete view %s", pTable->zName); + return; + } + + /* Generate code to remove the table from the master table + ** on disk. + */ + v = sqliteGetVdbe(pParse); + if( v ){ + static VdbeOpList dropTable[] = { + { OP_Rewind, 0, ADDR(8), 0}, + { OP_String, 0, 0, 0}, /* 1 */ + { OP_MemStore, 1, 1, 0}, + { OP_MemLoad, 1, 0, 0}, /* 3 */ + { OP_Column, 0, 2, 0}, + { OP_Ne, 0, ADDR(7), 0}, + { OP_Delete, 0, 0, 0}, + { OP_Next, 0, ADDR(3), 0}, /* 7 */ + }; + Index *pIdx; + Trigger *pTrigger; + sqliteBeginWriteOperation(pParse, 0, pTable->iDb); + + /* Drop all triggers associated with the table being dropped */ + pTrigger = pTable->pTrigger; + while( pTrigger ){ + assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 ); + sqliteDropTriggerPtr(pParse, pTrigger, 1); + if( pParse->explain ){ + pTrigger = pTrigger->pNext; + }else{ + pTrigger = pTable->pTrigger; + } + } + + /* Drop all SQLITE_MASTER entries that refer to the table */ + sqliteOpenMasterTable(v, pTable->iDb); + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+1, pTable->zName, 0); + + /* Drop all SQLITE_TEMP_MASTER entries that refer to the table */ + if( pTable->iDb!=1 ){ + sqliteOpenMasterTable(v, 1); + base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable); + sqliteVdbeChangeP3(v, base+1, pTable->zName, 0); + } + + if( pTable->iDb==0 ){ + sqliteChangeCookie(db, v); + } + sqliteVdbeAddOp(v, OP_Close, 0, 0); + if( !isView ){ + sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->iDb); + for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb); + } + } + sqliteEndWriteOperation(pParse); + } + + /* Delete the in-memory description of the table. + ** + ** Exception: if the SQL statement began with the EXPLAIN keyword, + ** then no changes should be made. + */ + if( !pParse->explain ){ + sqliteUnlinkAndDeleteTable(db, pTable); + db->flags |= SQLITE_InternChanges; + } + sqliteViewResetAll(db, iDb); +} + +/* +** This routine constructs a P3 string suitable for an OP_MakeIdxKey +** opcode and adds that P3 string to the most recently inserted instruction +** in the virtual machine. The P3 string consists of a single character +** for each column in the index pIdx of table pTab. If the column uses +** a numeric sort order, then the P3 string character corresponding to +** that column is 'n'. If the column uses a text sort order, then the +** P3 string is 't'. See the OP_MakeIdxKey opcode documentation for +** additional information. See also the sqliteAddKeyType() routine. +*/ +void sqliteAddIdxKeyType(Vdbe *v, Index *pIdx){ + char *zType; + Table *pTab; + int i, n; + assert( pIdx!=0 && pIdx->pTable!=0 ); + pTab = pIdx->pTable; + n = pIdx->nColumn; + zType = sqliteMallocRaw( n+1 ); + if( zType==0 ) return; + for(i=0; iaiColumn[i]; + assert( iCol>=0 && iColnCol ); + if( (pTab->aCol[iCol].sortOrder & SQLITE_SO_TYPEMASK)==SQLITE_SO_TEXT ){ + zType[i] = 't'; + }else{ + zType[i] = 'n'; + } + } + zType[n] = 0; + sqliteVdbeChangeP3(v, -1, zType, n); + sqliteFree(zType); +} + +/* +** This routine is called to create a new foreign key on the table +** currently under construction. pFromCol determines which columns +** in the current table point to the foreign key. If pFromCol==0 then +** connect the key to the last column inserted. pTo is the name of +** the table referred to. pToCol is a list of tables in the other +** pTo table that the foreign key points to. flags contains all +** information about the conflict resolution algorithms specified +** in the ON DELETE, ON UPDATE and ON INSERT clauses. +** +** An FKey structure is created and added to the table currently +** under construction in the pParse->pNewTable field. The new FKey +** is not linked into db->aFKey at this point - that does not happen +** until sqliteEndTable(). +** +** The foreign key is set for IMMEDIATE processing. A subsequent call +** to sqliteDeferForeignKey() might change this to DEFERRED. +*/ +void sqliteCreateForeignKey( + Parse *pParse, /* Parsing context */ + IdList *pFromCol, /* Columns in this table that point to other table */ + Token *pTo, /* Name of the other table */ + IdList *pToCol, /* Columns in the other table */ + int flags /* Conflict resolution algorithms. */ +){ + Table *p = pParse->pNewTable; + int nByte; + int i; + int nCol; + char *z; + FKey *pFKey = 0; + + assert( pTo!=0 ); + if( p==0 || pParse->nErr ) goto fk_end; + if( pFromCol==0 ){ + int iCol = p->nCol-1; + if( iCol<0 ) goto fk_end; + if( pToCol && pToCol->nId!=1 ){ + sqliteErrorMsg(pParse, "foreign key on %s" + " should reference only one column of table %T", + p->aCol[iCol].zName, pTo); + goto fk_end; + } + nCol = 1; + }else if( pToCol && pToCol->nId!=pFromCol->nId ){ + sqliteErrorMsg(pParse, + "number of columns in foreign key does not match the number of " + "columns in the referenced table"); + goto fk_end; + }else{ + nCol = pFromCol->nId; + } + nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1; + if( pToCol ){ + for(i=0; inId; i++){ + nByte += strlen(pToCol->a[i].zName) + 1; + } + } + pFKey = sqliteMalloc( nByte ); + if( pFKey==0 ) goto fk_end; + pFKey->pFrom = p; + pFKey->pNextFrom = p->pFKey; + z = (char*)&pFKey[1]; + pFKey->aCol = (struct sColMap*)z; + z += sizeof(struct sColMap)*nCol; + pFKey->zTo = z; + memcpy(z, pTo->z, pTo->n); + z[pTo->n] = 0; + z += pTo->n+1; + pFKey->pNextTo = 0; + pFKey->nCol = nCol; + if( pFromCol==0 ){ + pFKey->aCol[0].iFrom = p->nCol-1; + }else{ + for(i=0; inCol; j++){ + if( sqliteStrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){ + pFKey->aCol[i].iFrom = j; + break; + } + } + if( j>=p->nCol ){ + sqliteErrorMsg(pParse, + "unknown column \"%s\" in foreign key definition", + pFromCol->a[i].zName); + goto fk_end; + } + } + } + if( pToCol ){ + for(i=0; ia[i].zName); + pFKey->aCol[i].zCol = z; + memcpy(z, pToCol->a[i].zName, n); + z[n] = 0; + z += n+1; + } + } + pFKey->isDeferred = 0; + pFKey->deleteConf = flags & 0xff; + pFKey->updateConf = (flags >> 8 ) & 0xff; + pFKey->insertConf = (flags >> 16 ) & 0xff; + + /* Link the foreign key to the table as the last step. + */ + p->pFKey = pFKey; + pFKey = 0; + +fk_end: + sqliteFree(pFKey); + sqliteIdListDelete(pFromCol); + sqliteIdListDelete(pToCol); +} + +/* +** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED +** clause is seen as part of a foreign key definition. The isDeferred +** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE. +** The behavior of the most recently created foreign key is adjusted +** accordingly. +*/ +void sqliteDeferForeignKey(Parse *pParse, int isDeferred){ + Table *pTab; + FKey *pFKey; + if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; + pFKey->isDeferred = isDeferred; +} + +/* +** Create a new index for an SQL table. pIndex is the name of the index +** and pTable is the name of the table that is to be indexed. Both will +** be NULL for a primary key or an index that is created to satisfy a +** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable +** as the table to be indexed. pParse->pNewTable is a table that is +** currently being constructed by a CREATE TABLE statement. +** +** pList is a list of columns to be indexed. pList will be NULL if this +** is a primary key or unique-constraint on the most recent column added +** to the table currently under construction. +*/ +void sqliteCreateIndex( + Parse *pParse, /* All information about this parse */ + Token *pName, /* Name of the index. May be NULL */ + SrcList *pTable, /* Name of the table to index. Use pParse->pNewTable if 0 */ + IdList *pList, /* A list of columns to be indexed */ + int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ + Token *pEnd /* The ")" that closes the CREATE INDEX statement */ +){ + Table *pTab; /* Table to be indexed */ + Index *pIndex; /* The index to be created */ + char *zName = 0; + int i, j; + Token nullId; /* Fake token for an empty ID list */ + DbFixer sFix; /* For assigning database names to pTable */ + int isTemp; /* True for a temporary index */ + sqlite *db = pParse->db; + + if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; + if( db->init.busy + && sqliteFixInit(&sFix, pParse, db->init.iDb, "index", pName) + && sqliteFixSrcList(&sFix, pTable) + ){ + goto exit_create_index; + } + + /* + ** Find the table that is to be indexed. Return early if not found. + */ + if( pTable!=0 ){ + assert( pName!=0 ); + assert( pTable->nSrc==1 ); + pTab = sqliteSrcListLookup(pParse, pTable); + }else{ + assert( pName==0 ); + pTab = pParse->pNewTable; + } + if( pTab==0 || pParse->nErr ) goto exit_create_index; + if( pTab->readOnly ){ + sqliteErrorMsg(pParse, "table %s may not be indexed", pTab->zName); + goto exit_create_index; + } + if( pTab->iDb>=2 && db->init.busy==0 ){ + sqliteErrorMsg(pParse, "table %s may not have indices added", pTab->zName); + goto exit_create_index; + } + if( pTab->pSelect ){ + sqliteErrorMsg(pParse, "views may not be indexed"); + goto exit_create_index; + } + isTemp = pTab->iDb==1; + + /* + ** Find the name of the index. Make sure there is not already another + ** index or table with the same name. + ** + ** Exception: If we are reading the names of permanent indices from the + ** sqlite_master table (because some other process changed the schema) and + ** one of the index names collides with the name of a temporary table or + ** index, then we will continue to process this index. + ** + ** If pName==0 it means that we are + ** dealing with a primary key or UNIQUE constraint. We have to invent our + ** own name. + */ + if( pName && !db->init.busy ){ + Index *pISameName; /* Another index with the same name */ + Table *pTSameName; /* A table with same name as the index */ + zName = sqliteTableNameFromToken(pName); + if( zName==0 ) goto exit_create_index; + if( (pISameName = sqliteFindIndex(db, zName, 0))!=0 ){ + sqliteErrorMsg(pParse, "index %s already exists", zName); + goto exit_create_index; + } + if( (pTSameName = sqliteFindTable(db, zName, 0))!=0 ){ + sqliteErrorMsg(pParse, "there is already a table named %s", zName); + goto exit_create_index; + } + }else if( pName==0 ){ + char zBuf[30]; + int n; + Index *pLoop; + for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} + sprintf(zBuf,"%d)",n); + zName = 0; + sqliteSetString(&zName, "(", pTab->zName, " autoindex ", zBuf, (char*)0); + if( zName==0 ) goto exit_create_index; + }else{ + zName = sqliteTableNameFromToken(pName); + } + + /* Check for authorization to create an index. + */ +#ifndef SQLITE_OMIT_AUTHORIZATION + { + const char *zDb = db->aDb[pTab->iDb].zName; + + assert( pTab->iDb==db->init.iDb || isTemp ); + if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){ + goto exit_create_index; + } + i = SQLITE_CREATE_INDEX; + if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX; + if( sqliteAuthCheck(pParse, i, zName, pTab->zName, zDb) ){ + goto exit_create_index; + } + } +#endif + + /* If pList==0, it means this routine was called to make a primary + ** key out of the last column added to the table under construction. + ** So create a fake list to simulate this. + */ + if( pList==0 ){ + nullId.z = pTab->aCol[pTab->nCol-1].zName; + nullId.n = strlen(nullId.z); + pList = sqliteIdListAppend(0, &nullId); + if( pList==0 ) goto exit_create_index; + } + + /* + ** Allocate the index structure. + */ + pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + 1 + + sizeof(int)*pList->nId ); + if( pIndex==0 ) goto exit_create_index; + pIndex->aiColumn = (int*)&pIndex[1]; + pIndex->zName = (char*)&pIndex->aiColumn[pList->nId]; + strcpy(pIndex->zName, zName); + pIndex->pTable = pTab; + pIndex->nColumn = pList->nId; + pIndex->onError = onError; + pIndex->autoIndex = pName==0; + pIndex->iDb = isTemp ? 1 : db->init.iDb; + + /* Scan the names of the columns of the table to be indexed and + ** load the column indices into the Index structure. Report an error + ** if any column is not found. + */ + for(i=0; inId; i++){ + for(j=0; jnCol; j++){ + if( sqliteStrICmp(pList->a[i].zName, pTab->aCol[j].zName)==0 ) break; + } + if( j>=pTab->nCol ){ + sqliteErrorMsg(pParse, "table %s has no column named %s", + pTab->zName, pList->a[i].zName); + sqliteFree(pIndex); + goto exit_create_index; + } + pIndex->aiColumn[i] = j; + } + + /* Link the new Index structure to its table and to the other + ** in-memory database structures. + */ + if( !pParse->explain ){ + Index *p; + p = sqliteHashInsert(&db->aDb[pIndex->iDb].idxHash, + pIndex->zName, strlen(pIndex->zName)+1, pIndex); + if( p ){ + assert( p==pIndex ); /* Malloc must have failed */ + sqliteFree(pIndex); + goto exit_create_index; + } + db->flags |= SQLITE_InternChanges; + } + + /* When adding an index to the list of indices for a table, make + ** sure all indices labeled OE_Replace come after all those labeled + ** OE_Ignore. This is necessary for the correct operation of UPDATE + ** and INSERT. + */ + if( onError!=OE_Replace || pTab->pIndex==0 + || pTab->pIndex->onError==OE_Replace){ + pIndex->pNext = pTab->pIndex; + pTab->pIndex = pIndex; + }else{ + Index *pOther = pTab->pIndex; + while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){ + pOther = pOther->pNext; + } + pIndex->pNext = pOther->pNext; + pOther->pNext = pIndex; + } + + /* If the db->init.busy is 1 it means we are reading the SQL off the + ** "sqlite_master" table on the disk. So do not write to the disk + ** again. Extract the table number from the db->init.newTnum field. + */ + if( db->init.busy && pTable!=0 ){ + pIndex->tnum = db->init.newTnum; + } + + /* If the db->init.busy is 0 then create the index on disk. This + ** involves writing the index into the master table and filling in the + ** index with the current table contents. + ** + ** The db->init.busy is 0 when the user first enters a CREATE INDEX + ** command. db->init.busy is 1 when a database is opened and + ** CREATE INDEX statements are read out of the master table. In + ** the latter case the index already exists on disk, which is why + ** we don't want to recreate it. + ** + ** If pTable==0 it means this index is generated as a primary key + ** or UNIQUE constraint of a CREATE TABLE statement. Since the table + ** has just been created, it contains no data and the index initialization + ** step can be skipped. + */ + else if( db->init.busy==0 ){ + int n; + Vdbe *v; + int lbl1, lbl2; + int i; + int addr; + + v = sqliteGetVdbe(pParse); + if( v==0 ) goto exit_create_index; + if( pTable!=0 ){ + sqliteBeginWriteOperation(pParse, 0, isTemp); + sqliteOpenMasterTable(v, isTemp); + } + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, "index", P3_STATIC); + sqliteVdbeOp3(v, OP_String, 0, 0, pIndex->zName, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->zName, 0); + sqliteVdbeOp3(v, OP_CreateIndex, 0, isTemp,(char*)&pIndex->tnum,P3_POINTER); + pIndex->tnum = 0; + if( pTable ){ + sqliteVdbeCode(v, + OP_Dup, 0, 0, + OP_Integer, isTemp, 0, + OP_OpenWrite, 1, 0, + 0); + } + addr = sqliteVdbeAddOp(v, OP_String, 0, 0); + if( pStart && pEnd ){ + n = Addr(pEnd->z) - Addr(pStart->z) + 1; + sqliteVdbeChangeP3(v, addr, pStart->z, n); + } + sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0); + if( pTable ){ + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeOp3(v, OP_OpenRead, 2, pTab->tnum, pTab->zName, 0); + lbl2 = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Rewind, 2, lbl2); + lbl1 = sqliteVdbeAddOp(v, OP_Recno, 2, 0); + for(i=0; inColumn; i++){ + int iCol = pIndex->aiColumn[i]; + if( pTab->iPKey==iCol ){ + sqliteVdbeAddOp(v, OP_Dup, i, 0); + }else{ + sqliteVdbeAddOp(v, OP_Column, 2, iCol); + } + } + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0); + if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIndex); + sqliteVdbeOp3(v, OP_IdxPut, 1, pIndex->onError!=OE_None, + "indexed columns are not unique", P3_STATIC); + sqliteVdbeAddOp(v, OP_Next, 2, lbl1); + sqliteVdbeResolveLabel(v, lbl2); + sqliteVdbeAddOp(v, OP_Close, 2, 0); + sqliteVdbeAddOp(v, OP_Close, 1, 0); + } + if( pTable!=0 ){ + if( !isTemp ){ + sqliteChangeCookie(db, v); + } + sqliteVdbeAddOp(v, OP_Close, 0, 0); + sqliteEndWriteOperation(pParse); + } + } + + /* Clean up before exiting */ +exit_create_index: + sqliteIdListDelete(pList); + sqliteSrcListDelete(pTable); + sqliteFree(zName); + return; +} + +/* +** This routine will drop an existing named index. This routine +** implements the DROP INDEX statement. +*/ +void sqliteDropIndex(Parse *pParse, SrcList *pName){ + Index *pIndex; + Vdbe *v; + sqlite *db = pParse->db; + + if( pParse->nErr || sqlite_malloc_failed ) return; + assert( pName->nSrc==1 ); + pIndex = sqliteFindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); + if( pIndex==0 ){ + sqliteErrorMsg(pParse, "no such index: %S", pName, 0); + goto exit_drop_index; + } + if( pIndex->autoIndex ){ + sqliteErrorMsg(pParse, "index associated with UNIQUE " + "or PRIMARY KEY constraint cannot be dropped", 0); + goto exit_drop_index; + } + if( pIndex->iDb>1 ){ + sqliteErrorMsg(pParse, "cannot alter schema of attached " + "databases", 0); + goto exit_drop_index; + } +#ifndef SQLITE_OMIT_AUTHORIZATION + { + int code = SQLITE_DROP_INDEX; + Table *pTab = pIndex->pTable; + const char *zDb = db->aDb[pIndex->iDb].zName; + const char *zTab = SCHEMA_TABLE(pIndex->iDb); + if( sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ + goto exit_drop_index; + } + if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX; + if( sqliteAuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){ + goto exit_drop_index; + } + } +#endif + + /* Generate code to remove the index and from the master table */ + v = sqliteGetVdbe(pParse); + if( v ){ + static VdbeOpList dropIndex[] = { + { OP_Rewind, 0, ADDR(9), 0}, + { OP_String, 0, 0, 0}, /* 1 */ + { OP_MemStore, 1, 1, 0}, + { OP_MemLoad, 1, 0, 0}, /* 3 */ + { OP_Column, 0, 1, 0}, + { OP_Eq, 0, ADDR(8), 0}, + { OP_Next, 0, ADDR(3), 0}, + { OP_Goto, 0, ADDR(9), 0}, + { OP_Delete, 0, 0, 0}, /* 8 */ + }; + int base; + + sqliteBeginWriteOperation(pParse, 0, pIndex->iDb); + sqliteOpenMasterTable(v, pIndex->iDb); + base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); + sqliteVdbeChangeP3(v, base+1, pIndex->zName, 0); + if( pIndex->iDb==0 ){ + sqliteChangeCookie(db, v); + } + sqliteVdbeAddOp(v, OP_Close, 0, 0); + sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb); + sqliteEndWriteOperation(pParse); + } + + /* Delete the in-memory description of this index. + */ + if( !pParse->explain ){ + sqliteUnlinkAndDeleteIndex(db, pIndex); + db->flags |= SQLITE_InternChanges; + } + +exit_drop_index: + sqliteSrcListDelete(pName); +} + +/* +** Append a new element to the given IdList. Create a new IdList if +** need be. +** +** A new IdList is returned, or NULL if malloc() fails. +*/ +IdList *sqliteIdListAppend(IdList *pList, Token *pToken){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(IdList) ); + if( pList==0 ) return 0; + pList->nAlloc = 0; + } + if( pList->nId>=pList->nAlloc ){ + struct IdList_item *a; + pList->nAlloc = pList->nAlloc*2 + 5; + a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]) ); + if( a==0 ){ + sqliteIdListDelete(pList); + return 0; + } + pList->a = a; + } + memset(&pList->a[pList->nId], 0, sizeof(pList->a[0])); + if( pToken ){ + char **pz = &pList->a[pList->nId].zName; + sqliteSetNString(pz, pToken->z, pToken->n, 0); + if( *pz==0 ){ + sqliteIdListDelete(pList); + return 0; + }else{ + sqliteDequote(*pz); + } + } + pList->nId++; + return pList; +} + +/* +** Append a new table name to the given SrcList. Create a new SrcList if +** need be. A new entry is created in the SrcList even if pToken is NULL. +** +** A new SrcList is returned, or NULL if malloc() fails. +** +** If pDatabase is not null, it means that the table has an optional +** database name prefix. Like this: "database.table". The pDatabase +** points to the table name and the pTable points to the database name. +** The SrcList.a[].zName field is filled with the table name which might +** come from pTable (if pDatabase is NULL) or from pDatabase. +** SrcList.a[].zDatabase is filled with the database name from pTable, +** or with NULL if no database is specified. +** +** In other words, if call like this: +** +** sqliteSrcListAppend(A,B,0); +** +** Then B is a table name and the database name is unspecified. If called +** like this: +** +** sqliteSrcListAppend(A,B,C); +** +** Then C is the table name and B is the database name. +*/ +SrcList *sqliteSrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(SrcList) ); + if( pList==0 ) return 0; + pList->nAlloc = 1; + } + if( pList->nSrc>=pList->nAlloc ){ + SrcList *pNew; + pList->nAlloc *= 2; + pNew = sqliteRealloc(pList, + sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) ); + if( pNew==0 ){ + sqliteSrcListDelete(pList); + return 0; + } + pList = pNew; + } + memset(&pList->a[pList->nSrc], 0, sizeof(pList->a[0])); + if( pDatabase && pDatabase->z==0 ){ + pDatabase = 0; + } + if( pDatabase && pTable ){ + Token *pTemp = pDatabase; + pDatabase = pTable; + pTable = pTemp; + } + if( pTable ){ + char **pz = &pList->a[pList->nSrc].zName; + sqliteSetNString(pz, pTable->z, pTable->n, 0); + if( *pz==0 ){ + sqliteSrcListDelete(pList); + return 0; + }else{ + sqliteDequote(*pz); + } + } + if( pDatabase ){ + char **pz = &pList->a[pList->nSrc].zDatabase; + sqliteSetNString(pz, pDatabase->z, pDatabase->n, 0); + if( *pz==0 ){ + sqliteSrcListDelete(pList); + return 0; + }else{ + sqliteDequote(*pz); + } + } + pList->a[pList->nSrc].iCursor = -1; + pList->nSrc++; + return pList; +} + +/* +** Assign cursors to all tables in a SrcList +*/ +void sqliteSrcListAssignCursors(Parse *pParse, SrcList *pList){ + int i; + for(i=0; inSrc; i++){ + if( pList->a[i].iCursor<0 ){ + pList->a[i].iCursor = pParse->nTab++; + } + } +} + +/* +** Add an alias to the last identifier on the given identifier list. +*/ +void sqliteSrcListAddAlias(SrcList *pList, Token *pToken){ + if( pList && pList->nSrc>0 ){ + int i = pList->nSrc - 1; + sqliteSetNString(&pList->a[i].zAlias, pToken->z, pToken->n, 0); + sqliteDequote(pList->a[i].zAlias); + } +} + +/* +** Delete an IdList. +*/ +void sqliteIdListDelete(IdList *pList){ + int i; + if( pList==0 ) return; + for(i=0; inId; i++){ + sqliteFree(pList->a[i].zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Return the index in pList of the identifier named zId. Return -1 +** if not found. +*/ +int sqliteIdListIndex(IdList *pList, const char *zName){ + int i; + if( pList==0 ) return -1; + for(i=0; inId; i++){ + if( sqliteStrICmp(pList->a[i].zName, zName)==0 ) return i; + } + return -1; +} + +/* +** Delete an entire SrcList including all its substructure. +*/ +void sqliteSrcListDelete(SrcList *pList){ + int i; + if( pList==0 ) return; + for(i=0; inSrc; i++){ + sqliteFree(pList->a[i].zDatabase); + sqliteFree(pList->a[i].zName); + sqliteFree(pList->a[i].zAlias); + if( pList->a[i].pTab && pList->a[i].pTab->isTransient ){ + sqliteDeleteTable(0, pList->a[i].pTab); + } + sqliteSelectDelete(pList->a[i].pSelect); + sqliteExprDelete(pList->a[i].pOn); + sqliteIdListDelete(pList->a[i].pUsing); + } + sqliteFree(pList); +} + +/* +** Begin a transaction +*/ +void sqliteBeginTransaction(Parse *pParse, int onError){ + sqlite *db; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return; + if( db->flags & SQLITE_InTrans ){ + sqliteErrorMsg(pParse, "cannot start a transaction within a transaction"); + return; + } + sqliteBeginWriteOperation(pParse, 0, 0); + if( !pParse->explain ){ + db->flags |= SQLITE_InTrans; + db->onError = onError; + } +} + +/* +** Commit a transaction +*/ +void sqliteCommitTransaction(Parse *pParse){ + sqlite *db; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return; + if( (db->flags & SQLITE_InTrans)==0 ){ + sqliteErrorMsg(pParse, "cannot commit - no transaction is active"); + return; + } + if( !pParse->explain ){ + db->flags &= ~SQLITE_InTrans; + } + sqliteEndWriteOperation(pParse); + if( !pParse->explain ){ + db->onError = OE_Default; + } +} + +/* +** Rollback a transaction +*/ +void sqliteRollbackTransaction(Parse *pParse){ + sqlite *db; + Vdbe *v; + + if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return; + if( pParse->nErr || sqlite_malloc_failed ) return; + if( sqliteAuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return; + if( (db->flags & SQLITE_InTrans)==0 ){ + sqliteErrorMsg(pParse, "cannot rollback - no transaction is active"); + return; + } + v = sqliteGetVdbe(pParse); + if( v ){ + sqliteVdbeAddOp(v, OP_Rollback, 0, 0); + } + if( !pParse->explain ){ + db->flags &= ~SQLITE_InTrans; + db->onError = OE_Default; + } +} + +/* +** Generate VDBE code that will verify the schema cookie for all +** named database files. +*/ +void sqliteCodeVerifySchema(Parse *pParse, int iDb){ + sqlite *db = pParse->db; + Vdbe *v = sqliteGetVdbe(pParse); + assert( iDb>=0 && iDbnDb ); + assert( db->aDb[iDb].pBt!=0 ); + if( iDb!=1 && !DbHasProperty(db, iDb, DB_Cookie) ){ + sqliteVdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie); + DbSetProperty(db, iDb, DB_Cookie); + } +} + +/* +** Generate VDBE code that prepares for doing an operation that +** might change the database. +** +** This routine starts a new transaction if we are not already within +** a transaction. If we are already within a transaction, then a checkpoint +** is set if the setCheckpoint parameter is true. A checkpoint should +** be set for operations that might fail (due to a constraint) part of +** the way through and which will need to undo some writes without having to +** rollback the whole transaction. For operations where all constraints +** can be checked before any changes are made to the database, it is never +** necessary to undo a write and the checkpoint should not be set. +** +** Only database iDb and the temp database are made writable by this call. +** If iDb==0, then the main and temp databases are made writable. If +** iDb==1 then only the temp database is made writable. If iDb>1 then the +** specified auxiliary database and the temp database are made writable. +*/ +void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int iDb){ + Vdbe *v; + sqlite *db = pParse->db; + if( DbHasProperty(db, iDb, DB_Locked) ) return; + v = sqliteGetVdbe(pParse); + if( v==0 ) return; + if( !db->aDb[iDb].inTrans ){ + sqliteVdbeAddOp(v, OP_Transaction, iDb, 0); + DbSetProperty(db, iDb, DB_Locked); + sqliteCodeVerifySchema(pParse, iDb); + if( iDb!=1 ){ + sqliteBeginWriteOperation(pParse, setCheckpoint, 1); + } + }else if( setCheckpoint ){ + sqliteVdbeAddOp(v, OP_Checkpoint, iDb, 0); + DbSetProperty(db, iDb, DB_Locked); + } +} + +/* +** Generate code that concludes an operation that may have changed +** the database. If a statement transaction was started, then emit +** an OP_Commit that will cause the changes to be committed to disk. +** +** Note that checkpoints are automatically committed at the end of +** a statement. Note also that there can be multiple calls to +** sqliteBeginWriteOperation() but there should only be a single +** call to sqliteEndWriteOperation() at the conclusion of the statement. +*/ +void sqliteEndWriteOperation(Parse *pParse){ + Vdbe *v; + sqlite *db = pParse->db; + if( pParse->trigStack ) return; /* if this is in a trigger */ + v = sqliteGetVdbe(pParse); + if( v==0 ) return; + if( db->flags & SQLITE_InTrans ){ + /* A BEGIN has executed. Do not commit until we see an explicit + ** COMMIT statement. */ + }else{ + sqliteVdbeAddOp(v, OP_Commit, 0, 0); + } +} diff --git a/src/libs/sqlite2/copy.c b/src/libs/sqlite2/copy.c new file mode 100644 index 00000000..a289a7be --- /dev/null +++ b/src/libs/sqlite2/copy.c @@ -0,0 +1,110 @@ +/* +** 2003 April 6 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the COPY command. +** +** $Id: copy.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" + +/* +** The COPY command is for compatibility with PostgreSQL and specificially +** for the ability to read the output of pg_dump. The format is as +** follows: +** +** COPY table FROM file [USING DELIMITERS string] +** +** "table" is an existing table name. We will read lines of code from +** file to fill this table with data. File might be "stdin". The optional +** delimiter string identifies the field separators. The default is a tab. +*/ +void sqliteCopy( + Parse *pParse, /* The parser context */ + SrcList *pTableName, /* The name of the table into which we will insert */ + Token *pFilename, /* The file from which to obtain information */ + Token *pDelimiter, /* Use this as the field delimiter */ + int onError /* What to do if a constraint fails */ +){ + Table *pTab; + int i; + Vdbe *v; + int addr, end; + char *zFile = 0; + const char *zDb; + sqlite *db = pParse->db; + + + if( sqlite_malloc_failed ) goto copy_cleanup; + assert( pTableName->nSrc==1 ); + pTab = sqliteSrcListLookup(pParse, pTableName); + if( pTab==0 || sqliteIsReadOnly(pParse, pTab, 0) ) goto copy_cleanup; + zFile = sqliteStrNDup(pFilename->z, pFilename->n); + sqliteDequote(zFile); + assert( pTab->iDbnDb ); + zDb = db->aDb[pTab->iDb].zName; + if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) + || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){ + goto copy_cleanup; + } + v = sqliteGetVdbe(pParse); + if( v ){ + sqliteBeginWriteOperation(pParse, 1, pTab->iDb); + addr = sqliteVdbeOp3(v, OP_FileOpen, 0, 0, pFilename->z, pFilename->n); + sqliteVdbeDequoteP3(v, addr); + sqliteOpenTableAndIndices(pParse, pTab, 0); + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */ + } + end = sqliteVdbeMakeLabel(v); + addr = sqliteVdbeAddOp(v, OP_FileRead, pTab->nCol, end); + if( pDelimiter ){ + sqliteVdbeChangeP3(v, addr, pDelimiter->z, pDelimiter->n); + sqliteVdbeDequoteP3(v, addr); + }else{ + sqliteVdbeChangeP3(v, addr, "\t", 1); + } + if( pTab->iPKey>=0 ){ + sqliteVdbeAddOp(v, OP_FileColumn, pTab->iPKey, 0); + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_NewRecno, 0, 0); + } + for(i=0; inCol; i++){ + if( i==pTab->iPKey ){ + /* The integer primary key column is filled with NULL since its + ** value is always pulled from the record number */ + sqliteVdbeAddOp(v, OP_String, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_FileColumn, i, 0); + } + } + sqliteGenerateConstraintChecks(pParse, pTab, 0, 0, pTab->iPKey>=0, + 0, onError, addr); + sqliteCompleteInsertion(pParse, pTab, 0, 0, 0, 0, -1); + if( (db->flags & SQLITE_CountRows)!=0 ){ + sqliteVdbeAddOp(v, OP_AddImm, 1, 0); /* Increment row count */ + } + sqliteVdbeAddOp(v, OP_Goto, 0, addr); + sqliteVdbeResolveLabel(v, end); + sqliteVdbeAddOp(v, OP_Noop, 0, 0); + sqliteEndWriteOperation(pParse); + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeAddOp(v, OP_ColumnName, 0, 1); + sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC); + sqliteVdbeAddOp(v, OP_Callback, 1, 0); + } + } + +copy_cleanup: + sqliteSrcListDelete(pTableName); + sqliteFree(zFile); + return; +} diff --git a/src/libs/sqlite2/date.c b/src/libs/sqlite2/date.c new file mode 100644 index 00000000..9198b26f --- /dev/null +++ b/src/libs/sqlite2/date.c @@ -0,0 +1,875 @@ +/* +** 2003 October 31 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement date and time +** functions for SQLite. +** +** There is only one exported symbol in this file - the function +** sqliteRegisterDateTimeFunctions() found at the bottom of the file. +** All other code has file scope. +** +** $Id: date.c 875429 2008-10-24 12:20:41Z cgilles $ +** +** NOTES: +** +** SQLite processes all times and dates as Julian Day numbers. The +** dates and times are stored as the number of days since noon +** in Greenwich on November 24, 4714 B.C. according to the Gregorian +** calendar system. +** +** 1970-01-01 00:00:00 is JD 2440587.5 +** 2000-01-01 00:00:00 is JD 2451544.5 +** +** This implemention requires years to be expressed as a 4-digit number +** which means that only dates between 0000-01-01 and 9999-12-31 can +** be represented, even though julian day numbers allow a much wider +** range of dates. +** +** The Gregorian calendar system is used for all dates and times, +** even those that predate the Gregorian calendar. Historians usually +** use the Julian calendar for dates prior to 1582-10-15 and for some +** dates afterwards, depending on locale. Beware of this difference. +** +** The conversion algorithms are implemented based on descriptions +** in the following text: +** +** Jean Meeus +** Astronomical Algorithms, 2nd Edition, 1998 +** ISBM 0-943396-61-1 +** Willmann-Bell, Inc +** Richmond, Virginia (USA) +*/ +#include "os.h" +#include "sqliteInt.h" +#include +#include +#include +#include + +#ifndef SQLITE_OMIT_DATETIME_FUNCS + +/* +** A structure for holding a single date and time. +*/ +typedef struct DateTime DateTime; +struct DateTime { + double rJD; /* The julian day number */ + int Y, M, D; /* Year, month, and day */ + int h, m; /* Hour and minutes */ + int tz; /* Timezone offset in minutes */ + double s; /* Seconds */ + char validYMD; /* True if Y,M,D are valid */ + char validHMS; /* True if h,m,s are valid */ + char validJD; /* True if rJD is valid */ + char validTZ; /* True if tz is valid */ +}; + + +/* +** Convert zDate into one or more integers. Additional arguments +** come in groups of 5 as follows: +** +** N number of digits in the integer +** min minimum allowed value of the integer +** max maximum allowed value of the integer +** nextC first character after the integer +** pVal where to write the integers value. +** +** Conversions continue until one with nextC==0 is encountered. +** The function returns the number of successful conversions. +*/ +static int getDigits(const char *zDate, ...){ + va_list ap; + int val; + int N; + int min; + int max; + int nextC; + int *pVal; + int cnt = 0; + va_start(ap, zDate); + do{ + N = va_arg(ap, int); + min = va_arg(ap, int); + max = va_arg(ap, int); + nextC = va_arg(ap, int); + pVal = va_arg(ap, int*); + val = 0; + while( N-- ){ + if( !isdigit(*zDate) ){ + return cnt; + } + val = val*10 + *zDate - '0'; + zDate++; + } + if( valmax || (nextC!=0 && nextC!=*zDate) ){ + return cnt; + } + *pVal = val; + zDate++; + cnt++; + }while( nextC ); + return cnt; +} + +/* +** Read text from z[] and convert into a floating point number. Return +** the number of digits converted. +*/ +static int getValue(const char *z, double *pR){ + const char *zEnd; + *pR = sqliteAtoF(z, &zEnd); + return zEnd - z; +} + +/* +** Parse a timezone extension on the end of a date-time. +** The extension is of the form: +** +** (+/-)HH:MM +** +** If the parse is successful, write the number of minutes +** of change in *pnMin and return 0. If a parser error occurs, +** return 0. +** +** A missing specifier is not considered an error. +*/ +static int parseTimezone(const char *zDate, DateTime *p){ + int sgn = 0; + int nHr, nMn; + while( isspace(*zDate) ){ zDate++; } + p->tz = 0; + if( *zDate=='-' ){ + sgn = -1; + }else if( *zDate=='+' ){ + sgn = +1; + }else{ + return *zDate!=0; + } + zDate++; + if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){ + return 1; + } + zDate += 5; + p->tz = sgn*(nMn + nHr*60); + while( isspace(*zDate) ){ zDate++; } + return *zDate!=0; +} + +/* +** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF. +** The HH, MM, and SS must each be exactly 2 digits. The +** fractional seconds FFFF can be one or more digits. +** +** Return 1 if there is a parsing error and 0 on success. +*/ +static int parseHhMmSs(const char *zDate, DateTime *p){ + int h, m, s; + double ms = 0.0; + if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){ + return 1; + } + zDate += 5; + if( *zDate==':' ){ + zDate++; + if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){ + return 1; + } + zDate += 2; + if( *zDate=='.' && isdigit(zDate[1]) ){ + double rScale = 1.0; + zDate++; + while( isdigit(*zDate) ){ + ms = ms*10.0 + *zDate - '0'; + rScale *= 10.0; + zDate++; + } + ms /= rScale; + } + }else{ + s = 0; + } + p->validJD = 0; + p->validHMS = 1; + p->h = h; + p->m = m; + p->s = s + ms; + if( parseTimezone(zDate, p) ) return 1; + p->validTZ = p->tz!=0; + return 0; +} + +/* +** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume +** that the YYYY-MM-DD is according to the Gregorian calendar. +** +** Reference: Meeus page 61 +*/ +static void computeJD(DateTime *p){ + int Y, M, D, A, B, X1, X2; + + if( p->validJD ) return; + if( p->validYMD ){ + Y = p->Y; + M = p->M; + D = p->D; + }else{ + Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */ + M = 1; + D = 1; + } + if( M<=2 ){ + Y--; + M += 12; + } + A = Y/100; + B = 2 - A + (A/4); + X1 = 365.25*(Y+4716); + X2 = 30.6001*(M+1); + p->rJD = X1 + X2 + D + B - 1524.5; + p->validJD = 1; + p->validYMD = 0; + if( p->validHMS ){ + p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0; + if( p->validTZ ){ + p->rJD += p->tz*60/86400.0; + p->validHMS = 0; + p->validTZ = 0; + } + } +} + +/* +** Parse dates of the form +** +** YYYY-MM-DD HH:MM:SS.FFF +** YYYY-MM-DD HH:MM:SS +** YYYY-MM-DD HH:MM +** YYYY-MM-DD +** +** Write the result into the DateTime structure and return 0 +** on success and 1 if the input string is not a well-formed +** date. +*/ +static int parseYyyyMmDd(const char *zDate, DateTime *p){ + int Y, M, D, neg; + + if( zDate[0]=='-' ){ + zDate++; + neg = 1; + }else{ + neg = 0; + } + if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){ + return 1; + } + zDate += 10; + while( isspace(*zDate) ){ zDate++; } + if( parseHhMmSs(zDate, p)==0 ){ + /* We got the time */ + }else if( *zDate==0 ){ + p->validHMS = 0; + }else{ + return 1; + } + p->validJD = 0; + p->validYMD = 1; + p->Y = neg ? -Y : Y; + p->M = M; + p->D = D; + if( p->validTZ ){ + computeJD(p); + } + return 0; +} + +/* +** Attempt to parse the given string into a Julian Day Number. Return +** the number of errors. +** +** The following are acceptable forms for the input string: +** +** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM +** DDDD.DD +** now +** +** In the first form, the +/-HH:MM is always optional. The fractional +** seconds extension (the ".FFF") is optional. The seconds portion +** (":SS.FFF") is option. The year and date can be omitted as long +** as there is a time string. The time string can be omitted as long +** as there is a year and date. +*/ +static int parseDateOrTime(const char *zDate, DateTime *p){ + memset(p, 0, sizeof(*p)); + if( parseYyyyMmDd(zDate,p)==0 ){ + return 0; + }else if( parseHhMmSs(zDate, p)==0 ){ + return 0; + }else if( sqliteStrICmp(zDate,"now")==0){ + double r; + if( sqliteOsCurrentTime(&r)==0 ){ + p->rJD = r; + p->validJD = 1; + return 0; + } + return 1; + }else if( sqliteIsNumber(zDate) ){ + p->rJD = sqliteAtoF(zDate, 0); + p->validJD = 1; + return 0; + } + return 1; +} + +/* +** Compute the Year, Month, and Day from the julian day number. +*/ +static void computeYMD(DateTime *p){ + int Z, A, B, C, D, E, X1; + if( p->validYMD ) return; + if( !p->validJD ){ + p->Y = 2000; + p->M = 1; + p->D = 1; + }else{ + Z = p->rJD + 0.5; + A = (Z - 1867216.25)/36524.25; + A = Z + 1 + A - (A/4); + B = A + 1524; + C = (B - 122.1)/365.25; + D = 365.25*C; + E = (B-D)/30.6001; + X1 = 30.6001*E; + p->D = B - D - X1; + p->M = E<14 ? E-1 : E-13; + p->Y = p->M>2 ? C - 4716 : C - 4715; + } + p->validYMD = 1; +} + +/* +** Compute the Hour, Minute, and Seconds from the julian day number. +*/ +static void computeHMS(DateTime *p){ + int Z, s; + if( p->validHMS ) return; + Z = p->rJD + 0.5; + s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5; + p->s = 0.001*s; + s = p->s; + p->s -= s; + p->h = s/3600; + s -= p->h*3600; + p->m = s/60; + p->s += s - p->m*60; + p->validHMS = 1; +} + +/* +** Compute both YMD and HMS +*/ +static void computeYMD_HMS(DateTime *p){ + computeYMD(p); + computeHMS(p); +} + +/* +** Clear the YMD and HMS and the TZ +*/ +static void clearYMD_HMS_TZ(DateTime *p){ + p->validYMD = 0; + p->validHMS = 0; + p->validTZ = 0; +} + +/* +** Compute the difference (in days) between localtime and UTC (a.k.a. GMT) +** for the time value p where p is in UTC. +*/ +static double localtimeOffset(DateTime *p){ + DateTime x, y; + time_t t; + struct tm *pTm; + x = *p; + computeYMD_HMS(&x); + if( x.Y<1971 || x.Y>=2038 ){ + x.Y = 2000; + x.M = 1; + x.D = 1; + x.h = 0; + x.m = 0; + x.s = 0.0; + } else { + int s = x.s + 0.5; + x.s = s; + } + x.tz = 0; + x.validJD = 0; + computeJD(&x); + t = (x.rJD-2440587.5)*86400.0 + 0.5; + sqliteOsEnterMutex(); + pTm = localtime(&t); + y.Y = pTm->tm_year + 1900; + y.M = pTm->tm_mon + 1; + y.D = pTm->tm_mday; + y.h = pTm->tm_hour; + y.m = pTm->tm_min; + y.s = pTm->tm_sec; + sqliteOsLeaveMutex(); + y.validYMD = 1; + y.validHMS = 1; + y.validJD = 0; + y.validTZ = 0; + computeJD(&y); + return y.rJD - x.rJD; +} + +/* +** Process a modifier to a date-time stamp. The modifiers are +** as follows: +** +** NNN days +** NNN hours +** NNN minutes +** NNN.NNNN seconds +** NNN months +** NNN years +** start of month +** start of year +** start of week +** start of day +** weekday N +** unixepoch +** localtime +** utc +** +** Return 0 on success and 1 if there is any kind of error. +*/ +static int parseModifier(const char *zMod, DateTime *p){ + int rc = 1; + int n; + double r; + char *z, zBuf[30]; + z = zBuf; + for(n=0; nrJD += localtimeOffset(p); + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } + case 'u': { + /* + ** unixepoch + ** + ** Treat the current value of p->rJD as the number of + ** seconds since 1970. Convert to a real julian day number. + */ + if( strcmp(z, "unixepoch")==0 && p->validJD ){ + p->rJD = p->rJD/86400.0 + 2440587.5; + clearYMD_HMS_TZ(p); + rc = 0; + }else if( strcmp(z, "utc")==0 ){ + double c1; + computeJD(p); + c1 = localtimeOffset(p); + p->rJD -= c1; + clearYMD_HMS_TZ(p); + p->rJD += c1 - localtimeOffset(p); + rc = 0; + } + break; + } + case 'w': { + /* + ** weekday N + ** + ** Move the date to the same time on the next occurrance of + ** weekday N where 0==Sunday, 1==Monday, and so forth. If the + ** date is already on the appropriate weekday, this is a no-op. + */ + if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0 + && (n=r)==r && n>=0 && r<7 ){ + int Z; + computeYMD_HMS(p); + p->validTZ = 0; + p->validJD = 0; + computeJD(p); + Z = p->rJD + 1.5; + Z %= 7; + if( Z>n ) Z -= 7; + p->rJD += n - Z; + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } + case 's': { + /* + ** start of TTTTT + ** + ** Move the date backwards to the beginning of the current day, + ** or month or year. + */ + if( strncmp(z, "start of ", 9)!=0 ) break; + z += 9; + computeYMD(p); + p->validHMS = 1; + p->h = p->m = 0; + p->s = 0.0; + p->validTZ = 0; + p->validJD = 0; + if( strcmp(z,"month")==0 ){ + p->D = 1; + rc = 0; + }else if( strcmp(z,"year")==0 ){ + computeYMD(p); + p->M = 1; + p->D = 1; + rc = 0; + }else if( strcmp(z,"day")==0 ){ + rc = 0; + } + break; + } + case '+': + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + n = getValue(z, &r); + if( n<=0 ) break; + if( z[n]==':' ){ + /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the + ** specified number of hours, minutes, seconds, and fractional seconds + ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be + ** omitted. + */ + const char *z2 = z; + DateTime tx; + int day; + if( !isdigit(*z2) ) z2++; + memset(&tx, 0, sizeof(tx)); + if( parseHhMmSs(z2, &tx) ) break; + computeJD(&tx); + tx.rJD -= 0.5; + day = (int)tx.rJD; + tx.rJD -= day; + if( z[0]=='-' ) tx.rJD = -tx.rJD; + computeJD(p); + clearYMD_HMS_TZ(p); + p->rJD += tx.rJD; + rc = 0; + break; + } + z += n; + while( isspace(z[0]) ) z++; + n = strlen(z); + if( n>10 || n<3 ) break; + if( z[n-1]=='s' ){ z[n-1] = 0; n--; } + computeJD(p); + rc = 0; + if( n==3 && strcmp(z,"day")==0 ){ + p->rJD += r; + }else if( n==4 && strcmp(z,"hour")==0 ){ + p->rJD += r/24.0; + }else if( n==6 && strcmp(z,"minute")==0 ){ + p->rJD += r/(24.0*60.0); + }else if( n==6 && strcmp(z,"second")==0 ){ + p->rJD += r/(24.0*60.0*60.0); + }else if( n==5 && strcmp(z,"month")==0 ){ + int x, y; + computeYMD_HMS(p); + p->M += r; + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; + p->validJD = 0; + computeJD(p); + y = r; + if( y!=r ){ + p->rJD += (r - y)*30.0; + } + }else if( n==4 && strcmp(z,"year")==0 ){ + computeYMD_HMS(p); + p->Y += r; + p->validJD = 0; + computeJD(p); + }else{ + rc = 1; + } + clearYMD_HMS_TZ(p); + break; + } + default: { + break; + } + } + return rc; +} + +/* +** Process time function arguments. argv[0] is a date-time stamp. +** argv[1] and following are modifiers. Parse them all and write +** the resulting time into the DateTime structure p. Return 0 +** on success and 1 if there are any errors. +*/ +static int isDate(int argc, const char **argv, DateTime *p){ + int i; + if( argc==0 ) return 1; + if( argv[0]==0 || parseDateOrTime(argv[0], p) ) return 1; + for(i=1; izErrMsg and return NULL. If all tables +** are found, return a pointer to the last table. +*/ +Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){ + Table *pTab = 0; + int i; + for(i=0; inSrc; i++){ + const char *zTab = pSrc->a[i].zName; + const char *zDb = pSrc->a[i].zDatabase; + pTab = sqliteLocateTable(pParse, zTab, zDb); + pSrc->a[i].pTab = pTab; + } + return pTab; +} + +/* +** Check to make sure the given table is writable. If it is not +** writable, generate an error message and return 1. If it is +** writable return 0; +*/ +int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){ + if( pTab->readOnly ){ + sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName); + return 1; + } + if( !viewOk && pTab->pSelect ){ + sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName); + return 1; + } + return 0; +} + +/* +** Process a DELETE FROM statement. +*/ +void sqliteDeleteFrom( + Parse *pParse, /* The parser context */ + SrcList *pTabList, /* The table from which we should delete things */ + Expr *pWhere /* The WHERE clause. May be null */ +){ + Vdbe *v; /* The virtual database engine */ + Table *pTab; /* The table from which records will be deleted */ + const char *zDb; /* Name of database holding pTab */ + int end, addr; /* A couple addresses of generated code */ + int i; /* Loop counter */ + WhereInfo *pWInfo; /* Information about the WHERE clause */ + Index *pIdx; /* For looping over indices of the table */ + int iCur; /* VDBE Cursor number for pTab */ + sqlite *db; /* Main database structure */ + int isView; /* True if attempting to delete from a view */ + AuthContext sContext; /* Authorization context */ + + int row_triggers_exist = 0; /* True if any triggers exist */ + int before_triggers; /* True if there are BEFORE triggers */ + int after_triggers; /* True if there are AFTER triggers */ + int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ + + sContext.pParse = 0; + if( pParse->nErr || sqlite_malloc_failed ){ + pTabList = 0; + goto delete_from_cleanup; + } + db = pParse->db; + assert( pTabList->nSrc==1 ); + + /* Locate the table which we want to delete. This table has to be + ** put in an SrcList structure because some of the subroutines we + ** will be calling are designed to work with multiple tables and expect + ** an SrcList* parameter instead of just a Table* parameter. + */ + pTab = sqliteSrcListLookup(pParse, pTabList); + if( pTab==0 ) goto delete_from_cleanup; + before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, + TK_DELETE, TK_BEFORE, TK_ROW, 0); + after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, + TK_DELETE, TK_AFTER, TK_ROW, 0); + row_triggers_exist = before_triggers || after_triggers; + isView = pTab->pSelect!=0; + if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){ + goto delete_from_cleanup; + } + assert( pTab->iDbnDb ); + zDb = db->aDb[pTab->iDb].zName; + if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ + goto delete_from_cleanup; + } + + /* If pTab is really a view, make sure it has been initialized. + */ + if( isView && sqliteViewGetColumnNames(pParse, pTab) ){ + goto delete_from_cleanup; + } + + /* Allocate a cursor used to store the old.* data for a trigger. + */ + if( row_triggers_exist ){ + oldIdx = pParse->nTab++; + } + + /* Resolve the column names in all the expressions. + */ + assert( pTabList->nSrc==1 ); + iCur = pTabList->a[0].iCursor = pParse->nTab++; + if( pWhere ){ + if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){ + goto delete_from_cleanup; + } + if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ + goto delete_from_cleanup; + } + } + + /* Start the view context + */ + if( isView ){ + sqliteAuthContextPush(pParse, &sContext, pTab->zName); + } + + /* Begin generating code. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ){ + goto delete_from_cleanup; + } + sqliteBeginWriteOperation(pParse, row_triggers_exist, pTab->iDb); + + /* If we are trying to delete from a view, construct that view into + ** a temporary table. + */ + if( isView ){ + Select *pView = sqliteSelectDup(pTab->pSelect); + sqliteSelect(pParse, pView, SRT_TempTable, iCur, 0, 0, 0); + sqliteSelectDelete(pView); + } + + /* Initialize the counter of the number of rows deleted, if + ** we are counting rows. + */ + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeAddOp(v, OP_Integer, 0, 0); + } + + /* Special case: A DELETE without a WHERE clause deletes everything. + ** It is easier just to erase the whole table. Note, however, that + ** this means that the row change count will be incorrect. + */ + if( pWhere==0 && !row_triggers_exist ){ + if( db->flags & SQLITE_CountRows ){ + /* If counting rows deleted, just count the total number of + ** entries in the table. */ + int endOfLoop = sqliteVdbeMakeLabel(v); + int addr; + if( !isView ){ + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); + } + sqliteVdbeAddOp(v, OP_Rewind, iCur, sqliteVdbeCurrentAddr(v)+2); + addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0); + sqliteVdbeAddOp(v, OP_Next, iCur, addr); + sqliteVdbeResolveLabel(v, endOfLoop); + sqliteVdbeAddOp(v, OP_Close, iCur, 0); + } + if( !isView ){ + sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb); + } + } + } + + /* The usual case: There is a WHERE clause so we have to scan through + ** the table and pick which records to delete. + */ + else{ + /* Begin the database scan + */ + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1, 0); + if( pWInfo==0 ) goto delete_from_cleanup; + + /* Remember the key of every item to be deleted. + */ + sqliteVdbeAddOp(v, OP_ListWrite, 0, 0); + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeAddOp(v, OP_AddImm, 1, 0); + } + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* Open the pseudo-table used to store OLD if there are triggers. + */ + if( row_triggers_exist ){ + sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0); + } + + /* Delete every item whose key was written to the list during the + ** database scan. We have to delete items after the scan is complete + ** because deleting an item can change the scan order. + */ + sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); + end = sqliteVdbeMakeLabel(v); + + /* This is the beginning of the delete loop when there are + ** row triggers. + */ + if( row_triggers_exist ){ + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + if( !isView ){ + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum); + } + sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0); + + sqliteVdbeAddOp(v, OP_Recno, iCur, 0); + sqliteVdbeAddOp(v, OP_RowData, iCur, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); + if( !isView ){ + sqliteVdbeAddOp(v, OP_Close, iCur, 0); + } + + sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, + oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, + addr); + } + + if( !isView ){ + /* Open cursors for the table we are deleting from and all its + ** indices. If there are row triggers, this happens inside the + ** OP_ListRead loop because the cursor have to all be closed + ** before the trigger fires. If there are no row triggers, the + ** cursors are opened only once on the outside the loop. + */ + pParse->nTab = iCur + 1; + sqliteOpenTableAndIndices(pParse, pTab, iCur); + + /* This is the beginning of the delete loop when there are no + ** row triggers */ + if( !row_triggers_exist ){ + addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); + } + + /* Delete the row */ + sqliteGenerateRowDelete(db, v, pTab, iCur, pParse->trigStack==0); + } + + /* If there are row triggers, close all cursors then invoke + ** the AFTER triggers + */ + if( row_triggers_exist ){ + if( !isView ){ + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); + } + sqliteVdbeAddOp(v, OP_Close, iCur, 0); + } + sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, + oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, + addr); + } + + /* End of the delete loop */ + sqliteVdbeAddOp(v, OP_Goto, 0, addr); + sqliteVdbeResolveLabel(v, end); + sqliteVdbeAddOp(v, OP_ListReset, 0, 0); + + /* Close the cursors after the loop if there are no row triggers */ + if( !row_triggers_exist ){ + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + sqliteVdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum); + } + sqliteVdbeAddOp(v, OP_Close, iCur, 0); + pParse->nTab = iCur; + } + } + sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); + sqliteEndWriteOperation(pParse); + + /* + ** Return the number of rows that were deleted. + */ + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeAddOp(v, OP_ColumnName, 0, 1); + sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC); + sqliteVdbeAddOp(v, OP_Callback, 1, 0); + } + +delete_from_cleanup: + sqliteAuthContextPop(&sContext); + sqliteSrcListDelete(pTabList); + sqliteExprDelete(pWhere); + return; +} + +/* +** This routine generates VDBE code that causes a single row of a +** single table to be deleted. +** +** The VDBE must be in a particular state when this routine is called. +** These are the requirements: +** +** 1. A read/write cursor pointing to pTab, the table containing the row +** to be deleted, must be opened as cursor number "base". +** +** 2. Read/write cursors for all indices of pTab must be open as +** cursor number base+i for the i-th index. +** +** 3. The record number of the row to be deleted must be on the top +** of the stack. +** +** This routine pops the top of the stack to remove the record number +** and then generates code to remove both the table record and all index +** entries that point to that record. +*/ +void sqliteGenerateRowDelete( + sqlite *db, /* The database containing the index */ + Vdbe *v, /* Generate code into this VDBE */ + Table *pTab, /* Table containing the row to be deleted */ + int iCur, /* Cursor number for the table */ + int count /* Increment the row change counter */ +){ + int addr; + addr = sqliteVdbeAddOp(v, OP_NotExists, iCur, 0); + sqliteGenerateRowIndexDelete(db, v, pTab, iCur, 0); + sqliteVdbeAddOp(v, OP_Delete, iCur, + (count?OPFLAG_NCHANGE:0) | OPFLAG_CSCHANGE); + sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); +} + +/* +** This routine generates VDBE code that causes the deletion of all +** index entries associated with a single row of a single table. +** +** The VDBE must be in a particular state when this routine is called. +** These are the requirements: +** +** 1. A read/write cursor pointing to pTab, the table containing the row +** to be deleted, must be opened as cursor number "iCur". +** +** 2. Read/write cursors for all indices of pTab must be open as +** cursor number iCur+i for the i-th index. +** +** 3. The "iCur" cursor must be pointing to the row that is to be +** deleted. +*/ +void sqliteGenerateRowIndexDelete( + sqlite *db, /* The database containing the index */ + Vdbe *v, /* Generate code into this VDBE */ + Table *pTab, /* Table containing the row to be deleted */ + int iCur, /* Cursor number for the table */ + char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */ +){ + int i; + Index *pIdx; + + for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ + int j; + if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue; + sqliteVdbeAddOp(v, OP_Recno, iCur, 0); + for(j=0; jnColumn; j++){ + int idx = pIdx->aiColumn[j]; + if( idx==pTab->iPKey ){ + sqliteVdbeAddOp(v, OP_Dup, j, 0); + }else{ + sqliteVdbeAddOp(v, OP_Column, iCur, idx); + } + } + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); + if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); + sqliteVdbeAddOp(v, OP_IdxDelete, iCur+i, 0); + } +} diff --git a/src/libs/sqlite2/encode.c b/src/libs/sqlite2/encode.c new file mode 100644 index 00000000..7799b8b0 --- /dev/null +++ b/src/libs/sqlite2/encode.c @@ -0,0 +1,257 @@ +/* +** 2002 April 25 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains helper routines used to translate binary data into +** a null-terminated string (suitable for use in SQLite) and back again. +** These are convenience routines for use by people who want to store binary +** data in an SQLite database. The code in this file is not used by any other +** part of the SQLite library. +** +** $Id: encode.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include +#include + +/* +** How This Encoder Works +** +** The output is allowed to contain any character except 0x27 (') and +** 0x00. This is accomplished by using an escape character to encode +** 0x27 and 0x00 as a two-byte sequence. The escape character is always +** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The +** 0x27 character is encoded as the two byte sequence 0x01 0x28. Finally, +** the escape character itself is encoded as the two-character sequence +** 0x01 0x02. +** +** To summarize, the encoder works by using an escape sequences as follows: +** +** 0x00 -> 0x01 0x01 +** 0x01 -> 0x01 0x02 +** 0x27 -> 0x01 0x28 +** +** If that were all the encoder did, it would work, but in certain cases +** it could double the size of the encoded string. For example, to +** encode a string of 100 0x27 characters would require 100 instances of +** the 0x01 0x03 escape sequence resulting in a 200-character output. +** We would prefer to keep the size of the encoded string smaller than +** this. +** +** To minimize the encoding size, we first add a fixed offset value to each +** byte in the sequence. The addition is modulo 256. (That is to say, if +** the sum of the original character value and the offset exceeds 256, then +** the higher order bits are truncated.) The offset is chosen to minimize +** the number of characters in the string that need to be escaped. For +** example, in the case above where the string was composed of 100 0x27 +** characters, the offset might be 0x01. Each of the 0x27 characters would +** then be converted into an 0x28 character which would not need to be +** escaped at all and so the 100 character input string would be converted +** into just 100 characters of output. Actually 101 characters of output - +** we have to record the offset used as the first byte in the sequence so +** that the string can be decoded. Since the offset value is stored as +** part of the output string and the output string is not allowed to contain +** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27. +** +** Here, then, are the encoding steps: +** +** (1) Choose an offset value and make it the first character of +** output. +** +** (2) Copy each input character into the output buffer, one by +** one, adding the offset value as you copy. +** +** (3) If the value of an input character plus offset is 0x00, replace +** that one character by the two-character sequence 0x01 0x01. +** If the sum is 0x01, replace it with 0x01 0x02. If the sum +** is 0x27, replace it with 0x01 0x03. +** +** (4) Put a 0x00 terminator at the end of the output. +** +** Decoding is obvious: +** +** (5) Copy encoded characters except the first into the decode +** buffer. Set the first encoded character aside for use as +** the offset in step 7 below. +** +** (6) Convert each 0x01 0x01 sequence into a single character 0x00. +** Convert 0x01 0x02 into 0x01. Convert 0x01 0x28 into 0x27. +** +** (7) Subtract the offset value that was the first character of +** the encoded buffer from all characters in the output buffer. +** +** The only tricky part is step (1) - how to compute an offset value to +** minimize the size of the output buffer. This is accomplished by testing +** all offset values and picking the one that results in the fewest number +** of escapes. To do that, we first scan the entire input and count the +** number of occurances of each character value in the input. Suppose +** the number of 0x00 characters is N(0), the number of occurances of 0x01 +** is N(1), and so forth up to the number of occurances of 0xff is N(255). +** An offset of 0 is not allowed so we don't have to test it. The number +** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number +** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth. +** In this way we find the offset that gives the minimum number of escapes, +** and thus minimizes the length of the output string. +*/ + +/* +** Encode a binary buffer "in" of size n bytes so that it contains +** no instances of characters '\'' or '\000'. The output is +** null-terminated and can be used as a string value in an INSERT +** or UPDATE statement. Use sqlite_decode_binary() to convert the +** string back into its original binary. +** +** The result is written into a preallocated output buffer "out". +** "out" must be able to hold at least 2 +(257*n)/254 bytes. +** In other words, the output will be expanded by as much as 3 +** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. +** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) +** +** The return value is the number of characters in the encoded +** string, excluding the "\000" terminator. +** +** If out==NULL then no output is generated but the routine still returns +** the number of characters that would have been generated if out had +** not been NULL. +*/ +int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){ + int i, j, e, m; + unsigned char x; + int cnt[256]; + if( n<=0 ){ + if( out ){ + out[0] = 'x'; + out[1] = 0; + } + return 1; + } + memset(cnt, 0, sizeof(cnt)); + for(i=n-1; i>=0; i--){ cnt[in[i]]++; } + m = n; + for(i=1; i<256; i++){ + int sum; + if( i=='\'' ) continue; + sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff]; + if( sum +/* +** The subroutines above are not tested by the usual test suite. To test +** these routines, compile just this one file with a -DENCODER_TEST=1 option +** and run the result. +*/ +int main(int argc, char **argv){ + int i, j, n, m, nOut, nByteIn, nByteOut; + unsigned char in[30000]; + unsigned char out[33000]; + + nByteIn = nByteOut = 0; + for(i=0; i%d (max %d)", n, strlen(out)+1, m); + if( strlen(out)+1>m ){ + printf(" ERROR output too big\n"); + exit(1); + } + for(j=0; out[j]; j++){ + if( out[j]=='\'' ){ + printf(" ERROR contains (')\n"); + exit(1); + } + } + j = sqlite_decode_binary(out, out); + if( j!=n ){ + printf(" ERROR decode size %d\n", j); + exit(1); + } + if( memcmp(in, out, n)!=0 ){ + printf(" ERROR decode mismatch\n"); + exit(1); + } + printf(" OK\n"); + } + fprintf(stderr,"Finished. Total encoding: %d->%d bytes\n", + nByteIn, nByteOut); + fprintf(stderr,"Avg size increase: %.3f%%\n", + (nByteOut-nByteIn)*100.0/(double)nByteIn); +} +#endif /* ENCODER_TEST */ diff --git a/src/libs/sqlite2/expr.c b/src/libs/sqlite2/expr.c new file mode 100644 index 00000000..af4aa596 --- /dev/null +++ b/src/libs/sqlite2/expr.c @@ -0,0 +1,1662 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains routines used for analyzing expressions and +** for generating VDBE code that evaluates expressions in SQLite. +** +** $Id: expr.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "sqliteInt.h" +#include + +/* +** Construct a new expression node and return a pointer to it. Memory +** for this node is obtained from sqliteMalloc(). The calling function +** is responsible for making sure the node eventually gets freed. +*/ +Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + /* When malloc fails, we leak memory from pLeft and pRight */ + return 0; + } + pNew->op = op; + pNew->pLeft = pLeft; + pNew->pRight = pRight; + if( pToken ){ + assert( pToken->dyn==0 ); + pNew->token = *pToken; + pNew->span = *pToken; + }else{ + assert( pNew->token.dyn==0 ); + assert( pNew->token.z==0 ); + assert( pNew->token.n==0 ); + if( pLeft && pRight ){ + sqliteExprSpan(pNew, &pLeft->span, &pRight->span); + }else{ + pNew->span = pNew->token; + } + } + return pNew; +} + +/* +** Set the Expr.span field of the given expression to span all +** text between the two given tokens. +*/ +void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ + assert( pRight!=0 ); + assert( pLeft!=0 ); + /* Note: pExpr might be NULL due to a prior malloc failure */ + if( pExpr && pRight->z && pLeft->z ){ + if( pLeft->dyn==0 && pRight->dyn==0 ){ + pExpr->span.z = pLeft->z; + pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z); + }else{ + pExpr->span.z = 0; + } + } +} + +/* +** Construct a new expression node for a function with multiple +** arguments. +*/ +Expr *sqliteExprFunction(ExprList *pList, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + /* sqliteExprListDelete(pList); // Leak pList when malloc fails */ + return 0; + } + pNew->op = TK_FUNCTION; + pNew->pList = pList; + if( pToken ){ + assert( pToken->dyn==0 ); + pNew->token = *pToken; + }else{ + pNew->token.z = 0; + } + pNew->span = pNew->token; + return pNew; +} + +/* +** Recursively delete an expression tree. +*/ +void sqliteExprDelete(Expr *p){ + if( p==0 ) return; + if( p->span.dyn ) sqliteFree((char*)p->span.z); + if( p->token.dyn ) sqliteFree((char*)p->token.z); + sqliteExprDelete(p->pLeft); + sqliteExprDelete(p->pRight); + sqliteExprListDelete(p->pList); + sqliteSelectDelete(p->pSelect); + sqliteFree(p); +} + + +/* +** The following group of routines make deep copies of expressions, +** expression lists, ID lists, and select statements. The copies can +** be deleted (by being passed to their respective ...Delete() routines) +** without effecting the originals. +** +** The expression list, ID, and source lists return by sqliteExprListDup(), +** sqliteIdListDup(), and sqliteSrcListDup() can not be further expanded +** by subsequent calls to sqlite*ListAppend() routines. +** +** Any tables that the SrcList might point to are not duplicated. +*/ +Expr *sqliteExprDup(Expr *p){ + Expr *pNew; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*p) ); + if( pNew==0 ) return 0; + memcpy(pNew, p, sizeof(*pNew)); + if( p->token.z!=0 ){ + pNew->token.z = sqliteStrNDup(p->token.z, p->token.n); + pNew->token.dyn = 1; + }else{ + assert( pNew->token.z==0 ); + } + pNew->span.z = 0; + pNew->pLeft = sqliteExprDup(p->pLeft); + pNew->pRight = sqliteExprDup(p->pRight); + pNew->pList = sqliteExprListDup(p->pList); + pNew->pSelect = sqliteSelectDup(p->pSelect); + return pNew; +} +void sqliteTokenCopy(Token *pTo, Token *pFrom){ + if( pTo->dyn ) sqliteFree((char*)pTo->z); + if( pFrom->z ){ + pTo->n = pFrom->n; + pTo->z = sqliteStrNDup(pFrom->z, pFrom->n); + pTo->dyn = 1; + }else{ + pTo->z = 0; + } +} +ExprList *sqliteExprListDup(ExprList *p){ + ExprList *pNew; + struct ExprList_item *pItem; + int i; + if( p==0 ) return 0; + pNew = sqliteMalloc( sizeof(*pNew) ); + if( pNew==0 ) return 0; + pNew->nExpr = pNew->nAlloc = p->nExpr; + pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) ); + if( pItem==0 ){ + sqliteFree(pNew); + return 0; + } + for(i=0; inExpr; i++, pItem++){ + Expr *pNewExpr, *pOldExpr; + pItem->pExpr = pNewExpr = sqliteExprDup(pOldExpr = p->a[i].pExpr); + if( pOldExpr->span.z!=0 && pNewExpr ){ + /* Always make a copy of the span for top-level expressions in the + ** expression list. The logic in SELECT processing that determines + ** the names of columns in the result set needs this information */ + sqliteTokenCopy(&pNewExpr->span, &pOldExpr->span); + } + assert( pNewExpr==0 || pNewExpr->span.z!=0 + || pOldExpr->span.z==0 || sqlite_malloc_failed ); + pItem->zName = sqliteStrDup(p->a[i].zName); + pItem->sortOrder = p->a[i].sortOrder; + pItem->isAgg = p->a[i].isAgg; + pItem->done = 0; + } + return pNew; +} +SrcList *sqliteSrcListDup(SrcList *p){ + SrcList *pNew; + int i; + int nByte; + if( p==0 ) return 0; + nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); + pNew = sqliteMallocRaw( nByte ); + if( pNew==0 ) return 0; + pNew->nSrc = pNew->nAlloc = p->nSrc; + for(i=0; inSrc; i++){ + struct SrcList_item *pNewItem = &pNew->a[i]; + struct SrcList_item *pOldItem = &p->a[i]; + pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase); + pNewItem->zName = sqliteStrDup(pOldItem->zName); + pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias); + pNewItem->jointype = pOldItem->jointype; + pNewItem->iCursor = pOldItem->iCursor; + pNewItem->pTab = 0; + pNewItem->pSelect = sqliteSelectDup(pOldItem->pSelect); + pNewItem->pOn = sqliteExprDup(pOldItem->pOn); + pNewItem->pUsing = sqliteIdListDup(pOldItem->pUsing); + } + return pNew; +} +IdList *sqliteIdListDup(IdList *p){ + IdList *pNew; + int i; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*pNew) ); + if( pNew==0 ) return 0; + pNew->nId = pNew->nAlloc = p->nId; + pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) ); + if( pNew->a==0 ) return 0; + for(i=0; inId; i++){ + struct IdList_item *pNewItem = &pNew->a[i]; + struct IdList_item *pOldItem = &p->a[i]; + pNewItem->zName = sqliteStrDup(pOldItem->zName); + pNewItem->idx = pOldItem->idx; + } + return pNew; +} +Select *sqliteSelectDup(Select *p){ + Select *pNew; + if( p==0 ) return 0; + pNew = sqliteMallocRaw( sizeof(*p) ); + if( pNew==0 ) return 0; + pNew->isDistinct = p->isDistinct; + pNew->pEList = sqliteExprListDup(p->pEList); + pNew->pSrc = sqliteSrcListDup(p->pSrc); + pNew->pWhere = sqliteExprDup(p->pWhere); + pNew->pGroupBy = sqliteExprListDup(p->pGroupBy); + pNew->pHaving = sqliteExprDup(p->pHaving); + pNew->pOrderBy = sqliteExprListDup(p->pOrderBy); + pNew->op = p->op; + pNew->pPrior = sqliteSelectDup(p->pPrior); + pNew->nLimit = p->nLimit; + pNew->nOffset = p->nOffset; + pNew->zSelect = 0; + pNew->iLimit = -1; + pNew->iOffset = -1; + return pNew; +} + + +/* +** Add a new element to the end of an expression list. If pList is +** initially NULL, then create a new expression list. +*/ +ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){ + if( pList==0 ){ + pList = sqliteMalloc( sizeof(ExprList) ); + if( pList==0 ){ + /* sqliteExprDelete(pExpr); // Leak memory if malloc fails */ + return 0; + } + assert( pList->nAlloc==0 ); + } + if( pList->nAlloc<=pList->nExpr ){ + pList->nAlloc = pList->nAlloc*2 + 4; + pList->a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0])); + if( pList->a==0 ){ + /* sqliteExprDelete(pExpr); // Leak memory if malloc fails */ + pList->nExpr = pList->nAlloc = 0; + return pList; + } + } + assert( pList->a!=0 ); + if( pExpr || pName ){ + struct ExprList_item *pItem = &pList->a[pList->nExpr++]; + memset(pItem, 0, sizeof(*pItem)); + pItem->pExpr = pExpr; + if( pName ){ + sqliteSetNString(&pItem->zName, pName->z, pName->n, 0); + sqliteDequote(pItem->zName); + } + } + return pList; +} + +/* +** Delete an entire expression list. +*/ +void sqliteExprListDelete(ExprList *pList){ + int i; + if( pList==0 ) return; + assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) ); + assert( pList->nExpr<=pList->nAlloc ); + for(i=0; inExpr; i++){ + sqliteExprDelete(pList->a[i].pExpr); + sqliteFree(pList->a[i].zName); + } + sqliteFree(pList->a); + sqliteFree(pList); +} + +/* +** Walk an expression tree. Return 1 if the expression is constant +** and 0 if it involves variables. +** +** For the purposes of this function, a double-quoted string (ex: "abc") +** is considered a variable but a single-quoted string (ex: 'abc') is +** a constant. +*/ +int sqliteExprIsConstant(Expr *p){ + switch( p->op ){ + case TK_ID: + case TK_COLUMN: + case TK_DOT: + case TK_FUNCTION: + return 0; + case TK_NULL: + case TK_STRING: + case TK_INTEGER: + case TK_FLOAT: + case TK_VARIABLE: + return 1; + default: { + if( p->pLeft && !sqliteExprIsConstant(p->pLeft) ) return 0; + if( p->pRight && !sqliteExprIsConstant(p->pRight) ) return 0; + if( p->pList ){ + int i; + for(i=0; ipList->nExpr; i++){ + if( !sqliteExprIsConstant(p->pList->a[i].pExpr) ) return 0; + } + } + return p->pLeft!=0 || p->pRight!=0 || (p->pList && p->pList->nExpr>0); + } + } + return 0; +} + +/* +** If the given expression codes a constant integer that is small enough +** to fit in a 32-bit integer, return 1 and put the value of the integer +** in *pValue. If the expression is not an integer or if it is too big +** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. +*/ +int sqliteExprIsInteger(Expr *p, int *pValue){ + switch( p->op ){ + case TK_INTEGER: { + if( sqliteFitsIn32Bits(p->token.z) ){ + *pValue = atoi(p->token.z); + return 1; + } + break; + } + case TK_STRING: { + const char *z = p->token.z; + int n = p->token.n; + if( n>0 && z[0]=='-' ){ z++; n--; } + while( n>0 && *z && isdigit(*z) ){ z++; n--; } + if( n==0 && sqliteFitsIn32Bits(p->token.z) ){ + *pValue = atoi(p->token.z); + return 1; + } + break; + } + case TK_UPLUS: { + return sqliteExprIsInteger(p->pLeft, pValue); + } + case TK_UMINUS: { + int v; + if( sqliteExprIsInteger(p->pLeft, &v) ){ + *pValue = -v; + return 1; + } + break; + } + default: break; + } + return 0; +} + +/* +** Return TRUE if the given string is a row-id column name. +*/ +int sqliteIsRowid(const char *z){ + if( sqliteStrICmp(z, "_ROWID_")==0 ) return 1; + if( sqliteStrICmp(z, "ROWID")==0 ) return 1; + if( sqliteStrICmp(z, "OID")==0 ) return 1; + return 0; +} + +/* +** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up +** that name in the set of source tables in pSrcList and make the pExpr +** expression node refer back to that source column. The following changes +** are made to pExpr: +** +** pExpr->iDb Set the index in db->aDb[] of the database holding +** the table. +** pExpr->iTable Set to the cursor number for the table obtained +** from pSrcList. +** pExpr->iColumn Set to the column number within the table. +** pExpr->dataType Set to the appropriate data type for the column. +** pExpr->op Set to TK_COLUMN. +** pExpr->pLeft Any expression this points to is deleted +** pExpr->pRight Any expression this points to is deleted. +** +** The pDbToken is the name of the database (the "X"). This value may be +** NULL meaning that name is of the form Y.Z or Z. Any available database +** can be used. The pTableToken is the name of the table (the "Y"). This +** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it +** means that the form of the name is Z and that columns from any table +** can be used. +** +** If the name cannot be resolved unambiguously, leave an error message +** in pParse and return non-zero. Return zero on success. +*/ +static int lookupName( + Parse *pParse, /* The parsing context */ + Token *pDbToken, /* Name of the database containing table, or NULL */ + Token *pTableToken, /* Name of table containing column, or NULL */ + Token *pColumnToken, /* Name of the column. */ + SrcList *pSrcList, /* List of tables used to resolve column names */ + ExprList *pEList, /* List of expressions used to resolve "AS" */ + Expr *pExpr /* Make this EXPR node point to the selected column */ +){ + char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */ + char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */ + char *zCol = 0; /* Name of the column. The "Z" */ + int i, j; /* Loop counters */ + int cnt = 0; /* Number of matching column names */ + int cntTab = 0; /* Number of matching table names */ + sqlite *db = pParse->db; /* The database */ + + assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ + if( pDbToken && pDbToken->z ){ + zDb = sqliteStrNDup(pDbToken->z, pDbToken->n); + sqliteDequote(zDb); + }else{ + zDb = 0; + } + if( pTableToken && pTableToken->z ){ + zTab = sqliteStrNDup(pTableToken->z, pTableToken->n); + sqliteDequote(zTab); + }else{ + assert( zDb==0 ); + zTab = 0; + } + zCol = sqliteStrNDup(pColumnToken->z, pColumnToken->n); + sqliteDequote(zCol); + if( sqlite_malloc_failed ){ + return 1; /* Leak memory (zDb and zTab) if malloc fails */ + } + assert( zTab==0 || pEList==0 ); + + pExpr->iTable = -1; + for(i=0; inSrc; i++){ + struct SrcList_item *pItem = &pSrcList->a[i]; + Table *pTab = pItem->pTab; + Column *pCol; + + if( pTab==0 ) continue; + assert( pTab->nCol>0 ); + if( zTab ){ + if( pItem->zAlias ){ + char *zTabName = pItem->zAlias; + if( sqliteStrICmp(zTabName, zTab)!=0 ) continue; + }else{ + char *zTabName = pTab->zName; + if( zTabName==0 || sqliteStrICmp(zTabName, zTab)!=0 ) continue; + if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ + continue; + } + } + } + if( 0==(cntTab++) ){ + pExpr->iTable = pItem->iCursor; + pExpr->iDb = pTab->iDb; + } + for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ + if( sqliteStrICmp(pCol->zName, zCol)==0 ){ + cnt++; + pExpr->iTable = pItem->iCursor; + pExpr->iDb = pTab->iDb; + /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ + pExpr->iColumn = j==pTab->iPKey ? -1 : j; + pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK; + break; + } + } + } + + /* If we have not already resolved the name, then maybe + ** it is a new.* or old.* trigger argument reference + */ + if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ + TriggerStack *pTriggerStack = pParse->trigStack; + Table *pTab = 0; + if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zTab) == 0 ){ + pExpr->iTable = pTriggerStack->newIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + }else if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zTab) == 0 ){ + pExpr->iTable = pTriggerStack->oldIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + } + + if( pTab ){ + int j; + Column *pCol = pTab->aCol; + + pExpr->iDb = pTab->iDb; + cntTab++; + for(j=0; j < pTab->nCol; j++, pCol++) { + if( sqliteStrICmp(pCol->zName, zCol)==0 ){ + cnt++; + pExpr->iColumn = j==pTab->iPKey ? -1 : j; + pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK; + break; + } + } + } + } + + /* + ** Perhaps the name is a reference to the ROWID + */ + if( cnt==0 && cntTab==1 && sqliteIsRowid(zCol) ){ + cnt = 1; + pExpr->iColumn = -1; + pExpr->dataType = SQLITE_SO_NUM; + } + + /* + ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z + ** might refer to an result-set alias. This happens, for example, when + ** we are resolving names in the WHERE clause of the following command: + ** + ** SELECT a+b AS x FROM table WHERE x<10; + ** + ** In cases like this, replace pExpr with a copy of the expression that + ** forms the result set entry ("a+b" in the example) and return immediately. + ** Note that the expression in the result set should have already been + ** resolved by the time the WHERE clause is resolved. + */ + if( cnt==0 && pEList!=0 ){ + for(j=0; jnExpr; j++){ + char *zAs = pEList->a[j].zName; + if( zAs!=0 && sqliteStrICmp(zAs, zCol)==0 ){ + assert( pExpr->pLeft==0 && pExpr->pRight==0 ); + pExpr->op = TK_AS; + pExpr->iColumn = j; + pExpr->pLeft = sqliteExprDup(pEList->a[j].pExpr); + sqliteFree(zCol); + assert( zTab==0 && zDb==0 ); + return 0; + } + } + } + + /* + ** If X and Y are NULL (in other words if only the column name Z is + ** supplied) and the value of Z is enclosed in double-quotes, then + ** Z is a string literal if it doesn't match any column names. In that + ** case, we need to return right away and not make any changes to + ** pExpr. + */ + if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){ + sqliteFree(zCol); + return 0; + } + + /* + ** cnt==0 means there was not match. cnt>1 means there were two or + ** more matches. Either way, we have an error. + */ + if( cnt!=1 ){ + char *z = 0; + char *zErr; + zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s"; + if( zDb ){ + sqliteSetString(&z, zDb, ".", zTab, ".", zCol, 0); + }else if( zTab ){ + sqliteSetString(&z, zTab, ".", zCol, 0); + }else{ + z = sqliteStrDup(zCol); + } + sqliteErrorMsg(pParse, zErr, z); + sqliteFree(z); + } + + /* Clean up and return + */ + sqliteFree(zDb); + sqliteFree(zTab); + sqliteFree(zCol); + sqliteExprDelete(pExpr->pLeft); + pExpr->pLeft = 0; + sqliteExprDelete(pExpr->pRight); + pExpr->pRight = 0; + pExpr->op = TK_COLUMN; + sqliteAuthRead(pParse, pExpr, pSrcList); + return cnt!=1; +} + +/* +** This routine walks an expression tree and resolves references to +** table columns. Nodes of the form ID.ID or ID resolve into an +** index to the table in the table list and a column offset. The +** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable +** value is changed to the index of the referenced table in pTabList +** plus the "base" value. The base value will ultimately become the +** VDBE cursor number for a cursor that is pointing into the referenced +** table. The Expr.iColumn value is changed to the index of the column +** of the referenced table. The Expr.iColumn value for the special +** ROWID column is -1. Any INTEGER PRIMARY KEY column is tried as an +** alias for ROWID. +** +** We also check for instances of the IN operator. IN comes in two +** forms: +** +** expr IN (exprlist) +** and +** expr IN (SELECT ...) +** +** The first form is handled by creating a set holding the list +** of allowed values. The second form causes the SELECT to generate +** a temporary table. +** +** This routine also looks for scalar SELECTs that are part of an expression. +** If it finds any, it generates code to write the value of that select +** into a memory cell. +** +** Unknown columns or tables provoke an error. The function returns +** the number of errors seen and leaves an error message on pParse->zErrMsg. +*/ +int sqliteExprResolveIds( + Parse *pParse, /* The parser context */ + SrcList *pSrcList, /* List of tables used to resolve column names */ + ExprList *pEList, /* List of expressions used to resolve "AS" */ + Expr *pExpr /* The expression to be analyzed. */ +){ + int i; + + if( pExpr==0 || pSrcList==0 ) return 0; + for(i=0; inSrc; i++){ + assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursornTab ); + } + switch( pExpr->op ){ + /* Double-quoted strings (ex: "abc") are used as identifiers if + ** possible. Otherwise they remain as strings. Single-quoted + ** strings (ex: 'abc') are always string literals. + */ + case TK_STRING: { + if( pExpr->token.z[0]=='\'' ) break; + /* Fall thru into the TK_ID case if this is a double-quoted string */ + } + /* A lone identifier is the name of a columnd. + */ + case TK_ID: { + if( lookupName(pParse, 0, 0, &pExpr->token, pSrcList, pEList, pExpr) ){ + return 1; + } + break; + } + + /* A table name and column name: ID.ID + ** Or a database, table and column: ID.ID.ID + */ + case TK_DOT: { + Token *pColumn; + Token *pTable; + Token *pDb; + Expr *pRight; + + pRight = pExpr->pRight; + if( pRight->op==TK_ID ){ + pDb = 0; + pTable = &pExpr->pLeft->token; + pColumn = &pRight->token; + }else{ + assert( pRight->op==TK_DOT ); + pDb = &pExpr->pLeft->token; + pTable = &pRight->pLeft->token; + pColumn = &pRight->pRight->token; + } + if( lookupName(pParse, pDb, pTable, pColumn, pSrcList, 0, pExpr) ){ + return 1; + } + break; + } + + case TK_IN: { + Vdbe *v = sqliteGetVdbe(pParse); + if( v==0 ) return 1; + if( sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ + return 1; + } + if( pExpr->pSelect ){ + /* Case 1: expr IN (SELECT ...) + ** + ** Generate code to write the results of the select into a temporary + ** table. The cursor number of the temporary table has already + ** been put in iTable by sqliteExprResolveInSelect(). + */ + pExpr->iTable = pParse->nTab++; + sqliteVdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 1); + sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable, 0,0,0); + }else if( pExpr->pList ){ + /* Case 2: expr IN (exprlist) + ** + ** Create a set to put the exprlist values in. The Set id is stored + ** in iTable. + */ + int i, iSet; + for(i=0; ipList->nExpr; i++){ + Expr *pE2 = pExpr->pList->a[i].pExpr; + if( !sqliteExprIsConstant(pE2) ){ + sqliteErrorMsg(pParse, + "right-hand side of IN operator must be constant"); + return 1; + } + if( sqliteExprCheck(pParse, pE2, 0, 0) ){ + return 1; + } + } + iSet = pExpr->iTable = pParse->nSet++; + for(i=0; ipList->nExpr; i++){ + Expr *pE2 = pExpr->pList->a[i].pExpr; + switch( pE2->op ){ + case TK_FLOAT: + case TK_INTEGER: + case TK_STRING: { + int addr; + assert( pE2->token.z ); + addr = sqliteVdbeOp3(v, OP_SetInsert, iSet, 0, + pE2->token.z, pE2->token.n); + sqliteVdbeDequoteP3(v, addr); + break; + } + default: { + sqliteExprCode(pParse, pE2); + sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0); + break; + } + } + } + } + break; + } + + case TK_SELECT: { + /* This has to be a scalar SELECT. Generate code to put the + ** value of this select in a memory cell and record the number + ** of the memory cell in iColumn. + */ + pExpr->iColumn = pParse->nMem++; + if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn,0,0,0) ){ + return 1; + } + break; + } + + /* For all else, just recursively walk the tree */ + default: { + if( pExpr->pLeft + && sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ + return 1; + } + if( pExpr->pRight + && sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pRight) ){ + return 1; + } + if( pExpr->pList ){ + int i; + ExprList *pList = pExpr->pList; + for(i=0; inExpr; i++){ + Expr *pArg = pList->a[i].pExpr; + if( sqliteExprResolveIds(pParse, pSrcList, pEList, pArg) ){ + return 1; + } + } + } + } + } + return 0; +} + +/* +** pExpr is a node that defines a function of some kind. It might +** be a syntactic function like "count(x)" or it might be a function +** that implements an operator, like "a LIKE b". +** +** This routine makes *pzName point to the name of the function and +** *pnName hold the number of characters in the function name. +*/ +static void getFunctionName(Expr *pExpr, const char **pzName, int *pnName){ + switch( pExpr->op ){ + case TK_FUNCTION: { + *pzName = pExpr->token.z; + *pnName = pExpr->token.n; + break; + } + case TK_LIKE: { + *pzName = "like"; + *pnName = 4; + break; + } + case TK_GLOB: { + *pzName = "glob"; + *pnName = 4; + break; + } + default: { + *pzName = "can't happen"; + *pnName = 12; + break; + } + } +} + +/* +** Error check the functions in an expression. Make sure all +** function names are recognized and all functions have the correct +** number of arguments. Leave an error message in pParse->zErrMsg +** if anything is amiss. Return the number of errors. +** +** if pIsAgg is not null and this expression is an aggregate function +** (like count(*) or max(value)) then write a 1 into *pIsAgg. +*/ +int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ + int nErr = 0; + if( pExpr==0 ) return 0; + switch( pExpr->op ){ + case TK_GLOB: + case TK_LIKE: + case TK_FUNCTION: { + int n = pExpr->pList ? pExpr->pList->nExpr : 0; /* Number of arguments */ + int no_such_func = 0; /* True if no such function exists */ + int wrong_num_args = 0; /* True if wrong number of arguments */ + int is_agg = 0; /* True if is an aggregate function */ + int i; + int nId; /* Number of characters in function name */ + const char *zId; /* The function name. */ + FuncDef *pDef; + + getFunctionName(pExpr, &zId, &nId); + pDef = sqliteFindFunction(pParse->db, zId, nId, n, 0); + if( pDef==0 ){ + pDef = sqliteFindFunction(pParse->db, zId, nId, -1, 0); + if( pDef==0 ){ + no_such_func = 1; + }else{ + wrong_num_args = 1; + } + }else{ + is_agg = pDef->xFunc==0; + } + if( is_agg && !allowAgg ){ + sqliteErrorMsg(pParse, "misuse of aggregate function %.*s()", nId, zId); + nErr++; + is_agg = 0; + }else if( no_such_func ){ + sqliteErrorMsg(pParse, "no such function: %.*s", nId, zId); + nErr++; + }else if( wrong_num_args ){ + sqliteErrorMsg(pParse,"wrong number of arguments to function %.*s()", + nId, zId); + nErr++; + } + if( is_agg ){ + pExpr->op = TK_AGG_FUNCTION; + if( pIsAgg ) *pIsAgg = 1; + } + for(i=0; nErr==0 && ipList->a[i].pExpr, + allowAgg && !is_agg, pIsAgg); + } + if( pDef==0 ){ + /* Already reported an error */ + }else if( pDef->dataType>=0 ){ + if( pDef->dataTypedataType = + sqliteExprType(pExpr->pList->a[pDef->dataType].pExpr); + }else{ + pExpr->dataType = SQLITE_SO_NUM; + } + }else if( pDef->dataType==SQLITE_ARGS ){ + pDef->dataType = SQLITE_SO_TEXT; + for(i=0; ipList->a[i].pExpr)==SQLITE_SO_NUM ){ + pExpr->dataType = SQLITE_SO_NUM; + break; + } + } + }else if( pDef->dataType==SQLITE_NUMERIC ){ + pExpr->dataType = SQLITE_SO_NUM; + }else{ + pExpr->dataType = SQLITE_SO_TEXT; + } + } + default: { + if( pExpr->pLeft ){ + nErr = sqliteExprCheck(pParse, pExpr->pLeft, allowAgg, pIsAgg); + } + if( nErr==0 && pExpr->pRight ){ + nErr = sqliteExprCheck(pParse, pExpr->pRight, allowAgg, pIsAgg); + } + if( nErr==0 && pExpr->pList ){ + int n = pExpr->pList->nExpr; + int i; + for(i=0; nErr==0 && ipList->a[i].pExpr; + nErr = sqliteExprCheck(pParse, pE2, allowAgg, pIsAgg); + } + } + break; + } + } + return nErr; +} + +/* +** Return either SQLITE_SO_NUM or SQLITE_SO_TEXT to indicate whether the +** given expression should sort as numeric values or as text. +** +** The sqliteExprResolveIds() and sqliteExprCheck() routines must have +** both been called on the expression before it is passed to this routine. +*/ +int sqliteExprType(Expr *p){ + if( p==0 ) return SQLITE_SO_NUM; + while( p ) switch( p->op ){ + case TK_PLUS: + case TK_MINUS: + case TK_STAR: + case TK_SLASH: + case TK_AND: + case TK_OR: + case TK_ISNULL: + case TK_NOTNULL: + case TK_NOT: + case TK_UMINUS: + case TK_UPLUS: + case TK_BITAND: + case TK_BITOR: + case TK_BITNOT: + case TK_LSHIFT: + case TK_RSHIFT: + case TK_REM: + case TK_INTEGER: + case TK_FLOAT: + case TK_IN: + case TK_BETWEEN: + case TK_GLOB: + case TK_LIKE: + return SQLITE_SO_NUM; + + case TK_STRING: + case TK_NULL: + case TK_CONCAT: + case TK_VARIABLE: + return SQLITE_SO_TEXT; + + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: + if( sqliteExprType(p->pLeft)==SQLITE_SO_NUM ){ + return SQLITE_SO_NUM; + } + p = p->pRight; + break; + + case TK_AS: + p = p->pLeft; + break; + + case TK_COLUMN: + case TK_FUNCTION: + case TK_AGG_FUNCTION: + return p->dataType; + + case TK_SELECT: + assert( p->pSelect ); + assert( p->pSelect->pEList ); + assert( p->pSelect->pEList->nExpr>0 ); + p = p->pSelect->pEList->a[0].pExpr; + break; + + case TK_CASE: { + if( p->pRight && sqliteExprType(p->pRight)==SQLITE_SO_NUM ){ + return SQLITE_SO_NUM; + } + if( p->pList ){ + int i; + ExprList *pList = p->pList; + for(i=1; inExpr; i+=2){ + if( sqliteExprType(pList->a[i].pExpr)==SQLITE_SO_NUM ){ + return SQLITE_SO_NUM; + } + } + } + return SQLITE_SO_TEXT; + } + + default: + assert( p->op==TK_ABORT ); /* Can't Happen */ + break; + } + return SQLITE_SO_NUM; +} + +/* +** Generate code into the current Vdbe to evaluate the given +** expression and leave the result on the top of stack. +*/ +void sqliteExprCode(Parse *pParse, Expr *pExpr){ + Vdbe *v = pParse->pVdbe; + int op; + if( v==0 || pExpr==0 ) return; + switch( pExpr->op ){ + case TK_PLUS: op = OP_Add; break; + case TK_MINUS: op = OP_Subtract; break; + case TK_STAR: op = OP_Multiply; break; + case TK_SLASH: op = OP_Divide; break; + case TK_AND: op = OP_And; break; + case TK_OR: op = OP_Or; break; + case TK_LT: op = OP_Lt; break; + case TK_LE: op = OP_Le; break; + case TK_GT: op = OP_Gt; break; + case TK_GE: op = OP_Ge; break; + case TK_NE: op = OP_Ne; break; + case TK_EQ: op = OP_Eq; break; + case TK_ISNULL: op = OP_IsNull; break; + case TK_NOTNULL: op = OP_NotNull; break; + case TK_NOT: op = OP_Not; break; + case TK_UMINUS: op = OP_Negative; break; + case TK_BITAND: op = OP_BitAnd; break; + case TK_BITOR: op = OP_BitOr; break; + case TK_BITNOT: op = OP_BitNot; break; + case TK_LSHIFT: op = OP_ShiftLeft; break; + case TK_RSHIFT: op = OP_ShiftRight; break; + case TK_REM: op = OP_Remainder; break; + default: break; + } + switch( pExpr->op ){ + case TK_COLUMN: { + if( pParse->useAgg ){ + sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); + }else if( pExpr->iColumn>=0 ){ + sqliteVdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn); + }else{ + sqliteVdbeAddOp(v, OP_Recno, pExpr->iTable, 0); + } + break; + } + case TK_STRING: + case TK_FLOAT: + case TK_INTEGER: { + if( pExpr->op==TK_INTEGER && sqliteFitsIn32Bits(pExpr->token.z) ){ + sqliteVdbeAddOp(v, OP_Integer, atoi(pExpr->token.z), 0); + }else{ + sqliteVdbeAddOp(v, OP_String, 0, 0); + } + assert( pExpr->token.z ); + sqliteVdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n); + sqliteVdbeDequoteP3(v, -1); + break; + } + case TK_NULL: { + sqliteVdbeAddOp(v, OP_String, 0, 0); + break; + } + case TK_VARIABLE: { + sqliteVdbeAddOp(v, OP_Variable, pExpr->iTable, 0); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + op += 6; /* Convert numeric opcodes to text opcodes */ + } + /* Fall through into the next case */ + } + case TK_AND: + case TK_OR: + case TK_PLUS: + case TK_STAR: + case TK_MINUS: + case TK_REM: + case TK_BITAND: + case TK_BITOR: + case TK_SLASH: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, op, 0, 0); + break; + } + case TK_LSHIFT: + case TK_RSHIFT: { + sqliteExprCode(pParse, pExpr->pRight); + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 0, 0); + break; + } + case TK_CONCAT: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, OP_Concat, 2, 0); + break; + } + case TK_UMINUS: { + assert( pExpr->pLeft ); + if( pExpr->pLeft->op==TK_FLOAT || pExpr->pLeft->op==TK_INTEGER ){ + Token *p = &pExpr->pLeft->token; + char *z = sqliteMalloc( p->n + 2 ); + sprintf(z, "-%.*s", p->n, p->z); + if( pExpr->pLeft->op==TK_INTEGER && sqliteFitsIn32Bits(z) ){ + sqliteVdbeAddOp(v, OP_Integer, atoi(z), 0); + }else{ + sqliteVdbeAddOp(v, OP_String, 0, 0); + } + sqliteVdbeChangeP3(v, -1, z, p->n+1); + sqliteFree(z); + break; + } + /* Fall through into TK_NOT */ + } + case TK_BITNOT: + case TK_NOT: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 0, 0); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + int dest; + sqliteVdbeAddOp(v, OP_Integer, 1, 0); + sqliteExprCode(pParse, pExpr->pLeft); + dest = sqliteVdbeCurrentAddr(v) + 2; + sqliteVdbeAddOp(v, op, 1, dest); + sqliteVdbeAddOp(v, OP_AddImm, -1, 0); + break; + } + case TK_AGG_FUNCTION: { + sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg); + break; + } + case TK_GLOB: + case TK_LIKE: + case TK_FUNCTION: { + ExprList *pList = pExpr->pList; + int nExpr = pList ? pList->nExpr : 0; + FuncDef *pDef; + int nId; + const char *zId; + getFunctionName(pExpr, &zId, &nId); + pDef = sqliteFindFunction(pParse->db, zId, nId, nExpr, 0); + assert( pDef!=0 ); + nExpr = sqliteExprCodeExprList(pParse, pList, pDef->includeTypes); + sqliteVdbeOp3(v, OP_Function, nExpr, 0, (char*)pDef, P3_POINTER); + break; + } + case TK_SELECT: { + sqliteVdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0); + break; + } + case TK_IN: { + int addr; + sqliteVdbeAddOp(v, OP_Integer, 1, 0); + sqliteExprCode(pParse, pExpr->pLeft); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeAddOp(v, OP_NotNull, -1, addr+4); + sqliteVdbeAddOp(v, OP_Pop, 2, 0); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, addr+6); + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, addr+6); + }else{ + sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, addr+6); + } + sqliteVdbeAddOp(v, OP_AddImm, -1, 0); + break; + } + case TK_BETWEEN: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); + sqliteVdbeAddOp(v, OP_Ge, 0, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0); + sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); + sqliteVdbeAddOp(v, OP_Le, 0, 0); + sqliteVdbeAddOp(v, OP_And, 0, 0); + break; + } + case TK_UPLUS: + case TK_AS: { + sqliteExprCode(pParse, pExpr->pLeft); + break; + } + case TK_CASE: { + int expr_end_label; + int jumpInst; + int addr; + int nExpr; + int i; + + assert(pExpr->pList); + assert((pExpr->pList->nExpr % 2) == 0); + assert(pExpr->pList->nExpr > 0); + nExpr = pExpr->pList->nExpr; + expr_end_label = sqliteVdbeMakeLabel(v); + if( pExpr->pLeft ){ + sqliteExprCode(pParse, pExpr->pLeft); + } + for(i=0; ipList->a[i].pExpr); + if( pExpr->pLeft ){ + sqliteVdbeAddOp(v, OP_Dup, 1, 1); + jumpInst = sqliteVdbeAddOp(v, OP_Ne, 1, 0); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + }else{ + jumpInst = sqliteVdbeAddOp(v, OP_IfNot, 1, 0); + } + sqliteExprCode(pParse, pExpr->pList->a[i+1].pExpr); + sqliteVdbeAddOp(v, OP_Goto, 0, expr_end_label); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeChangeP2(v, jumpInst, addr); + } + if( pExpr->pLeft ){ + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + } + if( pExpr->pRight ){ + sqliteExprCode(pParse, pExpr->pRight); + }else{ + sqliteVdbeAddOp(v, OP_String, 0, 0); + } + sqliteVdbeResolveLabel(v, expr_end_label); + break; + } + case TK_RAISE: { + if( !pParse->trigStack ){ + sqliteErrorMsg(pParse, + "RAISE() may only be used within a trigger-program"); + pParse->nErr++; + return; + } + if( pExpr->iColumn == OE_Rollback || + pExpr->iColumn == OE_Abort || + pExpr->iColumn == OE_Fail ){ + sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn, + pExpr->token.z, pExpr->token.n); + sqliteVdbeDequoteP3(v, -1); + } else { + assert( pExpr->iColumn == OE_Ignore ); + sqliteVdbeOp3(v, OP_Goto, 0, pParse->trigStack->ignoreJump, + "(IGNORE jump)", 0); + } + } + break; + } +} + +/* +** Generate code that pushes the value of every element of the given +** expression list onto the stack. If the includeTypes flag is true, +** then also push a string that is the datatype of each element onto +** the stack after the value. +** +** Return the number of elements pushed onto the stack. +*/ +int sqliteExprCodeExprList( + Parse *pParse, /* Parsing context */ + ExprList *pList, /* The expression list to be coded */ + int includeTypes /* TRUE to put datatypes on the stack too */ +){ + struct ExprList_item *pItem; + int i, n; + Vdbe *v; + if( pList==0 ) return 0; + v = sqliteGetVdbe(pParse); + n = pList->nExpr; + for(pItem=pList->a, i=0; ipExpr); + if( includeTypes ){ + sqliteVdbeOp3(v, OP_String, 0, 0, + sqliteExprType(pItem->pExpr)==SQLITE_SO_NUM ? "numeric" : "text", + P3_STATIC); + } + } + return includeTypes ? n*2 : n; +} + +/* +** Generate code for a boolean expression such that a jump is made +** to the label "dest" if the expression is true but execution +** continues straight thru if the expression is false. +** +** If the expression evaluates to NULL (neither true nor false), then +** take the jump if the jumpIfNull flag is true. +*/ +void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ + Vdbe *v = pParse->pVdbe; + int op = 0; + if( v==0 || pExpr==0 ) return; + switch( pExpr->op ){ + case TK_LT: op = OP_Lt; break; + case TK_LE: op = OP_Le; break; + case TK_GT: op = OP_Gt; break; + case TK_GE: op = OP_Ge; break; + case TK_NE: op = OP_Ne; break; + case TK_EQ: op = OP_Eq; break; + case TK_ISNULL: op = OP_IsNull; break; + case TK_NOTNULL: op = OP_NotNull; break; + default: break; + } + switch( pExpr->op ){ + case TK_AND: { + int d2 = sqliteVdbeMakeLabel(v); + sqliteExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull); + sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + sqliteVdbeResolveLabel(v, d2); + break; + } + case TK_OR: { + sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + sqliteExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); + break; + } + case TK_NOT: { + sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + op += 6; /* Convert numeric opcodes to text opcodes */ + } + sqliteVdbeAddOp(v, op, jumpIfNull, dest); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 1, dest); + break; + } + case TK_IN: { + int addr; + sqliteExprCode(pParse, pExpr->pLeft); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4); + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_Found, pExpr->iTable, dest); + }else{ + sqliteVdbeAddOp(v, OP_SetFound, pExpr->iTable, dest); + } + break; + } + case TK_BETWEEN: { + int addr; + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); + addr = sqliteVdbeAddOp(v, OP_Lt, !jumpIfNull, 0); + sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); + sqliteVdbeAddOp(v, OP_Le, jumpIfNull, dest); + sqliteVdbeAddOp(v, OP_Integer, 0, 0); + sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + break; + } + default: { + sqliteExprCode(pParse, pExpr); + sqliteVdbeAddOp(v, OP_If, jumpIfNull, dest); + break; + } + } +} + +/* +** Generate code for a boolean expression such that a jump is made +** to the label "dest" if the expression is false but execution +** continues straight thru if the expression is true. +** +** If the expression evaluates to NULL (neither true nor false) then +** jump if jumpIfNull is true or fall through if jumpIfNull is false. +*/ +void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ + Vdbe *v = pParse->pVdbe; + int op = 0; + if( v==0 || pExpr==0 ) return; + switch( pExpr->op ){ + case TK_LT: op = OP_Ge; break; + case TK_LE: op = OP_Gt; break; + case TK_GT: op = OP_Le; break; + case TK_GE: op = OP_Lt; break; + case TK_NE: op = OP_Eq; break; + case TK_EQ: op = OP_Ne; break; + case TK_ISNULL: op = OP_NotNull; break; + case TK_NOTNULL: op = OP_IsNull; break; + default: break; + } + switch( pExpr->op ){ + case TK_AND: { + sqliteExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull); + sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + break; + } + case TK_OR: { + int d2 = sqliteVdbeMakeLabel(v); + sqliteExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull); + sqliteExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); + sqliteVdbeResolveLabel(v, d2); + break; + } + case TK_NOT: { + sqliteExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); + break; + } + case TK_LT: + case TK_LE: + case TK_GT: + case TK_GE: + case TK_NE: + case TK_EQ: { + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + /* Convert numeric comparison opcodes into text comparison opcodes. + ** This step depends on the fact that the text comparision opcodes are + ** always 6 greater than their corresponding numeric comparison + ** opcodes. + */ + assert( OP_Eq+6 == OP_StrEq ); + op += 6; + } + sqliteExprCode(pParse, pExpr->pLeft); + sqliteExprCode(pParse, pExpr->pRight); + sqliteVdbeAddOp(v, op, jumpIfNull, dest); + break; + } + case TK_ISNULL: + case TK_NOTNULL: { + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, op, 1, dest); + break; + } + case TK_IN: { + int addr; + sqliteExprCode(pParse, pExpr->pLeft); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeAddOp(v, OP_NotNull, -1, addr+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, jumpIfNull ? dest : addr+4); + if( pExpr->pSelect ){ + sqliteVdbeAddOp(v, OP_NotFound, pExpr->iTable, dest); + }else{ + sqliteVdbeAddOp(v, OP_SetNotFound, pExpr->iTable, dest); + } + break; + } + case TK_BETWEEN: { + int addr; + sqliteExprCode(pParse, pExpr->pLeft); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + sqliteExprCode(pParse, pExpr->pList->a[0].pExpr); + addr = sqliteVdbeCurrentAddr(v); + sqliteVdbeAddOp(v, OP_Ge, !jumpIfNull, addr+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, dest); + sqliteExprCode(pParse, pExpr->pList->a[1].pExpr); + sqliteVdbeAddOp(v, OP_Gt, jumpIfNull, dest); + break; + } + default: { + sqliteExprCode(pParse, pExpr); + sqliteVdbeAddOp(v, OP_IfNot, jumpIfNull, dest); + break; + } + } +} + +/* +** Do a deep comparison of two expression trees. Return TRUE (non-zero) +** if they are identical and return FALSE if they differ in any way. +*/ +int sqliteExprCompare(Expr *pA, Expr *pB){ + int i; + if( pA==0 ){ + return pB==0; + }else if( pB==0 ){ + return 0; + } + if( pA->op!=pB->op ) return 0; + if( !sqliteExprCompare(pA->pLeft, pB->pLeft) ) return 0; + if( !sqliteExprCompare(pA->pRight, pB->pRight) ) return 0; + if( pA->pList ){ + if( pB->pList==0 ) return 0; + if( pA->pList->nExpr!=pB->pList->nExpr ) return 0; + for(i=0; ipList->nExpr; i++){ + if( !sqliteExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){ + return 0; + } + } + }else if( pB->pList ){ + return 0; + } + if( pA->pSelect || pB->pSelect ) return 0; + if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0; + if( pA->token.z ){ + if( pB->token.z==0 ) return 0; + if( pB->token.n!=pA->token.n ) return 0; + if( sqliteStrNICmp(pA->token.z, pB->token.z, pB->token.n)!=0 ) return 0; + } + return 1; +} + +/* +** Add a new element to the pParse->aAgg[] array and return its index. +*/ +static int appendAggInfo(Parse *pParse){ + if( (pParse->nAgg & 0x7)==0 ){ + int amt = pParse->nAgg + 8; + AggExpr *aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0])); + if( aAgg==0 ){ + return -1; + } + pParse->aAgg = aAgg; + } + memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0])); + return pParse->nAgg++; +} + +/* +** Analyze the given expression looking for aggregate functions and +** for variables that need to be added to the pParse->aAgg[] array. +** Make additional entries to the pParse->aAgg[] array as necessary. +** +** This routine should only be called after the expression has been +** analyzed by sqliteExprResolveIds() and sqliteExprCheck(). +** +** If errors are seen, leave an error message in zErrMsg and return +** the number of errors. +*/ +int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){ + int i; + AggExpr *aAgg; + int nErr = 0; + + if( pExpr==0 ) return 0; + switch( pExpr->op ){ + case TK_COLUMN: { + aAgg = pParse->aAgg; + for(i=0; inAgg; i++){ + if( aAgg[i].isAgg ) continue; + if( aAgg[i].pExpr->iTable==pExpr->iTable + && aAgg[i].pExpr->iColumn==pExpr->iColumn ){ + break; + } + } + if( i>=pParse->nAgg ){ + i = appendAggInfo(pParse); + if( i<0 ) return 1; + pParse->aAgg[i].isAgg = 0; + pParse->aAgg[i].pExpr = pExpr; + } + pExpr->iAgg = i; + break; + } + case TK_AGG_FUNCTION: { + aAgg = pParse->aAgg; + for(i=0; inAgg; i++){ + if( !aAgg[i].isAgg ) continue; + if( sqliteExprCompare(aAgg[i].pExpr, pExpr) ){ + break; + } + } + if( i>=pParse->nAgg ){ + i = appendAggInfo(pParse); + if( i<0 ) return 1; + pParse->aAgg[i].isAgg = 1; + pParse->aAgg[i].pExpr = pExpr; + pParse->aAgg[i].pFunc = sqliteFindFunction(pParse->db, + pExpr->token.z, pExpr->token.n, + pExpr->pList ? pExpr->pList->nExpr : 0, 0); + } + pExpr->iAgg = i; + break; + } + default: { + if( pExpr->pLeft ){ + nErr = sqliteExprAnalyzeAggregates(pParse, pExpr->pLeft); + } + if( nErr==0 && pExpr->pRight ){ + nErr = sqliteExprAnalyzeAggregates(pParse, pExpr->pRight); + } + if( nErr==0 && pExpr->pList ){ + int n = pExpr->pList->nExpr; + int i; + for(i=0; nErr==0 && ipList->a[i].pExpr); + } + } + break; + } + } + return nErr; +} + +/* +** Locate a user function given a name and a number of arguments. +** Return a pointer to the FuncDef structure that defines that +** function, or return NULL if the function does not exist. +** +** If the createFlag argument is true, then a new (blank) FuncDef +** structure is created and liked into the "db" structure if a +** no matching function previously existed. When createFlag is true +** and the nArg parameter is -1, then only a function that accepts +** any number of arguments will be returned. +** +** If createFlag is false and nArg is -1, then the first valid +** function found is returned. A function is valid if either xFunc +** or xStep is non-zero. +*/ +FuncDef *sqliteFindFunction( + sqlite *db, /* An open database */ + const char *zName, /* Name of the function. Not null-terminated */ + int nName, /* Number of characters in the name */ + int nArg, /* Number of arguments. -1 means any number */ + int createFlag /* Create new entry if true and does not otherwise exist */ +){ + FuncDef *pFirst, *p, *pMaybe; + pFirst = p = (FuncDef*)sqliteHashFind(&db->aFunc, zName, nName); + if( p && !createFlag && nArg<0 ){ + while( p && p->xFunc==0 && p->xStep==0 ){ p = p->pNext; } + return p; + } + pMaybe = 0; + while( p && p->nArg!=nArg ){ + if( p->nArg<0 && !createFlag && (p->xFunc || p->xStep) ) pMaybe = p; + p = p->pNext; + } + if( p && !createFlag && p->xFunc==0 && p->xStep==0 ){ + return 0; + } + if( p==0 && pMaybe ){ + assert( createFlag==0 ); + return pMaybe; + } + if( p==0 && createFlag && (p = sqliteMalloc(sizeof(*p)))!=0 ){ + p->nArg = nArg; + p->pNext = pFirst; + p->dataType = pFirst ? pFirst->dataType : SQLITE_NUMERIC; + sqliteHashInsert(&db->aFunc, zName, nName, (void*)p); + } + return p; +} diff --git a/src/libs/sqlite2/func.c b/src/libs/sqlite2/func.c new file mode 100644 index 00000000..c86a75a3 --- /dev/null +++ b/src/libs/sqlite2/func.c @@ -0,0 +1,658 @@ +/* +** 2002 February 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement various SQL +** functions of SQLite. +** +** There is only one exported symbol in this file - the function +** sqliteRegisterBuildinFunctions() found at the bottom of the file. +** All other code has file scope. +** +** $Id: func.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include +#include +#include +#include +#include "sqliteInt.h" +#include "os.h" + +/* +** Implementation of the non-aggregate min() and max() functions +*/ +static void minmaxFunc(sqlite_func *context, int argc, const char **argv){ + const char *zBest; + int i; + int (*xCompare)(const char*, const char*); + int mask; /* 0 for min() or 0xffffffff for max() */ + + if( argc==0 ) return; + mask = (int)sqlite_user_data(context); + zBest = argv[0]; + if( zBest==0 ) return; + if( argv[1][0]=='n' ){ + xCompare = sqliteCompare; + }else{ + xCompare = strcmp; + } + for(i=2; i0 ){ + p1--; + } + if( p1+p2>len ){ + p2 = len-p1; + } +#ifdef SQLITE_UTF8 + for(i=0; i30 ) n = 30; + if( n<0 ) n = 0; + r = sqliteAtoF(argv[0], 0); + sprintf(zBuf,"%.*f",n,r); + sqlite_set_result_string(context, zBuf, -1); +} + +/* +** Implementation of the upper() and lower() SQL functions. +*/ +static void upperFunc(sqlite_func *context, int argc, const char **argv){ + unsigned char *z; + int i; + if( argc<1 || argv[0]==0 ) return; + z = (unsigned char*)sqlite_set_result_string(context, argv[0], -1); + if( z==0 ) return; + for(i=0; z[i]; i++){ + if( islower(z[i]) ) z[i] = toupper(z[i]); + } +} +static void lowerFunc(sqlite_func *context, int argc, const char **argv){ + unsigned char *z; + int i; + if( argc<1 || argv[0]==0 ) return; + z = (unsigned char*)sqlite_set_result_string(context, argv[0], -1); + if( z==0 ) return; + for(i=0; z[i]; i++){ + if( isupper(z[i]) ) z[i] = tolower(z[i]); + } +} + +/* +** Implementation of the IFNULL(), NVL(), and COALESCE() functions. +** All three do the same thing. They return the first non-NULL +** argument. +*/ +static void ifnullFunc(sqlite_func *context, int argc, const char **argv){ + int i; + for(i=0; i0 ){ + zResult[j++] = code + '0'; + } + } + while( j<4 ){ + zResult[j++] = '0'; + } + zResult[j] = 0; + sqlite_set_result_string(context, zResult, 4); + }else{ + sqlite_set_result_string(context, "?000", 4); + } +} +#endif + +#ifdef SQLITE_TEST +/* +** This function generates a string of random characters. Used for +** generating test data. +*/ +static void randStr(sqlite_func *context, int argc, const char **argv){ + static const unsigned char zSrc[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + ".-!,:*^+=_|?/<> "; + int iMin, iMax, n, r, i; + unsigned char zBuf[1000]; + if( argc>=1 ){ + iMin = atoi(argv[0]); + if( iMin<0 ) iMin = 0; + if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1; + }else{ + iMin = 1; + } + if( argc>=2 ){ + iMax = atoi(argv[1]); + if( iMax=sizeof(zBuf) ) iMax = sizeof(zBuf)-1; + }else{ + iMax = 50; + } + n = iMin; + if( iMax>iMin ){ + sqliteRandomness(sizeof(r), &r); + r &= 0x7fffffff; + n += r%(iMax + 1 - iMin); + } + assert( nsum += sqliteAtoF(argv[0], 0); + p->cnt++; + } +} +static void sumFinalize(sqlite_func *context){ + SumCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + sqlite_set_result_double(context, p ? p->sum : 0.0); +} +static void avgFinalize(sqlite_func *context){ + SumCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && p->cnt>0 ){ + sqlite_set_result_double(context, p->sum/(double)p->cnt); + } +} + +/* +** An instance of the following structure holds the context of a +** variance or standard deviation computation. +*/ +typedef struct StdDevCtx StdDevCtx; +struct StdDevCtx { + double sum; /* Sum of terms */ + double sum2; /* Sum of the squares of terms */ + int cnt; /* Number of terms counted */ +}; + +#if 0 /* Omit because math library is required */ +/* +** Routines used to compute the standard deviation as an aggregate. +*/ +static void stdDevStep(sqlite_func *context, int argc, const char **argv){ + StdDevCtx *p; + double x; + if( argc<1 ) return; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && argv[0] ){ + x = sqliteAtoF(argv[0], 0); + p->sum += x; + p->sum2 += x*x; + p->cnt++; + } +} +static void stdDevFinalize(sqlite_func *context){ + double rN = sqlite_aggregate_count(context); + StdDevCtx *p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && p->cnt>1 ){ + double rCnt = cnt; + sqlite_set_result_double(context, + sqrt((p->sum2 - p->sum*p->sum/rCnt)/(rCnt-1.0))); + } +} +#endif + +/* +** The following structure keeps track of state information for the +** count() aggregate function. +*/ +typedef struct CountCtx CountCtx; +struct CountCtx { + int n; +}; + +/* +** Routines to implement the count() aggregate function. +*/ +static void countStep(sqlite_func *context, int argc, const char **argv){ + CountCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( (argc==0 || argv[0]) && p ){ + p->n++; + } +} +static void countFinalize(sqlite_func *context){ + CountCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + sqlite_set_result_int(context, p ? p->n : 0); +} + +/* +** This function tracks state information for the min() and max() +** aggregate functions. +*/ +typedef struct MinMaxCtx MinMaxCtx; +struct MinMaxCtx { + char *z; /* The best so far */ + char zBuf[28]; /* Space that can be used for storage */ +}; + +/* +** Routines to implement min() and max() aggregate functions. +*/ +static void minmaxStep(sqlite_func *context, int argc, const char **argv){ + MinMaxCtx *p; + int (*xCompare)(const char*, const char*); + int mask; /* 0 for min() or 0xffffffff for max() */ + + assert( argc==2 ); + if( argv[0]==0 ) return; /* Ignore NULL values */ + if( argv[1][0]=='n' ){ + xCompare = sqliteCompare; + }else{ + xCompare = strcmp; + } + mask = (int)sqlite_user_data(context); + assert( mask==0 || mask==-1 ); + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p==0 || argc<1 ) return; + if( p->z==0 || (xCompare(argv[0],p->z)^mask)<0 ){ + int len; + if( p->zBuf[0] ){ + sqliteFree(p->z); + } + len = strlen(argv[0]); + if( len < sizeof(p->zBuf)-1 ){ + p->z = &p->zBuf[1]; + p->zBuf[0] = 0; + }else{ + p->z = sqliteMalloc( len+1 ); + p->zBuf[0] = 1; + if( p->z==0 ) return; + } + strcpy(p->z, argv[0]); + } +} +static void minMaxFinalize(sqlite_func *context){ + MinMaxCtx *p; + p = sqlite_aggregate_context(context, sizeof(*p)); + if( p && p->z && p->zBuf[0]<2 ){ + sqlite_set_result_string(context, p->z, strlen(p->z)); + } + if( p && p->zBuf[0] ){ + sqliteFree(p->z); + } +} + +/* +** This function registered all of the above C functions as SQL +** functions. This should be the only routine in this file with +** external linkage. +*/ +void sqliteRegisterBuiltinFunctions(sqlite *db){ + static struct { + char *zName; + signed char nArg; + signed char dataType; + u8 argType; /* 0: none. 1: db 2: (-1) */ + void (*xFunc)(sqlite_func*,int,const char**); + } aFuncs[] = { + { "min", -1, SQLITE_ARGS, 0, minmaxFunc }, + { "min", 0, 0, 0, 0 }, + { "max", -1, SQLITE_ARGS, 2, minmaxFunc }, + { "max", 0, 0, 2, 0 }, + { "typeof", 1, SQLITE_TEXT, 0, typeofFunc }, + { "length", 1, SQLITE_NUMERIC, 0, lengthFunc }, + { "substr", 3, SQLITE_TEXT, 0, substrFunc }, + { "abs", 1, SQLITE_NUMERIC, 0, absFunc }, + { "round", 1, SQLITE_NUMERIC, 0, roundFunc }, + { "round", 2, SQLITE_NUMERIC, 0, roundFunc }, + { "upper", 1, SQLITE_TEXT, 0, upperFunc }, + { "lower", 1, SQLITE_TEXT, 0, lowerFunc }, + { "coalesce", -1, SQLITE_ARGS, 0, ifnullFunc }, + { "coalesce", 0, 0, 0, 0 }, + { "coalesce", 1, 0, 0, 0 }, + { "ifnull", 2, SQLITE_ARGS, 0, ifnullFunc }, + { "random", -1, SQLITE_NUMERIC, 0, randomFunc }, + { "like", 2, SQLITE_NUMERIC, 0, likeFunc }, + { "glob", 2, SQLITE_NUMERIC, 0, globFunc }, + { "nullif", 2, SQLITE_ARGS, 0, nullifFunc }, + { "sqlite_version",0,SQLITE_TEXT, 0, versionFunc}, + { "quote", 1, SQLITE_ARGS, 0, quoteFunc }, + { "last_insert_rowid", 0, SQLITE_NUMERIC, 1, last_insert_rowid }, + { "change_count", 0, SQLITE_NUMERIC, 1, change_count }, + { "last_statement_change_count", + 0, SQLITE_NUMERIC, 1, last_statement_change_count }, +#ifdef SQLITE_SOUNDEX + { "soundex", 1, SQLITE_TEXT, 0, soundexFunc}, +#endif +#ifdef SQLITE_TEST + { "randstr", 2, SQLITE_TEXT, 0, randStr }, +#endif + }; + static struct { + char *zName; + signed char nArg; + signed char dataType; + u8 argType; + void (*xStep)(sqlite_func*,int,const char**); + void (*xFinalize)(sqlite_func*); + } aAggs[] = { + { "min", 1, 0, 0, minmaxStep, minMaxFinalize }, + { "max", 1, 0, 2, minmaxStep, minMaxFinalize }, + { "sum", 1, SQLITE_NUMERIC, 0, sumStep, sumFinalize }, + { "avg", 1, SQLITE_NUMERIC, 0, sumStep, avgFinalize }, + { "count", 0, SQLITE_NUMERIC, 0, countStep, countFinalize }, + { "count", 1, SQLITE_NUMERIC, 0, countStep, countFinalize }, +#if 0 + { "stddev", 1, SQLITE_NUMERIC, 0, stdDevStep, stdDevFinalize }, +#endif + }; + static const char *azTypeFuncs[] = { "min", "max", "typeof" }; + int i; + + for(i=0; iaFunc, azTypeFuncs[i], n); + while( p ){ + p->includeTypes = 1; + p = p->pNext; + } + } + sqliteRegisterDateTimeFunctions(db); +} diff --git a/src/libs/sqlite2/hash.c b/src/libs/sqlite2/hash.c new file mode 100644 index 00000000..e0137cb3 --- /dev/null +++ b/src/libs/sqlite2/hash.c @@ -0,0 +1,356 @@ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the implementation of generic hash-tables +** used in SQLite. +** +** $Id: hash.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" +#include + +/* Turn bulk memory into a hash table object by initializing the +** fields of the Hash structure. +** +** "new" is a pointer to the hash table that is to be initialized. +** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER, +** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass +** determines what kind of key the hash table will use. "copyKey" is +** true if the hash table should make its own private copy of keys and +** false if it should just use the supplied pointer. CopyKey only makes +** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored +** for other key classes. +*/ +void sqliteHashInit(Hash *new, int keyClass, int copyKey){ + assert( new!=0 ); + assert( keyClass>=SQLITE_HASH_INT && keyClass<=SQLITE_HASH_BINARY ); + new->keyClass = keyClass; + new->copyKey = copyKey && + (keyClass==SQLITE_HASH_STRING || keyClass==SQLITE_HASH_BINARY); + new->first = 0; + new->count = 0; + new->htsize = 0; + new->ht = 0; +} + +/* Remove all entries from a hash table. Reclaim all memory. +** Call this routine to delete a hash table or to reset a hash table +** to the empty state. +*/ +void sqliteHashClear(Hash *pH){ + HashElem *elem; /* For looping over all elements of the table */ + + assert( pH!=0 ); + elem = pH->first; + pH->first = 0; + if( pH->ht ) sqliteFree(pH->ht); + pH->ht = 0; + pH->htsize = 0; + while( elem ){ + HashElem *next_elem = elem->next; + if( pH->copyKey && elem->pKey ){ + sqliteFree(elem->pKey); + } + sqliteFree(elem); + elem = next_elem; + } + pH->count = 0; +} + +/* +** Hash and comparison functions when the mode is SQLITE_HASH_INT +*/ +static int intHash(const void *pKey, int nKey){ + return nKey ^ (nKey<<8) ^ (nKey>>8); +} +static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + return n2 - n1; +} + +#if 0 /* NOT USED */ +/* +** Hash and comparison functions when the mode is SQLITE_HASH_POINTER +*/ +static int ptrHash(const void *pKey, int nKey){ + uptr x = Addr(pKey); + return x ^ (x<<8) ^ (x>>8); +} +static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( pKey1==pKey2 ) return 0; + if( pKey1 0 ){ + h = (h<<3) ^ h ^ *(z++); + } + return h & 0x7fffffff; +} +static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( n1!=n2 ) return n2-n1; + return memcmp(pKey1,pKey2,n1); +} + +/* +** Return a pointer to the appropriate hash function given the key class. +** +** The C syntax in this function definition may be unfamilar to some +** programmers, so we provide the following additional explanation: +** +** The name of the function is "hashFunction". The function takes a +** single parameter "keyClass". The return value of hashFunction() +** is a pointer to another function. Specifically, the return value +** of hashFunction() is a pointer to a function that takes two parameters +** with types "const void*" and "int" and returns an "int". +*/ +static int (*hashFunction(int keyClass))(const void*,int){ + switch( keyClass ){ + case SQLITE_HASH_INT: return &intHash; + /* case SQLITE_HASH_POINTER: return &ptrHash; // NOT USED */ + case SQLITE_HASH_STRING: return &strHash; + case SQLITE_HASH_BINARY: return &binHash;; + default: break; + } + return 0; +} + +/* +** Return a pointer to the appropriate hash function given the key class. +** +** For help in interpreted the obscure C code in the function definition, +** see the header comment on the previous function. +*/ +static int (*compareFunction(int keyClass))(const void*,int,const void*,int){ + switch( keyClass ){ + case SQLITE_HASH_INT: return &intCompare; + /* case SQLITE_HASH_POINTER: return &ptrCompare; // NOT USED */ + case SQLITE_HASH_STRING: return &strCompare; + case SQLITE_HASH_BINARY: return &binCompare; + default: break; + } + return 0; +} + + +/* Resize the hash table so that it cantains "new_size" buckets. +** "new_size" must be a power of 2. The hash table might fail +** to resize if sqliteMalloc() fails. +*/ +static void rehash(Hash *pH, int new_size){ + struct _ht *new_ht; /* The new hash table */ + HashElem *elem, *next_elem; /* For looping over existing elements */ + HashElem *x; /* Element being copied to new hash table */ + int (*xHash)(const void*,int); /* The hash function */ + + assert( (new_size & (new_size-1))==0 ); + new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) ); + if( new_ht==0 ) return; + if( pH->ht ) sqliteFree(pH->ht); + pH->ht = new_ht; + pH->htsize = new_size; + xHash = hashFunction(pH->keyClass); + for(elem=pH->first, pH->first=0; elem; elem = next_elem){ + int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); + next_elem = elem->next; + x = new_ht[h].chain; + if( x ){ + elem->next = x; + elem->prev = x->prev; + if( x->prev ) x->prev->next = elem; + else pH->first = elem; + x->prev = elem; + }else{ + elem->next = pH->first; + if( pH->first ) pH->first->prev = elem; + elem->prev = 0; + pH->first = elem; + } + new_ht[h].chain = elem; + new_ht[h].count++; + } +} + +/* This function (for internal use only) locates an element in an +** hash table that matches the given key. The hash for this key has +** already been computed and is passed as the 4th parameter. +*/ +static HashElem *findElementGivenHash( + const Hash *pH, /* The pH to be searched */ + const void *pKey, /* The key we are searching for */ + int nKey, + int h /* The hash for this key. */ +){ + HashElem *elem; /* Used to loop thru the element list */ + int count; /* Number of elements left to test */ + int (*xCompare)(const void*,int,const void*,int); /* comparison function */ + + if( pH->ht ){ + elem = pH->ht[h].chain; + count = pH->ht[h].count; + xCompare = compareFunction(pH->keyClass); + while( count-- && elem ){ + if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ + return elem; + } + elem = elem->next; + } + } + return 0; +} + +/* Remove a single entry from the hash table given a pointer to that +** element and a hash on the element's key. +*/ +static void removeElementGivenHash( + Hash *pH, /* The pH containing "elem" */ + HashElem* elem, /* The element to be removed from the pH */ + int h /* Hash value for the element */ +){ + if( elem->prev ){ + elem->prev->next = elem->next; + }else{ + pH->first = elem->next; + } + if( elem->next ){ + elem->next->prev = elem->prev; + } + if( pH->ht[h].chain==elem ){ + pH->ht[h].chain = elem->next; + } + pH->ht[h].count--; + if( pH->ht[h].count<=0 ){ + pH->ht[h].chain = 0; + } + if( pH->copyKey && elem->pKey ){ + sqliteFree(elem->pKey); + } + sqliteFree( elem ); + pH->count--; +} + +/* Attempt to locate an element of the hash table pH with a key +** that matches pKey,nKey. Return the data for this element if it is +** found, or NULL if there is no match. +*/ +void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){ + int h; /* A hash on key */ + HashElem *elem; /* The element that matches key */ + int (*xHash)(const void*,int); /* The hash function */ + + if( pH==0 || pH->ht==0 ) return 0; + xHash = hashFunction(pH->keyClass); + assert( xHash!=0 ); + h = (*xHash)(pKey,nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1)); + return elem ? elem->data : 0; +} + +/* Insert an element into the hash table pH. The key is pKey,nKey +** and the data is "data". +** +** If no element exists with a matching key, then a new +** element is created. A copy of the key is made if the copyKey +** flag is set. NULL is returned. +** +** If another element already exists with the same key, then the +** new data replaces the old data and the old data is returned. +** The key is not copied in this instance. If a malloc fails, then +** the new data is returned and the hash table is unchanged. +** +** If the "data" parameter to this function is NULL, then the +** element corresponding to "key" is removed from the hash table. +*/ +void *sqliteHashInsert(Hash *pH, const void *pKey, int nKey, void *data){ + int hraw; /* Raw hash value of the key */ + int h; /* the hash of the key modulo hash table size */ + HashElem *elem; /* Used to loop thru the element list */ + HashElem *new_elem; /* New element added to the pH */ + int (*xHash)(const void*,int); /* The hash function */ + + assert( pH!=0 ); + xHash = hashFunction(pH->keyClass); + assert( xHash!=0 ); + hraw = (*xHash)(pKey, nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + elem = findElementGivenHash(pH,pKey,nKey,h); + if( elem ){ + void *old_data = elem->data; + if( data==0 ){ + removeElementGivenHash(pH,elem,h); + }else{ + elem->data = data; + } + return old_data; + } + if( data==0 ) return 0; + new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) ); + if( new_elem==0 ) return data; + if( pH->copyKey && pKey!=0 ){ + new_elem->pKey = sqliteMallocRaw( nKey ); + if( new_elem->pKey==0 ){ + sqliteFree(new_elem); + return data; + } + memcpy((void*)new_elem->pKey, pKey, nKey); + }else{ + new_elem->pKey = (void*)pKey; + } + new_elem->nKey = nKey; + pH->count++; + if( pH->htsize==0 ) rehash(pH,8); + if( pH->htsize==0 ){ + pH->count = 0; + sqliteFree(new_elem); + return data; + } + if( pH->count > pH->htsize ){ + rehash(pH,pH->htsize*2); + } + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + elem = pH->ht[h].chain; + if( elem ){ + new_elem->next = elem; + new_elem->prev = elem->prev; + if( elem->prev ){ elem->prev->next = new_elem; } + else { pH->first = new_elem; } + elem->prev = new_elem; + }else{ + new_elem->next = pH->first; + new_elem->prev = 0; + if( pH->first ){ pH->first->prev = new_elem; } + pH->first = new_elem; + } + pH->ht[h].count++; + pH->ht[h].chain = new_elem; + new_elem->data = data; + return 0; +} diff --git a/src/libs/sqlite2/hash.h b/src/libs/sqlite2/hash.h new file mode 100644 index 00000000..27dd30dc --- /dev/null +++ b/src/libs/sqlite2/hash.h @@ -0,0 +1,109 @@ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implemenation +** used in SQLite. +** +** $Id: hash.h 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, many of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +*/ +struct Hash { + char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */ + char copyKey; /* True if copy of key made on insert */ + int count; /* Number of entries in this table */ + HashElem *first; /* The first element of the array */ + int htsize; /* Number of buckets in the hash table */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + void *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** There are 4 different modes of operation for a hash table: +** +** SQLITE_HASH_INT nKey is used as the key and pKey is ignored. +** +** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored. +** +** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long +** (including the null-terminator, if any). Case +** is ignored in comparisons. +** +** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long. +** memcmp() is used to compare keys. +** +** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY +** if the copyKey parameter to HashInit is 1. +*/ +#define SQLITE_HASH_INT 1 +/* #define SQLITE_HASH_POINTER 2 // NOT USED */ +#define SQLITE_HASH_STRING 3 +#define SQLITE_HASH_BINARY 4 + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +void sqliteHashInit(Hash*, int keytype, int copyKey); +void *sqliteHashInsert(Hash*, const void *pKey, int nKey, void *pData); +void *sqliteHashFind(const Hash*, const void *pKey, int nKey); +void sqliteHashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +#define sqliteHashKey(E) ((E)->pKey) +#define sqliteHashKeysize(E) ((E)->nKey) + +/* +** Number of entries in a hash table +*/ +#define sqliteHashCount(H) ((H)->count) + +#endif /* _SQLITE_HASH_H_ */ diff --git a/src/libs/sqlite2/insert.c b/src/libs/sqlite2/insert.c new file mode 100644 index 00000000..2f73db4a --- /dev/null +++ b/src/libs/sqlite2/insert.c @@ -0,0 +1,919 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the parser +** to handle INSERT statements in SQLite. +** +** $Id: insert.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" + +/* +** This routine is call to handle SQL of the following forms: +** +** insert into TABLE (IDLIST) values(EXPRLIST) +** insert into TABLE (IDLIST) select +** +** The IDLIST following the table name is always optional. If omitted, +** then a list of all columns for the table is substituted. The IDLIST +** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. +** +** The pList parameter holds EXPRLIST in the first form of the INSERT +** statement above, and pSelect is NULL. For the second form, pList is +** NULL and pSelect is a pointer to the select statement used to generate +** data for the insert. +** +** The code generated follows one of three templates. For a simple +** select with data coming from a VALUES clause, the code executes +** once straight down through. The template looks like this: +** +** open write cursor to and its indices +** puts VALUES clause expressions onto the stack +** write the resulting record into
      +** cleanup +** +** If the statement is of the form +** +** INSERT INTO
      SELECT ... +** +** And the SELECT clause does not read from
      at any time, then +** the generated code follows this template: +** +** goto B +** A: setup for the SELECT +** loop over the tables in the SELECT +** gosub C +** end loop +** cleanup after the SELECT +** goto D +** B: open write cursor to
      and its indices +** goto A +** C: insert the select result into
      +** return +** D: cleanup +** +** The third template is used if the insert statement takes its +** values from a SELECT but the data is being inserted into a table +** that is also read as part of the SELECT. In the third form, +** we have to use a intermediate table to store the results of +** the select. The template is like this: +** +** goto B +** A: setup for the SELECT +** loop over the tables in the SELECT +** gosub C +** end loop +** cleanup after the SELECT +** goto D +** C: insert the select result into the intermediate table +** return +** B: open a cursor to an intermediate table +** goto A +** D: open write cursor to
      and its indices +** loop over the intermediate table +** transfer values form intermediate table into
      +** end the loop +** cleanup +*/ +void sqliteInsert( + Parse *pParse, /* Parser context */ + SrcList *pTabList, /* Name of table into which we are inserting */ + ExprList *pList, /* List of values to be inserted */ + Select *pSelect, /* A SELECT statement to use as the data source */ + IdList *pColumn, /* Column names corresponding to IDLIST. */ + int onError /* How to handle constraint errors */ +){ + Table *pTab; /* The table to insert into */ + char *zTab; /* Name of the table into which we are inserting */ + const char *zDb; /* Name of the database holding this table */ + int i, j, idx; /* Loop counters */ + Vdbe *v; /* Generate code into this virtual machine */ + Index *pIdx; /* For looping over indices of the table */ + int nColumn; /* Number of columns in the data */ + int base; /* VDBE Cursor number for pTab */ + int iCont, iBreak; /* Beginning and end of the loop over srcTab */ + sqlite *db; /* The main database structure */ + int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ + int endOfLoop; /* Label for the end of the insertion loop */ + int useTempTable; /* Store SELECT results in intermediate table */ + int srcTab; /* Data comes from this temporary cursor if >=0 */ + int iSelectLoop; /* Address of code that implements the SELECT */ + int iCleanup; /* Address of the cleanup code */ + int iInsertBlock; /* Address of the subroutine used to insert data */ + int iCntMem; /* Memory cell used for the row counter */ + int isView; /* True if attempting to insert into a view */ + + int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */ + int before_triggers; /* True if there are BEFORE triggers */ + int after_triggers; /* True if there are AFTER triggers */ + int newIdx = -1; /* Cursor for the NEW table */ + + if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; + db = pParse->db; + + /* Locate the table into which we will be inserting new information. + */ + assert( pTabList->nSrc==1 ); + zTab = pTabList->a[0].zName; + if( zTab==0 ) goto insert_cleanup; + pTab = sqliteSrcListLookup(pParse, pTabList); + if( pTab==0 ){ + goto insert_cleanup; + } + assert( pTab->iDbnDb ); + zDb = db->aDb[pTab->iDb].zName; + if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){ + goto insert_cleanup; + } + + /* Ensure that: + * (a) the table is not read-only, + * (b) that if it is a view then ON INSERT triggers exist + */ + before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, + TK_BEFORE, TK_ROW, 0); + after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT, + TK_AFTER, TK_ROW, 0); + row_triggers_exist = before_triggers || after_triggers; + isView = pTab->pSelect!=0; + if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){ + goto insert_cleanup; + } + if( pTab==0 ) goto insert_cleanup; + + /* If pTab is really a view, make sure it has been initialized. + */ + if( isView && sqliteViewGetColumnNames(pParse, pTab) ){ + goto insert_cleanup; + } + + /* Allocate a VDBE + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) goto insert_cleanup; + sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb); + + /* if there are row triggers, allocate a temp table for new.* references. */ + if( row_triggers_exist ){ + newIdx = pParse->nTab++; + } + + /* Figure out how many columns of data are supplied. If the data + ** is coming from a SELECT statement, then this step also generates + ** all the code to implement the SELECT statement and invoke a subroutine + ** to process each row of the result. (Template 2.) If the SELECT + ** statement uses the the table that is being inserted into, then the + ** subroutine is also coded here. That subroutine stores the SELECT + ** results in a temporary table. (Template 3.) + */ + if( pSelect ){ + /* Data is coming from a SELECT. Generate code to implement that SELECT + */ + int rc, iInitCode; + iInitCode = sqliteVdbeAddOp(v, OP_Goto, 0, 0); + iSelectLoop = sqliteVdbeCurrentAddr(v); + iInsertBlock = sqliteVdbeMakeLabel(v); + rc = sqliteSelect(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0); + if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; + iCleanup = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Goto, 0, iCleanup); + assert( pSelect->pEList ); + nColumn = pSelect->pEList->nExpr; + + /* Set useTempTable to TRUE if the result of the SELECT statement + ** should be written into a temporary table. Set to FALSE if each + ** row of the SELECT can be written directly into the result table. + ** + ** A temp table must be used if the table being updated is also one + ** of the tables being read by the SELECT statement. Also use a + ** temp table in the case of row triggers. + */ + if( row_triggers_exist ){ + useTempTable = 1; + }else{ + int addr = sqliteVdbeFindOp(v, OP_OpenRead, pTab->tnum); + useTempTable = 0; + if( addr>0 ){ + VdbeOp *pOp = sqliteVdbeGetOp(v, addr-2); + if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){ + useTempTable = 1; + } + } + } + + if( useTempTable ){ + /* Generate the subroutine that SELECT calls to process each row of + ** the result. Store the result in a temporary table + */ + srcTab = pParse->nTab++; + sqliteVdbeResolveLabel(v, iInsertBlock); + sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0); + sqliteVdbeAddOp(v, OP_NewRecno, srcTab, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, srcTab, 0); + sqliteVdbeAddOp(v, OP_Return, 0, 0); + + /* The following code runs first because the GOTO at the very top + ** of the program jumps to it. Create the temporary table, then jump + ** back up and execute the SELECT code above. + */ + sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v)); + sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop); + sqliteVdbeResolveLabel(v, iCleanup); + }else{ + sqliteVdbeChangeP2(v, iInitCode, sqliteVdbeCurrentAddr(v)); + } + }else{ + /* This is the case if the data for the INSERT is coming from a VALUES + ** clause + */ + SrcList dummy; + assert( pList!=0 ); + srcTab = -1; + useTempTable = 0; + assert( pList ); + nColumn = pList->nExpr; + dummy.nSrc = 0; + for(i=0; ia[i].pExpr) ){ + goto insert_cleanup; + } + if( sqliteExprCheck(pParse, pList->a[i].pExpr, 0, 0) ){ + goto insert_cleanup; + } + } + } + + /* Make sure the number of columns in the source data matches the number + ** of columns to be inserted into the table. + */ + if( pColumn==0 && nColumn!=pTab->nCol ){ + sqliteErrorMsg(pParse, + "table %S has %d columns but %d values were supplied", + pTabList, 0, pTab->nCol, nColumn); + goto insert_cleanup; + } + if( pColumn!=0 && nColumn!=pColumn->nId ){ + sqliteErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); + goto insert_cleanup; + } + + /* If the INSERT statement included an IDLIST term, then make sure + ** all elements of the IDLIST really are columns of the table and + ** remember the column indices. + ** + ** If the table has an INTEGER PRIMARY KEY column and that column + ** is named in the IDLIST, then record in the keyColumn variable + ** the index into IDLIST of the primary key column. keyColumn is + ** the index of the primary key as it appears in IDLIST, not as + ** is appears in the original table. (The index of the primary + ** key in the original table is pTab->iPKey.) + */ + if( pColumn ){ + for(i=0; inId; i++){ + pColumn->a[i].idx = -1; + } + for(i=0; inId; i++){ + for(j=0; jnCol; j++){ + if( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ + pColumn->a[i].idx = j; + if( j==pTab->iPKey ){ + keyColumn = i; + } + break; + } + } + if( j>=pTab->nCol ){ + if( sqliteIsRowid(pColumn->a[i].zName) ){ + keyColumn = i; + }else{ + sqliteErrorMsg(pParse, "table %S has no column named %s", + pTabList, 0, pColumn->a[i].zName); + pParse->nErr++; + goto insert_cleanup; + } + } + } + } + + /* If there is no IDLIST term but the table has an integer primary + ** key, the set the keyColumn variable to the primary key column index + ** in the original table definition. + */ + if( pColumn==0 ){ + keyColumn = pTab->iPKey; + } + + /* Open the temp table for FOR EACH ROW triggers + */ + if( row_triggers_exist ){ + sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0); + } + + /* Initialize the count of rows to be inserted + */ + if( db->flags & SQLITE_CountRows ){ + iCntMem = pParse->nMem++; + sqliteVdbeAddOp(v, OP_Integer, 0, 0); + sqliteVdbeAddOp(v, OP_MemStore, iCntMem, 1); + } + + /* Open tables and indices if there are no row triggers */ + if( !row_triggers_exist ){ + base = pParse->nTab; + idx = sqliteOpenTableAndIndices(pParse, pTab, base); + pParse->nTab += idx; + } + + /* If the data source is a temporary table, then we have to create + ** a loop because there might be multiple rows of data. If the data + ** source is a subroutine call from the SELECT statement, then we need + ** to launch the SELECT statement processing. + */ + if( useTempTable ){ + iBreak = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); + iCont = sqliteVdbeCurrentAddr(v); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Goto, 0, iSelectLoop); + sqliteVdbeResolveLabel(v, iInsertBlock); + } + + /* Run the BEFORE and INSTEAD OF triggers, if there are any + */ + endOfLoop = sqliteVdbeMakeLabel(v); + if( before_triggers ){ + + /* build the NEW.* reference row. Note that if there is an INTEGER + ** PRIMARY KEY into which a NULL is being inserted, that NULL will be + ** translated into a unique ID for the row. But on a BEFORE trigger, + ** we do not know what the unique ID will be (because the insert has + ** not happened yet) so we substitute a rowid of -1 + */ + if( keyColumn<0 ){ + sqliteVdbeAddOp(v, OP_Integer, -1, 0); + }else if( useTempTable ){ + sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1); + }else{ + sqliteExprCode(pParse, pList->a[keyColumn].pExpr); + sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_Integer, -1, 0); + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); + } + + /* Create the new column data + */ + for(i=0; inCol; i++){ + if( pColumn==0 ){ + j = i; + }else{ + for(j=0; jnId; j++){ + if( pColumn->a[j].idx==i ) break; + } + } + if( pColumn && j>=pColumn->nId ){ + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); + }else if( useTempTable ){ + sqliteVdbeAddOp(v, OP_Column, srcTab, j); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Dup, nColumn-j-1, 1); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); + + /* Fire BEFORE or INSTEAD OF triggers */ + if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, + newIdx, -1, onError, endOfLoop) ){ + goto insert_cleanup; + } + } + + /* If any triggers exists, the opening of tables and indices is deferred + ** until now. + */ + if( row_triggers_exist && !isView ){ + base = pParse->nTab; + idx = sqliteOpenTableAndIndices(pParse, pTab, base); + pParse->nTab += idx; + } + + /* Push the record number for the new entry onto the stack. The + ** record number is a randomly generate integer created by NewRecno + ** except when the table has an INTEGER PRIMARY KEY column, in which + ** case the record number is the same as that column. + */ + if( !isView ){ + if( keyColumn>=0 ){ + if( useTempTable ){ + sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1); + }else{ + sqliteExprCode(pParse, pList->a[keyColumn].pExpr); + } + /* If the PRIMARY KEY expression is NULL, then use OP_NewRecno + ** to generate a unique primary key value. + */ + sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_NewRecno, base, 0); + sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0); + }else{ + sqliteVdbeAddOp(v, OP_NewRecno, base, 0); + } + + /* Push onto the stack, data for all columns of the new entry, beginning + ** with the first column. + */ + for(i=0; inCol; i++){ + if( i==pTab->iPKey ){ + /* The value of the INTEGER PRIMARY KEY column is always a NULL. + ** Whenever this column is read, the record number will be substituted + ** in its place. So will fill this column with a NULL to avoid + ** taking up data space with information that will never be used. */ + sqliteVdbeAddOp(v, OP_String, 0, 0); + continue; + } + if( pColumn==0 ){ + j = i; + }else{ + for(j=0; jnId; j++){ + if( pColumn->a[j].idx==i ) break; + } + } + if( pColumn && j>=pColumn->nId ){ + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); + }else if( useTempTable ){ + sqliteVdbeAddOp(v, OP_Column, srcTab, j); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Dup, i+nColumn-j, 1); + }else{ + sqliteExprCode(pParse, pList->a[j].pExpr); + } + } + + /* Generate code to check constraints and generate index keys and + ** do the insertion. + */ + sqliteGenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0, + 0, onError, endOfLoop); + sqliteCompleteInsertion(pParse, pTab, base, 0,0,0, + after_triggers ? newIdx : -1); + } + + /* Update the count of rows that are inserted + */ + if( (db->flags & SQLITE_CountRows)!=0 ){ + sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0); + } + + if( row_triggers_exist ){ + /* Close all tables opened */ + if( !isView ){ + sqliteVdbeAddOp(v, OP_Close, base, 0); + for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ + sqliteVdbeAddOp(v, OP_Close, idx+base, 0); + } + } + + /* Code AFTER triggers */ + if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, + onError, endOfLoop) ){ + goto insert_cleanup; + } + } + + /* The bottom of the loop, if the data source is a SELECT statement + */ + sqliteVdbeResolveLabel(v, endOfLoop); + if( useTempTable ){ + sqliteVdbeAddOp(v, OP_Next, srcTab, iCont); + sqliteVdbeResolveLabel(v, iBreak); + sqliteVdbeAddOp(v, OP_Close, srcTab, 0); + }else if( pSelect ){ + sqliteVdbeAddOp(v, OP_Pop, nColumn, 0); + sqliteVdbeAddOp(v, OP_Return, 0, 0); + sqliteVdbeResolveLabel(v, iCleanup); + } + + if( !row_triggers_exist ){ + /* Close all tables opened */ + sqliteVdbeAddOp(v, OP_Close, base, 0); + for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ + sqliteVdbeAddOp(v, OP_Close, idx+base, 0); + } + } + + sqliteVdbeAddOp(v, OP_SetCounts, 0, 0); + sqliteEndWriteOperation(pParse); + + /* + ** Return the number of rows inserted. + */ + if( db->flags & SQLITE_CountRows ){ + sqliteVdbeOp3(v, OP_ColumnName, 0, 1, "rows inserted", P3_STATIC); + sqliteVdbeAddOp(v, OP_MemLoad, iCntMem, 0); + sqliteVdbeAddOp(v, OP_Callback, 1, 0); + } + +insert_cleanup: + sqliteSrcListDelete(pTabList); + if( pList ) sqliteExprListDelete(pList); + if( pSelect ) sqliteSelectDelete(pSelect); + sqliteIdListDelete(pColumn); +} + +/* +** Generate code to do a constraint check prior to an INSERT or an UPDATE. +** +** When this routine is called, the stack contains (from bottom to top) +** the following values: +** +** 1. The recno of the row to be updated before the update. This +** value is omitted unless we are doing an UPDATE that involves a +** change to the record number. +** +** 2. The recno of the row after the update. +** +** 3. The data in the first column of the entry after the update. +** +** i. Data from middle columns... +** +** N. The data in the last column of the entry after the update. +** +** The old recno shown as entry (1) above is omitted unless both isUpdate +** and recnoChng are 1. isUpdate is true for UPDATEs and false for +** INSERTs and recnoChng is true if the record number is being changed. +** +** The code generated by this routine pushes additional entries onto +** the stack which are the keys for new index entries for the new record. +** The order of index keys is the same as the order of the indices on +** the pTable->pIndex list. A key is only created for index i if +** aIdxUsed!=0 and aIdxUsed[i]!=0. +** +** This routine also generates code to check constraints. NOT NULL, +** CHECK, and UNIQUE constraints are all checked. If a constraint fails, +** then the appropriate action is performed. There are five possible +** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE. +** +** Constraint type Action What Happens +** --------------- ---------- ---------------------------------------- +** any ROLLBACK The current transaction is rolled back and +** sqlite_exec() returns immediately with a +** return code of SQLITE_CONSTRAINT. +** +** any ABORT Back out changes from the current command +** only (do not do a complete rollback) then +** cause sqlite_exec() to return immediately +** with SQLITE_CONSTRAINT. +** +** any FAIL Sqlite_exec() returns immediately with a +** return code of SQLITE_CONSTRAINT. The +** transaction is not rolled back and any +** prior changes are retained. +** +** any IGNORE The record number and data is popped from +** the stack and there is an immediate jump +** to label ignoreDest. +** +** NOT NULL REPLACE The NULL value is replace by the default +** value for that column. If the default value +** is NULL, the action is the same as ABORT. +** +** UNIQUE REPLACE The other row that conflicts with the row +** being inserted is removed. +** +** CHECK REPLACE Illegal. The results in an exception. +** +** Which action to take is determined by the overrideError parameter. +** Or if overrideError==OE_Default, then the pParse->onError parameter +** is used. Or if pParse->onError==OE_Default then the onError value +** for the constraint is used. +** +** The calling routine must open a read/write cursor for pTab with +** cursor number "base". All indices of pTab must also have open +** read/write cursors with cursor number base+i for the i-th cursor. +** Except, if there is no possibility of a REPLACE action then +** cursors do not need to be open for indices where aIdxUsed[i]==0. +** +** If the isUpdate flag is true, it means that the "base" cursor is +** initially pointing to an entry that is being updated. The isUpdate +** flag causes extra code to be generated so that the "base" cursor +** is still pointing at the same entry after the routine returns. +** Without the isUpdate flag, the "base" cursor might be moved. +*/ +void sqliteGenerateConstraintChecks( + Parse *pParse, /* The parser context */ + Table *pTab, /* the table into which we are inserting */ + int base, /* Index of a read/write cursor pointing at pTab */ + char *aIdxUsed, /* Which indices are used. NULL means all are used */ + int recnoChng, /* True if the record number will change */ + int isUpdate, /* True for UPDATE, False for INSERT */ + int overrideError, /* Override onError to this if not OE_Default */ + int ignoreDest /* Jump to this label on an OE_Ignore resolution */ +){ + int i; + Vdbe *v; + int nCol; + int onError; + int addr; + int extra; + int iCur; + Index *pIdx; + int seenReplace = 0; + int jumpInst1, jumpInst2; + int contAddr; + int hasTwoRecnos = (isUpdate && recnoChng); + + v = sqliteGetVdbe(pParse); + assert( v!=0 ); + assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + nCol = pTab->nCol; + + /* Test all NOT NULL constraints. + */ + for(i=0; iiPKey ){ + continue; + } + onError = pTab->aCol[i].notNull; + if( onError==OE_None ) continue; + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( pParse->db->onError!=OE_Default ){ + onError = pParse->db->onError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){ + onError = OE_Abort; + } + sqliteVdbeAddOp(v, OP_Dup, nCol-1-i, 1); + addr = sqliteVdbeAddOp(v, OP_NotNull, 1, 0); + switch( onError ){ + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + char *zMsg = 0; + sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError); + sqliteSetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName, + " may not be NULL", (char*)0); + sqliteVdbeChangeP3(v, -1, zMsg, P3_DYNAMIC); + break; + } + case OE_Ignore: { + sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + case OE_Replace: { + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zDflt, P3_STATIC); + sqliteVdbeAddOp(v, OP_Push, nCol-i, 0); + break; + } + default: assert(0); + } + sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v)); + } + + /* Test all CHECK constraints + */ + /**** TBD ****/ + + /* If we have an INTEGER PRIMARY KEY, make sure the primary key + ** of the new record does not previously exist. Except, if this + ** is an UPDATE and the primary key is not changing, that is OK. + */ + if( recnoChng ){ + onError = pTab->keyConf; + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( pParse->db->onError!=OE_Default ){ + onError = pParse->db->onError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + + if( isUpdate ){ + sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); + sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); + jumpInst1 = sqliteVdbeAddOp(v, OP_Eq, 0, 0); + } + sqliteVdbeAddOp(v, OP_Dup, nCol, 1); + jumpInst2 = sqliteVdbeAddOp(v, OP_NotExists, base, 0); + switch( onError ){ + default: { + onError = OE_Abort; + /* Fall thru into the next case */ + } + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, + "PRIMARY KEY must be unique", P3_STATIC); + break; + } + case OE_Replace: { + sqliteGenerateRowIndexDelete(pParse->db, v, pTab, base, 0); + if( isUpdate ){ + sqliteVdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0); + } + seenReplace = 1; + break; + } + case OE_Ignore: { + assert( seenReplace==0 ); + sqliteVdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + } + contAddr = sqliteVdbeCurrentAddr(v); + sqliteVdbeChangeP2(v, jumpInst2, contAddr); + if( isUpdate ){ + sqliteVdbeChangeP2(v, jumpInst1, contAddr); + sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0); + } + } + + /* Test all UNIQUE constraints by creating entries for each UNIQUE + ** index and making sure that duplicate entries do not already exist. + ** Add the new records to the indices as we go. + */ + extra = -1; + for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ + if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */ + extra++; + + /* Create a key for accessing the index entry */ + sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1); + for(i=0; inColumn; i++){ + int idx = pIdx->aiColumn[i]; + if( idx==pTab->iPKey ){ + sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1); + }else{ + sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1); + } + } + jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); + if( pParse->db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); + + /* Find out what action to take in case there is an indexing conflict */ + onError = pIdx->onError; + if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */ + if( overrideError!=OE_Default ){ + onError = overrideError; + }else if( pParse->db->onError!=OE_Default ){ + onError = pParse->db->onError; + }else if( onError==OE_Default ){ + onError = OE_Abort; + } + if( seenReplace ){ + if( onError==OE_Ignore ) onError = OE_Replace; + else if( onError==OE_Fail ) onError = OE_Abort; + } + + + /* Check to see if the new index entry will be unique */ + sqliteVdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1); + jumpInst2 = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0); + + /* Generate code that executes if the new index entry is not unique */ + switch( onError ){ + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + int j, n1, n2; + char zErrMsg[200]; + strcpy(zErrMsg, pIdx->nColumn>1 ? "columns " : "column "); + n1 = strlen(zErrMsg); + for(j=0; jnColumn && n1aCol[pIdx->aiColumn[j]].zName; + n2 = strlen(zCol); + if( j>0 ){ + strcpy(&zErrMsg[n1], ", "); + n1 += 2; + } + if( n1+n2>sizeof(zErrMsg)-30 ){ + strcpy(&zErrMsg[n1], "..."); + n1 += 3; + break; + }else{ + strcpy(&zErrMsg[n1], zCol); + n1 += n2; + } + } + strcpy(&zErrMsg[n1], + pIdx->nColumn>1 ? " are not unique" : " is not unique"); + sqliteVdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0); + break; + } + case OE_Ignore: { + assert( seenReplace==0 ); + sqliteVdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest); + break; + } + case OE_Replace: { + sqliteGenerateRowDelete(pParse->db, v, pTab, base, 0); + if( isUpdate ){ + sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0); + } + seenReplace = 1; + break; + } + default: assert(0); + } + contAddr = sqliteVdbeCurrentAddr(v); +#if NULL_DISTINCT_FOR_UNIQUE + sqliteVdbeChangeP2(v, jumpInst1, contAddr); +#endif + sqliteVdbeChangeP2(v, jumpInst2, contAddr); + } +} + +/* +** This routine generates code to finish the INSERT or UPDATE operation +** that was started by a prior call to sqliteGenerateConstraintChecks. +** The stack must contain keys for all active indices followed by data +** and the recno for the new entry. This routine creates the new +** entries in all indices and in the main table. +** +** The arguments to this routine should be the same as the first six +** arguments to sqliteGenerateConstraintChecks. +*/ +void sqliteCompleteInsertion( + Parse *pParse, /* The parser context */ + Table *pTab, /* the table into which we are inserting */ + int base, /* Index of a read/write cursor pointing at pTab */ + char *aIdxUsed, /* Which indices are used. NULL means all are used */ + int recnoChng, /* True if the record number will change */ + int isUpdate, /* True for UPDATE, False for INSERT */ + int newIdx /* Index of NEW table for triggers. -1 if none */ +){ + int i; + Vdbe *v; + int nIdx; + Index *pIdx; + + v = sqliteGetVdbe(pParse); + assert( v!=0 ); + assert( pTab->pSelect==0 ); /* This table is not a VIEW */ + for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){} + for(i=nIdx-1; i>=0; i--){ + if( aIdxUsed && aIdxUsed[i]==0 ) continue; + sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0); + } + sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); + if( newIdx>=0 ){ + sqliteVdbeAddOp(v, OP_Dup, 1, 0); + sqliteVdbeAddOp(v, OP_Dup, 1, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); + } + sqliteVdbeAddOp(v, OP_PutIntKey, base, + (pParse->trigStack?0:OPFLAG_NCHANGE) | + (isUpdate?0:OPFLAG_LASTROWID) | OPFLAG_CSCHANGE); + if( isUpdate && recnoChng ){ + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + } +} + +/* +** Generate code that will open write cursors for a table and for all +** indices of that table. The "base" parameter is the cursor number used +** for the table. Indices are opened on subsequent cursors. +** +** Return the total number of cursors opened. This is always at least +** 1 (for the main table) plus more for each cursor. +*/ +int sqliteOpenTableAndIndices(Parse *pParse, Table *pTab, int base){ + int i; + Index *pIdx; + Vdbe *v = sqliteGetVdbe(pParse); + assert( v!=0 ); + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeOp3(v, OP_OpenWrite, base, pTab->tnum, pTab->zName, P3_STATIC); + for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); + sqliteVdbeOp3(v, OP_OpenWrite, i+base, pIdx->tnum, pIdx->zName, P3_STATIC); + } + return i; +} diff --git a/src/libs/sqlite2/main.c b/src/libs/sqlite2/main.c new file mode 100644 index 00000000..7541b171 --- /dev/null +++ b/src/libs/sqlite2/main.c @@ -0,0 +1,1143 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Main file for the SQLite library. The routines in this file +** implement the programmer interface to the library. Routines in +** other files are for internal use by SQLite and should not be +** accessed by users of the library. +** +** $Id: main.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "sqliteInt.h" +#include "os.h" +#include + +/* +** A pointer to this structure is used to communicate information +** from sqliteInit into the sqliteInitCallback. +*/ +typedef struct { + sqlite *db; /* The database being initialized */ + char **pzErrMsg; /* Error message stored here */ +} InitData; + +/* +** Fill the InitData structure with an error message that indicates +** that the database is corrupt. +*/ +static void corruptSchema(InitData *pData, const char *zExtra){ + sqliteSetString(pData->pzErrMsg, "malformed database schema", + zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0); +} + +/* +** This is the callback routine for the code that initializes the +** database. See sqliteInit() below for additional information. +** +** Each callback contains the following information: +** +** argv[0] = "file-format" or "schema-cookie" or "table" or "index" +** argv[1] = table or index name or meta statement type. +** argv[2] = root page number for table or index. NULL for meta. +** argv[3] = SQL text for a CREATE TABLE or CREATE INDEX statement. +** argv[4] = "1" for temporary files, "0" for main database, "2" or more +** for auxiliary database files. +** +*/ +static +int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){ + InitData *pData = (InitData*)pInit; + int nErr = 0; + + assert( argc==5 ); + if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ + if( argv[0]==0 ){ + corruptSchema(pData, 0); + return 1; + } + switch( argv[0][0] ){ + case 'v': + case 'i': + case 't': { /* CREATE TABLE, CREATE INDEX, or CREATE VIEW statements */ + sqlite *db = pData->db; + if( argv[2]==0 || argv[4]==0 ){ + corruptSchema(pData, 0); + return 1; + } + if( argv[3] && argv[3][0] ){ + /* Call the parser to process a CREATE TABLE, INDEX or VIEW. + ** But because db->init.busy is set to 1, no VDBE code is generated + ** or executed. All the parser does is build the internal data + ** structures that describe the table, index, or view. + */ + char *zErr; + assert( db->init.busy ); + db->init.iDb = atoi(argv[4]); + assert( db->init.iDb>=0 && db->init.iDbnDb ); + db->init.newTnum = atoi(argv[2]); + if( sqlite_exec(db, argv[3], 0, 0, &zErr) ){ + corruptSchema(pData, zErr); + sqlite_freemem(zErr); + } + db->init.iDb = 0; + }else{ + /* If the SQL column is blank it means this is an index that + ** was created to be the PRIMARY KEY or to fulfill a UNIQUE + ** constraint for a CREATE TABLE. The index should have already + ** been created when we processed the CREATE TABLE. All we have + ** to do here is record the root page number for that index. + */ + int iDb; + Index *pIndex; + + iDb = atoi(argv[4]); + assert( iDb>=0 && iDbnDb ); + pIndex = sqliteFindIndex(db, argv[1], db->aDb[iDb].zName); + if( pIndex==0 || pIndex->tnum!=0 ){ + /* This can occur if there exists an index on a TEMP table which + ** has the same name as another index on a permanent index. Since + ** the permanent table is hidden by the TEMP table, we can also + ** safely ignore the index on the permanent table. + */ + /* Do Nothing */; + }else{ + pIndex->tnum = atoi(argv[2]); + } + } + break; + } + default: { + /* This can not happen! */ + nErr = 1; + assert( nErr==0 ); + } + } + return nErr; +} + +/* +** This is a callback procedure used to reconstruct a table. The +** name of the table to be reconstructed is passed in as argv[0]. +** +** This routine is used to automatically upgrade a database from +** format version 1 or 2 to version 3. The correct operation of +** this routine relys on the fact that no indices are used when +** copying a table out to a temporary file. +** +** The change from version 2 to version 3 occurred between SQLite +** version 2.5.6 and 2.6.0 on 2002-July-18. +*/ +static +int upgrade_3_callback(void *pInit, int argc, char **argv, char **NotUsed){ + InitData *pData = (InitData*)pInit; + int rc; + Table *pTab; + Trigger *pTrig; + char *zErr = 0; + + pTab = sqliteFindTable(pData->db, argv[0], 0); + assert( pTab!=0 ); + assert( sqliteStrICmp(pTab->zName, argv[0])==0 ); + if( pTab ){ + pTrig = pTab->pTrigger; + pTab->pTrigger = 0; /* Disable all triggers before rebuilding the table */ + } + rc = sqlite_exec_printf(pData->db, + "CREATE TEMP TABLE sqlite_x AS SELECT * FROM '%q'; " + "DELETE FROM '%q'; " + "INSERT INTO '%q' SELECT * FROM sqlite_x; " + "DROP TABLE sqlite_x;", + 0, 0, &zErr, argv[0], argv[0], argv[0]); + if( zErr ){ + if( *pData->pzErrMsg ) sqlite_freemem(*pData->pzErrMsg); + *pData->pzErrMsg = zErr; + } + + /* If an error occurred in the SQL above, then the transaction will + ** rollback which will delete the internal symbol tables. This will + ** cause the structure that pTab points to be deleted. In case that + ** happened, we need to refetch pTab. + */ + pTab = sqliteFindTable(pData->db, argv[0], 0); + if( pTab ){ + assert( sqliteStrICmp(pTab->zName, argv[0])==0 ); + pTab->pTrigger = pTrig; /* Re-enable triggers */ + } + return rc!=SQLITE_OK; +} + + + +/* +** Attempt to read the database schema and initialize internal +** data structures for a single database file. The index of the +** database file is given by iDb. iDb==0 is used for the main +** database. iDb==1 should never be used. iDb>=2 is used for +** auxiliary databases. Return one of the SQLITE_ error codes to +** indicate success or failure. +*/ +static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){ + int rc; + BtCursor *curMain; + int size; + Table *pTab; + char const *azArg[6]; + char zDbNum[30]; + int meta[SQLITE_N_BTREE_META]; + InitData initData; + char const *zMasterSchema; + char const *zMasterName; + char *zSql = 0; + + /* + ** The master database table has a structure like this + */ + static char master_schema[] = + "CREATE TABLE sqlite_master(\n" + " type text,\n" + " name text,\n" + " tbl_name text,\n" + " rootpage integer,\n" + " sql text\n" + ")" + ; + static char temp_master_schema[] = + "CREATE TEMP TABLE sqlite_temp_master(\n" + " type text,\n" + " name text,\n" + " tbl_name text,\n" + " rootpage integer,\n" + " sql text\n" + ")" + ; + + assert( iDb>=0 && iDbnDb ); + + /* zMasterSchema and zInitScript are set to point at the master schema + ** and initialisation script appropriate for the database being + ** initialised. zMasterName is the name of the master table. + */ + if( iDb==1 ){ + zMasterSchema = temp_master_schema; + zMasterName = TEMP_MASTER_NAME; + }else{ + zMasterSchema = master_schema; + zMasterName = MASTER_NAME; + } + + /* Construct the schema table. + */ + sqliteSafetyOff(db); + azArg[0] = "table"; + azArg[1] = zMasterName; + azArg[2] = "2"; + azArg[3] = zMasterSchema; + sprintf(zDbNum, "%d", iDb); + azArg[4] = zDbNum; + azArg[5] = 0; + initData.db = db; + initData.pzErrMsg = pzErrMsg; + sqliteInitCallback(&initData, 5, (char **)azArg, 0); + pTab = sqliteFindTable(db, zMasterName, db->aDb[iDb].zName); + if( pTab ){ + pTab->readOnly = 1; + }else{ + return SQLITE_NOMEM; + } + sqliteSafetyOn(db); + + /* Create a cursor to hold the database open + */ + if( db->aDb[iDb].pBt==0 ) return SQLITE_OK; + rc = sqliteBtreeCursor(db->aDb[iDb].pBt, 2, 0, &curMain); + if( rc ){ + sqliteSetString(pzErrMsg, sqlite_error_string(rc), (char*)0); + return rc; + } + + /* Get the database meta information + */ + rc = sqliteBtreeGetMeta(db->aDb[iDb].pBt, meta); + if( rc ){ + sqliteSetString(pzErrMsg, sqlite_error_string(rc), (char*)0); + sqliteBtreeCloseCursor(curMain); + return rc; + } + db->aDb[iDb].schema_cookie = meta[1]; + if( iDb==0 ){ + db->next_cookie = meta[1]; + db->file_format = meta[2]; + size = meta[3]; + if( size==0 ){ size = MAX_PAGES; } + db->cache_size = size; + db->safety_level = meta[4]; + if( meta[6]>0 && meta[6]<=2 && db->temp_store==0 ){ + db->temp_store = meta[6]; + } + if( db->safety_level==0 ) db->safety_level = 2; + + /* + ** file_format==1 Version 2.1.0. + ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. + ** file_format==3 Version 2.6.0. Fix empty-string index bug. + ** file_format==4 Version 2.7.0. Add support for separate numeric and + ** text datatypes. + */ + if( db->file_format==0 ){ + /* This happens if the database was initially empty */ + db->file_format = 4; + }else if( db->file_format>4 ){ + sqliteBtreeCloseCursor(curMain); + sqliteSetString(pzErrMsg, "unsupported file format", (char*)0); + return SQLITE_ERROR; + } + }else if( iDb!=1 && (db->file_format!=meta[2] || db->file_format<4) ){ + assert( db->file_format>=4 ); + if( meta[2]==0 ){ + sqliteSetString(pzErrMsg, "cannot attach empty database: ", + db->aDb[iDb].zName, (char*)0); + }else{ + sqliteSetString(pzErrMsg, "incompatible file format in auxiliary " + "database: ", db->aDb[iDb].zName, (char*)0); + } + sqliteBtreeClose(db->aDb[iDb].pBt); + db->aDb[iDb].pBt = 0; + return SQLITE_FORMAT; + } + sqliteBtreeSetCacheSize(db->aDb[iDb].pBt, db->cache_size); + sqliteBtreeSetSafetyLevel(db->aDb[iDb].pBt, meta[4]==0 ? 2 : meta[4]); + + /* Read the schema information out of the schema tables + */ + assert( db->init.busy ); + sqliteSafetyOff(db); + + /* The following SQL will read the schema from the master tables. + ** The first version works with SQLite file formats 2 or greater. + ** The second version is for format 1 files. + ** + ** Beginning with file format 2, the rowid for new table entries + ** (including entries in sqlite_master) is an increasing integer. + ** So for file format 2 and later, we can play back sqlite_master + ** and all the CREATE statements will appear in the right order. + ** But with file format 1, table entries were random and so we + ** have to make sure the CREATE TABLEs occur before their corresponding + ** CREATE INDEXs. (We don't have to deal with CREATE VIEW or + ** CREATE TRIGGER in file format 1 because those constructs did + ** not exist then.) + */ + if( db->file_format>=2 ){ + sqliteSetString(&zSql, + "SELECT type, name, rootpage, sql, ", zDbNum, " FROM \"", + db->aDb[iDb].zName, "\".", zMasterName, (char*)0); + }else{ + sqliteSetString(&zSql, + "SELECT type, name, rootpage, sql, ", zDbNum, " FROM \"", + db->aDb[iDb].zName, "\".", zMasterName, + " WHERE type IN ('table', 'index')" + " ORDER BY CASE type WHEN 'table' THEN 0 ELSE 1 END", (char*)0); + } + rc = sqlite_exec(db, zSql, sqliteInitCallback, &initData, 0); + + sqliteFree(zSql); + sqliteSafetyOn(db); + sqliteBtreeCloseCursor(curMain); + if( sqlite_malloc_failed ){ + sqliteSetString(pzErrMsg, "out of memory", (char*)0); + rc = SQLITE_NOMEM; + sqliteResetInternalSchema(db, 0); + } + if( rc==SQLITE_OK ){ + DbSetProperty(db, iDb, DB_SchemaLoaded); + }else{ + sqliteResetInternalSchema(db, iDb); + } + return rc; +} + +/* +** Initialize all database files - the main database file, the file +** used to store temporary tables, and any additional database files +** created using ATTACH statements. Return a success code. If an +** error occurs, write an error message into *pzErrMsg. +** +** After the database is initialized, the SQLITE_Initialized +** bit is set in the flags field of the sqlite structure. An +** attempt is made to initialize the database as soon as it +** is opened. If that fails (perhaps because another process +** has the sqlite_master table locked) than another attempt +** is made the first time the database is accessed. +*/ +int sqliteInit(sqlite *db, char **pzErrMsg){ + int i, rc; + + if( db->init.busy ) return SQLITE_OK; + assert( (db->flags & SQLITE_Initialized)==0 ); + rc = SQLITE_OK; + db->init.busy = 1; + for(i=0; rc==SQLITE_OK && inDb; i++){ + if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; + rc = sqliteInitOne(db, i, pzErrMsg); + if( rc ){ + sqliteResetInternalSchema(db, i); + } + } + + /* Once all the other databases have been initialised, load the schema + ** for the TEMP database. This is loaded last, as the TEMP database + ** schema may contain references to objects in other databases. + */ + if( rc==SQLITE_OK && db->nDb>1 && !DbHasProperty(db, 1, DB_SchemaLoaded) ){ + rc = sqliteInitOne(db, 1, pzErrMsg); + if( rc ){ + sqliteResetInternalSchema(db, 1); + } + } + + db->init.busy = 0; + if( rc==SQLITE_OK ){ + db->flags |= SQLITE_Initialized; + sqliteCommitInternalChanges(db); + } + + /* If the database is in formats 1 or 2, then upgrade it to + ** version 3. This will reconstruct all indices. If the + ** upgrade fails for any reason (ex: out of disk space, database + ** is read only, interrupt received, etc.) then fail the init. + */ + if( rc==SQLITE_OK && db->file_format<3 ){ + char *zErr = 0; + InitData initData; + int meta[SQLITE_N_BTREE_META]; + + db->magic = SQLITE_MAGIC_OPEN; + initData.db = db; + initData.pzErrMsg = &zErr; + db->file_format = 3; + rc = sqlite_exec(db, + "BEGIN; SELECT name FROM sqlite_master WHERE type='table';", + upgrade_3_callback, + &initData, + &zErr); + if( rc==SQLITE_OK ){ + sqliteBtreeGetMeta(db->aDb[0].pBt, meta); + meta[2] = 4; + sqliteBtreeUpdateMeta(db->aDb[0].pBt, meta); + sqlite_exec(db, "COMMIT", 0, 0, 0); + } + if( rc!=SQLITE_OK ){ + sqliteSetString(pzErrMsg, + "unable to upgrade database to the version 2.6 format", + zErr ? ": " : 0, zErr, (char*)0); + } + sqlite_freemem(zErr); + } + + if( rc!=SQLITE_OK ){ + db->flags &= ~SQLITE_Initialized; + } + return rc; +} + +/* +** The version of the library +*/ +const char rcsid[] = "@(#) \044Id: SQLite version " SQLITE_VERSION " $"; +const char sqlite_version[] = SQLITE_VERSION; + +/* +** Does the library expect data to be encoded as UTF-8 or iso8859? The +** following global constant always lets us know. +*/ +#ifdef SQLITE_UTF8 +const char sqlite_encoding[] = "UTF-8"; +#else +const char sqlite_encoding[] = "iso8859"; +#endif + +/* +** Open a new SQLite database. Construct an "sqlite" structure to define +** the state of this database and return a pointer to that structure. +** +** An attempt is made to initialize the in-memory data structures that +** hold the database schema. But if this fails (because the schema file +** is locked) then that step is deferred until the first call to +** sqlite_exec(). +*/ +sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ + sqlite *db; + int rc, i; + + /* Allocate the sqlite data structure */ + db = sqliteMalloc( sizeof(sqlite) ); + if( pzErrMsg ) *pzErrMsg = 0; + if( db==0 ) goto no_mem_on_open; + db->onError = OE_Default; + db->priorNewRowid = 0; + db->magic = SQLITE_MAGIC_BUSY; + db->nDb = 2; + db->aDb = db->aDbStatic; + /* db->flags |= SQLITE_ShortColNames; */ + sqliteHashInit(&db->aFunc, SQLITE_HASH_STRING, 1); + for(i=0; inDb; i++){ + sqliteHashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0); + sqliteHashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1); + } + + /* Open the backend database driver */ + if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){ + db->temp_store = 2; + } + rc = sqliteBtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt); + if( rc!=SQLITE_OK ){ + switch( rc ){ + default: { + sqliteSetString(pzErrMsg, "unable to open database: ", + zFilename, (char*)0); + } + } + sqliteFree(db); + sqliteStrRealloc(pzErrMsg); + return 0; + } + db->aDb[0].zName = "main"; + db->aDb[1].zName = "temp"; + + /* Attempt to read the schema */ + sqliteRegisterBuiltinFunctions(db); + rc = sqliteInit(db, pzErrMsg); + db->magic = SQLITE_MAGIC_OPEN; + if( sqlite_malloc_failed ){ + sqlite_close(db); + goto no_mem_on_open; + }else if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ + sqlite_close(db); + sqliteStrRealloc(pzErrMsg); + return 0; + }else if( pzErrMsg ){ + sqliteFree(*pzErrMsg); + *pzErrMsg = 0; + } + + /* Return a pointer to the newly opened database structure */ + return db; + +no_mem_on_open: + sqliteSetString(pzErrMsg, "out of memory", (char*)0); + sqliteStrRealloc(pzErrMsg); + return 0; +} + +/* +** Return the ROWID of the most recent insert +*/ +int sqlite_last_insert_rowid(sqlite *db){ + return db->lastRowid; +} + +/* +** Return the number of changes in the most recent call to sqlite_exec(). +*/ +int sqlite_changes(sqlite *db){ + return db->nChange; +} + +/* +** Return the number of changes produced by the last INSERT, UPDATE, or +** DELETE statement to complete execution. The count does not include +** changes due to SQL statements executed in trigger programs that were +** triggered by that statement +*/ +int sqlite_last_statement_changes(sqlite *db){ + return db->lsChange; +} + +/* +** Close an existing SQLite database +*/ +void sqlite_close(sqlite *db){ + HashElem *i; + int j; + db->want_to_close = 1; + if( sqliteSafetyCheck(db) || sqliteSafetyOn(db) ){ + /* printf("DID NOT CLOSE\n"); fflush(stdout); */ + return; + } + db->magic = SQLITE_MAGIC_CLOSED; + for(j=0; jnDb; j++){ + struct Db *pDb = &db->aDb[j]; + if( pDb->pBt ){ + sqliteBtreeClose(pDb->pBt); + pDb->pBt = 0; + } + } + sqliteResetInternalSchema(db, 0); + assert( db->nDb<=2 ); + assert( db->aDb==db->aDbStatic ); + for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){ + FuncDef *pFunc, *pNext; + for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){ + pNext = pFunc->pNext; + sqliteFree(pFunc); + } + } + sqliteHashClear(&db->aFunc); + sqliteFree(db); +} + +/* +** Rollback all database files. +*/ +void sqliteRollbackAll(sqlite *db){ + int i; + for(i=0; inDb; i++){ + if( db->aDb[i].pBt ){ + sqliteBtreeRollback(db->aDb[i].pBt); + db->aDb[i].inTrans = 0; + } + } + sqliteResetInternalSchema(db, 0); + /* sqliteRollbackInternalChanges(db); */ +} + +/* +** Execute SQL code. Return one of the SQLITE_ success/failure +** codes. Also write an error message into memory obtained from +** malloc() and make *pzErrMsg point to that message. +** +** If the SQL is a query, then for each row in the query result +** the xCallback() function is called. pArg becomes the first +** argument to xCallback(). If xCallback=NULL then no callback +** is invoked, even for queries. +*/ +int sqlite_exec( + sqlite *db, /* The database on which the SQL executes */ + const char *zSql, /* The SQL to be executed */ + sqlite_callback xCallback, /* Invoke this callback routine */ + void *pArg, /* First argument to xCallback() */ + char **pzErrMsg /* Write error messages here */ +){ + int rc = SQLITE_OK; + const char *zLeftover; + sqlite_vm *pVm; + int nRetry = 0; + int nChange = 0; + int nCallback; + + if( zSql==0 ) return SQLITE_OK; + while( rc==SQLITE_OK && zSql[0] ){ + pVm = 0; + rc = sqlite_compile(db, zSql, &zLeftover, &pVm, pzErrMsg); + if( rc!=SQLITE_OK ){ + assert( pVm==0 || sqlite_malloc_failed ); + return rc; + } + if( pVm==0 ){ + /* This happens if the zSql input contained only whitespace */ + break; + } + db->nChange += nChange; + nCallback = 0; + while(1){ + int nArg; + char **azArg, **azCol; + rc = sqlite_step(pVm, &nArg, (const char***)&azArg,(const char***)&azCol); + if( rc==SQLITE_ROW ){ + if( xCallback!=0 && xCallback(pArg, nArg, azArg, azCol) ){ + sqlite_finalize(pVm, 0); + return SQLITE_ABORT; + } + nCallback++; + }else{ + if( rc==SQLITE_DONE && nCallback==0 + && (db->flags & SQLITE_NullCallback)!=0 && xCallback!=0 ){ + xCallback(pArg, nArg, azArg, azCol); + } + rc = sqlite_finalize(pVm, pzErrMsg); + if( rc==SQLITE_SCHEMA && nRetry<2 ){ + nRetry++; + rc = SQLITE_OK; + break; + } + if( db->pVdbe==0 ){ + nChange = db->nChange; + } + nRetry = 0; + zSql = zLeftover; + while( isspace(zSql[0]) ) zSql++; + break; + } + } + } + return rc; +} + + +/* +** Compile a single statement of SQL into a virtual machine. Return one +** of the SQLITE_ success/failure codes. Also write an error message into +** memory obtained from malloc() and make *pzErrMsg point to that message. +*/ +int sqlite_compile( + sqlite *db, /* The database on which the SQL executes */ + const char *zSql, /* The SQL to be executed */ + const char **pzTail, /* OUT: Next statement after the first */ + sqlite_vm **ppVm, /* OUT: The virtual machine */ + char **pzErrMsg /* OUT: Write error messages here */ +){ + Parse sParse; + + if( pzErrMsg ) *pzErrMsg = 0; + if( sqliteSafetyOn(db) ) goto exec_misuse; + if( !db->init.busy ){ + if( (db->flags & SQLITE_Initialized)==0 ){ + int rc, cnt = 1; + while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY + && db->xBusyCallback + && db->xBusyCallback(db->pBusyArg, "", cnt++)!=0 ){} + if( rc!=SQLITE_OK ){ + sqliteStrRealloc(pzErrMsg); + sqliteSafetyOff(db); + return rc; + } + if( pzErrMsg ){ + sqliteFree(*pzErrMsg); + *pzErrMsg = 0; + } + } + if( db->file_format<3 ){ + sqliteSafetyOff(db); + sqliteSetString(pzErrMsg, "obsolete database file format", (char*)0); + return SQLITE_ERROR; + } + } + assert( (db->flags & SQLITE_Initialized)!=0 || db->init.busy ); + if( db->pVdbe==0 ){ db->nChange = 0; } + memset(&sParse, 0, sizeof(sParse)); + sParse.db = db; + sqliteRunParser(&sParse, zSql, pzErrMsg); + if( db->xTrace && !db->init.busy ){ + /* Trace only the statment that was compiled. + ** Make a copy of that part of the SQL string since zSQL is const + ** and we must pass a zero terminated string to the trace function + ** The copy is unnecessary if the tail pointer is pointing at the + ** beginnig or end of the SQL string. + */ + if( sParse.zTail && sParse.zTail!=zSql && *sParse.zTail ){ + char *tmpSql = sqliteStrNDup(zSql, sParse.zTail - zSql); + if( tmpSql ){ + db->xTrace(db->pTraceArg, tmpSql); + free(tmpSql); + }else{ + /* If a memory error occurred during the copy, + ** trace entire SQL string and fall through to the + ** sqlite_malloc_failed test to report the error. + */ + db->xTrace(db->pTraceArg, zSql); + } + }else{ + db->xTrace(db->pTraceArg, zSql); + } + } + if( sqlite_malloc_failed ){ + sqliteSetString(pzErrMsg, "out of memory", (char*)0); + sParse.rc = SQLITE_NOMEM; + sqliteRollbackAll(db); + sqliteResetInternalSchema(db, 0); + db->flags &= ~SQLITE_InTrans; + } + if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; + if( sParse.rc!=SQLITE_OK && pzErrMsg && *pzErrMsg==0 ){ + sqliteSetString(pzErrMsg, sqlite_error_string(sParse.rc), (char*)0); + } + sqliteStrRealloc(pzErrMsg); + if( sParse.rc==SQLITE_SCHEMA ){ + sqliteResetInternalSchema(db, 0); + } + assert( ppVm ); + *ppVm = (sqlite_vm*)sParse.pVdbe; + if( pzTail ) *pzTail = sParse.zTail; + if( sqliteSafetyOff(db) ) goto exec_misuse; + return sParse.rc; + +exec_misuse: + if( pzErrMsg ){ + *pzErrMsg = 0; + sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), (char*)0); + sqliteStrRealloc(pzErrMsg); + } + return SQLITE_MISUSE; +} + + +/* +** The following routine destroys a virtual machine that is created by +** the sqlite_compile() routine. +** +** The integer returned is an SQLITE_ success/failure code that describes +** the result of executing the virtual machine. An error message is +** written into memory obtained from malloc and *pzErrMsg is made to +** point to that error if pzErrMsg is not NULL. The calling routine +** should use sqlite_freemem() to delete the message when it has finished +** with it. +*/ +int sqlite_finalize( + sqlite_vm *pVm, /* The virtual machine to be destroyed */ + char **pzErrMsg /* OUT: Write error messages here */ +){ + int rc = sqliteVdbeFinalize((Vdbe*)pVm, pzErrMsg); + sqliteStrRealloc(pzErrMsg); + return rc; +} + +/* +** Terminate the current execution of a virtual machine then +** reset the virtual machine back to its starting state so that it +** can be reused. Any error message resulting from the prior execution +** is written into *pzErrMsg. A success code from the prior execution +** is returned. +*/ +int sqlite_reset( + sqlite_vm *pVm, /* The virtual machine to be destroyed */ + char **pzErrMsg /* OUT: Write error messages here */ +){ + int rc = sqliteVdbeReset((Vdbe*)pVm, pzErrMsg); + sqliteVdbeMakeReady((Vdbe*)pVm, -1, 0); + sqliteStrRealloc(pzErrMsg); + return rc; +} + +/* +** Return a static string that describes the kind of error specified in the +** argument. +*/ +const char *sqlite_error_string(int rc){ + const char *z; + switch( rc ){ + case SQLITE_OK: z = "not an error"; break; + case SQLITE_ERROR: z = "SQL logic error or missing database"; break; + case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break; + case SQLITE_PERM: z = "access permission denied"; break; + case SQLITE_ABORT: z = "callback requested query abort"; break; + case SQLITE_BUSY: z = "database is locked"; break; + case SQLITE_LOCKED: z = "database table is locked"; break; + case SQLITE_NOMEM: z = "out of memory"; break; + case SQLITE_READONLY: z = "attempt to write a readonly database"; break; + case SQLITE_INTERRUPT: z = "interrupted"; break; + case SQLITE_IOERR: z = "disk I/O error"; break; + case SQLITE_CORRUPT: z = "database disk image is malformed"; break; + case SQLITE_NOTFOUND: z = "table or record not found"; break; + case SQLITE_FULL: z = "database is full"; break; + case SQLITE_CANTOPEN: z = "unable to open database file"; break; + case SQLITE_PROTOCOL: z = "database locking protocol failure"; break; + case SQLITE_EMPTY: z = "table contains no data"; break; + case SQLITE_SCHEMA: z = "database schema has changed"; break; + case SQLITE_TOOBIG: z = "too much data for one table row"; break; + case SQLITE_CONSTRAINT: z = "constraint failed"; break; + case SQLITE_MISMATCH: z = "datatype mismatch"; break; + case SQLITE_MISUSE: z = "library routine called out of sequence";break; + case SQLITE_NOLFS: z = "kernel lacks large file support"; break; + case SQLITE_AUTH: z = "authorization denied"; break; + case SQLITE_FORMAT: z = "auxiliary database format error"; break; + case SQLITE_RANGE: z = "bind index out of range"; break; + case SQLITE_NOTADB: z = "file is encrypted or is not a database";break; + default: z = "unknown error"; break; + } + return z; +} + +/* +** This routine implements a busy callback that sleeps and tries +** again until a timeout value is reached. The timeout value is +** an integer number of milliseconds passed in as the first +** argument. +*/ +static int sqliteDefaultBusyCallback( + void *Timeout, /* Maximum amount of time to wait */ + const char *NotUsed, /* The name of the table that is busy */ + int count /* Number of times table has been busy */ +){ +#if SQLITE_MIN_SLEEP_MS==1 + static const char delays[] = + { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 50, 100}; + static const short int totals[] = + { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228, 287}; +# define NDELAY (sizeof(delays)/sizeof(delays[0])) + int timeout = (int)(long)Timeout; + int delay, prior; + + if( count <= NDELAY ){ + delay = delays[count-1]; + prior = totals[count-1]; + }else{ + delay = delays[NDELAY-1]; + prior = totals[NDELAY-1] + delay*(count-NDELAY-1); + } + if( prior + delay > timeout ){ + delay = timeout - prior; + if( delay<=0 ) return 0; + } + sqliteOsSleep(delay); + return 1; +#else + int timeout = (int)(long)Timeout; + if( (count+1)*1000 > timeout ){ + return 0; + } + sqliteOsSleep(1000); + return 1; +#endif +} + +/* +** This routine sets the busy callback for an Sqlite database to the +** given callback function with the given argument. +*/ +void sqlite_busy_handler( + sqlite *db, + int (*xBusy)(void*,const char*,int), + void *pArg +){ + db->xBusyCallback = xBusy; + db->pBusyArg = pArg; +} + +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK +/* +** This routine sets the progress callback for an Sqlite database to the +** given callback function with the given argument. The progress callback will +** be invoked every nOps opcodes. +*/ +void sqlite_progress_handler( + sqlite *db, + int nOps, + int (*xProgress)(void*), + void *pArg +){ + if( nOps>0 ){ + db->xProgress = xProgress; + db->nProgressOps = nOps; + db->pProgressArg = pArg; + }else{ + db->xProgress = 0; + db->nProgressOps = 0; + db->pProgressArg = 0; + } +} +#endif + + +/* +** This routine installs a default busy handler that waits for the +** specified number of milliseconds before returning 0. +*/ +void sqlite_busy_timeout(sqlite *db, int ms){ + if( ms>0 ){ + sqlite_busy_handler(db, sqliteDefaultBusyCallback, (void*)(long)ms); + }else{ + sqlite_busy_handler(db, 0, 0); + } +} + +/* +** Cause any pending operation to stop at its earliest opportunity. +*/ +void sqlite_interrupt(sqlite *db){ + db->flags |= SQLITE_Interrupt; +} + +/* +** Windows systems should call this routine to free memory that +** is returned in the in the errmsg parameter of sqlite_open() when +** SQLite is a DLL. For some reason, it does not work to call free() +** directly. +** +** Note that we need to call free() not sqliteFree() here, since every +** string that is exported from SQLite should have already passed through +** sqliteStrRealloc(). +*/ +void sqlite_freemem(void *p){ free(p); } + +/* +** Windows systems need functions to call to return the sqlite_version +** and sqlite_encoding strings since they are unable to access constants +** within DLLs. +*/ +const char *sqlite_libversion(void){ return sqlite_version; } +const char *sqlite_libencoding(void){ return sqlite_encoding; } + +/* +** Create new user-defined functions. The sqlite_create_function() +** routine creates a regular function and sqlite_create_aggregate() +** creates an aggregate function. +** +** Passing a NULL xFunc argument or NULL xStep and xFinalize arguments +** disables the function. Calling sqlite_create_function() with the +** same name and number of arguments as a prior call to +** sqlite_create_aggregate() disables the prior call to +** sqlite_create_aggregate(), and vice versa. +** +** If nArg is -1 it means that this function will accept any number +** of arguments, including 0. The maximum allowed value of nArg is 127. +*/ +int sqlite_create_function( + sqlite *db, /* Add the function to this database connection */ + const char *zName, /* Name of the function to add */ + int nArg, /* Number of arguments */ + void (*xFunc)(sqlite_func*,int,const char**), /* The implementation */ + void *pUserData /* User data */ +){ + FuncDef *p; + int nName; + if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1; + if( nArg<-1 || nArg>127 ) return 1; + nName = strlen(zName); + if( nName>255 ) return 1; + p = sqliteFindFunction(db, zName, nName, nArg, 1); + if( p==0 ) return 1; + p->xFunc = xFunc; + p->xStep = 0; + p->xFinalize = 0; + p->pUserData = pUserData; + return 0; +} +int sqlite_create_aggregate( + sqlite *db, /* Add the function to this database connection */ + const char *zName, /* Name of the function to add */ + int nArg, /* Number of arguments */ + void (*xStep)(sqlite_func*,int,const char**), /* The step function */ + void (*xFinalize)(sqlite_func*), /* The finalizer */ + void *pUserData /* User data */ +){ + FuncDef *p; + int nName; + if( db==0 || zName==0 || sqliteSafetyCheck(db) ) return 1; + if( nArg<-1 || nArg>127 ) return 1; + nName = strlen(zName); + if( nName>255 ) return 1; + p = sqliteFindFunction(db, zName, nName, nArg, 1); + if( p==0 ) return 1; + p->xFunc = 0; + p->xStep = xStep; + p->xFinalize = xFinalize; + p->pUserData = pUserData; + return 0; +} + +/* +** Change the datatype for all functions with a given name. See the +** header comment for the prototype of this function in sqlite.h for +** additional information. +*/ +int sqlite_function_type(sqlite *db, const char *zName, int dataType){ + FuncDef *p = (FuncDef*)sqliteHashFind(&db->aFunc, zName, strlen(zName)); + while( p ){ + p->dataType = dataType; + p = p->pNext; + } + return SQLITE_OK; +} + +/* +** Register a trace function. The pArg from the previously registered trace +** is returned. +** +** A NULL trace function means that no tracing is executes. A non-NULL +** trace is a pointer to a function that is invoked at the start of each +** sqlite_exec(). +*/ +void *sqlite_trace(sqlite *db, void (*xTrace)(void*,const char*), void *pArg){ + void *pOld = db->pTraceArg; + db->xTrace = xTrace; + db->pTraceArg = pArg; + return pOld; +} + +/*** EXPERIMENTAL *** +** +** Register a function to be invoked when a transaction comments. +** If either function returns non-zero, then the commit becomes a +** rollback. +*/ +void *sqlite_commit_hook( + sqlite *db, /* Attach the hook to this database */ + int (*xCallback)(void*), /* Function to invoke on each commit */ + void *pArg /* Argument to the function */ +){ + void *pOld = db->pCommitArg; + db->xCommitCallback = xCallback; + db->pCommitArg = pArg; + return pOld; +} + + +/* +** This routine is called to create a connection to a database BTree +** driver. If zFilename is the name of a file, then that file is +** opened and used. If zFilename is the magic name ":memory:" then +** the database is stored in memory (and is thus forgotten as soon as +** the connection is closed.) If zFilename is NULL then the database +** is for temporary use only and is deleted as soon as the connection +** is closed. +** +** A temporary database can be either a disk file (that is automatically +** deleted when the file is closed) or a set of red-black trees held in memory, +** depending on the values of the TEMP_STORE compile-time macro and the +** db->temp_store variable, according to the following chart: +** +** TEMP_STORE db->temp_store Location of temporary database +** ---------- -------------- ------------------------------ +** 0 any file +** 1 1 file +** 1 2 memory +** 1 0 file +** 2 1 file +** 2 2 memory +** 2 0 memory +** 3 any memory +*/ +int sqliteBtreeFactory( + const sqlite *db, /* Main database when opening aux otherwise 0 */ + const char *zFilename, /* Name of the file containing the BTree database */ + int omitJournal, /* if TRUE then do not journal this file */ + int nCache, /* How many pages in the page cache */ + Btree **ppBtree){ /* Pointer to new Btree object written here */ + + assert( ppBtree != 0); + +#ifndef SQLITE_OMIT_INMEMORYDB + if( zFilename==0 ){ + if (TEMP_STORE == 0) { + /* Always use file based temporary DB */ + return sqliteBtreeOpen(0, omitJournal, nCache, ppBtree); + } else if (TEMP_STORE == 1 || TEMP_STORE == 2) { + /* Switch depending on compile-time and/or runtime settings. */ + int location = db->temp_store==0 ? TEMP_STORE : db->temp_store; + + if (location == 1) { + return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree); + } else { + return sqliteRbtreeOpen(0, 0, 0, ppBtree); + } + } else { + /* Always use in-core DB */ + return sqliteRbtreeOpen(0, 0, 0, ppBtree); + } + }else if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){ + return sqliteRbtreeOpen(0, 0, 0, ppBtree); + }else +#endif + { + return sqliteBtreeOpen(zFilename, omitJournal, nCache, ppBtree); + } +} diff --git a/src/libs/sqlite2/opcodes.c b/src/libs/sqlite2/opcodes.c new file mode 100644 index 00000000..0907e0e7 --- /dev/null +++ b/src/libs/sqlite2/opcodes.c @@ -0,0 +1,140 @@ +/* Automatically generated file. Do not edit */ +char *sqliteOpcodeNames[] = { "???", + "Goto", + "Gosub", + "Return", + "Halt", + "Integer", + "String", + "Variable", + "Pop", + "Dup", + "Pull", + "Push", + "ColumnName", + "Callback", + "Concat", + "Add", + "Subtract", + "Multiply", + "Divide", + "Remainder", + "Function", + "BitAnd", + "BitOr", + "ShiftLeft", + "ShiftRight", + "AddImm", + "ForceInt", + "MustBeInt", + "Eq", + "Ne", + "Lt", + "Le", + "Gt", + "Ge", + "StrEq", + "StrNe", + "StrLt", + "StrLe", + "StrGt", + "StrGe", + "And", + "Or", + "Negative", + "AbsValue", + "Not", + "BitNot", + "Noop", + "If", + "IfNot", + "IsNull", + "NotNull", + "MakeRecord", + "MakeIdxKey", + "MakeKey", + "IncrKey", + "Checkpoint", + "Transaction", + "Commit", + "Rollback", + "ReadCookie", + "SetCookie", + "VerifyCookie", + "OpenRead", + "OpenWrite", + "OpenTemp", + "OpenPseudo", + "Close", + "MoveLt", + "MoveTo", + "Distinct", + "NotFound", + "Found", + "IsUnique", + "NotExists", + "NewRecno", + "PutIntKey", + "PutStrKey", + "Delete", + "SetCounts", + "KeyAsData", + "RowKey", + "RowData", + "Column", + "Recno", + "FullKey", + "NullRow", + "Last", + "Rewind", + "Prev", + "Next", + "IdxPut", + "IdxDelete", + "IdxRecno", + "IdxLT", + "IdxGT", + "IdxGE", + "IdxIsNull", + "Destroy", + "Clear", + "CreateIndex", + "CreateTable", + "IntegrityCk", + "ListWrite", + "ListRewind", + "ListRead", + "ListReset", + "ListPush", + "ListPop", + "ContextPush", + "ContextPop", + "SortPut", + "SortMakeRec", + "SortMakeKey", + "Sort", + "SortNext", + "SortCallback", + "SortReset", + "FileOpen", + "FileRead", + "FileColumn", + "MemStore", + "MemLoad", + "MemIncr", + "AggReset", + "AggInit", + "AggFunc", + "AggFocus", + "AggSet", + "AggGet", + "AggNext", + "SetInsert", + "SetFound", + "SetNotFound", + "SetFirst", + "SetNext", + "Vacuum", + "StackDepth", + "StackReset", +}; diff --git a/src/libs/sqlite2/opcodes.h b/src/libs/sqlite2/opcodes.h new file mode 100644 index 00000000..35e05069 --- /dev/null +++ b/src/libs/sqlite2/opcodes.h @@ -0,0 +1,138 @@ +/* Automatically generated file. Do not edit */ +#define OP_Goto 1 +#define OP_Gosub 2 +#define OP_Return 3 +#define OP_Halt 4 +#define OP_Integer 5 +#define OP_String 6 +#define OP_Variable 7 +#define OP_Pop 8 +#define OP_Dup 9 +#define OP_Pull 10 +#define OP_Push 11 +#define OP_ColumnName 12 +#define OP_Callback 13 +#define OP_Concat 14 +#define OP_Add 15 +#define OP_Subtract 16 +#define OP_Multiply 17 +#define OP_Divide 18 +#define OP_Remainder 19 +#define OP_Function 20 +#define OP_BitAnd 21 +#define OP_BitOr 22 +#define OP_ShiftLeft 23 +#define OP_ShiftRight 24 +#define OP_AddImm 25 +#define OP_ForceInt 26 +#define OP_MustBeInt 27 +#define OP_Eq 28 +#define OP_Ne 29 +#define OP_Lt 30 +#define OP_Le 31 +#define OP_Gt 32 +#define OP_Ge 33 +#define OP_StrEq 34 +#define OP_StrNe 35 +#define OP_StrLt 36 +#define OP_StrLe 37 +#define OP_StrGt 38 +#define OP_StrGe 39 +#define OP_And 40 +#define OP_Or 41 +#define OP_Negative 42 +#define OP_AbsValue 43 +#define OP_Not 44 +#define OP_BitNot 45 +#define OP_Noop 46 +#define OP_If 47 +#define OP_IfNot 48 +#define OP_IsNull 49 +#define OP_NotNull 50 +#define OP_MakeRecord 51 +#define OP_MakeIdxKey 52 +#define OP_MakeKey 53 +#define OP_IncrKey 54 +#define OP_Checkpoint 55 +#define OP_Transaction 56 +#define OP_Commit 57 +#define OP_Rollback 58 +#define OP_ReadCookie 59 +#define OP_SetCookie 60 +#define OP_VerifyCookie 61 +#define OP_OpenRead 62 +#define OP_OpenWrite 63 +#define OP_OpenTemp 64 +#define OP_OpenPseudo 65 +#define OP_Close 66 +#define OP_MoveLt 67 +#define OP_MoveTo 68 +#define OP_Distinct 69 +#define OP_NotFound 70 +#define OP_Found 71 +#define OP_IsUnique 72 +#define OP_NotExists 73 +#define OP_NewRecno 74 +#define OP_PutIntKey 75 +#define OP_PutStrKey 76 +#define OP_Delete 77 +#define OP_SetCounts 78 +#define OP_KeyAsData 79 +#define OP_RowKey 80 +#define OP_RowData 81 +#define OP_Column 82 +#define OP_Recno 83 +#define OP_FullKey 84 +#define OP_NullRow 85 +#define OP_Last 86 +#define OP_Rewind 87 +#define OP_Prev 88 +#define OP_Next 89 +#define OP_IdxPut 90 +#define OP_IdxDelete 91 +#define OP_IdxRecno 92 +#define OP_IdxLT 93 +#define OP_IdxGT 94 +#define OP_IdxGE 95 +#define OP_IdxIsNull 96 +#define OP_Destroy 97 +#define OP_Clear 98 +#define OP_CreateIndex 99 +#define OP_CreateTable 100 +#define OP_IntegrityCk 101 +#define OP_ListWrite 102 +#define OP_ListRewind 103 +#define OP_ListRead 104 +#define OP_ListReset 105 +#define OP_ListPush 106 +#define OP_ListPop 107 +#define OP_ContextPush 108 +#define OP_ContextPop 109 +#define OP_SortPut 110 +#define OP_SortMakeRec 111 +#define OP_SortMakeKey 112 +#define OP_Sort 113 +#define OP_SortNext 114 +#define OP_SortCallback 115 +#define OP_SortReset 116 +#define OP_FileOpen 117 +#define OP_FileRead 118 +#define OP_FileColumn 119 +#define OP_MemStore 120 +#define OP_MemLoad 121 +#define OP_MemIncr 122 +#define OP_AggReset 123 +#define OP_AggInit 124 +#define OP_AggFunc 125 +#define OP_AggFocus 126 +#define OP_AggSet 127 +#define OP_AggGet 128 +#define OP_AggNext 129 +#define OP_SetInsert 130 +#define OP_SetFound 131 +#define OP_SetNotFound 132 +#define OP_SetFirst 133 +#define OP_SetNext 134 +#define OP_Vacuum 135 +#define OP_StackDepth 136 +#define OP_StackReset 137 diff --git a/src/libs/sqlite2/os.c b/src/libs/sqlite2/os.c new file mode 100644 index 00000000..dccd65f1 --- /dev/null +++ b/src/libs/sqlite2/os.c @@ -0,0 +1,1850 @@ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to particular operating +** systems. The purpose of this file is to provide a uniform abstraction +** on which the rest of SQLite can operate. +*/ +#include "os.h" /* Must be first to enable large file support */ +#include "sqliteInt.h" + +#if OS_UNIX +# include +# include +# include +# ifndef O_LARGEFILE +# define O_LARGEFILE 0 +# endif +# ifdef SQLITE_DISABLE_LFS +# undef O_LARGEFILE +# define O_LARGEFILE 0 +# endif +# ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +# endif +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + + +#if OS_WIN +# include +#endif + +#if OS_MAC +# include +# include +# include +# include +# include +# include +# include +#endif + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call. So redefine fcntl() to be something +** that always succeeds. This means that locking does not occur under +** DJGPP. But its DOS - what did you expect? +*/ +#ifdef __DJGPP__ +# define fcntl(A,B,C) 0 +#endif + +/* +** Macros used to determine whether or not to use threads. The +** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for +** Posix threads and SQLITE_W32_THREADS is defined if we are +** synchronizing using Win32 threads. +*/ +#if OS_UNIX && defined(THREADSAFE) && THREADSAFE +# include +# define SQLITE_UNIX_THREADS 1 +#endif +#if OS_WIN && defined(THREADSAFE) && THREADSAFE +# define SQLITE_W32_THREADS 1 +#endif +#if OS_MAC && defined(THREADSAFE) && THREADSAFE +# include +# define SQLITE_MACOS_MULTITASKING 1 +#endif + +/* +** Macros for performance tracing. Normally turned off +*/ +#if 0 +static int last_page = 0; +__inline__ unsigned long long int hwtime(void){ + unsigned long long int x; + __asm__("rdtsc\n\t" + "mov %%edx, %%ecx\n\t" + :"=A" (x)); + return x; +} +static unsigned long long int g_start; +static unsigned int elapse; +#define TIMER_START g_start=hwtime() +#define TIMER_END elapse=hwtime()-g_start +#define SEEK(X) last_page=(X) +#define TRACE1(X) fprintf(stderr,X) +#define TRACE2(X,Y) fprintf(stderr,X,Y) +#define TRACE3(X,Y,Z) fprintf(stderr,X,Y,Z) +#define TRACE4(X,Y,Z,A) fprintf(stderr,X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) fprintf(stderr,X,Y,Z,A,B) +#else +#define TIMER_START +#define TIMER_END +#define SEEK(X) +#define TRACE1(X) +#define TRACE2(X,Y) +#define TRACE3(X,Y,Z) +#define TRACE4(X,Y,Z,A) +#define TRACE5(X,Y,Z,A,B) +#endif + + +#if OS_UNIX +/* +** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996) +** section 6.5.2.2 lines 483 through 490 specify that when a process +** sets or clears a lock, that operation overrides any prior locks set +** by the same process. It does not explicitly say so, but this implies +** that it overrides locks set by the same process using a different +** file descriptor. Consider this test case: +** +** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); +** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); +** +** Suppose ./file1 and ./file2 are really the same file (because +** one is a hard or symbolic link to the other) then if you set +** an exclusive lock on fd1, then try to get an exclusive lock +** on fd2, it works. I would have expected the second lock to +** fail since there was already a lock on the file due to fd1. +** But not so. Since both locks came from the same process, the +** second overrides the first, even though they were on different +** file descriptors opened on different file names. +** +** Bummer. If you ask me, this is broken. Badly broken. It means +** that we cannot use POSIX locks to synchronize file access among +** competing threads of the same process. POSIX locks will work fine +** to synchronize access for threads in separate processes, but not +** threads within the same process. +** +** To work around the problem, SQLite has to manage file locks internally +** on its own. Whenever a new database is opened, we have to find the +** specific inode of the database file (the inode is determined by the +** st_dev and st_ino fields of the stat structure that fstat() fills in) +** and check for locks already existing on that inode. When locks are +** created or removed, we have to look at our own internal record of the +** locks to see if another thread has previously set a lock on that same +** inode. +** +** The OsFile structure for POSIX is no longer just an integer file +** descriptor. It is now a structure that holds the integer file +** descriptor and a pointer to a structure that describes the internal +** locks on the corresponding inode. There is one locking structure +** per inode, so if the same inode is opened twice, both OsFile structures +** point to the same locking structure. The locking structure keeps +** a reference count (so we will know when to delete it) and a "cnt" +** field that tells us its internal lock status. cnt==0 means the +** file is unlocked. cnt==-1 means the file has an exclusive lock. +** cnt>0 means there are cnt shared locks on the file. +** +** Any attempt to lock or unlock a file first checks the locking +** structure. The fcntl() system call is only invoked to set a +** POSIX lock if the internal lock structure transitions between +** a locked and an unlocked state. +** +** 2004-Jan-11: +** More recent discoveries about POSIX advisory locks. (The more +** I discover, the more I realize the a POSIX advisory locks are +** an abomination.) +** +** If you close a file descriptor that points to a file that has locks, +** all locks on that file that are owned by the current process are +** released. To work around this problem, each OsFile structure contains +** a pointer to an openCnt structure. There is one openCnt structure +** per open inode, which means that multiple OsFiles can point to a single +** openCnt. When an attempt is made to close an OsFile, if there are +** other OsFiles open on the same inode that are holding locks, the call +** to close() the file descriptor is deferred until all of the locks clear. +** The openCnt structure keeps a list of file descriptors that need to +** be closed and that list is walked (and cleared) when the last lock +** clears. +** +** First, under Linux threads, because each thread has a separate +** process ID, lock operations in one thread do not override locks +** to the same file in other threads. Linux threads behave like +** separate processes in this respect. But, if you close a file +** descriptor in linux threads, all locks are cleared, even locks +** on other threads and even though the other threads have different +** process IDs. Linux threads is inconsistent in this respect. +** (I'm beginning to think that linux threads is an abomination too.) +** The consequence of this all is that the hash table for the lockInfo +** structure has to include the process id as part of its key because +** locks in different threads are treated as distinct. But the +** openCnt structure should not include the process id in its +** key because close() clears lock on all threads, not just the current +** thread. Were it not for this goofiness in linux threads, we could +** combine the lockInfo and openCnt structures into a single structure. +*/ + +/* +** An instance of the following structure serves as the key used +** to locate a particular lockInfo structure given its inode. Note +** that we have to include the process ID as part of the key. On some +** threading implementations (ex: linux), each thread has a separate +** process ID. +*/ +struct lockKey { + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ + pid_t pid; /* Process ID */ +}; + +/* +** An instance of the following structure is allocated for each open +** inode on each thread with a different process ID. (Threads have +** different process IDs on linux, but not on most other unixes.) +** +** A single inode can have multiple file descriptors, so each OsFile +** structure contains a pointer to an instance of this object and this +** object keeps a count of the number of OsFiles pointing to it. +*/ +struct lockInfo { + struct lockKey key; /* The lookup key */ + int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */ + int nRef; /* Number of pointers to this structure */ +}; + +/* +** An instance of the following structure serves as the key used +** to locate a particular openCnt structure given its inode. This +** is the same as the lockKey except that the process ID is omitted. +*/ +struct openKey { + dev_t dev; /* Device number */ + ino_t ino; /* Inode number */ +}; + +/* +** An instance of the following structure is allocated for each open +** inode. This structure keeps track of the number of locks on that +** inode. If a close is attempted against an inode that is holding +** locks, the close is deferred until all locks clear by adding the +** file descriptor to be closed to the pending list. +*/ +struct openCnt { + struct openKey key; /* The lookup key */ + int nRef; /* Number of pointers to this structure */ + int nLock; /* Number of outstanding locks */ + int nPending; /* Number of pending close() operations */ + int *aPending; /* Malloced space holding fd's awaiting a close() */ +}; + +/* +** These hash table maps inodes and process IDs into lockInfo and openCnt +** structures. Access to these hash tables must be protected by a mutex. +*/ +static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; +static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 }; + +/* +** Release a lockInfo structure previously allocated by findLockInfo(). +*/ +static void releaseLockInfo(struct lockInfo *pLock){ + pLock->nRef--; + if( pLock->nRef==0 ){ + sqliteHashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0); + sqliteFree(pLock); + } +} + +/* +** Release a openCnt structure previously allocated by findLockInfo(). +*/ +static void releaseOpenCnt(struct openCnt *pOpen){ + pOpen->nRef--; + if( pOpen->nRef==0 ){ + sqliteHashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); + sqliteFree(pOpen->aPending); + sqliteFree(pOpen); + } +} + +/* +** Given a file descriptor, locate lockInfo and openCnt structures that +** describes that file descriptor. Create a new ones if necessary. The +** return values might be unset if an error occurs. +** +** Return the number of errors. +*/ +int findLockInfo( + int fd, /* The file descriptor used in the key */ + struct lockInfo **ppLock, /* Return the lockInfo structure here */ + struct openCnt **ppOpen /* Return the openCnt structure here */ +){ + int rc; + struct lockKey key1; + struct openKey key2; + struct stat statbuf; + struct lockInfo *pLock; + struct openCnt *pOpen; + rc = fstat(fd, &statbuf); + if( rc!=0 ) return 1; + memset(&key1, 0, sizeof(key1)); + key1.dev = statbuf.st_dev; + key1.ino = statbuf.st_ino; + key1.pid = getpid(); + memset(&key2, 0, sizeof(key2)); + key2.dev = statbuf.st_dev; + key2.ino = statbuf.st_ino; + pLock = (struct lockInfo*)sqliteHashFind(&lockHash, &key1, sizeof(key1)); + if( pLock==0 ){ + struct lockInfo *pOld; + pLock = sqliteMallocRaw( sizeof(*pLock) ); + if( pLock==0 ) return 1; + pLock->key = key1; + pLock->nRef = 1; + pLock->cnt = 0; + pOld = sqliteHashInsert(&lockHash, &pLock->key, sizeof(key1), pLock); + if( pOld!=0 ){ + assert( pOld==pLock ); + sqliteFree(pLock); + return 1; + } + }else{ + pLock->nRef++; + } + *ppLock = pLock; + pOpen = (struct openCnt*)sqliteHashFind(&openHash, &key2, sizeof(key2)); + if( pOpen==0 ){ + struct openCnt *pOld; + pOpen = sqliteMallocRaw( sizeof(*pOpen) ); + if( pOpen==0 ){ + releaseLockInfo(pLock); + return 1; + } + pOpen->key = key2; + pOpen->nRef = 1; + pOpen->nLock = 0; + pOpen->nPending = 0; + pOpen->aPending = 0; + pOld = sqliteHashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen); + if( pOld!=0 ){ + assert( pOld==pOpen ); + sqliteFree(pOpen); + releaseLockInfo(pLock); + return 1; + } + }else{ + pOpen->nRef++; + } + *ppOpen = pOpen; + return 0; +} + +#endif /** POSIX advisory lock work-around **/ + +/* +** If we compile with the SQLITE_TEST macro set, then the following block +** of code will give us the ability to simulate a disk I/O error. This +** is used for testing the I/O recovery logic. +*/ +#ifdef SQLITE_TEST +int sqlite_io_error_pending = 0; +#define SimulateIOError(A) \ + if( sqlite_io_error_pending ) \ + if( sqlite_io_error_pending-- == 1 ){ local_ioerr(); return A; } +static void local_ioerr(){ + sqlite_io_error_pending = 0; /* Really just a place to set a breakpoint */ +} +#else +#define SimulateIOError(A) +#endif + +/* +** When testing, keep a count of the number of open files. +*/ +#ifdef SQLITE_TEST +int sqlite_open_file_count = 0; +#define OpenCounter(X) sqlite_open_file_count+=(X) +#else +#define OpenCounter(X) +#endif + + +/* +** Delete the named file +*/ +int sqliteOsDelete(const char *zFilename){ +#if OS_UNIX + unlink(zFilename); +#endif +#if OS_WIN + DeleteFile(zFilename); +#endif +#if OS_MAC + unlink(zFilename); +#endif + return SQLITE_OK; +} + +/* +** Return TRUE if the named file exists. +*/ +int sqliteOsFileExists(const char *zFilename){ +#if OS_UNIX + return access(zFilename, 0)==0; +#endif +#if OS_WIN + return GetFileAttributes(zFilename) != 0xffffffff; +#endif +#if OS_MAC + return access(zFilename, 0)==0; +#endif +} + + +#if 0 /* NOT USED */ +/* +** Change the name of an existing file. +*/ +int sqliteOsFileRename(const char *zOldName, const char *zNewName){ +#if OS_UNIX + if( link(zOldName, zNewName) ){ + return SQLITE_ERROR; + } + unlink(zOldName); + return SQLITE_OK; +#endif +#if OS_WIN + if( !MoveFile(zOldName, zNewName) ){ + return SQLITE_ERROR; + } + return SQLITE_OK; +#endif +#if OS_MAC + /**** FIX ME ***/ + return SQLITE_ERROR; +#endif +} +#endif /* NOT USED */ + +/* +** Attempt to open a file for both reading and writing. If that +** fails, try opening it read-only. If the file does not exist, +** try to create it. +** +** On success, a handle for the open file is written to *id +** and *pReadonly is set to 0 if the file was opened for reading and +** writing or 1 if the file was opened read-only. The function returns +** SQLITE_OK. +** +** On failure, the function returns SQLITE_CANTOPEN and leaves +** *id and *pReadonly unchanged. +*/ +int sqliteOsOpenReadWrite( + const char *zFilename, + OsFile *id, + int *pReadonly +){ +#if OS_UNIX + int rc; + id->dirfd = -1; + id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644); + if( id->fd<0 ){ +#ifdef EISDIR + if( errno==EISDIR ){ + return SQLITE_CANTOPEN; + } +#endif + id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); + if( id->fd<0 ){ + return SQLITE_CANTOPEN; + } + *pReadonly = 1; + }else{ + *pReadonly = 0; + } + sqliteOsEnterMutex(); + rc = findLockInfo(id->fd, &id->pLock, &id->pOpen); + sqliteOsLeaveMutex(); + if( rc ){ + close(id->fd); + return SQLITE_NOMEM; + } + id->locked = 0; + TRACE3("OPEN %-3d %s\n", id->fd, zFilename); + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_WIN + HANDLE h = CreateFile(zFilename, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + NULL + ); + if( h==INVALID_HANDLE_VALUE ){ + h = CreateFile(zFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + NULL + ); + if( h==INVALID_HANDLE_VALUE ){ + return SQLITE_CANTOPEN; + } + *pReadonly = 1; + }else{ + *pReadonly = 0; + } + id->h = h; + id->locked = 0; + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_MAC + FSSpec fsSpec; +# ifdef _LARGE_FILE + HFSUniStr255 dfName; + FSRef fsRef; + if( __path2fss(zFilename, &fsSpec) != noErr ){ + if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) + return SQLITE_CANTOPEN; + } + if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) + return SQLITE_CANTOPEN; + FSGetDataForkName(&dfName); + if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, + fsRdWrShPerm, &(id->refNum)) != noErr ){ + if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, + fsRdWrPerm, &(id->refNum)) != noErr ){ + if (FSOpenFork(&fsRef, dfName.length, dfName.unicode, + fsRdPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; + else + *pReadonly = 1; + } else + *pReadonly = 0; + } else + *pReadonly = 0; +# else + __path2fss(zFilename, &fsSpec); + if( !sqliteOsFileExists(zFilename) ){ + if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) + return SQLITE_CANTOPEN; + } + if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){ + if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){ + if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; + else + *pReadonly = 1; + } else + *pReadonly = 0; + } else + *pReadonly = 0; +# endif + if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ + id->refNumRF = -1; + } + id->locked = 0; + id->delOnClose = 0; + OpenCounter(+1); + return SQLITE_OK; +#endif +} + + +/* +** Attempt to open a new file for exclusive access by this process. +** The file will be opened for both reading and writing. To avoid +** a potential security problem, we do not allow the file to have +** previously existed. Nor do we allow the file to be a symbolic +** link. +** +** If delFlag is true, then make arrangements to automatically delete +** the file when it is closed. +** +** On success, write the file handle into *id and return SQLITE_OK. +** +** On failure, return SQLITE_CANTOPEN. +*/ +int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +#if OS_UNIX + int rc; + if( access(zFilename, 0)==0 ){ + return SQLITE_CANTOPEN; + } + id->dirfd = -1; + id->fd = open(zFilename, + O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600); + if( id->fd<0 ){ + return SQLITE_CANTOPEN; + } + sqliteOsEnterMutex(); + rc = findLockInfo(id->fd, &id->pLock, &id->pOpen); + sqliteOsLeaveMutex(); + if( rc ){ + close(id->fd); + unlink(zFilename); + return SQLITE_NOMEM; + } + id->locked = 0; + if( delFlag ){ + unlink(zFilename); + } + TRACE3("OPEN-EX %-3d %s\n", id->fd, zFilename); + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_WIN + HANDLE h; + int fileflags; + if( delFlag ){ + fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS + | FILE_FLAG_DELETE_ON_CLOSE; + }else{ + fileflags = FILE_FLAG_RANDOM_ACCESS; + } + h = CreateFile(zFilename, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + fileflags, + NULL + ); + if( h==INVALID_HANDLE_VALUE ){ + return SQLITE_CANTOPEN; + } + id->h = h; + id->locked = 0; + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_MAC + FSSpec fsSpec; +# ifdef _LARGE_FILE + HFSUniStr255 dfName; + FSRef fsRef; + __path2fss(zFilename, &fsSpec); + if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) + return SQLITE_CANTOPEN; + if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) + return SQLITE_CANTOPEN; + FSGetDataForkName(&dfName); + if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, + fsRdWrPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; +# else + __path2fss(zFilename, &fsSpec); + if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr ) + return SQLITE_CANTOPEN; + if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; +# endif + id->refNumRF = -1; + id->locked = 0; + id->delOnClose = delFlag; + if (delFlag) + id->pathToDel = sqliteOsFullPathname(zFilename); + OpenCounter(+1); + return SQLITE_OK; +#endif +} + +/* +** Attempt to open a new file for read-only access. +** +** On success, write the file handle into *id and return SQLITE_OK. +** +** On failure, return SQLITE_CANTOPEN. +*/ +int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){ +#if OS_UNIX + int rc; + id->dirfd = -1; + id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); + if( id->fd<0 ){ + return SQLITE_CANTOPEN; + } + sqliteOsEnterMutex(); + rc = findLockInfo(id->fd, &id->pLock, &id->pOpen); + sqliteOsLeaveMutex(); + if( rc ){ + close(id->fd); + return SQLITE_NOMEM; + } + id->locked = 0; + TRACE3("OPEN-RO %-3d %s\n", id->fd, zFilename); + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_WIN + HANDLE h = CreateFile(zFilename, + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, + NULL + ); + if( h==INVALID_HANDLE_VALUE ){ + return SQLITE_CANTOPEN; + } + id->h = h; + id->locked = 0; + OpenCounter(+1); + return SQLITE_OK; +#endif +#if OS_MAC + FSSpec fsSpec; +# ifdef _LARGE_FILE + HFSUniStr255 dfName; + FSRef fsRef; + if( __path2fss(zFilename, &fsSpec) != noErr ) + return SQLITE_CANTOPEN; + if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr ) + return SQLITE_CANTOPEN; + FSGetDataForkName(&dfName); + if( FSOpenFork(&fsRef, dfName.length, dfName.unicode, + fsRdPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; +# else + __path2fss(zFilename, &fsSpec); + if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr ) + return SQLITE_CANTOPEN; +# endif + if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){ + id->refNumRF = -1; + } + id->locked = 0; + id->delOnClose = 0; + OpenCounter(+1); + return SQLITE_OK; +#endif +} + +/* +** Attempt to open a file descriptor for the directory that contains a +** file. This file descriptor can be used to fsync() the directory +** in order to make sure the creation of a new file is actually written +** to disk. +** +** This routine is only meaningful for Unix. It is a no-op under +** windows since windows does not support hard links. +** +** On success, a handle for a previously open file is at *id is +** updated with the new directory file descriptor and SQLITE_OK is +** returned. +** +** On failure, the function returns SQLITE_CANTOPEN and leaves +** *id unchanged. +*/ +int sqliteOsOpenDirectory( + const char *zDirname, + OsFile *id +){ +#if OS_UNIX + if( id->fd<0 ){ + /* Do not open the directory if the corresponding file is not already + ** open. */ + return SQLITE_CANTOPEN; + } + assert( id->dirfd<0 ); + id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644); + if( id->dirfd<0 ){ + return SQLITE_CANTOPEN; + } + TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname); +#endif + return SQLITE_OK; +} + +/* +** If the following global variable points to a string which is the +** name of a directory, then that directory will be used to store +** temporary files. +*/ +const char *sqlite_temp_directory = 0; + +/* +** Create a temporary file name in zBuf. zBuf must be big enough to +** hold at least SQLITE_TEMPNAME_SIZE characters. +*/ +int sqliteOsTempFileName(char *zBuf){ +#if OS_UNIX + static const char *azDirs[] = { + 0, + "/var/tmp", + "/usr/tmp", + "/tmp", + ".", + }; + static unsigned char zChars[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + int i, j; + struct stat buf; + const char *zDir = "."; + azDirs[0] = sqlite_temp_directory; + for(i=0; i0 && zTempPath[i-1]=='\\'; i--){} + zTempPath[i] = 0; + zDir = zTempPath; + }else{ + zDir = sqlite_temp_directory; + } + for(;;){ + sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zDir); + j = strlen(zBuf); + sqliteRandomness(15, &zBuf[j]); + for(i=0; i<15; i++, j++){ + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + } + zBuf[j] = 0; + if( !sqliteOsFileExists(zBuf) ) break; + } +#endif +#if OS_MAC + static char zChars[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + int i, j; + char *zDir; + char zTempPath[SQLITE_TEMPNAME_SIZE]; + char zdirName[32]; + CInfoPBRec infoRec; + Str31 dirName; + memset(&infoRec, 0, sizeof(infoRec)); + memset(zTempPath, 0, SQLITE_TEMPNAME_SIZE); + if( sqlite_temp_directory!=0 ){ + zDir = sqlite_temp_directory; + }else if( FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, + &(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){ + infoRec.dirInfo.ioNamePtr = dirName; + do{ + infoRec.dirInfo.ioFDirIndex = -1; + infoRec.dirInfo.ioDrDirID = infoRec.dirInfo.ioDrParID; + if( PBGetCatInfoSync(&infoRec) == noErr ){ + CopyPascalStringToC(dirName, zdirName); + i = strlen(zdirName); + memmove(&(zTempPath[i+1]), zTempPath, strlen(zTempPath)); + strcpy(zTempPath, zdirName); + zTempPath[i] = ':'; + }else{ + *zTempPath = 0; + break; + } + } while( infoRec.dirInfo.ioDrDirID != fsRtDirID ); + zDir = zTempPath; + } + if( zDir[0]==0 ){ + getcwd(zTempPath, SQLITE_TEMPNAME_SIZE-24); + zDir = zTempPath; + } + for(;;){ + sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zDir); + j = strlen(zBuf); + sqliteRandomness(15, &zBuf[j]); + for(i=0; i<15; i++, j++){ + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + } + zBuf[j] = 0; + if( !sqliteOsFileExists(zBuf) ) break; + } +#endif + return SQLITE_OK; +} + +/* +** Close a file. +*/ +int sqliteOsClose(OsFile *id){ +#if OS_UNIX + sqliteOsUnlock(id); + if( id->dirfd>=0 ) close(id->dirfd); + id->dirfd = -1; + sqliteOsEnterMutex(); + if( id->pOpen->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pOpen->aPending. It will be automatically closed when + ** the last lock is cleared. + */ + int *aNew; + struct openCnt *pOpen = id->pOpen; + pOpen->nPending++; + aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) ); + if( aNew==0 ){ + /* If a malloc fails, just leak the file descriptor */ + }else{ + pOpen->aPending = aNew; + pOpen->aPending[pOpen->nPending-1] = id->fd; + } + }else{ + /* There are no outstanding locks so we can close the file immediately */ + close(id->fd); + } + releaseLockInfo(id->pLock); + releaseOpenCnt(id->pOpen); + sqliteOsLeaveMutex(); + TRACE2("CLOSE %-3d\n", id->fd); + OpenCounter(-1); + return SQLITE_OK; +#endif +#if OS_WIN + CloseHandle(id->h); + OpenCounter(-1); + return SQLITE_OK; +#endif +#if OS_MAC + if( id->refNumRF!=-1 ) + FSClose(id->refNumRF); +# ifdef _LARGE_FILE + FSCloseFork(id->refNum); +# else + FSClose(id->refNum); +# endif + if( id->delOnClose ){ + unlink(id->pathToDel); + sqliteFree(id->pathToDel); + } + OpenCounter(-1); + return SQLITE_OK; +#endif +} + +/* +** Read data from a file into a buffer. Return SQLITE_OK if all +** bytes were read successfully and SQLITE_IOERR if anything goes +** wrong. +*/ +int sqliteOsRead(OsFile *id, void *pBuf, int amt){ +#if OS_UNIX + int got; + SimulateIOError(SQLITE_IOERR); + TIMER_START; + got = read(id->fd, pBuf, amt); + TIMER_END; + TRACE4("READ %-3d %7d %d\n", id->fd, last_page, elapse); + SEEK(0); + /* if( got<0 ) got = 0; */ + if( got==amt ){ + return SQLITE_OK; + }else{ + return SQLITE_IOERR; + } +#endif +#if OS_WIN + DWORD got; + SimulateIOError(SQLITE_IOERR); + TRACE2("READ %d\n", last_page); + if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ + got = 0; + } + if( got==(DWORD)amt ){ + return SQLITE_OK; + }else{ + return SQLITE_IOERR; + } +#endif +#if OS_MAC + int got; + SimulateIOError(SQLITE_IOERR); + TRACE2("READ %d\n", last_page); +# ifdef _LARGE_FILE + FSReadFork(id->refNum, fsAtMark, 0, (ByteCount)amt, pBuf, (ByteCount*)&got); +# else + got = amt; + FSRead(id->refNum, &got, pBuf); +# endif + if( got==amt ){ + return SQLITE_OK; + }else{ + return SQLITE_IOERR; + } +#endif +} + +/* +** Write data from a buffer into a file. Return SQLITE_OK on success +** or some other error code on failure. +*/ +int sqliteOsWrite(OsFile *id, const void *pBuf, int amt){ +#if OS_UNIX + int wrote = 0; + SimulateIOError(SQLITE_IOERR); + TIMER_START; + while( amt>0 && (wrote = write(id->fd, pBuf, amt))>0 ){ + amt -= wrote; + pBuf = &((char*)pBuf)[wrote]; + } + TIMER_END; + TRACE4("WRITE %-3d %7d %d\n", id->fd, last_page, elapse); + SEEK(0); + if( amt>0 ){ + return SQLITE_FULL; + } + return SQLITE_OK; +#endif +#if OS_WIN + int rc; + DWORD wrote; + SimulateIOError(SQLITE_IOERR); + TRACE2("WRITE %d\n", last_page); + while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ + amt -= wrote; + pBuf = &((char*)pBuf)[wrote]; + } + if( !rc || amt>(int)wrote ){ + return SQLITE_FULL; + } + return SQLITE_OK; +#endif +#if OS_MAC + OSErr oserr; + int wrote = 0; + SimulateIOError(SQLITE_IOERR); + TRACE2("WRITE %d\n", last_page); + while( amt>0 ){ +# ifdef _LARGE_FILE + oserr = FSWriteFork(id->refNum, fsAtMark, 0, + (ByteCount)amt, pBuf, (ByteCount*)&wrote); +# else + wrote = amt; + oserr = FSWrite(id->refNum, &wrote, pBuf); +# endif + if( wrote == 0 || oserr != noErr) + break; + amt -= wrote; + pBuf = &((char*)pBuf)[wrote]; + } + if( oserr != noErr || amt>wrote ){ + return SQLITE_FULL; + } + return SQLITE_OK; +#endif +} + +/* +** Move the read/write pointer in a file. +*/ +int sqliteOsSeek(OsFile *id, off_t offset){ + SEEK(offset/1024 + 1); +#if OS_UNIX + lseek(id->fd, offset, SEEK_SET); + return SQLITE_OK; +#endif +#if OS_WIN + { + LONG upperBits = offset>>32; + LONG lowerBits = offset & 0xffffffff; + DWORD rc; + rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); + /* TRACE3("SEEK rc=0x%x upper=0x%x\n", rc, upperBits); */ + } + return SQLITE_OK; +#endif +#if OS_MAC + { + off_t curSize; + if( sqliteOsFileSize(id, &curSize) != SQLITE_OK ){ + return SQLITE_IOERR; + } + if( offset >= curSize ){ + if( sqliteOsTruncate(id, offset+1) != SQLITE_OK ){ + return SQLITE_IOERR; + } + } +# ifdef _LARGE_FILE + if( FSSetForkPosition(id->refNum, fsFromStart, offset) != noErr ){ +# else + if( SetFPos(id->refNum, fsFromStart, offset) != noErr ){ +# endif + return SQLITE_IOERR; + }else{ + return SQLITE_OK; + } + } +#endif +} + +#ifdef SQLITE_NOSYNC +# define fsync(X) 0 +#endif + +/* +** Make sure all writes to a particular file are committed to disk. +** +** Under Unix, also make sure that the directory entry for the file +** has been created by fsync-ing the directory that contains the file. +** If we do not do this and we encounter a power failure, the directory +** entry for the journal might not exist after we reboot. The next +** SQLite to access the file will not know that the journal exists (because +** the directory entry for the journal was never created) and the transaction +** will not roll back - possibly leading to database corruption. +*/ +int sqliteOsSync(OsFile *id){ +#if OS_UNIX + SimulateIOError(SQLITE_IOERR); + TRACE2("SYNC %-3d\n", id->fd); + if( fsync(id->fd) ){ + return SQLITE_IOERR; + }else{ + if( id->dirfd>=0 ){ + TRACE2("DIRSYNC %-3d\n", id->dirfd); + fsync(id->dirfd); + close(id->dirfd); /* Only need to sync once, so close the directory */ + id->dirfd = -1; /* when we are done. */ + } + return SQLITE_OK; + } +#endif +#if OS_WIN + if( FlushFileBuffers(id->h) ){ + return SQLITE_OK; + }else{ + return SQLITE_IOERR; + } +#endif +#if OS_MAC +# ifdef _LARGE_FILE + if( FSFlushFork(id->refNum) != noErr ){ +# else + ParamBlockRec params; + memset(¶ms, 0, sizeof(ParamBlockRec)); + params.ioParam.ioRefNum = id->refNum; + if( PBFlushFileSync(¶ms) != noErr ){ +# endif + return SQLITE_IOERR; + }else{ + return SQLITE_OK; + } +#endif +} + +/* +** Truncate an open file to a specified size +*/ +int sqliteOsTruncate(OsFile *id, off_t nByte){ + SimulateIOError(SQLITE_IOERR); +#if OS_UNIX + return ftruncate(id->fd, nByte)==0 ? SQLITE_OK : SQLITE_IOERR; +#endif +#if OS_WIN + { + LONG upperBits = nByte>>32; + SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); + SetEndOfFile(id->h); + } + return SQLITE_OK; +#endif +#if OS_MAC +# ifdef _LARGE_FILE + if( FSSetForkSize(id->refNum, fsFromStart, nByte) != noErr){ +# else + if( SetEOF(id->refNum, nByte) != noErr ){ +# endif + return SQLITE_IOERR; + }else{ + return SQLITE_OK; + } +#endif +} + +/* +** Determine the current size of a file in bytes +*/ +int sqliteOsFileSize(OsFile *id, off_t *pSize){ +#if OS_UNIX + struct stat buf; + SimulateIOError(SQLITE_IOERR); + if( fstat(id->fd, &buf)!=0 ){ + return SQLITE_IOERR; + } + *pSize = buf.st_size; + return SQLITE_OK; +#endif +#if OS_WIN + DWORD upperBits, lowerBits; + SimulateIOError(SQLITE_IOERR); + lowerBits = GetFileSize(id->h, &upperBits); + *pSize = (((off_t)upperBits)<<32) + lowerBits; + return SQLITE_OK; +#endif +#if OS_MAC +# ifdef _LARGE_FILE + if( FSGetForkSize(id->refNum, pSize) != noErr){ +# else + if( GetEOF(id->refNum, pSize) != noErr ){ +# endif + return SQLITE_IOERR; + }else{ + return SQLITE_OK; + } +#endif +} + +#if OS_WIN +/* +** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. +** Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation: Win95, Win98, and WinME lack +** the LockFileEx() API. But we can still statically link against that +** API as long as we don't call it win running Win95/98/ME. A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. +*/ +int isNT(void){ + static int osType = 0; /* 0=unknown 1=win95 2=winNT */ + if( osType==0 ){ + OSVERSIONINFO sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + GetVersionEx(&sInfo); + osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; + } + return osType==2; +} +#endif + +/* +** Windows file locking notes: [similar issues apply to MacOS] +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** (This is a design error on the part of Windows, but there is nothing +** we can do about that.) So the region used for locking is at the +** end of the file where it is unlikely to ever interfere with an +** actual read attempt. +** +** A database read lock is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** A database write lock is obtained by locking all bytes in the range. +** There can only be one writer. +** +** A lock is obtained on the first byte of the lock range before acquiring +** either a read lock or a write lock. This prevents two processes from +** attempting to get a lock at a same time. The semantics of +** sqliteOsReadLock() require that if there is already a write lock, that +** lock is converted into a read lock atomically. The lock on the first +** byte allows us to drop the old write lock and get the read lock without +** another process jumping into the middle and messing us up. The same +** argument applies to sqliteOsWriteLock(). +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** Note: On MacOS we use the resource fork for locking. +** +** The following #defines specify the range of bytes used for locking. +** N_LOCKBYTE is the number of bytes available for doing the locking. +** The first byte used to hold the lock while the lock is changing does +** not count toward this number. FIRST_LOCKBYTE is the address of +** the first byte in the range of bytes used for locking. +*/ +#define N_LOCKBYTE 10239 +#if OS_MAC +# define FIRST_LOCKBYTE (0x000fffff - N_LOCKBYTE) +#else +# define FIRST_LOCKBYTE (0xffffffff - N_LOCKBYTE) +#endif + +/* +** Change the status of the lock on the file "id" to be a readlock. +** If the file was write locked, then this reduces the lock to a read. +** If the file was read locked, then this acquires a new read lock. +** +** Return SQLITE_OK on success and SQLITE_BUSY on failure. If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an SQLITE_NOLFS is returned. +*/ +int sqliteOsReadLock(OsFile *id){ +#if OS_UNIX + int rc; + sqliteOsEnterMutex(); + if( id->pLock->cnt>0 ){ + if( !id->locked ){ + id->pLock->cnt++; + id->locked = 1; + id->pOpen->nLock++; + } + rc = SQLITE_OK; + }else if( id->locked || id->pLock->cnt==0 ){ + struct flock lock; + int s; + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + s = fcntl(id->fd, F_SETLK, &lock); + if( s!=0 ){ + rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; + }else{ + rc = SQLITE_OK; + if( !id->locked ){ + id->pOpen->nLock++; + id->locked = 1; + } + id->pLock->cnt = 1; + } + }else{ + rc = SQLITE_BUSY; + } + sqliteOsLeaveMutex(); + return rc; +#endif +#if OS_WIN + int rc; + if( id->locked>0 ){ + rc = SQLITE_OK; + }else{ + int lk; + int res; + int cnt = 100; + sqliteRandomness(sizeof(lk), &lk); + lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; + while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ + Sleep(1); + } + if( res ){ + UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); + if( isNT() ){ + OVERLAPPED ovlp; + ovlp.Offset = FIRST_LOCKBYTE+1; + ovlp.OffsetHigh = 0; + ovlp.hEvent = 0; + res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, + 0, N_LOCKBYTE, 0, &ovlp); + }else{ + res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0); + } + UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); + } + if( res ){ + id->locked = lk; + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } + } + return rc; +#endif +#if OS_MAC + int rc; + if( id->locked>0 || id->refNumRF == -1 ){ + rc = SQLITE_OK; + }else{ + int lk; + OSErr res; + int cnt = 5; + ParamBlockRec params; + sqliteRandomness(sizeof(lk), &lk); + lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; + memset(¶ms, 0, sizeof(params)); + params.ioParam.ioRefNum = id->refNumRF; + params.ioParam.ioPosMode = fsFromStart; + params.ioParam.ioPosOffset = FIRST_LOCKBYTE; + params.ioParam.ioReqCount = 1; + while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ + UInt32 finalTicks; + Delay(1, &finalTicks); /* 1/60 sec */ + } + if( res == noErr ){ + params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; + params.ioParam.ioReqCount = N_LOCKBYTE; + PBUnlockRangeSync(¶ms); + params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk; + params.ioParam.ioReqCount = 1; + res = PBLockRangeSync(¶ms); + params.ioParam.ioPosOffset = FIRST_LOCKBYTE; + params.ioParam.ioReqCount = 1; + PBUnlockRangeSync(¶ms); + } + if( res == noErr ){ + id->locked = lk; + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } + } + return rc; +#endif +} + +/* +** Change the lock status to be an exclusive or write lock. Return +** SQLITE_OK on success and SQLITE_BUSY on a failure. If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an SQLITE_NOLFS is returned. +*/ +int sqliteOsWriteLock(OsFile *id){ +#if OS_UNIX + int rc; + sqliteOsEnterMutex(); + if( id->pLock->cnt==0 || (id->pLock->cnt==1 && id->locked==1) ){ + struct flock lock; + int s; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + s = fcntl(id->fd, F_SETLK, &lock); + if( s!=0 ){ + rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; + }else{ + rc = SQLITE_OK; + if( !id->locked ){ + id->pOpen->nLock++; + id->locked = 1; + } + id->pLock->cnt = -1; + } + }else{ + rc = SQLITE_BUSY; + } + sqliteOsLeaveMutex(); + return rc; +#endif +#if OS_WIN + int rc; + if( id->locked<0 ){ + rc = SQLITE_OK; + }else{ + int res; + int cnt = 100; + while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ + Sleep(1); + } + if( res ){ + if( id->locked>0 ){ + if( isNT() ){ + UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); + }else{ + res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0); + } + } + if( res ){ + res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); + }else{ + res = 0; + } + UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); + } + if( res ){ + id->locked = -1; + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } + } + return rc; +#endif +#if OS_MAC + int rc; + if( id->locked<0 || id->refNumRF == -1 ){ + rc = SQLITE_OK; + }else{ + OSErr res; + int cnt = 5; + ParamBlockRec params; + memset(¶ms, 0, sizeof(params)); + params.ioParam.ioRefNum = id->refNumRF; + params.ioParam.ioPosMode = fsFromStart; + params.ioParam.ioPosOffset = FIRST_LOCKBYTE; + params.ioParam.ioReqCount = 1; + while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ + UInt32 finalTicks; + Delay(1, &finalTicks); /* 1/60 sec */ + } + if( res == noErr ){ + params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked; + params.ioParam.ioReqCount = 1; + if( id->locked==0 + || PBUnlockRangeSync(¶ms)==noErr ){ + params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; + params.ioParam.ioReqCount = N_LOCKBYTE; + res = PBLockRangeSync(¶ms); + }else{ + res = afpRangeNotLocked; + } + params.ioParam.ioPosOffset = FIRST_LOCKBYTE; + params.ioParam.ioReqCount = 1; + PBUnlockRangeSync(¶ms); + } + if( res == noErr ){ + id->locked = -1; + rc = SQLITE_OK; + }else{ + rc = SQLITE_BUSY; + } + } + return rc; +#endif +} + +/* +** Unlock the given file descriptor. If the file descriptor was +** not previously locked, then this routine is a no-op. If this +** library was compiled with large file support (LFS) but LFS is not +** available on the host, then an SQLITE_NOLFS is returned. +*/ +int sqliteOsUnlock(OsFile *id){ +#if OS_UNIX + int rc; + if( !id->locked ) return SQLITE_OK; + sqliteOsEnterMutex(); + assert( id->pLock->cnt!=0 ); + if( id->pLock->cnt>1 ){ + id->pLock->cnt--; + rc = SQLITE_OK; + }else{ + struct flock lock; + int s; + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + s = fcntl(id->fd, F_SETLK, &lock); + if( s!=0 ){ + rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; + }else{ + rc = SQLITE_OK; + id->pLock->cnt = 0; + } + } + if( rc==SQLITE_OK ){ + /* Decrement the count of locks against this same file. When the + ** count reaches zero, close any other file descriptors whose close + ** was deferred because of outstanding locks. + */ + struct openCnt *pOpen = id->pOpen; + pOpen->nLock--; + assert( pOpen->nLock>=0 ); + if( pOpen->nLock==0 && pOpen->nPending>0 ){ + int i; + for(i=0; inPending; i++){ + close(pOpen->aPending[i]); + } + sqliteFree(pOpen->aPending); + pOpen->nPending = 0; + pOpen->aPending = 0; + } + } + sqliteOsLeaveMutex(); + id->locked = 0; + return rc; +#endif +#if OS_WIN + int rc; + if( id->locked==0 ){ + rc = SQLITE_OK; + }else if( isNT() || id->locked<0 ){ + UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); + rc = SQLITE_OK; + id->locked = 0; + }else{ + UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0); + rc = SQLITE_OK; + id->locked = 0; + } + return rc; +#endif +#if OS_MAC + int rc; + ParamBlockRec params; + memset(¶ms, 0, sizeof(params)); + params.ioParam.ioRefNum = id->refNumRF; + params.ioParam.ioPosMode = fsFromStart; + if( id->locked==0 || id->refNumRF == -1 ){ + rc = SQLITE_OK; + }else if( id->locked<0 ){ + params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; + params.ioParam.ioReqCount = N_LOCKBYTE; + PBUnlockRangeSync(¶ms); + rc = SQLITE_OK; + id->locked = 0; + }else{ + params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked; + params.ioParam.ioReqCount = 1; + PBUnlockRangeSync(¶ms); + rc = SQLITE_OK; + id->locked = 0; + } + return rc; +#endif +} + +/* +** Get information to seed the random number generator. The seed +** is written into the buffer zBuf[256]. The calling function must +** supply a sufficiently large buffer. +*/ +int sqliteOsRandomSeed(char *zBuf){ + /* We have to initialize zBuf to prevent valgrind from reporting + ** errors. The reports issued by valgrind are incorrect - we would + ** prefer that the randomness be increased by making use of the + ** uninitialized space in zBuf - but valgrind errors tend to worry + ** some users. Rather than argue, it seems easier just to initialize + ** the whole array and silence valgrind, even if that means less randomness + ** in the random seed. + ** + ** When testing, initializing zBuf[] to zero is all we do. That means + ** that we always use the same random number sequence.* This makes the + ** tests repeatable. + */ + memset(zBuf, 0, 256); +#if OS_UNIX && !defined(SQLITE_TEST) + { + int pid; + time((time_t*)zBuf); + pid = getpid(); + memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid)); + } +#endif +#if OS_WIN && !defined(SQLITE_TEST) + GetSystemTime((LPSYSTEMTIME)zBuf); +#endif +#if OS_MAC + { + int pid; + Microseconds((UnsignedWide*)zBuf); + pid = getpid(); + memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid)); + } +#endif + return SQLITE_OK; +} + +/* +** Sleep for a little while. Return the amount of time slept. +*/ +int sqliteOsSleep(int ms){ +#if OS_UNIX +#if defined(HAVE_USLEEP) && HAVE_USLEEP + usleep(ms*1000); + return ms; +#else + sleep((ms+999)/1000); + return 1000*((ms+999)/1000); +#endif +#endif +#if OS_WIN + Sleep(ms); + return ms; +#endif +#if OS_MAC + UInt32 finalTicks; + UInt32 ticks = (((UInt32)ms+16)*3)/50; /* 1/60 sec per tick */ + Delay(ticks, &finalTicks); + return (int)((ticks*50)/3); +#endif +} + +/* +** Static variables used for thread synchronization +*/ +static int inMutex = 0; +#ifdef SQLITE_UNIX_THREADS + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +#ifdef SQLITE_W32_THREADS + static CRITICAL_SECTION cs; +#endif +#ifdef SQLITE_MACOS_MULTITASKING + static MPCriticalRegionID criticalRegion; +#endif + +/* +** The following pair of routine implement mutual exclusion for +** multi-threaded processes. Only a single thread is allowed to +** executed code that is surrounded by EnterMutex() and LeaveMutex(). +** +** SQLite uses only a single Mutex. There is not much critical +** code and what little there is executes quickly and without blocking. +*/ +void sqliteOsEnterMutex(){ +#ifdef SQLITE_UNIX_THREADS + pthread_mutex_lock(&mutex); +#endif +#ifdef SQLITE_W32_THREADS + static int isInit = 0; + while( !isInit ){ + static long lock = 0; + if( InterlockedIncrement(&lock)==1 ){ + InitializeCriticalSection(&cs); + isInit = 1; + }else{ + Sleep(1); + } + } + EnterCriticalSection(&cs); +#endif +#ifdef SQLITE_MACOS_MULTITASKING + static volatile int notInit = 1; + if( notInit ){ + if( notInit == 2 ) /* as close as you can get to thread safe init */ + MPYield(); + else{ + notInit = 2; + MPCreateCriticalRegion(&criticalRegion); + notInit = 0; + } + } + MPEnterCriticalRegion(criticalRegion, kDurationForever); +#endif + assert( !inMutex ); + inMutex = 1; +} +void sqliteOsLeaveMutex(){ + assert( inMutex ); + inMutex = 0; +#ifdef SQLITE_UNIX_THREADS + pthread_mutex_unlock(&mutex); +#endif +#ifdef SQLITE_W32_THREADS + LeaveCriticalSection(&cs); +#endif +#ifdef SQLITE_MACOS_MULTITASKING + MPExitCriticalRegion(criticalRegion); +#endif +} + +/* +** Turn a relative pathname into a full pathname. Return a pointer +** to the full pathname stored in space obtained from sqliteMalloc(). +** The calling function is responsible for freeing this space once it +** is no longer needed. +*/ +char *sqliteOsFullPathname(const char *zRelative){ +#if OS_UNIX + char *zFull = 0; + if( zRelative[0]=='/' ){ + sqliteSetString(&zFull, zRelative, (char*)0); + }else{ + char zBuf[5000]; + zBuf[0] = 0; + sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative, + (char*)0); + } + return zFull; +#endif +#if OS_WIN + char *zNotUsed; + char *zFull; + int nByte; + nByte = GetFullPathName(zRelative, 0, 0, &zNotUsed) + 1; + zFull = sqliteMalloc( nByte ); + if( zFull==0 ) return 0; + GetFullPathName(zRelative, nByte, zFull, &zNotUsed); + return zFull; +#endif +#if OS_MAC + char *zFull = 0; + if( zRelative[0]==':' ){ + char zBuf[_MAX_PATH+1]; + sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]), + (char*)0); + }else{ + if( strchr(zRelative, ':') ){ + sqliteSetString(&zFull, zRelative, (char*)0); + }else{ + char zBuf[_MAX_PATH+1]; + sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, (char*)0); + } + } + return zFull; +#endif +} + +/* +** The following variable, if set to a non-zero value, becomes the result +** returned from sqliteOsCurrentTime(). This is used for testing. +*/ +#ifdef SQLITE_TEST +int sqlite_current_time = 0; +#endif + +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +int sqliteOsCurrentTime(double *prNow){ +#if OS_UNIX + time_t t; + time(&t); + *prNow = t/86400.0 + 2440587.5; +#endif +#if OS_WIN + FILETIME ft; + /* FILETIME structure is a 64-bit value representing the number of + 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). + */ + double now; + GetSystemTimeAsFileTime( &ft ); + now = ((double)ft.dwHighDateTime) * 4294967296.0; + *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; +#endif +#ifdef SQLITE_TEST + if( sqlite_current_time ){ + *prNow = sqlite_current_time/86400.0 + 2440587.5; + } +#endif + return 0; +} diff --git a/src/libs/sqlite2/os.h b/src/libs/sqlite2/os.h new file mode 100644 index 00000000..d11198c9 --- /dev/null +++ b/src/libs/sqlite2/os.h @@ -0,0 +1,191 @@ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE +** to the compiler command line. +*/ + +/* +** These #defines should enable >2GB file support on Posix if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: RedHat 7.2) but you want your code to work +** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in RedHat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** Similar is true for MacOS. LFS is only supported on MacOS 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line. +*/ +#ifndef TEMP_FILE_PREFIX +# define TEMP_FILE_PREFIX "sqlite_" +#endif + +/* +** Figure out if we are dealing with Unix, Windows or MacOS. +** +** N.B. MacOS means Mac Classic (or Carbon). Treat Darwin (OS X) as Unix. +** The MacOS build is designed to use CodeWarrior (tested with v8) +*/ +#ifndef OS_UNIX +# ifndef OS_WIN +# ifndef OS_MAC +# if defined(__MACOS__) +# define OS_MAC 1 +# define OS_WIN 0 +# define OS_UNIX 0 +# elif defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) +# define OS_MAC 0 +# define OS_WIN 1 +# define OS_UNIX 0 +# else +# define OS_MAC 0 +# define OS_WIN 0 +# define OS_UNIX 1 +# endif +# else +# define OS_WIN 0 +# define OS_UNIX 0 +# endif +# else +# define OS_MAC 0 +# define OS_UNIX 0 +# endif +#else +# define OS_MAC 0 +# ifndef OS_WIN +# define OS_WIN 0 +# endif +#endif + +/* +** A handle for an open file is stored in an OsFile object. +*/ +#if OS_UNIX +# include +# include +# include +# include + typedef struct OsFile OsFile; + struct OsFile { + struct openCnt *pOpen; /* Info about all open fd's on this inode */ + struct lockInfo *pLock; /* Info about locks on this inode */ + int fd; /* The file descriptor */ + int locked; /* True if this instance holds the lock */ + int dirfd; /* File descriptor for the directory */ + }; +# define SQLITE_TEMPNAME_SIZE 200 +# if defined(HAVE_USLEEP) && HAVE_USLEEP +# define SQLITE_MIN_SLEEP_MS 1 +# else +# define SQLITE_MIN_SLEEP_MS 1000 +# endif +#endif + +#if OS_WIN +#include +#include + typedef struct OsFile OsFile; + struct OsFile { + HANDLE h; /* Handle for accessing the file */ + int locked; /* 0: unlocked, <0: write lock, >0: read lock */ + }; +# if defined(_MSC_VER) + typedef __int64 off_t; +# else +# if !defined(_CYGWIN_TYPES_H) + typedef long long off_t; +# if defined(__MINGW32__) +# define _OFF_T_ +# endif +# endif +# endif +# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) +# define SQLITE_MIN_SLEEP_MS 1 +#endif + +#if OS_MAC +# include +# include + typedef struct OsFile OsFile; + struct OsFile { + SInt16 refNum; /* Data fork/file reference number */ + SInt16 refNumRF; /* Resource fork reference number (for locking) */ + int locked; /* 0: unlocked, <0: write lock, >0: read lock */ + int delOnClose; /* True if file is to be deleted on close */ + char *pathToDel; /* Name of file to delete on close */ + }; +# ifdef _LARGE_FILE + typedef SInt64 off_t; +# else + typedef SInt32 off_t; +# endif +# define SQLITE_TEMPNAME_SIZE _MAX_PATH +# define SQLITE_MIN_SLEEP_MS 17 +#endif + +int sqliteOsDelete(const char*); +int sqliteOsFileExists(const char*); +int sqliteOsFileRename(const char*, const char*); +int sqliteOsOpenReadWrite(const char*, OsFile*, int*); +int sqliteOsOpenExclusive(const char*, OsFile*, int); +int sqliteOsOpenReadOnly(const char*, OsFile*); +int sqliteOsOpenDirectory(const char*, OsFile*); +int sqliteOsTempFileName(char*); +int sqliteOsClose(OsFile*); +int sqliteOsRead(OsFile*, void*, int amt); +int sqliteOsWrite(OsFile*, const void*, int amt); +int sqliteOsSeek(OsFile*, off_t offset); +int sqliteOsSync(OsFile*); +int sqliteOsTruncate(OsFile*, off_t size); +int sqliteOsFileSize(OsFile*, off_t *pSize); +int sqliteOsReadLock(OsFile*); +int sqliteOsWriteLock(OsFile*); +int sqliteOsUnlock(OsFile*); +int sqliteOsRandomSeed(char*); +int sqliteOsSleep(int ms); +int sqliteOsCurrentTime(double*); +void sqliteOsEnterMutex(void); +void sqliteOsLeaveMutex(void); +char *sqliteOsFullPathname(const char*); + + + +#endif /* _SQLITE_OS_H_ */ diff --git a/src/libs/sqlite2/pager.c b/src/libs/sqlite2/pager.c new file mode 100644 index 00000000..409f9201 --- /dev/null +++ b/src/libs/sqlite2/pager.c @@ -0,0 +1,2220 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the implementation of the page cache subsystem or "pager". +** +** The pager is used to access a database disk file. It implements +** atomic commit and rollback through the use of a journal file that +** is separate from the database file. The pager also implements file +** locking to prevent two processes from writing the same database +** file simultaneously, or one process from reading the database while +** another is writing. +** +** @(#) $Id: pager.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "os.h" /* Must be first to enable large file support */ +#include "sqliteInt.h" +#include "pager.h" +#include +#include + +/* +** Macros for troubleshooting. Normally turned off +*/ +#if 0 +static Pager *mainPager = 0; +#define SET_PAGER(X) if( mainPager==0 ) mainPager = (X) +#define CLR_PAGER(X) if( mainPager==(X) ) mainPager = 0 +#define TRACE1(X) if( pPager==mainPager ) fprintf(stderr,X) +#define TRACE2(X,Y) if( pPager==mainPager ) fprintf(stderr,X,Y) +#define TRACE3(X,Y,Z) if( pPager==mainPager ) fprintf(stderr,X,Y,Z) +#else +#define SET_PAGER(X) +#define CLR_PAGER(X) +#define TRACE1(X) +#define TRACE2(X,Y) +#define TRACE3(X,Y,Z) +#endif + + +/* +** The page cache as a whole is always in one of the following +** states: +** +** SQLITE_UNLOCK The page cache is not currently reading or +** writing the database file. There is no +** data held in memory. This is the initial +** state. +** +** SQLITE_READLOCK The page cache is reading the database. +** Writing is not permitted. There can be +** multiple readers accessing the same database +** file at the same time. +** +** SQLITE_WRITELOCK The page cache is writing the database. +** Access is exclusive. No other processes or +** threads can be reading or writing while one +** process is writing. +** +** The page cache comes up in SQLITE_UNLOCK. The first time a +** sqlite_page_get() occurs, the state transitions to SQLITE_READLOCK. +** After all pages have been released using sqlite_page_unref(), +** the state transitions back to SQLITE_UNLOCK. The first time +** that sqlite_page_write() is called, the state transitions to +** SQLITE_WRITELOCK. (Note that sqlite_page_write() can only be +** called on an outstanding page which means that the pager must +** be in SQLITE_READLOCK before it transitions to SQLITE_WRITELOCK.) +** The sqlite_page_rollback() and sqlite_page_commit() functions +** transition the state from SQLITE_WRITELOCK back to SQLITE_READLOCK. +*/ +#define SQLITE_UNLOCK 0 +#define SQLITE_READLOCK 1 +#define SQLITE_WRITELOCK 2 + + +/* +** Each in-memory image of a page begins with the following header. +** This header is only visible to this pager module. The client +** code that calls pager sees only the data that follows the header. +** +** Client code should call sqlitepager_write() on a page prior to making +** any modifications to that page. The first time sqlitepager_write() +** is called, the original page contents are written into the rollback +** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once +** the journal page has made it onto the disk surface, PgHdr.needSync +** is cleared. The modified page cannot be written back into the original +** database file until the journal pages has been synced to disk and the +** PgHdr.needSync has been cleared. +** +** The PgHdr.dirty flag is set when sqlitepager_write() is called and +** is cleared again when the page content is written back to the original +** database file. +*/ +typedef struct PgHdr PgHdr; +struct PgHdr { + Pager *pPager; /* The pager to which this page belongs */ + Pgno pgno; /* The page number for this page */ + PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */ + int nRef; /* Number of users of this page */ + PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */ + PgHdr *pNextAll, *pPrevAll; /* A list of all pages */ + PgHdr *pNextCkpt, *pPrevCkpt; /* List of pages in the checkpoint journal */ + u8 inJournal; /* TRUE if has been written to journal */ + u8 inCkpt; /* TRUE if written to the checkpoint journal */ + u8 dirty; /* TRUE if we need to write back changes */ + u8 needSync; /* Sync journal before writing this page */ + u8 alwaysRollback; /* Disable dont_rollback() for this page */ + PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */ + /* SQLITE_PAGE_SIZE bytes of page data follow this header */ + /* Pager.nExtra bytes of local data follow the page data */ +}; + + +/* +** A macro used for invoking the codec if there is one +*/ +#ifdef SQLITE_HAS_CODEC +# define CODEC(P,D,N,X) if( P->xCodec ){ P->xCodec(P->pCodecArg,D,N,X); } +#else +# define CODEC(P,D,N,X) +#endif + +/* +** Convert a pointer to a PgHdr into a pointer to its data +** and back again. +*/ +#define PGHDR_TO_DATA(P) ((void*)(&(P)[1])) +#define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1]) +#define PGHDR_TO_EXTRA(P) ((void*)&((char*)(&(P)[1]))[SQLITE_PAGE_SIZE]) + +/* +** How big to make the hash table used for locating in-memory pages +** by page number. +*/ +#define N_PG_HASH 2048 + +/* +** Hash a page number +*/ +#define pager_hash(PN) ((PN)&(N_PG_HASH-1)) + +/* +** A open page cache is an instance of the following structure. +*/ +struct Pager { + char *zFilename; /* Name of the database file */ + char *zJournal; /* Name of the journal file */ + char *zDirectory; /* Directory hold database and journal files */ + OsFile fd, jfd; /* File descriptors for database and journal */ + OsFile cpfd; /* File descriptor for the checkpoint journal */ + int dbSize; /* Number of pages in the file */ + int origDbSize; /* dbSize before the current change */ + int ckptSize; /* Size of database (in pages) at ckpt_begin() */ + off_t ckptJSize; /* Size of journal at ckpt_begin() */ + int nRec; /* Number of pages written to the journal */ + u32 cksumInit; /* Quasi-random value added to every checksum */ + int ckptNRec; /* Number of records in the checkpoint journal */ + int nExtra; /* Add this many bytes to each in-memory page */ + void (*xDestructor)(void*); /* Call this routine when freeing pages */ + int nPage; /* Total number of in-memory pages */ + int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */ + int mxPage; /* Maximum number of pages to hold in cache */ + int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */ + void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */ + void *pCodecArg; /* First argument to xCodec() */ + u8 journalOpen; /* True if journal file descriptors is valid */ + u8 journalStarted; /* True if header of journal is synced */ + u8 useJournal; /* Use a rollback journal on this file */ + u8 ckptOpen; /* True if the checkpoint journal is open */ + u8 ckptInUse; /* True we are in a checkpoint */ + u8 ckptAutoopen; /* Open ckpt journal when main journal is opened*/ + u8 noSync; /* Do not sync the journal if true */ + u8 fullSync; /* Do extra syncs of the journal for robustness */ + u8 state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */ + u8 errMask; /* One of several kinds of errors */ + u8 tempFile; /* zFilename is a temporary file */ + u8 readOnly; /* True for a read-only database */ + u8 needSync; /* True if an fsync() is needed on the journal */ + u8 dirtyFile; /* True if database file has changed in any way */ + u8 alwaysRollback; /* Disable dont_rollback() for all pages */ + u8 *aInJournal; /* One bit for each page in the database file */ + u8 *aInCkpt; /* One bit for each page in the database */ + PgHdr *pFirst, *pLast; /* List of free pages */ + PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */ + PgHdr *pAll; /* List of all pages */ + PgHdr *pCkpt; /* List of pages in the checkpoint journal */ + PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */ +}; + +/* +** These are bits that can be set in Pager.errMask. +*/ +#define PAGER_ERR_FULL 0x01 /* a write() failed */ +#define PAGER_ERR_MEM 0x02 /* malloc() failed */ +#define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */ +#define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */ +#define PAGER_ERR_DISK 0x10 /* general disk I/O error - bad hard drive? */ + +/* +** The journal file contains page records in the following +** format. +** +** Actually, this structure is the complete page record for pager +** formats less than 3. Beginning with format 3, this record is surrounded +** by two checksums. +*/ +typedef struct PageRecord PageRecord; +struct PageRecord { + Pgno pgno; /* The page number */ + char aData[SQLITE_PAGE_SIZE]; /* Original data for page pgno */ +}; + +/* +** Journal files begin with the following magic string. The data +** was obtained from /dev/random. It is used only as a sanity check. +** +** There are three journal formats (so far). The 1st journal format writes +** 32-bit integers in the byte-order of the host machine. New +** formats writes integers as big-endian. All new journals use the +** new format, but we have to be able to read an older journal in order +** to rollback journals created by older versions of the library. +** +** The 3rd journal format (added for 2.8.0) adds additional sanity +** checking information to the journal. If the power fails while the +** journal is being written, semi-random garbage data might appear in +** the journal file after power is restored. If an attempt is then made +** to roll the journal back, the database could be corrupted. The additional +** sanity checking data is an attempt to discover the garbage in the +** journal and ignore it. +** +** The sanity checking information for the 3rd journal format consists +** of a 32-bit checksum on each page of data. The checksum covers both +** the page number and the SQLITE_PAGE_SIZE bytes of data for the page. +** This cksum is initialized to a 32-bit random value that appears in the +** journal file right after the header. The random initializer is important, +** because garbage data that appears at the end of a journal is likely +** data that was once in other files that have now been deleted. If the +** garbage data came from an obsolete journal file, the checksums might +** be correct. But by initializing the checksum to random value which +** is different for every journal, we minimize that risk. +*/ +static const unsigned char aJournalMagic1[] = { + 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4, +}; +static const unsigned char aJournalMagic2[] = { + 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd5, +}; +static const unsigned char aJournalMagic3[] = { + 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd6, +}; +#define JOURNAL_FORMAT_1 1 +#define JOURNAL_FORMAT_2 2 +#define JOURNAL_FORMAT_3 3 + +/* +** The following integer determines what format to use when creating +** new primary journal files. By default we always use format 3. +** When testing, we can set this value to older journal formats in order to +** make sure that newer versions of the library are able to rollback older +** journal files. +** +** Note that checkpoint journals always use format 2 and omit the header. +*/ +#ifdef SQLITE_TEST +int journal_format = 3; +#else +# define journal_format 3 +#endif + +/* +** The size of the header and of each page in the journal varies according +** to which journal format is being used. The following macros figure out +** the sizes based on format numbers. +*/ +#define JOURNAL_HDR_SZ(X) \ + (sizeof(aJournalMagic1) + sizeof(Pgno) + ((X)>=3)*2*sizeof(u32)) +#define JOURNAL_PG_SZ(X) \ + (SQLITE_PAGE_SIZE + sizeof(Pgno) + ((X)>=3)*sizeof(u32)) + +/* +** Enable reference count tracking here: +*/ +#ifdef SQLITE_TEST + int pager_refinfo_enable = 0; + static void pager_refinfo(PgHdr *p){ + static int cnt = 0; + if( !pager_refinfo_enable ) return; + printf( + "REFCNT: %4d addr=0x%08x nRef=%d\n", + p->pgno, (int)PGHDR_TO_DATA(p), p->nRef + ); + cnt++; /* Something to set a breakpoint on */ + } +# define REFINFO(X) pager_refinfo(X) +#else +# define REFINFO(X) +#endif + +/* +** Read a 32-bit integer from the given file descriptor. Store the integer +** that is read in *pRes. Return SQLITE_OK if everything worked, or an +** error code is something goes wrong. +** +** If the journal format is 2 or 3, read a big-endian integer. If the +** journal format is 1, read an integer in the native byte-order of the +** host machine. +*/ +static int read32bits(int format, OsFile *fd, u32 *pRes){ + u32 res; + int rc; + rc = sqliteOsRead(fd, &res, sizeof(res)); + if( rc==SQLITE_OK && format>JOURNAL_FORMAT_1 ){ + unsigned char ac[4]; + memcpy(ac, &res, 4); + res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3]; + } + *pRes = res; + return rc; +} + +/* +** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK +** on success or an error code is something goes wrong. +** +** If the journal format is 2 or 3, write the integer as 4 big-endian +** bytes. If the journal format is 1, write the integer in the native +** byte order. In normal operation, only formats 2 and 3 are used. +** Journal format 1 is only used for testing. +*/ +static int write32bits(OsFile *fd, u32 val){ + unsigned char ac[4]; + if( journal_format<=1 ){ + return sqliteOsWrite(fd, &val, 4); + } + ac[0] = (val>>24) & 0xff; + ac[1] = (val>>16) & 0xff; + ac[2] = (val>>8) & 0xff; + ac[3] = val & 0xff; + return sqliteOsWrite(fd, ac, 4); +} + +/* +** Write a 32-bit integer into a page header right before the +** page data. This will overwrite the PgHdr.pDirty pointer. +** +** The integer is big-endian for formats 2 and 3 and native byte order +** for journal format 1. +*/ +static void store32bits(u32 val, PgHdr *p, int offset){ + unsigned char *ac; + ac = &((unsigned char*)PGHDR_TO_DATA(p))[offset]; + if( journal_format<=1 ){ + memcpy(ac, &val, 4); + }else{ + ac[0] = (val>>24) & 0xff; + ac[1] = (val>>16) & 0xff; + ac[2] = (val>>8) & 0xff; + ac[3] = val & 0xff; + } +} + + +/* +** Convert the bits in the pPager->errMask into an approprate +** return code. +*/ +static int pager_errcode(Pager *pPager){ + int rc = SQLITE_OK; + if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL; + if( pPager->errMask & PAGER_ERR_DISK ) rc = SQLITE_IOERR; + if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL; + if( pPager->errMask & PAGER_ERR_MEM ) rc = SQLITE_NOMEM; + if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT; + return rc; +} + +/* +** Add or remove a page from the list of all pages that are in the +** checkpoint journal. +** +** The Pager keeps a separate list of pages that are currently in +** the checkpoint journal. This helps the sqlitepager_ckpt_commit() +** routine run MUCH faster for the common case where there are many +** pages in memory but only a few are in the checkpoint journal. +*/ +static void page_add_to_ckpt_list(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + if( pPg->inCkpt ) return; + assert( pPg->pPrevCkpt==0 && pPg->pNextCkpt==0 ); + pPg->pPrevCkpt = 0; + if( pPager->pCkpt ){ + pPager->pCkpt->pPrevCkpt = pPg; + } + pPg->pNextCkpt = pPager->pCkpt; + pPager->pCkpt = pPg; + pPg->inCkpt = 1; +} +static void page_remove_from_ckpt_list(PgHdr *pPg){ + if( !pPg->inCkpt ) return; + if( pPg->pPrevCkpt ){ + assert( pPg->pPrevCkpt->pNextCkpt==pPg ); + pPg->pPrevCkpt->pNextCkpt = pPg->pNextCkpt; + }else{ + assert( pPg->pPager->pCkpt==pPg ); + pPg->pPager->pCkpt = pPg->pNextCkpt; + } + if( pPg->pNextCkpt ){ + assert( pPg->pNextCkpt->pPrevCkpt==pPg ); + pPg->pNextCkpt->pPrevCkpt = pPg->pPrevCkpt; + } + pPg->pNextCkpt = 0; + pPg->pPrevCkpt = 0; + pPg->inCkpt = 0; +} + +/* +** Find a page in the hash table given its page number. Return +** a pointer to the page or NULL if not found. +*/ +static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){ + PgHdr *p = pPager->aHash[pager_hash(pgno)]; + while( p && p->pgno!=pgno ){ + p = p->pNextHash; + } + return p; +} + +/* +** Unlock the database and clear the in-memory cache. This routine +** sets the state of the pager back to what it was when it was first +** opened. Any outstanding pages are invalidated and subsequent attempts +** to access those pages will likely result in a coredump. +*/ +static void pager_reset(Pager *pPager){ + PgHdr *pPg, *pNext; + for(pPg=pPager->pAll; pPg; pPg=pNext){ + pNext = pPg->pNextAll; + sqliteFree(pPg); + } + pPager->pFirst = 0; + pPager->pFirstSynced = 0; + pPager->pLast = 0; + pPager->pAll = 0; + memset(pPager->aHash, 0, sizeof(pPager->aHash)); + pPager->nPage = 0; + if( pPager->state>=SQLITE_WRITELOCK ){ + sqlitepager_rollback(pPager); + } + sqliteOsUnlock(&pPager->fd); + pPager->state = SQLITE_UNLOCK; + pPager->dbSize = -1; + pPager->nRef = 0; + assert( pPager->journalOpen==0 ); +} + +/* +** When this routine is called, the pager has the journal file open and +** a write lock on the database. This routine releases the database +** write lock and acquires a read lock in its place. The journal file +** is deleted and closed. +** +** TODO: Consider keeping the journal file open for temporary databases. +** This might give a performance improvement on windows where opening +** a file is an expensive operation. +*/ +static int pager_unwritelock(Pager *pPager){ + int rc; + PgHdr *pPg; + if( pPager->stateckptOpen ){ + sqliteOsClose(&pPager->cpfd); + pPager->ckptOpen = 0; + } + if( pPager->journalOpen ){ + sqliteOsClose(&pPager->jfd); + pPager->journalOpen = 0; + sqliteOsDelete(pPager->zJournal); + sqliteFree( pPager->aInJournal ); + pPager->aInJournal = 0; + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + pPg->inJournal = 0; + pPg->dirty = 0; + pPg->needSync = 0; + } + }else{ + assert( pPager->dirtyFile==0 || pPager->useJournal==0 ); + } + rc = sqliteOsReadLock(&pPager->fd); + if( rc==SQLITE_OK ){ + pPager->state = SQLITE_READLOCK; + }else{ + /* This can only happen if a process does a BEGIN, then forks and the + ** child process does the COMMIT. Because of the semantics of unix + ** file locking, the unlock will fail. + */ + pPager->state = SQLITE_UNLOCK; + } + return rc; +} + +/* +** Compute and return a checksum for the page of data. +** +** This is not a real checksum. It is really just the sum of the +** random initial value and the page number. We considered do a checksum +** of the database, but that was found to be too slow. +*/ +static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){ + u32 cksum = pPager->cksumInit + pgno; + return cksum; +} + +/* +** Read a single page from the journal file opened on file descriptor +** jfd. Playback this one page. +** +** There are three different journal formats. The format parameter determines +** which format is used by the journal that is played back. +*/ +static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){ + int rc; + PgHdr *pPg; /* An existing page in the cache */ + PageRecord pgRec; + u32 cksum; + + rc = read32bits(format, jfd, &pgRec.pgno); + if( rc!=SQLITE_OK ) return rc; + rc = sqliteOsRead(jfd, &pgRec.aData, sizeof(pgRec.aData)); + if( rc!=SQLITE_OK ) return rc; + + /* Sanity checking on the page. This is more important that I originally + ** thought. If a power failure occurs while the journal is being written, + ** it could cause invalid data to be written into the journal. We need to + ** detect this invalid data (with high probability) and ignore it. + */ + if( pgRec.pgno==0 ){ + return SQLITE_DONE; + } + if( pgRec.pgno>(unsigned)pPager->dbSize ){ + return SQLITE_OK; + } + if( format>=JOURNAL_FORMAT_3 ){ + rc = read32bits(format, jfd, &cksum); + if( rc ) return rc; + if( pager_cksum(pPager, pgRec.pgno, pgRec.aData)!=cksum ){ + return SQLITE_DONE; + } + } + + /* Playback the page. Update the in-memory copy of the page + ** at the same time, if there is one. + */ + pPg = pager_lookup(pPager, pgRec.pgno); + TRACE2("PLAYBACK %d\n", pgRec.pgno); + sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*(off_t)SQLITE_PAGE_SIZE); + rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE); + if( pPg ){ + /* No page should ever be rolled back that is in use, except for page + ** 1 which is held in use in order to keep the lock on the database + ** active. + */ + assert( pPg->nRef==0 || pPg->pgno==1 ); + memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE); + memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra); + pPg->dirty = 0; + pPg->needSync = 0; + CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); + } + return rc; +} + +/* +** Playback the journal and thus restore the database file to +** the state it was in before we started making changes. +** +** The journal file format is as follows: +** +** * 8 byte prefix. One of the aJournalMagic123 vectors defined +** above. The format of the journal file is determined by which +** of the three prefix vectors is seen. +** * 4 byte big-endian integer which is the number of valid page records +** in the journal. If this value is 0xffffffff, then compute the +** number of page records from the journal size. This field appears +** in format 3 only. +** * 4 byte big-endian integer which is the initial value for the +** sanity checksum. This field appears in format 3 only. +** * 4 byte integer which is the number of pages to truncate the +** database to during a rollback. +** * Zero or more pages instances, each as follows: +** + 4 byte page number. +** + SQLITE_PAGE_SIZE bytes of data. +** + 4 byte checksum (format 3 only) +** +** When we speak of the journal header, we mean the first 4 bullets above. +** Each entry in the journal is an instance of the 5th bullet. Note that +** bullets 2 and 3 only appear in format-3 journals. +** +** Call the value from the second bullet "nRec". nRec is the number of +** valid page entries in the journal. In most cases, you can compute the +** value of nRec from the size of the journal file. But if a power +** failure occurred while the journal was being written, it could be the +** case that the size of the journal file had already been increased but +** the extra entries had not yet made it safely to disk. In such a case, +** the value of nRec computed from the file size would be too large. For +** that reason, we always use the nRec value in the header. +** +** If the nRec value is 0xffffffff it means that nRec should be computed +** from the file size. This value is used when the user selects the +** no-sync option for the journal. A power failure could lead to corruption +** in this case. But for things like temporary table (which will be +** deleted when the power is restored) we don't care. +** +** Journal formats 1 and 2 do not have an nRec value in the header so we +** have to compute nRec from the file size. This has risks (as described +** above) which is why all persistent tables have been changed to use +** format 3. +** +** If the file opened as the journal file is not a well-formed +** journal file then the database will likely already be +** corrupted, so the PAGER_ERR_CORRUPT bit is set in pPager->errMask +** and SQLITE_CORRUPT is returned. If it all works, then this routine +** returns SQLITE_OK. +*/ +static int pager_playback(Pager *pPager, int useJournalSize){ + off_t szJ; /* Size of the journal file in bytes */ + int nRec; /* Number of Records in the journal */ + int i; /* Loop counter */ + Pgno mxPg = 0; /* Size of the original file in pages */ + int format; /* Format of the journal file. */ + unsigned char aMagic[sizeof(aJournalMagic1)]; + int rc; + + /* Figure out how many records are in the journal. Abort early if + ** the journal is empty. + */ + assert( pPager->journalOpen ); + sqliteOsSeek(&pPager->jfd, 0); + rc = sqliteOsFileSize(&pPager->jfd, &szJ); + if( rc!=SQLITE_OK ){ + goto end_playback; + } + + /* If the journal file is too small to contain a complete header, + ** it must mean that the process that created the journal was just + ** beginning to write the journal file when it died. In that case, + ** the database file should have still been completely unchanged. + ** Nothing needs to be rolled back. We can safely ignore this journal. + */ + if( szJ < sizeof(aMagic)+sizeof(Pgno) ){ + goto end_playback; + } + + /* Read the beginning of the journal and truncate the + ** database file back to its original size. + */ + rc = sqliteOsRead(&pPager->jfd, aMagic, sizeof(aMagic)); + if( rc!=SQLITE_OK ){ + rc = SQLITE_PROTOCOL; + goto end_playback; + } + if( memcmp(aMagic, aJournalMagic3, sizeof(aMagic))==0 ){ + format = JOURNAL_FORMAT_3; + }else if( memcmp(aMagic, aJournalMagic2, sizeof(aMagic))==0 ){ + format = JOURNAL_FORMAT_2; + }else if( memcmp(aMagic, aJournalMagic1, sizeof(aMagic))==0 ){ + format = JOURNAL_FORMAT_1; + }else{ + rc = SQLITE_PROTOCOL; + goto end_playback; + } + if( format>=JOURNAL_FORMAT_3 ){ + if( szJ < sizeof(aMagic) + 3*sizeof(u32) ){ + /* Ignore the journal if it is too small to contain a complete + ** header. We already did this test once above, but at the prior + ** test, we did not know the journal format and so we had to assume + ** the smallest possible header. Now we know the header is bigger + ** than the minimum so we test again. + */ + goto end_playback; + } + rc = read32bits(format, &pPager->jfd, (u32*)&nRec); + if( rc ) goto end_playback; + rc = read32bits(format, &pPager->jfd, &pPager->cksumInit); + if( rc ) goto end_playback; + if( nRec==0xffffffff || useJournalSize ){ + nRec = (szJ - JOURNAL_HDR_SZ(3))/JOURNAL_PG_SZ(3); + } + }else{ + nRec = (szJ - JOURNAL_HDR_SZ(2))/JOURNAL_PG_SZ(2); + assert( nRec*JOURNAL_PG_SZ(2)+JOURNAL_HDR_SZ(2)==szJ ); + } + rc = read32bits(format, &pPager->jfd, &mxPg); + if( rc!=SQLITE_OK ){ + goto end_playback; + } + assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg ); + rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)mxPg); + if( rc!=SQLITE_OK ){ + goto end_playback; + } + pPager->dbSize = mxPg; + + /* Copy original pages out of the journal and back into the database file. + */ + for(i=0; ijfd, format); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + } + break; + } + } + + /* Pages that have been written to the journal but never synced + ** where not restored by the loop above. We have to restore those + ** pages by reading them back from the original database. + */ + if( rc==SQLITE_OK ){ + PgHdr *pPg; + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + char zBuf[SQLITE_PAGE_SIZE]; + if( !pPg->dirty ) continue; + if( (int)pPg->pgno <= pPager->origDbSize ){ + sqliteOsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1)); + rc = sqliteOsRead(&pPager->fd, zBuf, SQLITE_PAGE_SIZE); + TRACE2("REFETCH %d\n", pPg->pgno); + CODEC(pPager, zBuf, pPg->pgno, 2); + if( rc ) break; + }else{ + memset(zBuf, 0, SQLITE_PAGE_SIZE); + } + if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE) ){ + memcpy(PGHDR_TO_DATA(pPg), zBuf, SQLITE_PAGE_SIZE); + memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra); + } + pPg->needSync = 0; + pPg->dirty = 0; + } + } + +end_playback: + if( rc!=SQLITE_OK ){ + pager_unwritelock(pPager); + pPager->errMask |= PAGER_ERR_CORRUPT; + rc = SQLITE_CORRUPT; + }else{ + rc = pager_unwritelock(pPager); + } + return rc; +} + +/* +** Playback the checkpoint journal. +** +** This is similar to playing back the transaction journal but with +** a few extra twists. +** +** (1) The number of pages in the database file at the start of +** the checkpoint is stored in pPager->ckptSize, not in the +** journal file itself. +** +** (2) In addition to playing back the checkpoint journal, also +** playback all pages of the transaction journal beginning +** at offset pPager->ckptJSize. +*/ +static int pager_ckpt_playback(Pager *pPager){ + off_t szJ; /* Size of the full journal */ + int nRec; /* Number of Records */ + int i; /* Loop counter */ + int rc; + + /* Truncate the database back to its original size. + */ + rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)pPager->ckptSize); + pPager->dbSize = pPager->ckptSize; + + /* Figure out how many records are in the checkpoint journal. + */ + assert( pPager->ckptInUse && pPager->journalOpen ); + sqliteOsSeek(&pPager->cpfd, 0); + nRec = pPager->ckptNRec; + + /* Copy original pages out of the checkpoint journal and back into the + ** database file. Note that the checkpoint journal always uses format + ** 2 instead of format 3 since it does not need to be concerned with + ** power failures corrupting the journal and can thus omit the checksums. + */ + for(i=nRec-1; i>=0; i--){ + rc = pager_playback_one_page(pPager, &pPager->cpfd, 2); + assert( rc!=SQLITE_DONE ); + if( rc!=SQLITE_OK ) goto end_ckpt_playback; + } + + /* Figure out how many pages need to be copied out of the transaction + ** journal. + */ + rc = sqliteOsSeek(&pPager->jfd, pPager->ckptJSize); + if( rc!=SQLITE_OK ){ + goto end_ckpt_playback; + } + rc = sqliteOsFileSize(&pPager->jfd, &szJ); + if( rc!=SQLITE_OK ){ + goto end_ckpt_playback; + } + nRec = (szJ - pPager->ckptJSize)/JOURNAL_PG_SZ(journal_format); + for(i=nRec-1; i>=0; i--){ + rc = pager_playback_one_page(pPager, &pPager->jfd, journal_format); + if( rc!=SQLITE_OK ){ + assert( rc!=SQLITE_DONE ); + goto end_ckpt_playback; + } + } + +end_ckpt_playback: + if( rc!=SQLITE_OK ){ + pPager->errMask |= PAGER_ERR_CORRUPT; + rc = SQLITE_CORRUPT; + } + return rc; +} + +/* +** Change the maximum number of in-memory pages that are allowed. +** +** The maximum number is the absolute value of the mxPage parameter. +** If mxPage is negative, the noSync flag is also set. noSync bypasses +** calls to sqliteOsSync(). The pager runs much faster with noSync on, +** but if the operating system crashes or there is an abrupt power +** failure, the database file might be left in an inconsistent and +** unrepairable state. +*/ +void sqlitepager_set_cachesize(Pager *pPager, int mxPage){ + if( mxPage>=0 ){ + pPager->noSync = pPager->tempFile; + if( pPager->noSync==0 ) pPager->needSync = 0; + }else{ + pPager->noSync = 1; + mxPage = -mxPage; + } + if( mxPage>10 ){ + pPager->mxPage = mxPage; + } +} + +/* +** Adjust the robustness of the database to damage due to OS crashes +** or power failures by changing the number of syncs()s when writing +** the rollback journal. There are three levels: +** +** OFF sqliteOsSync() is never called. This is the default +** for temporary and transient files. +** +** NORMAL The journal is synced once before writes begin on the +** database. This is normally adequate protection, but +** it is theoretically possible, though very unlikely, +** that an inopertune power failure could leave the journal +** in a state which would cause damage to the database +** when it is rolled back. +** +** FULL The journal is synced twice before writes begin on the +** database (with some additional information - the nRec field +** of the journal header - being written in between the two +** syncs). If we assume that writing a +** single disk sector is atomic, then this mode provides +** assurance that the journal will not be corrupted to the +** point of causing damage to the database during rollback. +** +** Numeric values associated with these states are OFF==1, NORMAL=2, +** and FULL=3. +*/ +void sqlitepager_set_safety_level(Pager *pPager, int level){ + pPager->noSync = level==1 || pPager->tempFile; + pPager->fullSync = level==3 && !pPager->tempFile; + if( pPager->noSync==0 ) pPager->needSync = 0; +} + +/* +** Open a temporary file. Write the name of the file into zName +** (zName must be at least SQLITE_TEMPNAME_SIZE bytes long.) Write +** the file descriptor into *fd. Return SQLITE_OK on success or some +** other error code if we fail. +** +** The OS will automatically delete the temporary file when it is +** closed. +*/ +static int sqlitepager_opentemp(char *zFile, OsFile *fd){ + int cnt = 8; + int rc; + do{ + cnt--; + sqliteOsTempFileName(zFile); + rc = sqliteOsOpenExclusive(zFile, fd, 1); + }while( cnt>0 && rc!=SQLITE_OK ); + return rc; +} + +/* +** Create a new page cache and put a pointer to the page cache in *ppPager. +** The file to be cached need not exist. The file is not locked until +** the first call to sqlitepager_get() and is only held open until the +** last page is released using sqlitepager_unref(). +** +** If zFilename is NULL then a randomly-named temporary file is created +** and used as the file to be cached. The file will be deleted +** automatically when it is closed. +*/ +int sqlitepager_open( + Pager **ppPager, /* Return the Pager structure here */ + const char *zFilename, /* Name of the database file to open */ + int mxPage, /* Max number of in-memory cache pages */ + int nExtra, /* Extra bytes append to each in-memory page */ + int useJournal /* TRUE to use a rollback journal on this file */ +){ + Pager *pPager; + char *zFullPathname; + int nameLen; + OsFile fd; + int rc, i; + int tempFile; + int readOnly = 0; + char zTemp[SQLITE_TEMPNAME_SIZE]; + + *ppPager = 0; + if( sqlite_malloc_failed ){ + return SQLITE_NOMEM; + } + if( zFilename && zFilename[0] ){ + zFullPathname = sqliteOsFullPathname(zFilename); + rc = sqliteOsOpenReadWrite(zFullPathname, &fd, &readOnly); + tempFile = 0; + }else{ + rc = sqlitepager_opentemp(zTemp, &fd); + zFilename = zTemp; + zFullPathname = sqliteOsFullPathname(zFilename); + tempFile = 1; + } + if( sqlite_malloc_failed ){ + return SQLITE_NOMEM; + } + if( rc!=SQLITE_OK ){ + sqliteFree(zFullPathname); + return SQLITE_CANTOPEN; + } + nameLen = strlen(zFullPathname); + pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); + if( pPager==0 ){ + sqliteOsClose(&fd); + sqliteFree(zFullPathname); + return SQLITE_NOMEM; + } + SET_PAGER(pPager); + pPager->zFilename = (char*)&pPager[1]; + pPager->zDirectory = &pPager->zFilename[nameLen+1]; + pPager->zJournal = &pPager->zDirectory[nameLen+1]; + strcpy(pPager->zFilename, zFullPathname); + strcpy(pPager->zDirectory, zFullPathname); + for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){} + if( i>0 ) pPager->zDirectory[i-1] = 0; + strcpy(pPager->zJournal, zFullPathname); + sqliteFree(zFullPathname); + strcpy(&pPager->zJournal[nameLen], "-journal"); + pPager->fd = fd; + pPager->journalOpen = 0; + pPager->useJournal = useJournal; + pPager->ckptOpen = 0; + pPager->ckptInUse = 0; + pPager->nRef = 0; + pPager->dbSize = -1; + pPager->ckptSize = 0; + pPager->ckptJSize = 0; + pPager->nPage = 0; + pPager->mxPage = mxPage>5 ? mxPage : 10; + pPager->state = SQLITE_UNLOCK; + pPager->errMask = 0; + pPager->tempFile = tempFile; + pPager->readOnly = readOnly; + pPager->needSync = 0; + pPager->noSync = pPager->tempFile || !useJournal; + pPager->pFirst = 0; + pPager->pFirstSynced = 0; + pPager->pLast = 0; + pPager->nExtra = nExtra; + memset(pPager->aHash, 0, sizeof(pPager->aHash)); + *ppPager = pPager; + return SQLITE_OK; +} + +/* +** Set the destructor for this pager. If not NULL, the destructor is called +** when the reference count on each page reaches zero. The destructor can +** be used to clean up information in the extra segment appended to each page. +** +** The destructor is not called as a result sqlitepager_close(). +** Destructors are only called by sqlitepager_unref(). +*/ +void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){ + pPager->xDestructor = xDesc; +} + +/* +** Return the total number of pages in the disk file associated with +** pPager. +*/ +int sqlitepager_pagecount(Pager *pPager){ + off_t n; + assert( pPager!=0 ); + if( pPager->dbSize>=0 ){ + return pPager->dbSize; + } + if( sqliteOsFileSize(&pPager->fd, &n)!=SQLITE_OK ){ + pPager->errMask |= PAGER_ERR_DISK; + return 0; + } + n /= SQLITE_PAGE_SIZE; + if( pPager->state!=SQLITE_UNLOCK ){ + pPager->dbSize = n; + } + return n; +} + +/* +** Forward declaration +*/ +static int syncJournal(Pager*); + +/* +** Truncate the file to the number of pages specified. +*/ +int sqlitepager_truncate(Pager *pPager, Pgno nPage){ + int rc; + if( pPager->dbSize<0 ){ + sqlitepager_pagecount(pPager); + } + if( pPager->errMask!=0 ){ + rc = pager_errcode(pPager); + return rc; + } + if( nPage>=(unsigned)pPager->dbSize ){ + return SQLITE_OK; + } + syncJournal(pPager); + rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage); + if( rc==SQLITE_OK ){ + pPager->dbSize = nPage; + } + return rc; +} + +/* +** Shutdown the page cache. Free all memory and close all files. +** +** If a transaction was in progress when this routine is called, that +** transaction is rolled back. All outstanding pages are invalidated +** and their memory is freed. Any attempt to use a page associated +** with this page cache after this function returns will likely +** result in a coredump. +*/ +int sqlitepager_close(Pager *pPager){ + PgHdr *pPg, *pNext; + switch( pPager->state ){ + case SQLITE_WRITELOCK: { + sqlitepager_rollback(pPager); + sqliteOsUnlock(&pPager->fd); + assert( pPager->journalOpen==0 ); + break; + } + case SQLITE_READLOCK: { + sqliteOsUnlock(&pPager->fd); + break; + } + default: { + /* Do nothing */ + break; + } + } + for(pPg=pPager->pAll; pPg; pPg=pNext){ + pNext = pPg->pNextAll; + sqliteFree(pPg); + } + sqliteOsClose(&pPager->fd); + assert( pPager->journalOpen==0 ); + /* Temp files are automatically deleted by the OS + ** if( pPager->tempFile ){ + ** sqliteOsDelete(pPager->zFilename); + ** } + */ + CLR_PAGER(pPager); + if( pPager->zFilename!=(char*)&pPager[1] ){ + assert( 0 ); /* Cannot happen */ + sqliteFree(pPager->zFilename); + sqliteFree(pPager->zJournal); + sqliteFree(pPager->zDirectory); + } + sqliteFree(pPager); + return SQLITE_OK; +} + +/* +** Return the page number for the given page data. +*/ +Pgno sqlitepager_pagenumber(void *pData){ + PgHdr *p = DATA_TO_PGHDR(pData); + return p->pgno; +} + +/* +** Increment the reference count for a page. If the page is +** currently on the freelist (the reference count is zero) then +** remove it from the freelist. +*/ +#define page_ref(P) ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++) +static void _page_ref(PgHdr *pPg){ + if( pPg->nRef==0 ){ + /* The page is currently on the freelist. Remove it. */ + if( pPg==pPg->pPager->pFirstSynced ){ + PgHdr *p = pPg->pNextFree; + while( p && p->needSync ){ p = p->pNextFree; } + pPg->pPager->pFirstSynced = p; + } + if( pPg->pPrevFree ){ + pPg->pPrevFree->pNextFree = pPg->pNextFree; + }else{ + pPg->pPager->pFirst = pPg->pNextFree; + } + if( pPg->pNextFree ){ + pPg->pNextFree->pPrevFree = pPg->pPrevFree; + }else{ + pPg->pPager->pLast = pPg->pPrevFree; + } + pPg->pPager->nRef++; + } + pPg->nRef++; + REFINFO(pPg); +} + +/* +** Increment the reference count for a page. The input pointer is +** a reference to the page data. +*/ +int sqlitepager_ref(void *pData){ + PgHdr *pPg = DATA_TO_PGHDR(pData); + page_ref(pPg); + return SQLITE_OK; +} + +/* +** Sync the journal. In other words, make sure all the pages that have +** been written to the journal have actually reached the surface of the +** disk. It is not safe to modify the original database file until after +** the journal has been synced. If the original database is modified before +** the journal is synced and a power failure occurs, the unsynced journal +** data would be lost and we would be unable to completely rollback the +** database changes. Database corruption would occur. +** +** This routine also updates the nRec field in the header of the journal. +** (See comments on the pager_playback() routine for additional information.) +** If the sync mode is FULL, two syncs will occur. First the whole journal +** is synced, then the nRec field is updated, then a second sync occurs. +** +** For temporary databases, we do not care if we are able to rollback +** after a power failure, so sync occurs. +** +** This routine clears the needSync field of every page current held in +** memory. +*/ +static int syncJournal(Pager *pPager){ + PgHdr *pPg; + int rc = SQLITE_OK; + + /* Sync the journal before modifying the main database + ** (assuming there is a journal and it needs to be synced.) + */ + if( pPager->needSync ){ + if( !pPager->tempFile ){ + assert( pPager->journalOpen ); + /* assert( !pPager->noSync ); // noSync might be set if synchronous + ** was turned off after the transaction was started. Ticket #615 */ +#ifndef NDEBUG + { + /* Make sure the pPager->nRec counter we are keeping agrees + ** with the nRec computed from the size of the journal file. + */ + off_t hdrSz, pgSz, jSz; + hdrSz = JOURNAL_HDR_SZ(journal_format); + pgSz = JOURNAL_PG_SZ(journal_format); + rc = sqliteOsFileSize(&pPager->jfd, &jSz); + if( rc!=0 ) return rc; + assert( pPager->nRec*pgSz+hdrSz==jSz ); + } +#endif + if( journal_format>=3 ){ + /* Write the nRec value into the journal file header */ + off_t szJ; + if( pPager->fullSync ){ + TRACE1("SYNC\n"); + rc = sqliteOsSync(&pPager->jfd); + if( rc!=0 ) return rc; + } + sqliteOsSeek(&pPager->jfd, sizeof(aJournalMagic1)); + rc = write32bits(&pPager->jfd, pPager->nRec); + if( rc ) return rc; + szJ = JOURNAL_HDR_SZ(journal_format) + + pPager->nRec*JOURNAL_PG_SZ(journal_format); + sqliteOsSeek(&pPager->jfd, szJ); + } + TRACE1("SYNC\n"); + rc = sqliteOsSync(&pPager->jfd); + if( rc!=0 ) return rc; + pPager->journalStarted = 1; + } + pPager->needSync = 0; + + /* Erase the needSync flag from every page. + */ + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + pPg->needSync = 0; + } + pPager->pFirstSynced = pPager->pFirst; + } + +#ifndef NDEBUG + /* If the Pager.needSync flag is clear then the PgHdr.needSync + ** flag must also be clear for all pages. Verify that this + ** invariant is true. + */ + else{ + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + assert( pPg->needSync==0 ); + } + assert( pPager->pFirstSynced==pPager->pFirst ); + } +#endif + + return rc; +} + +/* +** Given a list of pages (connected by the PgHdr.pDirty pointer) write +** every one of those pages out to the database file and mark them all +** as clean. +*/ +static int pager_write_pagelist(PgHdr *pList){ + Pager *pPager; + int rc; + + if( pList==0 ) return SQLITE_OK; + pPager = pList->pPager; + while( pList ){ + assert( pList->dirty ); + sqliteOsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE); + CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); + TRACE2("STORE %d\n", pList->pgno); + rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE); + CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0); + if( rc ) return rc; + pList->dirty = 0; + pList = pList->pDirty; + } + return SQLITE_OK; +} + +/* +** Collect every dirty page into a dirty list and +** return a pointer to the head of that list. All pages are +** collected even if they are still in use. +*/ +static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ + PgHdr *p, *pList; + pList = 0; + for(p=pPager->pAll; p; p=p->pNextAll){ + if( p->dirty ){ + p->pDirty = pList; + pList = p; + } + } + return pList; +} + +/* +** Acquire a page. +** +** A read lock on the disk file is obtained when the first page is acquired. +** This read lock is dropped when the last page is released. +** +** A _get works for any page number greater than 0. If the database +** file is smaller than the requested page, then no actual disk +** read occurs and the memory image of the page is initialized to +** all zeros. The extra data appended to a page is always initialized +** to zeros the first time a page is loaded into memory. +** +** The acquisition might fail for several reasons. In all cases, +** an appropriate error code is returned and *ppPage is set to NULL. +** +** See also sqlitepager_lookup(). Both this routine and _lookup() attempt +** to find a page in the in-memory cache first. If the page is not already +** in memory, this routine goes to disk to read it in whereas _lookup() +** just returns 0. This routine acquires a read-lock the first time it +** has to go to disk, and could also playback an old journal if necessary. +** Since _lookup() never goes to disk, it never has to deal with locks +** or journal files. +*/ +int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){ + PgHdr *pPg; + int rc; + + /* Make sure we have not hit any critical errors. + */ + assert( pPager!=0 ); + assert( pgno!=0 ); + *ppPage = 0; + if( pPager->errMask & ~(PAGER_ERR_FULL) ){ + return pager_errcode(pPager); + } + + /* If this is the first page accessed, then get a read lock + ** on the database file. + */ + if( pPager->nRef==0 ){ + rc = sqliteOsReadLock(&pPager->fd); + if( rc!=SQLITE_OK ){ + return rc; + } + pPager->state = SQLITE_READLOCK; + + /* If a journal file exists, try to play it back. + */ + if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){ + int rc; + + /* Get a write lock on the database + */ + rc = sqliteOsWriteLock(&pPager->fd); + if( rc!=SQLITE_OK ){ + if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){ + /* This should never happen! */ + rc = SQLITE_INTERNAL; + } + return rc; + } + pPager->state = SQLITE_WRITELOCK; + + /* Open the journal for reading only. Return SQLITE_BUSY if + ** we are unable to open the journal file. + ** + ** The journal file does not need to be locked itself. The + ** journal file is never open unless the main database file holds + ** a write lock, so there is never any chance of two or more + ** processes opening the journal at the same time. + */ + rc = sqliteOsOpenReadOnly(pPager->zJournal, &pPager->jfd); + if( rc!=SQLITE_OK ){ + rc = sqliteOsUnlock(&pPager->fd); + assert( rc==SQLITE_OK ); + return SQLITE_BUSY; + } + pPager->journalOpen = 1; + pPager->journalStarted = 0; + + /* Playback and delete the journal. Drop the database write + ** lock and reacquire the read lock. + */ + rc = pager_playback(pPager, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + } + pPg = 0; + }else{ + /* Search for page in cache */ + pPg = pager_lookup(pPager, pgno); + } + if( pPg==0 ){ + /* The requested page is not in the page cache. */ + int h; + pPager->nMiss++; + if( pPager->nPagemxPage || pPager->pFirst==0 ){ + /* Create a new page */ + pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE + + sizeof(u32) + pPager->nExtra ); + if( pPg==0 ){ + pager_unwritelock(pPager); + pPager->errMask |= PAGER_ERR_MEM; + return SQLITE_NOMEM; + } + memset(pPg, 0, sizeof(*pPg)); + pPg->pPager = pPager; + pPg->pNextAll = pPager->pAll; + if( pPager->pAll ){ + pPager->pAll->pPrevAll = pPg; + } + pPg->pPrevAll = 0; + pPager->pAll = pPg; + pPager->nPage++; + }else{ + /* Find a page to recycle. Try to locate a page that does not + ** require us to do an fsync() on the journal. + */ + pPg = pPager->pFirstSynced; + + /* If we could not find a page that does not require an fsync() + ** on the journal file then fsync the journal file. This is a + ** very slow operation, so we work hard to avoid it. But sometimes + ** it can't be helped. + */ + if( pPg==0 ){ + int rc = syncJournal(pPager); + if( rc!=0 ){ + sqlitepager_rollback(pPager); + return SQLITE_IOERR; + } + pPg = pPager->pFirst; + } + assert( pPg->nRef==0 ); + + /* Write the page to the database file if it is dirty. + */ + if( pPg->dirty ){ + assert( pPg->needSync==0 ); + pPg->pDirty = 0; + rc = pager_write_pagelist( pPg ); + if( rc!=SQLITE_OK ){ + sqlitepager_rollback(pPager); + return SQLITE_IOERR; + } + } + assert( pPg->dirty==0 ); + + /* If the page we are recycling is marked as alwaysRollback, then + ** set the global alwaysRollback flag, thus disabling the + ** sqlite_dont_rollback() optimization for the rest of this transaction. + ** It is necessary to do this because the page marked alwaysRollback + ** might be reloaded at a later time but at that point we won't remember + ** that is was marked alwaysRollback. This means that all pages must + ** be marked as alwaysRollback from here on out. + */ + if( pPg->alwaysRollback ){ + pPager->alwaysRollback = 1; + } + + /* Unlink the old page from the free list and the hash table + */ + if( pPg==pPager->pFirstSynced ){ + PgHdr *p = pPg->pNextFree; + while( p && p->needSync ){ p = p->pNextFree; } + pPager->pFirstSynced = p; + } + if( pPg->pPrevFree ){ + pPg->pPrevFree->pNextFree = pPg->pNextFree; + }else{ + assert( pPager->pFirst==pPg ); + pPager->pFirst = pPg->pNextFree; + } + if( pPg->pNextFree ){ + pPg->pNextFree->pPrevFree = pPg->pPrevFree; + }else{ + assert( pPager->pLast==pPg ); + pPager->pLast = pPg->pPrevFree; + } + pPg->pNextFree = pPg->pPrevFree = 0; + if( pPg->pNextHash ){ + pPg->pNextHash->pPrevHash = pPg->pPrevHash; + } + if( pPg->pPrevHash ){ + pPg->pPrevHash->pNextHash = pPg->pNextHash; + }else{ + h = pager_hash(pPg->pgno); + assert( pPager->aHash[h]==pPg ); + pPager->aHash[h] = pPg->pNextHash; + } + pPg->pNextHash = pPg->pPrevHash = 0; + pPager->nOvfl++; + } + pPg->pgno = pgno; + if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){ + sqliteCheckMemory(pPager->aInJournal, pgno/8); + assert( pPager->journalOpen ); + pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0; + pPg->needSync = 0; + }else{ + pPg->inJournal = 0; + pPg->needSync = 0; + } + if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize + && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){ + page_add_to_ckpt_list(pPg); + }else{ + page_remove_from_ckpt_list(pPg); + } + pPg->dirty = 0; + pPg->nRef = 1; + REFINFO(pPg); + pPager->nRef++; + h = pager_hash(pgno); + pPg->pNextHash = pPager->aHash[h]; + pPager->aHash[h] = pPg; + if( pPg->pNextHash ){ + assert( pPg->pNextHash->pPrevHash==0 ); + pPg->pNextHash->pPrevHash = pPg; + } + if( pPager->nExtra>0 ){ + memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra); + } + if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager); + if( pPager->errMask!=0 ){ + sqlitepager_unref(PGHDR_TO_DATA(pPg)); + rc = pager_errcode(pPager); + return rc; + } + if( pPager->dbSize<(int)pgno ){ + memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE); + }else{ + int rc; + sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE); + rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE); + TRACE2("FETCH %d\n", pPg->pgno); + CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); + if( rc!=SQLITE_OK ){ + off_t fileSize; + if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK + || fileSize>=pgno*SQLITE_PAGE_SIZE ){ + sqlitepager_unref(PGHDR_TO_DATA(pPg)); + return rc; + }else{ + memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE); + } + } + } + }else{ + /* The requested page is in the page cache. */ + pPager->nHit++; + page_ref(pPg); + } + *ppPage = PGHDR_TO_DATA(pPg); + return SQLITE_OK; +} + +/* +** Acquire a page if it is already in the in-memory cache. Do +** not read the page from disk. Return a pointer to the page, +** or 0 if the page is not in cache. +** +** See also sqlitepager_get(). The difference between this routine +** and sqlitepager_get() is that _get() will go to the disk and read +** in the page if the page is not already in cache. This routine +** returns NULL if the page is not in cache or if a disk I/O error +** has ever happened. +*/ +void *sqlitepager_lookup(Pager *pPager, Pgno pgno){ + PgHdr *pPg; + + assert( pPager!=0 ); + assert( pgno!=0 ); + if( pPager->errMask & ~(PAGER_ERR_FULL) ){ + return 0; + } + /* if( pPager->nRef==0 ){ + ** return 0; + ** } + */ + pPg = pager_lookup(pPager, pgno); + if( pPg==0 ) return 0; + page_ref(pPg); + return PGHDR_TO_DATA(pPg); +} + +/* +** Release a page. +** +** If the number of references to the page drop to zero, then the +** page is added to the LRU list. When all references to all pages +** are released, a rollback occurs and the lock on the database is +** removed. +*/ +int sqlitepager_unref(void *pData){ + PgHdr *pPg; + + /* Decrement the reference count for this page + */ + pPg = DATA_TO_PGHDR(pData); + assert( pPg->nRef>0 ); + pPg->nRef--; + REFINFO(pPg); + + /* When the number of references to a page reach 0, call the + ** destructor and add the page to the freelist. + */ + if( pPg->nRef==0 ){ + Pager *pPager; + pPager = pPg->pPager; + pPg->pNextFree = 0; + pPg->pPrevFree = pPager->pLast; + pPager->pLast = pPg; + if( pPg->pPrevFree ){ + pPg->pPrevFree->pNextFree = pPg; + }else{ + pPager->pFirst = pPg; + } + if( pPg->needSync==0 && pPager->pFirstSynced==0 ){ + pPager->pFirstSynced = pPg; + } + if( pPager->xDestructor ){ + pPager->xDestructor(pData); + } + + /* When all pages reach the freelist, drop the read lock from + ** the database file. + */ + pPager->nRef--; + assert( pPager->nRef>=0 ); + if( pPager->nRef==0 ){ + pager_reset(pPager); + } + } + return SQLITE_OK; +} + +/* +** Create a journal file for pPager. There should already be a write +** lock on the database file when this routine is called. +** +** Return SQLITE_OK if everything. Return an error code and release the +** write lock if anything goes wrong. +*/ +static int pager_open_journal(Pager *pPager){ + int rc; + assert( pPager->state==SQLITE_WRITELOCK ); + assert( pPager->journalOpen==0 ); + assert( pPager->useJournal ); + sqlitepager_pagecount(pPager); + pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 ); + if( pPager->aInJournal==0 ){ + sqliteOsReadLock(&pPager->fd); + pPager->state = SQLITE_READLOCK; + return SQLITE_NOMEM; + } + rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile); + if( rc!=SQLITE_OK ){ + sqliteFree(pPager->aInJournal); + pPager->aInJournal = 0; + sqliteOsReadLock(&pPager->fd); + pPager->state = SQLITE_READLOCK; + return SQLITE_CANTOPEN; + } + sqliteOsOpenDirectory(pPager->zDirectory, &pPager->jfd); + pPager->journalOpen = 1; + pPager->journalStarted = 0; + pPager->needSync = 0; + pPager->alwaysRollback = 0; + pPager->nRec = 0; + if( pPager->errMask!=0 ){ + rc = pager_errcode(pPager); + return rc; + } + pPager->origDbSize = pPager->dbSize; + if( journal_format==JOURNAL_FORMAT_3 ){ + rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3)); + if( rc==SQLITE_OK ){ + rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0); + } + if( rc==SQLITE_OK ){ + sqliteRandomness(sizeof(pPager->cksumInit), &pPager->cksumInit); + rc = write32bits(&pPager->jfd, pPager->cksumInit); + } + }else if( journal_format==JOURNAL_FORMAT_2 ){ + rc = sqliteOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2)); + }else{ + assert( journal_format==JOURNAL_FORMAT_1 ); + rc = sqliteOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1)); + } + if( rc==SQLITE_OK ){ + rc = write32bits(&pPager->jfd, pPager->dbSize); + } + if( pPager->ckptAutoopen && rc==SQLITE_OK ){ + rc = sqlitepager_ckpt_begin(pPager); + } + if( rc!=SQLITE_OK ){ + rc = pager_unwritelock(pPager); + if( rc==SQLITE_OK ){ + rc = SQLITE_FULL; + } + } + return rc; +} + +/* +** Acquire a write-lock on the database. The lock is removed when +** the any of the following happen: +** +** * sqlitepager_commit() is called. +** * sqlitepager_rollback() is called. +** * sqlitepager_close() is called. +** * sqlitepager_unref() is called to on every outstanding page. +** +** The parameter to this routine is a pointer to any open page of the +** database file. Nothing changes about the page - it is used merely +** to acquire a pointer to the Pager structure and as proof that there +** is already a read-lock on the database. +** +** A journal file is opened if this is not a temporary file. For +** temporary files, the opening of the journal file is deferred until +** there is an actual need to write to the journal. +** +** If the database is already write-locked, this routine is a no-op. +*/ +int sqlitepager_begin(void *pData){ + PgHdr *pPg = DATA_TO_PGHDR(pData); + Pager *pPager = pPg->pPager; + int rc = SQLITE_OK; + assert( pPg->nRef>0 ); + assert( pPager->state!=SQLITE_UNLOCK ); + if( pPager->state==SQLITE_READLOCK ){ + assert( pPager->aInJournal==0 ); + rc = sqliteOsWriteLock(&pPager->fd); + if( rc!=SQLITE_OK ){ + return rc; + } + pPager->state = SQLITE_WRITELOCK; + pPager->dirtyFile = 0; + TRACE1("TRANSACTION\n"); + if( pPager->useJournal && !pPager->tempFile ){ + rc = pager_open_journal(pPager); + } + } + return rc; +} + +/* +** Mark a data page as writeable. The page is written into the journal +** if it is not there already. This routine must be called before making +** changes to a page. +** +** The first time this routine is called, the pager creates a new +** journal and acquires a write lock on the database. If the write +** lock could not be acquired, this routine returns SQLITE_BUSY. The +** calling routine must check for that return value and be careful not to +** change any page data until this routine returns SQLITE_OK. +** +** If the journal file could not be written because the disk is full, +** then this routine returns SQLITE_FULL and does an immediate rollback. +** All subsequent write attempts also return SQLITE_FULL until there +** is a call to sqlitepager_commit() or sqlitepager_rollback() to +** reset. +*/ +int sqlitepager_write(void *pData){ + PgHdr *pPg = DATA_TO_PGHDR(pData); + Pager *pPager = pPg->pPager; + int rc = SQLITE_OK; + + /* Check for errors + */ + if( pPager->errMask ){ + return pager_errcode(pPager); + } + if( pPager->readOnly ){ + return SQLITE_PERM; + } + + /* Mark the page as dirty. If the page has already been written + ** to the journal then we can return right away. + */ + pPg->dirty = 1; + if( pPg->inJournal && (pPg->inCkpt || pPager->ckptInUse==0) ){ + pPager->dirtyFile = 1; + return SQLITE_OK; + } + + /* If we get this far, it means that the page needs to be + ** written to the transaction journal or the ckeckpoint journal + ** or both. + ** + ** First check to see that the transaction journal exists and + ** create it if it does not. + */ + assert( pPager->state!=SQLITE_UNLOCK ); + rc = sqlitepager_begin(pData); + if( rc!=SQLITE_OK ){ + return rc; + } + assert( pPager->state==SQLITE_WRITELOCK ); + if( !pPager->journalOpen && pPager->useJournal ){ + rc = pager_open_journal(pPager); + if( rc!=SQLITE_OK ) return rc; + } + assert( pPager->journalOpen || !pPager->useJournal ); + pPager->dirtyFile = 1; + + /* The transaction journal now exists and we have a write lock on the + ** main database file. Write the current page to the transaction + ** journal if it is not there already. + */ + if( !pPg->inJournal && pPager->useJournal ){ + if( (int)pPg->pgno <= pPager->origDbSize ){ + int szPg; + u32 saved; + if( journal_format>=JOURNAL_FORMAT_3 ){ + u32 cksum = pager_cksum(pPager, pPg->pgno, pData); + saved = *(u32*)PGHDR_TO_EXTRA(pPg); + store32bits(cksum, pPg, SQLITE_PAGE_SIZE); + szPg = SQLITE_PAGE_SIZE+8; + }else{ + szPg = SQLITE_PAGE_SIZE+4; + } + store32bits(pPg->pgno, pPg, -4); + CODEC(pPager, pData, pPg->pgno, 7); + rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); + TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync); + CODEC(pPager, pData, pPg->pgno, 0); + if( journal_format>=JOURNAL_FORMAT_3 ){ + *(u32*)PGHDR_TO_EXTRA(pPg) = saved; + } + if( rc!=SQLITE_OK ){ + sqlitepager_rollback(pPager); + pPager->errMask |= PAGER_ERR_FULL; + return rc; + } + pPager->nRec++; + assert( pPager->aInJournal!=0 ); + pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); + pPg->needSync = !pPager->noSync; + pPg->inJournal = 1; + if( pPager->ckptInUse ){ + pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); + page_add_to_ckpt_list(pPg); + } + }else{ + pPg->needSync = !pPager->journalStarted && !pPager->noSync; + TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync); + } + if( pPg->needSync ){ + pPager->needSync = 1; + } + } + + /* If the checkpoint journal is open and the page is not in it, + ** then write the current page to the checkpoint journal. Note that + ** the checkpoint journal always uses the simplier format 2 that lacks + ** checksums. The header is also omitted from the checkpoint journal. + */ + if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){ + assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); + store32bits(pPg->pgno, pPg, -4); + CODEC(pPager, pData, pPg->pgno, 7); + rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4); + TRACE2("CKPT-JOURNAL %d\n", pPg->pgno); + CODEC(pPager, pData, pPg->pgno, 0); + if( rc!=SQLITE_OK ){ + sqlitepager_rollback(pPager); + pPager->errMask |= PAGER_ERR_FULL; + return rc; + } + pPager->ckptNRec++; + assert( pPager->aInCkpt!=0 ); + pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); + page_add_to_ckpt_list(pPg); + } + + /* Update the database size and return. + */ + if( pPager->dbSize<(int)pPg->pgno ){ + pPager->dbSize = pPg->pgno; + } + return rc; +} + +/* +** Return TRUE if the page given in the argument was previously passed +** to sqlitepager_write(). In other words, return TRUE if it is ok +** to change the content of the page. +*/ +int sqlitepager_iswriteable(void *pData){ + PgHdr *pPg = DATA_TO_PGHDR(pData); + return pPg->dirty; +} + +/* +** Replace the content of a single page with the information in the third +** argument. +*/ +int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void *pData){ + void *pPage; + int rc; + + rc = sqlitepager_get(pPager, pgno, &pPage); + if( rc==SQLITE_OK ){ + rc = sqlitepager_write(pPage); + if( rc==SQLITE_OK ){ + memcpy(pPage, pData, SQLITE_PAGE_SIZE); + } + sqlitepager_unref(pPage); + } + return rc; +} + +/* +** A call to this routine tells the pager that it is not necessary to +** write the information on page "pgno" back to the disk, even though +** that page might be marked as dirty. +** +** The overlying software layer calls this routine when all of the data +** on the given page is unused. The pager marks the page as clean so +** that it does not get written to disk. +** +** Tests show that this optimization, together with the +** sqlitepager_dont_rollback() below, more than double the speed +** of large INSERT operations and quadruple the speed of large DELETEs. +** +** When this routine is called, set the alwaysRollback flag to true. +** Subsequent calls to sqlitepager_dont_rollback() for the same page +** will thereafter be ignored. This is necessary to avoid a problem +** where a page with data is added to the freelist during one part of +** a transaction then removed from the freelist during a later part +** of the same transaction and reused for some other purpose. When it +** is first added to the freelist, this routine is called. When reused, +** the dont_rollback() routine is called. But because the page contains +** critical data, we still need to be sure it gets rolled back in spite +** of the dont_rollback() call. +*/ +void sqlitepager_dont_write(Pager *pPager, Pgno pgno){ + PgHdr *pPg; + + pPg = pager_lookup(pPager, pgno); + pPg->alwaysRollback = 1; + if( pPg && pPg->dirty && !pPager->ckptInUse ){ + if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSizedbSize ){ + /* If this pages is the last page in the file and the file has grown + ** during the current transaction, then do NOT mark the page as clean. + ** When the database file grows, we must make sure that the last page + ** gets written at least once so that the disk file will be the correct + ** size. If you do not write this page and the size of the file + ** on the disk ends up being too small, that can lead to database + ** corruption during the next transaction. + */ + }else{ + TRACE2("DONT_WRITE %d\n", pgno); + pPg->dirty = 0; + } + } +} + +/* +** A call to this routine tells the pager that if a rollback occurs, +** it is not necessary to restore the data on the given page. This +** means that the pager does not have to record the given page in the +** rollback journal. +*/ +void sqlitepager_dont_rollback(void *pData){ + PgHdr *pPg = DATA_TO_PGHDR(pData); + Pager *pPager = pPg->pPager; + + if( pPager->state!=SQLITE_WRITELOCK || pPager->journalOpen==0 ) return; + if( pPg->alwaysRollback || pPager->alwaysRollback ) return; + if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){ + assert( pPager->aInJournal!=0 ); + pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); + pPg->inJournal = 1; + if( pPager->ckptInUse ){ + pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); + page_add_to_ckpt_list(pPg); + } + TRACE2("DONT_ROLLBACK %d\n", pPg->pgno); + } + if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){ + assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); + assert( pPager->aInCkpt!=0 ); + pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7); + page_add_to_ckpt_list(pPg); + } +} + +/* +** Commit all changes to the database and release the write lock. +** +** If the commit fails for any reason, a rollback attempt is made +** and an error code is returned. If the commit worked, SQLITE_OK +** is returned. +*/ +int sqlitepager_commit(Pager *pPager){ + int rc; + PgHdr *pPg; + + if( pPager->errMask==PAGER_ERR_FULL ){ + rc = sqlitepager_rollback(pPager); + if( rc==SQLITE_OK ){ + rc = SQLITE_FULL; + } + return rc; + } + if( pPager->errMask!=0 ){ + rc = pager_errcode(pPager); + return rc; + } + if( pPager->state!=SQLITE_WRITELOCK ){ + return SQLITE_ERROR; + } + TRACE1("COMMIT\n"); + if( pPager->dirtyFile==0 ){ + /* Exit early (without doing the time-consuming sqliteOsSync() calls) + ** if there have been no changes to the database file. */ + assert( pPager->needSync==0 ); + rc = pager_unwritelock(pPager); + pPager->dbSize = -1; + return rc; + } + assert( pPager->journalOpen ); + rc = syncJournal(pPager); + if( rc!=SQLITE_OK ){ + goto commit_abort; + } + pPg = pager_get_all_dirty_pages(pPager); + if( pPg ){ + rc = pager_write_pagelist(pPg); + if( rc || (!pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK) ){ + goto commit_abort; + } + } + rc = pager_unwritelock(pPager); + pPager->dbSize = -1; + return rc; + + /* Jump here if anything goes wrong during the commit process. + */ +commit_abort: + rc = sqlitepager_rollback(pPager); + if( rc==SQLITE_OK ){ + rc = SQLITE_FULL; + } + return rc; +} + +/* +** Rollback all changes. The database falls back to read-only mode. +** All in-memory cache pages revert to their original data contents. +** The journal is deleted. +** +** This routine cannot fail unless some other process is not following +** the correct locking protocol (SQLITE_PROTOCOL) or unless some other +** process is writing trash into the journal file (SQLITE_CORRUPT) or +** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error +** codes are returned for all these occasions. Otherwise, +** SQLITE_OK is returned. +*/ +int sqlitepager_rollback(Pager *pPager){ + int rc; + TRACE1("ROLLBACK\n"); + if( !pPager->dirtyFile || !pPager->journalOpen ){ + rc = pager_unwritelock(pPager); + pPager->dbSize = -1; + return rc; + } + + if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){ + if( pPager->state>=SQLITE_WRITELOCK ){ + pager_playback(pPager, 1); + } + return pager_errcode(pPager); + } + if( pPager->state!=SQLITE_WRITELOCK ){ + return SQLITE_OK; + } + rc = pager_playback(pPager, 1); + if( rc!=SQLITE_OK ){ + rc = SQLITE_CORRUPT; + pPager->errMask |= PAGER_ERR_CORRUPT; + } + pPager->dbSize = -1; + return rc; +} + +/* +** Return TRUE if the database file is opened read-only. Return FALSE +** if the database is (in theory) writable. +*/ +int sqlitepager_isreadonly(Pager *pPager){ + return pPager->readOnly; +} + +/* +** This routine is used for testing and analysis only. +*/ +int *sqlitepager_stats(Pager *pPager){ + static int a[9]; + a[0] = pPager->nRef; + a[1] = pPager->nPage; + a[2] = pPager->mxPage; + a[3] = pPager->dbSize; + a[4] = pPager->state; + a[5] = pPager->errMask; + a[6] = pPager->nHit; + a[7] = pPager->nMiss; + a[8] = pPager->nOvfl; + return a; +} + +/* +** Set the checkpoint. +** +** This routine should be called with the transaction journal already +** open. A new checkpoint journal is created that can be used to rollback +** changes of a single SQL command within a larger transaction. +*/ +int sqlitepager_ckpt_begin(Pager *pPager){ + int rc; + char zTemp[SQLITE_TEMPNAME_SIZE]; + if( !pPager->journalOpen ){ + pPager->ckptAutoopen = 1; + return SQLITE_OK; + } + assert( pPager->journalOpen ); + assert( !pPager->ckptInUse ); + pPager->aInCkpt = sqliteMalloc( pPager->dbSize/8 + 1 ); + if( pPager->aInCkpt==0 ){ + sqliteOsReadLock(&pPager->fd); + return SQLITE_NOMEM; + } +#ifndef NDEBUG + rc = sqliteOsFileSize(&pPager->jfd, &pPager->ckptJSize); + if( rc ) goto ckpt_begin_failed; + assert( pPager->ckptJSize == + pPager->nRec*JOURNAL_PG_SZ(journal_format)+JOURNAL_HDR_SZ(journal_format) ); +#endif + pPager->ckptJSize = pPager->nRec*JOURNAL_PG_SZ(journal_format) + + JOURNAL_HDR_SZ(journal_format); + pPager->ckptSize = pPager->dbSize; + if( !pPager->ckptOpen ){ + rc = sqlitepager_opentemp(zTemp, &pPager->cpfd); + if( rc ) goto ckpt_begin_failed; + pPager->ckptOpen = 1; + pPager->ckptNRec = 0; + } + pPager->ckptInUse = 1; + return SQLITE_OK; + +ckpt_begin_failed: + if( pPager->aInCkpt ){ + sqliteFree(pPager->aInCkpt); + pPager->aInCkpt = 0; + } + return rc; +} + +/* +** Commit a checkpoint. +*/ +int sqlitepager_ckpt_commit(Pager *pPager){ + if( pPager->ckptInUse ){ + PgHdr *pPg, *pNext; + sqliteOsSeek(&pPager->cpfd, 0); + /* sqliteOsTruncate(&pPager->cpfd, 0); */ + pPager->ckptNRec = 0; + pPager->ckptInUse = 0; + sqliteFree( pPager->aInCkpt ); + pPager->aInCkpt = 0; + for(pPg=pPager->pCkpt; pPg; pPg=pNext){ + pNext = pPg->pNextCkpt; + assert( pPg->inCkpt ); + pPg->inCkpt = 0; + pPg->pPrevCkpt = pPg->pNextCkpt = 0; + } + pPager->pCkpt = 0; + } + pPager->ckptAutoopen = 0; + return SQLITE_OK; +} + +/* +** Rollback a checkpoint. +*/ +int sqlitepager_ckpt_rollback(Pager *pPager){ + int rc; + if( pPager->ckptInUse ){ + rc = pager_ckpt_playback(pPager); + sqlitepager_ckpt_commit(pPager); + }else{ + rc = SQLITE_OK; + } + pPager->ckptAutoopen = 0; + return rc; +} + +/* +** Return the full pathname of the database file. +*/ +const char *sqlitepager_filename(Pager *pPager){ + return pPager->zFilename; +} + +/* +** Set the codec for this pager +*/ +void sqlitepager_set_codec( + Pager *pPager, + void (*xCodec)(void*,void*,Pgno,int), + void *pCodecArg +){ + pPager->xCodec = xCodec; + pPager->pCodecArg = pCodecArg; +} + +#ifdef SQLITE_TEST +/* +** Print a listing of all referenced pages and their ref count. +*/ +void sqlitepager_refdump(Pager *pPager){ + PgHdr *pPg; + for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ + if( pPg->nRef<=0 ) continue; + printf("PAGE %3d addr=0x%08x nRef=%d\n", + pPg->pgno, (int)PGHDR_TO_DATA(pPg), pPg->nRef); + } +} +#endif diff --git a/src/libs/sqlite2/pager.h b/src/libs/sqlite2/pager.h new file mode 100644 index 00000000..96f9d406 --- /dev/null +++ b/src/libs/sqlite2/pager.h @@ -0,0 +1,107 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. The page cache subsystem reads and writes a file a page +** at a time and provides a journal for rollback. +** +** @(#) $Id: pager.h 326789 2004-07-07 21:25:56Z pahlibar $ +*/ + +/* +** The size of one page +** +** You can change this value to another (reasonable) value you want. +** It need not be a power of two, though the interface to the disk +** will likely be faster if it is. +** +** Experiments show that a page size of 1024 gives the best speed +** for common usages. The speed differences for different sizes +** such as 512, 2048, 4096, an so forth, is minimal. Note, however, +** that changing the page size results in a completely imcompatible +** file format. +*/ +#ifndef SQLITE_PAGE_SIZE +#define SQLITE_PAGE_SIZE 1024 +#endif + +/* +** Number of extra bytes of data allocated at the end of each page and +** stored on disk but not used by the higher level btree layer. Changing +** this value results in a completely incompatible file format. +*/ +#ifndef SQLITE_PAGE_RESERVE +#define SQLITE_PAGE_RESERVE 0 +#endif + +/* +** The total number of usable bytes stored on disk for each page. +** The usable bytes come at the beginning of the page and the reserve +** bytes come at the end. +*/ +#define SQLITE_USABLE_SIZE (SQLITE_PAGE_SIZE-SQLITE_PAGE_RESERVE) + +/* +** Maximum number of pages in one database. (This is a limitation of +** imposed by 4GB files size limits.) +*/ +#define SQLITE_MAX_PAGE 1073741823 + +/* +** The type used to represent a page number. The first page in a file +** is called page 1. 0 is used to represent "not a page". +*/ +typedef unsigned int Pgno; + +/* +** Each open file is managed by a separate instance of the "Pager" structure. +*/ +typedef struct Pager Pager; + +/* +** See source code comments for a detailed description of the following +** routines: +*/ +int sqlitepager_open(Pager **ppPager, const char *zFilename, + int nPage, int nExtra, int useJournal); +void sqlitepager_set_destructor(Pager*, void(*)(void*)); +void sqlitepager_set_cachesize(Pager*, int); +int sqlitepager_close(Pager *pPager); +int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage); +void *sqlitepager_lookup(Pager *pPager, Pgno pgno); +int sqlitepager_ref(void*); +int sqlitepager_unref(void*); +Pgno sqlitepager_pagenumber(void*); +int sqlitepager_write(void*); +int sqlitepager_iswriteable(void*); +int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*); +int sqlitepager_pagecount(Pager*); +int sqlitepager_truncate(Pager*,Pgno); +int sqlitepager_begin(void*); +int sqlitepager_commit(Pager*); +int sqlitepager_rollback(Pager*); +int sqlitepager_isreadonly(Pager*); +int sqlitepager_ckpt_begin(Pager*); +int sqlitepager_ckpt_commit(Pager*); +int sqlitepager_ckpt_rollback(Pager*); +void sqlitepager_dont_rollback(void*); +void sqlitepager_dont_write(Pager*, Pgno); +int *sqlitepager_stats(Pager*); +void sqlitepager_set_safety_level(Pager*,int); +const char *sqlitepager_filename(Pager*); +int sqlitepager_rename(Pager*, const char *zNewName); +void sqlitepager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*); + +#ifdef SQLITE_TEST +void sqlitepager_refdump(Pager*); +int pager_refinfo_enable; +int journal_format; +#endif diff --git a/src/libs/sqlite2/parse.c b/src/libs/sqlite2/parse.c new file mode 100644 index 00000000..46353691 --- /dev/null +++ b/src/libs/sqlite2/parse.c @@ -0,0 +1,4035 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is include which follows the "include" declaration +** in the input file. */ +#include +#line 33 "parse.y" + +#include "sqliteInt.h" +#include "parse.h" + +/* +** An instance of this structure holds information about the +** LIMIT clause of a SELECT statement. +*/ +struct LimitVal { + int limit; /* The LIMIT value. -1 if there is no limit */ + int offset; /* The OFFSET. 0 if there is none */ +}; + +/* +** An instance of the following structure describes the event of a +** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT, +** TK_DELETE, or TK_INSTEAD. If the event is of the form +** +** UPDATE ON (a,b,c) +** +** Then the "b" IdList records the list "a,b,c". +*/ +struct TrigEvent { int a; IdList * b; }; + + +#line 34 "parse.c" +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** sqliteParserTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is sqliteParserTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. +** sqliteParserARG_SDECL A static variable declaration for the %extra_argument +** sqliteParserARG_PDECL A parameter declaration for the %extra_argument +** sqliteParserARG_STORE Code to store %extra_argument into yypParser +** sqliteParserARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +/*  */ +#define YYCODETYPE unsigned char +#define YYNOCODE 221 +#define YYACTIONTYPE unsigned short int +#define sqliteParserTOKENTYPE Token +typedef union { + sqliteParserTOKENTYPE yy0; + TriggerStep * yy19; + struct LimitVal yy124; + Select* yy179; + Expr * yy182; + Expr* yy242; + struct TrigEvent yy290; + Token yy298; + SrcList* yy307; + IdList* yy320; + ExprList* yy322; + int yy372; + struct {int value; int mask;} yy407; + int yy441; +} YYMINORTYPE; +#define YYSTACKDEPTH 100 +#define sqliteParserARG_SDECL Parse *pParse; +#define sqliteParserARG_PDECL ,Parse *pParse +#define sqliteParserARG_FETCH Parse *pParse = yypParser->pParse +#define sqliteParserARG_STORE yypParser->pParse = pParse +#define YYNSTATE 563 +#define YYNRULE 293 +#define YYERRORSYMBOL 131 +#define YYERRSYMDT yy441 +#define YYFALLBACK 1 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static YYACTIONTYPE yy_action[] = { + /* 0 */ 264, 5, 262, 119, 123, 117, 121, 129, 131, 133, + /* 10 */ 135, 144, 146, 148, 150, 152, 154, 568, 106, 106, + /* 20 */ 143, 857, 1, 562, 3, 142, 129, 131, 133, 135, + /* 30 */ 144, 146, 148, 150, 152, 154, 174, 103, 8, 115, + /* 40 */ 104, 139, 127, 125, 156, 161, 157, 162, 166, 119, + /* 50 */ 123, 117, 121, 129, 131, 133, 135, 144, 146, 148, + /* 60 */ 150, 152, 154, 31, 361, 392, 263, 143, 363, 369, + /* 70 */ 374, 97, 142, 148, 150, 152, 154, 68, 75, 377, + /* 80 */ 167, 64, 218, 46, 20, 289, 115, 104, 139, 127, + /* 90 */ 125, 156, 161, 157, 162, 166, 119, 123, 117, 121, + /* 100 */ 129, 131, 133, 135, 144, 146, 148, 150, 152, 154, + /* 110 */ 193, 41, 336, 563, 44, 54, 60, 62, 308, 331, + /* 120 */ 175, 20, 560, 561, 572, 333, 640, 18, 359, 144, + /* 130 */ 146, 148, 150, 152, 154, 143, 181, 179, 303, 18, + /* 140 */ 142, 84, 86, 20, 177, 66, 67, 111, 21, 22, + /* 150 */ 112, 105, 83, 792, 115, 104, 139, 127, 125, 156, + /* 160 */ 161, 157, 162, 166, 119, 123, 117, 121, 129, 131, + /* 170 */ 133, 135, 144, 146, 148, 150, 152, 154, 790, 560, + /* 180 */ 561, 46, 13, 113, 183, 21, 22, 534, 361, 2, + /* 190 */ 3, 14, 363, 369, 374, 338, 361, 690, 544, 542, + /* 200 */ 363, 369, 374, 377, 836, 143, 15, 21, 22, 16, + /* 210 */ 142, 377, 44, 54, 60, 62, 308, 331, 396, 535, + /* 220 */ 17, 9, 191, 333, 115, 104, 139, 127, 125, 156, + /* 230 */ 161, 157, 162, 166, 119, 123, 117, 121, 129, 131, + /* 240 */ 133, 135, 144, 146, 148, 150, 152, 154, 571, 230, + /* 250 */ 340, 343, 143, 20, 536, 537, 538, 142, 402, 337, + /* 260 */ 398, 339, 357, 68, 346, 347, 32, 64, 266, 391, + /* 270 */ 37, 115, 104, 139, 127, 125, 156, 161, 157, 162, + /* 280 */ 166, 119, 123, 117, 121, 129, 131, 133, 135, 144, + /* 290 */ 146, 148, 150, 152, 154, 839, 193, 651, 291, 298, + /* 300 */ 300, 221, 357, 43, 173, 689, 175, 251, 330, 36, + /* 310 */ 37, 106, 232, 40, 335, 58, 137, 21, 22, 330, + /* 320 */ 411, 143, 181, 179, 47, 59, 142, 358, 390, 174, + /* 330 */ 177, 66, 67, 111, 448, 49, 112, 105, 583, 213, + /* 340 */ 115, 104, 139, 127, 125, 156, 161, 157, 162, 166, + /* 350 */ 119, 123, 117, 121, 129, 131, 133, 135, 144, 146, + /* 360 */ 148, 150, 152, 154, 306, 301, 106, 249, 259, 113, + /* 370 */ 183, 793, 70, 253, 281, 219, 20, 106, 20, 11, + /* 380 */ 106, 482, 454, 444, 299, 143, 169, 10, 171, 172, + /* 390 */ 142, 169, 73, 171, 172, 103, 688, 69, 174, 169, + /* 400 */ 252, 171, 172, 12, 115, 104, 139, 127, 125, 156, + /* 410 */ 161, 157, 162, 166, 119, 123, 117, 121, 129, 131, + /* 420 */ 133, 135, 144, 146, 148, 150, 152, 154, 95, 237, + /* 430 */ 313, 20, 143, 295, 244, 424, 169, 142, 171, 172, + /* 440 */ 21, 22, 21, 22, 219, 386, 316, 323, 325, 837, + /* 450 */ 19, 115, 104, 139, 127, 125, 156, 161, 157, 162, + /* 460 */ 166, 119, 123, 117, 121, 129, 131, 133, 135, 144, + /* 470 */ 146, 148, 150, 152, 154, 106, 661, 20, 264, 143, + /* 480 */ 262, 844, 315, 169, 142, 171, 172, 333, 38, 842, + /* 490 */ 10, 356, 348, 184, 421, 21, 22, 282, 115, 104, + /* 500 */ 139, 127, 125, 156, 161, 157, 162, 166, 119, 123, + /* 510 */ 117, 121, 129, 131, 133, 135, 144, 146, 148, 150, + /* 520 */ 152, 154, 69, 254, 262, 251, 143, 639, 663, 35, + /* 530 */ 65, 142, 726, 313, 283, 259, 185, 417, 419, 418, + /* 540 */ 284, 21, 22, 690, 263, 115, 104, 139, 127, 125, + /* 550 */ 156, 161, 157, 162, 166, 119, 123, 117, 121, 129, + /* 560 */ 131, 133, 135, 144, 146, 148, 150, 152, 154, 256, + /* 570 */ 20, 791, 424, 143, 169, 52, 171, 172, 142, 169, + /* 580 */ 24, 171, 172, 247, 53, 315, 26, 169, 263, 171, + /* 590 */ 172, 253, 115, 164, 139, 127, 125, 156, 161, 157, + /* 600 */ 162, 166, 119, 123, 117, 121, 129, 131, 133, 135, + /* 610 */ 144, 146, 148, 150, 152, 154, 426, 349, 252, 425, + /* 620 */ 143, 262, 575, 297, 591, 142, 169, 296, 171, 172, + /* 630 */ 169, 471, 171, 172, 21, 22, 427, 221, 91, 115, + /* 640 */ 227, 139, 127, 125, 156, 161, 157, 162, 166, 119, + /* 650 */ 123, 117, 121, 129, 131, 133, 135, 144, 146, 148, + /* 660 */ 150, 152, 154, 388, 312, 106, 89, 143, 720, 376, + /* 670 */ 387, 170, 142, 487, 666, 248, 320, 216, 319, 217, + /* 680 */ 28, 459, 30, 305, 189, 263, 209, 104, 139, 127, + /* 690 */ 125, 156, 161, 157, 162, 166, 119, 123, 117, 121, + /* 700 */ 129, 131, 133, 135, 144, 146, 148, 150, 152, 154, + /* 710 */ 106, 106, 809, 494, 143, 489, 106, 816, 33, 142, + /* 720 */ 395, 234, 273, 217, 274, 420, 20, 545, 114, 481, + /* 730 */ 137, 429, 576, 321, 116, 139, 127, 125, 156, 161, + /* 740 */ 157, 162, 166, 119, 123, 117, 121, 129, 131, 133, + /* 750 */ 135, 144, 146, 148, 150, 152, 154, 7, 322, 23, + /* 760 */ 25, 27, 394, 68, 415, 416, 10, 64, 197, 477, + /* 770 */ 577, 533, 266, 548, 578, 831, 276, 201, 520, 4, + /* 780 */ 6, 245, 430, 557, 29, 266, 491, 106, 441, 497, + /* 790 */ 21, 22, 205, 168, 443, 195, 193, 531, 276, 448, + /* 800 */ 276, 808, 267, 272, 529, 174, 175, 318, 440, 341, + /* 810 */ 344, 106, 342, 345, 69, 286, 68, 582, 69, 69, + /* 820 */ 64, 540, 181, 179, 541, 328, 302, 366, 217, 118, + /* 830 */ 177, 66, 67, 111, 34, 143, 112, 105, 445, 510, + /* 840 */ 142, 215, 278, 800, 467, 276, 498, 503, 444, 193, + /* 850 */ 106, 219, 486, 443, 42, 73, 231, 73, 45, 175, + /* 860 */ 449, 39, 225, 229, 278, 451, 278, 68, 174, 113, + /* 870 */ 183, 64, 371, 55, 106, 181, 179, 292, 69, 276, + /* 880 */ 276, 69, 48, 177, 66, 67, 111, 224, 276, 112, + /* 890 */ 105, 106, 481, 393, 106, 106, 63, 106, 106, 106, + /* 900 */ 193, 653, 106, 467, 233, 51, 380, 437, 526, 120, + /* 910 */ 175, 278, 122, 124, 219, 126, 128, 130, 69, 453, + /* 920 */ 132, 106, 113, 183, 451, 106, 181, 179, 159, 106, + /* 930 */ 106, 106, 518, 106, 177, 66, 67, 111, 106, 134, + /* 940 */ 112, 105, 422, 136, 106, 278, 278, 138, 141, 145, + /* 950 */ 720, 147, 106, 329, 275, 274, 149, 106, 852, 158, + /* 960 */ 106, 106, 151, 106, 106, 351, 106, 352, 106, 464, + /* 970 */ 153, 106, 106, 113, 183, 155, 106, 106, 163, 165, + /* 980 */ 106, 176, 178, 106, 180, 106, 182, 106, 401, 190, + /* 990 */ 192, 106, 106, 293, 210, 212, 106, 367, 214, 274, + /* 1000 */ 372, 226, 274, 228, 381, 241, 274, 106, 106, 246, + /* 1010 */ 280, 290, 106, 69, 375, 438, 472, 274, 422, 832, + /* 1020 */ 106, 73, 474, 73, 458, 412, 462, 480, 464, 478, + /* 1030 */ 466, 690, 515, 519, 475, 478, 516, 50, 479, 221, + /* 1040 */ 690, 221, 56, 57, 61, 592, 71, 69, 593, 73, + /* 1050 */ 72, 74, 245, 242, 93, 81, 76, 69, 77, 240, + /* 1060 */ 78, 82, 79, 245, 85, 554, 80, 88, 87, 90, + /* 1070 */ 92, 94, 96, 102, 100, 99, 101, 107, 109, 160, + /* 1080 */ 154, 667, 98, 508, 108, 668, 110, 220, 211, 669, + /* 1090 */ 137, 140, 188, 194, 186, 196, 187, 199, 198, 200, + /* 1100 */ 203, 204, 202, 207, 206, 208, 221, 223, 222, 235, + /* 1110 */ 236, 239, 238, 217, 250, 258, 243, 261, 279, 270, + /* 1120 */ 271, 255, 257, 260, 269, 265, 285, 294, 277, 268, + /* 1130 */ 287, 304, 309, 307, 327, 312, 288, 354, 389, 314, + /* 1140 */ 364, 365, 370, 378, 379, 382, 310, 49, 311, 362, + /* 1150 */ 368, 373, 317, 324, 326, 332, 350, 355, 383, 400, + /* 1160 */ 353, 397, 399, 403, 404, 334, 405, 406, 407, 384, + /* 1170 */ 413, 409, 824, 414, 360, 385, 829, 423, 410, 431, + /* 1180 */ 428, 432, 830, 433, 434, 436, 439, 798, 799, 447, + /* 1190 */ 442, 450, 727, 728, 446, 823, 452, 838, 455, 445, + /* 1200 */ 456, 457, 408, 435, 460, 461, 463, 840, 465, 468, + /* 1210 */ 470, 469, 476, 841, 483, 485, 843, 660, 662, 493, + /* 1220 */ 806, 496, 473, 849, 499, 719, 501, 484, 488, 490, + /* 1230 */ 492, 502, 504, 495, 500, 507, 505, 506, 509, 722, + /* 1240 */ 513, 511, 512, 514, 517, 725, 528, 522, 524, 525, + /* 1250 */ 527, 523, 807, 530, 810, 532, 811, 812, 813, 814, + /* 1260 */ 817, 819, 539, 820, 818, 815, 521, 543, 546, 552, + /* 1270 */ 556, 550, 850, 547, 549, 851, 555, 558, 551, 855, + /* 1280 */ 553, 559, +}; +static YYCODETYPE yy_lookahead[] = { + /* 0 */ 21, 9, 23, 70, 71, 72, 73, 74, 75, 76, + /* 10 */ 77, 78, 79, 80, 81, 82, 83, 9, 140, 140, + /* 20 */ 41, 132, 133, 134, 135, 46, 74, 75, 76, 77, + /* 30 */ 78, 79, 80, 81, 82, 83, 158, 158, 138, 60, + /* 40 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + /* 50 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 60 */ 81, 82, 83, 19, 90, 21, 87, 41, 94, 95, + /* 70 */ 96, 192, 46, 80, 81, 82, 83, 19, 174, 105, + /* 80 */ 19, 23, 204, 62, 23, 181, 60, 61, 62, 63, + /* 90 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + /* 100 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + /* 110 */ 52, 90, 91, 0, 93, 94, 95, 96, 97, 98, + /* 120 */ 62, 23, 9, 10, 9, 104, 20, 12, 22, 78, + /* 130 */ 79, 80, 81, 82, 83, 41, 78, 79, 80, 12, + /* 140 */ 46, 78, 79, 23, 86, 87, 88, 89, 87, 88, + /* 150 */ 92, 93, 89, 127, 60, 61, 62, 63, 64, 65, + /* 160 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + /* 170 */ 76, 77, 78, 79, 80, 81, 82, 83, 14, 9, + /* 180 */ 10, 62, 15, 125, 126, 87, 88, 140, 90, 134, + /* 190 */ 135, 24, 94, 95, 96, 23, 90, 9, 78, 79, + /* 200 */ 94, 95, 96, 105, 11, 41, 39, 87, 88, 42, + /* 210 */ 46, 105, 93, 94, 95, 96, 97, 98, 17, 99, + /* 220 */ 53, 139, 128, 104, 60, 61, 62, 63, 64, 65, + /* 230 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + /* 240 */ 76, 77, 78, 79, 80, 81, 82, 83, 9, 19, + /* 250 */ 78, 79, 41, 23, 207, 208, 209, 46, 57, 87, + /* 260 */ 59, 89, 140, 19, 92, 93, 144, 23, 152, 147, + /* 270 */ 148, 60, 61, 62, 63, 64, 65, 66, 67, 68, + /* 280 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + /* 290 */ 79, 80, 81, 82, 83, 14, 52, 9, 182, 20, + /* 300 */ 20, 113, 140, 156, 20, 20, 62, 22, 161, 147, + /* 310 */ 148, 140, 20, 155, 156, 26, 200, 87, 88, 161, + /* 320 */ 127, 41, 78, 79, 93, 36, 46, 165, 166, 158, + /* 330 */ 86, 87, 88, 89, 53, 104, 92, 93, 9, 128, + /* 340 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + /* 350 */ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + /* 360 */ 80, 81, 82, 83, 20, 194, 140, 183, 184, 125, + /* 370 */ 126, 127, 146, 88, 19, 204, 23, 140, 23, 31, + /* 380 */ 140, 100, 101, 102, 158, 41, 107, 99, 109, 110, + /* 390 */ 46, 107, 111, 109, 110, 158, 20, 171, 158, 107, + /* 400 */ 115, 109, 110, 170, 60, 61, 62, 63, 64, 65, + /* 410 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + /* 420 */ 76, 77, 78, 79, 80, 81, 82, 83, 191, 192, + /* 430 */ 47, 23, 41, 80, 194, 140, 107, 46, 109, 110, + /* 440 */ 87, 88, 87, 88, 204, 62, 100, 101, 102, 11, + /* 450 */ 140, 60, 61, 62, 63, 64, 65, 66, 67, 68, + /* 460 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + /* 470 */ 79, 80, 81, 82, 83, 140, 9, 23, 21, 41, + /* 480 */ 23, 9, 99, 107, 46, 109, 110, 104, 149, 9, + /* 490 */ 99, 152, 153, 158, 199, 87, 88, 146, 60, 61, + /* 500 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + /* 510 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + /* 520 */ 82, 83, 171, 115, 23, 22, 41, 20, 9, 22, + /* 530 */ 19, 46, 9, 47, 183, 184, 201, 100, 101, 102, + /* 540 */ 189, 87, 88, 19, 87, 60, 61, 62, 63, 64, + /* 550 */ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + /* 560 */ 75, 76, 77, 78, 79, 80, 81, 82, 83, 115, + /* 570 */ 23, 14, 140, 41, 107, 34, 109, 110, 46, 107, + /* 580 */ 138, 109, 110, 22, 43, 99, 138, 107, 87, 109, + /* 590 */ 110, 88, 60, 61, 62, 63, 64, 65, 66, 67, + /* 600 */ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + /* 610 */ 78, 79, 80, 81, 82, 83, 25, 19, 115, 28, + /* 620 */ 41, 23, 9, 108, 113, 46, 107, 112, 109, 110, + /* 630 */ 107, 199, 109, 110, 87, 88, 45, 113, 22, 60, + /* 640 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + /* 650 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 660 */ 81, 82, 83, 161, 162, 140, 50, 41, 9, 139, + /* 670 */ 168, 108, 46, 17, 111, 114, 91, 20, 93, 22, + /* 680 */ 138, 22, 142, 158, 127, 87, 129, 61, 62, 63, + /* 690 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + /* 700 */ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + /* 710 */ 140, 140, 9, 57, 41, 59, 140, 9, 145, 46, + /* 720 */ 143, 20, 20, 22, 22, 49, 23, 19, 158, 158, + /* 730 */ 200, 18, 9, 29, 158, 62, 63, 64, 65, 66, + /* 740 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + /* 750 */ 77, 78, 79, 80, 81, 82, 83, 11, 54, 13, + /* 760 */ 14, 15, 16, 19, 55, 56, 99, 23, 15, 198, + /* 770 */ 9, 63, 152, 27, 9, 99, 140, 24, 32, 136, + /* 780 */ 137, 122, 205, 37, 141, 152, 130, 140, 211, 146, + /* 790 */ 87, 88, 39, 146, 146, 42, 52, 51, 140, 53, + /* 800 */ 140, 9, 182, 167, 58, 158, 62, 103, 95, 89, + /* 810 */ 89, 140, 92, 92, 171, 182, 19, 9, 171, 171, + /* 820 */ 23, 89, 78, 79, 92, 167, 20, 167, 22, 158, + /* 830 */ 86, 87, 88, 89, 20, 41, 92, 93, 60, 196, + /* 840 */ 46, 194, 206, 130, 196, 140, 100, 101, 102, 52, + /* 850 */ 140, 204, 106, 146, 140, 111, 146, 111, 139, 62, + /* 860 */ 212, 150, 68, 69, 206, 217, 206, 19, 158, 125, + /* 870 */ 126, 23, 167, 48, 140, 78, 79, 80, 171, 140, + /* 880 */ 140, 171, 139, 86, 87, 88, 89, 93, 140, 92, + /* 890 */ 93, 140, 158, 146, 140, 140, 19, 140, 140, 140, + /* 900 */ 52, 123, 140, 196, 194, 44, 167, 167, 116, 158, + /* 910 */ 62, 206, 158, 158, 204, 158, 158, 158, 171, 212, + /* 920 */ 158, 140, 125, 126, 217, 140, 78, 79, 62, 140, + /* 930 */ 140, 140, 198, 140, 86, 87, 88, 89, 140, 158, + /* 940 */ 92, 93, 22, 158, 140, 206, 206, 158, 158, 158, + /* 950 */ 9, 158, 140, 20, 206, 22, 158, 140, 9, 93, + /* 960 */ 140, 140, 158, 140, 140, 20, 140, 22, 140, 140, + /* 970 */ 158, 140, 140, 125, 126, 158, 140, 140, 158, 158, + /* 980 */ 140, 158, 158, 140, 158, 140, 158, 140, 146, 158, + /* 990 */ 158, 140, 140, 140, 158, 158, 140, 20, 158, 22, + /* 1000 */ 20, 158, 22, 158, 20, 158, 22, 140, 140, 158, + /* 1010 */ 158, 158, 140, 171, 158, 20, 20, 22, 22, 99, + /* 1020 */ 140, 111, 146, 111, 195, 158, 158, 20, 140, 22, + /* 1030 */ 158, 103, 146, 20, 124, 22, 124, 164, 158, 113, + /* 1040 */ 114, 113, 157, 139, 139, 113, 172, 171, 113, 111, + /* 1050 */ 171, 173, 122, 119, 117, 180, 175, 171, 176, 120, + /* 1060 */ 177, 121, 178, 122, 89, 116, 179, 154, 89, 154, + /* 1070 */ 154, 118, 22, 151, 98, 157, 23, 113, 113, 93, + /* 1080 */ 83, 111, 193, 195, 140, 111, 140, 140, 127, 111, + /* 1090 */ 200, 200, 14, 19, 202, 20, 203, 140, 22, 20, + /* 1100 */ 140, 20, 22, 140, 22, 20, 113, 186, 140, 140, + /* 1110 */ 186, 157, 193, 22, 185, 115, 118, 186, 99, 116, + /* 1120 */ 19, 140, 140, 140, 188, 140, 20, 113, 157, 187, + /* 1130 */ 187, 20, 140, 139, 19, 162, 188, 20, 166, 140, + /* 1140 */ 48, 19, 19, 48, 19, 97, 159, 104, 160, 140, + /* 1150 */ 139, 139, 163, 163, 163, 151, 154, 152, 140, 21, + /* 1160 */ 154, 140, 140, 140, 213, 164, 214, 99, 140, 159, + /* 1170 */ 40, 215, 11, 38, 166, 160, 99, 140, 216, 130, + /* 1180 */ 49, 140, 99, 99, 140, 19, 139, 9, 130, 169, + /* 1190 */ 11, 14, 123, 123, 170, 9, 9, 14, 169, 60, + /* 1200 */ 140, 103, 186, 186, 140, 63, 176, 9, 63, 123, + /* 1210 */ 19, 140, 19, 9, 114, 176, 9, 9, 9, 186, + /* 1220 */ 9, 186, 197, 9, 114, 9, 186, 140, 140, 140, + /* 1230 */ 140, 176, 169, 140, 140, 103, 140, 186, 176, 9, + /* 1240 */ 186, 123, 140, 197, 19, 9, 87, 140, 114, 140, + /* 1250 */ 35, 186, 9, 140, 9, 152, 9, 9, 9, 9, + /* 1260 */ 9, 9, 210, 9, 9, 9, 169, 210, 140, 140, + /* 1270 */ 33, 152, 9, 20, 218, 9, 152, 218, 21, 9, + /* 1280 */ 219, 140, +}; +#define YY_SHIFT_USE_DFLT (-68) +static short yy_shift_ofst[] = { + /* 0 */ 170, 113, -68, 746, -8, -68, 8, 127, 288, 239, + /* 10 */ 348, 167, -68, -68, -68, -68, -68, -68, 547, -68, + /* 20 */ -68, -68, -68, 115, 613, 115, 723, 115, 761, 44, + /* 30 */ 765, 547, 507, 814, 808, 98, -68, 501, -68, 21, + /* 40 */ -68, 547, 119, -68, 667, -68, 231, 667, -68, 861, + /* 50 */ -68, 541, -68, -68, 825, 289, 667, -68, -68, -68, + /* 60 */ 667, -68, 877, 848, 511, 58, 932, 935, 744, -68, + /* 70 */ 279, 938, -68, 515, -68, 561, 930, 934, 939, 937, + /* 80 */ 940, -68, 63, -68, 975, -68, 979, -68, 616, 63, + /* 90 */ -68, 63, -68, 953, 848, 1050, 848, 976, 289, -68, + /* 100 */ 1053, -68, -68, 485, 848, -68, 964, 547, 965, 547, + /* 110 */ -68, -68, -68, -68, 673, 848, 626, 848, -48, 848, + /* 120 */ -48, 848, -48, 848, -48, 848, -67, 848, -67, 848, + /* 130 */ 51, 848, 51, 848, 51, 848, 51, 848, -67, 794, + /* 140 */ 848, -67, -68, -68, 848, -7, 848, -7, 848, 997, + /* 150 */ 848, 997, 848, 997, 848, -68, -68, 866, -68, 986, + /* 160 */ -68, -68, 848, 532, 848, -67, 61, 744, 284, 563, + /* 170 */ 970, 974, 978, -68, 485, 848, 673, 848, -68, 848, + /* 180 */ -68, 848, -68, 244, 26, 961, 557, 1078, -68, 848, + /* 190 */ 94, 848, 485, 1074, 753, 1075, -68, 1076, 547, 1079, + /* 200 */ -68, 1080, 547, 1081, -68, 1082, 547, 1085, -68, 848, + /* 210 */ 164, 848, 211, 848, 485, 657, -68, 848, -68, -68, + /* 220 */ 993, 547, -68, -68, -68, 848, 579, 848, 673, 230, + /* 230 */ 744, 292, -68, 701, -68, 993, -68, 976, 289, -68, + /* 240 */ 848, 485, 998, 848, 1091, 848, 485, -68, -68, 503, + /* 250 */ -68, -68, -68, 408, -68, 454, -68, 1000, -68, 355, + /* 260 */ 993, 457, -68, -68, 547, -68, -68, 1019, 1003, -68, + /* 270 */ 1101, 547, 702, -68, 547, -68, 289, -68, -68, 848, + /* 280 */ 485, 938, 376, 285, 1106, 457, 1019, 1003, -68, 797, + /* 290 */ -21, -68, -68, 1014, 353, -68, -68, -68, -68, 280, + /* 300 */ -68, 806, -68, 1111, -68, 344, 667, -68, 547, 1115, + /* 310 */ -68, 486, -68, 547, -68, 346, 704, -68, 585, -68, + /* 320 */ -68, -68, -68, 704, -68, 704, -68, 547, 933, -68, + /* 330 */ -68, 1053, -68, 861, -68, -68, 172, -68, -68, -68, + /* 340 */ 720, -68, -68, 721, -68, -68, -68, -68, 598, 63, + /* 350 */ 945, -68, 63, 1117, -68, -68, -68, -68, 106, -26, + /* 360 */ -68, 547, -68, 1092, 1122, 547, 977, 667, -68, 1123, + /* 370 */ 547, 980, 667, -68, 848, 391, -68, 1095, 1125, 547, + /* 380 */ 984, 1048, 547, 1115, -68, 383, 1043, -68, -68, -68, + /* 390 */ -68, -68, 938, 329, 713, 201, 547, -68, 547, 1138, + /* 400 */ 938, 467, 547, 591, 437, 1068, 547, 993, 1130, 193, + /* 410 */ 1161, 848, 438, 1135, 709, -68, -68, 1077, 1083, 676, + /* 420 */ 547, 920, 547, -68, -68, -68, -68, 1131, -68, -68, + /* 430 */ 1049, 547, 1084, 547, 524, 1166, 547, 995, 288, 1178, + /* 440 */ 1058, 1179, 281, 472, 778, 167, -68, 1069, 1070, 1177, + /* 450 */ 1186, 1187, 281, 1183, 1139, 547, 1098, 547, 659, 547, + /* 460 */ 1142, 848, 485, 1198, 1145, 848, 485, 1086, 547, 1191, + /* 470 */ 547, 996, -68, 910, 480, 1193, 848, 1007, 848, 485, + /* 480 */ 1204, 485, 1100, 547, 941, 1207, 656, 547, 1208, 547, + /* 490 */ 1209, 547, 188, 1211, 547, 188, 1214, 519, 1110, 547, + /* 500 */ 993, 941, 1216, 1139, 547, 928, 1132, 547, 659, 1230, + /* 510 */ 1118, 547, 993, 1191, 912, 523, 1225, 848, 1013, 1236, + /* 520 */ 1139, 547, 926, 1134, 547, 792, 1215, 1159, 1243, 703, + /* 530 */ 1245, 501, 708, 120, 1247, 1248, 1249, 1250, 732, 1251, + /* 540 */ 1252, 1254, 732, 1255, -68, 547, 1253, 1256, 1237, 501, + /* 550 */ 1257, 547, 949, 1263, 501, 1266, -68, 1237, 547, 1270, + /* 560 */ -68, -68, -68, +}; +#define YY_REDUCE_USE_DFLT (-123) +static short yy_reduce_ofst[] = { + /* 0 */ -111, 55, -123, 643, -123, -123, -123, -100, 82, -123, + /* 10 */ -123, 233, -123, -123, -123, -123, -123, -123, 310, -123, + /* 20 */ -123, -123, -123, 442, -123, 448, -123, 542, -123, 540, + /* 30 */ -123, 122, 573, -123, -123, 162, -123, 339, 711, 158, + /* 40 */ -123, 714, 147, -123, 719, -123, -123, 743, -123, 873, + /* 50 */ -123, -123, -123, -123, -123, 885, 904, -123, -123, -123, + /* 60 */ 905, -123, -123, 525, -123, 171, -123, -123, 226, -123, + /* 70 */ 874, 879, -123, 878, -96, 881, 882, 883, 884, 887, + /* 80 */ 875, -123, 913, -123, -123, -123, -123, -123, -123, 915, + /* 90 */ -123, 916, -123, -123, 237, -123, -121, 889, 918, -123, + /* 100 */ 922, -123, -123, 890, 570, -123, -123, 944, -123, 946, + /* 110 */ -123, -123, -123, -123, 890, 576, 890, 671, 890, 751, + /* 120 */ 890, 754, 890, 755, 890, 757, 890, 758, 890, 759, + /* 130 */ 890, 762, 890, 781, 890, 785, 890, 789, 890, 891, + /* 140 */ 790, 890, -123, -123, 791, 890, 793, 890, 798, 890, + /* 150 */ 804, 890, 812, 890, 817, 890, -123, -123, -123, -123, + /* 160 */ -123, -123, 820, 890, 821, 890, 947, 647, 874, -123, + /* 170 */ -123, -123, -123, -123, 890, 823, 890, 824, 890, 826, + /* 180 */ 890, 828, 890, 335, 890, 892, 893, -123, -123, 831, + /* 190 */ 890, 832, 890, -123, -123, -123, -123, -123, 957, -123, + /* 200 */ -123, -123, 960, -123, -123, -123, 963, -123, -123, 836, + /* 210 */ 890, 837, 890, 840, 890, -123, -123, -122, -123, -123, + /* 220 */ 921, 968, -123, -123, -123, 843, 890, 845, 890, 969, + /* 230 */ 710, 874, -123, -123, -123, 924, -123, 919, 954, -123, + /* 240 */ 847, 890, -123, 240, -123, 851, 890, -123, 184, 929, + /* 250 */ -123, -123, -123, 981, -123, 982, -123, -123, -123, 983, + /* 260 */ 931, 620, -123, -123, 985, -123, -123, 942, 936, -123, + /* 270 */ -123, 636, -123, -123, 748, -123, 971, -123, -123, 852, + /* 280 */ 890, 351, 874, 929, -123, 633, 943, 948, -123, 853, + /* 290 */ 116, -123, -123, -123, 944, -123, -123, -123, -123, 890, + /* 300 */ -123, -123, -123, -123, -123, 890, 994, -123, 992, 987, + /* 310 */ 988, 973, -123, 999, -123, -123, 989, -123, -123, -123, + /* 320 */ -123, -123, -123, 990, -123, 991, -123, 658, -123, -123, + /* 330 */ -123, 1004, -123, 1001, -123, -123, -123, -123, -123, -123, + /* 340 */ -123, -123, -123, -123, -123, -123, -123, -123, 1005, 1002, + /* 350 */ -123, -123, 1006, -123, -123, -123, -123, -123, 972, 1008, + /* 360 */ -123, 1009, -123, -123, -123, 660, -123, 1011, -123, -123, + /* 370 */ 705, -123, 1012, -123, 856, 530, -123, -123, -123, 739, + /* 380 */ -123, -123, 1018, 1010, 1015, 502, -123, -123, -123, -123, + /* 390 */ -123, -123, 747, 874, 577, -123, 1021, -123, 1022, -123, + /* 400 */ 842, 874, 1023, 951, 952, -123, 1028, 1016, 956, 962, + /* 410 */ -123, 867, 890, -123, -123, -123, -123, -123, -123, -123, + /* 420 */ 295, -123, 1037, -123, -123, -123, -123, -123, -123, -123, + /* 430 */ -123, 1041, -123, 1044, 1017, -123, 740, -123, 1047, -123, + /* 440 */ -123, -123, 648, 874, 1020, 1024, -123, -123, -123, -123, + /* 450 */ -123, -123, 707, -123, 1029, 1060, -123, 829, 1030, 1064, + /* 460 */ -123, 868, 890, -123, -123, 872, 890, -123, 1071, 1025, + /* 470 */ 432, -123, -123, 876, 874, -123, 571, -123, 880, 890, + /* 480 */ -123, 890, -123, 1087, 1039, -123, -123, 1088, -123, 1089, + /* 490 */ -123, 1090, 1033, -123, 1093, 1035, -123, 874, -123, 1094, + /* 500 */ 1040, 1055, -123, 1063, 1096, 1051, -123, 888, 1062, -123, + /* 510 */ -123, 1102, 1054, 1046, 886, 874, -123, 734, -123, -123, + /* 520 */ 1097, 1107, 1065, -123, 1109, -123, -123, -123, -123, 1113, + /* 530 */ -123, 1103, -123, 47, -123, -123, -123, -123, 1052, -123, + /* 540 */ -123, -123, 1057, -123, -123, 1128, -123, -123, 1056, 1119, + /* 550 */ -123, 1129, 1061, -123, 1124, -123, -123, 1059, 1141, -123, + /* 560 */ -123, -123, -123, +}; +static YYACTIONTYPE yy_default[] = { + /* 0 */ 570, 570, 564, 856, 856, 566, 856, 572, 856, 856, + /* 10 */ 856, 856, 652, 655, 656, 657, 658, 659, 573, 574, + /* 20 */ 591, 592, 593, 856, 856, 856, 856, 856, 856, 856, + /* 30 */ 856, 856, 856, 856, 856, 856, 584, 594, 604, 586, + /* 40 */ 603, 856, 856, 605, 651, 616, 856, 651, 617, 636, + /* 50 */ 634, 856, 637, 638, 856, 708, 651, 618, 706, 707, + /* 60 */ 651, 619, 856, 856, 737, 797, 743, 738, 856, 664, + /* 70 */ 856, 856, 665, 673, 675, 682, 720, 711, 713, 701, + /* 80 */ 715, 670, 856, 600, 856, 601, 856, 602, 716, 856, + /* 90 */ 717, 856, 718, 856, 856, 702, 856, 709, 708, 703, + /* 100 */ 856, 588, 710, 705, 856, 736, 856, 856, 739, 856, + /* 110 */ 740, 741, 742, 744, 747, 856, 748, 856, 749, 856, + /* 120 */ 750, 856, 751, 856, 752, 856, 753, 856, 754, 856, + /* 130 */ 755, 856, 756, 856, 757, 856, 758, 856, 759, 856, + /* 140 */ 856, 760, 761, 762, 856, 763, 856, 764, 856, 765, + /* 150 */ 856, 766, 856, 767, 856, 768, 769, 856, 770, 856, + /* 160 */ 773, 771, 856, 856, 856, 779, 856, 797, 856, 856, + /* 170 */ 856, 856, 856, 782, 796, 856, 774, 856, 775, 856, + /* 180 */ 776, 856, 777, 856, 856, 856, 856, 856, 787, 856, + /* 190 */ 856, 856, 788, 856, 856, 856, 845, 856, 856, 856, + /* 200 */ 846, 856, 856, 856, 847, 856, 856, 856, 848, 856, + /* 210 */ 856, 856, 856, 856, 789, 856, 781, 797, 794, 795, + /* 220 */ 690, 856, 691, 785, 772, 856, 856, 856, 780, 856, + /* 230 */ 797, 856, 784, 856, 783, 690, 786, 709, 708, 704, + /* 240 */ 856, 714, 856, 797, 712, 856, 721, 674, 685, 683, + /* 250 */ 684, 692, 693, 856, 694, 856, 695, 856, 696, 856, + /* 260 */ 690, 681, 589, 590, 856, 679, 680, 698, 700, 686, + /* 270 */ 856, 856, 856, 699, 856, 803, 708, 805, 804, 856, + /* 280 */ 697, 685, 856, 856, 856, 681, 698, 700, 687, 856, + /* 290 */ 681, 676, 677, 856, 856, 678, 671, 672, 778, 856, + /* 300 */ 735, 856, 745, 856, 746, 856, 651, 620, 856, 801, + /* 310 */ 624, 621, 625, 856, 626, 856, 856, 627, 856, 630, + /* 320 */ 631, 632, 633, 856, 628, 856, 629, 856, 856, 802, + /* 330 */ 622, 856, 623, 636, 635, 606, 856, 607, 608, 609, + /* 340 */ 856, 610, 613, 856, 611, 614, 612, 615, 595, 856, + /* 350 */ 856, 596, 856, 856, 597, 599, 598, 587, 856, 856, + /* 360 */ 641, 856, 644, 856, 856, 856, 856, 651, 645, 856, + /* 370 */ 856, 856, 651, 646, 856, 651, 647, 856, 856, 856, + /* 380 */ 856, 856, 856, 801, 624, 649, 856, 648, 650, 642, + /* 390 */ 643, 585, 856, 856, 581, 856, 856, 579, 856, 856, + /* 400 */ 856, 856, 856, 828, 856, 856, 856, 690, 833, 856, + /* 410 */ 856, 856, 856, 856, 856, 834, 835, 856, 856, 856, + /* 420 */ 856, 856, 856, 733, 734, 825, 826, 856, 827, 580, + /* 430 */ 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + /* 440 */ 856, 856, 856, 856, 856, 856, 654, 856, 856, 856, + /* 450 */ 856, 856, 856, 856, 653, 856, 856, 856, 856, 856, + /* 460 */ 856, 856, 723, 856, 856, 856, 724, 856, 856, 731, + /* 470 */ 856, 856, 732, 856, 856, 856, 856, 856, 856, 729, + /* 480 */ 856, 730, 856, 856, 856, 856, 856, 856, 856, 856, + /* 490 */ 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + /* 500 */ 690, 856, 856, 653, 856, 856, 856, 856, 856, 856, + /* 510 */ 856, 856, 690, 731, 856, 856, 856, 856, 856, 856, + /* 520 */ 653, 856, 856, 856, 856, 856, 856, 856, 856, 856, + /* 530 */ 856, 856, 856, 822, 856, 856, 856, 856, 856, 856, + /* 540 */ 856, 856, 856, 856, 821, 856, 856, 856, 854, 856, + /* 550 */ 856, 856, 856, 856, 856, 856, 853, 854, 856, 856, + /* 560 */ 567, 569, 565, +}; +#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammer, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { + 0, /* $ => nothing */ + 0, /* END_OF_FILE => nothing */ + 0, /* ILLEGAL => nothing */ + 0, /* SPACE => nothing */ + 0, /* UNCLOSED_STRING => nothing */ + 0, /* COMMENT => nothing */ + 0, /* FUNCTION => nothing */ + 0, /* COLUMN => nothing */ + 0, /* AGG_FUNCTION => nothing */ + 0, /* SEMI => nothing */ + 23, /* EXPLAIN => ID */ + 23, /* BEGIN => ID */ + 0, /* TRANSACTION => nothing */ + 0, /* COMMIT => nothing */ + 23, /* END => ID */ + 0, /* ROLLBACK => nothing */ + 0, /* CREATE => nothing */ + 0, /* TABLE => nothing */ + 23, /* TEMP => ID */ + 0, /* LP => nothing */ + 0, /* RP => nothing */ + 0, /* AS => nothing */ + 0, /* COMMA => nothing */ + 0, /* ID => nothing */ + 23, /* ABORT => ID */ + 23, /* AFTER => ID */ + 23, /* ASC => ID */ + 23, /* ATTACH => ID */ + 23, /* BEFORE => ID */ + 23, /* CASCADE => ID */ + 23, /* CLUSTER => ID */ + 23, /* CONFLICT => ID */ + 23, /* COPY => ID */ + 23, /* DATABASE => ID */ + 23, /* DEFERRED => ID */ + 23, /* DELIMITERS => ID */ + 23, /* DESC => ID */ + 23, /* DETACH => ID */ + 23, /* EACH => ID */ + 23, /* FAIL => ID */ + 23, /* FOR => ID */ + 23, /* GLOB => ID */ + 23, /* IGNORE => ID */ + 23, /* IMMEDIATE => ID */ + 23, /* INITIALLY => ID */ + 23, /* INSTEAD => ID */ + 23, /* LIKE => ID */ + 23, /* MATCH => ID */ + 23, /* KEY => ID */ + 23, /* OF => ID */ + 23, /* OFFSET => ID */ + 23, /* PRAGMA => ID */ + 23, /* RAISE => ID */ + 23, /* REPLACE => ID */ + 23, /* RESTRICT => ID */ + 23, /* ROW => ID */ + 23, /* STATEMENT => ID */ + 23, /* TRIGGER => ID */ + 23, /* VACUUM => ID */ + 23, /* VIEW => ID */ + 0, /* OR => nothing */ + 0, /* AND => nothing */ + 0, /* NOT => nothing */ + 0, /* EQ => nothing */ + 0, /* NE => nothing */ + 0, /* ISNULL => nothing */ + 0, /* NOTNULL => nothing */ + 0, /* IS => nothing */ + 0, /* BETWEEN => nothing */ + 0, /* IN => nothing */ + 0, /* GT => nothing */ + 0, /* GE => nothing */ + 0, /* LT => nothing */ + 0, /* LE => nothing */ + 0, /* BITAND => nothing */ + 0, /* BITOR => nothing */ + 0, /* LSHIFT => nothing */ + 0, /* RSHIFT => nothing */ + 0, /* PLUS => nothing */ + 0, /* MINUS => nothing */ + 0, /* STAR => nothing */ + 0, /* SLASH => nothing */ + 0, /* REM => nothing */ + 0, /* CONCAT => nothing */ + 0, /* UMINUS => nothing */ + 0, /* UPLUS => nothing */ + 0, /* BITNOT => nothing */ + 0, /* STRING => nothing */ + 0, /* JOIN_KW => nothing */ + 0, /* INTEGER => nothing */ + 0, /* CONSTRAINT => nothing */ + 0, /* DEFAULT => nothing */ + 0, /* FLOAT => nothing */ + 0, /* NULL => nothing */ + 0, /* PRIMARY => nothing */ + 0, /* UNIQUE => nothing */ + 0, /* CHECK => nothing */ + 0, /* REFERENCES => nothing */ + 0, /* COLLATE => nothing */ + 0, /* ON => nothing */ + 0, /* DELETE => nothing */ + 0, /* UPDATE => nothing */ + 0, /* INSERT => nothing */ + 0, /* SET => nothing */ + 0, /* DEFERRABLE => nothing */ + 0, /* FOREIGN => nothing */ + 0, /* DROP => nothing */ + 0, /* UNION => nothing */ + 0, /* ALL => nothing */ + 0, /* INTERSECT => nothing */ + 0, /* EXCEPT => nothing */ + 0, /* SELECT => nothing */ + 0, /* DISTINCT => nothing */ + 0, /* DOT => nothing */ + 0, /* FROM => nothing */ + 0, /* JOIN => nothing */ + 0, /* USING => nothing */ + 0, /* ORDER => nothing */ + 0, /* BY => nothing */ + 0, /* GROUP => nothing */ + 0, /* HAVING => nothing */ + 0, /* LIMIT => nothing */ + 0, /* WHERE => nothing */ + 0, /* INTO => nothing */ + 0, /* VALUES => nothing */ + 0, /* VARIABLE => nothing */ + 0, /* CASE => nothing */ + 0, /* WHEN => nothing */ + 0, /* THEN => nothing */ + 0, /* ELSE => nothing */ + 0, /* INDEX => nothing */ +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + int stateno; /* The state-number */ + int major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ + int yyerrcnt; /* Shifts left before out of the error */ + sqliteParserARG_SDECL /* A place to hold %extra_argument */ + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include +static FILE *yyTraceFILE = 0; +static char *yyTracePrompt = 0; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +**
        +**
      • A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +**
      • A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +**
      +** +** Outputs: +** None. +*/ +void sqliteParserTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *yyTokenName[] = { + "$", "END_OF_FILE", "ILLEGAL", "SPACE", + "UNCLOSED_STRING", "COMMENT", "FUNCTION", "COLUMN", + "AGG_FUNCTION", "SEMI", "EXPLAIN", "BEGIN", + "TRANSACTION", "COMMIT", "END", "ROLLBACK", + "CREATE", "TABLE", "TEMP", "LP", + "RP", "AS", "COMMA", "ID", + "ABORT", "AFTER", "ASC", "ATTACH", + "BEFORE", "CASCADE", "CLUSTER", "CONFLICT", + "COPY", "DATABASE", "DEFERRED", "DELIMITERS", + "DESC", "DETACH", "EACH", "FAIL", + "FOR", "GLOB", "IGNORE", "IMMEDIATE", + "INITIALLY", "INSTEAD", "LIKE", "MATCH", + "KEY", "OF", "OFFSET", "PRAGMA", + "RAISE", "REPLACE", "RESTRICT", "ROW", + "STATEMENT", "TRIGGER", "VACUUM", "VIEW", + "OR", "AND", "NOT", "EQ", + "NE", "ISNULL", "NOTNULL", "IS", + "BETWEEN", "IN", "GT", "GE", + "LT", "LE", "BITAND", "BITOR", + "LSHIFT", "RSHIFT", "PLUS", "MINUS", + "STAR", "SLASH", "REM", "CONCAT", + "UMINUS", "UPLUS", "BITNOT", "STRING", + "JOIN_KW", "INTEGER", "CONSTRAINT", "DEFAULT", + "FLOAT", "NULL", "PRIMARY", "UNIQUE", + "CHECK", "REFERENCES", "COLLATE", "ON", + "DELETE", "UPDATE", "INSERT", "SET", + "DEFERRABLE", "FOREIGN", "DROP", "UNION", + "ALL", "INTERSECT", "EXCEPT", "SELECT", + "DISTINCT", "DOT", "FROM", "JOIN", + "USING", "ORDER", "BY", "GROUP", + "HAVING", "LIMIT", "WHERE", "INTO", + "VALUES", "VARIABLE", "CASE", "WHEN", + "THEN", "ELSE", "INDEX", "error", + "input", "cmdlist", "ecmd", "explain", + "cmdx", "cmd", "trans_opt", "onconf", + "nm", "create_table", "create_table_args", "temp", + "columnlist", "conslist_opt", "select", "column", + "columnid", "type", "carglist", "id", + "ids", "typename", "signed", "carg", + "ccons", "sortorder", "expr", "idxlist_opt", + "refargs", "defer_subclause", "refarg", "refact", + "init_deferred_pred_opt", "conslist", "tcons", "idxlist", + "defer_subclause_opt", "orconf", "resolvetype", "oneselect", + "multiselect_op", "distinct", "selcollist", "from", + "where_opt", "groupby_opt", "having_opt", "orderby_opt", + "limit_opt", "sclp", "as", "seltablist", + "stl_prefix", "joinop", "dbnm", "on_opt", + "using_opt", "seltablist_paren", "joinop2", "sortlist", + "sortitem", "collate", "exprlist", "setlist", + "insert_cmd", "inscollist_opt", "itemlist", "inscollist", + "likeop", "case_operand", "case_exprlist", "case_else", + "expritem", "uniqueflag", "idxitem", "plus_num", + "minus_num", "plus_opt", "number", "trigger_decl", + "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause", + "when_clause", "trigger_cmd", "database_kw_opt", "key_opt", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *yyRuleName[] = { + /* 0 */ "input ::= cmdlist", + /* 1 */ "cmdlist ::= cmdlist ecmd", + /* 2 */ "cmdlist ::= ecmd", + /* 3 */ "ecmd ::= explain cmdx SEMI", + /* 4 */ "ecmd ::= SEMI", + /* 5 */ "cmdx ::= cmd", + /* 6 */ "explain ::= EXPLAIN", + /* 7 */ "explain ::=", + /* 8 */ "cmd ::= BEGIN trans_opt onconf", + /* 9 */ "trans_opt ::=", + /* 10 */ "trans_opt ::= TRANSACTION", + /* 11 */ "trans_opt ::= TRANSACTION nm", + /* 12 */ "cmd ::= COMMIT trans_opt", + /* 13 */ "cmd ::= END trans_opt", + /* 14 */ "cmd ::= ROLLBACK trans_opt", + /* 15 */ "cmd ::= create_table create_table_args", + /* 16 */ "create_table ::= CREATE temp TABLE nm", + /* 17 */ "temp ::= TEMP", + /* 18 */ "temp ::=", + /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP", + /* 20 */ "create_table_args ::= AS select", + /* 21 */ "columnlist ::= columnlist COMMA column", + /* 22 */ "columnlist ::= column", + /* 23 */ "column ::= columnid type carglist", + /* 24 */ "columnid ::= nm", + /* 25 */ "id ::= ID", + /* 26 */ "ids ::= ID", + /* 27 */ "ids ::= STRING", + /* 28 */ "nm ::= ID", + /* 29 */ "nm ::= STRING", + /* 30 */ "nm ::= JOIN_KW", + /* 31 */ "type ::=", + /* 32 */ "type ::= typename", + /* 33 */ "type ::= typename LP signed RP", + /* 34 */ "type ::= typename LP signed COMMA signed RP", + /* 35 */ "typename ::= ids", + /* 36 */ "typename ::= typename ids", + /* 37 */ "signed ::= INTEGER", + /* 38 */ "signed ::= PLUS INTEGER", + /* 39 */ "signed ::= MINUS INTEGER", + /* 40 */ "carglist ::= carglist carg", + /* 41 */ "carglist ::=", + /* 42 */ "carg ::= CONSTRAINT nm ccons", + /* 43 */ "carg ::= ccons", + /* 44 */ "carg ::= DEFAULT STRING", + /* 45 */ "carg ::= DEFAULT ID", + /* 46 */ "carg ::= DEFAULT INTEGER", + /* 47 */ "carg ::= DEFAULT PLUS INTEGER", + /* 48 */ "carg ::= DEFAULT MINUS INTEGER", + /* 49 */ "carg ::= DEFAULT FLOAT", + /* 50 */ "carg ::= DEFAULT PLUS FLOAT", + /* 51 */ "carg ::= DEFAULT MINUS FLOAT", + /* 52 */ "carg ::= DEFAULT NULL", + /* 53 */ "ccons ::= NULL onconf", + /* 54 */ "ccons ::= NOT NULL onconf", + /* 55 */ "ccons ::= PRIMARY KEY sortorder onconf", + /* 56 */ "ccons ::= UNIQUE onconf", + /* 57 */ "ccons ::= CHECK LP expr RP onconf", + /* 58 */ "ccons ::= REFERENCES nm idxlist_opt refargs", + /* 59 */ "ccons ::= defer_subclause", + /* 60 */ "ccons ::= COLLATE id", + /* 61 */ "refargs ::=", + /* 62 */ "refargs ::= refargs refarg", + /* 63 */ "refarg ::= MATCH nm", + /* 64 */ "refarg ::= ON DELETE refact", + /* 65 */ "refarg ::= ON UPDATE refact", + /* 66 */ "refarg ::= ON INSERT refact", + /* 67 */ "refact ::= SET NULL", + /* 68 */ "refact ::= SET DEFAULT", + /* 69 */ "refact ::= CASCADE", + /* 70 */ "refact ::= RESTRICT", + /* 71 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 72 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 73 */ "init_deferred_pred_opt ::=", + /* 74 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 75 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 76 */ "conslist_opt ::=", + /* 77 */ "conslist_opt ::= COMMA conslist", + /* 78 */ "conslist ::= conslist COMMA tcons", + /* 79 */ "conslist ::= conslist tcons", + /* 80 */ "conslist ::= tcons", + /* 81 */ "tcons ::= CONSTRAINT nm", + /* 82 */ "tcons ::= PRIMARY KEY LP idxlist RP onconf", + /* 83 */ "tcons ::= UNIQUE LP idxlist RP onconf", + /* 84 */ "tcons ::= CHECK expr onconf", + /* 85 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", + /* 86 */ "defer_subclause_opt ::=", + /* 87 */ "defer_subclause_opt ::= defer_subclause", + /* 88 */ "onconf ::=", + /* 89 */ "onconf ::= ON CONFLICT resolvetype", + /* 90 */ "orconf ::=", + /* 91 */ "orconf ::= OR resolvetype", + /* 92 */ "resolvetype ::= ROLLBACK", + /* 93 */ "resolvetype ::= ABORT", + /* 94 */ "resolvetype ::= FAIL", + /* 95 */ "resolvetype ::= IGNORE", + /* 96 */ "resolvetype ::= REPLACE", + /* 97 */ "cmd ::= DROP TABLE nm", + /* 98 */ "cmd ::= CREATE temp VIEW nm AS select", + /* 99 */ "cmd ::= DROP VIEW nm", + /* 100 */ "cmd ::= select", + /* 101 */ "select ::= oneselect", + /* 102 */ "select ::= select multiselect_op oneselect", + /* 103 */ "multiselect_op ::= UNION", + /* 104 */ "multiselect_op ::= UNION ALL", + /* 105 */ "multiselect_op ::= INTERSECT", + /* 106 */ "multiselect_op ::= EXCEPT", + /* 107 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 108 */ "distinct ::= DISTINCT", + /* 109 */ "distinct ::= ALL", + /* 110 */ "distinct ::=", + /* 111 */ "sclp ::= selcollist COMMA", + /* 112 */ "sclp ::=", + /* 113 */ "selcollist ::= sclp expr as", + /* 114 */ "selcollist ::= sclp STAR", + /* 115 */ "selcollist ::= sclp nm DOT STAR", + /* 116 */ "as ::= AS nm", + /* 117 */ "as ::= ids", + /* 118 */ "as ::=", + /* 119 */ "from ::=", + /* 120 */ "from ::= FROM seltablist", + /* 121 */ "stl_prefix ::= seltablist joinop", + /* 122 */ "stl_prefix ::=", + /* 123 */ "seltablist ::= stl_prefix nm dbnm as on_opt using_opt", + /* 124 */ "seltablist ::= stl_prefix LP seltablist_paren RP as on_opt using_opt", + /* 125 */ "seltablist_paren ::= select", + /* 126 */ "seltablist_paren ::= seltablist", + /* 127 */ "dbnm ::=", + /* 128 */ "dbnm ::= DOT nm", + /* 129 */ "joinop ::= COMMA", + /* 130 */ "joinop ::= JOIN", + /* 131 */ "joinop ::= JOIN_KW JOIN", + /* 132 */ "joinop ::= JOIN_KW nm JOIN", + /* 133 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 134 */ "on_opt ::= ON expr", + /* 135 */ "on_opt ::=", + /* 136 */ "using_opt ::= USING LP idxlist RP", + /* 137 */ "using_opt ::=", + /* 138 */ "orderby_opt ::=", + /* 139 */ "orderby_opt ::= ORDER BY sortlist", + /* 140 */ "sortlist ::= sortlist COMMA sortitem collate sortorder", + /* 141 */ "sortlist ::= sortitem collate sortorder", + /* 142 */ "sortitem ::= expr", + /* 143 */ "sortorder ::= ASC", + /* 144 */ "sortorder ::= DESC", + /* 145 */ "sortorder ::=", + /* 146 */ "collate ::=", + /* 147 */ "collate ::= COLLATE id", + /* 148 */ "groupby_opt ::=", + /* 149 */ "groupby_opt ::= GROUP BY exprlist", + /* 150 */ "having_opt ::=", + /* 151 */ "having_opt ::= HAVING expr", + /* 152 */ "limit_opt ::=", + /* 153 */ "limit_opt ::= LIMIT signed", + /* 154 */ "limit_opt ::= LIMIT signed OFFSET signed", + /* 155 */ "limit_opt ::= LIMIT signed COMMA signed", + /* 156 */ "cmd ::= DELETE FROM nm dbnm where_opt", + /* 157 */ "where_opt ::=", + /* 158 */ "where_opt ::= WHERE expr", + /* 159 */ "cmd ::= UPDATE orconf nm dbnm SET setlist where_opt", + /* 160 */ "setlist ::= setlist COMMA nm EQ expr", + /* 161 */ "setlist ::= nm EQ expr", + /* 162 */ "cmd ::= insert_cmd INTO nm dbnm inscollist_opt VALUES LP itemlist RP", + /* 163 */ "cmd ::= insert_cmd INTO nm dbnm inscollist_opt select", + /* 164 */ "insert_cmd ::= INSERT orconf", + /* 165 */ "insert_cmd ::= REPLACE", + /* 166 */ "itemlist ::= itemlist COMMA expr", + /* 167 */ "itemlist ::= expr", + /* 168 */ "inscollist_opt ::=", + /* 169 */ "inscollist_opt ::= LP inscollist RP", + /* 170 */ "inscollist ::= inscollist COMMA nm", + /* 171 */ "inscollist ::= nm", + /* 172 */ "expr ::= LP expr RP", + /* 173 */ "expr ::= NULL", + /* 174 */ "expr ::= ID", + /* 175 */ "expr ::= JOIN_KW", + /* 176 */ "expr ::= nm DOT nm", + /* 177 */ "expr ::= nm DOT nm DOT nm", + /* 178 */ "expr ::= INTEGER", + /* 179 */ "expr ::= FLOAT", + /* 180 */ "expr ::= STRING", + /* 181 */ "expr ::= VARIABLE", + /* 182 */ "expr ::= ID LP exprlist RP", + /* 183 */ "expr ::= ID LP STAR RP", + /* 184 */ "expr ::= expr AND expr", + /* 185 */ "expr ::= expr OR expr", + /* 186 */ "expr ::= expr LT expr", + /* 187 */ "expr ::= expr GT expr", + /* 188 */ "expr ::= expr LE expr", + /* 189 */ "expr ::= expr GE expr", + /* 190 */ "expr ::= expr NE expr", + /* 191 */ "expr ::= expr EQ expr", + /* 192 */ "expr ::= expr BITAND expr", + /* 193 */ "expr ::= expr BITOR expr", + /* 194 */ "expr ::= expr LSHIFT expr", + /* 195 */ "expr ::= expr RSHIFT expr", + /* 196 */ "expr ::= expr likeop expr", + /* 197 */ "expr ::= expr NOT likeop expr", + /* 198 */ "likeop ::= LIKE", + /* 199 */ "likeop ::= GLOB", + /* 200 */ "expr ::= expr PLUS expr", + /* 201 */ "expr ::= expr MINUS expr", + /* 202 */ "expr ::= expr STAR expr", + /* 203 */ "expr ::= expr SLASH expr", + /* 204 */ "expr ::= expr REM expr", + /* 205 */ "expr ::= expr CONCAT expr", + /* 206 */ "expr ::= expr ISNULL", + /* 207 */ "expr ::= expr IS NULL", + /* 208 */ "expr ::= expr NOTNULL", + /* 209 */ "expr ::= expr NOT NULL", + /* 210 */ "expr ::= expr IS NOT NULL", + /* 211 */ "expr ::= NOT expr", + /* 212 */ "expr ::= BITNOT expr", + /* 213 */ "expr ::= MINUS expr", + /* 214 */ "expr ::= PLUS expr", + /* 215 */ "expr ::= LP select RP", + /* 216 */ "expr ::= expr BETWEEN expr AND expr", + /* 217 */ "expr ::= expr NOT BETWEEN expr AND expr", + /* 218 */ "expr ::= expr IN LP exprlist RP", + /* 219 */ "expr ::= expr IN LP select RP", + /* 220 */ "expr ::= expr NOT IN LP exprlist RP", + /* 221 */ "expr ::= expr NOT IN LP select RP", + /* 222 */ "expr ::= expr IN nm dbnm", + /* 223 */ "expr ::= expr NOT IN nm dbnm", + /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 226 */ "case_exprlist ::= WHEN expr THEN expr", + /* 227 */ "case_else ::= ELSE expr", + /* 228 */ "case_else ::=", + /* 229 */ "case_operand ::= expr", + /* 230 */ "case_operand ::=", + /* 231 */ "exprlist ::= exprlist COMMA expritem", + /* 232 */ "exprlist ::= expritem", + /* 233 */ "expritem ::= expr", + /* 234 */ "expritem ::=", + /* 235 */ "cmd ::= CREATE uniqueflag INDEX nm ON nm dbnm LP idxlist RP onconf", + /* 236 */ "uniqueflag ::= UNIQUE", + /* 237 */ "uniqueflag ::=", + /* 238 */ "idxlist_opt ::=", + /* 239 */ "idxlist_opt ::= LP idxlist RP", + /* 240 */ "idxlist ::= idxlist COMMA idxitem", + /* 241 */ "idxlist ::= idxitem", + /* 242 */ "idxitem ::= nm sortorder", + /* 243 */ "cmd ::= DROP INDEX nm dbnm", + /* 244 */ "cmd ::= COPY orconf nm dbnm FROM nm USING DELIMITERS STRING", + /* 245 */ "cmd ::= COPY orconf nm dbnm FROM nm", + /* 246 */ "cmd ::= VACUUM", + /* 247 */ "cmd ::= VACUUM nm", + /* 248 */ "cmd ::= PRAGMA ids EQ nm", + /* 249 */ "cmd ::= PRAGMA ids EQ ON", + /* 250 */ "cmd ::= PRAGMA ids EQ plus_num", + /* 251 */ "cmd ::= PRAGMA ids EQ minus_num", + /* 252 */ "cmd ::= PRAGMA ids LP nm RP", + /* 253 */ "cmd ::= PRAGMA ids", + /* 254 */ "plus_num ::= plus_opt number", + /* 255 */ "minus_num ::= MINUS number", + /* 256 */ "number ::= INTEGER", + /* 257 */ "number ::= FLOAT", + /* 258 */ "plus_opt ::= PLUS", + /* 259 */ "plus_opt ::=", + /* 260 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END", + /* 261 */ "trigger_decl ::= temp TRIGGER nm trigger_time trigger_event ON nm dbnm foreach_clause when_clause", + /* 262 */ "trigger_time ::= BEFORE", + /* 263 */ "trigger_time ::= AFTER", + /* 264 */ "trigger_time ::= INSTEAD OF", + /* 265 */ "trigger_time ::=", + /* 266 */ "trigger_event ::= DELETE", + /* 267 */ "trigger_event ::= INSERT", + /* 268 */ "trigger_event ::= UPDATE", + /* 269 */ "trigger_event ::= UPDATE OF inscollist", + /* 270 */ "foreach_clause ::=", + /* 271 */ "foreach_clause ::= FOR EACH ROW", + /* 272 */ "foreach_clause ::= FOR EACH STATEMENT", + /* 273 */ "when_clause ::=", + /* 274 */ "when_clause ::= WHEN expr", + /* 275 */ "trigger_cmd_list ::= trigger_cmd SEMI trigger_cmd_list", + /* 276 */ "trigger_cmd_list ::=", + /* 277 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt", + /* 278 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP", + /* 279 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select", + /* 280 */ "trigger_cmd ::= DELETE FROM nm where_opt", + /* 281 */ "trigger_cmd ::= select", + /* 282 */ "expr ::= RAISE LP IGNORE RP", + /* 283 */ "expr ::= RAISE LP ROLLBACK COMMA nm RP", + /* 284 */ "expr ::= RAISE LP ABORT COMMA nm RP", + /* 285 */ "expr ::= RAISE LP FAIL COMMA nm RP", + /* 286 */ "cmd ::= DROP TRIGGER nm dbnm", + /* 287 */ "cmd ::= ATTACH database_kw_opt ids AS nm key_opt", + /* 288 */ "key_opt ::= USING ids", + /* 289 */ "key_opt ::=", + /* 290 */ "database_kw_opt ::= DATABASE", + /* 291 */ "database_kw_opt ::=", + /* 292 */ "cmd ::= DETACH database_kw_opt nm", +}; +#endif /* NDEBUG */ + +/* +** This function returns the symbolic name associated with a token +** value. +*/ +const char *sqliteParserTokenName(int tokenType){ +#ifndef NDEBUG + if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + return yyTokenName[tokenType]; + }else{ + return "Unknown"; + } +#else + return ""; +#endif +} + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to sqliteParser and sqliteParserFree. +*/ +void *sqliteParserAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + case 146: +#line 286 "parse.y" +{sqliteSelectDelete((yypminor->yy179));} +#line 1235 "parse.c" + break; + case 158: +#line 533 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1240 "parse.c" + break; + case 159: +#line 746 "parse.y" +{sqliteIdListDelete((yypminor->yy320));} +#line 1245 "parse.c" + break; + case 167: +#line 744 "parse.y" +{sqliteIdListDelete((yypminor->yy320));} +#line 1250 "parse.c" + break; + case 171: +#line 288 "parse.y" +{sqliteSelectDelete((yypminor->yy179));} +#line 1255 "parse.c" + break; + case 174: +#line 322 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1260 "parse.c" + break; + case 175: +#line 353 "parse.y" +{sqliteSrcListDelete((yypminor->yy307));} +#line 1265 "parse.c" + break; + case 176: +#line 483 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1270 "parse.c" + break; + case 177: +#line 459 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1275 "parse.c" + break; + case 178: +#line 464 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1280 "parse.c" + break; + case 179: +#line 431 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1285 "parse.c" + break; + case 181: +#line 324 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1290 "parse.c" + break; + case 183: +#line 349 "parse.y" +{sqliteSrcListDelete((yypminor->yy307));} +#line 1295 "parse.c" + break; + case 184: +#line 351 "parse.y" +{sqliteSrcListDelete((yypminor->yy307));} +#line 1300 "parse.c" + break; + case 187: +#line 420 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1305 "parse.c" + break; + case 188: +#line 425 "parse.y" +{sqliteIdListDelete((yypminor->yy320));} +#line 1310 "parse.c" + break; + case 189: +#line 400 "parse.y" +{sqliteSelectDelete((yypminor->yy179));} +#line 1315 "parse.c" + break; + case 191: +#line 433 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1320 "parse.c" + break; + case 192: +#line 435 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1325 "parse.c" + break; + case 194: +#line 719 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1330 "parse.c" + break; + case 195: +#line 489 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1335 "parse.c" + break; + case 197: +#line 520 "parse.y" +{sqliteIdListDelete((yypminor->yy320));} +#line 1340 "parse.c" + break; + case 198: +#line 514 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1345 "parse.c" + break; + case 199: +#line 522 "parse.y" +{sqliteIdListDelete((yypminor->yy320));} +#line 1350 "parse.c" + break; + case 202: +#line 702 "parse.y" +{sqliteExprListDelete((yypminor->yy322));} +#line 1355 "parse.c" + break; + case 204: +#line 721 "parse.y" +{sqliteExprDelete((yypminor->yy242));} +#line 1360 "parse.c" + break; + case 212: +#line 828 "parse.y" +{sqliteDeleteTriggerStep((yypminor->yy19));} +#line 1365 "parse.c" + break; + case 214: +#line 812 "parse.y" +{sqliteIdListDelete((yypminor->yy290).b);} +#line 1370 "parse.c" + break; + case 217: +#line 836 "parse.y" +{sqliteDeleteTriggerStep((yypminor->yy19));} +#line 1375 "parse.c" + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; + + if( pParser->yyidx<0 ) return 0; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor( yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +**
        +**
      • A pointer to the parser. This should be a pointer +** obtained from sqliteParserAlloc. +**
      • A pointer to a function used to reclaim memory obtained +** from malloc. +**
      +*/ +void sqliteParserFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==0 ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); + (*freeProc)((void*)pParser); +} + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ + i = yy_shift_ofst[stateno]; + if( i==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + int iFallback; /* Fallback token */ + if( iLookAhead %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + i = yy_reduce_ofst[stateno]; + if( i==YY_REDUCE_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; + if( yypParser->yyidx>=YYSTACKDEPTH ){ + sqliteParserARG_FETCH; + yypParser->yyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ + sqliteParserARG_STORE; /* Suppress warning about unused %extra_argument var */ + return; + } + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 132, 1 }, + { 133, 2 }, + { 133, 1 }, + { 134, 3 }, + { 134, 1 }, + { 136, 1 }, + { 135, 1 }, + { 135, 0 }, + { 137, 3 }, + { 138, 0 }, + { 138, 1 }, + { 138, 2 }, + { 137, 2 }, + { 137, 2 }, + { 137, 2 }, + { 137, 2 }, + { 141, 4 }, + { 143, 1 }, + { 143, 0 }, + { 142, 4 }, + { 142, 2 }, + { 144, 3 }, + { 144, 1 }, + { 147, 3 }, + { 148, 1 }, + { 151, 1 }, + { 152, 1 }, + { 152, 1 }, + { 140, 1 }, + { 140, 1 }, + { 140, 1 }, + { 149, 0 }, + { 149, 1 }, + { 149, 4 }, + { 149, 6 }, + { 153, 1 }, + { 153, 2 }, + { 154, 1 }, + { 154, 2 }, + { 154, 2 }, + { 150, 2 }, + { 150, 0 }, + { 155, 3 }, + { 155, 1 }, + { 155, 2 }, + { 155, 2 }, + { 155, 2 }, + { 155, 3 }, + { 155, 3 }, + { 155, 2 }, + { 155, 3 }, + { 155, 3 }, + { 155, 2 }, + { 156, 2 }, + { 156, 3 }, + { 156, 4 }, + { 156, 2 }, + { 156, 5 }, + { 156, 4 }, + { 156, 1 }, + { 156, 2 }, + { 160, 0 }, + { 160, 2 }, + { 162, 2 }, + { 162, 3 }, + { 162, 3 }, + { 162, 3 }, + { 163, 2 }, + { 163, 2 }, + { 163, 1 }, + { 163, 1 }, + { 161, 3 }, + { 161, 2 }, + { 164, 0 }, + { 164, 2 }, + { 164, 2 }, + { 145, 0 }, + { 145, 2 }, + { 165, 3 }, + { 165, 2 }, + { 165, 1 }, + { 166, 2 }, + { 166, 6 }, + { 166, 5 }, + { 166, 3 }, + { 166, 10 }, + { 168, 0 }, + { 168, 1 }, + { 139, 0 }, + { 139, 3 }, + { 169, 0 }, + { 169, 2 }, + { 170, 1 }, + { 170, 1 }, + { 170, 1 }, + { 170, 1 }, + { 170, 1 }, + { 137, 3 }, + { 137, 6 }, + { 137, 3 }, + { 137, 1 }, + { 146, 1 }, + { 146, 3 }, + { 172, 1 }, + { 172, 2 }, + { 172, 1 }, + { 172, 1 }, + { 171, 9 }, + { 173, 1 }, + { 173, 1 }, + { 173, 0 }, + { 181, 2 }, + { 181, 0 }, + { 174, 3 }, + { 174, 2 }, + { 174, 4 }, + { 182, 2 }, + { 182, 1 }, + { 182, 0 }, + { 175, 0 }, + { 175, 2 }, + { 184, 2 }, + { 184, 0 }, + { 183, 6 }, + { 183, 7 }, + { 189, 1 }, + { 189, 1 }, + { 186, 0 }, + { 186, 2 }, + { 185, 1 }, + { 185, 1 }, + { 185, 2 }, + { 185, 3 }, + { 185, 4 }, + { 187, 2 }, + { 187, 0 }, + { 188, 4 }, + { 188, 0 }, + { 179, 0 }, + { 179, 3 }, + { 191, 5 }, + { 191, 3 }, + { 192, 1 }, + { 157, 1 }, + { 157, 1 }, + { 157, 0 }, + { 193, 0 }, + { 193, 2 }, + { 177, 0 }, + { 177, 3 }, + { 178, 0 }, + { 178, 2 }, + { 180, 0 }, + { 180, 2 }, + { 180, 4 }, + { 180, 4 }, + { 137, 5 }, + { 176, 0 }, + { 176, 2 }, + { 137, 7 }, + { 195, 5 }, + { 195, 3 }, + { 137, 9 }, + { 137, 6 }, + { 196, 2 }, + { 196, 1 }, + { 198, 3 }, + { 198, 1 }, + { 197, 0 }, + { 197, 3 }, + { 199, 3 }, + { 199, 1 }, + { 158, 3 }, + { 158, 1 }, + { 158, 1 }, + { 158, 1 }, + { 158, 3 }, + { 158, 5 }, + { 158, 1 }, + { 158, 1 }, + { 158, 1 }, + { 158, 1 }, + { 158, 4 }, + { 158, 4 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 4 }, + { 200, 1 }, + { 200, 1 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 3 }, + { 158, 2 }, + { 158, 3 }, + { 158, 2 }, + { 158, 3 }, + { 158, 4 }, + { 158, 2 }, + { 158, 2 }, + { 158, 2 }, + { 158, 2 }, + { 158, 3 }, + { 158, 5 }, + { 158, 6 }, + { 158, 5 }, + { 158, 5 }, + { 158, 6 }, + { 158, 6 }, + { 158, 4 }, + { 158, 5 }, + { 158, 5 }, + { 202, 5 }, + { 202, 4 }, + { 203, 2 }, + { 203, 0 }, + { 201, 1 }, + { 201, 0 }, + { 194, 3 }, + { 194, 1 }, + { 204, 1 }, + { 204, 0 }, + { 137, 11 }, + { 205, 1 }, + { 205, 0 }, + { 159, 0 }, + { 159, 3 }, + { 167, 3 }, + { 167, 1 }, + { 206, 2 }, + { 137, 4 }, + { 137, 9 }, + { 137, 6 }, + { 137, 1 }, + { 137, 2 }, + { 137, 4 }, + { 137, 4 }, + { 137, 4 }, + { 137, 4 }, + { 137, 5 }, + { 137, 2 }, + { 207, 2 }, + { 208, 2 }, + { 210, 1 }, + { 210, 1 }, + { 209, 1 }, + { 209, 0 }, + { 137, 5 }, + { 211, 10 }, + { 213, 1 }, + { 213, 1 }, + { 213, 2 }, + { 213, 0 }, + { 214, 1 }, + { 214, 1 }, + { 214, 1 }, + { 214, 3 }, + { 215, 0 }, + { 215, 3 }, + { 215, 3 }, + { 216, 0 }, + { 216, 2 }, + { 212, 3 }, + { 212, 0 }, + { 217, 6 }, + { 217, 8 }, + { 217, 5 }, + { 217, 4 }, + { 217, 1 }, + { 158, 4 }, + { 158, 6 }, + { 158, 6 }, + { 158, 6 }, + { 137, 4 }, + { 137, 6 }, + { 219, 2 }, + { 219, 0 }, + { 218, 1 }, + { 218, 0 }, + { 137, 3 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + sqliteParserARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && yyruleno>=0 + && yyruleno + ** { ... } // User supplied code + ** #line + ** break; + */ + case 0: + /* No destructor defined for cmdlist */ + break; + case 1: + /* No destructor defined for cmdlist */ + /* No destructor defined for ecmd */ + break; + case 2: + /* No destructor defined for ecmd */ + break; + case 3: + /* No destructor defined for explain */ + /* No destructor defined for cmdx */ + /* No destructor defined for SEMI */ + break; + case 4: + /* No destructor defined for SEMI */ + break; + case 5: +#line 72 "parse.y" +{ sqliteExec(pParse); } +#line 1901 "parse.c" + /* No destructor defined for cmd */ + break; + case 6: +#line 73 "parse.y" +{ sqliteBeginParse(pParse, 1); } +#line 1907 "parse.c" + /* No destructor defined for EXPLAIN */ + break; + case 7: +#line 74 "parse.y" +{ sqliteBeginParse(pParse, 0); } +#line 1913 "parse.c" + break; + case 8: +#line 79 "parse.y" +{sqliteBeginTransaction(pParse,yymsp[0].minor.yy372);} +#line 1918 "parse.c" + /* No destructor defined for BEGIN */ + /* No destructor defined for trans_opt */ + break; + case 9: + break; + case 10: + /* No destructor defined for TRANSACTION */ + break; + case 11: + /* No destructor defined for TRANSACTION */ + /* No destructor defined for nm */ + break; + case 12: +#line 83 "parse.y" +{sqliteCommitTransaction(pParse);} +#line 1934 "parse.c" + /* No destructor defined for COMMIT */ + /* No destructor defined for trans_opt */ + break; + case 13: +#line 84 "parse.y" +{sqliteCommitTransaction(pParse);} +#line 1941 "parse.c" + /* No destructor defined for END */ + /* No destructor defined for trans_opt */ + break; + case 14: +#line 85 "parse.y" +{sqliteRollbackTransaction(pParse);} +#line 1948 "parse.c" + /* No destructor defined for ROLLBACK */ + /* No destructor defined for trans_opt */ + break; + case 15: + /* No destructor defined for create_table */ + /* No destructor defined for create_table_args */ + break; + case 16: +#line 90 "parse.y" +{ + sqliteStartTable(pParse,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy298,yymsp[-2].minor.yy372,0); +} +#line 1961 "parse.c" + /* No destructor defined for TABLE */ + break; + case 17: +#line 94 "parse.y" +{yygotominor.yy372 = 1;} +#line 1967 "parse.c" + /* No destructor defined for TEMP */ + break; + case 18: +#line 95 "parse.y" +{yygotominor.yy372 = 0;} +#line 1973 "parse.c" + break; + case 19: +#line 96 "parse.y" +{ + sqliteEndTable(pParse,&yymsp[0].minor.yy0,0); +} +#line 1980 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for columnlist */ + /* No destructor defined for conslist_opt */ + break; + case 20: +#line 99 "parse.y" +{ + sqliteEndTable(pParse,0,yymsp[0].minor.yy179); + sqliteSelectDelete(yymsp[0].minor.yy179); +} +#line 1991 "parse.c" + /* No destructor defined for AS */ + break; + case 21: + /* No destructor defined for columnlist */ + /* No destructor defined for COMMA */ + /* No destructor defined for column */ + break; + case 22: + /* No destructor defined for column */ + break; + case 23: + /* No destructor defined for columnid */ + /* No destructor defined for type */ + /* No destructor defined for carglist */ + break; + case 24: +#line 111 "parse.y" +{sqliteAddColumn(pParse,&yymsp[0].minor.yy298);} +#line 2010 "parse.c" + break; + case 25: +#line 117 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2015 "parse.c" + break; + case 26: +#line 149 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2020 "parse.c" + break; + case 27: +#line 150 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2025 "parse.c" + break; + case 28: +#line 155 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2030 "parse.c" + break; + case 29: +#line 156 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2035 "parse.c" + break; + case 30: +#line 157 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 2040 "parse.c" + break; + case 31: + break; + case 32: +#line 160 "parse.y" +{sqliteAddColumnType(pParse,&yymsp[0].minor.yy298,&yymsp[0].minor.yy298);} +#line 2047 "parse.c" + break; + case 33: +#line 161 "parse.y" +{sqliteAddColumnType(pParse,&yymsp[-3].minor.yy298,&yymsp[0].minor.yy0);} +#line 2052 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for signed */ + break; + case 34: +#line 163 "parse.y" +{sqliteAddColumnType(pParse,&yymsp[-5].minor.yy298,&yymsp[0].minor.yy0);} +#line 2059 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for signed */ + /* No destructor defined for COMMA */ + /* No destructor defined for signed */ + break; + case 35: +#line 165 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy298;} +#line 2068 "parse.c" + break; + case 36: +#line 166 "parse.y" +{yygotominor.yy298 = yymsp[-1].minor.yy298;} +#line 2073 "parse.c" + /* No destructor defined for ids */ + break; + case 37: +#line 168 "parse.y" +{ yygotominor.yy372 = atoi(yymsp[0].minor.yy0.z); } +#line 2079 "parse.c" + break; + case 38: +#line 169 "parse.y" +{ yygotominor.yy372 = atoi(yymsp[0].minor.yy0.z); } +#line 2084 "parse.c" + /* No destructor defined for PLUS */ + break; + case 39: +#line 170 "parse.y" +{ yygotominor.yy372 = -atoi(yymsp[0].minor.yy0.z); } +#line 2090 "parse.c" + /* No destructor defined for MINUS */ + break; + case 40: + /* No destructor defined for carglist */ + /* No destructor defined for carg */ + break; + case 41: + break; + case 42: + /* No destructor defined for CONSTRAINT */ + /* No destructor defined for nm */ + /* No destructor defined for ccons */ + break; + case 43: + /* No destructor defined for ccons */ + break; + case 44: +#line 175 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2110 "parse.c" + /* No destructor defined for DEFAULT */ + break; + case 45: +#line 176 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2116 "parse.c" + /* No destructor defined for DEFAULT */ + break; + case 46: +#line 177 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2122 "parse.c" + /* No destructor defined for DEFAULT */ + break; + case 47: +#line 178 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2128 "parse.c" + /* No destructor defined for DEFAULT */ + /* No destructor defined for PLUS */ + break; + case 48: +#line 179 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,1);} +#line 2135 "parse.c" + /* No destructor defined for DEFAULT */ + /* No destructor defined for MINUS */ + break; + case 49: +#line 180 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2142 "parse.c" + /* No destructor defined for DEFAULT */ + break; + case 50: +#line 181 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,0);} +#line 2148 "parse.c" + /* No destructor defined for DEFAULT */ + /* No destructor defined for PLUS */ + break; + case 51: +#line 182 "parse.y" +{sqliteAddDefaultValue(pParse,&yymsp[0].minor.yy0,1);} +#line 2155 "parse.c" + /* No destructor defined for DEFAULT */ + /* No destructor defined for MINUS */ + break; + case 52: + /* No destructor defined for DEFAULT */ + /* No destructor defined for NULL */ + break; + case 53: + /* No destructor defined for NULL */ + /* No destructor defined for onconf */ + break; + case 54: +#line 189 "parse.y" +{sqliteAddNotNull(pParse, yymsp[0].minor.yy372);} +#line 2170 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for NULL */ + break; + case 55: +#line 190 "parse.y" +{sqliteAddPrimaryKey(pParse,0,yymsp[0].minor.yy372);} +#line 2177 "parse.c" + /* No destructor defined for PRIMARY */ + /* No destructor defined for KEY */ + /* No destructor defined for sortorder */ + break; + case 56: +#line 191 "parse.y" +{sqliteCreateIndex(pParse,0,0,0,yymsp[0].minor.yy372,0,0);} +#line 2185 "parse.c" + /* No destructor defined for UNIQUE */ + break; + case 57: + /* No destructor defined for CHECK */ + /* No destructor defined for LP */ + yy_destructor(158,&yymsp[-2].minor); + /* No destructor defined for RP */ + /* No destructor defined for onconf */ + break; + case 58: +#line 194 "parse.y" +{sqliteCreateForeignKey(pParse,0,&yymsp[-2].minor.yy298,yymsp[-1].minor.yy320,yymsp[0].minor.yy372);} +#line 2198 "parse.c" + /* No destructor defined for REFERENCES */ + break; + case 59: +#line 195 "parse.y" +{sqliteDeferForeignKey(pParse,yymsp[0].minor.yy372);} +#line 2204 "parse.c" + break; + case 60: +#line 196 "parse.y" +{ + sqliteAddCollateType(pParse, sqliteCollateType(yymsp[0].minor.yy298.z, yymsp[0].minor.yy298.n)); +} +#line 2211 "parse.c" + /* No destructor defined for COLLATE */ + break; + case 61: +#line 206 "parse.y" +{ yygotominor.yy372 = OE_Restrict * 0x010101; } +#line 2217 "parse.c" + break; + case 62: +#line 207 "parse.y" +{ yygotominor.yy372 = (yymsp[-1].minor.yy372 & yymsp[0].minor.yy407.mask) | yymsp[0].minor.yy407.value; } +#line 2222 "parse.c" + break; + case 63: +#line 209 "parse.y" +{ yygotominor.yy407.value = 0; yygotominor.yy407.mask = 0x000000; } +#line 2227 "parse.c" + /* No destructor defined for MATCH */ + /* No destructor defined for nm */ + break; + case 64: +#line 210 "parse.y" +{ yygotominor.yy407.value = yymsp[0].minor.yy372; yygotominor.yy407.mask = 0x0000ff; } +#line 2234 "parse.c" + /* No destructor defined for ON */ + /* No destructor defined for DELETE */ + break; + case 65: +#line 211 "parse.y" +{ yygotominor.yy407.value = yymsp[0].minor.yy372<<8; yygotominor.yy407.mask = 0x00ff00; } +#line 2241 "parse.c" + /* No destructor defined for ON */ + /* No destructor defined for UPDATE */ + break; + case 66: +#line 212 "parse.y" +{ yygotominor.yy407.value = yymsp[0].minor.yy372<<16; yygotominor.yy407.mask = 0xff0000; } +#line 2248 "parse.c" + /* No destructor defined for ON */ + /* No destructor defined for INSERT */ + break; + case 67: +#line 214 "parse.y" +{ yygotominor.yy372 = OE_SetNull; } +#line 2255 "parse.c" + /* No destructor defined for SET */ + /* No destructor defined for NULL */ + break; + case 68: +#line 215 "parse.y" +{ yygotominor.yy372 = OE_SetDflt; } +#line 2262 "parse.c" + /* No destructor defined for SET */ + /* No destructor defined for DEFAULT */ + break; + case 69: +#line 216 "parse.y" +{ yygotominor.yy372 = OE_Cascade; } +#line 2269 "parse.c" + /* No destructor defined for CASCADE */ + break; + case 70: +#line 217 "parse.y" +{ yygotominor.yy372 = OE_Restrict; } +#line 2275 "parse.c" + /* No destructor defined for RESTRICT */ + break; + case 71: +#line 219 "parse.y" +{yygotominor.yy372 = yymsp[0].minor.yy372;} +#line 2281 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for DEFERRABLE */ + break; + case 72: +#line 220 "parse.y" +{yygotominor.yy372 = yymsp[0].minor.yy372;} +#line 2288 "parse.c" + /* No destructor defined for DEFERRABLE */ + break; + case 73: +#line 222 "parse.y" +{yygotominor.yy372 = 0;} +#line 2294 "parse.c" + break; + case 74: +#line 223 "parse.y" +{yygotominor.yy372 = 1;} +#line 2299 "parse.c" + /* No destructor defined for INITIALLY */ + /* No destructor defined for DEFERRED */ + break; + case 75: +#line 224 "parse.y" +{yygotominor.yy372 = 0;} +#line 2306 "parse.c" + /* No destructor defined for INITIALLY */ + /* No destructor defined for IMMEDIATE */ + break; + case 76: + break; + case 77: + /* No destructor defined for COMMA */ + /* No destructor defined for conslist */ + break; + case 78: + /* No destructor defined for conslist */ + /* No destructor defined for COMMA */ + /* No destructor defined for tcons */ + break; + case 79: + /* No destructor defined for conslist */ + /* No destructor defined for tcons */ + break; + case 80: + /* No destructor defined for tcons */ + break; + case 81: + /* No destructor defined for CONSTRAINT */ + /* No destructor defined for nm */ + break; + case 82: +#line 236 "parse.y" +{sqliteAddPrimaryKey(pParse,yymsp[-2].minor.yy320,yymsp[0].minor.yy372);} +#line 2335 "parse.c" + /* No destructor defined for PRIMARY */ + /* No destructor defined for KEY */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 83: +#line 238 "parse.y" +{sqliteCreateIndex(pParse,0,0,yymsp[-2].minor.yy320,yymsp[0].minor.yy372,0,0);} +#line 2344 "parse.c" + /* No destructor defined for UNIQUE */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 84: + /* No destructor defined for CHECK */ + yy_destructor(158,&yymsp[-1].minor); + /* No destructor defined for onconf */ + break; + case 85: +#line 241 "parse.y" +{ + sqliteCreateForeignKey(pParse, yymsp[-6].minor.yy320, &yymsp[-3].minor.yy298, yymsp[-2].minor.yy320, yymsp[-1].minor.yy372); + sqliteDeferForeignKey(pParse, yymsp[0].minor.yy372); +} +#line 2360 "parse.c" + /* No destructor defined for FOREIGN */ + /* No destructor defined for KEY */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + /* No destructor defined for REFERENCES */ + break; + case 86: +#line 246 "parse.y" +{yygotominor.yy372 = 0;} +#line 2370 "parse.c" + break; + case 87: +#line 247 "parse.y" +{yygotominor.yy372 = yymsp[0].minor.yy372;} +#line 2375 "parse.c" + break; + case 88: +#line 255 "parse.y" +{ yygotominor.yy372 = OE_Default; } +#line 2380 "parse.c" + break; + case 89: +#line 256 "parse.y" +{ yygotominor.yy372 = yymsp[0].minor.yy372; } +#line 2385 "parse.c" + /* No destructor defined for ON */ + /* No destructor defined for CONFLICT */ + break; + case 90: +#line 257 "parse.y" +{ yygotominor.yy372 = OE_Default; } +#line 2392 "parse.c" + break; + case 91: +#line 258 "parse.y" +{ yygotominor.yy372 = yymsp[0].minor.yy372; } +#line 2397 "parse.c" + /* No destructor defined for OR */ + break; + case 92: +#line 259 "parse.y" +{ yygotominor.yy372 = OE_Rollback; } +#line 2403 "parse.c" + /* No destructor defined for ROLLBACK */ + break; + case 93: +#line 260 "parse.y" +{ yygotominor.yy372 = OE_Abort; } +#line 2409 "parse.c" + /* No destructor defined for ABORT */ + break; + case 94: +#line 261 "parse.y" +{ yygotominor.yy372 = OE_Fail; } +#line 2415 "parse.c" + /* No destructor defined for FAIL */ + break; + case 95: +#line 262 "parse.y" +{ yygotominor.yy372 = OE_Ignore; } +#line 2421 "parse.c" + /* No destructor defined for IGNORE */ + break; + case 96: +#line 263 "parse.y" +{ yygotominor.yy372 = OE_Replace; } +#line 2427 "parse.c" + /* No destructor defined for REPLACE */ + break; + case 97: +#line 267 "parse.y" +{sqliteDropTable(pParse,&yymsp[0].minor.yy298,0);} +#line 2433 "parse.c" + /* No destructor defined for DROP */ + /* No destructor defined for TABLE */ + break; + case 98: +#line 271 "parse.y" +{ + sqliteCreateView(pParse, &yymsp[-5].minor.yy0, &yymsp[-2].minor.yy298, yymsp[0].minor.yy179, yymsp[-4].minor.yy372); +} +#line 2442 "parse.c" + /* No destructor defined for VIEW */ + /* No destructor defined for AS */ + break; + case 99: +#line 274 "parse.y" +{ + sqliteDropTable(pParse, &yymsp[0].minor.yy298, 1); +} +#line 2451 "parse.c" + /* No destructor defined for DROP */ + /* No destructor defined for VIEW */ + break; + case 100: +#line 280 "parse.y" +{ + sqliteSelect(pParse, yymsp[0].minor.yy179, SRT_Callback, 0, 0, 0, 0); + sqliteSelectDelete(yymsp[0].minor.yy179); +} +#line 2461 "parse.c" + break; + case 101: +#line 290 "parse.y" +{yygotominor.yy179 = yymsp[0].minor.yy179;} +#line 2466 "parse.c" + break; + case 102: +#line 291 "parse.y" +{ + if( yymsp[0].minor.yy179 ){ + yymsp[0].minor.yy179->op = yymsp[-1].minor.yy372; + yymsp[0].minor.yy179->pPrior = yymsp[-2].minor.yy179; + } + yygotominor.yy179 = yymsp[0].minor.yy179; +} +#line 2477 "parse.c" + break; + case 103: +#line 299 "parse.y" +{yygotominor.yy372 = TK_UNION;} +#line 2482 "parse.c" + /* No destructor defined for UNION */ + break; + case 104: +#line 300 "parse.y" +{yygotominor.yy372 = TK_ALL;} +#line 2488 "parse.c" + /* No destructor defined for UNION */ + /* No destructor defined for ALL */ + break; + case 105: +#line 301 "parse.y" +{yygotominor.yy372 = TK_INTERSECT;} +#line 2495 "parse.c" + /* No destructor defined for INTERSECT */ + break; + case 106: +#line 302 "parse.y" +{yygotominor.yy372 = TK_EXCEPT;} +#line 2501 "parse.c" + /* No destructor defined for EXCEPT */ + break; + case 107: +#line 304 "parse.y" +{ + yygotominor.yy179 = sqliteSelectNew(yymsp[-6].minor.yy322,yymsp[-5].minor.yy307,yymsp[-4].minor.yy242,yymsp[-3].minor.yy322,yymsp[-2].minor.yy242,yymsp[-1].minor.yy322,yymsp[-7].minor.yy372,yymsp[0].minor.yy124.limit,yymsp[0].minor.yy124.offset); +} +#line 2509 "parse.c" + /* No destructor defined for SELECT */ + break; + case 108: +#line 312 "parse.y" +{yygotominor.yy372 = 1;} +#line 2515 "parse.c" + /* No destructor defined for DISTINCT */ + break; + case 109: +#line 313 "parse.y" +{yygotominor.yy372 = 0;} +#line 2521 "parse.c" + /* No destructor defined for ALL */ + break; + case 110: +#line 314 "parse.y" +{yygotominor.yy372 = 0;} +#line 2527 "parse.c" + break; + case 111: +#line 325 "parse.y" +{yygotominor.yy322 = yymsp[-1].minor.yy322;} +#line 2532 "parse.c" + /* No destructor defined for COMMA */ + break; + case 112: +#line 326 "parse.y" +{yygotominor.yy322 = 0;} +#line 2538 "parse.c" + break; + case 113: +#line 327 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(yymsp[-2].minor.yy322,yymsp[-1].minor.yy242,yymsp[0].minor.yy298.n?&yymsp[0].minor.yy298:0); +} +#line 2545 "parse.c" + break; + case 114: +#line 330 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(yymsp[-1].minor.yy322, sqliteExpr(TK_ALL, 0, 0, 0), 0); +} +#line 2552 "parse.c" + /* No destructor defined for STAR */ + break; + case 115: +#line 333 "parse.y" +{ + Expr *pRight = sqliteExpr(TK_ALL, 0, 0, 0); + Expr *pLeft = sqliteExpr(TK_ID, 0, 0, &yymsp[-2].minor.yy298); + yygotominor.yy322 = sqliteExprListAppend(yymsp[-3].minor.yy322, sqliteExpr(TK_DOT, pLeft, pRight, 0), 0); +} +#line 2562 "parse.c" + /* No destructor defined for DOT */ + /* No destructor defined for STAR */ + break; + case 116: +#line 343 "parse.y" +{ yygotominor.yy298 = yymsp[0].minor.yy298; } +#line 2569 "parse.c" + /* No destructor defined for AS */ + break; + case 117: +#line 344 "parse.y" +{ yygotominor.yy298 = yymsp[0].minor.yy298; } +#line 2575 "parse.c" + break; + case 118: +#line 345 "parse.y" +{ yygotominor.yy298.n = 0; } +#line 2580 "parse.c" + break; + case 119: +#line 357 "parse.y" +{yygotominor.yy307 = sqliteMalloc(sizeof(*yygotominor.yy307));} +#line 2585 "parse.c" + break; + case 120: +#line 358 "parse.y" +{yygotominor.yy307 = yymsp[0].minor.yy307;} +#line 2590 "parse.c" + /* No destructor defined for FROM */ + break; + case 121: +#line 363 "parse.y" +{ + yygotominor.yy307 = yymsp[-1].minor.yy307; + if( yygotominor.yy307 && yygotominor.yy307->nSrc>0 ) yygotominor.yy307->a[yygotominor.yy307->nSrc-1].jointype = yymsp[0].minor.yy372; +} +#line 2599 "parse.c" + break; + case 122: +#line 367 "parse.y" +{yygotominor.yy307 = 0;} +#line 2604 "parse.c" + break; + case 123: +#line 368 "parse.y" +{ + yygotominor.yy307 = sqliteSrcListAppend(yymsp[-5].minor.yy307,&yymsp[-4].minor.yy298,&yymsp[-3].minor.yy298); + if( yymsp[-2].minor.yy298.n ) sqliteSrcListAddAlias(yygotominor.yy307,&yymsp[-2].minor.yy298); + if( yymsp[-1].minor.yy242 ){ + if( yygotominor.yy307 && yygotominor.yy307->nSrc>1 ){ yygotominor.yy307->a[yygotominor.yy307->nSrc-2].pOn = yymsp[-1].minor.yy242; } + else { sqliteExprDelete(yymsp[-1].minor.yy242); } + } + if( yymsp[0].minor.yy320 ){ + if( yygotominor.yy307 && yygotominor.yy307->nSrc>1 ){ yygotominor.yy307->a[yygotominor.yy307->nSrc-2].pUsing = yymsp[0].minor.yy320; } + else { sqliteIdListDelete(yymsp[0].minor.yy320); } + } +} +#line 2620 "parse.c" + break; + case 124: +#line 381 "parse.y" +{ + yygotominor.yy307 = sqliteSrcListAppend(yymsp[-6].minor.yy307,0,0); + yygotominor.yy307->a[yygotominor.yy307->nSrc-1].pSelect = yymsp[-4].minor.yy179; + if( yymsp[-2].minor.yy298.n ) sqliteSrcListAddAlias(yygotominor.yy307,&yymsp[-2].minor.yy298); + if( yymsp[-1].minor.yy242 ){ + if( yygotominor.yy307 && yygotominor.yy307->nSrc>1 ){ yygotominor.yy307->a[yygotominor.yy307->nSrc-2].pOn = yymsp[-1].minor.yy242; } + else { sqliteExprDelete(yymsp[-1].minor.yy242); } + } + if( yymsp[0].minor.yy320 ){ + if( yygotominor.yy307 && yygotominor.yy307->nSrc>1 ){ yygotominor.yy307->a[yygotominor.yy307->nSrc-2].pUsing = yymsp[0].minor.yy320; } + else { sqliteIdListDelete(yymsp[0].minor.yy320); } + } +} +#line 2637 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 125: +#line 401 "parse.y" +{yygotominor.yy179 = yymsp[0].minor.yy179;} +#line 2644 "parse.c" + break; + case 126: +#line 402 "parse.y" +{ + yygotominor.yy179 = sqliteSelectNew(0,yymsp[0].minor.yy307,0,0,0,0,0,-1,0); +} +#line 2651 "parse.c" + break; + case 127: +#line 407 "parse.y" +{yygotominor.yy298.z=0; yygotominor.yy298.n=0;} +#line 2656 "parse.c" + break; + case 128: +#line 408 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy298;} +#line 2661 "parse.c" + /* No destructor defined for DOT */ + break; + case 129: +#line 412 "parse.y" +{ yygotominor.yy372 = JT_INNER; } +#line 2667 "parse.c" + /* No destructor defined for COMMA */ + break; + case 130: +#line 413 "parse.y" +{ yygotominor.yy372 = JT_INNER; } +#line 2673 "parse.c" + /* No destructor defined for JOIN */ + break; + case 131: +#line 414 "parse.y" +{ yygotominor.yy372 = sqliteJoinType(pParse,&yymsp[-1].minor.yy0,0,0); } +#line 2679 "parse.c" + /* No destructor defined for JOIN */ + break; + case 132: +#line 415 "parse.y" +{ yygotominor.yy372 = sqliteJoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy298,0); } +#line 2685 "parse.c" + /* No destructor defined for JOIN */ + break; + case 133: +#line 417 "parse.y" +{ yygotominor.yy372 = sqliteJoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy298,&yymsp[-1].minor.yy298); } +#line 2691 "parse.c" + /* No destructor defined for JOIN */ + break; + case 134: +#line 421 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 2697 "parse.c" + /* No destructor defined for ON */ + break; + case 135: +#line 422 "parse.y" +{yygotominor.yy242 = 0;} +#line 2703 "parse.c" + break; + case 136: +#line 426 "parse.y" +{yygotominor.yy320 = yymsp[-1].minor.yy320;} +#line 2708 "parse.c" + /* No destructor defined for USING */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 137: +#line 427 "parse.y" +{yygotominor.yy320 = 0;} +#line 2716 "parse.c" + break; + case 138: +#line 437 "parse.y" +{yygotominor.yy322 = 0;} +#line 2721 "parse.c" + break; + case 139: +#line 438 "parse.y" +{yygotominor.yy322 = yymsp[0].minor.yy322;} +#line 2726 "parse.c" + /* No destructor defined for ORDER */ + /* No destructor defined for BY */ + break; + case 140: +#line 439 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(yymsp[-4].minor.yy322,yymsp[-2].minor.yy242,0); + if( yygotominor.yy322 ) yygotominor.yy322->a[yygotominor.yy322->nExpr-1].sortOrder = yymsp[-1].minor.yy372+yymsp[0].minor.yy372; +} +#line 2736 "parse.c" + /* No destructor defined for COMMA */ + break; + case 141: +#line 443 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(0,yymsp[-2].minor.yy242,0); + if( yygotominor.yy322 ) yygotominor.yy322->a[0].sortOrder = yymsp[-1].minor.yy372+yymsp[0].minor.yy372; +} +#line 2745 "parse.c" + break; + case 142: +#line 447 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 2750 "parse.c" + break; + case 143: +#line 452 "parse.y" +{yygotominor.yy372 = SQLITE_SO_ASC;} +#line 2755 "parse.c" + /* No destructor defined for ASC */ + break; + case 144: +#line 453 "parse.y" +{yygotominor.yy372 = SQLITE_SO_DESC;} +#line 2761 "parse.c" + /* No destructor defined for DESC */ + break; + case 145: +#line 454 "parse.y" +{yygotominor.yy372 = SQLITE_SO_ASC;} +#line 2767 "parse.c" + break; + case 146: +#line 455 "parse.y" +{yygotominor.yy372 = SQLITE_SO_UNK;} +#line 2772 "parse.c" + break; + case 147: +#line 456 "parse.y" +{yygotominor.yy372 = sqliteCollateType(yymsp[0].minor.yy298.z, yymsp[0].minor.yy298.n);} +#line 2777 "parse.c" + /* No destructor defined for COLLATE */ + break; + case 148: +#line 460 "parse.y" +{yygotominor.yy322 = 0;} +#line 2783 "parse.c" + break; + case 149: +#line 461 "parse.y" +{yygotominor.yy322 = yymsp[0].minor.yy322;} +#line 2788 "parse.c" + /* No destructor defined for GROUP */ + /* No destructor defined for BY */ + break; + case 150: +#line 465 "parse.y" +{yygotominor.yy242 = 0;} +#line 2795 "parse.c" + break; + case 151: +#line 466 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 2800 "parse.c" + /* No destructor defined for HAVING */ + break; + case 152: +#line 469 "parse.y" +{yygotominor.yy124.limit = -1; yygotominor.yy124.offset = 0;} +#line 2806 "parse.c" + break; + case 153: +#line 470 "parse.y" +{yygotominor.yy124.limit = yymsp[0].minor.yy372; yygotominor.yy124.offset = 0;} +#line 2811 "parse.c" + /* No destructor defined for LIMIT */ + break; + case 154: +#line 472 "parse.y" +{yygotominor.yy124.limit = yymsp[-2].minor.yy372; yygotominor.yy124.offset = yymsp[0].minor.yy372;} +#line 2817 "parse.c" + /* No destructor defined for LIMIT */ + /* No destructor defined for OFFSET */ + break; + case 155: +#line 474 "parse.y" +{yygotominor.yy124.limit = yymsp[0].minor.yy372; yygotominor.yy124.offset = yymsp[-2].minor.yy372;} +#line 2824 "parse.c" + /* No destructor defined for LIMIT */ + /* No destructor defined for COMMA */ + break; + case 156: +#line 478 "parse.y" +{ + sqliteDeleteFrom(pParse, sqliteSrcListAppend(0,&yymsp[-2].minor.yy298,&yymsp[-1].minor.yy298), yymsp[0].minor.yy242); +} +#line 2833 "parse.c" + /* No destructor defined for DELETE */ + /* No destructor defined for FROM */ + break; + case 157: +#line 485 "parse.y" +{yygotominor.yy242 = 0;} +#line 2840 "parse.c" + break; + case 158: +#line 486 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 2845 "parse.c" + /* No destructor defined for WHERE */ + break; + case 159: +#line 494 "parse.y" +{sqliteUpdate(pParse,sqliteSrcListAppend(0,&yymsp[-4].minor.yy298,&yymsp[-3].minor.yy298),yymsp[-1].minor.yy322,yymsp[0].minor.yy242,yymsp[-5].minor.yy372);} +#line 2851 "parse.c" + /* No destructor defined for UPDATE */ + /* No destructor defined for SET */ + break; + case 160: +#line 497 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(yymsp[-4].minor.yy322,yymsp[0].minor.yy242,&yymsp[-2].minor.yy298);} +#line 2858 "parse.c" + /* No destructor defined for COMMA */ + /* No destructor defined for EQ */ + break; + case 161: +#line 498 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(0,yymsp[0].minor.yy242,&yymsp[-2].minor.yy298);} +#line 2865 "parse.c" + /* No destructor defined for EQ */ + break; + case 162: +#line 504 "parse.y" +{sqliteInsert(pParse, sqliteSrcListAppend(0,&yymsp[-6].minor.yy298,&yymsp[-5].minor.yy298), yymsp[-1].minor.yy322, 0, yymsp[-4].minor.yy320, yymsp[-8].minor.yy372);} +#line 2871 "parse.c" + /* No destructor defined for INTO */ + /* No destructor defined for VALUES */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 163: +#line 506 "parse.y" +{sqliteInsert(pParse, sqliteSrcListAppend(0,&yymsp[-3].minor.yy298,&yymsp[-2].minor.yy298), 0, yymsp[0].minor.yy179, yymsp[-1].minor.yy320, yymsp[-5].minor.yy372);} +#line 2880 "parse.c" + /* No destructor defined for INTO */ + break; + case 164: +#line 509 "parse.y" +{yygotominor.yy372 = yymsp[0].minor.yy372;} +#line 2886 "parse.c" + /* No destructor defined for INSERT */ + break; + case 165: +#line 510 "parse.y" +{yygotominor.yy372 = OE_Replace;} +#line 2892 "parse.c" + /* No destructor defined for REPLACE */ + break; + case 166: +#line 516 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(yymsp[-2].minor.yy322,yymsp[0].minor.yy242,0);} +#line 2898 "parse.c" + /* No destructor defined for COMMA */ + break; + case 167: +#line 517 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(0,yymsp[0].minor.yy242,0);} +#line 2904 "parse.c" + break; + case 168: +#line 524 "parse.y" +{yygotominor.yy320 = 0;} +#line 2909 "parse.c" + break; + case 169: +#line 525 "parse.y" +{yygotominor.yy320 = yymsp[-1].minor.yy320;} +#line 2914 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 170: +#line 526 "parse.y" +{yygotominor.yy320 = sqliteIdListAppend(yymsp[-2].minor.yy320,&yymsp[0].minor.yy298);} +#line 2921 "parse.c" + /* No destructor defined for COMMA */ + break; + case 171: +#line 527 "parse.y" +{yygotominor.yy320 = sqliteIdListAppend(0,&yymsp[0].minor.yy298);} +#line 2927 "parse.c" + break; + case 172: +#line 535 "parse.y" +{yygotominor.yy242 = yymsp[-1].minor.yy242; sqliteExprSpan(yygotominor.yy242,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); } +#line 2932 "parse.c" + break; + case 173: +#line 536 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_NULL, 0, 0, &yymsp[0].minor.yy0);} +#line 2937 "parse.c" + break; + case 174: +#line 537 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_ID, 0, 0, &yymsp[0].minor.yy0);} +#line 2942 "parse.c" + break; + case 175: +#line 538 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_ID, 0, 0, &yymsp[0].minor.yy0);} +#line 2947 "parse.c" + break; + case 176: +#line 539 "parse.y" +{ + Expr *temp1 = sqliteExpr(TK_ID, 0, 0, &yymsp[-2].minor.yy298); + Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &yymsp[0].minor.yy298); + yygotominor.yy242 = sqliteExpr(TK_DOT, temp1, temp2, 0); +} +#line 2956 "parse.c" + /* No destructor defined for DOT */ + break; + case 177: +#line 544 "parse.y" +{ + Expr *temp1 = sqliteExpr(TK_ID, 0, 0, &yymsp[-4].minor.yy298); + Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &yymsp[-2].minor.yy298); + Expr *temp3 = sqliteExpr(TK_ID, 0, 0, &yymsp[0].minor.yy298); + Expr *temp4 = sqliteExpr(TK_DOT, temp2, temp3, 0); + yygotominor.yy242 = sqliteExpr(TK_DOT, temp1, temp4, 0); +} +#line 2968 "parse.c" + /* No destructor defined for DOT */ + /* No destructor defined for DOT */ + break; + case 178: +#line 551 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_INTEGER, 0, 0, &yymsp[0].minor.yy0);} +#line 2975 "parse.c" + break; + case 179: +#line 552 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_FLOAT, 0, 0, &yymsp[0].minor.yy0);} +#line 2980 "parse.c" + break; + case 180: +#line 553 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_STRING, 0, 0, &yymsp[0].minor.yy0);} +#line 2985 "parse.c" + break; + case 181: +#line 554 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_VARIABLE, 0, 0, &yymsp[0].minor.yy0); + if( yygotominor.yy242 ) yygotominor.yy242->iTable = ++pParse->nVar; +} +#line 2993 "parse.c" + break; + case 182: +#line 558 "parse.y" +{ + yygotominor.yy242 = sqliteExprFunction(yymsp[-1].minor.yy322, &yymsp[-3].minor.yy0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); +} +#line 3001 "parse.c" + /* No destructor defined for LP */ + break; + case 183: +#line 562 "parse.y" +{ + yygotominor.yy242 = sqliteExprFunction(0, &yymsp[-3].minor.yy0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); +} +#line 3010 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for STAR */ + break; + case 184: +#line 566 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_AND, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3017 "parse.c" + /* No destructor defined for AND */ + break; + case 185: +#line 567 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_OR, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3023 "parse.c" + /* No destructor defined for OR */ + break; + case 186: +#line 568 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_LT, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3029 "parse.c" + /* No destructor defined for LT */ + break; + case 187: +#line 569 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_GT, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3035 "parse.c" + /* No destructor defined for GT */ + break; + case 188: +#line 570 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_LE, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3041 "parse.c" + /* No destructor defined for LE */ + break; + case 189: +#line 571 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_GE, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3047 "parse.c" + /* No destructor defined for GE */ + break; + case 190: +#line 572 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_NE, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3053 "parse.c" + /* No destructor defined for NE */ + break; + case 191: +#line 573 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_EQ, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3059 "parse.c" + /* No destructor defined for EQ */ + break; + case 192: +#line 574 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_BITAND, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3065 "parse.c" + /* No destructor defined for BITAND */ + break; + case 193: +#line 575 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_BITOR, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3071 "parse.c" + /* No destructor defined for BITOR */ + break; + case 194: +#line 576 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_LSHIFT, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3077 "parse.c" + /* No destructor defined for LSHIFT */ + break; + case 195: +#line 577 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_RSHIFT, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3083 "parse.c" + /* No destructor defined for RSHIFT */ + break; + case 196: +#line 578 "parse.y" +{ + ExprList *pList = sqliteExprListAppend(0, yymsp[0].minor.yy242, 0); + pList = sqliteExprListAppend(pList, yymsp[-2].minor.yy242, 0); + yygotominor.yy242 = sqliteExprFunction(pList, 0); + if( yygotominor.yy242 ) yygotominor.yy242->op = yymsp[-1].minor.yy372; + sqliteExprSpan(yygotominor.yy242, &yymsp[-2].minor.yy242->span, &yymsp[0].minor.yy242->span); +} +#line 3095 "parse.c" + break; + case 197: +#line 585 "parse.y" +{ + ExprList *pList = sqliteExprListAppend(0, yymsp[0].minor.yy242, 0); + pList = sqliteExprListAppend(pList, yymsp[-3].minor.yy242, 0); + yygotominor.yy242 = sqliteExprFunction(pList, 0); + if( yygotominor.yy242 ) yygotominor.yy242->op = yymsp[-1].minor.yy372; + yygotominor.yy242 = sqliteExpr(TK_NOT, yygotominor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-3].minor.yy242->span,&yymsp[0].minor.yy242->span); +} +#line 3107 "parse.c" + /* No destructor defined for NOT */ + break; + case 198: +#line 594 "parse.y" +{yygotominor.yy372 = TK_LIKE;} +#line 3113 "parse.c" + /* No destructor defined for LIKE */ + break; + case 199: +#line 595 "parse.y" +{yygotominor.yy372 = TK_GLOB;} +#line 3119 "parse.c" + /* No destructor defined for GLOB */ + break; + case 200: +#line 596 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_PLUS, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3125 "parse.c" + /* No destructor defined for PLUS */ + break; + case 201: +#line 597 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_MINUS, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3131 "parse.c" + /* No destructor defined for MINUS */ + break; + case 202: +#line 598 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_STAR, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3137 "parse.c" + /* No destructor defined for STAR */ + break; + case 203: +#line 599 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_SLASH, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3143 "parse.c" + /* No destructor defined for SLASH */ + break; + case 204: +#line 600 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_REM, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3149 "parse.c" + /* No destructor defined for REM */ + break; + case 205: +#line 601 "parse.y" +{yygotominor.yy242 = sqliteExpr(TK_CONCAT, yymsp[-2].minor.yy242, yymsp[0].minor.yy242, 0);} +#line 3155 "parse.c" + /* No destructor defined for CONCAT */ + break; + case 206: +#line 602 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_ISNULL, yymsp[-1].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3164 "parse.c" + break; + case 207: +#line 606 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_ISNULL, yymsp[-2].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-2].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3172 "parse.c" + /* No destructor defined for IS */ + break; + case 208: +#line 610 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_NOTNULL, yymsp[-1].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3181 "parse.c" + break; + case 209: +#line 614 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_NOTNULL, yymsp[-2].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-2].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3189 "parse.c" + /* No destructor defined for NOT */ + break; + case 210: +#line 618 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_NOTNULL, yymsp[-3].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-3].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3198 "parse.c" + /* No destructor defined for IS */ + /* No destructor defined for NOT */ + break; + case 211: +#line 622 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_NOT, yymsp[0].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy242->span); +} +#line 3208 "parse.c" + break; + case 212: +#line 626 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_BITNOT, yymsp[0].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy242->span); +} +#line 3216 "parse.c" + break; + case 213: +#line 630 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_UMINUS, yymsp[0].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy242->span); +} +#line 3224 "parse.c" + break; + case 214: +#line 634 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_UPLUS, yymsp[0].minor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy242->span); +} +#line 3232 "parse.c" + break; + case 215: +#line 638 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_SELECT, 0, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pSelect = yymsp[-1].minor.yy179; + sqliteExprSpan(yygotominor.yy242,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); +} +#line 3241 "parse.c" + break; + case 216: +#line 643 "parse.y" +{ + ExprList *pList = sqliteExprListAppend(0, yymsp[-2].minor.yy242, 0); + pList = sqliteExprListAppend(pList, yymsp[0].minor.yy242, 0); + yygotominor.yy242 = sqliteExpr(TK_BETWEEN, yymsp[-4].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pList = pList; + sqliteExprSpan(yygotominor.yy242,&yymsp[-4].minor.yy242->span,&yymsp[0].minor.yy242->span); +} +#line 3252 "parse.c" + /* No destructor defined for BETWEEN */ + /* No destructor defined for AND */ + break; + case 217: +#line 650 "parse.y" +{ + ExprList *pList = sqliteExprListAppend(0, yymsp[-2].minor.yy242, 0); + pList = sqliteExprListAppend(pList, yymsp[0].minor.yy242, 0); + yygotominor.yy242 = sqliteExpr(TK_BETWEEN, yymsp[-5].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pList = pList; + yygotominor.yy242 = sqliteExpr(TK_NOT, yygotominor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-5].minor.yy242->span,&yymsp[0].minor.yy242->span); +} +#line 3266 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for BETWEEN */ + /* No destructor defined for AND */ + break; + case 218: +#line 658 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-4].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pList = yymsp[-1].minor.yy322; + sqliteExprSpan(yygotominor.yy242,&yymsp[-4].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3278 "parse.c" + /* No destructor defined for IN */ + /* No destructor defined for LP */ + break; + case 219: +#line 663 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-4].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pSelect = yymsp[-1].minor.yy179; + sqliteExprSpan(yygotominor.yy242,&yymsp[-4].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3289 "parse.c" + /* No destructor defined for IN */ + /* No destructor defined for LP */ + break; + case 220: +#line 668 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-5].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pList = yymsp[-1].minor.yy322; + yygotominor.yy242 = sqliteExpr(TK_NOT, yygotominor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-5].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3301 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for IN */ + /* No destructor defined for LP */ + break; + case 221: +#line 674 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-5].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pSelect = yymsp[-1].minor.yy179; + yygotominor.yy242 = sqliteExpr(TK_NOT, yygotominor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-5].minor.yy242->span,&yymsp[0].minor.yy0); +} +#line 3314 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for IN */ + /* No destructor defined for LP */ + break; + case 222: +#line 680 "parse.y" +{ + SrcList *pSrc = sqliteSrcListAppend(0, &yymsp[-1].minor.yy298, &yymsp[0].minor.yy298); + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-3].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pSelect = sqliteSelectNew(0,pSrc,0,0,0,0,0,-1,0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-3].minor.yy242->span,yymsp[0].minor.yy298.z?&yymsp[0].minor.yy298:&yymsp[-1].minor.yy298); +} +#line 3327 "parse.c" + /* No destructor defined for IN */ + break; + case 223: +#line 686 "parse.y" +{ + SrcList *pSrc = sqliteSrcListAppend(0, &yymsp[-1].minor.yy298, &yymsp[0].minor.yy298); + yygotominor.yy242 = sqliteExpr(TK_IN, yymsp[-4].minor.yy242, 0, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pSelect = sqliteSelectNew(0,pSrc,0,0,0,0,0,-1,0); + yygotominor.yy242 = sqliteExpr(TK_NOT, yygotominor.yy242, 0, 0); + sqliteExprSpan(yygotominor.yy242,&yymsp[-4].minor.yy242->span,yymsp[0].minor.yy298.z?&yymsp[0].minor.yy298:&yymsp[-1].minor.yy298); +} +#line 3339 "parse.c" + /* No destructor defined for NOT */ + /* No destructor defined for IN */ + break; + case 224: +#line 696 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_CASE, yymsp[-3].minor.yy242, yymsp[-1].minor.yy242, 0); + if( yygotominor.yy242 ) yygotominor.yy242->pList = yymsp[-2].minor.yy322; + sqliteExprSpan(yygotominor.yy242, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0); +} +#line 3350 "parse.c" + break; + case 225: +#line 703 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(yymsp[-4].minor.yy322, yymsp[-2].minor.yy242, 0); + yygotominor.yy322 = sqliteExprListAppend(yygotominor.yy322, yymsp[0].minor.yy242, 0); +} +#line 3358 "parse.c" + /* No destructor defined for WHEN */ + /* No destructor defined for THEN */ + break; + case 226: +#line 707 "parse.y" +{ + yygotominor.yy322 = sqliteExprListAppend(0, yymsp[-2].minor.yy242, 0); + yygotominor.yy322 = sqliteExprListAppend(yygotominor.yy322, yymsp[0].minor.yy242, 0); +} +#line 3368 "parse.c" + /* No destructor defined for WHEN */ + /* No destructor defined for THEN */ + break; + case 227: +#line 712 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 3375 "parse.c" + /* No destructor defined for ELSE */ + break; + case 228: +#line 713 "parse.y" +{yygotominor.yy242 = 0;} +#line 3381 "parse.c" + break; + case 229: +#line 715 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 3386 "parse.c" + break; + case 230: +#line 716 "parse.y" +{yygotominor.yy242 = 0;} +#line 3391 "parse.c" + break; + case 231: +#line 724 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(yymsp[-2].minor.yy322,yymsp[0].minor.yy242,0);} +#line 3396 "parse.c" + /* No destructor defined for COMMA */ + break; + case 232: +#line 725 "parse.y" +{yygotominor.yy322 = sqliteExprListAppend(0,yymsp[0].minor.yy242,0);} +#line 3402 "parse.c" + break; + case 233: +#line 726 "parse.y" +{yygotominor.yy242 = yymsp[0].minor.yy242;} +#line 3407 "parse.c" + break; + case 234: +#line 727 "parse.y" +{yygotominor.yy242 = 0;} +#line 3412 "parse.c" + break; + case 235: +#line 732 "parse.y" +{ + SrcList *pSrc = sqliteSrcListAppend(0, &yymsp[-5].minor.yy298, &yymsp[-4].minor.yy298); + if( yymsp[-9].minor.yy372!=OE_None ) yymsp[-9].minor.yy372 = yymsp[0].minor.yy372; + if( yymsp[-9].minor.yy372==OE_Default) yymsp[-9].minor.yy372 = OE_Abort; + sqliteCreateIndex(pParse, &yymsp[-7].minor.yy298, pSrc, yymsp[-2].minor.yy320, yymsp[-9].minor.yy372, &yymsp[-10].minor.yy0, &yymsp[-1].minor.yy0); +} +#line 3422 "parse.c" + /* No destructor defined for INDEX */ + /* No destructor defined for ON */ + /* No destructor defined for LP */ + break; + case 236: +#line 740 "parse.y" +{ yygotominor.yy372 = OE_Abort; } +#line 3430 "parse.c" + /* No destructor defined for UNIQUE */ + break; + case 237: +#line 741 "parse.y" +{ yygotominor.yy372 = OE_None; } +#line 3436 "parse.c" + break; + case 238: +#line 749 "parse.y" +{yygotominor.yy320 = 0;} +#line 3441 "parse.c" + break; + case 239: +#line 750 "parse.y" +{yygotominor.yy320 = yymsp[-1].minor.yy320;} +#line 3446 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 240: +#line 751 "parse.y" +{yygotominor.yy320 = sqliteIdListAppend(yymsp[-2].minor.yy320,&yymsp[0].minor.yy298);} +#line 3453 "parse.c" + /* No destructor defined for COMMA */ + break; + case 241: +#line 752 "parse.y" +{yygotominor.yy320 = sqliteIdListAppend(0,&yymsp[0].minor.yy298);} +#line 3459 "parse.c" + break; + case 242: +#line 753 "parse.y" +{yygotominor.yy298 = yymsp[-1].minor.yy298;} +#line 3464 "parse.c" + /* No destructor defined for sortorder */ + break; + case 243: +#line 758 "parse.y" +{ + sqliteDropIndex(pParse, sqliteSrcListAppend(0,&yymsp[-1].minor.yy298,&yymsp[0].minor.yy298)); +} +#line 3472 "parse.c" + /* No destructor defined for DROP */ + /* No destructor defined for INDEX */ + break; + case 244: +#line 766 "parse.y" +{sqliteCopy(pParse,sqliteSrcListAppend(0,&yymsp[-6].minor.yy298,&yymsp[-5].minor.yy298),&yymsp[-3].minor.yy298,&yymsp[0].minor.yy0,yymsp[-7].minor.yy372);} +#line 3479 "parse.c" + /* No destructor defined for COPY */ + /* No destructor defined for FROM */ + /* No destructor defined for USING */ + /* No destructor defined for DELIMITERS */ + break; + case 245: +#line 768 "parse.y" +{sqliteCopy(pParse,sqliteSrcListAppend(0,&yymsp[-3].minor.yy298,&yymsp[-2].minor.yy298),&yymsp[0].minor.yy298,0,yymsp[-4].minor.yy372);} +#line 3488 "parse.c" + /* No destructor defined for COPY */ + /* No destructor defined for FROM */ + break; + case 246: +#line 772 "parse.y" +{sqliteVacuum(pParse,0);} +#line 3495 "parse.c" + /* No destructor defined for VACUUM */ + break; + case 247: +#line 773 "parse.y" +{sqliteVacuum(pParse,&yymsp[0].minor.yy298);} +#line 3501 "parse.c" + /* No destructor defined for VACUUM */ + break; + case 248: +#line 777 "parse.y" +{sqlitePragma(pParse,&yymsp[-2].minor.yy298,&yymsp[0].minor.yy298,0);} +#line 3507 "parse.c" + /* No destructor defined for PRAGMA */ + /* No destructor defined for EQ */ + break; + case 249: +#line 778 "parse.y" +{sqlitePragma(pParse,&yymsp[-2].minor.yy298,&yymsp[0].minor.yy0,0);} +#line 3514 "parse.c" + /* No destructor defined for PRAGMA */ + /* No destructor defined for EQ */ + break; + case 250: +#line 779 "parse.y" +{sqlitePragma(pParse,&yymsp[-2].minor.yy298,&yymsp[0].minor.yy298,0);} +#line 3521 "parse.c" + /* No destructor defined for PRAGMA */ + /* No destructor defined for EQ */ + break; + case 251: +#line 780 "parse.y" +{sqlitePragma(pParse,&yymsp[-2].minor.yy298,&yymsp[0].minor.yy298,1);} +#line 3528 "parse.c" + /* No destructor defined for PRAGMA */ + /* No destructor defined for EQ */ + break; + case 252: +#line 781 "parse.y" +{sqlitePragma(pParse,&yymsp[-3].minor.yy298,&yymsp[-1].minor.yy298,0);} +#line 3535 "parse.c" + /* No destructor defined for PRAGMA */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 253: +#line 782 "parse.y" +{sqlitePragma(pParse,&yymsp[0].minor.yy298,&yymsp[0].minor.yy298,0);} +#line 3543 "parse.c" + /* No destructor defined for PRAGMA */ + break; + case 254: +#line 783 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy298;} +#line 3549 "parse.c" + /* No destructor defined for plus_opt */ + break; + case 255: +#line 784 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy298;} +#line 3555 "parse.c" + /* No destructor defined for MINUS */ + break; + case 256: +#line 785 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 3561 "parse.c" + break; + case 257: +#line 786 "parse.y" +{yygotominor.yy298 = yymsp[0].minor.yy0;} +#line 3566 "parse.c" + break; + case 258: + /* No destructor defined for PLUS */ + break; + case 259: + break; + case 260: +#line 792 "parse.y" +{ + Token all; + all.z = yymsp[-4].minor.yy0.z; + all.n = (yymsp[0].minor.yy0.z - yymsp[-4].minor.yy0.z) + yymsp[0].minor.yy0.n; + sqliteFinishTrigger(pParse, yymsp[-1].minor.yy19, &all); +} +#line 3581 "parse.c" + /* No destructor defined for trigger_decl */ + /* No destructor defined for BEGIN */ + break; + case 261: +#line 800 "parse.y" +{ + SrcList *pTab = sqliteSrcListAppend(0, &yymsp[-3].minor.yy298, &yymsp[-2].minor.yy298); + sqliteBeginTrigger(pParse, &yymsp[-7].minor.yy298, yymsp[-6].minor.yy372, yymsp[-5].minor.yy290.a, yymsp[-5].minor.yy290.b, pTab, yymsp[-1].minor.yy372, yymsp[0].minor.yy182, yymsp[-9].minor.yy372); +} +#line 3591 "parse.c" + /* No destructor defined for TRIGGER */ + /* No destructor defined for ON */ + break; + case 262: +#line 806 "parse.y" +{ yygotominor.yy372 = TK_BEFORE; } +#line 3598 "parse.c" + /* No destructor defined for BEFORE */ + break; + case 263: +#line 807 "parse.y" +{ yygotominor.yy372 = TK_AFTER; } +#line 3604 "parse.c" + /* No destructor defined for AFTER */ + break; + case 264: +#line 808 "parse.y" +{ yygotominor.yy372 = TK_INSTEAD;} +#line 3610 "parse.c" + /* No destructor defined for INSTEAD */ + /* No destructor defined for OF */ + break; + case 265: +#line 809 "parse.y" +{ yygotominor.yy372 = TK_BEFORE; } +#line 3617 "parse.c" + break; + case 266: +#line 813 "parse.y" +{ yygotominor.yy290.a = TK_DELETE; yygotominor.yy290.b = 0; } +#line 3622 "parse.c" + /* No destructor defined for DELETE */ + break; + case 267: +#line 814 "parse.y" +{ yygotominor.yy290.a = TK_INSERT; yygotominor.yy290.b = 0; } +#line 3628 "parse.c" + /* No destructor defined for INSERT */ + break; + case 268: +#line 815 "parse.y" +{ yygotominor.yy290.a = TK_UPDATE; yygotominor.yy290.b = 0;} +#line 3634 "parse.c" + /* No destructor defined for UPDATE */ + break; + case 269: +#line 816 "parse.y" +{yygotominor.yy290.a = TK_UPDATE; yygotominor.yy290.b = yymsp[0].minor.yy320; } +#line 3640 "parse.c" + /* No destructor defined for UPDATE */ + /* No destructor defined for OF */ + break; + case 270: +#line 819 "parse.y" +{ yygotominor.yy372 = TK_ROW; } +#line 3647 "parse.c" + break; + case 271: +#line 820 "parse.y" +{ yygotominor.yy372 = TK_ROW; } +#line 3652 "parse.c" + /* No destructor defined for FOR */ + /* No destructor defined for EACH */ + /* No destructor defined for ROW */ + break; + case 272: +#line 821 "parse.y" +{ yygotominor.yy372 = TK_STATEMENT; } +#line 3660 "parse.c" + /* No destructor defined for FOR */ + /* No destructor defined for EACH */ + /* No destructor defined for STATEMENT */ + break; + case 273: +#line 824 "parse.y" +{ yygotominor.yy182 = 0; } +#line 3668 "parse.c" + break; + case 274: +#line 825 "parse.y" +{ yygotominor.yy182 = yymsp[0].minor.yy242; } +#line 3673 "parse.c" + /* No destructor defined for WHEN */ + break; + case 275: +#line 829 "parse.y" +{ + yymsp[-2].minor.yy19->pNext = yymsp[0].minor.yy19; + yygotominor.yy19 = yymsp[-2].minor.yy19; +} +#line 3682 "parse.c" + /* No destructor defined for SEMI */ + break; + case 276: +#line 833 "parse.y" +{ yygotominor.yy19 = 0; } +#line 3688 "parse.c" + break; + case 277: +#line 839 "parse.y" +{ yygotominor.yy19 = sqliteTriggerUpdateStep(&yymsp[-3].minor.yy298, yymsp[-1].minor.yy322, yymsp[0].minor.yy242, yymsp[-4].minor.yy372); } +#line 3693 "parse.c" + /* No destructor defined for UPDATE */ + /* No destructor defined for SET */ + break; + case 278: +#line 844 "parse.y" +{yygotominor.yy19 = sqliteTriggerInsertStep(&yymsp[-5].minor.yy298, yymsp[-4].minor.yy320, yymsp[-1].minor.yy322, 0, yymsp[-7].minor.yy372);} +#line 3700 "parse.c" + /* No destructor defined for INTO */ + /* No destructor defined for VALUES */ + /* No destructor defined for LP */ + /* No destructor defined for RP */ + break; + case 279: +#line 847 "parse.y" +{yygotominor.yy19 = sqliteTriggerInsertStep(&yymsp[-2].minor.yy298, yymsp[-1].minor.yy320, 0, yymsp[0].minor.yy179, yymsp[-4].minor.yy372);} +#line 3709 "parse.c" + /* No destructor defined for INTO */ + break; + case 280: +#line 851 "parse.y" +{yygotominor.yy19 = sqliteTriggerDeleteStep(&yymsp[-1].minor.yy298, yymsp[0].minor.yy242);} +#line 3715 "parse.c" + /* No destructor defined for DELETE */ + /* No destructor defined for FROM */ + break; + case 281: +#line 854 "parse.y" +{yygotominor.yy19 = sqliteTriggerSelectStep(yymsp[0].minor.yy179); } +#line 3722 "parse.c" + break; + case 282: +#line 857 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_RAISE, 0, 0, 0); + yygotominor.yy242->iColumn = OE_Ignore; + sqliteExprSpan(yygotominor.yy242, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0); +} +#line 3731 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for IGNORE */ + break; + case 283: +#line 862 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy298); + yygotominor.yy242->iColumn = OE_Rollback; + sqliteExprSpan(yygotominor.yy242, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0); +} +#line 3742 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for ROLLBACK */ + /* No destructor defined for COMMA */ + break; + case 284: +#line 867 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy298); + yygotominor.yy242->iColumn = OE_Abort; + sqliteExprSpan(yygotominor.yy242, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0); +} +#line 3754 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for ABORT */ + /* No destructor defined for COMMA */ + break; + case 285: +#line 872 "parse.y" +{ + yygotominor.yy242 = sqliteExpr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy298); + yygotominor.yy242->iColumn = OE_Fail; + sqliteExprSpan(yygotominor.yy242, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0); +} +#line 3766 "parse.c" + /* No destructor defined for LP */ + /* No destructor defined for FAIL */ + /* No destructor defined for COMMA */ + break; + case 286: +#line 879 "parse.y" +{ + sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&yymsp[-1].minor.yy298,&yymsp[0].minor.yy298)); +} +#line 3776 "parse.c" + /* No destructor defined for DROP */ + /* No destructor defined for TRIGGER */ + break; + case 287: +#line 884 "parse.y" +{ + sqliteAttach(pParse, &yymsp[-3].minor.yy298, &yymsp[-1].minor.yy298, &yymsp[0].minor.yy298); +} +#line 3785 "parse.c" + /* No destructor defined for ATTACH */ + /* No destructor defined for database_kw_opt */ + /* No destructor defined for AS */ + break; + case 288: +#line 888 "parse.y" +{ yygotominor.yy298 = yymsp[0].minor.yy298; } +#line 3793 "parse.c" + /* No destructor defined for USING */ + break; + case 289: +#line 889 "parse.y" +{ yygotominor.yy298.z = 0; yygotominor.yy298.n = 0; } +#line 3799 "parse.c" + break; + case 290: + /* No destructor defined for DATABASE */ + break; + case 291: + break; + case 292: +#line 895 "parse.y" +{ + sqliteDetach(pParse, &yymsp[0].minor.yy298); +} +#line 3811 "parse.c" + /* No destructor defined for DETACH */ + /* No destructor defined for database_kw_opt */ + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yypParser,yygoto); + if( yyact < YYNSTATE ){ + yy_shift(yypParser,yyact,yygoto,&yygotominor); + }else if( yyact == YYNSTATE + YYNRULE + 1 ){ + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + sqliteParserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ + sqliteParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + sqliteParserARG_FETCH; +#define TOKEN (yyminor.yy0) +#line 23 "parse.y" + + if( pParse->zErrMsg==0 ){ + if( TOKEN.z[0] ){ + sqliteErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN); + }else{ + sqliteErrorMsg(pParse, "incomplete SQL statement"); + } + } + +#line 3865 "parse.c" + sqliteParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + sqliteParserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + sqliteParserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "sqliteParserAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +**
        +**
      • A pointer to the parser (an opaque structure.) +**
      • The major token number. +**
      • The minor token number. +**
      • An option argument of a grammar-specified type. +**
      +** +** Outputs: +** None. +*/ +void sqliteParser( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + sqliteParserTOKENTYPE yyminor /* The value for the token */ + sqliteParserARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ + int yyerrorhit = 0; /* True if yymajor has invoked an error */ + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ + if( yymajor==0 ) return; + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + sqliteParserARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,yymajor); + if( yyactyyerrcnt--; + if( yyendofinput && yypParser->yyidx>=0 ){ + yymajor = 0; + }else{ + yymajor = YYNOCODE; + } + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else if( yyact == YY_ERROR_ACTION ){ + int yymx; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + }else{ + yy_accept(yypParser); + yymajor = YYNOCODE; + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/src/libs/sqlite2/parse.h b/src/libs/sqlite2/parse.h new file mode 100644 index 00000000..188a336c --- /dev/null +++ b/src/libs/sqlite2/parse.h @@ -0,0 +1,130 @@ +#define TK_END_OF_FILE 1 +#define TK_ILLEGAL 2 +#define TK_SPACE 3 +#define TK_UNCLOSED_STRING 4 +#define TK_COMMENT 5 +#define TK_FUNCTION 6 +#define TK_COLUMN 7 +#define TK_AGG_FUNCTION 8 +#define TK_SEMI 9 +#define TK_EXPLAIN 10 +#define TK_BEGIN 11 +#define TK_TRANSACTION 12 +#define TK_COMMIT 13 +#define TK_END 14 +#define TK_ROLLBACK 15 +#define TK_CREATE 16 +#define TK_TABLE 17 +#define TK_TEMP 18 +#define TK_LP 19 +#define TK_RP 20 +#define TK_AS 21 +#define TK_COMMA 22 +#define TK_ID 23 +#define TK_ABORT 24 +#define TK_AFTER 25 +#define TK_ASC 26 +#define TK_ATTACH 27 +#define TK_BEFORE 28 +#define TK_CASCADE 29 +#define TK_CLUSTER 30 +#define TK_CONFLICT 31 +#define TK_COPY 32 +#define TK_DATABASE 33 +#define TK_DEFERRED 34 +#define TK_DELIMITERS 35 +#define TK_DESC 36 +#define TK_DETACH 37 +#define TK_EACH 38 +#define TK_FAIL 39 +#define TK_FOR 40 +#define TK_GLOB 41 +#define TK_IGNORE 42 +#define TK_IMMEDIATE 43 +#define TK_INITIALLY 44 +#define TK_INSTEAD 45 +#define TK_LIKE 46 +#define TK_MATCH 47 +#define TK_KEY 48 +#define TK_OF 49 +#define TK_OFFSET 50 +#define TK_PRAGMA 51 +#define TK_RAISE 52 +#define TK_REPLACE 53 +#define TK_RESTRICT 54 +#define TK_ROW 55 +#define TK_STATEMENT 56 +#define TK_TRIGGER 57 +#define TK_VACUUM 58 +#define TK_VIEW 59 +#define TK_OR 60 +#define TK_AND 61 +#define TK_NOT 62 +#define TK_EQ 63 +#define TK_NE 64 +#define TK_ISNULL 65 +#define TK_NOTNULL 66 +#define TK_IS 67 +#define TK_BETWEEN 68 +#define TK_IN 69 +#define TK_GT 70 +#define TK_GE 71 +#define TK_LT 72 +#define TK_LE 73 +#define TK_BITAND 74 +#define TK_BITOR 75 +#define TK_LSHIFT 76 +#define TK_RSHIFT 77 +#define TK_PLUS 78 +#define TK_MINUS 79 +#define TK_STAR 80 +#define TK_SLASH 81 +#define TK_REM 82 +#define TK_CONCAT 83 +#define TK_UMINUS 84 +#define TK_UPLUS 85 +#define TK_BITNOT 86 +#define TK_STRING 87 +#define TK_JOIN_KW 88 +#define TK_INTEGER 89 +#define TK_CONSTRAINT 90 +#define TK_DEFAULT 91 +#define TK_FLOAT 92 +#define TK_NULL 93 +#define TK_PRIMARY 94 +#define TK_UNIQUE 95 +#define TK_CHECK 96 +#define TK_REFERENCES 97 +#define TK_COLLATE 98 +#define TK_ON 99 +#define TK_DELETE 100 +#define TK_UPDATE 101 +#define TK_INSERT 102 +#define TK_SET 103 +#define TK_DEFERRABLE 104 +#define TK_FOREIGN 105 +#define TK_DROP 106 +#define TK_UNION 107 +#define TK_ALL 108 +#define TK_INTERSECT 109 +#define TK_EXCEPT 110 +#define TK_SELECT 111 +#define TK_DISTINCT 112 +#define TK_DOT 113 +#define TK_FROM 114 +#define TK_JOIN 115 +#define TK_USING 116 +#define TK_ORDER 117 +#define TK_BY 118 +#define TK_GROUP 119 +#define TK_HAVING 120 +#define TK_LIMIT 121 +#define TK_WHERE 122 +#define TK_INTO 123 +#define TK_VALUES 124 +#define TK_VARIABLE 125 +#define TK_CASE 126 +#define TK_WHEN 127 +#define TK_THEN 128 +#define TK_ELSE 129 +#define TK_INDEX 130 diff --git a/src/libs/sqlite2/pragma.c b/src/libs/sqlite2/pragma.c new file mode 100644 index 00000000..7cb637fd --- /dev/null +++ b/src/libs/sqlite2/pragma.c @@ -0,0 +1,712 @@ +/* +** 2003 April 6 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code used to implement the PRAGMA command. +** +** $Id: pragma.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" +#include + +/* +** Interpret the given string as a boolean value. +*/ +static int getBoolean(const char *z){ + static char *azTrue[] = { "yes", "on", "true" }; + int i; + if( z[0]==0 ) return 0; + if( isdigit(z[0]) || (z[0]=='-' && isdigit(z[1])) ){ + return atoi(z); + } + for(i=0; i='0' && z[0]<='2' ){ + return z[0] - '0'; + }else if( sqliteStrICmp(z, "file")==0 ){ + return 1; + }else if( sqliteStrICmp(z, "memory")==0 ){ + return 2; + }else{ + return 0; + } +} + +/* +** If the TEMP database is open, close it and mark the database schema +** as needing reloading. This must be done when using the TEMP_STORE +** or DEFAULT_TEMP_STORE pragmas. +*/ +static int changeTempStorage(Parse *pParse, const char *zStorageType){ + int ts = getTempStore(zStorageType); + sqlite *db = pParse->db; + if( db->temp_store==ts ) return SQLITE_OK; + if( db->aDb[1].pBt!=0 ){ + if( db->flags & SQLITE_InTrans ){ + sqliteErrorMsg(pParse, "temporary storage cannot be changed " + "from within a transaction"); + return SQLITE_ERROR; + } + sqliteBtreeClose(db->aDb[1].pBt); + db->aDb[1].pBt = 0; + sqliteResetInternalSchema(db, 0); + } + db->temp_store = ts; + return SQLITE_OK; +} + +/* +** Check to see if zRight and zLeft refer to a pragma that queries +** or changes one of the flags in db->flags. Return 1 if so and 0 if not. +** Also, implement the pragma. +*/ +static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ + static const struct { + const char *zName; /* Name of the pragma */ + int mask; /* Mask for the db->flags value */ + } aPragma[] = { + { "vdbe_trace", SQLITE_VdbeTrace }, + { "full_column_names", SQLITE_FullColNames }, + { "short_column_names", SQLITE_ShortColNames }, + { "show_datatypes", SQLITE_ReportTypes }, + { "count_changes", SQLITE_CountRows }, + { "empty_result_callbacks", SQLITE_NullCallback }, + }; + int i; + for(i=0; idb; + Vdbe *v; + if( strcmp(zLeft,zRight)==0 && (v = sqliteGetVdbe(pParse))!=0 ){ + sqliteVdbeOp3(v, OP_ColumnName, 0, 1, aPragma[i].zName, P3_STATIC); + sqliteVdbeOp3(v, OP_ColumnName, 1, 0, "boolean", P3_STATIC); + sqliteVdbeCode(v, OP_Integer, (db->flags & aPragma[i].mask)!=0, 0, + OP_Callback, 1, 0, + 0); + }else if( getBoolean(zRight) ){ + db->flags |= aPragma[i].mask; + }else{ + db->flags &= ~aPragma[i].mask; + } + return 1; + } + } + return 0; +} + +/* +** Process a pragma statement. +** +** Pragmas are of this form: +** +** PRAGMA id = value +** +** The identifier might also be a string. The value is a string, and +** identifier, or a number. If minusFlag is true, then the value is +** a number that was preceded by a minus sign. +*/ +void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ + char *zLeft = 0; + char *zRight = 0; + sqlite *db = pParse->db; + Vdbe *v = sqliteGetVdbe(pParse); + if( v==0 ) return; + + zLeft = sqliteStrNDup(pLeft->z, pLeft->n); + sqliteDequote(zLeft); + if( minusFlag ){ + zRight = 0; + sqliteSetNString(&zRight, "-", 1, pRight->z, pRight->n, 0); + }else{ + zRight = sqliteStrNDup(pRight->z, pRight->n); + sqliteDequote(zRight); + } + if( sqliteAuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, 0) ){ + sqliteFree(zLeft); + sqliteFree(zRight); + return; + } + + /* + ** PRAGMA default_cache_size + ** PRAGMA default_cache_size=N + ** + ** The first form reports the current persistent setting for the + ** page cache size. The value returned is the maximum number of + ** pages in the page cache. The second form sets both the current + ** page cache size value and the persistent page cache size value + ** stored in the database file. + ** + ** The default cache size is stored in meta-value 2 of page 1 of the + ** database file. The cache size is actually the absolute value of + ** this memory location. The sign of meta-value 2 determines the + ** synchronous setting. A negative value means synchronous is off + ** and a positive value means synchronous is on. + */ + if( sqliteStrICmp(zLeft,"default_cache_size")==0 ){ + static VdbeOpList getCacheSize[] = { + { OP_ReadCookie, 0, 2, 0}, + { OP_AbsValue, 0, 0, 0}, + { OP_Dup, 0, 0, 0}, + { OP_Integer, 0, 0, 0}, + { OP_Ne, 0, 6, 0}, + { OP_Integer, 0, 0, 0}, /* 5 */ + { OP_ColumnName, 0, 1, "cache_size"}, + { OP_Callback, 1, 0, 0}, + }; + int addr; + if( pRight->z==pLeft->z ){ + addr = sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); + sqliteVdbeChangeP1(v, addr+5, MAX_PAGES); + }else{ + int size = atoi(zRight); + if( size<0 ) size = -size; + sqliteBeginWriteOperation(pParse, 0, 0); + sqliteVdbeAddOp(v, OP_Integer, size, 0); + sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); + addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); + sqliteVdbeAddOp(v, OP_Ge, 0, addr+3); + sqliteVdbeAddOp(v, OP_Negative, 0, 0); + sqliteVdbeAddOp(v, OP_SetCookie, 0, 2); + sqliteEndWriteOperation(pParse); + db->cache_size = db->cache_size<0 ? -size : size; + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + } + }else + + /* + ** PRAGMA cache_size + ** PRAGMA cache_size=N + ** + ** The first form reports the current local setting for the + ** page cache size. The local setting can be different from + ** the persistent cache size value that is stored in the database + ** file itself. The value returned is the maximum number of + ** pages in the page cache. The second form sets the local + ** page cache size value. It does not change the persistent + ** cache size stored on the disk so the cache size will revert + ** to its default value when the database is closed and reopened. + ** N should be a positive integer. + */ + if( sqliteStrICmp(zLeft,"cache_size")==0 ){ + static VdbeOpList getCacheSize[] = { + { OP_ColumnName, 0, 1, "cache_size"}, + { OP_Callback, 1, 0, 0}, + }; + if( pRight->z==pLeft->z ){ + int size = db->cache_size;; + if( size<0 ) size = -size; + sqliteVdbeAddOp(v, OP_Integer, size, 0); + sqliteVdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); + }else{ + int size = atoi(zRight); + if( size<0 ) size = -size; + if( db->cache_size<0 ) size = -size; + db->cache_size = size; + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + } + }else + + /* + ** PRAGMA default_synchronous + ** PRAGMA default_synchronous=ON|OFF|NORMAL|FULL + ** + ** The first form returns the persistent value of the "synchronous" setting + ** that is stored in the database. This is the synchronous setting that + ** is used whenever the database is opened unless overridden by a separate + ** "synchronous" pragma. The second form changes the persistent and the + ** local synchronous setting to the value given. + ** + ** If synchronous is OFF, SQLite does not attempt any fsync() systems calls + ** to make sure data is committed to disk. Write operations are very fast, + ** but a power failure can leave the database in an inconsistent state. + ** If synchronous is ON or NORMAL, SQLite will do an fsync() system call to + ** make sure data is being written to disk. The risk of corruption due to + ** a power loss in this mode is negligible but non-zero. If synchronous + ** is FULL, extra fsync()s occur to reduce the risk of corruption to near + ** zero, but with a write performance penalty. The default mode is NORMAL. + */ + if( sqliteStrICmp(zLeft,"default_synchronous")==0 ){ + static VdbeOpList getSync[] = { + { OP_ColumnName, 0, 1, "synchronous"}, + { OP_ReadCookie, 0, 3, 0}, + { OP_Dup, 0, 0, 0}, + { OP_If, 0, 0, 0}, /* 3 */ + { OP_ReadCookie, 0, 2, 0}, + { OP_Integer, 0, 0, 0}, + { OP_Lt, 0, 5, 0}, + { OP_AddImm, 1, 0, 0}, + { OP_Callback, 1, 0, 0}, + { OP_Halt, 0, 0, 0}, + { OP_AddImm, -1, 0, 0}, /* 10 */ + { OP_Callback, 1, 0, 0} + }; + if( pRight->z==pLeft->z ){ + int addr = sqliteVdbeAddOpList(v, ArraySize(getSync), getSync); + sqliteVdbeChangeP2(v, addr+3, addr+10); + }else{ + int addr; + int size = db->cache_size; + if( size<0 ) size = -size; + sqliteBeginWriteOperation(pParse, 0, 0); + sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); + sqliteVdbeAddOp(v, OP_Dup, 0, 0); + addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); + sqliteVdbeAddOp(v, OP_Ne, 0, addr+3); + sqliteVdbeAddOp(v, OP_AddImm, MAX_PAGES, 0); + sqliteVdbeAddOp(v, OP_AbsValue, 0, 0); + db->safety_level = getSafetyLevel(zRight)+1; + if( db->safety_level==1 ){ + sqliteVdbeAddOp(v, OP_Negative, 0, 0); + size = -size; + } + sqliteVdbeAddOp(v, OP_SetCookie, 0, 2); + sqliteVdbeAddOp(v, OP_Integer, db->safety_level, 0); + sqliteVdbeAddOp(v, OP_SetCookie, 0, 3); + sqliteEndWriteOperation(pParse); + db->cache_size = size; + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); + } + }else + + /* + ** PRAGMA synchronous + ** PRAGMA synchronous=OFF|ON|NORMAL|FULL + ** + ** Return or set the local value of the synchronous flag. Changing + ** the local value does not make changes to the disk file and the + ** default value will be restored the next time the database is + ** opened. + */ + if( sqliteStrICmp(zLeft,"synchronous")==0 ){ + static VdbeOpList getSync[] = { + { OP_ColumnName, 0, 1, "synchronous"}, + { OP_Callback, 1, 0, 0}, + }; + if( pRight->z==pLeft->z ){ + sqliteVdbeAddOp(v, OP_Integer, db->safety_level-1, 0); + sqliteVdbeAddOpList(v, ArraySize(getSync), getSync); + }else{ + int size = db->cache_size; + if( size<0 ) size = -size; + db->safety_level = getSafetyLevel(zRight)+1; + if( db->safety_level==1 ) size = -size; + db->cache_size = size; + sqliteBtreeSetCacheSize(db->aDb[0].pBt, db->cache_size); + sqliteBtreeSetSafetyLevel(db->aDb[0].pBt, db->safety_level); + } + }else + +#ifndef NDEBUG + if( sqliteStrICmp(zLeft, "trigger_overhead_test")==0 ){ + if( getBoolean(zRight) ){ + always_code_trigger_setup = 1; + }else{ + always_code_trigger_setup = 0; + } + }else +#endif + + if( flagPragma(pParse, zLeft, zRight) ){ + /* The flagPragma() call also generates any necessary code */ + }else + + if( sqliteStrICmp(zLeft, "table_info")==0 ){ + Table *pTab; + pTab = sqliteFindTable(db, zRight, 0); + if( pTab ){ + static VdbeOpList tableInfoPreface[] = { + { OP_ColumnName, 0, 0, "cid"}, + { OP_ColumnName, 1, 0, "name"}, + { OP_ColumnName, 2, 0, "type"}, + { OP_ColumnName, 3, 0, "notnull"}, + { OP_ColumnName, 4, 0, "dflt_value"}, + { OP_ColumnName, 5, 1, "pk"}, + }; + int i; + sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface); + sqliteViewGetColumnNames(pParse, pTab); + for(i=0; inCol; i++){ + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[i].zName, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, + pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", 0); + sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, + pTab->aCol[i].zDflt, P3_STATIC); + sqliteVdbeAddOp(v, OP_Integer, pTab->aCol[i].isPrimKey, 0); + sqliteVdbeAddOp(v, OP_Callback, 6, 0); + } + } + }else + + if( sqliteStrICmp(zLeft, "index_info")==0 ){ + Index *pIdx; + Table *pTab; + pIdx = sqliteFindIndex(db, zRight, 0); + if( pIdx ){ + static VdbeOpList tableInfoPreface[] = { + { OP_ColumnName, 0, 0, "seqno"}, + { OP_ColumnName, 1, 0, "cid"}, + { OP_ColumnName, 2, 1, "name"}, + }; + int i; + pTab = pIdx->pTable; + sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface); + for(i=0; inColumn; i++){ + int cnum = pIdx->aiColumn[i]; + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeAddOp(v, OP_Integer, cnum, 0); + assert( pTab->nCol>cnum ); + sqliteVdbeOp3(v, OP_String, 0, 0, pTab->aCol[cnum].zName, 0); + sqliteVdbeAddOp(v, OP_Callback, 3, 0); + } + } + }else + + if( sqliteStrICmp(zLeft, "index_list")==0 ){ + Index *pIdx; + Table *pTab; + pTab = sqliteFindTable(db, zRight, 0); + if( pTab ){ + v = sqliteGetVdbe(pParse); + pIdx = pTab->pIndex; + } + if( pTab && pIdx ){ + int i = 0; + static VdbeOpList indexListPreface[] = { + { OP_ColumnName, 0, 0, "seq"}, + { OP_ColumnName, 1, 0, "name"}, + { OP_ColumnName, 2, 1, "unique"}, + }; + + sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); + while(pIdx){ + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, pIdx->zName, 0); + sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0); + sqliteVdbeAddOp(v, OP_Callback, 3, 0); + ++i; + pIdx = pIdx->pNext; + } + } + }else + + if( sqliteStrICmp(zLeft, "foreign_key_list")==0 ){ + FKey *pFK; + Table *pTab; + pTab = sqliteFindTable(db, zRight, 0); + if( pTab ){ + v = sqliteGetVdbe(pParse); + pFK = pTab->pFKey; + } + if( pTab && pFK ){ + int i = 0; + static VdbeOpList indexListPreface[] = { + { OP_ColumnName, 0, 0, "id"}, + { OP_ColumnName, 1, 0, "seq"}, + { OP_ColumnName, 2, 0, "table"}, + { OP_ColumnName, 3, 0, "from"}, + { OP_ColumnName, 4, 1, "to"}, + }; + + sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); + while(pFK){ + int j; + for(j=0; jnCol; j++){ + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeAddOp(v, OP_Integer, j, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, pFK->zTo, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, + pTab->aCol[pFK->aCol[j].iFrom].zName, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, pFK->aCol[j].zCol, 0); + sqliteVdbeAddOp(v, OP_Callback, 5, 0); + } + ++i; + pFK = pFK->pNextFrom; + } + } + }else + + if( sqliteStrICmp(zLeft, "database_list")==0 ){ + int i; + static VdbeOpList indexListPreface[] = { + { OP_ColumnName, 0, 0, "seq"}, + { OP_ColumnName, 1, 0, "name"}, + { OP_ColumnName, 2, 1, "file"}, + }; + + sqliteVdbeAddOpList(v, ArraySize(indexListPreface), indexListPreface); + for(i=0; inDb; i++){ + if( db->aDb[i].pBt==0 ) continue; + assert( db->aDb[i].zName!=0 ); + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, db->aDb[i].zName, 0); + sqliteVdbeOp3(v, OP_String, 0, 0, + sqliteBtreeGetFilename(db->aDb[i].pBt), 0); + sqliteVdbeAddOp(v, OP_Callback, 3, 0); + } + }else + + + /* + ** PRAGMA temp_store + ** PRAGMA temp_store = "default"|"memory"|"file" + ** + ** Return or set the local value of the temp_store flag. Changing + ** the local value does not make changes to the disk file and the default + ** value will be restored the next time the database is opened. + ** + ** Note that it is possible for the library compile-time options to + ** override this setting + */ + if( sqliteStrICmp(zLeft, "temp_store")==0 ){ + static VdbeOpList getTmpDbLoc[] = { + { OP_ColumnName, 0, 1, "temp_store"}, + { OP_Callback, 1, 0, 0}, + }; + if( pRight->z==pLeft->z ){ + sqliteVdbeAddOp(v, OP_Integer, db->temp_store, 0); + sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc); + }else{ + changeTempStorage(pParse, zRight); + } + }else + + /* + ** PRAGMA default_temp_store + ** PRAGMA default_temp_store = "default"|"memory"|"file" + ** + ** Return or set the value of the persistent temp_store flag. Any + ** change does not take effect until the next time the database is + ** opened. + ** + ** Note that it is possible for the library compile-time options to + ** override this setting + */ + if( sqliteStrICmp(zLeft, "default_temp_store")==0 ){ + static VdbeOpList getTmpDbLoc[] = { + { OP_ColumnName, 0, 1, "temp_store"}, + { OP_ReadCookie, 0, 5, 0}, + { OP_Callback, 1, 0, 0}}; + if( pRight->z==pLeft->z ){ + sqliteVdbeAddOpList(v, ArraySize(getTmpDbLoc), getTmpDbLoc); + }else{ + sqliteBeginWriteOperation(pParse, 0, 0); + sqliteVdbeAddOp(v, OP_Integer, getTempStore(zRight), 0); + sqliteVdbeAddOp(v, OP_SetCookie, 0, 5); + sqliteEndWriteOperation(pParse); + } + }else + +#ifndef NDEBUG + if( sqliteStrICmp(zLeft, "parser_trace")==0 ){ + extern void sqliteParserTrace(FILE*, char *); + if( getBoolean(zRight) ){ + sqliteParserTrace(stdout, "parser: "); + }else{ + sqliteParserTrace(0, 0); + } + }else +#endif + + if( sqliteStrICmp(zLeft, "integrity_check")==0 ){ + int i, j, addr; + + /* Code that initializes the integrity check program. Set the + ** error count 0 + */ + static VdbeOpList initCode[] = { + { OP_Integer, 0, 0, 0}, + { OP_MemStore, 0, 1, 0}, + { OP_ColumnName, 0, 1, "integrity_check"}, + }; + + /* Code to do an BTree integrity check on a single database file. + */ + static VdbeOpList checkDb[] = { + { OP_SetInsert, 0, 0, "2"}, + { OP_Integer, 0, 0, 0}, /* 1 */ + { OP_OpenRead, 0, 2, 0}, + { OP_Rewind, 0, 7, 0}, /* 3 */ + { OP_Column, 0, 3, 0}, /* 4 */ + { OP_SetInsert, 0, 0, 0}, + { OP_Next, 0, 4, 0}, /* 6 */ + { OP_IntegrityCk, 0, 0, 0}, /* 7 */ + { OP_Dup, 0, 1, 0}, + { OP_String, 0, 0, "ok"}, + { OP_StrEq, 0, 12, 0}, /* 10 */ + { OP_MemIncr, 0, 0, 0}, + { OP_String, 0, 0, "*** in database "}, + { OP_String, 0, 0, 0}, /* 13 */ + { OP_String, 0, 0, " ***\n"}, + { OP_Pull, 3, 0, 0}, + { OP_Concat, 4, 1, 0}, + { OP_Callback, 1, 0, 0}, + }; + + /* Code that appears at the end of the integrity check. If no error + ** messages have been generated, output OK. Otherwise output the + ** error message + */ + static VdbeOpList endCode[] = { + { OP_MemLoad, 0, 0, 0}, + { OP_Integer, 0, 0, 0}, + { OP_Ne, 0, 0, 0}, /* 2 */ + { OP_String, 0, 0, "ok"}, + { OP_Callback, 1, 0, 0}, + }; + + /* Initialize the VDBE program */ + sqliteVdbeAddOpList(v, ArraySize(initCode), initCode); + + /* Do an integrity check on each database file */ + for(i=0; inDb; i++){ + HashElem *x; + + /* Do an integrity check of the B-Tree + */ + addr = sqliteVdbeAddOpList(v, ArraySize(checkDb), checkDb); + sqliteVdbeChangeP1(v, addr+1, i); + sqliteVdbeChangeP2(v, addr+3, addr+7); + sqliteVdbeChangeP2(v, addr+6, addr+4); + sqliteVdbeChangeP2(v, addr+7, i); + sqliteVdbeChangeP2(v, addr+10, addr+ArraySize(checkDb)); + sqliteVdbeChangeP3(v, addr+13, db->aDb[i].zName, P3_STATIC); + + /* Make sure all the indices are constructed correctly. + */ + sqliteCodeVerifySchema(pParse, i); + for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){ + Table *pTab = sqliteHashData(x); + Index *pIdx; + int loopTop; + + if( pTab->pIndex==0 ) continue; + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeOp3(v, OP_OpenRead, 1, pTab->tnum, pTab->zName, 0); + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ + if( pIdx->tnum==0 ) continue; + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); + sqliteVdbeOp3(v, OP_OpenRead, j+2, pIdx->tnum, pIdx->zName, 0); + } + sqliteVdbeAddOp(v, OP_Integer, 0, 0); + sqliteVdbeAddOp(v, OP_MemStore, 1, 1); + loopTop = sqliteVdbeAddOp(v, OP_Rewind, 1, 0); + sqliteVdbeAddOp(v, OP_MemIncr, 1, 0); + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ + int k, jmp2; + static VdbeOpList idxErr[] = { + { OP_MemIncr, 0, 0, 0}, + { OP_String, 0, 0, "rowid "}, + { OP_Recno, 1, 0, 0}, + { OP_String, 0, 0, " missing from index "}, + { OP_String, 0, 0, 0}, /* 4 */ + { OP_Concat, 4, 0, 0}, + { OP_Callback, 1, 0, 0}, + }; + sqliteVdbeAddOp(v, OP_Recno, 1, 0); + for(k=0; knColumn; k++){ + int idx = pIdx->aiColumn[k]; + if( idx==pTab->iPKey ){ + sqliteVdbeAddOp(v, OP_Recno, 1, 0); + }else{ + sqliteVdbeAddOp(v, OP_Column, 1, idx); + } + } + sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); + if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); + jmp2 = sqliteVdbeAddOp(v, OP_Found, j+2, 0); + addr = sqliteVdbeAddOpList(v, ArraySize(idxErr), idxErr); + sqliteVdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC); + sqliteVdbeChangeP2(v, jmp2, sqliteVdbeCurrentAddr(v)); + } + sqliteVdbeAddOp(v, OP_Next, 1, loopTop+1); + sqliteVdbeChangeP2(v, loopTop, sqliteVdbeCurrentAddr(v)); + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ + static VdbeOpList cntIdx[] = { + { OP_Integer, 0, 0, 0}, + { OP_MemStore, 2, 1, 0}, + { OP_Rewind, 0, 0, 0}, /* 2 */ + { OP_MemIncr, 2, 0, 0}, + { OP_Next, 0, 0, 0}, /* 4 */ + { OP_MemLoad, 1, 0, 0}, + { OP_MemLoad, 2, 0, 0}, + { OP_Eq, 0, 0, 0}, /* 7 */ + { OP_MemIncr, 0, 0, 0}, + { OP_String, 0, 0, "wrong # of entries in index "}, + { OP_String, 0, 0, 0}, /* 10 */ + { OP_Concat, 2, 0, 0}, + { OP_Callback, 1, 0, 0}, + }; + if( pIdx->tnum==0 ) continue; + addr = sqliteVdbeAddOpList(v, ArraySize(cntIdx), cntIdx); + sqliteVdbeChangeP1(v, addr+2, j+2); + sqliteVdbeChangeP2(v, addr+2, addr+5); + sqliteVdbeChangeP1(v, addr+4, j+2); + sqliteVdbeChangeP2(v, addr+4, addr+3); + sqliteVdbeChangeP2(v, addr+7, addr+ArraySize(cntIdx)); + sqliteVdbeChangeP3(v, addr+10, pIdx->zName, P3_STATIC); + } + } + } + addr = sqliteVdbeAddOpList(v, ArraySize(endCode), endCode); + sqliteVdbeChangeP2(v, addr+2, addr+ArraySize(endCode)); + }else + + {} + sqliteFree(zLeft); + sqliteFree(zRight); +} diff --git a/src/libs/sqlite2/printf.c b/src/libs/sqlite2/printf.c new file mode 100644 index 00000000..a5445f60 --- /dev/null +++ b/src/libs/sqlite2/printf.c @@ -0,0 +1,858 @@ +/* +** The "printf" code that follows dates from the 1980's. It is in +** the public domain. The original comments are included here for +** completeness. They are very out-of-date but might be useful as +** an historical reference. Most of the "enhancements" have been backed +** out so that the functionality is now the same as standard printf(). +** +************************************************************************** +** +** The following modules is an enhanced replacement for the "printf" subroutines +** found in the standard C library. The following enhancements are +** supported: +** +** + Additional functions. The standard set of "printf" functions +** includes printf, fprintf, sprintf, vprintf, vfprintf, and +** vsprintf. This module adds the following: +** +** * snprintf -- Works like sprintf, but has an extra argument +** which is the size of the buffer written to. +** +** * mprintf -- Similar to sprintf. Writes output to memory +** obtained from malloc. +** +** * xprintf -- Calls a function to dispose of output. +** +** * nprintf -- No output, but returns the number of characters +** that would have been output by printf. +** +** * A v- version (ex: vsnprintf) of every function is also +** supplied. +** +** + A few extensions to the formatting notation are supported: +** +** * The "=" flag (similar to "-") causes the output to be +** be centered in the appropriately sized field. +** +** * The %b field outputs an integer in binary notation. +** +** * The %c field now accepts a precision. The character output +** is repeated by the number of times the precision specifies. +** +** * The %' field works like %c, but takes as its character the +** next character of the format string, instead of the next +** argument. For example, printf("%.78'-") prints 78 minus +** signs, the same as printf("%.78c",'-'). +** +** + When compiled using GCC on a SPARC, this version of printf is +** faster than the library printf for SUN OS 4.1. +** +** + All functions are fully reentrant. +** +*/ +#include "sqliteInt.h" + +/* +** Conversion types fall into various categories as defined by the +** following enumeration. +*/ +#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ +#define etFLOAT 2 /* Floating point. %f */ +#define etEXP 3 /* Exponentional notation. %e and %E */ +#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ +#define etSIZE 5 /* Return number of characters processed so far. %n */ +#define etSTRING 6 /* Strings. %s */ +#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ +#define etPERCENT 8 /* Percent symbol. %% */ +#define etCHARX 9 /* Characters. %c */ +#define etERROR 10 /* Used to indicate no such conversion type */ +/* The rest are extensions, not normally found in printf() */ +#define etCHARLIT 11 /* Literal characters. %' */ +#define etSQLESCAPE 12 /* Strings with '\'' doubled. %q */ +#define etSQLESCAPE2 13 /* Strings with '\'' doubled and enclosed in '', + NULL pointers replaced by SQL NULL. %Q */ +#define etTOKEN 14 /* a pointer to a Token structure */ +#define etSRCLIST 15 /* a pointer to a SrcList */ + + +/* +** An "etByte" is an 8-bit unsigned value. +*/ +typedef unsigned char etByte; + +/* +** Each builtin conversion character (ex: the 'd' in "%d") is described +** by an instance of the following structure +*/ +typedef struct et_info { /* Information about each format field */ + char fmttype; /* The format field code letter */ + etByte base; /* The base for radix conversion */ + etByte flags; /* One or more of FLAG_ constants below */ + etByte type; /* Conversion paradigm */ + char *charset; /* The character set for conversion */ + char *prefix; /* Prefix on non-zero values in alt format */ +} et_info; + +/* +** Allowed values for et_info.flags +*/ +#define FLAG_SIGNED 1 /* True if the value to convert is signed */ +#define FLAG_INTERN 2 /* True if for internal use only */ + + +/* +** The following table is searched linearly, so it is good to put the +** most frequently used conversion types first. +*/ +static et_info fmtinfo[] = { + { 'd', 10, 1, etRADIX, "0123456789", 0 }, + { 's', 0, 0, etSTRING, 0, 0 }, + { 'z', 0, 2, etDYNSTRING, 0, 0 }, + { 'q', 0, 0, etSQLESCAPE, 0, 0 }, + { 'Q', 0, 0, etSQLESCAPE2, 0, 0 }, + { 'c', 0, 0, etCHARX, 0, 0 }, + { 'o', 8, 0, etRADIX, "01234567", "0" }, + { 'u', 10, 0, etRADIX, "0123456789", 0 }, + { 'x', 16, 0, etRADIX, "0123456789abcdef", "x0" }, + { 'X', 16, 0, etRADIX, "0123456789ABCDEF", "X0" }, + { 'f', 0, 1, etFLOAT, 0, 0 }, + { 'e', 0, 1, etEXP, "e", 0 }, + { 'E', 0, 1, etEXP, "E", 0 }, + { 'g', 0, 1, etGENERIC, "e", 0 }, + { 'G', 0, 1, etGENERIC, "E", 0 }, + { 'i', 10, 1, etRADIX, "0123456789", 0 }, + { 'n', 0, 0, etSIZE, 0, 0 }, + { '%', 0, 0, etPERCENT, 0, 0 }, + { 'p', 10, 0, etRADIX, "0123456789", 0 }, + { 'T', 0, 2, etTOKEN, 0, 0 }, + { 'S', 0, 2, etSRCLIST, 0, 0 }, +}; +#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0])) + +/* +** If NOFLOATINGPOINT is defined, then none of the floating point +** conversions will work. +*/ +#ifndef etNOFLOATINGPOINT +/* +** "*val" is a double such that 0.1 <= *val < 10.0 +** Return the ascii code for the leading digit of *val, then +** multiply "*val" by 10.0 to renormalize. +** +** Example: +** input: *val = 3.14159 +** output: *val = 1.4159 function return = '3' +** +** The counter *cnt is incremented each time. After counter exceeds +** 16 (the number of significant digits in a 64-bit float) '0' is +** always returned. +*/ +static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ + int digit; + LONGDOUBLE_TYPE d; + if( (*cnt)++ >= 16 ) return '0'; + digit = (int)*val; + d = digit; + digit += '0'; + *val = (*val - d)*10.0; + return digit; +} +#endif + +#define etBUFSIZE 1000 /* Size of the output buffer */ + +/* +** The root program. All variations call this core. +** +** INPUTS: +** func This is a pointer to a function taking three arguments +** 1. A pointer to anything. Same as the "arg" parameter. +** 2. A pointer to the list of characters to be output +** (Note, this list is NOT null terminated.) +** 3. An integer number of characters to be output. +** (Note: This number might be zero.) +** +** arg This is the pointer to anything which will be passed as the +** first argument to "func". Use it for whatever you like. +** +** fmt This is the format string, as in the usual print. +** +** ap This is a pointer to a list of arguments. Same as in +** vfprint. +** +** OUTPUTS: +** The return value is the total number of characters sent to +** the function "func". Returns -1 on a error. +** +** Note that the order in which automatic variables are declared below +** seems to make a big difference in determining how fast this beast +** will run. +*/ +static int vxprintf( + void (*func)(void*,const char*,int), /* Consumer of text */ + void *arg, /* First argument to the consumer */ + int useExtended, /* Allow extended %-conversions */ + const char *fmt, /* Format string */ + va_list ap /* arguments */ +){ + int c; /* Next character in the format string */ + char *bufpt; /* Pointer to the conversion buffer */ + int precision; /* Precision of the current field */ + int length; /* Length of the field */ + int idx; /* A general purpose loop counter */ + int count; /* Total number of characters output */ + int width; /* Width of the current field */ + etByte flag_leftjustify; /* True if "-" flag is present */ + etByte flag_plussign; /* True if "+" flag is present */ + etByte flag_blanksign; /* True if " " flag is present */ + etByte flag_alternateform; /* True if "#" flag is present */ + etByte flag_zeropad; /* True if field width constant starts with zero */ + etByte flag_long; /* True if "l" flag is present */ + unsigned long longvalue; /* Value for integer types */ + LONGDOUBLE_TYPE realvalue; /* Value for real types */ + et_info *infop; /* Pointer to the appropriate info structure */ + char buf[etBUFSIZE]; /* Conversion buffer */ + char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ + etByte errorflag = 0; /* True if an error is encountered */ + etByte xtype; /* Conversion paradigm */ + char *zExtra; /* Extra memory used for etTCLESCAPE conversions */ + static char spaces[] = " "; +#define etSPACESIZE (sizeof(spaces)-1) +#ifndef etNOFLOATINGPOINT + int exp; /* exponent of real numbers */ + double rounder; /* Used for rounding floating point values */ + etByte flag_dp; /* True if decimal point should be shown */ + etByte flag_rtz; /* True if trailing zeros should be removed */ + etByte flag_exp; /* True to force display of the exponent */ + int nsd; /* Number of significant digits returned */ +#endif + + func(arg,"",0); + count = length = 0; + bufpt = 0; + for(; (c=(*fmt))!=0; ++fmt){ + if( c!='%' ){ + int amt; + bufpt = (char *)fmt; + amt = 1; + while( (c=(*++fmt))!='%' && c!=0 ) amt++; + (*func)(arg,bufpt,amt); + count += amt; + if( c==0 ) break; + } + if( (c=(*++fmt))==0 ){ + errorflag = 1; + (*func)(arg,"%",1); + count++; + break; + } + /* Find out what flags are present */ + flag_leftjustify = flag_plussign = flag_blanksign = + flag_alternateform = flag_zeropad = 0; + do{ + switch( c ){ + case '-': flag_leftjustify = 1; c = 0; break; + case '+': flag_plussign = 1; c = 0; break; + case ' ': flag_blanksign = 1; c = 0; break; + case '#': flag_alternateform = 1; c = 0; break; + case '0': flag_zeropad = 1; c = 0; break; + default: break; + } + }while( c==0 && (c=(*++fmt))!=0 ); + /* Get the field width */ + width = 0; + if( c=='*' ){ + width = va_arg(ap,int); + if( width<0 ){ + flag_leftjustify = 1; + width = -width; + } + c = *++fmt; + }else{ + while( c>='0' && c<='9' ){ + width = width*10 + c - '0'; + c = *++fmt; + } + } + if( width > etBUFSIZE-10 ){ + width = etBUFSIZE-10; + } + /* Get the precision */ + if( c=='.' ){ + precision = 0; + c = *++fmt; + if( c=='*' ){ + precision = va_arg(ap,int); + if( precision<0 ) precision = -precision; + c = *++fmt; + }else{ + while( c>='0' && c<='9' ){ + precision = precision*10 + c - '0'; + c = *++fmt; + } + } + /* Limit the precision to prevent overflowing buf[] during conversion */ + if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40; + }else{ + precision = -1; + } + /* Get the conversion type modifier */ + if( c=='l' ){ + flag_long = 1; + c = *++fmt; + }else{ + flag_long = 0; + } + /* Fetch the info entry for the field */ + infop = 0; + xtype = etERROR; + for(idx=0; idxflags & FLAG_INTERN)==0 ){ + xtype = infop->type; + } + break; + } + } + zExtra = 0; + + /* + ** At this point, variables are initialized as follows: + ** + ** flag_alternateform TRUE if a '#' is present. + ** flag_plussign TRUE if a '+' is present. + ** flag_leftjustify TRUE if a '-' is present or if the + ** field width was negative. + ** flag_zeropad TRUE if the width began with 0. + ** flag_long TRUE if the letter 'l' (ell) prefixed + ** the conversion character. + ** flag_blanksign TRUE if a ' ' is present. + ** width The specified field width. This is + ** always non-negative. Zero is the default. + ** precision The specified precision. The default + ** is -1. + ** xtype The class of the conversion. + ** infop Pointer to the appropriate info struct. + */ + switch( xtype ){ + case etRADIX: + if( flag_long ) longvalue = va_arg(ap,long); + else longvalue = va_arg(ap,int); +#if 1 + /* For the format %#x, the value zero is printed "0" not "0x0". + ** I think this is stupid. */ + if( longvalue==0 ) flag_alternateform = 0; +#else + /* More sensible: turn off the prefix for octal (to prevent "00"), + ** but leave the prefix for hex. */ + if( longvalue==0 && infop->base==8 ) flag_alternateform = 0; +#endif + if( infop->flags & FLAG_SIGNED ){ + if( *(long*)&longvalue<0 ){ + longvalue = -*(long*)&longvalue; + prefix = '-'; + }else if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + }else prefix = 0; + if( flag_zeropad && precisioncharset; + base = infop->base; + do{ /* Convert to ascii */ + *(--bufpt) = cset[longvalue%base]; + longvalue = longvalue/base; + }while( longvalue>0 ); + } + length = &buf[etBUFSIZE-1]-bufpt; + for(idx=precision-length; idx>0; idx--){ + *(--bufpt) = '0'; /* Zero pad */ + } + if( prefix ) *(--bufpt) = prefix; /* Add sign */ + if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ + char *pre, x; + pre = infop->prefix; + if( *bufpt!=pre[0] ){ + for(pre=infop->prefix; (x=(*pre))!=0; pre++) *(--bufpt) = x; + } + } + length = &buf[etBUFSIZE-1]-bufpt; + break; + case etFLOAT: + case etEXP: + case etGENERIC: + realvalue = va_arg(ap,double); +#ifndef etNOFLOATINGPOINT + if( precision<0 ) precision = 6; /* Set default precision */ + if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10; + if( realvalue<0.0 ){ + realvalue = -realvalue; + prefix = '-'; + }else{ + if( flag_plussign ) prefix = '+'; + else if( flag_blanksign ) prefix = ' '; + else prefix = 0; + } + if( infop->type==etGENERIC && precision>0 ) precision--; + rounder = 0.0; +#if 0 + /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */ + for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1); +#else + /* It makes more sense to use 0.5 */ + for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1); +#endif + if( infop->type==etFLOAT ) realvalue += rounder; + /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ + exp = 0; + if( realvalue>0.0 ){ + while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; } + while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; } + while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; } + while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; } + if( exp>350 || exp<-350 ){ + bufpt = "NaN"; + length = 3; + break; + } + } + bufpt = buf; + /* + ** If the field type is etGENERIC, then convert to either etEXP + ** or etFLOAT, as appropriate. + */ + flag_exp = xtype==etEXP; + if( xtype!=etFLOAT ){ + realvalue += rounder; + if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } + } + if( xtype==etGENERIC ){ + flag_rtz = !flag_alternateform; + if( exp<-4 || exp>precision ){ + xtype = etEXP; + }else{ + precision = precision - exp; + xtype = etFLOAT; + } + }else{ + flag_rtz = 0; + } + /* + ** The "exp+precision" test causes output to be of type etEXP if + ** the precision is too large to fit in buf[]. + */ + nsd = 0; + if( xtype==etFLOAT && exp+precision0 || flag_alternateform); + if( prefix ) *(bufpt++) = prefix; /* Sign */ + if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */ + else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd); + if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */ + for(exp++; exp<0 && precision>0; precision--, exp++){ + *(bufpt++) = '0'; + } + while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd); + *(bufpt--) = 0; /* Null terminate */ + if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */ + while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; + if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; + } + bufpt++; /* point to next free slot */ + }else{ /* etEXP or etGENERIC */ + flag_dp = (precision>0 || flag_alternateform); + if( prefix ) *(bufpt++) = prefix; /* Sign */ + *(bufpt++) = et_getdigit(&realvalue,&nsd); /* First digit */ + if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */ + while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd); + bufpt--; /* point to last digit */ + if( flag_rtz && flag_dp ){ /* Remove tail zeros */ + while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0; + if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0; + } + bufpt++; /* point to next free slot */ + if( exp || flag_exp ){ + *(bufpt++) = infop->charset[0]; + if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */ + else { *(bufpt++) = '+'; } + if( exp>=100 ){ + *(bufpt++) = (exp/100)+'0'; /* 100's digit */ + exp %= 100; + } + *(bufpt++) = exp/10+'0'; /* 10's digit */ + *(bufpt++) = exp%10+'0'; /* 1's digit */ + } + } + /* The converted number is in buf[] and zero terminated. Output it. + ** Note that the number is in the usual order, not reversed as with + ** integer conversions. */ + length = bufpt-buf; + bufpt = buf; + + /* Special case: Add leading zeros if the flag_zeropad flag is + ** set and we are not left justified */ + if( flag_zeropad && !flag_leftjustify && length < width){ + int i; + int nPad = width - length; + for(i=width; i>=nPad; i--){ + bufpt[i] = bufpt[i-nPad]; + } + i = prefix!=0; + while( nPad-- ) bufpt[i++] = '0'; + length = width; + } +#endif + break; + case etSIZE: + *(va_arg(ap,int*)) = count; + length = width = 0; + break; + case etPERCENT: + buf[0] = '%'; + bufpt = buf; + length = 1; + break; + case etCHARLIT: + case etCHARX: + c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); + if( precision>=0 ){ + for(idx=1; idx=0 && precisionetBUFSIZE ){ + bufpt = zExtra = sqliteMalloc( n ); + if( bufpt==0 ) return -1; + }else{ + bufpt = buf; + } + j = 0; + if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\''; + for(i=0; (c=arg[i])!=0; i++){ + bufpt[j++] = c; + if( c=='\'' ) bufpt[j++] = c; + } + if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\''; + bufpt[j] = 0; + length = j; + if( precision>=0 && precisionz, pToken->n); + length = width = 0; + break; + } + case etSRCLIST: { + SrcList *pSrc = va_arg(ap, SrcList*); + int k = va_arg(ap, int); + struct SrcList_item *pItem = &pSrc->a[k]; + assert( k>=0 && knSrc ); + if( pItem->zDatabase && pItem->zDatabase[0] ){ + (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase)); + (*func)(arg, ".", 1); + } + (*func)(arg, pItem->zName, strlen(pItem->zName)); + length = width = 0; + break; + } + case etERROR: + buf[0] = '%'; + buf[1] = c; + errorflag = 0; + idx = 1+(c!=0); + (*func)(arg,"%",idx); + count += idx; + if( c==0 ) fmt--; + break; + }/* End switch over the format type */ + /* + ** The text of the conversion is pointed to by "bufpt" and is + ** "length" characters long. The field width is "width". Do + ** the output. + */ + if( !flag_leftjustify ){ + int nspace; + nspace = width-length; + if( nspace>0 ){ + count += nspace; + while( nspace>=etSPACESIZE ){ + (*func)(arg,spaces,etSPACESIZE); + nspace -= etSPACESIZE; + } + if( nspace>0 ) (*func)(arg,spaces,nspace); + } + } + if( length>0 ){ + (*func)(arg,bufpt,length); + count += length; + } + if( flag_leftjustify ){ + int nspace; + nspace = width-length; + if( nspace>0 ){ + count += nspace; + while( nspace>=etSPACESIZE ){ + (*func)(arg,spaces,etSPACESIZE); + nspace -= etSPACESIZE; + } + if( nspace>0 ) (*func)(arg,spaces,nspace); + } + } + if( zExtra ){ + sqliteFree(zExtra); + } + }/* End for loop over the format string */ + return errorflag ? -1 : count; +} /* End of function */ + + +/* This structure is used to store state information about the +** write to memory that is currently in progress. +*/ +struct sgMprintf { + char *zBase; /* A base allocation */ + char *zText; /* The string collected so far */ + int nChar; /* Length of the string so far */ + int nTotal; /* Output size if unconstrained */ + int nAlloc; /* Amount of space allocated in zText */ + void *(*xRealloc)(void*,int); /* Function used to realloc memory */ +}; + +/* +** This function implements the callback from vxprintf. +** +** This routine add nNewChar characters of text in zNewText to +** the sgMprintf structure pointed to by "arg". +*/ +static void mout(void *arg, const char *zNewText, int nNewChar){ + struct sgMprintf *pM = (struct sgMprintf*)arg; + pM->nTotal += nNewChar; + if( pM->nChar + nNewChar + 1 > pM->nAlloc ){ + if( pM->xRealloc==0 ){ + nNewChar = pM->nAlloc - pM->nChar - 1; + }else{ + pM->nAlloc = pM->nChar + nNewChar*2 + 1; + if( pM->zText==pM->zBase ){ + pM->zText = pM->xRealloc(0, pM->nAlloc); + if( pM->zText && pM->nChar ){ + memcpy(pM->zText, pM->zBase, pM->nChar); + } + }else{ + pM->zText = pM->xRealloc(pM->zText, pM->nAlloc); + } + } + } + if( pM->zText ){ + if( nNewChar>0 ){ + memcpy(&pM->zText[pM->nChar], zNewText, nNewChar); + pM->nChar += nNewChar; + } + pM->zText[pM->nChar] = 0; + } +} + +/* +** This routine is a wrapper around xprintf() that invokes mout() as +** the consumer. +*/ +static char *base_vprintf( + void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */ + int useInternal, /* Use internal %-conversions if true */ + char *zInitBuf, /* Initially write here, before mallocing */ + int nInitBuf, /* Size of zInitBuf[] */ + const char *zFormat, /* format string */ + va_list ap /* arguments */ +){ + struct sgMprintf sM; + sM.zBase = sM.zText = zInitBuf; + sM.nChar = sM.nTotal = 0; + sM.nAlloc = nInitBuf; + sM.xRealloc = xRealloc; + vxprintf(mout, &sM, useInternal, zFormat, ap); + if( xRealloc ){ + if( sM.zText==sM.zBase ){ + sM.zText = xRealloc(0, sM.nChar+1); + memcpy(sM.zText, sM.zBase, sM.nChar+1); + }else if( sM.nAlloc>sM.nChar+10 ){ + sM.zText = xRealloc(sM.zText, sM.nChar+1); + } + } + return sM.zText; +} + +/* +** Realloc that is a real function, not a macro. +*/ +static void *printf_realloc(void *old, int size){ + return sqliteRealloc(old,size); +} + +/* +** Print into memory obtained from sqliteMalloc(). Use the internal +** %-conversion extensions. +*/ +char *sqliteVMPrintf(const char *zFormat, va_list ap){ + char zBase[1000]; + return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); +} + +/* +** Print into memory obtained from sqliteMalloc(). Use the internal +** %-conversion extensions. +*/ +char *sqliteMPrintf(const char *zFormat, ...){ + va_list ap; + char *z; + char zBase[1000]; + va_start(ap, zFormat); + z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap); + va_end(ap); + return z; +} + +/* +** Print into memory obtained from malloc(). Do not use the internal +** %-conversion extensions. This routine is for use by external users. +*/ +char *sqlite_mprintf(const char *zFormat, ...){ + va_list ap; + char *z; + char zBuf[200]; + + va_start(ap,zFormat); + z = base_vprintf((void*(*)(void*,int))realloc, 0, + zBuf, sizeof(zBuf), zFormat, ap); + va_end(ap); + return z; +} + +/* This is the varargs version of sqlite_mprintf. +*/ +char *sqlite_vmprintf(const char *zFormat, va_list ap){ + char zBuf[200]; + return base_vprintf((void*(*)(void*,int))realloc, 0, + zBuf, sizeof(zBuf), zFormat, ap); +} + +/* +** sqlite_snprintf() works like snprintf() except that it ignores the +** current locale settings. This is important for SQLite because we +** are not able to use a "," as the decimal point in place of "." as +** specified by some locales. +*/ +char *sqlite_snprintf(int n, char *zBuf, const char *zFormat, ...){ + char *z; + va_list ap; + + va_start(ap,zFormat); + z = base_vprintf(0, 0, zBuf, n, zFormat, ap); + va_end(ap); + return z; +} + +/* +** The following four routines implement the varargs versions of the +** sqlite_exec() and sqlite_get_table() interfaces. See the sqlite.h +** header files for a more detailed description of how these interfaces +** work. +** +** These routines are all just simple wrappers. +*/ +int sqlite_exec_printf( + sqlite *db, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback xCallback, /* Callback function */ + void *pArg, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string. */ +){ + va_list ap; + int rc; + + va_start(ap, errmsg); + rc = sqlite_exec_vprintf(db, sqlFormat, xCallback, pArg, errmsg, ap); + va_end(ap); + return rc; +} +int sqlite_exec_vprintf( + sqlite *db, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback xCallback, /* Callback function */ + void *pArg, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string. */ +){ + char *zSql; + int rc; + + zSql = sqlite_vmprintf(sqlFormat, ap); + rc = sqlite_exec(db, zSql, xCallback, pArg, errmsg); + free(zSql); + return rc; +} +int sqlite_get_table_printf( + sqlite *db, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncol, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string */ +){ + va_list ap; + int rc; + + va_start(ap, errmsg); + rc = sqlite_get_table_vprintf(db, sqlFormat, resultp, nrow, ncol, errmsg, ap); + va_end(ap); + return rc; +} +int sqlite_get_table_vprintf( + sqlite *db, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string */ +){ + char *zSql; + int rc; + + zSql = sqlite_vmprintf(sqlFormat, ap); + rc = sqlite_get_table(db, zSql, resultp, nrow, ncolumn, errmsg); + free(zSql); + return rc; +} diff --git a/src/libs/sqlite2/random.c b/src/libs/sqlite2/random.c new file mode 100644 index 00000000..0d0a5447 --- /dev/null +++ b/src/libs/sqlite2/random.c @@ -0,0 +1,97 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement a pseudo-random number +** generator (PRNG) for SQLite. +** +** Random numbers are used by some of the database backends in order +** to generate random integer keys for tables or random filenames. +** +** $Id: random.c 326789 2004-07-07 21:25:56Z pahlibar $ +*/ +#include "sqliteInt.h" +#include "os.h" + + +/* +** Get a single 8-bit random value from the RC4 PRNG. The Mutex +** must be held while executing this routine. +** +** Why not just use a library random generator like lrand48() for this? +** Because the OP_NewRecno opcode in the VDBE depends on having a very +** good source of random numbers. The lrand48() library function may +** well be good enough. But maybe not. Or maybe lrand48() has some +** subtle problems on some systems that could cause problems. It is hard +** to know. To minimize the risk of problems due to bad lrand48() +** implementations, SQLite uses this random number generator based +** on RC4, which we know works very well. +*/ +static int randomByte(){ + unsigned char t; + + /* All threads share a single random number generator. + ** This structure is the current state of the generator. + */ + static struct { + unsigned char isInit; /* True if initialized */ + unsigned char i, j; /* State variables */ + unsigned char s[256]; /* State variables */ + } prng; + + /* Initialize the state of the random number generator once, + ** the first time this routine is called. The seed value does + ** not need to contain a lot of randomness since we are not + ** trying to do secure encryption or anything like that... + ** + ** Nothing in this file or anywhere else in SQLite does any kind of + ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random + ** number generator) not as an encryption device. + */ + if( !prng.isInit ){ + int i; + char k[256]; + prng.j = 0; + prng.i = 0; + sqliteOsRandomSeed(k); + for(i=0; i<256; i++){ + prng.s[i] = i; + } + for(i=0; i<256; i++){ + prng.j += prng.s[i] + k[i]; + t = prng.s[prng.j]; + prng.s[prng.j] = prng.s[i]; + prng.s[i] = t; + } + prng.isInit = 1; + } + + /* Generate and return single random byte + */ + prng.i++; + t = prng.s[prng.i]; + prng.j += t; + prng.s[prng.i] = prng.s[prng.j]; + prng.s[prng.j] = t; + t += prng.s[prng.i]; + return prng.s[t]; +} + +/* +** Return N random bytes. +*/ +void sqliteRandomness(int N, void *pBuf){ + unsigned char *zBuf = pBuf; + sqliteOsEnterMutex(); + while( N-- ){ + *(zBuf++) = randomByte(); + } + sqliteOsLeaveMutex(); +} diff --git a/src/libs/sqlite2/select.c b/src/libs/sqlite2/select.c new file mode 100644 index 00000000..4cf03606 --- /dev/null +++ b/src/libs/sqlite2/select.c @@ -0,0 +1,2434 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the parser +** to handle SELECT statements in SQLite. +** +** $Id: select.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include "sqliteInt.h" + + +/* +** Allocate a new Select structure and return a pointer to that +** structure. +*/ +Select *sqliteSelectNew( + ExprList *pEList, /* which columns to include in the result */ + SrcList *pSrc, /* the FROM clause -- which tables to scan */ + Expr *pWhere, /* the WHERE clause */ + ExprList *pGroupBy, /* the GROUP BY clause */ + Expr *pHaving, /* the HAVING clause */ + ExprList *pOrderBy, /* the ORDER BY clause */ + int isDistinct, /* true if the DISTINCT keyword is present */ + int nLimit, /* LIMIT value. -1 means not used */ + int nOffset /* OFFSET value. 0 means no offset */ +){ + Select *pNew; + pNew = sqliteMalloc( sizeof(*pNew) ); + if( pNew==0 ){ + sqliteExprListDelete(pEList); + sqliteSrcListDelete(pSrc); + sqliteExprDelete(pWhere); + sqliteExprListDelete(pGroupBy); + sqliteExprDelete(pHaving); + sqliteExprListDelete(pOrderBy); + }else{ + if( pEList==0 ){ + pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL,0,0,0), 0); + } + pNew->pEList = pEList; + pNew->pSrc = pSrc; + pNew->pWhere = pWhere; + pNew->pGroupBy = pGroupBy; + pNew->pHaving = pHaving; + pNew->pOrderBy = pOrderBy; + pNew->isDistinct = isDistinct; + pNew->op = TK_SELECT; + pNew->nLimit = nLimit; + pNew->nOffset = nOffset; + pNew->iLimit = -1; + pNew->iOffset = -1; + } + return pNew; +} + +/* +** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the +** type of join. Return an integer constant that expresses that type +** in terms of the following bit values: +** +** JT_INNER +** JT_OUTER +** JT_NATURAL +** JT_LEFT +** JT_RIGHT +** +** A full outer join is the combination of JT_LEFT and JT_RIGHT. +** +** If an illegal or unsupported join type is seen, then still return +** a join type, but put an error in the pParse structure. +*/ +int sqliteJoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){ + int jointype = 0; + Token *apAll[3]; + Token *p; + static struct { + const char *zKeyword; + int nChar; + int code; + } keywords[] = { + { "natural", 7, JT_NATURAL }, + { "left", 4, JT_LEFT|JT_OUTER }, + { "right", 5, JT_RIGHT|JT_OUTER }, + { "full", 4, JT_LEFT|JT_RIGHT|JT_OUTER }, + { "outer", 5, JT_OUTER }, + { "inner", 5, JT_INNER }, + { "cross", 5, JT_INNER }, + }; + int i, j; + apAll[0] = pA; + apAll[1] = pB; + apAll[2] = pC; + for(i=0; i<3 && apAll[i]; i++){ + p = apAll[i]; + for(j=0; jn==keywords[j].nChar + && sqliteStrNICmp(p->z, keywords[j].zKeyword, p->n)==0 ){ + jointype |= keywords[j].code; + break; + } + } + if( j>=sizeof(keywords)/sizeof(keywords[0]) ){ + jointype |= JT_ERROR; + break; + } + } + if( + (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) || + (jointype & JT_ERROR)!=0 + ){ + static Token dummy = { 0, 0 }; + char *zSp1 = " ", *zSp2 = " "; + if( pB==0 ){ pB = &dummy; zSp1 = 0; } + if( pC==0 ){ pC = &dummy; zSp2 = 0; } + sqliteSetNString(&pParse->zErrMsg, "unknown or unsupported join type: ", 0, + pA->z, pA->n, zSp1, 1, pB->z, pB->n, zSp2, 1, pC->z, pC->n, 0); + pParse->nErr++; + jointype = JT_INNER; + }else if( jointype & JT_RIGHT ){ + sqliteErrorMsg(pParse, + "RIGHT and FULL OUTER JOINs are not currently supported"); + jointype = JT_INNER; + } + return jointype; +} + +/* +** Return the index of a column in a table. Return -1 if the column +** is not contained in the table. +*/ +static int columnIndex(Table *pTab, const char *zCol){ + int i; + for(i=0; inCol; i++){ + if( sqliteStrICmp(pTab->aCol[i].zName, zCol)==0 ) return i; + } + return -1; +} + +/* +** Add a term to the WHERE expression in *ppExpr that requires the +** zCol column to be equal in the two tables pTab1 and pTab2. +*/ +static void addWhereTerm( + const char *zCol, /* Name of the column */ + const Table *pTab1, /* First table */ + const Table *pTab2, /* Second table */ + Expr **ppExpr /* Add the equality term to this expression */ +){ + Token dummy; + Expr *pE1a, *pE1b, *pE1c; + Expr *pE2a, *pE2b, *pE2c; + Expr *pE; + + dummy.z = zCol; + dummy.n = strlen(zCol); + dummy.dyn = 0; + pE1a = sqliteExpr(TK_ID, 0, 0, &dummy); + pE2a = sqliteExpr(TK_ID, 0, 0, &dummy); + dummy.z = pTab1->zName; + dummy.n = strlen(dummy.z); + pE1b = sqliteExpr(TK_ID, 0, 0, &dummy); + dummy.z = pTab2->zName; + dummy.n = strlen(dummy.z); + pE2b = sqliteExpr(TK_ID, 0, 0, &dummy); + pE1c = sqliteExpr(TK_DOT, pE1b, pE1a, 0); + pE2c = sqliteExpr(TK_DOT, pE2b, pE2a, 0); + pE = sqliteExpr(TK_EQ, pE1c, pE2c, 0); + ExprSetProperty(pE, EP_FromJoin); + if( *ppExpr ){ + *ppExpr = sqliteExpr(TK_AND, *ppExpr, pE, 0); + }else{ + *ppExpr = pE; + } +} + +/* +** Set the EP_FromJoin property on all terms of the given expression. +** +** The EP_FromJoin property is used on terms of an expression to tell +** the LEFT OUTER JOIN processing logic that this term is part of the +** join restriction specified in the ON or USING clause and not a part +** of the more general WHERE clause. These terms are moved over to the +** WHERE clause during join processing but we need to remember that they +** originated in the ON or USING clause. +*/ +static void setJoinExpr(Expr *p){ + while( p ){ + ExprSetProperty(p, EP_FromJoin); + setJoinExpr(p->pLeft); + p = p->pRight; + } +} + +/* +** This routine processes the join information for a SELECT statement. +** ON and USING clauses are converted into extra terms of the WHERE clause. +** NATURAL joins also create extra WHERE clause terms. +** +** This routine returns the number of errors encountered. +*/ +static int sqliteProcessJoin(Parse *pParse, Select *p){ + SrcList *pSrc; + int i, j; + pSrc = p->pSrc; + for(i=0; inSrc-1; i++){ + struct SrcList_item *pTerm = &pSrc->a[i]; + struct SrcList_item *pOther = &pSrc->a[i+1]; + + if( pTerm->pTab==0 || pOther->pTab==0 ) continue; + + /* When the NATURAL keyword is present, add WHERE clause terms for + ** every column that the two tables have in common. + */ + if( pTerm->jointype & JT_NATURAL ){ + Table *pTab; + if( pTerm->pOn || pTerm->pUsing ){ + sqliteErrorMsg(pParse, "a NATURAL join may not have " + "an ON or USING clause", 0); + return 1; + } + pTab = pTerm->pTab; + for(j=0; jnCol; j++){ + if( columnIndex(pOther->pTab, pTab->aCol[j].zName)>=0 ){ + addWhereTerm(pTab->aCol[j].zName, pTab, pOther->pTab, &p->pWhere); + } + } + } + + /* Disallow both ON and USING clauses in the same join + */ + if( pTerm->pOn && pTerm->pUsing ){ + sqliteErrorMsg(pParse, "cannot have both ON and USING " + "clauses in the same join"); + return 1; + } + + /* Add the ON clause to the end of the WHERE clause, connected by + ** and AND operator. + */ + if( pTerm->pOn ){ + setJoinExpr(pTerm->pOn); + if( p->pWhere==0 ){ + p->pWhere = pTerm->pOn; + }else{ + p->pWhere = sqliteExpr(TK_AND, p->pWhere, pTerm->pOn, 0); + } + pTerm->pOn = 0; + } + + /* Create extra terms on the WHERE clause for each column named + ** in the USING clause. Example: If the two tables to be joined are + ** A and B and the USING clause names X, Y, and Z, then add this + ** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z + ** Report an error if any column mentioned in the USING clause is + ** not contained in both tables to be joined. + */ + if( pTerm->pUsing ){ + IdList *pList; + int j; + assert( inSrc-1 ); + pList = pTerm->pUsing; + for(j=0; jnId; j++){ + if( columnIndex(pTerm->pTab, pList->a[j].zName)<0 || + columnIndex(pOther->pTab, pList->a[j].zName)<0 ){ + sqliteErrorMsg(pParse, "cannot join using column %s - column " + "not present in both tables", pList->a[j].zName); + return 1; + } + addWhereTerm(pList->a[j].zName, pTerm->pTab, pOther->pTab, &p->pWhere); + } + } + } + return 0; +} + +/* +** Delete the given Select structure and all of its substructures. +*/ +void sqliteSelectDelete(Select *p){ + if( p==0 ) return; + sqliteExprListDelete(p->pEList); + sqliteSrcListDelete(p->pSrc); + sqliteExprDelete(p->pWhere); + sqliteExprListDelete(p->pGroupBy); + sqliteExprDelete(p->pHaving); + sqliteExprListDelete(p->pOrderBy); + sqliteSelectDelete(p->pPrior); + sqliteFree(p->zSelect); + sqliteFree(p); +} + +/* +** Delete the aggregate information from the parse structure. +*/ +static void sqliteAggregateInfoReset(Parse *pParse){ + sqliteFree(pParse->aAgg); + pParse->aAgg = 0; + pParse->nAgg = 0; + pParse->useAgg = 0; +} + +/* +** Insert code into "v" that will push the record on the top of the +** stack into the sorter. +*/ +static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){ + char *zSortOrder; + int i; + zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 ); + if( zSortOrder==0 ) return; + for(i=0; inExpr; i++){ + int order = pOrderBy->a[i].sortOrder; + int type; + int c; + if( (order & SQLITE_SO_TYPEMASK)==SQLITE_SO_TEXT ){ + type = SQLITE_SO_TEXT; + }else if( (order & SQLITE_SO_TYPEMASK)==SQLITE_SO_NUM ){ + type = SQLITE_SO_NUM; + }else if( pParse->db->file_format>=4 ){ + type = sqliteExprType(pOrderBy->a[i].pExpr); + }else{ + type = SQLITE_SO_NUM; + } + if( (order & SQLITE_SO_DIRMASK)==SQLITE_SO_ASC ){ + c = type==SQLITE_SO_TEXT ? 'A' : '+'; + }else{ + c = type==SQLITE_SO_TEXT ? 'D' : '-'; + } + zSortOrder[i] = c; + sqliteExprCode(pParse, pOrderBy->a[i].pExpr); + } + zSortOrder[pOrderBy->nExpr] = 0; + sqliteVdbeOp3(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, P3_DYNAMIC); + sqliteVdbeAddOp(v, OP_SortPut, 0, 0); +} + +/* +** This routine adds a P3 argument to the last VDBE opcode that was +** inserted. The P3 argument added is a string suitable for the +** OP_MakeKey or OP_MakeIdxKey opcodes. The string consists of +** characters 't' or 'n' depending on whether or not the various +** fields of the key to be generated should be treated as numeric +** or as text. See the OP_MakeKey and OP_MakeIdxKey opcode +** documentation for additional information about the P3 string. +** See also the sqliteAddIdxKeyType() routine. +*/ +void sqliteAddKeyType(Vdbe *v, ExprList *pEList){ + int nColumn = pEList->nExpr; + char *zType = sqliteMalloc( nColumn+1 ); + int i; + if( zType==0 ) return; + for(i=0; ia[i].pExpr)==SQLITE_SO_NUM ? 'n' : 't'; + } + zType[i] = 0; + sqliteVdbeChangeP3(v, -1, zType, P3_DYNAMIC); +} + +/* +** Add code to implement the OFFSET and LIMIT +*/ +static void codeLimiter( + Vdbe *v, /* Generate code into this VM */ + Select *p, /* The SELECT statement being coded */ + int iContinue, /* Jump here to skip the current record */ + int iBreak, /* Jump here to end the loop */ + int nPop /* Number of times to pop stack when jumping */ +){ + if( p->iOffset>=0 ){ + int addr = sqliteVdbeCurrentAddr(v) + 2; + if( nPop>0 ) addr++; + sqliteVdbeAddOp(v, OP_MemIncr, p->iOffset, addr); + if( nPop>0 ){ + sqliteVdbeAddOp(v, OP_Pop, nPop, 0); + } + sqliteVdbeAddOp(v, OP_Goto, 0, iContinue); + } + if( p->iLimit>=0 ){ + sqliteVdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak); + } +} + +/* +** This routine generates the code for the inside of the inner loop +** of a SELECT. +** +** If srcTab and nColumn are both zero, then the pEList expressions +** are evaluated in order to get the data for this row. If nColumn>0 +** then data is pulled from srcTab and pEList is used only to get the +** datatypes for each column. +*/ +static int selectInnerLoop( + Parse *pParse, /* The parser context */ + Select *p, /* The complete select statement being coded */ + ExprList *pEList, /* List of values being extracted */ + int srcTab, /* Pull data from this table */ + int nColumn, /* Number of columns in the source table */ + ExprList *pOrderBy, /* If not NULL, sort results using this key */ + int distinct, /* If >=0, make sure results are distinct */ + int eDest, /* How to dispose of the results */ + int iParm, /* An argument to the disposal method */ + int iContinue, /* Jump here to continue with next row */ + int iBreak /* Jump here to break out of the inner loop */ +){ + Vdbe *v = pParse->pVdbe; + int i; + int hasDistinct; /* True if the DISTINCT keyword is present */ + + if( v==0 ) return 0; + assert( pEList!=0 ); + + /* If there was a LIMIT clause on the SELECT statement, then do the check + ** to see if this row should be output. + */ + hasDistinct = distinct>=0 && pEList && pEList->nExpr>0; + if( pOrderBy==0 && !hasDistinct ){ + codeLimiter(v, p, iContinue, iBreak, 0); + } + + /* Pull the requested columns. + */ + if( nColumn>0 ){ + for(i=0; inExpr; + for(i=0; inExpr; i++){ + sqliteExprCode(pParse, pEList->a[i].pExpr); + } + } + + /* If the DISTINCT keyword was present on the SELECT statement + ** and this row has been seen before, then do not make this row + ** part of the result. + */ + if( hasDistinct ){ +#if NULL_ALWAYS_DISTINCT + sqliteVdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqliteVdbeCurrentAddr(v)+7); +#endif + sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1); + if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pEList); + sqliteVdbeAddOp(v, OP_Distinct, distinct, sqliteVdbeCurrentAddr(v)+3); + sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, iContinue); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutStrKey, distinct, 0); + if( pOrderBy==0 ){ + codeLimiter(v, p, iContinue, iBreak, nColumn); + } + } + + switch( eDest ){ + /* In this mode, write each query result to the key of the temporary + ** table iParm. + */ + case SRT_Union: { + sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0); + break; + } + + /* Store the result as data using a unique key. + */ + case SRT_Table: + case SRT_TempTable: { + sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0); + if( pOrderBy ){ + pushOntoSorter(pParse, v, pOrderBy); + }else{ + sqliteVdbeAddOp(v, OP_NewRecno, iParm, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, iParm, 0); + } + break; + } + + /* Construct a record from the query result, but instead of + ** saving that record, use it as a key to delete elements from + ** the temporary table iParm. + */ + case SRT_Except: { + int addr; + addr = sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT); + sqliteVdbeAddOp(v, OP_NotFound, iParm, addr+3); + sqliteVdbeAddOp(v, OP_Delete, iParm, 0); + break; + } + + /* If we are creating a set for an "expr IN (SELECT ...)" construct, + ** then there should be a single item on the stack. Write this + ** item into the set table with bogus data. + */ + case SRT_Set: { + int addr1 = sqliteVdbeCurrentAddr(v); + int addr2; + assert( nColumn==1 ); + sqliteVdbeAddOp(v, OP_NotNull, -1, addr1+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + addr2 = sqliteVdbeAddOp(v, OP_Goto, 0, 0); + if( pOrderBy ){ + pushOntoSorter(pParse, v, pOrderBy); + }else{ + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0); + } + sqliteVdbeChangeP2(v, addr2, sqliteVdbeCurrentAddr(v)); + break; + } + + /* If this is a scalar select that is part of an expression, then + ** store the results in the appropriate memory cell and break out + ** of the scan loop. + */ + case SRT_Mem: { + assert( nColumn==1 ); + if( pOrderBy ){ + pushOntoSorter(pParse, v, pOrderBy); + }else{ + sqliteVdbeAddOp(v, OP_MemStore, iParm, 1); + sqliteVdbeAddOp(v, OP_Goto, 0, iBreak); + } + break; + } + + /* Send the data to the callback function. + */ + case SRT_Callback: + case SRT_Sorter: { + if( pOrderBy ){ + sqliteVdbeAddOp(v, OP_SortMakeRec, nColumn, 0); + pushOntoSorter(pParse, v, pOrderBy); + }else{ + assert( eDest==SRT_Callback ); + sqliteVdbeAddOp(v, OP_Callback, nColumn, 0); + } + break; + } + + /* Invoke a subroutine to handle the results. The subroutine itself + ** is responsible for popping the results off of the stack. + */ + case SRT_Subroutine: { + if( pOrderBy ){ + sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0); + pushOntoSorter(pParse, v, pOrderBy); + }else{ + sqliteVdbeAddOp(v, OP_Gosub, 0, iParm); + } + break; + } + + /* Discard the results. This is used for SELECT statements inside + ** the body of a TRIGGER. The purpose of such selects is to call + ** user-defined functions that have side effects. We do not care + ** about the actual results of the select. + */ + default: { + assert( eDest==SRT_Discard ); + sqliteVdbeAddOp(v, OP_Pop, nColumn, 0); + break; + } + } + return 0; +} + +/* +** If the inner loop was generated using a non-null pOrderBy argument, +** then the results were placed in a sorter. After the loop is terminated +** we need to run the sorter and output the results. The following +** routine generates the code needed to do that. +*/ +static void generateSortTail( + Select *p, /* The SELECT statement */ + Vdbe *v, /* Generate code into this VDBE */ + int nColumn, /* Number of columns of data */ + int eDest, /* Write the sorted results here */ + int iParm /* Optional parameter associated with eDest */ +){ + int end1 = sqliteVdbeMakeLabel(v); + int end2 = sqliteVdbeMakeLabel(v); + int addr; + if( eDest==SRT_Sorter ) return; + sqliteVdbeAddOp(v, OP_Sort, 0, 0); + addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end1); + codeLimiter(v, p, addr, end2, 1); + switch( eDest ){ + case SRT_Callback: { + sqliteVdbeAddOp(v, OP_SortCallback, nColumn, 0); + break; + } + case SRT_Table: + case SRT_TempTable: { + sqliteVdbeAddOp(v, OP_NewRecno, iParm, 0); + sqliteVdbeAddOp(v, OP_Pull, 1, 0); + sqliteVdbeAddOp(v, OP_PutIntKey, iParm, 0); + break; + } + case SRT_Set: { + assert( nColumn==1 ); + sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3); + sqliteVdbeAddOp(v, OP_Pop, 1, 0); + sqliteVdbeAddOp(v, OP_Goto, 0, sqliteVdbeCurrentAddr(v)+3); + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0); + break; + } + case SRT_Mem: { + assert( nColumn==1 ); + sqliteVdbeAddOp(v, OP_MemStore, iParm, 1); + sqliteVdbeAddOp(v, OP_Goto, 0, end1); + break; + } + case SRT_Subroutine: { + int i; + for(i=0; ipVdbe; + int i, j; + for(i=0; inExpr; i++){ + Expr *p = pEList->a[i].pExpr; + char *zType = 0; + if( p==0 ) continue; + if( p->op==TK_COLUMN && pTabList ){ + Table *pTab; + int iCol = p->iColumn; + for(j=0; jnSrc && pTabList->a[j].iCursor!=p->iTable; j++){} + assert( jnSrc ); + pTab = pTabList->a[j].pTab; + if( iCol<0 ) iCol = pTab->iPKey; + assert( iCol==-1 || (iCol>=0 && iColnCol) ); + if( iCol<0 ){ + zType = "INTEGER"; + }else{ + zType = pTab->aCol[iCol].zType; + } + }else{ + if( sqliteExprType(p)==SQLITE_SO_TEXT ){ + zType = "TEXT"; + }else{ + zType = "NUMERIC"; + } + } + sqliteVdbeOp3(v, OP_ColumnName, i + pEList->nExpr, 0, zType, 0); + } +} + +/* +** Generate code that will tell the VDBE the names of columns +** in the result set. This information is used to provide the +** azCol[] values in the callback. +*/ +static void generateColumnNames( + Parse *pParse, /* Parser context */ + SrcList *pTabList, /* List of tables */ + ExprList *pEList /* Expressions defining the result set */ +){ + Vdbe *v = pParse->pVdbe; + int i, j; + sqlite *db = pParse->db; + int fullNames, shortNames; + + assert( v!=0 ); + if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return; + pParse->colNamesSet = 1; + fullNames = (db->flags & SQLITE_FullColNames)!=0; + shortNames = (db->flags & SQLITE_ShortColNames)!=0; + for(i=0; inExpr; i++){ + Expr *p; + int p2 = i==pEList->nExpr-1; + p = pEList->a[i].pExpr; + if( p==0 ) continue; + if( pEList->a[i].zName ){ + char *zName = pEList->a[i].zName; + sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, 0); + continue; + } + if( p->op==TK_COLUMN && pTabList ){ + Table *pTab; + char *zCol; + int iCol = p->iColumn; + for(j=0; jnSrc && pTabList->a[j].iCursor!=p->iTable; j++){} + assert( jnSrc ); + pTab = pTabList->a[j].pTab; + if( iCol<0 ) iCol = pTab->iPKey; + assert( iCol==-1 || (iCol>=0 && iColnCol) ); + if( iCol<0 ){ + zCol = "_ROWID_"; + }else{ + zCol = pTab->aCol[iCol].zName; + } + if( !shortNames && !fullNames && p->span.z && p->span.z[0] ){ + int addr = sqliteVdbeOp3(v,OP_ColumnName, i, p2, p->span.z, p->span.n); + sqliteVdbeCompressSpace(v, addr); + }else if( fullNames || (!shortNames && pTabList->nSrc>1) ){ + char *zName = 0; + char *zTab; + + zTab = pTabList->a[j].zAlias; + if( fullNames || zTab==0 ) zTab = pTab->zName; + sqliteSetString(&zName, zTab, ".", zCol, 0); + sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, P3_DYNAMIC); + }else{ + sqliteVdbeOp3(v, OP_ColumnName, i, p2, zCol, 0); + } + }else if( p->span.z && p->span.z[0] ){ + int addr = sqliteVdbeOp3(v,OP_ColumnName, i, p2, p->span.z, p->span.n); + sqliteVdbeCompressSpace(v, addr); + }else{ + char zName[30]; + assert( p->op!=TK_COLUMN || pTabList==0 ); + sprintf(zName, "column%d", i+1); + sqliteVdbeOp3(v, OP_ColumnName, i, p2, zName, 0); + } + } +} + +/* +** Name of the connection operator, used for error messages. +*/ +static const char *selectOpName(int id){ + char *z; + switch( id ){ + case TK_ALL: z = "UNION ALL"; break; + case TK_INTERSECT: z = "INTERSECT"; break; + case TK_EXCEPT: z = "EXCEPT"; break; + default: z = "UNION"; break; + } + return z; +} + +/* +** Forward declaration +*/ +static int fillInColumnList(Parse*, Select*); + +/* +** Given a SELECT statement, generate a Table structure that describes +** the result set of that SELECT. +*/ +Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ + Table *pTab; + int i, j; + ExprList *pEList; + Column *aCol; + + if( fillInColumnList(pParse, pSelect) ){ + return 0; + } + pTab = sqliteMalloc( sizeof(Table) ); + if( pTab==0 ){ + return 0; + } + pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0; + pEList = pSelect->pEList; + pTab->nCol = pEList->nExpr; + assert( pTab->nCol>0 ); + pTab->aCol = aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol ); + for(i=0; inCol; i++){ + Expr *p, *pR; + if( pEList->a[i].zName ){ + aCol[i].zName = sqliteStrDup(pEList->a[i].zName); + }else if( (p=pEList->a[i].pExpr)->op==TK_DOT + && (pR=p->pRight)!=0 && pR->token.z && pR->token.z[0] ){ + int cnt; + sqliteSetNString(&aCol[i].zName, pR->token.z, pR->token.n, 0); + for(j=cnt=0; jtoken.z, pR->token.n, zBuf, n,0); + j = -1; + } + } + }else if( p->span.z && p->span.z[0] ){ + sqliteSetNString(&pTab->aCol[i].zName, p->span.z, p->span.n, 0); + }else{ + char zBuf[30]; + sprintf(zBuf, "column%d", i+1); + aCol[i].zName = sqliteStrDup(zBuf); + } + sqliteDequote(aCol[i].zName); + } + pTab->iPKey = -1; + return pTab; +} + +/* +** For the given SELECT statement, do three things. +** +** (1) Fill in the pTabList->a[].pTab fields in the SrcList that +** defines the set of tables that should be scanned. For views, +** fill pTabList->a[].pSelect with a copy of the SELECT statement +** that implements the view. A copy is made of the view's SELECT +** statement so that we can freely modify or delete that statement +** without worrying about messing up the presistent representation +** of the view. +** +** (2) Add terms to the WHERE clause to accomodate the NATURAL keyword +** on joins and the ON and USING clause of joins. +** +** (3) Scan the list of columns in the result set (pEList) looking +** for instances of the "*" operator or the TABLE.* operator. +** If found, expand each "*" to be every column in every table +** and TABLE.* to be every column in TABLE. +** +** Return 0 on success. If there are problems, leave an error message +** in pParse and return non-zero. +*/ +static int fillInColumnList(Parse *pParse, Select *p){ + int i, j, k, rc; + SrcList *pTabList; + ExprList *pEList; + Table *pTab; + + if( p==0 || p->pSrc==0 ) return 1; + pTabList = p->pSrc; + pEList = p->pEList; + + /* Look up every table in the table list. + */ + for(i=0; inSrc; i++){ + if( pTabList->a[i].pTab ){ + /* This routine has run before! No need to continue */ + return 0; + } + if( pTabList->a[i].zName==0 ){ + /* A sub-query in the FROM clause of a SELECT */ + assert( pTabList->a[i].pSelect!=0 ); + if( pTabList->a[i].zAlias==0 ){ + char zFakeName[60]; + sprintf(zFakeName, "sqlite_subquery_%p_", + (void*)pTabList->a[i].pSelect); + sqliteSetString(&pTabList->a[i].zAlias, zFakeName, 0); + } + pTabList->a[i].pTab = pTab = + sqliteResultSetOfSelect(pParse, pTabList->a[i].zAlias, + pTabList->a[i].pSelect); + if( pTab==0 ){ + return 1; + } + /* The isTransient flag indicates that the Table structure has been + ** dynamically allocated and may be freed at any time. In other words, + ** pTab is not pointing to a persistent table structure that defines + ** part of the schema. */ + pTab->isTransient = 1; + }else{ + /* An ordinary table or view name in the FROM clause */ + pTabList->a[i].pTab = pTab = + sqliteLocateTable(pParse,pTabList->a[i].zName,pTabList->a[i].zDatabase); + if( pTab==0 ){ + return 1; + } + if( pTab->pSelect ){ + /* We reach here if the named table is a really a view */ + if( sqliteViewGetColumnNames(pParse, pTab) ){ + return 1; + } + /* If pTabList->a[i].pSelect!=0 it means we are dealing with a + ** view within a view. The SELECT structure has already been + ** copied by the outer view so we can skip the copy step here + ** in the inner view. + */ + if( pTabList->a[i].pSelect==0 ){ + pTabList->a[i].pSelect = sqliteSelectDup(pTab->pSelect); + } + } + } + } + + /* Process NATURAL keywords, and ON and USING clauses of joins. + */ + if( sqliteProcessJoin(pParse, p) ) return 1; + + /* For every "*" that occurs in the column list, insert the names of + ** all columns in all tables. And for every TABLE.* insert the names + ** of all columns in TABLE. The parser inserted a special expression + ** with the TK_ALL operator for each "*" that it found in the column list. + ** The following code just has to locate the TK_ALL expressions and expand + ** each one to the list of all columns in all tables. + ** + ** The first loop just checks to see if there are any "*" operators + ** that need expanding. + */ + for(k=0; knExpr; k++){ + Expr *pE = pEList->a[k].pExpr; + if( pE->op==TK_ALL ) break; + if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL + && pE->pLeft && pE->pLeft->op==TK_ID ) break; + } + rc = 0; + if( knExpr ){ + /* + ** If we get here it means the result set contains one or more "*" + ** operators that need to be expanded. Loop through each expression + ** in the result set and expand them one by one. + */ + struct ExprList_item *a = pEList->a; + ExprList *pNew = 0; + for(k=0; knExpr; k++){ + Expr *pE = a[k].pExpr; + if( pE->op!=TK_ALL && + (pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){ + /* This particular expression does not need to be expanded. + */ + pNew = sqliteExprListAppend(pNew, a[k].pExpr, 0); + pNew->a[pNew->nExpr-1].zName = a[k].zName; + a[k].pExpr = 0; + a[k].zName = 0; + }else{ + /* This expression is a "*" or a "TABLE.*" and needs to be + ** expanded. */ + int tableSeen = 0; /* Set to 1 when TABLE matches */ + char *zTName; /* text of name of TABLE */ + if( pE->op==TK_DOT && pE->pLeft ){ + zTName = sqliteTableNameFromToken(&pE->pLeft->token); + }else{ + zTName = 0; + } + for(i=0; inSrc; i++){ + Table *pTab = pTabList->a[i].pTab; + char *zTabName = pTabList->a[i].zAlias; + if( zTabName==0 || zTabName[0]==0 ){ + zTabName = pTab->zName; + } + if( zTName && (zTabName==0 || zTabName[0]==0 || + sqliteStrICmp(zTName, zTabName)!=0) ){ + continue; + } + tableSeen = 1; + for(j=0; jnCol; j++){ + Expr *pExpr, *pLeft, *pRight; + char *zName = pTab->aCol[j].zName; + + if( i>0 && (pTabList->a[i-1].jointype & JT_NATURAL)!=0 && + columnIndex(pTabList->a[i-1].pTab, zName)>=0 ){ + /* In a NATURAL join, omit the join columns from the + ** table on the right */ + continue; + } + if( i>0 && sqliteIdListIndex(pTabList->a[i-1].pUsing, zName)>=0 ){ + /* In a join with a USING clause, omit columns in the + ** using clause from the table on the right. */ + continue; + } + pRight = sqliteExpr(TK_ID, 0, 0, 0); + if( pRight==0 ) break; + pRight->token.z = zName; + pRight->token.n = strlen(zName); + pRight->token.dyn = 0; + if( zTabName && pTabList->nSrc>1 ){ + pLeft = sqliteExpr(TK_ID, 0, 0, 0); + pExpr = sqliteExpr(TK_DOT, pLeft, pRight, 0); + if( pExpr==0 ) break; + pLeft->token.z = zTabName; + pLeft->token.n = strlen(zTabName); + pLeft->token.dyn = 0; + sqliteSetString((char**)&pExpr->span.z, zTabName, ".", zName, 0); + pExpr->span.n = strlen(pExpr->span.z); + pExpr->span.dyn = 1; + pExpr->token.z = 0; + pExpr->token.n = 0; + pExpr->token.dyn = 0; + }else{ + pExpr = pRight; + pExpr->span = pExpr->token; + } + pNew = sqliteExprListAppend(pNew, pExpr, 0); + } + } + if( !tableSeen ){ + if( zTName ){ + sqliteErrorMsg(pParse, "no such table: %s", zTName); + }else{ + sqliteErrorMsg(pParse, "no tables specified"); + } + rc = 1; + } + sqliteFree(zTName); + } + } + sqliteExprListDelete(pEList); + p->pEList = pNew; + } + return rc; +} + +/* +** This routine recursively unlinks the Select.pSrc.a[].pTab pointers +** in a select structure. It just sets the pointers to NULL. This +** routine is recursive in the sense that if the Select.pSrc.a[].pSelect +** pointer is not NULL, this routine is called recursively on that pointer. +** +** This routine is called on the Select structure that defines a +** VIEW in order to undo any bindings to tables. This is necessary +** because those tables might be DROPed by a subsequent SQL command. +** If the bindings are not removed, then the Select.pSrc->a[].pTab field +** will be left pointing to a deallocated Table structure after the +** DROP and a coredump will occur the next time the VIEW is used. +*/ +void sqliteSelectUnbind(Select *p){ + int i; + SrcList *pSrc = p->pSrc; + Table *pTab; + if( p==0 ) return; + for(i=0; inSrc; i++){ + if( (pTab = pSrc->a[i].pTab)!=0 ){ + if( pTab->isTransient ){ + sqliteDeleteTable(0, pTab); + } + pSrc->a[i].pTab = 0; + if( pSrc->a[i].pSelect ){ + sqliteSelectUnbind(pSrc->a[i].pSelect); + } + } + } +} + +/* +** This routine associates entries in an ORDER BY expression list with +** columns in a result. For each ORDER BY expression, the opcode of +** the top-level node is changed to TK_COLUMN and the iColumn value of +** the top-level node is filled in with column number and the iTable +** value of the top-level node is filled with iTable parameter. +** +** If there are prior SELECT clauses, they are processed first. A match +** in an earlier SELECT takes precedence over a later SELECT. +** +** Any entry that does not match is flagged as an error. The number +** of errors is returned. +** +** This routine does NOT correctly initialize the Expr.dataType field +** of the ORDER BY expressions. The multiSelectSortOrder() routine +** must be called to do that after the individual select statements +** have all been analyzed. This routine is unable to compute Expr.dataType +** because it must be called before the individual select statements +** have been analyzed. +*/ +static int matchOrderbyToColumn( + Parse *pParse, /* A place to leave error messages */ + Select *pSelect, /* Match to result columns of this SELECT */ + ExprList *pOrderBy, /* The ORDER BY values to match against columns */ + int iTable, /* Insert this value in iTable */ + int mustComplete /* If TRUE all ORDER BYs must match */ +){ + int nErr = 0; + int i, j; + ExprList *pEList; + + if( pSelect==0 || pOrderBy==0 ) return 1; + if( mustComplete ){ + for(i=0; inExpr; i++){ pOrderBy->a[i].done = 0; } + } + if( fillInColumnList(pParse, pSelect) ){ + return 1; + } + if( pSelect->pPrior ){ + if( matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0) ){ + return 1; + } + } + pEList = pSelect->pEList; + for(i=0; inExpr; i++){ + Expr *pE = pOrderBy->a[i].pExpr; + int iCol = -1; + if( pOrderBy->a[i].done ) continue; + if( sqliteExprIsInteger(pE, &iCol) ){ + if( iCol<=0 || iCol>pEList->nExpr ){ + sqliteErrorMsg(pParse, + "ORDER BY position %d should be between 1 and %d", + iCol, pEList->nExpr); + nErr++; + break; + } + if( !mustComplete ) continue; + iCol--; + } + for(j=0; iCol<0 && jnExpr; j++){ + if( pEList->a[j].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){ + char *zName, *zLabel; + zName = pEList->a[j].zName; + assert( pE->token.z ); + zLabel = sqliteStrNDup(pE->token.z, pE->token.n); + sqliteDequote(zLabel); + if( sqliteStrICmp(zName, zLabel)==0 ){ + iCol = j; + } + sqliteFree(zLabel); + } + if( iCol<0 && sqliteExprCompare(pE, pEList->a[j].pExpr) ){ + iCol = j; + } + } + if( iCol>=0 ){ + pE->op = TK_COLUMN; + pE->iColumn = iCol; + pE->iTable = iTable; + pOrderBy->a[i].done = 1; + } + if( iCol<0 && mustComplete ){ + sqliteErrorMsg(pParse, + "ORDER BY term number %d does not match any result column", i+1); + nErr++; + break; + } + } + return nErr; +} + +/* +** Get a VDBE for the given parser context. Create a new one if necessary. +** If an error occurs, return NULL and leave a message in pParse. +*/ +Vdbe *sqliteGetVdbe(Parse *pParse){ + Vdbe *v = pParse->pVdbe; + if( v==0 ){ + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db); + } + return v; +} + +/* +** This routine sets the Expr.dataType field on all elements of +** the pOrderBy expression list. The pOrderBy list will have been +** set up by matchOrderbyToColumn(). Hence each expression has +** a TK_COLUMN as its root node. The Expr.iColumn refers to a +** column in the result set. The datatype is set to SQLITE_SO_TEXT +** if the corresponding column in p and every SELECT to the left of +** p has a datatype of SQLITE_SO_TEXT. If the cooressponding column +** in p or any of the left SELECTs is SQLITE_SO_NUM, then the datatype +** of the order-by expression is set to SQLITE_SO_NUM. +** +** Examples: +** +** CREATE TABLE one(a INTEGER, b TEXT); +** CREATE TABLE two(c VARCHAR(5), d FLOAT); +** +** SELECT b, b FROM one UNION SELECT d, c FROM two ORDER BY 1, 2; +** +** The primary sort key will use SQLITE_SO_NUM because the "d" in +** the second SELECT is numeric. The 1st column of the first SELECT +** is text but that does not matter because a numeric always overrides +** a text. +** +** The secondary key will use the SQLITE_SO_TEXT sort order because +** both the (second) "b" in the first SELECT and the "c" in the second +** SELECT have a datatype of text. +*/ +static void multiSelectSortOrder(Select *p, ExprList *pOrderBy){ + int i; + ExprList *pEList; + if( pOrderBy==0 ) return; + if( p==0 ){ + for(i=0; inExpr; i++){ + pOrderBy->a[i].pExpr->dataType = SQLITE_SO_TEXT; + } + return; + } + multiSelectSortOrder(p->pPrior, pOrderBy); + pEList = p->pEList; + for(i=0; inExpr; i++){ + Expr *pE = pOrderBy->a[i].pExpr; + if( pE->dataType==SQLITE_SO_NUM ) continue; + assert( pE->iColumn>=0 ); + if( pEList->nExpr>pE->iColumn ){ + pE->dataType = sqliteExprType(pEList->a[pE->iColumn].pExpr); + } + } +} + +/* +** Compute the iLimit and iOffset fields of the SELECT based on the +** nLimit and nOffset fields. nLimit and nOffset hold the integers +** that appear in the original SQL statement after the LIMIT and OFFSET +** keywords. Or that hold -1 and 0 if those keywords are omitted. +** iLimit and iOffset are the integer memory register numbers for +** counters used to compute the limit and offset. If there is no +** limit and/or offset, then iLimit and iOffset are negative. +** +** This routine changes the values if iLimit and iOffset only if +** a limit or offset is defined by nLimit and nOffset. iLimit and +** iOffset should have been preset to appropriate default values +** (usually but not always -1) prior to calling this routine. +** Only if nLimit>=0 or nOffset>0 do the limit registers get +** redefined. The UNION ALL operator uses this property to force +** the reuse of the same limit and offset registers across multiple +** SELECT statements. +*/ +static void computeLimitRegisters(Parse *pParse, Select *p){ + /* + ** If the comparison is p->nLimit>0 then "LIMIT 0" shows + ** all rows. It is the same as no limit. If the comparision is + ** p->nLimit>=0 then "LIMIT 0" show no rows at all. + ** "LIMIT -1" always shows all rows. There is some + ** contraversy about what the correct behavior should be. + ** The current implementation interprets "LIMIT 0" to mean + ** no rows. + */ + if( p->nLimit>=0 ){ + int iMem = pParse->nMem++; + Vdbe *v = sqliteGetVdbe(pParse); + if( v==0 ) return; + sqliteVdbeAddOp(v, OP_Integer, -p->nLimit, 0); + sqliteVdbeAddOp(v, OP_MemStore, iMem, 1); + p->iLimit = iMem; + } + if( p->nOffset>0 ){ + int iMem = pParse->nMem++; + Vdbe *v = sqliteGetVdbe(pParse); + if( v==0 ) return; + sqliteVdbeAddOp(v, OP_Integer, -p->nOffset, 0); + sqliteVdbeAddOp(v, OP_MemStore, iMem, 1); + p->iOffset = iMem; + } +} + +/* +** This routine is called to process a query that is really the union +** or intersection of two or more separate queries. +** +** "p" points to the right-most of the two queries. the query on the +** left is p->pPrior. The left query could also be a compound query +** in which case this routine will be called recursively. +** +** The results of the total query are to be written into a destination +** of type eDest with parameter iParm. +** +** Example 1: Consider a three-way compound SQL statement. +** +** SELECT a FROM t1 UNION SELECT b FROM t2 UNION SELECT c FROM t3 +** +** This statement is parsed up as follows: +** +** SELECT c FROM t3 +** | +** `-----> SELECT b FROM t2 +** | +** `------> SELECT a FROM t1 +** +** The arrows in the diagram above represent the Select.pPrior pointer. +** So if this routine is called with p equal to the t3 query, then +** pPrior will be the t2 query. p->op will be TK_UNION in this case. +** +** Notice that because of the way SQLite parses compound SELECTs, the +** individual selects always group from left to right. +*/ +static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ + int rc; /* Success code from a subroutine */ + Select *pPrior; /* Another SELECT immediately to our left */ + Vdbe *v; /* Generate code to this VDBE */ + + /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only + ** the last SELECT in the series may have an ORDER BY or LIMIT. + */ + if( p==0 || p->pPrior==0 ) return 1; + pPrior = p->pPrior; + if( pPrior->pOrderBy ){ + sqliteErrorMsg(pParse,"ORDER BY clause should come after %s not before", + selectOpName(p->op)); + return 1; + } + if( pPrior->nLimit>=0 || pPrior->nOffset>0 ){ + sqliteErrorMsg(pParse,"LIMIT clause should come after %s not before", + selectOpName(p->op)); + return 1; + } + + /* Make sure we have a valid query engine. If not, create a new one. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) return 1; + + /* Create the destination temporary table if necessary + */ + if( eDest==SRT_TempTable ){ + sqliteVdbeAddOp(v, OP_OpenTemp, iParm, 0); + eDest = SRT_Table; + } + + /* Generate code for the left and right SELECT statements. + */ + switch( p->op ){ + case TK_ALL: { + if( p->pOrderBy==0 ){ + pPrior->nLimit = p->nLimit; + pPrior->nOffset = p->nOffset; + rc = sqliteSelect(pParse, pPrior, eDest, iParm, 0, 0, 0); + if( rc ) return rc; + p->pPrior = 0; + p->iLimit = pPrior->iLimit; + p->iOffset = pPrior->iOffset; + p->nLimit = -1; + p->nOffset = 0; + rc = sqliteSelect(pParse, p, eDest, iParm, 0, 0, 0); + p->pPrior = pPrior; + if( rc ) return rc; + break; + } + /* For UNION ALL ... ORDER BY fall through to the next case */ + } + case TK_EXCEPT: + case TK_UNION: { + int unionTab; /* Cursor number of the temporary table holding result */ + int op; /* One of the SRT_ operations to apply to self */ + int priorOp; /* The SRT_ operation to apply to prior selects */ + int nLimit, nOffset; /* Saved values of p->nLimit and p->nOffset */ + ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */ + + priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union; + if( eDest==priorOp && p->pOrderBy==0 && p->nLimit<0 && p->nOffset==0 ){ + /* We can reuse a temporary table generated by a SELECT to our + ** right. + */ + unionTab = iParm; + }else{ + /* We will need to create our own temporary table to hold the + ** intermediate results. + */ + unionTab = pParse->nTab++; + if( p->pOrderBy + && matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){ + return 1; + } + if( p->op!=TK_ALL ){ + sqliteVdbeAddOp(v, OP_OpenTemp, unionTab, 1); + sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1); + }else{ + sqliteVdbeAddOp(v, OP_OpenTemp, unionTab, 0); + } + } + + /* Code the SELECT statements to our left + */ + rc = sqliteSelect(pParse, pPrior, priorOp, unionTab, 0, 0, 0); + if( rc ) return rc; + + /* Code the current SELECT statement + */ + switch( p->op ){ + case TK_EXCEPT: op = SRT_Except; break; + case TK_UNION: op = SRT_Union; break; + case TK_ALL: op = SRT_Table; break; + } + p->pPrior = 0; + pOrderBy = p->pOrderBy; + p->pOrderBy = 0; + nLimit = p->nLimit; + p->nLimit = -1; + nOffset = p->nOffset; + p->nOffset = 0; + rc = sqliteSelect(pParse, p, op, unionTab, 0, 0, 0); + p->pPrior = pPrior; + p->pOrderBy = pOrderBy; + p->nLimit = nLimit; + p->nOffset = nOffset; + if( rc ) return rc; + + /* Convert the data in the temporary table into whatever form + ** it is that we currently need. + */ + if( eDest!=priorOp || unionTab!=iParm ){ + int iCont, iBreak, iStart; + assert( p->pEList ); + if( eDest==SRT_Callback ){ + generateColumnNames(pParse, 0, p->pEList); + generateColumnTypes(pParse, p->pSrc, p->pEList); + } + iBreak = sqliteVdbeMakeLabel(v); + iCont = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak); + computeLimitRegisters(pParse, p); + iStart = sqliteVdbeCurrentAddr(v); + multiSelectSortOrder(p, p->pOrderBy); + rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr, + p->pOrderBy, -1, eDest, iParm, + iCont, iBreak); + if( rc ) return 1; + sqliteVdbeResolveLabel(v, iCont); + sqliteVdbeAddOp(v, OP_Next, unionTab, iStart); + sqliteVdbeResolveLabel(v, iBreak); + sqliteVdbeAddOp(v, OP_Close, unionTab, 0); + if( p->pOrderBy ){ + generateSortTail(p, v, p->pEList->nExpr, eDest, iParm); + } + } + break; + } + case TK_INTERSECT: { + int tab1, tab2; + int iCont, iBreak, iStart; + int nLimit, nOffset; + + /* INTERSECT is different from the others since it requires + ** two temporary tables. Hence it has its own case. Begin + ** by allocating the tables we will need. + */ + tab1 = pParse->nTab++; + tab2 = pParse->nTab++; + if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){ + return 1; + } + sqliteVdbeAddOp(v, OP_OpenTemp, tab1, 1); + sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1); + + /* Code the SELECTs to our left into temporary table "tab1". + */ + rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1, 0, 0, 0); + if( rc ) return rc; + + /* Code the current SELECT into temporary table "tab2" + */ + sqliteVdbeAddOp(v, OP_OpenTemp, tab2, 1); + sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1); + p->pPrior = 0; + nLimit = p->nLimit; + p->nLimit = -1; + nOffset = p->nOffset; + p->nOffset = 0; + rc = sqliteSelect(pParse, p, SRT_Union, tab2, 0, 0, 0); + p->pPrior = pPrior; + p->nLimit = nLimit; + p->nOffset = nOffset; + if( rc ) return rc; + + /* Generate code to take the intersection of the two temporary + ** tables. + */ + assert( p->pEList ); + if( eDest==SRT_Callback ){ + generateColumnNames(pParse, 0, p->pEList); + generateColumnTypes(pParse, p->pSrc, p->pEList); + } + iBreak = sqliteVdbeMakeLabel(v); + iCont = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak); + computeLimitRegisters(pParse, p); + iStart = sqliteVdbeAddOp(v, OP_FullKey, tab1, 0); + sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont); + multiSelectSortOrder(p, p->pOrderBy); + rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, + p->pOrderBy, -1, eDest, iParm, + iCont, iBreak); + if( rc ) return 1; + sqliteVdbeResolveLabel(v, iCont); + sqliteVdbeAddOp(v, OP_Next, tab1, iStart); + sqliteVdbeResolveLabel(v, iBreak); + sqliteVdbeAddOp(v, OP_Close, tab2, 0); + sqliteVdbeAddOp(v, OP_Close, tab1, 0); + if( p->pOrderBy ){ + generateSortTail(p, v, p->pEList->nExpr, eDest, iParm); + } + break; + } + } + assert( p->pEList && pPrior->pEList ); + if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ + sqliteErrorMsg(pParse, "SELECTs to the left and right of %s" + " do not have the same number of result columns", selectOpName(p->op)); + return 1; + } + return 0; +} + +/* +** Scan through the expression pExpr. Replace every reference to +** a column in table number iTable with a copy of the iColumn-th +** entry in pEList. (But leave references to the ROWID column +** unchanged.) +** +** This routine is part of the flattening procedure. A subquery +** whose result set is defined by pEList appears as entry in the +** FROM clause of a SELECT such that the VDBE cursor assigned to that +** FORM clause entry is iTable. This routine make the necessary +** changes to pExpr so that it refers directly to the source table +** of the subquery rather the result set of the subquery. +*/ +static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */ +static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){ + if( pExpr==0 ) return; + if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){ + if( pExpr->iColumn<0 ){ + pExpr->op = TK_NULL; + }else{ + Expr *pNew; + assert( pEList!=0 && pExpr->iColumnnExpr ); + assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 ); + pNew = pEList->a[pExpr->iColumn].pExpr; + assert( pNew!=0 ); + pExpr->op = pNew->op; + pExpr->dataType = pNew->dataType; + assert( pExpr->pLeft==0 ); + pExpr->pLeft = sqliteExprDup(pNew->pLeft); + assert( pExpr->pRight==0 ); + pExpr->pRight = sqliteExprDup(pNew->pRight); + assert( pExpr->pList==0 ); + pExpr->pList = sqliteExprListDup(pNew->pList); + pExpr->iTable = pNew->iTable; + pExpr->iColumn = pNew->iColumn; + pExpr->iAgg = pNew->iAgg; + sqliteTokenCopy(&pExpr->token, &pNew->token); + sqliteTokenCopy(&pExpr->span, &pNew->span); + } + }else{ + substExpr(pExpr->pLeft, iTable, pEList); + substExpr(pExpr->pRight, iTable, pEList); + substExprList(pExpr->pList, iTable, pEList); + } +} +static void +substExprList(ExprList *pList, int iTable, ExprList *pEList){ + int i; + if( pList==0 ) return; + for(i=0; inExpr; i++){ + substExpr(pList->a[i].pExpr, iTable, pEList); + } +} + +/* +** This routine attempts to flatten subqueries in order to speed +** execution. It returns 1 if it makes changes and 0 if no flattening +** occurs. +** +** To understand the concept of flattening, consider the following +** query: +** +** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5 +** +** The default way of implementing this query is to execute the +** subquery first and store the results in a temporary table, then +** run the outer query on that temporary table. This requires two +** passes over the data. Furthermore, because the temporary table +** has no indices, the WHERE clause on the outer query cannot be +** optimized. +** +** This routine attempts to rewrite queries such as the above into +** a single flat select, like this: +** +** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5 +** +** The code generated for this simpification gives the same result +** but only has to scan the data once. And because indices might +** exist on the table t1, a complete scan of the data might be +** avoided. +** +** Flattening is only attempted if all of the following are true: +** +** (1) The subquery and the outer query do not both use aggregates. +** +** (2) The subquery is not an aggregate or the outer query is not a join. +** +** (3) The subquery is not the right operand of a left outer join, or +** the subquery is not itself a join. (Ticket #306) +** +** (4) The subquery is not DISTINCT or the outer query is not a join. +** +** (5) The subquery is not DISTINCT or the outer query does not use +** aggregates. +** +** (6) The subquery does not use aggregates or the outer query is not +** DISTINCT. +** +** (7) The subquery has a FROM clause. +** +** (8) The subquery does not use LIMIT or the outer query is not a join. +** +** (9) The subquery does not use LIMIT or the outer query does not use +** aggregates. +** +** (10) The subquery does not use aggregates or the outer query does not +** use LIMIT. +** +** (11) The subquery and the outer query do not both have ORDER BY clauses. +** +** (12) The subquery is not the right term of a LEFT OUTER JOIN or the +** subquery has no WHERE clause. (added by ticket #350) +** +** In this routine, the "p" parameter is a pointer to the outer query. +** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query +** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. +** +** If flattening is not attempted, this routine is a no-op and returns 0. +** If flattening is attempted this routine returns 1. +** +** All of the expression analysis must occur on both the outer query and +** the subquery before this routine runs. +*/ +static int flattenSubquery( + Parse *pParse, /* The parsing context */ + Select *p, /* The parent or outer SELECT statement */ + int iFrom, /* Index in p->pSrc->a[] of the inner subquery */ + int isAgg, /* True if outer SELECT uses aggregate functions */ + int subqueryIsAgg /* True if the subquery uses aggregate functions */ +){ + Select *pSub; /* The inner query or "subquery" */ + SrcList *pSrc; /* The FROM clause of the outer query */ + SrcList *pSubSrc; /* The FROM clause of the subquery */ + ExprList *pList; /* The result set of the outer query */ + int iParent; /* VDBE cursor number of the pSub result set temp table */ + int i; + Expr *pWhere; + + /* Check to see if flattening is permitted. Return 0 if not. + */ + if( p==0 ) return 0; + pSrc = p->pSrc; + assert( pSrc && iFrom>=0 && iFromnSrc ); + pSub = pSrc->a[iFrom].pSelect; + assert( pSub!=0 ); + if( isAgg && subqueryIsAgg ) return 0; + if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; + pSubSrc = pSub->pSrc; + assert( pSubSrc ); + if( pSubSrc->nSrc==0 ) return 0; + if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){ + return 0; + } + if( (p->isDistinct || p->nLimit>=0) && subqueryIsAgg ) return 0; + if( p->pOrderBy && pSub->pOrderBy ) return 0; + + /* Restriction 3: If the subquery is a join, make sure the subquery is + ** not used as the right operand of an outer join. Examples of why this + ** is not allowed: + ** + ** t1 LEFT OUTER JOIN (t2 JOIN t3) + ** + ** If we flatten the above, we would get + ** + ** (t1 LEFT OUTER JOIN t2) JOIN t3 + ** + ** which is not at all the same thing. + */ + if( pSubSrc->nSrc>1 && iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 ){ + return 0; + } + + /* Restriction 12: If the subquery is the right operand of a left outer + ** join, make sure the subquery has no WHERE clause. + ** An examples of why this is not allowed: + ** + ** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0) + ** + ** If we flatten the above, we would get + ** + ** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0 + ** + ** But the t2.x>0 test will always fail on a NULL row of t2, which + ** effectively converts the OUTER JOIN into an INNER JOIN. + */ + if( iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 + && pSub->pWhere!=0 ){ + return 0; + } + + /* If we reach this point, it means flattening is permitted for the + ** iFrom-th entry of the FROM clause in the outer query. + */ + + /* Move all of the FROM elements of the subquery into the + ** the FROM clause of the outer query. Before doing this, remember + ** the cursor number for the original outer query FROM element in + ** iParent. The iParent cursor will never be used. Subsequent code + ** will scan expressions looking for iParent references and replace + ** those references with expressions that resolve to the subquery FROM + ** elements we are now copying in. + */ + iParent = pSrc->a[iFrom].iCursor; + { + int nSubSrc = pSubSrc->nSrc; + int jointype = pSrc->a[iFrom].jointype; + + if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){ + sqliteDeleteTable(0, pSrc->a[iFrom].pTab); + } + sqliteFree(pSrc->a[iFrom].zDatabase); + sqliteFree(pSrc->a[iFrom].zName); + sqliteFree(pSrc->a[iFrom].zAlias); + if( nSubSrc>1 ){ + int extra = nSubSrc - 1; + for(i=1; ipSrc = pSrc; + for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){ + pSrc->a[i] = pSrc->a[i-extra]; + } + } + for(i=0; ia[i+iFrom] = pSubSrc->a[i]; + memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); + } + pSrc->a[iFrom+nSubSrc-1].jointype = jointype; + } + + /* Now begin substituting subquery result set expressions for + ** references to the iParent in the outer query. + ** + ** Example: + ** + ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; + ** \ \_____________ subquery __________/ / + ** \_____________________ outer query ______________________________/ + ** + ** We look at every expression in the outer query and every place we see + ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". + */ + substExprList(p->pEList, iParent, pSub->pEList); + pList = p->pEList; + for(i=0; inExpr; i++){ + Expr *pExpr; + if( pList->a[i].zName==0 && (pExpr = pList->a[i].pExpr)->span.z!=0 ){ + pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n); + } + } + if( isAgg ){ + substExprList(p->pGroupBy, iParent, pSub->pEList); + substExpr(p->pHaving, iParent, pSub->pEList); + } + if( pSub->pOrderBy ){ + assert( p->pOrderBy==0 ); + p->pOrderBy = pSub->pOrderBy; + pSub->pOrderBy = 0; + }else if( p->pOrderBy ){ + substExprList(p->pOrderBy, iParent, pSub->pEList); + } + if( pSub->pWhere ){ + pWhere = sqliteExprDup(pSub->pWhere); + }else{ + pWhere = 0; + } + if( subqueryIsAgg ){ + assert( p->pHaving==0 ); + p->pHaving = p->pWhere; + p->pWhere = pWhere; + substExpr(p->pHaving, iParent, pSub->pEList); + if( pSub->pHaving ){ + Expr *pHaving = sqliteExprDup(pSub->pHaving); + if( p->pHaving ){ + p->pHaving = sqliteExpr(TK_AND, p->pHaving, pHaving, 0); + }else{ + p->pHaving = pHaving; + } + } + assert( p->pGroupBy==0 ); + p->pGroupBy = sqliteExprListDup(pSub->pGroupBy); + }else if( p->pWhere==0 ){ + p->pWhere = pWhere; + }else{ + substExpr(p->pWhere, iParent, pSub->pEList); + if( pWhere ){ + p->pWhere = sqliteExpr(TK_AND, p->pWhere, pWhere, 0); + } + } + + /* The flattened query is distinct if either the inner or the + ** outer query is distinct. + */ + p->isDistinct = p->isDistinct || pSub->isDistinct; + + /* Transfer the limit expression from the subquery to the outer + ** query. + */ + if( pSub->nLimit>=0 ){ + if( p->nLimit<0 ){ + p->nLimit = pSub->nLimit; + }else if( p->nLimit+p->nOffset > pSub->nLimit+pSub->nOffset ){ + p->nLimit = pSub->nLimit + pSub->nOffset - p->nOffset; + } + } + p->nOffset += pSub->nOffset; + + /* Finially, delete what is left of the subquery and return + ** success. + */ + sqliteSelectDelete(pSub); + return 1; +} + +/* +** Analyze the SELECT statement passed in as an argument to see if it +** is a simple min() or max() query. If it is and this query can be +** satisfied using a single seek to the beginning or end of an index, +** then generate the code for this SELECT and return 1. If this is not a +** simple min() or max() query, then return 0; +** +** A simply min() or max() query looks like this: +** +** SELECT min(a) FROM table; +** SELECT max(a) FROM table; +** +** The query may have only a single table in its FROM argument. There +** can be no GROUP BY or HAVING or WHERE clauses. The result set must +** be the min() or max() of a single column of the table. The column +** in the min() or max() function must be indexed. +** +** The parameters to this routine are the same as for sqliteSelect(). +** See the header comment on that routine for additional information. +*/ +static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ + Expr *pExpr; + int iCol; + Table *pTab; + Index *pIdx; + int base; + Vdbe *v; + int seekOp; + int cont; + ExprList *pEList, *pList, eList; + struct ExprList_item eListItem; + SrcList *pSrc; + + + /* Check to see if this query is a simple min() or max() query. Return + ** zero if it is not. + */ + if( p->pGroupBy || p->pHaving || p->pWhere ) return 0; + pSrc = p->pSrc; + if( pSrc->nSrc!=1 ) return 0; + pEList = p->pEList; + if( pEList->nExpr!=1 ) return 0; + pExpr = pEList->a[0].pExpr; + if( pExpr->op!=TK_AGG_FUNCTION ) return 0; + pList = pExpr->pList; + if( pList==0 || pList->nExpr!=1 ) return 0; + if( pExpr->token.n!=3 ) return 0; + if( sqliteStrNICmp(pExpr->token.z,"min",3)==0 ){ + seekOp = OP_Rewind; + }else if( sqliteStrNICmp(pExpr->token.z,"max",3)==0 ){ + seekOp = OP_Last; + }else{ + return 0; + } + pExpr = pList->a[0].pExpr; + if( pExpr->op!=TK_COLUMN ) return 0; + iCol = pExpr->iColumn; + pTab = pSrc->a[0].pTab; + + /* If we get to here, it means the query is of the correct form. + ** Check to make sure we have an index and make pIdx point to the + ** appropriate index. If the min() or max() is on an INTEGER PRIMARY + ** key column, no index is necessary so set pIdx to NULL. If no + ** usable index is found, return 0. + */ + if( iCol<0 ){ + pIdx = 0; + }else{ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + assert( pIdx->nColumn>=1 ); + if( pIdx->aiColumn[0]==iCol ) break; + } + if( pIdx==0 ) return 0; + } + + /* Identify column types if we will be using the callback. This + ** step is skipped if the output is going to a table or a memory cell. + ** The column names have already been generated in the calling function. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) return 0; + if( eDest==SRT_Callback ){ + generateColumnTypes(pParse, p->pSrc, p->pEList); + } + + /* If the output is destined for a temporary table, open that table. + */ + if( eDest==SRT_TempTable ){ + sqliteVdbeAddOp(v, OP_OpenTemp, iParm, 0); + } + + /* Generating code to find the min or the max. Basically all we have + ** to do is find the first or the last entry in the chosen index. If + ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first + ** or last entry in the main table. + */ + sqliteCodeVerifySchema(pParse, pTab->iDb); + base = pSrc->a[0].iCursor; + computeLimitRegisters(pParse, p); + if( pSrc->a[0].pSelect==0 ){ + sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0); + sqliteVdbeOp3(v, OP_OpenRead, base, pTab->tnum, pTab->zName, 0); + } + cont = sqliteVdbeMakeLabel(v); + if( pIdx==0 ){ + sqliteVdbeAddOp(v, seekOp, base, 0); + }else{ + sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0); + sqliteVdbeOp3(v, OP_OpenRead, base+1, pIdx->tnum, pIdx->zName, P3_STATIC); + if( seekOp==OP_Rewind ){ + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_MakeKey, 1, 0); + sqliteVdbeAddOp(v, OP_IncrKey, 0, 0); + seekOp = OP_MoveTo; + } + sqliteVdbeAddOp(v, seekOp, base+1, 0); + sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0); + sqliteVdbeAddOp(v, OP_Close, base+1, 0); + sqliteVdbeAddOp(v, OP_MoveTo, base, 0); + } + eList.nExpr = 1; + memset(&eListItem, 0, sizeof(eListItem)); + eList.a = &eListItem; + eList.a[0].pExpr = pExpr; + selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, cont, cont); + sqliteVdbeResolveLabel(v, cont); + sqliteVdbeAddOp(v, OP_Close, base, 0); + + return 1; +} + +/* +** Generate code for the given SELECT statement. +** +** The results are distributed in various ways depending on the +** value of eDest and iParm. +** +** eDest Value Result +** ------------ ------------------------------------------- +** SRT_Callback Invoke the callback for each row of the result. +** +** SRT_Mem Store first result in memory cell iParm +** +** SRT_Set Store results as keys of a table with cursor iParm +** +** SRT_Union Store results as a key in a temporary table iParm +** +** SRT_Except Remove results from the temporary table iParm. +** +** SRT_Table Store results in temporary table iParm +** +** The table above is incomplete. Additional eDist value have be added +** since this comment was written. See the selectInnerLoop() function for +** a complete listing of the allowed values of eDest and their meanings. +** +** This routine returns the number of errors. If any errors are +** encountered, then an appropriate error message is left in +** pParse->zErrMsg. +** +** This routine does NOT free the Select structure passed in. The +** calling function needs to do that. +** +** The pParent, parentTab, and *pParentAgg fields are filled in if this +** SELECT is a subquery. This routine may try to combine this SELECT +** with its parent to form a single flat query. In so doing, it might +** change the parent query from a non-aggregate to an aggregate query. +** For that reason, the pParentAgg flag is passed as a pointer, so it +** can be changed. +** +** Example 1: The meaning of the pParent parameter. +** +** SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3; +** \ \_______ subquery _______/ / +** \ / +** \____________________ outer query ___________________/ +** +** This routine is called for the outer query first. For that call, +** pParent will be NULL. During the processing of the outer query, this +** routine is called recursively to handle the subquery. For the recursive +** call, pParent will point to the outer query. Because the subquery is +** the second element in a three-way join, the parentTab parameter will +** be 1 (the 2nd value of a 0-indexed array.) +*/ +int sqliteSelect( + Parse *pParse, /* The parser context */ + Select *p, /* The SELECT statement being coded. */ + int eDest, /* How to dispose of the results */ + int iParm, /* A parameter used by the eDest disposal method */ + Select *pParent, /* Another SELECT for which this is a sub-query */ + int parentTab, /* Index in pParent->pSrc of this query */ + int *pParentAgg /* True if pParent uses aggregate functions */ +){ + int i; + WhereInfo *pWInfo; + Vdbe *v; + int isAgg = 0; /* True for select lists like "count(*)" */ + ExprList *pEList; /* List of columns to extract. */ + SrcList *pTabList; /* List of tables to select from */ + Expr *pWhere; /* The WHERE clause. May be NULL */ + ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */ + ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */ + Expr *pHaving; /* The HAVING clause. May be NULL */ + int isDistinct; /* True if the DISTINCT keyword is present */ + int distinct; /* Table to use for the distinct set */ + int rc = 1; /* Value to return from this function */ + + if( sqlite_malloc_failed || pParse->nErr || p==0 ) return 1; + if( sqliteAuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; + + /* If there is are a sequence of queries, do the earlier ones first. + */ + if( p->pPrior ){ + return multiSelect(pParse, p, eDest, iParm); + } + + /* Make local copies of the parameters for this query. + */ + pTabList = p->pSrc; + pWhere = p->pWhere; + pOrderBy = p->pOrderBy; + pGroupBy = p->pGroupBy; + pHaving = p->pHaving; + isDistinct = p->isDistinct; + + /* Allocate VDBE cursors for each table in the FROM clause + */ + sqliteSrcListAssignCursors(pParse, pTabList); + + /* + ** Do not even attempt to generate any code if we have already seen + ** errors before this routine starts. + */ + if( pParse->nErr>0 ) goto select_end; + + /* Expand any "*" terms in the result set. (For example the "*" in + ** "SELECT * FROM t1") The fillInColumnlist() routine also does some + ** other housekeeping - see the header comment for details. + */ + if( fillInColumnList(pParse, p) ){ + goto select_end; + } + pWhere = p->pWhere; + pEList = p->pEList; + if( pEList==0 ) goto select_end; + + /* If writing to memory or generating a set + ** only a single column may be output. + */ + if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){ + sqliteErrorMsg(pParse, "only a single result allowed for " + "a SELECT that is part of an expression"); + goto select_end; + } + + /* ORDER BY is ignored for some destinations. + */ + switch( eDest ){ + case SRT_Union: + case SRT_Except: + case SRT_Discard: + pOrderBy = 0; + break; + default: + break; + } + + /* At this point, we should have allocated all the cursors that we + ** need to handle subquerys and temporary tables. + ** + ** Resolve the column names and do a semantics check on all the expressions. + */ + for(i=0; inExpr; i++){ + if( sqliteExprResolveIds(pParse, pTabList, 0, pEList->a[i].pExpr) ){ + goto select_end; + } + if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){ + goto select_end; + } + } + if( pWhere ){ + if( sqliteExprResolveIds(pParse, pTabList, pEList, pWhere) ){ + goto select_end; + } + if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ + goto select_end; + } + } + if( pHaving ){ + if( pGroupBy==0 ){ + sqliteErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); + goto select_end; + } + if( sqliteExprResolveIds(pParse, pTabList, pEList, pHaving) ){ + goto select_end; + } + if( sqliteExprCheck(pParse, pHaving, 1, &isAgg) ){ + goto select_end; + } + } + if( pOrderBy ){ + for(i=0; inExpr; i++){ + int iCol; + Expr *pE = pOrderBy->a[i].pExpr; + if( sqliteExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){ + sqliteExprDelete(pE); + pE = pOrderBy->a[i].pExpr = sqliteExprDup(pEList->a[iCol-1].pExpr); + } + if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){ + goto select_end; + } + if( sqliteExprCheck(pParse, pE, isAgg, 0) ){ + goto select_end; + } + if( sqliteExprIsConstant(pE) ){ + if( sqliteExprIsInteger(pE, &iCol)==0 ){ + sqliteErrorMsg(pParse, + "ORDER BY terms must not be non-integer constants"); + goto select_end; + }else if( iCol<=0 || iCol>pEList->nExpr ){ + sqliteErrorMsg(pParse, + "ORDER BY column number %d out of range - should be " + "between 1 and %d", iCol, pEList->nExpr); + goto select_end; + } + } + } + } + if( pGroupBy ){ + for(i=0; inExpr; i++){ + int iCol; + Expr *pE = pGroupBy->a[i].pExpr; + if( sqliteExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){ + sqliteExprDelete(pE); + pE = pGroupBy->a[i].pExpr = sqliteExprDup(pEList->a[iCol-1].pExpr); + } + if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){ + goto select_end; + } + if( sqliteExprCheck(pParse, pE, isAgg, 0) ){ + goto select_end; + } + if( sqliteExprIsConstant(pE) ){ + if( sqliteExprIsInteger(pE, &iCol)==0 ){ + sqliteErrorMsg(pParse, + "GROUP BY terms must not be non-integer constants"); + goto select_end; + }else if( iCol<=0 || iCol>pEList->nExpr ){ + sqliteErrorMsg(pParse, + "GROUP BY column number %d out of range - should be " + "between 1 and %d", iCol, pEList->nExpr); + goto select_end; + } + } + } + } + + /* Begin generating code. + */ + v = sqliteGetVdbe(pParse); + if( v==0 ) goto select_end; + + /* Identify column names if we will be using them in a callback. This + ** step is skipped if the output is going to some other destination. + */ + if( eDest==SRT_Callback ){ + generateColumnNames(pParse, pTabList, pEList); + } + + /* Generate code for all sub-queries in the FROM clause + */ + for(i=0; inSrc; i++){ + const char *zSavedAuthContext; + int needRestoreContext; + + if( pTabList->a[i].pSelect==0 ) continue; + if( pTabList->a[i].zName!=0 ){ + zSavedAuthContext = pParse->zAuthContext; + pParse->zAuthContext = pTabList->a[i].zName; + needRestoreContext = 1; + }else{ + needRestoreContext = 0; + } + sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, + pTabList->a[i].iCursor, p, i, &isAgg); + if( needRestoreContext ){ + pParse->zAuthContext = zSavedAuthContext; + } + pTabList = p->pSrc; + pWhere = p->pWhere; + if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){ + pOrderBy = p->pOrderBy; + } + pGroupBy = p->pGroupBy; + pHaving = p->pHaving; + isDistinct = p->isDistinct; + } + + /* Check for the special case of a min() or max() function by itself + ** in the result set. + */ + if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){ + rc = 0; + goto select_end; + } + + /* Check to see if this is a subquery that can be "flattened" into its parent. + ** If flattening is a possiblity, do so and return immediately. + */ + if( pParent && pParentAgg && + flattenSubquery(pParse, pParent, parentTab, *pParentAgg, isAgg) ){ + if( isAgg ) *pParentAgg = 1; + return rc; + } + + /* Set the limiter. + */ + computeLimitRegisters(pParse, p); + + /* Identify column types if we will be using a callback. This + ** step is skipped if the output is going to a destination other + ** than a callback. + ** + ** We have to do this separately from the creation of column names + ** above because if the pTabList contains views then they will not + ** have been resolved and we will not know the column types until + ** now. + */ + if( eDest==SRT_Callback ){ + generateColumnTypes(pParse, pTabList, pEList); + } + + /* If the output is destined for a temporary table, open that table. + */ + if( eDest==SRT_TempTable ){ + sqliteVdbeAddOp(v, OP_OpenTemp, iParm, 0); + } + + /* Do an analysis of aggregate expressions. + */ + sqliteAggregateInfoReset(pParse); + if( isAgg || pGroupBy ){ + assert( pParse->nAgg==0 ); + isAgg = 1; + for(i=0; inExpr; i++){ + if( sqliteExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){ + goto select_end; + } + } + if( pGroupBy ){ + for(i=0; inExpr; i++){ + if( sqliteExprAnalyzeAggregates(pParse, pGroupBy->a[i].pExpr) ){ + goto select_end; + } + } + } + if( pHaving && sqliteExprAnalyzeAggregates(pParse, pHaving) ){ + goto select_end; + } + if( pOrderBy ){ + for(i=0; inExpr; i++){ + if( sqliteExprAnalyzeAggregates(pParse, pOrderBy->a[i].pExpr) ){ + goto select_end; + } + } + } + } + + /* Reset the aggregator + */ + if( isAgg ){ + sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg); + for(i=0; inAgg; i++){ + FuncDef *pFunc; + if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){ + sqliteVdbeOp3(v, OP_AggInit, 0, i, (char*)pFunc, P3_POINTER); + } + } + if( pGroupBy==0 ){ + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_AggFocus, 0, 0); + } + } + + /* Initialize the memory cell to NULL + */ + if( eDest==SRT_Mem ){ + sqliteVdbeAddOp(v, OP_String, 0, 0); + sqliteVdbeAddOp(v, OP_MemStore, iParm, 1); + } + + /* Open a temporary table to use for the distinct set. + */ + if( isDistinct ){ + distinct = pParse->nTab++; + sqliteVdbeAddOp(v, OP_OpenTemp, distinct, 1); + }else{ + distinct = -1; + } + + /* Begin the database scan + */ + pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0, + pGroupBy ? 0 : &pOrderBy); + if( pWInfo==0 ) goto select_end; + + /* Use the standard inner loop if we are not dealing with + ** aggregates + */ + if( !isAgg ){ + if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, + iParm, pWInfo->iContinue, pWInfo->iBreak) ){ + goto select_end; + } + } + + /* If we are dealing with aggregates, then do the special aggregate + ** processing. + */ + else{ + AggExpr *pAgg; + if( pGroupBy ){ + int lbl1; + for(i=0; inExpr; i++){ + sqliteExprCode(pParse, pGroupBy->a[i].pExpr); + } + sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0); + if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pGroupBy); + lbl1 = sqliteVdbeMakeLabel(v); + sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1); + for(i=0, pAgg=pParse->aAgg; inAgg; i++, pAgg++){ + if( pAgg->isAgg ) continue; + sqliteExprCode(pParse, pAgg->pExpr); + sqliteVdbeAddOp(v, OP_AggSet, 0, i); + } + sqliteVdbeResolveLabel(v, lbl1); + } + for(i=0, pAgg=pParse->aAgg; inAgg; i++, pAgg++){ + Expr *pE; + int nExpr; + FuncDef *pDef; + if( !pAgg->isAgg ) continue; + assert( pAgg->pFunc!=0 ); + assert( pAgg->pFunc->xStep!=0 ); + pDef = pAgg->pFunc; + pE = pAgg->pExpr; + assert( pE!=0 ); + assert( pE->op==TK_AGG_FUNCTION ); + nExpr = sqliteExprCodeExprList(pParse, pE->pList, pDef->includeTypes); + sqliteVdbeAddOp(v, OP_Integer, i, 0); + sqliteVdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_POINTER); + } + } + + /* End the database scan loop. + */ + sqliteWhereEnd(pWInfo); + + /* If we are processing aggregates, we need to set up a second loop + ** over all of the aggregate values and process them. + */ + if( isAgg ){ + int endagg = sqliteVdbeMakeLabel(v); + int startagg; + startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg); + pParse->useAgg = 1; + if( pHaving ){ + sqliteExprIfFalse(pParse, pHaving, startagg, 1); + } + if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest, + iParm, startagg, endagg) ){ + goto select_end; + } + sqliteVdbeAddOp(v, OP_Goto, 0, startagg); + sqliteVdbeResolveLabel(v, endagg); + sqliteVdbeAddOp(v, OP_Noop, 0, 0); + pParse->useAgg = 0; + } + + /* If there is an ORDER BY clause, then we need to sort the results + ** and send them to the callback one by one. + */ + if( pOrderBy ){ + generateSortTail(p, v, pEList->nExpr, eDest, iParm); + } + + /* If this was a subquery, we have now converted the subquery into a + ** temporary table. So delete the subquery structure from the parent + ** to prevent this subquery from being evaluated again and to force the + ** the use of the temporary table. + */ + if( pParent ){ + assert( pParent->pSrc->nSrc>parentTab ); + assert( pParent->pSrc->a[parentTab].pSelect==p ); + sqliteSelectDelete(p); + pParent->pSrc->a[parentTab].pSelect = 0; + } + + /* The SELECT was successfully coded. Set the return code to 0 + ** to indicate no errors. + */ + rc = 0; + + /* Control jumps to here if an error is encountered above, or upon + ** successful coding of the SELECT. + */ +select_end: + sqliteAggregateInfoReset(pParse); + return rc; +} diff --git a/src/libs/sqlite2/shell.c b/src/libs/sqlite2/shell.c new file mode 100644 index 00000000..89898ab4 --- /dev/null +++ b/src/libs/sqlite2/shell.c @@ -0,0 +1,1354 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement the "sqlite" command line +** utility for accessing SQLite databases. +** +** $Id: shell.c 875429 2008-10-24 12:20:41Z cgilles $ +*/ +#include +#include +#include +#include "sqlite.h" +#include + +#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) +# include +# include +# include +# include +#endif + +#ifdef __MACOS__ +# include +# include +# include +# include +# include +# include +#endif + +#if defined(HAVE_READLINE) && HAVE_READLINE==1 +# include +# include +#else +# define readline(p) local_getline(p,stdin) +# define add_history(X) +# define read_history(X) +# define write_history(X) +# define stifle_history(X) +#endif + +/* Make sure isatty() has a prototype. +*/ +extern int isatty(); + +/* +** The following is the open SQLite database. We make a pointer +** to this database a static variable so that it can be accessed +** by the SIGINT handler to interrupt database processing. +*/ +static sqlite *db = 0; + +/* +** True if an interrupt (Control-C) has been received. +*/ +static int seenInterrupt = 0; + +/* +** This is the name of our program. It is set in main(), used +** in a number of other places, mostly for error messages. +*/ +static char *Argv0; + +/* +** Prompt strings. Initialized in main. Settable with +** .prompt main continue +*/ +static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ +static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ + + +/* +** Determines if a string is a number of not. +*/ +extern int sqliteIsNumber(const char*); + +/* +** This routine reads a line of text from standard input, stores +** the text in memory obtained from malloc() and returns a pointer +** to the text. NULL is returned at end of file, or if malloc() +** fails. +** +** The interface is like "readline" but no command-line editing +** is done. +*/ +static char *local_getline(char *zPrompt, FILE *in){ + char *zLine; + int nLine; + int n; + int eol; + + if( zPrompt && *zPrompt ){ + printf("%s",zPrompt); + fflush(stdout); + } + nLine = 100; + zLine = malloc( nLine ); + if( zLine==0 ) return 0; + n = 0; + eol = 0; + while( !eol ){ + if( n+100>nLine ){ + nLine = nLine*2 + 100; + zLine = realloc(zLine, nLine); + if( zLine==0 ) return 0; + } + if( fgets(&zLine[n], nLine - n, in)==0 ){ + if( n==0 ){ + free(zLine); + return 0; + } + zLine[n] = 0; + eol = 1; + break; + } + while( zLine[n] ){ n++; } + if( n>0 && zLine[n-1]=='\n' ){ + n--; + zLine[n] = 0; + eol = 1; + } + } + zLine = realloc( zLine, n+1 ); + return zLine; +} + +/* +** Retrieve a single line of input text. "isatty" is true if text +** is coming from a terminal. In that case, we issue a prompt and +** attempt to use "readline" for command-line editing. If "isatty" +** is false, use "local_getline" instead of "readline" and issue no prompt. +** +** zPrior is a string of prior text retrieved. If not the empty +** string, then issue a continuation prompt. +*/ +static char *one_input_line(const char *zPrior, FILE *in){ + char *zPrompt; + char *zResult; + if( in!=0 ){ + return local_getline(0, in); + } + if( zPrior && zPrior[0] ){ + zPrompt = continuePrompt; + }else{ + zPrompt = mainPrompt; + } + zResult = readline(zPrompt); + if( zResult ) add_history(zResult); + return zResult; +} + +struct previous_mode_data { + int valid; /* Is there legit data in here? */ + int mode; + int showHeader; + int colWidth[100]; +}; +/* +** An pointer to an instance of this structure is passed from +** the main program to the callback. This is used to communicate +** state and mode information. +*/ +struct callback_data { + sqlite *db; /* The database */ + int echoOn; /* True to echo input commands */ + int cnt; /* Number of records displayed so far */ + FILE *out; /* Write results here */ + int mode; /* An output mode setting */ + int showHeader; /* True to show column names in List or Column mode */ + char *zDestTable; /* Name of destination table when MODE_Insert */ + char separator[20]; /* Separator character for MODE_List */ + int colWidth[100]; /* Requested width of each column when in column mode*/ + int actualWidth[100]; /* Actual width of each column */ + char nullvalue[20]; /* The text to print when a NULL comes back from + ** the database */ + struct previous_mode_data explainPrev; + /* Holds the mode information just before + ** .explain ON */ + char outfile[FILENAME_MAX]; /* Filename for *out */ + const char *zDbFilename; /* name of the database file */ + char *zKey; /* Encryption key */ +}; + +/* +** These are the allowed modes. +*/ +#define MODE_Line 0 /* One column per line. Blank line between records */ +#define MODE_Column 1 /* One record per line in neat columns */ +#define MODE_List 2 /* One record per line with a separator */ +#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ +#define MODE_Html 4 /* Generate an XHTML table */ +#define MODE_Insert 5 /* Generate SQL "insert" statements */ +#define MODE_NUM_OF 6 /* The number of modes (not a mode itself) */ + +char *modeDescr[MODE_NUM_OF] = { + "line", + "column", + "list", + "semi", + "html", + "insert" +}; + +/* +** Number of elements in an array +*/ +#define ArraySize(X) (sizeof(X)/sizeof(X[0])) + +/* +** Output the given string as a quoted string using SQL quoting conventions. +*/ +static void output_quoted_string(FILE *out, const char *z){ + int i; + int nSingle = 0; + for(i=0; z[i]; i++){ + if( z[i]=='\'' ) nSingle++; + } + if( nSingle==0 ){ + fprintf(out,"'%s'",z); + }else{ + fprintf(out,"'"); + while( *z ){ + for(i=0; z[i] && z[i]!='\''; i++){} + if( i==0 ){ + fprintf(out,"''"); + z++; + }else if( z[i]=='\'' ){ + fprintf(out,"%.*s''",i,z); + z += i+1; + }else{ + fprintf(out,"%s",z); + break; + } + } + fprintf(out,"'"); + } +} + +/* +** Output the given string with characters that are special to +** HTML escaped. +*/ +static void output_html_string(FILE *out, const char *z){ + int i; + while( *z ){ + for(i=0; z[i] && z[i]!='<' && z[i]!='&'; i++){} + if( i>0 ){ + fprintf(out,"%.*s",i,z); + } + if( z[i]=='<' ){ + fprintf(out,"<"); + }else if( z[i]=='&' ){ + fprintf(out,"&"); + }else{ + break; + } + z += i + 1; + } +} + +/* +** This routine runs when the user presses Ctrl-C +*/ +static void interrupt_handler(int NotUsed){ + seenInterrupt = 1; + if( db ) sqlite_interrupt(db); +} + +/* +** This is the callback routine that the SQLite library +** invokes for each row of a query result. +*/ +static int callback(void *pArg, int nArg, char **azArg, char **azCol){ + int i; + struct callback_data *p = (struct callback_data*)pArg; + switch( p->mode ){ + case MODE_Line: { + int w = 5; + if( azArg==0 ) break; + for(i=0; iw ) w = len; + } + if( p->cnt++>0 ) fprintf(p->out,"\n"); + for(i=0; iout,"%*s = %s\n", w, azCol[i], + azArg[i] ? azArg[i] : p->nullvalue); + } + break; + } + case MODE_Column: { + if( p->cnt++==0 ){ + for(i=0; icolWidth) ){ + w = p->colWidth[i]; + }else{ + w = 0; + } + if( w<=0 ){ + w = strlen(azCol[i] ? azCol[i] : ""); + if( w<10 ) w = 10; + n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue); + if( wactualWidth) ){ + p->actualWidth[i] = w; + } + if( p->showHeader ){ + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + } + } + if( p->showHeader ){ + for(i=0; iactualWidth) ){ + w = p->actualWidth[i]; + }else{ + w = 10; + } + fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" + "----------------------------------------------------------", + i==nArg-1 ? "\n": " "); + } + } + } + if( azArg==0 ) break; + for(i=0; iactualWidth) ){ + w = p->actualWidth[i]; + }else{ + w = 10; + } + fprintf(p->out,"%-*.*s%s",w,w, + azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + } + break; + } + case MODE_Semi: + case MODE_List: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); + } + } + if( azArg==0 ) break; + for(i=0; inullvalue; + fprintf(p->out, "%s", z); + if( iout, "%s", p->separator); + }else if( p->mode==MODE_Semi ){ + fprintf(p->out, ";\n"); + }else{ + fprintf(p->out, "\n"); + } + } + break; + } + case MODE_Html: { + if( p->cnt++==0 && p->showHeader ){ + fprintf(p->out,"
      "); + for(i=0; iout,"",azCol[i]); + } + fprintf(p->out,"\n"); + } + if( azArg==0 ) break; + fprintf(p->out,""); + for(i=0; iout,"\n"); + } + fprintf(p->out,"\n"); + break; + } + case MODE_Insert: { + if( azArg==0 ) break; + fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable); + for(i=0; i0 ? ",": ""; + if( azArg[i]==0 ){ + fprintf(p->out,"%sNULL",zSep); + }else if( sqliteIsNumber(azArg[i]) ){ + fprintf(p->out,"%s%s",zSep, azArg[i]); + }else{ + if( zSep[0] ) fprintf(p->out,"%s",zSep); + output_quoted_string(p->out, azArg[i]); + } + } + fprintf(p->out,");\n"); + break; + } + } + return 0; +} + +/* +** Set the destination table field of the callback_data structure to +** the name of the table given. Escape any quote characters in the +** table name. +*/ +static void set_table_name(struct callback_data *p, const char *zName){ + int i, n; + int needQuote; + char *z; + + if( p->zDestTable ){ + free(p->zDestTable); + p->zDestTable = 0; + } + if( zName==0 ) return; + needQuote = !isalpha(*zName) && *zName!='_'; + for(i=n=0; zName[i]; i++, n++){ + if( !isalnum(zName[i]) && zName[i]!='_' ){ + needQuote = 1; + if( zName[i]=='\'' ) n++; + } + } + if( needQuote ) n += 2; + z = p->zDestTable = malloc( n+1 ); + if( z==0 ){ + fprintf(stderr,"Out of memory!\n"); + exit(1); + } + n = 0; + if( needQuote ) z[n++] = '\''; + for(i=0; zName[i]; i++){ + z[n++] = zName[i]; + if( zName[i]=='\'' ) z[n++] = '\''; + } + if( needQuote ) z[n++] = '\''; + z[n] = 0; +} + +/* +** This is a different callback routine used for dumping the database. +** Each row received by this callback consists of a table name, +** the table type ("index" or "table") and SQL to create the table. +** This routine should print text sufficient to recreate the table. +*/ +static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ + struct callback_data *p = (struct callback_data *)pArg; + if( nArg!=3 ) return 1; + fprintf(p->out, "%s;\n", azArg[2]); + if( strcmp(azArg[1],"table")==0 ){ + struct callback_data d2; + d2 = *p; + d2.mode = MODE_Insert; + d2.zDestTable = 0; + set_table_name(&d2, azArg[0]); + sqlite_exec_printf(p->db, + "SELECT * FROM '%q'", + callback, &d2, 0, azArg[0] + ); + set_table_name(&d2, 0); + } + return 0; +} + +/* +** Text of a help message +*/ +static char zHelp[] = + ".databases List names and files of attached databases\n" + ".dump ?TABLE? ... Dump the database in a text format\n" + ".echo ON|OFF Turn command echo on or off\n" + ".exit Exit this program\n" + ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n" + ".header(s) ON|OFF Turn display of headers on or off\n" + ".help Show this message\n" + ".indices TABLE Show names of all indices on TABLE\n" + ".mode MODE Set mode to one of \"line(s)\", \"column(s)\", \n" + " \"insert\", \"list\", or \"html\"\n" + ".mode insert TABLE Generate SQL insert statements for TABLE\n" + ".nullvalue STRING Print STRING instead of nothing for NULL data\n" + ".output FILENAME Send output to FILENAME\n" + ".output stdout Send output to the screen\n" + ".prompt MAIN CONTINUE Replace the standard prompts\n" + ".quit Exit this program\n" + ".read FILENAME Execute SQL in FILENAME\n" +#ifdef SQLITE_HAS_CODEC + ".rekey OLD NEW NEW Change the encryption key\n" +#endif + ".schema ?TABLE? Show the CREATE statements\n" + ".separator STRING Change separator string for \"list\" mode\n" + ".show Show the current values for various settings\n" + ".tables ?PATTERN? List names of tables matching a pattern\n" + ".timeout MS Try opening locked tables for MS milliseconds\n" + ".width NUM NUM ... Set column widths for \"column\" mode\n" +; + +/* Forward reference */ +static void process_input(struct callback_data *p, FILE *in); + +/* +** Make sure the database is open. If it is not, then open it. If +** the database fails to open, print an error message and exit. +*/ +static void open_db(struct callback_data *p){ + if( p->db==0 ){ + char *zErrMsg = 0; +#ifdef SQLITE_HAS_CODEC + int n = p->zKey ? strlen(p->zKey) : 0; + db = p->db = sqlite_open_encrypted(p->zDbFilename, p->zKey, n, 0, &zErrMsg); +#else + db = p->db = sqlite_open(p->zDbFilename, 0, &zErrMsg); +#endif + if( p->db==0 ){ + if( zErrMsg ){ + fprintf(stderr,"Unable to open database \"%s\": %s\n", + p->zDbFilename, zErrMsg); + }else{ + fprintf(stderr,"Unable to open database %s\n", p->zDbFilename); + } + exit(1); + } + } +} + +/* +** If an input line begins with "." then invoke this routine to +** process that line. +** +** Return 1 to exit and 0 to continue. +*/ +static int do_meta_command(char *zLine, struct callback_data *p){ + int i = 1; + int nArg = 0; + int n, c; + int rc = 0; + char *azArg[50]; + + /* Parse the input line into tokens. + */ + while( zLine[i] && nArg1 && strncmp(azArg[0], "databases", n)==0 ){ + struct callback_data data; + char *zErrMsg = 0; + open_db(p); + memcpy(&data, p, sizeof(data)); + data.showHeader = 1; + data.mode = MODE_Column; + data.colWidth[0] = 3; + data.colWidth[1] = 15; + data.colWidth[2] = 58; + sqlite_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + } + }else + + if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ + char *zErrMsg = 0; + open_db(p); + fprintf(p->out, "BEGIN TRANSACTION;\n"); + if( nArg==1 ){ + sqlite_exec(p->db, + "SELECT name, type, sql FROM sqlite_master " + "WHERE type!='meta' AND sql NOT NULL " + "ORDER BY substr(type,2,1), rowid", + dump_callback, p, &zErrMsg + ); + }else{ + int i; + for(i=1; idb, + "SELECT name, type, sql FROM sqlite_master " + "WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOT NULL " + "ORDER BY substr(type,2,1), rowid", + dump_callback, p, &zErrMsg, azArg[i] + ); + } + } + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + }else{ + fprintf(p->out, "COMMIT;\n"); + } + }else + + if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ + int j; + char *z = azArg[1]; + int val = atoi(azArg[1]); + for(j=0; z[j]; j++){ + if( isupper(z[j]) ) z[j] = tolower(z[j]); + } + if( strcmp(z,"on")==0 ){ + val = 1; + }else if( strcmp(z,"yes")==0 ){ + val = 1; + } + p->echoOn = val; + }else + + if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ + rc = 1; + }else + + if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ + int j; + char *z = nArg>=2 ? azArg[1] : "1"; + int val = atoi(z); + for(j=0; z[j]; j++){ + if( isupper(z[j]) ) z[j] = tolower(z[j]); + } + if( strcmp(z,"on")==0 ){ + val = 1; + }else if( strcmp(z,"yes")==0 ){ + val = 1; + } + if(val == 1) { + if(!p->explainPrev.valid) { + p->explainPrev.valid = 1; + p->explainPrev.mode = p->mode; + p->explainPrev.showHeader = p->showHeader; + memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth)); + } + /* We could put this code under the !p->explainValid + ** condition so that it does not execute if we are already in + ** explain mode. However, always executing it allows us an easy + ** was to reset to explain mode in case the user previously + ** did an .explain followed by a .width, .mode or .header + ** command. + */ + p->mode = MODE_Column; + p->showHeader = 1; + memset(p->colWidth,0,ArraySize(p->colWidth)); + p->colWidth[0] = 4; + p->colWidth[1] = 12; + p->colWidth[2] = 10; + p->colWidth[3] = 10; + p->colWidth[4] = 35; + }else if (p->explainPrev.valid) { + p->explainPrev.valid = 0; + p->mode = p->explainPrev.mode; + p->showHeader = p->explainPrev.showHeader; + memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); + } + }else + + if( c=='h' && (strncmp(azArg[0], "header", n)==0 + || + strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ + int j; + char *z = azArg[1]; + int val = atoi(azArg[1]); + for(j=0; z[j]; j++){ + if( isupper(z[j]) ) z[j] = tolower(z[j]); + } + if( strcmp(z,"on")==0 ){ + val = 1; + }else if( strcmp(z,"yes")==0 ){ + val = 1; + } + p->showHeader = val; + }else + + if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ + fprintf(stderr, "%s", zHelp); + }else + + if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){ + struct callback_data data; + char *zErrMsg = 0; + open_db(p); + memcpy(&data, p, sizeof(data)); + data.showHeader = 0; + data.mode = MODE_List; + sqlite_exec_printf(p->db, + "SELECT name FROM sqlite_master " + "WHERE type='index' AND tbl_name LIKE '%q' " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type='index' AND tbl_name LIKE '%q' " + "ORDER BY 1", + callback, &data, &zErrMsg, azArg[1], azArg[1] + ); + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + } + }else + + if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){ + int n2 = strlen(azArg[1]); + if( strncmp(azArg[1],"line",n2)==0 + || + strncmp(azArg[1],"lines",n2)==0 ){ + p->mode = MODE_Line; + }else if( strncmp(azArg[1],"column",n2)==0 + || + strncmp(azArg[1],"columns",n2)==0 ){ + p->mode = MODE_Column; + }else if( strncmp(azArg[1],"list",n2)==0 ){ + p->mode = MODE_List; + }else if( strncmp(azArg[1],"html",n2)==0 ){ + p->mode = MODE_Html; + }else if( strncmp(azArg[1],"insert",n2)==0 ){ + p->mode = MODE_Insert; + if( nArg>=3 ){ + set_table_name(p, azArg[2]); + }else{ + set_table_name(p, "table"); + } + }else { + fprintf(stderr,"mode should be on of: column html insert line list\n"); + } + }else + + if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) { + sprintf(p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); + }else + + if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ + if( p->out!=stdout ){ + fclose(p->out); + } + if( strcmp(azArg[1],"stdout")==0 ){ + p->out = stdout; + strcpy(p->outfile,"stdout"); + }else{ + p->out = fopen(azArg[1], "wb"); + if( p->out==0 ){ + fprintf(stderr,"can't write to \"%s\"\n", azArg[1]); + p->out = stdout; + } else { + strcpy(p->outfile,azArg[1]); + } + } + }else + + if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){ + if( nArg >= 2) { + strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); + } + if( nArg >= 3) { + strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); + } + }else + + if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ + rc = 1; + }else + + if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){ + FILE *alt = fopen(azArg[1], "rb"); + if( alt==0 ){ + fprintf(stderr,"can't open \"%s\"\n", azArg[1]); + }else{ + process_input(p, alt); + fclose(alt); + } + }else + +#ifdef SQLITE_HAS_CODEC + if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){ + char *zOld = p->zKey; + if( zOld==0 ) zOld = ""; + if( strcmp(azArg[1],zOld) ){ + fprintf(stderr,"old key is incorrect\n"); + }else if( strcmp(azArg[2], azArg[3]) ){ + fprintf(stderr,"2nd copy of new key does not match the 1st\n"); + }else{ + sqlite_freemem(p->zKey); + p->zKey = sqlite_mprintf("%s", azArg[2]); + sqlite_rekey(p->db, p->zKey, strlen(p->zKey)); + } + }else +#endif + + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ + struct callback_data data; + char *zErrMsg = 0; + open_db(p); + memcpy(&data, p, sizeof(data)); + data.showHeader = 0; + data.mode = MODE_Semi; + if( nArg>1 ){ + extern int sqliteStrICmp(const char*,const char*); + if( sqliteStrICmp(azArg[1],"sqlite_master")==0 ){ + char *new_argv[2], *new_colv[2]; + new_argv[0] = "CREATE TABLE sqlite_master (\n" + " type text,\n" + " name text,\n" + " tbl_name text,\n" + " rootpage integer,\n" + " sql text\n" + ")"; + new_argv[1] = 0; + new_colv[0] = "sql"; + new_colv[1] = 0; + callback(&data, 1, new_argv, new_colv); + }else if( sqliteStrICmp(azArg[1],"sqlite_temp_master")==0 ){ + char *new_argv[2], *new_colv[2]; + new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n" + " type text,\n" + " name text,\n" + " tbl_name text,\n" + " rootpage integer,\n" + " sql text\n" + ")"; + new_argv[1] = 0; + new_colv[0] = "sql"; + new_colv[1] = 0; + callback(&data, 1, new_argv, new_colv); + }else{ + sqlite_exec_printf(p->db, + "SELECT sql FROM " + " (SELECT * FROM sqlite_master UNION ALL" + " SELECT * FROM sqlite_temp_master) " + "WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOTNULL " + "ORDER BY substr(type,2,1), name", + callback, &data, &zErrMsg, azArg[1]); + } + }else{ + sqlite_exec(p->db, + "SELECT sql FROM " + " (SELECT * FROM sqlite_master UNION ALL" + " SELECT * FROM sqlite_temp_master) " + "WHERE type!='meta' AND sql NOTNULL " + "ORDER BY substr(type,2,1), name", + callback, &data, &zErrMsg + ); + } + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + } + }else + + if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){ + sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]); + }else + + if( c=='s' && strncmp(azArg[0], "show", n)==0){ + int i; + fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); + fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off"); + fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); + fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); + fprintf(p->out,"%9.9s: %s\n","nullvalue", p->nullvalue); + fprintf(p->out,"%9.9s: %s\n","output", + strlen(p->outfile) ? p->outfile : "stdout"); + fprintf(p->out,"%9.9s: %s\n","separator", p->separator); + fprintf(p->out,"%9.9s: ","width"); + for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { + fprintf(p->out,"%d ",p->colWidth[i]); + } + fprintf(p->out,"\n\n"); + }else + + if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){ + char **azResult; + int nRow, rc; + char *zErrMsg; + open_db(p); + if( nArg==1 ){ + rc = sqlite_get_table(p->db, + "SELECT name FROM sqlite_master " + "WHERE type IN ('table','view') " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type IN ('table','view') " + "ORDER BY 1", + &azResult, &nRow, 0, &zErrMsg + ); + }else{ + rc = sqlite_get_table_printf(p->db, + "SELECT name FROM sqlite_master " + "WHERE type IN ('table','view') AND name LIKE '%%%q%%' " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type IN ('table','view') AND name LIKE '%%%q%%' " + "ORDER BY 1", + &azResult, &nRow, 0, &zErrMsg, azArg[1], azArg[1] + ); + } + if( zErrMsg ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + } + if( rc==SQLITE_OK ){ + int len, maxlen = 0; + int i, j; + int nPrintCol, nPrintRow; + for(i=1; i<=nRow; i++){ + if( azResult[i]==0 ) continue; + len = strlen(azResult[i]); + if( len>maxlen ) maxlen = len; + } + nPrintCol = 80/(maxlen+2); + if( nPrintCol<1 ) nPrintCol = 1; + nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; + for(i=0; i1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){ + open_db(p); + sqlite_busy_timeout(p->db, atoi(azArg[1])); + }else + + if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ + int j; + for(j=1; jcolWidth); j++){ + p->colWidth[j-1] = atoi(azArg[j]); + } + }else + + { + fprintf(stderr, "unknown command or invalid arguments: " + " \"%s\". Enter \".help\" for help\n", azArg[0]); + } + + return rc; +} + +/* +** Return TRUE if the last non-whitespace character in z[] is a semicolon. +** z[] is N characters long. +*/ +static int _ends_with_semicolon(const char *z, int N){ + while( N>0 && isspace(z[N-1]) ){ N--; } + return N>0 && z[N-1]==';'; +} + +/* +** Test to see if a line consists entirely of whitespace. +*/ +static int _all_whitespace(const char *z){ + for(; *z; z++){ + if( isspace(*z) ) continue; + if( *z=='/' && z[1]=='*' ){ + z += 2; + while( *z && (*z!='*' || z[1]!='/') ){ z++; } + if( *z==0 ) return 0; + z++; + continue; + } + if( *z=='-' && z[1]=='-' ){ + z += 2; + while( *z && *z!='\n' ){ z++; } + if( *z==0 ) return 1; + continue; + } + return 0; + } + return 1; +} + +/* +** Return TRUE if the line typed in is an SQL command terminator other +** than a semi-colon. The SQL Server style "go" command is understood +** as is the Oracle "/". +*/ +static int _is_command_terminator(const char *zLine){ + extern int sqliteStrNICmp(const char*,const char*,int); + while( isspace(*zLine) ){ zLine++; }; + if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */ + if( sqliteStrNICmp(zLine,"go",2)==0 && _all_whitespace(&zLine[2]) ){ + return 1; /* SQL Server */ + } + return 0; +} + +/* +** Read input from *in and process it. If *in==0 then input +** is interactive - the user is typing it it. Otherwise, input +** is coming from a file or device. A prompt is issued and history +** is saved only if input is interactive. An interrupt signal will +** cause this routine to exit immediately, unless input is interactive. +*/ +static void process_input(struct callback_data *p, FILE *in){ + char *zLine; + char *zSql = 0; + int nSql = 0; + char *zErrMsg; + int rc; + while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){ + if( seenInterrupt ){ + if( in!=0 ) break; + seenInterrupt = 0; + } + if( p->echoOn ) printf("%s\n", zLine); + if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; + if( zLine && zLine[0]=='.' && nSql==0 ){ + int rc = do_meta_command(zLine, p); + free(zLine); + if( rc ) break; + continue; + } + if( _is_command_terminator(zLine) ){ + strcpy(zLine,";"); + } + if( zSql==0 ){ + int i; + for(i=0; zLine[i] && isspace(zLine[i]); i++){} + if( zLine[i]!=0 ){ + nSql = strlen(zLine); + zSql = malloc( nSql+1 ); + strcpy(zSql, zLine); + } + }else{ + int len = strlen(zLine); + zSql = realloc( zSql, nSql + len + 2 ); + if( zSql==0 ){ + fprintf(stderr,"%s: out of memory!\n", Argv0); + exit(1); + } + strcpy(&zSql[nSql++], "\n"); + strcpy(&zSql[nSql], zLine); + nSql += len; + } + free(zLine); + if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite_complete(zSql) ){ + p->cnt = 0; + open_db(p); + rc = sqlite_exec(p->db, zSql, callback, p, &zErrMsg); + if( rc || zErrMsg ){ + if( in!=0 && !p->echoOn ) printf("%s\n",zSql); + if( zErrMsg!=0 ){ + printf("SQL error: %s\n", zErrMsg); + sqlite_freemem(zErrMsg); + zErrMsg = 0; + }else{ + printf("SQL error: %s\n", sqlite_error_string(rc)); + } + } + free(zSql); + zSql = 0; + nSql = 0; + } + } + if( zSql ){ + if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql); + free(zSql); + } +} + +/* +** Return a pathname which is the user's home directory. A +** 0 return indicates an error of some kind. Space to hold the +** resulting string is obtained from malloc(). The calling +** function should free the result. +*/ +static char *find_home_dir(void){ + char *home_dir = NULL; + +#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__) + struct passwd *pwent; + uid_t uid = getuid(); + if( (pwent=getpwuid(uid)) != NULL) { + home_dir = pwent->pw_dir; + } +#endif + +#ifdef __MACOS__ + char home_path[_MAX_PATH+1]; + home_dir = getcwd(home_path, _MAX_PATH); +#endif + + if (!home_dir) { + home_dir = getenv("HOME"); + if (!home_dir) { + home_dir = getenv("HOMEPATH"); /* Windows? */ + } + } + +#if defined(_WIN32) || defined(WIN32) + if (!home_dir) { + home_dir = "c:"; + } +#endif + + if( home_dir ){ + char *z = malloc( strlen(home_dir)+1 ); + if( z ) strcpy(z, home_dir); + home_dir = z; + } + + return home_dir; +} + +/* +** Read input from the file given by sqliterc_override. Or if that +** parameter is NULL, take input from ~/.sqliterc +*/ +static void process_sqliterc( + struct callback_data *p, /* Configuration data */ + const char *sqliterc_override /* Name of config file. NULL to use default */ +){ + char *home_dir = NULL; + const char *sqliterc = sqliterc_override; + char *zBuf; + FILE *in = NULL; + + if (sqliterc == NULL) { + home_dir = find_home_dir(); + if( home_dir==0 ){ + fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0); + return; + } + zBuf = malloc(strlen(home_dir) + 15); + if( zBuf==0 ){ + fprintf(stderr,"%s: out of memory!\n", Argv0); + exit(1); + } + sprintf(zBuf,"%s/.sqliterc",home_dir); + free(home_dir); + sqliterc = (const char*)zBuf; + } + in = fopen(sqliterc,"rb"); + if( in ){ + if( isatty(fileno(stdout)) ){ + printf("Loading resources from %s\n",sqliterc); + } + process_input(p,in); + fclose(in); + } + return; +} + +/* +** Show available command line options +*/ +static const char zOptions[] = + " -init filename read/process named file\n" + " -echo print commands before execution\n" + " -[no]header turn headers on or off\n" + " -column set output mode to 'column'\n" + " -html set output mode to HTML\n" +#ifdef SQLITE_HAS_CODEC + " -key KEY encryption key\n" +#endif + " -line set output mode to 'line'\n" + " -list set output mode to 'list'\n" + " -separator 'x' set output field separator (|)\n" + " -nullvalue 'text' set text string for NULL values\n" + " -version show SQLite version\n" + " -help show this text, also show dot-commands\n" +; +static void usage(int showDetail){ + fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n", Argv0); + if( showDetail ){ + fprintf(stderr, "Options are:\n%s", zOptions); + }else{ + fprintf(stderr, "Use the -help option for additional information\n"); + } + exit(1); +} + +/* +** Initialize the state information in data +*/ +void main_init(struct callback_data *data) { + memset(data, 0, sizeof(*data)); + data->mode = MODE_List; + strcpy(data->separator,"|"); + data->showHeader = 0; + strcpy(mainPrompt,"sqlite> "); + strcpy(continuePrompt," ...> "); +} + +int main(int argc, char **argv){ + char *zErrMsg = 0; + struct callback_data data; + const char *zInitFile = 0; + char *zFirstCmd = 0; + int i; + extern int sqliteOsFileExists(const char*); + +#ifdef __MACOS__ + argc = ccommand(&argv); +#endif + + Argv0 = argv[0]; + main_init(&data); + + /* Make sure we have a valid signal handler early, before anything + ** else is done. + */ +#ifdef SIGINT + signal(SIGINT, interrupt_handler); +#endif + + /* Do an initial pass through the command-line argument to locate + ** the name of the database file, the name of the initialization file, + ** and the first command to execute. + */ + for(i=1; i /* Needed for the definition of va_list */ + +/* +** Make sure we can call this stuff from C++. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +/* +** The version of the SQLite library. +*/ +#define SQLITE_VERSION "2.8.14" + +/* +** The version string is also compiled into the library so that a program +** can check to make sure that the lib*.a file and the *.h file are from +** the same version. +*/ +extern const char sqlite_version[]; + +/* +** The SQLITE_UTF8 macro is defined if the library expects to see +** UTF-8 encoded data. The SQLITE_ISO8859 macro is defined if the +** iso8859 encoded should be used. +*/ +/* #define SQLITE_ISO8859 1 */ + +/* DigiKam customizations */ +#define SQLITE_UTF8 1 +#define THREADSAFE 1 + +/* +** The following constant holds one of two strings, "UTF-8" or "iso8859", +** depending on which character encoding the SQLite library expects to +** see. The character encoding makes a difference for the LIKE and GLOB +** operators and for the LENGTH() and SUBSTR() functions. +*/ +extern const char sqlite_encoding[]; + +/* +** Each open sqlite database is represented by an instance of the +** following opaque structure. +*/ +typedef struct sqlite sqlite; + +/* +** A function to open a new sqlite database. +** +** If the database does not exist and mode indicates write +** permission, then a new database is created. If the database +** does not exist and mode does not indicate write permission, +** then the open fails, an error message generated (if errmsg!=0) +** and the function returns 0. +** +** If mode does not indicates user write permission, then the +** database is opened read-only. +** +** The Truth: As currently implemented, all databases are opened +** for writing all the time. Maybe someday we will provide the +** ability to open a database readonly. The mode parameters is +** provided in anticipation of that enhancement. +*/ +sqlite *sqlite_open(const char *filename, int mode, char **errmsg); + +/* +** A function to close the database. +** +** Call this function with a pointer to a structure that was previously +** returned from sqlite_open() and the corresponding database will by closed. +*/ +void sqlite_close(sqlite *); + +/* +** The type for a callback function. +*/ +typedef int (*sqlite_callback)(void*,int,char**, char**); + +/* +** A function to executes one or more statements of SQL. +** +** If one or more of the SQL statements are queries, then +** the callback function specified by the 3rd parameter is +** invoked once for each row of the query result. This callback +** should normally return 0. If the callback returns a non-zero +** value then the query is aborted, all subsequent SQL statements +** are skipped and the sqlite_exec() function returns the SQLITE_ABORT. +** +** The 4th parameter is an arbitrary pointer that is passed +** to the callback function as its first parameter. +** +** The 2nd parameter to the callback function is the number of +** columns in the query result. The 3rd parameter to the callback +** is an array of strings holding the values for each column. +** The 4th parameter to the callback is an array of strings holding +** the names of each column. +** +** The callback function may be NULL, even for queries. A NULL +** callback is not an error. It just means that no callback +** will be invoked. +** +** If an error occurs while parsing or evaluating the SQL (but +** not while executing the callback) then an appropriate error +** message is written into memory obtained from malloc() and +** *errmsg is made to point to that message. The calling function +** is responsible for freeing the memory that holds the error +** message. Use sqlite_freemem() for this. If errmsg==NULL, +** then no error message is ever written. +** +** The return value is is SQLITE_OK if there are no errors and +** some other return code if there is an error. The particular +** return value depends on the type of error. +** +** If the query could not be executed because a database file is +** locked or busy, then this function returns SQLITE_BUSY. (This +** behavior can be modified somewhat using the sqlite_busy_handler() +** and sqlite_busy_timeout() functions below.) +*/ +int sqlite_exec( + sqlite*, /* An open database */ + const char *sql, /* SQL to be executed */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg /* Error msg written here */ +); + +/* +** Return values for sqlite_exec() and sqlite_step() +*/ +#define SQLITE_OK 0 /* Successful result */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Auxiliary database format error */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_ROW 100 /* sqlite_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite_step() has finished executing */ + +/* +** Each entry in an SQLite table has a unique integer key. (The key is +** the value of the INTEGER PRIMARY KEY column if there is such a column, +** otherwise the key is generated at random. The unique key is always +** available as the ROWID, OID, or _ROWID_ column.) The following routine +** returns the integer key of the most recent insert in the database. +** +** This function is similar to the mysql_insert_id() function from MySQL. +*/ +int sqlite_last_insert_rowid(sqlite*); + +/* +** This function returns the number of database rows that were changed +** (or inserted or deleted) by the most recent called sqlite_exec(). +** +** All changes are counted, even if they were later undone by a +** ROLLBACK or ABORT. Except, changes associated with creating and +** dropping tables are not counted. +** +** If a callback invokes sqlite_exec() recursively, then the changes +** in the inner, recursive call are counted together with the changes +** in the outer call. +** +** SQLite implements the command "DELETE FROM table" without a WHERE clause +** by dropping and recreating the table. (This is much faster than going +** through and deleting individual elements form the table.) Because of +** this optimization, the change count for "DELETE FROM table" will be +** zero regardless of the number of elements that were originally in the +** table. To get an accurate count of the number of rows deleted, use +** "DELETE FROM table WHERE 1" instead. +*/ +int sqlite_changes(sqlite*); + +/* +** This function returns the number of database rows that were changed +** by the last INSERT, UPDATE, or DELETE statement executed by sqlite_exec(), +** or by the last VM to run to completion. The change count is not updated +** by SQL statements other than INSERT, UPDATE or DELETE. +** +** Changes are counted, even if they are later undone by a ROLLBACK or +** ABORT. Changes associated with trigger programs that execute as a +** result of the INSERT, UPDATE, or DELETE statement are not counted. +** +** If a callback invokes sqlite_exec() recursively, then the changes +** in the inner, recursive call are counted together with the changes +** in the outer call. +** +** SQLite implements the command "DELETE FROM table" without a WHERE clause +** by dropping and recreating the table. (This is much faster than going +** through and deleting individual elements form the table.) Because of +** this optimization, the change count for "DELETE FROM table" will be +** zero regardless of the number of elements that were originally in the +** table. To get an accurate count of the number of rows deleted, use +** "DELETE FROM table WHERE 1" instead. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +int sqlite_last_statement_changes(sqlite*); + +/* If the parameter to this routine is one of the return value constants +** defined above, then this routine returns a constant text string which +** describes (in English) the meaning of the return value. +*/ +const char *sqlite_error_string(int); +#define sqliteErrStr sqlite_error_string /* Legacy. Do not use in new code. */ + +/* This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user action such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +*/ +void sqlite_interrupt(sqlite*); + + +/* This function returns true if the given input string comprises +** one or more complete SQL statements. +** +** The algorithm is simple. If the last token other than spaces +** and comments is a semicolon, then return true. otherwise return +** false. +*/ +int sqlite_complete(const char *sql); + +/* +** This routine identifies a callback function that is invoked +** whenever an attempt is made to open a database table that is +** currently locked by another process or thread. If the busy callback +** is NULL, then sqlite_exec() returns SQLITE_BUSY immediately if +** it finds a locked table. If the busy callback is not NULL, then +** sqlite_exec() invokes the callback with three arguments. The +** second argument is the name of the locked table and the third +** argument is the number of times the table has been busy. If the +** busy callback returns 0, then sqlite_exec() immediately returns +** SQLITE_BUSY. If the callback returns non-zero, then sqlite_exec() +** tries to open the table again and the cycle repeats. +** +** The default busy callback is NULL. +** +** Sqlite is re-entrant, so the busy handler may start a new query. +** (It is not clear why anyone would every want to do this, but it +** is allowed, in theory.) But the busy handler may not close the +** database. Closing the database from a busy handler will delete +** data structures out from under the executing query and will +** probably result in a coredump. +*/ +void sqlite_busy_handler(sqlite*, int(*)(void*,const char*,int), void*); + +/* +** This routine sets a busy handler that sleeps for a while when a +** table is locked. The handler will sleep multiple times until +** at least "ms" milliseconds of sleeping have been done. After +** "ms" milliseconds of sleeping, the handler returns 0 which +** causes sqlite_exec() to return SQLITE_BUSY. +** +** Calling this routine with an argument less than or equal to zero +** turns off all busy handlers. +*/ +void sqlite_busy_timeout(sqlite*, int ms); + +/* +** This next routine is really just a wrapper around sqlite_exec(). +** Instead of invoking a user-supplied callback for each row of the +** result, this routine remembers each row of the result in memory +** obtained from malloc(), then returns all of the result after the +** query has finished. +** +** As an example, suppose the query result where this table: +** +** Name | Age +** ----------------------- +** Alice | 43 +** Bob | 28 +** Cindy | 21 +** +** If the 3rd argument were &azResult then after the function returns +** azResult will contain the following data: +** +** azResult[0] = "Name"; +** azResult[1] = "Age"; +** azResult[2] = "Alice"; +** azResult[3] = "43"; +** azResult[4] = "Bob"; +** azResult[5] = "28"; +** azResult[6] = "Cindy"; +** azResult[7] = "21"; +** +** Notice that there is an extra row of data containing the column +** headers. But the *nrow return value is still 3. *ncolumn is +** set to 2. In general, the number of values inserted into azResult +** will be ((*nrow) + 1)*(*ncolumn). +** +** After the calling function has finished using the result, it should +** pass the result data pointer to sqlite_free_table() in order to +** release the memory that was malloc-ed. Because of the way the +** malloc() happens, the calling function must not try to call +** malloc() directly. Only sqlite_free_table() is able to release +** the memory properly and safely. +** +** The return value of this routine is the same as from sqlite_exec(). +*/ +int sqlite_get_table( + sqlite*, /* An open database */ + const char *sql, /* SQL to be executed */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg /* Error msg written here */ +); + +/* +** Call this routine to free the memory that sqlite_get_table() allocated. +*/ +void sqlite_free_table(char **result); + +/* +** The following routines are wrappers around sqlite_exec() and +** sqlite_get_table(). The only difference between the routines that +** follow and the originals is that the second argument to the +** routines that follow is really a printf()-style format +** string describing the SQL to be executed. Arguments to the format +** string appear at the end of the argument list. +** +** All of the usual printf formatting options apply. In addition, there +** is a "%q" option. %q works like %s in that it substitutes a null-terminated +** string from the argument list. But %q also doubles every '\'' character. +** %q is designed for use inside a string literal. By doubling each '\'' +** character it escapes that character and allows it to be inserted into +** the string. +** +** For example, so some string variable contains text as follows: +** +** char *zText = "It's a happy day!"; +** +** We can use this text in an SQL statement as follows: +** +** sqlite_exec_printf(db, "INSERT INTO table VALUES('%q')", +** callback1, 0, 0, zText); +** +** Because the %q format string is used, the '\'' character in zText +** is escaped and the SQL generated is as follows: +** +** INSERT INTO table1 VALUES('It''s a happy day!') +** +** This is correct. Had we used %s instead of %q, the generated SQL +** would have looked like this: +** +** INSERT INTO table1 VALUES('It's a happy day!'); +** +** This second example is an SQL syntax error. As a general rule you +** should always use %q instead of %s when inserting text into a string +** literal. +*/ +int sqlite_exec_printf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string. */ +); +int sqlite_exec_vprintf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + sqlite_callback, /* Callback function */ + void *, /* 1st argument to callback function */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string. */ +); +int sqlite_get_table_printf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + ... /* Arguments to the format string */ +); +int sqlite_get_table_vprintf( + sqlite*, /* An open database */ + const char *sqlFormat, /* printf-style format string for the SQL */ + char ***resultp, /* Result written to a char *[] that this points to */ + int *nrow, /* Number of result rows written here */ + int *ncolumn, /* Number of result columns written here */ + char **errmsg, /* Error msg written here */ + va_list ap /* Arguments to the format string */ +); +char *sqlite_mprintf(const char*,...); +char *sqlite_vmprintf(const char*, va_list); + +/* +** Windows systems should call this routine to free memory that +** is returned in the in the errmsg parameter of sqlite_open() when +** SQLite is a DLL. For some reason, it does not work to call free() +** directly. +*/ +void sqlite_freemem(void *p); + +/* +** Windows systems need functions to call to return the sqlite_version +** and sqlite_encoding strings. +*/ +const char *sqlite_libversion(void); +const char *sqlite_libencoding(void); + +/* +** A pointer to the following structure is used to communicate with +** the implementations of user-defined functions. +*/ +typedef struct sqlite_func sqlite_func; + +/* +** Use the following routines to create new user-defined functions. See +** the documentation for details. +*/ +int sqlite_create_function( + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the new function */ + int nArg, /* Number of arguments. -1 means any number */ + void (*xFunc)(sqlite_func*,int,const char**), /* C code to implement */ + void *pUserData /* Available via the sqlite_user_data() call */ +); +int sqlite_create_aggregate( + sqlite*, /* Database where the new function is registered */ + const char *zName, /* Name of the function */ + int nArg, /* Number of arguments */ + void (*xStep)(sqlite_func*,int,const char**), /* Called for each row */ + void (*xFinalize)(sqlite_func*), /* Called once to get final result */ + void *pUserData /* Available via the sqlite_user_data() call */ +); + +/* +** Use the following routine to define the datatype returned by a +** user-defined function. The second argument can be one of the +** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it +** can be an integer greater than or equal to zero. When the datatype +** parameter is non-negative, the type of the result will be the +** same as the datatype-th argument. If datatype==SQLITE_NUMERIC +** then the result is always numeric. If datatype==SQLITE_TEXT then +** the result is always text. If datatype==SQLITE_ARGS then the result +** is numeric if any argument is numeric and is text otherwise. +*/ +int sqlite_function_type( + sqlite *db, /* The database there the function is registered */ + const char *zName, /* Name of the function */ + int datatype /* The datatype for this function */ +); +#define SQLITE_NUMERIC (-1) +#define SQLITE_TEXT (-2) +#define SQLITE_ARGS (-3) + +/* +** The user function implementations call one of the following four routines +** in order to return their results. The first parameter to each of these +** routines is a copy of the first argument to xFunc() or xFinialize(). +** The second parameter to these routines is the result to be returned. +** A NULL can be passed as the second parameter to sqlite_set_result_string() +** in order to return a NULL result. +** +** The 3rd argument to _string and _error is the number of characters to +** take from the string. If this argument is negative, then all characters +** up to and including the first '\000' are used. +** +** The sqlite_set_result_string() function allocates a buffer to hold the +** result and returns a pointer to this buffer. The calling routine +** (that is, the implementation of a user function) can alter the content +** of this buffer if desired. +*/ +char *sqlite_set_result_string(sqlite_func*,const char*,int); +void sqlite_set_result_int(sqlite_func*,int); +void sqlite_set_result_double(sqlite_func*,double); +void sqlite_set_result_error(sqlite_func*,const char*,int); + +/* +** The pUserData parameter to the sqlite_create_function() and +** sqlite_create_aggregate() routines used to register user functions +** is available to the implementation of the function using this +** call. +*/ +void *sqlite_user_data(sqlite_func*); + +/* +** Aggregate functions use the following routine to allocate +** a structure for storing their state. The first time this routine +** is called for a particular aggregate, a new structure of size nBytes +** is allocated, zeroed, and returned. On subsequent calls (for the +** same aggregate instance) the same buffer is returned. The implementation +** of the aggregate can use the returned buffer to accumulate data. +** +** The buffer allocated is freed automatically be SQLite. +*/ +void *sqlite_aggregate_context(sqlite_func*, int nBytes); + +/* +** The next routine returns the number of calls to xStep for a particular +** aggregate function instance. The current call to xStep counts so this +** routine always returns at least 1. +*/ +int sqlite_aggregate_count(sqlite_func*); + +/* +** This routine registers a callback with the SQLite library. The +** callback is invoked (at compile-time, not at run-time) for each +** attempt to access a column of a table in the database. The callback +** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire +** SQL statement should be aborted with an error and SQLITE_IGNORE +** if the column should be treated as a NULL value. +*/ +int sqlite_set_authorizer( + sqlite*, + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), + void *pUserData +); + +/* +** The second parameter to the access authorization function above will +** be one of the values below. These values signify what kind of operation +** is to be authorized. The 3rd and 4th parameters to the authorization +** function will be parameters or NULL depending on which of the following +** codes is used as the second parameter. The 5th parameter is the name +** of the database ("main", "temp", etc.) if applicable. The 6th parameter +** is the name of the inner-most trigger or view that is responsible for +** the access attempt or NULL if this access attempt is directly from +** input SQL code. +** +** Arg-3 Arg-4 +*/ +#define SQLITE_COPY 0 /* Table Name File Name */ +#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ +#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ +#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ +#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ +#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ +#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ +#define SQLITE_CREATE_VIEW 8 /* View Name NULL */ +#define SQLITE_DELETE 9 /* Table Name NULL */ +#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ +#define SQLITE_DROP_TABLE 11 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ +#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ +#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ +#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ +#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ +#define SQLITE_DROP_VIEW 17 /* View Name NULL */ +#define SQLITE_INSERT 18 /* Table Name NULL */ +#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ +#define SQLITE_READ 20 /* Table Name Column Name */ +#define SQLITE_SELECT 21 /* NULL NULL */ +#define SQLITE_TRANSACTION 22 /* NULL NULL */ +#define SQLITE_UPDATE 23 /* Table Name Column Name */ +#define SQLITE_ATTACH 24 /* Filename NULL */ +#define SQLITE_DETACH 25 /* Database Name NULL */ + + +/* +** The return value of the authorization function should be one of the +** following constants: +*/ +/* #define SQLITE_OK 0 // Allow access (This is actually defined above) */ +#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ +#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ + +/* +** Register a function that is called at every invocation of sqlite_exec() +** or sqlite_compile(). This function can be used (for example) to generate +** a log file of all SQL executed against a database. +*/ +void *sqlite_trace(sqlite*, void(*xTrace)(void*,const char*), void*); + +/*** The Callback-Free API +** +** The following routines implement a new way to access SQLite that does not +** involve the use of callbacks. +** +** An sqlite_vm is an opaque object that represents a single SQL statement +** that is ready to be executed. +*/ +typedef struct sqlite_vm sqlite_vm; + +/* +** To execute an SQLite query without the use of callbacks, you first have +** to compile the SQL using this routine. The 1st parameter "db" is a pointer +** to an sqlite object obtained from sqlite_open(). The 2nd parameter +** "zSql" is the text of the SQL to be compiled. The remaining parameters +** are all outputs. +** +** *pzTail is made to point to the first character past the end of the first +** SQL statement in zSql. This routine only compiles the first statement +** in zSql, so *pzTail is left pointing to what remains uncompiled. +** +** *ppVm is left pointing to a "virtual machine" that can be used to execute +** the compiled statement. Or if there is an error, *ppVm may be set to NULL. +** If the input text contained no SQL (if the input is and empty string or +** a comment) then *ppVm is set to NULL. +** +** If any errors are detected during compilation, an error message is written +** into space obtained from malloc() and *pzErrMsg is made to point to that +** error message. The calling routine is responsible for freeing the text +** of this message when it has finished with it. Use sqlite_freemem() to +** free the message. pzErrMsg may be NULL in which case no error message +** will be generated. +** +** On success, SQLITE_OK is returned. Otherwise and error code is returned. +*/ +int sqlite_compile( + sqlite *db, /* The open database */ + const char *zSql, /* SQL statement to be compiled */ + const char **pzTail, /* OUT: uncompiled tail of zSql */ + sqlite_vm **ppVm, /* OUT: the virtual machine to execute zSql */ + char **pzErrmsg /* OUT: Error message. */ +); + +/* +** After an SQL statement has been compiled, it is handed to this routine +** to be executed. This routine executes the statement as far as it can +** go then returns. The return value will be one of SQLITE_DONE, +** SQLITE_ERROR, SQLITE_BUSY, SQLITE_ROW, or SQLITE_MISUSE. +** +** SQLITE_DONE means that the execute of the SQL statement is complete +** an no errors have occurred. sqlite_step() should not be called again +** for the same virtual machine. *pN is set to the number of columns in +** the result set and *pazColName is set to an array of strings that +** describe the column names and datatypes. The name of the i-th column +** is (*pazColName)[i] and the datatype of the i-th column is +** (*pazColName)[i+*pN]. *pazValue is set to NULL. +** +** SQLITE_ERROR means that the virtual machine encountered a run-time +** error. sqlite_step() should not be called again for the same +** virtual machine. *pN is set to 0 and *pazColName and *pazValue are set +** to NULL. Use sqlite_finalize() to obtain the specific error code +** and the error message text for the error. +** +** SQLITE_BUSY means that an attempt to open the database failed because +** another thread or process is holding a lock. The calling routine +** can try again to open the database by calling sqlite_step() again. +** The return code will only be SQLITE_BUSY if no busy handler is registered +** using the sqlite_busy_handler() or sqlite_busy_timeout() routines. If +** a busy handler callback has been registered but returns 0, then this +** routine will return SQLITE_ERROR and sqltie_finalize() will return +** SQLITE_BUSY when it is called. +** +** SQLITE_ROW means that a single row of the result is now available. +** The data is contained in *pazValue. The value of the i-th column is +** (*azValue)[i]. *pN and *pazColName are set as described in SQLITE_DONE. +** Invoke sqlite_step() again to advance to the next row. +** +** SQLITE_MISUSE is returned if sqlite_step() is called incorrectly. +** For example, if you call sqlite_step() after the virtual machine +** has halted (after a prior call to sqlite_step() has returned SQLITE_DONE) +** or if you call sqlite_step() with an incorrectly initialized virtual +** machine or a virtual machine that has been deleted or that is associated +** with an sqlite structure that has been closed. +*/ +int sqlite_step( + sqlite_vm *pVm, /* The virtual machine to execute */ + int *pN, /* OUT: Number of columns in result */ + const char ***pazValue, /* OUT: Column data */ + const char ***pazColName /* OUT: Column names and datatypes */ +); + +/* +** This routine is called to delete a virtual machine after it has finished +** executing. The return value is the result code. SQLITE_OK is returned +** if the statement executed successfully and some other value is returned if +** there was any kind of error. If an error occurred and pzErrMsg is not +** NULL, then an error message is written into memory obtained from malloc() +** and *pzErrMsg is made to point to that error message. The calling routine +** should use sqlite_freemem() to delete this message when it has finished +** with it. +** +** This routine can be called at any point during the execution of the +** virtual machine. If the virtual machine has not completed execution +** when this routine is called, that is like encountering an error or +** an interrupt. (See sqlite_interrupt().) Incomplete updates may be +** rolled back and transactions canceled, depending on the circumstances, +** and the result code returned will be SQLITE_ABORT. +*/ +int sqlite_finalize(sqlite_vm*, char **pzErrMsg); + +/* +** This routine deletes the virtual machine, writes any error message to +** *pzErrMsg and returns an SQLite return code in the same way as the +** sqlite_finalize() function. +** +** Additionally, if ppVm is not NULL, *ppVm is left pointing to a new virtual +** machine loaded with the compiled version of the original query ready for +** execution. +** +** If sqlite_reset() returns SQLITE_SCHEMA, then *ppVm is set to NULL. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +int sqlite_reset(sqlite_vm*, char **pzErrMsg); + +/* +** If the SQL that was handed to sqlite_compile contains variables that +** are represented in the SQL text by a question mark ('?'). This routine +** is used to assign values to those variables. +** +** The first parameter is a virtual machine obtained from sqlite_compile(). +** The 2nd "idx" parameter determines which variable in the SQL statement +** to bind the value to. The left most '?' is 1. The 3rd parameter is +** the value to assign to that variable. The 4th parameter is the number +** of bytes in the value, including the terminating \000 for strings. +** Finally, the 5th "copy" parameter is TRUE if SQLite should make its +** own private copy of this value, or false if the space that the 3rd +** parameter points to will be unchanging and can be used directly by +** SQLite. +** +** Unbound variables are treated as having a value of NULL. To explicitly +** set a variable to NULL, call this routine with the 3rd parameter as a +** NULL pointer. +** +** If the 4th "len" parameter is -1, then strlen() is used to find the +** length. +** +** This routine can only be called immediately after sqlite_compile() +** or sqlite_reset() and before any calls to sqlite_step(). +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +int sqlite_bind(sqlite_vm*, int idx, const char *value, int len, int copy); + +/* +** This routine configures a callback function - the progress callback - that +** is invoked periodically during long running calls to sqlite_exec(), +** sqlite_step() and sqlite_get_table(). An example use for this API is to keep +** a GUI updated during a large query. +** +** The progress callback is invoked once for every N virtual machine opcodes, +** where N is the second argument to this function. The progress callback +** itself is identified by the third argument to this function. The fourth +** argument to this function is a void pointer passed to the progress callback +** function each time it is invoked. +** +** If a call to sqlite_exec(), sqlite_step() or sqlite_get_table() results +** in less than N opcodes being executed, then the progress callback is not +** invoked. +** +** Calling this routine overwrites any previously installed progress callback. +** To remove the progress callback altogether, pass NULL as the third +** argument to this function. +** +** If the progress callback returns a result other than 0, then the current +** query is immediately terminated and any database changes rolled back. If the +** query was part of a larger transaction, then the transaction is not rolled +** back and remains active. The sqlite_exec() call returns SQLITE_ABORT. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +void sqlite_progress_handler(sqlite*, int, int(*)(void*), void*); + +/* +** Register a callback function to be invoked whenever a new transaction +** is committed. The pArg argument is passed through to the callback. +** callback. If the callback function returns non-zero, then the commit +** is converted into a rollback. +** +** If another function was previously registered, its pArg value is returned. +** Otherwise NULL is returned. +** +** Registering a NULL function disables the callback. +** +******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ****** +*/ +void *sqlite_commit_hook(sqlite*, int(*)(void*), void*); + +/* +** Open an encrypted SQLite database. If pKey==0 or nKey==0, this routine +** is the same as sqlite_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +sqlite *sqlite_open_encrypted( + const char *zFilename, /* Name of the encrypted database */ + const void *pKey, /* Pointer to the key */ + int nKey, /* Number of bytes in the key */ + int *pErrcode, /* Write error code here */ + char **pzErrmsg /* Write error message here */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +int sqlite_rekey( + sqlite *db, /* Database to be re-keyed */ + const void *pKey, int nKey /* The new key */ +); + +/* +** Encode a binary buffer "in" of size n bytes so that it contains +** no instances of characters '\'' or '\000'. The output is +** null-terminated and can be used as a string value in an INSERT +** or UPDATE statement. Use sqlite_decode_binary() to convert the +** string back into its original binary. +** +** The result is written into a preallocated output buffer "out". +** "out" must be able to hold at least 2 +(257*n)/254 bytes. +** In other words, the output will be expanded by as much as 3 +** bytes for every 254 bytes of input plus 2 bytes of fixed overhead. +** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.) +** +** The return value is the number of characters in the encoded +** string, excluding the "\000" terminator. +** +** If out==NULL then no output is generated but the routine still returns +** the number of characters that would have been generated if out had +** not been NULL. +*/ +int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out); + +/* +** Decode the string "in" into binary data and write it into "out". +** This routine reverses the encoding created by sqlite_encode_binary(). +** The output will always be a few bytes less than the input. The number +** of bytes of output is returned. If the input is not a well-formed +** encoding, -1 is returned. +** +** The "in" and "out" parameters may point to the same buffer in order +** to decode a string in place. +*/ +int sqlite_decode_binary(const unsigned char *in, unsigned char *out); + +#ifdef __cplusplus +} /* End of the 'extern "C"' block */ +#endif + +#endif /* _SQLITE_H_ */ diff --git a/src/libs/sqlite2/sqliteInt.h b/src/libs/sqlite2/sqliteInt.h new file mode 100644 index 00000000..3c4d818a --- /dev/null +++ b/src/libs/sqlite2/sqliteInt.h @@ -0,0 +1,1274 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Internal interface definitions for SQLite. +** +** @(#) $Id: sqliteInt.h 875675 2008-10-25 07:31:30Z cgilles $ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "sqlite.h" +#include "hash.h" +#include "parse.h" +#include "btree.h" +#include +#include +#include +#include + +/* +** The maximum number of in-memory pages to use for the main database +** table and for temporary tables. +*/ +#define MAX_PAGES 2000 +#define TEMP_PAGES 500 + +/* +** If the following macro is set to 1, then NULL values are considered +** distinct for the SELECT DISTINCT statement and for UNION or EXCEPT +** compound queries. No other SQL database engine (among those tested) +** works this way except for OCELOT. But the SQL92 spec implies that +** this is how things should work. +** +** If the following macro is set to 0, then NULLs are indistinct for +** SELECT DISTINCT and for UNION. +*/ +#define NULL_ALWAYS_DISTINCT 0 + +/* +** If the following macro is set to 1, then NULL values are considered +** distinct when determining whether or not two entries are the same +** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL, +** OCELOT, and Firebird all work. The SQL92 spec explicitly says this +** is the way things are suppose to work. +** +** If the following macro is set to 0, the NULLs are indistinct for +** a UNIQUE index. In this mode, you can only have a single NULL entry +** for a column declared UNIQUE. This is the way Informix and SQL Server +** work. +*/ +#define NULL_DISTINCT_FOR_UNIQUE 1 + +/* +** The maximum number of attached databases. This must be at least 2 +** in order to support the main database file (0) and the file used to +** hold temporary tables (1). And it must be less than 256 because +** an unsigned character is used to stored the database index. +*/ +#define MAX_ATTACHED 10 + +/* +** The next macro is used to determine where TEMP tables and indices +** are stored. Possible values: +** +** 0 Always use a temporary files +** 1 Use a file unless overridden by "PRAGMA temp_store" +** 2 Use memory unless overridden by "PRAGMA temp_store" +** 3 Always use memory +*/ +#ifndef TEMP_STORE +# define TEMP_STORE 1 +#endif + +/* +** When building SQLite for embedded systems where memory is scarce, +** you can define one or more of the following macros to omit extra +** features of the library and thus keep the size of the library to +** a minimum. +*/ +/* #define SQLITE_OMIT_AUTHORIZATION 1 */ +/* #define SQLITE_OMIT_INMEMORYDB 1 */ +/* #define SQLITE_OMIT_VACUUM 1 */ +/* #define SQLITE_OMIT_DATETIME_FUNCS 1 */ +/* #define SQLITE_OMIT_PROGRESS_CALLBACK 1 */ + +/* +** Integers of known sizes. These typedefs might change for architectures +** where the sizes very. Preprocessor macros are available so that the +** types can be conveniently redefined at compile-type. Like this: +** +** cc '-DUINTPTR_TYPE=long long int' ... +*/ +#ifndef UINT32_TYPE +# define UINT32_TYPE unsigned int +#endif +#ifndef UINT16_TYPE +# define UINT16_TYPE unsigned short int +#endif +#ifndef INT16_TYPE +# define INT16_TYPE short int +#endif +#ifndef UINT8_TYPE +# define UINT8_TYPE unsigned char +#endif +#ifndef INT8_TYPE +# define INT8_TYPE signed char +#endif +#ifndef INTPTR_TYPE +# if SQLITE_PTR_SZ==4 +# define INTPTR_TYPE int +# else +# define INTPTR_TYPE long long +# endif +#endif +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +typedef INT16_TYPE i16; /* 2-byte signed integer */ +typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ +typedef UINT8_TYPE i8; /* 1-byte signed integer */ +typedef INTPTR_TYPE ptr; /* Big enough to hold a pointer */ +typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */ + +/* +** Defer sourcing vdbe.h until after the "u8" typedef is defined. +*/ +#include "vdbe.h" + +/* +** Most C compilers these days recognize "long double", don't they? +** Just in case we encounter one that does not, we will create a macro +** for long double so that it can be easily changed to just "double". +*/ +#ifndef LONGDOUBLE_TYPE +# define LONGDOUBLE_TYPE long double +#endif + +/* +** This macro casts a pointer to an integer. Useful for doing +** pointer arithmetic. +*/ +#define Addr(X) ((uptr)X) + +/* +** The maximum number of bytes of data that can be put into a single +** row of a single table. The upper bound on this limit is 16777215 +** bytes (or 16MB-1). We have arbitrarily set the limit to just 1MB +** here because the overflow page chain is inefficient for really big +** records and we want to discourage people from thinking that +** multi-megabyte records are OK. If your needs are different, you can +** change this define and recompile to increase or decrease the record +** size. +** +** The 16777198 is computed as follows: 238 bytes of payload on the +** original pages plus 16448 overflow pages each holding 1020 bytes of +** data. +*/ +#define MAX_BYTES_PER_ROW 1048576 +/* #define MAX_BYTES_PER_ROW 16777198 */ + +/* +** If memory allocation problems are found, recompile with +** +** -DMEMORY_DEBUG=1 +** +** to enable some sanity checking on malloc() and free(). To +** check for memory leaks, recompile with +** +** -DMEMORY_DEBUG=2 +** +** and a line of text will be written to standard error for +** each malloc() and free(). This output can be analyzed +** by an AWK script to determine if there are any leaks. +*/ +#ifdef MEMORY_DEBUG +# define sqliteMalloc(X) sqliteMalloc_(X,1,__FILE__,__LINE__) +# define sqliteMallocRaw(X) sqliteMalloc_(X,0,__FILE__,__LINE__) +# define sqliteFree(X) sqliteFree_(X,__FILE__,__LINE__) +# define sqliteRealloc(X,Y) sqliteRealloc_(X,Y,__FILE__,__LINE__) +# define sqliteStrDup(X) sqliteStrDup_(X,__FILE__,__LINE__) +# define sqliteStrNDup(X,Y) sqliteStrNDup_(X,Y,__FILE__,__LINE__) + void sqliteStrRealloc(char**); +#else +# define sqliteRealloc_(X,Y) sqliteRealloc(X,Y) +# define sqliteStrRealloc(X) +#endif + +/* +** This variable gets set if malloc() ever fails. After it gets set, +** the SQLite library shuts down permanently. +*/ +extern int sqlite_malloc_failed; + +/* +** The following global variables are used for testing and debugging +** only. They only work if MEMORY_DEBUG is defined. +*/ +#ifdef MEMORY_DEBUG +extern int sqlite_nMalloc; /* Number of sqliteMalloc() calls */ +extern int sqlite_nFree; /* Number of sqliteFree() calls */ +extern int sqlite_iMallocFail; /* Fail sqliteMalloc() after this many calls */ +#endif + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" +#define TEMP_MASTER_NAME "sqlite_temp_master" + +/* +** The name of the schema table. +*/ +#define SCHEMA_TABLE(x) (x?TEMP_MASTER_NAME:MASTER_NAME) + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) (sizeof(X)/sizeof(X[0])) + +/* +** Forward references to structures +*/ +typedef struct Column Column; +typedef struct Table Table; +typedef struct Index Index; +typedef struct Instruction Instruction; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct Parse Parse; +typedef struct Token Token; +typedef struct IdList IdList; +typedef struct SrcList SrcList; +typedef struct WhereInfo WhereInfo; +typedef struct WhereLevel WhereLevel; +typedef struct Select Select; +typedef struct AggExpr AggExpr; +typedef struct FuncDef FuncDef; +typedef struct Trigger Trigger; +typedef struct TriggerStep TriggerStep; +typedef struct TriggerStack TriggerStack; +typedef struct FKey FKey; +typedef struct Db Db; +typedef struct AuthContext AuthContext; + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. Additional +** databases may be attached. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + int schema_cookie; /* Database schema version number for this file */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash aFKey; /* Foreign keys indexed by to-table */ + u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ + u16 flags; /* Flags associated with this database */ + void *pAux; /* Auxiliary data. Usually NULL */ + void (*xFreeAux)(void*); /* Routine to free pAux */ +}; + +/* +** These macros can be used to test, set, or clear bits in the +** Db.flags field. +*/ +#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P)) +#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0) +#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P) +#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P) + +/* +** Allowed values for the DB.flags field. +** +** The DB_Locked flag is set when the first OP_Transaction or OP_Checkpoint +** opcode is emitted for a database. This prevents multiple occurances +** of those opcodes for the same database in the same program. Similarly, +** the DB_Cookie flag is set when the OP_VerifyCookie opcode is emitted, +** and prevents duplicate OP_VerifyCookies from taking up space and slowing +** down execution. +** +** The DB_SchemaLoaded flag is set after the database schema has been +** read into internal hash tables. +** +** DB_UnresetViews means that one or more views have column names that +** have been filled out. If the schema changes, these column names might +** changes and so the view will need to be reset. +*/ +#define DB_Locked 0x0001 /* OP_Transaction opcode has been emitted */ +#define DB_Cookie 0x0002 /* OP_VerifyCookie opcode has been emiited */ +#define DB_SchemaLoaded 0x0004 /* The schema has been loaded */ +#define DB_UnresetViews 0x0008 /* Some views have defined column names */ + + +/* +** Each database is an instance of the following structure. +** +** The sqlite.file_format is initialized by the database file +** and helps determines how the data in the database file is +** represented. This field allows newer versions of the library +** to read and write older databases. The various file formats +** are as follows: +** +** file_format==1 Version 2.1.0. +** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. +** file_format==3 Version 2.6.0. Fix empty-string index bug. +** file_format==4 Version 2.7.0. Add support for separate numeric and +** text datatypes. +** +** The sqlite.temp_store determines where temporary database files +** are stored. If 1, then a file is created to hold those tables. If +** 2, then they are held in memory. 0 means use the default value in +** the TEMP_STORE macro. +** +** The sqlite.lastRowid records the last insert rowid generated by an +** insert statement. Inserts on views do not affect its value. Each +** trigger has its own context, so that lastRowid can be updated inside +** triggers as usual. The previous value will be restored once the trigger +** exits. Upon entering a before or instead of trigger, lastRowid is no +** longer (since after version 2.8.12) reset to -1. +** +** The sqlite.nChange does not count changes within triggers and keeps no +** context. It is reset at start of sqlite_exec. +** The sqlite.lsChange represents the number of changes made by the last +** insert, update, or delete statement. It remains constant throughout the +** length of a statement and is then updated by OP_SetCounts. It keeps a +** context stack just like lastRowid so that the count of changes +** within a trigger is not seen outside the trigger. Changes to views do not +** affect the value of lsChange. +** The sqlite.csChange keeps track of the number of current changes (since +** the last statement) and is used to update sqlite_lsChange. +*/ +struct sqlite { + int nDb; /* Number of backends currently in use */ + Db *aDb; /* All backends */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ + int flags; /* Miscellanous flags. See below */ + u8 file_format; /* What file format version is this database? */ + u8 safety_level; /* How aggressive at synching data to disk */ + u8 want_to_close; /* Close after all VDBEs are deallocated */ + u8 temp_store; /* 1=file, 2=memory, 0=compile-time default */ + u8 onError; /* Default conflict algorithm */ + int next_cookie; /* Next value of aDb[0].schema_cookie */ + int cache_size; /* Number of pages to use in the cache */ + int nTable; /* Number of tables in the database */ + void *pBusyArg; /* 1st Argument to the busy callback */ + int (*xBusyCallback)(void *,const char*,int); /* The busy callback */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*);/* Invoked at every commit. */ + Hash aFunc; /* All functions that can be in SQL exprs */ + int lastRowid; /* ROWID of most recent insert (see above) */ + int priorNewRowid; /* Last randomly generated ROWID */ + int magic; /* Magic number for detect library misuse */ + int nChange; /* Number of rows changed (see above) */ + int lsChange; /* Last statement change count (see above) */ + int csChange; /* Current statement change count (see above) */ + struct sqliteInitInfo { /* Information used during initialization */ + int iDb; /* When back is being initialized */ + int newTnum; /* Rootpage of table being initialized */ + u8 busy; /* TRUE if currently initializing */ + } init; + struct Vdbe *pVdbe; /* List of active virtual machines */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ +#ifndef SQLITE_OMIT_AUTHORIZATION + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); + /* Access authorization function */ + void *pAuthArg; /* 1st argument to the access auth function */ +#endif +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int (*xProgress)(void *); /* The progress callback */ + void *pProgressArg; /* Argument to the progress callback */ + int nProgressOps; /* Number of opcodes for progress callback */ +#endif +}; + +/* +** Possible values for the sqlite.flags and or Db.flags fields. +** +** On sqlite.flags, the SQLITE_InTrans value means that we have +** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement +** transaction is active on that particular database file. +*/ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_Initialized 0x00000002 /* True after initialization */ +#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */ +#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ +#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ +#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ +#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ + /* result set is empty */ +#define SQLITE_ReportTypes 0x00000200 /* Include information on datatypes */ + /* in 4th argument of callback */ + +/* +** Possible values for the sqlite.magic field. +** The numbers are obtained at random and have no special meaning, other +** than being distinct from one another. +*/ +#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ + +/* +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc +** hash table. When multiple functions have the same name, the hash table +** points to a linked list of these structures. +*/ +struct FuncDef { + void (*xFunc)(sqlite_func*,int,const char**); /* Regular function */ + void (*xStep)(sqlite_func*,int,const char**); /* Aggregate function step */ + void (*xFinalize)(sqlite_func*); /* Aggregate function finializer */ + signed char nArg; /* Number of arguments. -1 means unlimited */ + signed char dataType; /* Arg that determines datatype. -1=NUMERIC, */ + /* -2=TEXT. -3=SQLITE_ARGS */ + u8 includeTypes; /* Add datatypes to args of xFunc and xStep */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ +}; + +/* +** information about each column of an SQL table is held in an instance +** of this structure. +*/ +struct Column { + char *zName; /* Name of this column */ + char *zDflt; /* Default value of this column */ + char *zType; /* Data type for this column */ + u8 notNull; /* True if there is a NOT NULL constraint */ + u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ + u8 sortOrder; /* Some combination of SQLITE_SO_... values */ + u8 dottedName; /* True if zName contains a "." character */ +}; + +/* +** The allowed sort orders. +** +** The TEXT and NUM values use bits that do not overlap with DESC and ASC. +** That way the two can be combined into a single number. +*/ +#define SQLITE_SO_UNK 0 /* Use the default collating type. (SCT_NUM) */ +#define SQLITE_SO_TEXT 2 /* Sort using memcmp() */ +#define SQLITE_SO_NUM 4 /* Sort using sqliteCompare() */ +#define SQLITE_SO_TYPEMASK 6 /* Mask to extract the collating sequence */ +#define SQLITE_SO_ASC 0 /* Sort in ascending order */ +#define SQLITE_SO_DESC 1 /* Sort in descending order */ +#define SQLITE_SO_DIRMASK 1 /* Mask to extract the sort direction */ + +/* +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Table.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Table.nCol is the number of columns in this table. Table.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. Table.hasPrimKey is true if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If Table.isTransient +** is true, then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Table.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. +*/ +struct Table { + char *zName; /* Name of the table */ + int nCol; /* Number of columns in this table */ + Column *aCol; /* Information about each column */ + int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */ + Index *pIndex; /* List of SQL indexes on this table. */ + int tnum; /* Root BTree node for this table (see note above) */ + Select *pSelect; /* NULL for tables. Points to definition if a view. */ + u8 readOnly; /* True if this table should not be written by the user */ + u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */ + u8 isTransient; /* True if automatically deleted when VDBE finishes */ + u8 hasPrimKey; /* True if there exists a primary key */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ + Trigger *pTrigger; /* List of SQL triggers on this table */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ +}; + +/* +** Each foreign key constraint is an instance of the following structure. +** +** A foreign key is associated with two tables. The "from" table is +** the table that contains the REFERENCES clause that creates the foreign +** key. The "to" table is the table that is named in the REFERENCES clause. +** Consider this example: +** +** CREATE TABLE ex1( +** a INTEGER PRIMARY KEY, +** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) +** ); +** +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** +** Each REFERENCES clause generates an instance of the following structure +** which is attached to the from-table. The to-table need not exist when +** the from-table is created. The existance of the to-table is not checked +** until an attempt is made to insert data into the from-table. +** +** The sqlite.aFKey hash table stores pointers to this structure +** given the name of a to-table. For each to-table, all foreign keys +** associated with that table are on a linked list using the FKey.pNextTo +** field. +*/ +struct FKey { + Table *pFrom; /* The table that constains the REFERENCES clause */ + FKey *pNextFrom; /* Next foreign key in pFrom */ + char *zTo; /* Name of table that the key points to */ + FKey *pNextTo; /* Next foreign key that points to zTo */ + int nCol; /* Number of columns in this key */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ + } *aCol; /* One entry for each of nCol column s */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ + u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ + u8 insertConf; /* How to resolve conflicts that occur on INSERT */ +}; + +/* +** SQLite supports many different ways to resolve a contraint +** error. ROLLBACK processing means that a constraint violation +** causes the operation in process to fail and for the current transaction +** to be rolled back. ABORT processing means the operation in process +** fails and any prior changes from that one operation are backed out, +** but the transaction is not rolled back. FAIL processing means that +** the operation in progress stops and returns an error code. But prior +** changes due to the same operation are not backed out and no rollback +** occurs. IGNORE means that the particular row that caused the constraint +** error is not inserted or updated. Processing continues and no error +** is returned. REPLACE means that preexisting database rows that caused +** a UNIQUE constraint violation are removed so that the new insert or +** update can proceed. Processing continues and no error is reported. +** +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the +** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** referenced table row is propagated into the row that holds the +** foreign key. +** +** The following symbolic values are used to record which type +** of action to take. +*/ +#define OE_None 0 /* There is no constraint to check */ +#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +#define OE_Abort 2 /* Back out changes but do no rollback transaction */ +#define OE_Fail 3 /* Stop the operation but leave all prior changes */ +#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ +#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ + +#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ +#define OE_SetNull 7 /* Set the foreign key value to NULL */ +#define OE_SetDflt 8 /* Set the foreign key value to its default */ +#define OE_Cascade 9 /* Cascade the changes */ + +#define OE_Default 99 /* Do whatever the default action is */ + +/* +** Each SQL index is represented in memory by an +** instance of the following structure. +** +** The columns of the table that are to be indexed are described +** by the aiColumn[] field of this structure. For example, suppose +** we have the following table and index: +** +** CREATE TABLE Ex1(c1 int, c2 int, c3 text); +** CREATE INDEX Ex2 ON Ex1(c3,c1); +** +** In the Table structure describing Ex1, nCol==3 because there are +** three columns in the table. In the Index structure describing +** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. +** The second column to be indexed (c1) has an index of 0 in +** Ex1.aCol[], hence Ex2.aiColumn[1]==0. +** +** The Index.onError field determines whether or not the indexed columns +** must be unique and what to do if they are not. When Index.onError=OE_None, +** it means this is not a unique index. Otherwise it is a unique index +** and the value of Index.onError indicate the which conflict resolution +** algorithm to employ whenever an attempt is made to insert a non-unique +** element. +*/ +struct Index { + char *zName; /* Name of this index */ + int nColumn; /* Number of columns in the table used by this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + Table *pTable; /* The SQL table being indexed */ + int tnum; /* Page containing root of this index in database file */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */ + Index *pNext; /* The next index associated with the same table */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. Tokens are also used as part of an expression. +** +** Note if Token.z==0 then Token.dyn and Token.n are undefined and +** may contain random values. Do not make any assuptions about Token.dyn +** and Token.n when Token.z==0. +*/ +struct Token { + const char *z; /* Text of the token. Not NULL-terminated! */ + unsigned dyn : 1; /* True for malloced memory, false for static */ + unsigned n : 31; /* Number of characters in this token */ +}; + +/* +** Each node of an expression in the parse tree is an instance +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list +** of argument if the expression is a function. +** +** Expr.token is the operator token for this node. For some expressions +** that have subexpressions, Expr.token can be the complete text that gave +** rise to the Expr. In the latter case, the token is marked as being +** a compound token. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is a function, the Expr.iTable is an integer code +** representing which function. If the expression is an unbound variable +** marker (a question mark character '?' in the original SQL) then the +** Expr.iTable holds the index number for that variable. +** +** The Expr.pSelect field points to a SELECT statement. The SELECT might +** be the right operand of an IN operator. Or, if a scalar SELECT appears +** in an expression the opcode is TK_SELECT and Expr.pSelect is the only +** operand. +*/ +struct Expr { + u8 op; /* Operation performed by this node */ + u8 dataType; /* Either SQLITE_SO_TEXT or SQLITE_SO_NUM */ + u8 iDb; /* Database referenced by this expression */ + u8 flags; /* Various flags. See below */ + Expr *pLeft, *pRight; /* Left and right subnodes */ + ExprList *pList; /* A list of expressions used as function arguments + ** or in " IN (useAgg==TRUE, pull + ** result from the iAgg-th element of the aggregator */ + Select *pSelect; /* When the expression is a sub-select. Also the + ** right side of " IN (
      %s
      "); + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); + fprintf(p->out,"
      +**
      Internal
      Type
      Requested
      Type
      Conversion +** +**
      NULL INTEGER Result is 0 +**
      NULL FLOAT Result is 0.0 +**
      NULL TEXT Result is NULL pointer +**
      NULL BLOB Result is NULL pointer +**
      INTEGER FLOAT Convert from integer to float +**
      INTEGER TEXT ASCII rendering of the integer +**
      INTEGER BLOB Same as for INTEGER->TEXT +**
      FLOAT INTEGER Convert from float to integer +**
      FLOAT TEXT ASCII rendering of the float +**
      FLOAT BLOB Same as FLOAT->TEXT +**
      TEXT INTEGER Use atoi() +**
      TEXT FLOAT Use atof() +**
      TEXT BLOB No change +**
      BLOB INTEGER Convert to TEXT then use atoi() +**
      BLOB FLOAT Convert to TEXT then use atof() +**
      BLOB TEXT Add a zero terminator if needed +**
      +** +** +** The table above makes reference to standard C library functions atoi() +** and atof(). SQLite does not really use these functions. It has its +** on equavalent internal routines. The atoi() and atof() names are +** used in the table for brevity and because they are familiar to most +** C programmers. +** +** Note that when type conversions occur, pointers returned by prior +** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or +** sqlite3_column_text16() may be invalidated. +** Type conversions and pointer invalidations might occur +** in the following cases: +** +**

        +**
      • The initial content is a BLOB and sqlite3_column_text() +** or sqlite3_column_text16() is called. A zero-terminator might +** need to be added to the string.

      • +** +**
      • The initial content is UTF-8 text and sqlite3_column_bytes16() or +** sqlite3_column_text16() is called. The content must be converted +** to UTF-16.

      • +** +**
      • The initial content is UTF-16 text and sqlite3_column_bytes() or +** sqlite3_column_text() is called. The content must be converted +** to UTF-8.

      • +**
      +** +** Conversions between UTF-16be and UTF-16le are always done in place and do +** not invalidate a prior pointer, though of course the content of the buffer +** that the prior pointer points to will have been modified. Other kinds +** of conversion are done in place when it is possible, but sometime it is +** not possible and in those cases prior pointers are invalidated. +** +** The safest and easiest to remember policy is to invoke these routines +** in one of the following ways: +** +**
        +**
      • sqlite3_column_text() followed by sqlite3_column_bytes()
      • +**
      • sqlite3_column_blob() followed by sqlite3_column_bytes()
      • +**
      • sqlite3_column_text16() followed by sqlite3_column_bytes16()
      • +**
      +** +** In other words, you should call sqlite3_column_text(), sqlite3_column_blob(), +** or sqlite3_column_text16() first to force the result into the desired +** format, then invoke sqlite3_column_bytes() or sqlite3_column_bytes16() to +** find the size of the result. Do not mix call to sqlite3_column_text() or +** sqlite3_column_blob() with calls to sqlite3_column_bytes16(). And do not +** mix calls to sqlite3_column_text16() with calls to sqlite3_column_bytes(). +** +** The pointers returned are valid until a type conversion occurs as +** described above, or until [sqlite3_step()] or [sqlite3_reset()] or +** [sqlite3_finalize()] is called. The memory space used to hold strings +** and blobs is freed automatically. Do not pass the pointers returned +** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into +** [sqlite3_free()]. +** +** If a memory allocation error occurs during the evaluation of any +** of these routines, a default value is returned. The default value +** is either the integer 0, the floating point number 0.0, or a NULL +** pointer. Subsequent calls to [sqlite3_errcode()] will return +** [SQLITE_NOMEM]. +** +** INVARIANTS: +** +** {F13803} The [sqlite3_column_blob(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a blob and then returns a +** pointer to the converted value. +** +** {F13806} The [sqlite3_column_bytes(S,N)] interface returns the +** number of bytes in the blob or string (exclusive of the +** zero terminator on the string) that was returned by the +** most recent call to [sqlite3_column_blob(S,N)] or +** [sqlite3_column_text(S,N)]. +** +** {F13809} The [sqlite3_column_bytes16(S,N)] interface returns the +** number of bytes in the string (exclusive of the +** zero terminator on the string) that was returned by the +** most recent call to [sqlite3_column_text16(S,N)]. +** +** {F13812} The [sqlite3_column_double(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a floating point value and +** returns a copy of that value. +** +** {F13815} The [sqlite3_column_int(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a 64-bit signed integer and +** returns the lower 32 bits of that integer. +** +** {F13818} The [sqlite3_column_int64(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a 64-bit signed integer and +** returns a copy of that integer. +** +** {F13821} The [sqlite3_column_text(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a zero-terminated UTF-8 +** string and returns a pointer to that string. +** +** {F13824} The [sqlite3_column_text16(S,N)] interface converts the +** Nth column in the current row of the result set for +** [prepared statement] S into a zero-terminated 2-byte +** aligned UTF-16 native byte order +** string and returns a pointer to that string. +** +** {F13827} The [sqlite3_column_type(S,N)] interface returns +** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT], +** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for +** the Nth column in the current row of the result set for +** [prepared statement] S. +** +** {F13830} The [sqlite3_column_value(S,N)] interface returns a +** pointer to an [unprotected sqlite3_value] object for the +** Nth column in the current row of the result set for +** [prepared statement] S. +*/ +SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); + +/* +** CAPI3REF: Destroy A Prepared Statement Object {F13300} +** +** The sqlite3_finalize() function is called to delete a +** [prepared statement]. If the statement was +** executed successfully, or not executed at all, then SQLITE_OK is returned. +** If execution of the statement failed then an +** [error code] or [extended error code] +** is returned. +** +** This routine can be called at any point during the execution of the +** [prepared statement]. If the virtual machine has not +** completed execution when this routine is called, that is like +** encountering an error or an interrupt. (See [sqlite3_interrupt()].) +** Incomplete updates may be rolled back and transactions cancelled, +** depending on the circumstances, and the +** [error code] returned will be [SQLITE_ABORT]. +** +** INVARIANTS: +** +** {F11302} The [sqlite3_finalize(S)] interface destroys the +** [prepared statement] S and releases all +** memory and file resources held by that object. +** +** {F11304} If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned an error, +** then [sqlite3_finalize(S)] returns that same error. +*/ +SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Reset A Prepared Statement Object {F13330} +** +** The sqlite3_reset() function is called to reset a +** [prepared statement] object. +** back to its initial state, ready to be re-executed. +** Any SQL statement variables that had values bound to them using +** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. +** Use [sqlite3_clear_bindings()] to reset the bindings. +** +** {F11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. +** +** {F11334} If the most recent call to [sqlite3_step(S)] for +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** +** {F11336} If the most recent call to [sqlite3_step(S)] for +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. +** +** {F11338} The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on [prepared statement] S. +*/ +SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Create Or Redefine SQL Functions {F16100} +** KEYWORDS: {function creation routines} +** +** These two functions (collectively known as +** "function creation routines") are used to add SQL functions or aggregates +** or to redefine the behavior of existing SQL functions or aggregates. The +** difference only between the two is that the second parameter, the +** name of the (scalar) function or aggregate, is encoded in UTF-8 for +** sqlite3_create_function() and UTF-16 for sqlite3_create_function16(). +** +** The first parameter is the [database connection] to which the SQL +** function is to be added. If a single +** program uses more than one [database connection] internally, then SQL +** functions must be added individually to each [database connection]. +** +** The second parameter is the name of the SQL function to be created +** or redefined. +** The length of the name is limited to 255 bytes, exclusive of the +** zero-terminator. Note that the name length limit is in bytes, not +** characters. Any attempt to create a function with a longer name +** will result in an SQLITE_ERROR error. +** +** The third parameter is the number of arguments that the SQL function or +** aggregate takes. If this parameter is negative, then the SQL function or +** aggregate may take any number of arguments. +** +** The fourth parameter, eTextRep, specifies what +** [SQLITE_UTF8 | text encoding] this SQL function prefers for +** its parameters. Any SQL function implementation should be able to work +** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be +** more efficient with one encoding than another. It is allowed to +** invoke sqlite3_create_function() or sqlite3_create_function16() multiple +** times with the same function but with different values of eTextRep. +** When multiple implementations of the same function are available, SQLite +** will pick the one that involves the least amount of data conversion. +** If there is only a single implementation which does not care what +** text encoding is used, then the fourth argument should be +** [SQLITE_ANY]. +** +** The fifth parameter is an arbitrary pointer. The implementation +** of the function can gain access to this pointer using +** [sqlite3_user_data()]. +** +** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are +** pointers to C-language functions that implement the SQL +** function or aggregate. A scalar SQL function requires an implementation of +** the xFunc callback only, NULL pointers should be passed as the xStep +** and xFinal parameters. An aggregate SQL function requires an implementation +** of xStep and xFinal and NULL should be passed for xFunc. To delete an +** existing SQL function or aggregate, pass NULL for all three function +** callback. +** +** It is permitted to register multiple implementations of the same +** functions with the same name but with either differing numbers of +** arguments or differing perferred text encodings. SQLite will use +** the implementation most closely matches the way in which the +** SQL function is used. +** +** INVARIANTS: +** +** {F16103} The [sqlite3_create_function16()] interface behaves exactly +** like [sqlite3_create_function()] in every way except that it +** interprets the zFunctionName argument as +** zero-terminated UTF-16 native byte order instead of as a +** zero-terminated UTF-8. +** +** {F16106} A successful invocation of +** the [sqlite3_create_function(D,X,N,E,...)] interface registers +** or replaces callback functions in [database connection] D +** used to implement the SQL function named X with N parameters +** and having a perferred text encoding of E. +** +** {F16109} A successful call to [sqlite3_create_function(D,X,N,E,P,F,S,L)] +** replaces the P, F, S, and L values from any prior calls with +** the same D, X, N, and E values. +** +** {F16112} The [sqlite3_create_function(D,X,...)] interface fails with +** a return code of [SQLITE_ERROR] if the SQL function name X is +** longer than 255 bytes exclusive of the zero terminator. +** +** {F16118} Either F must be NULL and S and L are non-NULL or else F +** is non-NULL and S and L are NULL, otherwise +** [sqlite3_create_function(D,X,N,E,P,F,S,L)] returns [SQLITE_ERROR]. +** +** {F16121} The [sqlite3_create_function(D,...)] interface fails with an +** error code of [SQLITE_BUSY] if there exist [prepared statements] +** associated with the [database connection] D. +** +** {F16124} The [sqlite3_create_function(D,X,N,...)] interface fails with an +** error code of [SQLITE_ERROR] if parameter N (specifying the number +** of arguments to the SQL function being registered) is less +** than -1 or greater than 127. +** +** {F16127} When N is non-negative, the [sqlite3_create_function(D,X,N,...)] +** interface causes callbacks to be invoked for the SQL function +** named X when the number of arguments to the SQL function is +** exactly N. +** +** {F16130} When N is -1, the [sqlite3_create_function(D,X,N,...)] +** interface causes callbacks to be invoked for the SQL function +** named X with any number of arguments. +** +** {F16133} When calls to [sqlite3_create_function(D,X,N,...)] +** specify multiple implementations of the same function X +** and when one implementation has N>=0 and the other has N=(-1) +** the implementation with a non-zero N is preferred. +** +** {F16136} When calls to [sqlite3_create_function(D,X,N,E,...)] +** specify multiple implementations of the same function X with +** the same number of arguments N but with different +** encodings E, then the implementation where E matches the +** database encoding is preferred. +** +** {F16139} For an aggregate SQL function created using +** [sqlite3_create_function(D,X,N,E,P,0,S,L)] the finializer +** function L will always be invoked exactly once if the +** step function S is called one or more times. +** +** {F16142} When SQLite invokes either the xFunc or xStep function of +** an application-defined SQL function or aggregate created +** by [sqlite3_create_function()] or [sqlite3_create_function16()], +** then the array of [sqlite3_value] objects passed as the +** third parameter are always [protected sqlite3_value] objects. +*/ +SQLITE_API int sqlite3_create_function( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); +SQLITE_API int sqlite3_create_function16( + sqlite3 *db, + const void *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*) +); + +/* +** CAPI3REF: Text Encodings {F10267} +** +** These constant define integer codes that represent the various +** text encodings supported by SQLite. +*/ +#define SQLITE_UTF8 1 +#define SQLITE_UTF16LE 2 +#define SQLITE_UTF16BE 3 +#define SQLITE_UTF16 4 /* Use native byte order */ +#define SQLITE_ANY 5 /* sqlite3_create_function only */ +#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ + +/* +** CAPI3REF: Obsolete Functions +** +** These functions are all now obsolete. In order to maintain +** backwards compatibility with older code, we continue to support +** these functions. However, new development projects should avoid +** the use of these functions. To help encourage people to avoid +** using these functions, we are not going to tell you want they do. +*/ +SQLITE_API int sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API int sqlite3_expired(sqlite3_stmt*); +SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API int sqlite3_global_recover(void); +SQLITE_API void sqlite3_thread_cleanup(void); +SQLITE_API int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); + +/* +** CAPI3REF: Obtaining SQL Function Parameter Values {F15100} +** +** The C-language implementation of SQL functions and aggregates uses +** this set of interface routines to access the parameter values on +** the function or aggregate. +** +** The xFunc (for scalar functions) or xStep (for aggregates) parameters +** to [sqlite3_create_function()] and [sqlite3_create_function16()] +** define callbacks that implement the SQL functions and aggregates. +** The 4th parameter to these callbacks is an array of pointers to +** [protected sqlite3_value] objects. There is one [sqlite3_value] object for +** each parameter to the SQL function. These routines are used to +** extract values from the [sqlite3_value] objects. +** +** These routines work only with [protected sqlite3_value] objects. +** Any attempt to use these routines on an [unprotected sqlite3_value] +** object results in undefined behavior. +** +** These routines work just like the corresponding +** [sqlite3_column_blob | sqlite3_column_* routines] except that +** these routines take a single [protected sqlite3_value] object pointer +** instead of an [sqlite3_stmt*] pointer and an integer column number. +** +** The sqlite3_value_text16() interface extracts a UTF16 string +** in the native byte-order of the host machine. The +** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces +** extract UTF16 strings as big-endian and little-endian respectively. +** +** The sqlite3_value_numeric_type() interface attempts to apply +** numeric affinity to the value. This means that an attempt is +** made to convert the value to an integer or floating point. If +** such a conversion is possible without loss of information (in other +** words if the value is a string that looks like a number) +** then the conversion is done. Otherwise no conversion occurs. The +** [SQLITE_INTEGER | datatype] after conversion is returned. +** +** Please pay particular attention to the fact that the pointer that +** is returned from [sqlite3_value_blob()], [sqlite3_value_text()], or +** [sqlite3_value_text16()] can be invalidated by a subsequent call to +** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], +** or [sqlite3_value_text16()]. +** +** These routines must be called from the same thread as +** the SQL function that supplied the [sqlite3_value*] parameters. +** +** +** INVARIANTS: +** +** {F15103} The [sqlite3_value_blob(V)] interface converts the +** [protected sqlite3_value] object V into a blob and then returns a +** pointer to the converted value. +** +** {F15106} The [sqlite3_value_bytes(V)] interface returns the +** number of bytes in the blob or string (exclusive of the +** zero terminator on the string) that was returned by the +** most recent call to [sqlite3_value_blob(V)] or +** [sqlite3_value_text(V)]. +** +** {F15109} The [sqlite3_value_bytes16(V)] interface returns the +** number of bytes in the string (exclusive of the +** zero terminator on the string) that was returned by the +** most recent call to [sqlite3_value_text16(V)], +** [sqlite3_value_text16be(V)], or [sqlite3_value_text16le(V)]. +** +** {F15112} The [sqlite3_value_double(V)] interface converts the +** [protected sqlite3_value] object V into a floating point value and +** returns a copy of that value. +** +** {F15115} The [sqlite3_value_int(V)] interface converts the +** [protected sqlite3_value] object V into a 64-bit signed integer and +** returns the lower 32 bits of that integer. +** +** {F15118} The [sqlite3_value_int64(V)] interface converts the +** [protected sqlite3_value] object V into a 64-bit signed integer and +** returns a copy of that integer. +** +** {F15121} The [sqlite3_value_text(V)] interface converts the +** [protected sqlite3_value] object V into a zero-terminated UTF-8 +** string and returns a pointer to that string. +** +** {F15124} The [sqlite3_value_text16(V)] interface converts the +** [protected sqlite3_value] object V into a zero-terminated 2-byte +** aligned UTF-16 native byte order +** string and returns a pointer to that string. +** +** {F15127} The [sqlite3_value_text16be(V)] interface converts the +** [protected sqlite3_value] object V into a zero-terminated 2-byte +** aligned UTF-16 big-endian +** string and returns a pointer to that string. +** +** {F15130} The [sqlite3_value_text16le(V)] interface converts the +** [protected sqlite3_value] object V into a zero-terminated 2-byte +** aligned UTF-16 little-endian +** string and returns a pointer to that string. +** +** {F15133} The [sqlite3_value_type(V)] interface returns +** one of [SQLITE_NULL], [SQLITE_INTEGER], [SQLITE_FLOAT], +** [SQLITE_TEXT], or [SQLITE_BLOB] as appropriate for +** the [sqlite3_value] object V. +** +** {F15136} The [sqlite3_value_numeric_type(V)] interface converts +** the [protected sqlite3_value] object V into either an integer or +** a floating point value if it can do so without loss of +** information, and returns one of [SQLITE_NULL], +** [SQLITE_INTEGER], [SQLITE_FLOAT], [SQLITE_TEXT], or +** [SQLITE_BLOB] as appropriate for +** the [protected sqlite3_value] object V after the conversion attempt. +*/ +SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double sqlite3_value_double(sqlite3_value*); +SQLITE_API int sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int sqlite3_value_type(sqlite3_value*); +SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + +/* +** CAPI3REF: Obtain Aggregate Function Context {F16210} +** +** The implementation of aggregate SQL functions use this routine to allocate +** a structure for storing their state. +** The first time the sqlite3_aggregate_context() routine is +** is called for a particular aggregate, SQLite allocates nBytes of memory +** zeros that memory, and returns a pointer to it. +** On second and subsequent calls to sqlite3_aggregate_context() +** for the same aggregate function index, the same buffer is returned. +** The implementation +** of the aggregate can use the returned buffer to accumulate data. +** +** SQLite automatically frees the allocated buffer when the aggregate +** query concludes. +** +** The first parameter should be a copy of the +** [sqlite3_context | SQL function context] that is the first +** parameter to the callback routine that implements the aggregate +** function. +** +** This routine must be called from the same thread in which +** the aggregate SQL function is running. +** +** INVARIANTS: +** +** {F16211} The first invocation of [sqlite3_aggregate_context(C,N)] for +** a particular instance of an aggregate function (for a particular +** context C) causes SQLite to allocation N bytes of memory, +** zero that memory, and return a pointer to the allocationed +** memory. +** +** {F16213} If a memory allocation error occurs during +** [sqlite3_aggregate_context(C,N)] then the function returns 0. +** +** {F16215} Second and subsequent invocations of +** [sqlite3_aggregate_context(C,N)] for the same context pointer C +** ignore the N parameter and return a pointer to the same +** block of memory returned by the first invocation. +** +** {F16217} The memory allocated by [sqlite3_aggregate_context(C,N)] is +** automatically freed on the next call to [sqlite3_reset()] +** or [sqlite3_finalize()] for the [prepared statement] containing +** the aggregate function associated with context C. +*/ +SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); + +/* +** CAPI3REF: User Data For Functions {F16240} +** +** The sqlite3_user_data() interface returns a copy of +** the pointer that was the pUserData parameter (the 5th parameter) +** of the the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. {END} +** +** This routine must be called from the same thread in which +** the application-defined function is running. +** +** INVARIANTS: +** +** {F16243} The [sqlite3_user_data(C)] interface returns a copy of the +** P pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)] +** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that +** registered the SQL function associated with +** [sqlite3_context] C. +*/ +SQLITE_API void *sqlite3_user_data(sqlite3_context*); + +/* +** CAPI3REF: Database Connection For Functions {F16250} +** +** The sqlite3_context_db_handle() interface returns a copy of +** the pointer to the [database connection] (the 1st parameter) +** of the the [sqlite3_create_function()] +** and [sqlite3_create_function16()] routines that originally +** registered the application defined function. +** +** INVARIANTS: +** +** {F16253} The [sqlite3_context_db_handle(C)] interface returns a copy of the +** D pointer from the [sqlite3_create_function(D,X,N,E,P,F,S,L)] +** or [sqlite3_create_function16(D,X,N,E,P,F,S,L)] call that +** registered the SQL function associated with +** [sqlite3_context] C. +*/ +SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); + +/* +** CAPI3REF: Function Auxiliary Data {F16270} +** +** The following two functions may be used by scalar SQL functions to +** associate meta-data with argument values. If the same value is passed to +** multiple invocations of the same SQL function during query execution, under +** some circumstances the associated meta-data may be preserved. This may +** be used, for example, to add a regular-expression matching scalar +** function. The compiled version of the regular expression is stored as +** meta-data associated with the SQL value passed as the regular expression +** pattern. The compiled regular expression can be reused on multiple +** invocations of the same function so that the original pattern string +** does not need to be recompiled on each invocation. +** +** The sqlite3_get_auxdata() interface returns a pointer to the meta-data +** associated by the sqlite3_set_auxdata() function with the Nth argument +** value to the application-defined function. +** If no meta-data has been ever been set for the Nth +** argument of the function, or if the cooresponding function parameter +** has changed since the meta-data was set, then sqlite3_get_auxdata() +** returns a NULL pointer. +** +** The sqlite3_set_auxdata() interface saves the meta-data +** pointed to by its 3rd parameter as the meta-data for the N-th +** argument of the application-defined function. Subsequent +** calls to sqlite3_get_auxdata() might return this data, if it has +** not been destroyed. +** If it is not NULL, SQLite will invoke the destructor +** function given by the 4th parameter to sqlite3_set_auxdata() on +** the meta-data when the corresponding function parameter changes +** or when the SQL statement completes, whichever comes first. +** +** SQLite is free to call the destructor and drop meta-data on +** any parameter of any function at any time. The only guarantee +** is that the destructor will be called before the metadata is +** dropped. +** +** In practice, meta-data is preserved between function calls for +** expressions that are constant at compile time. This includes literal +** values and SQL variables. +** +** These routines must be called from the same thread in which +** the SQL function is running. +** +** INVARIANTS: +** +** {F16272} The [sqlite3_get_auxdata(C,N)] interface returns a pointer +** to metadata associated with the Nth parameter of the SQL function +** whose context is C, or NULL if there is no metadata associated +** with that parameter. +** +** {F16274} The [sqlite3_set_auxdata(C,N,P,D)] interface assigns a metadata +** pointer P to the Nth parameter of the SQL function with context +** C. +** +** {F16276} SQLite will invoke the destructor D with a single argument +** which is the metadata pointer P following a call to +** [sqlite3_set_auxdata(C,N,P,D)] when SQLite ceases to hold +** the metadata. +** +** {F16277} SQLite ceases to hold metadata for an SQL function parameter +** when the value of that parameter changes. +** +** {F16278} When [sqlite3_set_auxdata(C,N,P,D)] is invoked, the destructor +** is called for any prior metadata associated with the same function +** context C and parameter N. +** +** {F16279} SQLite will call destructors for any metadata it is holding +** in a particular [prepared statement] S when either +** [sqlite3_reset(S)] or [sqlite3_finalize(S)] is called. +*/ +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); + + +/* +** CAPI3REF: Constants Defining Special Destructor Behavior {F10280} +** +** These are special value for the destructor that is passed in as the +** final argument to routines like [sqlite3_result_blob()]. If the destructor +** argument is SQLITE_STATIC, it means that the content pointer is constant +** and will never change. It does not need to be destroyed. The +** SQLITE_TRANSIENT value means that the content will likely change in +** the near future and that SQLite should make its own private copy of +** the content before returning. +** +** The typedef is necessary to work around problems in certain +** C++ compilers. See ticket #2191. +*/ +typedef void (*sqlite3_destructor_type)(void*); +#define SQLITE_STATIC ((sqlite3_destructor_type)0) +#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) + +/* +** CAPI3REF: Setting The Result Of An SQL Function {F16400} +** +** These routines are used by the xFunc or xFinal callbacks that +** implement SQL functions and aggregates. See +** [sqlite3_create_function()] and [sqlite3_create_function16()] +** for additional information. +** +** These functions work very much like the +** [sqlite3_bind_blob | sqlite3_bind_*] family of functions used +** to bind values to host parameters in prepared statements. +** Refer to the +** [sqlite3_bind_blob | sqlite3_bind_* documentation] for +** additional information. +** +** The sqlite3_result_blob() interface sets the result from +** an application defined function to be the BLOB whose content is pointed +** to by the second parameter and which is N bytes long where N is the +** third parameter. +** The sqlite3_result_zeroblob() inerfaces set the result of +** the application defined function to be a BLOB containing all zero +** bytes and N bytes in size, where N is the value of the 2nd parameter. +** +** The sqlite3_result_double() interface sets the result from +** an application defined function to be a floating point value specified +** by its 2nd argument. +** +** The sqlite3_result_error() and sqlite3_result_error16() functions +** cause the implemented SQL function to throw an exception. +** SQLite uses the string pointed to by the +** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() +** as the text of an error message. SQLite interprets the error +** message string from sqlite3_result_error() as UTF8. SQLite +** interprets the string from sqlite3_result_error16() as UTF16 in native +** byte order. If the third parameter to sqlite3_result_error() +** or sqlite3_result_error16() is negative then SQLite takes as the error +** message all text up through the first zero character. +** If the third parameter to sqlite3_result_error() or +** sqlite3_result_error16() is non-negative then SQLite takes that many +** bytes (not characters) from the 2nd parameter as the error message. +** The sqlite3_result_error() and sqlite3_result_error16() +** routines make a copy private copy of the error message text before +** they return. Hence, the calling function can deallocate or +** modify the text after they return without harm. +** The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. By default, +** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error() +** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. +** +** The sqlite3_result_toobig() interface causes SQLite +** to throw an error indicating that a string or BLOB is to long +** to represent. The sqlite3_result_nomem() interface +** causes SQLite to throw an exception indicating that the a +** memory allocation failed. +** +** The sqlite3_result_int() interface sets the return value +** of the application-defined function to be the 32-bit signed integer +** value given in the 2nd argument. +** The sqlite3_result_int64() interface sets the return value +** of the application-defined function to be the 64-bit signed integer +** value given in the 2nd argument. +** +** The sqlite3_result_null() interface sets the return value +** of the application-defined function to be NULL. +** +** The sqlite3_result_text(), sqlite3_result_text16(), +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces +** set the return value of the application-defined function to be +** a text string which is represented as UTF-8, UTF-16 native byte order, +** UTF-16 little endian, or UTF-16 big endian, respectively. +** SQLite takes the text result from the application from +** the 2nd parameter of the sqlite3_result_text* interfaces. +** If the 3rd parameter to the sqlite3_result_text* interfaces +** is negative, then SQLite takes result text from the 2nd parameter +** through the first zero character. +** If the 3rd parameter to the sqlite3_result_text* interfaces +** is non-negative, then as many bytes (not characters) of the text +** pointed to by the 2nd parameter are taken as the application-defined +** function result. +** If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that +** function as the destructor on the text or blob result when it has +** finished using that result. +** If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is the special constant SQLITE_STATIC, then +** SQLite assumes that the text or blob result is constant space and +** does not copy the space or call a destructor when it has +** finished using that result. +** If the 4th parameter to the sqlite3_result_text* interfaces +** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT +** then SQLite makes a copy of the result into space obtained from +** from [sqlite3_malloc()] before it returns. +** +** The sqlite3_result_value() interface sets the result of +** the application-defined function to be a copy the +** [unprotected sqlite3_value] object specified by the 2nd parameter. The +** sqlite3_result_value() interface makes a copy of the [sqlite3_value] +** so that [sqlite3_value] specified in the parameter may change or +** be deallocated after sqlite3_result_value() returns without harm. +** A [protected sqlite3_value] object may always be used where an +** [unprotected sqlite3_value] object is required, so either +** kind of [sqlite3_value] object can be used with this interface. +** +** If these routines are called from within the different thread +** than the one containing the application-defined function that recieved +** the [sqlite3_context] pointer, the results are undefined. +** +** INVARIANTS: +** +** {F16403} The default return value from any SQL function is NULL. +** +** {F16406} The [sqlite3_result_blob(C,V,N,D)] interface changes the +** return value of function C to be a blob that is N bytes +** in length and with content pointed to by V. +** +** {F16409} The [sqlite3_result_double(C,V)] interface changes the +** return value of function C to be the floating point value V. +** +** {F16412} The [sqlite3_result_error(C,V,N)] interface changes the return +** value of function C to be an exception with error code +** [SQLITE_ERROR] and a UTF8 error message copied from V up to the +** first zero byte or until N bytes are read if N is positive. +** +** {F16415} The [sqlite3_result_error16(C,V,N)] interface changes the return +** value of function C to be an exception with error code +** [SQLITE_ERROR] and a UTF16 native byte order error message +** copied from V up to the first zero terminator or until N bytes +** are read if N is positive. +** +** {F16418} The [sqlite3_result_error_toobig(C)] interface changes the return +** value of the function C to be an exception with error code +** [SQLITE_TOOBIG] and an appropriate error message. +** +** {F16421} The [sqlite3_result_error_nomem(C)] interface changes the return +** value of the function C to be an exception with error code +** [SQLITE_NOMEM] and an appropriate error message. +** +** {F16424} The [sqlite3_result_error_code(C,E)] interface changes the return +** value of the function C to be an exception with error code E. +** The error message text is unchanged. +** +** {F16427} The [sqlite3_result_int(C,V)] interface changes the +** return value of function C to be the 32-bit integer value V. +** +** {F16430} The [sqlite3_result_int64(C,V)] interface changes the +** return value of function C to be the 64-bit integer value V. +** +** {F16433} The [sqlite3_result_null(C)] interface changes the +** return value of function C to be NULL. +** +** {F16436} The [sqlite3_result_text(C,V,N,D)] interface changes the +** return value of function C to be the UTF8 string +** V up to the first zero if N is negative +** or the first N bytes of V if N is non-negative. +** +** {F16439} The [sqlite3_result_text16(C,V,N,D)] interface changes the +** return value of function C to be the UTF16 native byte order +** string V up to the first zero if N is +** negative or the first N bytes of V if N is non-negative. +** +** {F16442} The [sqlite3_result_text16be(C,V,N,D)] interface changes the +** return value of function C to be the UTF16 big-endian +** string V up to the first zero if N is +** is negative or the first N bytes or V if N is non-negative. +** +** {F16445} The [sqlite3_result_text16le(C,V,N,D)] interface changes the +** return value of function C to be the UTF16 little-endian +** string V up to the first zero if N is +** negative or the first N bytes of V if N is non-negative. +** +** {F16448} The [sqlite3_result_value(C,V)] interface changes the +** return value of function C to be [unprotected sqlite3_value] +** object V. +** +** {F16451} The [sqlite3_result_zeroblob(C,N)] interface changes the +** return value of function C to be an N-byte blob of all zeros. +** +** {F16454} The [sqlite3_result_error()] and [sqlite3_result_error16()] +** interfaces make a copy of their error message strings before +** returning. +** +** {F16457} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], +** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], +** [sqlite3_result_text16be(C,V,N,D)], or +** [sqlite3_result_text16le(C,V,N,D)] is the constant [SQLITE_STATIC] +** then no destructor is ever called on the pointer V and SQLite +** assumes that V is immutable. +** +** {F16460} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], +** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], +** [sqlite3_result_text16be(C,V,N,D)], or +** [sqlite3_result_text16le(C,V,N,D)] is the constant +** [SQLITE_TRANSIENT] then the interfaces makes a copy of the +** content of V and retains the copy. +** +** {F16463} If the D destructor parameter to [sqlite3_result_blob(C,V,N,D)], +** [sqlite3_result_text(C,V,N,D)], [sqlite3_result_text16(C,V,N,D)], +** [sqlite3_result_text16be(C,V,N,D)], or +** [sqlite3_result_text16le(C,V,N,D)] is some value other than +** the constants [SQLITE_STATIC] and [SQLITE_TRANSIENT] then +** SQLite will invoke the destructor D with V as its only argument +** when it has finished with the V value. +*/ +SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void sqlite3_result_null(sqlite3_context*); +SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); + +/* +** CAPI3REF: Define New Collating Sequences {F16600} +** +** These functions are used to add new collation sequences to the +** [sqlite3*] handle specified as the first argument. +** +** The name of the new collation sequence is specified as a UTF-8 string +** for sqlite3_create_collation() and sqlite3_create_collation_v2() +** and a UTF-16 string for sqlite3_create_collation16(). In all cases +** the name is passed as the second function argument. +** +** The third argument may be one of the constants [SQLITE_UTF8], +** [SQLITE_UTF16LE] or [SQLITE_UTF16BE], indicating that the user-supplied +** routine expects to be passed pointers to strings encoded using UTF-8, +** UTF-16 little-endian or UTF-16 big-endian respectively. The +** third argument might also be [SQLITE_UTF16_ALIGNED] to indicate that +** the routine expects pointers to 16-bit word aligned strings +** of UTF16 in the native byte order of the host computer. +** +** A pointer to the user supplied routine must be passed as the fifth +** argument. If it is NULL, this is the same as deleting the collation +** sequence (so that SQLite cannot call it anymore). +** Each time the application +** supplied function is invoked, it is passed a copy of the void* passed as +** the fourth argument to sqlite3_create_collation() or +** sqlite3_create_collation16() as its first parameter. +** +** The remaining arguments to the application-supplied routine are two strings, +** each represented by a (length, data) pair and encoded in the encoding +** that was passed as the third argument when the collation sequence was +** registered. {END} The application defined collation routine should +** return negative, zero or positive if +** the first string is less than, equal to, or greater than the second +** string. i.e. (STRING1 - STRING2). +** +** The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** excapt that it takes an extra argument which is a destructor for +** the collation. The destructor is called when the collation is +** destroyed and is passed a copy of the fourth parameter void* pointer +** of the sqlite3_create_collation_v2(). +** Collations are destroyed when +** they are overridden by later calls to the collation creation functions +** or when the [sqlite3*] database handle is closed using [sqlite3_close()]. +** +** INVARIANTS: +** +** {F16603} A successful call to the +** [sqlite3_create_collation_v2(B,X,E,P,F,D)] interface +** registers function F as the comparison function used to +** implement collation X on [database connection] B for +** databases having encoding E. +** +** {F16604} SQLite understands the X parameter to +** [sqlite3_create_collation_v2(B,X,E,P,F,D)] as a zero-terminated +** UTF-8 string in which case is ignored for ASCII characters and +** is significant for non-ASCII characters. +** +** {F16606} Successive calls to [sqlite3_create_collation_v2(B,X,E,P,F,D)] +** with the same values for B, X, and E, override prior values +** of P, F, and D. +** +** {F16609} The destructor D in [sqlite3_create_collation_v2(B,X,E,P,F,D)] +** is not NULL then it is called with argument P when the +** collating function is dropped by SQLite. +** +** {F16612} A collating function is dropped when it is overloaded. +** +** {F16615} A collating function is dropped when the database connection +** is closed using [sqlite3_close()]. +** +** {F16618} The pointer P in [sqlite3_create_collation_v2(B,X,E,P,F,D)] +** is passed through as the first parameter to the comparison +** function F for all subsequent invocations of F. +** +** {F16621} A call to [sqlite3_create_collation(B,X,E,P,F)] is exactly +** the same as a call to [sqlite3_create_collation_v2()] with +** the same parameters and a NULL destructor. +** +** {F16624} Following a [sqlite3_create_collation_v2(B,X,E,P,F,D)], +** SQLite uses the comparison function F for all text comparison +** operations on [database connection] B on text values that +** use the collating sequence name X. +** +** {F16627} The [sqlite3_create_collation16(B,X,E,P,F)] works the same +** as [sqlite3_create_collation(B,X,E,P,F)] except that the +** collation name X is understood as UTF-16 in native byte order +** instead of UTF-8. +** +** {F16630} When multiple comparison functions are available for the same +** collating sequence, SQLite chooses the one whose text encoding +** requires the least amount of conversion from the default +** text encoding of the database. +*/ +SQLITE_API int sqlite3_create_collation( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*) +); +SQLITE_API int sqlite3_create_collation_v2( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*), + void(*xDestroy)(void*) +); +SQLITE_API int sqlite3_create_collation16( + sqlite3*, + const char *zName, + int eTextRep, + void*, + int(*xCompare)(void*,int,const void*,int,const void*) +); + +/* +** CAPI3REF: Collation Needed Callbacks {F16700} +** +** To avoid having to register all collation sequences before a database +** can be used, a single callback function may be registered with the +** database handle to be called whenever an undefined collation sequence is +** required. +** +** If the function is registered using the sqlite3_collation_needed() API, +** then it is passed the names of undefined collation sequences as strings +** encoded in UTF-8. {F16703} If sqlite3_collation_needed16() is used, the names +** are passed as UTF-16 in machine native byte order. A call to either +** function replaces any existing callback. +** +** When the callback is invoked, the first argument passed is a copy +** of the second argument to sqlite3_collation_needed() or +** sqlite3_collation_needed16(). The second argument is the database +** handle. The third argument is one of [SQLITE_UTF8], +** [SQLITE_UTF16BE], or [SQLITE_UTF16LE], indicating the most +** desirable form of the collation sequence function required. +** The fourth parameter is the name of the +** required collation sequence. +** +** The callback function should register the desired collation using +** [sqlite3_create_collation()], [sqlite3_create_collation16()], or +** [sqlite3_create_collation_v2()]. +** +** INVARIANTS: +** +** {F16702} A successful call to [sqlite3_collation_needed(D,P,F)] +** or [sqlite3_collation_needed16(D,P,F)] causes +** the [database connection] D to invoke callback F with first +** parameter P whenever it needs a comparison function for a +** collating sequence that it does not know about. +** +** {F16704} Each successful call to [sqlite3_collation_needed()] or +** [sqlite3_collation_needed16()] overrides the callback registered +** on the same [database connection] by prior calls to either +** interface. +** +** {F16706} The name of the requested collating function passed in the +** 4th parameter to the callback is in UTF-8 if the callback +** was registered using [sqlite3_collation_needed()] and +** is in UTF-16 native byte order if the callback was +** registered using [sqlite3_collation_needed16()]. +** +** +*/ +SQLITE_API int sqlite3_collation_needed( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const char*) +); +SQLITE_API int sqlite3_collation_needed16( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const void*) +); + +/* +** Specify the key for an encrypted database. This routine should be +** called right after sqlite3_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_key( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The key */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +SQLITE_API int sqlite3_rekey( + sqlite3 *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + +/* +** CAPI3REF: Suspend Execution For A Short Time {F10530} +** +** The sqlite3_sleep() function +** causes the current thread to suspend execution +** for at least a number of milliseconds specified in its parameter. +** +** If the operating system does not support sleep requests with +** millisecond time resolution, then the time will be rounded up to +** the nearest second. The number of milliseconds of sleep actually +** requested from the operating system is returned. +** +** SQLite implements this interface by calling the xSleep() +** method of the default [sqlite3_vfs] object. +** +** INVARIANTS: +** +** {F10533} The [sqlite3_sleep(M)] interface invokes the xSleep +** method of the default [sqlite3_vfs|VFS] in order to +** suspend execution of the current thread for at least +** M milliseconds. +** +** {F10536} The [sqlite3_sleep(M)] interface returns the number of +** milliseconds of sleep actually requested of the operating +** system, which might be larger than the parameter M. +*/ +SQLITE_API int sqlite3_sleep(int); + +/* +** CAPI3REF: Name Of The Folder Holding Temporary Files {F10310} +** +** If this global variable is made to point to a string which is +** the name of a folder (a.ka. directory), then all temporary files +** created by SQLite will be placed in that directory. If this variable +** is NULL pointer, then SQLite does a search for an appropriate temporary +** file directory. +** +** It is not safe to modify this variable once a database connection +** has been opened. It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been call and remain unchanged thereafter. +*/ +SQLITE_API char *sqlite3_temp_directory; + +/* +** CAPI3REF: Test To See If The Database Is In Auto-Commit Mode {F12930} +** +** The sqlite3_get_autocommit() interfaces returns non-zero or +** zero if the given database connection is or is not in autocommit mode, +** respectively. Autocommit mode is on +** by default. Autocommit mode is disabled by a [BEGIN] statement. +** Autocommit mode is reenabled by a [COMMIT] or [ROLLBACK]. +** +** If certain kinds of errors occur on a statement within a multi-statement +** transactions (errors including [SQLITE_FULL], [SQLITE_IOERR], +** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the +** transaction might be rolled back automatically. The only way to +** find out if SQLite automatically rolled back the transaction after +** an error is to use this function. +** +** INVARIANTS: +** +** {F12931} The [sqlite3_get_autocommit(D)] interface returns non-zero or +** zero if the [database connection] D is or is not in autocommit +** mode, respectively. +** +** {F12932} Autocommit mode is on by default. +** +** {F12933} Autocommit mode is disabled by a successful [BEGIN] statement. +** +** {F12934} Autocommit mode is enabled by a successful [COMMIT] or [ROLLBACK] +** statement. +** +** +** LIMITATIONS: +*** +** {U12936} If another thread changes the autocommit status of the database +** connection while this routine is running, then the return value +** is undefined. +*/ +SQLITE_API int sqlite3_get_autocommit(sqlite3*); + +/* +** CAPI3REF: Find The Database Handle Of A Prepared Statement {F13120} +** +** The sqlite3_db_handle interface +** returns the [sqlite3*] database handle to which a +** [prepared statement] belongs. +** The database handle returned by sqlite3_db_handle +** is the same database handle that was +** the first argument to the [sqlite3_prepare_v2()] or its variants +** that was used to create the statement in the first place. +** +** INVARIANTS: +** +** {F13123} The [sqlite3_db_handle(S)] interface returns a pointer +** to the [database connection] associated with +** [prepared statement] S. +*/ +SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); + + +/* +** CAPI3REF: Commit And Rollback Notification Callbacks {F12950} +** +** The sqlite3_commit_hook() interface registers a callback +** function to be invoked whenever a transaction is committed. +** Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** The sqlite3_rollback_hook() interface registers a callback +** function to be invoked whenever a transaction is committed. +** Any callback set by a previous call to sqlite3_commit_hook() +** for the same database connection is overridden. +** The pArg argument is passed through +** to the callback. If the callback on a commit hook function +** returns non-zero, then the commit is converted into a rollback. +** +** If another function was previously registered, its +** pArg value is returned. Otherwise NULL is returned. +** +** Registering a NULL function disables the callback. +** +** For the purposes of this API, a transaction is said to have been +** rolled back if an explicit "ROLLBACK" statement is executed, or +** an error or constraint causes an implicit rollback to occur. +** The rollback callback is not invoked if a transaction is +** automatically rolled back because the database connection is closed. +** The rollback callback is not invoked if a transaction is +** rolled back because a commit callback returned non-zero. +** Check on this +** +** These are experimental interfaces and are subject to change. +** +** INVARIANTS: +** +** {F12951} The [sqlite3_commit_hook(D,F,P)] interface registers the +** callback function F to be invoked with argument P whenever +** a transaction commits on [database connection] D. +** +** {F12952} The [sqlite3_commit_hook(D,F,P)] interface returns the P +** argument from the previous call with the same +** [database connection ] D , or NULL on the first call +** for a particular [database connection] D. +** +** {F12953} Each call to [sqlite3_commit_hook()] overwrites the callback +** registered by prior calls. +** +** {F12954} If the F argument to [sqlite3_commit_hook(D,F,P)] is NULL +** then the commit hook callback is cancelled and no callback +** is invoked when a transaction commits. +** +** {F12955} If the commit callback returns non-zero then the commit is +** converted into a rollback. +** +** {F12961} The [sqlite3_rollback_hook(D,F,P)] interface registers the +** callback function F to be invoked with argument P whenever +** a transaction rolls back on [database connection] D. +** +** {F12962} The [sqlite3_rollback_hook(D,F,P)] interface returns the P +** argument from the previous call with the same +** [database connection ] D , or NULL on the first call +** for a particular [database connection] D. +** +** {F12963} Each call to [sqlite3_rollback_hook()] overwrites the callback +** registered by prior calls. +** +** {F12964} If the F argument to [sqlite3_rollback_hook(D,F,P)] is NULL +** then the rollback hook callback is cancelled and no callback +** is invoked when a transaction rolls back. +*/ +SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + +/* +** CAPI3REF: Data Change Notification Callbacks {F12970} +** +** The sqlite3_update_hook() interface +** registers a callback function with the database connection identified by the +** first argument to be invoked whenever a row is updated, inserted or deleted. +** Any callback set by a previous call to this function for the same +** database connection is overridden. +** +** The second argument is a pointer to the function to invoke when a +** row is updated, inserted or deleted. +** The first argument to the callback is +** a copy of the third argument to sqlite3_update_hook(). +** The second callback +** argument is one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], +** depending on the operation that caused the callback to be invoked. +** The third and +** fourth arguments to the callback contain pointers to the database and +** table name containing the affected row. +** The final callback parameter is +** the rowid of the row. +** In the case of an update, this is the rowid after +** the update takes place. +** +** The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence). +** +** If another function was previously registered, its pArg value +** is returned. Otherwise NULL is returned. +** +** INVARIANTS: +** +** {F12971} The [sqlite3_update_hook(D,F,P)] interface causes callback +** function F to be invoked with first parameter P whenever +** a table row is modified, inserted, or deleted on +** [database connection] D. +** +** {F12973} The [sqlite3_update_hook(D,F,P)] interface returns the value +** of P for the previous call on the same [database connection] D, +** or NULL for the first call. +** +** {F12975} If the update hook callback F in [sqlite3_update_hook(D,F,P)] +** is NULL then the no update callbacks are made. +** +** {F12977} Each call to [sqlite3_update_hook(D,F,P)] overrides prior calls +** to the same interface on the same [database connection] D. +** +** {F12979} The update hook callback is not invoked when internal system +** tables such as sqlite_master and sqlite_sequence are modified. +** +** {F12981} The second parameter to the update callback +** is one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], +** depending on the operation that caused the callback to be invoked. +** +** {F12983} The third and fourth arguments to the callback contain pointers +** to zero-terminated UTF-8 strings which are the names of the +** database and table that is being updated. + +** {F12985} The final callback parameter is the rowid of the row after +** the change occurs. +*/ +SQLITE_API void *sqlite3_update_hook( + sqlite3*, + void(*)(void *,int ,char const *,char const *,sqlite3_int64), + void* +); + +/* +** CAPI3REF: Enable Or Disable Shared Pager Cache {F10330} +** +** This routine enables or disables the sharing of the database cache +** and schema data structures between connections to the same database. +** Sharing is enabled if the argument is true and disabled if the argument +** is false. +** +** Cache sharing is enabled and disabled +** for an entire process. {END} This is a change as of SQLite version 3.5.0. +** In prior versions of SQLite, sharing was +** enabled or disabled for each thread separately. +** +** The cache sharing mode set by this interface effects all subsequent +** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. +** Existing database connections continue use the sharing mode +** that was in effect at the time they were opened. +** +** Virtual tables cannot be used with a shared cache. When shared +** cache is enabled, the [sqlite3_create_module()] API used to register +** virtual tables will always return an error. +** +** This routine returns [SQLITE_OK] if shared cache was +** enabled or disabled successfully. An [error code] +** is returned otherwise. +** +** Shared cache is disabled by default. But this might change in +** future releases of SQLite. Applications that care about shared +** cache setting should set it explicitly. +** +** INVARIANTS: +** +** {F10331} A successful invocation of [sqlite3_enable_shared_cache(B)] +** will enable or disable shared cache mode for any subsequently +** created [database connection] in the same process. +** +** {F10336} When shared cache is enabled, the [sqlite3_create_module()] +** interface will always return an error. +** +** {F10337} The [sqlite3_enable_shared_cache(B)] interface returns +** [SQLITE_OK] if shared cache was enabled or disabled successfully. +** +** {F10339} Shared cache is disabled by default. +*/ +SQLITE_API int sqlite3_enable_shared_cache(int); + +/* +** CAPI3REF: Attempt To Free Heap Memory {F17340} +** +** The sqlite3_release_memory() interface attempts to +** free N bytes of heap memory by deallocating non-essential memory +** allocations held by the database labrary. {END} Memory used +** to cache database pages to improve performance is an example of +** non-essential memory. Sqlite3_release_memory() returns +** the number of bytes actually freed, which might be more or less +** than the amount requested. +** +** INVARIANTS: +** +** {F17341} The [sqlite3_release_memory(N)] interface attempts to +** free N bytes of heap memory by deallocating non-essential +** memory allocations held by the database labrary. +** +** {F16342} The [sqlite3_release_memory(N)] returns the number +** of bytes actually freed, which might be more or less +** than the amount requested. +*/ +SQLITE_API int sqlite3_release_memory(int); + +/* +** CAPI3REF: Impose A Limit On Heap Size {F17350} +** +** The sqlite3_soft_heap_limit() interface +** places a "soft" limit on the amount of heap memory that may be allocated +** by SQLite. If an internal allocation is requested +** that would exceed the soft heap limit, [sqlite3_release_memory()] is +** invoked one or more times to free up some space before the allocation +** is made. +** +** The limit is called "soft", because if +** [sqlite3_release_memory()] cannot +** free sufficient memory to prevent the limit from being exceeded, +** the memory is allocated anyway and the current operation proceeds. +** +** A negative or zero value for N means that there is no soft heap limit and +** [sqlite3_release_memory()] will only be called when memory is exhausted. +** The default value for the soft heap limit is zero. +** +** SQLite makes a best effort to honor the soft heap limit. +** But if the soft heap limit cannot honored, execution will +** continue without error or notification. This is why the limit is +** called a "soft" limit. It is advisory only. +** +** Prior to SQLite version 3.5.0, this routine only constrained the memory +** allocated by a single thread - the same thread in which this routine +** runs. Beginning with SQLite version 3.5.0, the soft heap limit is +** applied to all threads. The value specified for the soft heap limit +** is an upper bound on the total memory allocation for all threads. In +** version 3.5.0 there is no mechanism for limiting the heap usage for +** individual threads. +** +** INVARIANTS: +** +** {F16351} The [sqlite3_soft_heap_limit(N)] interface places a soft limit +** of N bytes on the amount of heap memory that may be allocated +** using [sqlite3_malloc()] or [sqlite3_realloc()] at any point +** in time. +** +** {F16352} If a call to [sqlite3_malloc()] or [sqlite3_realloc()] would +** cause the total amount of allocated memory to exceed the +** soft heap limit, then [sqlite3_release_memory()] is invoked +** in an attempt to reduce the memory usage prior to proceeding +** with the memory allocation attempt. +** +** {F16353} Calls to [sqlite3_malloc()] or [sqlite3_realloc()] that trigger +** attempts to reduce memory usage through the soft heap limit +** mechanism continue even if the attempt to reduce memory +** usage is unsuccessful. +** +** {F16354} A negative or zero value for N in a call to +** [sqlite3_soft_heap_limit(N)] means that there is no soft +** heap limit and [sqlite3_release_memory()] will only be +** called when memory is completely exhausted. +** +** {F16355} The default value for the soft heap limit is zero. +** +** {F16358} Each call to [sqlite3_soft_heap_limit(N)] overrides the +** values set by all prior calls. +*/ +SQLITE_API void sqlite3_soft_heap_limit(int); + +/* +** CAPI3REF: Extract Metadata About A Column Of A Table {F12850} +** +** This routine +** returns meta-data about a specific column of a specific database +** table accessible using the connection handle passed as the first function +** argument. +** +** The column is identified by the second, third and fourth parameters to +** this function. The second parameter is either the name of the database +** (i.e. "main", "temp" or an attached database) containing the specified +** table or NULL. If it is NULL, then all attached databases are searched +** for the table using the same algorithm as the database engine uses to +** resolve unqualified table references. +** +** The third and fourth parameters to this function are the table and column +** name of the desired column, respectively. Neither of these parameters +** may be NULL. +** +** Meta information is returned by writing to the memory locations passed as +** the 5th and subsequent parameters to this function. Any of these +** arguments may be NULL, in which case the corresponding element of meta +** information is ommitted. +** +**
      +** Parameter     Output Type      Description
      +** -----------------------------------
      +**
      +**   5th         const char*      Data type
      +**   6th         const char*      Name of the default collation sequence 
      +**   7th         int              True if the column has a NOT NULL constraint
      +**   8th         int              True if the column is part of the PRIMARY KEY
      +**   9th         int              True if the column is AUTOINCREMENT
      +** 
      +** +** +** The memory pointed to by the character pointers returned for the +** declaration type and collation sequence is valid only until the next +** call to any sqlite API function. +** +** If the specified table is actually a view, then an error is returned. +** +** If the specified column is "rowid", "oid" or "_rowid_" and an +** INTEGER PRIMARY KEY column has been explicitly declared, then the output +** parameters are set for the explicitly declared column. If there is no +** explicitly declared IPK column, then the output parameters are set as +** follows: +** +**
      +**     data type: "INTEGER"
      +**     collation sequence: "BINARY"
      +**     not null: 0
      +**     primary key: 1
      +**     auto increment: 0
      +** 
      +** +** This function may load one or more schemas from database files. If an +** error occurs during this process, or if the requested table or column +** cannot be found, an SQLITE error code is returned and an error message +** left in the database handle (to be retrieved using sqlite3_errmsg()). +** +** This API is only available if the library was compiled with the +** SQLITE_ENABLE_COLUMN_METADATA preprocessor symbol defined. +*/ +SQLITE_API int sqlite3_table_column_metadata( + sqlite3 *db, /* Connection handle */ + const char *zDbName, /* Database name or NULL */ + const char *zTableName, /* Table name */ + const char *zColumnName, /* Column name */ + char const **pzDataType, /* OUTPUT: Declared data type */ + char const **pzCollSeq, /* OUTPUT: Collation sequence name */ + int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ + int *pPrimaryKey, /* OUTPUT: True if column part of PK */ + int *pAutoinc /* OUTPUT: True if column is auto-increment */ +); + +/* +** CAPI3REF: Load An Extension {F12600} +** +** {F12601} The sqlite3_load_extension() interface +** attempts to load an SQLite extension library contained in the file +** zFile. {F12602} The entry point is zProc. {F12603} zProc may be 0 +** in which case the name of the entry point defaults +** to "sqlite3_extension_init". +** +** {F12604} The sqlite3_load_extension() interface shall +** return [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** +** {F12605} +** If an error occurs and pzErrMsg is not 0, then the +** sqlite3_load_extension() interface shall attempt to fill *pzErrMsg with +** error message text stored in memory obtained from [sqlite3_malloc()]. +** {END} The calling function should free this memory +** by calling [sqlite3_free()]. +** +** {F12606} +** Extension loading must be enabled using [sqlite3_enable_load_extension()] +** prior to calling this API or an error will be returned. +*/ +SQLITE_API int sqlite3_load_extension( + sqlite3 *db, /* Load the extension into this database connection */ + const char *zFile, /* Name of the shared library containing extension */ + const char *zProc, /* Entry point. Derived from zFile if 0 */ + char **pzErrMsg /* Put error message here if not 0 */ +); + +/* +** CAPI3REF: Enable Or Disable Extension Loading {F12620} +** +** So as not to open security holes in older applications that are +** unprepared to deal with extension loading, and as a means of disabling +** extension loading while evaluating user-entered SQL, the following +** API is provided to turn the [sqlite3_load_extension()] mechanism on and +** off. {F12622} It is off by default. {END} See ticket #1863. +** +** {F12621} Call the sqlite3_enable_load_extension() routine +** with onoff==1 to turn extension loading on +** and call it with onoff==0 to turn it back off again. {END} +*/ +SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); + +/* +** CAPI3REF: Make Arrangements To Automatically Load An Extension {F12640} +** +** {F12641} This function +** registers an extension entry point that is automatically invoked +** whenever a new database connection is opened using +** [sqlite3_open()], [sqlite3_open16()], or [sqlite3_open_v2()]. {END} +** +** This API can be invoked at program startup in order to register +** one or more statically linked extensions that will be available +** to all new database connections. +** +** {F12642} Duplicate extensions are detected so calling this routine multiple +** times with the same extension is harmless. +** +** {F12643} This routine stores a pointer to the extension in an array +** that is obtained from sqlite_malloc(). {END} If you run a memory leak +** checker on your program and it reports a leak because of this +** array, then invoke [sqlite3_reset_auto_extension()] prior +** to shutdown to free the memory. +** +** {F12644} Automatic extensions apply across all threads. {END} +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +SQLITE_API int sqlite3_auto_extension(void *xEntryPoint); + + +/* +** CAPI3REF: Reset Automatic Extension Loading {F12660} +** +** {F12661} This function disables all previously registered +** automatic extensions. {END} This +** routine undoes the effect of all prior [sqlite3_auto_extension()] +** calls. +** +** {F12662} This call disabled automatic extensions in all threads. {END} +** +** This interface is experimental and is subject to change or +** removal in future releases of SQLite. +*/ +SQLITE_API void sqlite3_reset_auto_extension(void); + + +/* +****** EXPERIMENTAL - subject to change without notice ************** +** +** The interface to the virtual-table mechanism is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stablizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +*/ + +/* +** Structures used by the virtual table interface +*/ +typedef struct sqlite3_vtab sqlite3_vtab; +typedef struct sqlite3_index_info sqlite3_index_info; +typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; +typedef struct sqlite3_module sqlite3_module; + +/* +** CAPI3REF: Virtual Table Object {F18000} +** KEYWORDS: sqlite3_module +** +** A module is a class of virtual tables. Each module is defined +** by an instance of the following structure. This structure consists +** mostly of methods for the module. +*/ +struct sqlite3_module { + int iVersion; + int (*xCreate)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xConnect)(sqlite3*, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**); + int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); + int (*xDisconnect)(sqlite3_vtab *pVTab); + int (*xDestroy)(sqlite3_vtab *pVTab); + int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); + int (*xClose)(sqlite3_vtab_cursor*); + int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, + int argc, sqlite3_value **argv); + int (*xNext)(sqlite3_vtab_cursor*); + int (*xEof)(sqlite3_vtab_cursor*); + int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); + int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); + int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); + int (*xBegin)(sqlite3_vtab *pVTab); + int (*xSync)(sqlite3_vtab *pVTab); + int (*xCommit)(sqlite3_vtab *pVTab); + int (*xRollback)(sqlite3_vtab *pVTab); + int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg); + + int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); +}; + +/* +** CAPI3REF: Virtual Table Indexing Information {F18100} +** KEYWORDS: sqlite3_index_info +** +** The sqlite3_index_info structure and its substructures is used to +** pass information into and receive the reply from the xBestIndex +** method of an sqlite3_module. The fields under **Inputs** are the +** inputs to xBestIndex and are read-only. xBestIndex inserts its +** results into the **Outputs** fields. +** +** The aConstraint[] array records WHERE clause constraints of the +** form: +** +** column OP expr +** +** Where OP is =, <, <=, >, or >=. +** The particular operator is stored +** in aConstraint[].op. The index of the column is stored in +** aConstraint[].iColumn. aConstraint[].usable is TRUE if the +** expr on the right-hand side can be evaluated (and thus the constraint +** is usable) and false if it cannot. +** +** The optimizer automatically inverts terms of the form "expr OP column" +** and makes other simplifications to the WHERE clause in an attempt to +** get as many WHERE clause terms into the form shown above as possible. +** The aConstraint[] array only reports WHERE clause terms in the correct +** form that refer to the particular virtual table being queried. +** +** Information about the ORDER BY clause is stored in aOrderBy[]. +** Each term of aOrderBy records a column of the ORDER BY clause. +** +** The xBestIndex method must fill aConstraintUsage[] with information +** about what parameters to pass to xFilter. If argvIndex>0 then +** the right-hand side of the corresponding aConstraint[] is evaluated +** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit +** is true, then the constraint is assumed to be fully handled by the +** virtual table and is not checked again by SQLite. +** +** The idxNum and idxPtr values are recorded and passed into xFilter. +** sqlite3_free() is used to free idxPtr if needToFreeIdxPtr is true. +** +** The orderByConsumed means that output from xFilter will occur in +** the correct order to satisfy the ORDER BY clause so that no separate +** sorting step is required. +** +** The estimatedCost value is an estimate of the cost of doing the +** particular lookup. A full scan of a table with N entries should have +** a cost of N. A binary search of a table of N entries should have a +** cost of approximately log(N). +*/ +struct sqlite3_index_info { + /* Inputs */ + int nConstraint; /* Number of entries in aConstraint */ + struct sqlite3_index_constraint { + int iColumn; /* Column on left-hand side of constraint */ + unsigned char op; /* Constraint operator */ + unsigned char usable; /* True if this constraint is usable */ + int iTermOffset; /* Used internally - xBestIndex should ignore */ + } *aConstraint; /* Table of WHERE clause constraints */ + int nOrderBy; /* Number of terms in the ORDER BY clause */ + struct sqlite3_index_orderby { + int iColumn; /* Column number */ + unsigned char desc; /* True for DESC. False for ASC. */ + } *aOrderBy; /* The ORDER BY clause */ + + /* Outputs */ + struct sqlite3_index_constraint_usage { + int argvIndex; /* if >0, constraint is part of argv to xFilter */ + unsigned char omit; /* Do not code a test for this constraint */ + } *aConstraintUsage; + int idxNum; /* Number used to identify the index */ + char *idxStr; /* String, possibly obtained from sqlite3_malloc */ + int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ + int orderByConsumed; /* True if output is already ordered */ + double estimatedCost; /* Estimated cost of using this index */ +}; +#define SQLITE_INDEX_CONSTRAINT_EQ 2 +#define SQLITE_INDEX_CONSTRAINT_GT 4 +#define SQLITE_INDEX_CONSTRAINT_LE 8 +#define SQLITE_INDEX_CONSTRAINT_LT 16 +#define SQLITE_INDEX_CONSTRAINT_GE 32 +#define SQLITE_INDEX_CONSTRAINT_MATCH 64 + +/* +** CAPI3REF: Register A Virtual Table Implementation {F18200} +** +** This routine is used to register a new module name with an SQLite +** connection. Module names must be registered before creating new +** virtual tables on the module, or before using preexisting virtual +** tables of the module. +*/ +SQLITE_API int sqlite3_create_module( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *, /* Methods for the module */ + void * /* Client data for xCreate/xConnect */ +); + +/* +** CAPI3REF: Register A Virtual Table Implementation {F18210} +** +** This routine is identical to the sqlite3_create_module() method above, +** except that it allows a destructor function to be specified. It is +** even more experimental than the rest of the virtual tables API. +*/ +SQLITE_API int sqlite3_create_module_v2( + sqlite3 *db, /* SQLite connection to register module with */ + const char *zName, /* Name of the module */ + const sqlite3_module *, /* Methods for the module */ + void *, /* Client data for xCreate/xConnect */ + void(*xDestroy)(void*) /* Module destructor function */ +); + +/* +** CAPI3REF: Virtual Table Instance Object {F18010} +** KEYWORDS: sqlite3_vtab +** +** Every module implementation uses a subclass of the following structure +** to describe a particular instance of the module. Each subclass will +** be tailored to the specific needs of the module implementation. The +** purpose of this superclass is to define certain fields that are common +** to all module implementations. +** +** Virtual tables methods can set an error message by assigning a +** string obtained from sqlite3_mprintf() to zErrMsg. The method should +** take care that any prior string is freed by a call to sqlite3_free() +** prior to assigning a new string to zErrMsg. After the error message +** is delivered up to the client application, the string will be automatically +** freed by sqlite3_free() and the zErrMsg field will be zeroed. Note +** that sqlite3_mprintf() and sqlite3_free() are used on the zErrMsg field +** since virtual tables are commonly implemented in loadable extensions which +** do not have access to sqlite3MPrintf() or sqlite3Free(). +*/ +struct sqlite3_vtab { + const sqlite3_module *pModule; /* The module for this virtual table */ + int nRef; /* Used internally */ + char *zErrMsg; /* Error message from sqlite3_mprintf() */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Virtual Table Cursor Object {F18020} +** KEYWORDS: sqlite3_vtab_cursor +** +** Every module implementation uses a subclass of the following structure +** to describe cursors that point into the virtual table and are used +** to loop through the virtual table. Cursors are created using the +** xOpen method of the module. Each module implementation will define +** the content of a cursor structure to suit its own needs. +** +** This superclass exists in order to define fields of the cursor that +** are common to all implementations. +*/ +struct sqlite3_vtab_cursor { + sqlite3_vtab *pVtab; /* Virtual table of this cursor */ + /* Virtual table implementations will typically add additional fields */ +}; + +/* +** CAPI3REF: Declare The Schema Of A Virtual Table {F18280} +** +** The xCreate and xConnect methods of a module use the following API +** to declare the format (the names and datatypes of the columns) of +** the virtual tables they implement. +*/ +SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zCreateTable); + +/* +** CAPI3REF: Overload A Function For A Virtual Table {F18300} +** +** Virtual tables can provide alternative implementations of functions +** using the xFindFunction method. But global versions of those functions +** must exist in order to be overloaded. +** +** This API makes sure a global version of a function with a particular +** name and number of parameters exists. If no such function exists +** before this API is called, a new function is created. The implementation +** of the new function always causes an exception to be thrown. So +** the new function is not good for anything by itself. Its only +** purpose is to be a place-holder function that can be overloaded +** by virtual tables. +** +** This API should be considered part of the virtual table interface, +** which is experimental and subject to change. +*/ +SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); + +/* +** The interface to the virtual-table mechanism defined above (back up +** to a comment remarkably similar to this one) is currently considered +** to be experimental. The interface might change in incompatible ways. +** If this is a problem for you, do not use the interface at this time. +** +** When the virtual-table mechanism stabilizes, we will declare the +** interface fixed, support it indefinitely, and remove this comment. +** +****** EXPERIMENTAL - subject to change without notice ************** +*/ + +/* +** CAPI3REF: A Handle To An Open BLOB {F17800} +** +** An instance of this object represents an open BLOB on which +** incremental I/O can be preformed. +** Objects of this type are created by +** [sqlite3_blob_open()] and destroyed by [sqlite3_blob_close()]. +** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** can be used to read or write small subsections of the blob. +** The [sqlite3_blob_bytes()] interface returns the size of the +** blob in bytes. +*/ +typedef struct sqlite3_blob sqlite3_blob; + +/* +** CAPI3REF: Open A BLOB For Incremental I/O {F17810} +** +** This interfaces opens a handle to the blob located +** in row iRow, column zColumn, table zTable in database zDb; +** in other words, the same blob that would be selected by: +** +**
      +**     SELECT zColumn FROM zDb.zTable WHERE rowid = iRow;
      +** 
      {END} +** +** If the flags parameter is non-zero, the blob is opened for +** read and write access. If it is zero, the blob is opened for read +** access. +** +** Note that the database name is not the filename that contains +** the database but rather the symbolic name of the database that +** is assigned when the database is connected using [ATTACH]. +** For the main database file, the database name is "main". For +** TEMP tables, the database name is "temp". +** +** On success, [SQLITE_OK] is returned and the new +** [sqlite3_blob | blob handle] is written to *ppBlob. +** Otherwise an error code is returned and +** any value written to *ppBlob should not be used by the caller. +** This function sets the database-handle error code and message +** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()]. +** +** INVARIANTS: +** +** {F17813} A successful invocation of the [sqlite3_blob_open(D,B,T,C,R,F,P)] +** interface opens an [sqlite3_blob] object P on the blob +** in column C of table T in database B on [database connection] D. +** +** {F17814} A successful invocation of [sqlite3_blob_open(D,...)] starts +** a new transaction on [database connection] D if that connection +** is not already in a transaction. +** +** {F17816} The [sqlite3_blob_open(D,B,T,C,R,F,P)] interface opens the blob +** for read and write access if and only if the F parameter +** is non-zero. +** +** {F17819} The [sqlite3_blob_open()] interface returns [SQLITE_OK] on +** success and an appropriate [error code] on failure. +** +** {F17821} If an error occurs during evaluation of [sqlite3_blob_open(D,...)] +** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] will return +** information approprate for that error. +*/ +SQLITE_API int sqlite3_blob_open( + sqlite3*, + const char *zDb, + const char *zTable, + const char *zColumn, + sqlite3_int64 iRow, + int flags, + sqlite3_blob **ppBlob +); + +/* +** CAPI3REF: Close A BLOB Handle {F17830} +** +** Close an open [sqlite3_blob | blob handle]. +** +** Closing a BLOB shall cause the current transaction to commit +** if there are no other BLOBs, no pending prepared statements, and the +** database connection is in autocommit mode. +** If any writes were made to the BLOB, they might be held in cache +** until the close operation if they will fit. {END} +** Closing the BLOB often forces the changes +** out to disk and so if any I/O errors occur, they will likely occur +** at the time when the BLOB is closed. {F17833} Any errors that occur during +** closing are reported as a non-zero return value. +** +** The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed. +** +** INVARIANTS: +** +** {F17833} The [sqlite3_blob_close(P)] interface closes an +** [sqlite3_blob] object P previously opened using +** [sqlite3_blob_open()]. +** +** {F17836} Closing an [sqlite3_blob] object using +** [sqlite3_blob_close()] shall cause the current transaction to +** commit if there are no other open [sqlite3_blob] objects +** or [prepared statements] on the same [database connection] and +** the [database connection] is in +** [sqlite3_get_autocommit | autocommit mode]. +** +** {F17839} The [sqlite3_blob_close(P)] interfaces closes the +** [sqlite3_blob] object P unconditionally, even if +** [sqlite3_blob_close(P)] returns something other than [SQLITE_OK]. +** +*/ +SQLITE_API int sqlite3_blob_close(sqlite3_blob *); + +/* +** CAPI3REF: Return The Size Of An Open BLOB {F17840} +** +** Return the size in bytes of the blob accessible via the open +** [sqlite3_blob] object in its only argument. +** +** INVARIANTS: +** +** {F17843} The [sqlite3_blob_bytes(P)] interface returns the size +** in bytes of the BLOB that the [sqlite3_blob] object P +** refers to. +*/ +SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); + +/* +** CAPI3REF: Read Data From A BLOB Incrementally {F17850} +** +** This function is used to read data from an open +** [sqlite3_blob | blob-handle] into a caller supplied buffer. +** N bytes of data are copied into buffer +** Z from the open blob, starting at offset iOffset. +** +** If offset iOffset is less than N bytes from the end of the blob, +** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is +** less than zero [SQLITE_ERROR] is returned and no data is read. +** +** On success, SQLITE_OK is returned. Otherwise, an +** [error code] or an [extended error code] is returned. +** +** INVARIANTS: +** +** {F17853} The [sqlite3_blob_read(P,Z,N,X)] interface reads N bytes +** beginning at offset X from +** the blob that [sqlite3_blob] object P refers to +** and writes those N bytes into buffer Z. +** +** {F17856} In [sqlite3_blob_read(P,Z,N,X)] if the size of the blob +** is less than N+X bytes, then the function returns [SQLITE_ERROR] +** and nothing is read from the blob. +** +** {F17859} In [sqlite3_blob_read(P,Z,N,X)] if X or N is less than zero +** then the function returns [SQLITE_ERROR] +** and nothing is read from the blob. +** +** {F17862} The [sqlite3_blob_read(P,Z,N,X)] interface returns [SQLITE_OK] +** if N bytes where successfully read into buffer Z. +** +** {F17865} If the requested read could not be completed, +** the [sqlite3_blob_read(P,Z,N,X)] interface returns an +** appropriate [error code] or [extended error code]. +** +** {F17868} If an error occurs during evaluation of [sqlite3_blob_read(P,...)] +** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] will return +** information approprate for that error, where D is the +** database handle that was used to open blob handle P. +*/ +SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); + +/* +** CAPI3REF: Write Data Into A BLOB Incrementally {F17870} +** +** This function is used to write data into an open +** [sqlite3_blob | blob-handle] from a user supplied buffer. +** n bytes of data are copied from the buffer +** pointed to by z into the open blob, starting at offset iOffset. +** +** If the [sqlite3_blob | blob-handle] passed as the first argument +** was not opened for writing (the flags parameter to [sqlite3_blob_open()] +*** was zero), this function returns [SQLITE_READONLY]. +** +** This function may only modify the contents of the blob; it is +** not possible to increase the size of a blob using this API. +** If offset iOffset is less than n bytes from the end of the blob, +** [SQLITE_ERROR] is returned and no data is written. If n is +** less than zero [SQLITE_ERROR] is returned and no data is written. +** +** On success, SQLITE_OK is returned. Otherwise, an +** [error code] or an [extended error code] is returned. +** +** INVARIANTS: +** +** {F17873} The [sqlite3_blob_write(P,Z,N,X)] interface writes N bytes +** from buffer Z into +** the blob that [sqlite3_blob] object P refers to +** beginning at an offset of X into the blob. +** +** {F17875} The [sqlite3_blob_write(P,Z,N,X)] interface returns +** [SQLITE_READONLY] if the [sqlite3_blob] object P was +** [sqlite3_blob_open | opened] for reading only. +** +** {F17876} In [sqlite3_blob_write(P,Z,N,X)] if the size of the blob +** is less than N+X bytes, then the function returns [SQLITE_ERROR] +** and nothing is written into the blob. +** +** {F17879} In [sqlite3_blob_write(P,Z,N,X)] if X or N is less than zero +** then the function returns [SQLITE_ERROR] +** and nothing is written into the blob. +** +** {F17882} The [sqlite3_blob_write(P,Z,N,X)] interface returns [SQLITE_OK] +** if N bytes where successfully written into blob. +** +** {F17885} If the requested write could not be completed, +** the [sqlite3_blob_write(P,Z,N,X)] interface returns an +** appropriate [error code] or [extended error code]. +** +** {F17888} If an error occurs during evaluation of [sqlite3_blob_write(D,...)] +** then subsequent calls to [sqlite3_errcode(D)], +** [sqlite3_errmsg(D)], and [sqlite3_errmsg16(D)] will return +** information approprate for that error. +*/ +SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); + +/* +** CAPI3REF: Virtual File System Objects {F11200} +** +** A virtual filesystem (VFS) is an [sqlite3_vfs] object +** that SQLite uses to interact +** with the underlying operating system. Most SQLite builds come with a +** single default VFS that is appropriate for the host computer. +** New VFSes can be registered and existing VFSes can be unregistered. +** The following interfaces are provided. +** +** The sqlite3_vfs_find() interface returns a pointer to +** a VFS given its name. Names are case sensitive. +** Names are zero-terminated UTF-8 strings. +** If there is no match, a NULL +** pointer is returned. If zVfsName is NULL then the default +** VFS is returned. +** +** New VFSes are registered with sqlite3_vfs_register(). +** Each new VFS becomes the default VFS if the makeDflt flag is set. +** The same VFS can be registered multiple times without injury. +** To make an existing VFS into the default VFS, register it again +** with the makeDflt flag set. If two different VFSes with the +** same name are registered, the behavior is undefined. If a +** VFS is registered with a name that is NULL or an empty string, +** then the behavior is undefined. +** +** Unregister a VFS with the sqlite3_vfs_unregister() interface. +** If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary. +** +** INVARIANTS: +** +** {F11203} The [sqlite3_vfs_find(N)] interface returns a pointer to the +** registered [sqlite3_vfs] object whose name exactly matches +** the zero-terminated UTF-8 string N, or it returns NULL if +** there is no match. +** +** {F11206} If the N parameter to [sqlite3_vfs_find(N)] is NULL then +** the function returns a pointer to the default [sqlite3_vfs] +** object if there is one, or NULL if there is no default +** [sqlite3_vfs] object. +** +** {F11209} The [sqlite3_vfs_register(P,F)] interface registers the +** well-formed [sqlite3_vfs] object P using the name given +** by the zName field of the object. +** +** {F11212} Using the [sqlite3_vfs_register(P,F)] interface to register +** the same [sqlite3_vfs] object multiple times is a harmless no-op. +** +** {F11215} The [sqlite3_vfs_register(P,F)] interface makes the +** the [sqlite3_vfs] object P the default [sqlite3_vfs] object +** if F is non-zero. +** +** {F11218} The [sqlite3_vfs_unregister(P)] interface unregisters the +** [sqlite3_vfs] object P so that it is no longer returned by +** subsequent calls to [sqlite3_vfs_find()]. +*/ +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); + +/* +** CAPI3REF: Mutexes {F17000} +** +** The SQLite core uses these routines for thread +** synchronization. Though they are intended for internal +** use by SQLite, code that links against SQLite is +** permitted to use any of these routines. +** +** The SQLite source code contains multiple implementations +** of these mutex routines. An appropriate implementation +** is selected automatically at compile-time. The following +** implementations are available in the SQLite core: +** +**
        +**
      • SQLITE_MUTEX_OS2 +**
      • SQLITE_MUTEX_PTHREAD +**
      • SQLITE_MUTEX_W32 +**
      • SQLITE_MUTEX_NOOP +**
      +** +** The SQLITE_MUTEX_NOOP implementation is a set of routines +** that does no real locking and is appropriate for use in +** a single-threaded application. The SQLITE_MUTEX_OS2, +** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations +** are appropriate for use on os/2, unix, and windows. +** +** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex +** implementation is included with the library. The +** mutex interface routines defined here become external +** references in the SQLite library for which implementations +** must be provided by the application. This facility allows an +** application that links against SQLite to provide its own mutex +** implementation without having to modify the SQLite core. +** +** {F17011} The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. {F17012} If it returns NULL +** that means that a mutex could not be allocated. {F17013} SQLite +** will unwind its stack and return an error. {F17014} The argument +** to sqlite3_mutex_alloc() is one of these integer constants: +** +**
        +**
      • SQLITE_MUTEX_FAST +**
      • SQLITE_MUTEX_RECURSIVE +**
      • SQLITE_MUTEX_STATIC_MASTER +**
      • SQLITE_MUTEX_STATIC_MEM +**
      • SQLITE_MUTEX_STATIC_MEM2 +**
      • SQLITE_MUTEX_STATIC_PRNG +**
      • SQLITE_MUTEX_STATIC_LRU +**
      • SQLITE_MUTEX_STATIC_LRU2 +**
      {END} +** +** {F17015} The first two constants cause sqlite3_mutex_alloc() to create +** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END} +** The mutex implementation does not need to make a distinction +** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does +** not want to. {F17016} But SQLite will only request a recursive mutex in +** cases where it really needs one. {END} If a faster non-recursive mutex +** implementation is available on the host platform, the mutex subsystem +** might return such a mutex in response to SQLITE_MUTEX_FAST. +** +** {F17017} The other allowed parameters to sqlite3_mutex_alloc() each return +** a pointer to a static preexisting mutex. {END} Four static mutexes are +** used by the current version of SQLite. Future versions of SQLite +** may add additional static mutexes. Static mutexes are for internal +** use by SQLite only. Applications that use SQLite mutexes should +** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or +** SQLITE_MUTEX_RECURSIVE. +** +** {F17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() +** returns a different mutex on every call. {F17034} But for the static +** mutex types, the same mutex is returned on every call that has +** the same type number. {END} +** +** {F17019} The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. {F17020} SQLite is careful to deallocate every +** dynamic mutex that it allocates. {U17021} The dynamic mutexes must not be in +** use when they are deallocated. {U17022} Attempting to deallocate a static +** mutex results in undefined behavior. {F17023} SQLite never deallocates +** a static mutex. {END} +** +** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. {F17024} If another thread is already within the mutex, +** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return +** SQLITE_BUSY. {F17025} The sqlite3_mutex_try() interface returns SQLITE_OK +** upon successful entry. {F17026} Mutexes created using +** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. +** {F17027} In such cases the, +** mutex must be exited an equal number of times before another thread +** can enter. {U17028} If the same thread tries to enter any other +** kind of mutex more than once, the behavior is undefined. +** {F17029} SQLite will never exhibit +** such behavior in its own use of mutexes. {END} +** +** Some systems (ex: windows95) do not the operation implemented by +** sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() will +** always return SQLITE_BUSY. {F17030} The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior. {END} +** +** {F17031} The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. {U17032} The behavior +** is undefined if the mutex is not currently entered by the +** calling thread or is not currently allocated. {F17033} SQLite will +** never do either. {END} +** +** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. +*/ +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Verifcation Routines {F17080} +** +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines +** are intended for use inside assert() statements. {F17081} The SQLite core +** never uses these routines except inside an assert() and applications +** are advised to follow the lead of the core. {F17082} The core only +** provides implementations for these routines when it is compiled +** with the SQLITE_DEBUG flag. {U17087} External mutex implementations +** are only required to provide these routines if SQLITE_DEBUG is +** defined and if NDEBUG is not defined. +** +** {F17083} These routines should return true if the mutex in their argument +** is held or not held, respectively, by the calling thread. {END} +** +** {X17084} The implementation is not required to provided versions of these +** routines that actually work. +** If the implementation does not provide working +** versions of these routines, it should at least provide stubs +** that always return true so that one does not get spurious +** assertion failures. {END} +** +** {F17085} If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. {END} This seems counter-intuitive since +** clearly the mutex cannot be held if it does not exist. But the +** the reason the mutex does not exist is because the build is not +** using mutexes. And we do not want the assert() containing the +** call to sqlite3_mutex_held() to fail, so a non-zero return is +** the appropriate thing to do. {F17086} The sqlite3_mutex_notheld() +** interface should also return 1 when given a NULL pointer. +*/ +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); + +/* +** CAPI3REF: Mutex Types {F17001} +** +** {F17002} The [sqlite3_mutex_alloc()] interface takes a single argument +** which is one of these integer constants. {END} +*/ +#define SQLITE_MUTEX_FAST 0 +#define SQLITE_MUTEX_RECURSIVE 1 +#define SQLITE_MUTEX_STATIC_MASTER 2 +#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ +#define SQLITE_MUTEX_STATIC_MEM2 4 /* sqlite3_release_memory() */ +#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ +#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ + +/* +** CAPI3REF: Low-Level Control Of Database Files {F11300} +** +** {F11301} The [sqlite3_file_control()] interface makes a direct call to the +** xFileControl method for the [sqlite3_io_methods] object associated +** with a particular database identified by the second argument. {F11302} The +** name of the database is the name assigned to the database by the +** ATTACH SQL command that opened the +** database. {F11303} To control the main database file, use the name "main" +** or a NULL pointer. {F11304} The third and fourth parameters to this routine +** are passed directly through to the second and third parameters of +** the xFileControl method. {F11305} The return value of the xFileControl +** method becomes the return value of this routine. +** +** {F11306} If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. {F11307} This error +** code is not remembered and will not be recalled by [sqlite3_errcode()] +** or [sqlite3_errmsg()]. {U11308} The underlying xFileControl method might +** also return SQLITE_ERROR. {U11309} There is no way to distinguish between +** an incorrect zDbName and an SQLITE_ERROR return from the underlying +** xFileControl method. {END} +** +** See also: [SQLITE_FCNTL_LOCKSTATE] +*/ +SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); + +/* +** CAPI3REF: Testing Interface {F11400} +** +** The sqlite3_test_control() interface is used to read out internal +** state of SQLite and to inject faults into SQLite for testing +** purposes. The first parameter a operation code that determines +** the number, meaning, and operation of all subsequent parameters. +** +** This interface is not for use by applications. It exists solely +** for verifying the correct operation of the SQLite library. Depending +** on how the SQLite library is compiled, this interface might not exist. +** +** The details of the operation codes, their meanings, the parameters +** they take, and what they do are all subject to change without notice. +** Unlike most of the SQLite API, this function is not guaranteed to +** operate consistently from one release to the next. +*/ +SQLITE_API int sqlite3_test_control(int op, ...); + +/* +** CAPI3REF: Testing Interface Operation Codes {F11410} +** +** These constants are the valid operation code parameters used +** as the first argument to [sqlite3_test_control()]. +** +** These parameters and their meansing are subject to change +** without notice. These values are for testing purposes only. +** Applications should not use any of these parameters or the +** [sqlite3_test_control()] interface. +*/ +#define SQLITE_TESTCTRL_FAULT_CONFIG 1 +#define SQLITE_TESTCTRL_FAULT_FAILURES 2 +#define SQLITE_TESTCTRL_FAULT_BENIGN_FAILURES 3 +#define SQLITE_TESTCTRL_FAULT_PENDING 4 +#define SQLITE_TESTCTRL_PRNG_SAVE 5 +#define SQLITE_TESTCTRL_PRNG_RESTORE 6 +#define SQLITE_TESTCTRL_PRNG_RESET 7 +#define SQLITE_TESTCTRL_BITVEC_TEST 8 + + +/* +** Undo the hack that converts floating point types to integer for +** builds on processors without floating point support. +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# undef double +#endif + +#if 0 +} /* End of the 'extern "C"' block */ +#endif +#endif + +/************** End of sqlite3.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include hash.h in the middle of sqliteInt.h ******************/ +/************** Begin file hash.h ********************************************/ +/* +** 2001 September 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This is the header file for the generic hash-table implemenation +** used in SQLite. +** +** $Id: hash.h,v 1.11 2007/09/04 14:31:47 danielk1977 Exp $ +*/ +#ifndef _SQLITE_HASH_H_ +#define _SQLITE_HASH_H_ + +/* Forward declarations of structures. */ +typedef struct Hash Hash; +typedef struct HashElem HashElem; + +/* A complete hash table is an instance of the following structure. +** The internals of this structure are intended to be opaque -- client +** code should not attempt to access or modify the fields of this structure +** directly. Change this structure only by using the routines below. +** However, many of the "procedures" and "functions" for modifying and +** accessing this structure are really macros, so we can't really make +** this structure opaque. +*/ +struct Hash { + char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */ + char copyKey; /* True if copy of key made on insert */ + int count; /* Number of entries in this table */ + int htsize; /* Number of buckets in the hash table */ + HashElem *first; /* The first element of the array */ + struct _ht { /* the hash table */ + int count; /* Number of entries with this hash */ + HashElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* Each element in the hash table is an instance of the following +** structure. All elements are stored on a single doubly-linked list. +** +** Again, this structure is intended to be opaque, but it can't really +** be opaque because it is used by macros. +*/ +struct HashElem { + HashElem *next, *prev; /* Next and previous elements in the table */ + void *data; /* Data associated with this element */ + void *pKey; int nKey; /* Key associated with this element */ +}; + +/* +** There are 4 different modes of operation for a hash table: +** +** SQLITE_HASH_INT nKey is used as the key and pKey is ignored. +** +** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored. +** +** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long +** (including the null-terminator, if any). Case +** is ignored in comparisons. +** +** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long. +** memcmp() is used to compare keys. +** +** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY +** if the copyKey parameter to HashInit is 1. +*/ +/* #define SQLITE_HASH_INT 1 // NOT USED */ +/* #define SQLITE_HASH_POINTER 2 // NOT USED */ +#define SQLITE_HASH_STRING 3 +#define SQLITE_HASH_BINARY 4 + +/* +** Access routines. To delete, insert a NULL pointer. +*/ +SQLITE_PRIVATE void sqlite3HashInit(Hash*, int keytype, int copyKey); +SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData); +SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const void *pKey, int nKey); +SQLITE_PRIVATE HashElem *sqlite3HashFindElem(const Hash*, const void *pKey, int nKey); +SQLITE_PRIVATE void sqlite3HashClear(Hash*); + +/* +** Macros for looping over all elements of a hash table. The idiom is +** like this: +** +** Hash h; +** HashElem *p; +** ... +** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ +** SomeStructure *pData = sqliteHashData(p); +** // do something with pData +** } +*/ +#define sqliteHashFirst(H) ((H)->first) +#define sqliteHashNext(E) ((E)->next) +#define sqliteHashData(E) ((E)->data) +#define sqliteHashKey(E) ((E)->pKey) +#define sqliteHashKeysize(E) ((E)->nKey) + +/* +** Number of entries in a hash table +*/ +#define sqliteHashCount(H) ((H)->count) + +#endif /* _SQLITE_HASH_H_ */ + +/************** End of hash.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include parse.h in the middle of sqliteInt.h *****************/ +/************** Begin file parse.h *******************************************/ +#define TK_SEMI 1 +#define TK_EXPLAIN 2 +#define TK_QUERY 3 +#define TK_PLAN 4 +#define TK_BEGIN 5 +#define TK_TRANSACTION 6 +#define TK_DEFERRED 7 +#define TK_IMMEDIATE 8 +#define TK_EXCLUSIVE 9 +#define TK_COMMIT 10 +#define TK_END 11 +#define TK_ROLLBACK 12 +#define TK_CREATE 13 +#define TK_TABLE 14 +#define TK_IF 15 +#define TK_NOT 16 +#define TK_EXISTS 17 +#define TK_TEMP 18 +#define TK_LP 19 +#define TK_RP 20 +#define TK_AS 21 +#define TK_COMMA 22 +#define TK_ID 23 +#define TK_ABORT 24 +#define TK_AFTER 25 +#define TK_ANALYZE 26 +#define TK_ASC 27 +#define TK_ATTACH 28 +#define TK_BEFORE 29 +#define TK_CASCADE 30 +#define TK_CAST 31 +#define TK_CONFLICT 32 +#define TK_DATABASE 33 +#define TK_DESC 34 +#define TK_DETACH 35 +#define TK_EACH 36 +#define TK_FAIL 37 +#define TK_FOR 38 +#define TK_IGNORE 39 +#define TK_INITIALLY 40 +#define TK_INSTEAD 41 +#define TK_LIKE_KW 42 +#define TK_MATCH 43 +#define TK_KEY 44 +#define TK_OF 45 +#define TK_OFFSET 46 +#define TK_PRAGMA 47 +#define TK_RAISE 48 +#define TK_REPLACE 49 +#define TK_RESTRICT 50 +#define TK_ROW 51 +#define TK_TRIGGER 52 +#define TK_VACUUM 53 +#define TK_VIEW 54 +#define TK_VIRTUAL 55 +#define TK_REINDEX 56 +#define TK_RENAME 57 +#define TK_CTIME_KW 58 +#define TK_ANY 59 +#define TK_OR 60 +#define TK_AND 61 +#define TK_IS 62 +#define TK_BETWEEN 63 +#define TK_IN 64 +#define TK_ISNULL 65 +#define TK_NOTNULL 66 +#define TK_NE 67 +#define TK_EQ 68 +#define TK_GT 69 +#define TK_LE 70 +#define TK_LT 71 +#define TK_GE 72 +#define TK_ESCAPE 73 +#define TK_BITAND 74 +#define TK_BITOR 75 +#define TK_LSHIFT 76 +#define TK_RSHIFT 77 +#define TK_PLUS 78 +#define TK_MINUS 79 +#define TK_STAR 80 +#define TK_SLASH 81 +#define TK_REM 82 +#define TK_CONCAT 83 +#define TK_COLLATE 84 +#define TK_UMINUS 85 +#define TK_UPLUS 86 +#define TK_BITNOT 87 +#define TK_STRING 88 +#define TK_JOIN_KW 89 +#define TK_CONSTRAINT 90 +#define TK_DEFAULT 91 +#define TK_NULL 92 +#define TK_PRIMARY 93 +#define TK_UNIQUE 94 +#define TK_CHECK 95 +#define TK_REFERENCES 96 +#define TK_AUTOINCR 97 +#define TK_ON 98 +#define TK_DELETE 99 +#define TK_UPDATE 100 +#define TK_INSERT 101 +#define TK_SET 102 +#define TK_DEFERRABLE 103 +#define TK_FOREIGN 104 +#define TK_DROP 105 +#define TK_UNION 106 +#define TK_ALL 107 +#define TK_EXCEPT 108 +#define TK_INTERSECT 109 +#define TK_SELECT 110 +#define TK_DISTINCT 111 +#define TK_DOT 112 +#define TK_FROM 113 +#define TK_JOIN 114 +#define TK_USING 115 +#define TK_ORDER 116 +#define TK_BY 117 +#define TK_GROUP 118 +#define TK_HAVING 119 +#define TK_LIMIT 120 +#define TK_WHERE 121 +#define TK_INTO 122 +#define TK_VALUES 123 +#define TK_INTEGER 124 +#define TK_FLOAT 125 +#define TK_BLOB 126 +#define TK_REGISTER 127 +#define TK_VARIABLE 128 +#define TK_CASE 129 +#define TK_WHEN 130 +#define TK_THEN 131 +#define TK_ELSE 132 +#define TK_INDEX 133 +#define TK_ALTER 134 +#define TK_TO 135 +#define TK_ADD 136 +#define TK_COLUMNKW 137 +#define TK_TO_TEXT 138 +#define TK_TO_BLOB 139 +#define TK_TO_NUMERIC 140 +#define TK_TO_INT 141 +#define TK_TO_REAL 142 +#define TK_END_OF_FILE 143 +#define TK_ILLEGAL 144 +#define TK_SPACE 145 +#define TK_UNCLOSED_STRING 146 +#define TK_COMMENT 147 +#define TK_FUNCTION 148 +#define TK_COLUMN 149 +#define TK_AGG_FUNCTION 150 +#define TK_AGG_COLUMN 151 +#define TK_CONST_FUNC 152 + +/************** End of parse.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +#include +#include +#include +#include +#include + +/* +** If compiling for a processor that lacks floating point support, +** substitute integer for floating-point +*/ +#ifdef SQLITE_OMIT_FLOATING_POINT +# define double sqlite_int64 +# define LONGDOUBLE_TYPE sqlite_int64 +# ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (0x7fffffffffffffff) +# endif +# define SQLITE_OMIT_DATETIME_FUNCS 1 +# define SQLITE_OMIT_TRACE 1 +# undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +#endif +#ifndef SQLITE_BIG_DBL +# define SQLITE_BIG_DBL (1e99) +#endif + +/* +** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 +** afterward. Having this macro allows us to cause the C compiler +** to omit code used by TEMP tables without messy #ifndef statements. +*/ +#ifdef SQLITE_OMIT_TEMPDB +#define OMIT_TEMPDB 1 +#else +#define OMIT_TEMPDB 0 +#endif + +/* +** If the following macro is set to 1, then NULL values are considered +** distinct when determining whether or not two entries are the same +** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL, +** OCELOT, and Firebird all work. The SQL92 spec explicitly says this +** is the way things are suppose to work. +** +** If the following macro is set to 0, the NULLs are indistinct for +** a UNIQUE index. In this mode, you can only have a single NULL entry +** for a column declared UNIQUE. This is the way Informix and SQL Server +** work. +*/ +#define NULL_DISTINCT_FOR_UNIQUE 1 + +/* +** The "file format" number is an integer that is incremented whenever +** the VDBE-level file format changes. The following macros define the +** the default file format for new databases and the maximum file format +** that the library can read. +*/ +#define SQLITE_MAX_FILE_FORMAT 4 +#ifndef SQLITE_DEFAULT_FILE_FORMAT +# define SQLITE_DEFAULT_FILE_FORMAT 1 +#endif + +/* +** Provide a default value for TEMP_STORE in case it is not specified +** on the command-line +*/ +#ifndef TEMP_STORE +# define TEMP_STORE 1 +#endif + +/* +** GCC does not define the offsetof() macro so we'll have to do it +** ourselves. +*/ +#ifndef offsetof +#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) +#endif + +/* +** Check to see if this machine uses EBCDIC. (Yes, believe it or +** not, there are still machines out there that use EBCDIC.) +*/ +#if 'A' == '\301' +# define SQLITE_EBCDIC 1 +#else +# define SQLITE_ASCII 1 +#endif + +/* +** Integers of known sizes. These typedefs might change for architectures +** where the sizes very. Preprocessor macros are available so that the +** types can be conveniently redefined at compile-type. Like this: +** +** cc '-DUINTPTR_TYPE=long long int' ... +*/ +#ifndef UINT32_TYPE +# ifdef HAVE_UINT32_T +# define UINT32_TYPE uint32_t +# else +# define UINT32_TYPE unsigned int +# endif +#endif +#ifndef UINT16_TYPE +# ifdef HAVE_UINT16_T +# define UINT16_TYPE uint16_t +# else +# define UINT16_TYPE unsigned short int +# endif +#endif +#ifndef INT16_TYPE +# ifdef HAVE_INT16_T +# define INT16_TYPE int16_t +# else +# define INT16_TYPE short int +# endif +#endif +#ifndef UINT8_TYPE +# ifdef HAVE_UINT8_T +# define UINT8_TYPE uint8_t +# else +# define UINT8_TYPE unsigned char +# endif +#endif +#ifndef INT8_TYPE +# ifdef HAVE_INT8_T +# define INT8_TYPE int8_t +# else +# define INT8_TYPE signed char +# endif +#endif +#ifndef LONGDOUBLE_TYPE +# define LONGDOUBLE_TYPE long double +#endif +typedef sqlite_int64 i64; /* 8-byte signed integer */ +typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ +typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ +typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ +typedef INT16_TYPE i16; /* 2-byte signed integer */ +typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ +typedef UINT8_TYPE i8; /* 1-byte signed integer */ + +/* +** Macros to determine whether the machine is big or little endian, +** evaluated at runtime. +*/ +#ifdef SQLITE_AMALGAMATION +SQLITE_PRIVATE const int sqlite3one; +#else +SQLITE_PRIVATE const int sqlite3one; +#endif +#if defined(i386) || defined(__i386__) || defined(_M_IX86) +# define SQLITE_BIGENDIAN 0 +# define SQLITE_LITTLEENDIAN 1 +# define SQLITE_UTF16NATIVE SQLITE_UTF16LE +#else +# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) +# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) +# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) +#endif + +/* +** Constants for the largest and smallest possible 64-bit signed integers. +** These macros are designed to work correctly on both 32-bit and 64-bit +** compilers. +*/ +#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) +#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) + +/* +** An instance of the following structure is used to store the busy-handler +** callback for a given sqlite handle. +** +** The sqlite.busyHandler member of the sqlite struct contains the busy +** callback for the database handle. Each pager opened via the sqlite +** handle is passed a pointer to sqlite.busyHandler. The busy-handler +** callback is currently invoked only from within pager.c. +*/ +typedef struct BusyHandler BusyHandler; +struct BusyHandler { + int (*xFunc)(void *,int); /* The busy callback */ + void *pArg; /* First arg to busy callback */ + int nBusy; /* Incremented with each busy call */ +}; + +/* +** Name of the master database table. The master database table +** is a special table that holds the names and attributes of all +** user tables and indices. +*/ +#define MASTER_NAME "sqlite_master" +#define TEMP_MASTER_NAME "sqlite_temp_master" + +/* +** The root-page of the master database table. +*/ +#define MASTER_ROOT 1 + +/* +** The name of the schema table. +*/ +#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) + +/* +** A convenience macro that returns the number of elements in +** an array. +*/ +#define ArraySize(X) (sizeof(X)/sizeof(X[0])) + +/* +** Forward references to structures +*/ +typedef struct AggInfo AggInfo; +typedef struct AuthContext AuthContext; +typedef struct Bitvec Bitvec; +typedef struct CollSeq CollSeq; +typedef struct Column Column; +typedef struct Db Db; +typedef struct Schema Schema; +typedef struct Expr Expr; +typedef struct ExprList ExprList; +typedef struct FKey FKey; +typedef struct FuncDef FuncDef; +typedef struct IdList IdList; +typedef struct Index Index; +typedef struct KeyClass KeyClass; +typedef struct KeyInfo KeyInfo; +typedef struct Module Module; +typedef struct NameContext NameContext; +typedef struct Parse Parse; +typedef struct Select Select; +typedef struct SrcList SrcList; +typedef struct StrAccum StrAccum; +typedef struct Table Table; +typedef struct TableLock TableLock; +typedef struct Token Token; +typedef struct TriggerStack TriggerStack; +typedef struct TriggerStep TriggerStep; +typedef struct Trigger Trigger; +typedef struct WhereInfo WhereInfo; +typedef struct WhereLevel WhereLevel; + +/* +** Defer sourcing vdbe.h and btree.h until after the "u8" and +** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque +** pointer types (i.e. FuncDef) defined above. +*/ +/************** Include btree.h in the middle of sqliteInt.h *****************/ +/************** Begin file btree.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite B-Tree file +** subsystem. See comments in the source code for a detailed description +** of what each interface routine does. +** +** @(#) $Id: btree.h,v 1.98 2008/04/26 13:39:47 drh Exp $ +*/ +#ifndef _BTREE_H_ +#define _BTREE_H_ + +/* TODO: This definition is just included so other modules compile. It +** needs to be revisited. +*/ +#define SQLITE_N_BTREE_META 10 + +/* +** If defined as non-zero, auto-vacuum is enabled by default. Otherwise +** it must be turned on for each database using "PRAGMA auto_vacuum = 1". +*/ +#ifndef SQLITE_DEFAULT_AUTOVACUUM + #define SQLITE_DEFAULT_AUTOVACUUM 0 +#endif + +#define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ +#define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ +#define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ + +/* +** Forward declarations of structure +*/ +typedef struct Btree Btree; +typedef struct BtCursor BtCursor; +typedef struct BtShared BtShared; +typedef struct BtreeMutexArray BtreeMutexArray; + +/* +** This structure records all of the Btrees that need to hold +** a mutex before we enter sqlite3VdbeExec(). The Btrees are +** are placed in aBtree[] in order of aBtree[]->pBt. That way, +** we can always lock and unlock them all quickly. +*/ +struct BtreeMutexArray { + int nMutex; + Btree *aBtree[SQLITE_MAX_ATTACHED+1]; +}; + + +SQLITE_PRIVATE int sqlite3BtreeOpen( + const char *zFilename, /* Name of database file to open */ + sqlite3 *db, /* Associated database connection */ + Btree **, /* Return open Btree* here */ + int flags, /* Flags */ + int vfsFlags /* Flags passed through to VFS open */ +); + +/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the +** following values. +** +** NOTE: These values must match the corresponding PAGER_ values in +** pager.h. +*/ +#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ +#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ +#define BTREE_MEMORY 4 /* In-memory DB. No argument */ +#define BTREE_READONLY 8 /* Open the database in read-only mode */ +#define BTREE_READWRITE 16 /* Open for both reading and writing */ +#define BTREE_CREATE 32 /* Create the database if it does not exist */ + +/* Additional values for the 4th argument of sqlite3BtreeOpen that +** are not associated with PAGER_ values. +*/ +#define BTREE_PRIVATE 64 /* Never share with other connections */ + +SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int); +SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree*,int,int); +SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); +SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); +SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); +SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*); +SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCommitStmt(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollbackStmt(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); +SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInStmt(Btree*); +SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); +SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); +SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *); +SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *, int, u8); + +SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); +SQLITE_PRIVATE const char *sqlite3BtreeGetDirname(Btree *); +SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); +SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); + +SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); + +/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR +** of the following flags: +*/ +#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ +#define BTREE_ZERODATA 2 /* Table has keys only - no data */ +#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ + +SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); +SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int); +SQLITE_PRIVATE int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); +SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); + +struct UnpackedRecord; /* Forward declaration. Definition in vdbeaux.c. */ + +SQLITE_PRIVATE int sqlite3BtreeCursor( + Btree*, /* BTree containing table to open */ + int iTable, /* Index of root page */ + int wrFlag, /* 1 for writing. 0 for read-only */ + struct KeyInfo*, /* First argument to compare function */ + BtCursor *pCursor /* Space to write cursor structure */ +); +SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); + +SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeMoveto( + BtCursor*, + const void *pKey, + struct UnpackedRecord *pUnKey, + i64 nKey, + int bias, + int *pRes +); +SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, + const void *pData, int nData, + int nZero, int bias); +SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreeFlags(BtCursor*); +SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); +SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); +SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE sqlite3 *sqlite3BtreeCursorDb(const BtCursor*); +SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); +SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); +SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); + +SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); +SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); + +SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); +SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); + +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); +SQLITE_PRIVATE int sqlite3BtreePageDump(Btree*, int, int recursive); +#endif + +/* +** If we are not using shared cache, then there is no need to +** use mutexes to access the BtShared structures. So make the +** Enter and Leave procedures no-ops. +*/ +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); +SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); +SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); +SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); +SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); +SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); +SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); +SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); +#else +# define sqlite3BtreeEnter(X) +# define sqlite3BtreeLeave(X) +# define sqlite3BtreeHoldsMutex(X) 1 +# define sqlite3BtreeEnterCursor(X) +# define sqlite3BtreeLeaveCursor(X) +# define sqlite3BtreeEnterAll(X) +# define sqlite3BtreeLeaveAll(X) +# define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3BtreeMutexArrayEnter(X) +# define sqlite3BtreeMutexArrayLeave(X) +# define sqlite3BtreeMutexArrayInsert(X,Y) +#endif + + +#endif /* _BTREE_H_ */ + +/************** End of btree.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include vdbe.h in the middle of sqliteInt.h ******************/ +/************** Begin file vdbe.h ********************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Header file for the Virtual DataBase Engine (VDBE) +** +** This header defines the interface to the virtual database engine +** or VDBE. The VDBE implements an abstract machine that runs a +** simple program to access and modify the underlying database. +** +** $Id: vdbe.h,v 1.131 2008/05/01 17:03:49 drh Exp $ +*/ +#ifndef _SQLITE_VDBE_H_ +#define _SQLITE_VDBE_H_ + +/* +** A single VDBE is an opaque structure named "Vdbe". Only routines +** in the source file sqliteVdbe.c are allowed to see the insides +** of this structure. +*/ +typedef struct Vdbe Vdbe; + +/* +** The names of the following types declared in vdbeInt.h are required +** for the VdbeOp definition. +*/ +typedef struct VdbeFunc VdbeFunc; +typedef struct Mem Mem; +typedef struct UnpackedRecord UnpackedRecord; + +/* +** A single instruction of the virtual machine has an opcode +** and as many as three operands. The instruction is recorded +** as an instance of the following structure: +*/ +struct VdbeOp { + u8 opcode; /* What operation to perform */ + signed char p4type; /* One of the P4_xxx constants for p4 */ + u8 opflags; /* Not currently used */ + u8 p5; /* Fifth parameter is an unsigned character */ + int p1; /* First operand */ + int p2; /* Second parameter (often the jump destination) */ + int p3; /* The third parameter */ + union { /* forth parameter */ + int i; /* Integer value if p4type==P4_INT32 */ + void *p; /* Generic pointer */ + char *z; /* Pointer to data for string (char array) types */ + i64 *pI64; /* Used when p4type is P4_INT64 */ + double *pReal; /* Used when p4type is P4_REAL */ + FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ + VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ + CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ + Mem *pMem; /* Used when p4type is P4_MEM */ + sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */ + KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ + } p4; +#ifdef SQLITE_DEBUG + char *zComment; /* Comment to improve readability */ +#endif +#ifdef VDBE_PROFILE + int cnt; /* Number of times this instruction was executed */ + long long cycles; /* Total time spend executing this instruction */ +#endif +}; +typedef struct VdbeOp VdbeOp; + +/* +** A smaller version of VdbeOp used for the VdbeAddOpList() function because +** it takes up less space. +*/ +struct VdbeOpList { + u8 opcode; /* What operation to perform */ + signed char p1; /* First operand */ + signed char p2; /* Second parameter (often the jump destination) */ + signed char p3; /* Third parameter */ +}; +typedef struct VdbeOpList VdbeOpList; + +/* +** Allowed values of VdbeOp.p3type +*/ +#define P4_NOTUSED 0 /* The P4 parameter is not used */ +#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ +#define P4_STATIC (-2) /* Pointer to a static string */ +#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ +#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ +#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ +#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ +#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ +#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ +#define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ +#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ +#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ + +/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure +** is made. That copy is freed when the Vdbe is finalized. But if the +** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still +** gets freed when the Vdbe is finalized so it still should be obtained +** from a single sqliteMalloc(). But no copy is made and the calling +** function should *not* try to free the KeyInfo. +*/ +#define P4_KEYINFO_HANDOFF (-9) + +/* +** The Vdbe.aColName array contains 5n Mem structures, where n is the +** number of columns of data returned by the statement. +*/ +#define COLNAME_NAME 0 +#define COLNAME_DECLTYPE 1 +#define COLNAME_DATABASE 2 +#define COLNAME_TABLE 3 +#define COLNAME_COLUMN 4 +#ifdef SQLITE_ENABLE_COLUMN_METADATA +# define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ +#else +# ifdef SQLITE_OMIT_DECLTYPE +# define COLNAME_N 1 /* Store only the name */ +# else +# define COLNAME_N 2 /* Store the name and decltype */ +# endif +#endif + +/* +** The following macro converts a relative address in the p2 field +** of a VdbeOp structure into a negative number so that +** sqlite3VdbeAddOpList() knows that the address is relative. Calling +** the macro again restores the address. +*/ +#define ADDR(X) (-1-(X)) + +/* +** The makefile scans the vdbe.c source file and creates the "opcodes.h" +** header file that defines a number for each opcode used by the VDBE. +*/ +/************** Include opcodes.h in the middle of vdbe.h ********************/ +/************** Begin file opcodes.h *****************************************/ +/* Automatically generated. Do not edit */ +/* See the mkopcodeh.awk script for details */ +#define OP_VNext 1 +#define OP_Affinity 2 +#define OP_Column 3 +#define OP_SetCookie 4 +#define OP_Real 125 /* same as TK_FLOAT */ +#define OP_Sequence 5 +#define OP_MoveGt 6 +#define OP_Ge 72 /* same as TK_GE */ +#define OP_RowKey 7 +#define OP_SCopy 8 +#define OP_Eq 68 /* same as TK_EQ */ +#define OP_OpenWrite 9 +#define OP_NotNull 66 /* same as TK_NOTNULL */ +#define OP_If 10 +#define OP_ToInt 141 /* same as TK_TO_INT */ +#define OP_String8 88 /* same as TK_STRING */ +#define OP_VRowid 11 +#define OP_CollSeq 12 +#define OP_OpenRead 13 +#define OP_Expire 14 +#define OP_AutoCommit 15 +#define OP_Gt 69 /* same as TK_GT */ +#define OP_IntegrityCk 17 +#define OP_Sort 18 +#define OP_Copy 19 +#define OP_Trace 20 +#define OP_Function 21 +#define OP_IfNeg 22 +#define OP_And 61 /* same as TK_AND */ +#define OP_Subtract 79 /* same as TK_MINUS */ +#define OP_Noop 23 +#define OP_Return 24 +#define OP_Remainder 82 /* same as TK_REM */ +#define OP_NewRowid 25 +#define OP_Multiply 80 /* same as TK_STAR */ +#define OP_Variable 26 +#define OP_String 27 +#define OP_RealAffinity 28 +#define OP_VRename 29 +#define OP_ParseSchema 30 +#define OP_VOpen 31 +#define OP_Close 32 +#define OP_CreateIndex 33 +#define OP_IsUnique 34 +#define OP_NotFound 35 +#define OP_Int64 36 +#define OP_MustBeInt 37 +#define OP_Halt 38 +#define OP_Rowid 39 +#define OP_IdxLT 40 +#define OP_AddImm 41 +#define OP_Statement 42 +#define OP_RowData 43 +#define OP_MemMax 44 +#define OP_Or 60 /* same as TK_OR */ +#define OP_NotExists 45 +#define OP_Gosub 46 +#define OP_Divide 81 /* same as TK_SLASH */ +#define OP_Integer 47 +#define OP_ToNumeric 140 /* same as TK_TO_NUMERIC*/ +#define OP_Prev 48 +#define OP_Concat 83 /* same as TK_CONCAT */ +#define OP_BitAnd 74 /* same as TK_BITAND */ +#define OP_VColumn 49 +#define OP_CreateTable 50 +#define OP_Last 51 +#define OP_IsNull 65 /* same as TK_ISNULL */ +#define OP_IncrVacuum 52 +#define OP_IdxRowid 53 +#define OP_ShiftRight 77 /* same as TK_RSHIFT */ +#define OP_ResetCount 54 +#define OP_FifoWrite 55 +#define OP_ContextPush 56 +#define OP_DropTrigger 57 +#define OP_DropIndex 58 +#define OP_IdxGE 59 +#define OP_IdxDelete 62 +#define OP_Vacuum 63 +#define OP_MoveLe 64 +#define OP_IfNot 73 +#define OP_DropTable 84 +#define OP_MakeRecord 85 +#define OP_ToBlob 139 /* same as TK_TO_BLOB */ +#define OP_ResultRow 86 +#define OP_Delete 89 +#define OP_AggFinal 90 +#define OP_ShiftLeft 76 /* same as TK_LSHIFT */ +#define OP_Goto 91 +#define OP_TableLock 92 +#define OP_FifoRead 93 +#define OP_Clear 94 +#define OP_MoveLt 95 +#define OP_Le 70 /* same as TK_LE */ +#define OP_VerifyCookie 96 +#define OP_AggStep 97 +#define OP_ToText 138 /* same as TK_TO_TEXT */ +#define OP_Not 16 /* same as TK_NOT */ +#define OP_ToReal 142 /* same as TK_TO_REAL */ +#define OP_SetNumColumns 98 +#define OP_Transaction 99 +#define OP_VFilter 100 +#define OP_Ne 67 /* same as TK_NE */ +#define OP_VDestroy 101 +#define OP_ContextPop 102 +#define OP_BitOr 75 /* same as TK_BITOR */ +#define OP_Next 103 +#define OP_IdxInsert 104 +#define OP_Lt 71 /* same as TK_LT */ +#define OP_Insert 105 +#define OP_Destroy 106 +#define OP_ReadCookie 107 +#define OP_ForceInt 108 +#define OP_LoadAnalysis 109 +#define OP_Explain 110 +#define OP_OpenPseudo 111 +#define OP_OpenEphemeral 112 +#define OP_Null 113 +#define OP_Move 114 +#define OP_Blob 115 +#define OP_Add 78 /* same as TK_PLUS */ +#define OP_Rewind 116 +#define OP_MoveGe 117 +#define OP_VBegin 118 +#define OP_VUpdate 119 +#define OP_IfZero 120 +#define OP_BitNot 87 /* same as TK_BITNOT */ +#define OP_VCreate 121 +#define OP_Found 122 +#define OP_IfPos 123 +#define OP_NullRow 124 + +/* The following opcode values are never used */ +#define OP_NotUsed_126 126 +#define OP_NotUsed_127 127 +#define OP_NotUsed_128 128 +#define OP_NotUsed_129 129 +#define OP_NotUsed_130 130 +#define OP_NotUsed_131 131 +#define OP_NotUsed_132 132 +#define OP_NotUsed_133 133 +#define OP_NotUsed_134 134 +#define OP_NotUsed_135 135 +#define OP_NotUsed_136 136 +#define OP_NotUsed_137 137 + + +/* Properties such as "out2" or "jump" that are specified in +** comments following the "case" for each opcode in the vdbe.c +** are encoded into bitvectors as follows: +*/ +#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ +#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ +#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ +#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */ +#define OPFLG_INITIALIZER {\ +/* 0 */ 0x00, 0x01, 0x00, 0x00, 0x10, 0x02, 0x11, 0x00,\ +/* 8 */ 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00,\ +/* 16 */ 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,\ +/* 24 */ 0x00, 0x02, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,\ +/* 32 */ 0x00, 0x02, 0x11, 0x11, 0x02, 0x05, 0x00, 0x02,\ +/* 40 */ 0x11, 0x04, 0x00, 0x00, 0x0c, 0x11, 0x01, 0x02,\ +/* 48 */ 0x01, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00, 0x04,\ +/* 56 */ 0x00, 0x00, 0x00, 0x11, 0x2c, 0x2c, 0x00, 0x00,\ +/* 64 */ 0x11, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ +/* 72 */ 0x15, 0x05, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\ +/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x00, 0x00, 0x00, 0x04,\ +/* 88 */ 0x02, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x11,\ +/* 96 */ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,\ +/* 104 */ 0x08, 0x00, 0x02, 0x02, 0x05, 0x00, 0x00, 0x00,\ +/* 112 */ 0x00, 0x02, 0x00, 0x02, 0x01, 0x11, 0x00, 0x00,\ +/* 120 */ 0x05, 0x00, 0x11, 0x05, 0x00, 0x02, 0x00, 0x00,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 136 */ 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04,} + +/************** End of opcodes.h *********************************************/ +/************** Continuing where we left off in vdbe.h ***********************/ + +/* +** Prototypes for the VDBE interface. See comments on the implementation +** for a description of what each of these routines does. +*/ +SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); +SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); +SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); +SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); +SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); +SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); +SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); +SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); +SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); +SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); +SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); +SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); +SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); +#endif +SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*, int); +SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); +SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int); +SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); +SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n); +SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); + +#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +SQLITE_PRIVATE int sqlite3VdbeReleaseMemory(int); +#endif +SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,void*,int); +SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); + + +#ifndef NDEBUG +SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); +# define VdbeComment(X) sqlite3VdbeComment X +#else +# define VdbeComment(X) +#endif + +#endif + +/************** End of vdbe.h ************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include pager.h in the middle of sqliteInt.h *****************/ +/************** Begin file pager.h *******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the interface that the sqlite page cache +** subsystem. The page cache subsystem reads and writes a file a page +** at a time and provides a journal for rollback. +** +** @(#) $Id: pager.h,v 1.72 2008/05/01 17:03:49 drh Exp $ +*/ + +#ifndef _PAGER_H_ +#define _PAGER_H_ + +/* +** The type used to represent a page number. The first page in a file +** is called page 1. 0 is used to represent "not a page". +*/ +typedef unsigned int Pgno; + +/* +** Each open file is managed by a separate instance of the "Pager" structure. +*/ +typedef struct Pager Pager; + +/* +** Handle type for pages. +*/ +typedef struct PgHdr DbPage; + +/* +** Allowed values for the flags parameter to sqlite3PagerOpen(). +** +** NOTE: This values must match the corresponding BTREE_ values in btree.h. +*/ +#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ +#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ + +/* +** Valid values for the second argument to sqlite3PagerLockingMode(). +*/ +#define PAGER_LOCKINGMODE_QUERY -1 +#define PAGER_LOCKINGMODE_NORMAL 0 +#define PAGER_LOCKINGMODE_EXCLUSIVE 1 + +/* +** Valid values for the second argument to sqlite3PagerJournalMode(). +*/ +#define PAGER_JOURNALMODE_QUERY -1 +#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ +#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ +#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ + +/* +** See source code comments for a detailed description of the following +** routines: +*/ +SQLITE_PRIVATE int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int); +SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, BusyHandler *pBusyHandler); +SQLITE_PRIVATE void sqlite3PagerSetDestructor(Pager*, void(*)(DbPage*,int)); +SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*,int)); +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*); +SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); +SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); +SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); +SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); +#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) +SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); +SQLITE_PRIVATE int sqlite3PagerRef(DbPage*); +SQLITE_PRIVATE int sqlite3PagerUnref(DbPage*); +SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); +SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*); +SQLITE_PRIVATE int sqlite3PagerTruncate(Pager*,Pgno); +SQLITE_PRIVATE int sqlite3PagerBegin(DbPage*, int exFlag); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno, int); +SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); +SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); +SQLITE_PRIVATE int sqlite3PagerIsreadonly(Pager*); +SQLITE_PRIVATE int sqlite3PagerStmtBegin(Pager*); +SQLITE_PRIVATE int sqlite3PagerStmtCommit(Pager*); +SQLITE_PRIVATE int sqlite3PagerStmtRollback(Pager*); +SQLITE_PRIVATE void sqlite3PagerDontRollback(DbPage*); +SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); +SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); +SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); +SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*); +SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); +SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerDirname(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); +SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); +SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno); +SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); +SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); +SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerJournalMode(Pager *, int); +SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); +SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); + +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) && !defined(SQLITE_OMIT_DISKIO) +SQLITE_PRIVATE int sqlite3PagerReleaseMemory(int); +#endif + +#ifdef SQLITE_HAS_CODEC +SQLITE_PRIVATE void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*); +#endif + +#if !defined(NDEBUG) || defined(SQLITE_TEST) +SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); +SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); +#endif + +#ifdef SQLITE_TEST +SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); +SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); +#endif + +#ifdef SQLITE_TEST +void disable_simulated_io_errors(void); +void enable_simulated_io_errors(void); +#else +# define disable_simulated_io_errors() +# define enable_simulated_io_errors() +#endif + +#endif /* _PAGER_H_ */ + +/************** End of pager.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/************** Include os.h in the middle of sqliteInt.h ********************/ +/************** Begin file os.h **********************************************/ +/* +** 2001 September 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This header file (together with is companion C source-code file +** "os.c") attempt to abstract the underlying operating system so that +** the SQLite library will work on both POSIX and windows systems. +** +** This header file is #include-ed by sqliteInt.h and thus ends up +** being included by every source file. +*/ +#ifndef _SQLITE_OS_H_ +#define _SQLITE_OS_H_ + +/* +** Figure out if we are dealing with Unix, Windows, or some other +** operating system. After the following block of preprocess macros, +** all of OS_UNIX, OS_WIN, OS_OS2, and OS_OTHER will defined to either +** 1 or 0. One of the four will be 1. The other three will be 0. +*/ +#if defined(OS_OTHER) +# if OS_OTHER==1 +# undef OS_UNIX +# define OS_UNIX 0 +# undef OS_WIN +# define OS_WIN 0 +# undef OS_OS2 +# define OS_OS2 0 +# else +# undef OS_OTHER +# endif +#endif +#if !defined(OS_UNIX) && !defined(OS_OTHER) +# define OS_OTHER 0 +# ifndef OS_WIN +# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) +# define OS_WIN 1 +# define OS_UNIX 0 +# define OS_OS2 0 +# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__) +# define OS_WIN 0 +# define OS_UNIX 0 +# define OS_OS2 1 +# else +# define OS_WIN 0 +# define OS_UNIX 1 +# define OS_OS2 0 +# endif +# else +# define OS_UNIX 0 +# define OS_OS2 0 +# endif +#else +# ifndef OS_WIN +# define OS_WIN 0 +# endif +#endif + + + +/* +** Define the maximum size of a temporary filename +*/ +#if OS_WIN +# include +# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) +#elif OS_OS2 +# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY) +# include /* has to be included before os2.h for linking to work */ +# endif +# define INCL_DOSDATETIME +# define INCL_DOSFILEMGR +# define INCL_DOSERRORS +# define INCL_DOSMISC +# define INCL_DOSPROCESS +# define INCL_DOSMODULEMGR +# define INCL_DOSSEMAPHORES +# include +# include +# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP) +#else +# define SQLITE_TEMPNAME_SIZE 200 +#endif + +/* If the SET_FULLSYNC macro is not defined above, then make it +** a no-op +*/ +#ifndef SET_FULLSYNC +# define SET_FULLSYNC(x,y) +#endif + +/* +** The default size of a disk sector +*/ +#ifndef SQLITE_DEFAULT_SECTOR_SIZE +# define SQLITE_DEFAULT_SECTOR_SIZE 512 +#endif + +/* +** Temporary files are named starting with this prefix followed by 16 random +** alphanumeric characters, and no file extension. They are stored in the +** OS's standard temporary file directory, and are deleted prior to exit. +** If sqlite is being embedded in another program, you may wish to change the +** prefix to reflect your program's name, so that if your program exits +** prematurely, old temporary files can be easily identified. This can be done +** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. +** +** 2006-10-31: The default prefix used to be "sqlite_". But then +** Mcafee started using SQLite in their anti-virus product and it +** started putting files with the "sqlite" name in the c:/temp folder. +** This annoyed many windows users. Those users would then do a +** Google search for "sqlite", find the telephone numbers of the +** developers and call to wake them up at night and complain. +** For this reason, the default name prefix is changed to be "sqlite" +** spelled backwards. So the temp files are still identified, but +** anybody smart enough to figure out the code is also likely smart +** enough to know that calling the developer will not help get rid +** of the file. +*/ +#ifndef SQLITE_TEMP_FILE_PREFIX +# define SQLITE_TEMP_FILE_PREFIX "etilqs_" +#endif + +/* +** The following values may be passed as the second argument to +** sqlite3OsLock(). The various locks exhibit the following semantics: +** +** SHARED: Any number of processes may hold a SHARED lock simultaneously. +** RESERVED: A single process may hold a RESERVED lock on a file at +** any time. Other processes may hold and obtain new SHARED locks. +** PENDING: A single process may hold a PENDING lock on a file at +** any one time. Existing SHARED locks may persist, but no new +** SHARED locks may be obtained by other processes. +** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. +** +** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a +** process that requests an EXCLUSIVE lock may actually obtain a PENDING +** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to +** sqlite3OsLock(). +*/ +#define NO_LOCK 0 +#define SHARED_LOCK 1 +#define RESERVED_LOCK 2 +#define PENDING_LOCK 3 +#define EXCLUSIVE_LOCK 4 + +/* +** File Locking Notes: (Mostly about windows but also some info for Unix) +** +** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because +** those functions are not available. So we use only LockFile() and +** UnlockFile(). +** +** LockFile() prevents not just writing but also reading by other processes. +** A SHARED_LOCK is obtained by locking a single randomly-chosen +** byte out of a specific range of bytes. The lock byte is obtained at +** random so two separate readers can probably access the file at the +** same time, unless they are unlucky and choose the same lock byte. +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. +** +** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, +** which means we can use reader/writer locks. When reader/writer locks +** are used, the lock is placed on the same range of bytes that is used +** for probabilistic locking in Win95/98/ME. Hence, the locking scheme +** will support two or more Win95 readers or two or more WinNT readers. +** But a single Win95 reader will lock out all WinNT readers and a single +** WinNT reader will lock out all other Win95 readers. +** +** The following #defines specify the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. +** +** These #defines are available in sqlite_aux.h so that adaptors for +** connecting SQLite to other operating systems can use the same byte +** ranges for locking. In particular, the same locking strategy and +** byte ranges are used for Unix. This leaves open the possiblity of having +** clients on win95, winNT, and unix all talking to the same shared file +** and all locking correctly. To do so would require that samba (or whatever +** tool is being used for file sharing) implements locks correctly between +** windows and unix. I'm guessing that isn't likely to happen, but by +** using the same locking range we are at least open to the possibility. +** +** Locking in windows is manditory. For this reason, we cannot store +** actual data in the bytes used for locking. The pager never allocates +** the pages involved in locking therefore. SHARED_SIZE is selected so +** that all locks will fit on a single page even at the minimum page size. +** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE +** is set high so that we don't have to allocate an unused page except +** for very large databases. But one should test the page skipping logic +** by setting PENDING_BYTE low and running the entire regression suite. +** +** Changing the value of PENDING_BYTE results in a subtly incompatible +** file format. Depending on how it is changed, you might not notice +** the incompatibility right away, even running a full regression test. +** The default location of PENDING_BYTE is the first byte past the +** 1GB boundary. +** +*/ +#ifndef SQLITE_TEST +#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */ +#else +SQLITE_API extern unsigned int sqlite3_pending_byte; +#define PENDING_BYTE sqlite3_pending_byte +#endif + +#define RESERVED_BYTE (PENDING_BYTE+1) +#define SHARED_FIRST (PENDING_BYTE+2) +#define SHARED_SIZE 510 + +/* +** Functions for accessing sqlite3_file methods +*/ +SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); +SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); +SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); +SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); + +/* +** Functions for accessing sqlite3_vfs methods +*/ +SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); +SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int); +SQLITE_PRIVATE int sqlite3OsGetTempname(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); +SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); +SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE void *sqlite3OsDlSym(sqlite3_vfs *, void *, const char *); +SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); +SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); +SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); +SQLITE_PRIVATE int sqlite3OsCurrentTime(sqlite3_vfs *, double*); + +/* +** Convenience functions for opening and closing files using +** sqlite3_malloc() to obtain space for the file-handle structure. +*/ +SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); +SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); + +/* +** Each OS-specific backend defines an instance of the following +** structure for returning a pointer to its sqlite3_vfs. If OS_OTHER +** is defined (meaning that the application-defined OS interface layer +** is used) then there is no default VFS. The application must +** register one or more VFS structures using sqlite3_vfs_register() +** before attempting to use SQLite. +*/ +SQLITE_PRIVATE sqlite3_vfs *sqlite3OsDefaultVfs(void); + +#endif /* _SQLITE_OS_H_ */ + +/************** End of os.h **************************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ +/************** Include mutex.h in the middle of sqliteInt.h *****************/ +/************** Begin file mutex.h *******************************************/ +/* +** 2007 August 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the common header for all mutex implementations. +** The sqliteInt.h header #includes this file so that it is available +** to all source files. We break it out in an effort to keep the code +** better organized. +** +** NOTE: source files should *not* #include this header file directly. +** Source files should #include the sqliteInt.h file and let that file +** include this one indirectly. +** +** $Id: mutex.h,v 1.2 2007/08/30 14:10:30 drh Exp $ +*/ + + +#ifdef SQLITE_MUTEX_APPDEF +/* +** If SQLITE_MUTEX_APPDEF is defined, then this whole module is +** omitted and equivalent functionality must be provided by the +** application that links against the SQLite library. +*/ +#else +/* +** Figure out what version of the code to use. The choices are +** +** SQLITE_MUTEX_NOOP For single-threaded applications that +** do not desire error checking. +** +** SQLITE_MUTEX_NOOP_DEBUG For single-threaded applications with +** error checking to help verify that mutexes +** are being used correctly even though they +** are not needed. Used when SQLITE_DEBUG is +** defined on single-threaded builds. +** +** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. +** +** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. +** +** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2. +*/ +#define SQLITE_MUTEX_NOOP 1 /* The default */ +#if defined(SQLITE_DEBUG) && !SQLITE_THREADSAFE +# undef SQLITE_MUTEX_NOOP +# define SQLITE_MUTEX_NOOP_DEBUG +#endif +#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && OS_UNIX +# undef SQLITE_MUTEX_NOOP +# define SQLITE_MUTEX_PTHREADS +#endif +#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && OS_WIN +# undef SQLITE_MUTEX_NOOP +# define SQLITE_MUTEX_W32 +#endif +#if defined(SQLITE_MUTEX_NOOP) && SQLITE_THREADSAFE && OS_OS2 +# undef SQLITE_MUTEX_NOOP +# define SQLITE_MUTEX_OS2 +#endif + +#ifdef SQLITE_MUTEX_NOOP +/* +** If this is a no-op implementation, implement everything as macros. +*/ +#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) +#define sqlite3_mutex_free(X) +#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_try(X) SQLITE_OK +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) 1 +#define sqlite3_mutex_notheld(X) 1 +#endif + +#endif /* SQLITE_MUTEX_APPDEF */ + +/************** End of mutex.h ***********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + + +/* +** Each database file to be accessed by the system is an instance +** of the following structure. There are normally two of these structures +** in the sqlite.aDb[] array. aDb[0] is the main database file and +** aDb[1] is the database file used to hold temporary tables. Additional +** databases may be attached. +*/ +struct Db { + char *zName; /* Name of this database */ + Btree *pBt; /* The B*Tree structure for this database file */ + u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ + u8 safety_level; /* How aggressive at synching data to disk */ + void *pAux; /* Auxiliary data. Usually NULL */ + void (*xFreeAux)(void*); /* Routine to free pAux */ + Schema *pSchema; /* Pointer to database schema (possibly shared) */ +}; + +/* +** An instance of the following structure stores a database schema. +** +** If there are no virtual tables configured in this schema, the +** Schema.db variable is set to NULL. After the first virtual table +** has been added, it is set to point to the database connection +** used to create the connection. Once a virtual table has been +** added to the Schema structure and the Schema.db variable populated, +** only that database connection may use the Schema to prepare +** statements. +*/ +struct Schema { + int schema_cookie; /* Database schema version number for this file */ + Hash tblHash; /* All tables indexed by name */ + Hash idxHash; /* All (named) indices indexed by name */ + Hash trigHash; /* All triggers indexed by name */ + Hash aFKey; /* Foreign keys indexed by to-table */ + Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ + u8 file_format; /* Schema format version for this file */ + u8 enc; /* Text encoding used by this database */ + u16 flags; /* Flags associated with this schema */ + int cache_size; /* Number of pages to use in the cache */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + sqlite3 *db; /* "Owner" connection. See comment above */ +#endif +}; + +/* +** These macros can be used to test, set, or clear bits in the +** Db.flags field. +*/ +#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) +#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) +#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) +#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) + +/* +** Allowed values for the DB.flags field. +** +** The DB_SchemaLoaded flag is set after the database schema has been +** read into internal hash tables. +** +** DB_UnresetViews means that one or more views have column names that +** have been filled out. If the schema changes, these column names might +** changes and so the view will need to be reset. +*/ +#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ +#define DB_UnresetViews 0x0002 /* Some views have defined column names */ +#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ + +/* +** The number of different kinds of things that can be limited +** using the sqlite3_limit() interface. +*/ +#define SQLITE_N_LIMIT (SQLITE_LIMIT_VARIABLE_NUMBER+1) + +/* +** Each database is an instance of the following structure. +** +** The sqlite.lastRowid records the last insert rowid generated by an +** insert statement. Inserts on views do not affect its value. Each +** trigger has its own context, so that lastRowid can be updated inside +** triggers as usual. The previous value will be restored once the trigger +** exits. Upon entering a before or instead of trigger, lastRowid is no +** longer (since after version 2.8.12) reset to -1. +** +** The sqlite.nChange does not count changes within triggers and keeps no +** context. It is reset at start of sqlite3_exec. +** The sqlite.lsChange represents the number of changes made by the last +** insert, update, or delete statement. It remains constant throughout the +** length of a statement and is then updated by OP_SetCounts. It keeps a +** context stack just like lastRowid so that the count of changes +** within a trigger is not seen outside the trigger. Changes to views do not +** affect the value of lsChange. +** The sqlite.csChange keeps track of the number of current changes (since +** the last statement) and is used to update sqlite_lsChange. +** +** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 +** store the most recent error code and, if applicable, string. The +** internal function sqlite3Error() is used to set these variables +** consistently. +*/ +struct sqlite3 { + sqlite3_vfs *pVfs; /* OS Interface */ + int nDb; /* Number of backends currently in use */ + Db *aDb; /* All backends */ + int flags; /* Miscellanous flags. See below */ + int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + int errCode; /* Most recent error code (SQLITE_*) */ + int errMask; /* & result codes with this before returning */ + u8 autoCommit; /* The auto-commit flag. */ + u8 temp_store; /* 1: file 2: memory 0: default */ + u8 mallocFailed; /* True if we have seen a malloc failure */ + u8 dfltLockMode; /* Default locking-mode for attached dbs */ + u8 dfltJournalMode; /* Default journal mode for attached dbs */ + signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + int nextPagesize; /* Pagesize after VACUUM if >0 */ + int nTable; /* Number of tables in the database */ + CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + i64 lastRowid; /* ROWID of most recent insert (see above) */ + i64 priorNewRowid; /* Last randomly generated ROWID */ + int magic; /* Magic number for detect library misuse */ + int nChange; /* Value returned by sqlite3_changes() */ + int nTotalChange; /* Value returned by sqlite3_total_changes() */ + sqlite3_mutex *mutex; /* Connection mutex */ + int aLimit[SQLITE_N_LIMIT]; /* Limits */ + struct sqlite3InitInfo { /* Information used during initialization */ + int iDb; /* When back is being initialized */ + int newTnum; /* Rootpage of table being initialized */ + u8 busy; /* TRUE if currently initializing */ + } init; + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared libraray handles */ + struct Vdbe *pVdbe; /* List of active virtual machines */ + int activeVdbeCnt; /* Number of vdbes currently executing */ + void (*xTrace)(void*,const char*); /* Trace function */ + void *pTraceArg; /* Argument to the trace function */ + void (*xProfile)(void*,const char*,u64); /* Profiling function */ + void *pProfileArg; /* Argument to profile function */ + void *pCommitArg; /* Argument to xCommitCallback() */ + int (*xCommitCallback)(void*); /* Invoked at every commit. */ + void *pRollbackArg; /* Argument to xRollbackCallback() */ + void (*xRollbackCallback)(void*); /* Invoked at every commit. */ + void *pUpdateArg; + void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); + void *pCollNeededArg; + sqlite3_value *pErr; /* Most recent error message */ + char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ + char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ + union { + int isInterrupted; /* True if sqlite3_interrupt has been called */ + double notUsed1; /* Spacer */ + } u1; +#ifndef SQLITE_OMIT_AUTHORIZATION + int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); + /* Access authorization function */ + void *pAuthArg; /* 1st argument to the access auth function */ +#endif +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int (*xProgress)(void *); /* The progress callback */ + void *pProgressArg; /* Argument to the progress callback */ + int nProgressOps; /* Number of opcodes for progress callback */ +#endif +#ifndef SQLITE_OMIT_VIRTUALTABLE + Hash aModule; /* populated by sqlite3_create_module() */ + Table *pVTab; /* vtab with active Connect/Create method */ + sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */ + int nVTrans; /* Allocated size of aVTrans */ +#endif + Hash aFunc; /* All functions that can be in SQL exprs */ + Hash aCollSeq; /* All collating sequences */ + BusyHandler busyHandler; /* Busy callback */ + int busyTimeout; /* Busy handler timeout, in msec */ + Db aDbStatic[2]; /* Static space for the 2 default backends */ +#ifdef SQLITE_SSE + sqlite3_stmt *pFetch; /* Used by SSE to fetch stored statements */ +#endif +}; + +/* +** A macro to discover the encoding of a database. +*/ +#define ENC(db) ((db)->aDb[0].pSchema->enc) + +/* +** Possible values for the sqlite.flags and or Db.flags fields. +** +** On sqlite.flags, the SQLITE_InTrans value means that we have +** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement +** transaction is active on that particular database file. +*/ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ +#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ +#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ + /* DELETE, or UPDATE and return */ + /* the count using a callback. */ +#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ + /* result set is empty */ +#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */ +#define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when + ** accessing read-only databases */ +#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x00004000 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00010000 /* Use full fsync on the backend */ +#define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ + +#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ +#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ +#define SQLITE_Vtab 0x00100000 /* There exists a virtual table */ + +/* +** Possible values for the sqlite.magic field. +** The numbers are obtained at random and have no special meaning, other +** than being distinct from one another. +*/ +#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ +#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ +#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ +#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ +#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ + +/* +** Each SQL function is defined by an instance of the following +** structure. A pointer to this structure is stored in the sqlite.aFunc +** hash table. When multiple functions have the same name, the hash table +** points to a linked list of these structures. +*/ +struct FuncDef { + i16 nArg; /* Number of arguments. -1 means unlimited */ + u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ + u8 needCollSeq; /* True if sqlite3GetFuncCollSeq() might be called */ + u8 flags; /* Some combination of SQLITE_FUNC_* */ + void *pUserData; /* User data parameter */ + FuncDef *pNext; /* Next function with same name */ + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ + void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ + void (*xFinalize)(sqlite3_context*); /* Aggregate finializer */ + char zName[1]; /* SQL name of the function. MUST BE LAST */ +}; + +/* +** Each SQLite module (virtual table definition) is defined by an +** instance of the following structure, stored in the sqlite3.aModule +** hash table. +*/ +struct Module { + const sqlite3_module *pModule; /* Callback pointers */ + const char *zName; /* Name passed to create_module() */ + void *pAux; /* pAux passed to create_module() */ + void (*xDestroy)(void *); /* Module destructor function */ +}; + +/* +** Possible values for FuncDef.flags +*/ +#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ +#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ +#define SQLITE_FUNC_EPHEM 0x04 /* Ephermeral. Delete with VDBE */ + +/* +** information about each column of an SQL table is held in an instance +** of this structure. +*/ +struct Column { + char *zName; /* Name of this column */ + Expr *pDflt; /* Default value of this column */ + char *zType; /* Data type for this column */ + char *zColl; /* Collating sequence. If NULL, use the default */ + u8 notNull; /* True if there is a NOT NULL constraint */ + u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ + char affinity; /* One of the SQLITE_AFF_... values */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + u8 isHidden; /* True if this column is 'hidden' */ +#endif +}; + +/* +** A "Collating Sequence" is defined by an instance of the following +** structure. Conceptually, a collating sequence consists of a name and +** a comparison routine that defines the order of that sequence. +** +** There may two seperate implementations of the collation function, one +** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that +** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine +** native byte order. When a collation sequence is invoked, SQLite selects +** the version that will require the least expensive encoding +** translations, if any. +** +** The CollSeq.pUser member variable is an extra parameter that passed in +** as the first argument to the UTF-8 comparison function, xCmp. +** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function, +** xCmp16. +** +** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the +** collating sequence is undefined. Indices built on an undefined +** collating sequence may not be read or written. +*/ +struct CollSeq { + char *zName; /* Name of the collating sequence, UTF-8 encoded */ + u8 enc; /* Text encoding handled by xCmp() */ + u8 type; /* One of the SQLITE_COLL_... values below */ + void *pUser; /* First argument to xCmp() */ + int (*xCmp)(void*,int, const void*, int, const void*); + void (*xDel)(void*); /* Destructor for pUser */ +}; + +/* +** Allowed values of CollSeq flags: +*/ +#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */ +#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */ +#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */ +#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */ + +/* +** A sort order can be either ASC or DESC. +*/ +#define SQLITE_SO_ASC 0 /* Sort in ascending order */ +#define SQLITE_SO_DESC 1 /* Sort in ascending order */ + +/* +** Column affinity types. +** +** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and +** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve +** the speed a little by number the values consecutively. +** +** But rather than start with 0 or 1, we begin with 'a'. That way, +** when multiple affinity types are concatenated into a string and +** used as the P4 operand, they will be more readable. +** +** Note also that the numeric types are grouped together so that testing +** for a numeric type is a single comparison. +*/ +#define SQLITE_AFF_TEXT 'a' +#define SQLITE_AFF_NONE 'b' +#define SQLITE_AFF_NUMERIC 'c' +#define SQLITE_AFF_INTEGER 'd' +#define SQLITE_AFF_REAL 'e' + +#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) + +/* +** The SQLITE_AFF_MASK values masks off the significant bits of an +** affinity value. +*/ +#define SQLITE_AFF_MASK 0x67 + +/* +** Additional bit values that can be ORed with an affinity without +** changing the affinity. +*/ +#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ +#define SQLITE_NULLEQUAL 0x10 /* compare NULLs equal */ +#define SQLITE_STOREP2 0x80 /* Store result in reg[P2] rather than jump */ + +/* +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Table.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Table.nCol is the number of columns in this table. Table.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of +** the column that is that key. Otherwise Table.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. Table.hasPrimKey is true if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Table.tnum is the page number for the root BTree page of the table in the +** database file. If Table.iDb is the index of the database table backend +** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that +** holds temporary tables and indices. If Table.isEphem +** is true, then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Table.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. +*/ +struct Table { + char *zName; /* Name of the table */ + int nCol; /* Number of columns in this table */ + Column *aCol; /* Information about each column */ + int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */ + Index *pIndex; /* List of SQL indexes on this table. */ + int tnum; /* Root BTree node for this table (see note above) */ + Select *pSelect; /* NULL for tables. Points to definition if a view. */ + int nRef; /* Number of pointers to this Table */ + Trigger *pTrigger; /* List of SQL triggers on this table */ + FKey *pFKey; /* Linked list of all foreign keys in this table */ + char *zColAff; /* String defining the affinity of each column */ +#ifndef SQLITE_OMIT_CHECK + Expr *pCheck; /* The AND of all CHECK constraints */ +#endif +#ifndef SQLITE_OMIT_ALTERTABLE + int addColOffset; /* Offset in CREATE TABLE statement to add a new column */ +#endif + u8 readOnly; /* True if this table should not be written by the user */ + u8 isEphem; /* True if created using OP_OpenEphermeral */ + u8 hasPrimKey; /* True if there exists a primary key */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ + u8 autoInc; /* True if the integer primary key is autoincrement */ +#ifndef SQLITE_OMIT_VIRTUALTABLE + u8 isVirtual; /* True if this is a virtual table */ + u8 isCommit; /* True once the CREATE TABLE has been committed */ + Module *pMod; /* Pointer to the implementation of the module */ + sqlite3_vtab *pVtab; /* Pointer to the module instance */ + int nModuleArg; /* Number of arguments to the module */ + char **azModuleArg; /* Text of all module args. [0] is module name */ +#endif + Schema *pSchema; /* Schema that contains this table */ +}; + +/* +** Test to see whether or not a table is a virtual table. This is +** done as a macro so that it will be optimized out when virtual +** table support is omitted from the build. +*/ +#ifndef SQLITE_OMIT_VIRTUALTABLE +# define IsVirtual(X) ((X)->isVirtual) +# define IsHiddenColumn(X) ((X)->isHidden) +#else +# define IsVirtual(X) 0 +# define IsHiddenColumn(X) 0 +#endif + +/* +** Each foreign key constraint is an instance of the following structure. +** +** A foreign key is associated with two tables. The "from" table is +** the table that contains the REFERENCES clause that creates the foreign +** key. The "to" table is the table that is named in the REFERENCES clause. +** Consider this example: +** +** CREATE TABLE ex1( +** a INTEGER PRIMARY KEY, +** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) +** ); +** +** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". +** +** Each REFERENCES clause generates an instance of the following structure +** which is attached to the from-table. The to-table need not exist when +** the from-table is created. The existance of the to-table is not checked +** until an attempt is made to insert data into the from-table. +** +** The sqlite.aFKey hash table stores pointers to this structure +** given the name of a to-table. For each to-table, all foreign keys +** associated with that table are on a linked list using the FKey.pNextTo +** field. +*/ +struct FKey { + Table *pFrom; /* The table that constains the REFERENCES clause */ + FKey *pNextFrom; /* Next foreign key in pFrom */ + char *zTo; /* Name of table that the key points to */ + FKey *pNextTo; /* Next foreign key that points to zTo */ + int nCol; /* Number of columns in this key */ + struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ + int iFrom; /* Index of column in pFrom */ + char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ + } *aCol; /* One entry for each of nCol column s */ + u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ + u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ + u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ + u8 insertConf; /* How to resolve conflicts that occur on INSERT */ +}; + +/* +** SQLite supports many different ways to resolve a constraint +** error. ROLLBACK processing means that a constraint violation +** causes the operation in process to fail and for the current transaction +** to be rolled back. ABORT processing means the operation in process +** fails and any prior changes from that one operation are backed out, +** but the transaction is not rolled back. FAIL processing means that +** the operation in progress stops and returns an error code. But prior +** changes due to the same operation are not backed out and no rollback +** occurs. IGNORE means that the particular row that caused the constraint +** error is not inserted or updated. Processing continues and no error +** is returned. REPLACE means that preexisting database rows that caused +** a UNIQUE constraint violation are removed so that the new insert or +** update can proceed. Processing continues and no error is reported. +** +** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. +** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the +** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign +** key is set to NULL. CASCADE means that a DELETE or UPDATE of the +** referenced table row is propagated into the row that holds the +** foreign key. +** +** The following symbolic values are used to record which type +** of action to take. +*/ +#define OE_None 0 /* There is no constraint to check */ +#define OE_Rollback 1 /* Fail the operation and rollback the transaction */ +#define OE_Abort 2 /* Back out changes but do no rollback transaction */ +#define OE_Fail 3 /* Stop the operation but leave all prior changes */ +#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ +#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ + +#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ +#define OE_SetNull 7 /* Set the foreign key value to NULL */ +#define OE_SetDflt 8 /* Set the foreign key value to its default */ +#define OE_Cascade 9 /* Cascade the changes */ + +#define OE_Default 99 /* Do whatever the default action is */ + + +/* +** An instance of the following structure is passed as the first +** argument to sqlite3VdbeKeyCompare and is used to control the +** comparison of the two index keys. +** +** If the KeyInfo.incrKey value is true and the comparison would +** otherwise be equal, then return a result as if the second key +** were larger. +*/ +struct KeyInfo { + sqlite3 *db; /* The database connection */ + u8 enc; /* Text encoding - one of the TEXT_Utf* values */ + u8 incrKey; /* Increase 2nd key by epsilon before comparison */ + u8 prefixIsEqual; /* Treat a prefix as equal */ + int nField; /* Number of entries in aColl[] */ + u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */ + CollSeq *aColl[1]; /* Collating sequence for each term of the key */ +}; + +/* +** Each SQL index is represented in memory by an +** instance of the following structure. +** +** The columns of the table that are to be indexed are described +** by the aiColumn[] field of this structure. For example, suppose +** we have the following table and index: +** +** CREATE TABLE Ex1(c1 int, c2 int, c3 text); +** CREATE INDEX Ex2 ON Ex1(c3,c1); +** +** In the Table structure describing Ex1, nCol==3 because there are +** three columns in the table. In the Index structure describing +** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. +** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the +** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. +** The second column to be indexed (c1) has an index of 0 in +** Ex1.aCol[], hence Ex2.aiColumn[1]==0. +** +** The Index.onError field determines whether or not the indexed columns +** must be unique and what to do if they are not. When Index.onError=OE_None, +** it means this is not a unique index. Otherwise it is a unique index +** and the value of Index.onError indicate the which conflict resolution +** algorithm to employ whenever an attempt is made to insert a non-unique +** element. +*/ +struct Index { + char *zName; /* Name of this index */ + int nColumn; /* Number of columns in the table used by this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + int tnum; /* Page containing root of this index in database file */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ +}; + +/* +** Each token coming out of the lexer is an instance of +** this structure. Tokens are also used as part of an expression. +** +** Note if Token.z==0 then Token.dyn and Token.n are undefined and +** may contain random values. Do not make any assuptions about Token.dyn +** and Token.n when Token.z==0. +*/ +struct Token { + const unsigned char *z; /* Text of the token. Not NULL-terminated! */ + unsigned dyn : 1; /* True for malloced memory, false for static */ + unsigned n : 31; /* Number of characters in this token */ +}; + +/* +** An instance of this structure contains information needed to generate +** code for a SELECT that contains aggregate functions. +** +** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a +** pointer to this structure. The Expr.iColumn field is the index in +** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate +** code for that node. +** +** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the +** original Select structure that describes the SELECT statement. These +** fields do not need to be freed when deallocating the AggInfo structure. +*/ +struct AggInfo { + u8 directMode; /* Direct rendering mode means take data directly + ** from source tables rather than from accumulators */ + u8 useSortingIdx; /* In direct mode, reference the sorting index rather + ** than the source table */ + int sortingIdx; /* Cursor number of the sorting index */ + ExprList *pGroupBy; /* The group by clause */ + int nSortingColumn; /* Number of columns in the sorting index */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ + int iTable; /* Cursor number of the source table */ + int iColumn; /* Column number within the source table */ + int iSorterColumn; /* Column number in the sorting index */ + int iMem; /* Memory location that acts as accumulator */ + Expr *pExpr; /* The original expression */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nColumnAlloc; /* Number of slots allocated for aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. + ** Additional columns are used only as parameters to + ** aggregate functions */ + struct AggInfo_func { /* For each aggregate function */ + Expr *pExpr; /* Expression encoding the function */ + FuncDef *pFunc; /* The aggregate function implementation */ + int iMem; /* Memory location that acts as accumulator */ + int iDistinct; /* Ephermeral table used to enforce DISTINCT */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ + int nFuncAlloc; /* Number of slots allocated for aFunc[] */ +}; + +/* +** Each node of an expression in the parse tree is an instance +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list +** of argument if the expression is a function. +** +** Expr.token is the operator token for this node. For some expressions +** that have subexpressions, Expr.token can be the complete text that gave +** rise to the Expr. In the latter case, the token is marked as being +** a compound token. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is a function, the Expr.iTable is an integer code +** representing which function. If the expression is an unbound variable +** marker (a question mark character '?' in the original SQL) then the +** Expr.iTable holds the index number for that variable. +** +** If the expression is a subquery then Expr.iColumn holds an integer +** register number containing the result of the subquery. If the +** subquery gives a constant result, then iTable is -1. If the subquery +** gives a different answer at different times during statement processing +** then iTable is the address of a subroutine that computes the subquery. +** +** The Expr.pSelect field points to a SELECT statement. The SELECT might +** be the right operand of an IN operator. Or, if a scalar SELECT appears +** in an expression the opcode is TK_SELECT and Expr.pSelect is the only +** operand. +** +** If the Expr is of type OP_Column, and the table it is selecting from +** is a disk table or the "old.*" pseudo-table, then pTab points to the +** corresponding table definition. +*/ +struct Expr { + u8 op; /* Operation performed by this node */ + char affinity; /* The affinity of the column or 0 if not a column */ + u16 flags; /* Various flags. See below */ + CollSeq *pColl; /* The collation type of the column or 0 */ + Expr *pLeft, *pRight; /* Left and right subnodes */ + ExprList *pList; /* A list of expressions used as function arguments + ** or in " IN (aCol[] or ->aFunc[] */ + int iRightJoinTable; /* If EP_FromJoin, the right table of the join */ + Select *pSelect; /* When the expression is a sub-select. Also the + ** right side of " IN (