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 */
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'");
}
/* 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",
/* 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;
}
}
}
+ /* 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;
}