more options

This commit is contained in:
David Rose 2002-04-07 17:56:15 +00:00
parent 0580d974ee
commit 07f576a596
8 changed files with 303 additions and 41 deletions

View File

@ -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.
}

View File

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

View File

@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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