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