From 7862c3f1939de409e17379b243dfada5ac0cc99e Mon Sep 17 00:00:00 2001 From: karri Date: Sat, 3 Nov 2012 17:25:01 +0000 Subject: [PATCH] First quadrant literal sprite working git-svn-id: svn://svn.cc65.org/cc65/trunk@5898 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/sp65/lynxsprite.c | 215 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 192 insertions(+), 23 deletions(-) diff --git a/src/sp65/lynxsprite.c b/src/sp65/lynxsprite.c index e5cfda46f..74add649a 100644 --- a/src/sp65/lynxsprite.c +++ b/src/sp65/lynxsprite.c @@ -54,15 +54,10 @@ enum Mode { smAuto, smLiteral, - smPacked + smPacked, + smPackedTransparent }; -/* Screen size of a koala picture */ -#define WIDTH_HR 24U -#define WIDTH_MC 12U -#define HEIGHT 21U - - /*****************************************************************************/ /* Code */ @@ -80,6 +75,8 @@ static enum Mode GetMode (const Collection* A) return smLiteral; } else if (strcmp (Mode, "packed") == 0) { return smPacked; + } else if (strcmp (Mode, "transparent") == 0) { + return smPackedTransparent; } else { Error ("Invalid value for attribute `mode'"); } @@ -94,12 +91,42 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Generate binary output in Lynx sprite format for the bitmap B. The output * 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 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 */ { enum Mode M; StrBuf* D; unsigned X, Y; + unsigned OX, OY; + char LineBuffer[512]; /* The maximum size is 508 pixels */ + char OutBuffer[512]; /* The maximum size is 508 pixels */ + char ColorBits; + char ColorMask; + /* Anchor point of the sprite */ + OX = 0; + OY = 0; /* Output the image properties */ Print (stdout, 1, "Image is %ux%u with %u colors%s\n", @@ -109,49 +136,167 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) /* Get the sprite mode */ M = GetMode (A); - /* Check the height of the bitmap */ - if (GetBitmapHeight (B) != HEIGHT) { - Error ("Invalid bitmap height (%u) for conversion to vic2 sprite", - GetBitmapHeight (B)); - } - /* If the mode wasn't given, fallback to packed */ if (M == smAuto) { - M = smPacked; + M = smLiteral; } /* Now check if bitmap indexes are ok */ if (GetBitmapColors (B) > 16) { Error ("Too many colors for a Lynx sprite"); } + 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; + } /* Create the output buffer and resize it to the required size. */ D = NewStrBuf (); SB_Realloc (D, 63); - /* Convert the image */ - for (Y = 0; Y < HEIGHT; ++Y) { + /* Convert the image for quadrant bottom right */ + for (Y = OY; Y < GetBitmapHeight (B); ++Y) { + unsigned char V = 0; + unsigned char OutIndex; + unsigned W = 0; + signed i = 0; + signed j; + signed k; + signed LastOpaquePixel = -1; + + /* Fill the LineBuffer for easier optimisation */ + for (X = OX; X < GetBitmapWidth (B); ++X) { + + /* Fetch next bit into byte buffer */ + LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask; + + if (LineBuffer[i]) { + LastOpaquePixel = i; + } + ++i; + } + + switch (M) { + case smLiteral: + OutIndex = 0; + for (j = 0; j < i; j++) { + /* Fetch next pixel index into pixel buffer */ + W = (W << ColorBits) | (LineBuffer[j] & ColorMask); + k += ColorBits; + if (k > 7) { + /* The byte is ready */ + k -= 8; + V = (W >> k) & 0xFF; + OutBuffer[OutIndex++] = V; + if (!OutIndex) { + Error ("Sprite is too large for the Lynx"); + } + } + } + /* Output last bits */ + if (k != 0) { + W = (W << (8-k)); + k = 0; + V = W & 0xFF; + OutBuffer[OutIndex++] = V; + if (!OutIndex) { + Error ("Sprite is too large for the Lynx"); + } + } + /* Fix bug in Lynx where the last bit on a line is 1 */ + if (V & 1) { + OutBuffer[OutIndex++] = 0; + if (!OutIndex) { + Error ("Sprite is too large for the Lynx"); + } + } + /* 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 (j = 0; j < OutIndex; j++) { + SB_AppendChar (D, OutBuffer[j]); + } + break; + case smPacked: + /* Bug workaround: If last bit is 1 and it is in bit0 add a zero byte */ + /* Note: These extra pixels will be painted also. There is no workaround for this */ + if (LineBuffer[i - 1] & 0x01) { + LineBuffer[i++] = 0; + } + /* Logical problem workaround: The count can not be 1 so add an extra byte */ + if (i == 1) { + LineBuffer[i++] = 0; + } + /* Write the byte count for this partial scanline */ + SB_AppendChar (D, i); + for (i = 0; i < LineBuffer[0]; i++) { + SB_AppendChar (D, LineBuffer[i]); + } + break; + case smPackedTransparent: + break; + } + } + + if ((OY == 0) && (OX == 0)) { + /* Trivial case only one quadrant */ + + /* 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) { unsigned char V = 0; if (M == smLiteral) { - for (X = 0; X < WIDTH_HR; ++X) { + for (X = OX; X < GetBitmapWidth (B); ++X) { /* Fetch next bit into byte buffer */ - V = (V << 1) | (GetPixel (B, X, Y).Index & 0x01); + V = (V << 4) | (GetPixel (B, X, Y).Index & 0x0f); /* Store full bytes into the output buffer */ - if ((X & 0x07) == 0x07) { + if ((X & 0x01) == 0x01) { SB_AppendChar (D, V); V = 0; } } - } else { - for (X = 0; X < WIDTH_MC; ++X) { + } + } + + /* Next quadrant */ + SB_AppendChar (D, 1); + + /* Convert the image for quadrant top left */ + for (Y = OY - 1; Y >= 0; --Y) { + unsigned char V = 0; + if (M == smLiteral) { + for (X = OX - 1; X >= 0; --X) { /* Fetch next bit into byte buffer */ - V = (V << 2) | (GetPixel (B, X, Y).Index & 0x03); + V = (V << 4) | (GetPixel (B, X, Y).Index & 0x0f); /* Store full bytes into the output buffer */ - if ((X & 0x03) == 0x03) { + if ((X & 0x01) == 0x01) { SB_AppendChar (D, V); V = 0; } @@ -159,6 +304,30 @@ StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A) } } + /* Next quadrant */ + SB_AppendChar (D, 1); + + /* Convert the image for quadrant bottom left */ + for (Y = OY; Y < GetBitmapHeight (B); ++Y) { + unsigned char V = 0; + if (M == smLiteral) { + for (X = OX - 1; X >= 0; --X) { + + /* Fetch next bit into byte buffer */ + V = (V << 4) | (GetPixel (B, X, Y).Index & 0x0f); + + /* Store full bytes into the output buffer */ + if ((X & 0x01) == 0x01) { + SB_AppendChar (D, V); + V = 0; + } + } + } + } + + /* End sprite */ + SB_AppendChar (D, 0); + /* Return the converted bitmap */ return D; } -- 2.39.2