1 /* 2 3 Copyright (c) 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 "JPEG2000Translator.h" 39 #include "jp2_cod.h" 40 #include "jpc_cs.h" 41 42 //---------------------------------------------------------------------------- 43 // 44 // Global variables initialization 45 // 46 //---------------------------------------------------------------------------- 47 48 // Set these accordingly 49 #define JP2_ACRONYM "JP2" 50 #define JP2_FORMAT 'JP2 ' 51 #define JP2_MIME_STRING "image/jp2" 52 #define JP2_DESCRIPTION "JPEG2000 image" 53 54 // The translation kit's native file type 55 #define B_TRANSLATOR_BITMAP_MIME_STRING "image/x-be-bitmap" 56 #define B_TRANSLATOR_BITMAP_DESCRIPTION "Be Bitmap image" 57 58 // Translation Kit required globals 59 char translatorName[] = "JPEG2000 translator"; 60 char translatorInfo[] = "© 2002-2003, Shard 61 62 Based on JasPer library: 63 © 1999-2000, Image Power, Inc. and 64 the University of British Columbia, Canada. 65 © 2001-2003 Michael David Adams. 66 http://www.ece.uvic.ca/~mdadams/jasper/ 67 68 ImageMagick's jp2 codec was used as \"tutorial\". 69 http://www.imagemagick.org/ 70 "; 71 int32 translatorVersion = 256; // 256 = v1.0.0 72 73 // Define the formats we know how to read 74 translation_format inputFormats[] = { 75 { JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5, 76 JP2_MIME_STRING, JP2_DESCRIPTION }, 77 { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5, 78 B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }, 79 { 0, 0, 0, 0, 0, 0 }, 80 }; 81 82 // Define the formats we know how to write 83 translation_format outputFormats[] = { 84 { JP2_FORMAT, B_TRANSLATOR_BITMAP, 0.5, 0.5, 85 JP2_MIME_STRING, JP2_DESCRIPTION }, 86 { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.5, 0.5, 87 B_TRANSLATOR_BITMAP_MIME_STRING, B_TRANSLATOR_BITMAP_DESCRIPTION }, 88 { 0, 0, 0, 0, 0, 0 }, 89 }; 90 91 bool AreSettingsRunning = false; 92 93 //---------------------------------------------------------------------------- 94 // 95 // Functions :: jas_stream for BPositionIO 96 // 97 //---------------------------------------------------------------------------- 98 99 static int Read(jas_stream_obj_t *object,char *buffer,const int length) 100 { 101 return (*(BPositionIO**) object)->Read(buffer, length); 102 } 103 104 static int Write(jas_stream_obj_t *object,char *buffer,const int length) 105 { 106 return (*(BPositionIO**) object)->Write(buffer, length); 107 } 108 109 static long Seek(jas_stream_obj_t *object,long offset,int origin) 110 { 111 return (*(BPositionIO**) object)->Seek(offset, origin); 112 } 113 114 static int Close(jas_stream_obj_t *object) 115 { 116 return(0); 117 } 118 119 static jas_stream_ops_t 120 positionIOops = 121 { 122 Read, 123 Write, 124 Seek, 125 Close 126 }; 127 128 static jas_stream_t *jas_stream_positionIOopen(BPositionIO *positionIO) 129 { 130 jas_stream_t *stream; 131 132 stream=(jas_stream_t *) malloc( sizeof(jas_stream_t)); 133 if (stream == (jas_stream_t *) NULL) 134 return((jas_stream_t *) NULL); 135 (void) memset(stream, 0, sizeof(jas_stream_t)); 136 stream->rwlimit_=(-1); 137 stream->obj_=(jas_stream_obj_t *) malloc( sizeof(BPositionIO*)); 138 if (stream->obj_ == (jas_stream_obj_t *) NULL) 139 return((jas_stream_t *) NULL); 140 *((BPositionIO**)stream->obj_) = positionIO; 141 stream->ops_=(&positionIOops); 142 stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY; 143 stream->bufbase_=stream->tinybuf_; 144 stream->bufsize_=1; 145 stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]); 146 stream->ptr_=stream->bufstart_; 147 stream->bufmode_|=JAS_STREAM_UNBUF & JAS_STREAM_BUFMODEMASK; 148 return(stream); 149 } 150 151 //---------------------------------------------------------------------------- 152 // 153 // Functions :: SSlider 154 // 155 //---------------------------------------------------------------------------- 156 157 //--------------------------------------------------- 158 // Constructor 159 //--------------------------------------------------- 160 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) 161 : BSlider(frame, name, label, message, minValue, maxValue, posture, thumbType, resizingMode, flags) 162 { 163 rgb_color bar_color = { 0, 0, 229, 255 }; 164 UseFillColor(true, &bar_color); 165 } 166 167 //--------------------------------------------------- 168 // Update status string - show actual value 169 //--------------------------------------------------- 170 char* 171 SSlider::UpdateText() const 172 { 173 sprintf( (char*)statusLabel, "%ld", Value()); 174 return (char*)statusLabel; 175 } 176 177 //--------------------------------------------------- 178 // BSlider::ResizeToPreferred + Resize width if it's too small to show label and status 179 //--------------------------------------------------- 180 void 181 SSlider::ResizeToPreferred() 182 { 183 int32 width = (int32)ceil(StringWidth( Label()) + StringWidth("9999")); 184 if (width < 230) width = 230; 185 float w, h; 186 GetPreferredSize(&w, &h); 187 ResizeTo(width, h); 188 } 189 190 191 //---------------------------------------------------------------------------- 192 // 193 // Functions :: TranslatorReadView 194 // 195 //---------------------------------------------------------------------------- 196 197 //--------------------------------------------------- 198 // Constructor 199 //--------------------------------------------------- 200 TranslatorReadView::TranslatorReadView(const char *name, SETTINGS *settings, float x, float y) 201 : SView(name, x, y), 202 Settings(settings) 203 { 204 grayasrgb32 = new BCheckBox( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_GRAYASRGB32, new BMessage(VIEW_MSG_SET_GRAYASRGB32)); 205 grayasrgb32->SetFont(be_plain_font); 206 if (Settings->B_GRAY8_as_B_RGB32) 207 grayasrgb32->SetValue(1); 208 209 AddChild(grayasrgb32); 210 211 ResizeToPreferred(); 212 } 213 214 //--------------------------------------------------- 215 // Attached to window - set children target 216 //--------------------------------------------------- 217 void 218 TranslatorReadView::AttachedToWindow() 219 { 220 grayasrgb32->SetTarget(this); 221 } 222 223 //--------------------------------------------------- 224 // MessageReceived - receive GUI changes, save settings 225 //--------------------------------------------------- 226 void 227 TranslatorReadView::MessageReceived(BMessage *message) 228 { 229 switch (message->what) 230 { 231 case VIEW_MSG_SET_GRAYASRGB32: 232 { 233 int32 value; 234 if (message->FindInt32("be:value", &value) == B_OK) { 235 Settings->B_GRAY8_as_B_RGB32 = value; 236 SaveSettings(Settings); 237 } 238 break; 239 } 240 default: 241 BView::MessageReceived(message); 242 break; 243 } 244 } 245 246 247 //---------------------------------------------------------------------------- 248 // 249 // Functions :: TranslatorWriteView 250 // 251 //---------------------------------------------------------------------------- 252 253 //--------------------------------------------------- 254 // Constructor 255 //--------------------------------------------------- 256 TranslatorWriteView::TranslatorWriteView(const char *name, SETTINGS *settings, float x, float y) 257 : SView(name, x, y), 258 Settings(settings) 259 { 260 quality = new SSlider( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "quality", VIEW_LABEL_QUALITY, new BMessage(VIEW_MSG_SET_QUALITY), 1, 100); 261 quality->SetHashMarks(B_HASH_MARKS_BOTTOM); 262 quality->SetHashMarkCount(10); 263 quality->SetLimitLabels("Low", "High"); 264 quality->SetFont(be_plain_font); 265 quality->SetValue(Settings->Quality); 266 267 AddChild(quality); 268 269 gray1asrgb24 = new BCheckBox( BRect(10, GetPreferredHeight()+10, 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_GRAY1ASRGB24, new BMessage(VIEW_MSG_SET_GRAY1ASRGB24)); 270 gray1asrgb24->SetFont(be_plain_font); 271 if (Settings->B_GRAY1_as_B_RGB24) 272 gray1asrgb24->SetValue(1); 273 274 AddChild(gray1asrgb24); 275 276 jpc = new BCheckBox( BRect(10, GetPreferredHeight()+5, 10, GetPreferredHeight()), "alwaysrgb32", VIEW_LABEL_JPC, new BMessage(VIEW_MSG_SET_JPC)); 277 jpc->SetFont(be_plain_font); 278 if (Settings->JPC) 279 jpc->SetValue(1); 280 281 AddChild(jpc); 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 gray1asrgb24->SetTarget(this); 294 jpc->SetTarget(this); 295 } 296 297 //--------------------------------------------------- 298 // MessageReceived - receive GUI changes, save settings 299 //--------------------------------------------------- 300 void 301 TranslatorWriteView::MessageReceived(BMessage *message) 302 { 303 switch (message->what) 304 { 305 case VIEW_MSG_SET_QUALITY: 306 { 307 int32 value; 308 if (message->FindInt32("be:value", &value) == B_OK) { 309 Settings->Quality = value; 310 SaveSettings(Settings); 311 } 312 break; 313 } 314 case VIEW_MSG_SET_GRAY1ASRGB24: 315 { 316 int32 value; 317 if (message->FindInt32("be:value", &value) == B_OK) { 318 Settings->B_GRAY1_as_B_RGB24 = value; 319 SaveSettings(Settings); 320 } 321 break; 322 } 323 case VIEW_MSG_SET_JPC: 324 { 325 int32 value; 326 if (message->FindInt32("be:value", &value) == B_OK) { 327 Settings->JPC = value; 328 SaveSettings(Settings); 329 } 330 break; 331 } 332 default: 333 BView::MessageReceived(message); 334 break; 335 } 336 } 337 338 339 //---------------------------------------------------------------------------- 340 // 341 // Functions :: TranslatorAboutView 342 // 343 //---------------------------------------------------------------------------- 344 345 //--------------------------------------------------- 346 // Constructor 347 //--------------------------------------------------- 348 TranslatorAboutView::TranslatorAboutView(const char *name, float x, float y) 349 : SView(name, x, y) 350 { 351 BStringView *title = new BStringView( BRect(10, 0, 10, 0), "Title", translatorName); 352 title->SetFont(be_bold_font); 353 354 AddChild(title); 355 356 BRect rect = title->Bounds(); 357 float space = title->StringWidth(" "); 358 359 char versionString[16]; 360 sprintf(versionString, "v%d.%d.%d", (int)(translatorVersion >> 8), (int)((translatorVersion >> 4) & 0xf), (int)(translatorVersion & 0xf)); 361 362 BStringView *version = new BStringView( BRect(rect.right+space, rect.top, rect.right+space, rect.top), "Version", versionString); 363 version->SetFont(be_plain_font); 364 version->SetFontSize( 9); 365 // Make version be in the same line as title 366 version->ResizeToPreferred(); 367 version->MoveBy(0, rect.bottom-version->Frame().bottom); 368 369 AddChild(version); 370 371 // Now for each line in translatorInfo add BStringView 372 BStringView *copyright; 373 const char *current = translatorInfo; 374 char *temp = translatorInfo; 375 while (*current != 0) { 376 // Find next line char 377 temp = strchr(current, 0x0a); 378 // If found replace it with 0 so current will look like ending here 379 if (temp) 380 *temp = 0; 381 // Add BStringView showing what's under current 382 copyright = new BStringView( BRect(10, GetPreferredHeight(), 10, GetPreferredHeight()), "Copyright", current); 383 copyright->SetFont(be_plain_font); 384 copyright->SetFontSize( 9); 385 AddChild(copyright); 386 387 // If there was next line, move current there and put next line char back 388 if (temp) { 389 current = temp+1; 390 *temp = 0x0a; 391 } else 392 // If there was no next line char break loop 393 break; 394 } 395 396 ResizeToPreferred(); 397 } 398 399 400 //---------------------------------------------------------------------------- 401 // 402 // Functions :: TranslatorView 403 // 404 //---------------------------------------------------------------------------- 405 406 //--------------------------------------------------- 407 // Constructor 408 //--------------------------------------------------- 409 TranslatorView::TranslatorView(const char *name) 410 : SView(name), 411 tabWidth(30), 412 tabHeight((int32)ceilf(7 + be_plain_font->Size())), 413 activeChild(0) 414 { 415 // Set global var to true 416 AreSettingsRunning = true; 417 418 // Without this strings are not correctly aliased 419 // THX to Jack Burton for info :) 420 SetLowColor( ViewColor()); 421 422 // Load settings to global Settings struct 423 LoadSettings(&Settings); 424 425 // Add left and top margins 426 float top = tabHeight+15; 427 float left = 0; 428 429 // This will remember longest string width 430 float nameWidth = 0; 431 432 SView *view = new TranslatorWriteView("Write", &Settings, left, top); 433 AddChild(view); 434 nameWidth = StringWidth(view->Name()); 435 436 view = new TranslatorReadView("Read", &Settings, left, top); 437 AddChild(view); 438 if (nameWidth < StringWidth(view->Name())) 439 nameWidth = StringWidth(view->Name()); 440 441 view = new TranslatorAboutView("About", left, top); 442 AddChild(view); 443 if (nameWidth < StringWidth(view->Name())) 444 nameWidth = StringWidth(view->Name()); 445 446 tabWidth += (int32)ceilf(nameWidth); 447 if (tabWidth * CountChildren() > GetPreferredWidth()) 448 ResizePreferredBy((tabWidth * CountChildren()) - GetPreferredWidth(), 0); 449 450 // Add right and bottom margins 451 ResizePreferredBy(10, 15); 452 453 ResizeToPreferred(); 454 455 // Make TranslatorView resize itself with parent 456 SetFlags( Flags() | B_FOLLOW_ALL); 457 } 458 459 //--------------------------------------------------- 460 // Attached to window - resize parent to preferred 461 //--------------------------------------------------- 462 void 463 TranslatorView::AttachedToWindow() 464 { 465 // Hide all children except first one 466 BView *child = NULL; 467 int32 index = 1; 468 while ((child = ChildAt(index++))) 469 child->Hide(); 470 471 // Hack for DataTranslations which doesn't resize visible area to requested by view 472 // which makes some parts of bigger than usual translationviews out of visible area 473 // so if it was loaded to DataTranslations resize window if needed 474 BWindow *window = Window(); 475 if (!strcmp(window->Name(), "DataTranslations")) { 476 BView *view = Parent(); 477 if (view) { 478 BRect frame = view->Frame(); 479 if (frame.Width() < GetPreferredWidth() || (frame.Height()-48) < GetPreferredHeight()) { 480 float x = ceil(GetPreferredWidth() - frame.Width()); 481 float y = ceil(GetPreferredHeight() - (frame.Height()-48)); 482 if (x < 0) x = 0; 483 if (y < 0) y = 0; 484 485 // DataTranslations has main view called "Background" 486 // change it's resizing mode so it will always resize with window 487 // also make sure view will be redrawed after resize 488 view = window->FindView("Background"); 489 if (view) { 490 view->SetResizingMode(B_FOLLOW_ALL); 491 view->SetFlags(B_FULL_UPDATE_ON_RESIZE); 492 } 493 494 // The same with "Info..." button, except redrawing, which isn't needed 495 view = window->FindView("Info…"); 496 if (view) 497 view->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 498 499 window->ResizeBy( x, y); 500 501 // Let user resize window if resizing option is not already there... 502 uint32 flags = window->Flags(); 503 if (flags & B_NOT_RESIZABLE) { 504 // ...but first prevent too small window (so "Info..." button will not look strange ;) 505 // max will be 800x600 which should be enough for now 506 window->SetSizeLimits(400, 800, 66, 600); 507 508 flags ^= B_NOT_RESIZABLE; 509 window->SetFlags(flags); 510 } 511 } 512 } 513 } 514 } 515 516 //--------------------------------------------------- 517 // DrawTabs 518 //--------------------------------------------------- 519 void 520 TranslatorView::Draw(BRect updateRect) 521 { 522 // This is needed because DataTranslations app hides children 523 // after user changes translator 524 if (ChildAt(activeChild)->IsHidden()) 525 ChildAt(activeChild)->Show(); 526 527 // Prepare colors used for drawing "tabs" 528 rgb_color dark_line_color = tint_color( ViewColor(), B_DARKEN_2_TINT); 529 rgb_color darkest_line_color = tint_color( ViewColor(), B_DARKEN_3_TINT); 530 rgb_color light_line_color = tint_color( ViewColor(), B_LIGHTEN_MAX_TINT); 531 rgb_color text_color = ui_color(B_MENU_ITEM_TEXT_COLOR); 532 533 int32 index = 0; 534 BView *child = NULL; 535 float left = 0; 536 537 // Clear 538 SetHighColor( ViewColor()); 539 FillRect( BRect(0, 0, Frame().right, tabHeight)); 540 541 while ((child = ChildAt(index))) { 542 // Draw outline 543 SetHighColor(dark_line_color); 544 StrokeLine( BPoint(left, 10), BPoint(left, tabHeight)); 545 StrokeArc( BPoint(left+10, 10), 10, 10, 90, 90); 546 StrokeLine( BPoint(left+10, 0), BPoint(left+tabWidth-10, 0)); 547 StrokeArc( BPoint(left+tabWidth-10, 10), 9, 10, 0, 90); 548 StrokeLine( BPoint(left+tabWidth-1, 10), BPoint(left+tabWidth-1, tabHeight)); 549 // Draw "shadow" on the right side 550 SetHighColor(darkest_line_color); 551 StrokeArc( BPoint(left+tabWidth-10, 10), 10, 10, 0, 50); 552 StrokeLine( BPoint(left+tabWidth, 10), BPoint(left+tabWidth, tabHeight-1)); 553 // Draw label 554 SetHighColor(text_color); 555 DrawString( child->Name(), BPoint(left+(tabWidth/2)-(StringWidth(child->Name())/2), 3+be_plain_font->Size())); 556 // Draw "light" on left and top side 557 SetHighColor(light_line_color); 558 StrokeArc( BPoint(left+10, 10), 9, 9, 90, 90); 559 StrokeLine( BPoint(left+1, 10), BPoint(left+1, tabHeight)); 560 StrokeLine( BPoint(left+10, 1), BPoint(left+tabWidth-8, 1)); 561 // Draw bottom edge 562 if (activeChild != index) 563 StrokeLine( BPoint(left-2,tabHeight), BPoint(left+tabWidth,tabHeight)); 564 else 565 StrokeLine( BPoint(left-2,tabHeight), BPoint(left+1,tabHeight)); 566 567 left += tabWidth+2; 568 index++; 569 } 570 // Draw bottom edge to the rigth side 571 StrokeLine( BPoint(left-2,tabHeight), BPoint(Bounds().Width(),tabHeight)); 572 } 573 574 //--------------------------------------------------- 575 // MouseDown, check if on tab, if so change tab if needed 576 //--------------------------------------------------- 577 void 578 TranslatorView::MouseDown(BPoint where) 579 { 580 // If user clicked on tabs part of view 581 if (where.y <= tabHeight) 582 // If there is a tab (not whole width is occupied by tabs) 583 if (where.x < tabWidth*CountChildren()) { 584 // Which tab was selected? 585 int32 index = (int32)(where.x / tabWidth); 586 if (activeChild != index) { 587 // Hide current visible child 588 ChildAt(activeChild)->Hide(); 589 // This loop is needed because it looks like in DataTranslations 590 // view gets hidden more than one time when user changes translator 591 while (ChildAt(index)->IsHidden()) 592 ChildAt(index)->Show(); 593 // Remember which one is currently visible 594 activeChild = index; 595 // Redraw 596 Draw( Frame()); 597 } 598 } 599 } 600 601 602 //---------------------------------------------------------------------------- 603 // 604 // Functions :: TranslatorWindow 605 // 606 //---------------------------------------------------------------------------- 607 608 //--------------------------------------------------- 609 // Constructor 610 //--------------------------------------------------- 611 TranslatorWindow::TranslatorWindow(bool quit_on_close) 612 : BWindow(BRect(100, 100, 100, 100), "JPEG2000 Settings", B_TITLED_WINDOW, B_NOT_ZOOMABLE) 613 { 614 BRect extent(0, 0, 0, 0); 615 BView *config = NULL; 616 MakeConfig(NULL, &config, &extent); 617 618 AddChild(config); 619 ResizeTo(extent.Width(), extent.Height()); 620 621 // Make application quit after this window close 622 if (quit_on_close) 623 SetFlags(Flags() | B_QUIT_ON_WINDOW_CLOSE); 624 } 625 626 627 //---------------------------------------------------------------------------- 628 // 629 // Functions :: main 630 // 631 //---------------------------------------------------------------------------- 632 633 //--------------------------------------------------- 634 // main function 635 //--------------------------------------------------- 636 int 637 main() { 638 BApplication app("application/x-vnd.Shard.JPEG2000Translator"); 639 640 TranslatorWindow *window = new TranslatorWindow(); 641 window->Show(); 642 643 app.Run(); 644 return 0; 645 } 646 647 //--------------------------------------------------- 648 // Hook to create and return our configuration view 649 //--------------------------------------------------- 650 status_t 651 MakeConfig(BMessage *ioExtension, BView **outView, BRect *outExtent) 652 { 653 *outView = new TranslatorView("TranslatorView"); 654 *outExtent = (*outView)->Frame(); 655 return B_OK; 656 } 657 658 //--------------------------------------------------- 659 // Determine whether or not we can handle this data 660 //--------------------------------------------------- 661 status_t 662 Identify(BPositionIO *inSource, const translation_format *inFormat, BMessage *ioExtension, translator_info *outInfo, uint32 outType) 663 { 664 665 if ((outType != 0) && (outType != B_TRANSLATOR_BITMAP) && outType != JP2_FORMAT) 666 return B_NO_TRANSLATOR; 667 668 // !!! You might need to make this buffer bigger to test for your native format 669 off_t position = inSource->Position(); 670 uint8 header[sizeof(TranslatorBitmap)]; 671 status_t err = inSource->Read(header, sizeof(TranslatorBitmap)); 672 inSource->Seek( position, SEEK_SET); 673 if (err < B_OK) return err; 674 675 if (B_BENDIAN_TO_HOST_INT32(((TranslatorBitmap *)header)->magic) == B_TRANSLATOR_BITMAP) { 676 outInfo->type = inputFormats[1].type; 677 outInfo->translator = 0; 678 outInfo->group = inputFormats[1].group; 679 outInfo->quality = inputFormats[1].quality; 680 outInfo->capability = inputFormats[1].capability; 681 strcpy(outInfo->name, inputFormats[1].name); 682 strcpy(outInfo->MIME, inputFormats[1].MIME); 683 } else { 684 if ((((header[4] << 24) | (header[5] << 16) | (header[6] << 8) | header[7]) == JP2_BOX_JP) || // JP2 685 (header[0] == (JPC_MS_SOC >> 8) && header[1] == (JPC_MS_SOC & 0xff))) // JPC 686 { 687 outInfo->type = inputFormats[0].type; 688 outInfo->translator = 0; 689 outInfo->group = inputFormats[0].group; 690 outInfo->quality = inputFormats[0].quality; 691 outInfo->capability = inputFormats[0].capability; 692 strcpy(outInfo->name, inputFormats[0].name); 693 strcpy(outInfo->MIME, inputFormats[0].MIME); 694 return B_OK; 695 } else 696 return B_NO_TRANSLATOR; 697 } 698 699 return B_OK; 700 } 701 702 //--------------------------------------------------- 703 // Arguably the most important method in the add-on 704 //--------------------------------------------------- 705 status_t 706 Translate(BPositionIO *inSource, const translator_info *inInfo, BMessage *ioExtension, uint32 outType, BPositionIO *outDestination) 707 { 708 // If no specific type was requested, convert to the interchange format 709 if (outType == 0) outType = B_TRANSLATOR_BITMAP; 710 711 // What action to take, based on the findings of Identify() 712 if (outType == inInfo->type) { 713 return Copy(inSource, outDestination); 714 } else if (inInfo->type == B_TRANSLATOR_BITMAP && outType == JP2_FORMAT) { 715 return Compress(inSource, outDestination); 716 } else if (inInfo->type == JP2_FORMAT && outType == B_TRANSLATOR_BITMAP) { 717 return Decompress(inSource, outDestination); 718 } 719 720 return B_NO_TRANSLATOR; 721 } 722 723 //--------------------------------------------------- 724 // The user has requested the same format for input and output, so just copy 725 //--------------------------------------------------- 726 status_t 727 Copy(BPositionIO *in, BPositionIO *out) 728 { 729 int block_size = 65536; 730 void *buffer = malloc(block_size); 731 char temp[1024]; 732 if (buffer == NULL) { 733 buffer = temp; 734 block_size = 1024; 735 } 736 status_t err = B_OK; 737 738 // Read until end of file or error 739 while (1) { 740 ssize_t to_read = block_size; 741 err = in->Read(buffer, to_read); 742 // Explicit check for EOF 743 if (err == -1) { 744 if (buffer != temp) free(buffer); 745 return B_OK; 746 } 747 if (err <= B_OK) break; 748 to_read = err; 749 err = out->Write(buffer, to_read); 750 if (err != to_read) if (err >= 0) err = B_DEVICE_FULL; 751 if (err < B_OK) break; 752 } 753 754 if (buffer != temp) free(buffer); 755 return (err >= 0) ? B_OK : err; 756 } 757 758 //--------------------------------------------------- 759 // Encode into the native format 760 //--------------------------------------------------- 761 status_t 762 Compress(BPositionIO *in, BPositionIO *out) 763 { 764 // Load Settings 765 SETTINGS Settings; 766 LoadSettings(&Settings); 767 768 // Read info about bitmap 769 TranslatorBitmap header; 770 status_t err = in->Read(&header, sizeof(TranslatorBitmap)); 771 if (err < B_OK) return err; 772 else if (err < (int)sizeof(TranslatorBitmap)) return B_ERROR; 773 774 // Grab dimension, color space, and size information from the stream 775 BRect bounds; 776 bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left); 777 bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top); 778 bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right); 779 bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom); 780 781 int32 in_row_bytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes); 782 783 int width = bounds.IntegerWidth() + 1; 784 int height = bounds.IntegerHeight() + 1; 785 786 // Function pointer to write function 787 // It MUST point to proper function 788 void (*converter)(jas_matrix_t **pixels, jpr_uchar_t *inscanline, int width) = write_rgba32; 789 790 // Default color info 791 int out_color_space = JAS_IMAGE_CS_RGB; 792 int out_color_components = 3; 793 794 switch ((color_space)B_BENDIAN_TO_HOST_INT32(header.colors)) 795 { 796 case B_GRAY1: 797 { 798 if (Settings.B_GRAY1_as_B_RGB24) { 799 converter = write_gray1_to_rgb24; 800 } else { 801 out_color_components = 1; 802 out_color_space = JAS_IMAGE_CS_GRAY; 803 converter = write_gray1_to_gray; 804 } 805 break; 806 } 807 case B_CMAP8: 808 { 809 converter = write_cmap8_to_rgb24; 810 break; 811 } 812 case B_GRAY8: 813 { 814 out_color_components = 1; 815 out_color_space = JAS_IMAGE_CS_GRAY; 816 converter = write_gray; 817 break; 818 } 819 case B_RGB15: 820 case B_RGBA15: 821 { 822 converter = write_rgb15_to_rgb24; 823 break; 824 } 825 case B_RGB15_BIG: 826 case B_RGBA15_BIG: 827 { 828 converter = write_rgb15b_to_rgb24; 829 break; 830 } 831 case B_RGB16: 832 { 833 converter = write_rgb16_to_rgb24; 834 break; 835 } 836 case B_RGB16_BIG: 837 { 838 converter = write_rgb16b_to_rgb24; 839 break; 840 } 841 case B_RGB24: 842 { 843 converter = write_rgb24; 844 break; 845 } 846 case B_RGB24_BIG: 847 { 848 converter = write_rgb24b; 849 break; 850 } 851 case B_RGB32: 852 { 853 converter = write_rgb32_to_rgb24; 854 break; 855 } 856 case B_RGB32_BIG: 857 { 858 converter = write_rgb32b_to_rgb24; 859 break; 860 } 861 case B_RGBA32: 862 { 863 /* 864 // In theory it should be possible to write 4 color components 865 // to jp2, so it should be possible to have transparency. 866 // Unfortunetly libjasper does not agree with that 867 // For now i don't know how to modify it :( 868 869 out_color_components = 4; 870 converter = write_rgba32; 871 */ 872 converter = write_rgb32_to_rgb24; 873 break; 874 } 875 case B_RGBA32_BIG: 876 { 877 /* 878 // In theory it should be possible to write 4 color components 879 // to jp2, so it should be possible to have transparency. 880 // Unfortunetly libjasper does not agree with that 881 // For now i don't know how to modify it :( 882 883 out_color_components = 4; 884 converter = write_rgba32b; 885 */ 886 converter = write_rgb32b_to_rgb24; 887 break; 888 } 889 default: 890 { 891 (new BAlert("Error", "Unknown color space.", "Quit"))->Go(); 892 return B_ERROR; 893 } 894 } 895 896 jas_image_t *image; 897 jas_stream_t *outs; 898 jas_matrix_t *pixels[4]; 899 jas_image_cmptparm_t component_info[4]; 900 901 if (jas_init()) 902 return B_ERROR; 903 904 if (!(outs = jas_stream_positionIOopen(out))) 905 return B_ERROR; 906 907 int32 i = 0; 908 for (i = 0; i < (long)out_color_components; i++) 909 { 910 (void) memset(component_info + i, 0, sizeof(jas_image_cmptparm_t)); 911 component_info[i].hstep = 1; 912 component_info[i].vstep = 1; 913 component_info[i].width = (unsigned int)width; 914 component_info[i].height = (unsigned int)height; 915 component_info[i].prec = (unsigned int)8; 916 } 917 918 image = jas_image_create( (short)out_color_components, component_info, out_color_space); 919 if (image == (jas_image_t *)NULL) 920 return Error(outs, NULL, NULL, 0, NULL, B_ERROR); 921 922 jpr_uchar_t *in_scanline = (jpr_uchar_t*) malloc(in_row_bytes); 923 if (in_scanline == NULL) return Error(outs, image, NULL, 0, NULL, B_ERROR); 924 925 for (i = 0; i < (long)out_color_components; i++) 926 { 927 pixels[i] = jas_matrix_create(1, (unsigned int)width); 928 if (pixels[i] == (jas_matrix_t *)NULL) 929 return Error(outs, image, pixels, i+1, in_scanline, B_ERROR); 930 } 931 932 int32 y = 0; 933 for (y = 0; y < (long)height; y++) 934 { 935 err = in->Read(in_scanline, in_row_bytes); 936 if (err < in_row_bytes) 937 return (err < B_OK) ? Error(outs, image, pixels, out_color_components, in_scanline, err) : Error(outs, image, pixels, out_color_components, in_scanline, B_ERROR); 938 939 converter(pixels, in_scanline, width); 940 941 for (i = 0; i < (long)out_color_components; i++) 942 (void)jas_image_writecmpt(image, (short)i, 0, (unsigned int)y, (unsigned int)width, 1, pixels[i]); 943 } 944 945 char opts[16]; 946 sprintf(opts, "rate=%1f", (float)Settings.Quality / 100.0); 947 if ( jas_image_encode(image, outs, jas_image_strtofmt(Settings.JPC ? (char*)"jpc" : (char*)"jp2"), opts)) 948 return Error(outs, image, pixels, out_color_components, in_scanline, err); 949 950 free(in_scanline); 951 952 for (i = 0; i < (long)out_color_components; i++) 953 jas_matrix_destroy(pixels[i]); 954 jas_stream_close(outs); 955 jas_image_destroy(image); 956 jas_image_clearfmts(); 957 958 return B_OK; 959 } 960 961 //--------------------------------------------------- 962 // Decode the native format 963 //--------------------------------------------------- 964 status_t 965 Decompress(BPositionIO *in, BPositionIO *out) 966 { 967 SETTINGS Settings; 968 LoadSettings(&Settings); 969 970 jas_image_t *image; 971 jas_stream_t *ins; 972 jas_matrix_t *pixels[4]; 973 974 if (jas_init()) 975 return B_ERROR; 976 977 if (!(ins = jas_stream_positionIOopen(in))) 978 return B_ERROR; 979 980 if (!(image = jas_image_decode(ins, -1, 0))) 981 return Error(ins, NULL, NULL, 0, NULL, B_ERROR); 982 983 // Default color info 984 color_space out_color_space; 985 int out_color_components; 986 int in_color_components = jas_image_numcmpts(image); 987 988 // Function pointer to read function 989 // It MUST point to proper function 990 void (*converter)(jas_matrix_t **pixels, jpr_uchar_t *outscanline, int width) = NULL; 991 992 switch( jas_image_colorspace(image)) 993 { 994 case JAS_IMAGE_CS_RGB: 995 out_color_components = 4; 996 if (in_color_components == 3) { 997 out_color_space = B_RGB32; 998 converter = read_rgb24_to_rgb32; 999 } else if (in_color_components == 4) { 1000 out_color_space = B_RGBA32; 1001 converter = read_rgba32; 1002 } else { 1003 (new BAlert("Error", "Other than RGB with 3 or 4 color components not implemented.", "Quit"))->Go(); 1004 return Error(ins, image, NULL, 0, NULL, B_ERROR); 1005 } 1006 break; 1007 case JAS_IMAGE_CS_GRAY: 1008 if (Settings.B_GRAY8_as_B_RGB32) { 1009 out_color_space = B_RGB32; 1010 out_color_components = 4; 1011 converter = read_gray_to_rgb32; 1012 } else { 1013 out_color_space = B_GRAY8; 1014 out_color_components = 1; 1015 converter = read_gray; 1016 } 1017 break; 1018 case JAS_IMAGE_CS_YCBCR: 1019 (new BAlert("Error", "color space YCBCR not implemented yet.", "Quit"))->Go(); 1020 return Error(ins, image, NULL, 0, NULL, B_ERROR); 1021 break; 1022 case JAS_IMAGE_CS_UNKNOWN: 1023 default: 1024 (new BAlert("Error", "color space unknown.", "Quit"))->Go(); 1025 return Error(ins, image, NULL, 0, NULL, B_ERROR); 1026 break; 1027 } 1028 1029 float width = (float)jas_image_width(image); 1030 float height = (float)jas_image_height(image); 1031 1032 // Bytes count in one line of image (scanline) 1033 int64 out_row_bytes = (int32)width * out_color_components; 1034 // NOTE: things will go wrong if "out_row_bytes" wouldn't fit into 32 bits 1035 1036 // !!! Initialize this bounds rect to the size of your image 1037 BRect bounds( 0, 0, width-1, height-1); 1038 1039 // Fill out the B_TRANSLATOR_BITMAP's header 1040 TranslatorBitmap header; 1041 header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); 1042 header.bounds.left = B_HOST_TO_BENDIAN_FLOAT(bounds.left); 1043 header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top); 1044 header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right); 1045 header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom); 1046 header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_color_space); 1047 header.rowBytes = B_HOST_TO_BENDIAN_INT32(out_row_bytes); 1048 header.dataSize = B_HOST_TO_BENDIAN_INT32((int32)(out_row_bytes * height)); 1049 1050 // Write out the header 1051 status_t err = out->Write(&header, sizeof(TranslatorBitmap)); 1052 if (err < B_OK) return Error(ins, image, NULL, 0, NULL, err); 1053 else if (err < (int)sizeof(TranslatorBitmap)) return Error(ins, image, NULL, 0, NULL, B_ERROR); 1054 1055 jpr_uchar_t *out_scanline = (jpr_uchar_t*) malloc(out_row_bytes); 1056 if (out_scanline == NULL) return Error(ins, image, NULL, 0, NULL, B_ERROR); 1057 1058 int32 i = 0; 1059 for (i = 0; i < (long)in_color_components; i++) 1060 { 1061 pixels[i] = jas_matrix_create(1, (unsigned int)width); 1062 if (pixels[i] == (jas_matrix_t *)NULL) 1063 return Error(ins, image, pixels, i+1, out_scanline, B_ERROR); 1064 } 1065 1066 int32 y = 0; 1067 for (y = 0; y < (long)height; y++) 1068 { 1069 for (i = 0; i < (long)in_color_components; i++) 1070 (void)jas_image_readcmpt(image, (short)i, 0, (unsigned int)y, (unsigned int)width, 1, pixels[i]); 1071 1072 converter(pixels, out_scanline, (int32)width); 1073 1074 err = out->Write(out_scanline, out_row_bytes); 1075 if (err < out_row_bytes) 1076 return (err < B_OK) ? Error(ins, image, pixels, in_color_components, out_scanline, err) : Error(ins, image, pixels, in_color_components, out_scanline, B_ERROR); 1077 } 1078 1079 free(out_scanline); 1080 1081 for (i = 0; i < (long)in_color_components; i++) 1082 jas_matrix_destroy(pixels[i]); 1083 jas_stream_close(ins); 1084 jas_image_destroy(image); 1085 jas_image_clearfmts(); 1086 1087 return B_OK; 1088 } 1089 1090 //--------------------------------------------------- 1091 // Frees jpeg alocated memory 1092 // Returns given error (B_ERROR by default) 1093 //--------------------------------------------------- 1094 status_t 1095 Error(jas_stream_t *stream, jas_image_t *image, jas_matrix_t **pixels, int32 pixels_count, jpr_uchar_t *scanline, status_t error) 1096 { 1097 if (pixels) 1098 { 1099 int32 i; 1100 for (i = 0; i < (long)pixels_count; i++) 1101 if (pixels[i] != NULL) 1102 jas_matrix_destroy(pixels[i]); 1103 } 1104 if (stream) 1105 jas_stream_close(stream); 1106 if (image) 1107 jas_image_destroy(image); 1108 jas_image_clearfmts(); 1109 if (scanline) 1110 free(scanline); 1111 1112 return error; 1113 } 1114