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