xref: /haiku/src/add-ons/translators/ppm/PPMTranslator.cpp (revision 70d5966963513ff47a456ba70b7d2eec0f99648d)
19949213aSStephan Aßmus /*
29949213aSStephan Aßmus 	Copyright 1999, Be Incorporated.   All Rights Reserved.
39949213aSStephan Aßmus 	This file may be used under the terms of the Be Sample Code License.
49949213aSStephan Aßmus */
59949213aSStephan Aßmus 
69949213aSStephan Aßmus /*	Parse the ASCII and raw versions of PPM.	*/
79949213aSStephan Aßmus /*	Note that the parsing of ASCII is very inefficient, because BFile	*/
89949213aSStephan Aßmus /*	does not buffer data. We should wrap a buffering thing around	*/
99949213aSStephan Aßmus /*	the input or output when they are in ASCII mode.				*/
109949213aSStephan Aßmus 
119949213aSStephan Aßmus #include <Bitmap.h>
12*70d59669SSiarzhuk Zharski #include <ByteOrder.h>
13*70d59669SSiarzhuk Zharski #include <Catalog.h>
14*70d59669SSiarzhuk Zharski #include <CheckBox.h>
15*70d59669SSiarzhuk Zharski #include <FindDirectory.h>
16a76f629eSRyan Leavengood #include <GridLayoutBuilder.h>
17a76f629eSRyan Leavengood #include <GroupLayout.h>
18a76f629eSRyan Leavengood #include <GroupLayoutBuilder.h>
19*70d59669SSiarzhuk Zharski #include <Locker.h>
20*70d59669SSiarzhuk Zharski #include <MenuField.h>
21*70d59669SSiarzhuk Zharski #include <MenuItem.h>
22*70d59669SSiarzhuk Zharski #include <Message.h>
23*70d59669SSiarzhuk Zharski #include <Path.h>
24*70d59669SSiarzhuk Zharski #include <PopUpMenu.h>
25*70d59669SSiarzhuk Zharski #include <Screen.h>
26a76f629eSRyan Leavengood #include <SpaceLayoutItem.h>
27*70d59669SSiarzhuk Zharski #include <StringView.h>
28*70d59669SSiarzhuk Zharski #include <TranslatorAddOn.h>
29*70d59669SSiarzhuk Zharski #include <TranslationKit.h>
309949213aSStephan Aßmus 
319949213aSStephan Aßmus #include <ctype.h>
329949213aSStephan Aßmus #include <string.h>
339949213aSStephan Aßmus #include <stdlib.h>
349949213aSStephan Aßmus #include <stdio.h>
35*70d59669SSiarzhuk Zharski #include <syslog.h>
369949213aSStephan Aßmus 
379949213aSStephan Aßmus #include "colorspace.h"
389949213aSStephan Aßmus 
39*70d59669SSiarzhuk Zharski #undef B_TRANSLATE_CONTEXT
40*70d59669SSiarzhuk Zharski #define B_TRANSLATE_CONTEXT "PPMTranslator"
419949213aSStephan Aßmus 
429949213aSStephan Aßmus #if DEBUG
439949213aSStephan Aßmus  #define dprintf(x) printf x
449949213aSStephan Aßmus #else
459949213aSStephan Aßmus  #define dprintf(x)
469949213aSStephan Aßmus #endif
479949213aSStephan Aßmus 
489949213aSStephan Aßmus 
499949213aSStephan Aßmus #if !defined(_PR3_COMPATIBLE_)	/* R4 headers? Else we need to define these constants. */
509949213aSStephan Aßmus  #define B_CMY24 ((color_space)0xC001) /* C[7:0]  M[7:0]  Y[7:0]          No gray removal done */
519949213aSStephan Aßmus  #define B_CMY32 ((color_space)0xC002) /* C[7:0]  M[7:0]  Y[7:0]  X[7:0]  No gray removal done */
529949213aSStephan Aßmus  #define B_CMYA32 ((color_space)0xE002) /* C[7:0]  M[7:0]  Y[7:0]  A[7:0] No gray removal done */
539949213aSStephan Aßmus  #define B_CMYK32 ((color_space)0xC003) /* C[7:0]  M[7:0]  Y[7:0]  K[7:0]                      */
549949213aSStephan Aßmus #endif
559949213aSStephan Aßmus 
569949213aSStephan Aßmus #define PPM_TRANSLATOR_VERSION 0x100
579949213aSStephan Aßmus 
589949213aSStephan Aßmus /* These three data items are exported by every translator. */
59fcc3e627SStephan Aßmus char translatorName[] = "PPM images";
609949213aSStephan Aßmus char translatorInfo[] = "PPM image translator v1.0.0, " __DATE__;
619949213aSStephan Aßmus int32 translatorVersion = PPM_TRANSLATOR_VERSION;
629949213aSStephan Aßmus 	// Revision: lowest 4 bits
639949213aSStephan Aßmus 	// Minor: next 4 bits
649949213aSStephan Aßmus 	// Major: highest 24 bits
659949213aSStephan Aßmus 
66*70d59669SSiarzhuk Zharski /*	Be reserves all codes with non-lowercase letters in them.	*/
679949213aSStephan Aßmus /*	Luckily, there is already a reserved code for PPM. If you	*/
689949213aSStephan Aßmus /*	make up your own for a new type, use lower-case letters.	*/
699949213aSStephan Aßmus #define PPM_TYPE 'PPM '
709949213aSStephan Aßmus 
719949213aSStephan Aßmus 
729949213aSStephan Aßmus /* These two data arrays are a really good idea to export from Translators, but not required. */
739949213aSStephan Aßmus translation_format inputFormats[] = {
74c095606eSRyan Leavengood 	{	B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap", "Be Bitmap Format (PPMTranslator)" },
759949213aSStephan Aßmus 	{	PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap", "PPM image" },
769949213aSStephan Aßmus 	{	0, 0, 0, 0, "\0", "\0" }
779949213aSStephan Aßmus };		/*	optional (else Identify is always called)	*/
789949213aSStephan Aßmus 
799949213aSStephan Aßmus translation_format outputFormats[] = {
80c095606eSRyan Leavengood 	{	B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap", "Be Bitmap Format (PPMTranslator)" },
819949213aSStephan Aßmus 	{	PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap", "PPM image" },
829949213aSStephan Aßmus 	{	0, 0, 0, 0, "\0", "\0" }
839949213aSStephan Aßmus };	/*	optional (else Translate is called anyway)	*/
849949213aSStephan Aßmus 
859949213aSStephan Aßmus /*	Translators that don't export outputFormats 	*/
869949213aSStephan Aßmus /*	will not be considered by files looking for 	*/
879949213aSStephan Aßmus /*	specific output formats.	*/
889949213aSStephan Aßmus 
899949213aSStephan Aßmus 
909949213aSStephan Aßmus /*	We keep our settings in a global struct, and wrap a lock around them.	*/
919949213aSStephan Aßmus struct ppm_settings {
929949213aSStephan Aßmus 	color_space		out_space;
939949213aSStephan Aßmus 	BPoint				window_pos;
949949213aSStephan Aßmus 	bool				write_ascii;
959949213aSStephan Aßmus 	bool				settings_touched;
969949213aSStephan Aßmus };
97bf243977SPhilippe Houdoin static BLocker g_settings_lock("PPM settings lock");
98bf243977SPhilippe Houdoin static ppm_settings g_settings;
999949213aSStephan Aßmus 
1009949213aSStephan Aßmus BPoint get_window_origin();
1019949213aSStephan Aßmus void set_window_origin(BPoint pos);
1029949213aSStephan Aßmus BPoint get_window_origin()
1039949213aSStephan Aßmus {
1049949213aSStephan Aßmus 	BPoint ret;
1059949213aSStephan Aßmus 	g_settings_lock.Lock();
1069949213aSStephan Aßmus 	ret = g_settings.window_pos;
1079949213aSStephan Aßmus 	g_settings_lock.Unlock();
1089949213aSStephan Aßmus 	return ret;
1099949213aSStephan Aßmus }
1109949213aSStephan Aßmus 
1119949213aSStephan Aßmus void set_window_origin(BPoint pos)
1129949213aSStephan Aßmus {
1139949213aSStephan Aßmus 	g_settings_lock.Lock();
1149949213aSStephan Aßmus 	g_settings.window_pos = pos;
1159949213aSStephan Aßmus 	g_settings.settings_touched = true;
1169949213aSStephan Aßmus 	g_settings_lock.Unlock();
1179949213aSStephan Aßmus }
1189949213aSStephan Aßmus 
1199949213aSStephan Aßmus 
1209949213aSStephan Aßmus class PrefsLoader {
1219949213aSStephan Aßmus public:
1229949213aSStephan Aßmus 		PrefsLoader(
1239949213aSStephan Aßmus 				const char * str)
1249949213aSStephan Aßmus 			{
1259949213aSStephan Aßmus 				dprintf(("PPMTranslator: PrefsLoader()\n"));
1269949213aSStephan Aßmus 				/* defaults */
1279949213aSStephan Aßmus 				g_settings.out_space = B_NO_COLOR_SPACE;
1289949213aSStephan Aßmus 				g_settings.window_pos = B_ORIGIN;
1299949213aSStephan Aßmus 				g_settings.write_ascii = false;
1309949213aSStephan Aßmus 				g_settings.settings_touched = false;
1319949213aSStephan Aßmus 				BPath path;
1329949213aSStephan Aßmus 				if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) {
1339949213aSStephan Aßmus 					path.SetTo("/tmp");
1349949213aSStephan Aßmus 				}
1359949213aSStephan Aßmus 				path.Append(str);
1369949213aSStephan Aßmus 				FILE * f = fopen(path.Path(), "r");
1379949213aSStephan Aßmus 				/*	parse text settings file -- this should be a library...	*/
1389949213aSStephan Aßmus 				if (f) {
1399949213aSStephan Aßmus 					char line[1024];
1409949213aSStephan Aßmus 					char name[32];
1419949213aSStephan Aßmus 					char * ptr;
1429949213aSStephan Aßmus 					while (true) {
1439949213aSStephan Aßmus 						line[0] = 0;
1449949213aSStephan Aßmus 						fgets(line, 1024, f);
1459949213aSStephan Aßmus 						if (!line[0]) {
1469949213aSStephan Aßmus 							break;
1479949213aSStephan Aßmus 						}
1489949213aSStephan Aßmus 						/* remember: line ends with \n, so printf()s don't have to */
1499949213aSStephan Aßmus 						ptr = line;
1509949213aSStephan Aßmus 						while (isspace(*ptr)) {
1519949213aSStephan Aßmus 							ptr++;
1529949213aSStephan Aßmus 						}
1539949213aSStephan Aßmus 						if (*ptr == '#' || !*ptr) {	/* comment or blank */
1549949213aSStephan Aßmus 							continue;
1559949213aSStephan Aßmus 						}
1569949213aSStephan Aßmus 						if (sscanf(ptr, "%31[a-zA-Z_0-9] =", name) != 1) {
157*70d59669SSiarzhuk Zharski 							syslog(LOG_ERR, "unknown PPMTranslator "
158*70d59669SSiarzhuk Zharski 												"settings line: %s", line);
1599949213aSStephan Aßmus 						}
1609949213aSStephan Aßmus 						else {
1619949213aSStephan Aßmus 							if (!strcmp(name, "color_space")) {
1629949213aSStephan Aßmus 								while (*ptr != '=') {
1639949213aSStephan Aßmus 									ptr++;
1649949213aSStephan Aßmus 								}
1659949213aSStephan Aßmus 								ptr++;
166*70d59669SSiarzhuk Zharski 								if (sscanf(ptr, "%d",
167*70d59669SSiarzhuk Zharski 									(int*)&g_settings.out_space) != 1) {
168*70d59669SSiarzhuk Zharski 									syslog(LOG_ERR, "illegal color space "
169*70d59669SSiarzhuk Zharski 										"in PPMTranslator settings: %s", ptr);
1709949213aSStephan Aßmus 								}
1719949213aSStephan Aßmus 							}
1729949213aSStephan Aßmus 							else if (!strcmp(name, "window_pos")) {
1739949213aSStephan Aßmus 								while (*ptr != '=') {
1749949213aSStephan Aßmus 									ptr++;
1759949213aSStephan Aßmus 								}
1769949213aSStephan Aßmus 								ptr++;
177*70d59669SSiarzhuk Zharski 								if (sscanf(ptr, "%f,%f",
178*70d59669SSiarzhuk Zharski 									&g_settings.window_pos.x,
179*70d59669SSiarzhuk Zharski 									&g_settings.window_pos.y) != 2) {
180*70d59669SSiarzhuk Zharski 									syslog(LOG_ERR, "illegal window position "
181*70d59669SSiarzhuk Zharski 										"in PPMTranslator settings: %s", ptr);
1829949213aSStephan Aßmus 								}
1839949213aSStephan Aßmus 							}
1849949213aSStephan Aßmus 							else if (!strcmp(name, "write_ascii")) {
1859949213aSStephan Aßmus 								while (*ptr != '=') {
1869949213aSStephan Aßmus 									ptr++;
1879949213aSStephan Aßmus 								}
1889949213aSStephan Aßmus 								ptr++;
1899949213aSStephan Aßmus 								int ascii = g_settings.write_ascii;
1909949213aSStephan Aßmus 								if (sscanf(ptr, "%d", &ascii) != 1) {
191*70d59669SSiarzhuk Zharski 									syslog(LOG_ERR, "illegal write_ascii value "
192*70d59669SSiarzhuk Zharski 										"in PPMTranslator settings: %s", ptr);
1939949213aSStephan Aßmus 								}
1949949213aSStephan Aßmus 								else {
1959949213aSStephan Aßmus 									g_settings.write_ascii = ascii;
1969949213aSStephan Aßmus 								}
1979949213aSStephan Aßmus 							}
1989949213aSStephan Aßmus 							else {
199*70d59669SSiarzhuk Zharski 								syslog(LOG_ERR,
200*70d59669SSiarzhuk Zharski 									"unknown PPMTranslator setting: %s", line);
2019949213aSStephan Aßmus 							}
2029949213aSStephan Aßmus 						}
2039949213aSStephan Aßmus 					}
2049949213aSStephan Aßmus 					fclose(f);
2059949213aSStephan Aßmus 				}
2069949213aSStephan Aßmus 			}
2079949213aSStephan Aßmus 		~PrefsLoader()
2089949213aSStephan Aßmus 			{
2099949213aSStephan Aßmus 				/*	No need writing settings if there aren't any	*/
2109949213aSStephan Aßmus 				if (g_settings.settings_touched) {
2119949213aSStephan Aßmus 					BPath path;
2129949213aSStephan Aßmus 					if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) {
2139949213aSStephan Aßmus 						path.SetTo("/tmp");
2149949213aSStephan Aßmus 					}
2159949213aSStephan Aßmus 					path.Append("PPMTranslator_Settings");
2169949213aSStephan Aßmus 					FILE * f = fopen(path.Path(), "w");
2179949213aSStephan Aßmus 					if (f) {
2189949213aSStephan Aßmus 						fprintf(f, "# PPMTranslator settings version %d.%d.%d\n",
2199949213aSStephan Aßmus 							static_cast<int>(translatorVersion >> 8),
2209949213aSStephan Aßmus 							static_cast<int>((translatorVersion >> 4) & 0xf),
2219949213aSStephan Aßmus 							static_cast<int>(translatorVersion & 0xf));
2229949213aSStephan Aßmus 						fprintf(f, "color_space = %d\n", g_settings.out_space);
223*70d59669SSiarzhuk Zharski 						fprintf(f, "window_pos = %g,%g\n", g_settings.window_pos.x,
224*70d59669SSiarzhuk Zharski 															g_settings.window_pos.y);
2259949213aSStephan Aßmus 						fprintf(f, "write_ascii = %d\n", g_settings.write_ascii ? 1 : 0);
2269949213aSStephan Aßmus 						fclose(f);
2279949213aSStephan Aßmus 					}
2289949213aSStephan Aßmus 				}
2299949213aSStephan Aßmus 			}
2309949213aSStephan Aßmus };
2319949213aSStephan Aßmus 
2329949213aSStephan Aßmus PrefsLoader g_prefs_loader("PPMTranslator_Settings");
2339949213aSStephan Aßmus 
2349949213aSStephan Aßmus /*	Some prototypes for functions we use.	*/
2359949213aSStephan Aßmus status_t read_ppm_header(BDataIO * io, int * width, int * rowbytes, int * height,
2369949213aSStephan Aßmus 	int * max, bool * ascii, color_space * space, bool * is_ppm, char ** comment);
2379949213aSStephan Aßmus status_t read_bits_header(BDataIO * io, int skipped, int * width, int * rowbytes,
2389949213aSStephan Aßmus 	int * height, int * max, bool * ascii, color_space * space);
2399949213aSStephan Aßmus status_t write_comment(const char * str, BDataIO * io);
2409949213aSStephan Aßmus status_t copy_data(BDataIO * in, BDataIO * out, int rowbytes, int out_rowbytes,
2419949213aSStephan Aßmus 	int height, int max, bool in_ascii, bool out_ascii, color_space in_space,
2429949213aSStephan Aßmus 	color_space out_space);
2439949213aSStephan Aßmus 
2449949213aSStephan Aßmus 	/*	Return B_NO_TRANSLATOR if not handling this data.	*/
2459949213aSStephan Aßmus 	/*	Even if inputFormats exists, may be called for data without hints	*/
2469949213aSStephan Aßmus 	/*	If outType is not 0, must be able to export in wanted format	*/
2479949213aSStephan Aßmus 
2489949213aSStephan Aßmus status_t
2499949213aSStephan Aßmus Identify(	/*	required	*/
2509949213aSStephan Aßmus 	BPositionIO * inSource,
2519949213aSStephan Aßmus 	const translation_format * inFormat,	/*	can beNULL	*/
2529949213aSStephan Aßmus 	BMessage * ioExtension,	/*	can be NULL	*/
2539949213aSStephan Aßmus 	translator_info * outInfo,
2549949213aSStephan Aßmus 	uint32 outType)
2559949213aSStephan Aßmus {
2569949213aSStephan Aßmus 	dprintf(("PPMTranslator: Identify()\n"));
2579949213aSStephan Aßmus 	/* Silence compiler warnings. */
2589949213aSStephan Aßmus 	inFormat = inFormat;
2599949213aSStephan Aßmus 	ioExtension = ioExtension;
2609949213aSStephan Aßmus 
2619949213aSStephan Aßmus 	/* Check that requested format is something we can deal with. */
2629949213aSStephan Aßmus 	if (outType == 0) {
2639949213aSStephan Aßmus 		outType = B_TRANSLATOR_BITMAP;
2649949213aSStephan Aßmus 	}
2659949213aSStephan Aßmus 	if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) {
2669949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
2679949213aSStephan Aßmus 	}
2689949213aSStephan Aßmus 
2699949213aSStephan Aßmus 	/* Check header. */
2709949213aSStephan Aßmus 	int width, rowbytes, height, max;
2719949213aSStephan Aßmus 	bool ascii, is_ppm;
2729949213aSStephan Aßmus 	color_space space;
2739949213aSStephan Aßmus 	status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max, &ascii, &space, &is_ppm, NULL);
2749949213aSStephan Aßmus 	if (err != B_OK) {
2759949213aSStephan Aßmus 		return err;
2769949213aSStephan Aßmus 	}
2779949213aSStephan Aßmus 	/* Stuff info into info struct -- Translation Kit will do "translator" for us. */
2789949213aSStephan Aßmus 	outInfo->group = B_TRANSLATOR_BITMAP;
2799949213aSStephan Aßmus 	if (is_ppm) {
2809949213aSStephan Aßmus 		outInfo->type = PPM_TYPE;
2819949213aSStephan Aßmus 		outInfo->quality = 0.3;		/* no alpha, etc */
2829949213aSStephan Aßmus 		outInfo->capability = 0.8;	/* we're pretty good at PPM reading, though */
283*70d59669SSiarzhuk Zharski 		strcpy(outInfo->name, B_TRANSLATE("PPM image"));
2849949213aSStephan Aßmus 		strcpy(outInfo->MIME, "image/x-portable-pixmap");
2859949213aSStephan Aßmus 	}
2869949213aSStephan Aßmus 	else {
2879949213aSStephan Aßmus 		outInfo->type = B_TRANSLATOR_BITMAP;
2889949213aSStephan Aßmus 		outInfo->quality = 0.4;		/* B_TRANSLATOR_BITMAP can do alpha, at least */
2899949213aSStephan Aßmus 		outInfo->capability = 0.8;	/* and we might not know many variations thereof */
290*70d59669SSiarzhuk Zharski 		strcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format (PPMTranslator)"));
2919949213aSStephan Aßmus 		strcpy(outInfo->MIME, "image/x-be-bitmap");	/* this is the MIME type of B_TRANSLATOR_BITMAP */
2929949213aSStephan Aßmus 	}
2939949213aSStephan Aßmus 	return B_OK;
2949949213aSStephan Aßmus }
2959949213aSStephan Aßmus 
2969949213aSStephan Aßmus 
2979949213aSStephan Aßmus 	/*	Return B_NO_TRANSLATOR if not handling the output format	*/
2989949213aSStephan Aßmus 	/*	If outputFormats exists, will only be called for those formats	*/
2999949213aSStephan Aßmus 
3009949213aSStephan Aßmus status_t
3019949213aSStephan Aßmus Translate(	/*	required	*/
3029949213aSStephan Aßmus 	BPositionIO * inSource,
3039949213aSStephan Aßmus 	const translator_info * /* inInfo*/ ,	/* silence compiler warning */
3049949213aSStephan Aßmus 	BMessage * ioExtension,	/*	can be NULL	*/
3059949213aSStephan Aßmus 	uint32 outType,
3069949213aSStephan Aßmus 	BPositionIO * outDestination)
3079949213aSStephan Aßmus {
3089949213aSStephan Aßmus 	dprintf(("PPMTranslator: Translate()\n"));
3099949213aSStephan Aßmus 	inSource->Seek(0, SEEK_SET);	/* paranoia */
3109949213aSStephan Aßmus //	inInfo = inInfo;	/* silence compiler warning */
3119949213aSStephan Aßmus 	/* Check what we're being asked to produce. */
3129949213aSStephan Aßmus 	if (!outType) {
3139949213aSStephan Aßmus 		outType = B_TRANSLATOR_BITMAP;
3149949213aSStephan Aßmus 	}
3159949213aSStephan Aßmus 	dprintf(("PPMTranslator: outType is '%c%c%c%c'\n", char(outType>>24), char(outType>>16), char(outType>>8), char(outType)));
3169949213aSStephan Aßmus 	if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) {
3179949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
3189949213aSStephan Aßmus 	}
3199949213aSStephan Aßmus 
3209949213aSStephan Aßmus 	/* Figure out what we've been given (again). */
3219949213aSStephan Aßmus 	int width, rowbytes, height, max;
3229949213aSStephan Aßmus 	bool ascii, is_ppm;
3239949213aSStephan Aßmus 	color_space space;
3249949213aSStephan Aßmus 	/* Read_ppm_header() will always return with stream at beginning of data */
3259949213aSStephan Aßmus 	/* for both B_TRANSLATOR_BITMAP and PPM_TYPE, and indicate what it is. */
3269949213aSStephan Aßmus 	char * comment = NULL;
3279949213aSStephan Aßmus 	status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max, &ascii, &space, &is_ppm, &comment);
3289949213aSStephan Aßmus 	if (comment != NULL) {
3299949213aSStephan Aßmus 		if (ioExtension != NULL) {
3309949213aSStephan Aßmus #if defined(_PR3_COMPATIBLE_)	/* R4 headers? */
3319949213aSStephan Aßmus 			ioExtension->AddString(B_TRANSLATOR_EXT_COMMENT, comment);
3329949213aSStephan Aßmus #else
3339949213aSStephan Aßmus 			ioExtension->AddString("/comment", comment);
3349949213aSStephan Aßmus #endif
3359949213aSStephan Aßmus 		}
3369949213aSStephan Aßmus 		free(comment);
3379949213aSStephan Aßmus 	}
3389949213aSStephan Aßmus 	if (err < B_OK) {
3399949213aSStephan Aßmus 		dprintf(("read_ppm_header() error %s [%lx]\n", strerror(err), err));
3409949213aSStephan Aßmus 		return err;
3419949213aSStephan Aßmus 	}
3429949213aSStephan Aßmus 	/* Check if we're configured to write ASCII format file. */
3439949213aSStephan Aßmus 	bool out_ascii = false;
3449949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3459949213aSStephan Aßmus 		out_ascii = g_settings.write_ascii;
3469949213aSStephan Aßmus 		if (ioExtension != NULL) {
3479949213aSStephan Aßmus 			ioExtension->FindBool("ppm /ascii", &out_ascii);
3489949213aSStephan Aßmus 		}
3499949213aSStephan Aßmus 	}
3509949213aSStephan Aßmus 	err = B_OK;
3519949213aSStephan Aßmus 	/* Figure out which color space to convert to */
3529949213aSStephan Aßmus 	color_space out_space;
3539949213aSStephan Aßmus 	int out_rowbytes;
3549949213aSStephan Aßmus 	g_settings_lock.Lock();
3559949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3569949213aSStephan Aßmus 		out_space = B_RGB24_BIG;
3579949213aSStephan Aßmus 		out_rowbytes = 3*width;
3589949213aSStephan Aßmus 	}
3599949213aSStephan Aßmus 	else {	/*	When outputting to B_TRANSLATOR_BITMAP, follow user's wishes.	*/
3609949213aSStephan Aßmus #if defined(_PR3_COMPATIBLE_)	/* R4 headers? */
3619949213aSStephan Aßmus 		if (!ioExtension || ioExtension->FindInt32(B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE, (int32*)&out_space) ||
3629949213aSStephan Aßmus #else
3639949213aSStephan Aßmus 		if (!ioExtension || ioExtension->FindInt32("bits/space", (int32*)&out_space) ||
3649949213aSStephan Aßmus #endif
3659949213aSStephan Aßmus 			(out_space == B_NO_COLOR_SPACE)) {
3669949213aSStephan Aßmus 			if (g_settings.out_space == B_NO_COLOR_SPACE) {
3679949213aSStephan Aßmus 				switch (space) {	/*	The 24-bit versions are pretty silly, use 32 instead.	*/
3689949213aSStephan Aßmus 				case B_RGB24:
3699949213aSStephan Aßmus 				case B_RGB24_BIG:
3709949213aSStephan Aßmus 					out_space = B_RGB32;
3719949213aSStephan Aßmus 					break;
3729949213aSStephan Aßmus 				default:
3739949213aSStephan Aßmus 					/* use whatever is there */
3749949213aSStephan Aßmus 					out_space = space;
3759949213aSStephan Aßmus 					break;
3769949213aSStephan Aßmus 				}
3779949213aSStephan Aßmus 			}
3789949213aSStephan Aßmus 			else {
3799949213aSStephan Aßmus 				out_space = g_settings.out_space;
3809949213aSStephan Aßmus 			}
3819949213aSStephan Aßmus 		}
3829949213aSStephan Aßmus 		out_rowbytes = calc_rowbytes(out_space, width);
3839949213aSStephan Aßmus 	}
3849949213aSStephan Aßmus 	g_settings_lock.Unlock();
3859949213aSStephan Aßmus 	/* Write file header */
3869949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3879949213aSStephan Aßmus 		dprintf(("PPMTranslator: write PPM\n"));
3889949213aSStephan Aßmus 		/* begin PPM header */
3899949213aSStephan Aßmus 		const char * magic;
3909949213aSStephan Aßmus 		if (out_ascii)
3919949213aSStephan Aßmus 			magic = "P3\n";
3929949213aSStephan Aßmus 		else
3939949213aSStephan Aßmus 			magic = "P6\n";
3949949213aSStephan Aßmus 		err = outDestination->Write(magic, strlen(magic));
3959949213aSStephan Aßmus 		if (err == (long)strlen(magic)) err = 0;
3969949213aSStephan Aßmus 		//comment = NULL;
3979949213aSStephan Aßmus 		const char* fsComment;
3989949213aSStephan Aßmus #if defined(_PR3_COMPATIBLE_)	/* R4 headers? */
3999949213aSStephan Aßmus 		if ((ioExtension != NULL) && !ioExtension->FindString(B_TRANSLATOR_EXT_COMMENT, &fsComment)) {
4009949213aSStephan Aßmus #else
4019949213aSStephan Aßmus 		if ((ioExtension != NULL) && !ioExtension->FindString("/comment", &fsComment)) {
4029949213aSStephan Aßmus #endif
4039949213aSStephan Aßmus 			err = write_comment(fsComment, outDestination);
4049949213aSStephan Aßmus 		}
4059949213aSStephan Aßmus 		if (err == B_OK) {
4069949213aSStephan Aßmus 			char data[40];
4079949213aSStephan Aßmus 			sprintf(data, "%d %d %d\n", width, height, max);
4089949213aSStephan Aßmus 			err = outDestination->Write(data, strlen(data));
4099949213aSStephan Aßmus 			if (err == (long)strlen(data)) err = 0;
4109949213aSStephan Aßmus 		}
4119949213aSStephan Aßmus 		/* header done */
4129949213aSStephan Aßmus 	}
4139949213aSStephan Aßmus 	else {
4149949213aSStephan Aßmus 		dprintf(("PPMTranslator: write B_TRANSLATOR_BITMAP\n"));
4159949213aSStephan Aßmus 		/* B_TRANSLATOR_BITMAP header */
4169949213aSStephan Aßmus 		TranslatorBitmap hdr;
4179949213aSStephan Aßmus 		hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
4189949213aSStephan Aßmus 		hdr.bounds.left = B_HOST_TO_BENDIAN_FLOAT(0);
4199949213aSStephan Aßmus 		hdr.bounds.top = B_HOST_TO_BENDIAN_FLOAT(0);
4209949213aSStephan Aßmus 		hdr.bounds.right = B_HOST_TO_BENDIAN_FLOAT(width-1);
4219949213aSStephan Aßmus 		hdr.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(height-1);
4229949213aSStephan Aßmus 		hdr.rowBytes = B_HOST_TO_BENDIAN_INT32(out_rowbytes);
4239949213aSStephan Aßmus 		hdr.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_space);
4249949213aSStephan Aßmus 		hdr.dataSize = B_HOST_TO_BENDIAN_INT32(out_rowbytes*height);
4259949213aSStephan Aßmus 		dprintf(("rowBytes is %d, width %d, out_space %x, space %x\n", out_rowbytes, width, out_space, space));
4269949213aSStephan Aßmus 		err = outDestination->Write(&hdr, sizeof(hdr));
4279949213aSStephan Aßmus 		dprintf(("PPMTranslator: Write() returns %lx\n", err));
4289949213aSStephan Aßmus #if DEBUG
4299949213aSStephan Aßmus 		{
4309949213aSStephan Aßmus 			BBitmap * map = new BBitmap(BRect(0,0,width-1,height-1), out_space);
4319949213aSStephan Aßmus 			printf("map rb = %ld\n", map->BytesPerRow());
4329949213aSStephan Aßmus 			delete map;
4339949213aSStephan Aßmus 		}
4349949213aSStephan Aßmus #endif
4359949213aSStephan Aßmus 		if (err == sizeof(hdr)) err = 0;
4369949213aSStephan Aßmus 		/* header done */
4379949213aSStephan Aßmus 	}
4389949213aSStephan Aßmus 	if (err != B_OK) {
4399949213aSStephan Aßmus 		return err > 0 ? B_IO_ERROR : err;
4409949213aSStephan Aßmus 	}
4419949213aSStephan Aßmus 	/* Write data. Luckily, PPM and B_TRANSLATOR_BITMAP both scan from left to right, top to bottom. */
4429949213aSStephan Aßmus 	return copy_data(inSource, outDestination, rowbytes, out_rowbytes, height, max, ascii, out_ascii, space, out_space);
4439949213aSStephan Aßmus }
4449949213aSStephan Aßmus 
4459949213aSStephan Aßmus 
4469949213aSStephan Aßmus class PPMView :
4479949213aSStephan Aßmus 	public BView
4489949213aSStephan Aßmus {
4499949213aSStephan Aßmus public:
4509949213aSStephan Aßmus 		PPMView(
4519949213aSStephan Aßmus 				const char * name,
4529949213aSStephan Aßmus 				uint32 flags) :
453a76f629eSRyan Leavengood 			BView(name, flags)
4549949213aSStephan Aßmus 			{
455dc0d6e4cSStefano Ceccherini 				SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
456dc0d6e4cSStefano Ceccherini 				SetLowColor(ViewColor());
457a76f629eSRyan Leavengood 
458*70d59669SSiarzhuk Zharski 				mTitle = new BStringView("title",
459*70d59669SSiarzhuk Zharski 					B_TRANSLATE("PPM Image Translator"));
460a76f629eSRyan Leavengood 				mTitle->SetFont(be_bold_font);
461a76f629eSRyan Leavengood 
462a76f629eSRyan Leavengood 				char detail[100];
463a76f629eSRyan Leavengood 				int ver = static_cast<int>(translatorVersion);
464*70d59669SSiarzhuk Zharski 				sprintf(detail, B_TRANSLATE("Version %d.%d.%d %s"), ver >> 8,
465*70d59669SSiarzhuk Zharski 					((ver >> 4) & 0xf),
466a76f629eSRyan Leavengood 					(ver & 0xf), __DATE__);
467a76f629eSRyan Leavengood 				mDetail = new BStringView("detail", detail);
468a76f629eSRyan Leavengood 
469a76f629eSRyan Leavengood 				mBasedOn = new BStringView("basedOn",
470*70d59669SSiarzhuk Zharski 					B_TRANSLATE("Based on PPMTranslator sample code"));
471a76f629eSRyan Leavengood 
472a76f629eSRyan Leavengood 				mCopyright = new BStringView("copyright",
473*70d59669SSiarzhuk Zharski 					B_TRANSLATE("Sample code copyright 1999, Be Incorporated"));
474a76f629eSRyan Leavengood 
4759949213aSStephan Aßmus 				mMenu = new BPopUpMenu("Color Space");
476*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("None"),
477*70d59669SSiarzhuk Zharski 					CSMessage(B_NO_COLOR_SPACE)));
478*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 8:8:8 32 bits"),
479*70d59669SSiarzhuk Zharski 					CSMessage(B_RGB32)));
480*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
481*70d59669SSiarzhuk Zharski 					"bits"), CSMessage(B_RGBA32)));
482*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:5:5 16 bits"),
483*70d59669SSiarzhuk Zharski 					CSMessage(B_RGB15)));
484*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
485*70d59669SSiarzhuk Zharski 					"bits"), CSMessage(B_RGBA15)));
486*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:6:5 16 bits"),
487*70d59669SSiarzhuk Zharski 					CSMessage(B_RGB16)));
488*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("System palette 8 "
489*70d59669SSiarzhuk Zharski 					"bits"), CSMessage(B_CMAP8)));
4909949213aSStephan Aßmus 				mMenu->AddSeparatorItem();
491*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("Grayscale 8 bits"),
492*70d59669SSiarzhuk Zharski 					CSMessage(B_GRAY8)));
493*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("Bitmap 1 bit"),
494*70d59669SSiarzhuk Zharski 					CSMessage(B_GRAY1)));
495*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("CMY 8:8:8 32 bits"),
496*70d59669SSiarzhuk Zharski 					CSMessage(B_CMY32)));
497*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYA 8:8:8:8 32 "
498*70d59669SSiarzhuk Zharski 					"bits"), CSMessage(B_CMYA32)));
499*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYK 8:8:8:8 32 "
500*70d59669SSiarzhuk Zharski 					"bits"), CSMessage(B_CMYK32)));
5019949213aSStephan Aßmus 				mMenu->AddSeparatorItem();
502*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 8:8:8 32 bits "
503*70d59669SSiarzhuk Zharski 					"big-endian"), CSMessage(B_RGB32_BIG)));
504*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
505*70d59669SSiarzhuk Zharski 					"bits big-endian"), CSMessage(B_RGBA32_BIG)));
506*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:5:5 16 bits "
507*70d59669SSiarzhuk Zharski 					"big-endian"), CSMessage(B_RGB15_BIG)));
508*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
509*70d59669SSiarzhuk Zharski 					"bits big-endian"), CSMessage(B_RGBA15_BIG)));
510*70d59669SSiarzhuk Zharski 				mMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:6:5 16 bits "
511*70d59669SSiarzhuk Zharski 					"big-endian"), CSMessage(B_RGB16)));
512*70d59669SSiarzhuk Zharski  				mField = new BMenuField(B_TRANSLATE("Input Color Space"),
513*70d59669SSiarzhuk Zharski  					mMenu, NULL);
5149949213aSStephan Aßmus  				mField->SetViewColor(ViewColor());
5159949213aSStephan Aßmus  				SelectColorSpace(g_settings.out_space);
5169949213aSStephan Aßmus  				BMessage * msg = new BMessage(CHANGE_ASCII);
517*70d59669SSiarzhuk Zharski  				mAscii = new BCheckBox(B_TRANSLATE("Write ASCII"), msg);
518a76f629eSRyan Leavengood  				if (g_settings.write_ascii)
5199949213aSStephan Aßmus  					mAscii->SetValue(1);
5209949213aSStephan Aßmus  				mAscii->SetViewColor(ViewColor());
521a76f629eSRyan Leavengood 
522a76f629eSRyan Leavengood  				// Build the layout
523a76f629eSRyan Leavengood  				SetLayout(new BGroupLayout(B_HORIZONTAL));
524a76f629eSRyan Leavengood 
525a76f629eSRyan Leavengood  				AddChild(BGroupLayoutBuilder(B_VERTICAL, 7)
526a76f629eSRyan Leavengood  					.Add(mTitle)
527a76f629eSRyan Leavengood  					.Add(mDetail)
528a76f629eSRyan Leavengood  					.AddGlue()
529a76f629eSRyan Leavengood  					.Add(mBasedOn)
530a76f629eSRyan Leavengood  					.Add(mCopyright)
531a76f629eSRyan Leavengood  					.AddGlue()
532a76f629eSRyan Leavengood  					.Add(BGridLayoutBuilder(10, 10)
533a76f629eSRyan Leavengood  						.Add(mField->CreateLabelLayoutItem(), 0, 0)
534a76f629eSRyan Leavengood  						.Add(mField->CreateMenuBarLayoutItem(), 1, 0)
535a76f629eSRyan Leavengood  						.Add(mAscii, 0, 1)
536a76f629eSRyan Leavengood  					)
537a76f629eSRyan Leavengood  					.AddGlue()
538a76f629eSRyan Leavengood  					.SetInsets(5, 5, 5, 5)
539a76f629eSRyan Leavengood  				);
540a76f629eSRyan Leavengood 
541a76f629eSRyan Leavengood  				BFont font;
542a76f629eSRyan Leavengood  				GetFont(&font);
543a76f629eSRyan Leavengood  				SetExplicitPreferredSize(BSize((font.Size() * 350)/12, (font.Size() * 200)/12));
5449949213aSStephan Aßmus 			}
5459949213aSStephan Aßmus 		~PPMView()
5469949213aSStephan Aßmus 			{
5479949213aSStephan Aßmus 				/* nothing here */
5489949213aSStephan Aßmus 			}
5499949213aSStephan Aßmus 
5509949213aSStephan Aßmus 		enum {
5519949213aSStephan Aßmus 			SET_COLOR_SPACE = 'ppm=',
5529949213aSStephan Aßmus 			CHANGE_ASCII
5539949213aSStephan Aßmus 		};
5549949213aSStephan Aßmus 
5559949213aSStephan Aßmus virtual	void MessageReceived(
5569949213aSStephan Aßmus 				BMessage * message)
5579949213aSStephan Aßmus 			{
5589949213aSStephan Aßmus 				if (message->what == SET_COLOR_SPACE) {
5599949213aSStephan Aßmus 					SetSettings(message);
5609949213aSStephan Aßmus 				}
5619949213aSStephan Aßmus 				else if (message->what == CHANGE_ASCII) {
5629949213aSStephan Aßmus 					BMessage msg;
5639949213aSStephan Aßmus 					msg.AddBool("ppm /ascii", mAscii->Value());
5649949213aSStephan Aßmus 					SetSettings(&msg);
5659949213aSStephan Aßmus 				}
5669949213aSStephan Aßmus 				else {
5679949213aSStephan Aßmus 					BView::MessageReceived(message);
5689949213aSStephan Aßmus 				}
5699949213aSStephan Aßmus 			}
5709949213aSStephan Aßmus virtual	void AllAttached()
5719949213aSStephan Aßmus 			{
5729949213aSStephan Aßmus 				BView::AllAttached();
5739949213aSStephan Aßmus 				BMessenger msgr(this);
5749949213aSStephan Aßmus 				/*	Tell all menu items we're the man.	*/
5759949213aSStephan Aßmus 				for (int ix=0; ix<mMenu->CountItems(); ix++) {
5769949213aSStephan Aßmus 					BMenuItem * i = mMenu->ItemAt(ix);
5779949213aSStephan Aßmus 					if (i) {
5789949213aSStephan Aßmus 						i->SetTarget(msgr);
5799949213aSStephan Aßmus 					}
5809949213aSStephan Aßmus 				}
5819949213aSStephan Aßmus 				mAscii->SetTarget(msgr);
5829949213aSStephan Aßmus 			}
5839949213aSStephan Aßmus 
5849949213aSStephan Aßmus 		void SetSettings(
5859949213aSStephan Aßmus 				BMessage * message)
5869949213aSStephan Aßmus 			{
5879949213aSStephan Aßmus 				g_settings_lock.Lock();
5889949213aSStephan Aßmus 				color_space space;
5899949213aSStephan Aßmus 				if (!message->FindInt32("space", (int32*)&space)) {
5909949213aSStephan Aßmus 					g_settings.out_space = space;
5919949213aSStephan Aßmus 					SelectColorSpace(space);
5929949213aSStephan Aßmus 					g_settings.settings_touched = true;
5939949213aSStephan Aßmus 				}
5949949213aSStephan Aßmus 				bool ascii;
5959949213aSStephan Aßmus 				if (!message->FindBool("ppm /ascii", &ascii)) {
5969949213aSStephan Aßmus 					g_settings.write_ascii = ascii;
5979949213aSStephan Aßmus 					g_settings.settings_touched = true;
5989949213aSStephan Aßmus 				}
5999949213aSStephan Aßmus 				g_settings_lock.Unlock();
6009949213aSStephan Aßmus 			}
6019949213aSStephan Aßmus 
6029949213aSStephan Aßmus private:
603a76f629eSRyan Leavengood 		BStringView * mTitle;
604a76f629eSRyan Leavengood 		BStringView * mDetail;
605a76f629eSRyan Leavengood 		BStringView * mBasedOn;
606a76f629eSRyan Leavengood 		BStringView * mCopyright;
6079949213aSStephan Aßmus 		BPopUpMenu * mMenu;
6089949213aSStephan Aßmus 		BMenuField * mField;
6099949213aSStephan Aßmus 		BCheckBox * mAscii;
6109949213aSStephan Aßmus 
6119949213aSStephan Aßmus 		BMessage * CSMessage(
6129949213aSStephan Aßmus 				color_space space)
6139949213aSStephan Aßmus 			{
6149949213aSStephan Aßmus 				BMessage * ret = new BMessage(SET_COLOR_SPACE);
6159949213aSStephan Aßmus 				ret->AddInt32("space", space);
6169949213aSStephan Aßmus 				return ret;
6179949213aSStephan Aßmus 			}
6189949213aSStephan Aßmus 
6199949213aSStephan Aßmus 		void SelectColorSpace(
6209949213aSStephan Aßmus 				color_space space)
6219949213aSStephan Aßmus 			{
6229949213aSStephan Aßmus 				for (int ix=0; ix<mMenu->CountItems(); ix++) {
6239949213aSStephan Aßmus 					int32 s;
6249949213aSStephan Aßmus 					BMenuItem * i = mMenu->ItemAt(ix);
6259949213aSStephan Aßmus 					if (i) {
6269949213aSStephan Aßmus 						BMessage * m = i->Message();
6279949213aSStephan Aßmus 						if (m && !m->FindInt32("space", &s) && (s == space)) {
6289949213aSStephan Aßmus 							mMenu->Superitem()->SetLabel(i->Label());
6299949213aSStephan Aßmus 							break;
6309949213aSStephan Aßmus 						}
6319949213aSStephan Aßmus 					}
6329949213aSStephan Aßmus 				}
6339949213aSStephan Aßmus 			}
6349949213aSStephan Aßmus };
6359949213aSStephan Aßmus 
6369949213aSStephan Aßmus 
6379949213aSStephan Aßmus 	/*	The view will get resized to what the parent thinks is 	*/
6389949213aSStephan Aßmus 	/*	reasonable. However, it will still receive MouseDowns etc.	*/
6399949213aSStephan Aßmus 	/*	Your view should change settings in the translator immediately, 	*/
6409949213aSStephan Aßmus 	/*	taking care not to change parameters for a translation that is 	*/
6419949213aSStephan Aßmus 	/*	currently running. Typically, you'll have a global struct for 	*/
6429949213aSStephan Aßmus 	/*	settings that is atomically copied into the translator function 	*/
6439949213aSStephan Aßmus 	/*	as a local when translation starts.	*/
6449949213aSStephan Aßmus 	/*	Store your settings wherever you feel like it.	*/
6459949213aSStephan Aßmus 
6469949213aSStephan Aßmus status_t
6479949213aSStephan Aßmus MakeConfig(	/*	optional	*/
6489949213aSStephan Aßmus 	BMessage * ioExtension,	/*	can be NULL	*/
6499949213aSStephan Aßmus 	BView * * outView,
6509949213aSStephan Aßmus 	BRect * outExtent)
6519949213aSStephan Aßmus {
652*70d59669SSiarzhuk Zharski 	PPMView * v = new PPMView(B_TRANSLATE("PPMTranslator Settings"),
653*70d59669SSiarzhuk Zharski 		B_WILL_DRAW);
6549949213aSStephan Aßmus 	*outView = v;
655a76f629eSRyan Leavengood 	v->ResizeTo(v->ExplicitPreferredSize());;
6569949213aSStephan Aßmus 	*outExtent = v->Bounds();
6579949213aSStephan Aßmus 	if (ioExtension) {
6589949213aSStephan Aßmus 		v->SetSettings(ioExtension);
6599949213aSStephan Aßmus 	}
6609949213aSStephan Aßmus 	return B_OK;
6619949213aSStephan Aßmus }
6629949213aSStephan Aßmus 
6639949213aSStephan Aßmus 
6649949213aSStephan Aßmus 	/*	Copy your current settings to a BMessage that may be passed 	*/
6659949213aSStephan Aßmus 	/*	to BTranslators::Translate at some later time when the user wants to 	*/
6669949213aSStephan Aßmus 	/*	use whatever settings you're using right now.	*/
6679949213aSStephan Aßmus 
6689949213aSStephan Aßmus status_t
6699949213aSStephan Aßmus GetConfigMessage(	/*	optional	*/
6709949213aSStephan Aßmus 	BMessage * ioExtension)
6719949213aSStephan Aßmus {
6729949213aSStephan Aßmus 	status_t err = B_OK;
6739949213aSStephan Aßmus #if defined(_PR3_COMPATIBLE_)
6749949213aSStephan Aßmus 	const char * name = B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE;
6759949213aSStephan Aßmus #else
676*70d59669SSiarzhuk Zharski 	const char * name = B_TRANSLATE_MARK("bits/space");
6779949213aSStephan Aßmus #endif
6789949213aSStephan Aßmus 	g_settings_lock.Lock();
6799949213aSStephan Aßmus 	(void)ioExtension->RemoveName(name);
6809949213aSStephan Aßmus 	err = ioExtension->AddInt32(name, g_settings.out_space);
6819949213aSStephan Aßmus 	g_settings_lock.Unlock();
6829949213aSStephan Aßmus 	return err;
6839949213aSStephan Aßmus }
6849949213aSStephan Aßmus 
6859949213aSStephan Aßmus 
6869949213aSStephan Aßmus 
6879949213aSStephan Aßmus 
6889949213aSStephan Aßmus 
6899949213aSStephan Aßmus status_t
6909949213aSStephan Aßmus read_ppm_header(
6919949213aSStephan Aßmus 	BDataIO * inSource,
6929949213aSStephan Aßmus 	int * width,
6939949213aSStephan Aßmus 	int * rowbytes,
6949949213aSStephan Aßmus 	int * height,
6959949213aSStephan Aßmus 	int * max,
6969949213aSStephan Aßmus 	bool * ascii,
6979949213aSStephan Aßmus 	color_space * space,
6989949213aSStephan Aßmus 	bool * is_ppm,
6999949213aSStephan Aßmus 	char ** comment)
7009949213aSStephan Aßmus {
7019949213aSStephan Aßmus 	/* check for PPM magic number */
7029949213aSStephan Aßmus 	char ch[2];
7039949213aSStephan Aßmus 	if (inSource->Read(ch, 2) != 2) {
7049949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
7059949213aSStephan Aßmus 	}
7069949213aSStephan Aßmus 	/* look for magic number */
7079949213aSStephan Aßmus 	if (ch[0] != 'P') {
7089949213aSStephan Aßmus 		/* B_TRANSLATOR_BITMAP magic? */
7099949213aSStephan Aßmus 		if (ch[0] == 'b' && ch[1] == 'i') {
7109949213aSStephan Aßmus 			*is_ppm = false;
7119949213aSStephan Aßmus 			return read_bits_header(inSource, 2, width, rowbytes, height, max, ascii, space);
7129949213aSStephan Aßmus 		}
7139949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
7149949213aSStephan Aßmus 	}
7159949213aSStephan Aßmus 	*is_ppm = true;
7169949213aSStephan Aßmus 	if (ch[1] == '6') {
7179949213aSStephan Aßmus 		*ascii = false;
7189949213aSStephan Aßmus 	}
7199949213aSStephan Aßmus 	else if (ch[1] == '3') {
7209949213aSStephan Aßmus 		*ascii = true;
7219949213aSStephan Aßmus 	}
7229949213aSStephan Aßmus 	else {
7239949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
7249949213aSStephan Aßmus 	}
7259949213aSStephan Aßmus 	// status_t err = B_NO_TRANSLATOR;
7269949213aSStephan Aßmus 	enum scan_state {
7279949213aSStephan Aßmus 		scan_width,
7289949213aSStephan Aßmus 		scan_height,
7299949213aSStephan Aßmus 		scan_max,
7309949213aSStephan Aßmus 		scan_white
7319949213aSStephan Aßmus 	} state = scan_width;
7329949213aSStephan Aßmus 	int * scan = NULL;
7339949213aSStephan Aßmus 	bool in_comment = false;
7349949213aSStephan Aßmus 	*space = B_RGB24_BIG;
7359949213aSStephan Aßmus 	/* The description of PPM is slightly ambiguous as far as comments */
7369949213aSStephan Aßmus 	/* go. We choose to allow comments anywhere, in the spirit of laxness. */
7379949213aSStephan Aßmus 	/* See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/PPM.txt */
7389949213aSStephan Aßmus 	int comlen = 0;
7399949213aSStephan Aßmus 	int comptr = 0;
7409949213aSStephan Aßmus 	while (inSource->Read(ch, 1) == 1) {
7419949213aSStephan Aßmus 		if (in_comment && (ch[0] != 10) && (ch[0] != 13)) {
7429949213aSStephan Aßmus 			if (comment) {	/* collect comment(s) into comment string */
7439949213aSStephan Aßmus 				if (comptr >= comlen-1) {
7449949213aSStephan Aßmus 					char * n = (char *)realloc(*comment, comlen+100);
7459949213aSStephan Aßmus 					if (!n) {
7469949213aSStephan Aßmus 						free(*comment);
7479949213aSStephan Aßmus 						*comment = NULL;
7489949213aSStephan Aßmus 					}
7499949213aSStephan Aßmus 					*comment = n;
7509949213aSStephan Aßmus 					comlen += 100;
7519949213aSStephan Aßmus 				}
7529949213aSStephan Aßmus 				(*comment)[comptr++] = ch[0];
7539949213aSStephan Aßmus 				(*comment)[comptr] = 0;
7549949213aSStephan Aßmus 			}
7559949213aSStephan Aßmus 			continue;
7569949213aSStephan Aßmus 		}
7579949213aSStephan Aßmus 		in_comment = false;
7589949213aSStephan Aßmus 		if (ch[0] == '#') {
7599949213aSStephan Aßmus 			in_comment = true;
7609949213aSStephan Aßmus 			continue;
7619949213aSStephan Aßmus 		}
7629949213aSStephan Aßmus 		/* once we're done with whitespace after max, we're done with header */
7639949213aSStephan Aßmus 		if (isdigit(ch[0])) {
7649949213aSStephan Aßmus 			if (!scan) {	/* first digit for this value */
7659949213aSStephan Aßmus 				switch(state) {
7669949213aSStephan Aßmus 				case scan_width:
7679949213aSStephan Aßmus 					scan = width;
7689949213aSStephan Aßmus 					break;
7699949213aSStephan Aßmus 				case scan_height:
7709949213aSStephan Aßmus 					*rowbytes = *width*3;
7719949213aSStephan Aßmus 					scan = height;
7729949213aSStephan Aßmus 					break;
7739949213aSStephan Aßmus 				case scan_max:
7749949213aSStephan Aßmus 					scan = max;
7759949213aSStephan Aßmus 					break;
7769949213aSStephan Aßmus 				default:
7779949213aSStephan Aßmus 					return B_OK;	/* done with header, all OK */
7789949213aSStephan Aßmus 				}
7799949213aSStephan Aßmus 				*scan = 0;
7809949213aSStephan Aßmus 			}
7819949213aSStephan Aßmus 			*scan = (*scan)*10 + (ch[0]-'0');
7829949213aSStephan Aßmus 		}
7839949213aSStephan Aßmus 		else if (isspace(ch[0])) {
7849949213aSStephan Aßmus 			if (scan) {	/* are we done with one value? */
7859949213aSStephan Aßmus 				scan = NULL;
7869949213aSStephan Aßmus 				state = (enum scan_state)(state+1);
7879949213aSStephan Aßmus 			}
7889949213aSStephan Aßmus 			if (state == scan_white) {	/* we only ever read one whitespace, since we skip space */
7899949213aSStephan Aßmus 				return B_OK;	/* when reading ASCII, and there is a single whitespace after max in raw mode */
7909949213aSStephan Aßmus 			}
7919949213aSStephan Aßmus 		}
7929949213aSStephan Aßmus 		else {
7939949213aSStephan Aßmus 			if (state != scan_white) {
7949949213aSStephan Aßmus 				return B_NO_TRANSLATOR;
7959949213aSStephan Aßmus 			}
7969949213aSStephan Aßmus 			return B_OK;	/* header done */
7979949213aSStephan Aßmus 		}
7989949213aSStephan Aßmus 	}
7999949213aSStephan Aßmus 	return B_NO_TRANSLATOR;
8009949213aSStephan Aßmus }
8019949213aSStephan Aßmus 
8029949213aSStephan Aßmus 
8039949213aSStephan Aßmus 
8049949213aSStephan Aßmus status_t
8059949213aSStephan Aßmus read_bits_header(
8069949213aSStephan Aßmus 	BDataIO * io,
8079949213aSStephan Aßmus 	int skipped,
8089949213aSStephan Aßmus 	int * width,
8099949213aSStephan Aßmus 	int * rowbytes,
8109949213aSStephan Aßmus 	int * height,
8119949213aSStephan Aßmus 	int * max,
8129949213aSStephan Aßmus 	bool * ascii,
8139949213aSStephan Aßmus 	color_space * space)
8149949213aSStephan Aßmus {
8159949213aSStephan Aßmus 	/* read the rest of a possible B_TRANSLATOR_BITMAP header */
8169949213aSStephan Aßmus 	if (skipped < 0 || skipped > 4) return B_NO_TRANSLATOR;
8179949213aSStephan Aßmus 	int rd = sizeof(TranslatorBitmap)-skipped;
8189949213aSStephan Aßmus 	TranslatorBitmap hdr;
8199949213aSStephan Aßmus 	/* pre-initialize magic because we might have skipped part of it already */
8209949213aSStephan Aßmus 	hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
8219949213aSStephan Aßmus 	char * ptr = (char *)&hdr;
8229949213aSStephan Aßmus 	if (io->Read(ptr+skipped, rd) != rd) {
8239949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8249949213aSStephan Aßmus 	}
8259949213aSStephan Aßmus 	/* swap header values */
8269949213aSStephan Aßmus 	hdr.magic = B_BENDIAN_TO_HOST_INT32(hdr.magic);
8279949213aSStephan Aßmus 	hdr.bounds.left = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.left);
8289949213aSStephan Aßmus 	hdr.bounds.right = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.right);
8299949213aSStephan Aßmus 	hdr.bounds.top = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.top);
8309949213aSStephan Aßmus 	hdr.bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.bottom);
8319949213aSStephan Aßmus 	hdr.rowBytes = B_BENDIAN_TO_HOST_INT32(hdr.rowBytes);
8329949213aSStephan Aßmus 	hdr.colors = (color_space)B_BENDIAN_TO_HOST_INT32(hdr.colors);
8339949213aSStephan Aßmus 	hdr.dataSize = B_BENDIAN_TO_HOST_INT32(hdr.dataSize);
8349949213aSStephan Aßmus 	/* sanity checking */
8359949213aSStephan Aßmus 	if (hdr.magic != B_TRANSLATOR_BITMAP) {
8369949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8379949213aSStephan Aßmus 	}
8389949213aSStephan Aßmus 	if (hdr.colors & 0xffff0000) {	/* according to <GraphicsDefs.h> this is a reasonable check. */
8399949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8409949213aSStephan Aßmus 	}
8419949213aSStephan Aßmus 	if (hdr.rowBytes * (hdr.bounds.Height()+1) > hdr.dataSize) {
8429949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8439949213aSStephan Aßmus 	}
8449949213aSStephan Aßmus 	/* return information about the data in the stream */
8459949213aSStephan Aßmus 	*width = (int)hdr.bounds.Width()+1;
8469949213aSStephan Aßmus 	*rowbytes = hdr.rowBytes;
8479949213aSStephan Aßmus 	*height = (int)hdr.bounds.Height()+1;
8489949213aSStephan Aßmus 	*max = 255;
8499949213aSStephan Aßmus 	*ascii = false;
8509949213aSStephan Aßmus 	*space = hdr.colors;
8519949213aSStephan Aßmus 	return B_OK;
8529949213aSStephan Aßmus }
8539949213aSStephan Aßmus 
8549949213aSStephan Aßmus 
8559949213aSStephan Aßmus /*	String may contain newlines, after which we need to insert extra hash signs. */
8569949213aSStephan Aßmus status_t
8579949213aSStephan Aßmus write_comment(
8589949213aSStephan Aßmus 	const char * str,
8599949213aSStephan Aßmus 	BDataIO * io)
8609949213aSStephan Aßmus {
8619949213aSStephan Aßmus 	const char * end = str+strlen(str);
8629949213aSStephan Aßmus 	const char * ptr = str;
8639949213aSStephan Aßmus 	status_t err = B_OK;
8649949213aSStephan Aßmus 	/* write each line as it's found */
8659949213aSStephan Aßmus 	while ((ptr < end) && !err) {
8669949213aSStephan Aßmus 		if ((*ptr == 10) || (*ptr == 13)) {
8679949213aSStephan Aßmus 			err = io->Write("# ", 2);
8689949213aSStephan Aßmus 			if (err == 2) {
8699949213aSStephan Aßmus 				err = io->Write(str, ptr-str);
8709949213aSStephan Aßmus 				if (err == ptr-str) {
8719949213aSStephan Aßmus 					if (io->Write("\n", 1) == 1) {
8729949213aSStephan Aßmus 						err = 0;
8739949213aSStephan Aßmus 					}
8749949213aSStephan Aßmus 				}
8759949213aSStephan Aßmus 			}
8769949213aSStephan Aßmus 			str = ptr+1;
8779949213aSStephan Aßmus 		}
8789949213aSStephan Aßmus 		ptr++;
8799949213aSStephan Aßmus 	}
8809949213aSStephan Aßmus 	/* write the last data, if any, as a line */
8819949213aSStephan Aßmus 	if (ptr > str) {
8829949213aSStephan Aßmus 		err = io->Write("# ", 2);
8839949213aSStephan Aßmus 		if (err == 2) {
8849949213aSStephan Aßmus 			err = io->Write(str, ptr-str);
8859949213aSStephan Aßmus 			if (err == ptr-str) {
8869949213aSStephan Aßmus 				if (io->Write("\n", 1) == 1) {
8879949213aSStephan Aßmus 					err = 0;
8889949213aSStephan Aßmus 				}
8899949213aSStephan Aßmus 			}
8909949213aSStephan Aßmus 		}
8919949213aSStephan Aßmus 	}
8929949213aSStephan Aßmus 	if (err > 0) {
8939949213aSStephan Aßmus 		err = B_IO_ERROR;
8949949213aSStephan Aßmus 	}
8959949213aSStephan Aßmus 	return err;
8969949213aSStephan Aßmus }
8979949213aSStephan Aßmus 
8989949213aSStephan Aßmus 
8999949213aSStephan Aßmus static status_t
9009949213aSStephan Aßmus read_ascii_line(
9019949213aSStephan Aßmus 	BDataIO * in,
9029949213aSStephan Aßmus 	int max,
9039949213aSStephan Aßmus 	unsigned char * data,
9049949213aSStephan Aßmus 	int rowbytes)
9059949213aSStephan Aßmus {
9069949213aSStephan Aßmus 	char ch;
9079949213aSStephan Aßmus 	status_t err;
9089949213aSStephan Aßmus 	// int nread = 0;
9099949213aSStephan Aßmus 	bool comment = false;
9109949213aSStephan Aßmus 	int val = 0;
9119949213aSStephan Aßmus 	bool dig = false;
9129949213aSStephan Aßmus 	while ((err = in->Read(&ch, 1)) == 1) {
9139949213aSStephan Aßmus 		if (comment) {
9149949213aSStephan Aßmus 			if ((ch == '\n') || (ch == '\r')) {
9159949213aSStephan Aßmus 				comment = false;
9169949213aSStephan Aßmus 			}
9179949213aSStephan Aßmus 		}
9189949213aSStephan Aßmus 		if (isdigit(ch)) {
9199949213aSStephan Aßmus 			dig = true;
9209949213aSStephan Aßmus 			val = val * 10 + (ch - '0');
9219949213aSStephan Aßmus 		}
9229949213aSStephan Aßmus 		else {
9239949213aSStephan Aßmus 			if (dig) {
9249949213aSStephan Aßmus 				*(data++) = val*255/max;
9259949213aSStephan Aßmus 				val = 0;
9269949213aSStephan Aßmus 				rowbytes--;
9279949213aSStephan Aßmus 				dig = false;
9289949213aSStephan Aßmus 			}
9299949213aSStephan Aßmus 			if (ch == '#') {
9309949213aSStephan Aßmus 				comment = true;
9319949213aSStephan Aßmus 				continue;
9329949213aSStephan Aßmus 			}
9339949213aSStephan Aßmus 		}
9349949213aSStephan Aßmus 		if (rowbytes < 1) {
9359949213aSStephan Aßmus 			break;
9369949213aSStephan Aßmus 		}
9379949213aSStephan Aßmus 	}
9389949213aSStephan Aßmus 	if (dig) {
9399949213aSStephan Aßmus 		*(data++) = val*255/max;
9409949213aSStephan Aßmus 		val = 0;
9419949213aSStephan Aßmus 		rowbytes--;
9429949213aSStephan Aßmus 		dig = false;
9439949213aSStephan Aßmus 	}
9449949213aSStephan Aßmus 	if (rowbytes < 1) {
9459949213aSStephan Aßmus 		return B_OK;
9469949213aSStephan Aßmus 	}
9479949213aSStephan Aßmus 	return B_IO_ERROR;
9489949213aSStephan Aßmus }
9499949213aSStephan Aßmus 
9509949213aSStephan Aßmus 
9519949213aSStephan Aßmus static status_t
9529949213aSStephan Aßmus write_ascii_line(
9539949213aSStephan Aßmus 	BDataIO * out,
9549949213aSStephan Aßmus 	unsigned char * data,
9559949213aSStephan Aßmus 	int rowbytes)
9569949213aSStephan Aßmus {
9579949213aSStephan Aßmus 	char buffer[20];
9589949213aSStephan Aßmus 	int linelen = 0;
9599949213aSStephan Aßmus 	while (rowbytes > 2) {
9609949213aSStephan Aßmus 		sprintf(buffer, "%d %d %d ", data[0], data[1], data[2]);
9619949213aSStephan Aßmus 		rowbytes -= 3;
9629949213aSStephan Aßmus 		int l = strlen(buffer);
9639949213aSStephan Aßmus 		if (l + linelen > 70) {
9649949213aSStephan Aßmus 			out->Write("\n", 1);
9659949213aSStephan Aßmus 			linelen = 0;
9669949213aSStephan Aßmus 		}
9679949213aSStephan Aßmus 		if (out->Write(buffer, l) != l) {
9689949213aSStephan Aßmus 			return B_IO_ERROR;
9699949213aSStephan Aßmus 		}
9709949213aSStephan Aßmus 		linelen += l;
9719949213aSStephan Aßmus 		data += 3;
9729949213aSStephan Aßmus 	}
9739949213aSStephan Aßmus 	out->Write("\n", 1);	/* terminate each scanline */
9749949213aSStephan Aßmus 	return B_OK;
9759949213aSStephan Aßmus }
9769949213aSStephan Aßmus 
9779949213aSStephan Aßmus 
9789949213aSStephan Aßmus static unsigned char *
9799949213aSStephan Aßmus make_scale_data(
9809949213aSStephan Aßmus 	int max)
9819949213aSStephan Aßmus {
9829949213aSStephan Aßmus 	unsigned char * ptr = (unsigned char *)malloc(max);
9839949213aSStephan Aßmus 	for (int ix=0; ix<max; ix++) {
9849949213aSStephan Aßmus 		ptr[ix] = ix*255/max;
9859949213aSStephan Aßmus 	}
9869949213aSStephan Aßmus 	return ptr;
9879949213aSStephan Aßmus }
9889949213aSStephan Aßmus 
9899949213aSStephan Aßmus 
9909949213aSStephan Aßmus static void
9919949213aSStephan Aßmus scale_data(
9929949213aSStephan Aßmus 	unsigned char * scale,
9939949213aSStephan Aßmus 	unsigned char * data,
9949949213aSStephan Aßmus 	int bytes)
9959949213aSStephan Aßmus {
9969949213aSStephan Aßmus 	for (int ix=0; ix<bytes; ix++) {
9979949213aSStephan Aßmus 		data[ix] = scale[data[ix]];
9989949213aSStephan Aßmus 	}
9999949213aSStephan Aßmus }
10009949213aSStephan Aßmus 
10019949213aSStephan Aßmus 
10029949213aSStephan Aßmus status_t
10039949213aSStephan Aßmus copy_data(
10049949213aSStephan Aßmus 	BDataIO * in,
10059949213aSStephan Aßmus 	BDataIO * out,
10069949213aSStephan Aßmus 	int rowbytes,
10079949213aSStephan Aßmus 	int out_rowbytes,
10089949213aSStephan Aßmus 	int height,
10099949213aSStephan Aßmus 	int max,
10109949213aSStephan Aßmus 	bool in_ascii,
10119949213aSStephan Aßmus 	bool out_ascii,
10129949213aSStephan Aßmus 	color_space in_space,
10139949213aSStephan Aßmus 	color_space out_space)
10149949213aSStephan Aßmus {
10159949213aSStephan Aßmus 	dprintf(("copy_data(%x, %x, %x, %x, %x, %x)\n", rowbytes, out_rowbytes, height, max, in_space, out_space));
10169949213aSStephan Aßmus 	/*	We read/write one scanline at a time.	*/
10179949213aSStephan Aßmus 	unsigned char * data = (unsigned char *)malloc(rowbytes);
10189949213aSStephan Aßmus 	unsigned char * out_data = (unsigned char *)malloc(out_rowbytes);
10199949213aSStephan Aßmus 	if (data == NULL || out_data == NULL) {
10209949213aSStephan Aßmus 		free(data);
10219949213aSStephan Aßmus 		free(out_data);
10229949213aSStephan Aßmus 		return B_NO_MEMORY;
10239949213aSStephan Aßmus 	}
10249949213aSStephan Aßmus 	unsigned char * scale = NULL;
10259949213aSStephan Aßmus 	if (max != 255) {
10269949213aSStephan Aßmus 		scale = make_scale_data(max);
10279949213aSStephan Aßmus 	}
10289949213aSStephan Aßmus 	status_t err = B_OK;
10299949213aSStephan Aßmus 	/*	There is no data format conversion, so we can just copy data.	*/
10309949213aSStephan Aßmus 	while ((height-- > 0) && !err) {
10319949213aSStephan Aßmus 		if (in_ascii) {
10329949213aSStephan Aßmus 			err = read_ascii_line(in, max, data, rowbytes);
10339949213aSStephan Aßmus 		}
10349949213aSStephan Aßmus 		else {
10359949213aSStephan Aßmus 			err = in->Read(data, rowbytes);
10369949213aSStephan Aßmus 			if (err == rowbytes) {
10379949213aSStephan Aßmus 				err = B_OK;
10389949213aSStephan Aßmus 			}
10399949213aSStephan Aßmus 			if (scale) {	/* for reading PPM that is smaller than 8 bit */
10409949213aSStephan Aßmus 				scale_data(scale, data, rowbytes);
10419949213aSStephan Aßmus 			}
10429949213aSStephan Aßmus 		}
10439949213aSStephan Aßmus 		if (err == B_OK) {
10449949213aSStephan Aßmus 			unsigned char * wbuf = data;
10459949213aSStephan Aßmus 			if (in_space != out_space) {
10469949213aSStephan Aßmus 				err = convert_space(in_space, out_space, data, rowbytes, out_data);
10479949213aSStephan Aßmus 				wbuf = out_data;
10489949213aSStephan Aßmus 			}
10499949213aSStephan Aßmus 			if (!err && out_ascii) {
10509949213aSStephan Aßmus 				err = write_ascii_line(out, wbuf, out_rowbytes);
10519949213aSStephan Aßmus 			}
10529949213aSStephan Aßmus 			else if (!err) {
10539949213aSStephan Aßmus 				err = out->Write(wbuf, out_rowbytes);
10549949213aSStephan Aßmus 				if (err == out_rowbytes) {
10559949213aSStephan Aßmus 					err = B_OK;
10569949213aSStephan Aßmus 				}
10579949213aSStephan Aßmus 			}
10589949213aSStephan Aßmus 		}
10599949213aSStephan Aßmus 	}
10609949213aSStephan Aßmus 	free(data);
10619949213aSStephan Aßmus 	free(out_data);
10629949213aSStephan Aßmus 	free(scale);
10639949213aSStephan Aßmus 	return err > 0 ? B_IO_ERROR : err;
10649949213aSStephan Aßmus }
10659949213aSStephan Aßmus 
10669949213aSStephan Aßmus 
1067