1 /*
2 | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
3 | Part of the gtkpod project.
4 |
5 | URL: http://www.gtkpod.org/
6 | URL: http://gtkpod.sourceforge.net/
7 |
8 | The code contained in this file is free software; you can redistribute
9 | it and/or modify it under the terms of the GNU Lesser General Public
10 | License as published by the Free Software Foundation; either version
11 | 2.1 of the License, or (at your option) any later version.
12 |
13 | This file is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | Lesser General Public License for more details.
17 |
18 | You should have received a copy of the GNU Lesser General Public
19 | License along with this code; if not, write to the Free Software
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 |
22 | iTunes and iPod are trademarks of Apple
23 |
24 | This product is not supported/written/published by Apple!
25 |
26 | $Id$
27 */
28
29 #include <config.h>
30
31 #include "itdb_device.h"
32 #include "itdb_private.h"
33 #include "itdb_thumb.h"
34 #include "db-image-parser.h"
35 #include "itdb_endianness.h"
36 #include <errno.h>
37 #include <stdio.h>
38 #include <string.h>
39 #if HAVE_GDKPIXBUF
40 #include <gdk-pixbuf/gdk-pixbuf.h>
41 #endif
42 #include <glib/gi18n-lib.h>
43 #include <glib/gstdio.h>
44 #include <stdlib.h>
45
46 /**
47 * itdb_artwork_new:
48 *
49 * Creates a new #Itdb_Artwork
50 *
51 * Returns: a new #Itdb_Artwork to be freed with itdb_artwork_free() when
52 * no longer needed
53 *
54 * Since: 0.3.0
55 */
itdb_artwork_new(void)56 Itdb_Artwork *itdb_artwork_new (void)
57 {
58 Itdb_Artwork *artwork = g_new0 (Itdb_Artwork, 1);
59 return artwork;
60 }
61
62 /**
63 * itdb_artwork_free:
64 * @artwork: an #Itdb_Artwork
65 *
66 * Frees memory used by @artwork
67 *
68 * Since: 0.3.0
69 */
itdb_artwork_free(Itdb_Artwork * artwork)70 void itdb_artwork_free (Itdb_Artwork *artwork)
71 {
72 g_return_if_fail (artwork);
73 itdb_artwork_remove_thumbnails (artwork);
74 if (artwork->userdata && artwork->userdata_destroy)
75 (*artwork->userdata_destroy) (artwork->userdata);
76 g_free (artwork);
77 }
78
79 /**
80 * itdb_artwork_duplicate:
81 * @artwork: an #Itdb_Artwork
82 *
83 * Duplicates @artwork
84 *
85 * Returns: a new copy of @artwork
86 *
87 * Since: 0.3.0
88 */
itdb_artwork_duplicate(Itdb_Artwork * artwork)89 Itdb_Artwork *itdb_artwork_duplicate (Itdb_Artwork *artwork)
90 {
91 Itdb_Artwork *dup;
92 g_return_val_if_fail (artwork, NULL);
93
94 dup = g_new0 (Itdb_Artwork, 1);
95
96 memcpy (dup, artwork, sizeof (Itdb_Artwork));
97
98 if (artwork->thumbnail != NULL) {
99 dup->thumbnail = itdb_thumb_duplicate (artwork->thumbnail);
100 }
101
102 return dup;
103 }
104
105 /**
106 * itdb_artwork_remove_thumbnails:
107 * @artwork: an #Itdb_Artwork
108 *
109 * Removes all thumbnails from @artwork
110 *
111 * Since: 0.3.0
112 */
113 void
itdb_artwork_remove_thumbnails(Itdb_Artwork * artwork)114 itdb_artwork_remove_thumbnails (Itdb_Artwork *artwork)
115 {
116 g_return_if_fail (artwork);
117
118 if (artwork->thumbnail != NULL) {
119 itdb_thumb_free (artwork->thumbnail);
120 }
121 artwork->thumbnail = NULL;
122 artwork->artwork_size = 0;
123 artwork->id = 0;
124 }
125
126 /**
127 * itdb_artwork_set_thumbnail
128 * @artwork: an #Itdb_Artwork
129 * @filename: image file to use to create the thumbnail
130 * @rotation: angle by which the image should be rotated
131 * counterclockwise. Valid values are 0, 90, 180 and 270.
132 * @error: return location for a #GError or NULL
133 *
134 * Appends a thumbnail of type @type to existing thumbnails in @artwork. No
135 * data is read from @filename yet, the file will be when @artwork is saved to
136 * disk. @filename must still exist when that happens.
137 *
138 * For the rotation angle you can also use the gdk constants
139 * %GDK_PIXBUF_ROTATE_NONE, %GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE,
140 * %GDK_PIXBUF_ROTATE_UPSIDEDOWN, AND %GDK_PIXBUF_ROTATE_CLOCKWISE.
141 *
142 * Returns: TRUE if the thumbnail could be successfully added, FALSE
143 * otherwise. @error is set appropriately.
144 *
145 * Since: 0.7.0
146 */
147 gboolean
itdb_artwork_set_thumbnail(Itdb_Artwork * artwork,const gchar * filename,gint rotation,GError ** error)148 itdb_artwork_set_thumbnail (Itdb_Artwork *artwork,
149 const gchar *filename,
150 gint rotation,
151 GError **error)
152 {
153 #ifdef HAVE_GDKPIXBUF
154 /* This operation doesn't make sense when we can't save thumbnail files */
155 struct stat statbuf;
156 Itdb_Thumb *thumb;
157
158 g_return_val_if_fail (artwork, FALSE);
159 g_return_val_if_fail (filename, FALSE);
160
161 if (g_stat (filename, &statbuf) != 0) {
162 g_set_error (error, 0, -1,
163 _("Could not access file '%s'."),
164 filename);
165 return FALSE;
166 }
167
168 artwork->artwork_size = statbuf.st_size;
169 artwork->creation_date = statbuf.st_mtime;
170
171 thumb = itdb_thumb_new_from_file (filename);
172 itdb_thumb_set_rotation (thumb, rotation);
173 if (artwork->thumbnail != NULL) {
174 itdb_thumb_free (artwork->thumbnail);
175 }
176 artwork->thumbnail = thumb;
177
178 return TRUE;
179 #else
180 g_set_error (error, 0, -1,
181 _("Artwork support not compiled into libgpod."));
182 return FALSE;
183 #endif
184 }
185
186 /**
187 * itdb_artwork_set_thumbnail_from_pixbuf
188 * @artwork: an #Itdb_Artwork
189 * @pixbuf: #GdkPixbuf to use to create the thumbnail
190 * @rotation: angle by which the image should be rotated
191 * counterclockwise. Valid values are 0, 90, 180 and 270.
192 * @error: return location for a #GError or NULL
193 *
194 * Set a thumbnail in @artwork. No data is generated from @pixbuf yet, it will
195 * be done when @artwork is saved to disk. @pixbuf is ref'ed by this function,
196 * but is not copied, so it should not be modified before the database is saved.
197 *
198 * For the rotation angle you can also use the gdk constants
199 * %GDK_PIXBUF_ROTATE_NONE, %GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE,
200 * %GDK_PIXBUF_ROTATE_UPSIDEDOWN, AND %GDK_PIXBUF_ROTATE_CLOCKWISE.
201 *
202 * Returns: TRUE if the thumbnail could be successfully added, FALSE
203 * otherwise. @error is set appropriately.
204 *
205 * Since: 0.7.0
206 */
207 gboolean
itdb_artwork_set_thumbnail_from_pixbuf(Itdb_Artwork * artwork,gpointer pixbuf,gint rotation,GError ** error)208 itdb_artwork_set_thumbnail_from_pixbuf (Itdb_Artwork *artwork,
209 gpointer pixbuf,
210 gint rotation,
211 GError **error)
212 {
213 #ifdef HAVE_GDKPIXBUF
214 /* This operation doesn't make sense when we can't save thumbnail files */
215 Itdb_Thumb *thumb;
216 GTimeVal time;
217 gint rowstride;
218 gint height;
219
220 g_return_val_if_fail (artwork, FALSE);
221 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
222
223 g_get_current_time (&time);
224 g_object_get (G_OBJECT (pixbuf),
225 "height", &height,
226 "rowstride", &rowstride,
227 NULL);
228 artwork->artwork_size = rowstride * height;
229 artwork->creation_date = time.tv_sec;
230
231 thumb = itdb_thumb_new_from_pixbuf (pixbuf);
232 itdb_thumb_set_rotation (thumb, rotation);
233 if (artwork->thumbnail != NULL) {
234 itdb_thumb_free (artwork->thumbnail);
235 }
236 artwork->thumbnail = thumb;
237
238 return TRUE;
239 #else
240 g_set_error (error, 0, -1,
241 _("Artwork support not compiled into libgpod."));
242 return FALSE;
243 #endif
244 }
245
246 /**
247 * itdb_artwork_set_thumbnail_from_data
248 * @artwork: an #Itdb_Artwork
249 * @image_data: data used to create the thumbnail (the raw contents of
250 * an image file)
251 * @image_data_len: length of above data block
252 * @rotation: angle by which the image should be rotated
253 * counterclockwise. Valid values are 0, 90, 180 and 270.
254 * @error: return location for a #GError or NULL
255 *
256 * Set a thumbnail in @artwork. No data is processed yet. This will be done when
257 * @artwork is saved to disk.
258 *
259 * For the rotation angle you can also use the gdk constants
260 * %GDK_PIXBUF_ROTATE_NONE, %GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE,
261 * %GDK_PIXBUF_ROTATE_UPSIDEDOWN, AND %GDK_PIXBUF_ROTATE_CLOCKWISE.
262 *
263 * Returns: TRUE if the thumbnail could be successfully added, FALSE
264 * otherwise. @error is set appropriately.
265 *
266 * Since: 0.7.0
267 */
268 gboolean
itdb_artwork_set_thumbnail_from_data(Itdb_Artwork * artwork,const guchar * image_data,gsize image_data_len,gint rotation,GError ** error)269 itdb_artwork_set_thumbnail_from_data (Itdb_Artwork *artwork,
270 const guchar *image_data,
271 gsize image_data_len,
272 gint rotation,
273 GError **error)
274 {
275 #ifdef HAVE_GDKPIXBUF
276 /* This operation doesn't make sense when we can't save thumbnail files */
277 Itdb_Thumb *thumb;
278 GTimeVal time;
279
280 g_return_val_if_fail (artwork, FALSE);
281 g_return_val_if_fail (image_data, FALSE);
282
283 g_get_current_time (&time);
284
285 artwork->artwork_size = image_data_len;
286 artwork->creation_date = time.tv_sec;
287
288 thumb = itdb_thumb_new_from_data (image_data, image_data_len);
289 itdb_thumb_set_rotation (thumb, rotation);
290 if (artwork->thumbnail != NULL) {
291 itdb_thumb_free (artwork->thumbnail);
292 }
293 artwork->thumbnail = thumb;
294
295 return TRUE;
296 #else
297 g_set_error (error, 0, -1,
298 _("Artwork support not compiled into libgpod."));
299 return FALSE;
300 #endif
301 }
302
303 #if HAVE_GDKPIXBUF
304 static guchar *
unpack_RGB_565(guint16 * pixels,guint bytes_len,guint byte_order)305 unpack_RGB_565 (guint16 *pixels, guint bytes_len, guint byte_order)
306 {
307 guchar *result;
308 guint i;
309
310 g_return_val_if_fail (bytes_len < 2*(G_MAXUINT/3), NULL);
311
312 result = g_malloc ((bytes_len/2) * 3);
313
314 for (i = 0; i < bytes_len/2; i++) {
315 guint16 cur_pixel;
316 /* FIXME: endianness */
317 cur_pixel = get_gint16 (pixels[i], byte_order);
318 /* Unpack pixels */
319 result[3*i] = (cur_pixel & RED_MASK_565) >> RED_SHIFT_565;
320 result[3*i+1] = (cur_pixel & GREEN_MASK_565) >> GREEN_SHIFT_565;
321 result[3*i+2] = (cur_pixel & BLUE_MASK_565) >> BLUE_SHIFT_565;
322
323 /* Normalize color values so that they use a [0..255] range */
324 result[3*i] <<= (8 - RED_BITS_565);
325 result[3*i+1] <<= (8 - GREEN_BITS_565);
326 result[3*i+2] <<= (8 - BLUE_BITS_565);
327 }
328
329 return result;
330 }
331
332 static guchar *
unpack_RGB_555(guint16 * pixels,guint bytes_len,guint byte_order)333 unpack_RGB_555 (guint16 *pixels, guint bytes_len, guint byte_order)
334 {
335 guchar *result;
336 guint i;
337
338 g_return_val_if_fail (bytes_len < 2*(G_MAXUINT/3), NULL);
339
340 result = g_malloc ((bytes_len/2) * 3);
341
342 for (i = 0; i < bytes_len/2; i++) {
343 guint16 cur_pixel;
344 /* FIXME: endianness */
345 cur_pixel = get_gint16 (pixels[i], byte_order);
346 /* Unpack pixels */
347 result[3*i] = (cur_pixel & RED_MASK_555) >> RED_SHIFT_555;
348 result[3*i+1] = (cur_pixel & GREEN_MASK_555) >> GREEN_SHIFT_555;
349 result[3*i+2] = (cur_pixel & BLUE_MASK_555) >> BLUE_SHIFT_555;
350
351 /* Normalize color values so that they use a [0..255] range */
352 result[3*i] <<= (8 - RED_BITS_555);
353 result[3*i+1] <<= (8 - GREEN_BITS_555);
354 result[3*i+2] <<= (8 - BLUE_BITS_555);
355 }
356
357 return result;
358 }
359
360 static guchar *
unpack_RGB_888(guint16 * pixels,guint bytes_len,guint byte_order)361 unpack_RGB_888 (guint16 *pixels, guint bytes_len, guint byte_order)
362 {
363 guchar *result;
364 guint i;
365 guint32 *pixels32;
366
367 result = g_malloc ((bytes_len/4) * 3);
368
369 pixels32 = (guint32 *)pixels;
370
371 for (i = 0; i < bytes_len/4; i++) {
372 guint32 cur_pixel;
373 /* FIXME: endianness */
374 cur_pixel = get_gint32 (pixels32[i], byte_order);
375 /* Unpack pixels */
376 result[3*i] = (cur_pixel & RED_MASK_888) >> RED_SHIFT_888;
377 result[3*i+1] = (cur_pixel & GREEN_MASK_888) >> GREEN_SHIFT_888;
378 result[3*i+2] = (cur_pixel & BLUE_MASK_888) >> BLUE_SHIFT_888;
379
380 /* Normalize color values so that they use a [0..255] range */
381 /* (not necessary for 888 encoding) */
382 /* result[3*i] <<= (8 - RED_BITS_888); */
383 /* result[3*i+1] <<= (8 - GREEN_BITS_888); */
384 /* result[3*i+2] <<= (8 - BLUE_BITS_888); */
385 }
386
387 return result;
388 }
389
390
rearrange_pixels(guint16 * pixels_s,guint16 * pixels_d,gint width,gint height,gint row_stride)391 static guint16 *rearrange_pixels (guint16 *pixels_s, guint16 *pixels_d,
392 gint width, gint height, gint row_stride)
393 {
394 g_return_val_if_fail (width == height, pixels_d);
395
396 if (pixels_d == NULL)
397 {
398 pixels_d = g_malloc0 (sizeof (guint16)*width*height);
399 }
400
401 if (width == 1)
402 {
403 *pixels_d = *pixels_s;
404 }
405 else
406 {
407 rearrange_pixels (pixels_s + 0,
408 pixels_d + 0 + 0,
409 width/2, height/2,
410 row_stride);
411 rearrange_pixels (pixels_s + (width/2)*(height/2),
412 pixels_d + (height/2)*row_stride + 0,
413 width/2, height/2,
414 row_stride);
415 rearrange_pixels (pixels_s + 2*(width/2)*(height/2),
416 pixels_d + width/2,
417 width/2, height/2,
418 row_stride);
419 rearrange_pixels (pixels_s + 3*(width/2)*(height/2),
420 pixels_d + (height/2)*row_stride + width/2,
421 width/2, height/2,
422 row_stride);
423 }
424
425 return pixels_d;
426 }
427
428
429 static guchar *
unpack_rec_RGB_555(guint16 * pixels,guint bytes_len,guint byte_order,gint width,gint height)430 unpack_rec_RGB_555 (guint16 *pixels, guint bytes_len, guint byte_order,
431 gint width, gint height)
432 {
433 guchar *result;
434 guint16 *use_pixels;
435 gboolean free_use_pixels = FALSE;
436 guint16 *pixels_arranged;
437
438 g_return_val_if_fail (bytes_len < 2*(G_MAXUINT/3), NULL);
439 g_return_val_if_fail (2*width*height < G_MAXUINT, NULL);
440 g_return_val_if_fail (width==height, NULL);
441
442 if (2*width*height > bytes_len)
443 {
444 use_pixels = g_malloc0 (2*width*height);
445 g_memmove (use_pixels, pixels, bytes_len);
446 free_use_pixels = TRUE;
447 }
448 else
449 {
450 use_pixels = pixels;
451 }
452
453 pixels_arranged = rearrange_pixels (use_pixels, NULL,
454 width, height, width);
455
456 if (pixels_arranged == NULL)
457 {
458 return NULL;
459 }
460
461 result = unpack_RGB_555 (pixels_arranged, bytes_len, byte_order);
462
463 g_free (pixels_arranged);
464 if (free_use_pixels)
465 {
466 g_free (use_pixels);
467 }
468
469 return result;
470 }
471
472
473 #if DEBUG_ARTWORK
474 static guchar *
unpack_experimental(guint16 * pixels,guint bytes_len,guint byte_order,gint width,gint height)475 unpack_experimental (guint16 *pixels, guint bytes_len, guint byte_order,
476 gint width, gint height)
477 {
478 guchar *result;
479 guint i;
480 guint32 *rpixels;
481
482 g_return_val_if_fail (bytes_len < (G_MAXUINT/3), NULL);
483
484 result = g_malloc ((bytes_len/4) * 3);
485
486 rpixels = (guint32 *)pixels;
487
488 for (i = 0; i < bytes_len/4; i++) {
489 guint32 cur_pixel;
490 /* FIXME: endianness */
491 cur_pixel = get_gint32 (rpixels[i], byte_order);
492 printf ("%8x\n", cur_pixel);
493 /* Unpack pixels */
494 result[3*i] = (cur_pixel & RED_MASK_888) >> RED_SHIFT_888;
495 result[3*i+1] = (cur_pixel & GREEN_MASK_888) >> GREEN_SHIFT_888;
496 result[3*i+2] = (cur_pixel & BLUE_MASK_888) >> BLUE_SHIFT_888;
497
498 /* Normalize color values so that they use a [0..255] range */
499 /* (not really necessary for 888 encoding) */
500 /* result[3*i] <<= (8 - RED_BITS_888); */
501 /* result[3*i+1] <<= (8 - GREEN_BITS_888); */
502 /* result[3*i+2] <<= (8 - BLUE_BITS_888); */
503 }
504
505 return result;
506 }
507 #endif
508
509
510 /* limit8bit() and unpack_UYVY() adapted from imgconvert.c from the
511 * GPixPod project (www.gpixpod.org) */
limit8bit(float x)512 static gint limit8bit (float x)
513 {
514 if(x >= 255)
515 {
516 return 255;
517 }
518 if(x <= 0)
519 {
520 return 0;
521 }
522 return x;
523 }
524
525 /* swapping U and V planes this unpacks YV12 */
526 static guchar *
unpack_I420(guchar * yuvdata,gint bytes_len,guint byte_order,gint width,gint height)527 unpack_I420 (guchar *yuvdata, gint bytes_len, guint byte_order,
528 gint width, gint height)
529 {
530 gint imgsize = width*3*height;
531 gint yuvdim = width*height;
532 guchar* rgbdata;
533 gint row, col;
534 gint z = 0;
535 gint h = 0;
536 gint y, u, v;
537 gint ustart = yuvdim;
538 gint vstart = yuvdim + yuvdim / 4;
539
540 g_return_val_if_fail (bytes_len < 2*(G_MAXUINT/3), NULL);
541 g_return_val_if_fail (width * height * 2 == bytes_len, NULL);
542
543 rgbdata = g_malloc(imgsize);
544
545 /* FIXME could be faster */
546 while(h < yuvdim){
547 y = yuvdata[h];
548
549 row = h / width;
550 col = h % width;
551
552 u = yuvdata[ustart + (row/2)*(width/2) + col/2];
553 v = yuvdata[vstart + (row/2)*(width/2) + col/2];
554
555 rgbdata[z] = limit8bit((y-16)*1.164 + (v-128)*1.596);
556 rgbdata[z+1] = limit8bit((y-16)*1.164 - (v-128)*0.813 - (u-128)*0.391);
557 rgbdata[z+2] = limit8bit((y-16)*1.164 + (u-128)*2.018);
558
559 z+=3;
560 h++;
561 }
562 return rgbdata;
563 }
564
565 static guchar *
unpack_UYVY(guchar * yuvdata,gint bytes_len,guint byte_order,gint width,gint height)566 unpack_UYVY (guchar *yuvdata, gint bytes_len, guint byte_order,
567 gint width, gint height)
568 {
569 gint imgsize = width*3*height;
570 guchar* rgbdata;
571 gint halfimgsize = imgsize/2;
572 gint halfyuv = halfimgsize/3*2;
573 gint x = 0;
574 gint z = 0;
575 gint z2 = 0;
576 gint u0, y0, v0, y1, u1, y2, v1, y3;
577 gint h = 0;
578
579 g_return_val_if_fail (bytes_len < 2*(G_MAXUINT/3), NULL);
580 /* printf ("w=%d h=%d s=%d\n", width, height, bytes_len); */
581 g_return_val_if_fail (width * height * 2 == bytes_len, NULL);
582
583 rgbdata = g_malloc(imgsize);
584
585 while(h < height)
586 {
587 gint w = 0;
588 if((h % 2) == 0)
589 {
590 while(w < width)
591 {
592 u0 = yuvdata[z];
593 y0 = yuvdata[z+1];
594 v0 = yuvdata[z+2];
595 y1 = yuvdata[z+3];
596 rgbdata[x] = limit8bit((y0-16)*1.164 + (v0-128)*1.596);/*R0*/
597 rgbdata[x+1] = limit8bit((y0-16)*1.164 - (v0-128)*0.813 - (u0-128)*0.391);/*G0*/
598 rgbdata[x+2] = limit8bit((y0-16)*1.164 + (u0-128)*2.018);/*B0*/
599 rgbdata[x+3] = limit8bit((y0-16)*1.164 + (v0-128)*1.596);/*R1*/
600 rgbdata[x+4] = limit8bit((y1-16)*1.164 - (v0-128)*0.813 - (u0-128)*0.391);/*G1*/
601 rgbdata[x+5] = limit8bit((y1-16)*1.164 + (u0-128)*2.018);/*B1*/
602 w += 2;
603 z += 4;
604 x += 6;
605 }
606 }
607 else
608 {
609 while(w < width)
610 {
611 u1 = yuvdata[halfyuv+z2];
612 y2 = yuvdata[halfyuv+z2+1];
613 v1 = yuvdata[halfyuv+z2+2];
614 y3 = yuvdata[halfyuv+z2+3];
615 rgbdata[x] = limit8bit((y2-16)*1.164 + (v1-128)*1.596);
616 rgbdata[x+1] = limit8bit((y2-16)*1.164 - (v1-128)*0.813 - (u1-128)*0.391);
617 rgbdata[x+2] = limit8bit((y2-16)*1.164 + (u1-128)*2.018);
618 rgbdata[x+3] = limit8bit((y2-16)*1.164 + (v1-128)*1.596);
619 rgbdata[x+4] = limit8bit((y3-16)*1.164 - (v1-128)*0.813 - (u1-128)*0.391);
620 rgbdata[x+5] = limit8bit((y3-16)*1.164 + (u1-128)*2.018);
621 w += 2;
622 z2 += 4;
623 x += 6;
624 }
625 }
626 h++;
627 }
628 return rgbdata;
629 }
630
631 static guchar *
get_pixel_data(Itdb_Device * device,Itdb_Thumb_Ipod_Item * thumb)632 get_pixel_data (Itdb_Device *device, Itdb_Thumb_Ipod_Item *thumb)
633 {
634 gchar *filename = NULL;
635 guchar *result = NULL;
636 FILE *f = NULL;
637 gint res;
638
639 g_return_val_if_fail (thumb, NULL);
640 g_return_val_if_fail (thumb->filename, NULL);
641
642 /* thumb->size is read as a guint32 from the iPod, so no overflow
643 * can occur here
644 */
645 result = g_malloc (thumb->size);
646
647 filename = itdb_thumb_ipod_get_filename (device, thumb);
648
649 if (!filename)
650 {
651 g_print (_("Could not find on iPod: '%s'\n"),
652 thumb->filename);
653 goto error;
654 }
655
656 f = fopen (filename, "r");
657 if (f == NULL) {
658 g_print ("Failed to open %s: %s\n",
659 filename, strerror (errno));
660 goto error;
661 }
662
663 res = fseek (f, thumb->offset, SEEK_SET);
664 if (res != 0) {
665 g_print ("Seek to %d failed on %s: %s\n",
666 thumb->offset, thumb->filename, strerror (errno));
667 goto error;
668 }
669
670 res = fread (result, thumb->size, 1, f);
671 if (res != 1) {
672 g_print ("Failed to read %u bytes from %s: %s\n",
673 thumb->size, thumb->filename, strerror (errno));
674 goto error;
675 }
676
677 goto cleanup;
678
679 error:
680 g_free (result);
681 result = NULL;
682 cleanup:
683 if (f != NULL) {
684 fclose (f);
685 }
686 g_free (filename);
687
688 return result;
689 }
690
691 static guchar *
itdb_thumb_get_rgb_data(Itdb_Device * device,Itdb_Thumb_Ipod_Item * item)692 itdb_thumb_get_rgb_data (Itdb_Device *device, Itdb_Thumb_Ipod_Item *item)
693 {
694 #if 0
695 #include <unistd.h>
696 #include <fcntl.h>
697 static gint i=0;
698 int fd;
699 gchar *name;
700 #endif
701 void *pixels_raw;
702 guchar *pixels=NULL;
703
704 g_return_val_if_fail (device, NULL);
705 g_return_val_if_fail (item, NULL);
706 g_return_val_if_fail (item->size != 0, NULL);
707
708 if (item->format == NULL) {
709 return NULL;
710 }
711
712 pixels_raw = get_pixel_data (device, item);
713
714 #if 0
715 name = g_strdup_printf ("thumb_%03d.raw", i++);
716 fd = creat (name, S_IRWXU|S_IRWXG|S_IRWXO);
717 write (fd, pixels_raw, thumb->size);
718 close (fd);
719 g_free (name);
720 #endif
721 if (pixels_raw == NULL) {
722 return NULL;
723 }
724
725 switch (item->format->format)
726 {
727 case THUMB_FORMAT_RGB565_LE_90:
728 case THUMB_FORMAT_RGB565_BE_90:
729 /* FIXME: actually the previous two might require
730 different treatment (used on iPod Photo for the full
731 screen photo thumbnail) */
732 case THUMB_FORMAT_RGB565_LE:
733 case THUMB_FORMAT_RGB565_BE:
734 pixels = unpack_RGB_565 (pixels_raw, item->size,
735 itdb_thumb_get_byteorder (item->format->format));
736 break;
737 case THUMB_FORMAT_RGB555_LE_90:
738 case THUMB_FORMAT_RGB555_BE_90:
739 /* FIXME: actually the previous two might require
740 different treatment (used on iPod Photo for the full
741 screen photo thumbnail) */
742 case THUMB_FORMAT_RGB555_LE:
743 case THUMB_FORMAT_RGB555_BE:
744 pixels = unpack_RGB_555 (pixels_raw, item->size,
745 itdb_thumb_get_byteorder (item->format->format));
746 break;
747 case THUMB_FORMAT_RGB888_LE_90:
748 case THUMB_FORMAT_RGB888_BE_90:
749 /* FIXME: actually the previous two might require
750 different treatment */
751 case THUMB_FORMAT_RGB888_LE:
752 case THUMB_FORMAT_RGB888_BE:
753 pixels = unpack_RGB_888 (pixels_raw, item->size,
754 itdb_thumb_get_byteorder (item->format->format));
755 break;
756 case THUMB_FORMAT_REC_RGB555_LE_90:
757 case THUMB_FORMAT_REC_RGB555_BE_90:
758 /* FIXME: actually the previous two might require
759 different treatment (used on iPod Photo for the full
760 screen photo thumbnail) */
761 case THUMB_FORMAT_REC_RGB555_LE:
762 case THUMB_FORMAT_REC_RGB555_BE:
763 pixels = unpack_rec_RGB_555 (pixels_raw, item->size,
764 itdb_thumb_get_byteorder (item->format->format),
765 item->format->width, item->format->height);
766 break;
767 case THUMB_FORMAT_EXPERIMENTAL_LE:
768 case THUMB_FORMAT_EXPERIMENTAL_BE:
769 #if DEBUG_ARTWORK
770 pixels = unpack_experimental (pixels_raw, item->size,
771 itdb_thumb_get_byteorder (item->format->format),
772 item->format->width, item->format->height);
773 break;
774 #endif
775 case THUMB_FORMAT_UYVY_LE:
776 case THUMB_FORMAT_UYVY_BE:
777 pixels = unpack_UYVY (pixels_raw, item->size,
778 itdb_thumb_get_byteorder (item->format->format),
779 item->format->width, item->format->height);
780 break;
781 case THUMB_FORMAT_I420_LE:
782 case THUMB_FORMAT_I420_BE:
783 pixels = unpack_I420 (pixels_raw, item->size,
784 itdb_thumb_get_byteorder (item->format->format),
785 item->format->width, item->format->height);
786 break;
787 }
788 g_free (pixels_raw);
789
790 return pixels;
791
792 }
793
get_aligned_width(const Itdb_ArtworkFormat * img_info,gsize pixel_size)794 static guint get_aligned_width (const Itdb_ArtworkFormat *img_info,
795 gsize pixel_size)
796 {
797 guint width;
798 guint alignment = img_info->row_bytes_alignment/pixel_size;
799
800 if (alignment * pixel_size != img_info->row_bytes_alignment) {
801 g_warning ("RowBytesAlignment (%d) not a multiple of pixel size (%"G_GSIZE_FORMAT")",
802 img_info->row_bytes_alignment, pixel_size);
803 }
804
805 width = img_info->width;
806 if ((alignment != 0) && ((img_info->width % alignment) != 0)) {
807 width += alignment - (img_info->width % alignment);
808 }
809 return width;
810 }
811
itdb_thumb_ipod_item_to_pixbuf(Itdb_Device * device,Itdb_Thumb_Ipod_Item * item)812 gpointer itdb_thumb_ipod_item_to_pixbuf (Itdb_Device *device,
813 Itdb_Thumb_Ipod_Item *item)
814 {
815 /* pixbuf is already on the iPod -> read from there */
816 GdkPixbuf *pixbuf_full;
817 GdkPixbuf *pixbuf_sub;
818 GdkPixbuf *pixbuf;
819 gint pad_x = item->horizontal_padding;
820 gint pad_y = item->vertical_padding;
821 gint width = item->width;
822 gint height = item->height;
823 guint rowstride;
824 const Itdb_ArtworkFormat *img_info = item->format;
825 guchar *pixels;
826
827 /* printf ("hp%d vp%d w%d h%d\n",
828 pad_x, pad_y, width, height);*/
829 if (item->format == NULL) {
830 g_warning (_("Unable to retrieve thumbnail (appears to be on iPod, but no image info available): filename: '%s'\n"),
831 item->filename);
832 return NULL;
833 }
834 pixels = itdb_thumb_get_rgb_data (device, item);
835 if (pixels == NULL)
836 {
837 return NULL;
838 }
839
840 /* FIXME: this is broken for non-16bpp image formats :-/ */
841 rowstride = get_aligned_width (img_info, sizeof(guint16))*3;
842 pixbuf_full =
843 gdk_pixbuf_new_from_data (pixels,
844 GDK_COLORSPACE_RGB,
845 FALSE, 8,
846 img_info->width, img_info->height,
847 rowstride,
848 (GdkPixbufDestroyNotify)g_free,
849 NULL);
850
851 /* !! do not g_free(pixels) here: it will be freed when doing a
852 * g_object_unref() on the GdkPixbuf !! */
853
854 /* Remove padding from the pixmap and/or cut the pixmap to the
855 right size. */
856
857 /* Negative offsets indicate that part of the image was cut
858 off at the left/top. thumb->width/height include that part
859 of the image. Positive offsets indicate that part of the
860 thumbnail are padded in black. thumb->width/height also
861 include that part of the image -> reduce width and height
862 by the absolute value of the padding */
863 width = width - abs (pad_x);
864 height = height - abs (pad_y);
865 /* And throw out "negative" padding */
866 if (pad_x < 0) pad_x = 0;
867 if (pad_y < 0) pad_y = 0;
868 /* Width/height might still be larger than
869 img_info->width/height, indicating that part of the image
870 was cut off at the right/bottom (similar to negative
871 padding above). Adjust width/height accordingly. */
872 if (pad_x + width > img_info->width)
873 width = img_info->width - pad_x;
874 if (pad_y + height > img_info->height)
875 height = img_info->height - pad_y;
876
877 #if DEBUG_ARTWORK
878 printf ("px=%2d py=%2d x=%3d y=%3d\n", pad_x, pad_y, width, height);
879 #endif
880
881 pixbuf_sub = gdk_pixbuf_new_subpixbuf (pixbuf_full,
882 pad_x, pad_y,
883 width, height);
884 pixbuf = gdk_pixbuf_copy (pixbuf_sub);
885 g_object_unref (pixbuf_full);
886 g_object_unref (pixbuf_sub);
887
888 return pixbuf;
889 }
890 #else
itdb_thumb_ipod_item_to_pixbuf(Itdb_Device * device,Itdb_Thumb_Ipod_Item * item)891 gpointer itdb_thumb_ipod_item_to_pixbuf (Itdb_Device *device,
892 Itdb_Thumb_Ipod_Item *item)
893 {
894 return NULL;
895 }
896 #endif
897
898 /**
899 * itdb_artwork_get_pixbuf:
900 * @artwork: an #Itdb_Artwork
901 * @device: an #Itdb_Device
902 * @width: width of the pixbuf to retrieve, -1 for the biggest
903 * possible size and 0 for the smallest possible size (with no
904 * scaling)
905 * @height: height of the pixbuf to retrieve, -1 for the biggest possible
906 * size and 0 for the smallest possible size (with no scaling)
907 *
908 * Returns a #GdkPixbuf representing the thumbnail stored in @artwork
909 * scaling it if appropriate. If either height or width is -1, then the
910 * biggest unscaled thumbnail available will be returned
911 *
912 * Returns: a #GdkPixbuf that must be unreffed when no longer used, NULL
913 * if no artwork could be found or if libgpod is compiled without GdkPixbuf
914 * support
915 *
916 * Since: 0.7.0
917 */
itdb_artwork_get_pixbuf(Itdb_Device * device,Itdb_Artwork * artwork,gint width,gint height)918 gpointer itdb_artwork_get_pixbuf (Itdb_Device *device, Itdb_Artwork *artwork,
919 gint width, gint height)
920 {
921 g_return_val_if_fail (artwork != NULL, NULL);
922 if (artwork->thumbnail == NULL) {
923 return NULL;
924 }
925 return itdb_thumb_to_pixbuf_at_size (device, artwork->thumbnail,
926 width, height);
927 }
928