1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
/* libpbm3.c - pbm utility library part 3
**
** Copyright (C) 1988 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#include "pbm.h"
#include "libpbm.h"
#include "bitreverse.h"
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 301) && defined (__SSE__)
/* intel MMX-SSE enhancement for pbm_writepbmowraw() */
/* GCC only. Turn on with -msse */
#define HAVE_MMX_SSE 1
#else
#define HAVE_MMX_SSE 0
#endif
void
pbm_writepbminit(FILE * const fileP,
int const cols,
int const rows,
int const forceplain) {
if (!forceplain && !pm_plain_output) {
fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows);
#ifdef VMS
set_outfile_binary();
#endif
} else
fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows);
}
static void
writePackedRawRow(FILE * const fileP,
const unsigned char * const packed_bits,
int const cols) {
int bytesWritten;
bytesWritten = fwrite(packed_bits, 1, pbm_packed_bytes(cols), fileP);
if (bytesWritten < pbm_packed_bytes(cols))
pm_error("I/O error writing packed row to raw PBM file.");
}
static void
packBitsWithMmxSse(FILE * const fileP,
const bit * const bitrow,
unsigned char * const packedBits,
unsigned int const cols,
unsigned int * const nextColP) {
/*----------------------------------------------------------------------------
Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right,
stop when there aren't enough bits left to fill a whole byte. Return
as *nextColP the number of the next column after the rightmost one we
packed.
Use the Pentium MMX and SSE facilities to pack the bits quickly, but
perform the exact same function as the simpler packBitsGeneric().
-----------------------------------------------------------------------------*/
#if HAVE_MMX_SSE
/*
We use MMX/SSE facilities that operate on 8 bytes at once to pack
the bits quickly.
We use 2 MMX registers (no SSE registers).
The key machine instructions are:
PCMPEQB Packed CoMPare EQual Byte
Compares 8 bytes in parallel
Result is x00 if equal, xFF if unequal for each byte
PMOVMSKB Packed MOVe MaSK Byte
Result is a byte of the MSBs of 8 bytes
x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C
EMMS Empty MMx State
Free MMX registers
Here's a one-statement version of the code in our foor loop. It's harder
to read, but if we find out this generates more efficient code, we could
use this.
packedBits[col/8]
= bitreverse [ ~ (unsigned char) __builtin_ia32_pmovmskb (
__builtin_ia32_pcmpeqb ( *(v8qi*) (&bitrow[col]), *(v8qi*) &zero64)
) ];
*/
typedef int v8qi __attribute__ ((mode(V8QI)));
typedef int di __attribute__ ((mode(DI)));
di const zero64 = 0; /* to clear with PXOR */
unsigned int col;
for (col = 0; col + 7 < cols; col += 8) {
v8qi const compare =
__builtin_ia32_pcmpeqb(*(v8qi*) (&bitrow[col]), *(v8qi*) &zero64);
unsigned char const backwardWhiteMask = (unsigned char)
__builtin_ia32_pmovmskb(compare);
unsigned char const backwardBlackMask = ~backwardWhiteMask;
unsigned char const blackMask = bitreverse[backwardBlackMask];
packedBits[col/8] = blackMask;
}
*nextColP = col;
__builtin_ia32_emms();
#else
if (bitreverse == bitreverse) {}; /* avoid unused vbl compiler warning */
#endif
}
static unsigned int
bitValue(unsigned char const byteValue) {
return byteValue == 0 ? 0 : 1;
}
static void
packBitsGeneric(FILE * const fileP,
const bit * const bitrow,
unsigned char * const packedBits,
unsigned int const cols,
unsigned int * const nextColP) {
/*----------------------------------------------------------------------------
Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right,
stop when there aren't enough bits left to fill a whole byte. Return
as *nextColP the number of the next column after the rightmost one we
packed.
Don't use any special CPU facilities to do the packing.
-----------------------------------------------------------------------------*/
unsigned int col;
for (col = 0; col + 7 < cols; col += 8)
packedBits[col/8] = (
bitValue(bitrow[col+0]) << 7 |
bitValue(bitrow[col+1]) << 6 |
bitValue(bitrow[col+2]) << 5 |
bitValue(bitrow[col+3]) << 4 |
bitValue(bitrow[col+4]) << 3 |
bitValue(bitrow[col+5]) << 2 |
bitValue(bitrow[col+6]) << 1 |
bitValue(bitrow[col+7]) << 0
);
*nextColP = col;
}
static void
packPartialBytes(const bit * const bitrow,
unsigned int const cols,
unsigned int const nextCol,
unsigned char * const packedBits) {
/* routine for partial byte at the end of packedBits[]
Prior to addition of the above enhancement,
this method was used for the entire process
*/
unsigned int col;
int bitshift;
unsigned char item;
bitshift = 7; /* initial value */
item = 0; /* initial value */
for (col = nextCol; col < cols; ++col, --bitshift)
if (bitrow[col] != 0)
item |= 1 << bitshift;
packedBits[col/8] = item;
}
static void
writePbmRowRaw(FILE * const fileP,
const bit * const bitrow,
int const cols) {
jmp_buf jmpbuf;
jmp_buf * origJmpbufP;
unsigned char * packedBits;
packedBits = pbm_allocrow_packed(cols);
if (setjmp(jmpbuf) != 0) {
pbm_freerow_packed(packedBits);
pm_setjmpbuf(origJmpbufP);
pm_longjmp();
} else {
unsigned int nextCol;
pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
if (HAVE_MMX_SSE)
packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol);
else
packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
if (cols % 8 > 0)
packPartialBytes(bitrow, cols, nextCol, packedBits);
writePackedRawRow(fileP, packedBits, cols);
pm_setjmpbuf(origJmpbufP);
}
pbm_freerow_packed(packedBits);
}
static void
writePbmRowPlain(FILE * const fileP,
bit * const bitrow,
int const cols) {
int col, charcount;
charcount = 0;
for (col = 0; col < cols; ++col) {
if (charcount >= 70) {
putc('\n', fileP);
charcount = 0;
}
putc(bitrow[col] ? '1' : '0', fileP);
++charcount;
}
putc('\n', fileP);
}
void
pbm_writepbmrow(FILE * const fileP,
bit * const bitrow,
int const cols,
int const forceplain) {
if (!forceplain && !pm_plain_output)
writePbmRowRaw(fileP, bitrow, cols);
else
writePbmRowPlain(fileP, bitrow, cols);
}
void
pbm_writepbmrow_packed(FILE * const fileP,
const unsigned char * const packedBits,
int const cols,
int const forceplain) {
if (!forceplain && !pm_plain_output)
writePackedRawRow(fileP, packedBits, cols);
else {
jmp_buf jmpbuf;
jmp_buf * origJmpbufP;
bit * bitrow;
bitrow = pbm_allocrow(cols);
if (setjmp(jmpbuf) != 0) {
pbm_freerow(bitrow);
pm_setjmpbuf(origJmpbufP);
pm_longjmp();
} else {
unsigned int col;
pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
for (col = 0; col < cols; ++col)
bitrow[col] =
packedBits[col/8] & (0x80 >> (col%8)) ?
PBM_BLACK : PBM_WHITE;
writePbmRowPlain(fileP, bitrow, cols);
pm_setjmpbuf(origJmpbufP);
}
pbm_freerow(bitrow);
}
}
void
pbm_writepbm(FILE * const fileP,
bit ** const bits,
int const cols,
int const rows,
int const forceplain) {
int row;
pbm_writepbminit(fileP, cols, rows, forceplain);
for (row = 0; row < rows; ++row)
pbm_writepbmrow(fileP, bits[row], cols, forceplain);
}
|