1 /*****************************************************************************/
5 /* Lynx sprite format backend for the sp65 sprite and bitmap utility */
9 /* (C) 2012, Ullrich von Bassewitz */
10 /* Roemerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
43 #include "lynxsprite.h"
47 /*****************************************************************************/
49 /*****************************************************************************/
62 /*****************************************************************************/
64 /*****************************************************************************/
68 static enum Mode GetMode (const Collection* A)
69 /* Return the sprite mode from the attribute collection A */
71 /* Check for a mode attribute */
72 const char* Mode = GetAttrVal (A, "mode");
74 if (strcmp (Mode, "literal") == 0) {
76 } else if (strcmp (Mode, "packed") == 0) {
78 } else if (strcmp (Mode, "transparent") == 0) {
79 return smPackedTransparent;
81 Error ("Invalid value for attribute `mode'");
89 static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512],
90 int i, int LastOpaquePixel) {
92 * The data starts with a byte count. It tells the number of bytes on this
94 * Special case is a count of 1. It will change to next quadrant.
95 * Other special case is 0. It will end the sprite.
97 * Ordinary data packet. These are bits in a stream.
100 * for literal you put "count" values
101 * for packed you repeat the value "count" times
102 * Never use packed mode for one pixel
103 * If the last bit on a line is 1 you need to add a byte of zeroes
104 * A sequence 00000 ends a scan line
106 * All data is high nybble first
108 char OutBuffer[512]; /* The maximum size is 508 pixels */
109 unsigned char OutIndex = 0;
120 for (j = 0; j < i; j++) {
121 /* Fetch next pixel index into pixel buffer */
122 W = (W << ColorBits) | (LineBuffer[j] & ColorMask);
125 /* The byte is ready */
128 OutBuffer[OutIndex++] = V;
130 Error ("Sprite is too large for the Lynx");
134 /* Output last bits */
139 OutBuffer[OutIndex++] = V;
141 Error ("Sprite is too large for the Lynx");
144 /* Fix bug in Lynx where the last bit on a line is 1 */
146 OutBuffer[OutIndex++] = 0;
148 Error ("Sprite is too large for the Lynx");
151 /* Fix bug in Lynx where the count cannot be 1 */
153 OutBuffer[OutIndex++] = 0;
155 /* Write the byte count to the end of the scanline */
156 if (OutIndex == 255) {
157 Error ("Sprite is too large for the Lynx");
159 SB_AppendChar (D, OutIndex+1);
160 /* Write scanline data */
161 for (j = 0; j < OutIndex; j++) {
162 SB_AppendChar (D, OutBuffer[j]);
166 /* Bug workaround: If last bit is 1 and it is in bit0 add a zero byte */
167 /* Note: These extra pixels will be painted also. There is no workaround for this */
168 if (LineBuffer[i - 1] & 0x01) {
171 /* Logical problem workaround: The count can not be 1 so add an extra byte */
175 /* Write the byte count for this partial scanline */
176 SB_AppendChar (D, i);
177 for (i = 0; i < LineBuffer[0]; i++) {
178 SB_AppendChar (D, LineBuffer[i]);
181 case smPackedTransparent:
186 StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
187 /* Generate binary output in Lynx sprite format for the bitmap B. The output
188 * is stored in a string buffer (which is actually a dynamic char array) and
191 * The Lynx will draw 4 quadrants:
197 * The data starts with a byte count. It tells the number of bytes on this
199 * Special case is a count of 1. It will change to next quadrant.
200 * Other special case is 0. It will end the sprite.
202 * Ordinary data packet. These are bits in a stream.
205 * for literal you put "count" values
206 * for packed you repeat the value "count" times
207 * Never use packed mode for one pixel
208 * If the last bit on a line is 1 you need to add a byte of zeroes
209 * A sequence 00000 ends a scan line
211 * All data is high nybble first
221 /* Anchor point of the sprite */
225 /* Output the image properties */
226 Print (stdout, 1, "Image is %ux%u with %u colors%s\n",
227 GetBitmapWidth (B), GetBitmapHeight (B), GetBitmapColors (B),
228 BitmapIsIndexed (B)? " (indexed)" : "");
230 /* Get the sprite mode */
233 /* Now check if bitmap indexes are ok */
234 if (GetBitmapColors (B) > 16) {
235 Error ("Too many colors for a Lynx sprite");
239 if (GetBitmapColors (B) < 9) {
243 if (GetBitmapColors (B) < 5) {
247 if (GetBitmapColors (B) < 3) {
252 /* Create the output buffer and resize it to the required size. */
256 /* Convert the image for quadrant bottom right */
257 for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
259 signed LastOpaquePixel = -1;
260 char LineBuffer[512]; /* The maximum size is 508 pixels */
262 /* Fill the LineBuffer for easier optimisation */
263 for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
265 /* Fetch next bit into byte buffer */
266 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
274 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
277 if ((OY == 0) && (OX == 0)) {
278 /* Trivial case only one quadrant */
280 /* Mark end of sprite */
281 SB_AppendChar (D, 0);
283 /* Return the converted bitmap */
288 SB_AppendChar (D, 1);
290 /* Convert the image for quadrant top right */
291 for (Y = OY - 1; Y >= 0; --Y) {
293 signed LastOpaquePixel = -1;
294 char LineBuffer[512]; /* The maximum size is 508 pixels */
296 /* Fill the LineBuffer for easier optimisation */
297 for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
299 /* Fetch next bit into byte buffer */
300 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
308 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
312 SB_AppendChar (D, 1);
314 /* Convert the image for quadrant top left */
315 for (Y = OY - 1; Y >= 0; --Y) {
317 signed LastOpaquePixel = -1;
318 char LineBuffer[512]; /* The maximum size is 508 pixels */
320 /* Fill the LineBuffer for easier optimisation */
321 for (X = OX - 1; X >= 0; --X) {
323 /* Fetch next bit into byte buffer */
324 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
332 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
336 SB_AppendChar (D, 1);
338 /* Convert the image for quadrant bottom left */
339 for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
341 signed LastOpaquePixel = -1;
342 char LineBuffer[512]; /* The maximum size is 508 pixels */
344 /* Fill the LineBuffer for easier optimisation */
345 for (X = OX - 1; X >= 0; --X) {
347 /* Fetch next bit into byte buffer */
348 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
356 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
360 SB_AppendChar (D, 0);
362 /* Return the converted bitmap */