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>
971aec297SAdrien 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
get_window_origin()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
set_window_origin(BPoint pos)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:
PrefsLoader(const char * str)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;
127*9d617761SX512 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path))
1289949213aSStephan Aßmus path.SetTo("/tmp");
1299949213aSStephan Aßmus path.Append(str);
1309949213aSStephan Aßmus FILE* f = fopen(path.Path(), "r");
1319949213aSStephan Aßmus /* parse text settings file -- this should be a library... */
1329949213aSStephan Aßmus if (f) {
1339949213aSStephan Aßmus char line[1024];
1349949213aSStephan Aßmus char name[32];
1359949213aSStephan Aßmus char* ptr;
1369949213aSStephan Aßmus while (true) {
1379949213aSStephan Aßmus line[0] = 0;
1389949213aSStephan Aßmus fgets(line, 1024, f);
139*9d617761SX512 if (!line[0])
1409949213aSStephan Aßmus break;
1419949213aSStephan Aßmus /* remember: line ends with \n, so printf()s don't have to */
1429949213aSStephan Aßmus ptr = line;
143*9d617761SX512 while (isspace(*ptr))
1449949213aSStephan Aßmus ptr++;
145*9d617761SX512 if (*ptr == '#' || !*ptr) /* comment or blank */
1469949213aSStephan Aßmus continue;
1479949213aSStephan Aßmus if (sscanf(ptr, "%31[a-zA-Z_0-9] =", name) != 1) {
14872c5a125SAdrien Destugues syslog(LOG_ERR,
14972c5a125SAdrien Destugues "unknown PPMTranslator "
15072c5a125SAdrien Destugues "settings line: %s",
15172c5a125SAdrien Destugues line);
15272c5a125SAdrien Destugues } else {
1539949213aSStephan Aßmus if (!strcmp(name, "color_space")) {
154*9d617761SX512 while (*ptr != '=')
1559949213aSStephan Aßmus ptr++;
1569949213aSStephan Aßmus ptr++;
15772c5a125SAdrien Destugues if (sscanf(ptr, "%d", (int*) &g_settings.out_space)
15872c5a125SAdrien Destugues != 1) {
15972c5a125SAdrien Destugues syslog(LOG_ERR,
16072c5a125SAdrien Destugues "illegal color space "
16172c5a125SAdrien Destugues "in PPMTranslator settings: %s",
16272c5a125SAdrien Destugues ptr);
1639949213aSStephan Aßmus }
16472c5a125SAdrien Destugues } else if (!strcmp(name, "window_pos")) {
165*9d617761SX512 while (*ptr != '=')
1669949213aSStephan Aßmus ptr++;
1679949213aSStephan Aßmus ptr++;
16872c5a125SAdrien Destugues if (sscanf(ptr, "%f,%f", &g_settings.window_pos.x,
16972c5a125SAdrien Destugues &g_settings.window_pos.y)
17072c5a125SAdrien Destugues != 2) {
17172c5a125SAdrien Destugues syslog(LOG_ERR,
17272c5a125SAdrien Destugues "illegal window position "
17372c5a125SAdrien Destugues "in PPMTranslator settings: %s",
17472c5a125SAdrien Destugues ptr);
1759949213aSStephan Aßmus }
17672c5a125SAdrien Destugues } else if (!strcmp(name, "write_ascii")) {
177*9d617761SX512 while (*ptr != '=')
1789949213aSStephan Aßmus ptr++;
1799949213aSStephan Aßmus ptr++;
1809949213aSStephan Aßmus int ascii = g_settings.write_ascii;
1819949213aSStephan Aßmus if (sscanf(ptr, "%d", &ascii) != 1) {
18272c5a125SAdrien Destugues syslog(LOG_ERR,
18372c5a125SAdrien Destugues "illegal write_ascii value "
18472c5a125SAdrien Destugues "in PPMTranslator settings: %s",
18572c5a125SAdrien Destugues ptr);
18672c5a125SAdrien Destugues } else {
1879949213aSStephan Aßmus g_settings.write_ascii = ascii;
1889949213aSStephan Aßmus }
18972c5a125SAdrien Destugues } else {
19072c5a125SAdrien Destugues syslog(
19172c5a125SAdrien Destugues LOG_ERR, "unknown PPMTranslator setting: %s", line);
1929949213aSStephan Aßmus }
1939949213aSStephan Aßmus }
1949949213aSStephan Aßmus }
1959949213aSStephan Aßmus fclose(f);
1969949213aSStephan Aßmus }
1979949213aSStephan Aßmus }
19871aec297SAdrien Destugues
~PrefsLoader()1999949213aSStephan Aßmus ~PrefsLoader()
2009949213aSStephan Aßmus {
2019949213aSStephan Aßmus /* No need writing settings if there aren't any */
2029949213aSStephan Aßmus if (g_settings.settings_touched) {
2039949213aSStephan Aßmus BPath path;
204*9d617761SX512 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path))
2059949213aSStephan Aßmus path.SetTo("/tmp");
2069949213aSStephan Aßmus path.Append("PPMTranslator_Settings");
2079949213aSStephan Aßmus FILE* f = fopen(path.Path(), "w");
2089949213aSStephan Aßmus if (f) {
2099949213aSStephan Aßmus fprintf(f, "# PPMTranslator settings version %d.%d.%d\n",
2109949213aSStephan Aßmus static_cast<int>(translatorVersion >> 8),
2119949213aSStephan Aßmus static_cast<int>((translatorVersion >> 4) & 0xf),
2129949213aSStephan Aßmus static_cast<int>(translatorVersion & 0xf));
2139949213aSStephan Aßmus fprintf(f, "color_space = %d\n", g_settings.out_space);
21470d59669SSiarzhuk Zharski fprintf(f, "window_pos = %g,%g\n", g_settings.window_pos.x,
21570d59669SSiarzhuk Zharski g_settings.window_pos.y);
21672c5a125SAdrien Destugues fprintf(
21772c5a125SAdrien Destugues f, "write_ascii = %d\n", g_settings.write_ascii ? 1 : 0);
2189949213aSStephan Aßmus fclose(f);
2199949213aSStephan Aßmus }
2209949213aSStephan Aßmus }
2219949213aSStephan Aßmus }
2229949213aSStephan Aßmus };
2239949213aSStephan Aßmus
2249949213aSStephan Aßmus PrefsLoader g_prefs_loader("PPMTranslator_Settings");
2259949213aSStephan Aßmus
2269949213aSStephan Aßmus /* Some prototypes for functions we use. */
2279949213aSStephan Aßmus status_t read_ppm_header(BDataIO* io, int* width, int* rowbytes, int* height,
2289949213aSStephan Aßmus int* max, bool* ascii, color_space* space, bool* is_ppm, char** comment);
2299949213aSStephan Aßmus status_t read_bits_header(BDataIO* io, int skipped, int* width, int* rowbytes,
2309949213aSStephan Aßmus int* height, int* max, bool* ascii, color_space* space);
2319949213aSStephan Aßmus status_t write_comment(const char* str, BDataIO* io);
2329949213aSStephan Aßmus status_t copy_data(BDataIO* in, BDataIO* out, int rowbytes, int out_rowbytes,
2339949213aSStephan Aßmus int height, int max, bool in_ascii, bool out_ascii, color_space in_space,
2349949213aSStephan Aßmus color_space out_space);
2359949213aSStephan Aßmus
23671aec297SAdrien Destugues
2379949213aSStephan Aßmus /* Return B_NO_TRANSLATOR if not handling this data. */
2389949213aSStephan Aßmus /* Even if inputFormats exists, may be called for data without hints */
2399949213aSStephan Aßmus /* If outType is not 0, must be able to export in wanted format */
2409949213aSStephan Aßmus status_t
Identify(BPositionIO * inSource,const translation_format * inFormat,BMessage * ioExtension,translator_info * outInfo,uint32 outType)24171aec297SAdrien Destugues Identify(BPositionIO* inSource, const translation_format* inFormat,
24271aec297SAdrien Destugues BMessage* ioExtension, translator_info* outInfo, uint32 outType)
2439949213aSStephan Aßmus {
2449949213aSStephan Aßmus dprintf(("PPMTranslator: Identify()\n"));
2459949213aSStephan Aßmus /* Silence compiler warnings. */
246*9d617761SX512 (void)inFormat;
247*9d617761SX512 (void)ioExtension;
2489949213aSStephan Aßmus
2499949213aSStephan Aßmus /* Check that requested format is something we can deal with. */
250*9d617761SX512 if (outType == 0)
2519949213aSStephan Aßmus outType = B_TRANSLATOR_BITMAP;
252*9d617761SX512
253*9d617761SX512 if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE)
2549949213aSStephan Aßmus return B_NO_TRANSLATOR;
2559949213aSStephan Aßmus
2569949213aSStephan Aßmus /* Check header. */
2579949213aSStephan Aßmus int width, rowbytes, height, max;
2589949213aSStephan Aßmus bool ascii, is_ppm;
2599949213aSStephan Aßmus color_space space;
26072c5a125SAdrien Destugues status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max,
26172c5a125SAdrien Destugues &ascii, &space, &is_ppm, NULL);
262*9d617761SX512 if (err != B_OK)
2639949213aSStephan Aßmus return err;
264*9d617761SX512
26572c5a125SAdrien Destugues /* Stuff info into info struct -- Translation Kit will do "translator" for
26672c5a125SAdrien Destugues * us. */
2679949213aSStephan Aßmus outInfo->group = B_TRANSLATOR_BITMAP;
2689949213aSStephan Aßmus if (is_ppm) {
2699949213aSStephan Aßmus outInfo->type = PPM_TYPE;
2709949213aSStephan Aßmus outInfo->quality = 0.3; /* no alpha, etc */
27172c5a125SAdrien Destugues outInfo->capability
27272c5a125SAdrien Destugues = 0.8; /* we're pretty good at PPM reading, though */
273aec33db1SPhilippe Saint-Pierre strlcpy(outInfo->name, B_TRANSLATE("PPM image"), sizeof(outInfo->name));
2749949213aSStephan Aßmus strcpy(outInfo->MIME, "image/x-portable-pixmap");
27572c5a125SAdrien Destugues } else {
2769949213aSStephan Aßmus outInfo->type = B_TRANSLATOR_BITMAP;
2779949213aSStephan Aßmus outInfo->quality = 0.4; /* B_TRANSLATOR_BITMAP can do alpha, at least */
27872c5a125SAdrien Destugues outInfo->capability
27972c5a125SAdrien Destugues = 0.8; /* and we might not know many variations thereof */
280aec33db1SPhilippe Saint-Pierre strlcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format (PPMTranslator)"),
2813927bd3cSPhilippe Saint-Pierre sizeof(outInfo->name));
28272c5a125SAdrien Destugues strcpy(outInfo->MIME, "image/x-be-bitmap"); /* this is the MIME type of
28372c5a125SAdrien Destugues B_TRANSLATOR_BITMAP */
2849949213aSStephan Aßmus }
2859949213aSStephan Aßmus return B_OK;
2869949213aSStephan Aßmus }
2879949213aSStephan Aßmus
2889949213aSStephan Aßmus
2899949213aSStephan Aßmus /* Return B_NO_TRANSLATOR if not handling the output format */
2909949213aSStephan Aßmus /* If outputFormats exists, will only be called for those formats */
2919949213aSStephan Aßmus status_t
Translate(BPositionIO * inSource,const translator_info *,BMessage * ioExtension,uint32 outType,BPositionIO * outDestination)2925d57984bSAdrien Destugues Translate(BPositionIO* inSource, const translator_info* /*inInfo*/,
2935d57984bSAdrien Destugues BMessage* ioExtension, uint32 outType, BPositionIO* outDestination)
2949949213aSStephan Aßmus {
2959949213aSStephan Aßmus dprintf(("PPMTranslator: Translate()\n"));
2969949213aSStephan Aßmus inSource->Seek(0, SEEK_SET); /* paranoia */
2979949213aSStephan Aßmus // inInfo = inInfo; /* silence compiler warning */
2989949213aSStephan Aßmus /* Check what we're being asked to produce. */
299*9d617761SX512 if (!outType)
3009949213aSStephan Aßmus outType = B_TRANSLATOR_BITMAP;
30172c5a125SAdrien Destugues dprintf(("PPMTranslator: outType is '%c%c%c%c'\n", char(outType >> 24),
30272c5a125SAdrien Destugues char(outType >> 16), char(outType >> 8), char(outType)));
303*9d617761SX512 if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE)
3049949213aSStephan Aßmus return B_NO_TRANSLATOR;
3059949213aSStephan Aßmus
3069949213aSStephan Aßmus /* Figure out what we've been given (again). */
3079949213aSStephan Aßmus int width, rowbytes, height, max;
3089949213aSStephan Aßmus bool ascii, is_ppm;
3099949213aSStephan Aßmus color_space space;
3109949213aSStephan Aßmus /* Read_ppm_header() will always return with stream at beginning of data */
3119949213aSStephan Aßmus /* for both B_TRANSLATOR_BITMAP and PPM_TYPE, and indicate what it is. */
3129949213aSStephan Aßmus char* comment = NULL;
31372c5a125SAdrien Destugues status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max,
31472c5a125SAdrien Destugues &ascii, &space, &is_ppm, &comment);
3159949213aSStephan Aßmus if (comment != NULL) {
316*9d617761SX512 if (ioExtension != NULL)
3179949213aSStephan Aßmus ioExtension->AddString(B_TRANSLATOR_EXT_COMMENT, comment);
3189949213aSStephan Aßmus free(comment);
3199949213aSStephan Aßmus }
3209949213aSStephan Aßmus if (err < B_OK) {
3211a7bcf69SOliver Tappe dprintf(("read_ppm_header() error %s [%" B_PRIx32 "]\n", strerror(err),
3221a7bcf69SOliver Tappe err));
3239949213aSStephan Aßmus return err;
3249949213aSStephan Aßmus }
3259949213aSStephan Aßmus /* Check if we're configured to write ASCII format file. */
3269949213aSStephan Aßmus bool out_ascii = false;
3279949213aSStephan Aßmus if (outType == PPM_TYPE) {
3289949213aSStephan Aßmus out_ascii = g_settings.write_ascii;
329*9d617761SX512 if (ioExtension != NULL)
3309949213aSStephan Aßmus ioExtension->FindBool("ppm /ascii", &out_ascii);
3319949213aSStephan Aßmus }
3329949213aSStephan Aßmus err = B_OK;
3339949213aSStephan Aßmus /* Figure out which color space to convert to */
3349949213aSStephan Aßmus color_space out_space;
3359949213aSStephan Aßmus int out_rowbytes;
3369949213aSStephan Aßmus g_settings_lock.Lock();
3379949213aSStephan Aßmus if (outType == PPM_TYPE) {
3389949213aSStephan Aßmus out_space = B_RGB24_BIG;
3399949213aSStephan Aßmus out_rowbytes = 3 * width;
34072c5a125SAdrien Destugues } else { /* When outputting to B_TRANSLATOR_BITMAP, follow user's wishes.
34172c5a125SAdrien Destugues */
34272c5a125SAdrien Destugues if (!ioExtension
3435d57984bSAdrien Destugues || ioExtension->FindInt32( B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE,
3445d57984bSAdrien Destugues (int32*) &out_space)
3455d57984bSAdrien Destugues || (out_space == B_NO_COLOR_SPACE)) {
3469949213aSStephan Aßmus if (g_settings.out_space == B_NO_COLOR_SPACE) {
34772c5a125SAdrien Destugues switch (space) { /* The 24-bit versions are pretty silly, use 32
34872c5a125SAdrien Destugues instead. */
3499949213aSStephan Aßmus case B_RGB24:
3509949213aSStephan Aßmus case B_RGB24_BIG:
3519949213aSStephan Aßmus out_space = B_RGB32;
3529949213aSStephan Aßmus break;
3539949213aSStephan Aßmus default:
3549949213aSStephan Aßmus /* use whatever is there */
3559949213aSStephan Aßmus out_space = space;
3569949213aSStephan Aßmus break;
3579949213aSStephan Aßmus }
35872c5a125SAdrien Destugues } else {
3599949213aSStephan Aßmus out_space = g_settings.out_space;
3609949213aSStephan Aßmus }
3619949213aSStephan Aßmus }
3629949213aSStephan Aßmus out_rowbytes = calc_rowbytes(out_space, width);
3639949213aSStephan Aßmus }
3649949213aSStephan Aßmus g_settings_lock.Unlock();
3659949213aSStephan Aßmus /* Write file header */
3669949213aSStephan Aßmus if (outType == PPM_TYPE) {
3679949213aSStephan Aßmus dprintf(("PPMTranslator: write PPM\n"));
3689949213aSStephan Aßmus /* begin PPM header */
3699949213aSStephan Aßmus const char* magic;
3709949213aSStephan Aßmus if (out_ascii)
3719949213aSStephan Aßmus magic = "P3\n";
3729949213aSStephan Aßmus else
3739949213aSStephan Aßmus magic = "P6\n";
3749949213aSStephan Aßmus err = outDestination->Write(magic, strlen(magic));
37572c5a125SAdrien Destugues if (err == (long) strlen(magic))
37672c5a125SAdrien Destugues err = 0;
3779949213aSStephan Aßmus // comment = NULL;
3789949213aSStephan Aßmus const char* fsComment;
37972c5a125SAdrien Destugues if ((ioExtension != NULL)
380*9d617761SX512 && !ioExtension->FindString(B_TRANSLATOR_EXT_COMMENT, &fsComment))
3819949213aSStephan Aßmus err = write_comment(fsComment, outDestination);
3829949213aSStephan Aßmus if (err == B_OK) {
3839949213aSStephan Aßmus char data[40];
3849949213aSStephan Aßmus sprintf(data, "%d %d %d\n", width, height, max);
3859949213aSStephan Aßmus err = outDestination->Write(data, strlen(data));
38672c5a125SAdrien Destugues if (err == (long) strlen(data))
38772c5a125SAdrien Destugues err = 0;
3889949213aSStephan Aßmus }
3899949213aSStephan Aßmus /* header done */
39072c5a125SAdrien Destugues } else {
3919949213aSStephan Aßmus dprintf(("PPMTranslator: write B_TRANSLATOR_BITMAP\n"));
3929949213aSStephan Aßmus /* B_TRANSLATOR_BITMAP header */
3939949213aSStephan Aßmus TranslatorBitmap hdr;
3949949213aSStephan Aßmus hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
3959949213aSStephan Aßmus hdr.bounds.left = B_HOST_TO_BENDIAN_FLOAT(0);
3969949213aSStephan Aßmus hdr.bounds.top = B_HOST_TO_BENDIAN_FLOAT(0);
3979949213aSStephan Aßmus hdr.bounds.right = B_HOST_TO_BENDIAN_FLOAT(width - 1);
3989949213aSStephan Aßmus hdr.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(height - 1);
3999949213aSStephan Aßmus hdr.rowBytes = B_HOST_TO_BENDIAN_INT32(out_rowbytes);
4009949213aSStephan Aßmus hdr.colors = (color_space) B_HOST_TO_BENDIAN_INT32(out_space);
4019949213aSStephan Aßmus hdr.dataSize = B_HOST_TO_BENDIAN_INT32(out_rowbytes * height);
40272c5a125SAdrien Destugues dprintf(("rowBytes is %d, width %d, out_space %x, space %x\n",
40372c5a125SAdrien Destugues out_rowbytes, width, out_space, space));
4049949213aSStephan Aßmus err = outDestination->Write(&hdr, sizeof(hdr));
4051a7bcf69SOliver Tappe dprintf(("PPMTranslator: Write() returns %" B_PRIx32 "\n", err));
4069949213aSStephan Aßmus #if DEBUG
4079949213aSStephan Aßmus {
40872c5a125SAdrien Destugues BBitmap* map
40972c5a125SAdrien Destugues = new BBitmap(BRect(0, 0, width - 1, height - 1), out_space);
4101a7bcf69SOliver Tappe printf("map rb = %" B_PRId32 "\n", map->BytesPerRow());
4119949213aSStephan Aßmus delete map;
4129949213aSStephan Aßmus }
4139949213aSStephan Aßmus #endif
41472c5a125SAdrien Destugues if (err == sizeof(hdr))
41572c5a125SAdrien Destugues err = 0;
4169949213aSStephan Aßmus /* header done */
4179949213aSStephan Aßmus }
418*9d617761SX512 if (err != B_OK)
4199949213aSStephan Aßmus return err > 0 ? B_IO_ERROR : err;
42072c5a125SAdrien Destugues /* Write data. Luckily, PPM and B_TRANSLATOR_BITMAP both scan from left to
42172c5a125SAdrien Destugues * right, top to bottom. */
42272c5a125SAdrien Destugues return copy_data(inSource, outDestination, rowbytes, out_rowbytes, height,
42372c5a125SAdrien Destugues max, ascii, out_ascii, space, out_space);
4249949213aSStephan Aßmus }
4259949213aSStephan Aßmus
4269949213aSStephan Aßmus
42772c5a125SAdrien Destugues class PPMView : public BView
4289949213aSStephan Aßmus {
4299949213aSStephan Aßmus public:
PPMView(const char * name,uint32 flags)43072c5a125SAdrien Destugues PPMView(const char* name, uint32 flags) : BView(name, flags)
4319949213aSStephan Aßmus {
432f0650dc9Slooncraz SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
433a76f629eSRyan Leavengood
43472c5a125SAdrien Destugues fTitle = new BStringView("title", B_TRANSLATE("PPM image translator"));
435037bc75eSJanus fTitle->SetFont(be_bold_font);
436a76f629eSRyan Leavengood
437a76f629eSRyan Leavengood char detail[100];
438a76f629eSRyan Leavengood int ver = static_cast<int>(translatorVersion);
439037bc75eSJanus sprintf(detail, B_TRANSLATE("Version %d.%d.%d, %s"), ver >> 8,
44072c5a125SAdrien Destugues ((ver >> 4) & 0xf), (ver & 0xf), __DATE__);
441037bc75eSJanus fDetail = new BStringView("detail", detail);
442a76f629eSRyan Leavengood
44372c5a125SAdrien Destugues fBasedOn = new BStringView(
44472c5a125SAdrien Destugues "basedOn", B_TRANSLATE("Based on PPMTranslator sample code"));
445a76f629eSRyan Leavengood
446037bc75eSJanus fCopyright = new BStringView("copyright",
44770d59669SSiarzhuk Zharski B_TRANSLATE("Sample code copyright 1999, Be Incorporated"));
448a76f629eSRyan Leavengood
449037bc75eSJanus fMenu = new BPopUpMenu("Color Space");
45072c5a125SAdrien Destugues fMenu->AddItem(
45172c5a125SAdrien Destugues new BMenuItem(B_TRANSLATE("None"), CSMessage(B_NO_COLOR_SPACE)));
45272c5a125SAdrien Destugues fMenu->AddItem(new BMenuItem(
45372c5a125SAdrien Destugues B_TRANSLATE("RGB 8:8:8 32 bits"), CSMessage(B_RGB32)));
454037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
45572c5a125SAdrien Destugues "bits"),
45672c5a125SAdrien Destugues CSMessage(B_RGBA32)));
45772c5a125SAdrien Destugues fMenu->AddItem(new BMenuItem(
45872c5a125SAdrien Destugues B_TRANSLATE("RGB 5:5:5 16 bits"), CSMessage(B_RGB15)));
459037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
46072c5a125SAdrien Destugues "bits"),
46172c5a125SAdrien Destugues CSMessage(B_RGBA15)));
46272c5a125SAdrien Destugues fMenu->AddItem(new BMenuItem(
46372c5a125SAdrien Destugues B_TRANSLATE("RGB 5:6:5 16 bits"), CSMessage(B_RGB16)));
464037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("System palette 8 "
46572c5a125SAdrien Destugues "bits"),
46672c5a125SAdrien Destugues CSMessage(B_CMAP8)));
467037bc75eSJanus fMenu->AddSeparatorItem();
46872c5a125SAdrien Destugues fMenu->AddItem(
46972c5a125SAdrien Destugues new BMenuItem(B_TRANSLATE("Grayscale 8 bits"), CSMessage(B_GRAY8)));
47072c5a125SAdrien Destugues fMenu->AddItem(
47172c5a125SAdrien Destugues new BMenuItem(B_TRANSLATE("Bitmap 1 bit"), CSMessage(B_GRAY1)));
47272c5a125SAdrien Destugues fMenu->AddItem(new BMenuItem(
47372c5a125SAdrien Destugues B_TRANSLATE("CMY 8:8:8 32 bits"), CSMessage(B_CMY32)));
474037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYA 8:8:8:8 32 "
47572c5a125SAdrien Destugues "bits"),
47672c5a125SAdrien Destugues CSMessage(B_CMYA32)));
477037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYK 8:8:8:8 32 "
47872c5a125SAdrien Destugues "bits"),
47972c5a125SAdrien Destugues CSMessage(B_CMYK32)));
480037bc75eSJanus fMenu->AddSeparatorItem();
481037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 8:8:8 32 bits "
48272c5a125SAdrien Destugues "big-endian"),
48372c5a125SAdrien Destugues CSMessage(B_RGB32_BIG)));
484037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
48572c5a125SAdrien Destugues "bits big-endian"),
48672c5a125SAdrien Destugues CSMessage(B_RGBA32_BIG)));
487037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:5:5 16 bits "
48872c5a125SAdrien Destugues "big-endian"),
48972c5a125SAdrien Destugues CSMessage(B_RGB15_BIG)));
490037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
49172c5a125SAdrien Destugues "bits big-endian"),
49272c5a125SAdrien Destugues CSMessage(B_RGBA15_BIG)));
493037bc75eSJanus fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:6:5 16 bits "
49472c5a125SAdrien Destugues "big-endian"),
49572c5a125SAdrien Destugues CSMessage(B_RGB16)));
49672c5a125SAdrien Destugues fField = new BMenuField(B_TRANSLATE("Input color space:"), fMenu);
497037bc75eSJanus fField->SetViewColor(ViewColor());
4989949213aSStephan Aßmus SelectColorSpace(g_settings.out_space);
4999949213aSStephan Aßmus BMessage* msg = new BMessage(CHANGE_ASCII);
500037bc75eSJanus fAscii = new BCheckBox(B_TRANSLATE("Write ASCII"), msg);
501a76f629eSRyan Leavengood if (g_settings.write_ascii)
502037bc75eSJanus fAscii->SetValue(1);
503037bc75eSJanus fAscii->SetViewColor(ViewColor());
504a76f629eSRyan Leavengood
505a76f629eSRyan Leavengood // Build the layout
506037bc75eSJanus BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
507037bc75eSJanus .SetInsets(B_USE_DEFAULT_SPACING)
508037bc75eSJanus .Add(fTitle)
509037bc75eSJanus .Add(fDetail)
510a76f629eSRyan Leavengood .AddGlue()
5117bfb4a1eSJanus .AddGroup(B_HORIZONTAL)
5127bfb4a1eSJanus .Add(fField)
5137bfb4a1eSJanus .AddGlue()
5147d48219bSHannah Boneß .End()
5157bfb4a1eSJanus .Add(fAscii)
516037bc75eSJanus .AddGlue()
517037bc75eSJanus .Add(fBasedOn)
518037bc75eSJanus .Add(fCopyright);
5199949213aSStephan Aßmus }
~PPMView()52072c5a125SAdrien Destugues ~PPMView() { /* nothing here */ }
5219949213aSStephan Aßmus
52272c5a125SAdrien Destugues enum { SET_COLOR_SPACE = 'ppm=', CHANGE_ASCII };
5239949213aSStephan Aßmus
MessageReceived(BMessage * message)52472c5a125SAdrien Destugues virtual void MessageReceived(BMessage* message)
5259949213aSStephan Aßmus {
5269949213aSStephan Aßmus if (message->what == SET_COLOR_SPACE) {
5279949213aSStephan Aßmus SetSettings(message);
52872c5a125SAdrien Destugues } else if (message->what == CHANGE_ASCII) {
5299949213aSStephan Aßmus BMessage msg;
530037bc75eSJanus msg.AddBool("ppm /ascii", fAscii->Value());
5319949213aSStephan Aßmus SetSettings(&msg);
53272c5a125SAdrien Destugues } else {
5339949213aSStephan Aßmus BView::MessageReceived(message);
5349949213aSStephan Aßmus }
5359949213aSStephan Aßmus }
AllAttached()5369949213aSStephan Aßmus virtual void AllAttached()
5379949213aSStephan Aßmus {
5389949213aSStephan Aßmus BView::AllAttached();
5399949213aSStephan Aßmus BMessenger msgr(this);
5409949213aSStephan Aßmus /* Tell all menu items we're the man. */
541037bc75eSJanus for (int ix = 0; ix < fMenu->CountItems(); ix++) {
542037bc75eSJanus BMenuItem* i = fMenu->ItemAt(ix);
543*9d617761SX512 if (i)
5449949213aSStephan Aßmus i->SetTarget(msgr);
5459949213aSStephan Aßmus }
546037bc75eSJanus fAscii->SetTarget(msgr);
5479949213aSStephan Aßmus }
5489949213aSStephan Aßmus
SetSettings(BMessage * message)54972c5a125SAdrien Destugues void SetSettings(BMessage* message)
5509949213aSStephan Aßmus {
5519949213aSStephan Aßmus g_settings_lock.Lock();
5529949213aSStephan Aßmus color_space space;
5539949213aSStephan Aßmus if (!message->FindInt32("space", (int32*) &space)) {
5549949213aSStephan Aßmus g_settings.out_space = space;
5559949213aSStephan Aßmus SelectColorSpace(space);
5569949213aSStephan Aßmus g_settings.settings_touched = true;
5579949213aSStephan Aßmus }
5589949213aSStephan Aßmus bool ascii;
5599949213aSStephan Aßmus if (!message->FindBool("ppm /ascii", &ascii)) {
5609949213aSStephan Aßmus g_settings.write_ascii = ascii;
5619949213aSStephan Aßmus g_settings.settings_touched = true;
5629949213aSStephan Aßmus }
5639949213aSStephan Aßmus g_settings_lock.Unlock();
5649949213aSStephan Aßmus }
5659949213aSStephan Aßmus
5669949213aSStephan Aßmus private:
567037bc75eSJanus BStringView* fTitle;
568037bc75eSJanus BStringView* fDetail;
569037bc75eSJanus BStringView* fBasedOn;
570037bc75eSJanus BStringView* fCopyright;
571037bc75eSJanus BPopUpMenu* fMenu;
572037bc75eSJanus BMenuField* fField;
573037bc75eSJanus BCheckBox* fAscii;
5749949213aSStephan Aßmus
CSMessage(color_space space)57572c5a125SAdrien Destugues BMessage* CSMessage(color_space space)
5769949213aSStephan Aßmus {
5779949213aSStephan Aßmus BMessage* ret = new BMessage(SET_COLOR_SPACE);
5789949213aSStephan Aßmus ret->AddInt32("space", space);
5799949213aSStephan Aßmus return ret;
5809949213aSStephan Aßmus }
5819949213aSStephan Aßmus
SelectColorSpace(color_space space)58272c5a125SAdrien Destugues void SelectColorSpace(color_space space)
5839949213aSStephan Aßmus {
584037bc75eSJanus for (int ix = 0; ix < fMenu->CountItems(); ix++) {
5859949213aSStephan Aßmus int32 s;
586037bc75eSJanus BMenuItem* i = fMenu->ItemAt(ix);
5879949213aSStephan Aßmus if (i) {
5889949213aSStephan Aßmus BMessage* m = i->Message();
5899949213aSStephan Aßmus if (m && !m->FindInt32("space", &s) && (s == space)) {
590037bc75eSJanus fMenu->Superitem()->SetLabel(i->Label());
5919949213aSStephan Aßmus break;
5929949213aSStephan Aßmus }
5939949213aSStephan Aßmus }
5949949213aSStephan Aßmus }
5959949213aSStephan Aßmus }
5969949213aSStephan Aßmus };
5979949213aSStephan Aßmus
5989949213aSStephan Aßmus
5999949213aSStephan Aßmus /* The view will get resized to what the parent thinks is */
6009949213aSStephan Aßmus /* reasonable. However, it will still receive MouseDowns etc. */
6019949213aSStephan Aßmus /* Your view should change settings in the translator immediately, */
6029949213aSStephan Aßmus /* taking care not to change parameters for a translation that is */
6039949213aSStephan Aßmus /* currently running. Typically, you'll have a global struct for */
6049949213aSStephan Aßmus /* settings that is atomically copied into the translator function */
6059949213aSStephan Aßmus /* as a local when translation starts. */
6069949213aSStephan Aßmus /* Store your settings wherever you feel like it. */
6079949213aSStephan Aßmus status_t
MakeConfig(BMessage * ioExtension,BView ** outView,BRect * outExtent)60871aec297SAdrien Destugues MakeConfig(BMessage* ioExtension, BView** outView, BRect* outExtent)
6099949213aSStephan Aßmus {
61072c5a125SAdrien Destugues PPMView* v
61172c5a125SAdrien Destugues = new PPMView(B_TRANSLATE("PPMTranslator Settings"), B_WILL_DRAW);
6129949213aSStephan Aßmus *outView = v;
61372c5a125SAdrien Destugues v->ResizeTo(v->ExplicitPreferredSize());
61472c5a125SAdrien Destugues ;
6159949213aSStephan Aßmus *outExtent = v->Bounds();
616*9d617761SX512 if (ioExtension)
6179949213aSStephan Aßmus v->SetSettings(ioExtension);
6189949213aSStephan Aßmus return B_OK;
6199949213aSStephan Aßmus }
6209949213aSStephan Aßmus
6219949213aSStephan Aßmus
6229949213aSStephan Aßmus /* Copy your current settings to a BMessage that may be passed */
6239949213aSStephan Aßmus /* to BTranslators::Translate at some later time when the user wants to */
6249949213aSStephan Aßmus /* use whatever settings you're using right now. */
6259949213aSStephan Aßmus status_t
GetConfigMessage(BMessage * ioExtension)62671aec297SAdrien Destugues GetConfigMessage(BMessage* ioExtension)
6279949213aSStephan Aßmus {
6289949213aSStephan Aßmus status_t err = B_OK;
6299949213aSStephan Aßmus const char* name = B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE;
6309949213aSStephan Aßmus g_settings_lock.Lock();
6319949213aSStephan Aßmus (void) ioExtension->RemoveName(name);
6329949213aSStephan Aßmus err = ioExtension->AddInt32(name, g_settings.out_space);
6339949213aSStephan Aßmus g_settings_lock.Unlock();
6349949213aSStephan Aßmus return err;
6359949213aSStephan Aßmus }
6369949213aSStephan Aßmus
6379949213aSStephan Aßmus
6389949213aSStephan Aßmus status_t
read_ppm_header(BDataIO * inSource,int * width,int * rowbytes,int * height,int * max,bool * ascii,color_space * space,bool * is_ppm,char ** comment)63972c5a125SAdrien Destugues read_ppm_header(BDataIO* inSource, int* width, int* rowbytes, int* height,
64072c5a125SAdrien Destugues int* max, bool* ascii, color_space* space, bool* is_ppm, char** comment)
6419949213aSStephan Aßmus {
6429949213aSStephan Aßmus /* check for PPM magic number */
6439949213aSStephan Aßmus char ch[2];
64458913f60SAdrien Destugues bool monochrome = false;
64558913f60SAdrien Destugues bool greyscale = false;
646*9d617761SX512 if (inSource->Read(ch, 2) != 2)
6479949213aSStephan Aßmus return B_NO_TRANSLATOR;
6489949213aSStephan Aßmus /* look for magic number */
6499949213aSStephan Aßmus if (ch[0] != 'P') {
6509949213aSStephan Aßmus /* B_TRANSLATOR_BITMAP magic? */
6519949213aSStephan Aßmus if (ch[0] == 'b' && ch[1] == 'i') {
6529949213aSStephan Aßmus *is_ppm = false;
65372c5a125SAdrien Destugues return read_bits_header(
65472c5a125SAdrien Destugues inSource, 2, width, rowbytes, height, max, ascii, space);
6559949213aSStephan Aßmus }
6569949213aSStephan Aßmus return B_NO_TRANSLATOR;
6579949213aSStephan Aßmus }
6589949213aSStephan Aßmus *is_ppm = true;
65958913f60SAdrien Destugues if (ch[1] == '6' || ch[1] == '5' || ch[1] == '4') {
6609949213aSStephan Aßmus *ascii = false;
66158913f60SAdrien Destugues } else if (ch[1] == '3' || ch[1] == '2' || ch[1] == '1') {
6629949213aSStephan Aßmus *ascii = true;
66372c5a125SAdrien Destugues } else {
6649949213aSStephan Aßmus return B_NO_TRANSLATOR;
6659949213aSStephan Aßmus }
66658913f60SAdrien Destugues
66758913f60SAdrien Destugues if (ch[1] == '4' || ch[1] == '1')
66858913f60SAdrien Destugues monochrome = true;
66958913f60SAdrien Destugues else if (ch[1] == '5' || ch[1] == '2')
67058913f60SAdrien Destugues greyscale = true;
67158913f60SAdrien Destugues
6729949213aSStephan Aßmus // status_t err = B_NO_TRANSLATOR;
6739949213aSStephan Aßmus enum scan_state {
6749949213aSStephan Aßmus scan_width,
6759949213aSStephan Aßmus scan_height,
6769949213aSStephan Aßmus scan_max,
6779949213aSStephan Aßmus scan_white
67872c5a125SAdrien Destugues } state
67972c5a125SAdrien Destugues = scan_width;
6809949213aSStephan Aßmus int* scan = NULL;
6819949213aSStephan Aßmus bool in_comment = false;
68258913f60SAdrien Destugues if (monochrome)
68358913f60SAdrien Destugues *space = B_GRAY1;
68458913f60SAdrien Destugues else if (greyscale)
68558913f60SAdrien Destugues *space = B_GRAY8;
68658913f60SAdrien Destugues else
6879949213aSStephan Aßmus *space = B_RGB24_BIG;
6889949213aSStephan Aßmus /* The description of PPM is slightly ambiguous as far as comments */
6899949213aSStephan Aßmus /* go. We choose to allow comments anywhere, in the spirit of laxness. */
6909949213aSStephan Aßmus /* See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/PPM.txt */
6919949213aSStephan Aßmus int comlen = 0;
6929949213aSStephan Aßmus int comptr = 0;
6939949213aSStephan Aßmus while (inSource->Read(ch, 1) == 1) {
6949949213aSStephan Aßmus if (in_comment && (ch[0] != 10) && (ch[0] != 13)) {
6959949213aSStephan Aßmus if (comment) { /* collect comment(s) into comment string */
6969949213aSStephan Aßmus if (comptr >= comlen - 1) {
6979949213aSStephan Aßmus char* n = (char*) realloc(*comment, comlen + 100);
6989949213aSStephan Aßmus if (!n) {
6999949213aSStephan Aßmus free(*comment);
7009949213aSStephan Aßmus *comment = NULL;
7019949213aSStephan Aßmus }
7029949213aSStephan Aßmus *comment = n;
7039949213aSStephan Aßmus comlen += 100;
7049949213aSStephan Aßmus }
7059949213aSStephan Aßmus (*comment)[comptr++] = ch[0];
7069949213aSStephan Aßmus (*comment)[comptr] = 0;
7079949213aSStephan Aßmus }
7089949213aSStephan Aßmus continue;
7099949213aSStephan Aßmus }
7109949213aSStephan Aßmus in_comment = false;
7119949213aSStephan Aßmus if (ch[0] == '#') {
7129949213aSStephan Aßmus in_comment = true;
7139949213aSStephan Aßmus continue;
7149949213aSStephan Aßmus }
7159949213aSStephan Aßmus /* once we're done with whitespace after max, we're done with header */
7169949213aSStephan Aßmus if (isdigit(ch[0])) {
7179949213aSStephan Aßmus if (!scan) { /* first digit for this value */
7189949213aSStephan Aßmus switch (state) {
7199949213aSStephan Aßmus case scan_width:
7209949213aSStephan Aßmus scan = width;
7219949213aSStephan Aßmus break;
7229949213aSStephan Aßmus case scan_height:
72358913f60SAdrien Destugues if (monochrome)
72458913f60SAdrien Destugues *rowbytes = (*width + 7) / 8;
72558913f60SAdrien Destugues else if (greyscale)
72658913f60SAdrien Destugues *rowbytes = *width;
72758913f60SAdrien Destugues else
7289949213aSStephan Aßmus *rowbytes = *width * 3;
7299949213aSStephan Aßmus scan = height;
7309949213aSStephan Aßmus break;
7319949213aSStephan Aßmus case scan_max:
7329949213aSStephan Aßmus scan = max;
7339949213aSStephan Aßmus break;
7349949213aSStephan Aßmus default:
7359949213aSStephan Aßmus return B_OK; /* done with header, all OK */
7369949213aSStephan Aßmus }
7379949213aSStephan Aßmus *scan = 0;
7389949213aSStephan Aßmus }
7399949213aSStephan Aßmus *scan = (*scan) * 10 + (ch[0] - '0');
74072c5a125SAdrien Destugues } else if (isspace(ch[0])) {
7419949213aSStephan Aßmus if (scan) { /* are we done with one value? */
7429949213aSStephan Aßmus scan = NULL;
7439949213aSStephan Aßmus state = (enum scan_state)(state + 1);
74458913f60SAdrien Destugues
74558913f60SAdrien Destugues /* in monochrome ppm, there is no max in the file, so we
74658913f60SAdrien Destugues * skip that step. */
74758913f60SAdrien Destugues if ((state == scan_max) && monochrome) {
74858913f60SAdrien Destugues state = (enum scan_state)(state + 1);
74958913f60SAdrien Destugues *max = 1;
7509949213aSStephan Aßmus }
75158913f60SAdrien Destugues }
75258913f60SAdrien Destugues
75372c5a125SAdrien Destugues if (state == scan_white) { /* we only ever read one whitespace,
75472c5a125SAdrien Destugues since we skip space */
75572c5a125SAdrien Destugues return B_OK; /* when reading ASCII, and there is a single
75672c5a125SAdrien Destugues whitespace after max in raw mode */
7579949213aSStephan Aßmus }
75872c5a125SAdrien Destugues } else {
759*9d617761SX512 if (state != scan_white)
7609949213aSStephan Aßmus return B_NO_TRANSLATOR;
7619949213aSStephan Aßmus return B_OK; /* header done */
7629949213aSStephan Aßmus }
7639949213aSStephan Aßmus }
7649949213aSStephan Aßmus return B_NO_TRANSLATOR;
7659949213aSStephan Aßmus }
7669949213aSStephan Aßmus
7679949213aSStephan Aßmus
7689949213aSStephan Aßmus status_t
read_bits_header(BDataIO * io,int skipped,int * width,int * rowbytes,int * height,int * max,bool * ascii,color_space * space)76972c5a125SAdrien Destugues read_bits_header(BDataIO* io, int skipped, int* width, int* rowbytes,
77072c5a125SAdrien Destugues int* height, int* max, bool* ascii, color_space* space)
7719949213aSStephan Aßmus {
7729949213aSStephan Aßmus /* read the rest of a possible B_TRANSLATOR_BITMAP header */
77372c5a125SAdrien Destugues if (skipped < 0 || skipped > 4)
77472c5a125SAdrien Destugues return B_NO_TRANSLATOR;
7759949213aSStephan Aßmus int rd = sizeof(TranslatorBitmap) - skipped;
7769949213aSStephan Aßmus TranslatorBitmap hdr;
7779949213aSStephan Aßmus /* pre-initialize magic because we might have skipped part of it already */
7789949213aSStephan Aßmus hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
7799949213aSStephan Aßmus char* ptr = (char*) &hdr;
780*9d617761SX512 if (io->Read(ptr + skipped, rd) != rd)
7819949213aSStephan Aßmus return B_NO_TRANSLATOR;
7829949213aSStephan Aßmus /* swap header values */
7839949213aSStephan Aßmus hdr.magic = B_BENDIAN_TO_HOST_INT32(hdr.magic);
7849949213aSStephan Aßmus hdr.bounds.left = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.left);
7859949213aSStephan Aßmus hdr.bounds.right = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.right);
7869949213aSStephan Aßmus hdr.bounds.top = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.top);
7879949213aSStephan Aßmus hdr.bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.bottom);
7889949213aSStephan Aßmus hdr.rowBytes = B_BENDIAN_TO_HOST_INT32(hdr.rowBytes);
7899949213aSStephan Aßmus hdr.colors = (color_space) B_BENDIAN_TO_HOST_INT32(hdr.colors);
7909949213aSStephan Aßmus hdr.dataSize = B_BENDIAN_TO_HOST_INT32(hdr.dataSize);
7919949213aSStephan Aßmus /* sanity checking */
792*9d617761SX512 if (hdr.magic != B_TRANSLATOR_BITMAP)
7939949213aSStephan Aßmus return B_NO_TRANSLATOR;
79472c5a125SAdrien Destugues if (hdr.colors & 0xffff0000) { /* according to <GraphicsDefs.h> this is a
79572c5a125SAdrien Destugues reasonable check. */
7969949213aSStephan Aßmus return B_NO_TRANSLATOR;
7979949213aSStephan Aßmus }
798*9d617761SX512 if (hdr.rowBytes * (hdr.bounds.Height() + 1) > hdr.dataSize)
7999949213aSStephan Aßmus return B_NO_TRANSLATOR;
8009949213aSStephan Aßmus /* return information about the data in the stream */
8019949213aSStephan Aßmus *width = (int) hdr.bounds.Width() + 1;
8029949213aSStephan Aßmus *rowbytes = hdr.rowBytes;
8039949213aSStephan Aßmus *height = (int) hdr.bounds.Height() + 1;
8049949213aSStephan Aßmus *max = 255;
8059949213aSStephan Aßmus *ascii = false;
8069949213aSStephan Aßmus *space = hdr.colors;
8079949213aSStephan Aßmus return B_OK;
8089949213aSStephan Aßmus }
8099949213aSStephan Aßmus
8109949213aSStephan Aßmus
81172c5a125SAdrien Destugues /* String may contain newlines, after which we need to insert extra hash signs.
81272c5a125SAdrien Destugues */
8139949213aSStephan Aßmus status_t
write_comment(const char * str,BDataIO * io)81472c5a125SAdrien Destugues write_comment(const char* str, BDataIO* io)
8159949213aSStephan Aßmus {
8169949213aSStephan Aßmus const char* end = str + strlen(str);
8179949213aSStephan Aßmus const char* ptr = str;
8189949213aSStephan Aßmus status_t err = B_OK;
8199949213aSStephan Aßmus /* write each line as it's found */
8209949213aSStephan Aßmus while ((ptr < end) && !err) {
8219949213aSStephan Aßmus if ((*ptr == 10) || (*ptr == 13)) {
8229949213aSStephan Aßmus err = io->Write("# ", 2);
8239949213aSStephan Aßmus if (err == 2) {
8249949213aSStephan Aßmus err = io->Write(str, ptr - str);
8259949213aSStephan Aßmus if (err == ptr - str) {
826*9d617761SX512 if (io->Write("\n", 1) == 1)
8279949213aSStephan Aßmus err = 0;
8289949213aSStephan Aßmus }
8299949213aSStephan Aßmus }
8309949213aSStephan Aßmus str = ptr + 1;
8319949213aSStephan Aßmus }
8329949213aSStephan Aßmus ptr++;
8339949213aSStephan Aßmus }
8349949213aSStephan Aßmus /* write the last data, if any, as a line */
8359949213aSStephan Aßmus if (ptr > str) {
8369949213aSStephan Aßmus err = io->Write("# ", 2);
8379949213aSStephan Aßmus if (err == 2) {
8389949213aSStephan Aßmus err = io->Write(str, ptr - str);
8399949213aSStephan Aßmus if (err == ptr - str) {
840*9d617761SX512 if (io->Write("\n", 1) == 1)
8419949213aSStephan Aßmus err = 0;
8429949213aSStephan Aßmus }
8439949213aSStephan Aßmus }
8449949213aSStephan Aßmus }
845*9d617761SX512 if (err > 0)
8469949213aSStephan Aßmus err = B_IO_ERROR;
8479949213aSStephan Aßmus return err;
8489949213aSStephan Aßmus }
8499949213aSStephan Aßmus
8509949213aSStephan Aßmus
8519949213aSStephan Aßmus static status_t
read_ascii_line(BDataIO * in,int max,unsigned char * data,int rowbytes)85272c5a125SAdrien Destugues read_ascii_line(BDataIO* in, int max, unsigned char* data, int rowbytes)
8539949213aSStephan Aßmus {
8549949213aSStephan Aßmus char ch;
8559949213aSStephan Aßmus status_t err;
8569949213aSStephan Aßmus // int nread = 0;
8579949213aSStephan Aßmus bool comment = false;
8589949213aSStephan Aßmus int val = 0;
8599949213aSStephan Aßmus bool dig = false;
8609949213aSStephan Aßmus while ((err = in->Read(&ch, 1)) == 1) {
8619949213aSStephan Aßmus if (comment) {
862*9d617761SX512 if ((ch == '\n') || (ch == '\r'))
8639949213aSStephan Aßmus comment = false;
8649949213aSStephan Aßmus }
8659949213aSStephan Aßmus if (isdigit(ch)) {
8669949213aSStephan Aßmus dig = true;
8679949213aSStephan Aßmus val = val * 10 + (ch - '0');
86872c5a125SAdrien Destugues } else {
8699949213aSStephan Aßmus if (dig) {
8709949213aSStephan Aßmus *(data++) = val * 255 / max;
8719949213aSStephan Aßmus val = 0;
8729949213aSStephan Aßmus rowbytes--;
8739949213aSStephan Aßmus dig = false;
8749949213aSStephan Aßmus }
8759949213aSStephan Aßmus if (ch == '#') {
8769949213aSStephan Aßmus comment = true;
8779949213aSStephan Aßmus continue;
8789949213aSStephan Aßmus }
8799949213aSStephan Aßmus }
880*9d617761SX512 if (rowbytes < 1)
8819949213aSStephan Aßmus break;
8829949213aSStephan Aßmus }
8839949213aSStephan Aßmus if (dig) {
8849949213aSStephan Aßmus *(data++) = val * 255 / max;
8859949213aSStephan Aßmus val = 0;
8869949213aSStephan Aßmus rowbytes--;
8879949213aSStephan Aßmus dig = false;
8889949213aSStephan Aßmus }
889*9d617761SX512 if (rowbytes < 1)
8909949213aSStephan Aßmus return B_OK;
8919949213aSStephan Aßmus return B_IO_ERROR;
8929949213aSStephan Aßmus }
8939949213aSStephan Aßmus
8949949213aSStephan Aßmus
8959949213aSStephan Aßmus static status_t
write_ascii_line(BDataIO * out,unsigned char * data,int rowbytes)89672c5a125SAdrien Destugues write_ascii_line(BDataIO* out, unsigned char* data, int rowbytes)
8979949213aSStephan Aßmus {
8989949213aSStephan Aßmus char buffer[20];
8999949213aSStephan Aßmus int linelen = 0;
9009949213aSStephan Aßmus while (rowbytes > 2) {
9019949213aSStephan Aßmus sprintf(buffer, "%d %d %d ", data[0], data[1], data[2]);
9029949213aSStephan Aßmus rowbytes -= 3;
9039949213aSStephan Aßmus int l = strlen(buffer);
9049949213aSStephan Aßmus if (l + linelen > 70) {
9059949213aSStephan Aßmus out->Write("\n", 1);
9069949213aSStephan Aßmus linelen = 0;
9079949213aSStephan Aßmus }
908*9d617761SX512 if (out->Write(buffer, l) != l)
9099949213aSStephan Aßmus return B_IO_ERROR;
9109949213aSStephan Aßmus linelen += l;
9119949213aSStephan Aßmus data += 3;
9129949213aSStephan Aßmus }
9139949213aSStephan Aßmus out->Write("\n", 1); /* terminate each scanline */
9149949213aSStephan Aßmus return B_OK;
9159949213aSStephan Aßmus }
9169949213aSStephan Aßmus
9179949213aSStephan Aßmus
9189949213aSStephan Aßmus static unsigned char*
make_scale_data(int max)91972c5a125SAdrien Destugues make_scale_data(int max)
9209949213aSStephan Aßmus {
9219949213aSStephan Aßmus unsigned char* ptr = (unsigned char*) malloc(max);
922*9d617761SX512 for (int ix = 0; ix < max; ix++)
9239949213aSStephan Aßmus ptr[ix] = ix * 255 / max;
9249949213aSStephan Aßmus return ptr;
9259949213aSStephan Aßmus }
9269949213aSStephan Aßmus
9279949213aSStephan Aßmus
9289949213aSStephan Aßmus static void
scale_data(unsigned char * scale,unsigned char * data,int bytes)92972c5a125SAdrien Destugues scale_data(unsigned char* scale, unsigned char* data, int bytes)
9309949213aSStephan Aßmus {
931*9d617761SX512 for (int ix = 0; ix < bytes; ix++)
9329949213aSStephan Aßmus data[ix] = scale[data[ix]];
9339949213aSStephan Aßmus }
9349949213aSStephan Aßmus
9359949213aSStephan Aßmus
9369949213aSStephan Aßmus status_t
copy_data(BDataIO * in,BDataIO * out,int rowbytes,int out_rowbytes,int height,int max,bool in_ascii,bool out_ascii,color_space in_space,color_space out_space)93772c5a125SAdrien Destugues copy_data(BDataIO* in, BDataIO* out, int rowbytes, int out_rowbytes, int height,
93872c5a125SAdrien Destugues int max, bool in_ascii, bool out_ascii, color_space in_space,
9399949213aSStephan Aßmus color_space out_space)
9409949213aSStephan Aßmus {
9415d57984bSAdrien Destugues dprintf(("copy_data(%d, %d, %d, %d, %x, %x)\n", rowbytes, out_rowbytes,
94272c5a125SAdrien Destugues height, max, in_space, out_space));
94371aec297SAdrien Destugues
94471aec297SAdrien Destugues // We will be reading 1 char at a time, so use a buffer
94571aec297SAdrien Destugues BBufferedDataIO inBuffer(*in, 65536, false);
94671aec297SAdrien Destugues
9479949213aSStephan Aßmus /* We read/write one scanline at a time. */
9489949213aSStephan Aßmus unsigned char* data = (unsigned char*) malloc(rowbytes);
9499949213aSStephan Aßmus unsigned char* out_data = (unsigned char*) malloc(out_rowbytes);
9509949213aSStephan Aßmus if (data == NULL || out_data == NULL) {
9519949213aSStephan Aßmus free(data);
9529949213aSStephan Aßmus free(out_data);
9539949213aSStephan Aßmus return B_NO_MEMORY;
9549949213aSStephan Aßmus }
9559949213aSStephan Aßmus unsigned char* scale = NULL;
956*9d617761SX512 if (max != 255 && in_space != B_GRAY1)
9579949213aSStephan Aßmus scale = make_scale_data(max);
9589949213aSStephan Aßmus status_t err = B_OK;
9599949213aSStephan Aßmus /* There is no data format conversion, so we can just copy data. */
9609949213aSStephan Aßmus while ((height-- > 0) && !err) {
9619949213aSStephan Aßmus if (in_ascii) {
96271aec297SAdrien Destugues err = read_ascii_line(&inBuffer, max, data, rowbytes);
96372c5a125SAdrien Destugues } else {
96471aec297SAdrien Destugues err = inBuffer.Read(data, rowbytes);
965*9d617761SX512 if (err == rowbytes)
9669949213aSStephan Aßmus err = B_OK;
967*9d617761SX512 if (scale) /* for reading PPM that is smaller than 8 bit */
9689949213aSStephan Aßmus scale_data(scale, data, rowbytes);
9699949213aSStephan Aßmus }
9709949213aSStephan Aßmus if (err == B_OK) {
9719949213aSStephan Aßmus unsigned char* wbuf = data;
9729949213aSStephan Aßmus if (in_space != out_space) {
97372c5a125SAdrien Destugues err = convert_space(
97472c5a125SAdrien Destugues in_space, out_space, data, rowbytes, out_data);
9759949213aSStephan Aßmus wbuf = out_data;
9769949213aSStephan Aßmus }
9779949213aSStephan Aßmus if (!err && out_ascii) {
9789949213aSStephan Aßmus err = write_ascii_line(out, wbuf, out_rowbytes);
97972c5a125SAdrien Destugues } else if (!err) {
9809949213aSStephan Aßmus err = out->Write(wbuf, out_rowbytes);
981*9d617761SX512 if (err == out_rowbytes)
9829949213aSStephan Aßmus err = B_OK;
9839949213aSStephan Aßmus }
9849949213aSStephan Aßmus }
9859949213aSStephan Aßmus }
9869949213aSStephan Aßmus free(data);
9879949213aSStephan Aßmus free(out_data);
9889949213aSStephan Aßmus free(scale);
9899949213aSStephan Aßmus return err > 0 ? B_IO_ERROR : err;
9909949213aSStephan Aßmus }
991