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 image" 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 © 1991-1998, Thomas G. Lane\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 bool gAreSettingsRunning = false; 96 97 98 //! Make settings to defaults 99 void 100 LoadDefaultSettings(jpeg_settings *settings) 101 { 102 settings->Smoothing = 0; 103 settings->Quality = 95; 104 settings->Progressive = true; 105 settings->OptimizeColors = true; 106 settings->SmallerFile = false; 107 settings->B_GRAY1_as_B_RGB24 = false; 108 settings->Always_B_RGB32 = true; 109 settings->PhotoshopCMYK = true; 110 settings->ShowReadWarningBox = true; 111 } 112 113 114 //! Save settings to config file 115 void 116 SaveSettings(jpeg_settings *settings) 117 { 118 // Make path to settings file 119 BPath path; 120 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 121 return; 122 123 path.Append(SETTINGS_FILE); 124 125 // Open settings file (create it if there's no file) and write settings 126 FILE *file = NULL; 127 if ((file = fopen(path.Path(), "wb+"))) { 128 fwrite(settings, sizeof(jpeg_settings), 1, file); 129 fclose(file); 130 } 131 } 132 133 134 //! Return true if settings were run, false if not 135 bool 136 SettingsChangedAlert() 137 { 138 // If settings view wasn't already initialized (settings not running) 139 // and user wants to run settings 140 if (!gAreSettingsRunning 141 && (new BAlert("Different settings file", 142 "JPEG settings were set to default because of incompatible settings file.", 143 "Configure settings", "OK", NULL, B_WIDTH_AS_USUAL, 144 B_WARNING_ALERT))->Go() == 0) { 145 // Create settings window (with no quit on close!), launch 146 // it and wait until it's closed 147 TranslatorWindow *window = new TranslatorWindow(false); 148 window->Show(); 149 150 status_t err; 151 wait_for_thread(window->Thread(), &err); 152 return true; 153 } 154 155 return false; 156 } 157 158 159 /*! 160 Load settings from config file 161 If can't find it make them default and try to save 162 */ 163 void 164 LoadSettings(jpeg_settings *settings) 165 { 166 // Make path to settings file 167 BPath path; 168 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) { 169 LoadDefaultSettings(settings); 170 return; 171 } 172 173 path.Append(SETTINGS_FILE); 174 175 // Open settings file (create it if there's no file) and write settings 176 FILE *file = NULL; 177 if ((file = fopen(path.Path(), "rb")) != NULL) { 178 if (!fread(settings, sizeof(jpeg_settings), 1, file)) { 179 // settings struct has changed size 180 // Load default settings, and Save them 181 fclose(file); 182 LoadDefaultSettings(settings); 183 SaveSettings(settings); 184 // Tell user settings were changed to default, and ask to run settings panel or not 185 if (SettingsChangedAlert()) 186 // User configured settings, load them again 187 LoadSettings(settings); 188 } else 189 fclose(file); 190 } else if ((file = fopen(path.Path(), "wb+")) != NULL) { 191 LoadDefaultSettings(settings); 192 fwrite(settings, sizeof(jpeg_settings), 1, file); 193 fclose(file); 194 } 195 } 196 197 198 static bool 199 x_flipped(int32 orientation) 200 { 201 return orientation == 2 || orientation == 3 202 || orientation == 6 || orientation == 7; 203 } 204 205 206 static bool 207 y_flipped(int32 orientation) 208 { 209 return orientation == 3 || orientation == 4 210 || orientation == 7 || orientation == 8; 211 } 212 213 214 static int32 215 dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation) 216 { 217 if (orientation > 4) { 218 uint32 temp = x; 219 x = y; 220 y = temp; 221 } 222 if (y_flipped(orientation)) 223 y = height - 1 - y; 224 if (x_flipped(orientation)) 225 x = width - 1 - x; 226 227 return y * width + x; 228 } 229 230 231 // #pragma mark - conversion for compression 232 233 234 inline void 235 convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes) 236 { 237 int32 index = 0; 238 int32 index2 = 0; 239 while (index < inRowBytes) { 240 unsigned char c = in[index++]; 241 for (int b = 128; b; b = b>>1) { 242 unsigned char color; 243 if (c & b) 244 color = 0; 245 else 246 color = 255; 247 out[index2++] = color; 248 } 249 } 250 } 251 252 253 inline void 254 convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes) 255 { 256 int32 index = 0; 257 int32 index2 = 0; 258 while (index < inRowBytes) { 259 unsigned char c = in[index++]; 260 for (int b = 128; b; b = b>>1) { 261 unsigned char color; 262 if (c & b) 263 color = 0; 264 else 265 color = 255; 266 out[index2++] = color; 267 out[index2++] = color; 268 out[index2++] = color; 269 } 270 } 271 } 272 273 274 inline void 275 convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes) 276 { 277 const color_map * map = system_colors(); 278 int32 index = 0; 279 int32 index2 = 0; 280 while (index < inRowBytes) { 281 rgb_color color = map->color_list[in[index++]]; 282 283 out[index2++] = color.red; 284 out[index2++] = color.green; 285 out[index2++] = color.blue; 286 } 287 } 288 289 290 inline void 291 convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes) 292 { 293 int32 index = 0; 294 int32 index2 = 0; 295 int16 in_pixel; 296 while (index < inRowBytes) { 297 in_pixel = in[index] | (in[index+1] << 8); 298 index += 2; 299 300 out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12); 301 out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7); 302 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 303 } 304 } 305 306 307 inline void 308 convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes) 309 { 310 int32 index = 0; 311 int32 index2 = 0; 312 int16 in_pixel; 313 while (index < inRowBytes) { 314 in_pixel = in[index+1] | (in[index] << 8); 315 index += 2; 316 317 out[index2++] = (((in_pixel & 0x7c00)) >> 7) | (((in_pixel & 0x7c00)) >> 12); 318 out[index2++] = (((in_pixel & 0x3e0)) >> 2) | (((in_pixel & 0x3e0)) >> 7); 319 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 320 } 321 } 322 323 324 inline void 325 convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes) 326 { 327 int32 index = 0; 328 int32 index2 = 0; 329 int16 in_pixel; 330 while (index < inRowBytes) { 331 in_pixel = in[index] | (in[index+1] << 8); 332 index += 2; 333 334 out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13); 335 out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9); 336 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 337 } 338 } 339 340 341 inline void 342 convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes) 343 { 344 int32 index = 0; 345 int32 index2 = 0; 346 int16 in_pixel; 347 while (index < inRowBytes) { 348 in_pixel = in[index+1] | (in[index] << 8); 349 index += 2; 350 351 out[index2++] = (((in_pixel & 0xf800)) >> 8) | (((in_pixel & 0xf800)) >> 13); 352 out[index2++] = (((in_pixel & 0x7e0)) >> 3) | (((in_pixel & 0x7e0)) >> 9); 353 out[index2++] = (((in_pixel & 0x1f)) << 3) | (((in_pixel & 0x1f)) >> 2); 354 } 355 } 356 357 358 inline void 359 convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes) 360 { 361 int32 index = 0; 362 int32 index2 = 0; 363 while (index < inRowBytes) { 364 out[index2++] = in[index+2]; 365 out[index2++] = in[index+1]; 366 out[index2++] = in[index]; 367 index+=3; 368 } 369 } 370 371 372 inline void 373 convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes) 374 { 375 inRowBytes /= 4; 376 377 for (int32 i = 0; i < inRowBytes; i++) { 378 out[0] = in[2]; 379 out[1] = in[1]; 380 out[2] = in[0]; 381 382 in += 4; 383 out += 3; 384 } 385 } 386 387 388 inline void 389 convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes) 390 { 391 inRowBytes /= 4; 392 393 for (int32 i = 0; i < inRowBytes; i++) { 394 out[0] = in[1]; 395 out[1] = in[2]; 396 out[2] = in[3]; 397 398 in += 4; 399 out += 3; 400 } 401 } 402 403 404 // #pragma mark - conversion for decompression 405 406 407 inline void 408 convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 409 { 410 for (int32 i = 0; i < inRowBytes; i += 4) { 411 int32 black = in[3]; 412 out[0] = in[2] * black / 255; 413 out[1] = in[1] * black / 255; 414 out[2] = in[0] * black / 255; 415 out[3] = 255; 416 417 in += 4; 418 out += xStep; 419 } 420 } 421 422 423 //! !!! UNTESTED !!! 424 inline void 425 convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 426 { 427 for (int32 i = 0; i < inRowBytes; i += 4) { 428 int32 black = 255 - in[3]; 429 out[0] = ((255 - in[2]) * black) / 255; 430 out[1] = ((255 - in[1]) * black) / 255; 431 out[2] = ((255 - in[0]) * black) / 255; 432 out[3] = 255; 433 434 in += 4; 435 out += xStep; 436 } 437 } 438 439 440 //! RGB24 8:8:8 to xRGB 8:8:8:8 441 inline void 442 convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 443 { 444 for (int32 i = 0; i < inRowBytes; i += 3) { 445 out[0] = in[2]; 446 out[1] = in[1]; 447 out[2] = in[0]; 448 out[3] = 255; 449 450 in += 3; 451 out += xStep; 452 } 453 } 454 455 456 //! 8-bit to 8-bit, only need when rotating the image 457 void 458 translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep) 459 { 460 for (int32 i = 0; i < inRowBytes; i++) { 461 out[0] = in[0]; 462 463 in++; 464 out += xStep; 465 } 466 } 467 468 469 // #pragma mark - SView 470 471 472 SView::SView(const char *name, float x, float y) 473 : BView(BRect(x, y, x, y), name, B_FOLLOW_NONE, B_WILL_DRAW) 474 { 475 fPreferredWidth = 0; 476 fPreferredHeight = 0; 477 478 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 479 SetLowColor(ViewColor()); 480 481 SetFont(be_plain_font); 482 } 483 484 485 void 486 SView::GetPreferredSize(float* _width, float* _height) 487 { 488 if (_width) 489 *_width = fPreferredWidth; 490 if (_height) 491 *_height = fPreferredHeight; 492 } 493 494 495 void 496 SView::ResizeToPreferred() 497 { 498 ResizeTo(fPreferredWidth, fPreferredHeight); 499 } 500 501 502 void 503 SView::ResizePreferredBy(float width, float height) 504 { 505 fPreferredWidth += width; 506 fPreferredHeight += height; 507 } 508 509 510 void 511 SView::AddChild(BView *child, BView *before) 512 { 513 BView::AddChild(child, before); 514 child->ResizeToPreferred(); 515 BRect frame = child->Frame(); 516 517 if (frame.right > fPreferredWidth) 518 fPreferredWidth = frame.right; 519 if (frame.bottom > fPreferredHeight) 520 fPreferredHeight = frame.bottom; 521 } 522 523 524 // #pragma mark - 525 526 527 SSlider::SSlider(BRect frame, const char *name, const char *label, 528 BMessage *message, int32 minValue, int32 maxValue, orientation posture, 529 thumb_style thumbType, uint32 resizingMode, uint32 flags) 530 : BSlider(frame, name, label, message, minValue, maxValue, 531 posture, thumbType, resizingMode, flags) 532 { 533 rgb_color barColor = { 0, 0, 229, 255 }; 534 UseFillColor(true, &barColor); 535 } 536 537 538 //! Update status string - show actual value 539 char* 540 SSlider::UpdateText() const 541 { 542 snprintf(fStatusLabel, sizeof(fStatusLabel), "%ld", Value()); 543 return fStatusLabel; 544 } 545 546 547 //! BSlider::ResizeToPreferred + Resize width if it's too small to show label and status 548 void 549 SSlider::ResizeToPreferred() 550 { 551 int32 width = (int32)ceil(StringWidth(Label()) + StringWidth("9999")); 552 if (width < 230) 553 width = 230; 554 555 float w, h; 556 GetPreferredSize(&w, &h); 557 ResizeTo(width, h); 558 } 559 560 561 // #pragma mark - 562 563 564 TranslatorReadView::TranslatorReadView(const char *name, jpeg_settings *settings, 565 float x, float y) 566 : SView(name, x, y), 567 fSettings(settings) 568 { 569 fAlwaysRGB32 = new BCheckBox(BRect(10, GetPreferredHeight(), 10, 570 GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_ALWAYSRGB32, 571 new BMessage(VIEW_MSG_SET_ALWAYSRGB32)); 572 fAlwaysRGB32->SetFont(be_plain_font); 573 if (fSettings->Always_B_RGB32) 574 fAlwaysRGB32->SetValue(1); 575 576 AddChild(fAlwaysRGB32); 577 578 fPhotoshopCMYK = new BCheckBox(BRect(10, GetPreferredHeight(), 10, 579 GetPreferredHeight()), "photoshopCMYK", VIEW_LABEL_PHOTOSHOPCMYK, 580 new BMessage(VIEW_MSG_SET_PHOTOSHOPCMYK)); 581 fPhotoshopCMYK->SetFont(be_plain_font); 582 if (fSettings->PhotoshopCMYK) 583 fPhotoshopCMYK->SetValue(1); 584 585 AddChild(fPhotoshopCMYK); 586 587 fShowErrorBox = new BCheckBox(BRect(10, GetPreferredHeight(), 10, 588 GetPreferredHeight()), "error", VIEW_LABEL_SHOWREADERRORBOX, 589 new BMessage(VIEW_MSG_SET_SHOWREADERRORBOX)); 590 fShowErrorBox->SetFont(be_plain_font); 591 if (fSettings->ShowReadWarningBox) 592 fShowErrorBox->SetValue(1); 593 594 AddChild(fShowErrorBox); 595 596 ResizeToPreferred(); 597 } 598 599 600 void 601 TranslatorReadView::AttachedToWindow() 602 { 603 fAlwaysRGB32->SetTarget(this); 604 fPhotoshopCMYK->SetTarget(this); 605 fShowErrorBox->SetTarget(this); 606 } 607 608 609 void 610 TranslatorReadView::MessageReceived(BMessage* message) 611 { 612 switch (message->what) { 613 case VIEW_MSG_SET_ALWAYSRGB32: 614 { 615 int32 value; 616 if (message->FindInt32("be:value", &value) == B_OK) { 617 fSettings->Always_B_RGB32 = value; 618 SaveSettings(fSettings); 619 } 620 break; 621 } 622 case VIEW_MSG_SET_PHOTOSHOPCMYK: 623 { 624 int32 value; 625 if (message->FindInt32("be:value", &value) == B_OK) { 626 fSettings->PhotoshopCMYK = value; 627 SaveSettings(fSettings); 628 } 629 break; 630 } 631 case VIEW_MSG_SET_SHOWREADERRORBOX: 632 { 633 int32 value; 634 if (message->FindInt32("be:value", &value) == B_OK) { 635 fSettings->ShowReadWarningBox = value; 636 SaveSettings(fSettings); 637 } 638 break; 639 } 640 default: 641 BView::MessageReceived(message); 642 break; 643 } 644 } 645 646 647 // #pragma mark - TranslatorWriteView 648 649 650 TranslatorWriteView::TranslatorWriteView(const char *name, jpeg_settings *settings, 651 float x, float y) 652 : SView(name, x, y), 653 fSettings(settings) 654 { 655 fQualitySlider = new SSlider(BRect(10, GetPreferredHeight(), 10, 656 GetPreferredHeight()), "quality", VIEW_LABEL_QUALITY, 657 new BMessage(VIEW_MSG_SET_QUALITY), 0, 100); 658 fQualitySlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 659 fQualitySlider->SetHashMarkCount(10); 660 fQualitySlider->SetLimitLabels("Low", "High"); 661 fQualitySlider->SetFont(be_plain_font); 662 fQualitySlider->SetValue(fSettings->Quality); 663 AddChild(fQualitySlider); 664 665 fSmoothingSlider = new SSlider(BRect(10, GetPreferredHeight()+10, 10, 666 GetPreferredHeight()), "smoothing", VIEW_LABEL_SMOOTHING, 667 new BMessage(VIEW_MSG_SET_SMOOTHING), 0, 100); 668 fSmoothingSlider->SetHashMarks(B_HASH_MARKS_BOTTOM); 669 fSmoothingSlider->SetHashMarkCount(10); 670 fSmoothingSlider->SetLimitLabels("None", "High"); 671 fSmoothingSlider->SetFont(be_plain_font); 672 fSmoothingSlider->SetValue(fSettings->Smoothing); 673 AddChild(fSmoothingSlider); 674 675 fProgress = new BCheckBox(BRect(10, GetPreferredHeight()+10, 10, 676 GetPreferredHeight()), "progress", VIEW_LABEL_PROGRESSIVE, 677 new BMessage(VIEW_MSG_SET_PROGRESSIVE)); 678 fProgress->SetFont(be_plain_font); 679 if (fSettings->Progressive) 680 fProgress->SetValue(1); 681 682 AddChild(fProgress); 683 684 fOptimizeColors = new BCheckBox(BRect(10, GetPreferredHeight()+5, 10, 685 GetPreferredHeight() + 5), "optimizecolors", VIEW_LABEL_OPTIMIZECOLORS, 686 new BMessage(VIEW_MSG_SET_OPTIMIZECOLORS)); 687 fOptimizeColors->SetFont(be_plain_font); 688 if (fSettings->OptimizeColors) 689 fOptimizeColors->SetValue(1); 690 691 AddChild(fOptimizeColors); 692 693 fSmallerFile = new BCheckBox(BRect(25, GetPreferredHeight()+5, 25, 694 GetPreferredHeight() + 5), "smallerfile", VIEW_LABEL_SMALLERFILE, 695 new BMessage(VIEW_MSG_SET_SMALLERFILE)); 696 fSmallerFile->SetFont(be_plain_font); 697 if (fSettings->SmallerFile) 698 fSmallerFile->SetValue(1); 699 if (!fSettings->OptimizeColors) 700 fSmallerFile->SetEnabled(false); 701 702 AddChild(fSmallerFile); 703 704 fGrayAsRGB24 = new BCheckBox(BRect(10, GetPreferredHeight()+5, 25, 705 GetPreferredHeight()+5), "gray1asrgb24", VIEW_LABEL_GRAY1ASRGB24, 706 new BMessage(VIEW_MSG_SET_GRAY1ASRGB24)); 707 fGrayAsRGB24->SetFont(be_plain_font); 708 if (fSettings->B_GRAY1_as_B_RGB24) 709 fGrayAsRGB24->SetValue(1); 710 711 AddChild(fGrayAsRGB24); 712 713 ResizeToPreferred(); 714 } 715 716 717 void 718 TranslatorWriteView::AttachedToWindow() 719 { 720 fQualitySlider->SetTarget(this); 721 fSmoothingSlider->SetTarget(this); 722 fProgress->SetTarget(this); 723 fOptimizeColors->SetTarget(this); 724 fSmallerFile->SetTarget(this); 725 fGrayAsRGB24->SetTarget(this); 726 } 727 728 729 void 730 TranslatorWriteView::MessageReceived(BMessage *message) 731 { 732 switch (message->what) { 733 case VIEW_MSG_SET_QUALITY: 734 { 735 int32 value; 736 if (message->FindInt32("be:value", &value) == B_OK) { 737 fSettings->Quality = value; 738 SaveSettings(fSettings); 739 } 740 break; 741 } 742 case VIEW_MSG_SET_SMOOTHING: 743 { 744 int32 value; 745 if (message->FindInt32("be:value", &value) == B_OK) { 746 fSettings->Smoothing = value; 747 SaveSettings(fSettings); 748 } 749 break; 750 } 751 case VIEW_MSG_SET_PROGRESSIVE: 752 { 753 int32 value; 754 if (message->FindInt32("be:value", &value) == B_OK) { 755 fSettings->Progressive = value; 756 SaveSettings(fSettings); 757 } 758 break; 759 } 760 case VIEW_MSG_SET_OPTIMIZECOLORS: 761 { 762 int32 value; 763 if (message->FindInt32("be:value", &value) == B_OK) { 764 fSettings->OptimizeColors = value; 765 SaveSettings(fSettings); 766 } 767 fSmallerFile->SetEnabled(fSettings->OptimizeColors); 768 break; 769 } 770 case VIEW_MSG_SET_SMALLERFILE: 771 { 772 int32 value; 773 if (message->FindInt32("be:value", &value) == B_OK) { 774 fSettings->SmallerFile = value; 775 SaveSettings(fSettings); 776 } 777 break; 778 } 779 case VIEW_MSG_SET_GRAY1ASRGB24: 780 { 781 int32 value; 782 if (message->FindInt32("be:value", &value) == B_OK) { 783 fSettings->B_GRAY1_as_B_RGB24 = value; 784 SaveSettings(fSettings); 785 } 786 break; 787 } 788 default: 789 BView::MessageReceived(message); 790 break; 791 } 792 } 793 794 795 // #pragma mark - 796 797 798 TranslatorAboutView::TranslatorAboutView(const char *name, float x, float y) 799 : SView(name, x, y) 800 { 801 BStringView *title = new BStringView(BRect(10, 0, 10, 0), "Title", 802 translatorName); 803 title->SetFont(be_bold_font); 804 805 AddChild(title); 806 807 BRect rect = title->Bounds(); 808 float space = title->StringWidth(" "); 809 810 char versionString[16]; 811 sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8), 812 (int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf)); 813 814 BStringView *version = new BStringView(BRect(rect.right+space, rect.top, 815 rect.right+space, rect.top), "Version", versionString); 816 version->SetFont(be_plain_font); 817 version->SetFontSize(9); 818 // Make version be in the same line as title 819 version->ResizeToPreferred(); 820 version->MoveBy(0, rect.bottom-version->Frame().bottom); 821 822 AddChild(version); 823 824 // Now for each line in translatorInfo add a BStringView 825 char* current = translatorInfo; 826 int32 index = 1; 827 while (current != NULL && current[0]) { 828 char text[128]; 829 char* newLine = strchr(current, '\n'); 830 if (newLine == NULL) { 831 strlcpy(text, current, sizeof(text)); 832 current = NULL; 833 } else { 834 strlcpy(text, current, min_c((int32)sizeof(text), newLine + 1 - current)); 835 current = newLine + 1; 836 } 837 838 BStringView* string = new BStringView(BRect(10, GetPreferredHeight(), 839 10, GetPreferredHeight()), "copyright", text); 840 if (index > 3) 841 string->SetFontSize(9); 842 AddChild(string); 843 844 index++; 845 } 846 847 ResizeToPreferred(); 848 } 849 850 851 // #pragma mark - 852 853 854 TranslatorView::TranslatorView(const char *name) 855 : SView(name), 856 fTabWidth(30), 857 fActiveChild(0) 858 { 859 // Set global var to true 860 gAreSettingsRunning = true; 861 862 // Load settings to global settings struct 863 LoadSettings(&fSettings); 864 865 font_height fontHeight; 866 GetFontHeight(&fontHeight); 867 fTabHeight = (int32)ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading) + 7; 868 // Add left and top margins 869 float top = fTabHeight + 20; 870 float left = 0; 871 872 // This will remember longest string width 873 int32 nameWidth = 0; 874 875 SView *view = new TranslatorWriteView("Write", &fSettings, left, top); 876 AddChild(view); 877 nameWidth = (int32)StringWidth(view->Name()); 878 fTabs.AddItem(new BTab(view)); 879 880 view = new TranslatorReadView("Read", &fSettings, left, top); 881 AddChild(view); 882 if (nameWidth < StringWidth(view->Name())) 883 nameWidth = (int32)StringWidth(view->Name()); 884 fTabs.AddItem(new BTab(view)); 885 886 view = new TranslatorAboutView("About", left, top); 887 AddChild(view); 888 if (nameWidth < StringWidth(view->Name())) 889 nameWidth = (int32)StringWidth(view->Name()); 890 fTabs.AddItem(new BTab(view)); 891 892 fTabWidth += nameWidth; 893 if (fTabWidth * CountChildren() > GetPreferredWidth()) 894 ResizePreferredBy((fTabWidth * CountChildren()) - GetPreferredWidth(), 0); 895 896 // Add right and bottom margins 897 ResizePreferredBy(10, 15); 898 899 ResizeToPreferred(); 900 901 // Make TranslatorView resize itself with parent 902 SetFlags(Flags() | B_FOLLOW_ALL); 903 } 904 905 906 TranslatorView::~TranslatorView() 907 { 908 gAreSettingsRunning = false; 909 910 BTab* tab; 911 while ((tab = (BTab*)fTabs.RemoveItem((int32)0)) != NULL) { 912 delete tab; 913 } 914 } 915 916 917 //! Attached to window - resize parent to preferred 918 void 919 TranslatorView::AttachedToWindow() 920 { 921 // Hide all children except first one 922 BView *child; 923 int32 index = 1; 924 while ((child = ChildAt(index++)) != NULL) 925 child->Hide(); 926 927 } 928 929 930 BRect 931 TranslatorView::_TabFrame(int32 index) const 932 { 933 return BRect(index * fTabWidth, 10, (index + 1) * fTabWidth, 10 + fTabHeight); 934 } 935 936 937 void 938 TranslatorView::Draw(BRect updateRect) 939 { 940 // This is needed because DataTranslations app hides children 941 // after user changes translator 942 if (ChildAt(fActiveChild)->IsHidden()) 943 ChildAt(fActiveChild)->Show(); 944 945 // Clear 946 SetHighColor(ViewColor()); 947 BRect frame = _TabFrame(0); 948 FillRect(BRect(frame.left, frame.top, Bounds().right, frame.bottom - 1)); 949 950 int32 index = 0; 951 BTab* tab; 952 while ((tab = (BTab*)fTabs.ItemAt(index)) != NULL) { 953 tab_position position; 954 if (fActiveChild == index) 955 position = B_TAB_FRONT; 956 else if (index == 0) 957 position = B_TAB_FIRST; 958 else 959 position = B_TAB_ANY; 960 961 tab->DrawTab(this, _TabFrame(index), position, index + 1 != fActiveChild); 962 index++; 963 } 964 965 // Draw bottom edge 966 SetHighColor(tint_color(ViewColor(), B_LIGHTEN_MAX_TINT)); 967 968 BRect selectedFrame = _TabFrame(fActiveChild); 969 float offset = ceilf(frame.Height() / 2.0); 970 971 if (selectedFrame.left > frame.left) { 972 StrokeLine(BPoint(frame.left, frame.bottom), 973 BPoint(selectedFrame.left, frame.bottom)); 974 } 975 if (selectedFrame.right + offset < Bounds().right) { 976 StrokeLine(BPoint(selectedFrame.right + offset, frame.bottom), 977 BPoint(Bounds().right, frame.bottom)); 978 } 979 } 980 981 982 //! MouseDown, check if on tab, if so change tab if needed 983 void 984 TranslatorView::MouseDown(BPoint where) 985 { 986 BRect frame = _TabFrame(fTabs.CountItems() - 1); 987 frame.left = 0; 988 if (!frame.Contains(where)) 989 return; 990 991 for (int32 index = fTabs.CountItems(); index-- > 0;) { 992 if (!_TabFrame(index).Contains(where)) 993 continue; 994 995 if (fActiveChild != index) { 996 // Hide current visible child 997 ChildAt(fActiveChild)->Hide(); 998 999 // This loop is needed because it looks like in DataTranslations 1000 // view gets hidden more than one time when user changes translator 1001 while (ChildAt(index)->IsHidden()) { 1002 ChildAt(index)->Show(); 1003 } 1004 1005 // Remember which one is currently visible 1006 fActiveChild = index; 1007 Invalidate(frame); 1008 break; 1009 } 1010 } 1011 } 1012 1013 1014 // #pragma mark - 1015 1016 1017 TranslatorWindow::TranslatorWindow(bool quitOnClose) 1018 : BWindow(BRect(100, 100, 100, 100), "JPEG Settings", B_TITLED_WINDOW, 1019 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) 1020 { 1021 BRect extent(0, 0, 0, 0); 1022 BView *config = NULL; 1023 MakeConfig(NULL, &config, &extent); 1024 1025 AddChild(config); 1026 ResizeTo(extent.Width(), extent.Height()); 1027 1028 // Make application quit after this window close 1029 if (quitOnClose) 1030 SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE); 1031 } 1032 1033 1034 // #pragma mark - Translator Add-On 1035 1036 1037 1038 /*! Hook to create and return our configuration view */ 1039 status_t 1040 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent) 1041 { 1042 *outView = new TranslatorView("TranslatorView"); 1043 *outExtent = (*outView)->Frame(); 1044 return B_OK; 1045 } 1046 1047 /*! Determine whether or not we can handle this data */ 1048 status_t 1049 Identify(BPositionIO *inSource, const translation_format *inFormat, 1050 BMessage *ioExtension, translator_info *outInfo, uint32 outType) 1051 { 1052 if (outType != 0 && outType != B_TRANSLATOR_BITMAP && outType != JPEG_FORMAT) 1053 return B_NO_TRANSLATOR; 1054 1055 // !!! You might need to make this buffer bigger to test for your native format 1056 off_t position = inSource->Position(); 1057 char header[sizeof(TranslatorBitmap)]; 1058 status_t err = inSource->Read(header, sizeof(TranslatorBitmap)); 1059 inSource->Seek(position, SEEK_SET); 1060 if (err < B_OK) 1061 return err; 1062 1063 if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) { 1064 outInfo->type = inputFormats[1].type; 1065 outInfo->translator = 0; 1066 outInfo->group = inputFormats[1].group; 1067 outInfo->quality = inputFormats[1].quality; 1068 outInfo->capability = inputFormats[1].capability; 1069 strcpy(outInfo->name, inputFormats[1].name); 1070 strcpy(outInfo->MIME, inputFormats[1].MIME); 1071 } else { 1072 // First 3 bytes in jpg files are always the same from what i've seen so far 1073 // check them 1074 if (header[0] == (char)0xff && header[1] == (char)0xd8 && header[2] == (char)0xff) { 1075 /* this below would be safer but it slows down whole thing 1076 1077 struct jpeg_decompress_struct cinfo; 1078 struct jpeg_error_mgr jerr; 1079 cinfo.err = jpeg_std_error(&jerr); 1080 jpeg_create_decompress(&cinfo); 1081 be_jpeg_stdio_src(&cinfo, inSource); 1082 // now try to read header 1083 // it can't be read before checking first 3 bytes 1084 // because it will hang up if there is no header (not jpeg file) 1085 int result = jpeg_read_header(&cinfo, FALSE); 1086 jpeg_destroy_decompress(&cinfo); 1087 if (result == JPEG_HEADER_OK) { 1088 */ outInfo->type = inputFormats[0].type; 1089 outInfo->translator = 0; 1090 outInfo->group = inputFormats[0].group; 1091 outInfo->quality = inputFormats[0].quality; 1092 outInfo->capability = inputFormats[0].capability; 1093 strcpy(outInfo->name, inputFormats[0].name); 1094 strcpy(outInfo->MIME, inputFormats[0].MIME); 1095 return B_OK; 1096 /* } else 1097 return B_NO_TRANSLATOR; 1098 */ 1099 } else 1100 return B_NO_TRANSLATOR; 1101 } 1102 1103 return B_OK; 1104 } 1105 1106 /*! Arguably the most important method in the add-on */ 1107 status_t 1108 Translate(BPositionIO *inSource, const translator_info *inInfo, 1109 BMessage *ioExtension, uint32 outType, BPositionIO *outDestination) 1110 { 1111 // If no specific type was requested, convert to the interchange format 1112 if (outType == 0) 1113 outType = B_TRANSLATOR_BITMAP; 1114 1115 // Setup a "breakpoint" since throwing exceptions does not seem to work 1116 // at all in an add-on. (?) 1117 // In the be_jerror.cpp we implement a handler for critical library errors 1118 // (be_error_exit()) and there we use the longjmp() function to return to 1119 // this place. If this happens, it is as if the setjmp() call is called 1120 // a second time, but this time the return value will be 1. The first 1121 // invokation will return 0. 1122 jmp_buf longJumpBuffer; 1123 int jmpRet = setjmp(longJumpBuffer); 1124 if (jmpRet == 1) 1125 return B_ERROR; 1126 1127 try { 1128 // What action to take, based on the findings of Identify() 1129 if (outType == inInfo->type) { 1130 return Copy(inSource, outDestination); 1131 } else if (inInfo->type == B_TRANSLATOR_BITMAP 1132 && outType == JPEG_FORMAT) { 1133 return Compress(inSource, outDestination, &longJumpBuffer); 1134 } else if (inInfo->type == JPEG_FORMAT 1135 && outType == B_TRANSLATOR_BITMAP) { 1136 return Decompress(inSource, outDestination, ioExtension, 1137 &longJumpBuffer); 1138 } 1139 } catch (...) { 1140 fprintf(stderr, "libjpeg encoutered a critical error " 1141 "(caught C++ exception).\n"); 1142 return B_ERROR; 1143 } 1144 1145 return B_NO_TRANSLATOR; 1146 } 1147 1148 /*! The user has requested the same format for input and output, so just copy */ 1149 static status_t 1150 Copy(BPositionIO *in, BPositionIO *out) 1151 { 1152 int block_size = 65536; 1153 void *buffer = malloc(block_size); 1154 char temp[1024]; 1155 if (buffer == NULL) { 1156 buffer = temp; 1157 block_size = 1024; 1158 } 1159 status_t err = B_OK; 1160 1161 // Read until end of file or error 1162 while (1) { 1163 ssize_t to_read = block_size; 1164 err = in->Read(buffer, to_read); 1165 // Explicit check for EOF 1166 if (err == -1) { 1167 if (buffer != temp) free(buffer); 1168 return B_OK; 1169 } 1170 if (err <= B_OK) break; 1171 to_read = err; 1172 err = out->Write(buffer, to_read); 1173 if (err != to_read) if (err >= 0) err = B_DEVICE_FULL; 1174 if (err < B_OK) break; 1175 } 1176 1177 if (buffer != temp) free(buffer); 1178 return (err >= 0) ? B_OK : err; 1179 } 1180 1181 1182 /*! Encode into the native format */ 1183 static status_t 1184 Compress(BPositionIO *in, BPositionIO *out, const jmp_buf* longJumpBuffer) 1185 { 1186 // Load Settings 1187 jpeg_settings settings; 1188 LoadSettings(&settings); 1189 1190 // Read info about bitmap 1191 TranslatorBitmap header; 1192 status_t err = in->Read(&header, sizeof(TranslatorBitmap)); 1193 if (err < B_OK) 1194 return err; 1195 else if (err < (int)sizeof(TranslatorBitmap)) 1196 return B_ERROR; 1197 1198 // Grab dimension, color space, and size information from the stream 1199 BRect bounds; 1200 bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left); 1201 bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top); 1202 bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right); 1203 bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom); 1204 1205 int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes); 1206 1207 int width = bounds.IntegerWidth() + 1; 1208 int height = bounds.IntegerHeight() + 1; 1209 1210 // Function pointer to convert function 1211 // It will point to proper function if needed 1212 void (*converter)(uchar *inscanline, uchar *outscanline, 1213 int32 inRowBytes) = NULL; 1214 1215 // Default color info 1216 J_COLOR_SPACE jpg_color_space = JCS_RGB; 1217 int jpg_input_components = 3; 1218 int32 out_row_bytes; 1219 int padding = 0; 1220 1221 switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) { 1222 case B_CMAP8: 1223 converter = convert_from_cmap8_to_24; 1224 padding = in_row_bytes - width; 1225 break; 1226 1227 case B_GRAY1: 1228 if (settings.B_GRAY1_as_B_RGB24) { 1229 converter = convert_from_gray1_to_24; 1230 } else { 1231 jpg_input_components = 1; 1232 jpg_color_space = JCS_GRAYSCALE; 1233 converter = convert_from_gray1_to_gray8; 1234 } 1235 padding = in_row_bytes - (width/8); 1236 break; 1237 1238 case B_GRAY8: 1239 jpg_input_components = 1; 1240 jpg_color_space = JCS_GRAYSCALE; 1241 padding = in_row_bytes - width; 1242 break; 1243 1244 case B_RGB15: 1245 case B_RGBA15: 1246 converter = convert_from_15_to_24; 1247 padding = in_row_bytes - (width * 2); 1248 break; 1249 1250 case B_RGB15_BIG: 1251 case B_RGBA15_BIG: 1252 converter = convert_from_15b_to_24; 1253 padding = in_row_bytes - (width * 2); 1254 break; 1255 1256 case B_RGB16: 1257 converter = convert_from_16_to_24; 1258 padding = in_row_bytes - (width * 2); 1259 break; 1260 1261 case B_RGB16_BIG: 1262 converter = convert_from_16b_to_24; 1263 padding = in_row_bytes - (width * 2); 1264 break; 1265 1266 case B_RGB24: 1267 converter = convert_from_24_to_24; 1268 padding = in_row_bytes - (width * 3); 1269 break; 1270 1271 case B_RGB24_BIG: 1272 padding = in_row_bytes - (width * 3); 1273 break; 1274 1275 case B_RGB32: 1276 case B_RGBA32: 1277 converter = convert_from_32_to_24; 1278 padding = in_row_bytes - (width * 4); 1279 break; 1280 1281 case B_RGB32_BIG: 1282 case B_RGBA32_BIG: 1283 converter = convert_from_32b_to_24; 1284 padding = in_row_bytes - (width * 4); 1285 break; 1286 1287 case B_CMYK32: 1288 jpg_color_space = JCS_CMYK; 1289 jpg_input_components = 4; 1290 padding = in_row_bytes - (width * 4); 1291 break; 1292 1293 default: 1294 fprintf(stderr, "Wrong type: Color space not implemented.\n"); 1295 return B_ERROR; 1296 } 1297 out_row_bytes = jpg_input_components * width; 1298 1299 // Set basic things needed for jpeg writing 1300 struct jpeg_compress_struct cinfo; 1301 struct jpeg_error_mgr jerr; 1302 cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer); 1303 jpeg_create_compress(&cinfo); 1304 be_jpeg_stdio_dest(&cinfo, out); 1305 1306 // Set basic values 1307 cinfo.image_width = width; 1308 cinfo.image_height = height; 1309 cinfo.input_components = jpg_input_components; 1310 cinfo.in_color_space = jpg_color_space; 1311 jpeg_set_defaults(&cinfo); 1312 1313 // Set better accuracy 1314 cinfo.dct_method = JDCT_ISLOW; 1315 1316 // This is needed to prevent some colors loss 1317 // With it generated jpegs are as good as from Fireworks (at last! :D) 1318 if (settings.OptimizeColors) { 1319 int index = 0; 1320 while (index < cinfo.num_components) { 1321 cinfo.comp_info[index].h_samp_factor = 1; 1322 cinfo.comp_info[index].v_samp_factor = 1; 1323 // This will make file smaller, but with worse quality more or less 1324 // like with 93%-94% (but it's subjective opinion) on tested images 1325 // but with smaller size (between 92% and 93% on tested images) 1326 if (settings.SmallerFile) 1327 cinfo.comp_info[index].quant_tbl_no = 1; 1328 // This will make bigger file, but also better quality ;] 1329 // from my tests it seems like useless - better quality with smaller 1330 // can be acheived without this 1331 // cinfo.comp_info[index].dc_tbl_no = 1; 1332 // cinfo.comp_info[index].ac_tbl_no = 1; 1333 index++; 1334 } 1335 } 1336 1337 // Set quality 1338 jpeg_set_quality(&cinfo, settings.Quality, true); 1339 1340 // Set progressive compression if needed 1341 // if not, turn on optimizing in libjpeg 1342 if (settings.Progressive) 1343 jpeg_simple_progression(&cinfo); 1344 else 1345 cinfo.optimize_coding = TRUE; 1346 1347 // Set smoothing (effect like Blur) 1348 cinfo.smoothing_factor = settings.Smoothing; 1349 1350 // Initialize compression 1351 jpeg_start_compress(&cinfo, TRUE); 1352 1353 // Declare scanlines 1354 JSAMPROW in_scanline = NULL; 1355 JSAMPROW out_scanline = NULL; 1356 JSAMPROW writeline; // Pointer to in_scanline (default) or out_scanline (if there will be conversion) 1357 1358 // Allocate scanline 1359 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1360 in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1361 JPOOL_PERMANENT, in_row_bytes); 1362 1363 // We need 2nd scanline storage ony for conversion 1364 if (converter != NULL) { 1365 // There will be conversion, allocate second scanline... 1366 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1367 out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1368 JPOOL_PERMANENT, out_row_bytes); 1369 // ... and make it the one to write to file 1370 writeline = out_scanline; 1371 } else 1372 writeline = in_scanline; 1373 1374 while (cinfo.next_scanline < cinfo.image_height) { 1375 // Read scanline 1376 err = in->Read(in_scanline, in_row_bytes); 1377 if (err < in_row_bytes) 1378 return err < B_OK ? Error((j_common_ptr)&cinfo, err) 1379 : Error((j_common_ptr)&cinfo, B_ERROR); 1380 1381 // Convert if needed 1382 if (converter != NULL) 1383 converter(in_scanline, out_scanline, in_row_bytes - padding); 1384 1385 // Write scanline 1386 jpeg_write_scanlines(&cinfo, &writeline, 1); 1387 } 1388 1389 jpeg_finish_compress(&cinfo); 1390 jpeg_destroy_compress(&cinfo); 1391 return B_OK; 1392 } 1393 1394 1395 /*! Decode the native format */ 1396 static status_t 1397 Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension, 1398 const jmp_buf* longJumpBuffer) 1399 { 1400 // Load Settings 1401 jpeg_settings settings; 1402 LoadSettings(&settings); 1403 1404 // Set basic things needed for jpeg reading 1405 struct jpeg_decompress_struct cinfo; 1406 struct jpeg_error_mgr jerr; 1407 cinfo.err = be_jpeg_std_error(&jerr, &settings, longJumpBuffer); 1408 jpeg_create_decompress(&cinfo); 1409 be_jpeg_stdio_src(&cinfo, in); 1410 1411 jpeg_save_markers(&cinfo, MARKER_EXIF, 131072); 1412 // make sure the EXIF tag is stored 1413 1414 // Read info about image 1415 jpeg_read_header(&cinfo, TRUE); 1416 1417 BMessage exif; 1418 1419 // parse EXIF data and add it ioExtension, if any 1420 jpeg_marker_struct* marker = cinfo.marker_list; 1421 while (marker != NULL) { 1422 if (marker->marker == MARKER_EXIF 1423 && !strncmp((char*)marker->data, "Exif", 4)) { 1424 if (ioExtension != NULL) { 1425 // Strip EXIF header from TIFF data 1426 ioExtension->AddData("exif", B_RAW_TYPE, 1427 (uint8 *)marker->data + 6, marker->data_length - 6); 1428 } 1429 1430 BMemoryIO io(marker->data + 6, marker->data_length - 6); 1431 convert_exif_to_message(io, exif); 1432 } 1433 marker = marker->next; 1434 } 1435 1436 // Default color info 1437 color_space outColorSpace = B_RGB32; 1438 int outColorComponents = 4; 1439 1440 // Function pointer to convert function 1441 // It will point to proper function if needed 1442 void (*converter)(uchar *inScanLine, uchar *outScanLine, 1443 int32 inRowBytes, int32 xStep) = convert_from_24_to_32; 1444 1445 // If color space isn't rgb 1446 if (cinfo.out_color_space != JCS_RGB) { 1447 switch (cinfo.out_color_space) { 1448 case JCS_UNKNOWN: /* error/unspecified */ 1449 fprintf(stderr, "From Type: Jpeg uses unknown color type\n"); 1450 break; 1451 case JCS_GRAYSCALE: /* monochrome */ 1452 // Check if user wants to read only as RGB32 or not 1453 if (!settings.Always_B_RGB32) { 1454 // Grayscale 1455 outColorSpace = B_GRAY8; 1456 outColorComponents = 1; 1457 converter = translate_8; 1458 } else { 1459 // RGB 1460 cinfo.out_color_space = JCS_RGB; 1461 cinfo.output_components = 3; 1462 converter = convert_from_24_to_32; 1463 } 1464 break; 1465 case JCS_YCbCr: /* Y/Cb/Cr (also known as YUV) */ 1466 cinfo.out_color_space = JCS_RGB; 1467 converter = convert_from_24_to_32; 1468 break; 1469 case JCS_YCCK: /* Y/Cb/Cr/K */ 1470 // Let libjpeg convert it to CMYK 1471 cinfo.out_color_space = JCS_CMYK; 1472 // Fall through to CMYK since we need the same settings 1473 case JCS_CMYK: /* C/M/Y/K */ 1474 // Use proper converter 1475 if (settings.PhotoshopCMYK) 1476 converter = convert_from_CMYK_to_32_photoshop; 1477 else 1478 converter = convert_from_CMYK_to_32; 1479 break; 1480 default: 1481 fprintf(stderr, "From Type: Jpeg uses hmm... i don't know really :(\n"); 1482 break; 1483 } 1484 } 1485 1486 // Initialize decompression 1487 jpeg_start_decompress(&cinfo); 1488 1489 // retrieve orientation from settings/EXIF 1490 int32 orientation; 1491 if (ioExtension == NULL 1492 || ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) { 1493 if (exif.FindInt32("Orientation", &orientation) != B_OK) 1494 orientation = 1; 1495 } 1496 1497 if (orientation != 1 && converter == NULL) 1498 converter = translate_8; 1499 1500 int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width; 1501 int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height; 1502 1503 int32 destOffset = dest_index(outputWidth, outputHeight, 1504 0, 0, orientation) * outColorComponents; 1505 int32 xStep = dest_index(outputWidth, outputHeight, 1506 1, 0, orientation) * outColorComponents - destOffset; 1507 int32 yStep = dest_index(outputWidth, outputHeight, 1508 0, 1, orientation) * outColorComponents - destOffset; 1509 bool needAll = orientation != 1; 1510 1511 // Initialize this bounds rect to the size of your image 1512 BRect bounds(0, 0, outputWidth - 1, outputHeight - 1); 1513 1514 #if 0 1515 printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n", 1516 destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height, 1517 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation); 1518 #endif 1519 1520 // Bytes count in one line of image (scanline) 1521 int32 inRowBytes = cinfo.output_width * cinfo.output_components; 1522 int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents; 1523 int32 dataSize = cinfo.output_width * cinfo.output_height 1524 * outColorComponents; 1525 1526 // Fill out the B_TRANSLATOR_BITMAP's header 1527 TranslatorBitmap header; 1528 header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); 1529 header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left); 1530 header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top); 1531 header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right); 1532 header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom); 1533 header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace); 1534 header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes); 1535 header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize); 1536 1537 // Write out the header 1538 status_t err = out->Write(&header, sizeof(TranslatorBitmap)); 1539 if (err < B_OK) 1540 return Error((j_common_ptr)&cinfo, err); 1541 else if (err < (int)sizeof(TranslatorBitmap)) 1542 return Error((j_common_ptr)&cinfo, B_ERROR); 1543 1544 // Declare scanlines 1545 JSAMPROW inScanLine = NULL; 1546 uint8* dest = NULL; 1547 uint8* destLine = NULL; 1548 1549 // Allocate scanline 1550 // Use libjpeg memory allocation functions, so in case of error it will free them itself 1551 inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1552 JPOOL_PERMANENT, inRowBytes); 1553 1554 // We need 2nd scanline storage only for conversion 1555 if (converter != NULL) { 1556 // There will be conversion, allocate second scanline... 1557 // Use libjpeg memory allocation functions, so in case of error it will free 1558 // them itself 1559 dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, 1560 JPOOL_PERMANENT, needAll ? dataSize : rowBytes); 1561 destLine = dest + destOffset; 1562 } else 1563 destLine = inScanLine; 1564 1565 while (cinfo.output_scanline < cinfo.output_height) { 1566 // Read scanline 1567 jpeg_read_scanlines(&cinfo, &inScanLine, 1); 1568 1569 // Convert if needed 1570 if (converter != NULL) 1571 converter(inScanLine, destLine, inRowBytes, xStep); 1572 1573 if (!needAll) { 1574 // Write the scanline buffer to the output stream 1575 ssize_t bytesWritten = out->Write(destLine, rowBytes); 1576 if (bytesWritten < rowBytes) { 1577 return bytesWritten < B_OK 1578 ? Error((j_common_ptr)&cinfo, bytesWritten) 1579 : Error((j_common_ptr)&cinfo, B_ERROR); 1580 } 1581 } else 1582 destLine += yStep; 1583 } 1584 1585 if (needAll) { 1586 ssize_t bytesWritten = out->Write(dest, dataSize); 1587 if (bytesWritten < dataSize) { 1588 return bytesWritten < B_OK 1589 ? Error((j_common_ptr)&cinfo, bytesWritten) 1590 : Error((j_common_ptr)&cinfo, B_ERROR); 1591 } 1592 } 1593 1594 jpeg_finish_decompress(&cinfo); 1595 jpeg_destroy_decompress(&cinfo); 1596 return B_OK; 1597 } 1598 1599 /*! 1600 Frees jpeg alocated memory 1601 Returns given error (B_ERROR by default) 1602 */ 1603 static status_t 1604 Error(j_common_ptr cinfo, status_t error) 1605 { 1606 jpeg_destroy(cinfo); 1607 return error; 1608 } 1609 1610 1611 // #pragma mark - 1612 1613 1614 int 1615 main(int, char**) 1616 { 1617 BApplication app("application/x-vnd.Haiku-JPEGTranslator"); 1618 1619 TranslatorWindow *window = new TranslatorWindow(); 1620 window->Show(); 1621 1622 app.Run(); 1623 return 0; 1624 } 1625 1626