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 /*****************************************************************************/
45 #include "lynxsprite.h"
49 /*****************************************************************************/
51 /*****************************************************************************/
64 /*****************************************************************************/
66 /*****************************************************************************/
70 static enum Mode GetMode (const Collection* A)
71 /* Return the sprite mode from the attribute collection A */
73 /* Check for a mode attribute */
74 const char* Mode = GetAttrVal (A, "mode");
76 if (strcmp (Mode, "literal") == 0) {
78 } else if (strcmp (Mode, "packed") == 0) {
80 } else if (strcmp (Mode, "transparent") == 0) {
81 return smPackedTransparent;
83 Error ("Invalid value for attribute `mode'");
91 static unsigned GetActionPointX (const Collection* A)
92 /* Return the sprite mode from the attribute collection A */
94 /* Check for a action point x attribute */
95 const char* ActionPointX = GetAttrVal (A, "ax");
97 return atoi(ActionPointX);
104 static unsigned GetActionPointY (const Collection* A)
105 /* Return the sprite mode from the attribute collection A */
107 /* Check for a action point y attribute */
108 const char* ActionPointY = GetAttrVal (A, "ay");
110 return atoi(ActionPointY);
117 static void encodeSprite(StrBuf *D, enum Mode M, char ColorBits, char ColorMask, char LineBuffer[512],
118 int i, int LastOpaquePixel) {
120 * The data starts with a byte count. It tells the number of bytes on this
122 * Special case is a count of 1. It will change to next quadrant.
123 * Other special case is 0. It will end the sprite.
125 * Ordinary data packet. These are bits in a stream.
128 * for literal you put "count" values
129 * for packed you repeat the value "count" times
130 * Never use packed mode for one pixel
131 * If the last bit on a line is 1 you need to add a byte of zeroes
132 * A sequence 00000 ends a scan line
134 * All data is high nybble first
136 char OutBuffer[512]; /* The maximum size is 508 pixels */
137 unsigned char OutIndex = 0;
148 for (j = 0; j < i; j++) {
149 /* Fetch next pixel index into pixel buffer */
150 W = (W << ColorBits) | (LineBuffer[j] & ColorMask);
153 /* The byte is ready */
156 OutBuffer[OutIndex++] = V;
158 Error ("Sprite is too large for the Lynx");
162 /* Output last bits */
167 OutBuffer[OutIndex++] = V;
169 Error ("Sprite is too large for the Lynx");
172 /* Fix bug in Lynx where the last bit on a line is 1 */
174 OutBuffer[OutIndex++] = 0;
176 Error ("Sprite is too large for the Lynx");
179 /* Fix bug in Lynx where the count cannot be 1 */
181 OutBuffer[OutIndex++] = 0;
183 /* Write the byte count to the end of the scanline */
184 if (OutIndex == 255) {
185 Error ("Sprite is too large for the Lynx");
187 SB_AppendChar (D, OutIndex+1);
188 /* Write scanline data */
189 for (j = 0; j < OutIndex; j++) {
190 SB_AppendChar (D, OutBuffer[j]);
194 /* Bug workaround: If last bit is 1 and it is in bit0 add a zero byte */
195 /* Note: These extra pixels will be painted also. There is no workaround for this */
196 if (LineBuffer[i - 1] & 0x01) {
199 /* Logical problem workaround: The count can not be 1 so add an extra byte */
203 /* Write the byte count for this partial scanline */
204 SB_AppendChar (D, i);
205 for (i = 0; i < LineBuffer[0]; i++) {
206 SB_AppendChar (D, LineBuffer[i]);
209 case smPackedTransparent:
214 StrBuf* GenLynxSprite (const Bitmap* B, const Collection* A)
215 /* Generate binary output in Lynx sprite format for the bitmap B. The output
216 * is stored in a string buffer (which is actually a dynamic char array) and
219 * The Lynx will draw 4 quadrants:
225 * The data starts with a byte count. It tells the number of bytes on this
227 * Special case is a count of 1. It will change to next quadrant.
228 * Other special case is 0. It will end the sprite.
230 * Ordinary data packet. These are bits in a stream.
233 * for literal you put "count" values
234 * for packed you repeat the value "count" times
235 * Never use packed mode for one pixel
236 * If the last bit on a line is 1 you need to add a byte of zeroes
237 * A sequence 00000 ends a scan line
239 * All data is high nybble first
249 /* Action point of the sprite */
250 OX = GetActionPointX (A);
251 OY = GetActionPointY (A);
252 if (OX >= GetBitmapWidth (B)) {
253 Error ("Action point X cannot be larger than bitmap width");
255 if (OY >= GetBitmapHeight (B)) {
256 Error ("Action point Y cannot be larger than bitmap height");
259 /* Output the image properties */
260 Print (stdout, 1, "Image is %ux%u with %u colors%s\n",
261 GetBitmapWidth (B), GetBitmapHeight (B), GetBitmapColors (B),
262 BitmapIsIndexed (B)? " (indexed)" : "");
264 /* Get the sprite mode */
267 /* Now check if bitmap indexes are ok */
268 if (GetBitmapColors (B) > 16) {
269 Error ("Too many colors for a Lynx sprite");
273 if (GetBitmapColors (B) < 9) {
277 if (GetBitmapColors (B) < 5) {
281 if (GetBitmapColors (B) < 3) {
286 /* Create the output buffer and resize it to the required size. */
290 /* Convert the image for quadrant bottom right */
291 for (Y = OY; Y < (signed)GetBitmapHeight (B); ++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);
311 if ((OY == 0) && (OX == 0)) {
312 /* Trivial case only one quadrant */
314 /* Mark end of sprite */
315 SB_AppendChar (D, 0);
317 /* Return the converted bitmap */
322 SB_AppendChar (D, 1);
324 /* Convert the image for quadrant top right */
325 for (Y = OY - 1; Y >= 0; --Y) {
327 signed LastOpaquePixel = -1;
328 char LineBuffer[512]; /* The maximum size is 508 pixels */
330 /* Fill the LineBuffer for easier optimisation */
331 for (X = OX; X < (signed)GetBitmapWidth (B); ++X) {
333 /* Fetch next bit into byte buffer */
334 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
342 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
346 SB_AppendChar (D, 1);
348 /* Convert the image for quadrant top left */
349 for (Y = OY - 1; Y >= 0; --Y) {
351 signed LastOpaquePixel = -1;
352 char LineBuffer[512]; /* The maximum size is 508 pixels */
354 /* Fill the LineBuffer for easier optimisation */
355 for (X = OX - 1; X >= 0; --X) {
357 /* Fetch next bit into byte buffer */
358 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
366 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
370 SB_AppendChar (D, 1);
372 /* Convert the image for quadrant bottom left */
373 for (Y = OY; Y < (signed)GetBitmapHeight (B); ++Y) {
375 signed LastOpaquePixel = -1;
376 char LineBuffer[512]; /* The maximum size is 508 pixels */
378 /* Fill the LineBuffer for easier optimisation */
379 for (X = OX - 1; X >= 0; --X) {
381 /* Fetch next bit into byte buffer */
382 LineBuffer[i] = GetPixel (B, X, Y).Index & ColorMask;
390 encodeSprite(D, M, ColorBits, ColorMask, LineBuffer, i, LastOpaquePixel);
394 SB_AppendChar (D, 0);
396 /* Return the converted bitmap */