#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <ByteOrder.h>
#include <DataIO.h>
#include <TranslationErrors.h>
#include "SGIImage.h"
const char kSGICopyright[] = "" B_UTF8_COPYRIGHT " 1997-1998 Michael Sweet "
"<mike@easysw.com>";
SGIImage::SGIImage()
: fStream(NULL),
fMode(0),
fBytesPerChannel(0),
fCompression(0),
fWidth(0),
fHeight(0),
fChannelCount(0),
fFirstRowOffset(0),
fNextRowOffset(0),
fOffsetTable(NULL),
fLengthTable(NULL),
fARLERow(NULL),
fARLEOffset(0),
fARLELength(0)
{
}
SGIImage::~SGIImage()
{
Unset();
}
status_t
SGIImage::InitCheck() const
{
if (fStream)
return B_OK;
return B_NO_INIT;
}
status_t
SGIImage::SetTo(BPositionIO* stream)
{
if (!stream)
return B_BAD_VALUE;
fStream = stream;
stream->Seek(0, SEEK_SET);
int16 magic = _ReadShort();
if (magic != SGI_MAGIC) {
fStream = NULL;
return B_NO_TRANSLATOR;
}
fMode = SGI_READ;
fCompression = _ReadChar();
fBytesPerChannel = _ReadChar();
_ReadShort();
fWidth = _ReadShort();
fHeight = _ReadShort();
fChannelCount = _ReadShort();
if (fCompression) {
fStream->Seek(512, SEEK_SET);
fOffsetTable = (int32**)calloc(fChannelCount, sizeof(int32*));
fOffsetTable[0] = (int32*)calloc(fHeight * fChannelCount, sizeof(int32));
for (uint32 i = 1; i < fChannelCount; i++)
fOffsetTable[i] = fOffsetTable[0] + i * fHeight;
for (uint32 i = 0; i < fChannelCount; i++)
for (uint16 j = 0; j < fHeight; j++)
fOffsetTable[i][j] = _ReadLong();
fLengthTable = (int32**)calloc(fChannelCount, sizeof(int32*));
fLengthTable[0] = (int32*)calloc(fHeight * fChannelCount, sizeof(int32));
for (int32 i = 1; i < fChannelCount; i ++)
fLengthTable[i] = fLengthTable[0] + i * fHeight;
for (uint32 i = 0; i < fChannelCount; i++)
for (uint16 j = 0; j < fHeight; j++)
fLengthTable[i][j] = _ReadLong();
}
return B_OK;
}
status_t
SGIImage::SetTo(BPositionIO* stream,
uint16 width, uint16 height,
uint16 channels, uint32 bytesPerChannel,
uint32 compression)
{
if (!stream ||
width < 1 || height < 1 || channels < 1 ||
bytesPerChannel < 1 || bytesPerChannel > 2 ||
compression < SGI_COMP_NONE || compression > SGI_COMP_ARLE)
return B_BAD_VALUE;
fStream = stream;
fMode = SGI_WRITE;
_WriteShort(SGI_MAGIC);
_WriteChar((fCompression = compression) != 0);
_WriteChar(fBytesPerChannel = bytesPerChannel);
_WriteShort(3);
_WriteShort(fWidth = width);
_WriteShort(fHeight = height);
_WriteShort(fChannelCount = channels);
if (fBytesPerChannel == 1) {
_WriteLong(0);
_WriteLong(255);
} else {
_WriteLong(-32768);
_WriteLong(32767);
}
_WriteLong(0);
char name[80];
memset(name, 0, sizeof(name));
sprintf(name, "Haiku SGITranslator");
fStream->Write(name, sizeof(name));
for (int32 i = 0; i < 102; i++)
_WriteLong(0);
switch (fCompression) {
case SGI_COMP_NONE :
for (int32 i = fWidth * fHeight * fChannelCount; i > 0; i --)
_WriteChar(0);
} else {
for (int32 i = fWidth * fHeight * fChannelCount; i > 0; i --)
_WriteShort(0);
}*/
break;
case SGI_COMP_ARLE:
fARLERow = (uint16*)calloc(fWidth, sizeof(uint16));
fARLEOffset = 0;
case SGI_COMP_RLE :
fStream->Seek(2 * fHeight * fChannelCount * sizeof(int32), SEEK_CUR);
fFirstRowOffset = fStream->Position();
fNextRowOffset = fStream->Position();
fOffsetTable = (int32**)calloc(fChannelCount, sizeof(int32*));
fOffsetTable[0] = (int32*)calloc(fHeight * fChannelCount, sizeof(int32));
for (int32 i = 1; i < fChannelCount; i ++)
fOffsetTable[i] = fOffsetTable[0] + i * fHeight;
fLengthTable = (int32**)calloc(fChannelCount, sizeof(int32*));
fLengthTable[0] = (int32*)calloc(fHeight * fChannelCount, sizeof(int32));
for (int32 i = 1; i < fChannelCount; i ++)
fLengthTable[i] = fLengthTable[0] + i * fHeight;
break;
}
return B_OK;
}
status_t
SGIImage::Unset()
{
status_t ret = InitCheck();
if (ret >= B_OK) {
if (fMode == SGI_WRITE && fCompression != SGI_COMP_NONE) {
fStream->Seek(512, SEEK_SET);
for (int32 i = fHeight * fChannelCount; i > 0; i--) {
if ((ret = _WriteLong(offset[0])) < B_OK)
break;
offset++;
}*/
int32 size = fHeight * fChannelCount * sizeof(int32);
swap_data(B_INT32_TYPE, fOffsetTable[0], size, B_SWAP_HOST_TO_BENDIAN);
ret = fStream->Write(fOffsetTable[0], size);
if (ret >= B_OK) {
for (int32 i = fHeight * fChannelCount; i > 0; i--) {
if ((ret = _WriteLong(length[0])) < B_OK)
break;
length++;
}*/
swap_data(B_INT32_TYPE, fLengthTable[0], size, B_SWAP_HOST_TO_BENDIAN);
ret = fStream->Write(fLengthTable[0], size);
}
}
if (fOffsetTable != NULL) {
free(fOffsetTable[0]);
free(fOffsetTable);
fOffsetTable = NULL;
}
if (fLengthTable != NULL) {
free(fLengthTable[0]);
free(fLengthTable);
fLengthTable = NULL;
}
if (fARLERow) {
free(fARLERow);
fARLERow = NULL;
}
fStream = NULL;
}
return ret;
}
status_t
SGIImage::ReadRow(void* row, int32 y, int32 z)
{
if (row == NULL ||
y < 0 || y >= fHeight ||
z < 0 || z >= fChannelCount)
return B_BAD_VALUE;
status_t ret = B_ERROR;
switch (fCompression) {
case SGI_COMP_NONE: {
off_t offset = 512 + (y + z * fHeight) * fWidth * fBytesPerChannel;
fStream->Seek(offset, SEEK_SET);
uint32 bytes = fWidth * fBytesPerChannel;
ret = fStream->Read(row, bytes);
break;
}
case SGI_COMP_RLE: {
int32 offset = fOffsetTable[z][y];
int32 rleLength = fLengthTable[z][y];
fStream->Seek(offset, SEEK_SET);
uint8* rleBuffer = new uint8[rleLength];
fStream->Read(rleBuffer, rleLength);
if (fBytesPerChannel == 1) {
ret = _ReadRLE8((uint8*)row, rleBuffer, fWidth);
} else {
if ((ret = swap_data(B_INT16_TYPE, rleBuffer, rleLength, B_SWAP_BENDIAN_TO_HOST)) >= B_OK)
ret = _ReadRLE16((uint16*)row, (uint16*)rleBuffer, fWidth);
}
delete[] rleBuffer;
break;
}
}
return ret;
}
status_t
SGIImage::WriteRow(void* row, int32 y, int32 z)
{
if (row == NULL ||
y < 0 || y >= fHeight ||
z < 0 || z >= fChannelCount)
return B_BAD_VALUE;
int32 x;
int32 offset;
status_t ret = B_ERROR;
switch (fCompression) {
case SGI_COMP_NONE: {
offset = 512 + (y + z * fHeight) * fWidth * fBytesPerChannel;
fStream->Seek(offset, SEEK_SET);
uint32 bytes = fWidth * fBytesPerChannel;
ret = fStream->Write(row, bytes);
for (x = fWidth; x > 0; x--) {
_WriteChar(*row);
row++;
}
} else {
for (x = fWidth; x > 0; x--) {
_WriteShort(*row);
row++;
}
}*/
break;
}
case SGI_COMP_ARLE:
if (fOffsetTable[z][y] != 0)
return B_ERROR;
if (fARLEOffset > 0) {
if (fBytesPerChannel == 1) {
uint8* arleRow = (uint8*)fARLERow;
uint8* src = (uint8*)row;
for (x = 0; x < fWidth; x++)
if (*src++ != *arleRow++)
break;
} else {
uint16* arleRow = (uint16*)fARLERow;
uint16* src = (uint16*)row;
for (x = 0; x < fWidth; x++)
if (*src++ != *arleRow++)
break;
}
if (x == fWidth) {
fOffsetTable[z][y] = fARLEOffset;
fLengthTable[z][y] = fARLELength;
return B_OK;
}
}
fStream->Seek(fFirstRowOffset, SEEK_SET);
if (fBytesPerChannel == 1) {
do {
fARLEOffset = fStream->Position();
uint8* arleRow = (uint8*)fARLERow;
if ((fARLELength = _ReadRLE8(arleRow, fWidth)) < B_OK) {
x = 0;
break;
}
uint8* src = (uint8*)row;
for (x = 0; x < fWidth; x++)
if (*src++ != *arleRow++)
break;
} while (x < fWidth);
} else {
do {
fARLEOffset = fStream->Position();
uint16* arleRow = (uint16*)fARLERow;
if ((fARLELength = _ReadRLE16(arleRow, fWidth)) < B_OK) {
x = 0;
break;
}
uint16* src = (uint16*)row;
for (x = 0; x < fWidth; x++)
if (*src++ != *arleRow++)
break;
} while (x < fWidth);
}
if (x == fWidth) {
fOffsetTable[z][y] = fARLEOffset;
fLengthTable[z][y] = fARLELength;
return B_OK;
} else
fStream->Seek(0, SEEK_END);
case SGI_COMP_RLE :
if (fOffsetTable[z][y] != 0)
return B_ERROR;
offset = fOffsetTable[z][y] = fNextRowOffset;
if (offset != fStream->Position())
fStream->Seek(offset, SEEK_SET);
if (fBytesPerChannel == 1)
x = _WriteRLE8((uint8*)row, fWidth);
else
x = _WriteRLE16((uint16*)row, fWidth);
if (fCompression == SGI_COMP_ARLE) {
fARLEOffset = offset;
fARLELength = x;
memcpy(fARLERow, row, fWidth * fBytesPerChannel);
}
fNextRowOffset = fStream->Position();
fLengthTable[z][y] = x;
return x;
default:
break;
}
return ret;
}
int32
SGIImage::_ReadLong() const
{
int32 n;
if (fStream->Read(&n, 4) == 4) {
return B_BENDIAN_TO_HOST_INT32(n);
} else
return 0;
}
int16
SGIImage::_ReadShort() const
{
int16 n;
if (fStream->Read(&n, 2) == 2) {
return B_BENDIAN_TO_HOST_INT16(n);
} else
return 0;
}
int8
SGIImage::_ReadChar() const
{
int8 b;
ssize_t read = fStream->Read(&b, 1);
if (read == 1)
return b;
else if (read < B_OK)
return (int8)read;
return (int8)B_ERROR;
}
status_t
SGIImage::_WriteLong(int32 n) const
{
int32 bigN = B_HOST_TO_BENDIAN_INT32(n);
ssize_t written = fStream->Write(&bigN, sizeof(int32));
if (written == sizeof(int32))
return B_OK;
if (written < B_OK)
return written;
return B_ERROR;
}
status_t
SGIImage::_WriteShort(uint16 n) const
{
uint16 bigN = B_HOST_TO_BENDIAN_INT16(n);
ssize_t written = fStream->Write(&bigN, sizeof(uint16));
if (written == sizeof(uint16))
return B_OK;
if (written < B_OK)
return written;
return B_ERROR;
}
status_t
SGIImage::_WriteChar(int8 n) const
{
ssize_t written = fStream->Write(&n, sizeof(int8));
if (written == sizeof(int8))
return B_OK;
if (written < B_OK)
return written;
return B_ERROR;
}
ssize_t
SGIImage::_ReadRLE8(uint8* row, int32 numPixels) const
{
int32 ch;
uint32 count;
uint32 length = 0;
uint32 bufferSize = 1024;
uint8* buffer = new uint8[bufferSize];
uint32 bufferPos = bufferSize;
status_t ret = B_OK;
while (numPixels > 0) {
if (bufferPos >= bufferSize) {
ret = fStream->Read(buffer, bufferSize);
if (ret < B_OK)
break;
else
bufferPos = 0;
}
ch = buffer[bufferPos ++];
length ++;
count = ch & 127;
if (count == 0)
break;
if (ch & 128) {
for (uint32 i = 0; i < count; i++) {
if (bufferPos >= bufferSize) {
ret = fStream->Read(buffer, bufferSize);
if (ret < B_OK) {
delete[] buffer;
return ret;
} else
bufferPos = 0;
}
*row = buffer[bufferPos ++];
row ++;
numPixels --;
length ++;
}
} else {
if (bufferPos >= bufferSize) {
ret = fStream->Read(buffer, bufferSize);
if (ret < B_OK) {
delete[] buffer;
return ret;
} else
bufferPos = 0;
}
ch = buffer[bufferPos ++];
length ++;
for (uint32 i = 0; i < count; i++) {
*row = ch;
row ++;
numPixels --;
}
}
}
delete[] buffer;
return (numPixels > 0 ? ret : length);
}
ssize_t
SGIImage::_ReadRLE8(uint8* row, uint8* rleBuffer, int32 numPixels) const
{
int32 ch;
uint32 count;
uint32 length = 0;
if (numPixels <= 0)
return B_ERROR;
while (numPixels > 0) {
ch = *rleBuffer ++;
length ++;
count = ch & 127;
if (count == 0)
break;
if (ch & 128) {
for (uint32 i = 0; i < count; i++) {
*row = *rleBuffer ++;
row ++;
numPixels --;
length ++;
}
} else {
ch = *rleBuffer ++;
length ++;
for (uint32 i = 0; i < count; i++) {
*row = ch;
row ++;
numPixels --;
}
}
}
return length;
}
SGIImage::_ReadRLE8(uint8* row, int32 numPixels) const
{
int32 ch; // current charater
uint32 count; // RLE count
uint32 length = 0; // number of bytes read
while (numPixels > 0) {
ch = _ReadChar();
length ++;
count = ch & 127;
if (count == 0)
break;
if (ch & 128) {
for (uint32 i = 0; i < count; i++) {
*row = _ReadChar();
row ++;
numPixels --;
length ++;
}
} else {
ch = _ReadChar();
length ++;
for (uint32 i = 0; i < count; i++) {
*row = ch;
row ++;
numPixels --;
}
}
}
return (numPixels > 0 ? B_ERROR : length);
}*/
status_t
read_and_swap(BPositionIO* stream, int16* buffer, uint32 size)
{
status_t ret = stream->Read(buffer, size);
if (ret >= B_OK)
return swap_data(B_INT16_TYPE, buffer, ret, B_SWAP_BENDIAN_TO_HOST);
return ret;
}
ssize_t
SGIImage::_ReadRLE16(uint16* row, int32 numPixels) const
{
int32 ch;
uint32 count;
uint32 length = 0;
uint32 bufferSize = 1024;
int16* buffer = new int16[bufferSize];
uint32 bufferPos = bufferSize;
status_t ret = B_OK;
while (numPixels > 0) {
if (bufferPos >= bufferSize) {
ret = read_and_swap(fStream, buffer, bufferSize * 2);
if (ret < B_OK)
break;
bufferPos = 0;
}
ch = buffer[bufferPos ++];
length ++;
count = ch & 127;
if (count == 0)
break;
if (ch & 128) {
for (uint32 i = 0; i < count; i++) {
if (bufferPos >= bufferSize) {
ret = read_and_swap(fStream, buffer, bufferSize * 2);
if (ret < B_OK) {
delete[] buffer;
return ret;
} else
bufferPos = 0;
}
*row = B_HOST_TO_BENDIAN_INT16(buffer[bufferPos ++]);
row++;
numPixels--;
length++;
}
} else {
if (bufferPos >= bufferSize) {
ret = read_and_swap(fStream, buffer, bufferSize * 2);
if (ret < B_OK) {
delete[] buffer;
return ret;
} else
bufferPos = 0;
}
ch = B_HOST_TO_BENDIAN_INT16(buffer[bufferPos ++]);
length ++;
for (uint32 i = 0; i < count; i++) {
*row = ch;
row++;
numPixels--;
}
}
}
delete[] buffer;
return (numPixels > 0 ? ret : length * 2);
}
ssize_t
SGIImage::_ReadRLE16(uint16* row, uint16* rleBuffer, int32 numPixels) const
{
int32 ch;
uint32 count;
uint32 length = 0;
if (numPixels <= 0)
return B_ERROR;
while (numPixels > 0) {
ch = *rleBuffer ++;
length ++;
count = ch & 127;
if (count == 0)
break;
if (ch & 128) {
for (uint32 i = 0; i < count; i++) {
*row = B_HOST_TO_BENDIAN_INT16(*rleBuffer ++);
row++;
numPixels--;
length++;
}
} else {
ch = B_HOST_TO_BENDIAN_INT16(*rleBuffer ++);
length ++;
for (uint32 i = 0; i < count; i++) {
*row = ch;
row++;
numPixels--;
}
}
}
return length * 2;
}
ssize_t
SGIImage::_WriteRLE8(uint8* row, int32 numPixels) const
{
int32 length = 0;
int32 count;
int32 i;
uint8* start;
uint16 repeat;
for (int32 x = numPixels; x > 0;) {
start = row;
row += 2;
x -= 2;
while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0])) {
row++;
x--;
}
row -= 2;
x += 2;
count = row - start;
while (count > 0) {
i = count > 126 ? 126 : count;
count -= i;
if (_WriteChar(128 | i) == EOF)
return EOF;
length ++;
while (i > 0) {
if (_WriteChar(*start) == EOF)
return EOF;
start ++;
i --;
length ++;
}
}
if (x <= 0)
break;
start = row;
repeat = row[0];
row ++;
x --;
while (x > 0 && *row == repeat) {
row ++;
x --;
}
count = row - start;
while (count > 0) {
i = count > 126 ? 126 : count;
count -= i;
if (_WriteChar(i) == EOF)
return EOF;
length ++;
if (_WriteChar(repeat) == EOF)
return (-1);
length ++;
}
}
length ++;
if (_WriteChar(0) == EOF)
return EOF;
else
return length;
}
ssize_t
SGIImage::_WriteRLE16(uint16* row, int32 numPixels) const
{
int32 length = 0;
int32 count;
int32 i;
int32 x;
uint16* start;
uint16 repeat;
for (x = numPixels; x > 0;) {
start = row;
row += 2;
x -= 2;
while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0])) {
row ++;
x --;
}
row -= 2;
x += 2;
count = row - start;
while (count > 0) {
i = count > 126 ? 126 : count;
count -= i;
if (_WriteShort(128 | i) == EOF)
return EOF;
length ++;
while (i > 0) {
if (_WriteShort(*start) == EOF)
return EOF;
start ++;
i --;
length ++;
}
}
if (x <= 0)
break;
start = row;
repeat = row[0];
row ++;
x --;
while (x > 0 && *row == repeat) {
row ++;
x --;
}
count = row - start;
while (count > 0) {
i = count > 126 ? 126 : count;
count -= i;
if (_WriteShort(i) == EOF)
return EOF;
length ++;
if (_WriteShort(repeat) == EOF)
return EOF;
length ++;
}
}
length ++;
if (_WriteShort(0) == EOF)
return EOF;
else
return (2 * length);
}