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