Browse Source

more options

David Rose 24 years ago
parent
commit
07f576a596

+ 118 - 8
pandaapp/src/indexify/indexImage.cxx

@@ -105,7 +105,7 @@ generate_images(const Filename &archive_dir, TextMaker *text_maker) {
   // First, scan the image files to see if we can avoid regenerating
   // the index image.
   bool generate_index_image = true;
-  if (!force_regenerate && output_filename.exists()) {
+  if (!dummy_mode && !force_regenerate && output_filename.exists()) {
     // Maybe we don't need to renegerated the index.
     generate_index_image = false;
 
@@ -144,8 +144,9 @@ generate_images(const Filename &archive_dir, TextMaker *text_maker) {
     Filename reduced_filename(reduced_dir, photo->get_basename());
     PNMImage reduced_image;
 
-    if (force_regenerate || 
-	reduced_filename.compare_timestamps(photo_filename) < 0) {
+    if (!dummy_mode &&
+	(force_regenerate || 
+	 reduced_filename.compare_timestamps(photo_filename) < 0)) {
       // If the reduced filename does not exist or is older than the
       // source filename, we must read the complete source filename to
       // generate the reduced image.
@@ -187,7 +188,7 @@ generate_images(const Filename &archive_dir, TextMaker *text_maker) {
       photo->_full_x_size = photo_image.get_x_size();
       photo->_full_y_size = photo_image.get_y_size();
 
-      if (generate_index_image) {
+      if (!dummy_mode && generate_index_image) {
 	// Now read the reduced image from disk, so we can put it on
 	// the index image.
 	nout << "Reading " << reduced_filename << "\n";
@@ -203,7 +204,6 @@ generate_images(const Filename &archive_dir, TextMaker *text_maker) {
       } else {
 	// If we're not generating an index image, we don't even need
 	// the reduced image--just scan its header to get its size.
-	PNMImageHeader reduced_image;
 	if (!reduced_image.read_header(reduced_filename)) {
 	  nout << "Unable to read " << reduced_filename << "\n";
 	  return false;
@@ -216,12 +216,25 @@ generate_images(const Filename &archive_dir, TextMaker *text_maker) {
     if (generate_index_image) {
       // Generate a thumbnail image for the photo.
       PNMImage thumbnail_image;
-      compute_reduction(reduced_image, thumbnail_image, thumb_width, thumb_height);
-      
-      thumbnail_image.quick_filter_from(reduced_image);
+      compute_reduction(reduced_image, thumbnail_image, 
+			thumb_interior_width, thumb_interior_height);
+
+      if (dummy_mode) {
+	draw_box(thumbnail_image);
+      } else {
+	thumbnail_image.quick_filter_from(reduced_image);
+      }
       // Center the thumbnail image within its box.
       int x_center = (thumb_width - thumbnail_image.get_x_size()) / 2;
       int y_center = (thumb_height - thumbnail_image.get_y_size()) / 2;
+
+      if (draw_frames) {
+	draw_frame(index_image, 
+		   pinfo._x_place, pinfo._y_place,
+		   thumb_width, thumb_height,
+		   pinfo._x_place + x_center, pinfo._y_place + y_center,
+		   thumbnail_image.get_x_size(), thumbnail_image.get_y_size());
+      }
       
       index_image.copy_sub_image(thumbnail_image, 
 				 pinfo._x_place + x_center, 
@@ -590,6 +603,8 @@ compute_reduction(const PNMImage &source_image, PNMImage &dest_image,
   double y_scale = (double)y_size / (double)source_image.get_y_size();
   double scale = min(x_scale, y_scale);
 
+  // Don't ever enlarge an image to fit the rectangle; if the image is
+  // smaller than the rectangle, just leave it small.
   scale = min(scale, 1.0);
 
   int new_x_size = (int)(source_image.get_x_size() * scale + 0.5);
@@ -599,3 +614,98 @@ compute_reduction(const PNMImage &source_image, PNMImage &dest_image,
                    source_image.get_num_channels(),
                    source_image.get_maxval());
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: IndexImage::draw_box
+//       Access: Private, Static
+//  Description: Called in dummy mode to draw a little box in black
+//               around the border of the empty thumbnail image.
+////////////////////////////////////////////////////////////////////
+void IndexImage::
+draw_box(PNMImage &image) {
+  // First, fill it in white.
+  image.fill(1, 1, 1);
+
+  if (!draw_frames) {
+    // Now make the border pixel black.  We only need to do this if we
+    // aren't drawing frames, since the frames will reveal the shape
+    // of the image too.
+    int x_size = image.get_x_size();
+    int y_size = image.get_y_size();
+    for (int xi = 0; xi < x_size; xi++) {
+      image.set_xel(xi, 0, 0, 0, 0);
+      image.set_xel(xi, y_size - 1, 0, 0, 0);
+    }
+    
+    for (int yi = 1; yi < y_size - 1; yi++) {
+      image.set_xel(0, yi, 0, 0, 0);
+      image.set_xel(x_size - 1, yi, 0, 0, 0);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: IndexImage::draw_frame
+//       Access: Private, Static
+//  Description: Called in draw_frames mode to draw a slide mount in
+//               gray on the index image before drawing the thumbnail
+//               image in the center.
+////////////////////////////////////////////////////////////////////
+void IndexImage::
+draw_frame(PNMImage &image,
+	   int frame_left, int frame_top, int frame_width, int frame_height,
+	   int hole_left, int hole_top, int hole_width, int hole_height) {
+  // Gray levels.
+  static const RGBColord mid(0.5, 0.5, 0.5);
+  static const RGBColord light(0.7, 0.7, 0.7);
+  static const RGBColord lighter(0.9, 0.9, 0.9);
+  static const RGBColord dark(0.3, 0.3, 0.3);
+  static const RGBColord darker(0.1, 0.1, 0.1);
+
+  // First, fill in the whole rectangle in gray.
+  int xi, yi;
+  for (yi = 0; yi < frame_height; yi++) {
+    for (xi = 0; xi < frame_width; xi++) {
+      image.set_xel(xi + frame_left, yi + frame_top, mid);
+    }
+  }
+
+  // Now draw the bevel.
+  for (xi = 0; xi < frame_outer_bevel; xi++) {
+    for (yi = xi; yi < frame_height - xi; yi++) { 
+      // Left edge.
+      image.set_xel(xi + frame_left, yi + frame_top, light);
+      // Right edge.
+      image.set_xel(frame_width - 1 - xi + frame_left, yi + frame_top, dark);
+    }
+  }
+  for (yi = 0; yi < frame_outer_bevel; yi++) {
+    for (xi = yi; xi < frame_width - yi; xi++) { 
+      // Top edge.
+      image.set_xel(xi + frame_left, yi + frame_top, lighter);
+      // Bottom edge.
+      image.set_xel(xi + frame_left, frame_height - 1 - yi + frame_top, darker);
+    }
+  }
+
+  // Interior bevel.
+  for (xi = -1; xi >= -frame_inner_bevel; xi--) {
+    for (yi = xi; yi < hole_height - xi; yi++) { 
+      // Left edge.
+      image.set_xel(xi + hole_left, yi + hole_top, dark);
+      // Right edge.
+      image.set_xel(hole_width - 1 - xi + hole_left, yi + hole_top, light);
+    }
+  }
+  for (yi = -1; yi >= -frame_inner_bevel; yi--) {
+    for (xi = yi; xi < hole_width - yi; xi++) { 
+      // Top edge.
+      image.set_xel(xi + hole_left, yi + hole_top, darker);
+      // Bottom edge.
+      image.set_xel(xi + hole_left, hole_height - 1 - yi + hole_top, lighter);
+    }
+  }
+
+  // We don't have to cut out the hole, since the thumbnail image will
+  // do that when it is placed.
+}

+ 7 - 0
pandaapp/src/indexify/indexImage.h

@@ -61,6 +61,13 @@ private:
   static void compute_reduction(const PNMImage &source_image,
                                 PNMImage &dest_image,
                                 int x_size, int y_size);
+  static void draw_box(PNMImage &image);
+
+  static void draw_frame(PNMImage &image,
+			 int frame_left, int frame_top,
+			 int frame_width, int frame_height,
+			 int hole_left, int hole_top,
+			 int hole_width, int hole_height);
 
 private:
   RollDirectory *_dir;

+ 22 - 4
pandaapp/src/indexify/indexParameters.cxx

@@ -24,11 +24,16 @@ int max_index_height = 700;
 
 int thumb_width = 100;
 int thumb_height = 100;
-int thumb_caption_height = 16;
-int thumb_x_space = 16;
-int thumb_y_space = 16;
 
-int caption_font_size = 14;
+int thumb_caption_height = 12;
+int caption_font_size = 12;
+
+int thumb_x_space = 14;
+int thumb_y_space = 14;
+
+double frame_reduction_factor = 0.75;
+int frame_outer_bevel = 2;
+int frame_inner_bevel = 1;
 
 int reduced_width = 800;
 int reduced_height = 700;
@@ -39,6 +44,8 @@ Filename up_icon;
 
 bool force_regenerate = false;
 bool format_rose = false;
+bool dummy_mode = false;
+bool draw_frames = false;
 
 // Computed parameters
 int thumb_count_x;
@@ -46,6 +53,9 @@ int thumb_count_y;
 int max_thumbs;
 int actual_index_width;
 
+int thumb_interior_width;
+int thumb_interior_height;
+
 ////////////////////////////////////////////////////////////////////
 //     Function: finalize_parameters
 //  Description: This is called after all user parameters have been
@@ -63,4 +73,12 @@ finalize_parameters() {
   max_thumbs = thumb_count_x * thumb_count_y;
   
   actual_index_width = thumb_x_space + thumb_count_x * (thumb_width + thumb_x_space);
+
+  if (draw_frames) {
+    thumb_interior_width = (int)(thumb_width * frame_reduction_factor + 0.5);
+    thumb_interior_height = (int)(thumb_height * frame_reduction_factor + 0.5);
+  } else {
+    thumb_interior_width = thumb_width;
+    thumb_interior_height = thumb_height;
+  }
 }

+ 57 - 1
pandaapp/src/indexify/indexParameters.h

@@ -26,36 +26,92 @@
 // Some of these constants may be modified by command-line parameters
 // from the user.
 
+// The maximum size of the index image.  It will shrink vertically to
+// fit the images it contains, and it will shrink horizontally to fit
+// a complete row of images (even if it does not contain a complete
+// row).  It will never be larger than this.
 extern int max_index_width;
 extern int max_index_height;
 
+// The size of the individual thumbnail images, including the frames
+// (if present).  Thumbnail images are scaled to fit within this box.
 extern int thumb_width;
 extern int thumb_height;
+
+// The total number of pixels reserved for the caption under each
+// thumbnail image.  This is the caption_font_size plus whatever
+// spacing should be included between the caption and the image.
 extern int thumb_caption_height;
+
+// The size in pixels of the caption font.  This depends on the point
+// size of the font as reported by FreeType, so the actual height of
+// the letters might be slightly lower or higher than this, depending
+// on the font.
+extern int caption_font_size;
+
+// The amount of space, in pixels, between each two neighboring
+// thumbnail images, and around the overall index image.
 extern int thumb_x_space;
 extern int thumb_y_space;
 
-extern int caption_font_size;
+// The ratio by which the thumbnail images are reduced further when
+// frames are drawn, to allow room for a frame that resembles a slide
+// mount.
+extern double frame_reduction_factor;
+
+// The number of pixels of thickness to draw for the frames' outer
+// bevels and inner bevels, respectively.
+extern int frame_outer_bevel;
+extern int frame_inner_bevel;
 
+// The size of the reduced images on the individual image pages.  The
+// source image will be scaled to fit within this rectangle.
 extern int reduced_width;
 extern int reduced_height;
 
+// The filenames (or URLS) to the icon images for navigating the
+// individual image pages.
 extern Filename prev_icon;
 extern Filename next_icon;
 extern Filename up_icon;
 
+// True to regenerate every image, whether it appears to need it or
+// not.
 extern bool force_regenerate;
+
+// True to use the Rose formatting convention for roll directory names.
 extern bool format_rose;
 
+// True to place dummy thumbnails instead of loading actual images.
+extern bool dummy_mode;
+
+// True to draw frames (slide mounts) around each thumbnail image.
+extern bool draw_frames;
+
+
 void finalize_parameters();
 
+
+
 // The following parameters are all computed based on the above.
 
+// The number of thumbnail images that fit across an index image,
+// horizontally and vertically.
 extern int thumb_count_x;
 extern int thumb_count_y;
+
+// The total number of thumbnail images within each index image.
 extern int max_thumbs;
+
+// The number of pixels wide each index image will actually be, based
+// on thumb_count_x.
 extern int actual_index_width;
 
+// The actual size of the rectangle each thumbnail image must be
+// scaled into, accounting for the presence of a frame.
+extern int thumb_interior_width;
+extern int thumb_interior_height;
+
 #endif
 
 

+ 85 - 24
pandaapp/src/indexify/indexify.cxx

@@ -23,6 +23,7 @@
 #include "default_font.h"
 #include "default_index_icons.h"
 #include "indexParameters.h"
+#include "string_utils.h"
 
 #include <math.h>
 
@@ -104,6 +105,17 @@ Indexify() {
      "name will be reformatted to m-yy/s for output.",
      &Indexify::dispatch_none, &format_rose);
 
+  add_option
+    ("d", "", 0,
+     "Run in \"dummy\" mode; don't load any images, but instead just "
+     "draw an empty box indicating where the thumbnails will be.",
+     &Indexify::dispatch_none, &dummy_mode);
+
+  add_option
+    ("fr", "", 0,
+     "Draw a frame, like a slide mount, around each thumbnail image.",
+     &Indexify::dispatch_none, &draw_frames);
+
   add_option
     ("e", "extension", 0,
      "Specifies the filename extension (without a leading dot) to identify "
@@ -119,9 +131,21 @@ Indexify() {
      &Indexify::dispatch_none, &_generate_icons);
 
   add_option
-    ("caption", "size", 0,
-     "Specifies the font size in pixels of the thumbnail captions.",
-     &Indexify::dispatch_int, NULL, &caption_font_size);
+    ("caption", "size[,spacing]", 0,
+     "Specifies the font size in pixels of the thumbnail captions.  If the "
+     "optional spacing parameter is included, it is the number of pixels "
+     "below each thumbnail that the caption should be placed.  Specify "
+     "-caption 0 to disable thumbnail captions.",
+     &Indexify::dispatch_caption, NULL);
+
+  add_option
+    ("fontaa", "factor", 0,
+     "Specifies a scale factor to apply to the fonts used for captioning "
+     "when generating text for the purpose of antialiasing the fonts a "
+     "little better than FreeType can do by itself.  The letters are "
+     "generated large and then scaled to their proper size.  Normally this "
+     "should be a number in the range 3 to 4 for best effect.",
+     &Indexify::dispatch_double, NULL, &_font_aa_factor);
 
   add_option
     ("font", "fontname", 0,
@@ -170,6 +194,7 @@ Indexify() {
 
   _photo_extension = "jpg";
   _text_maker = (TextMaker *)NULL;
+  _font_aa_factor = 4.0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -277,26 +302,27 @@ post_command_line() {
     }
   }
     
-
-  if (!_font_filename.empty()) {
-    _text_maker = new TextMaker(_font_filename, 0);
-    if (!_text_maker->is_valid()) {
-      delete _text_maker;
-      _text_maker = (TextMaker *)NULL;
+  if (caption_font_size != 0) {
+    if (!_font_filename.empty()) {
+      _text_maker = new TextMaker(_font_filename, 0);
+      if (!_text_maker->is_valid()) {
+	delete _text_maker;
+	_text_maker = (TextMaker *)NULL;
+      }
     }
-  }
-
-  if (_text_maker == (TextMaker *)NULL) {
-    _text_maker = new TextMaker(default_font, default_font_size, 0);
-    if (!_text_maker->is_valid()) {
-      nout << "Unable to open default font.\n";
-      delete _text_maker;
-      _text_maker = (TextMaker *)NULL;
+    
+    if (_text_maker == (TextMaker *)NULL) {
+      _text_maker = new TextMaker(default_font, default_font_size, 0);
+      if (!_text_maker->is_valid()) {
+	nout << "Unable to open default font.\n";
+	delete _text_maker;
+	_text_maker = (TextMaker *)NULL;
+      }
+    }
+    
+    if (_text_maker != (TextMaker *)NULL) {
+      _text_maker->set_pixel_size(caption_font_size, _font_aa_factor);
     }
-  }
-  
-  if (_text_maker != (TextMaker *)NULL) {
-    _text_maker->set_pixel_size(caption_font_size, 4.0);
   }
 
   if (_generate_icons) {
@@ -351,15 +377,50 @@ post_command_line() {
     }
   }
 
-  // Provide a little bit of whitespace above the captions.
-  thumb_caption_height = (int)ceil(caption_font_size * 8.0 / 7.0);
-
   finalize_parameters();
 
 
   return ProgramBase::post_command_line();
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: Indexify::dispatch_caption
+//       Access: Protected, Static
+//  Description: Dispatch function for the -caption parameter, which
+//               takes either one or two numbers separated by a comma,
+//               representing the caption font size and the optional
+//               pixel spacing of the caption under the image.
+////////////////////////////////////////////////////////////////////
+bool Indexify::
+dispatch_caption(const string &opt, const string &arg, void *) {
+  vector_string words;
+  tokenize(arg, words, ",");
+
+  int caption_spacing = 0;
+
+  bool okflag = false;
+  if (words.size() == 1) {
+    okflag =
+      string_to_int(words[0], caption_font_size);
+
+  } else if (words.size() == 2) {
+    okflag =
+      string_to_int(words[0], caption_font_size) &&
+      string_to_int(words[1], caption_spacing);
+  }
+
+  if (!okflag) {
+    nout << "-" << opt
+         << " requires one or two integers separated by a comma.\n";
+    return false;
+  }
+
+  thumb_caption_height = caption_font_size + caption_spacing;
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Indexify::run
 //       Access: Public

+ 3 - 0
pandaapp/src/indexify/indexify.h

@@ -41,6 +41,8 @@ protected:
   virtual bool handle_args(Args &args);
   virtual bool post_command_line();
 
+  static bool dispatch_caption(const string &opt, const string &arg, void *var);
+
 public:
   void run();
 
@@ -50,6 +52,7 @@ public:
   string _photo_extension;
   Filename _font_filename;
   bool _generate_icons;
+  double _font_aa_factor;
 
   typedef pvector<RollDirectory *> RollDirs;
   RollDirs _roll_dirs;

+ 6 - 3
pandaapp/src/indexify/textGlyph.cxx

@@ -49,6 +49,9 @@ TextGlyph::
 ////////////////////////////////////////////////////////////////////
 void TextGlyph::
 rescale(double scale_factor) {
+  if (scale_factor == 1.0) {
+    return;
+  }
   nassertv(scale_factor != 0.0);
   _advance /= scale_factor;
   _int_advance = (int)floor(_advance + 0.5);
@@ -64,8 +67,8 @@ rescale(double scale_factor) {
     int extra_pad = (int)ceil(scale_factor);
     orig_x_size += 2*extra_pad;
     orig_y_size += 2*extra_pad;
-    orig_left += extra_pad;
-    orig_top -= extra_pad;
+    orig_left -= extra_pad;
+    orig_top += extra_pad;
 
     // Now compute the reduced size.
     int new_x_size = (int)ceil(orig_x_size / scale_factor);
@@ -85,7 +88,7 @@ rescale(double scale_factor) {
     int pad_top = old_top - orig_top;
 
     // These shouldn't go negative.
-    nassertv(pad_left >= 0 && pad_top >= 0);
+    nassertv(extra_pad + pad_left >= 0 && extra_pad + pad_top >= 0);
 
     PNMImage enlarged(old_x_size, old_y_size, 1);
     enlarged.fill(1, 1, 1);

+ 5 - 1
pandaapp/src/indexify/textMaker.cxx

@@ -70,6 +70,9 @@ TextMaker(const Filename &font_filename, int face_index) {
       }
       set_name(name);
 
+      // Maybe we don't care about enforcing this.  It doesn't work
+      // with older versions of FreeType anyway.
+      /*
       error = FT_Select_Charmap(_face, ft_encoding_unicode);
       if (error) {
         error = FT_Select_Charmap(_face, ft_encoding_latin_2);
@@ -78,7 +81,8 @@ TextMaker(const Filename &font_filename, int face_index) {
         nout << "Unable to select ISO encoding for " << get_name() << ".\n";
         FT_Done_Face(_face);
 
-      } else {
+      } else */
+      {
         nout << "Loaded font " << get_name() << "\n";
         _is_valid = true;
         _scale_factor = 0.0;