1 /* 2 3 Copyright (c) 2002-2003, Marcin 'Shard' Konicki 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions are met: 8 9 * Redistributions of source code must retain the above copyright notice, 10 this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above copyright notice, 12 this list of conditions and the following disclaimer in the documentation and/or 13 other materials provided with the distribution. 14 * Name "Marcin Konicki", "Shard" or any combination of them, 15 must not be used to endorse or promote products derived from this 16 software without specific prior written permission from Marcin Konicki. 17 18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 22 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 23 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30 */ 31 32 33 #include "JPEGTranslator.h" 34 35 #include "exif_parser.h" 36 37 #include <TabView.h> 38 39 40 #define MARKER_EXIF 0xe1 41 42 // Set these accordingly 43 #define JPEG_ACRONYM "JPEG" 44 #define JPEG_FORMAT 'JPEG' 45 #define JPEG_MIME_STRING "image/jpeg" 46 #define JPEG_DESCRIPTION "JPEG image" 47 48 // The translation kit's native file type 49 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap" 50 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap Format (JPEGTranslator)" 51 52 // Translation Kit required globals 53 char translatorName[] = "JPEG images"; 54 char translatorInfo[] = 55 "©2002-2003, Marcin Konicki\n" 56 "©2005-2007, Haiku\n" 57 "\n" 58 "Based on IJG library © 1994-2009, Thomas G. Lane, Guido Vollbeding.\n" 59 " http://www.ijg.org/files/\n" 60 "with \"lossless\" encoding support patch by Ken Murchison\n" 61 " http://www.oceana.com/ftp/ljpeg/\n" 62 "\n" 63 "With some colorspace conversion routines by Magnus Hellman\n" 64 " http://www.bebits.com/app/802\n"; 65 66 int32 translatorVersion = 0x120; 67 68 // Define the formats we know how to read 69 translation_format inputFormats[] = { 70 { JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5, 71 JPEG_MIME_STRING, JPEG_DESCRIPTION }, 72 { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5, 73 B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }, 74 {} 75 }; 76 77 // Define the formats we know how to write 78 translation_format outputFormats[] = { 79 { JPEG_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5, 80 JPEG_MIME_STRING, JPEG_DESCRIPTION }, 81 { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5, 82 B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }, 83 {} 84 }; 85 86 // Main functions of translator :) 87 static status_t Copy(BPositionIO *in, BPositionIO *out); 88 static status_t Compress(BPositionIO *in, BPositionIO *out, 89 const jmp_buf* longJumpBuffer); 90 static status_t Decompress(BPositionIO *in, BPositionIO *out, 91 BMessage* ioExtension, const jmp_buf* longJumpBuffer); 92 static status_t Error(j_common_ptr cinfo, status_t error = B_ERROR); 93 94 95 96 //! Make settings to defaults 97 void 98 LoadDefaultSettings(jpeg_settings *settings) 99 { 100 settings->Smoothing = 0; 101 settings->Quality = 95; 102 settings->Progressive = true; 103 settings->OptimizeColors = true; 104 settings->SmallerFile = false; 105 settings->B_GRAY1_as_B_RGB24 = false; 106 settings->Always_B_RGB32 = true; 107 settings->PhotoshopCMYK = true; 108 settings->ShowReadWarningBox = true; 109 } 110 111 112 //! Save settings to config file 113 void 114 SaveSettings(jpeg_settings *settings) 115 { 116 // Make path to settings file 117 BPath path; 118 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 119 return; 120 121 path.Append(SETTINGS_FILE); 122 123 // Open settings file (create it if there's no file) and write settings 124 FILE *file = NULL; 125 if ((file = fopen(path.Path(), "wb+"))) { 126 fwrite(settings, sizeof(jpeg_settings), 1, file); 127 fclose(file); 128 } 129 } 130 131 132 /*! 133 Load settings from config file 134 If can't find it make them default and try to save 135 */ 136 void 137 LoadSettings(jpeg_settings *settings) 138 { 139 // Make path to settings file 140 BPath path; 141 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) { 142 LoadDefaultSettings(settings); 143 return; 144 } 145 146 path.Append(SETTINGS_FILE); 147 148 // Open settings file (create it if there's no file) and write settings 149 FILE *file = NULL; 150 if ((file = fopen(path.Path(), "rb")) != NULL) { 151 if (!fread(settings, sizeof(jpeg_settings), 1, file)) { 152 // settings struct has changed size 153 // Load default settings, and Save them 154 fclose(file); 155 LoadDefaultSettings(settings); 156 SaveSettings(settings); 157 } else 158 fclose(file); 159 } else if ((file = fopen(path.Path(), "wb+")) != NULL) { 160 LoadDefaultSettings(settings); 161 fwrite(settings, sizeof(jpeg_settings), 1, file); 162 fclose(file); 163 } 164 } 165 166 167 static bool 168 x_flipped(int32 orientation) 169 { 170 return orientation == 2 || orientation == 3 171 || orientation == 6 || orientation == 7; 172 } 173 174 175 static bool 176 y_flipped(int32 orientation) 177 { 178 return orientation == 3 || orientation == 4 179 || orientation == 7 || orientation == 8; 180 } 181 182 183 static int32 184 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation) 185 { 186 if (orientation > 4) { 187 uint32 temp = x; 188 x = y; 189 y = temp; 190 } 191 if (y_flipped(orientation)) 192 y = height - 1 - y; 193 if (x_flipped(orientation)) 194 x = width - 1 - x; 195 196 return y * width + x; 197 } 198 199 200 // #pragma mark - conversion for compression 201 202 203 inline void 204 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes) 205 { 206 int32 index = 0; 207 int32 index2 = 0; 208 while (index < inRowBytes) { 209 unsigned char c = in[index++]; 210 for (int b = 128; b; b = b>>1) { 211 unsigned char color; 212 if (c & b) 213 color = 0; 214 else 215 color = 255; 216 out[index2++] = color; 217 } 218 } 219 } 220 221 222 inline void 223 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes) 224 { 225 int32 index = 0; 226 int32 index2 = 0; 227 while (index < inRowBytes) { 228 unsigned char c = in[index++]; 229 for (int b = 128; b; b = b>>1) { 230 unsigned char color; 231 if (c & b) 232 color = 0; 233 else 234 color = 255; 235 out[index2++] = color; 236 out[index2++] = color; 237 out[index2++] = color; 238 } 239 } 240 } 241 242 243 inline void 244 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes) 245 { 246 const color_map * map = system_colors(); 247 int32 index = 0; 248 int32 index2 = 0; 249 while (index < inRowBytes) { 250 rgb_color color = map->color_list[in[index++]]; 251 252 out[index2++] = color.red; 253 out[index2++] = color.green; 254 out[index2++] = color.blue; 255 } 256 } 257 258 259 inline void 260 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes) 261 { 262 int32 index = 0; 263 int32 index2 = 0; 264 int16 in_pixel; 265 while (index < inRowBytes) { 266 in_pixel = in[index] | (in[index+1] << 8); 267 index += 2; 268 269 out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12); 270 out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7); 271 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 272 } 273 } 274 275 276 inline void 277 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes) 278 { 279 int32 index = 0; 280 int32 index2 = 0; 281 int16 in_pixel; 282 while (index < inRowBytes) { 283 in_pixel = in[index+1] | (in[index] << 8); 284 index += 2; 285 286 out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12); 287 out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7); 288 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 289 } 290 } 291 292 293 inline void 294 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes) 295 { 296 int32 index = 0; 297 int32 index2 = 0; 298 int16 in_pixel; 299 while (index < inRowBytes) { 300 in_pixel = in[index] | (in[index+1] << 8); 301 index += 2; 302 303 out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13); 304 out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9); 305 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 306 } 307 } 308 309 310 inline void 311 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes) 312 { 313 int32 index = 0; 314 int32 index2 = 0; 315 int16 in_pixel; 316 while (index < inRowBytes) { 317 in_pixel = in[index+1] | (in[index] << 8); 318 index += 2; 319 320 out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13); 321 out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9); 322 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 323 } 324 } 325 326 327 inline void 328 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes) 329 { 330 int32 index = 0; 331 int32 index2 = 0; 332 while (index < inRowBytes) { 333 out[index2++] = in[index+2]; 334 out[index2++] = in[index+1]; 335 out[index2++] = in[index]; 336 index+=3; 337 } 338 } 339 340 341 inline void 342 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes) 343 { 344 inRowBytes /= 4; 345 346 for (int32 i = 0; i < inRowBytes; i++) { 347 out[0] = in[2]; 348 out[1] = in[1]; 349 out[2] = in[0]; 350 351 in += 4; 352 out += 3; 353 } 354 } 355 356 357 inline void 358 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes) 359 { 360 inRowBytes /= 4; 361 362 for (int32 i = 0; i < inRowBytes; i++) { 363 out[0] = in[1]; 364 out[1] = in[2]; 365 out[2] = in[3]; 366 367 in += 4; 368 out += 3; 369 } 370 } 371 372 373 // #pragma mark - conversion for decompression 374 375 376 inline void 377 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 378 { 379 for (int32 i = 0; i < inRowBytes; i += 4) { 380 int32 black = in[3]; 381 out[0] = in[2] * black / 255; 382 out[1] = in[1] * black / 255; 383 out[2] = in[0] * black / 255; 384 out[3] = 255; 385 386 in += 4; 387 out += xStep; 388 } 389 } 390 391 392 //! !!! UNTESTED !!! 393 inline void 394 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 395 { 396 for (int32 i = 0; i < inRowBytes; i += 4) { 397 int32 black = 255 - in[3]; 398 out[0] = ((255 - in[2]) * black) / 255; 399 out[1] = ((255 - in[1]) * black) / 255; 400 out[2] = ((255 - in[0]) * black) / 255; 401 out[3] = 255; 402 403 in += 4; 404 out += xStep; 405 } 406 } 407 408 409 //! RGB24 8:8:8 to xRGB 8:8:8:8 410 inline void 411 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 412 { 413 for (int32 i = 0; i < inRowBytes; i += 3) { 414 out[0] = in[2]; 415 out[1] = in[1]; 416 out[2] = in[0]; 417 out[3] = 255; 418 419 in += 3; 420 out += xStep; 421 } 422 } 423 424 425 //! 8-bit to 8-bit, only need when rotating the image 426 void 427 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 428 { 429 for (int32 i = 0; i < inRowBytes; i++) { 430 out[0] = in[0]; 431 432 in++; 433 out += xStep; 434 } 435 } 436 437 438 // #pragma mark - SView 439 440 441 SView::SView(BRect frame, const char *name) 442 : BView(frame, name, B_FOLLOW_ALL, B_WILL_DRAW) 443 { 444 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 445 SetLowColor(ViewColor()); 446 } 447 448 449 void 450 SView::AttachedToWindow() 451 { 452 BView::AttachedToWindow(); 453 ResizeTo(Parent()->Bounds().Width(), Parent()->Bounds().Height()); 454 } 455 456 457 // #pragma mark - 458 459 460 SSlider::SSlider(BRect frame, const char *name, const char *label, 461 BMessage *message, int32 minValue, int32 maxValue, orientation posture, 462 thumb_style thumbType, uint32 resizingMode, uint32 flags) 463 : BSlider(frame, name, label, message, minValue, maxValue, 464 posture, thumbType, resizingMode, flags) 465 { 466 rgb_color barColor = { 0, 0, 229, 255 }; 467 UseFillColor(true, &barColor); 468 } 469 470 471 //! Update status string - show actual value 472 const char* 473 SSlider::UpdateText() const 474 { 475 snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value()); 476 return fStatusLabel; 477 } 478 479 480 //! BSlider::ResizeToPreferred + Resize width if it's too small to show label and status 481 void 482 SSlider::ResizeToPreferred() 483 { 484 int32 width = (int32)ceil(StringWidth(Label()) + StringWidth("9999")); 485 if (width < 230) 486 width = 230; 487 488 float w, h; 489 GetPreferredSize(&w, &h); 490 ResizeTo(width, h); 491 } 492 493 494 // #pragma mark - 495 496 497 TranslatorReadView::TranslatorReadView(BRect frame, const char *name, 498 jpeg_settings *settings) 499 : SView(frame, name), 500 fSettings(settings) 501 { 502 BRect rect(5, 5, 30, 30); 503 fAlwaysRGB32 = new BCheckBox(rect, "alwaysrgb32", VIEW_LABEL_ALWAYSRGB32, 504 new BMessage(VIEW_MSG_SET_ALWAYSRGB32)); 505 fAlwaysRGB32->SetFont(be_plain_font); 506 if (fSettings->Always_B_RGB32) 507 fAlwaysRGB32->SetValue(1); 508 509 AddChild(fAlwaysRGB32); 510 fAlwaysRGB32->ResizeToPreferred(); 511 rect.OffsetBy(0, fAlwaysRGB32->Bounds().Height() + 5); 512 513 fPhotoshopCMYK = new BCheckBox(rect, "photoshopCMYK", VIEW_LABEL_PHOTOSHOPCMYK, 514 new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK)); 515 fPhotoshopCMYK->SetFont(be_plain_font); 516 if (fSettings->PhotoshopCMYK) 517 fPhotoshopCMYK->SetValue(1); 518 519 AddChild(fPhotoshopCMYK); 520 fPhotoshopCMYK->ResizeToPreferred(); 521 rect.OffsetBy(0, fPhotoshopCMYK->Bounds().Height() + 5); 522 523 fShowErrorBox = new BCheckBox(rect, "error", VIEW_LABEL_SHOWREADERRORBOX, 524 new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX)); 525 fShowErrorBox->SetFont(be_plain_font); 526 if (fSettings->ShowReadWarningBox) 527 fShowErrorBox->SetValue(1); 528 529 AddChild(fShowErrorBox); 530 531 fShowErrorBox->ResizeToPreferred(); 532 } 533 534 535 void 536 TranslatorReadView::AttachedToWindow() 537 { 538 SView::AttachedToWindow(); 539 540 fAlwaysRGB32->SetTarget(this); 541 fPhotoshopCMYK->SetTarget(this); 542 fShowErrorBox->SetTarget(this); 543 } 544 545 546 void 547 TranslatorReadView::MessageReceived(BMessage* message) 548 { 549 switch (message->what) { 550 case VIEW_MSG_SET_ALWAYSRGB32: 551 { 552 int32 value; 553 if (message->FindInt32("be:value", &value) == B_OK) { 554 fSettings->Always_B_RGB32 = value; 555 SaveSettings(fSettings); 556 } 557 break; 558 } 559 case VIEW_MSG_SET_PHOTOSHOPCMYK: 560 { 561 int32 value; 562 if (message->FindInt32("be:value", &value) == B_OK) { 563 fSettings->PhotoshopCMYK = value; 564 SaveSettings(fSettings); 565 } 566 break; 567 } 568 case VIEW_MSG_SET_SHOWREADERRORBOX: 569 { 570 int32 value; 571 if (message->FindInt32("be:value", &value) == B_OK) { 572 fSettings->ShowReadWarningBox = value; 573 SaveSettings(fSettings); 574 } 575 break; 576 } 577 default: 578 BView::MessageReceived(message); 579 break; 580 } 581 } 582 583 584 // #pragma mark - TranslatorWriteView 585 586 587 TranslatorWriteView::TranslatorWriteView(BRect frame, const char *name, 588 jpeg_settings *settings) 589 : SView(frame, name), 590 fSettings(settings) 591 { 592 BRect rect(10, 10, 20, 30); 593 fQualitySlider = new SSlider(rect, "quality", VIEW_LABEL_QUALITY, 594 new BMessage(VIEW_MSG_SET_QUALITY), 0, 100); 595 fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 596 fQualitySlider->SetHashMarkCount(10); 597 fQualitySlider->SetLimitLabels("Low", "High"); 598 fQualitySlider->SetFont(be_plain_font); 599 fQualitySlider->SetValue(fSettings->Quality); 600 AddChild(fQualitySlider); 601 fQualitySlider->ResizeToPreferred(); 602 603 rect.OffsetBy(0, fQualitySlider->Bounds().Height() + 5); 604 605 fSmoothingSlider = new SSlider(rect, "smoothing", VIEW_LABEL_SMOOTHING, 606 new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100); 607 fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 608 fSmoothingSlider->SetHashMarkCount(10); 609 fSmoothingSlider->SetLimitLabels("None", "High"); 610 fSmoothingSlider->SetFont(be_plain_font); 611 fSmoothingSlider->SetValue(fSettings->Smoothing); 612 AddChild(fSmoothingSlider); 613 fSmoothingSlider->ResizeToPreferred(); 614 615 rect.OffsetBy(0, fSmoothingSlider->Bounds().Height() + 5); 616 617 fProgress = new BCheckBox(rect, "progress", VIEW_LABEL_PROGRESSIVE, 618 new BMessage(VIEW_MSG_SET_PROGRESSIVE)); 619 fProgress->SetFont(be_plain_font); 620 if (fSettings->Progressive) 621 fProgress->SetValue(1); 622 623 AddChild(fProgress); 624 fProgress->ResizeToPreferred(); 625 626 rect.OffsetBy(0, fProgress->Bounds().Height() + 5); 627 628 fOptimizeColors = new BCheckBox(rect, "optimizecolors", VIEW_LABEL_OPTIMIZECOLORS, 629 new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS)); 630 fOptimizeColors->SetFont(be_plain_font); 631 if (fSettings->OptimizeColors) 632 fOptimizeColors->SetValue(1); 633 634 AddChild(fOptimizeColors); 635 fOptimizeColors->ResizeToPreferred(); 636 rect.OffsetBy(0, fOptimizeColors->Bounds().Height() + 5); 637 638 fSmallerFile = new BCheckBox(rect, "smallerfile", VIEW_LABEL_SMALLERFILE, 639 new BMessage(VIEW_MSG_SET_SMALLERFILE)); 640 fSmallerFile->SetFont(be_plain_font); 641 if (fSettings->SmallerFile) 642 fSmallerFile->SetValue(1); 643 if (!fSettings->OptimizeColors) 644 fSmallerFile->SetEnabled(false); 645 646 AddChild(fSmallerFile); 647 fSmallerFile->ResizeToPreferred(); 648 rect.OffsetBy(0, fSmallerFile->Bounds().Height() + 5); 649 650 fGrayAsRGB24 = new BCheckBox(rect, "gray1asrgb24", VIEW_LABEL_GRAY1ASRGB24, 651 new BMessage(VIEW_MSG_SET_GRAY1ASRGB24)); 652 fGrayAsRGB24->SetFont(be_plain_font); 653 if (fSettings->B_GRAY1_as_B_RGB24) 654 fGrayAsRGB24->SetValue(1); 655 656 AddChild(fGrayAsRGB24); 657 658 fGrayAsRGB24->ResizeToPreferred(); 659 } 660 661 662 void 663 TranslatorWriteView::AttachedToWindow() 664 { 665 SView::AttachedToWindow(); 666 667 fQualitySlider->SetTarget(this); 668 fSmoothingSlider->SetTarget(this); 669 fProgress->SetTarget(this); 670 fOptimizeColors->SetTarget(this); 671 fSmallerFile->SetTarget(this); 672 fGrayAsRGB24->SetTarget(this); 673 } 674 675 676 void 677 TranslatorWriteView::MessageReceived(BMessage *message) 678 { 679 switch (message->what) { 680 case VIEW_MSG_SET_QUALITY: 681 { 682 int32 value; 683 if (message->FindInt32("be:value", &value) == B_OK) { 684 fSettings->Quality = value; 685 SaveSettings(fSettings); 686 } 687 break; 688 } 689 case VIEW_MSG_SET_SMOOTHING: 690 { 691 int32 value; 692 if (message->FindInt32("be:value", &value) == B_OK) { 693 fSettings->Smoothing = value; 694 SaveSettings(fSettings); 695 } 696 break; 697 } 698 case VIEW_MSG_SET_PROGRESSIVE: 699 { 700 int32 value; 701 if (message->FindInt32("be:value", &value) == B_OK) { 702 fSettings->Progressive = value; 703 SaveSettings(fSettings); 704 } 705 break; 706 } 707 case VIEW_MSG_SET_OPTIMIZECOLORS: 708 { 709 int32 value; 710 if (message->FindInt32("be:value", &value) == B_OK) { 711 fSettings->OptimizeColors = value; 712 SaveSettings(fSettings); 713 } 714 fSmallerFile->SetEnabled(fSettings->OptimizeColors); 715 break; 716 } 717 case VIEW_MSG_SET_SMALLERFILE: 718 { 719 int32 value; 720 if (message->FindInt32("be:value", &value) == B_OK) { 721 fSettings->SmallerFile = value; 722 SaveSettings(fSettings); 723 } 724 break; 725 } 726 case VIEW_MSG_SET_GRAY1ASRGB24: 727 { 728 int32 value; 729 if (message->FindInt32("be:value", &value) == B_OK) { 730 fSettings->B_GRAY1_as_B_RGB24 = value; 731 SaveSettings(fSettings); 732 } 733 break; 734 } 735 default: 736 BView::MessageReceived(message); 737 break; 738 } 739 } 740 741 742 // #pragma mark - 743 744 745 TranslatorAboutView::TranslatorAboutView(BRect frame, const char *name) 746 : SView(frame, name) 747 { 748 BStringView *title = new BStringView(BRect(10, 0, 10, 0), "Title", 749 translatorName); 750 title->SetFont(be_bold_font); 751 752 AddChild(title); 753 title->ResizeToPreferred(); 754 755 BRect rect = title->Bounds(); 756 float space = title->StringWidth(" "); 757 758 char versionString[16]; 759 sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8), 760 (int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf)); 761 762 BStringView *version = new BStringView(BRect(rect.right + space, rect.top, 763 rect.right+space, rect.top), "Version", versionString); 764 version->SetFont(be_plain_font); 765 version->SetFontSize(9); 766 // Make version be in the same line as title 767 version->ResizeToPreferred(); 768 version->MoveBy(0, rect.bottom-version->Frame().bottom); 769 770 AddChild(version); 771 772 // Now for each line in translatorInfo add a BStringView 773 char* current = translatorInfo; 774 int32 index = 1; 775 BRect stringFrame = title->Frame(); 776 while (current != NULL && current[0]) { 777 char text[128]; 778 char* newLine = strchr(current, '\n'); 779 if (newLine == NULL) { 780 strlcpy(text, current, sizeof(text)); 781 current = NULL; 782 } else { 783 strlcpy(text, current, min_c((int32)sizeof(text), newLine + 1 - current)); 784 current = newLine + 1; 785 } 786 stringFrame.OffsetBy(0, stringFrame.Height() + 2); 787 BStringView* string = new BStringView(stringFrame, "copyright", text); 788 if (index > 3) 789 string->SetFontSize(9); 790 AddChild(string); 791 string->ResizeToPreferred(); 792 793 index++; 794 } 795 796 ResizeToPreferred(); 797 } 798 799 800 // #pragma mark - 801 802 803 TranslatorView::TranslatorView(BRect frame, const char *name) 804 : BTabView(frame, name) 805 { 806 // Load settings to global settings struct 807 LoadSettings(&fSettings); 808 809 BRect contentSize = ContainerView()->Bounds(); 810 SView *view = new TranslatorWriteView(contentSize, "Write", 811 &fSettings); 812 AddTab(view); 813 view = new TranslatorReadView(contentSize, "Read", &fSettings); 814 AddTab(view); 815 view = new TranslatorAboutView(contentSize, "About"); 816 AddTab(view); 817 818 ResizeToPreferred(); 819 820 // Make TranslatorView resize itself with parent 821 SetFlags(Flags() | B_FOLLOW_ALL); 822 } 823 824 825 TranslatorView::~TranslatorView() 826 { 827 } 828 829 830 // #pragma mark - 831 832 833 TranslatorWindow::TranslatorWindow(bool quitOnClose) 834 : BWindow(BRect(100, 100, 100, 100), "JPEG Settings", B_TITLED_WINDOW, 835 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) 836 { 837 BRect extent(0, 0, 0, 0); 838 BView *config = NULL; 839 MakeConfig(NULL, &config, &extent); 840 841 AddChild(config); 842 ResizeTo(extent.Width(), extent.Height()); 843 844 // Make application quit after this window close 845 if (quitOnClose) 846 SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE); 847 } 848 849 850 // #pragma mark - Translator Add-On 851 852 853 854 /*! Hook to create and return our configuration view */ 855 status_t 856 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent) 857 { 858 *outView = new TranslatorView(BRect(0, 0, 320, 300), "TranslatorView"); 859 *outExtent = (*outView)->Frame(); 860 return B_OK; 861 } 862 863 /*! Determine whether or not we can handle this data */ 864 status_t 865 Identify(BPositionIO *inSource, const translation_format *inFormat, 866 BMessage *ioExtension, translator_info *outInfo, uint32 outType) 867 { 868 if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT) 869 return B_NO_TRANSLATOR; 870 871 // !!! You might need to make this buffer bigger to test for your native format 872 off_t position = inSource->Position(); 873 char header[sizeof(TranslatorBitmap)]; 874 status_t err = inSource->Read(header, sizeof(TranslatorBitmap)); 875 inSource->Seek(position, SEEK_SET); 876 if (err < B_OK) 877 return err; 878 879 if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) { 880 outInfo->type = inputFormats[1].type; 881 outInfo->translator = 0; 882 outInfo->group = inputFormats[1].group; 883 outInfo->quality = inputFormats[1].quality; 884 outInfo->capability = inputFormats[1].capability; 885 strcpy(outInfo->name, inputFormats[1].name); 886 strcpy(outInfo->MIME, inputFormats[1].MIME); 887 } else { 888 // First 3 bytes in jpg files are always the same from what i've seen so far 889 // check them 890 if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) { 891 /* this below would be safer but it slows down whole thing 892 893 struct jpeg_decompress_struct cinfo; 894 struct jpeg_error_mgr jerr; 895 cinfo.err = jpeg_std_error(&jerr); 896 jpeg_create_decompress(&cinfo); 897 be_jpeg_stdio_src(&cinfo, inSource); 898 // now try to read header 899 // it can't be read before checking first 3 bytes 900 // because it will hang up if there is no header (not jpeg file) 901 int result = jpeg_read_header(&cinfo, FALSE); 902 jpeg_destroy_decompress(&cinfo); 903 if (result == JPEG_HEADER_OK) { 904 */ outInfo->type = inputFormats[0].type; 905 outInfo->translator = 0; 906 outInfo->group = inputFormats[0].group; 907 outInfo->quality = inputFormats[0].quality; 908 outInfo->capability = inputFormats[0].capability; 909 strcpy(outInfo->name, inputFormats[0].name); 910 strcpy(outInfo->MIME, inputFormats[0].MIME); 911 return B_OK; 912 /* } else 913 return B_NO_TRANSLATOR; 914 */ 915 } else 916 return B_NO_TRANSLATOR; 917 } 918 919 return B_OK; 920 } 921 922 /*! Arguably the most important method in the add-on */ 923 status_t 924 Translate(BPositionIO *inSource, const translator_info *inInfo, 925 BMessage *ioExtension, uint32 outType, BPositionIO *outDestination) 926 { 927 // If no specific type was requested, convert to the interchange format 928 if (outType == 0) 929 outType = B_TRANSLATOR_BITMAP; 930 931 // Setup a "breakpoint" since throwing exceptions does not seem to work 932 // at all in an add-on. (?) 933 // In the be_jerror.cpp we implement a handler for critical library errors 934 // (be_error_exit()) and there we use the longjmp() function to return to 935 // this place. If this happens, it is as if the setjmp() call is called 936 // a second time, but this time the return value will be 1. The first 937 // invokation will return 0. 938 jmp_buf longJumpBuffer; 939 int jmpRet = setjmp(longJumpBuffer); 940 if (jmpRet == 1) 941 return B_ERROR; 942 943 try { 944 // What action to take, based on the findings of Identify() 945 if (outType == inInfo->type) { 946 return Copy(inSource, outDestination); 947 } else if (inInfo->type == B_TRANSLATOR_BITMAP 948 && outType == JPEG_FORMAT) { 949 return Compress(inSource, outDestination, &longJumpBuffer); 950 } else if (inInfo->type == JPEG_FORMAT 951 && outType == B_TRANSLATOR_BITMAP) { 952 return Decompress(inSource, outDestination, ioExtension, 953 &longJumpBuffer); 954 } 955 } catch (...) { 956 fprintf(stderr, "libjpeg encoutered a critical error " 957 "(caught C++ exception).\n"); 958 return B_ERROR; 959 } 960 961 return B_NO_TRANSLATOR; 962 } 963 964 /*! The user has requested the same format for input and output, so just copy */ 965 static status_t 966 Copy(BPositionIO *in, BPositionIO *out) 967 { 968 int block_size = 65536; 969 void *buffer = malloc(block_size); 970 char temp[1024]; 971 if (buffer == NULL) { 972 buffer = temp; 973 block_size = 1024; 974 } 975 status_t err = B_OK; 976 977 // Read until end of file or error 978 while (1) { 979 ssize_t to_read = block_size; 980 err = in->Read(buffer, to_read); 981 // Explicit check for EOF 982 if (err == -1) { 983 if (buffer != temp) free(buffer); 984 return B_OK; 985 } 986 if (err <= B_OK) break; 987 to_read = err; 988 err = out->Write(buffer, to_read); 989 if (err != to_read) if (err >= 0) err = B_DEVICE_FULL; 990 if (err < B_OK) break; 991 } 992 993 if (buffer != temp) free(buffer); 994 return (err >= 0) ? B_OK : err; 995 } 996 997 998 /*! Encode into the native format */ 999 static status_t 1000 Compress(BPositionIO *in, BPositionIO *out, const jmp_buf* longJumpBuffer) 1001 { 1002 // Load Settings 1003 jpeg_settings settings; 1004 LoadSettings(&settings); 1005 1006 // Read info about bitmap 1007 TranslatorBitmap header; 1008 status_t err = in->Read(&header, sizeof(TranslatorBitmap)); 1009 if (err < B_OK) 1010 return err; 1011 else if (err < (int)sizeof(TranslatorBitmap)) 1012 return B_ERROR; 1013 1014 // Grab dimension, color space, and size information from the stream 1015 BRect bounds; 1016 bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left); 1017 bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top); 1018 bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right); 1019 bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom); 1020 1021 int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes); 1022 1023 int width = bounds.IntegerWidth() + 1; 1024 int height = bounds.IntegerHeight() + 1; 1025 1026 // Function pointer to convert function 1027 // It will point to proper function if needed 1028 void (*converter)(uchar *inscanline, uchar *outscanline, 1029 int32 inRowBytes) = NULL; 1030 1031 // Default color info 1032 J_COLOR_SPACE jpg_color_space = JCS_RGB; 1033 int jpg_input_components = 3; 1034 int32 out_row_bytes; 1035 int padding = 0; 1036 1037 switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) { 1038 case B_CMAP8: 1039 converter = convert_from_cmap8_to_24; 1040 padding = in_row_bytes - width; 1041 break; 1042 1043 case B_GRAY1: 1044 if (settings.B_GRAY1_as_B_RGB24) { 1045 converter = convert_from_gray1_to_24; 1046 } else { 1047 jpg_input_components = 1; 1048 jpg_color_space = JCS_GRAYSCALE; 1049 converter = convert_from_gray1_to_gray8; 1050 } 1051 padding = in_row_bytes - (width/8); 1052 break; 1053 1054 case B_GRAY8: 1055 jpg_input_components = 1; 1056 jpg_color_space = JCS_GRAYSCALE; 1057 padding = in_row_bytes - width; 1058 break; 1059 1060 case B_RGB15: 1061 case B_RGBA15: 1062 converter = convert_from_15_to_24; 1063 padding = in_row_bytes - (width * 2); 1064 break; 1065 1066 case B_RGB15_BIG: 1067 case B_RGBA15_BIG: 1068 converter = convert_from_15b_to_24; 1069 padding = in_row_bytes - (width * 2); 1070 break; 1071 1072 case B_RGB16: 1073 converter = convert_from_16_to_24; 1074 padding = in_row_bytes - (width * 2); 1075 break; 1076 1077 case B_RGB16_BIG: 1078 converter = convert_from_16b_to_24; 1079 padding = in_row_bytes - (width * 2); 1080 break; 1081 1082 case B_RGB24: 1083 converter = convert_from_24_to_24; 1084 padding = in_row_bytes - (width * 3); 1085 break; 1086 1087 case B_RGB24_BIG: 1088 padding = in_row_bytes - (width * 3); 1089 break; 1090 1091 case B_RGB32: 1092 case B_RGBA32: 1093 converter = convert_from_32_to_24; 1094 padding = in_row_bytes - (width * 4); 1095 break; 1096 1097 case B_RGB32_BIG: 1098 case B_RGBA32_BIG: 1099 converter = convert_from_32b_to_24; 1100 padding = in_row_bytes - (width * 4); 1101 break; 1102 1103 case B_CMYK32: 1104 jpg_color_space = JCS_CMYK; 1105 jpg_input_components = 4; 1106 padding = in_row_bytes - (width * 4); 1107 break; 1108 1109 default: 1110 fprintf(stderr, "Wrong type: Color space not implemented.\n"); 1111 return B_ERROR; 1112 } 1113 out_row_bytes = jpg_input_components * width; 1114 1115 // Set basic things needed for jpeg writing 1116 struct jpeg_compress_struct cinfo; 1117 struct jpeg_error_mgr jerr; 1118 cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer); 1119 jpeg_create_compress(&cinfo); 1120 be_jpeg_stdio_dest(&cinfo, out); 1121 1122 // Set basic values 1123 cinfo.image_width = width; 1124 cinfo.image_height = height; 1125 cinfo.input_components = jpg_input_components; 1126 cinfo.in_color_space = jpg_color_space; 1127 jpeg_set_defaults(&cinfo); 1128 1129 // Set better accuracy 1130 cinfo.dct_method = JDCT_ISLOW; 1131 1132 // This is needed to prevent some colors loss 1133 // With it generated jpegs are as good as from Fireworks (at last! :D) 1134 if (settings.OptimizeColors) { 1135 int index = 0; 1136 while (index < cinfo.num_components) { 1137 cinfo.comp_info[index].h_samp_factor = 1; 1138 cinfo.comp_info[index].v_samp_factor = 1; 1139 // This will make file smaller, but with worse quality more or less 1140 // like with 93%-94% (but it's subjective opinion) on tested images 1141 // but with smaller size (between 92% and 93% on tested images) 1142 if (settings.SmallerFile) 1143 cinfo.comp_info[index].quant_tbl_no = 1; 1144 // This will make bigger file, but also better quality ;] 1145 // from my tests it seems like useless - better quality with smaller 1146 // can be acheived without this 1147 // cinfo.comp_info[index].dc_tbl_no = 1; 1148 // cinfo.comp_info[index].ac_tbl_no = 1; 1149 index++; 1150 } 1151 } 1152 1153 // Set quality 1154 jpeg_set_quality(&cinfo, settings.Quality, true); 1155 1156 // Set progressive compression if needed 1157 // if not, turn on optimizing in libjpeg 1158 if (settings.Progressive) 1159 jpeg_simple_progression(&cinfo); 1160 else 1161 cinfo.optimize_coding = TRUE; 1162 1163 // Set smoothing (effect like Blur) 1164 cinfo.smoothing_factor = settings.Smoothing; 1165 1166 // Initialize compression 1167 jpeg_start_compress(&cinfo, TRUE); 1168 1169 // Declare scanlines 1170 JSAMPROW in_scanline = NULL; 1171 JSAMPROW out_scanline = NULL; 1172 JSAMPROW writeline; // Pointer to in_scanline (default) or out_scanline (if there will be conversion) 1173 1174 // Allocate scanline 1175 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1176 in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1177 JPOOL_PERMANENT, in_row_bytes); 1178 1179 // We need 2nd scanline storage ony for conversion 1180 if (converter != NULL) { 1181 // There will be conversion, allocate second scanline... 1182 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1183 out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1184 JPOOL_PERMANENT, out_row_bytes); 1185 // ... and make it the one to write to file 1186 writeline = out_scanline; 1187 } else 1188 writeline = in_scanline; 1189 1190 while (cinfo.next_scanline < cinfo.image_height) { 1191 // Read scanline 1192 err = in->Read(in_scanline, in_row_bytes); 1193 if (err < in_row_bytes) 1194 return err < B_OK ? Error((j_common_ptr)&cinfo, err) 1195 : Error((j_common_ptr)&cinfo, B_ERROR); 1196 1197 // Convert if needed 1198 if (converter != NULL) 1199 converter(in_scanline, out_scanline, in_row_bytes - padding); 1200 1201 // Write scanline 1202 jpeg_write_scanlines(&cinfo, &writeline, 1); 1203 } 1204 1205 jpeg_finish_compress(&cinfo); 1206 jpeg_destroy_compress(&cinfo); 1207 return B_OK; 1208 } 1209 1210 1211 /*! Decode the native format */ 1212 static status_t 1213 Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension, 1214 const jmp_buf* longJumpBuffer) 1215 { 1216 // Load Settings 1217 jpeg_settings settings; 1218 LoadSettings(&settings); 1219 1220 // Set basic things needed for jpeg reading 1221 struct jpeg_decompress_struct cinfo; 1222 struct jpeg_error_mgr jerr; 1223 cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer); 1224 jpeg_create_decompress(&cinfo); 1225 be_jpeg_stdio_src(&cinfo, in); 1226 1227 jpeg_save_markers(&cinfo, MARKER_EXIF, 131072); 1228 // make sure the EXIF tag is stored 1229 1230 // Read info about image 1231 jpeg_read_header(&cinfo, TRUE); 1232 1233 BMessage exif; 1234 1235 // parse EXIF data and add it ioExtension, if any 1236 jpeg_marker_struct* marker = cinfo.marker_list; 1237 while (marker != NULL) { 1238 if (marker->marker == MARKER_EXIF 1239 && !strncmp((char*)marker->data, "Exif", 4)) { 1240 if (ioExtension != NULL) { 1241 // Strip EXIF header from TIFF data 1242 ioExtension->AddData("exif", B_RAW_TYPE, 1243 (uint8 *)marker->data + 6, marker->data_length - 6); 1244 } 1245 1246 BMemoryIO io(marker->data + 6, marker->data_length - 6); 1247 convert_exif_to_message(io, exif); 1248 } 1249 marker = marker->next; 1250 } 1251 1252 // Default color info 1253 color_space outColorSpace = B_RGB32; 1254 int outColorComponents = 4; 1255 1256 // Function pointer to convert function 1257 // It will point to proper function if needed 1258 void (*converter)(uchar *inScanLine, uchar *outScanLine, 1259 int32 inRowBytes, int32 xStep) = convert_from_24_to_32; 1260 1261 // If color space isn't rgb 1262 if (cinfo.out_color_space != JCS_RGB) { 1263 switch (cinfo.out_color_space) { 1264 case JCS_UNKNOWN: /* error/unspecified */ 1265 fprintf(stderr, "From Type: Jpeg uses unknown color type\n"); 1266 break; 1267 case JCS_GRAYSCALE: /* monochrome */ 1268 // Check if user wants to read only as RGB32 or not 1269 if (!settings.Always_B_RGB32) { 1270 // Grayscale 1271 outColorSpace = B_GRAY8; 1272 outColorComponents = 1; 1273 converter = translate_8; 1274 } else { 1275 // RGB 1276 cinfo.out_color_space = JCS_RGB; 1277 cinfo.output_components = 3; 1278 converter = convert_from_24_to_32; 1279 } 1280 break; 1281 case JCS_YCbCr: /* Y/Cb/Cr (also known as YUV) */ 1282 cinfo.out_color_space = JCS_RGB; 1283 converter = convert_from_24_to_32; 1284 break; 1285 case JCS_YCCK: /* Y/Cb/Cr/K */ 1286 // Let libjpeg convert it to CMYK 1287 cinfo.out_color_space = JCS_CMYK; 1288 // Fall through to CMYK since we need the same settings 1289 case JCS_CMYK: /* C/M/Y/K */ 1290 // Use proper converter 1291 if (settings.PhotoshopCMYK) 1292 converter = convert_from_CMYK_to_32_photoshop; 1293 else 1294 converter = convert_from_CMYK_to_32; 1295 break; 1296 default: 1297 fprintf(stderr, "From Type: Jpeg uses hmm... i don't know really :(\n"); 1298 break; 1299 } 1300 } 1301 1302 // Initialize decompression 1303 jpeg_start_decompress(&cinfo); 1304 1305 // retrieve orientation from settings/EXIF 1306 int32 orientation; 1307 if (ioExtension == NULL 1308 || ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) { 1309 if (exif.FindInt32("Orientation", &orientation) != B_OK) 1310 orientation = 1; 1311 } 1312 1313 if (orientation != 1 && converter == NULL) 1314 converter = translate_8; 1315 1316 int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width; 1317 int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height; 1318 1319 int32 destOffset = dest_index(outputWidth, outputHeight, 1320 0, 0, orientation) * outColorComponents; 1321 int32 xStep = dest_index(outputWidth, outputHeight, 1322 1, 0, orientation) * outColorComponents - destOffset; 1323 int32 yStep = dest_index(outputWidth, outputHeight, 1324 0, 1, orientation) * outColorComponents - destOffset; 1325 bool needAll = orientation != 1; 1326 1327 // Initialize this bounds rect to the size of your image 1328 BRect bounds(0, 0, outputWidth - 1, outputHeight - 1); 1329 1330 #if 0 1331 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n", 1332 destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height, 1333 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation); 1334 #endif 1335 1336 // Bytes count in one line of image (scanline) 1337 int32 inRowBytes = cinfo.output_width * cinfo.output_components; 1338 int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents; 1339 int32 dataSize = cinfo.output_width * cinfo.output_height 1340 * outColorComponents; 1341 1342 // Fill out the B_TRANSLATOR_BITMAP's header 1343 TranslatorBitmap header; 1344 header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); 1345 header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left); 1346 header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top); 1347 header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right); 1348 header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom); 1349 header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace); 1350 header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes); 1351 header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize); 1352 1353 // Write out the header 1354 status_t err = out->Write(&header, sizeof(TranslatorBitmap)); 1355 if (err < B_OK) 1356 return Error((j_common_ptr)&cinfo, err); 1357 else if (err < (int)sizeof(TranslatorBitmap)) 1358 return Error((j_common_ptr)&cinfo, B_ERROR); 1359 1360 // Declare scanlines 1361 JSAMPROW inScanLine = NULL; 1362 uint8* dest = NULL; 1363 uint8* destLine = NULL; 1364 1365 // Allocate scanline 1366 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1367 inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1368 JPOOL_PERMANENT, inRowBytes); 1369 1370 // We need 2nd scanline storage only for conversion 1371 if (converter != NULL) { 1372 // There will be conversion, allocate second scanline... 1373 // Use libjpeg memory allocation functions, so in case of error it will free 1374 // them itself 1375 dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1376 JPOOL_PERMANENT, needAll ? dataSize : rowBytes); 1377 destLine = dest + destOffset; 1378 } else 1379 destLine = inScanLine; 1380 1381 while (cinfo.output_scanline < cinfo.output_height) { 1382 // Read scanline 1383 jpeg_read_scanlines(&cinfo, &inScanLine, 1); 1384 1385 // Convert if needed 1386 if (converter != NULL) 1387 converter(inScanLine, destLine, inRowBytes, xStep); 1388 1389 if (!needAll) { 1390 // Write the scanline buffer to the output stream 1391 ssize_t bytesWritten = out->Write(destLine, rowBytes); 1392 if (bytesWritten < rowBytes) { 1393 return bytesWritten < B_OK 1394 ? Error((j_common_ptr)&cinfo, bytesWritten) 1395 : Error((j_common_ptr)&cinfo, B_ERROR); 1396 } 1397 } else 1398 destLine += yStep; 1399 } 1400 1401 if (needAll) { 1402 ssize_t bytesWritten = out->Write(dest, dataSize); 1403 if (bytesWritten < dataSize) { 1404 return bytesWritten < B_OK 1405 ? Error((j_common_ptr)&cinfo, bytesWritten) 1406 : Error((j_common_ptr)&cinfo, B_ERROR); 1407 } 1408 } 1409 1410 jpeg_finish_decompress(&cinfo); 1411 jpeg_destroy_decompress(&cinfo); 1412 return B_OK; 1413 } 1414 1415 /*! 1416 Frees jpeg alocated memory 1417 Returns given error (B_ERROR by default) 1418 */ 1419 static status_t 1420 Error(j_common_ptr cinfo, status_t error) 1421 { 1422 jpeg_destroy(cinfo); 1423 return error; 1424 } 1425 1426 1427 // #pragma mark - 1428 1429 1430 int 1431 main(int, char**) 1432 { 1433 BApplication app("application/x-vnd.Haiku-JPEGTranslator"); 1434 1435 TranslatorWindow *window = new TranslatorWindow(); 1436 window->Show(); 1437 1438 app.Run(); 1439 return 0; 1440 } 1441 1442