xref: /haiku/src/add-ons/translators/ppm/PPMTranslator.cpp (revision 71aec297511d8693eb083236b4aa080342fb137e)
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 
89949213aSStephan Aßmus #include <Bitmap.h>
9*71aec297SAdrien Destugues #include <BufferedDataIO.h>
1070d59669SSiarzhuk Zharski #include <ByteOrder.h>
1170d59669SSiarzhuk Zharski #include <Catalog.h>
1270d59669SSiarzhuk Zharski #include <CheckBox.h>
1370d59669SSiarzhuk Zharski #include <FindDirectory.h>
147d48219bSHannah Boneß #include <LayoutBuilder.h>
1570d59669SSiarzhuk Zharski #include <Locker.h>
1670d59669SSiarzhuk Zharski #include <MenuField.h>
1770d59669SSiarzhuk Zharski #include <MenuItem.h>
1870d59669SSiarzhuk Zharski #include <Message.h>
1970d59669SSiarzhuk Zharski #include <Path.h>
2070d59669SSiarzhuk Zharski #include <PopUpMenu.h>
2170d59669SSiarzhuk Zharski #include <Screen.h>
2270d59669SSiarzhuk Zharski #include <StringView.h>
2370d59669SSiarzhuk Zharski #include <TranslationKit.h>
2472c5a125SAdrien Destugues #include <TranslatorAddOn.h>
259949213aSStephan Aßmus 
269949213aSStephan Aßmus #include <ctype.h>
279949213aSStephan Aßmus #include <stdio.h>
2872c5a125SAdrien Destugues #include <stdlib.h>
2972c5a125SAdrien Destugues #include <string.h>
3070d59669SSiarzhuk Zharski #include <syslog.h>
319949213aSStephan Aßmus 
329949213aSStephan Aßmus #include "colorspace.h"
339949213aSStephan Aßmus 
34546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
35546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "PPMTranslator"
369949213aSStephan Aßmus 
379949213aSStephan Aßmus #if DEBUG
389949213aSStephan Aßmus #define dprintf(x) printf x
399949213aSStephan Aßmus #else
409949213aSStephan Aßmus #define dprintf(x)
419949213aSStephan Aßmus #endif
429949213aSStephan Aßmus 
439949213aSStephan Aßmus 
449949213aSStephan Aßmus #define PPM_TRANSLATOR_VERSION 0x100
459949213aSStephan Aßmus 
469949213aSStephan Aßmus /* These three data items are exported by every translator. */
47fcc3e627SStephan Aßmus char translatorName[] = "PPM images";
489949213aSStephan Aßmus char translatorInfo[] = "PPM image translator v1.0.0, " __DATE__;
499949213aSStephan Aßmus int32 translatorVersion = PPM_TRANSLATOR_VERSION;
509949213aSStephan Aßmus // Revision: lowest 4 bits
519949213aSStephan Aßmus // Minor: next 4 bits
529949213aSStephan Aßmus // Major: highest 24 bits
539949213aSStephan Aßmus 
5470d59669SSiarzhuk Zharski /*	Be reserves all codes with non-lowercase letters in them.	*/
559949213aSStephan Aßmus /*	Luckily, there is already a reserved code for PPM. If you	*/
569949213aSStephan Aßmus /*	make up your own for a new type, use lower-case letters.	*/
579949213aSStephan Aßmus #define PPM_TYPE 'PPM '
589949213aSStephan Aßmus 
599949213aSStephan Aßmus 
6072c5a125SAdrien Destugues /* These two data arrays are a really good idea to export from Translators, but
6172c5a125SAdrien Destugues  * not required. */
6272c5a125SAdrien Destugues translation_format inputFormats[]
6372c5a125SAdrien Destugues 	= {{B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap",
6472c5a125SAdrien Destugues 		   "Be Bitmap Format (PPMTranslator)"},
6572c5a125SAdrien Destugues 		{PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap",
6672c5a125SAdrien Destugues 			"PPM image"},
6772c5a125SAdrien Destugues 		{0, 0, 0, 0, "\0",
6872c5a125SAdrien Destugues 			"\0"}}; /*	optional (else Identify is always called)	*/
699949213aSStephan Aßmus 
7072c5a125SAdrien Destugues translation_format outputFormats[]
7172c5a125SAdrien Destugues 	= {{B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap",
7272c5a125SAdrien Destugues 		   "Be Bitmap Format (PPMTranslator)"},
7372c5a125SAdrien Destugues 		{PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap",
7472c5a125SAdrien Destugues 			"PPM image"},
7572c5a125SAdrien Destugues 		{0, 0, 0, 0, "\0",
7672c5a125SAdrien Destugues 			"\0"}}; /*	optional (else Translate is called anyway)	*/
779949213aSStephan Aßmus 
789949213aSStephan Aßmus /*	Translators that don't export outputFormats 	*/
799949213aSStephan Aßmus /*	will not be considered by files looking for 	*/
809949213aSStephan Aßmus /*	specific output formats.	*/
819949213aSStephan Aßmus 
829949213aSStephan Aßmus 
839949213aSStephan Aßmus /*	We keep our settings in a global struct, and wrap a lock around them.	*/
849949213aSStephan Aßmus struct ppm_settings {
859949213aSStephan Aßmus 	color_space out_space;
869949213aSStephan Aßmus 	BPoint window_pos;
879949213aSStephan Aßmus 	bool write_ascii;
889949213aSStephan Aßmus 	bool settings_touched;
899949213aSStephan Aßmus };
90bf243977SPhilippe Houdoin static BLocker g_settings_lock("PPM settings lock");
91bf243977SPhilippe Houdoin static ppm_settings g_settings;
929949213aSStephan Aßmus 
939949213aSStephan Aßmus BPoint get_window_origin();
949949213aSStephan Aßmus void set_window_origin(BPoint pos);
9572c5a125SAdrien Destugues BPoint
9672c5a125SAdrien Destugues get_window_origin()
979949213aSStephan Aßmus {
989949213aSStephan Aßmus 	BPoint ret;
999949213aSStephan Aßmus 	g_settings_lock.Lock();
1009949213aSStephan Aßmus 	ret = g_settings.window_pos;
1019949213aSStephan Aßmus 	g_settings_lock.Unlock();
1029949213aSStephan Aßmus 	return ret;
1039949213aSStephan Aßmus }
1049949213aSStephan Aßmus 
10572c5a125SAdrien Destugues void
10672c5a125SAdrien Destugues set_window_origin(BPoint pos)
1079949213aSStephan Aßmus {
1089949213aSStephan Aßmus 	g_settings_lock.Lock();
1099949213aSStephan Aßmus 	g_settings.window_pos = pos;
1109949213aSStephan Aßmus 	g_settings.settings_touched = true;
1119949213aSStephan Aßmus 	g_settings_lock.Unlock();
1129949213aSStephan Aßmus }
1139949213aSStephan Aßmus 
1149949213aSStephan Aßmus 
11572c5a125SAdrien Destugues class PrefsLoader
11672c5a125SAdrien Destugues {
1179949213aSStephan Aßmus public:
11872c5a125SAdrien Destugues 	PrefsLoader(const char* str)
1199949213aSStephan Aßmus 	{
1209949213aSStephan Aßmus 		dprintf(("PPMTranslator: PrefsLoader()\n"));
1219949213aSStephan Aßmus 		/* defaults */
1229949213aSStephan Aßmus 		g_settings.out_space = B_NO_COLOR_SPACE;
1239949213aSStephan Aßmus 		g_settings.window_pos = B_ORIGIN;
1249949213aSStephan Aßmus 		g_settings.write_ascii = false;
1259949213aSStephan Aßmus 		g_settings.settings_touched = false;
1269949213aSStephan Aßmus 		BPath path;
1279949213aSStephan Aßmus 		if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) {
1289949213aSStephan Aßmus 			path.SetTo("/tmp");
1299949213aSStephan Aßmus 		}
1309949213aSStephan Aßmus 		path.Append(str);
1319949213aSStephan Aßmus 		FILE* f = fopen(path.Path(), "r");
1329949213aSStephan Aßmus 		/*	parse text settings file -- this should be a library...	*/
1339949213aSStephan Aßmus 		if (f) {
1349949213aSStephan Aßmus 			char line[1024];
1359949213aSStephan Aßmus 			char name[32];
1369949213aSStephan Aßmus 			char* ptr;
1379949213aSStephan Aßmus 			while (true) {
1389949213aSStephan Aßmus 				line[0] = 0;
1399949213aSStephan Aßmus 				fgets(line, 1024, f);
1409949213aSStephan Aßmus 				if (!line[0]) {
1419949213aSStephan Aßmus 					break;
1429949213aSStephan Aßmus 				}
1439949213aSStephan Aßmus 				/* remember: line ends with \n, so printf()s don't have to */
1449949213aSStephan Aßmus 				ptr = line;
1459949213aSStephan Aßmus 				while (isspace(*ptr)) {
1469949213aSStephan Aßmus 					ptr++;
1479949213aSStephan Aßmus 				}
1489949213aSStephan Aßmus 				if (*ptr == '#' || !*ptr) { /* comment or blank */
1499949213aSStephan Aßmus 					continue;
1509949213aSStephan Aßmus 				}
1519949213aSStephan Aßmus 				if (sscanf(ptr, "%31[a-zA-Z_0-9] =", name) != 1) {
15272c5a125SAdrien Destugues 					syslog(LOG_ERR,
15372c5a125SAdrien Destugues 						"unknown PPMTranslator "
15472c5a125SAdrien Destugues 						"settings line: %s",
15572c5a125SAdrien Destugues 						line);
15672c5a125SAdrien Destugues 				} else {
1579949213aSStephan Aßmus 					if (!strcmp(name, "color_space")) {
1589949213aSStephan Aßmus 						while (*ptr != '=') {
1599949213aSStephan Aßmus 							ptr++;
1609949213aSStephan Aßmus 						}
1619949213aSStephan Aßmus 						ptr++;
16272c5a125SAdrien Destugues 						if (sscanf(ptr, "%d", (int*) &g_settings.out_space)
16372c5a125SAdrien Destugues 							!= 1) {
16472c5a125SAdrien Destugues 							syslog(LOG_ERR,
16572c5a125SAdrien Destugues 								"illegal color space "
16672c5a125SAdrien Destugues 								"in PPMTranslator settings: %s",
16772c5a125SAdrien Destugues 								ptr);
1689949213aSStephan Aßmus 						}
16972c5a125SAdrien Destugues 					} else if (!strcmp(name, "window_pos")) {
1709949213aSStephan Aßmus 						while (*ptr != '=') {
1719949213aSStephan Aßmus 							ptr++;
1729949213aSStephan Aßmus 						}
1739949213aSStephan Aßmus 						ptr++;
17472c5a125SAdrien Destugues 						if (sscanf(ptr, "%f,%f", &g_settings.window_pos.x,
17572c5a125SAdrien Destugues 								&g_settings.window_pos.y)
17672c5a125SAdrien Destugues 							!= 2) {
17772c5a125SAdrien Destugues 							syslog(LOG_ERR,
17872c5a125SAdrien Destugues 								"illegal window position "
17972c5a125SAdrien Destugues 								"in PPMTranslator settings: %s",
18072c5a125SAdrien Destugues 								ptr);
1819949213aSStephan Aßmus 						}
18272c5a125SAdrien Destugues 					} else if (!strcmp(name, "write_ascii")) {
1839949213aSStephan Aßmus 						while (*ptr != '=') {
1849949213aSStephan Aßmus 							ptr++;
1859949213aSStephan Aßmus 						}
1869949213aSStephan Aßmus 						ptr++;
1879949213aSStephan Aßmus 						int ascii = g_settings.write_ascii;
1889949213aSStephan Aßmus 						if (sscanf(ptr, "%d", &ascii) != 1) {
18972c5a125SAdrien Destugues 							syslog(LOG_ERR,
19072c5a125SAdrien Destugues 								"illegal write_ascii value "
19172c5a125SAdrien Destugues 								"in PPMTranslator settings: %s",
19272c5a125SAdrien Destugues 								ptr);
19372c5a125SAdrien Destugues 						} else {
1949949213aSStephan Aßmus 							g_settings.write_ascii = ascii;
1959949213aSStephan Aßmus 						}
19672c5a125SAdrien Destugues 					} else {
19772c5a125SAdrien Destugues 						syslog(
19872c5a125SAdrien Destugues 							LOG_ERR, "unknown PPMTranslator setting: %s", line);
1999949213aSStephan Aßmus 					}
2009949213aSStephan Aßmus 				}
2019949213aSStephan Aßmus 			}
2029949213aSStephan Aßmus 			fclose(f);
2039949213aSStephan Aßmus 		}
2049949213aSStephan Aßmus 	}
205*71aec297SAdrien Destugues 
2069949213aSStephan Aßmus 	~PrefsLoader()
2079949213aSStephan Aßmus 	{
2089949213aSStephan Aßmus 		/*	No need writing settings if there aren't any	*/
2099949213aSStephan Aßmus 		if (g_settings.settings_touched) {
2109949213aSStephan Aßmus 			BPath path;
2119949213aSStephan Aßmus 			if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) {
2129949213aSStephan Aßmus 				path.SetTo("/tmp");
2139949213aSStephan Aßmus 			}
2149949213aSStephan Aßmus 			path.Append("PPMTranslator_Settings");
2159949213aSStephan Aßmus 			FILE* f = fopen(path.Path(), "w");
2169949213aSStephan Aßmus 			if (f) {
2179949213aSStephan Aßmus 				fprintf(f, "# PPMTranslator settings version %d.%d.%d\n",
2189949213aSStephan Aßmus 					static_cast<int>(translatorVersion >> 8),
2199949213aSStephan Aßmus 					static_cast<int>((translatorVersion >> 4) & 0xf),
2209949213aSStephan Aßmus 					static_cast<int>(translatorVersion & 0xf));
2219949213aSStephan Aßmus 				fprintf(f, "color_space = %d\n", g_settings.out_space);
22270d59669SSiarzhuk Zharski 				fprintf(f, "window_pos = %g,%g\n", g_settings.window_pos.x,
22370d59669SSiarzhuk Zharski 					g_settings.window_pos.y);
22472c5a125SAdrien Destugues 				fprintf(
22572c5a125SAdrien Destugues 					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 
244*71aec297SAdrien Destugues 
2459949213aSStephan Aßmus /*	Return B_NO_TRANSLATOR if not handling this data.	*/
2469949213aSStephan Aßmus /*	Even if inputFormats exists, may be called for data without hints	*/
2479949213aSStephan Aßmus /*	If outType is not 0, must be able to export in wanted format	*/
2489949213aSStephan Aßmus status_t
249*71aec297SAdrien Destugues Identify(BPositionIO* inSource, const translation_format* inFormat,
250*71aec297SAdrien Destugues 	BMessage* ioExtension, translator_info* outInfo, uint32 outType)
2519949213aSStephan Aßmus {
2529949213aSStephan Aßmus 	dprintf(("PPMTranslator: Identify()\n"));
2539949213aSStephan Aßmus 	/* Silence compiler warnings. */
2549949213aSStephan Aßmus 	inFormat = inFormat;
2559949213aSStephan Aßmus 	ioExtension = ioExtension;
2569949213aSStephan Aßmus 
2579949213aSStephan Aßmus 	/* Check that requested format is something we can deal with. */
2589949213aSStephan Aßmus 	if (outType == 0) {
2599949213aSStephan Aßmus 		outType = B_TRANSLATOR_BITMAP;
2609949213aSStephan Aßmus 	}
2619949213aSStephan Aßmus 	if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) {
2629949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
2639949213aSStephan Aßmus 	}
2649949213aSStephan Aßmus 
2659949213aSStephan Aßmus 	/* Check header. */
2669949213aSStephan Aßmus 	int width, rowbytes, height, max;
2679949213aSStephan Aßmus 	bool ascii, is_ppm;
2689949213aSStephan Aßmus 	color_space space;
26972c5a125SAdrien Destugues 	status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max,
27072c5a125SAdrien Destugues 		&ascii, &space, &is_ppm, NULL);
2719949213aSStephan Aßmus 	if (err != B_OK) {
2729949213aSStephan Aßmus 		return err;
2739949213aSStephan Aßmus 	}
27472c5a125SAdrien Destugues 	/* Stuff info into info struct -- Translation Kit will do "translator" for
27572c5a125SAdrien Destugues 	 * us. */
2769949213aSStephan Aßmus 	outInfo->group = B_TRANSLATOR_BITMAP;
2779949213aSStephan Aßmus 	if (is_ppm) {
2789949213aSStephan Aßmus 		outInfo->type = PPM_TYPE;
2799949213aSStephan Aßmus 		outInfo->quality = 0.3; /* no alpha, etc */
28072c5a125SAdrien Destugues 		outInfo->capability
28172c5a125SAdrien Destugues 			= 0.8; /* we're pretty good at PPM reading, though */
282aec33db1SPhilippe Saint-Pierre 		strlcpy(outInfo->name, B_TRANSLATE("PPM image"), sizeof(outInfo->name));
2839949213aSStephan Aßmus 		strcpy(outInfo->MIME, "image/x-portable-pixmap");
28472c5a125SAdrien Destugues 	} else {
2859949213aSStephan Aßmus 		outInfo->type = B_TRANSLATOR_BITMAP;
2869949213aSStephan Aßmus 		outInfo->quality = 0.4; /* B_TRANSLATOR_BITMAP can do alpha, at least */
28772c5a125SAdrien Destugues 		outInfo->capability
28872c5a125SAdrien Destugues 			= 0.8; /* and we might not know many variations thereof */
289aec33db1SPhilippe Saint-Pierre 		strlcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format (PPMTranslator)"),
2903927bd3cSPhilippe Saint-Pierre 			sizeof(outInfo->name));
29172c5a125SAdrien Destugues 		strcpy(outInfo->MIME, "image/x-be-bitmap"); /* this is the MIME type of
29272c5a125SAdrien Destugues 													   B_TRANSLATOR_BITMAP */
2939949213aSStephan Aßmus 	}
2949949213aSStephan Aßmus 	return B_OK;
2959949213aSStephan Aßmus }
2969949213aSStephan Aßmus 
2979949213aSStephan Aßmus 
2989949213aSStephan Aßmus /*	Return B_NO_TRANSLATOR if not handling the output format	*/
2999949213aSStephan Aßmus /*	If outputFormats exists, will only be called for those formats	*/
3009949213aSStephan Aßmus status_t
3015d57984bSAdrien Destugues Translate(BPositionIO* inSource, const translator_info* /*inInfo*/,
3025d57984bSAdrien Destugues 	BMessage* ioExtension, uint32 outType, BPositionIO* outDestination)
3039949213aSStephan Aßmus {
3049949213aSStephan Aßmus 	dprintf(("PPMTranslator: Translate()\n"));
3059949213aSStephan Aßmus 	inSource->Seek(0, SEEK_SET); /* paranoia */
3069949213aSStephan Aßmus 	//	inInfo = inInfo;	/* silence compiler warning */
3079949213aSStephan Aßmus 	/* Check what we're being asked to produce. */
3089949213aSStephan Aßmus 	if (!outType) {
3099949213aSStephan Aßmus 		outType = B_TRANSLATOR_BITMAP;
3109949213aSStephan Aßmus 	}
31172c5a125SAdrien Destugues 	dprintf(("PPMTranslator: outType is '%c%c%c%c'\n", char(outType >> 24),
31272c5a125SAdrien Destugues 		char(outType >> 16), char(outType >> 8), char(outType)));
3139949213aSStephan Aßmus 	if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) {
3149949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
3159949213aSStephan Aßmus 	}
3169949213aSStephan Aßmus 
3179949213aSStephan Aßmus 	/* Figure out what we've been given (again). */
3189949213aSStephan Aßmus 	int width, rowbytes, height, max;
3199949213aSStephan Aßmus 	bool ascii, is_ppm;
3209949213aSStephan Aßmus 	color_space space;
3219949213aSStephan Aßmus 	/* Read_ppm_header() will always return with stream at beginning of data */
3229949213aSStephan Aßmus 	/* for both B_TRANSLATOR_BITMAP and PPM_TYPE, and indicate what it is. */
3239949213aSStephan Aßmus 	char* comment = NULL;
32472c5a125SAdrien Destugues 	status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max,
32572c5a125SAdrien Destugues 		&ascii, &space, &is_ppm, &comment);
3269949213aSStephan Aßmus 	if (comment != NULL) {
3279949213aSStephan Aßmus 		if (ioExtension != NULL) {
3289949213aSStephan Aßmus 			ioExtension->AddString(B_TRANSLATOR_EXT_COMMENT, comment);
3299949213aSStephan Aßmus 		}
3309949213aSStephan Aßmus 		free(comment);
3319949213aSStephan Aßmus 	}
3329949213aSStephan Aßmus 	if (err < B_OK) {
3331a7bcf69SOliver Tappe 		dprintf(("read_ppm_header() error %s [%" B_PRIx32 "]\n", strerror(err),
3341a7bcf69SOliver Tappe 			err));
3359949213aSStephan Aßmus 		return err;
3369949213aSStephan Aßmus 	}
3379949213aSStephan Aßmus 	/* Check if we're configured to write ASCII format file. */
3389949213aSStephan Aßmus 	bool out_ascii = false;
3399949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3409949213aSStephan Aßmus 		out_ascii = g_settings.write_ascii;
3419949213aSStephan Aßmus 		if (ioExtension != NULL) {
3429949213aSStephan Aßmus 			ioExtension->FindBool("ppm /ascii", &out_ascii);
3439949213aSStephan Aßmus 		}
3449949213aSStephan Aßmus 	}
3459949213aSStephan Aßmus 	err = B_OK;
3469949213aSStephan Aßmus 	/* Figure out which color space to convert to */
3479949213aSStephan Aßmus 	color_space out_space;
3489949213aSStephan Aßmus 	int out_rowbytes;
3499949213aSStephan Aßmus 	g_settings_lock.Lock();
3509949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3519949213aSStephan Aßmus 		out_space = B_RGB24_BIG;
3529949213aSStephan Aßmus 		out_rowbytes = 3 * width;
35372c5a125SAdrien Destugues 	} else { /*	When outputting to B_TRANSLATOR_BITMAP, follow user's wishes.
35472c5a125SAdrien Destugues 			  */
35572c5a125SAdrien Destugues 		if (!ioExtension
3565d57984bSAdrien Destugues 			|| ioExtension->FindInt32( B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE,
3575d57984bSAdrien Destugues 				(int32*) &out_space)
3585d57984bSAdrien Destugues 			|| (out_space == B_NO_COLOR_SPACE)) {
3599949213aSStephan Aßmus 			if (g_settings.out_space == B_NO_COLOR_SPACE) {
36072c5a125SAdrien Destugues 				switch (space) { /*	The 24-bit versions are pretty silly, use 32
36172c5a125SAdrien Destugues 									instead.	*/
3629949213aSStephan Aßmus 					case B_RGB24:
3639949213aSStephan Aßmus 					case B_RGB24_BIG:
3649949213aSStephan Aßmus 						out_space = B_RGB32;
3659949213aSStephan Aßmus 						break;
3669949213aSStephan Aßmus 					default:
3679949213aSStephan Aßmus 						/* use whatever is there */
3689949213aSStephan Aßmus 						out_space = space;
3699949213aSStephan Aßmus 						break;
3709949213aSStephan Aßmus 				}
37172c5a125SAdrien Destugues 			} else {
3729949213aSStephan Aßmus 				out_space = g_settings.out_space;
3739949213aSStephan Aßmus 			}
3749949213aSStephan Aßmus 		}
3759949213aSStephan Aßmus 		out_rowbytes = calc_rowbytes(out_space, width);
3769949213aSStephan Aßmus 	}
3779949213aSStephan Aßmus 	g_settings_lock.Unlock();
3789949213aSStephan Aßmus 	/* Write file header */
3799949213aSStephan Aßmus 	if (outType == PPM_TYPE) {
3809949213aSStephan Aßmus 		dprintf(("PPMTranslator: write PPM\n"));
3819949213aSStephan Aßmus 		/* begin PPM header */
3829949213aSStephan Aßmus 		const char* magic;
3839949213aSStephan Aßmus 		if (out_ascii)
3849949213aSStephan Aßmus 			magic = "P3\n";
3859949213aSStephan Aßmus 		else
3869949213aSStephan Aßmus 			magic = "P6\n";
3879949213aSStephan Aßmus 		err = outDestination->Write(magic, strlen(magic));
38872c5a125SAdrien Destugues 		if (err == (long) strlen(magic))
38972c5a125SAdrien Destugues 			err = 0;
3909949213aSStephan Aßmus 		// comment = NULL;
3919949213aSStephan Aßmus 		const char* fsComment;
39272c5a125SAdrien Destugues 		if ((ioExtension != NULL)
39372c5a125SAdrien Destugues 			&& !ioExtension->FindString(B_TRANSLATOR_EXT_COMMENT, &fsComment)) {
3949949213aSStephan Aßmus 			err = write_comment(fsComment, outDestination);
3959949213aSStephan Aßmus 		}
3969949213aSStephan Aßmus 		if (err == B_OK) {
3979949213aSStephan Aßmus 			char data[40];
3989949213aSStephan Aßmus 			sprintf(data, "%d %d %d\n", width, height, max);
3999949213aSStephan Aßmus 			err = outDestination->Write(data, strlen(data));
40072c5a125SAdrien Destugues 			if (err == (long) strlen(data))
40172c5a125SAdrien Destugues 				err = 0;
4029949213aSStephan Aßmus 		}
4039949213aSStephan Aßmus 		/* header done */
40472c5a125SAdrien Destugues 	} else {
4059949213aSStephan Aßmus 		dprintf(("PPMTranslator: write B_TRANSLATOR_BITMAP\n"));
4069949213aSStephan Aßmus 		/* B_TRANSLATOR_BITMAP header */
4079949213aSStephan Aßmus 		TranslatorBitmap hdr;
4089949213aSStephan Aßmus 		hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
4099949213aSStephan Aßmus 		hdr.bounds.left = B_HOST_TO_BENDIAN_FLOAT(0);
4109949213aSStephan Aßmus 		hdr.bounds.top = B_HOST_TO_BENDIAN_FLOAT(0);
4119949213aSStephan Aßmus 		hdr.bounds.right = B_HOST_TO_BENDIAN_FLOAT(width - 1);
4129949213aSStephan Aßmus 		hdr.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(height - 1);
4139949213aSStephan Aßmus 		hdr.rowBytes = B_HOST_TO_BENDIAN_INT32(out_rowbytes);
4149949213aSStephan Aßmus 		hdr.colors = (color_space) B_HOST_TO_BENDIAN_INT32(out_space);
4159949213aSStephan Aßmus 		hdr.dataSize = B_HOST_TO_BENDIAN_INT32(out_rowbytes * height);
41672c5a125SAdrien Destugues 		dprintf(("rowBytes is %d, width %d, out_space %x, space %x\n",
41772c5a125SAdrien Destugues 			out_rowbytes, width, out_space, space));
4189949213aSStephan Aßmus 		err = outDestination->Write(&hdr, sizeof(hdr));
4191a7bcf69SOliver Tappe 		dprintf(("PPMTranslator: Write() returns %" B_PRIx32 "\n", err));
4209949213aSStephan Aßmus #if DEBUG
4219949213aSStephan Aßmus 		{
42272c5a125SAdrien Destugues 			BBitmap* map
42372c5a125SAdrien Destugues 				= new BBitmap(BRect(0, 0, width - 1, height - 1), out_space);
4241a7bcf69SOliver Tappe 			printf("map rb = %" B_PRId32 "\n", map->BytesPerRow());
4259949213aSStephan Aßmus 			delete map;
4269949213aSStephan Aßmus 		}
4279949213aSStephan Aßmus #endif
42872c5a125SAdrien Destugues 		if (err == sizeof(hdr))
42972c5a125SAdrien Destugues 			err = 0;
4309949213aSStephan Aßmus 		/* header done */
4319949213aSStephan Aßmus 	}
4329949213aSStephan Aßmus 	if (err != B_OK) {
4339949213aSStephan Aßmus 		return err > 0 ? B_IO_ERROR : err;
4349949213aSStephan Aßmus 	}
43572c5a125SAdrien Destugues 	/* Write data. Luckily, PPM and B_TRANSLATOR_BITMAP both scan from left to
43672c5a125SAdrien Destugues 	 * right, top to bottom. */
43772c5a125SAdrien Destugues 	return copy_data(inSource, outDestination, rowbytes, out_rowbytes, height,
43872c5a125SAdrien Destugues 		max, ascii, out_ascii, space, out_space);
4399949213aSStephan Aßmus }
4409949213aSStephan Aßmus 
4419949213aSStephan Aßmus 
44272c5a125SAdrien Destugues class PPMView : public BView
4439949213aSStephan Aßmus {
4449949213aSStephan Aßmus public:
44572c5a125SAdrien Destugues 	PPMView(const char* name, uint32 flags) : BView(name, flags)
4469949213aSStephan Aßmus 	{
447f0650dc9Slooncraz 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
448a76f629eSRyan Leavengood 
44972c5a125SAdrien Destugues 		fTitle = new BStringView("title", B_TRANSLATE("PPM image translator"));
450037bc75eSJanus 		fTitle->SetFont(be_bold_font);
451a76f629eSRyan Leavengood 
452a76f629eSRyan Leavengood 		char detail[100];
453a76f629eSRyan Leavengood 		int ver = static_cast<int>(translatorVersion);
454037bc75eSJanus 		sprintf(detail, B_TRANSLATE("Version %d.%d.%d, %s"), ver >> 8,
45572c5a125SAdrien Destugues 			((ver >> 4) & 0xf), (ver & 0xf), __DATE__);
456037bc75eSJanus 		fDetail = new BStringView("detail", detail);
457a76f629eSRyan Leavengood 
45872c5a125SAdrien Destugues 		fBasedOn = new BStringView(
45972c5a125SAdrien Destugues 			"basedOn", B_TRANSLATE("Based on PPMTranslator sample code"));
460a76f629eSRyan Leavengood 
461037bc75eSJanus 		fCopyright = new BStringView("copyright",
46270d59669SSiarzhuk Zharski 			B_TRANSLATE("Sample code copyright 1999, Be Incorporated"));
463a76f629eSRyan Leavengood 
464037bc75eSJanus 		fMenu = new BPopUpMenu("Color Space");
46572c5a125SAdrien Destugues 		fMenu->AddItem(
46672c5a125SAdrien Destugues 			new BMenuItem(B_TRANSLATE("None"), CSMessage(B_NO_COLOR_SPACE)));
46772c5a125SAdrien Destugues 		fMenu->AddItem(new BMenuItem(
46872c5a125SAdrien Destugues 			B_TRANSLATE("RGB 8:8:8 32 bits"), CSMessage(B_RGB32)));
469037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
47072c5a125SAdrien Destugues 												 "bits"),
47172c5a125SAdrien Destugues 			CSMessage(B_RGBA32)));
47272c5a125SAdrien Destugues 		fMenu->AddItem(new BMenuItem(
47372c5a125SAdrien Destugues 			B_TRANSLATE("RGB 5:5:5 16 bits"), CSMessage(B_RGB15)));
474037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
47572c5a125SAdrien Destugues 												 "bits"),
47672c5a125SAdrien Destugues 			CSMessage(B_RGBA15)));
47772c5a125SAdrien Destugues 		fMenu->AddItem(new BMenuItem(
47872c5a125SAdrien Destugues 			B_TRANSLATE("RGB 5:6:5 16 bits"), CSMessage(B_RGB16)));
479037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("System palette 8 "
48072c5a125SAdrien Destugues 												 "bits"),
48172c5a125SAdrien Destugues 			CSMessage(B_CMAP8)));
482037bc75eSJanus 		fMenu->AddSeparatorItem();
48372c5a125SAdrien Destugues 		fMenu->AddItem(
48472c5a125SAdrien Destugues 			new BMenuItem(B_TRANSLATE("Grayscale 8 bits"), CSMessage(B_GRAY8)));
48572c5a125SAdrien Destugues 		fMenu->AddItem(
48672c5a125SAdrien Destugues 			new BMenuItem(B_TRANSLATE("Bitmap 1 bit"), CSMessage(B_GRAY1)));
48772c5a125SAdrien Destugues 		fMenu->AddItem(new BMenuItem(
48872c5a125SAdrien Destugues 			B_TRANSLATE("CMY 8:8:8 32 bits"), CSMessage(B_CMY32)));
489037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYA 8:8:8:8 32 "
49072c5a125SAdrien Destugues 												 "bits"),
49172c5a125SAdrien Destugues 			CSMessage(B_CMYA32)));
492037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYK 8:8:8:8 32 "
49372c5a125SAdrien Destugues 												 "bits"),
49472c5a125SAdrien Destugues 			CSMessage(B_CMYK32)));
495037bc75eSJanus 		fMenu->AddSeparatorItem();
496037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 8:8:8 32 bits "
49772c5a125SAdrien Destugues 												 "big-endian"),
49872c5a125SAdrien Destugues 			CSMessage(B_RGB32_BIG)));
499037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
50072c5a125SAdrien Destugues 												 "bits big-endian"),
50172c5a125SAdrien Destugues 			CSMessage(B_RGBA32_BIG)));
502037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:5:5 16 bits "
50372c5a125SAdrien Destugues 												 "big-endian"),
50472c5a125SAdrien Destugues 			CSMessage(B_RGB15_BIG)));
505037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
50672c5a125SAdrien Destugues 												 "bits big-endian"),
50772c5a125SAdrien Destugues 			CSMessage(B_RGBA15_BIG)));
508037bc75eSJanus 		fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:6:5 16 bits "
50972c5a125SAdrien Destugues 												 "big-endian"),
51072c5a125SAdrien Destugues 			CSMessage(B_RGB16)));
51172c5a125SAdrien Destugues 		fField = new BMenuField(B_TRANSLATE("Input color space:"), fMenu);
512037bc75eSJanus 		fField->SetViewColor(ViewColor());
5139949213aSStephan Aßmus 		SelectColorSpace(g_settings.out_space);
5149949213aSStephan Aßmus 		BMessage* msg = new BMessage(CHANGE_ASCII);
515037bc75eSJanus 		fAscii = new BCheckBox(B_TRANSLATE("Write ASCII"), msg);
516a76f629eSRyan Leavengood 		if (g_settings.write_ascii)
517037bc75eSJanus 			fAscii->SetValue(1);
518037bc75eSJanus 		fAscii->SetViewColor(ViewColor());
519a76f629eSRyan Leavengood 
520a76f629eSRyan Leavengood 		// Build the layout
521037bc75eSJanus 		BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
522037bc75eSJanus 			.SetInsets(B_USE_DEFAULT_SPACING)
523037bc75eSJanus 			.Add(fTitle)
524037bc75eSJanus 			.Add(fDetail)
525a76f629eSRyan Leavengood 			.AddGlue()
5267bfb4a1eSJanus 			.AddGroup(B_HORIZONTAL)
5277bfb4a1eSJanus 			.Add(fField)
5287bfb4a1eSJanus 			.AddGlue()
5297d48219bSHannah Boneß 			.End()
5307bfb4a1eSJanus 			.Add(fAscii)
531037bc75eSJanus 			.AddGlue()
532037bc75eSJanus 			.Add(fBasedOn)
533037bc75eSJanus 			.Add(fCopyright);
534a76f629eSRyan Leavengood 
535a76f629eSRyan Leavengood 		BFont font;
536a76f629eSRyan Leavengood 		GetFont(&font);
53772c5a125SAdrien Destugues 		SetExplicitPreferredSize(
53872c5a125SAdrien Destugues 			BSize((font.Size() * 350) / 12, (font.Size() * 200) / 12));
5399949213aSStephan Aßmus 	}
54072c5a125SAdrien Destugues 	~PPMView() { /* nothing here */ }
5419949213aSStephan Aßmus 
54272c5a125SAdrien Destugues 	enum { SET_COLOR_SPACE = 'ppm=', CHANGE_ASCII };
5439949213aSStephan Aßmus 
54472c5a125SAdrien Destugues 	virtual void MessageReceived(BMessage* message)
5459949213aSStephan Aßmus 	{
5469949213aSStephan Aßmus 		if (message->what == SET_COLOR_SPACE) {
5479949213aSStephan Aßmus 			SetSettings(message);
54872c5a125SAdrien Destugues 		} else if (message->what == CHANGE_ASCII) {
5499949213aSStephan Aßmus 			BMessage msg;
550037bc75eSJanus 			msg.AddBool("ppm /ascii", fAscii->Value());
5519949213aSStephan Aßmus 			SetSettings(&msg);
55272c5a125SAdrien Destugues 		} else {
5539949213aSStephan Aßmus 			BView::MessageReceived(message);
5549949213aSStephan Aßmus 		}
5559949213aSStephan Aßmus 	}
5569949213aSStephan Aßmus 	virtual void AllAttached()
5579949213aSStephan Aßmus 	{
5589949213aSStephan Aßmus 		BView::AllAttached();
5599949213aSStephan Aßmus 		BMessenger msgr(this);
5609949213aSStephan Aßmus 		/*	Tell all menu items we're the man.	*/
561037bc75eSJanus 		for (int ix = 0; ix < fMenu->CountItems(); ix++) {
562037bc75eSJanus 			BMenuItem* i = fMenu->ItemAt(ix);
5639949213aSStephan Aßmus 			if (i) {
5649949213aSStephan Aßmus 				i->SetTarget(msgr);
5659949213aSStephan Aßmus 			}
5669949213aSStephan Aßmus 		}
567037bc75eSJanus 		fAscii->SetTarget(msgr);
5689949213aSStephan Aßmus 	}
5699949213aSStephan Aßmus 
57072c5a125SAdrien Destugues 	void SetSettings(BMessage* message)
5719949213aSStephan Aßmus 	{
5729949213aSStephan Aßmus 		g_settings_lock.Lock();
5739949213aSStephan Aßmus 		color_space space;
5749949213aSStephan Aßmus 		if (!message->FindInt32("space", (int32*) &space)) {
5759949213aSStephan Aßmus 			g_settings.out_space = space;
5769949213aSStephan Aßmus 			SelectColorSpace(space);
5779949213aSStephan Aßmus 			g_settings.settings_touched = true;
5789949213aSStephan Aßmus 		}
5799949213aSStephan Aßmus 		bool ascii;
5809949213aSStephan Aßmus 		if (!message->FindBool("ppm /ascii", &ascii)) {
5819949213aSStephan Aßmus 			g_settings.write_ascii = ascii;
5829949213aSStephan Aßmus 			g_settings.settings_touched = true;
5839949213aSStephan Aßmus 		}
5849949213aSStephan Aßmus 		g_settings_lock.Unlock();
5859949213aSStephan Aßmus 	}
5869949213aSStephan Aßmus 
5879949213aSStephan Aßmus private:
588037bc75eSJanus 	BStringView* fTitle;
589037bc75eSJanus 	BStringView* fDetail;
590037bc75eSJanus 	BStringView* fBasedOn;
591037bc75eSJanus 	BStringView* fCopyright;
592037bc75eSJanus 	BPopUpMenu* fMenu;
593037bc75eSJanus 	BMenuField* fField;
594037bc75eSJanus 	BCheckBox* fAscii;
5959949213aSStephan Aßmus 
59672c5a125SAdrien Destugues 	BMessage* CSMessage(color_space space)
5979949213aSStephan Aßmus 	{
5989949213aSStephan Aßmus 		BMessage* ret = new BMessage(SET_COLOR_SPACE);
5999949213aSStephan Aßmus 		ret->AddInt32("space", space);
6009949213aSStephan Aßmus 		return ret;
6019949213aSStephan Aßmus 	}
6029949213aSStephan Aßmus 
60372c5a125SAdrien Destugues 	void SelectColorSpace(color_space space)
6049949213aSStephan Aßmus 	{
605037bc75eSJanus 		for (int ix = 0; ix < fMenu->CountItems(); ix++) {
6069949213aSStephan Aßmus 			int32 s;
607037bc75eSJanus 			BMenuItem* i = fMenu->ItemAt(ix);
6089949213aSStephan Aßmus 			if (i) {
6099949213aSStephan Aßmus 				BMessage* m = i->Message();
6109949213aSStephan Aßmus 				if (m && !m->FindInt32("space", &s) && (s == space)) {
611037bc75eSJanus 					fMenu->Superitem()->SetLabel(i->Label());
6129949213aSStephan Aßmus 					break;
6139949213aSStephan Aßmus 				}
6149949213aSStephan Aßmus 			}
6159949213aSStephan Aßmus 		}
6169949213aSStephan Aßmus 	}
6179949213aSStephan Aßmus };
6189949213aSStephan Aßmus 
6199949213aSStephan Aßmus 
6209949213aSStephan Aßmus /*	The view will get resized to what the parent thinks is 	*/
6219949213aSStephan Aßmus /*	reasonable. However, it will still receive MouseDowns etc.	*/
6229949213aSStephan Aßmus /*	Your view should change settings in the translator immediately, 	*/
6239949213aSStephan Aßmus /*	taking care not to change parameters for a translation that is 	*/
6249949213aSStephan Aßmus /*	currently running. Typically, you'll have a global struct for 	*/
6259949213aSStephan Aßmus /*	settings that is atomically copied into the translator function 	*/
6269949213aSStephan Aßmus /*	as a local when translation starts.	*/
6279949213aSStephan Aßmus /*	Store your settings wherever you feel like it.	*/
6289949213aSStephan Aßmus status_t
629*71aec297SAdrien Destugues MakeConfig(BMessage* ioExtension, BView** outView, BRect* outExtent)
6309949213aSStephan Aßmus {
63172c5a125SAdrien Destugues 	PPMView* v
63272c5a125SAdrien Destugues 		= new PPMView(B_TRANSLATE("PPMTranslator Settings"), B_WILL_DRAW);
6339949213aSStephan Aßmus 	*outView = v;
63472c5a125SAdrien Destugues 	v->ResizeTo(v->ExplicitPreferredSize());
63572c5a125SAdrien Destugues 	;
6369949213aSStephan Aßmus 	*outExtent = v->Bounds();
6379949213aSStephan Aßmus 	if (ioExtension) {
6389949213aSStephan Aßmus 		v->SetSettings(ioExtension);
6399949213aSStephan Aßmus 	}
6409949213aSStephan Aßmus 	return B_OK;
6419949213aSStephan Aßmus }
6429949213aSStephan Aßmus 
6439949213aSStephan Aßmus 
6449949213aSStephan Aßmus /*	Copy your current settings to a BMessage that may be passed 	*/
6459949213aSStephan Aßmus /*	to BTranslators::Translate at some later time when the user wants to 	*/
6469949213aSStephan Aßmus /*	use whatever settings you're using right now.	*/
6479949213aSStephan Aßmus status_t
648*71aec297SAdrien Destugues GetConfigMessage(BMessage* ioExtension)
6499949213aSStephan Aßmus {
6509949213aSStephan Aßmus 	status_t err = B_OK;
6519949213aSStephan Aßmus 	const char* name = B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE;
6529949213aSStephan Aßmus 	g_settings_lock.Lock();
6539949213aSStephan Aßmus 	(void) ioExtension->RemoveName(name);
6549949213aSStephan Aßmus 	err = ioExtension->AddInt32(name, g_settings.out_space);
6559949213aSStephan Aßmus 	g_settings_lock.Unlock();
6569949213aSStephan Aßmus 	return err;
6579949213aSStephan Aßmus }
6589949213aSStephan Aßmus 
6599949213aSStephan Aßmus 
6609949213aSStephan Aßmus status_t
66172c5a125SAdrien Destugues read_ppm_header(BDataIO* inSource, int* width, int* rowbytes, int* height,
66272c5a125SAdrien Destugues 	int* max, bool* ascii, color_space* space, bool* is_ppm, char** comment)
6639949213aSStephan Aßmus {
6649949213aSStephan Aßmus 	/* check for PPM magic number */
6659949213aSStephan Aßmus 	char ch[2];
66658913f60SAdrien Destugues 	bool monochrome = false;
66758913f60SAdrien Destugues 	bool greyscale = false;
6689949213aSStephan Aßmus 	if (inSource->Read(ch, 2) != 2) {
6699949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
6709949213aSStephan Aßmus 	}
6719949213aSStephan Aßmus 	/* look for magic number */
6729949213aSStephan Aßmus 	if (ch[0] != 'P') {
6739949213aSStephan Aßmus 		/* B_TRANSLATOR_BITMAP magic? */
6749949213aSStephan Aßmus 		if (ch[0] == 'b' && ch[1] == 'i') {
6759949213aSStephan Aßmus 			*is_ppm = false;
67672c5a125SAdrien Destugues 			return read_bits_header(
67772c5a125SAdrien Destugues 				inSource, 2, width, rowbytes, height, max, ascii, space);
6789949213aSStephan Aßmus 		}
6799949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
6809949213aSStephan Aßmus 	}
6819949213aSStephan Aßmus 	*is_ppm = true;
68258913f60SAdrien Destugues 	if (ch[1] == '6' || ch[1] == '5' || ch[1] == '4') {
6839949213aSStephan Aßmus 		*ascii = false;
68458913f60SAdrien Destugues 	} else if (ch[1] == '3' || ch[1] == '2' || ch[1] == '1') {
6859949213aSStephan Aßmus 		*ascii = true;
68672c5a125SAdrien Destugues 	} else {
6879949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
6889949213aSStephan Aßmus 	}
68958913f60SAdrien Destugues 
69058913f60SAdrien Destugues 	if (ch[1] == '4' || ch[1] == '1')
69158913f60SAdrien Destugues 		monochrome = true;
69258913f60SAdrien Destugues 	else if (ch[1] == '5' || ch[1] == '2')
69358913f60SAdrien Destugues 		greyscale = true;
69458913f60SAdrien Destugues 
6959949213aSStephan Aßmus 	// status_t err = B_NO_TRANSLATOR;
6969949213aSStephan Aßmus 	enum scan_state {
6979949213aSStephan Aßmus 		scan_width,
6989949213aSStephan Aßmus 		scan_height,
6999949213aSStephan Aßmus 		scan_max,
7009949213aSStephan Aßmus 		scan_white
70172c5a125SAdrien Destugues 	} state
70272c5a125SAdrien Destugues 		= scan_width;
7039949213aSStephan Aßmus 	int* scan = NULL;
7049949213aSStephan Aßmus 	bool in_comment = false;
70558913f60SAdrien Destugues 	if (monochrome)
70658913f60SAdrien Destugues 		*space = B_GRAY1;
70758913f60SAdrien Destugues 	else if (greyscale)
70858913f60SAdrien Destugues 		*space = B_GRAY8;
70958913f60SAdrien Destugues 	else
7109949213aSStephan Aßmus 		*space = B_RGB24_BIG;
7119949213aSStephan Aßmus 	/* The description of PPM is slightly ambiguous as far as comments */
7129949213aSStephan Aßmus 	/* go. We choose to allow comments anywhere, in the spirit of laxness. */
7139949213aSStephan Aßmus 	/* See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/PPM.txt */
7149949213aSStephan Aßmus 	int comlen = 0;
7159949213aSStephan Aßmus 	int comptr = 0;
7169949213aSStephan Aßmus 	while (inSource->Read(ch, 1) == 1) {
7179949213aSStephan Aßmus 		if (in_comment && (ch[0] != 10) && (ch[0] != 13)) {
7189949213aSStephan Aßmus 			if (comment) { /* collect comment(s) into comment string */
7199949213aSStephan Aßmus 				if (comptr >= comlen - 1) {
7209949213aSStephan Aßmus 					char* n = (char*) realloc(*comment, comlen + 100);
7219949213aSStephan Aßmus 					if (!n) {
7229949213aSStephan Aßmus 						free(*comment);
7239949213aSStephan Aßmus 						*comment = NULL;
7249949213aSStephan Aßmus 					}
7259949213aSStephan Aßmus 					*comment = n;
7269949213aSStephan Aßmus 					comlen += 100;
7279949213aSStephan Aßmus 				}
7289949213aSStephan Aßmus 				(*comment)[comptr++] = ch[0];
7299949213aSStephan Aßmus 				(*comment)[comptr] = 0;
7309949213aSStephan Aßmus 			}
7319949213aSStephan Aßmus 			continue;
7329949213aSStephan Aßmus 		}
7339949213aSStephan Aßmus 		in_comment = false;
7349949213aSStephan Aßmus 		if (ch[0] == '#') {
7359949213aSStephan Aßmus 			in_comment = true;
7369949213aSStephan Aßmus 			continue;
7379949213aSStephan Aßmus 		}
7389949213aSStephan Aßmus 		/* once we're done with whitespace after max, we're done with header */
7399949213aSStephan Aßmus 		if (isdigit(ch[0])) {
7409949213aSStephan Aßmus 			if (!scan) { /* first digit for this value */
7419949213aSStephan Aßmus 				switch (state) {
7429949213aSStephan Aßmus 					case scan_width:
7439949213aSStephan Aßmus 						scan = width;
7449949213aSStephan Aßmus 						break;
7459949213aSStephan Aßmus 					case scan_height:
74658913f60SAdrien Destugues 						if (monochrome)
74758913f60SAdrien Destugues 							*rowbytes = (*width + 7) / 8;
74858913f60SAdrien Destugues 						else if (greyscale)
74958913f60SAdrien Destugues 							*rowbytes = *width;
75058913f60SAdrien Destugues 						else
7519949213aSStephan Aßmus 							*rowbytes = *width * 3;
7529949213aSStephan Aßmus 						scan = height;
7539949213aSStephan Aßmus 						break;
7549949213aSStephan Aßmus 					case scan_max:
7559949213aSStephan Aßmus 						scan = max;
7569949213aSStephan Aßmus 						break;
7579949213aSStephan Aßmus 					default:
7589949213aSStephan Aßmus 						return B_OK; /* done with header, all OK */
7599949213aSStephan Aßmus 				}
7609949213aSStephan Aßmus 				*scan = 0;
7619949213aSStephan Aßmus 			}
7629949213aSStephan Aßmus 			*scan = (*scan) * 10 + (ch[0] - '0');
76372c5a125SAdrien Destugues 		} else if (isspace(ch[0])) {
7649949213aSStephan Aßmus 			if (scan) { /* are we done with one value? */
7659949213aSStephan Aßmus 				scan = NULL;
7669949213aSStephan Aßmus 				state = (enum scan_state)(state + 1);
76758913f60SAdrien Destugues 
76858913f60SAdrien Destugues 				/* in monochrome ppm, there is no max in the file, so we
76958913f60SAdrien Destugues 				 * skip that step. */
77058913f60SAdrien Destugues 				if ((state == scan_max) && monochrome) {
77158913f60SAdrien Destugues 					state = (enum scan_state)(state + 1);
77258913f60SAdrien Destugues 					*max = 1;
7739949213aSStephan Aßmus 				}
77458913f60SAdrien Destugues 			}
77558913f60SAdrien Destugues 
77672c5a125SAdrien Destugues 			if (state == scan_white) { /* we only ever read one whitespace,
77772c5a125SAdrien Destugues 										  since we skip space */
77872c5a125SAdrien Destugues 				return B_OK; /* when reading ASCII, and there is a single
77972c5a125SAdrien Destugues 								whitespace after max in raw mode */
7809949213aSStephan Aßmus 			}
78172c5a125SAdrien Destugues 		} else {
7829949213aSStephan Aßmus 			if (state != scan_white) {
7839949213aSStephan Aßmus 				return B_NO_TRANSLATOR;
7849949213aSStephan Aßmus 			}
7859949213aSStephan Aßmus 			return B_OK; /* header done */
7869949213aSStephan Aßmus 		}
7879949213aSStephan Aßmus 	}
7889949213aSStephan Aßmus 	return B_NO_TRANSLATOR;
7899949213aSStephan Aßmus }
7909949213aSStephan Aßmus 
7919949213aSStephan Aßmus 
7929949213aSStephan Aßmus status_t
79372c5a125SAdrien Destugues read_bits_header(BDataIO* io, int skipped, int* width, int* rowbytes,
79472c5a125SAdrien Destugues 	int* height, int* max, bool* ascii, color_space* space)
7959949213aSStephan Aßmus {
7969949213aSStephan Aßmus 	/* read the rest of a possible B_TRANSLATOR_BITMAP header */
79772c5a125SAdrien Destugues 	if (skipped < 0 || skipped > 4)
79872c5a125SAdrien Destugues 		return B_NO_TRANSLATOR;
7999949213aSStephan Aßmus 	int rd = sizeof(TranslatorBitmap) - skipped;
8009949213aSStephan Aßmus 	TranslatorBitmap hdr;
8019949213aSStephan Aßmus 	/* pre-initialize magic because we might have skipped part of it already */
8029949213aSStephan Aßmus 	hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
8039949213aSStephan Aßmus 	char* ptr = (char*) &hdr;
8049949213aSStephan Aßmus 	if (io->Read(ptr + skipped, rd) != rd) {
8059949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8069949213aSStephan Aßmus 	}
8079949213aSStephan Aßmus 	/* swap header values */
8089949213aSStephan Aßmus 	hdr.magic = B_BENDIAN_TO_HOST_INT32(hdr.magic);
8099949213aSStephan Aßmus 	hdr.bounds.left = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.left);
8109949213aSStephan Aßmus 	hdr.bounds.right = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.right);
8119949213aSStephan Aßmus 	hdr.bounds.top = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.top);
8129949213aSStephan Aßmus 	hdr.bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.bottom);
8139949213aSStephan Aßmus 	hdr.rowBytes = B_BENDIAN_TO_HOST_INT32(hdr.rowBytes);
8149949213aSStephan Aßmus 	hdr.colors = (color_space) B_BENDIAN_TO_HOST_INT32(hdr.colors);
8159949213aSStephan Aßmus 	hdr.dataSize = B_BENDIAN_TO_HOST_INT32(hdr.dataSize);
8169949213aSStephan Aßmus 	/* sanity checking */
8179949213aSStephan Aßmus 	if (hdr.magic != B_TRANSLATOR_BITMAP) {
8189949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8199949213aSStephan Aßmus 	}
82072c5a125SAdrien Destugues 	if (hdr.colors & 0xffff0000) { /* according to <GraphicsDefs.h> this is a
82172c5a125SAdrien Destugues 									  reasonable check. */
8229949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8239949213aSStephan Aßmus 	}
8249949213aSStephan Aßmus 	if (hdr.rowBytes * (hdr.bounds.Height() + 1) > hdr.dataSize) {
8259949213aSStephan Aßmus 		return B_NO_TRANSLATOR;
8269949213aSStephan Aßmus 	}
8279949213aSStephan Aßmus 	/* return information about the data in the stream */
8289949213aSStephan Aßmus 	*width = (int) hdr.bounds.Width() + 1;
8299949213aSStephan Aßmus 	*rowbytes = hdr.rowBytes;
8309949213aSStephan Aßmus 	*height = (int) hdr.bounds.Height() + 1;
8319949213aSStephan Aßmus 	*max = 255;
8329949213aSStephan Aßmus 	*ascii = false;
8339949213aSStephan Aßmus 	*space = hdr.colors;
8349949213aSStephan Aßmus 	return B_OK;
8359949213aSStephan Aßmus }
8369949213aSStephan Aßmus 
8379949213aSStephan Aßmus 
83872c5a125SAdrien Destugues /*	String may contain newlines, after which we need to insert extra hash signs.
83972c5a125SAdrien Destugues  */
8409949213aSStephan Aßmus status_t
84172c5a125SAdrien Destugues write_comment(const char* str, BDataIO* io)
8429949213aSStephan Aßmus {
8439949213aSStephan Aßmus 	const char* end = str + strlen(str);
8449949213aSStephan Aßmus 	const char* ptr = str;
8459949213aSStephan Aßmus 	status_t err = B_OK;
8469949213aSStephan Aßmus 	/* write each line as it's found */
8479949213aSStephan Aßmus 	while ((ptr < end) && !err) {
8489949213aSStephan Aßmus 		if ((*ptr == 10) || (*ptr == 13)) {
8499949213aSStephan Aßmus 			err = io->Write("# ", 2);
8509949213aSStephan Aßmus 			if (err == 2) {
8519949213aSStephan Aßmus 				err = io->Write(str, ptr - str);
8529949213aSStephan Aßmus 				if (err == ptr - str) {
8539949213aSStephan Aßmus 					if (io->Write("\n", 1) == 1) {
8549949213aSStephan Aßmus 						err = 0;
8559949213aSStephan Aßmus 					}
8569949213aSStephan Aßmus 				}
8579949213aSStephan Aßmus 			}
8589949213aSStephan Aßmus 			str = ptr + 1;
8599949213aSStephan Aßmus 		}
8609949213aSStephan Aßmus 		ptr++;
8619949213aSStephan Aßmus 	}
8629949213aSStephan Aßmus 	/* write the last data, if any, as a line */
8639949213aSStephan Aßmus 	if (ptr > str) {
8649949213aSStephan Aßmus 		err = io->Write("# ", 2);
8659949213aSStephan Aßmus 		if (err == 2) {
8669949213aSStephan Aßmus 			err = io->Write(str, ptr - str);
8679949213aSStephan Aßmus 			if (err == ptr - str) {
8689949213aSStephan Aßmus 				if (io->Write("\n", 1) == 1) {
8699949213aSStephan Aßmus 					err = 0;
8709949213aSStephan Aßmus 				}
8719949213aSStephan Aßmus 			}
8729949213aSStephan Aßmus 		}
8739949213aSStephan Aßmus 	}
8749949213aSStephan Aßmus 	if (err > 0) {
8759949213aSStephan Aßmus 		err = B_IO_ERROR;
8769949213aSStephan Aßmus 	}
8779949213aSStephan Aßmus 	return err;
8789949213aSStephan Aßmus }
8799949213aSStephan Aßmus 
8809949213aSStephan Aßmus 
8819949213aSStephan Aßmus static status_t
88272c5a125SAdrien Destugues read_ascii_line(BDataIO* in, int max, unsigned char* data, int rowbytes)
8839949213aSStephan Aßmus {
8849949213aSStephan Aßmus 	char ch;
8859949213aSStephan Aßmus 	status_t err;
8869949213aSStephan Aßmus 	// int nread = 0;
8879949213aSStephan Aßmus 	bool comment = false;
8889949213aSStephan Aßmus 	int val = 0;
8899949213aSStephan Aßmus 	bool dig = false;
8909949213aSStephan Aßmus 	while ((err = in->Read(&ch, 1)) == 1) {
8919949213aSStephan Aßmus 		if (comment) {
8929949213aSStephan Aßmus 			if ((ch == '\n') || (ch == '\r')) {
8939949213aSStephan Aßmus 				comment = false;
8949949213aSStephan Aßmus 			}
8959949213aSStephan Aßmus 		}
8969949213aSStephan Aßmus 		if (isdigit(ch)) {
8979949213aSStephan Aßmus 			dig = true;
8989949213aSStephan Aßmus 			val = val * 10 + (ch - '0');
89972c5a125SAdrien Destugues 		} else {
9009949213aSStephan Aßmus 			if (dig) {
9019949213aSStephan Aßmus 				*(data++) = val * 255 / max;
9029949213aSStephan Aßmus 				val = 0;
9039949213aSStephan Aßmus 				rowbytes--;
9049949213aSStephan Aßmus 				dig = false;
9059949213aSStephan Aßmus 			}
9069949213aSStephan Aßmus 			if (ch == '#') {
9079949213aSStephan Aßmus 				comment = true;
9089949213aSStephan Aßmus 				continue;
9099949213aSStephan Aßmus 			}
9109949213aSStephan Aßmus 		}
9119949213aSStephan Aßmus 		if (rowbytes < 1) {
9129949213aSStephan Aßmus 			break;
9139949213aSStephan Aßmus 		}
9149949213aSStephan Aßmus 	}
9159949213aSStephan Aßmus 	if (dig) {
9169949213aSStephan Aßmus 		*(data++) = val * 255 / max;
9179949213aSStephan Aßmus 		val = 0;
9189949213aSStephan Aßmus 		rowbytes--;
9199949213aSStephan Aßmus 		dig = false;
9209949213aSStephan Aßmus 	}
9219949213aSStephan Aßmus 	if (rowbytes < 1) {
9229949213aSStephan Aßmus 		return B_OK;
9239949213aSStephan Aßmus 	}
9249949213aSStephan Aßmus 	return B_IO_ERROR;
9259949213aSStephan Aßmus }
9269949213aSStephan Aßmus 
9279949213aSStephan Aßmus 
9289949213aSStephan Aßmus static status_t
92972c5a125SAdrien Destugues write_ascii_line(BDataIO* out, unsigned char* data, int rowbytes)
9309949213aSStephan Aßmus {
9319949213aSStephan Aßmus 	char buffer[20];
9329949213aSStephan Aßmus 	int linelen = 0;
9339949213aSStephan Aßmus 	while (rowbytes > 2) {
9349949213aSStephan Aßmus 		sprintf(buffer, "%d %d %d ", data[0], data[1], data[2]);
9359949213aSStephan Aßmus 		rowbytes -= 3;
9369949213aSStephan Aßmus 		int l = strlen(buffer);
9379949213aSStephan Aßmus 		if (l + linelen > 70) {
9389949213aSStephan Aßmus 			out->Write("\n", 1);
9399949213aSStephan Aßmus 			linelen = 0;
9409949213aSStephan Aßmus 		}
9419949213aSStephan Aßmus 		if (out->Write(buffer, l) != l) {
9429949213aSStephan Aßmus 			return B_IO_ERROR;
9439949213aSStephan Aßmus 		}
9449949213aSStephan Aßmus 		linelen += l;
9459949213aSStephan Aßmus 		data += 3;
9469949213aSStephan Aßmus 	}
9479949213aSStephan Aßmus 	out->Write("\n", 1); /* terminate each scanline */
9489949213aSStephan Aßmus 	return B_OK;
9499949213aSStephan Aßmus }
9509949213aSStephan Aßmus 
9519949213aSStephan Aßmus 
9529949213aSStephan Aßmus static unsigned char*
95372c5a125SAdrien Destugues make_scale_data(int max)
9549949213aSStephan Aßmus {
9559949213aSStephan Aßmus 	unsigned char* ptr = (unsigned char*) malloc(max);
9569949213aSStephan Aßmus 	for (int ix = 0; ix < max; ix++) {
9579949213aSStephan Aßmus 		ptr[ix] = ix * 255 / max;
9589949213aSStephan Aßmus 	}
9599949213aSStephan Aßmus 	return ptr;
9609949213aSStephan Aßmus }
9619949213aSStephan Aßmus 
9629949213aSStephan Aßmus 
9639949213aSStephan Aßmus static void
96472c5a125SAdrien Destugues scale_data(unsigned char* scale, unsigned char* data, int bytes)
9659949213aSStephan Aßmus {
9669949213aSStephan Aßmus 	for (int ix = 0; ix < bytes; ix++) {
9679949213aSStephan Aßmus 		data[ix] = scale[data[ix]];
9689949213aSStephan Aßmus 	}
9699949213aSStephan Aßmus }
9709949213aSStephan Aßmus 
9719949213aSStephan Aßmus 
9729949213aSStephan Aßmus status_t
97372c5a125SAdrien Destugues copy_data(BDataIO* in, BDataIO* out, int rowbytes, int out_rowbytes, int height,
97472c5a125SAdrien Destugues 	int max, bool in_ascii, bool out_ascii, color_space in_space,
9759949213aSStephan Aßmus 	color_space out_space)
9769949213aSStephan Aßmus {
9775d57984bSAdrien Destugues 	dprintf(("copy_data(%d, %d, %d, %d, %x, %x)\n", rowbytes, out_rowbytes,
97872c5a125SAdrien Destugues 		height, max, in_space, out_space));
979*71aec297SAdrien Destugues 
980*71aec297SAdrien Destugues 	// We will be reading 1 char at a time, so use a buffer
981*71aec297SAdrien Destugues 	BBufferedDataIO inBuffer(*in, 65536, false);
982*71aec297SAdrien Destugues 
9839949213aSStephan Aßmus 	/*	We read/write one scanline at a time.	*/
9849949213aSStephan Aßmus 	unsigned char* data = (unsigned char*) malloc(rowbytes);
9859949213aSStephan Aßmus 	unsigned char* out_data = (unsigned char*) malloc(out_rowbytes);
9869949213aSStephan Aßmus 	if (data == NULL || out_data == NULL) {
9879949213aSStephan Aßmus 		free(data);
9889949213aSStephan Aßmus 		free(out_data);
9899949213aSStephan Aßmus 		return B_NO_MEMORY;
9909949213aSStephan Aßmus 	}
9919949213aSStephan Aßmus 	unsigned char* scale = NULL;
99258913f60SAdrien Destugues 	if (max != 255 && in_space != B_GRAY1) {
9939949213aSStephan Aßmus 		scale = make_scale_data(max);
9949949213aSStephan Aßmus 	}
9959949213aSStephan Aßmus 	status_t err = B_OK;
9969949213aSStephan Aßmus 	/*	There is no data format conversion, so we can just copy data.	*/
9979949213aSStephan Aßmus 	while ((height-- > 0) && !err) {
9989949213aSStephan Aßmus 		if (in_ascii) {
999*71aec297SAdrien Destugues 			err = read_ascii_line(&inBuffer, max, data, rowbytes);
100072c5a125SAdrien Destugues 		} else {
1001*71aec297SAdrien Destugues 			err = inBuffer.Read(data, rowbytes);
10029949213aSStephan Aßmus 			if (err == rowbytes) {
10039949213aSStephan Aßmus 				err = B_OK;
10049949213aSStephan Aßmus 			}
10059949213aSStephan Aßmus 			if (scale) { /* for reading PPM that is smaller than 8 bit */
10069949213aSStephan Aßmus 				scale_data(scale, data, rowbytes);
100758913f60SAdrien Destugues 
10089949213aSStephan Aßmus 			}
10099949213aSStephan Aßmus 		}
10109949213aSStephan Aßmus 		if (err == B_OK) {
10119949213aSStephan Aßmus 			unsigned char* wbuf = data;
10129949213aSStephan Aßmus 			if (in_space != out_space) {
101372c5a125SAdrien Destugues 				err = convert_space(
101472c5a125SAdrien Destugues 					in_space, out_space, data, rowbytes, out_data);
10159949213aSStephan Aßmus 				wbuf = out_data;
10169949213aSStephan Aßmus 			}
10179949213aSStephan Aßmus 			if (!err && out_ascii) {
10189949213aSStephan Aßmus 				err = write_ascii_line(out, wbuf, out_rowbytes);
101972c5a125SAdrien Destugues 			} else if (!err) {
10209949213aSStephan Aßmus 				err = out->Write(wbuf, out_rowbytes);
10219949213aSStephan Aßmus 				if (err == out_rowbytes) {
10229949213aSStephan Aßmus 					err = B_OK;
10239949213aSStephan Aßmus 				}
10249949213aSStephan Aßmus 			}
10259949213aSStephan Aßmus 		}
10269949213aSStephan Aßmus 	}
10279949213aSStephan Aßmus 	free(data);
10289949213aSStephan Aßmus 	free(out_data);
10299949213aSStephan Aßmus 	free(scale);
10309949213aSStephan Aßmus 	return err > 0 ? B_IO_ERROR : err;
10319949213aSStephan Aßmus }
1032