Merge pull request #1958 from karrika/sprite

[sp65] Sprite generation fixed for 1BPP Lynx sprites. Fix for #1957
This commit is contained in:
Bob Andrews
2023-01-04 18:16:46 +01:00
committed by GitHub
3 changed files with 534 additions and 247 deletions

View File

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

View File

@@ -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;
} }

View File

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