summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorteuf <teuf@f01d2545-417e-4e96-918e-98f8d0dbbcb6>2008-05-24 09:18:42 +0000
committerteuf <teuf@f01d2545-417e-4e96-918e-98f8d0dbbcb6>2008-05-24 09:18:42 +0000
commit99de9e7cfd098dcf494445273efd868aa36971a3 (patch)
tree61adff2ba2b8057426609036e969ccee7b0de94d
parentbbbb7fd88022366478c6cba34e0310172aee3443 (diff)
downloadlibgpod-99de9e7cfd098dcf494445273efd868aa36971a3.tar.gz
libgpod-99de9e7cfd098dcf494445273efd868aa36971a3.tar.xz
libgpod-99de9e7cfd098dcf494445273efd868aa36971a3.zip
Handle cropping for thumbnails needing it
git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1976 f01d2545-417e-4e96-918e-98f8d0dbbcb6
-rw-r--r--ChangeLog9
-rw-r--r--src/itdb_device.c15
-rw-r--r--src/itdb_device.h3
-rw-r--r--src/ithumb-writer.c211
4 files changed, 179 insertions, 59 deletions
diff --git a/ChangeLog b/ChangeLog
index 994cba0..a37a81c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,15 @@
2008-05-24 Christophe Fergeau <teuf at gnome.org>
Patch from Jacob Hoffman-Andrews <jsha at newview.org>
+
+ * src/itdb_device.h: add 'crop' field to Itdb_ArtworkFormat
+ * src/itdb_device.c: fill this 'crop' field for the iphone
+ * src/ithumb-writer.c: use that new 'crop' field to crop the
+ thumbnails when the Itdb_ArtworkFormat requires it.
+
+2008-05-24 Christophe Fergeau <teuf at gnome.org>
+
+ Patch from Jacob Hoffman-Andrews <jsha at newview.org>
* tools/hal-callout.c: add missing #include <unistd.h>
diff --git a/src/itdb_device.c b/src/itdb_device.c
index 2116fe5..3b5e7e3 100644
--- a/src/itdb_device.c
+++ b/src/itdb_device.c
@@ -306,9 +306,20 @@ static const Itdb_ArtworkFormat ipod_touch_1_artwork_info[] = {
{ITDB_THUMB_COVER_XLARGE, 320, 320, 3005, THUMB_FORMAT_RGB555_LE},
{ITDB_THUMB_COVER_XSMALL, 56, 56, 3006, THUMB_FORMAT_RGB555_LE, 8192}, /*pad data to 8192 bytes */
{ITDB_THUMB_COVER_SMEDIUM, 88, 88, 3007, THUMB_FORMAT_RGB555_LE, 16364}, /*pad data to 16384 bytes */
- {ITDB_THUMB_PHOTO_SMALL, 56, 55, 3004, THUMB_FORMAT_RGB555_LE, 8192},
- {ITDB_THUMB_PHOTO_LARGE, 80, 79, 3011, THUMB_FORMAT_RGB555_LE},
+
+ /* In the album list, if a photo is being used to represent a whole album,
+ PHOTO_SMALL is used. We specify TRUE for the crop option so we fill
+ the square completely. */
+ {ITDB_THUMB_PHOTO_SMALL, 56, 55, 3004, THUMB_FORMAT_RGB555_LE, 8192, TRUE},
+ /* In thumbnail view, PHOTO_LARGE is used. It's actually 79x79, with a 4px
+ white border on the right and bottom. We specify TRUE for the crop option
+ so we fill the square completely. */
+ {ITDB_THUMB_PHOTO_LARGE, 80, 79, 3011, THUMB_FORMAT_RGB555_LE, 0, TRUE},
{ITDB_THUMB_PHOTO_FULL_SCREEN,160, 120, 3009, THUMB_FORMAT_RGB555_LE},
+ /* When viewing an individual photo, PHOTO_TV_SCREEN is used. Note that it
+ is acceptable to write a thumbnail less than the specified width or
+ height, the iPhone / iTouch will scale it to fit the screen. This is
+ important for images that are taller than they wide. */
{ITDB_THUMB_PHOTO_TV_SCREEN, 640, 480, 3008, THUMB_FORMAT_RGB555_LE},
{-1, -1, -1, -1, -1}
};
diff --git a/src/itdb_device.h b/src/itdb_device.h
index e62a9b3..c21be94 100644
--- a/src/itdb_device.h
+++ b/src/itdb_device.h
@@ -101,6 +101,9 @@ struct _Itdb_ArtworkFormat
gint16 correlation_id;
ItdbThumbFormat format;
gint32 padding;
+ /* If true, crop the artwork to completely fill the target size,
+ rather than leaving empty bars on the top or sides. */
+ gboolean crop;
};
G_GNUC_INTERNAL const Itdb_ArtworkFormat *itdb_device_get_artwork_formats (Itdb_Device *device);
diff --git a/src/ithumb-writer.c b/src/ithumb-writer.c
index a5fb527..e101e33 100644
--- a/src/ithumb-writer.c
+++ b/src/ithumb-writer.c
@@ -45,6 +45,7 @@
#endif
#include <sys/types.h>
#include <sys/stat.h>
+#include <math.h>
#include <fcntl.h>
#include <glib/gstdio.h>
@@ -556,7 +557,123 @@ ipod_image_get_ithmb_filename (const char *mount_point, gint correlation_id, gin
return filename;
}
+/* If appropriate, rotate thumb->pixbuf by the value specified in
+ * thumb->rotation or thumb->pixbuf's EXIF orientation value. */
+static void
+ithumb_writer_handle_rotation (Itdb_Thumb *thumb) {
+ /* Make sure @rotation is valid (0, 90, 180, 270) */
+ thumb->rotation = thumb->rotation % 360;
+ thumb->rotation /= 90;
+ thumb->rotation *= 90;
+
+
+ /* If the caller did not specify a rotation, and there is an orientation header
+ present in the pixbuf (from EXIF), use that to choose a rotation.
+ NOTE: Do this before doing any transforms on the pixbuf, or you will lose
+ the EXIF metadata.
+ List of orientation values: http://sylvana.net/jpegcrop/exif_orientation.html */
+ if (thumb->rotation == 0) {
+ /* In GdkPixbuf 2.12 or above, this returns the EXIF orientation value. */
+ const char* exif_orientation = gdk_pixbuf_get_option(thumb->pixbuf, "orientation");
+ if (exif_orientation != NULL) {
+ switch (exif_orientation[0]) {
+ case '3':
+ thumb->rotation = 180;
+ break;
+ case '6':
+ thumb->rotation = 270;
+ break;
+ case '8':
+ thumb->rotation = 90;
+ break;
+ /* '1' means no rotation. The other four values are all various
+ transpositions, which are rare in real photos so we don't
+ implement them. */
+ }
+ }
+ }
+
+ /* Rotate if necessary */
+ if (thumb->rotation != 0)
+ {
+ GdkPixbuf *new_pixbuf = gdk_pixbuf_rotate_simple (thumb->pixbuf, thumb->rotation);
+ g_object_unref (thumb->pixbuf);
+ thumb->pixbuf = new_pixbuf;
+ /* Clean up */
+ thumb->rotation = 0;
+ }
+}
+
+/* On the iPhone, thumbnails are presented as squares in a grid.
+ In order to fit the grid, they have to be cropped as well as
+ scaled. */
+static GdkPixbuf *
+ithumb_writer_scale_and_crop (Itdb_Thumb *thumb,
+ gint width, gint height,
+ gboolean crop)
+{
+ gint input_width, input_height;
+ double width_scale, height_scale;
+ gdouble scale;
+ gint offset_x, offset_y;
+ gint border_width = 0;
+
+ GdkPixbuf *input_pixbuf, *output_pixbuf;
+
+ input_pixbuf = GDK_PIXBUF(thumb->pixbuf);
+ g_object_get (G_OBJECT (input_pixbuf),
+ "width", &input_width,
+ "height", &input_height,
+ NULL);
+
+ width_scale = (double) width / input_width;
+ height_scale = (double) height / input_height;
+ if (!crop)
+ {
+ /* If we're not cropping, we need to be able to fit both the whole width
+ and whole height, so we use the smaller of the two possible scale
+ factors. */
+ scale = MIN(width_scale, height_scale);
+ offset_x = offset_y = 0;
+ }
+ else
+ {
+ double scaled_width, scaled_height;
+ /* If we are cropping, we use the max of the two possible scale factors,
+ so that the image is large enough to fill both dimensions. */
+ scale = MAX(width_scale, height_scale);
+
+ /* In order to crop the image, we shift it either left or up by
+ a certain amount. Note that the offset args to gdk_pixbuf_scale are
+ expressed in terms of the *output* pixbuf, not the input, so we scal the
+ offsets. Here we figure out whether this is a vertical or horizontal
+ offset. */
+ scaled_width = input_width * scale;
+ scaled_height = input_height * scale;
+ offset_x = round((width - scaled_width) / 2);
+ offset_y = round((height - scaled_height) / 2);
+
+ g_assert(round(scaled_width) == width ||
+ round(scaled_height) == height);
+ }
+
+ output_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
+ width + border_width,
+ height + border_width);
+ gdk_pixbuf_fill(output_pixbuf, 0xffffffff);
+
+ gdk_pixbuf_scale (input_pixbuf,
+ output_pixbuf,
+ 0, 0, /* dest x, dest y */
+ width, /* dest width */
+ height, /* dest height */
+ offset_x, offset_y, /* offset x, offset y */
+ scale, scale, /* scale x, scale y */
+ GDK_INTERP_BILINEAR);
+
+ return output_pixbuf;
+}
static gboolean
ithumb_writer_write_thumbnail (iThumbWriter *writer,
@@ -570,34 +687,20 @@ ithumb_writer_write_thumbnail (iThumbWriter *writer,
g_return_val_if_fail (writer->img_info, FALSE);
g_return_val_if_fail (thumb, FALSE);
- /* Make sure @rotation is valid (0, 90, 180, 270) */
- thumb->rotation = thumb->rotation % 360;
- thumb->rotation /= 90;
- thumb->rotation *= 90;
-
- /* If we rotate by 90 or 270 degrees interchange the width and
- * height */
-
- if ((thumb->rotation == 0) || (thumb->rotation == 180))
- {
- width = writer->img_info->width;
- height = writer->img_info->height;
- }
- else
- {
- width = writer->img_info->height;
- height = writer->img_info->width;
- }
-
+ /* An thumb can start with one of:
+ 1. a filename
+ 2. raw image data read from a file
+ 3. a GdkPixbuf struct
+ In case 1 and 2, we load the relevant data into a GdkPixbuf and proceed
+ with case 3.
+ */
if (thumb->filename)
{ /* read image from filename */
- pixbuf = gdk_pixbuf_new_from_file_at_size (thumb->filename,
- width, height,
- NULL);
-
+ thumb->pixbuf = gdk_pixbuf_new_from_file (thumb->filename,
+ NULL);
g_free (thumb->filename);
thumb->filename = NULL;
- }
+ }
else if (thumb->image_data)
{ /* image data is stored in image_data and image_data_len */
GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
@@ -609,61 +712,55 @@ ithumb_writer_write_thumbnail (iThumbWriter *writer,
thumb->image_data_len,
NULL);
gdk_pixbuf_loader_close (loader, NULL);
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- if (pixbuf)
- g_object_ref (pixbuf);
+ thumb->pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (thumb->pixbuf)
+ g_object_ref (thumb->pixbuf);
g_object_unref (loader);
g_free (thumb->image_data);
thumb->image_data = NULL;
thumb->image_data_len = 0;
}
- else if (thumb->pixbuf)
- {
- pixbuf = gdk_pixbuf_scale_simple (GDK_PIXBUF(thumb->pixbuf),
- width, height,
- GDK_INTERP_BILINEAR);
- g_object_unref (thumb->pixbuf);
- thumb->pixbuf = NULL;
- }
- if (pixbuf == NULL)
+ if (thumb->pixbuf == NULL)
{
/* This is quite bad... if we just return FALSE the ArtworkDB
gets messed up. */
- pixbuf = gdk_pixbuf_from_pixdata (&questionmark_pixdata, FALSE, NULL);
+ thumb->pixbuf = gdk_pixbuf_from_pixdata (&questionmark_pixdata, FALSE, NULL);
- if (pixbuf)
- {
- GdkPixbuf *pixbuf2;
- pixbuf2 = gdk_pixbuf_scale_simple (pixbuf,
- writer->img_info->width,
- writer->img_info->height,
- GDK_INTERP_BILINEAR);
- g_object_unref (pixbuf);
- pixbuf = pixbuf2;
- }
- else
+ if (!thumb->pixbuf)
{
/* Somethin went wrong. let's insert a red thumbnail */
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+ thumb->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
writer->img_info->width,
writer->img_info->height);
- gdk_pixbuf_fill (pixbuf, 0xff000000);
+ gdk_pixbuf_fill (thumb->pixbuf, 0xff000000);
}
/* avoid rotation */
thumb->rotation = 0;
}
- /* Rotate if necessary */
- if (thumb->rotation != 0)
+ g_assert(thumb->pixbuf);
+
+ ithumb_writer_handle_rotation(thumb);
+
+ /* If we rotate by 90 or 270 degrees interchange the width and
+ * height */
+ if ((thumb->rotation == 0) || (thumb->rotation == 180))
{
- GdkPixbuf *new_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, thumb->rotation);
- g_object_unref (pixbuf);
- pixbuf = new_pixbuf;
- /* Clean up */
- thumb->rotation = 0;
+ width = writer->img_info->width;
+ height = writer->img_info->height;
}
+ else
+ {
+ width = writer->img_info->height;
+ height = writer->img_info->width;
+ }
+
+ pixbuf = ithumb_writer_scale_and_crop (thumb, width, height,
+ writer->img_info->crop);
+ g_object_unref (thumb->pixbuf);
+ thumb->pixbuf = NULL;
/* !! cannot write directly to &thumb->width/height because
g_object_get() returns a gint, but thumb->width/height are