initial check in for PPMTranslator from BeOS R5 sample-code folder

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3551 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Matthew Wilber 2003-06-17 00:17:30 +00:00
parent fae8d042d5
commit 60afeeca54
6 changed files with 1752 additions and 0 deletions

View File

@ -0,0 +1,5 @@
SubDir OBOS_TOP src add-ons translators ppmtranslator ;
Translator PPMTranslator : PPMMain.cpp PPMTranslator.cpp colorspace.cpp ;
LinkSharedOSLibs PPMTranslator : be translation ;

View File

@ -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.

View File

@ -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 <TranslatorAddOn.h>
#include <View.h>
#include <Window.h>
#include <Application.h>
#include <Alert.h>
#include <Screen.h>
#include <stdio.h>
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;
}

View File

@ -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 <TranslatorAddOn.h>
#include <TranslationKit.h>
#include <ByteOrder.h>
#include <Message.h>
#include <Screen.h>
#include <Locker.h>
#include <FindDirectory.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <CheckBox.h>
#include <Bitmap.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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; ix<mMenu->CountItems(); 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; ix<mMenu->CountItems(); 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 <GraphicsDefs.h> 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<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(%x, %x, %x, %x, %x, %x)\n", rowbytes, out_rowbytes, height, max, in_space, out_space));
/* We read/write one scanline at a time. */
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) {
scale = make_scale_data(max);
}
status_t err = B_OK;
/* There is no data format conversion, so we can just copy data. */
while ((height-- > 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;
}

View File

@ -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 <GraphicsDefs.h>
#include <InterfaceDefs.h>
#include <Debug.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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 <whatever> 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;
}

View File

@ -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 */