1/*****************************************************************************/
2/* */
3/* geosbitmap.c */
4/* */
5/* GEOS compacted bitmap backend for the sp65 sprite and bitmap utility */
6/* */
7/* */
8/* */
9/* (C) 2012, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* D-70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
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. */
18/* */
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: */
22/* */
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 */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36/* common */
37#include "attrib.h"
38#include "print.h"
39#include "xmalloc.h"
40
41/* sp65 */
42#include "attr.h"
43#include "error.h"
44#include "geosbitmap.h"
45
46
47
48/*****************************************************************************/
49/* Data */
50/*****************************************************************************/
51
52
53
54#define UNIQUE_MAX 91U /* Maximum number of unique bytes */
55#define REPEAT_MAX 127U /* Maximum number of repeated bytes */
56
57
58
59/* Structure used for RLE compression */
60struct RLE {
61 unsigned char* Buf; /* Pointer to pixel buffer */
62 unsigned Remaining; /* Remaining bytes in buffer */
63 unsigned char LastVal; /* Last value seen */
64 unsigned Count; /* Number of occurrences for LastVal */
65 StrBuf* D; /* Buffer for compressed data */
66 StrBuf UniqueData; /* Temp buffer for unique data */
67};
68
69
70
71/*****************************************************************************/
72/* Code */
73/*****************************************************************************/
74
75
76
77static void StoreByte (struct RLE* RLE)
78/* Store a unique byte or a run of repeated bytes. If count is zero, the
79** function will flush the internal buffers, so that all data is in RLE->D.
80*/
81{
82
83
84 if (RLE->Count == 1 || RLE->Count == 2) {
85 /* A run of two identical bytes is treated as two unique bytes, since
86 ** this will usually merge with the following run.
87 */
88 SB_AppendChar (&RLE->UniqueData, RLE->LastVal);
89 if (RLE->Count == 2) {
90 SB_AppendChar (&RLE->UniqueData, RLE->LastVal);
91 }
92
93 /* Clear the repeat counter */
94 RLE->Count = 0;
95
96 } else {
97
98 /* Run of repeated bytes. First flush the temp buffer for unique
99 ** bytes.
100 */
101 const char* Buf = SB_GetConstBuf (&RLE->UniqueData);
102 unsigned Count = SB_GetLen (&RLE->UniqueData);
103 while (Count) {
104
105 /* Determine the count for this sequence */
106 unsigned Chunk = Count;
107 if (Chunk > UNIQUE_MAX) {
108 Chunk = UNIQUE_MAX;
109 }
110
111 /* Output the unique op */
112 SB_AppendChar (RLE->D, 0x80 + (unsigned char) Chunk);
113 SB_AppendBuf (RLE->D, Buf, Chunk);
114
115 /* Bump the counters */
116 Buf += Chunk;
117 Count -= Chunk;
118 }
119
120 /* Clear the unique byte buffer */
121 SB_Clear (&RLE->UniqueData);
122
123 /* Now output the repeat sequence */
124 while (RLE->Count) {
125
126 /* Determine the count for this sequence */
127 unsigned Chunk = RLE->Count;
128 if (Chunk > REPEAT_MAX) {
129 Chunk = REPEAT_MAX;
130 }
131 /* Output the repeat op */
132 SB_AppendChar (RLE->D, (unsigned char) Chunk);
133 SB_AppendChar (RLE->D, RLE->LastVal);
134
135 /* Bump the counters */
136 RLE->Count -= Chunk;
137 }
138 }
139}
140
141
142
143StrBuf* GenGeosBitmap (const Bitmap* B, const Collection* A attribute ((unused)))
144/* Generate binary output in GEOS compacted bitmap format for the bitmap B.
145** The output is stored in a string buffer (which is actually a dynamic char
146** array) and returned.
147*/
148{
149 unsigned LineWidth;
150 unsigned char* Buf;
151 unsigned char* BP;
152 StrBuf* D;
153 unsigned X, Y;
154 struct RLE RLE;
155
156
157 /* Output the image properties */
158 Print (stdout, 1, "Image is %ux%u with %u colors%s\n",
159 GetBitmapWidth (B), GetBitmapHeight (B), GetBitmapColors (B),
160 BitmapIsIndexed (B)? " (indexed)" : "");
161
162 /* Check the bitmap properties */
163 if (!BitmapIsIndexed (B) || GetBitmapColors (B) > 2) {
164 Error ("Bitmaps converted to GEOS compacted bitmap must be "
165 "indexed with two colors");
166 }
167
168 /* Calculate the width of one line in bytes */
169 LineWidth = (GetBitmapWidth (B) + 7U) / 8U;
170
171 /* Allocate a buffer for the raw image */
172 Buf = xmalloc (LineWidth * GetBitmapHeight (B));
173
174 /* Convert the bitmap into a raw image */
175 BP = Buf;
176 for (Y = 0; Y < GetBitmapHeight (B); ++Y) {
177 for (X = 0; X < GetBitmapWidth (B); ) {
178 unsigned char V = 0;
179 int Bits = 8;
180 if ((unsigned)Bits > GetBitmapWidth (B) - X) {
181 Bits = (GetBitmapWidth (B) - X);
182 }
183 while (--Bits >= 0) {
184 V |= (GetPixel (B, X++, Y).Index & 0x01) << Bits;
185 }
186 *BP++ = V;
187 }
188 }
189
190 /* Create the output buffer and resize it to something reasonable */
191 D = NewStrBuf ();
192 SB_Realloc (D, 64);
193
194 /* Compact the image. We're currently using only REPEAT and UNIQUE opcodes.
195 ** BIGCOUNT is rather difficult to apply.
196 */
197 RLE.Buf = Buf;
198 RLE.Remaining = LineWidth * GetBitmapHeight (B) - 1;
199 RLE.LastVal = *RLE.Buf++;
200 RLE.Count = 1;
201 RLE.D = D;
202 RLE.UniqueData = EmptyStrBuf;
203 while (RLE.Remaining--) {
204 /* */
205 if (RLE.LastVal == *RLE.Buf) {
206 ++RLE.Buf;
207 ++RLE.Count;
208 } else {
209 StoreByte (&RLE);
210 RLE.LastVal = *RLE.Buf++;
211 RLE.Count = 1;
212 }
213 }
214
215 /* If something remains, store it */
216 if (RLE.Count) {
217 StoreByte (&RLE);
218 }
219
220 /* Flush the unique byte buffer */
221 StoreByte (&RLE);
222
223 /* Release the unique byte buffer */
224 SB_Done (&RLE.UniqueData);
225
226 /* Free the raw image buffer */
227 xfree (Buf);
228
229 /* Return the converted bitmap */
230 return D;
231}
232