]> git.sur5r.net Git - cc65/commitdiff
First quadrant literal sprite working
authorkarri <karri@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 3 Nov 2012 17:25:01 +0000 (17:25 +0000)
committerkarri <karri@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 3 Nov 2012 17:25:01 +0000 (17:25 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@5898 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/sp65/lynxsprite.c

index e5cfda46fd012eca39d9d978ca1850ff9b6d320a..74add649a6d1113ade1ce02f71c21f12dd79869a 100644 (file)
 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;
 }