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