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