1 /*****************************************************************************/ 2 // SGITranslator 3 // Written by Stephan Aßmus 4 // based on TIFFTranslator written mostly by 5 // Michael Wilber 6 // 7 // SGITranslator.cpp 8 // 9 // This BTranslator based object is for opening and writing 10 // SGI images. 11 // 12 // 13 // Copyright (c) 2003-2009 Haiku, Inc. All rights reserved. 14 // 15 // Permission is hereby granted, free of charge, to any person obtaining a 16 // copy of this software and associated documentation files (the "Software"), 17 // to deal in the Software without restriction, including without limitation 18 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 // and/or sell copies of the Software, and to permit persons to whom the 20 // Software is furnished to do so, subject to the following conditions: 21 // 22 // The above copyright notice and this permission notice shall be included 23 // in all copies or substantial portions of the Software. 24 // 25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 26 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 28 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 31 // DEALINGS IN THE SOFTWARE. 32 /*****************************************************************************/ 33 // 34 // How this works: 35 // 36 // libtiff has a special version of SGIOpen() that gets passed custom 37 // functions for reading writing etc. and a handle. This handle in our case 38 // is a BPositionIO object, which libtiff passes on to the functions for reading 39 // writing etc. So when operations are performed on the SGI* handle that is 40 // returned by SGIOpen(), libtiff uses the special reading writing etc 41 // functions so that all stream io happens on the BPositionIO object. 42 43 #include <new> 44 #include <stdio.h> 45 #include <string.h> 46 47 #include <OS.h> 48 49 #include "SGIImage.h" 50 #include "SGITranslator.h" 51 #include "SGIView.h" 52 53 using std::nothrow; 54 55 // The input formats that this translator supports. 56 translation_format gInputFormats[] = { 57 { 58 B_TRANSLATOR_BITMAP, 59 B_TRANSLATOR_BITMAP, 60 BBT_IN_QUALITY, 61 BBT_IN_CAPABILITY, 62 "image/x-be-bitmap", 63 "Be Bitmap Format (SGITranslator)" 64 }, 65 { 66 SGI_FORMAT, 67 B_TRANSLATOR_BITMAP, 68 SGI_IN_QUALITY, 69 SGI_IN_CAPABILITY, 70 "image/sgi", 71 "SGI image" 72 } 73 }; 74 75 // The output formats that this translator supports. 76 translation_format gOutputFormats[] = { 77 { 78 B_TRANSLATOR_BITMAP, 79 B_TRANSLATOR_BITMAP, 80 BBT_OUT_QUALITY, 81 BBT_OUT_CAPABILITY, 82 "image/x-be-bitmap", 83 "Be Bitmap Format (SGITranslator)" 84 }, 85 { 86 SGI_FORMAT, 87 B_TRANSLATOR_BITMAP, 88 SGI_OUT_QUALITY, 89 SGI_OUT_CAPABILITY, 90 "image/sgi", 91 "SGI image" 92 } 93 }; 94 95 // Default settings for the Translator 96 TranSetting gDefaultSettings[] = { 97 {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false}, 98 {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}, 99 {SGI_SETTING_COMPRESSION, TRAN_SETTING_INT32, SGI_COMP_RLE} 100 // compression is set to RLE by default 101 }; 102 103 // --------------------------------------------------------------- 104 // make_nth_translator 105 // 106 // Creates a SGITranslator object to be used by BTranslatorRoster 107 // 108 // Preconditions: 109 // 110 // Parameters: n, The translator to return. Since 111 // SGITranslator only publishes one 112 // translator, it only returns a 113 // SGITranslator if n == 0 114 // 115 // you, The image_id of the add-on that 116 // contains code (not used). 117 // 118 // flags, Has no meaning yet, should be 0. 119 // 120 // Postconditions: 121 // 122 // Returns: NULL if n is not zero, 123 // a new SGITranslator if n is zero 124 // --------------------------------------------------------------- 125 BTranslator * 126 make_nth_translator(int32 n, image_id you, uint32 flags, ...) 127 { 128 if (!n) 129 return new SGITranslator(); 130 else 131 return NULL; 132 } 133 134 // --------------------------------------------------------------- 135 // Constructor 136 // 137 // Sets up the version info and the name of the translator so that 138 // these values can be returned when they are requested. 139 // 140 // Preconditions: 141 // 142 // Parameters: 143 // 144 // Postconditions: 145 // 146 // Returns: 147 // --------------------------------------------------------------- 148 SGITranslator::SGITranslator() 149 : 150 BaseTranslator("SGI images", "SGI image translator", 151 SGI_TRANSLATOR_VERSION, 152 gInputFormats, sizeof(gInputFormats) / sizeof(translation_format), 153 gOutputFormats, sizeof(gOutputFormats) / sizeof(translation_format), 154 "SGITranslator_Settings", 155 gDefaultSettings, sizeof(gDefaultSettings) / sizeof(TranSetting), 156 B_TRANSLATOR_BITMAP, SGI_FORMAT) 157 { 158 } 159 160 // --------------------------------------------------------------- 161 // Destructor 162 // 163 // Does nothing 164 // 165 // Preconditions: 166 // 167 // Parameters: 168 // 169 // Postconditions: 170 // 171 // Returns: 172 // --------------------------------------------------------------- 173 SGITranslator::~SGITranslator() 174 { 175 } 176 177 status_t 178 identify_sgi_header(BPositionIO *inSource, translator_info *outInfo, uint32 outType, 179 SGIImage **poutSGIImage = NULL) 180 { 181 status_t status = B_NO_MEMORY; 182 // construct new SGIImage object and set it to the provided BPositionIO 183 SGIImage* sgiImage = new(nothrow) SGIImage(); 184 if (sgiImage) 185 status = sgiImage->SetTo(inSource); 186 187 if (status >= B_OK) { 188 if (outInfo) { 189 outInfo->type = SGI_FORMAT; 190 outInfo->group = B_TRANSLATOR_BITMAP; 191 outInfo->quality = SGI_IN_QUALITY; 192 outInfo->capability = SGI_IN_CAPABILITY; 193 strcpy(outInfo->MIME, "image/sgi"); 194 strcpy(outInfo->name, "SGI image"); 195 } 196 } else { 197 delete sgiImage; 198 sgiImage = NULL; 199 } 200 if (!poutSGIImage) 201 // close SGIImage if caller is not interested in SGIImage handle 202 delete sgiImage; 203 else 204 // leave SGIImage open (if it is) and return handle if caller needs it 205 *poutSGIImage = sgiImage; 206 207 return status; 208 } 209 210 // --------------------------------------------------------------- 211 // DerivedIdentify 212 // 213 // Examines the data from inSource and determines if it is in a 214 // format that this translator knows how to work with. 215 // 216 // Preconditions: 217 // 218 // Parameters: inSource, where the data to examine is 219 // 220 // inFormat, a hint about the data in inSource, 221 // it is ignored since it is only a hint 222 // 223 // ioExtension, configuration settings for the 224 // translator (not used) 225 // 226 // outInfo, information about what data is in 227 // inSource and how well this translator 228 // can handle that data is stored here 229 // 230 // outType, The format that the user wants 231 // the data in inSource to be 232 // converted to 233 // 234 // Postconditions: 235 // 236 // Returns: B_NO_TRANSLATOR, if this translator can't handle 237 // the data in inSource 238 // 239 // B_ERROR, if there was an error converting the data to the host 240 // format 241 // 242 // B_BAD_VALUE, if the settings in ioExtension are bad 243 // 244 // B_OK, if this translator understood the data and there were 245 // no errors found 246 // 247 // Other errors if BPositionIO::Read() returned an error value 248 // --------------------------------------------------------------- 249 status_t 250 SGITranslator::DerivedIdentify(BPositionIO *inSource, 251 const translation_format *inFormat, BMessage *ioExtension, 252 translator_info *outInfo, uint32 outType) 253 { 254 return identify_sgi_header(inSource, outInfo, outType); 255 } 256 257 // translate_from_bits 258 status_t 259 SGITranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, 260 BPositionIO *outDestination) 261 { 262 TranslatorBitmap bitsHeader; 263 264 uint32 compression = fSettings->SetGetInt32(SGI_SETTING_COMPRESSION); 265 266 status_t ret = identify_bits_header(inSource, NULL, &bitsHeader); 267 if (ret < B_OK) 268 return ret; 269 270 // Translate B_TRANSLATOR_BITMAP to SGI_FORMAT 271 if (outType == SGI_FORMAT) { 272 273 // common fields which are independent of the bitmap format 274 uint32 width = bitsHeader.bounds.IntegerWidth() + 1; 275 uint32 height = bitsHeader.bounds.IntegerHeight() + 1; 276 uint32 bytesPerRow = bitsHeader.rowBytes; 277 uint32 bytesPerChannel = 1; 278 color_space format = bitsHeader.colors; 279 280 uint32 channelCount; 281 switch (format) { 282 case B_GRAY8: 283 channelCount = 1; 284 break; 285 case B_RGB32: 286 case B_RGB32_BIG: 287 case B_RGB24: 288 case B_RGB24_BIG: 289 channelCount = 3; 290 break; 291 case B_RGBA32: 292 case B_RGBA32_BIG: 293 channelCount = 4; 294 break; 295 default: 296 return B_NO_TRANSLATOR; 297 } 298 299 // Set up SGI header 300 SGIImage* sgiImage = new SGIImage(); 301 status_t ret = sgiImage->SetTo(outDestination, width, height, 302 channelCount, bytesPerChannel, compression); 303 if (ret >= B_OK) { 304 // read one row at a time, 305 // convert to the correct format 306 // and write out the results 307 308 // SGI Images store each channel separately 309 // a buffer is allocated big enough to hold all channels 310 // then the pointers are assigned with offsets into that buffer 311 uint8** rows = new(nothrow) uint8*[channelCount]; 312 if (rows) 313 rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel]; 314 // rowBuffer is going to hold the converted data 315 uint8* rowBuffer = new(nothrow) uint8[bytesPerRow]; 316 if (rows && rows[0] && rowBuffer) { 317 // assign the other pointers (channel offsets in row buffer) 318 for (uint32 i = 1; i < channelCount; i++) 319 rows[i] = rows[0] + i * width; 320 // loop through all lines of the image 321 for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) { 322 323 ret = inSource->Read(rowBuffer, bytesPerRow); 324 // see if an error happened while reading 325 if (ret < B_OK) 326 break; 327 // convert to native format (big endian) 328 switch (format) { 329 case B_GRAY8: { 330 uint8* src = rowBuffer; 331 for (uint32 x = 0; x < width; x++) { 332 rows[0][x] = src[0]; 333 src += 1; 334 } 335 break; 336 } 337 case B_RGB24: { 338 uint8* src = rowBuffer; 339 for (uint32 x = 0; x < width; x++) { 340 rows[0][x] = src[2]; 341 rows[1][x] = src[1]; 342 rows[2][x] = src[0]; 343 src += 3; 344 } 345 break; 346 } 347 case B_RGB24_BIG: { 348 uint8* src = rowBuffer; 349 for (uint32 x = 0; x < width; x++) { 350 rows[0][x] = src[0]; 351 rows[1][x] = src[1]; 352 rows[2][x] = src[2]; 353 src += 3; 354 } 355 break; 356 } 357 case B_RGB32: { 358 uint8* src = rowBuffer; 359 for (uint32 x = 0; x < width; x++) { 360 rows[0][x] = src[2]; 361 rows[1][x] = src[1]; 362 rows[2][x] = src[0]; 363 // ignore src[3] 364 src += 4; 365 } 366 break; 367 } 368 case B_RGB32_BIG: { 369 uint8* src = rowBuffer; 370 for (uint32 x = 0; x < width; x++) { 371 rows[0][x] = src[1]; 372 rows[1][x] = src[2]; 373 rows[2][x] = src[3]; 374 // ignore src[0] 375 src += 4; 376 } 377 break; 378 } 379 case B_RGBA32: { 380 uint8* src = rowBuffer; 381 for (uint32 x = 0; x < width; x++) { 382 rows[0][x] = src[2]; 383 rows[1][x] = src[1]; 384 rows[2][x] = src[0]; 385 rows[3][x] = src[3]; 386 src += 4; 387 } 388 break; 389 } 390 case B_RGBA32_BIG: { 391 uint8* src = rowBuffer; 392 for (uint32 x = 0; x < width; x++) { 393 rows[0][x] = src[1]; 394 rows[1][x] = src[2]; 395 rows[2][x] = src[3]; 396 rows[3][x] = src[0]; 397 src += 4; 398 } 399 break; 400 } 401 default: 402 // cannot be here 403 break; 404 } // switch (format) 405 406 // for each channel, write a row buffer 407 for (uint32 z = 0; z < channelCount; z++) { 408 ret = sgiImage->WriteRow(rows[z], y, z); 409 if (ret < B_OK) { 410 printf("WriteRow() returned %s!\n", strerror(ret)); 411 break; 412 } 413 } 414 415 } // for (uint32 y = 0; y < height && ret >= B_OK; y++) 416 if (ret >= B_OK) 417 ret = B_OK; 418 } else // if (rows && rows[0] && rowBuffer) 419 ret = B_NO_MEMORY; 420 421 delete[] rows[0]; 422 delete[] rows; 423 delete[] rowBuffer; 424 } 425 426 // done with the SGIImage object 427 delete sgiImage; 428 429 return ret; 430 } 431 return B_NO_TRANSLATOR; 432 } 433 434 // translate_from_sgi 435 status_t 436 SGITranslator::translate_from_sgi(BPositionIO *inSource, uint32 outType, 437 BPositionIO *outDestination) 438 { 439 status_t ret = B_NO_TRANSLATOR; 440 441 // if copying SGI_FORMAT to SGI_FORMAT 442 if (outType == SGI_FORMAT) { 443 translate_direct_copy(inSource, outDestination); 444 return B_OK; 445 } 446 447 // variables needing cleanup 448 SGIImage* sgiImage = NULL; 449 450 ret = identify_sgi_header(inSource, NULL, outType, &sgiImage); 451 452 if (ret >= B_OK) { 453 454 bool bheaderonly = false, bdataonly = false; 455 456 uint32 width = sgiImage->Width(); 457 uint32 height = sgiImage->Height(); 458 uint32 channelCount = sgiImage->CountChannels(); 459 color_space format = B_RGBA32; 460 uint32 bytesPerRow = 0; 461 uint32 bytesPerChannel = sgiImage->BytesPerChannel(); 462 463 if (channelCount == 1) { 464 // format = B_GRAY8; // this format is not supported by most applications 465 // bytesPerRow = width; 466 format = B_RGB32; 467 bytesPerRow = width * 4; 468 } else if (channelCount == 2) { 469 // means gray (luminance) + alpha, we convert that to B_RGBA32 470 format = B_RGBA32; 471 bytesPerRow = width * 4; 472 } else if (channelCount == 3) { 473 format = B_RGB32; // should be B_RGB24, but let's not push it too hard... 474 bytesPerRow = width * 4; 475 } else if (channelCount == 4) { 476 format = B_RGBA32; 477 bytesPerRow = width * 4; 478 } else 479 ret = B_NO_TRANSLATOR; // we cannot handle this image 480 481 if (ret >= B_OK && !bdataonly) { 482 // Construct and write Be bitmap header 483 TranslatorBitmap bitsHeader; 484 bitsHeader.magic = B_TRANSLATOR_BITMAP; 485 bitsHeader.bounds.left = 0; 486 bitsHeader.bounds.top = 0; 487 bitsHeader.bounds.right = width - 1; 488 bitsHeader.bounds.bottom = height - 1; 489 bitsHeader.rowBytes = bytesPerRow; 490 bitsHeader.colors = format; 491 bitsHeader.dataSize = bitsHeader.rowBytes * height; 492 if ((ret = swap_data(B_UINT32_TYPE, &bitsHeader, 493 sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN)) < B_OK) { 494 return ret; 495 } else 496 ret = outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); 497 } 498 if (ret < B_OK) 499 printf("error writing bits header: %s\n", strerror(ret)); 500 if (ret >= B_OK && !bheaderonly) { 501 // read one row at a time, 502 // convert to the correct format 503 // and write out the results 504 505 // SGI Images store each channel separately 506 // a buffer is allocated big enough to hold all channels 507 // then the pointers are assigned with offsets into that buffer 508 uint8** rows = new(nothrow) uint8*[channelCount]; 509 if (rows) 510 rows[0] = new(nothrow) uint8[width * channelCount * bytesPerChannel]; 511 // rowBuffer is going to hold the converted data 512 uint8* rowBuffer = new(nothrow) uint8[bytesPerRow]; 513 if (rows && rows[0] && rowBuffer) { 514 // assign the other pointers (channel offsets in row buffer) 515 for (uint32 i = 1; i < channelCount; i++) 516 rows[i] = rows[0] + i * width * bytesPerChannel; 517 // loop through all lines of the image 518 for (int32 y = height - 1; y >= 0 && ret >= B_OK; y--) { 519 // fill the row buffer with each channel 520 for (uint32 z = 0; z < channelCount; z++) { 521 ret = sgiImage->ReadRow(rows[z], y, z); 522 if (ret < B_OK) 523 break; 524 } 525 // see if an error happened while reading 526 if (ret < B_OK) 527 break; 528 // convert to native format (big endian) 529 if (bytesPerChannel == 1) { 530 switch (format) { 531 case B_GRAY8: { 532 uint8* dst = rowBuffer; 533 for (uint32 x = 0; x < width; x++) { 534 dst[0] = rows[0][x]; 535 dst += 1; 536 } 537 break; 538 } 539 case B_RGB24: { 540 uint8* dst = rowBuffer; 541 for (uint32 x = 0; x < width; x++) { 542 dst[0] = rows[2][x]; 543 dst[1] = rows[1][x]; 544 dst[2] = rows[0][x]; 545 dst += 3; 546 } 547 break; 548 } 549 case B_RGB32: { 550 uint8* dst = rowBuffer; 551 if (channelCount == 1) { 552 for (uint32 x = 0; x < width; x++) { 553 dst[0] = rows[0][x]; 554 dst[1] = rows[0][x]; 555 dst[2] = rows[0][x]; 556 dst[3] = 255; 557 dst += 4; 558 } 559 } else { 560 for (uint32 x = 0; x < width; x++) { 561 dst[0] = rows[2][x]; 562 dst[1] = rows[1][x]; 563 dst[2] = rows[0][x]; 564 dst[3] = 255; 565 dst += 4; 566 } 567 } 568 break; 569 } 570 case B_RGBA32: { 571 uint8* dst = rowBuffer; 572 if (channelCount == 2) { 573 for (uint32 x = 0; x < width; x++) { 574 dst[0] = rows[0][x]; 575 dst[1] = rows[0][x]; 576 dst[2] = rows[0][x]; 577 dst[3] = rows[1][x]; 578 dst += 4; 579 } 580 } else { 581 for (uint32 x = 0; x < width; x++) { 582 dst[0] = rows[2][x]; 583 dst[1] = rows[1][x]; 584 dst[2] = rows[0][x]; 585 dst[3] = rows[3][x]; 586 dst += 4; 587 } 588 } 589 break; 590 } 591 default: 592 // cannot be here 593 break; 594 } // switch (format) 595 ret = outDestination->Write(rowBuffer, bytesPerRow); 596 } else { 597 // support for 16 bits per channel images 598 uint16** rows16 = (uint16**)rows; 599 switch (format) { 600 case B_GRAY8: { 601 uint8* dst = rowBuffer; 602 for (uint32 x = 0; x < width; x++) { 603 dst[0] = rows16[0][x] >> 8; 604 dst += 1; 605 } 606 break; 607 } 608 case B_RGB24: { 609 uint8* dst = rowBuffer; 610 for (uint32 x = 0; x < width; x++) { 611 dst[0] = rows16[2][x] >> 8; 612 dst[1] = rows16[1][x] >> 8; 613 dst[2] = rows16[0][x] >> 8; 614 dst += 3; 615 } 616 break; 617 } 618 case B_RGB32: { 619 uint8* dst = rowBuffer; 620 if (channelCount == 1) { 621 for (uint32 x = 0; x < width; x++) { 622 dst[0] = rows16[0][x] >> 8; 623 dst[1] = rows16[0][x] >> 8; 624 dst[2] = rows16[0][x] >> 8; 625 dst[3] = 255; 626 dst += 4; 627 } 628 } else { 629 for (uint32 x = 0; x < width; x++) { 630 dst[0] = rows16[2][x] >> 8; 631 dst[1] = rows16[1][x] >> 8; 632 dst[2] = rows16[0][x] >> 8; 633 dst[3] = 255; 634 dst += 4; 635 } 636 } 637 break; 638 } 639 case B_RGBA32: { 640 uint8* dst = rowBuffer; 641 if (channelCount == 2) { 642 for (uint32 x = 0; x < width; x++) { 643 dst[0] = rows16[0][x] >> 8; 644 dst[1] = rows16[0][x] >> 8; 645 dst[2] = rows16[0][x] >> 8; 646 dst[3] = rows16[1][x] >> 8; 647 dst += 4; 648 } 649 } else { 650 for (uint32 x = 0; x < width; x++) { 651 dst[0] = rows16[2][x] >> 8; 652 dst[1] = rows16[1][x] >> 8; 653 dst[2] = rows16[0][x] >> 8; 654 dst[3] = rows16[3][x] >> 8; 655 dst += 4; 656 } 657 } 658 break; 659 } 660 default: 661 // cannot be here 662 break; 663 } // switch (format) 664 ret = outDestination->Write(rowBuffer, bytesPerRow); 665 } // 16 bit version 666 } // for (uint32 y = 0; y < height && ret >= B_OK; y++) 667 if (ret >= B_OK) 668 ret = B_OK; 669 } else // if (rows && rows[0] && rowBuffer) 670 ret = B_NO_MEMORY; 671 delete[] rows[0]; 672 delete[] rows; 673 delete[] rowBuffer; 674 } // if (ret >= B_OK && !bheaderonly) 675 } // if (ret >= B_OK) 676 delete sgiImage; 677 678 return ret; 679 } 680 681 // --------------------------------------------------------------- 682 // DerivedTranslate 683 // 684 // Translates the data in inSource to the type outType and stores 685 // the translated data in outDestination. 686 // 687 // Preconditions: 688 // 689 // Parameters: inSource, the data to be translated 690 // 691 // inInfo, hint about the data in inSource (not used) 692 // 693 // ioExtension, configuration options for the 694 // translator 695 // 696 // outType, the type to convert inSource to 697 // 698 // outDestination, where the translated data is 699 // put 700 // 701 // baseType, indicates whether inSource is in the 702 // bits format, not in the bits format or 703 // is unknown 704 // 705 // Postconditions: 706 // 707 // Returns: B_BAD_VALUE, if the options in ioExtension are bad 708 // 709 // B_NO_TRANSLATOR, if this translator doesn't understand the data 710 // 711 // B_ERROR, if there was an error allocating memory or converting 712 // data 713 // 714 // B_OK, if all went well 715 // --------------------------------------------------------------- 716 status_t 717 SGITranslator::DerivedTranslate(BPositionIO *inSource, 718 const translator_info *inInfo, BMessage *ioExtension, 719 uint32 outType, BPositionIO *outDestination, int32 baseType) 720 { 721 if (baseType == 1) 722 // if inSource is in bits format 723 return translate_from_bits(inSource, outType, outDestination); 724 else if (baseType == 0) 725 // if inSource is NOT in bits format 726 return translate_from_sgi(inSource, outType, outDestination); 727 else 728 // if BaseTranslator did not properly identify the data as 729 // bits or not bits 730 return B_NO_TRANSLATOR; 731 } 732 733 BView * 734 SGITranslator::NewConfigView(TranslatorSettings *settings) 735 { 736 return new SGIView("SGITranslator Settings", B_WILL_DRAW, settings); 737 } 738