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