Merge pull request #1958 from karrika/sprite
[sp65] Sprite generation fixed for 1BPP Lynx sprites. Fix for #1957
This commit is contained in:
@@ -75,6 +75,9 @@ struct Bitmap {
|
|||||||
unsigned Width;
|
unsigned Width;
|
||||||
unsigned Height;
|
unsigned Height;
|
||||||
|
|
||||||
|
/* Bits per pixels */
|
||||||
|
unsigned BPP;
|
||||||
|
|
||||||
/* Palette for indexed bitmap types, otherwise NULL */
|
/* Palette for indexed bitmap types, otherwise NULL */
|
||||||
Palette* Pal;
|
Palette* Pal;
|
||||||
|
|
||||||
@@ -179,6 +182,17 @@ INLINE unsigned GetBitmapColors (const Bitmap* B)
|
|||||||
# define GetBitmapColors(B) ((B)->Pal? (B)->Pal->Count : (1U << 24))
|
# define GetBitmapColors(B) ((B)->Pal? (B)->Pal->Count : (1U << 24))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_INLINE)
|
||||||
|
INLINE unsigned GetBitmapBPP (const Bitmap* B)
|
||||||
|
/* Get the bits per pixel of the converted sprite
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
return B->BPP;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define GetBitmapBPP(B) ((B)->BPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* End of bitmap.h */
|
/* End of bitmap.h */
|
||||||
|
|||||||
@@ -88,12 +88,18 @@ static enum Mode GetMode (const Collection* A)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static unsigned GetActionPointX (const Collection* A)
|
static unsigned GetActionPointX (const Bitmap* B, const Collection* A)
|
||||||
/* Return the sprite mode from the attribute collection A */
|
/* Return the sprite mode from the attribute collection A */
|
||||||
{
|
{
|
||||||
/* Check for a action point x attribute */
|
/* Check for a action point x attribute */
|
||||||
const char* ActionPointX = GetAttrVal (A, "ax");
|
const char* ActionPointX = GetAttrVal (A, "ax");
|
||||||
if (ActionPointX) {
|
if (ActionPointX) {
|
||||||
|
if (strcmp (ActionPointX, "mid") == 0) {
|
||||||
|
return GetBitmapWidth (B) / 2;
|
||||||
|
}
|
||||||
|
if (strcmp (ActionPointX, "max") == 0) {
|
||||||
|
return GetBitmapWidth (B) - 1;
|
||||||
|
}
|
||||||
return atoi(ActionPointX);
|
return atoi(ActionPointX);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -101,12 +107,18 @@ static unsigned GetActionPointX (const Collection* A)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static unsigned GetActionPointY (const Collection* A)
|
static unsigned GetActionPointY (const Bitmap* B, const Collection* A)
|
||||||
/* Return the sprite mode from the attribute collection A */
|
/* Return the sprite mode from the attribute collection A */
|
||||||
{
|
{
|
||||||
/* Check for a action point y attribute */
|
/* Check for a action point y attribute */
|
||||||
const char* ActionPointY = GetAttrVal (A, "ay");
|
const char* ActionPointY = GetAttrVal (A, "ay");
|
||||||
if (ActionPointY) {
|
if (ActionPointY) {
|
||||||
|
if (strcmp (ActionPointY, "mid") == 0) {
|
||||||
|
return GetBitmapHeight (B) / 2;
|
||||||
|
}
|
||||||
|
if (strcmp (ActionPointY, "max") == 0) {
|
||||||
|
return GetBitmapHeight (B) - 1;
|
||||||
|
}
|
||||||
return atoi(ActionPointY);
|
return atoi(ActionPointY);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -125,6 +137,124 @@ static unsigned GetEdgeIndex (const Collection* A)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned GetQuadrant (const Collection* A)
|
||||||
|
/* Return the sprite mode from the attribute collection A */
|
||||||
|
{
|
||||||
|
/* Get index for edge color in shaped mode */
|
||||||
|
const char* Quadrant = GetAttrVal (A, "quadrant");
|
||||||
|
if (Quadrant) {
|
||||||
|
return atoi(Quadrant);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OptimizePenpal (const Bitmap* B, char *PenPal)
|
||||||
|
/* Create an optimal Penpal */
|
||||||
|
{
|
||||||
|
char usage[16];
|
||||||
|
unsigned I, J, Val;
|
||||||
|
|
||||||
|
memset(usage, 0, sizeof(usage));
|
||||||
|
for (J = 0; J < GetBitmapHeight (B); J++) {
|
||||||
|
for (I = 0; I < GetBitmapWidth (B); I++) {
|
||||||
|
Val = GetPixel (B, I, J).Index;
|
||||||
|
if (Val < 16) {
|
||||||
|
usage[Val] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
J = 0;
|
||||||
|
for (I = 0; I < 16; I++) {
|
||||||
|
if (usage[I]) {
|
||||||
|
switch (I) {
|
||||||
|
case 0:
|
||||||
|
PenPal[J] = '0';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
PenPal[J] = '1';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
PenPal[J] = '2';
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
PenPal[J] = '3';
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
PenPal[J] = '4';
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
PenPal[J] = '5';
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
PenPal[J] = '6';
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
PenPal[J] = '7';
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
PenPal[J] = '8';
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
PenPal[J] = '9';
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
PenPal[J] = 'a';
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
PenPal[J] = 'b';
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
PenPal[J] = 'c';
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
PenPal[J] = 'd';
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
PenPal[J] = 'e';
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
PenPal[J] = 'f';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
J++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (J < 16) {
|
||||||
|
PenPal[J] = 0;
|
||||||
|
J++;
|
||||||
|
}
|
||||||
|
/* printf("Penpal %s\n", PenPal); */
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned GetPenpal (const Bitmap* B, const Collection* A, char *PenPal)
|
||||||
|
/* Return the penpal from the attribute collection A */
|
||||||
|
{
|
||||||
|
const char* Pen = GetAttrVal (A, "pen");
|
||||||
|
if (Pen) {
|
||||||
|
if (strcmp (Pen, "opt") == 0) {
|
||||||
|
/* So we need to optimize the penpal and colour depth */
|
||||||
|
OptimizePenpal (B, PenPal);
|
||||||
|
} else {
|
||||||
|
strncpy(PenPal, Pen, 17);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned GetBPP (const Collection* A)
|
||||||
|
/* Return the sprite depth from the attribute collection A */
|
||||||
|
{
|
||||||
|
/* Get index for edge color in shaped mode */
|
||||||
|
const char* BPP = GetAttrVal (A, "bpp");
|
||||||
|
if (BPP) {
|
||||||
|
return atoi(BPP);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char OutBuffer[512]; /* The maximum size is 508 pixels */
|
static char OutBuffer[512]; /* The maximum size is 508 pixels */
|
||||||
static unsigned char OutIndex;
|
static unsigned char OutIndex;
|
||||||
|
|
||||||
@@ -140,26 +270,16 @@ static void AssembleByte(unsigned bits, char val)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* handle end of line */
|
/* handle end of line */
|
||||||
if (bits == 8) {
|
if (bits == 7) {
|
||||||
if (bit_counter != 8) {
|
if (bit_counter != 8) {
|
||||||
byte <<= bit_counter;
|
byte <<= bit_counter;
|
||||||
OutBuffer[OutIndex++] = byte;
|
OutBuffer[OutIndex++] = byte;
|
||||||
if (!OutIndex) {
|
if (!OutIndex) {
|
||||||
Error ("Sprite is too large for the Lynx");
|
Error ("Sprite is too large for the Lynx");
|
||||||
}
|
}
|
||||||
if (byte & 0x1) {
|
} else {
|
||||||
OutBuffer[OutIndex++] = byte;
|
/* Add pad byte */
|
||||||
if (!OutIndex) {
|
byte = 0;
|
||||||
Error ("Sprite is too large for the Lynx");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* handle end of line for literal */
|
|
||||||
if (bits == 7) {
|
|
||||||
if (bit_counter != 8) {
|
|
||||||
byte <<= bit_counter;
|
|
||||||
OutBuffer[OutIndex++] = byte;
|
OutBuffer[OutIndex++] = byte;
|
||||||
if (!OutIndex) {
|
if (!OutIndex) {
|
||||||
Error ("Sprite is too large for the Lynx");
|
Error ("Sprite is too large for the Lynx");
|
||||||
@@ -189,28 +309,78 @@ static void AssembleByte(unsigned bits, char val)
|
|||||||
} while (--bits);
|
} while (--bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char ChoosePackagingMode(signed len, signed index, char ColorBits, char LineBuffer[512])
|
static unsigned char AnalyseNextChunks(signed *newlen, signed len, char data[32], char ColorBits)
|
||||||
{
|
{
|
||||||
--len;
|
char longest = 1;
|
||||||
if (!len) {
|
char prev = 255;
|
||||||
return 0;
|
char count = 0;
|
||||||
|
char index = 0;
|
||||||
|
char lindex = 0;
|
||||||
|
int i;
|
||||||
|
int literal_cost;
|
||||||
|
int packed_cost;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
index = index + 1;
|
||||||
|
if (data[i] == prev) {
|
||||||
|
count = count + 1;
|
||||||
|
if (count >= longest) {
|
||||||
|
longest = count;
|
||||||
|
lindex = index - count;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prev = data[i];
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (LineBuffer[index] != LineBuffer[index + 1]) {
|
if (longest == 1) {
|
||||||
return 0;
|
if (len > 16) {
|
||||||
|
*newlen = 16;
|
||||||
|
} else {
|
||||||
|
*newlen = len;
|
||||||
|
}
|
||||||
|
return 'L';
|
||||||
}
|
}
|
||||||
if (ColorBits > 2) {
|
if ((lindex > 0) && (lindex + longest > 15)) {
|
||||||
return 1;
|
/* We cannot pack the stride in this packet */
|
||||||
|
*newlen = lindex;
|
||||||
|
return 'A';
|
||||||
}
|
}
|
||||||
if (LineBuffer[index] != LineBuffer[index + 2]) {
|
/* Cost till end of area */
|
||||||
return 0;
|
literal_cost = 5 + lindex * ColorBits + longest * ColorBits;
|
||||||
|
packed_cost = 5 + lindex * ColorBits + 5 + ColorBits;
|
||||||
|
if (packed_cost < literal_cost) {
|
||||||
|
if (lindex == 0) {
|
||||||
|
/* Use packed data */
|
||||||
|
if (longest > 16) {
|
||||||
|
*newlen = 16;
|
||||||
|
} else {
|
||||||
|
*newlen = longest;
|
||||||
|
}
|
||||||
|
return 'P';
|
||||||
|
}
|
||||||
|
/* We had a good find, but it was not at the start of the line */
|
||||||
|
*newlen = lindex;
|
||||||
|
return 'A';
|
||||||
}
|
}
|
||||||
if (ColorBits > 1) {
|
/* There is no point in packing - use literal */
|
||||||
return 1;
|
if (len > 16) {
|
||||||
|
*newlen = 16;
|
||||||
|
} else {
|
||||||
|
*newlen = len;
|
||||||
}
|
}
|
||||||
if (LineBuffer[index] != LineBuffer[index + 3]) {
|
return 'L';
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
|
static unsigned char GetNextChunk(signed *newlen, signed len, char data[32], char ColorBits)
|
||||||
|
{
|
||||||
|
char oper = 'A';
|
||||||
|
|
||||||
|
while (oper == 'A') {
|
||||||
|
oper = AnalyseNextChunks(newlen, len, data, ColorBits);
|
||||||
|
len = *newlen;
|
||||||
}
|
}
|
||||||
return 1;
|
return oper; /* The packet type is now P or L and the length is in newlen */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteOutBuffer(StrBuf *D)
|
static void WriteOutBuffer(StrBuf *D)
|
||||||
@@ -235,27 +405,25 @@ static void WriteOutBuffer(StrBuf *D)
|
|||||||
static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512],
|
static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512],
|
||||||
int len, int LastOpaquePixel) {
|
int len, int LastOpaquePixel) {
|
||||||
/*
|
/*
|
||||||
** The data starts with a byte count. It tells the number of bytes on this
|
* The data starts with a byte count. It tells the number of bytes on this
|
||||||
** line + 1.
|
* line + 1.
|
||||||
** Special case is a count of 1. It will change to next quadrant.
|
* Special case is a count of 1. It will change to next quadrant.
|
||||||
** Other special case is 0. It will end the sprite.
|
* Other special case is 0. It will end the sprite.
|
||||||
**
|
*
|
||||||
** Ordinary data packet. These are bits in a stream.
|
* Ordinary data packet. These are bits in a stream.
|
||||||
** 1=literal 0=packed
|
* 1=literal 0=packed
|
||||||
** 4 bit count (+1)
|
* 4 bit count (+1)
|
||||||
** for literal you put "count" values
|
* for literal you put "count" values
|
||||||
** for packed you repeat the value "count" times
|
* for packed you repeat the value "count" times
|
||||||
** Never use packed mode for one pixel
|
* Never use packed mode for one pixel
|
||||||
** If the last bit on a line is 1 you need to add a byte of zeroes
|
* If the last bit on a line is in use you need to add a byte of zeroes
|
||||||
** A sequence 00000 ends a scan line
|
* A sequence 00000 ends a scan line
|
||||||
**
|
*
|
||||||
** All data is high nybble first
|
* All data is high nybble first
|
||||||
*/
|
*/
|
||||||
unsigned char V = 0;
|
unsigned char V = 0;
|
||||||
signed i;
|
signed i;
|
||||||
signed count;
|
signed count;
|
||||||
unsigned char differ[16];
|
|
||||||
unsigned char *d_ptr;
|
|
||||||
|
|
||||||
AssembleByte(0, 0);
|
AssembleByte(0, 0);
|
||||||
switch (M) {
|
switch (M) {
|
||||||
@@ -270,100 +438,46 @@ static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask,
|
|||||||
WriteOutBuffer(D);
|
WriteOutBuffer(D);
|
||||||
break;
|
break;
|
||||||
case smPacked:
|
case smPacked:
|
||||||
|
case smShaped:
|
||||||
|
if (M == smShaped) {
|
||||||
|
if (LastOpaquePixel > -1) {
|
||||||
|
if (LastOpaquePixel < len - 1) {
|
||||||
|
len = LastOpaquePixel + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
while (len) {
|
while (len) {
|
||||||
if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) {
|
signed analyselen;
|
||||||
|
analyselen = len;
|
||||||
|
if (analyselen > 32) {
|
||||||
|
analyselen = 32;
|
||||||
|
}
|
||||||
|
if (GetNextChunk(&count, analyselen, LineBuffer + i, ColorBits) == 'P') {
|
||||||
/* Make runlength packet */
|
/* Make runlength packet */
|
||||||
V = LineBuffer[i];
|
V = LineBuffer[i];
|
||||||
++i;
|
i += count;
|
||||||
--len;
|
len -= count;
|
||||||
count = 0;
|
AssembleByte(5, count-1);
|
||||||
do {
|
AssembleByte(ColorBits, V & ColorMask);
|
||||||
++count;
|
|
||||||
++i;
|
|
||||||
--len;
|
|
||||||
} while (V == LineBuffer[i] && len && count != 15);
|
|
||||||
|
|
||||||
AssembleByte(5, count);
|
|
||||||
AssembleByte(ColorBits, V);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Make packed literal packet */
|
/* Make packed literal packet */
|
||||||
d_ptr = differ;
|
AssembleByte(5, (count-1) | 0x10);
|
||||||
V = LineBuffer[i++];
|
|
||||||
*d_ptr++ = V;
|
|
||||||
--len;
|
|
||||||
count = 0;
|
|
||||||
while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) {
|
|
||||||
V = LineBuffer[i++];
|
|
||||||
*d_ptr++ = V;
|
|
||||||
++count;
|
|
||||||
--len;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssembleByte(5, count | 0x10);
|
|
||||||
d_ptr = differ;
|
|
||||||
do {
|
do {
|
||||||
AssembleByte(ColorBits, *d_ptr++);
|
AssembleByte(ColorBits, LineBuffer[i]);
|
||||||
} while (--count >= 0);
|
i++;
|
||||||
|
len--;
|
||||||
|
} while (--count > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssembleByte(8, 0);
|
/* Force EOL for shaped? AssembleByte(5, 0); */
|
||||||
|
AssembleByte(7, 0);
|
||||||
/* Write the buffer to file */
|
/* Write the buffer to file */
|
||||||
WriteOutBuffer(D);
|
WriteOutBuffer(D);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case smShaped:
|
|
||||||
if (LastOpaquePixel > -1) {
|
|
||||||
if (LastOpaquePixel < len - 1) {
|
|
||||||
len = LastOpaquePixel + 1;
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
while (len) {
|
|
||||||
if (ChoosePackagingMode(len, i, ColorBits, LineBuffer)) {
|
|
||||||
/* Make runlength packet */
|
|
||||||
V = LineBuffer[i];
|
|
||||||
++i;
|
|
||||||
--len;
|
|
||||||
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;
|
|
||||||
V = LineBuffer[i++];
|
|
||||||
*d_ptr++ = V;
|
|
||||||
--len;
|
|
||||||
count = 0;
|
|
||||||
while (ChoosePackagingMode(len, i, ColorBits, LineBuffer) == 0 && len && count != 15) {
|
|
||||||
V = LineBuffer[i++];
|
|
||||||
*d_ptr++ = V;
|
|
||||||
++count;
|
|
||||||
--len;
|
|
||||||
}
|
|
||||||
|
|
||||||
AssembleByte(5, count | 0x10);
|
|
||||||
d_ptr = differ;
|
|
||||||
do {
|
|
||||||
AssembleByte(ColorBits, *d_ptr++);
|
|
||||||
} while (--count >= 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AssembleByte(5, 0);
|
|
||||||
AssembleByte(8, 0);
|
|
||||||
/* Write the buffer to file */
|
|
||||||
WriteOutBuffer(D);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,10 +487,10 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
|
|||||||
** returned.
|
** returned.
|
||||||
**
|
**
|
||||||
** The Lynx will draw 4 quadrants:
|
** The Lynx will draw 4 quadrants:
|
||||||
** - Down right
|
** 0 - Down right
|
||||||
** - Up right
|
** 1 - Up right
|
||||||
** - Up left
|
** 2 - Up left
|
||||||
** - Down left
|
** 3 - Down left
|
||||||
**
|
**
|
||||||
** The sprite will end with a byte 0.
|
** The sprite will end with a byte 0.
|
||||||
*/
|
*/
|
||||||
@@ -388,13 +502,24 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
|
|||||||
char ColorBits;
|
char ColorBits;
|
||||||
char ColorMask;
|
char ColorMask;
|
||||||
char EdgeIndex;
|
char EdgeIndex;
|
||||||
|
char Quadrant;
|
||||||
|
char quad;
|
||||||
|
char BPP;
|
||||||
|
/* The default mapping is 1:1 plus extra colours become 0 */
|
||||||
|
char Map[17] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
|
||||||
|
signed PenColors;
|
||||||
|
char PenPal[18];
|
||||||
|
signed Val;
|
||||||
|
|
||||||
/* Get EdgeIndex */
|
/* Get EdgeIndex */
|
||||||
EdgeIndex = GetEdgeIndex (A);
|
EdgeIndex = GetEdgeIndex (A);
|
||||||
|
|
||||||
|
/* Get Quadrant for starting the draw process */
|
||||||
|
Quadrant = GetQuadrant (A) & 3;
|
||||||
|
|
||||||
/* Action point of the sprite */
|
/* Action point of the sprite */
|
||||||
OX = GetActionPointX (A);
|
OX = GetActionPointX (B, A);
|
||||||
OY = GetActionPointY (A);
|
OY = GetActionPointY (B, A);
|
||||||
if (OX >= GetBitmapWidth (B)) {
|
if (OX >= GetBitmapWidth (B)) {
|
||||||
Error ("Action point X cannot be larger than bitmap width");
|
Error ("Action point X cannot be larger than bitmap width");
|
||||||
}
|
}
|
||||||
@@ -410,145 +535,293 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
|
|||||||
/* Get the sprite mode */
|
/* Get the sprite mode */
|
||||||
M = GetMode (A);
|
M = GetMode (A);
|
||||||
|
|
||||||
/* Now check if bitmap indexes are ok */
|
/* Now check how to do the mapping */
|
||||||
if (GetBitmapColors (B) > 16) {
|
if (GetPenpal (B, A, &PenPal[0])) {
|
||||||
Error ("Too many colors for a Lynx sprite");
|
signed I;
|
||||||
|
|
||||||
|
/* Modify the map by content of PenPal */
|
||||||
|
PenColors = strlen(PenPal);
|
||||||
|
for (I = 0; I < PenColors; I++) {
|
||||||
|
switch (PenPal[I]) {
|
||||||
|
case '0':
|
||||||
|
Map[0] = I;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
Map[1] = I;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
Map[2] = I;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
Map[3] = I;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
Map[4] = I;
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
Map[5] = I;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
Map[6] = I;
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
Map[7] = I;
|
||||||
|
break;
|
||||||
|
case '8':
|
||||||
|
Map[8] = I;
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
Map[9] = I;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
Map[10] = I;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
Map[11] = I;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
case 'C':
|
||||||
|
Map[12] = I;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
case 'D':
|
||||||
|
Map[13] = I;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
Map[14] = I;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
Map[15] = I;
|
||||||
|
break;
|
||||||
|
/* The X is reserved as transparency. This allows for shaped sprites */
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
Map[16] = I;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PenColors = GetBitmapColors (B);
|
||||||
}
|
}
|
||||||
ColorBits = 4;
|
ColorBits = 4;
|
||||||
ColorMask = 0x0f;
|
if (PenColors < 9) {
|
||||||
if (GetBitmapColors (B) < 9) {
|
|
||||||
ColorBits = 3;
|
ColorBits = 3;
|
||||||
ColorMask = 0x07;
|
|
||||||
}
|
}
|
||||||
if (GetBitmapColors (B) < 5) {
|
if (PenColors < 5) {
|
||||||
ColorBits = 2;
|
ColorBits = 2;
|
||||||
ColorMask = 0x03;
|
|
||||||
}
|
}
|
||||||
if (GetBitmapColors (B) < 3) {
|
if (PenColors < 3) {
|
||||||
ColorBits = 1;
|
ColorBits = 1;
|
||||||
ColorMask = 0x01;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BPP = GetBPP (A);
|
||||||
|
if (BPP > 0) {
|
||||||
|
ColorBits = BPP;
|
||||||
|
}
|
||||||
|
switch (ColorBits) {
|
||||||
|
case 1:
|
||||||
|
ColorMask = 0x01;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ColorMask = 0x03;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ColorMask = 0x07;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 4:
|
||||||
|
ColorMask = 0x0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* B->BPP = ColorBits; */
|
||||||
/* Create the output buffer and resize it to the required size. */
|
/* Create the output buffer and resize it to the required size. */
|
||||||
D = NewStrBuf ();
|
D = NewStrBuf ();
|
||||||
SB_Realloc (D, 63);
|
SB_Realloc (D, 63);
|
||||||
|
|
||||||
/* Convert the image for quadrant bottom right */
|
for (quad = 0; quad < 4; quad++) {
|
||||||
for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
|
switch ((Quadrant + quad) & 3) {
|
||||||
signed i = 0;
|
case 0:
|
||||||
signed LastOpaquePixel = -1;
|
/* Convert the image for quadrant bottom right */
|
||||||
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
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 */
|
/* Fill the LineBuffer for easier optimisation */
|
||||||
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
||||||
|
/* Fetch next bit into byte buffer */
|
||||||
|
Val = GetPixel (B, X, Y).Index;
|
||||||
|
if (Val > 16) Val = 16;
|
||||||
|
LineBuffer[i] = Map[Val] & ColorMask;
|
||||||
|
|
||||||
/* Fetch next bit into byte buffer */
|
if (Val != EdgeIndex) {
|
||||||
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
LastOpaquePixel = i;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
if (LineBuffer[i] != EdgeIndex) {
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
||||||
LastOpaquePixel = i;
|
|
||||||
}
|
}
|
||||||
++i;
|
if ((OY == 0) && (OX == 0)) {
|
||||||
}
|
/* Trivial case only one quadrant */
|
||||||
|
|
||||||
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
/* Mark end of sprite */
|
||||||
}
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
if ((OY == 0) && (OX == 0)) {
|
/* Return the converted bitmap */
|
||||||
/* Trivial case only one quadrant */
|
return D;
|
||||||
|
|
||||||
/* Mark end of sprite */
|
|
||||||
SB_AppendChar (D, 0);
|
|
||||||
|
|
||||||
/* 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) {
|
|
||||||
signed i = 0;
|
|
||||||
signed LastOpaquePixel = -1;
|
|
||||||
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
|
||||||
|
|
||||||
/* Fill the LineBuffer for easier optimisation */
|
|
||||||
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
|
||||||
|
|
||||||
/* Fetch next bit into byte buffer */
|
|
||||||
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
||||||
|
|
||||||
if (LineBuffer[i] != EdgeIndex) {
|
|
||||||
LastOpaquePixel = i;
|
|
||||||
}
|
}
|
||||||
++i;
|
if ((quad == 1) && (OY == 0)) {
|
||||||
}
|
/* Special case only two quadrants */
|
||||||
|
|
||||||
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
/* Mark end of sprite */
|
||||||
}
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
if (OX == 0) {
|
/* Return the converted bitmap */
|
||||||
/* Special case only two quadrants */
|
return D;
|
||||||
|
|
||||||
/* Mark end of sprite */
|
|
||||||
SB_AppendChar (D, 0);
|
|
||||||
|
|
||||||
/* Return the converted bitmap */
|
|
||||||
return D;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Next quadrant */
|
|
||||||
SB_AppendChar (D, 1);
|
|
||||||
|
|
||||||
/* Convert the image for quadrant top left */
|
|
||||||
for (Y = OY - 1; Y >= 0; --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;
|
|
||||||
|
|
||||||
if (LineBuffer[i] != EdgeIndex) {
|
|
||||||
LastOpaquePixel = i;
|
|
||||||
}
|
}
|
||||||
++i;
|
break;
|
||||||
}
|
case 1:
|
||||||
|
/* Convert the image for quadrant top right */
|
||||||
|
for (Y = OY - 1; Y >= 0; --Y) {
|
||||||
|
signed i = 0;
|
||||||
|
signed LastOpaquePixel = -1;
|
||||||
|
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
||||||
|
|
||||||
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
/* Fill the LineBuffer for easier optimisation */
|
||||||
}
|
for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
|
||||||
|
/* Fetch next bit into byte buffer */
|
||||||
|
Val = GetPixel (B, X, Y).Index;
|
||||||
|
if (Val > 16) Val = 16;
|
||||||
|
|
||||||
/* Next quadrant */
|
LineBuffer[i] = Map[Val] & ColorMask;
|
||||||
SB_AppendChar (D, 1);
|
|
||||||
|
|
||||||
/* Convert the image for quadrant bottom left */
|
if (Val != EdgeIndex) {
|
||||||
for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
|
LastOpaquePixel = i;
|
||||||
signed i = 0;
|
}
|
||||||
signed LastOpaquePixel = -1;
|
++i;
|
||||||
char LineBuffer[512]; /* The maximum size is 508 pixels */
|
}
|
||||||
|
|
||||||
/* Fill the LineBuffer for easier optimisation */
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
||||||
for (X = OX - 1; X >= 0; --X) {
|
|
||||||
|
|
||||||
/* Fetch next bit into byte buffer */
|
|
||||||
LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
|
|
||||||
|
|
||||||
if (LineBuffer[i] != EdgeIndex) {
|
|
||||||
LastOpaquePixel = i;
|
|
||||||
}
|
}
|
||||||
++i;
|
|
||||||
|
if ((OY == GetBitmapHeight (B) - 1) && (OX == 0)) {
|
||||||
|
/* Trivial case only one quadrant */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
if ((quad == 1) && (OX == 0)) {
|
||||||
|
/* Special case only two quadrants */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* Convert the image for quadrant top left */
|
||||||
|
for (Y = OY - 1; Y >= 0; --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 */
|
||||||
|
Val = GetPixel (B, X, Y).Index;
|
||||||
|
if (Val > 16) Val = 16;
|
||||||
|
|
||||||
|
LineBuffer[i] = Map[Val] & ColorMask;
|
||||||
|
|
||||||
|
if (Val != EdgeIndex) {
|
||||||
|
LastOpaquePixel = i;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
||||||
|
}
|
||||||
|
if ((OY == GetBitmapHeight (B) - 1) && (OX == GetBitmapWidth (B) - 1)) {
|
||||||
|
/* Trivial case only one quadrant */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
if ((quad == 1) && (OY == GetBitmapHeight (B) - 1)) {
|
||||||
|
/* Special case only two quadrants */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/* Convert the image for quadrant bottom left */
|
||||||
|
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 */
|
||||||
|
Val = GetPixel (B, X, Y).Index;
|
||||||
|
if (Val > 16) Val = 16;
|
||||||
|
|
||||||
|
LineBuffer[i] = Map[Val] & ColorMask;
|
||||||
|
|
||||||
|
if (Val != EdgeIndex) {
|
||||||
|
LastOpaquePixel = i;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
||||||
|
}
|
||||||
|
if ((OY == 0) && (OX == GetBitmapWidth (B) - 1)) {
|
||||||
|
/* Trivial case only one quadrant */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
if ((quad == 1) && (OX == GetBitmapWidth (B) - 1)) {
|
||||||
|
/* Special case only two quadrants */
|
||||||
|
|
||||||
|
/* Mark end of sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
|
|
||||||
|
/* Return the converted bitmap */
|
||||||
|
return D;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (quad < 3) {
|
||||||
|
/* Next quadrant */
|
||||||
|
SB_AppendChar (D, 1);
|
||||||
|
} else {
|
||||||
|
/* End sprite */
|
||||||
|
SB_AppendChar (D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End sprite */
|
|
||||||
SB_AppendChar (D, 0);
|
|
||||||
|
|
||||||
/* Return the converted bitmap */
|
/* Return the converted bitmap */
|
||||||
return D;
|
return D;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,9 +54,9 @@
|
|||||||
|
|
||||||
|
|
||||||
StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A);
|
StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A);
|
||||||
/* Generate binary output in packed Lynx sprite format for the bitmap B. The output
|
/* Generate binary output in packed Lynx sprite format for the bitmap B.
|
||||||
** is stored in a string buffer (which is actually a dynamic char array) and
|
** The output is stored in a string buffer (which is actually a dynamic
|
||||||
** returned.
|
** char array) and returned.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user