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