diff --git a/src/add-ons/translators/ppmtranslator/Jamfile b/src/add-ons/translators/ppmtranslator/Jamfile new file mode 100644 index 0000000000..0685f71d41 --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/Jamfile @@ -0,0 +1,5 @@ +SubDir OBOS_TOP src add-ons translators ppmtranslator ; + +Translator PPMTranslator : PPMMain.cpp PPMTranslator.cpp colorspace.cpp ; + +LinkSharedOSLibs PPMTranslator : be translation ; diff --git a/src/add-ons/translators/ppmtranslator/LICENSE b/src/add-ons/translators/ppmtranslator/LICENSE new file mode 100644 index 0000000000..86a4268fa9 --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/LICENSE @@ -0,0 +1,31 @@ +---------------------- +Be Sample Code License +---------------------- + +Copyright 1991-1999, Be Incorporated. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/add-ons/translators/ppmtranslator/PPMMain.cpp b/src/add-ons/translators/ppmtranslator/PPMMain.cpp new file mode 100644 index 0000000000..ee5c1b4ac0 --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/PPMMain.cpp @@ -0,0 +1,74 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#include +#include +#include +#include +#include +#include + +#include + + +BPoint get_window_origin(); +void set_window_origin(BPoint pt); + +class PPMWindow : + public BWindow +{ +public: + PPMWindow( + BRect area) : + BWindow(area, "PPMTranslator", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) + { + } + ~PPMWindow() + { + BPoint pt(0,0); + ConvertToScreen(&pt); + set_window_origin(pt); + be_app->PostMessage(B_QUIT_REQUESTED); + } +}; + +int +main() +{ + BApplication app("application/x-vnd.hplus-ppm-translator"); + BView * v = NULL; + BRect r(0,0,200,100); + if (MakeConfig(NULL, &v, &r)) { + BAlert * err = new BAlert("Error", "Something is wrong with the PPMTranslator!", "OK"); + err->Go(); + return 1; + } + PPMWindow *w = new PPMWindow(r); + v->ResizeTo(r.Width(), r.Height()); + w->AddChild(v); + BPoint o = get_window_origin(); + { + BScreen scrn; + BRect f = scrn.Frame(); + f.InsetBy(10,23); + /* if not in a good place, start where the cursor is */ + if (!f.Contains(o)) { + uint32 i; + v->GetMouse(&o, &i, false); + o.x -= r.Width()/2; + o.y -= r.Height()/2; + /* clamp location to screen */ + if (o.x < f.left) o.x = f.left; + if (o.y < f.top) o.y = f.top; + if (o.x > f.right) o.x = f.right; + if (o.y > f.bottom) o.y = f.bottom; + } + } + w->MoveTo(o); + w->Show(); + app.Run(); + return 0; +} + diff --git a/src/add-ons/translators/ppmtranslator/PPMTranslator.cpp b/src/add-ons/translators/ppmtranslator/PPMTranslator.cpp new file mode 100644 index 0000000000..69fe7bff71 --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/PPMTranslator.cpp @@ -0,0 +1,992 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +/* Parse the ASCII and raw versions of PPM. */ +/* Note that the parsing of ASCII is very inefficient, because BFile */ +/* does not buffer data. We should wrap a buffering thing around */ +/* the input or output when they are in ASCII mode. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "colorspace.h" + + +#if DEBUG + #define dprintf(x) printf x +#else + #define dprintf(x) +#endif + + +#if !defined(_PR3_COMPATIBLE_) /* R4 headers? Else we need to define these constants. */ + #define B_CMY24 ((color_space)0xC001) /* C[7:0] M[7:0] Y[7:0] No gray removal done */ + #define B_CMY32 ((color_space)0xC002) /* C[7:0] M[7:0] Y[7:0] X[7:0] No gray removal done */ + #define B_CMYA32 ((color_space)0xE002) /* C[7:0] M[7:0] Y[7:0] A[7:0] No gray removal done */ + #define B_CMYK32 ((color_space)0xC003) /* C[7:0] M[7:0] Y[7:0] K[7:0] */ +#endif + + +/* These three data items are exported by every translator. */ +char translatorName[] = "PPMTranslator"; +char translatorInfo[] = "Reads and writes images in the PPM file format. http://www.be.com/"; +int32 translatorVersion = 100; /* format is revision+minor*10+major*100 */ + + +/* Be reserves all codes with non-lowecase letters in them. */ +/* Luckily, there is already a reserved code for PPM. If you */ +/* make up your own for a new type, use lower-case letters. */ +#define PPM_TYPE 'PPM ' + + +/* These two data arrays are a really good idea to export from Translators, but not required. */ +translation_format inputFormats[] = { + { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap", "Be Bitmap Format (PPMHandler)" }, + { PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-ppm", "PPM portable pixmap format" }, + { 0, 0, 0, 0, "\0", "\0" } +}; /* optional (else Identify is always called) */ + +translation_format outputFormats[] = { + { B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap", "Be Bitmap Format (PPMHandler)" }, + { PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-ppm", "PPM portable pixmap format" }, + { 0, 0, 0, 0, "\0", "\0" } +}; /* optional (else Translate is called anyway) */ + +/* Translators that don't export outputFormats */ +/* will not be considered by files looking for */ +/* specific output formats. */ + + +/* We keep our settings in a global struct, and wrap a lock around them. */ +struct ppm_settings { + color_space out_space; + BPoint window_pos; + bool write_ascii; + bool settings_touched; +}; +BLocker g_settings_lock("PPM settings lock"); +ppm_settings g_settings; + +BPoint get_window_origin(); +void set_window_origin(BPoint pos); +BPoint get_window_origin() +{ + BPoint ret; + g_settings_lock.Lock(); + ret = g_settings.window_pos; + g_settings_lock.Unlock(); + return ret; +} + +void set_window_origin(BPoint pos) +{ + g_settings_lock.Lock(); + g_settings.window_pos = pos; + g_settings.settings_touched = true; + g_settings_lock.Unlock(); +} + + +class PrefsLoader { +public: + PrefsLoader( + const char * str) + { + dprintf(("PPMTranslator: PrefsLoader()\n")); + /* defaults */ + g_settings.out_space = B_NO_COLOR_SPACE; + g_settings.window_pos = B_ORIGIN; + g_settings.write_ascii = false; + g_settings.settings_touched = false; + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) { + path.SetTo("/tmp"); + } + path.Append(str); + FILE * f = fopen(path.Path(), "r"); + /* parse text settings file -- this should be a library... */ + if (f) { + char line[1024]; + char name[32]; + char * ptr; + while (true) { + line[0] = 0; + fgets(line, 1024, f); + if (!line[0]) { + break; + } + /* remember: line ends with \n, so printf()s don't have to */ + ptr = line; + while (isspace(*ptr)) { + ptr++; + } + if (*ptr == '#' || !*ptr) { /* comment or blank */ + continue; + } + if (sscanf(ptr, "%31[a-zA-Z_0-9] =", name) != 1) { + fprintf(stderr, "unknown PPMTranslator settings line: %s", line); + } + else { + if (!strcmp(name, "color_space")) { + while (*ptr != '=') { + ptr++; + } + ptr++; + if (sscanf(ptr, "%d", (int*)&g_settings.out_space) != 1) { + fprintf(stderr, "illegal color space in PPMTranslator settings: %s", ptr); + } + } + else if (!strcmp(name, "window_pos")) { + while (*ptr != '=') { + ptr++; + } + ptr++; + if (sscanf(ptr, "%f,%f", &g_settings.window_pos.x, &g_settings.window_pos.y) != 2) { + fprintf(stderr, "illegal window position in PPMTranslator settings: %s", ptr); + } + } + else if (!strcmp(name, "write_ascii")) { + while (*ptr != '=') { + ptr++; + } + ptr++; + int ascii = g_settings.write_ascii; + if (sscanf(ptr, "%d", &ascii) != 1) { + fprintf(stderr, "illegal write_ascii value in PPMTranslator settings: %s", ptr); + } + else { + g_settings.write_ascii = ascii; + } + } + else { + fprintf(stderr, "unknown PPMTranslator setting: %s", line); + } + } + } + fclose(f); + } + } + ~PrefsLoader() + { + /* No need writing settings if there aren't any */ + if (g_settings.settings_touched) { + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path)) { + path.SetTo("/tmp"); + } + path.Append("PPMTranslator_Settings"); + FILE * f = fopen(path.Path(), "w"); + if (f) { + fprintf(f, "# PPMTranslator settings version %.2f\n", (float)translatorVersion/100.0); + fprintf(f, "color_space = %d\n", g_settings.out_space); + fprintf(f, "window_pos = %g,%g\n", g_settings.window_pos.x, g_settings.window_pos.y); + fprintf(f, "write_ascii = %d\n", g_settings.write_ascii ? 1 : 0); + fclose(f); + } + } + } +}; + +PrefsLoader g_prefs_loader("PPMTranslator_Settings"); + +/* Some prototypes for functions we use. */ +status_t read_ppm_header(BDataIO * io, int * width, int * rowbytes, int * height, + int * max, bool * ascii, color_space * space, bool * is_ppm, char ** comment); +status_t read_bits_header(BDataIO * io, int skipped, int * width, int * rowbytes, + int * height, int * max, bool * ascii, color_space * space); +status_t write_comment(const char * str, BDataIO * io); +status_t copy_data(BDataIO * in, BDataIO * out, int rowbytes, int out_rowbytes, + int height, int max, bool in_ascii, bool out_ascii, color_space in_space, + color_space out_space); + + /* Return B_NO_TRANSLATOR if not handling this data. */ + /* Even if inputFormats exists, may be called for data without hints */ + /* If outType is not 0, must be able to export in wanted format */ + +status_t +Identify( /* required */ + BPositionIO * inSource, + const translation_format * inFormat, /* can beNULL */ + BMessage * ioExtension, /* can be NULL */ + translator_info * outInfo, + uint32 outType) +{ + dprintf(("PPMTranslator: Identify()\n")); + /* Silence compiler warnings. */ + inFormat = inFormat; + ioExtension = ioExtension; + + /* Check that requested format is something we can deal with. */ + if (outType == 0) { + outType = B_TRANSLATOR_BITMAP; + } + if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) { + return B_NO_TRANSLATOR; + } + + /* Check header. */ + int width, rowbytes, height, max; + bool ascii, is_ppm; + color_space space; + status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max, &ascii, &space, &is_ppm, NULL); + if (err != B_OK) { + return err; + } + /* Stuff info into info struct -- Translation Kit will do "translator" for us. */ + outInfo->group = B_TRANSLATOR_BITMAP; + if (is_ppm) { + outInfo->type = PPM_TYPE; + outInfo->quality = 0.3; /* no alpha, etc */ + outInfo->capability = 0.8; /* we're pretty good at PPM reading, though */ + strcpy(outInfo->name, "PPM portable pixmap format"); + strcpy(outInfo->MIME, "image/x-ppm"); + } + else { + outInfo->type = B_TRANSLATOR_BITMAP; + outInfo->quality = 0.4; /* B_TRANSLATOR_BITMAP can do alpha, at least */ + outInfo->capability = 0.8; /* and we might not know many variations thereof */ + strcpy(outInfo->name, "Be Bitmap Format (PPMHandler)"); + strcpy(outInfo->MIME, "image/x-be-bitmap"); /* this is the MIME type of B_TRANSLATOR_BITMAP */ + } + return B_OK; +} + + + /* Return B_NO_TRANSLATOR if not handling the output format */ + /* If outputFormats exists, will only be called for those formats */ + +status_t +Translate( /* required */ + BPositionIO * inSource, + const translator_info * /* inInfo*/ , /* silence compiler warning */ + BMessage * ioExtension, /* can be NULL */ + uint32 outType, + BPositionIO * outDestination) +{ + dprintf(("PPMTranslator: Translate()\n")); + inSource->Seek(0, SEEK_SET); /* paranoia */ +// inInfo = inInfo; /* silence compiler warning */ + /* Check what we're being asked to produce. */ + if (!outType) { + outType = B_TRANSLATOR_BITMAP; + } + dprintf(("PPMTranslator: outType is '%c%c%c%c'\n", char(outType>>24), char(outType>>16), char(outType>>8), char(outType))); + if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE) { + return B_NO_TRANSLATOR; + } + + /* Figure out what we've been given (again). */ + int width, rowbytes, height, max; + bool ascii, is_ppm; + color_space space; + /* Read_ppm_header() will always return with stream at beginning of data */ + /* for both B_TRANSLATOR_BITMAP and PPM_TYPE, and indicate what it is. */ + char * comment = NULL; + status_t err = read_ppm_header(inSource, &width, &rowbytes, &height, &max, &ascii, &space, &is_ppm, &comment); + if (comment != NULL) { + if (ioExtension != NULL) { +#if defined(_PR3_COMPATIBLE_) /* R4 headers? */ + ioExtension->AddString(B_TRANSLATOR_EXT_COMMENT, comment); +#else + ioExtension->AddString("/comment", comment); +#endif + } + free(comment); + } + if (err < B_OK) { + dprintf(("read_ppm_header() error %s [%lx]\n", strerror(err), err)); + return err; + } + /* Check if we're configured to write ASCII format file. */ + bool out_ascii = false; + if (outType == PPM_TYPE) { + out_ascii = g_settings.write_ascii; + if (ioExtension != NULL) { + ioExtension->FindBool("ppm /ascii", &out_ascii); + } + } + err = B_OK; + /* Figure out which color space to convert to */ + color_space out_space; + int out_rowbytes; + g_settings_lock.Lock(); + if (outType == PPM_TYPE) { + out_space = B_RGB24_BIG; + out_rowbytes = 3*width; + } + else { /* When outputting to B_TRANSLATOR_BITMAP, follow user's wishes. */ +#if defined(_PR3_COMPATIBLE_) /* R4 headers? */ + if (!ioExtension || ioExtension->FindInt32(B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE, (int32*)&out_space) || +#else + if (!ioExtension || ioExtension->FindInt32("bits/space", (int32*)&out_space) || +#endif + (out_space == B_NO_COLOR_SPACE)) { + if (g_settings.out_space == B_NO_COLOR_SPACE) { + switch (space) { /* The 24-bit versions are pretty silly, use 32 instead. */ + case B_RGB24: + case B_RGB24_BIG: + out_space = B_RGB32; + break; + default: + /* use whatever is there */ + out_space = space; + break; + } + } + else { + out_space = g_settings.out_space; + } + } + out_rowbytes = calc_rowbytes(out_space, width); + } + g_settings_lock.Unlock(); + /* Write file header */ + if (outType == PPM_TYPE) { + dprintf(("PPMTranslator: write PPM\n")); + /* begin PPM header */ + const char * magic; + if (out_ascii) + magic = "P3\n"; + else + magic = "P6\n"; + err = outDestination->Write(magic, strlen(magic)); + if (err == (long)strlen(magic)) err = 0; + //comment = NULL; + const char* fsComment; +#if defined(_PR3_COMPATIBLE_) /* R4 headers? */ + if ((ioExtension != NULL) && !ioExtension->FindString(B_TRANSLATOR_EXT_COMMENT, &fsComment)) { +#else + if ((ioExtension != NULL) && !ioExtension->FindString("/comment", &fsComment)) { +#endif + err = write_comment(fsComment, outDestination); + } + if (err == B_OK) { + char data[40]; + sprintf(data, "%d %d %d\n", width, height, max); + err = outDestination->Write(data, strlen(data)); + if (err == (long)strlen(data)) err = 0; + } + /* header done */ + } + else { + dprintf(("PPMTranslator: write B_TRANSLATOR_BITMAP\n")); + /* B_TRANSLATOR_BITMAP header */ + TranslatorBitmap hdr; + hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); + hdr.bounds.left = B_HOST_TO_BENDIAN_FLOAT(0); + hdr.bounds.top = B_HOST_TO_BENDIAN_FLOAT(0); + hdr.bounds.right = B_HOST_TO_BENDIAN_FLOAT(width-1); + hdr.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(height-1); + hdr.rowBytes = B_HOST_TO_BENDIAN_INT32(out_rowbytes); + hdr.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_space); + hdr.dataSize = B_HOST_TO_BENDIAN_INT32(out_rowbytes*height); + dprintf(("rowBytes is %d, width %d, out_space %x, space %x\n", out_rowbytes, width, out_space, space)); + err = outDestination->Write(&hdr, sizeof(hdr)); + dprintf(("PPMTranslator: Write() returns %lx\n", err)); +#if DEBUG + { + BBitmap * map = new BBitmap(BRect(0,0,width-1,height-1), out_space); + printf("map rb = %ld\n", map->BytesPerRow()); + delete map; + } +#endif + if (err == sizeof(hdr)) err = 0; + /* header done */ + } + if (err != B_OK) { + return err > 0 ? B_IO_ERROR : err; + } + /* Write data. Luckily, PPM and B_TRANSLATOR_BITMAP both scan from left to right, top to bottom. */ + return copy_data(inSource, outDestination, rowbytes, out_rowbytes, height, max, ascii, out_ascii, space, out_space); +} + + +class PPMView : + public BView +{ +public: + PPMView( + const BRect & frame, + const char * name, + uint32 resize, + uint32 flags) : + BView(frame, name, resize, flags) + { + SetViewColor(220,220,220,0); + mMenu = new BPopUpMenu("Color Space"); + mMenu->AddItem(new BMenuItem("None", CSMessage(B_NO_COLOR_SPACE))); + mMenu->AddItem(new BMenuItem("RGB 8:8:8 32 bits", CSMessage(B_RGB32))); + mMenu->AddItem(new BMenuItem("RGBA 8:8:8:8 32 bits", CSMessage(B_RGBA32))); + mMenu->AddItem(new BMenuItem("RGB 5:5:5 16 bits", CSMessage(B_RGB15))); + mMenu->AddItem(new BMenuItem("RGBA 5:5:5:1 16 bits", CSMessage(B_RGBA15))); + mMenu->AddItem(new BMenuItem("RGB 5:6:5 16 bits", CSMessage(B_RGB16))); + mMenu->AddItem(new BMenuItem("System Palette 8 bits", CSMessage(B_CMAP8))); + mMenu->AddSeparatorItem(); + mMenu->AddItem(new BMenuItem("Grayscale 8 bits", CSMessage(B_GRAY8))); + mMenu->AddItem(new BMenuItem("Bitmap 1 bit", CSMessage(B_GRAY1))); + mMenu->AddItem(new BMenuItem("CMY 8:8:8 32 bits", CSMessage(B_CMY32))); + mMenu->AddItem(new BMenuItem("CMYA 8:8:8:8 32 bits", CSMessage(B_CMYA32))); + mMenu->AddItem(new BMenuItem("CMYK 8:8:8:8 32 bits", CSMessage(B_CMYK32))); + mMenu->AddSeparatorItem(); + mMenu->AddItem(new BMenuItem("RGB 8:8:8 32 bits big-endian", CSMessage(B_RGB32_BIG))); + mMenu->AddItem(new BMenuItem("RGBA 8:8:8:8 32 bits big-endian", CSMessage(B_RGBA32_BIG))); + mMenu->AddItem(new BMenuItem("RGB 5:5:5 16 bits big-endian", CSMessage(B_RGB15_BIG))); + mMenu->AddItem(new BMenuItem("RGBA 5:5:5:1 16 bits big-endian", CSMessage(B_RGBA15_BIG))); + mMenu->AddItem(new BMenuItem("RGB 5:6:5 16 bits big-endian", CSMessage(B_RGB16))); + mField = new BMenuField(BRect(20,70,180,90), "Color Space Field", "Color Space", mMenu); + mField->SetViewColor(ViewColor()); + AddChild(mField); + SelectColorSpace(g_settings.out_space); + BMessage * msg = new BMessage(CHANGE_ASCII); + mAscii = new BCheckBox(BRect(20,45,180,62), "Write ASCII", "Write ASCII", msg); + if (g_settings.write_ascii) { + mAscii->SetValue(1); + } + mAscii->SetViewColor(ViewColor()); + AddChild(mAscii); + } + ~PPMView() + { + /* nothing here */ + } + + enum { + SET_COLOR_SPACE = 'ppm=', + CHANGE_ASCII + }; + +virtual void Draw( + BRect area) + { + area = area; /* silence compiler */ + SetFont(be_bold_font); + font_height fh; + GetFontHeight(&fh); + char str[100]; + sprintf(str, "PPMTranslator %.2f", (float)translatorVersion/100.0); + DrawString(str, BPoint(fh.descent+1, fh.ascent+fh.descent*2+fh.leading)); + } +virtual void MessageReceived( + BMessage * message) + { + if (message->what == SET_COLOR_SPACE) { + SetSettings(message); + } + else if (message->what == CHANGE_ASCII) { + BMessage msg; + msg.AddBool("ppm /ascii", mAscii->Value()); + SetSettings(&msg); + } + else { + BView::MessageReceived(message); + } + } +virtual void AllAttached() + { + BView::AllAttached(); + BMessenger msgr(this); + /* Tell all menu items we're the man. */ + for (int ix=0; ixCountItems(); ix++) { + BMenuItem * i = mMenu->ItemAt(ix); + if (i) { + i->SetTarget(msgr); + } + } + mAscii->SetTarget(msgr); + } + + void SetSettings( + BMessage * message) + { + g_settings_lock.Lock(); + color_space space; + if (!message->FindInt32("space", (int32*)&space)) { + g_settings.out_space = space; + SelectColorSpace(space); + g_settings.settings_touched = true; + } + bool ascii; + if (!message->FindBool("ppm /ascii", &ascii)) { + g_settings.write_ascii = ascii; + g_settings.settings_touched = true; + } + g_settings_lock.Unlock(); + } + +private: + BPopUpMenu * mMenu; + BMenuField * mField; + BCheckBox * mAscii; + + BMessage * CSMessage( + color_space space) + { + BMessage * ret = new BMessage(SET_COLOR_SPACE); + ret->AddInt32("space", space); + return ret; + } + + void SelectColorSpace( + color_space space) + { + for (int ix=0; ixCountItems(); ix++) { + int32 s; + BMenuItem * i = mMenu->ItemAt(ix); + if (i) { + BMessage * m = i->Message(); + if (m && !m->FindInt32("space", &s) && (s == space)) { + mMenu->Superitem()->SetLabel(i->Label()); + break; + } + } + } + } +}; + + + /* The view will get resized to what the parent thinks is */ + /* reasonable. However, it will still receive MouseDowns etc. */ + /* Your view should change settings in the translator immediately, */ + /* taking care not to change parameters for a translation that is */ + /* currently running. Typically, you'll have a global struct for */ + /* settings that is atomically copied into the translator function */ + /* as a local when translation starts. */ + /* Store your settings wherever you feel like it. */ + +status_t +MakeConfig( /* optional */ + BMessage * ioExtension, /* can be NULL */ + BView * * outView, + BRect * outExtent) +{ + PPMView * v = new PPMView(BRect(0,0,200,100), "PPMTranslator Settings", B_FOLLOW_ALL, B_WILL_DRAW); + *outView = v; + *outExtent = v->Bounds(); + if (ioExtension) { + v->SetSettings(ioExtension); + } + return B_OK; +} + + + /* Copy your current settings to a BMessage that may be passed */ + /* to BTranslators::Translate at some later time when the user wants to */ + /* use whatever settings you're using right now. */ + +status_t +GetConfigMessage( /* optional */ + BMessage * ioExtension) +{ + status_t err = B_OK; +#if defined(_PR3_COMPATIBLE_) + const char * name = B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE; +#else + const char * name = "bits/space"; +#endif + g_settings_lock.Lock(); + (void)ioExtension->RemoveName(name); + err = ioExtension->AddInt32(name, g_settings.out_space); + g_settings_lock.Unlock(); + return err; +} + + + + + +status_t +read_ppm_header( + BDataIO * inSource, + int * width, + int * rowbytes, + int * height, + int * max, + bool * ascii, + color_space * space, + bool * is_ppm, + char ** comment) +{ + /* check for PPM magic number */ + char ch[2]; + if (inSource->Read(ch, 2) != 2) { + return B_NO_TRANSLATOR; + } + /* look for magic number */ + if (ch[0] != 'P') { + /* B_TRANSLATOR_BITMAP magic? */ + if (ch[0] == 'b' && ch[1] == 'i') { + *is_ppm = false; + return read_bits_header(inSource, 2, width, rowbytes, height, max, ascii, space); + } + return B_NO_TRANSLATOR; + } + *is_ppm = true; + if (ch[1] == '6') { + *ascii = false; + } + else if (ch[1] == '3') { + *ascii = true; + } + else { + return B_NO_TRANSLATOR; + } + // status_t err = B_NO_TRANSLATOR; + enum scan_state { + scan_width, + scan_height, + scan_max, + scan_white + } state = scan_width; + int * scan = NULL; + bool in_comment = false; + *space = B_RGB24_BIG; + /* The description of PPM is slightly ambiguous as far as comments */ + /* go. We choose to allow comments anywhere, in the spirit of laxness. */ + /* See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/PPM.txt */ + int comlen = 0; + int comptr = 0; + while (inSource->Read(ch, 1) == 1) { + if (in_comment && (ch[0] != 10) && (ch[0] != 13)) { + if (comment) { /* collect comment(s) into comment string */ + if (comptr >= comlen-1) { + char * n = (char *)realloc(*comment, comlen+100); + if (!n) { + free(*comment); + *comment = NULL; + } + *comment = n; + comlen += 100; + } + (*comment)[comptr++] = ch[0]; + (*comment)[comptr] = 0; + } + continue; + } + in_comment = false; + if (ch[0] == '#') { + in_comment = true; + continue; + } + /* once we're done with whitespace after max, we're done with header */ + if (isdigit(ch[0])) { + if (!scan) { /* first digit for this value */ + switch(state) { + case scan_width: + scan = width; + break; + case scan_height: + *rowbytes = *width*3; + scan = height; + break; + case scan_max: + scan = max; + break; + default: + return B_OK; /* done with header, all OK */ + } + *scan = 0; + } + *scan = (*scan)*10 + (ch[0]-'0'); + } + else if (isspace(ch[0])) { + if (scan) { /* are we done with one value? */ + scan = NULL; + state = (enum scan_state)(state+1); + } + if (state == scan_white) { /* we only ever read one whitespace, since we skip space */ + return B_OK; /* when reading ASCII, and there is a single whitespace after max in raw mode */ + } + } + else { + if (state != scan_white) { + return B_NO_TRANSLATOR; + } + return B_OK; /* header done */ + } + } + return B_NO_TRANSLATOR; +} + + + +status_t +read_bits_header( + BDataIO * io, + int skipped, + int * width, + int * rowbytes, + int * height, + int * max, + bool * ascii, + color_space * space) +{ + /* read the rest of a possible B_TRANSLATOR_BITMAP header */ + if (skipped < 0 || skipped > 4) return B_NO_TRANSLATOR; + int rd = sizeof(TranslatorBitmap)-skipped; + TranslatorBitmap hdr; + /* pre-initialize magic because we might have skipped part of it already */ + hdr.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP); + char * ptr = (char *)&hdr; + if (io->Read(ptr+skipped, rd) != rd) { + return B_NO_TRANSLATOR; + } + /* swap header values */ + hdr.magic = B_BENDIAN_TO_HOST_INT32(hdr.magic); + hdr.bounds.left = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.left); + hdr.bounds.right = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.right); + hdr.bounds.top = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.top); + hdr.bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(hdr.bounds.bottom); + hdr.rowBytes = B_BENDIAN_TO_HOST_INT32(hdr.rowBytes); + hdr.colors = (color_space)B_BENDIAN_TO_HOST_INT32(hdr.colors); + hdr.dataSize = B_BENDIAN_TO_HOST_INT32(hdr.dataSize); + /* sanity checking */ + if (hdr.magic != B_TRANSLATOR_BITMAP) { + return B_NO_TRANSLATOR; + } + if (hdr.colors & 0xffff0000) { /* according to this is a reasonable check. */ + return B_NO_TRANSLATOR; + } + if (hdr.rowBytes * (hdr.bounds.Height()+1) > hdr.dataSize) { + return B_NO_TRANSLATOR; + } + /* return information about the data in the stream */ + *width = (int)hdr.bounds.Width()+1; + *rowbytes = hdr.rowBytes; + *height = (int)hdr.bounds.Height()+1; + *max = 255; + *ascii = false; + *space = hdr.colors; + return B_OK; +} + + +/* String may contain newlines, after which we need to insert extra hash signs. */ +status_t +write_comment( + const char * str, + BDataIO * io) +{ + const char * end = str+strlen(str); + const char * ptr = str; + status_t err = B_OK; + /* write each line as it's found */ + while ((ptr < end) && !err) { + if ((*ptr == 10) || (*ptr == 13)) { + err = io->Write("# ", 2); + if (err == 2) { + err = io->Write(str, ptr-str); + if (err == ptr-str) { + if (io->Write("\n", 1) == 1) { + err = 0; + } + } + } + str = ptr+1; + } + ptr++; + } + /* write the last data, if any, as a line */ + if (ptr > str) { + err = io->Write("# ", 2); + if (err == 2) { + err = io->Write(str, ptr-str); + if (err == ptr-str) { + if (io->Write("\n", 1) == 1) { + err = 0; + } + } + } + } + if (err > 0) { + err = B_IO_ERROR; + } + return err; +} + + +static status_t +read_ascii_line( + BDataIO * in, + int max, + unsigned char * data, + int rowbytes) +{ + char ch; + status_t err; + // int nread = 0; + bool comment = false; + int val = 0; + bool dig = false; + while ((err = in->Read(&ch, 1)) == 1) { + if (comment) { + if ((ch == '\n') || (ch == '\r')) { + comment = false; + } + } + if (isdigit(ch)) { + dig = true; + val = val * 10 + (ch - '0'); + } + else { + if (dig) { + *(data++) = val*255/max; + val = 0; + rowbytes--; + dig = false; + } + if (ch == '#') { + comment = true; + continue; + } + } + if (rowbytes < 1) { + break; + } + } + if (dig) { + *(data++) = val*255/max; + val = 0; + rowbytes--; + dig = false; + } + if (rowbytes < 1) { + return B_OK; + } + return B_IO_ERROR; +} + + +static status_t +write_ascii_line( + BDataIO * out, + unsigned char * data, + int rowbytes) +{ + char buffer[20]; + int linelen = 0; + while (rowbytes > 2) { + sprintf(buffer, "%d %d %d ", data[0], data[1], data[2]); + rowbytes -= 3; + int l = strlen(buffer); + if (l + linelen > 70) { + out->Write("\n", 1); + linelen = 0; + } + if (out->Write(buffer, l) != l) { + return B_IO_ERROR; + } + linelen += l; + data += 3; + } + out->Write("\n", 1); /* terminate each scanline */ + return B_OK; +} + + +static unsigned char * +make_scale_data( + int max) +{ + unsigned char * ptr = (unsigned char *)malloc(max); + for (int ix=0; ix 0) && !err) { + if (in_ascii) { + err = read_ascii_line(in, max, data, rowbytes); + } + else { + err = in->Read(data, rowbytes); + if (err == rowbytes) { + err = B_OK; + } + if (scale) { /* for reading PPM that is smaller than 8 bit */ + scale_data(scale, data, rowbytes); + } + } + if (err == B_OK) { + unsigned char * wbuf = data; + if (in_space != out_space) { + err = convert_space(in_space, out_space, data, rowbytes, out_data); + wbuf = out_data; + } + if (!err && out_ascii) { + err = write_ascii_line(out, wbuf, out_rowbytes); + } + else if (!err) { + err = out->Write(wbuf, out_rowbytes); + if (err == out_rowbytes) { + err = B_OK; + } + } + } + } + free(data); + free(out_data); + free(scale); + return err > 0 ? B_IO_ERROR : err; +} + + diff --git a/src/add-ons/translators/ppmtranslator/colorspace.cpp b/src/add-ons/translators/ppmtranslator/colorspace.cpp new file mode 100644 index 0000000000..c64dcc1943 --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/colorspace.cpp @@ -0,0 +1,618 @@ +/* colorspace.cpp */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + + +#include +#include +#include + +#include +#include +#include + +#include "colorspace.h" + + +#if !defined(_PR3_COMPATIBLE_) + #define B_CMY24 ((color_space)0xC001) /* C[7:0] M[7:0] Y[7:0] No gray removal done */ + #define B_CMY32 ((color_space)0xC002) /* C[7:0] M[7:0] Y[7:0] X[7:0] No gray removal done */ + #define B_CMYA32 ((color_space)0xE002) /* C[7:0] M[7:0] Y[7:0] A[7:0] No gray removal done */ + #define B_CMYK32 ((color_space)0xC003) /* C[7:0] M[7:0] Y[7:0] K[7:0] */ +#endif + +int +expand_data( /* expands to BGRA in the output buffer from in the input buffer */ + color_space from_space, + unsigned char * in_data, + int rowbytes, + unsigned char * out_buf) +{ + ASSERT(in_data != out_buf); + + /* We don't do YUV and friends yet. */ + /* It's important to replicate the most significant component bits to LSB when going 15->24 */ + unsigned char * in_out = out_buf; + switch (from_space) { + case B_RGB32: + while (rowbytes > 3) { + out_buf[0] = in_data[0]; + out_buf[1] = in_data[1]; + out_buf[2] = in_data[2]; + out_buf[3] = 255; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + case B_RGBA32: + memcpy(out_buf, in_data, rowbytes); + break; + case B_RGB24: + while (rowbytes > 2) { + out_buf[0] = in_data[0]; + out_buf[1] = in_data[1]; + out_buf[2] = in_data[2]; + out_buf[3] = 255; + out_buf += 4; + in_data += 3; + rowbytes -= 3; + } + break; + case B_RGB15: + while (rowbytes > 1) { + uint16 val = in_data[0]+(in_data[1]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x3e0)>>2)|((val&0x3e0)>>7); + out_buf[2] = ((val&0x7c00)>>7)|((val&0x7c00)>>12); + out_buf[3] = 255; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_RGBA15: + while (rowbytes > 1) { + uint16 val = in_data[0]+(in_data[1]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x3e0)>>2)|((val&0x3e0)>>7); + out_buf[2] = ((val&0x7c00)>>7)|((val&0x7c00)>>12); + out_buf[3] = (val&0x8000) ? 255 : 0; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_RGB16: + while (rowbytes > 1) { + uint16 val = in_data[0]+(in_data[1]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x7e0)>>3)|((val&0x7e0)>>9); + out_buf[2] = ((val&0xf800)>>8)|((val&0xf800)>>13); + out_buf[3] = 255; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_RGB32_BIG: + while (rowbytes > 3) { + out_buf[0] = in_data[3]; + out_buf[1] = in_data[2]; + out_buf[2] = in_data[1]; + out_buf[3] = 255; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + case B_RGBA32_BIG: + while (rowbytes > 3) { + out_buf[0] = in_data[3]; + out_buf[1] = in_data[2]; + out_buf[2] = in_data[1]; + out_buf[3] = in_data[0]; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + case B_RGB24_BIG: + while (rowbytes > 2) { + out_buf[0] = in_data[2]; + out_buf[1] = in_data[1]; + out_buf[2] = in_data[0]; + out_buf[3] = 255; + out_buf += 4; + in_data += 3; + rowbytes -= 3; + } + break; + case B_RGB15_BIG: + while (rowbytes > 1) { + uint16 val = in_data[1]+(in_data[0]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x3e0)>>2)|((val&0x3e0)>>7); + out_buf[2] = ((val&0x7c00)>>7)|((val&0x7c00)>>12); + out_buf[3] = 255; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_RGBA15_BIG: + while (rowbytes > 1) { + uint16 val = in_data[1]+(in_data[0]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x3e0)>>2)|((val&0x3e0)>>7); + out_buf[2] = ((val&0x7c00)>>7)|((val&0x7c00)>>12); + out_buf[3] = (val&0x8000) ? 255 : 0; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_RGB16_BIG: + while (rowbytes > 1) { + uint16 val = in_data[1]+(in_data[0]<<8); + out_buf[0] = ((val&0x1f)<<3)|((val&0x1f)>>2); + out_buf[1] = ((val&0x7e0)>>3)|((val&0x7e0)>>9); + out_buf[2] = ((val&0xf800)>>8)|((val&0xf800)>>13); + out_buf[3] = 255; + out_buf += 4; + in_data += 2; + rowbytes -= 2; + } + break; + case B_CMAP8: { + const color_map * map = system_colors(); + while (rowbytes > 0) { + rgb_color c = map->color_list[in_data[0]]; + out_buf[0] = c.blue; + out_buf[1] = c.green; + out_buf[2] = c.red; + out_buf[3] = c.alpha; + out_buf += 4; + in_data += 1; + rowbytes -= 1; + } + } break; + case B_GRAY8: + while (rowbytes > 0) { + unsigned char ch = *in_data; + out_buf[0] = ch; + out_buf[1] = ch; + out_buf[2] = ch; + out_buf[3] = 255; + out_buf += 4; + in_data += 1; + rowbytes -= 1; + } + break; + case B_GRAY1: + while (rowbytes > 0) { /* expansion 1->32 is pretty good :-) */ + unsigned char c1 = *in_data; + for (int b = 128; b; b = b>>1) { + unsigned char ch; + if (c1 & b) { + ch = 0; + } + else { + ch = 255; + } + out_buf[0] = ch; + out_buf[1] = ch; + out_buf[2] = ch; + out_buf[3] = 255; + out_buf += 4; + } + in_data += 1; + rowbytes -= 1; + } + break; + case B_CMY24: /* We do the "clean" inversion which doesn't correct for printing ink deficiencies. */ + while (rowbytes > 2) { + out_buf[0] = 255-in_data[2]; + out_buf[1] = 255-in_data[1]; + out_buf[2] = 255-in_data[0]; + out_buf[3] = 255; + out_buf += 4; + in_data += 3; + rowbytes -= 3; + } + break; + case B_CMY32: + while (rowbytes > 3) { + out_buf[0] = 255-in_data[2]; + out_buf[1] = 255-in_data[1]; + out_buf[2] = 255-in_data[0]; + out_buf[3] = 255; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + case B_CMYA32: + while (rowbytes > 3) { + out_buf[0] = 255-in_data[2]; + out_buf[1] = 255-in_data[1]; + out_buf[2] = 255-in_data[0]; + out_buf[3] = in_data[3]; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + case B_CMYK32: /* We assume uniform gray removal, and no under-color-removal. */ + while (rowbytes > 3) { + int comp = 255-in_data[2]-in_data[3]; + out_buf[0] = comp < 0 ? 0 : comp; + comp = 255-in_data[1]-in_data[3]; + out_buf[1] = comp < 0 ? 0 : comp; + comp = 255-in_data[0]-in_data[3]; + out_buf[2] = comp < 0 ? 0 : comp; + out_buf[3] = 255; + out_buf += 4; + in_data += 4; + rowbytes -= 4; + } + break; + default: + break; + } + return out_buf - in_out; +} + + +int +collapse_data( + unsigned char * in_buf, + int num_bytes, + color_space out_space, + unsigned char * out_buf) +{ + ASSERT(in_buf != out_buf); + + unsigned char * in_out = out_buf; + /* We could increase perceived image quality of down conversions by implementing */ + /* dithering. However, someone might want to operate on the images after */ + /* conversion, in which case dithering would be un-good. Besides, good error */ + /* diffusion requires more than one scan line to propagate errors to. */ + switch(out_space) { + case B_RGB32: + memcpy(out_buf, in_buf, num_bytes); + break; + case B_RGBA32: + memcpy(out_buf, in_buf, num_bytes); + break; + case B_RGB24: + while (num_bytes > 3) { + out_buf[0] = in_buf[0]; + out_buf[1] = in_buf[1]; + out_buf[2] = in_buf[2]; + out_buf += 3; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGB16: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<3)&0x7e0)|((in_buf[2]<<8)&0xf800); + out_buf[0] = val; + out_buf[1] = val>>8; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGB15: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<2)&0x3e0)|((in_buf[2]<<7)&0x7c00)|0x8000; + out_buf[0] = val; + out_buf[1] = val>>8; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGBA15: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<2)&0x3e0)|((in_buf[2]<<7)&0x7c00); + if (in_buf[3] > 127) { + val = val | 0x8000; + } + out_buf[0] = val; + out_buf[1] = val>>8; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_CMAP8: { + const color_map * map = system_colors(); + while (num_bytes > 3) { + if (in_buf[3] < 128) { + out_buf[0] = B_TRANSPARENT_8_BIT; + } + else { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<2)&0x3e0)|((in_buf[2]<<7)&0x7c00); + out_buf[0] = map->index_map[val]; + } + out_buf += 1; + in_buf += 4; + num_bytes -= 4; + } + } + break; + case B_GRAY8: + while (num_bytes > 3) { /* There are better algorithms than Y = .25B+.50G+.25R */ + /* but hardly faster -- and it's still better than (B+G+R)/3 ! */ + out_buf[0] = (in_buf[0]+in_buf[1]*2+in_buf[2])>>2; + out_buf += 1; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_GRAY1: { + uchar ob = 0; + int cnt = 0; + uchar c = 0; + while (num_bytes > 3) { + if (cnt == 8) { + out_buf[0] = ob; + out_buf += 1; + cnt = 0; + ob = 0; + } + c = ((in_buf[0]+in_buf[1]*2+in_buf[2])&0x200)>>(2+cnt); + ob = ob | c; + cnt++; + in_buf += 4; + num_bytes -= 4; + } + if (cnt > 0) { + out_buf[0] = ob; + out_buf += 1; + } + } break; + /* big endian version, when the encoding is not endianess independant */ + case B_RGB32_BIG: + while (num_bytes > 3) { + out_buf[3] = in_buf[0]; + out_buf[2] = in_buf[1]; + out_buf[1] = in_buf[2]; + out_buf += 4; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGBA32_BIG: + while (num_bytes > 3) { + out_buf[3] = in_buf[0]; + out_buf[2] = in_buf[1]; + out_buf[1] = in_buf[2]; + out_buf[0] = in_buf[3]; + out_buf += 4; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGB24_BIG: + while (num_bytes > 3) { + out_buf[2] = in_buf[0]; + out_buf[1] = in_buf[1]; + out_buf[0] = in_buf[2]; + out_buf += 3; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGB16_BIG: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<3)&0x7e0)|((in_buf[2]<<8)&0xf800); + out_buf[0] = val>>8; + out_buf[1] = val; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGB15_BIG: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<2)&0x3e0)|((in_buf[2]<<7)&0x7c00)|0x8000; + out_buf[0] = val>>8; + out_buf[1] = val; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_RGBA15_BIG: + while (num_bytes > 3) { + uint16 val = (in_buf[0]>>3)|((in_buf[1]<<2)&0x3e0)|((in_buf[2]<<7)&0x7c00); + if (in_buf[3] > 127) { + val = val | 0x8000; + } + out_buf[0] = val>>8; + out_buf[1] = val; + out_buf += 2; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_CMY24: + while (num_bytes > 3) { + out_buf[0] = 255-in_buf[2]; + out_buf[1] = 255-in_buf[1]; + out_buf[2] = 255-in_buf[0]; + out_buf += 3; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_CMY32: + while (num_bytes > 3) { + out_buf[0] = 255-in_buf[2]; + out_buf[1] = 255-in_buf[1]; + out_buf[2] = 255-in_buf[0]; + out_buf += 4; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_CMYA32: + while (num_bytes > 3) { + out_buf[0] = 255-in_buf[2]; + out_buf[1] = 255-in_buf[1]; + out_buf[2] = 255-in_buf[0]; + out_buf[3] = in_buf[3]; + out_buf += 4; + in_buf += 4; + num_bytes -= 4; + } + break; + case B_CMYK32: + while (num_bytes > 3) { /* We do direct gray removal */ + int c = 255-in_buf[2]; + int m = 255-in_buf[1]; + int y = 255-in_buf[0]; + int k = (c>m)?((y>c)?y:c):((y>m)?y:m); + out_buf[0] = c-k; + out_buf[1] = m-k; + out_buf[2] = y-k; + out_buf[3] = k; + out_buf += 4; + in_buf += 4; + num_bytes -= 4; + } + break; + default: + break; + } + return out_buf-in_out; +} + + +#if DEBUG_DATA +static void +print_data( + unsigned char * ptr, + int n) +{ + while (n-- > 0) { + printf("%02x ", *(ptr++)); + } + printf("\n"); +} +#endif + + +status_t +convert_space( + color_space in_space, + color_space out_space, + unsigned char * in_data, + int rowbytes, + unsigned char * out_data) +{ + ASSERT(in_data != out_data); + + /* Instead of coding each transformation separately, which would create */ + /* a very large number of conversion functions, we write one function to */ + /* convert to RGBA32, and another function to convert from RGBA32, and */ + /* put them together to get a manageable program, at a slight expense in */ + /* conversion speed. */ + + int n; +#if DEBUG_DATA + printf("convert_space(%x, %x, %x)\n", in_space, out_space, rowbytes); + printf("raw data: "); + print_data(in_data, rowbytes); +#endif + /* If we convert from a format to itself, well... */ + if (in_space == out_space) { + memcpy(out_data, in_data, rowbytes); + return B_OK; + } + /* When the input format is RGBA32, we don't need the first conversion. */ + if (in_space == B_RGBA32) { + n = collapse_data(in_data, rowbytes, out_space, out_data); +#if DEBUG_DATA + printf("collapsed data: "); + print_data(out_data, n); +#endif + return B_OK; + } + /* When the output format is RGBA32, we don't need any second conversion. */ + if (out_space == B_RGB32 || out_space == B_RGBA32) { + n = expand_data(in_space, in_data, rowbytes, out_data); +#if DEBUG_DATA + printf("expanded data: "); + print_data(out_data, n); +#endif + return B_OK; + } + /* Figure out byte expansion rate -- usually isn't more than 4 */ + int mul = 4; + if (in_space == B_GRAY1) { + mul = 32; + } + unsigned char * buf = (unsigned char *)malloc(rowbytes*mul); + if (buf == NULL) { + /* oops! */ + return B_NO_MEMORY; + } + n = expand_data(in_space, in_data, rowbytes, buf); +#if DEBUG_DATA + printf("expanded data: "); + print_data(out_data, n); +#endif + n = collapse_data(buf, n, out_space, out_data); +#if DEBUG_DATA + printf("collapsed data: "); + print_data(out_data, n); +#endif + free(buf); + return B_OK; +} + + + +/* Figure out what the rowbytes is for a given width in a given color space. */ +/* Rowbytes is bytes per pixel times width, rounded up to nearest multiple of 4. */ +int +calc_rowbytes( + color_space space, + int width) +{ + int v = width*4; + switch (space) { + default: + /* 4 is fine */ + break; + case B_RGB24: + case B_CMY24: + case B_RGB24_BIG: + v = width*3; + break; + case B_RGB15: + case B_RGBA15: + case B_RGB16: + case B_RGB15_BIG: + case B_RGBA15_BIG: + case B_RGB16_BIG: + v = width*2; + break; + case B_CMAP8: + case B_GRAY8: + v = width; + break; + case B_GRAY1: + v = (width+7)/8; /* whole bytes only, please */ + break; + } + v = (v+3)&~3; + return v; +} diff --git a/src/add-ons/translators/ppmtranslator/colorspace.h b/src/add-ons/translators/ppmtranslator/colorspace.h new file mode 100644 index 0000000000..808b5da06f --- /dev/null +++ b/src/add-ons/translators/ppmtranslator/colorspace.h @@ -0,0 +1,32 @@ +/* colorspace.h */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(COLORSPACE_H) +#define COLORSPACE_H + + +/* This is the main conversion function; it converts data in in_data */ +/* with rowbytes amount of pixel data into some other color space in */ +/* out_data, with enough memory assumed to be in out_data for the */ +/* converted data. */ +status_t convert_space(color_space in_space, color_space out_space, + unsigned char * in_data, int rowbytes, unsigned char * out_data); + +/* This function expands rowbytes amount of data from in_data into */ +/* RGBA32 data in out_buf, which must be big enough. */ +int expand_data(color_space from_space, unsigned char * in_data, + int rowbytes, unsigned char * out_buf); + +/* This function converts num_bytes bytes of RGBA32 data into some new */ +/* color space in out_buf, where out_buf must be big enough. */ +int collapse_data(unsigned char * in_buf, int num_bytes, + color_space out_space, unsigned char * out_buf); + +/* Given a specific number of pixels in width in the color space space */ +/* this function calculates what the row_bytes should be. */ +int calc_rowbytes(color_space space, int width); + +#endif /* COLORSPACE_H */