2012-03-11 13:48:40 +00:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* lynxsprite.c */
|
|
|
|
/* */
|
|
|
|
/* Lynx sprite format backend for the sp65 sprite and bitmap utility */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* (C) 2012, Ullrich von Bassewitz */
|
|
|
|
/* Roemerstrasse 52 */
|
|
|
|
/* D-70794 Filderstadt */
|
|
|
|
/* EMail: uz@cc65.org */
|
|
|
|
/* */
|
|
|
|
/* */
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
/* */
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
/* */
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
/* distribution. */
|
|
|
|
/* */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-11-06 06:24:50 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2012-03-11 13:48:40 +00:00
|
|
|
/* common */
|
|
|
|
#include "attrib.h"
|
|
|
|
#include "print.h"
|
|
|
|
|
|
|
|
/* sp65 */
|
|
|
|
#include "attr.h"
|
|
|
|
#include "error.h"
|
|
|
|
#include "lynxsprite.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Data */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Sprite mode */
|
|
|
|
enum Mode {
|
|
|
|
smAuto,
|
|
|
|
smLiteral,
|
2012-11-03 17:25:01 +00:00
|
|
|
smPacked,
|
2012-11-11 18:49:27 +00:00
|
|
|
smShaped
|
2012-03-11 13:48:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Code */
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static enum Mode GetMode (const Collection* A)
|
|
|
|
/* Return the sprite mode from the attribute collection A */
|
|
|
|
{
|
|
|
|
/* Check for a mode attribute */
|
|
|
|
const char* Mode = GetAttrVal (A, "mode");
|
|
|
|
if (Mode) {
|
|
|
|
if (strcmp (Mode, "literal") == 0) {
|
|
|
|
return smLiteral;
|
|
|
|
} else if (strcmp (Mode, "packed") == 0) {
|
|
|
|
return smPacked;
|
2012-11-11 18:49:27 +00:00
|
|
|
} else if (strcmp (Mode, "shaped") == 0) {
|
|
|
|
return smShaped;
|
2012-03-11 13:48:40 +00:00
|
|
|
} else {
|
2019-01-05 19:57:12 +00:00
|
|
|
Error ("Invalid value for attribute 'mode'");
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-06 20:41:02 +00:00
|
|
|
|
|
|
|
return smAuto;
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-06 06:24:50 +00:00
|
|
|
static unsigned GetActionPointX (const Collection* A)
|
|
|
|
/* Return the sprite mode from the attribute collection A */
|
|
|
|
{
|
|
|
|
/* Check for a action point x attribute */
|
|
|
|
const char* ActionPointX = GetAttrVal (A, "ax");
|
|
|
|
if (ActionPointX) {
|
|
|
|
return atoi(ActionPointX);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned GetActionPointY (const Collection* A)
|
|
|
|
/* Return the sprite mode from the attribute collection A */
|
|
|
|
{
|
|
|
|
/* Check for a action point y attribute */
|
|
|
|
const char* ActionPointY = GetAttrVal (A, "ay");
|
|
|
|
if (ActionPointY) {
|
|
|
|
return atoi(ActionPointY);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-14 19:08:24 +00:00
|
|
|
static unsigned GetEdgeIndex (const Collection* A)
|
|
|
|
/* Return the sprite mode from the attribute collection A */
|
|
|
|
{
|
|
|
|
/* Get index for edge color in shaped mode */
|
|
|
|
const char* EdgeIndex = GetAttrVal (A, "edge");
|
|
|
|
if (EdgeIndex) {
|
|
|
|
return atoi(EdgeIndex);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-11 18:49:27 +00:00
|
|
|
static char OutBuffer[512]; /* The maximum size is 508 pixels */
|
|
|
|
static unsigned char OutIndex;
|
|
|
|
|
|
|
|
static void AssembleByte(unsigned bits, char val)
|
|
|
|
{
|
|
|
|
static char bit_counter = 8, byte = 0;
|
|
|
|
|
|
|
|
/* initialize */
|
|
|
|
if (!bits) {
|
|
|
|
OutIndex = 0;
|
|
|
|
bit_counter = 8;
|
|
|
|
byte = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* handle end of line */
|
|
|
|
if (bits == 8) {
|
2012-11-12 18:43:03 +00:00
|
|
|
if (bit_counter != 8) {
|
|
|
|
byte <<= bit_counter;
|
|
|
|
OutBuffer[OutIndex++] = byte;
|
|
|
|
if (!OutIndex) {
|
2012-11-14 19:08:24 +00:00
|
|
|
Error ("Sprite is too large for the Lynx");
|
2012-11-12 18:43:03 +00:00
|
|
|
}
|
|
|
|
if (byte & 0x1) {
|
|
|
|
OutBuffer[OutIndex++] = byte;
|
|
|
|
if (!OutIndex) {
|
2012-11-14 19:08:24 +00:00
|
|
|
Error ("Sprite is too large for the Lynx");
|
2012-11-12 18:43:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2012-11-13 14:48:25 +00:00
|
|
|
/* handle end of line for literal */
|
|
|
|
if (bits == 7) {
|
2012-11-12 18:43:03 +00:00
|
|
|
if (bit_counter != 8) {
|
|
|
|
byte <<= bit_counter;
|
2012-11-11 18:49:27 +00:00
|
|
|
OutBuffer[OutIndex++] = byte;
|
|
|
|
if (!OutIndex) {
|
2012-11-14 19:08:24 +00:00
|
|
|
Error ("Sprite is too large for the Lynx");
|
2012-11-11 18:49:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
val <<= 8 - bits;
|
|
|
|
|
|
|
|
do {
|
|
|
|
byte <<= 1;
|
|
|
|
|
|
|
|
if (val & 0x80)
|
|
|
|
++byte;
|
|
|
|
|
|
|
|
if (!(--bit_counter)) {
|
|
|
|
OutBuffer[OutIndex++] = byte;
|
|
|
|
if (!OutIndex) {
|
|
|
|
Error ("Sprite is too large for the Lynx");
|
|
|
|
}
|
|
|
|
byte = 0;
|
|
|
|
bit_counter = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
val <<= 1;
|
|
|
|
|
|
|
|
} while (--bits);
|
|
|
|
}
|
|
|
|
|
2012-12-17 17:48:56 +00:00
|
|
|
static unsigned char ChoosePackagingMode(signed len, signed index, char ColorBits, char LineBuffer[512])
|
2012-11-11 18:49:27 +00:00
|
|
|
{
|
|
|
|
--len;
|
|
|
|
if (!len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (LineBuffer[index] != LineBuffer[index + 1]) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-12-17 17:48:56 +00:00
|
|
|
if (ColorBits > 2) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (LineBuffer[index] != LineBuffer[index + 2]) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ColorBits > 1) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (LineBuffer[index] != LineBuffer[index + 3]) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-11 18:49:27 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteOutBuffer(StrBuf *D)
|
|
|
|
{
|
|
|
|
signed i;
|
|
|
|
|
|
|
|
/* Fix bug in Lynx where the count cannot be 1 */
|
|
|
|
if (OutIndex == 1) {
|
|
|
|
OutBuffer[OutIndex++] = 0;
|
|
|
|
}
|
|
|
|
/* Write the byte count to the end of the scanline */
|
|
|
|
if (OutIndex == 255) {
|
|
|
|
Error ("Sprite is too large for the Lynx");
|
|
|
|
}
|
|
|
|
SB_AppendChar (D, OutIndex+1);
|
|
|
|
/* Write scanline data */
|
|
|
|
for (i = 0; i < OutIndex; i++) {
|
|
|
|
SB_AppendChar (D, OutBuffer[i]);
|
|
|
|
}
|
|
|
|
}
|
2012-11-06 06:24:50 +00:00
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512],
|
2012-11-11 18:49:27 +00:00
|
|
|
int len, int LastOpaquePixel) {
|
2012-11-06 04:49:34 +00:00
|
|
|
/*
|
2014-06-30 09:10:35 +00:00
|
|
|
** The data starts with a byte count. It tells the number of bytes on this
|
|
|
|
** line + 1.
|
|
|
|
** Special case is a count of 1. It will change to next quadrant.
|
|
|
|
** Other special case is 0. It will end the sprite.
|
|
|
|
**
|
|
|
|
** Ordinary data packet. These are bits in a stream.
|
|
|
|
** 1=literal 0=packed
|
|
|
|
** 4 bit count (+1)
|
|
|
|
** for literal you put "count" values
|
|
|
|
** for packed you repeat the value "count" times
|
|
|
|
** Never use packed mode for one pixel
|
|
|
|
** If the last bit on a line is 1 you need to add a byte of zeroes
|
|
|
|
** A sequence 00000 ends a scan line
|
|
|
|
**
|
|
|
|
** All data is high nybble first
|
|
|
|
*/
|
2012-11-06 04:49:34 +00:00
|
|
|
unsigned char V = 0;
|
2012-11-11 18:49:27 +00:00
|
|
|
signed i;
|
|
|
|
signed count;
|
2012-11-12 18:43:03 +00:00
|
|
|
unsigned char differ[16];
|
|
|
|
unsigned char *d_ptr;
|
2012-11-06 04:49:34 +00:00
|
|
|
|
2012-11-13 14:48:25 +00:00
|
|
|
AssembleByte(0, 0);
|
2012-11-06 04:49:34 +00:00
|
|
|
switch (M) {
|
|
|
|
case smAuto:
|
|
|
|
case smLiteral:
|
2012-11-11 18:49:27 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
2012-11-06 04:49:34 +00:00
|
|
|
/* Fetch next pixel index into pixel buffer */
|
2012-11-13 14:48:25 +00:00
|
|
|
AssembleByte(ColorBits, LineBuffer[i] & ColorMask);
|
2012-11-06 04:49:34 +00:00
|
|
|
}
|
2012-11-13 14:48:25 +00:00
|
|
|
AssembleByte(7, 0);
|
2012-11-11 18:49:27 +00:00
|
|
|
/* Write the buffer to file */
|
|
|
|
WriteOutBuffer(D);
|
2012-11-06 04:49:34 +00:00
|
|
|
break;
|
|
|
|
case smPacked:
|
2012-11-11 18:49:27 +00:00
|
|
|
i = 0;
|
|
|
|
while (len) {
|
2012-12-17 17:48:56 +00:00
|
|
|
if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) {
|
2012-11-11 18:49:27 +00:00
|
|
|
/* Make runlength packet */
|
2012-11-12 18:43:03 +00:00
|
|
|
V = LineBuffer[i];
|
|
|
|
++i;
|
|
|
|
--len;
|
|
|
|
count = 0;
|
2012-11-11 18:49:27 +00:00
|
|
|
do {
|
|
|
|
++count;
|
|
|
|
++i;
|
|
|
|
--len;
|
|
|
|
} while (V == LineBuffer[i] && len && count != 15);
|
|
|
|
|
|
|
|
AssembleByte(5, count);
|
|
|
|
AssembleByte(ColorBits, V);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Make packed literal packet */
|
|
|
|
d_ptr = differ;
|
2012-11-12 18:43:03 +00:00
|
|
|
V = LineBuffer[i++];
|
|
|
|
*d_ptr++ = V;
|
|
|
|
--len;
|
|
|
|
count = 0;
|
2012-12-17 17:48:56 +00:00
|
|
|
while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) {
|
2012-11-12 18:43:03 +00:00
|
|
|
V = LineBuffer[i++];
|
2012-11-11 18:49:27 +00:00
|
|
|
*d_ptr++ = V;
|
|
|
|
++count;
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssembleByte(5, count | 0x10);
|
|
|
|
d_ptr = differ;
|
|
|
|
do {
|
|
|
|
AssembleByte(ColorBits, *d_ptr++);
|
|
|
|
} while (--count >= 0);
|
|
|
|
|
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
}
|
2012-11-11 18:49:27 +00:00
|
|
|
AssembleByte(8, 0);
|
2012-11-12 18:43:03 +00:00
|
|
|
/* Write the buffer to file */
|
|
|
|
WriteOutBuffer(D);
|
2012-11-06 04:49:34 +00:00
|
|
|
break;
|
2012-11-11 18:49:27 +00:00
|
|
|
|
|
|
|
case smShaped:
|
2012-11-13 14:48:25 +00:00
|
|
|
if (LastOpaquePixel > -1) {
|
|
|
|
if (LastOpaquePixel < len - 1) {
|
|
|
|
len = LastOpaquePixel + 1;
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
while (len) {
|
2012-12-17 17:48:56 +00:00
|
|
|
if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) {
|
2012-11-13 14:48:25 +00:00
|
|
|
/* Make runlength packet */
|
|
|
|
V = LineBuffer[i];
|
2012-11-12 19:26:11 +00:00
|
|
|
++i;
|
|
|
|
--len;
|
2012-11-13 14:48:25 +00:00
|
|
|
count = 0;
|
|
|
|
do {
|
|
|
|
++count;
|
|
|
|
++i;
|
|
|
|
--len;
|
|
|
|
} while (V == LineBuffer[i] && len && count != 15);
|
|
|
|
|
|
|
|
AssembleByte(5, count);
|
|
|
|
AssembleByte(ColorBits, V);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Make packed literal packet */
|
|
|
|
d_ptr = differ;
|
2012-11-12 19:26:11 +00:00
|
|
|
V = LineBuffer[i++];
|
|
|
|
*d_ptr++ = V;
|
|
|
|
--len;
|
2012-11-13 14:48:25 +00:00
|
|
|
count = 0;
|
2012-12-17 17:48:56 +00:00
|
|
|
while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) {
|
2012-11-13 14:48:25 +00:00
|
|
|
V = LineBuffer[i++];
|
|
|
|
*d_ptr++ = V;
|
|
|
|
++count;
|
|
|
|
--len;
|
|
|
|
}
|
|
|
|
|
|
|
|
AssembleByte(5, count | 0x10);
|
|
|
|
d_ptr = differ;
|
|
|
|
do {
|
|
|
|
AssembleByte(ColorBits, *d_ptr++);
|
|
|
|
} while (--count >= 0);
|
2012-11-12 19:26:11 +00:00
|
|
|
|
2012-11-13 14:48:25 +00:00
|
|
|
}
|
2012-11-12 19:26:11 +00:00
|
|
|
}
|
2012-11-13 14:48:25 +00:00
|
|
|
AssembleByte(5, 0);
|
|
|
|
AssembleByte(8, 0);
|
|
|
|
/* Write the buffer to file */
|
|
|
|
WriteOutBuffer(D);
|
2012-11-12 19:26:11 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-03-11 13:48:40 +00:00
|
|
|
|
|
|
|
StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
|
|
|
|
/* Generate binary output in Lynx sprite format for the bitmap B. The output
|
2014-06-30 09:10:35 +00:00
|
|
|
** is stored in a string buffer (which is actually a dynamic char array) and
|
|
|
|
** returned.
|
|
|
|
**
|
|
|
|
** The Lynx will draw 4 quadrants:
|
|
|
|
** - Down right
|
|
|
|
** - Up right
|
|
|
|
** - Up left
|
|
|
|
** - Down left
|
|
|
|
**
|
|
|
|
** The sprite will end with a byte 0.
|
|
|
|
*/
|
2012-03-11 13:48:40 +00:00
|
|
|
{
|
|
|
|
enum Mode M;
|
|
|
|
StrBuf* D;
|
2012-11-06 04:49:34 +00:00
|
|
|
signed X, Y;
|
2012-11-03 17:25:01 +00:00
|
|
|
unsigned OX, OY;
|
|
|
|
char ColorBits;
|
|
|
|
char ColorMask;
|
2012-11-14 19:08:24 +00:00
|
|
|
char EdgeIndex;
|
|
|
|
|
|
|
|
/* Get EdgeIndex */
|
|
|
|
EdgeIndex = GetEdgeIndex (A);
|
2012-03-11 13:48:40 +00:00
|
|
|
|
2012-11-06 06:24:50 +00:00
|
|
|
/* Action point of the sprite */
|
|
|
|
OX = GetActionPointX (A);
|
|
|
|
OY = GetActionPointY (A);
|
|
|
|
if (OX >= GetBitmapWidth (B)) {
|
|
|
|
Error ("Action point X cannot be larger than bitmap width");
|
|
|
|
}
|
|
|
|
if (OY >= GetBitmapHeight (B)) {
|
|
|
|
Error ("Action point Y cannot be larger than bitmap height");
|
|
|
|
}
|
2012-03-11 13:48:40 +00:00
|
|
|
|
|
|
|
/* Output the image properties */
|
|
|
|
Print (stdout, 1, "Image is %ux%u with %u colors%s\n",
|
|
|
|
GetBitmapWidth (B), GetBitmapHeight (B), GetBitmapColors (B),
|
|
|
|
BitmapIsIndexed (B)? " (indexed)" : "");
|
|
|
|
|
|
|
|
/* Get the sprite mode */
|
|
|
|
M = GetMode (A);
|
|
|
|
|
|
|
|
/* Now check if bitmap indexes are ok */
|
|
|
|
if (GetBitmapColors (B) > 16) {
|
|
|
|
Error ("Too many colors for a Lynx sprite");
|
|
|
|
}
|
2012-11-03 17:25:01 +00:00
|
|
|
ColorBits = 4;
|
|
|
|
ColorMask = 0x0f;
|
|
|
|
if (GetBitmapColors (B) < 9) {
|
|
|
|
ColorBits = 3;
|
|
|
|
ColorMask = 0x07;
|
|
|
|
}
|
|
|
|
if (GetBitmapColors (B) < 5) {
|
|
|
|
ColorBits = 2;
|
|
|
|
ColorMask = 0x03;
|
|
|
|
}
|
|
|
|
if (GetBitmapColors (B) < 3) {
|
|
|
|
ColorBits = 1;
|
|
|
|
ColorMask = 0x01;
|
|
|
|
}
|
2012-03-11 13:48:40 +00:00
|
|
|
|
|
|
|
/* Create the output buffer and resize it to the required size. */
|
|
|
|
D = NewStrBuf ();
|
|
|
|
SB_Realloc (D, 63);
|
|
|
|
|
2012-11-03 17:25:01 +00:00
|
|
|
/* Convert the image for quadrant bottom right */
|
2012-11-06 04:49:34 +00:00
|
|
|
for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
|
2012-11-03 17:25:01 +00:00
|
|
|
signed i = 0;
|
|
|
|
signed LastOpaquePixel = -1;
|
2012-11-06 04:49:34 +00:00
|
|
|
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
2012-11-03 17:25:01 +00:00
|
|
|
|
|
|
|
/* Fill the LineBuffer for easier optimisation */
|
2012-11-06 04:49:34 +00:00
|
|
|
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
2012-11-03 17:25:01 +00:00
|
|
|
|
|
|
|
/* Fetch next bit into byte buffer */
|
|
|
|
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
|
|
|
2012-11-14 19:08:24 +00:00
|
|
|
if (LineBuffer[i] != EdgeIndex) {
|
2012-11-03 17:25:01 +00:00
|
|
|
LastOpaquePixel = i;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((OY == 0) && (OX == 0)) {
|
|
|
|
/* Trivial case only one quadrant */
|
|
|
|
|
2012-11-03 20:13:25 +00:00
|
|
|
/* Mark end of sprite */
|
|
|
|
SB_AppendChar (D, 0);
|
|
|
|
|
2012-11-03 17:25:01 +00:00
|
|
|
/* Return the converted bitmap */
|
|
|
|
return D;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next quadrant */
|
|
|
|
SB_AppendChar (D, 1);
|
|
|
|
|
|
|
|
/* Convert the image for quadrant top right */
|
|
|
|
for (Y = OY - 1; Y >= 0; --Y) {
|
2012-11-06 04:49:34 +00:00
|
|
|
signed i = 0;
|
|
|
|
signed LastOpaquePixel = -1;
|
|
|
|
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
2012-03-11 13:48:40 +00:00
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
/* Fill the LineBuffer for easier optimisation */
|
|
|
|
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
2012-03-11 13:48:40 +00:00
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
/* Fetch next bit into byte buffer */
|
|
|
|
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
|
|
|
2012-11-14 19:08:24 +00:00
|
|
|
if (LineBuffer[i] != EdgeIndex) {
|
2012-11-06 04:49:34 +00:00
|
|
|
LastOpaquePixel = i;
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
++i;
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
|
|
|
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
|
|
|
|
2012-11-06 06:33:17 +00:00
|
|
|
if (OX == 0) {
|
|
|
|
/* Special case only two quadrants */
|
|
|
|
|
|
|
|
/* Mark end of sprite */
|
|
|
|
SB_AppendChar (D, 0);
|
|
|
|
|
|
|
|
/* Return the converted bitmap */
|
|
|
|
return D;
|
|
|
|
}
|
|
|
|
|
2012-11-03 17:25:01 +00:00
|
|
|
/* Next quadrant */
|
|
|
|
SB_AppendChar (D, 1);
|
|
|
|
|
|
|
|
/* Convert the image for quadrant top left */
|
|
|
|
for (Y = OY - 1; Y >= 0; --Y) {
|
2012-11-06 04:49:34 +00:00
|
|
|
signed i = 0;
|
|
|
|
signed LastOpaquePixel = -1;
|
|
|
|
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
2012-03-11 13:48:40 +00:00
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
/* Fill the LineBuffer for easier optimisation */
|
|
|
|
for (X = OX - 1; X >= 0; --X) {
|
2012-03-11 13:48:40 +00:00
|
|
|
|
2012-11-06 04:49:34 +00:00
|
|
|
/* Fetch next bit into byte buffer */
|
|
|
|
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
|
|
|
2012-11-14 19:08:24 +00:00
|
|
|
if (LineBuffer[i] != EdgeIndex) {
|
2012-11-06 04:49:34 +00:00
|
|
|
LastOpaquePixel = i;
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
++i;
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
|
|
|
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
2012-03-11 13:48:40 +00:00
|
|
|
}
|
|
|
|
|
2012-11-03 17:25:01 +00:00
|
|
|
/* Next quadrant */
|
|
|
|
SB_AppendChar (D, 1);
|
|
|
|
|
|
|
|
/* Convert the image for quadrant bottom left */
|
2012-11-06 04:49:34 +00:00
|
|
|
for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
|
|
|
|
signed i = 0;
|
|
|
|
signed LastOpaquePixel = -1;
|
|
|
|
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
|
|
|
|
|
|
|
/* Fill the LineBuffer for easier optimisation */
|
|
|
|
for (X = OX - 1; X >= 0; --X) {
|
|
|
|
|
|
|
|
/* Fetch next bit into byte buffer */
|
|
|
|
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
|
|
|
2012-11-14 19:08:24 +00:00
|
|
|
if (LineBuffer[i] != EdgeIndex) {
|
2012-11-06 04:49:34 +00:00
|
|
|
LastOpaquePixel = i;
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
++i;
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
2012-11-06 04:49:34 +00:00
|
|
|
|
|
|
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
2012-11-03 17:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* End sprite */
|
|
|
|
SB_AppendChar (D, 0);
|
|
|
|
|
2012-03-11 13:48:40 +00:00
|
|
|
/* Return the converted bitmap */
|
|
|
|
return D;
|
|
|
|
}
|