diff options
Diffstat (limited to 'src/ithumb-writer.c')
-rw-r--r-- | src/ithumb-writer.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/src/ithumb-writer.c b/src/ithumb-writer.c new file mode 100644 index 0000000..99c2f7a --- /dev/null +++ b/src/ithumb-writer.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2005 Christophe Fergeau + * + * + * The code contained in this file 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.1 of the License, or (at your option) any later version. + * + * This file is distributed in the hope that 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 code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * iTunes and iPod are trademarks of Apple + * + * This product is not supported/written/published by Apple! + * + */ + +#include "itdb.h" +#include "itdb_private.h" +#include "db-image-parser.h" + +#include <errno.h> +#include <locale.h> +#include <string.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#define FULL_THUMB_SIDE_LEN 0x8c +#define NOW_PLAYING_THUMB_SIDE_LEN 0x38 + +#define IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID 1016 +#define IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID 1017 + +struct _iThumbWriter { + off_t cur_offset; + FILE *f; + guint correlation_id; + enum ItdbImageType type; + int size; + GHashTable *cache; +}; +typedef struct _iThumbWriter iThumbWriter; + +/* The iPod expect square thumbnails with 2 specific side sizes (0x38 and 0x8c + * respectively for small and fullscreen thumbnails), the 'size' parameter is + * here to specify which size we are interested in in case the pixbuf is non + * square + */ +static void +pack_RGB_565 (GdkPixbuf *pixbuf, int size, + gushort **pixels565, unsigned int *bytes_len) +{ + guchar *pixels; + gushort *result; + gint row_stride; + gint channels; + gint width; + gint height; + gint w; + gint h; + + g_return_if_fail (pixels565 != NULL); + *pixels565 = NULL; + g_return_if_fail (bytes_len != NULL); + + g_object_get (G_OBJECT (pixbuf), + "rowstride", &row_stride, "n-channels", &channels, + "height", &height, "width", &width, + "pixels", &pixels, NULL); + g_return_if_fail ((width <= size) && (height <= size)); + result = g_malloc0 (size * size * 2); + + for (h = 0; h < height; h++) { + for (w = 0; w < width; w++) { + gint r; + gint g; + gint b; + + r = pixels[h*row_stride + w*channels]; + g = pixels[h*row_stride + w*channels + 1]; + b = pixels[h*row_stride + w*channels + 2]; + r >>= (8 - RED_BITS); + g >>= (8 - GREEN_BITS); + b >>= (8 - BLUE_BITS); + r = (r << RED_SHIFT) & RED_MASK; + g = (g << GREEN_SHIFT) & GREEN_MASK; + b = (b << BLUE_SHIFT) & BLUE_MASK; + result[h*size + w] = (GINT16_TO_LE (r | g | b)); + } + } + + *pixels565 = result; + *bytes_len = size * size * 2; +} + + + +static Itdb_Image * +itdb_image_dup (Itdb_Image *image) +{ + Itdb_Image *result; + + result = g_new0 (Itdb_Image, 1); + if (result == NULL) { + return NULL; + } + result->type = image->type; + result->height = image->height; + result->width = image->width; + result->offset = image->offset; + result->size = image->size; + + return result; +} + +static Itdb_Image * +ithumb_writer_write_thumbnail (iThumbWriter *writer, + const char *filename) +{ + GdkPixbuf *thumb; + gushort *pixels; + Itdb_Image *image; + + image = g_hash_table_lookup (writer->cache, filename); + if (image != NULL) { + return itdb_image_dup (image); + } + + image = g_new0 (Itdb_Image, 1); + if (image == NULL) { + return NULL; + } + + thumb = gdk_pixbuf_new_from_file_at_scale (filename, writer->size, -1, + TRUE, NULL); + if (thumb == NULL) { + g_free (image); + return NULL; + } + g_object_get (G_OBJECT (thumb), "height", &image->height, NULL); + if (image->height > writer->size) { + g_object_unref (thumb); + thumb = gdk_pixbuf_new_from_file_at_scale (filename, + -1, writer->size, + TRUE, NULL); + if (thumb == NULL) { + g_free (image); + return NULL; + } + } + g_object_get (G_OBJECT (thumb), + "height", &image->height, + "width", &image->width, + NULL); + image->offset = writer->cur_offset; + image->type = writer->type; + pack_RGB_565 (thumb, writer->size, &pixels, &image->size); + g_object_unref (G_OBJECT (thumb)); + if (pixels == NULL) { + g_free (image); + return NULL; + } + if (fwrite (pixels, image->size, 1, writer->f) != 1) { + g_free (image); + g_free (pixels); + g_print ("Error writing to file: %s\n", strerror (errno)); + return NULL; + } + g_free (pixels); + writer->cur_offset += image->size; + g_hash_table_insert (writer->cache, g_strdup (filename), image); + + return image; +} + + +#define FULL_THUMB_SIDE_LEN 0x8c +#define NOW_PLAYING_THUMB_SIDE_LEN 0x38 + +#define FULL_THUMB_CORRELATION_ID 1016 +#define NOW_PLAYING_THUMB_CORRELATION_ID 1017 + + + +static iThumbWriter * +ithumb_writer_new (const char *mount_point, enum ItdbImageType type, + int correlation_id, int size) +{ + char *filename; + iThumbWriter *writer; + writer = g_new0 (iThumbWriter, 1); + if (writer == NULL) { + return NULL; + } + writer->correlation_id = correlation_id; + writer->size = size; + writer->type = type; + writer->cache = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + if (writer->cache == NULL) { + g_free (writer); + return NULL; + } + filename = ipod_image_get_ithmb_filename (mount_point, correlation_id); + writer->f = fopen (filename, "w"); + if (writer->f == NULL) { + g_print ("Error opening %s: %s\n", filename, strerror (errno)); + g_free (filename); + g_hash_table_destroy (writer->cache); + g_free (writer); + return NULL; + } + g_free (filename); + + return writer; +} + +static void +ithumb_writer_free (iThumbWriter *writer) +{ + g_return_if_fail (writer != NULL); + g_hash_table_destroy (writer->cache); + fclose (writer->f); + g_free (writer); +} + +G_GNUC_INTERNAL int +itdb_write_ithumb_files (Itdb_iTunesDB *db, const char *mount_point) +{ + GList *it; + iThumbWriter *fullsize_writer; + iThumbWriter *nowplaying_writer; + g_print ("%s\n", G_GNUC_FUNCTION); + + fullsize_writer = ithumb_writer_new (mount_point, + ITDB_IMAGE_FULL_SCREEN, + FULL_THUMB_CORRELATION_ID, + FULL_THUMB_SIDE_LEN); + if (fullsize_writer == NULL) { + return -1; + } + + nowplaying_writer = ithumb_writer_new (mount_point, + ITDB_IMAGE_NOW_PLAYING, + NOW_PLAYING_THUMB_CORRELATION_ID, + NOW_PLAYING_THUMB_SIDE_LEN); + if (nowplaying_writer == NULL) { + ithumb_writer_free (fullsize_writer); + return -1; + } + + for (it = db->tracks; it != NULL; it = it->next) { + Itdb_Track *song; + Itdb_Image *thumb; + + song = (Itdb_Track *)it->data; + song->artwork_count = 0; + itdb_track_free_generated_thumbnails (song); + if (song->orig_image_filename == NULL) { + continue; + } + thumb = ithumb_writer_write_thumbnail (nowplaying_writer, + song->orig_image_filename); + if (thumb != NULL) { + song->thumbnails = g_list_append (song->thumbnails, + thumb); + song->artwork_count++; + } + thumb = ithumb_writer_write_thumbnail (fullsize_writer, + song->orig_image_filename); + if (thumb != NULL) { + song->thumbnails = g_list_append (song->thumbnails, + thumb); + song->artwork_count++; + } + } + + ithumb_writer_free (nowplaying_writer); + ithumb_writer_free (fullsize_writer); + + return 0; +} |