1/* MiniDLNA media server 2 * Copyright (C) 2009 Justin Maggard 3 * 4 * This file is part of MiniDLNA. 5 * 6 * MiniDLNA is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * MiniDLNA is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19/* These functions are mostly based on code from other projects. 20 * There are function to effiently resize a JPEG image, and some utility functions. 21 * They are here to allow loading and saving JPEG data directly to or from memory with libjpeg. 22 * The standard functions only allow you to read from or write to a file. 23 * 24 * The reading code comes from the JpgAlleg library, at http://wiki.allegro.cc/index.php?title=Libjpeg 25 * The writing code was posted on a Google group from openjpeg, at http://groups.google.com/group/openjpeg/browse_thread/thread/331e6cf60f70797f 26 * The resize functions come from the resize_image project, at http://www.golac.fr/Image-Resizer 27 */ 28 29#include "config.h" 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34#include <setjmp.h> 35#include <jpeglib.h> 36#ifdef HAVE_MACHINE_ENDIAN_H 37#include <machine/endian.h> 38#else 39#include <endian.h> 40#endif 41 42#include "upnpreplyparse.h" 43#include "image_utils.h" 44#include "log.h" 45 46#if __BYTE_ORDER == __LITTLE_ENDIAN 47# define SWAP16(w) ( (((w) >> 8) & 0x00ff) | (((w) << 8) & 0xff00) ) 48#else 49# define SWAP16(w) (w) 50#endif 51 52#define JPEG_QUALITY 96 53 54#define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF) 55#define COL_FULL(red, green, blue, alpha) (((red) << 24) | ((green) << 16) | ((blue) << 8) | (alpha)) 56#define COL_RED(col) (col >> 24) 57#define COL_GREEN(col) ((col >> 16) & 0xFF) 58#define COL_BLUE(col) ((col >> 8) & 0xFF) 59#define COL_ALPHA(col) (col & 0xFF) 60#define BLACK 0x000000FF 61 62 63struct my_dst_mgr { 64 struct jpeg_destination_mgr jdst; 65 JOCTET *buf; 66 JOCTET *off; 67 size_t sz; 68 size_t used; 69}; 70 71/* Destination manager to store data in a buffer */ 72static void 73my_dst_mgr_init(j_compress_ptr cinfo) 74{ 75 struct my_dst_mgr *dst = (void *)cinfo->dest; 76 77 dst->used = 0; 78 dst->sz = cinfo->image_width 79 * cinfo->image_height 80 * cinfo->input_components; 81 dst->buf = malloc(dst->sz * sizeof *dst->buf); 82 dst->off = dst->buf; 83 dst->jdst.next_output_byte = dst->off; 84 dst->jdst.free_in_buffer = dst->sz; 85 86 return; 87} 88 89static boolean 90my_dst_mgr_empty(j_compress_ptr cinfo) 91{ 92 struct my_dst_mgr *dst = (void *)cinfo->dest; 93 94 dst->sz *= 2; 95 dst->used = dst->off - dst->buf; 96 dst->buf = realloc(dst->buf, dst->sz * sizeof *dst->buf); 97 dst->off = dst->buf + dst->used; 98 dst->jdst.next_output_byte = dst->off; 99 dst->jdst.free_in_buffer = dst->sz - dst->used; 100 101 return TRUE; 102} 103 104static void 105my_dst_mgr_term(j_compress_ptr cinfo) 106{ 107 struct my_dst_mgr *dst = (void *)cinfo->dest; 108 109 dst->used += dst->sz - dst->jdst.free_in_buffer; 110 dst->off = dst->buf + dst->used; 111 112 return; 113} 114 115static void 116jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst) 117{ 118 dst->jdst.init_destination = my_dst_mgr_init; 119 dst->jdst.empty_output_buffer = my_dst_mgr_empty; 120 dst->jdst.term_destination = my_dst_mgr_term; 121 cinfo->dest = (void *)dst; 122 123 return; 124} 125 126/* Source manager to read data from a buffer */ 127struct 128my_src_mgr 129{ 130 struct jpeg_source_mgr pub; 131 JOCTET eoi_buffer[2]; 132}; 133 134static void 135init_source(j_decompress_ptr cinfo) 136{ 137 return; 138} 139 140static int 141fill_input_buffer(j_decompress_ptr cinfo) 142{ 143 struct my_src_mgr *src = (void *)cinfo->src; 144 145 /* Create a fake EOI marker */ 146 src->eoi_buffer[0] = (JOCTET) 0xFF; 147 src->eoi_buffer[1] = (JOCTET) JPEG_EOI; 148 src->pub.next_input_byte = src->eoi_buffer; 149 src->pub.bytes_in_buffer = 2; 150 151 return TRUE; 152} 153 154static void 155skip_input_data(j_decompress_ptr cinfo, long num_bytes) 156{ 157 struct my_src_mgr *src = (void *)cinfo->src; 158 if (num_bytes > 0) 159 { 160 while (num_bytes > (long)src->pub.bytes_in_buffer) 161 { 162 num_bytes -= (long)src->pub.bytes_in_buffer; 163 fill_input_buffer(cinfo); 164 } 165 } 166 src->pub.next_input_byte += num_bytes; 167 src->pub.bytes_in_buffer -= num_bytes; 168} 169 170static void 171term_source(j_decompress_ptr cinfo) 172{ 173 return; 174} 175 176void 177jpeg_memory_src(j_decompress_ptr cinfo, const unsigned char * buffer, size_t bufsize) 178{ 179 struct my_src_mgr *src; 180 181 if (!cinfo->src) 182 cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));; 183 src = (void *)cinfo->src; 184 src->pub.init_source = init_source; 185 src->pub.fill_input_buffer = fill_input_buffer; 186 src->pub.skip_input_data = skip_input_data; 187 src->pub.resync_to_restart = jpeg_resync_to_restart; 188 src->pub.term_source = term_source; 189 src->pub.next_input_byte = buffer; 190 src->pub.bytes_in_buffer = bufsize; 191} 192 193jmp_buf setjmp_buffer; 194/* Don't exit on error like libjpeg likes to do */ 195static void 196libjpeg_error_handler(j_common_ptr cinfo) 197{ 198 cinfo->err->output_message(cinfo); 199 longjmp(setjmp_buffer, 1); 200 return; 201} 202 203void 204image_free(image_s *pimage) 205{ 206 free(pimage->buf); 207 free(pimage); 208} 209 210pix 211get_pix(image_s *pimage, int32_t x, int32_t y) 212{ 213 if (x < 0) 214 x = 0; 215 else if (x >= pimage->width) 216 x = pimage->width - 1; 217 218 if (y < 0) 219 y = 0; 220 else if (y >= pimage->height) 221 y = pimage->height - 1; 222 223 return(pimage->buf[(y * pimage->width) + x]); 224} 225 226void 227put_pix_alpha_replace(image_s *pimage, int32_t x, int32_t y, pix col) 228{ 229 if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height)) 230 pimage->buf[(y * pimage->width) + x] = col; 231} 232 233int 234image_get_jpeg_resolution(const char * path, int * width, int * height) 235{ 236 FILE *img; 237 unsigned char buf[8]; 238 uint16_t offset, h, w; 239 int ret = 1; 240 size_t nread; 241 long size; 242 243 244 img = fopen(path, "r"); 245 if( !img ) 246 return -1; 247 248 fseek(img, 0, SEEK_END); 249 size = ftell(img); 250 rewind(img); 251 252 nread = fread(&buf, 2, 1, img); 253 if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) ) 254 { 255 fclose(img); 256 return -1; 257 } 258 memset(&buf, 0, sizeof(buf)); 259 260 while( ftell(img) < size ) 261 { 262 while( nread > 0 && buf[0] != 0xFF && !feof(img) ) 263 nread = fread(&buf, 1, 1, img); 264 265 while( nread > 0 && buf[0] == 0xFF && !feof(img) ) 266 nread = fread(&buf, 1, 1, img); 267 268 if( (buf[0] >= 0xc0) && (buf[0] <= 0xc3) ) 269 { 270 nread = fread(&buf, 7, 1, img); 271 *width = 0; 272 *height = 0; 273 if( nread < 1 ) 274 break; 275 memcpy(&h, buf+3, 2); 276 *height = SWAP16(h); 277 memcpy(&w, buf+5, 2); 278 *width = SWAP16(w); 279 ret = 0; 280 break; 281 } 282 else 283 { 284 offset = 0; 285 nread = fread(&buf, 2, 1, img); 286 if( nread < 1 ) 287 break; 288 memcpy(&offset, buf, 2); 289 offset = SWAP16(offset) - 2; 290 if( fseek(img, offset, SEEK_CUR) == -1 ) 291 break; 292 } 293 } 294 fclose(img); 295 return ret; 296} 297 298int 299image_get_jpeg_date_xmp(const char * path, char ** date) 300{ 301 FILE *img; 302 unsigned char buf[8]; 303 char *data = NULL, *newdata; 304 uint16_t offset; 305 struct NameValueParserData xml; 306 char * exif; 307 int ret = 1; 308 size_t nread; 309 310 img = fopen(path, "r"); 311 if( !img ) 312 return(-1); 313 314 nread = fread(&buf, 2, 1, img); 315 if( (nread < 1) || (buf[0] != 0xFF) || (buf[1] != 0xD8) ) 316 { 317 fclose(img); 318 return(-1); 319 } 320 memset(&buf, 0, sizeof(buf)); 321 322 while( !feof(img) ) 323 { 324 while( nread > 0 && buf[0] != 0xFF && !feof(img) ) 325 nread = fread(&buf, 1, 1, img); 326 327 while( nread > 0 && buf[0] == 0xFF && !feof(img) ) 328 nread = fread(&buf, 1, 1, img); 329 330 if( feof(img) ) 331 break; 332 333 if( buf[0] == 0xE1 ) // APP1 marker 334 { 335 offset = 0; 336 nread = fread(&buf, 2, 1, img); 337 if( nread < 1 ) 338 break; 339 memcpy(&offset, buf, 2); 340 offset = SWAP16(offset) - 2; 341 342 if( offset < 30 ) 343 { 344 fseek(img, offset, SEEK_CUR); 345 continue; 346 } 347 348 newdata = realloc(data, 30); 349 if( !newdata ) 350 break; 351 data = newdata; 352 353 nread = fread(data, 29, 1, img); 354 if( nread < 1 ) 355 break; 356 offset -= 29; 357 if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 ) 358 { 359 fseek(img, offset, SEEK_CUR); 360 continue; 361 } 362 363 newdata = realloc(data, offset+1); 364 if( !newdata ) 365 break; 366 data = newdata; 367 nread = fread(data, offset, 1, img); 368 if( nread < 1 ) 369 break; 370 371 ParseNameValue(data, offset, &xml, 0); 372 exif = GetValueFromNameValueList(&xml, "DateTimeOriginal"); 373 if( !exif ) 374 { 375 ClearNameValueList(&xml); 376 break; 377 } 378 *date = realloc(*date, strlen(exif)+1); 379 strcpy(*date, exif); 380 ClearNameValueList(&xml); 381 382 ret = 0; 383 break; 384 } 385 else 386 { 387 offset = 0; 388 nread = fread(&buf, 2, 1, img); 389 if( nread < 1 ) 390 break; 391 memcpy(&offset, buf, 2); 392 offset = SWAP16(offset) - 2; 393 fseek(img, offset, SEEK_CUR); 394 } 395 } 396 fclose(img); 397 free(data); 398 return ret; 399} 400 401image_s * 402image_new(int32_t width, int32_t height) 403{ 404 image_s *vimage; 405 406 if((vimage = (image_s *)malloc(sizeof(image_s))) == NULL) 407 { 408 DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); 409 return NULL; 410 } 411 vimage->width = width; vimage->height = height; 412 413 if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL) 414 { 415 DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); 416 free(vimage); 417 return NULL; 418 } 419 return(vimage); 420} 421 422image_s * 423image_new_from_jpeg(const char *path, int is_file, const uint8_t *buf, int size, int scale, int rotate) 424{ 425 image_s *vimage; 426 FILE *file = NULL; 427 struct jpeg_decompress_struct cinfo; 428 unsigned char *line[16], *ptr; 429 int x, y, i, w, h, ofs; 430 int maxbuf; 431 struct jpeg_error_mgr pub; 432 433 cinfo.err = jpeg_std_error(&pub); 434 pub.error_exit = libjpeg_error_handler; 435 jpeg_create_decompress(&cinfo); 436 if( is_file ) 437 { 438 if( (file = fopen(path, "r")) == NULL ) 439 { 440 return NULL; 441 } 442 jpeg_stdio_src(&cinfo, file); 443 } 444 else 445 { 446 jpeg_memory_src(&cinfo, buf, size); 447 } 448 if( setjmp(setjmp_buffer) ) 449 { 450 jpeg_destroy_decompress(&cinfo); 451 if( is_file && file ) 452 fclose(file); 453 return NULL; 454 } 455 jpeg_read_header(&cinfo, TRUE); 456 cinfo.scale_denom = scale; 457 cinfo.do_fancy_upsampling = FALSE; 458 cinfo.do_block_smoothing = FALSE; 459 cinfo.dct_method = JDCT_IFAST; 460 jpeg_start_decompress(&cinfo); 461 w = cinfo.output_width; 462 h = cinfo.output_height; 463 vimage = (rotate & (ROTATE_90|ROTATE_270)) ? image_new(h, w) : image_new(w, h); 464 if(!vimage) 465 { 466 jpeg_destroy_decompress(&cinfo); 467 if( is_file ) 468 fclose(file); 469 return NULL; 470 } 471 472 if( setjmp(setjmp_buffer) ) 473 { 474 jpeg_destroy_decompress(&cinfo); 475 if( is_file && file ) 476 fclose(file); 477 if( vimage ) 478 { 479 free(vimage->buf); 480 free(vimage); 481 } 482 return NULL; 483 } 484 485 if(cinfo.rec_outbuf_height > 16) 486 { 487 DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n"); 488 jpeg_destroy_decompress(&cinfo); 489 image_free(vimage); 490 if( is_file ) 491 fclose(file); 492 return NULL; 493 } 494 maxbuf = vimage->width * vimage->height; 495 if(cinfo.output_components == 3) 496 { 497 int rx, ry; 498 ofs = 0; 499 if((ptr = malloc(w * 3 * cinfo.rec_outbuf_height + 16)) == NULL) 500 { 501 DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); 502 jpeg_destroy_decompress(&cinfo); 503 image_free(vimage); 504 if( is_file ) 505 fclose(file); 506 return NULL; 507 } 508 509 for(y = 0; y < h; y += cinfo.rec_outbuf_height) 510 { 511 ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y; 512 for(i = 0; i < cinfo.rec_outbuf_height; i++) 513 { 514 line[i] = ptr + (w * 3 * i); 515 } 516 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); 517 for(x = 0; x < w * cinfo.rec_outbuf_height; x++) 518 { 519 rx = (rotate & (ROTATE_180|ROTATE_270)) ? (x - w + 1) * -1 : x; 520 ofs = (rotate & (ROTATE_90|ROTATE_270)) ? ry + (rx * h) : rx + (ry * w); 521 if( ofs < maxbuf ) 522 vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]); 523 } 524 } 525 free(ptr); 526 } 527 else if(cinfo.output_components == 1) 528 { 529 int rx, ry; 530 ofs = 0; 531 for(i = 0; i < cinfo.rec_outbuf_height; i++) 532 { 533 if((line[i] = malloc(w)) == NULL) 534 { 535 int t = 0; 536 537 for(t = 0; t < i; t++) free(line[t]); 538 jpeg_destroy_decompress(&cinfo); 539 image_free(vimage); 540 if( is_file ) 541 fclose(file); 542 return NULL; 543 } 544 } 545 for(y = 0; y < h; y += cinfo.rec_outbuf_height) 546 { 547 ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y; 548 jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); 549 for(i = 0; i < cinfo.rec_outbuf_height; i++) 550 { 551 for(x = 0; x < w; x++) 552 { 553 rx = (rotate & (ROTATE_180|ROTATE_270)) ? 554 (x - w + 1) * -1 : x; 555 ofs = (rotate & (ROTATE_90|ROTATE_270)) ? 556 ry + (rx * h) : rx + (ry * w); 557 if( ofs < maxbuf ) 558 vimage->buf[ofs] = 559 COL(line[i][x], line[i][x], line[i][x]); 560 } 561 } 562 } 563 for(i = 0; i < cinfo.rec_outbuf_height; i++) 564 { 565 free(line[i]); 566 } 567 } 568 jpeg_finish_decompress(&cinfo); 569 jpeg_destroy_decompress(&cinfo); 570 if( is_file ) 571 fclose(file); 572 573 return vimage; 574} 575 576void 577image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height) 578{ 579 int32_t vx, vy; 580#if !defined __i386__ && !defined __x86_64__ 581 int32_t rx, ry; 582 pix vcol; 583 584 if((pdest == NULL) || (psrc == NULL)) 585 return; 586 587 for(vy = 0; vy < height; vy++) 588 { 589 for(vx = 0; vx < width; vx++) 590 { 591 rx = ((vx * psrc->width) / width); 592 ry = ((vy * psrc->height) / height); 593 vcol = get_pix(psrc, rx, ry); 594#else 595 pix vcol,vcol1,vcol2,vcol3,vcol4; 596 float rx,ry; 597 float width_scale, height_scale; 598 float x_dist, y_dist; 599 600 width_scale = (float)psrc->width / (float)width; 601 height_scale = (float)psrc->height / (float)height; 602 603 for(vy = 0;vy < height; vy++) 604 { 605 for(vx = 0;vx < width; vx++) 606 { 607 rx = vx * width_scale; 608 ry = vy * height_scale; 609 vcol1 = get_pix(psrc, (int32_t)rx, (int32_t)ry); 610 vcol2 = get_pix(psrc, ((int32_t)rx)+1, (int32_t)ry); 611 vcol3 = get_pix(psrc, (int32_t)rx, ((int32_t)ry)+1); 612 vcol4 = get_pix(psrc, ((int32_t)rx)+1, ((int32_t)ry)+1); 613 614 x_dist = rx - ((float)((int32_t)rx)); 615 y_dist = ry - ((float)((int32_t)ry)); 616 vcol = COL_FULL( (uint8_t)((COL_RED(vcol1)*(1.0-x_dist) 617 + COL_RED(vcol2)*(x_dist))*(1.0-y_dist) 618 + (COL_RED(vcol3)*(1.0-x_dist) 619 + COL_RED(vcol4)*(x_dist))*(y_dist)), 620 (uint8_t)((COL_GREEN(vcol1)*(1.0-x_dist) 621 + COL_GREEN(vcol2)*(x_dist))*(1.0-y_dist) 622 + (COL_GREEN(vcol3)*(1.0-x_dist) 623 + COL_GREEN(vcol4)*(x_dist))*(y_dist)), 624 (uint8_t)((COL_BLUE(vcol1)*(1.0-x_dist) 625 + COL_BLUE(vcol2)*(x_dist))*(1.0-y_dist) 626 + (COL_BLUE(vcol3)*(1.0-x_dist) 627 + COL_BLUE(vcol4)*(x_dist))*(y_dist)), 628 (uint8_t)((COL_ALPHA(vcol1)*(1.0-x_dist) 629 + COL_ALPHA(vcol2)*(x_dist))*(1.0-y_dist) 630 + (COL_ALPHA(vcol3)*(1.0-x_dist) 631 + COL_ALPHA(vcol4)*(x_dist))*(y_dist)) 632 ); 633#endif 634 put_pix_alpha_replace(pdest, vx, vy, vcol); 635 } 636 } 637} 638 639void 640image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height) 641{ 642 int32_t vx, vy; 643 pix vcol; 644 int32_t i, j; 645#if !defined __i386__ && !defined __x86_64__ 646 int32_t rx, ry, rx_next, ry_next; 647 int red, green, blue, alpha; 648 int factor; 649 650 if((pdest == NULL) || (psrc == NULL)) 651 return; 652 653 for(vy = 0; vy < height; vy++) 654 { 655 for(vx = 0; vx < width; vx++) 656 { 657 658 rx = ((vx * psrc->width) / width); 659 ry = ((vy * psrc->height) / height); 660 661 red = green = blue = alpha = 0; 662 663 rx_next = rx + (psrc->width / width); 664 ry_next = ry + (psrc->width / width); 665 factor = 0; 666 667 for( j = rx; j < rx_next; j++) 668 { 669 for( i = ry; i < ry_next; i++) 670 { 671 factor += 1; 672 vcol = get_pix(psrc, j, i); 673 674 red += COL_RED(vcol); 675 green += COL_GREEN(vcol); 676 blue += COL_BLUE(vcol); 677 alpha += COL_ALPHA(vcol); 678 } 679 } 680 681 red /= factor; 682 green /= factor; 683 blue /= factor; 684 alpha /= factor; 685 686 /* on sature les valeurs */ 687 red = (red > 255) ? 255 : ((red < 0) ? 0 : red ); 688 green = (green > 255) ? 255 : ((green < 0) ? 0 : green); 689 blue = (blue > 255) ? 255 : ((blue < 0) ? 0 : blue ); 690 alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha); 691#else 692 float rx,ry; 693 float width_scale, height_scale; 694 float red, green, blue, alpha; 695 int32_t half_square_width, half_square_height; 696 float round_width, round_height; 697 698 if( (pdest == NULL) || (psrc == NULL) ) 699 return; 700 701 width_scale = (float)psrc->width / (float)width; 702 height_scale = (float)psrc->height / (float)height; 703 704 half_square_width = (int32_t)(width_scale / 2.0); 705 half_square_height = (int32_t)(height_scale / 2.0); 706 round_width = (width_scale / 2.0) - (float)half_square_width; 707 round_height = (height_scale / 2.0) - (float)half_square_height; 708 if(round_width > 0.0) 709 half_square_width++; 710 else 711 round_width = 1.0; 712 if(round_height > 0.0) 713 half_square_height++; 714 else 715 round_height = 1.0; 716 717 for(vy = 0;vy < height; vy++) 718 { 719 for(vx = 0;vx < width; vx++) 720 { 721 rx = vx * width_scale; 722 ry = vy * height_scale; 723 vcol = get_pix(psrc, (int32_t)rx, (int32_t)ry); 724 725 red = green = blue = alpha = 0.0; 726 727 for(j=0;j<half_square_height<<1;j++) 728 { 729 for(i=0;i<half_square_width<<1;i++) 730 { 731 vcol = get_pix(psrc, ((int32_t)rx)-half_square_width+i, 732 ((int32_t)ry)-half_square_height+j); 733 734 if(((j == 0) || (j == (half_square_height<<1)-1)) && 735 ((i == 0) || (i == (half_square_width<<1)-1))) 736 { 737 red += round_width*round_height*(float)COL_RED (vcol); 738 green += round_width*round_height*(float)COL_GREEN(vcol); 739 blue += round_width*round_height*(float)COL_BLUE (vcol); 740 alpha += round_width*round_height*(float)COL_ALPHA(vcol); 741 } 742 else if((j == 0) || (j == (half_square_height<<1)-1)) 743 { 744 red += round_height*(float)COL_RED (vcol); 745 green += round_height*(float)COL_GREEN(vcol); 746 blue += round_height*(float)COL_BLUE (vcol); 747 alpha += round_height*(float)COL_ALPHA(vcol); 748 } 749 else if((i == 0) || (i == (half_square_width<<1)-1)) 750 { 751 red += round_width*(float)COL_RED (vcol); 752 green += round_width*(float)COL_GREEN(vcol); 753 blue += round_width*(float)COL_BLUE (vcol); 754 alpha += round_width*(float)COL_ALPHA(vcol); 755 } 756 else 757 { 758 red += (float)COL_RED (vcol); 759 green += (float)COL_GREEN(vcol); 760 blue += (float)COL_BLUE (vcol); 761 alpha += (float)COL_ALPHA(vcol); 762 } 763 } 764 } 765 766 red /= width_scale*height_scale; 767 green /= width_scale*height_scale; 768 blue /= width_scale*height_scale; 769 alpha /= width_scale*height_scale; 770 771 /* on sature les valeurs */ 772 red = (red > 255.0)? 255.0 : ((red < 0.0)? 0.0:red ); 773 green = (green > 255.0)? 255.0 : ((green < 0.0)? 0.0:green); 774 blue = (blue > 255.0)? 255.0 : ((blue < 0.0)? 0.0:blue ); 775 alpha = (alpha > 255.0)? 255.0 : ((alpha < 0.0)? 0.0:alpha); 776#endif 777 put_pix_alpha_replace(pdest, vx, vy, 778 COL_FULL((uint8_t)red, (uint8_t)green, (uint8_t)blue, (uint8_t)alpha)); 779 } 780 } 781} 782 783image_s * 784image_resize(image_s * src_image, int32_t width, int32_t height) 785{ 786 image_s * dst_image; 787 788 dst_image = image_new(width, height); 789 if( !dst_image ) 790 return NULL; 791 if( (src_image->width < width) || (src_image->height < height) ) 792 image_upsize(dst_image, src_image, width, height); 793 else 794 image_downsize(dst_image, src_image, width, height); 795 796 return dst_image; 797} 798 799 800unsigned char * 801image_save_to_jpeg_buf(image_s * pimage, int * size) 802{ 803 struct jpeg_compress_struct cinfo; 804 struct jpeg_error_mgr jerr; 805 JSAMPROW row_pointer[1]; 806 int row_stride; 807 char *data; 808 int i, x; 809 struct my_dst_mgr dst; 810 811 cinfo.err = jpeg_std_error(&jerr); 812 jpeg_create_compress(&cinfo); 813 jpeg_memory_dest(&cinfo, &dst); 814 cinfo.image_width = pimage->width; 815 cinfo.image_height = pimage->height; 816 cinfo.input_components = 3; 817 cinfo.in_color_space = JCS_RGB; 818 jpeg_set_defaults(&cinfo); 819 jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE); 820 jpeg_start_compress(&cinfo, TRUE); 821 row_stride = cinfo.image_width * 3; 822 if((data = malloc(row_stride)) == NULL) 823 { 824 DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); 825 free(dst.buf); 826 jpeg_destroy_compress(&cinfo); 827 return NULL; 828 } 829 i = 0; 830 while(cinfo.next_scanline < cinfo.image_height) 831 { 832 for(x = 0; x < pimage->width; x++) 833 { 834 data[x * 3] = COL_RED(pimage->buf[i]); 835 data[x * 3 + 1] = COL_GREEN(pimage->buf[i]); 836 data[x * 3 + 2] = COL_BLUE(pimage->buf[i]); 837 i++; 838 } 839 row_pointer[0] = (unsigned char *)data; 840 jpeg_write_scanlines(&cinfo, row_pointer, 1); 841 } 842 jpeg_finish_compress(&cinfo); 843 *size = dst.used; 844 free(data); 845 jpeg_destroy_compress(&cinfo); 846 847 return dst.buf; 848} 849 850char * 851image_save_to_jpeg_file(image_s * pimage, char * path) 852{ 853 int nwritten, size = 0; 854 unsigned char * buf; 855 FILE * dst_file; 856 857 buf = image_save_to_jpeg_buf(pimage, &size); 858 if( !buf ) 859 return NULL; 860 dst_file = fopen(path, "w"); 861 if( !dst_file ) 862 { 863 free(buf); 864 return NULL; 865 } 866 nwritten = fwrite(buf, 1, size, dst_file); 867 fclose(dst_file); 868 free(buf); 869 870 return (nwritten == size) ? path : NULL; 871} 872