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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
|
$NetBSD: patch-ae,v 1.2 1998/08/07 10:40:56 agc Exp $
--- xvimage.c~ Fri Jan 13 18:11:36 1995
+++ xvimage.c Tue Oct 15 16:41:47 1996
@@ -46,6 +46,274 @@
static int ReadImageFile1 PARM((char *, PICINFO *));
+/* The following array represents the pixel values for each shade of
+ * the primary color components.
+ * If 'p' is a pointer to a source image rgb-byte-triplet, we can
+ * construct the output pixel value simply by 'oring' together
+ * the corresponding components:
+ *
+ * unsigned char *p;
+ * unsigned long pixval;
+ *
+ * pixval = screen_rgb[0][*p++];
+ * pixval |= screen_rgb[1][*p++];
+ * pixval |= screen_rgb[2][*p++];
+ *
+ * This is both efficient and generic, since the only assumption
+ * is that the primary color components have separate bits.
+ * The order and distribution of bits does not matter, and we
+ * don't need additional vaiables and shifting/masking code.
+ * The array size is 3 KBytes total and thus very reasonable.
+ */
+
+static unsigned long screen_rgb[3][256];
+
+/* The following array holds the exact color representations
+ * reported by the system.
+ * This is useful for less than 24 bit deep displays as a base
+ * for additional dithering to get smoother output.
+ */
+
+static byte screen_set[3][256];
+
+/* The following routine initializes the screen_rgb and screen_set
+ * arrays.
+ * Since it is executed only once per program run, it does not need
+ * to be super-efficient.
+ *
+ * The method is to draw points in a pixmap with the specified shades
+ * of primary colors and then get the corresponding XImage pixel
+ * representation.
+ * Thus we can get away with any Bit-order/Byte-Order dependencies.
+ *
+ * The routine uses some global X variables: theDisp, theScreen,
+ * and dispDEEP. Adapt these to your application as necessary.
+ * I've not passed them in as parameters, since for other platforms
+ * than X these may be different (see vfixpix.c), and so the
+ * screen_init() interface is unique.
+ *
+ * BUG: I've read in the "Xlib Programming Manual" from O'Reilly &
+ * Associates, that the DefaultColormap in TrueColor might not
+ * provide the full shade representation in XAllocColor.
+ * In this case one had to provide a 'best' colormap instead.
+ * However, my tests with Xaccel on a Linux-Box with a Mach64
+ * card were fully successful, so I leave that potential problem
+ * to you at the moment and would appreciate any suggestions...
+ */
+
+static void screen_init()
+{
+ static int init_flag; /* assume auto-init as 0 */
+ Pixmap check_map;
+ GC check_gc;
+ XColor check_col;
+ XImage *check_image;
+ int ci, i;
+
+ if (init_flag) return;
+ init_flag = 1;
+
+ check_map = XCreatePixmap(theDisp, RootWindow(theDisp,theScreen),
+ 1, 1, dispDEEP);
+ check_gc = XCreateGC(theDisp, RootWindow(theDisp,theScreen), 0, NULL);
+ for (ci = 0; ci < 3; ci++) {
+ for (i = 0; i < 256; i++) {
+ check_col.flags = DoRed | DoGreen | DoBlue;
+ check_col.red = 0;
+ check_col.green = 0;
+ check_col.blue = 0;
+ /* Do proper upscaling from unsigned 8 bit (image data values)
+ to unsigned 16 bit (X color representation). */
+ ((unsigned short *)&check_col.red)[ci] = (unsigned short)((i << 8) | i);
+ if (!XAllocColor(theDisp, DefaultColormap(theDisp,theScreen), &check_col))
+ FatalError("XAllocColor in screen_init() failed"); /* shouldn't happen */
+ screen_set[ci][i] =
+ (((unsigned short *)&check_col.red)[ci] >> 8) & 0xff;
+ XSetForeground(theDisp, check_gc, check_col.pixel);
+ XDrawPoint(theDisp, check_map, check_gc, 0, 0);
+ check_image = XGetImage(theDisp, check_map, 0, 0, 1, 1,
+ AllPlanes, ZPixmap);
+ if (!check_image) FatalError("XGetImage in screen_init() failed");
+ switch (check_image->bits_per_pixel) {
+ case 8:
+ screen_rgb[ci][i] = *(CARD8 *)check_image->data;
+ break;
+ case 16:
+ screen_rgb[ci][i] = *(CARD16 *)check_image->data;
+ break;
+ case 24:
+ screen_rgb[ci][i] =
+ ((unsigned long)*(CARD8 *)check_image->data << 16) |
+ ((unsigned long)*(CARD8 *)(check_image->data + 1) << 8) |
+ (unsigned long)*(CARD8 *)(check_image->data + 2);
+ break;
+ case 32:
+ screen_rgb[ci][i] = *(CARD32 *)check_image->data;
+ break;
+ }
+ XDestroyImage(check_image);
+ }
+ }
+ XFreeGC(theDisp, check_gc);
+ XFreePixmap(theDisp, check_map);
+}
+
+
+/* The following switch should better be provided at runtime for
+ * comparison purposes.
+ * At the moment it's only compile time, unfortunately.
+ * Who can make adaptions for use as a runtime switch by a menu option?
+ */
+
+#define DO_FIXPIX_SMOOTH
+
+#ifdef DO_FIXPIX_SMOOTH
+
+/* The following code is based in part on:
+ *
+ * jquant1.c
+ *
+ * Copyright (C) 1991-1996, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains 1-pass color quantization (color mapping) routines.
+ * These routines provide mapping to a fixed color map using equally spaced
+ * color values. Optional Floyd-Steinberg or ordered dithering is available.
+ */
+
+/* Declarations for Floyd-Steinberg dithering.
+ *
+ * Errors are accumulated into the array fserrors[], at a resolution of
+ * 1/16th of a pixel count. The error at a given pixel is propagated
+ * to its not-yet-processed neighbors using the standard F-S fractions,
+ * ... (here) 7/16
+ * 3/16 5/16 1/16
+ * We work left-to-right on even rows, right-to-left on odd rows.
+ *
+ * We can get away with a single array (holding one row's worth of errors)
+ * by using it to store the current row's errors at pixel columns not yet
+ * processed, but the next row's errors at columns already processed. We
+ * need only a few extra variables to hold the errors immediately around the
+ * current column. (If we are lucky, those variables are in registers, but
+ * even if not, they're probably cheaper to access than array elements are.)
+ *
+ * The fserrors[] array is indexed [component#][position].
+ * We provide (#columns + 2) entries per component; the extra entry at each
+ * end saves us from special-casing the first and last pixels.
+ */
+
+typedef INT16 FSERROR; /* 16 bits should be enough */
+typedef int LOCFSERROR; /* use 'int' for calculation temps */
+
+typedef struct { byte *colorset;
+ FSERROR *fserrors;
+ } FSBUF;
+
+/* Floyd-Steinberg initialization function.
+ *
+ * It is called 'fs2_init' since it's specialized for our purpose and
+ * could be embedded in a more general FS-package.
+ *
+ * Returns a malloced FSBUF pointer which has to be passed as first
+ * parameter to subsequent 'fs2_dither' calls.
+ * The FSBUF structure does not need to be referenced by the calling
+ * application, it can be treated from the app like a void pointer.
+ *
+ * The current implementation does only require to free() this returned
+ * pointer after processing.
+ *
+ * Returns NULL if malloc fails.
+ *
+ * NOTE: The FSBUF structure is designed to allow the 'fs2_dither'
+ * function to work with an *arbitrary* number of color components
+ * at runtime! This is an enhancement over the IJG code base :-).
+ * Only fs2_init() specifies the (maximum) number of components.
+ */
+
+static FSBUF *fs2_init(width)
+int width;
+{
+ FSBUF *fs;
+ FSERROR *p;
+
+ fs = (FSBUF *)
+ malloc(sizeof(FSBUF) * 3 + ((size_t)width + 2) * sizeof(FSERROR) * 3);
+ if (fs == 0) return fs;
+
+ fs[0].colorset = screen_set[0];
+ fs[1].colorset = screen_set[1];
+ fs[2].colorset = screen_set[2];
+
+ p = (FSERROR *)(fs + 3);
+ memset(p, 0, ((size_t)width + 2) * sizeof(FSERROR) * 3);
+
+ fs[0].fserrors = p;
+ fs[1].fserrors = p + 1;
+ fs[2].fserrors = p + 2;
+
+ return fs;
+}
+
+/* Floyd-Steinberg dithering function.
+ *
+ * NOTE:
+ * (1) The image data referenced by 'ptr' is *overwritten* (input *and*
+ * output) to allow more efficient implementation.
+ * (2) Alternate FS dithering is provided by the sign of 'nc'. Pass in
+ * a negative value for right-to-left processing. The return value
+ * provides the right-signed value for subsequent calls!
+ * (3) This particular implementation assumes *no* padding between lines!
+ * Adapt this if necessary.
+ */
+
+static int fs2_dither(fs, ptr, nc, num_rows, num_cols)
+FSBUF *fs;
+byte *ptr;
+int nc, num_rows, num_cols;
+{
+ int abs_nc, ci, row, col;
+ LOCFSERROR delta, cur, belowerr, bpreverr;
+ byte *dataptr, *colsetptr;
+ FSERROR *errorptr;
+
+ if ((abs_nc = nc) < 0) abs_nc = -abs_nc;
+ for (row = 0; row < num_rows; row++) {
+ for (ci = 0; ci < abs_nc; ci++, ptr++) {
+ dataptr = ptr;
+ colsetptr = fs[ci].colorset;
+ errorptr = fs[ci].fserrors;
+ if (nc < 0) {
+ dataptr += (num_cols - 1) * abs_nc;
+ errorptr += (num_cols + 1) * abs_nc;
+ }
+ cur = belowerr = bpreverr = 0;
+ for (col = 0; col < num_cols; col++) {
+ cur += errorptr[nc];
+ cur += 8; cur >>= 4;
+ if ((cur += *dataptr) < 0) cur = 0;
+ else if (cur > 255) cur = 255;
+ *dataptr = cur & 0xff;
+ cur -= colsetptr[cur];
+ delta = cur << 1; cur += delta;
+ bpreverr += cur; cur += delta;
+ belowerr += cur; cur += delta;
+ errorptr[0] = (FSERROR)bpreverr;
+ bpreverr = belowerr;
+ belowerr = delta >> 1;
+ dataptr += nc;
+ errorptr += nc;
+ }
+ errorptr[0] = (FSERROR)bpreverr;
+ }
+ ptr += (num_cols - 1) * abs_nc;
+ nc = -nc;
+ }
+ return nc;
+}
+
+#endif /* DO_FIXPIX_SMOOTH */
+
#define DO_CROP 0
#define DO_ZOOM 1
@@ -1883,33 +2151,17 @@
/* Non-ColorMapped Visuals: TrueColor, DirectColor */
/************************************************************************/
- unsigned long r, g, b, rmask, gmask, bmask, xcol;
- int rshift, gshift, bshift, bperpix, bperline, border, cshift;
- int maplen;
+ unsigned long xcol;
+ int bperpix, bperline;
byte *imagedata, *lip, *ip, *pp;
- /* compute various shifting constants that we'll need... */
-
- rmask = theVisual->red_mask;
- gmask = theVisual->green_mask;
- bmask = theVisual->blue_mask;
-
- rshift = 7 - highbit(rmask);
- gshift = 7 - highbit(gmask);
- bshift = 7 - highbit(bmask);
-
- maplen = theVisual->map_entries;
- if (maplen>256) maplen=256;
- cshift = 7 - highbit((u_long) (maplen-1));
-
xim = XCreateImage(theDisp, theVisual, dispDEEP, ZPixmap, 0, NULL,
wide, high, 32, 0);
if (!xim) FatalError("couldn't create X image!");
bperline = xim->bytes_per_line;
bperpix = xim->bits_per_pixel;
- border = xim->byte_order;
imagedata = (byte *) malloc((size_t) (high * bperline));
if (!imagedata) FatalError("couldn't malloc imagedata");
@@ -1923,82 +2175,87 @@
FatalError(buf);
}
+ screen_init();
- lip = imagedata; pp = pic24;
- for (i=0; i<high; i++, lip+=bperline) {
- for (j=0, ip=lip; j<wide; j++) {
- r = *pp++; g = *pp++; b = *pp++;
-
- /* shift r,g,b so that high bit of 8-bit color specification is
- * aligned with high bit of r,g,b-mask in visual,
- * AND each component with its mask,
- * and OR the three components together
- */
-
- if (theVisual->class == DirectColor) {
- r = (u_long) directConv[(r>>cshift) & 0xff] << cshift;
- g = (u_long) directConv[(g>>cshift) & 0xff] << cshift;
- b = (u_long) directConv[(b>>cshift) & 0xff] << cshift;
- }
-
-
- /* shift the bits around */
- if (rshift<0) r = r << (-rshift);
- else r = r >> rshift;
-
- if (gshift<0) g = g << (-gshift);
- else g = g >> gshift;
-
- if (bshift<0) b = b << (-bshift);
- else b = b >> bshift;
-
- r = r & rmask;
- g = g & gmask;
- b = b & bmask;
-
- xcol = r | g | b;
-
- if (bperpix == 32) {
- if (border == MSBFirst) {
- *ip++ = (xcol>>24) & 0xff;
- *ip++ = (xcol>>16) & 0xff;
- *ip++ = (xcol>>8) & 0xff;
- *ip++ = xcol & 0xff;
- }
- else { /* LSBFirst */
- *ip++ = xcol & 0xff;
- *ip++ = (xcol>>8) & 0xff;
- *ip++ = (xcol>>16) & 0xff;
- *ip++ = (xcol>>24) & 0xff;
- }
- }
-
- else if (bperpix == 24) {
- if (border == MSBFirst) {
- *ip++ = (xcol>>16) & 0xff;
- *ip++ = (xcol>>8) & 0xff;
- *ip++ = xcol & 0xff;
- }
- else { /* LSBFirst */
- *ip++ = xcol & 0xff;
- *ip++ = (xcol>>8) & 0xff;
- *ip++ = (xcol>>16) & 0xff;
+#ifdef DO_FIXPIX_SMOOTH
+#if 0
+ /* If we wouldn't have to save the original pic24 image data,
+ * the following code would do the dither job by overwriting
+ * the image data, and the normal render code would then work
+ * without any change on that data.
+ * Unfortunately, this approach would hurt the xv assumptions...
+ */
+ if (bperpix < 24) {
+ FSBUF *fs = fs2_init(wide);
+ if (fs) {
+ fs2_dither(fs, pic24, 3, high, wide);
+ free(fs);
+ }
+ }
+#else
+ /* ...so we have to take a different approach with linewise
+ * dithering/rendering in a loop using a temporary line buffer.
+ */
+ if (bperpix < 24) {
+ int alldone = 0;
+ FSBUF *fs = fs2_init(wide);
+ if (fs) {
+ byte *row_buf = malloc((size_t)wide * 3);
+ if (row_buf) {
+ int nc = 3;
+ byte *picp = pic24; lip = imagedata;
+ for (i=0; i<high; i++, lip+=bperline, picp+=(size_t)wide*3) {
+ memcpy(row_buf, picp, (size_t)wide * 3);
+ nc = fs2_dither(fs, row_buf, nc, 1, wide);
+ for (j=0, ip=lip, pp=row_buf; j<wide; j++) {
+
+ xcol = screen_rgb[0][*pp++];
+ xcol |= screen_rgb[1][*pp++];
+ xcol |= screen_rgb[2][*pp++];
+
+ switch (bperpix) {
+ case 8:
+ *ip++ = xcol & 0xff;
+ break;
+ case 16:
+ *((CARD16 *)ip)++ = (CARD16)xcol;
+ break;
+ }
+ }
}
+ alldone = 1;
+ free(row_buf);
}
+ free(fs);
+ }
+ if (alldone) return xim;
+ }
+#endif
+#endif
- else if (bperpix == 16) {
- if (border == MSBFirst) {
- *ip++ = (xcol>>8) & 0xff;
- *ip++ = xcol & 0xff;
- }
- else { /* LSBFirst */
- *ip++ = xcol & 0xff;
- *ip++ = (xcol>>8) & 0xff;
- }
- }
+ lip = imagedata; pp = pic24;
+ for (i=0; i<high; i++, lip+=bperline) {
+ for (j=0, ip=lip; j<wide; j++) {
- else if (bperpix == 8) {
- *ip++ = xcol & 0xff;
+ xcol = screen_rgb[0][*pp++];
+ xcol |= screen_rgb[1][*pp++];
+ xcol |= screen_rgb[2][*pp++];
+
+ switch (bperpix) {
+ case 8:
+ *ip++ = xcol & 0xff;
+ break;
+ case 16:
+ *((CARD16 *)ip)++ = (CARD16)xcol;
+ break;
+ case 24:
+ *ip++ = (xcol >> 16) & 0xff;
+ *ip++ = (xcol >> 8) & 0xff;
+ *ip++ = xcol & 0xff;
+ break;
+ case 32:
+ *((CARD32 *)ip)++ = (CARD32)xcol;
+ break;
}
}
}
|