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