macemu/BasiliskII/src/pict.c
Charles 6f2635ee31 More clipboard-related fixes for Basilisk / SheepShaver
This patch fixes one lingering problem with the 64-bit clipboard code; the way it was designed, the Mac clipboard was being cleared every time a single item was being requested by GetScrap, causing clipboards with multiple items to be unceremoniously whittled down to one. On the other hand, a similar issue was causing some items to get duplicated on the host pasteboard. This patch fixes the issue by making conversion between the host pasteboard and the Mac clipboard a singular operation; when the pasteboard data changes on the host side, it is all converted and sent to the Mac pasteboard at once, and similarly, all Mac clipboard data is sent to the host pasteboard in one operation. Also, data from the host side is copied to the Mac clipboard only if it has changed since the last check, which should improve performance as conversions will not be done over and over every time the Mac side checks whether the scrap has changed.

In addition, I've added a rudimentary PICT converter. It's rudimentary at the moment, only going in one direction, converting to PICT and not from PICT, and currently it always rasterizes the source image and creates a PICT containing bitmap data. However, it's a start, and it should solve Ronald's issue with copying images from OS X to Mac OS. In the future, more could possibly be added. I've put the new PICT code in the main source directory instead of in the MacOSX subdirectory, so that it can be used by other platforms if needed.

I would like to leave the license on the new PICT code as "Public Domain" if that is okay.

Thanks,
Charles
2012-07-07 11:30:21 -04:00

268 lines
7.9 KiB
C

/*
* pict.c - convert an image to PICT.
*
* Currently creates a bitmap PICT resource; vector graphics are not preserved.
*
* By Charles Srstka.
*
* Public Domain. Do with it as you wish.
*
*/
/*
* PICT format:
*
* Size: 2 bytes
* Bounding Rect: 8 bytes
*
* This is followed by a series of opcodes.
* Each opcode is 1 byte long for a Version 1 PICT, or 2 bytes long for a Version 2 PICT.
* The ones we currently care about are:
*
* 0x0011 VersionOp: begins PICT version 2
* 0x02ff Version: identifies PICT version 2
* 0x0c00 HeaderOp: followed by 24 header bytes
* 4 bytes: 0xFFFFFFFF for regular v2 PICT or 0xFFFE0000 for extended v2 PICT
* 16 bytes: fixed-point bounding rectangle (or resolution, if an extended v2 PICT)
* 4 bytes: reserved
*
* 0x0001 Clip: set clipping region: followed by variable-sized region
* 0x001e DefHilite: set default highlight color
* 0x009b DirectBitsRgn: bitmap data
* pixMap: 50 bytes (PixMap)
* srcRect: 8 bytes (Rect)
* dstRect: 8 bytes (Rect)
* mode: 2 bytes (Mode)
* maskRgn: variable (Region)
* pixData: variable
* 0x00ff End of File
*/
/*
* PixMap format:
*
* baseAddr: Ptr (4 bytes)
* rowBytes: Integer (2 bytes)
* bounds: Rect (8 bytes)
* pmVersion: Integer (2 bytes)
* packType: Integer (2 bytes)
* packSize: LongInt (4 bytes)
* hRes: Fixed (4 bytes)
* vRes: Fixed (4 bytes)
* pixelType: Integer (2 bytes)
* pixelSize: Integer (2 bytes)
* cmpCount: Integer (2 bytes)
* cmpSize: Integer (2 bytes)
* planeBytes: LongInt (4 bytes)
* pmTable: CTabHandle (4 bytes)
* pmReserved: LongInt (4 bytes)
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <stdint.h>
static ssize_t CompressUsingRLE(uint8_t *row, uint16_t uncmpLength, uint8_t *outBuf, size_t bufSize)
{
int byteCountLength = 1 + (uncmpLength > 250);
uint16_t cmpCursor = byteCountLength;
uint16_t cursor = 0;
// enough to output the data uncompressed if we have to, plus the length bytes
size_t maxSize = byteCountLength + uncmpLength + (uncmpLength + 126) / 127;
int outOfRoom = 0;
uint16_t cmpLength;
if (row == NULL || outBuf == NULL || bufSize == 0)
return maxSize;
while (cursor < uncmpLength) {
uint8_t byte = row[cursor++];
uint8_t nextByte;
if (cursor < uncmpLength && (nextByte = row[cursor]) == byte) {
int8_t matches = 1;
while (++cursor < uncmpLength && matches < 127 && row[cursor] == byte) {
matches++;
}
if(cmpCursor + 2 > bufSize) {
outOfRoom = 1;
break;
}
outBuf[cmpCursor++] = -matches;
outBuf[cmpCursor++] = byte;
} else {
uint8_t literals = 0;
uint8_t i;
while (cursor + literals + 1 < uncmpLength && literals < 127 && nextByte != (nextByte = row[cursor + literals + 1])) {
literals++;
}
if(cmpCursor + 2 + literals > bufSize) {
outOfRoom = 1;
break;
}
outBuf[cmpCursor++] = literals;
outBuf[cmpCursor++] = byte;
for (i = 0; i < literals; i++) {
outBuf[cmpCursor++] = row[cursor++];
}
}
}
if(outOfRoom) {
// Trying to compress this just made it larger; just output the data uncompressed instead
if(bufSize < maxSize) {
// sorry folks, don't have enough buffer
return -1;
}
cursor = 0;
cmpCursor = byteCountLength;
while (cursor < uncmpLength) {
uint8_t bytesToCopy = uncmpLength - cursor > 128 ? 128 : uncmpLength - cursor;
outBuf[cmpCursor++] = bytesToCopy - 1;
memcpy(outBuf + cmpCursor, row + cursor, bytesToCopy); cmpCursor += bytesToCopy; cursor += bytesToCopy;
}
cmpLength = cmpCursor - 1;
}
cmpLength = cmpCursor - byteCountLength;
if (byteCountLength == 2) {
outBuf[0] = cmpLength >> 8;
outBuf[1] = cmpLength & 0xff;
} else {
outBuf[0] = cmpLength;
}
return cmpCursor;
}
ssize_t ConvertRGBAToPICT(uint8_t *buf, unsigned long bufSize, uint8_t *rgbaPixels, uint16_t width, uint16_t height)
{
unsigned long initialSize = (10 /* size + rect */ +
6 /* initial opcodes */ +
24 /* header */ +
12 /* clip region */ +
2 /* DefHilite */ +
70 /* DirectBitsRgn - pixData - maskRgn */ +
10 /* maskRgn */);
#define RECT_SIZE 8
#define REGION_SIZE 10
#define FIXED_RECT_SIZE 16
char rect[RECT_SIZE] = {0, 0, 0, 0, height >> 8, height & 0xff, width >> 8, width & 0xff };
char region[REGION_SIZE] = { 0x00, 0x0a, rect[0], rect[1], rect[2], rect[3], rect[4], rect[5], rect[6], rect[7] };
char fixedRect[FIXED_RECT_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, width >> 8, width & 0xff, 0, 0, height >> 8, height & 0xff, 0, 0 };
uint32_t hDPI = htonl(0x00480000); // 72 dpi
uint32_t vDPI = hDPI;
uint16_t bytesPerPixel = 4; // RGBA
uint16_t bytesPerRow = width * bytesPerPixel;
unsigned long cursor = 2; // size bytes filled in at the end
ssize_t cmpBufSize = CompressUsingRLE(NULL, bytesPerRow, NULL, 0);
uint8_t cmpBuf[cmpBufSize];
uint16_t i;
if (buf == NULL || bufSize == 0) {
// Give an upper bound for the buffer size.
return initialSize + height * cmpBufSize + 2;
}
if (bufSize < initialSize) {
return -1;
}
memcpy(buf + cursor, rect, RECT_SIZE); cursor += RECT_SIZE;
buf[cursor++] = 0x00; buf[cursor++] = 0x11; buf[cursor++] = 0x02; buf[cursor++] = 0xff;
buf[cursor++] = 0x0c; buf[cursor++] = 0x00;
buf[cursor++] = 0xff; buf[cursor++] = 0xff; buf[cursor++] = 0xff; buf[cursor++] = 0xff;
memcpy(buf + cursor, fixedRect, FIXED_RECT_SIZE); cursor += FIXED_RECT_SIZE;
memset(buf + cursor, '\0', 4); cursor += 4;
buf[cursor++] = 0x00; buf[cursor++] = 0x1e;
buf[cursor++] = 0x00; buf[cursor++] = 0x01;
memcpy(buf + cursor, region, REGION_SIZE); cursor += REGION_SIZE;
buf[cursor++] = 0x00; buf[cursor++] = 0x9b;
memset(buf + cursor, '\0', 4); cursor += 4; // I think this pointer isn't used
buf[cursor++] = (bytesPerRow >> 8) | 0x80; buf[cursor++] = bytesPerRow & 0xff; // rowBytes
memcpy(buf + cursor, rect, RECT_SIZE); cursor += RECT_SIZE; //bounds
buf[cursor++] = 0x00; buf[cursor++] = 0x00; // pmVersion
buf[cursor++] = 0x00; buf[cursor++] = 0x04; // packType
buf[cursor++] = 0x00; buf[cursor++] = 0x00; buf[cursor++] = 0x00; buf[cursor++] = 0x00; // packSize is always 0
memcpy(buf + cursor, &hDPI, 4); cursor += 4; // hRes
memcpy(buf + cursor, &vDPI, 4); cursor += 4; // vRes
buf[cursor++] = 0x00; buf[cursor++] = 0x10; // pixelType; direct device
buf[cursor++] = 0x00; buf[cursor++] = 0x20; // pixelSize; 32 bits per pixel
buf[cursor++] = bytesPerPixel >> 8; buf[cursor++] = bytesPerPixel & 0xff; // components per pixel
buf[cursor++] = 0x00; buf[cursor++] = 0x08; // 8 bits per component
memset(buf + cursor, '\0', 4); cursor += 4; // planeBytes isn't used
memset(buf + cursor, '\0', 4); cursor += 4; // don't think we need pmTable
memset(buf + cursor, '\0', 4); cursor += 4; // reserved
memcpy(buf + cursor, rect, RECT_SIZE); cursor += RECT_SIZE;
memcpy(buf + cursor, rect, RECT_SIZE); cursor += RECT_SIZE;
buf[cursor++] = 0x00; buf[cursor++] = 0x00; // no transfer mode
memcpy(buf + cursor, region, REGION_SIZE); cursor += REGION_SIZE;
for (i = 0; i < height; i++) {
uint8_t row[bytesPerRow];
ssize_t cmpLength;
uint16_t j;
for (j = 0; j < width; j++) {
row[j] = rgbaPixels[i * bytesPerRow + j * bytesPerPixel + 3];
row[width + j] = rgbaPixels[i * bytesPerRow + j * bytesPerPixel];
row[width * 2 + j] = rgbaPixels[i * bytesPerRow + j * bytesPerPixel + 1];
row[width * 3 + j] = rgbaPixels[i * bytesPerRow + j * bytesPerPixel + 2];
}
cmpLength = CompressUsingRLE(row, bytesPerRow, cmpBuf, cmpBufSize);
if (cmpLength < 0 || cursor + cmpLength > bufSize)
return -1;
memcpy(buf + cursor, cmpBuf, cmpLength); cursor += cmpLength;
}
// Fun fact: forgetting to put 0x00ff at the end of a PICT picture causes the entire
// Classic Mac OS to crash when it tries to read it! Don't ask me how I learned this.
if (cursor + 2 > bufSize)
return -1;
buf[cursor++] = 0x00; buf[cursor++] = 0xff;
if(cursor > UINT16_MAX) {
buf[0] = buf[1] = 0xff;
} else {
buf[0] = cursor >> 8;
buf[1] = cursor & 0xff;
}
return cursor;
}