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