#include <Bitmap.h>
#include <BufferedDataIO.h>
#include <ByteOrder.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Locker.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Message.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Screen.h>
#include <StringView.h>
#include <TranslationKit.h>
#include <TranslatorAddOn.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "colorspace.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PPMTranslator"
#if DEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
#define PPM_TRANSLATOR_VERSION 0x100
char translatorName[] = "PPM images";
char translatorInfo[] = "PPM image translator v1.0.0, " __DATE__;
int32 translatorVersion = PPM_TRANSLATOR_VERSION;
#define PPM_TYPE 'PPM '
translation_format inputFormats[]
= {{B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap",
"Be Bitmap Format (PPMTranslator)"},
{PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap",
"PPM image"},
{0, 0, 0, 0, "\0",
"\0"}};
translation_format outputFormats[]
= {{B_TRANSLATOR_BITMAP, B_TRANSLATOR_BITMAP, 0.4, 0.8, "image/x-be-bitmap",
"Be Bitmap Format (PPMTranslator)"},
{PPM_TYPE, B_TRANSLATOR_BITMAP, 0.3, 0.8, "image/x-portable-pixmap",
"PPM image"},
{0, 0, 0, 0, "\0",
"\0"}};
struct ppm_settings {
color_space out_space;
BPoint window_pos;
bool write_ascii;
bool settings_touched;
};
static BLocker g_settings_lock("PPM settings lock");
static 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"));
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");
if (f) {
char line[1024];
char name[32];
char* ptr;
while (true) {
line[0] = 0;
fgets(line, 1024, f);
if (!line[0])
break;
ptr = line;
while (isspace(*ptr))
ptr++;
if (*ptr == '#' || !*ptr)
continue;
if (sscanf(ptr, "%31[a-zA-Z_0-9] =", name) != 1) {
syslog(LOG_ERR,
"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) {
syslog(LOG_ERR,
"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) {
syslog(LOG_ERR,
"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) {
syslog(LOG_ERR,
"illegal write_ascii value "
"in PPMTranslator settings: %s",
ptr);
} else {
g_settings.write_ascii = ascii;
}
} else {
syslog(
LOG_ERR, "unknown PPMTranslator setting: %s", line);
}
}
}
fclose(f);
}
}
~PrefsLoader()
{
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 %d.%d.%d\n",
static_cast<int>(translatorVersion >> 8),
static_cast<int>((translatorVersion >> 4) & 0xf),
static_cast<int>(translatorVersion & 0xf));
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");
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);
status_t
Identify(BPositionIO* inSource, const translation_format* inFormat,
BMessage* ioExtension, translator_info* outInfo, uint32 outType)
{
dprintf(("PPMTranslator: Identify()\n"));
(void)inFormat;
(void)ioExtension;
if (outType == 0)
outType = B_TRANSLATOR_BITMAP;
if (outType != B_TRANSLATOR_BITMAP && outType != PPM_TYPE)
return B_NO_TRANSLATOR;
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;
outInfo->group = B_TRANSLATOR_BITMAP;
if (is_ppm) {
outInfo->type = PPM_TYPE;
outInfo->quality = 0.3;
outInfo->capability
= 0.8;
strlcpy(outInfo->name, B_TRANSLATE("PPM image"), sizeof(outInfo->name));
strcpy(outInfo->MIME, "image/x-portable-pixmap");
} else {
outInfo->type = B_TRANSLATOR_BITMAP;
outInfo->quality = 0.4;
outInfo->capability
= 0.8;
strlcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format (PPMTranslator)"),
sizeof(outInfo->name));
strcpy(outInfo->MIME, "image/x-be-bitmap");
}
return B_OK;
}
status_t
Translate(BPositionIO* inSource, const translator_info* ,
BMessage* ioExtension, uint32 outType, BPositionIO* outDestination)
{
dprintf(("PPMTranslator: Translate()\n"));
inSource->Seek(0, SEEK_SET);
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;
int width, rowbytes, height, max;
bool ascii, is_ppm;
color_space space;
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)
ioExtension->AddString(B_TRANSLATOR_EXT_COMMENT, comment);
free(comment);
}
if (err < B_OK) {
dprintf(("read_ppm_header() error %s [%" B_PRIx32 "]\n", strerror(err),
err));
return err;
}
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;
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 {
if (!ioExtension
|| ioExtension->FindInt32( B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE,
(int32*) &out_space)
|| (out_space == B_NO_COLOR_SPACE)) {
if (g_settings.out_space == B_NO_COLOR_SPACE) {
switch (space) {
case B_RGB24:
case B_RGB24_BIG:
out_space = B_RGB32;
break;
default:
out_space = space;
break;
}
} else {
out_space = g_settings.out_space;
}
}
out_rowbytes = calc_rowbytes(out_space, width);
}
g_settings_lock.Unlock();
if (outType == PPM_TYPE) {
dprintf(("PPMTranslator: write PPM\n"));
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;
const char* fsComment;
if ((ioExtension != NULL)
&& !ioExtension->FindString(B_TRANSLATOR_EXT_COMMENT, &fsComment))
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;
}
} else {
dprintf(("PPMTranslator: write B_TRANSLATOR_BITMAP\n"));
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 %" B_PRIx32 "\n", err));
#if DEBUG
{
BBitmap* map
= new BBitmap(BRect(0, 0, width - 1, height - 1), out_space);
printf("map rb = %" B_PRId32 "\n", map->BytesPerRow());
delete map;
}
#endif
if (err == sizeof(hdr))
err = 0;
}
if (err != B_OK)
return err > 0 ? B_IO_ERROR : err;
return copy_data(inSource, outDestination, rowbytes, out_rowbytes, height,
max, ascii, out_ascii, space, out_space);
}
class PPMView : public BView
{
public:
PPMView(const char* name, uint32 flags) : BView(name, flags)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fTitle = new BStringView("title", B_TRANSLATE("PPM image translator"));
fTitle->SetFont(be_bold_font);
char detail[100];
int ver = static_cast<int>(translatorVersion);
sprintf(detail, B_TRANSLATE("Version %d.%d.%d, %s"), ver >> 8,
((ver >> 4) & 0xf), (ver & 0xf), __DATE__);
fDetail = new BStringView("detail", detail);
fBasedOn = new BStringView(
"basedOn", B_TRANSLATE("Based on PPMTranslator sample code"));
fCopyright = new BStringView("copyright",
B_TRANSLATE("Sample code copyright 1999, Be Incorporated"));
fMenu = new BPopUpMenu("Color Space");
fMenu->AddItem(
new BMenuItem(B_TRANSLATE("None"), CSMessage(B_NO_COLOR_SPACE)));
fMenu->AddItem(new BMenuItem(
B_TRANSLATE("RGB 8:8:8 32 bits"), CSMessage(B_RGB32)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
"bits"),
CSMessage(B_RGBA32)));
fMenu->AddItem(new BMenuItem(
B_TRANSLATE("RGB 5:5:5 16 bits"), CSMessage(B_RGB15)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
"bits"),
CSMessage(B_RGBA15)));
fMenu->AddItem(new BMenuItem(
B_TRANSLATE("RGB 5:6:5 16 bits"), CSMessage(B_RGB16)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("System palette 8 "
"bits"),
CSMessage(B_CMAP8)));
fMenu->AddSeparatorItem();
fMenu->AddItem(
new BMenuItem(B_TRANSLATE("Grayscale 8 bits"), CSMessage(B_GRAY8)));
fMenu->AddItem(
new BMenuItem(B_TRANSLATE("Bitmap 1 bit"), CSMessage(B_GRAY1)));
fMenu->AddItem(new BMenuItem(
B_TRANSLATE("CMY 8:8:8 32 bits"), CSMessage(B_CMY32)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYA 8:8:8:8 32 "
"bits"),
CSMessage(B_CMYA32)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("CMYK 8:8:8:8 32 "
"bits"),
CSMessage(B_CMYK32)));
fMenu->AddSeparatorItem();
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 8:8:8 32 bits "
"big-endian"),
CSMessage(B_RGB32_BIG)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 8:8:8:8 32 "
"bits big-endian"),
CSMessage(B_RGBA32_BIG)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:5:5 16 bits "
"big-endian"),
CSMessage(B_RGB15_BIG)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGBA 5:5:5:1 16 "
"bits big-endian"),
CSMessage(B_RGBA15_BIG)));
fMenu->AddItem(new BMenuItem(B_TRANSLATE("RGB 5:6:5 16 bits "
"big-endian"),
CSMessage(B_RGB16)));
fField = new BMenuField(B_TRANSLATE("Input color space:"), fMenu);
fField->SetViewColor(ViewColor());
SelectColorSpace(g_settings.out_space);
BMessage* msg = new BMessage(CHANGE_ASCII);
fAscii = new BCheckBox(B_TRANSLATE("Write ASCII"), msg);
if (g_settings.write_ascii)
fAscii->SetValue(1);
fAscii->SetViewColor(ViewColor());
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fTitle)
.Add(fDetail)
.AddGlue()
.AddGroup(B_HORIZONTAL)
.Add(fField)
.AddGlue()
.End()
.Add(fAscii)
.AddGlue()
.Add(fBasedOn)
.Add(fCopyright);
}
~PPMView() { }
enum { SET_COLOR_SPACE = 'ppm=', CHANGE_ASCII };
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", fAscii->Value());
SetSettings(&msg);
} else {
BView::MessageReceived(message);
}
}
virtual void AllAttached()
{
BView::AllAttached();
BMessenger msgr(this);
for (int ix = 0; ix < fMenu->CountItems(); ix++) {
BMenuItem* i = fMenu->ItemAt(ix);
if (i)
i->SetTarget(msgr);
}
fAscii->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:
BStringView* fTitle;
BStringView* fDetail;
BStringView* fBasedOn;
BStringView* fCopyright;
BPopUpMenu* fMenu;
BMenuField* fField;
BCheckBox* fAscii;
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; ix < fMenu->CountItems(); ix++) {
int32 s;
BMenuItem* i = fMenu->ItemAt(ix);
if (i) {
BMessage* m = i->Message();
if (m && !m->FindInt32("space", &s) && (s == space)) {
fMenu->Superitem()->SetLabel(i->Label());
break;
}
}
}
}
};
status_t
MakeConfig(BMessage* ioExtension, BView** outView, BRect* outExtent)
{
PPMView* v
= new PPMView(B_TRANSLATE("PPMTranslator Settings"), B_WILL_DRAW);
*outView = v;
v->ResizeTo(v->ExplicitPreferredSize());
;
*outExtent = v->Bounds();
if (ioExtension)
v->SetSettings(ioExtension);
return B_OK;
}
status_t
GetConfigMessage(BMessage* ioExtension)
{
status_t err = B_OK;
const char* name = B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE;
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)
{
char ch[2];
bool monochrome = false;
bool greyscale = false;
if (inSource->Read(ch, 2) != 2)
return B_NO_TRANSLATOR;
if (ch[0] != 'P') {
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' || ch[1] == '5' || ch[1] == '4') {
*ascii = false;
} else if (ch[1] == '3' || ch[1] == '2' || ch[1] == '1') {
*ascii = true;
} else {
return B_NO_TRANSLATOR;
}
if (ch[1] == '4' || ch[1] == '1')
monochrome = true;
else if (ch[1] == '5' || ch[1] == '2')
greyscale = true;
enum scan_state {
scan_width,
scan_height,
scan_max,
scan_white
} state
= scan_width;
int* scan = NULL;
bool in_comment = false;
if (monochrome)
*space = B_GRAY1;
else if (greyscale)
*space = B_GRAY8;
else
*space = B_RGB24_BIG;
int comlen = 0;
int comptr = 0;
while (inSource->Read(ch, 1) == 1) {
if (in_comment && (ch[0] != 10) && (ch[0] != 13)) {
if (comment) {
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;
}
if (isdigit(ch[0])) {
if (!scan) {
switch (state) {
case scan_width:
scan = width;
break;
case scan_height:
if (monochrome)
*rowbytes = (*width + 7) / 8;
else if (greyscale)
*rowbytes = *width;
else
*rowbytes = *width * 3;
scan = height;
break;
case scan_max:
scan = max;
break;
default:
return B_OK;
}
*scan = 0;
}
*scan = (*scan) * 10 + (ch[0] - '0');
} else if (isspace(ch[0])) {
if (scan) {
scan = NULL;
state = (enum scan_state)(state + 1);
if ((state == scan_max) && monochrome) {
state = (enum scan_state)(state + 1);
*max = 1;
}
}
if (state == scan_white) {
return B_OK;
}
} else {
if (state != scan_white)
return B_NO_TRANSLATOR;
return B_OK;
}
}
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)
{
if (skipped < 0 || skipped > 4)
return B_NO_TRANSLATOR;
int rd = sizeof(TranslatorBitmap) - skipped;
TranslatorBitmap hdr;
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;
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);
if (hdr.magic != B_TRANSLATOR_BITMAP)
return B_NO_TRANSLATOR;
if (hdr.colors & 0xffff0000) {
return B_NO_TRANSLATOR;
}
if (hdr.rowBytes * (hdr.bounds.Height() + 1) > hdr.dataSize)
return B_NO_TRANSLATOR;
*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;
}
status_t
write_comment(const char* str, BDataIO* io)
{
const char* end = str + strlen(str);
const char* ptr = str;
status_t err = B_OK;
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++;
}
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;
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);
return B_OK;
}
static unsigned char*
make_scale_data(int max)
{
unsigned char* ptr = (unsigned char*) malloc(max);
for (int ix = 0; ix < max; ix++)
ptr[ix] = ix * 255 / max;
return ptr;
}
static void
scale_data(unsigned char* scale, unsigned char* data, int bytes)
{
for (int ix = 0; ix < bytes; ix++)
data[ix] = scale[data[ix]];
}
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)
{
dprintf(("copy_data(%d, %d, %d, %d, %x, %x)\n", rowbytes, out_rowbytes,
height, max, in_space, out_space));
BBufferedDataIO inBuffer(*in, 65536, false);
unsigned char* data = (unsigned char*) malloc(rowbytes);
unsigned char* out_data = (unsigned char*) malloc(out_rowbytes);
if (data == NULL || out_data == NULL) {
free(data);
free(out_data);
return B_NO_MEMORY;
}
unsigned char* scale = NULL;
if (max != 255 && in_space != B_GRAY1)
scale = make_scale_data(max);
status_t err = B_OK;
while ((height-- > 0) && !err) {
if (in_ascii) {
err = read_ascii_line(&inBuffer, max, data, rowbytes);
} else {
err = inBuffer.Read(data, rowbytes);
if (err == rowbytes)
err = B_OK;
if (scale)
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;
}