images_scanner.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: images_scanner.cpp
00003  */
00004 
00005 /*
00006  * This file is part of uds-plugin-images.
00007  *
00008  * uds-plugin-images is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * uds-plugin-images is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2008 iRex Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include <string.h>
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #ifndef WIN32
00031 #include <unistd.h>
00032 #endif
00033 
00034 #include <cassert>
00035 #include <iostream>
00036 #include <algorithm>
00037 #include <glib.h>
00038 #include <gdk-pixbuf/gdk-pixbuf.h>
00039 #ifndef WIN32
00040 #include <libexif/exif-data.h>
00041 #endif
00042 
00043 #include "utils.h"
00044 #include "images_scanner.h"
00045 #include "image_page.h"
00046 #include "log.h"
00047 
00048 namespace images
00049 {
00050 
00051 /*
00052  * class ImagesScanner
00053  */
00054 ImagesScanner::ImagesScanner(void)
00055 :filepath("")
00056 ,recursive(false)
00057 ,isdir(false)
00058 ,dir("")
00059 ,active(-1) 
00060 {
00061     LOGPRINTF("entry");
00062 
00063     get_supported_extnames();
00064 }
00065 
00066 ImagesScanner::~ImagesScanner(void)
00067 {
00068     LOGPRINTF("entry");
00069 }
00070 
00071 bool ImagesScanner::is_image(const std::string & filename)
00072 {
00073     // Only check the extension name to imporve the performance of scanning. 
00074     bool ret = check_extname(filename);
00075 
00076     LOGPRINTF("%s is image? %d", filename.c_str(), ret);
00077 
00078     return ret;
00079 }
00080 
00081 bool ImagesScanner::get_size(const std::string & filename,
00082                              int * width, int * height)
00083 {
00084     // Set the default values.
00085     if (width) { *width = 0; }
00086     if (height) { *height = 0; }
00087     
00088     if (gdk_pixbuf_get_file_info(filename.c_str(), width, height))
00089     {
00090         return true;
00091     }
00092     return false;
00093 }
00094 
00095 void ImagesScanner::get_original_rotation(const std::string & filename, 
00096                                           PluginRotationDegree & rotation)
00097 {
00098     LOGPRINTF("entry");
00099 
00100     // Set the default return value.
00101     rotation = Clockwise_Degrees_0;
00102 #ifndef WIN32
00103     ExifData * ped = exif_data_new_from_file (filename.c_str());
00104     if (ped == 0) { return; }
00105 
00106     ExifEntry * pee = exif_data_get_entry(ped, EXIF_TAG_ORIENTATION);
00107     if (pee == 0) 
00108     {
00109         exif_data_unref(ped);
00110         return; 
00111     }
00112 
00113     // CAUTION: The character set of the returned string is not defined.
00114     // It may be UTF-8, latin1, the native encoding of the
00115     // computer, or the native encoding of the camera.
00116     const unsigned int maxlen = 1024;
00117     char val[maxlen] = {0};
00118 
00119     if (exif_entry_get_value (pee, val, maxlen))
00120     {
00121         // Conversion table for EXIT orientation to rotation degrees.
00122         // See also:
00123         //   http://www.impulseadventure.com/photo/exif-orientation.html
00124         //   http://sylvana.net/jpegcrop/exif_orientation.html
00125         const struct
00126               {
00127                   const char                    *exif_orientation;
00128                   const PluginRotationDegree    rotation;
00129               } exif_conversion_tbl[]
00130               =
00131               {
00132                   { "top - left",     Clockwise_Degrees_0   },
00133                   { "top - right",    Clockwise_Degrees_0   },
00134                   { "bottom - right", Clockwise_Degrees_180 },
00135                   { "bottom - left",  Clockwise_Degrees_180 },
00136                   { "left - top",     Clockwise_Degrees_270 },
00137                   { "right - top",    Clockwise_Degrees_270 },
00138                   { "right - bottom", Clockwise_Degrees_90  },
00139                   { "left - bottom",  Clockwise_Degrees_90  },
00140                   { NULL,             Clockwise_Degrees_0   }
00141               };
00142 
00143         for (int i = 0; exif_conversion_tbl[i].exif_orientation; i++)
00144         {
00145             if ( strcmp(val, exif_conversion_tbl[i].exif_orientation) == 0 )
00146             {
00147                 rotation = exif_conversion_tbl[i].rotation;
00148                 break;
00149             }
00150         }
00151     }
00152     
00153     exif_data_unref(ped);
00154     ped = 0;
00155 #endif
00156     LOGPRINTF("%d", rotation);
00157 }
00158 
00159 int ImagesScanner::scan_images(const std::string & filename,
00160                             bool to_scan_dir,
00161                             bool recursive_flag,
00162                             SortType sort_type,
00163                             bool sort_ascending,
00164                             Images & results)
00165 {
00166     set_filename(filename);
00167     recursive = recursive_flag;
00168 
00169     if (!to_scan_dir)
00170     {
00171         if (g_file_test(filename.c_str(), (GFileTest)G_FILE_TEST_IS_REGULAR)
00172                 && is_image(filename))
00173         {
00174             ImagePtr image = new Image;
00175             if (image)
00176             { 
00177                 image->path = filename;
00178                 image->width = -1;
00179                 image->height = -1;
00180                 image->is_rotation_calculated = false;
00181                 image->rotation = Clockwise_Degrees_0;
00182                 results.push_back(image);
00183             }
00184         }
00185     }
00186     else
00187     {
00188         // Do scanning.
00189         scan_dir(dir, results);
00190         // Do sorting.
00191         sort_images(results, sort_type, sort_ascending);
00192     }
00193 
00194     return ((active != -1) ? active : 0);
00195 }
00196 
00197 void ImagesScanner::get_supported_extnames(void)
00198 {
00199     // Get the formats supported by gdk_pixbuf.
00200     GSList * list = gdk_pixbuf_get_formats();
00201     for (GSList * ptr = list; ptr; ptr = ptr->next)
00202     {
00203         GdkPixbufFormat * format = (GdkPixbufFormat*)ptr->data;
00204         gchar ** extensions = gdk_pixbuf_format_get_extensions(format);
00205         if (extensions)
00206         {
00207             for (int i = 0; extensions[i] != 0; i++)
00208             {
00209                 LOGPRINTF("%s", extensions[i]);
00210                 extnames.push_back(extensions[i]);
00211             }
00212             g_strfreev(extensions);
00213         }
00214     }
00215     
00216     if (list)
00217     {
00218         g_slist_free(list);
00219     }
00220 }
00221 
00222 bool ImagesScanner::check_extname(const std::string & filename)
00223 {
00224     bool ret = false;
00225 
00226     char ext[MAX_PATH_LEN];
00227     if (get_ext_name(filename.c_str(), ext, MAX_PATH_LEN))
00228     {
00229         for (unsigned int i = 0; i < extnames.size(); i++)
00230         {
00231             if (!g_ascii_strncasecmp(extnames[i].c_str(), 
00232             ext, MAX_PATH_LEN))
00233             {
00234                 ret = true;
00235             }
00236         }
00237     }
00238 
00239     LOGPRINTF("%s is image? %d", filename.c_str(), ret);
00240 
00241     return ret;
00242 }
00243 
00244 void ImagesScanner::set_filename(const std::string & filename)
00245 {
00246     filepath = filename;
00247 
00248     isdir = (bool)g_file_test(filename.c_str(),
00249             (GFileTest)(G_FILE_TEST_IS_DIR));
00250     if (isdir)
00251     {
00252         dir = filename;
00253     }
00254     else
00255     {
00256         char dirpath[MAX_PATH_LEN] = {0};
00257         if (get_dir_path(filename.c_str(), dirpath, MAX_PATH_LEN))
00258         {
00259             dir = dirpath;
00260         }
00261     }
00262  
00263     if (!dir.empty())
00264     {
00265         append_dir_separator(dir);
00266     }
00267 
00268     LOGPRINTF("%s, %s", filepath.c_str(), dir.c_str());
00269 }
00270 
00271 // If there's no G_DIR_SEPARATOR at the end of dirpath, append it.
00272 void ImagesScanner::append_dir_separator(std::string & dirpath)
00273 {
00274      if (!(G_DIR_SEPARATOR == dirpath.at(dirpath.length() - 1)))
00275      {
00276          dirpath += G_DIR_SEPARATOR;
00277      }
00278 }
00279 
00280 void ImagesScanner::scan_dir(const std::string & dirpath,
00281                              Images & results)
00282 {
00283      // The directory to scan is empty, return immediately.
00284      if (dirpath.empty())
00285      {
00286          return;
00287      }
00288      
00289      // The pixbuf doesn't support any formats, no need to scan.
00290      if (!gdk_pixbuf_get_formats())
00291      {
00292          return;
00293      }
00294      
00295      // Do the scanning...
00296      GDir * p = g_dir_open(dirpath.c_str(), 0, 0);
00297      if (p)
00298      {
00299          const char * filename = g_dir_read_name(p);
00300 
00301          std::string path;
00302          while (filename)
00303          {
00304              // Get the whole filepath
00305              path.clear();
00306              path = dirpath;
00307              path.append(filename);
00308              
00309              // Check the path mataches against filter.              
00310              if (g_file_test(path.c_str(), (GFileTest)G_FILE_TEST_IS_REGULAR)
00311                  && is_image(path))
00312              {
00313                  ImagePtr image = new Image;
00314                  if (!image) { return; }
00315 
00316                  LOGPRINTF("%s", path.c_str());
00317                 
00318                  image->path = path;
00319                  
00320                  // For performance issue, we don't calculate these fields 
00321                  // 'width' 'height' 'rotation' until we need them. 
00322                  image->width = -1;
00323                  image->height = -1;
00324                  image->is_rotation_calculated = false;
00325                  image->rotation = Clockwise_Degrees_0;
00326 
00327                  results.push_back(image);
00328              }
00329              else if (recursive
00330                      && g_file_test(path.c_str(),
00331                          (GFileTest)G_FILE_TEST_IS_DIR))
00332              {
00333                  // Scan recursively.
00334                  append_dir_separator(path);
00335                  scan_dir(path, results);
00336              }
00337              
00338              // Get the next 'filename'.
00339              filename = g_dir_read_name(p);
00340          }
00341          g_dir_close(p);
00342      }
00343 }
00344 
00345 void ImagesScanner::sort_images(Images & results,
00346                                 SortType sort_type, 
00347                                 bool sort_ascending)
00348 {
00349     ImagesIter begin = results.begin();
00350     ImagesIter end = results.end();
00351     ImagesIter iter;
00352 #ifndef WIN32  
00353     // Calculate the sort field before sorting.
00354     // If sort by filepath, no need to calculate.
00355     if (sort_type != BY_FILEPATH)
00356     {
00357         char tmp[MAX_PATH_LEN] = {0};
00358         struct stat stat_buf;
00359         
00360         for (iter = begin; iter != end; ++iter)
00361         {
00362             tmp[0] = '\0';
00363             switch (sort_type)
00364             {
00365                 case BY_FILENAME:
00366                     get_file_name((*iter)->path.c_str(), tmp, MAX_PATH_LEN);
00367                     break;
00368                 case BY_EXT:
00369                     get_ext_name((*iter)->path.c_str(), tmp, MAX_PATH_LEN);
00370                     break;
00371                 case BY_DATE:
00372                     if ((stat((*iter)->path.c_str(), &stat_buf) == 0)
00373                      && (S_ISREG(stat_buf.st_mode) != 0))
00374                     {
00375                         // What is date?
00376                         struct tm local_time;
00377                         localtime_r(&stat_buf.st_mtime, &local_time);
00378                         strftime(tmp, MAX_PATH_LEN, 
00379                                 "%Y-%m-%dT%H:%M:%S", &local_time);
00380                     }
00381                     break;
00382                 case BY_SIZE:
00383                     if ((stat((*iter)->path.c_str(), &stat_buf) == 0)
00384                         && (S_ISREG(stat_buf.st_mode)))
00385                     {
00386                         snprintf(tmp, MAX_PATH_LEN, 
00387                                 "%ld", stat_buf.st_blocks * 512);
00388                     }    
00389                     break;
00390                 default:
00391                     break;
00392             }
00393 
00394             (*iter)->sort_field = tmp;
00395         }
00396     }
00397 
00398     // Sorting
00399     switch (sort_type)
00400     {
00401         case BY_FILENAME:
00402             if (sort_ascending) 
00403             { 
00404                 std::sort(begin, end, less_by_filename); 
00405             }
00406             else 
00407             { 
00408                 std::sort(begin, end, greater_by_filename); 
00409             }
00410             break;
00411 
00412         case BY_EXT:
00413             if (sort_ascending) 
00414             { 
00415                 std::sort(begin, end, less_by_ext); 
00416             }
00417             else 
00418             { 
00419                 std::sort(begin, end, greater_by_ext); 
00420             }
00421             break;
00422 
00423         case BY_DATE:
00424             if (sort_ascending) 
00425             { 
00426                 std::sort(begin, end, less_by_date); 
00427             }
00428             else 
00429             { 
00430                 std::sort(begin, end, greater_by_date); 
00431             }
00432             break;
00433 
00434         case BY_SIZE:
00435             if (sort_ascending) 
00436             { 
00437                 std::sort(begin, end, less_by_size); 
00438             }
00439             else 
00440             { 
00441                 std::sort(begin, end, greater_by_size); 
00442             }
00443             break;
00444 
00445         case BY_FILEPATH:
00446         default:
00447             if (sort_ascending) 
00448             { 
00449                 std::sort(begin, end, less_by_filepath); 
00450             }
00451             else 
00452             { 
00453                 std::sort(begin, end, greater_by_filepath); 
00454             } 
00455             break;
00456     }
00457 
00458     // Clear the sort field after sorting.
00459     if (sort_type != BY_FILEPATH)
00460     {
00461         for (iter = begin; iter != end; ++iter)
00462         {
00463             (*iter)->sort_field.clear();
00464         }
00465     }
00466 #endif
00467     // Calculate the 'active'.
00468     int i = 0;
00469     for (iter = begin; iter != end; ++iter)
00470     {
00471         if ((*iter)->path == filepath)
00472         {
00473             active = i;
00474             break;
00475         }
00476         i++;
00477     }
00478 }
00479 
00480 #ifndef WIN32
00481 bool ImagesScanner::greater_by_filepath(Image * a, Image * b)
00482 {
00483     int ret = 0;
00484 
00485     if ((!a->path.empty()) && (!b->path.empty()))
00486     {
00487         ret = strcasecmp(a->path.c_str(), b->path.c_str());
00488     }
00489     else if ((!a->path.empty()) && (b->path.empty()))
00490     {
00491         ret = 1;
00492     }
00493     else if ((a->path.empty()) && (!b->path.empty()))
00494     {
00495         ret = -1;
00496     }
00497     
00498     return ((ret > 0) ? true : false);
00499 }
00500 
00501 bool ImagesScanner::greater_by_filename(Image * a, Image * b)
00502 {
00503     int ret = 0;
00504     
00505     if ((!a->sort_field.empty()) && (!b->sort_field.empty()))
00506     {
00507         ret = strcasecmp(a->sort_field.c_str(), b->sort_field.c_str());
00508     }
00509     else if ((!a->sort_field.empty()) && (b->sort_field.empty()))
00510     {
00511         ret = 1;
00512     }
00513     else if ((a->sort_field.empty()) && (!b->sort_field.empty()))
00514     {
00515         ret = -1;
00516     }
00517 
00518     if (!ret) { ret = strcasecmp(a->path.c_str(), b->path.c_str());}
00519     return ((ret > 0) ? true : false);
00520 }
00521 
00522 bool ImagesScanner::greater_by_ext(Image * a, Image * b)
00523 {
00524     int ret = 0;
00525 
00526     if ((!a->sort_field.empty()) && (!b->sort_field.empty()))
00527     {
00528         ret = strcasecmp(a->sort_field.c_str(), b->sort_field.c_str());
00529     }
00530     else if ((!a->sort_field.empty()) && (b->sort_field.empty()))
00531     {
00532         ret = 1;
00533     }
00534     else if ((a->sort_field.empty()) && (!b->sort_field.empty()))
00535     {
00536         ret = -1;
00537     }
00538 
00539     if (!ret) { ret = strcasecmp(a->path.c_str(), b->path.c_str());}
00540     return ((ret > 0) ? true : false);
00541 }
00542 
00543 bool ImagesScanner::greater_by_date(Image * a, Image * b)
00544 {
00545     int ret = 0;
00546 
00547     if ((!a->sort_field.empty()) && (!b->sort_field.empty()))
00548     {
00549         struct tm tm_a, tm_b;
00550         strptime(a->sort_field.c_str(), "%Y-%m-%dT%H:%M:%S", &tm_a);
00551         strptime(b->sort_field.c_str(), "%Y-%m-%dT%H:%M:%S", &tm_b);
00552 
00553         time_t time_a = mktime(&tm_a); 
00554         time_t time_b = mktime(&tm_b);
00555 
00556         double diff = difftime(time_a, time_b);
00557         ret = (int)diff;
00558     }
00559     else if ((!a->sort_field.empty()) && (b->sort_field.empty()))
00560     {
00561         ret = 1;
00562     }
00563     else if ((a->sort_field.empty()) && (!b->sort_field.empty()))
00564     {
00565         ret = -1;
00566     }
00567         
00568     if (!ret) { ret = strcasecmp(a->path.c_str(), b->path.c_str());}
00569     return ((ret > 0) ? true : false);
00570 }
00571 
00572 bool ImagesScanner::greater_by_size(Image * a, Image * b)
00573 {
00574     int ret = 0;
00575 
00576     if ((!a->sort_field.empty()) && (!b->sort_field.empty()))
00577     {
00578         ret = atoi(a->sort_field.c_str()) - atoi(b->sort_field.c_str());
00579     }
00580     else if ((!a->sort_field.empty()) && (b->sort_field.empty()))
00581     {
00582         ret = 1;
00583     }
00584     else if ((a->sort_field.empty()) && (!b->sort_field.empty()))
00585     {
00586         ret = -1;
00587     }
00588  
00589     if (!ret) { ret = strcasecmp(a->path.c_str(), b->path.c_str());}
00590     return ((ret > 0) ? true : false);
00591 }
00592 
00593 bool ImagesScanner::less_by_filepath(Image * a, Image * b)
00594 {
00595     return greater_by_filepath(b, a);
00596 }
00597 
00598 bool ImagesScanner::less_by_filename(Image * a, Image * b)
00599 {
00600     return greater_by_filename(b, a);
00601 }
00602 
00603 bool ImagesScanner::less_by_ext(Image * a, Image * b)
00604 {
00605     return greater_by_ext(b, a);
00606 }
00607 
00608 bool ImagesScanner::less_by_date(Image * a, Image * b)
00609 {
00610     return greater_by_date(b, a);
00611 }
00612 
00613 bool ImagesScanner::less_by_size(Image * a, Image * b)
00614 {
00615     return greater_by_size(b, a);
00616 }
00617 #endif
00618 
00619 }; // namespace image
00620 
00621 
Generated by  doxygen 1.6.2-20100208