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