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