1 /*
2 * bmp.c --
3 *
4 * BMP photo image type, Tcl/Tk package
5 *
6 * Copyright (c) 1997-2003 Jan Nijtmans <nijtmans@users.sourceforge.net>
7 * Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
8 *
9 * The following format options are available:
10 *
11 * Write BMP image: "ico -resolution <list>"
12 *
13 * -resolution <list>: Set the resolution property of the output file.
14 * The default is 74 dpi (no option)
15 * Possible forms for list:
16 * {xRes unit} : set x and y resolution to same value
17 * {xRes yRes} : set x to 74dpi and y to 74*YRes/XRes
18 * {xRes yRes unit} : set x and y resolution to given values
19 * xRes, yRes are floats >= 0. 0 is the "no value" value.
20 * unit is one of "c" cm, "m" mm, "i" inch or "p" inch/72.
21 */
22
23 /*
24 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
25 */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <math.h>
30 #include "init.c"
31
32 /* Compression types */
33 #define BI_RGB 0
34 #define BI_RLE8 1
35 #define BI_RLE4 2
36 #define BI_BITFIELDS 3
37
38 /* Structure for reading bit masks for compression type BI_BITFIELDS */
39 typedef struct {
40 unsigned int mask;
41 unsigned int shiftin;
42 unsigned int shiftout;
43 } BitmapChannel;
44
45 /*
46 * Now the implementation
47 */
48 /*
49 * Prototypes for local procedures defined in this file:
50 */
51
52 static int CommonMatch(tkimg_MFile *handle, int *widthPtr,
53 int *heightPtr, unsigned char **colorMap, int *numBits,
54 int *numCols, int *comp, unsigned int *mask);
55
56 static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
57 Tk_PhotoHandle imageHandle, int destX, int destY, int width,
58 int height, int srcX, int srcY);
59
60 static int CommonWrite(Tcl_Interp *interp, Tcl_Obj *format,
61 tkimg_MFile *handle,
62 Tk_PhotoImageBlock *blockPtr);
63
64 static void putint(tkimg_MFile *handle, int i);
65
66 /*
67 * Entrypoints for the photo image type.
68 */
69
70 static int
ChnMatch(Tcl_Channel chan,const char * fileName,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)71 ChnMatch(
72 Tcl_Channel chan,
73 const char *fileName,
74 Tcl_Obj *format,
75 int *widthPtr,
76 int *heightPtr,
77 Tcl_Interp *interp
78 ) {
79 tkimg_MFile handle;
80
81 handle.data = (char *) chan;
82 handle.state = IMG_CHAN;
83
84 return CommonMatch(&handle, widthPtr, heightPtr,
85 NULL, NULL, NULL, NULL, NULL);
86 }
87
88 static int
ObjMatch(Tcl_Obj * data,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)89 ObjMatch(
90 Tcl_Obj *data,
91 Tcl_Obj *format,
92 int *widthPtr,
93 int *heightPtr,
94 Tcl_Interp *interp
95 ) {
96 tkimg_MFile handle;
97
98 if (!tkimg_ReadInit(data, 'B', &handle)) {
99 return 0;
100 }
101 return CommonMatch(&handle, widthPtr, heightPtr,
102 NULL, NULL, NULL, NULL, NULL);
103 }
104
105 static int
ChnRead(Tcl_Interp * interp,Tcl_Channel chan,const char * fileName,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)106 ChnRead(
107 Tcl_Interp *interp,
108 Tcl_Channel chan,
109 const char *fileName,
110 Tcl_Obj *format,
111 Tk_PhotoHandle imageHandle,
112 int destX, int destY,
113 int width, int height,
114 int srcX, int srcY
115 ) {
116 tkimg_MFile handle;
117
118 handle.data = (char *) chan;
119 handle.state = IMG_CHAN;
120
121 return CommonRead(interp, &handle, imageHandle, destX, destY,
122 width, height, srcX, srcY);
123 }
124
125 static int
ObjRead(Tcl_Interp * interp,Tcl_Obj * data,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)126 ObjRead(
127 Tcl_Interp *interp,
128 Tcl_Obj *data,
129 Tcl_Obj *format,
130 Tk_PhotoHandle imageHandle,
131 int destX, int destY,
132 int width, int height,
133 int srcX, int srcY
134 ) {
135 tkimg_MFile handle;
136
137 tkimg_ReadInit(data,'B',&handle);
138 return CommonRead(interp, &handle, imageHandle, destX, destY,
139 width, height, srcX, srcY);
140 }
141
142 static int
ChnWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)143 ChnWrite(
144 Tcl_Interp *interp,
145 const char *filename,
146 Tcl_Obj *format,
147 Tk_PhotoImageBlock *blockPtr
148 ) {
149 Tcl_Channel chan;
150 tkimg_MFile handle;
151 int result;
152
153 chan = tkimg_OpenFileChannel(interp, filename, 0644);
154 if (!chan) {
155 return TCL_ERROR;
156 }
157
158 handle.data = (char *) chan;
159 handle.state = IMG_CHAN;
160
161 result = CommonWrite(interp, format, &handle, blockPtr);
162 if (Tcl_Close(interp, chan) == TCL_ERROR) {
163 return TCL_ERROR;
164 }
165 return result;
166 }
167
StringWrite(Tcl_Interp * interp,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)168 static int StringWrite(
169 Tcl_Interp *interp,
170 Tcl_Obj *format,
171 Tk_PhotoImageBlock *blockPtr
172 ) {
173 tkimg_MFile handle;
174 int result;
175 Tcl_DString data;
176
177 Tcl_DStringInit(&data);
178 tkimg_WriteInit(&data, &handle);
179 result = CommonWrite(interp, format, &handle, blockPtr);
180 tkimg_Putc(IMG_DONE, &handle);
181
182 if (result == TCL_OK) {
183 Tcl_DStringResult(interp, &data);
184 } else {
185 Tcl_DStringFree(&data);
186 }
187 return result;
188 }
189
190 static unsigned int
getUInt32(unsigned char * buf)191 getUInt32(unsigned char *buf)
192 {
193 return (buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
194 }
195
196 static unsigned int
getUInt16(unsigned char * buf)197 getUInt16(unsigned char *buf)
198 {
199 return (buf[0] | buf[1] << 8);
200 }
201
202 static void
GetChannelMasks(unsigned int * intMask,BitmapChannel * masks)203 GetChannelMasks(
204 unsigned int *intMask,
205 BitmapChannel *masks
206 ) {
207 unsigned int mask;
208 int i, nbits, offset, bit;
209
210 for (i=0; i<3; i++) {
211 mask = getUInt32((unsigned char *) &intMask[i]);
212 masks[i].mask = mask;
213 nbits = 0;
214 offset = -1;
215 for (bit=0; bit<32; bit++) {
216 if (mask & 1) {
217 nbits++;
218 if (offset == -1) {
219 offset = bit;
220 }
221 }
222 mask = mask >> 1;
223 }
224 masks[i].shiftin = offset;
225 masks[i].shiftout = 8 - nbits;
226 }
227 }
228
229 /*
230 * Helper functions for the entry points. Work horses.
231 */
232
233 static int
CommonMatch(tkimg_MFile * handle,int * widthPtr,int * heightPtr,unsigned char ** colorMap,int * numBits,int * numCols,int * comp,unsigned int * mask)234 CommonMatch(
235 tkimg_MFile *handle,
236 int *widthPtr, int *heightPtr,
237 unsigned char **colorMap,
238 int *numBits, int *numCols, int *comp,
239 unsigned int *mask
240 ) {
241 unsigned char buf[28];
242 int c,i, compression, nBits, clrUsed, offBits;
243
244 if ((tkimg_Read2(handle, (char *) buf, 2) != 2)
245 || (strncmp("BM", (char *) buf, 2) != 0)
246 || (tkimg_Read2(handle, (char *) buf, 24) != 24)
247 || buf[13] || buf[14] || buf[15]) {
248 return 0;
249 }
250
251 offBits = (buf[11]<<24) + (buf[10]<<16) + (buf[9]<<8) + buf[8];
252 c = buf[12];
253 if ((c == 40) || (c == 64)) {
254 *widthPtr = (buf[19]<<24) + (buf[18]<<16) + (buf[17]<<8) + buf[16];
255 *heightPtr = (buf[23]<<24) + (buf[22]<<16) + (buf[21]<<8) + buf[20];
256 if (tkimg_Read2(handle, (char *) buf, 24) != 24) {
257 return 0;
258 }
259 nBits = buf[2];
260 compression = buf[4];
261 clrUsed = (buf[21]<<8) + buf[20];
262 offBits -= c+14;
263 } else if (c == 12) {
264 *widthPtr = (buf[17]<<8) + buf[16];
265 *heightPtr = (buf[19]<<8) + buf[18];
266 nBits = buf[22];
267 compression = BI_RGB;
268 clrUsed = 0;
269 } else {
270 return 0;
271 }
272 if (*widthPtr <= 0 || *heightPtr <= 0)
273 return 0;
274
275 if (colorMap) {
276 if (c > 36) {
277 if (tkimg_Read2(handle, (char *) buf, c - 36) != c - 36 )
278 return 0;
279 }
280 if (compression == BI_BITFIELDS) {
281 /* Read the channel masks. */
282 if (tkimg_Read2(handle, (char *) buf, 3*4) != 3*4 )
283 return 0;
284 if (mask) {
285 mask[0] = getUInt32((unsigned char *) &buf[0]);
286 mask[1] = getUInt32((unsigned char *) &buf[4]);
287 mask[2] = getUInt32((unsigned char *) &buf[8]);
288 }
289 offBits -= 3*4;
290 }
291 if (!clrUsed && nBits < 24) {
292 clrUsed = 1 << nBits;
293 }
294 if (nBits<16) {
295 unsigned char colbuf[4], *ptr;
296 offBits -= (3+(c!=12)) * clrUsed;
297 *colorMap = ptr = (unsigned char *) ckalloc(3*clrUsed);
298 for (i = 0; i < clrUsed; i++) {
299 if (tkimg_Read2(handle, (char *) colbuf, 3+(c!=12)) != 3+(c!=12))
300 return 0;
301 *ptr++ = colbuf[0]; *ptr++ = colbuf[1]; *ptr++ = colbuf[2];
302 /*printf("color %d: %d %d %d\n", i, colbuf[2], colbuf[1], colbuf[0]);*/
303 }
304 }
305 while (offBits>28) {
306 offBits -= 28;
307 if (tkimg_Read2(handle, (char *) buf, 28) != 28)
308 return 0;
309 }
310 if (offBits > 0) {
311 if (tkimg_Read2(handle, (char *) buf, offBits) != offBits)
312 return 0;
313 }
314 if (numCols) {
315 *numCols = clrUsed;
316 }
317 }
318 if (numBits) {
319 *numBits = nBits;
320 }
321 if (comp) {
322 *comp = compression;
323 }
324 return 1;
325 }
326
327 static int
CommonRead(Tcl_Interp * interp,tkimg_MFile * handle,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)328 CommonRead(
329 Tcl_Interp *interp,
330 tkimg_MFile *handle,
331 Tk_PhotoHandle imageHandle,
332 int destX, int destY,
333 int width, int height,
334 int srcX, int srcY
335 ) {
336 Tk_PhotoImageBlock block;
337 int numBits, bytesPerLine, numCols, comp, x, y;
338 int fileWidth, fileHeight;
339 unsigned char *colorMap = NULL;
340 unsigned int intMask[3];
341 BitmapChannel masks[3];
342 char buf[10];
343 unsigned char *line = NULL, *expline = NULL;
344 unsigned short rgb;
345
346 CommonMatch(handle, &fileWidth, &fileHeight, &colorMap, &numBits,
347 &numCols, &comp, intMask);
348
349 if (numBits == 16) {
350 if (comp == BI_BITFIELDS) {
351 GetChannelMasks(intMask, masks);
352 } else {
353 masks[0].mask = 0x7c00;
354 masks[0].shiftin = 10;
355 masks[0].shiftout = 3;
356 masks[1].mask = 0x03e0;
357 masks[1].shiftin = 5;
358 masks[1].shiftout = 3;
359 masks[2].mask = 0x001f;
360 masks[2].shiftin = 0;
361 masks[2].shiftout = 3;
362 }
363 }
364
365 /* printf("reading %d-bit BMP %dx%d\n", numBits, width, height); */
366 if (comp == BI_RLE8 || comp == BI_RLE4) {
367 tkimg_ReadBuffer(1);
368 }
369
370 if (tkimg_PhotoExpand(interp, imageHandle, destX + width, destY + height) == TCL_ERROR) {
371 goto error;
372 }
373
374 bytesPerLine = ((numBits * fileWidth + 31)/32)*4;
375
376 /* printf("bytesPerLine = %d numBits=%d (%dx%d)\n",
377 * bytesPerLine, numBits, width, height);
378 */
379 block.pixelSize = 3;
380 block.pitch = width * 3;
381 block.width = width;
382 block.height = 1;
383 block.offset[0] = 2;
384 block.offset[1] = 1;
385 block.offset[2] = 0;
386 block.offset[3] = block.offset[0];
387
388 if (comp == BI_RGB || comp == BI_BITFIELDS) { /* No compression */
389 line = (unsigned char *) ckalloc(bytesPerLine);
390 for(y=srcY+height; y<fileHeight; y++) {
391 tkimg_Read2(handle, (char *)line, bytesPerLine);
392 }
393 switch (numBits) {
394 case 32:
395 block.pixelPtr = expline = (unsigned char *) ckalloc(3*width);
396 for( y = height-1; y>=0; y--) {
397 tkimg_Read2(handle, (char *)line, bytesPerLine);
398 for (x = srcX; x < (srcX+width); x++) {
399 *expline++ = line[x*4 + 0];
400 *expline++ = line[x*4 + 1];
401 *expline++ = line[x*4 + 2];
402 }
403 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
404 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
405 goto error;
406 }
407 expline = block.pixelPtr;
408 }
409 break;
410 case 24:
411 block.pixelPtr = line + srcX*3;
412 for( y = height-1; y>=0; y--) {
413 tkimg_Read2(handle, (char *)line, bytesPerLine);
414 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
415 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
416 goto error;
417 }
418 }
419 break;
420 case 16:
421 block.pixelPtr = expline = (unsigned char *) ckalloc(3*width);
422 for( y = height-1; y>=0; y--) {
423 tkimg_Read2(handle, (char *)line, bytesPerLine);
424 for (x = srcX; x < (srcX+width); x++) {
425 rgb = getUInt16 (&line[x*2]);
426 *expline++ = ((rgb & masks[2].mask) >> masks[2].shiftin) << masks[2].shiftout;
427 *expline++ = ((rgb & masks[1].mask) >> masks[1].shiftin) << masks[1].shiftout;
428 *expline++ = ((rgb & masks[0].mask) >> masks[0].shiftin) << masks[0].shiftout;
429 }
430 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
431 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
432 goto error;
433 }
434 expline = block.pixelPtr;
435 }
436 break;
437 case 8:
438 block.pixelPtr = expline = (unsigned char *) ckalloc(3*width);
439 for( y = height-1; y>=0; y--) {
440 tkimg_Read2(handle, (char *)line, bytesPerLine);
441 for (x = srcX; x < (srcX+width); x++) {
442 memcpy(expline, colorMap+(3*line[x]),3);
443 expline += 3;
444 }
445 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
446 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
447 goto error;
448 }
449 expline = block.pixelPtr;
450 }
451 break;
452 case 4:
453 block.pixelPtr = expline = (unsigned char *) ckalloc(3*width);
454 for( y = height-1; y>=0; y--) {
455 int c;
456 tkimg_Read2(handle, (char *)line, bytesPerLine);
457 for (x = srcX; x < (srcX+width); x++) {
458 if (x&1) {
459 c = line[x/2] & 0x0f;
460 } else {
461 c = line[x/2] >> 4;
462 }
463 memcpy(expline, colorMap+(3*c),3);
464 expline += 3;
465 }
466 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
467 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
468 goto error;
469 }
470 expline = block.pixelPtr;
471 }
472 break;
473 case 1:
474 block.pixelPtr = expline = (unsigned char *) ckalloc(3*width);
475 for( y = height-1; y>=0; y--) {
476 int c;
477 tkimg_Read2(handle, (char *)line, bytesPerLine);
478 for (x = srcX; x < (srcX+width); x++) {
479 c = (line[x/8] >> (7-(x%8))) & 1;
480 memcpy(expline, colorMap+(3*c),3);
481 expline += 3;
482 }
483 if (tkimg_PhotoPutBlock(interp, imageHandle, &block,
484 destX, destY+y, width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
485 goto error;
486 }
487 expline = block.pixelPtr;
488 }
489 break;
490 default:
491 sprintf(buf, "%d", numBits);
492 Tcl_AppendResult(interp, buf,
493 "-bits BMP file not (yet) supported", (char *) NULL);
494 goto error;
495 }
496 } else { /* RLE Compression */
497 int i, j, c;
498 unsigned char howMuch;
499 unsigned char rleBuf[2];
500 unsigned char rleDelta[2];
501 unsigned char val;
502
503 x = srcX;
504 y = fileHeight - 1;
505 block.pixelPtr = expline = (unsigned char *) ckalloc (3*fileWidth);
506
507 if (numBits != 4 && numBits != 8) {
508 sprintf (buf, "%d", numBits);
509 Tcl_AppendResult(interp, "RLE compression not supported for ",
510 buf, "-bit pixel values", (char *) NULL);
511 goto error;
512 }
513
514 while (1) {
515 if (2 != tkimg_Read2(handle, (char *)rleBuf, 2)) {
516 Tcl_AppendResult(interp, "Unexpected EOF", (char *) NULL);
517 goto error;
518 }
519 /* printf("In: (%d %X) --> \n", rleBuf[0], rleBuf[1]); */
520 if (rleBuf[0] != 0) {
521 howMuch = rleBuf[0];
522 switch (numBits) {
523 case 8: {
524 for (i=0; i<howMuch; i++, x++) {
525 memcpy (expline, colorMap + 3*rleBuf[1], 3);
526 expline += 3;
527 }
528 break;
529 }
530 case 4: {
531 for (i=0; i<howMuch; i++, x++) {
532 if (x&1) {
533 c = rleBuf[1] & 0x0f;
534 } else {
535 c = rleBuf[1] >> 4;
536 }
537 memcpy(expline, colorMap + 3*c, 3);
538 if (x>=srcX+width-1) {
539 break;
540 }
541 expline += 3;
542 }
543 break;
544 }
545 }
546 } else {
547 if (rleBuf[1]>2) {
548 /* uncompressed record */
549 howMuch = rleBuf[1];
550 /* printf ("Uncompressed: %d\n", howMuch); fflush (stdout); */
551 switch (numBits) {
552 case 8: {
553 for (i=0; i<howMuch; i++, x++) {
554 if (1 != tkimg_Read2(handle, (char *)&val, 1)) {
555 Tcl_AppendResult(interp, "Unexpected EOF", (char *)NULL);
556 goto error;
557 }
558 memcpy(expline, colorMap + 3*val, 3);
559 expline += 3;
560 }
561 break;
562 }
563 case 4: {
564 for (i=0; i<howMuch; i+=2) {
565 if (1 != tkimg_Read2(handle, (char *)&val, 1)) {
566 Tcl_AppendResult(interp, "Unexpected EOF", (char *)NULL);
567 goto error;
568 }
569 for (j=0; j<2; j++, x++) {
570 if (x&1) {
571 c = val & 0x0f;
572 } else {
573 c = val >> 4;
574 }
575 memcpy(expline, colorMap + 3*c, 3);
576 if (x>=srcX+width-1) {
577 break;
578 }
579 expline += 3;
580 }
581 }
582 break;
583 }
584 }
585
586 if ((howMuch % 2) && (numBits==4)) {
587 howMuch++;
588 }
589
590 if ((howMuch / (8 / numBits)) % 2) {
591 if (1 != tkimg_Read2(handle, (char *)&val, 1)) {
592 Tcl_AppendResult(interp, "Unexpected EOF", (char *)NULL);
593 goto error;
594 }
595 }
596 } else if (rleBuf[1]==0) {
597 /* End of line */
598 /* printf("New line: y=%d x=%d\n", y, x); fflush(stdout); */
599 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, destY+y,
600 width, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
601 goto error;
602 }
603 y--;
604 x = srcX;
605 expline = block.pixelPtr;
606 } else if (rleBuf[1]==1) {
607 /* End of bitmap */
608 break;
609 } else if (rleBuf[1]==2) {
610 /* Deltarecord */
611 /* printf("Deltarecord\n"); fflush(stdout); */
612 if (2 != tkimg_Read2(handle, (char *) rleDelta, 2)) {
613 Tcl_AppendResult(interp, "Unexpected EOF", (char *) NULL);
614 goto error;
615 }
616 x += rleDelta[0];
617 y += rleDelta[1];
618 }
619 }
620 }
621 }
622 tkimg_ReadBuffer(0);
623
624 if (colorMap) {
625 ckfree((char *) colorMap);
626 }
627 if (line) {
628 ckfree((char *) line);
629 }
630 if (expline) {
631 ckfree((char *) block.pixelPtr);
632 }
633 return TCL_OK ;
634
635 error:
636 tkimg_ReadBuffer(0);
637 if (colorMap) {
638 ckfree((char *) colorMap);
639 }
640 if (line) {
641 ckfree((char *) line);
642 }
643 if (expline) {
644 ckfree((char *) block.pixelPtr);
645 }
646 return TCL_ERROR;
647 }
648
649 static int
CommonWrite(Tcl_Interp * interp,Tcl_Obj * format,tkimg_MFile * handle,Tk_PhotoImageBlock * blockPtr)650 CommonWrite(
651 Tcl_Interp *interp,
652 Tcl_Obj *format,
653 tkimg_MFile *handle,
654 Tk_PhotoImageBlock *blockPtr
655 ) {
656 int bperline, nbytes, ncolors, i, x, y, greenOffset, blueOffset, alphaOffset;
657 unsigned char *imagePtr, *pixelPtr;
658 unsigned char buf[4];
659 int colors[256];
660 int resX=75*39, resY=75*39;
661 int objc = 0;
662 Tcl_Obj **objv = NULL;
663
664 /* Decode resolution parameter -resolution List */
665 if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
666 return TCL_ERROR;
667 }
668 /* List parameter given ? */
669 if (objc > 1) {
670 Tcl_Obj *objList;
671 int nBytes;
672 double fResX = -1, fResY = -1, fFactor = 0;
673 char unit = '\0';
674 char *c = Tcl_GetStringFromObj(objv[1], &nBytes);
675 if ((objc > 3) || ((objc == 3) && ((c[0] != '-') ||
676 (c[1] != 'r') || strncmp(c, "-resolution", strlen(c))))) {
677 Tcl_AppendResult(interp, "invalid format: \"",
678 tkimg_GetStringFromObj2(format, NULL), "\"", (char *) NULL);
679 return TCL_ERROR;
680 }
681 objList = objv[objc-1];
682 if (tkimg_ListObjGetElements(interp, objList, &objc, &objv) != TCL_OK) {
683 return TCL_ERROR;
684 }
685 if (!objc || objc > 3) {
686 Tcl_AppendResult(interp, "Wrong resolution parameters: \"",
687 tkimg_GetStringFromObj2(objList, NULL), "\"", (char *) NULL);
688 return TCL_ERROR;
689 }
690 if (Tcl_GetDoubleFromObj(interp, objv[0], &fResX) != TCL_OK) {
691 return TCL_ERROR;
692 }
693 if ( fResX < 0 || fResX > 1e20 ) {
694 Tcl_AppendResult(interp, "Wrong resolution: \"",
695 tkimg_GetStringFromObj2(objv[0], NULL), "\"", (char *) NULL);
696 return TCL_ERROR;
697 }
698 /* more than xRes value given ? */
699 if (objc > 1) {
700 /* check last for unit */
701 c = Tcl_GetStringFromObj(objv[objc-1], &nBytes);
702 if ( nBytes == 1 && ( c[0] == 'c' || c[0] == 'i' || c[0] == 'm' || c[0] == 'p') ) {
703 unit = c[0];
704 objc--;
705 }
706 if (objc > 2) {
707 Tcl_AppendResult(interp, "Wrong unit: \"",
708 tkimg_GetStringFromObj2(objv[objc-1], NULL), "\"", (char *) NULL);
709 return TCL_ERROR;
710 }
711 if (objc > 1) {
712 if (Tcl_GetDoubleFromObj(interp, objv[1], &fResY) != TCL_OK) {
713 return TCL_ERROR;
714 }
715 if ( fResY < 0 || fResY > 1e20 ) {
716 Tcl_AppendResult(interp, "Wrong resolution: \"",
717 tkimg_GetStringFromObj2(objv[0], NULL), "\"", (char *) NULL);
718 return TCL_ERROR;
719 }
720 }
721 }
722 /* Process unit and eventually get factor value */
723 switch (unit) {
724 case 'c': /* centimeter */
725 fFactor = 100.0;
726 break;
727 case 'i': /* inches */
728 fFactor = 1.0/0.0254;
729 break;
730 case 'm': /* millimeter */
731 fFactor = 1000.0;
732 break;
733 case 'p': /* printer points (1/72 inch) */
734 fFactor = 72.0/0.0254;
735 break;
736 default: /* no unit given - 75*39 as X resolution */
737 if (fResX == 0) {
738 resX = 0;
739 resY = 0;
740 } else {
741 if (fResY != -1) {
742 resY = (int) ( fResY * 75.0*39.0 / fResX + 0.5);
743 }
744 }
745 break;
746 }
747 if (fFactor != 0) {
748 resX = (int) ( fResX * fFactor + 0.5);
749 if (fResY == -1) {
750 resY = resX;
751 } else {
752 resY = (int) ( fResY * fFactor + 0.5);
753 }
754 }
755 }
756
757 greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
758 blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
759 alphaOffset = blockPtr->offset[0];
760 if (alphaOffset < blockPtr->offset[2]) {
761 alphaOffset = blockPtr->offset[2];
762 }
763 if (++alphaOffset < blockPtr->pixelSize) {
764 alphaOffset -= blockPtr->offset[0];
765 } else {
766 alphaOffset = 0;
767 }
768 ncolors = 0;
769 for (y = 0; ncolors <= 256 && y < blockPtr->height; y++) {
770 pixelPtr = blockPtr->pixelPtr + y*blockPtr->pitch + blockPtr->offset[0];
771 for (x=0; ncolors <= 256 && x<blockPtr->width; x++) {
772 int pixel;
773 if (alphaOffset && (pixelPtr[alphaOffset] == 0))
774 pixel = 0xd9d9d9;
775 else
776 pixel = (pixelPtr[0]<<16) | (pixelPtr[greenOffset]<<8) | pixelPtr[blueOffset];
777 for (i = 0; i < ncolors && pixel != colors[i]; i++);
778 if (i == ncolors) {
779 if (ncolors < 256) {
780 colors[ncolors] = pixel;
781 }
782 ncolors++;
783 }
784 pixelPtr += blockPtr->pixelSize;
785 }
786 }
787 if (ncolors <= 256 && (blockPtr->width * blockPtr->height >= 512)) {
788 while (ncolors < 256) {
789 colors[ncolors++] = 0;
790 }
791 nbytes = 1;
792 } else {
793 nbytes = 3;
794 ncolors = 0;
795 }
796
797 bperline = ((blockPtr->width * nbytes + 3) / 4) * 4;
798
799 tkimg_Write2(handle,"BM", 2);
800 putint(handle, 54 + (ncolors*4) + bperline * blockPtr->height);
801 putint(handle, 0);
802 putint(handle, 54 + (ncolors*4));
803 putint(handle, 40);
804 putint(handle, blockPtr->width);
805 putint(handle, blockPtr->height);
806 putint(handle, 1 + (nbytes<<19));
807 putint(handle, 0);
808 putint(handle, bperline * blockPtr->height);
809 putint(handle, resX);
810 putint(handle, resY);
811 putint(handle, ncolors);
812 putint(handle, ncolors);
813
814 for (i = 0; i < ncolors ; i++) {
815 putint(handle, colors[i]);
816 }
817
818 bperline -= blockPtr->width * nbytes;
819
820 imagePtr = blockPtr->pixelPtr + blockPtr->offset[0]
821 + blockPtr->height * blockPtr->pitch;
822 for (y = 0; y < blockPtr->height; y++) {
823 pixelPtr = imagePtr -= blockPtr->pitch;
824 for (x=0; x<blockPtr->width; x++) {
825 if (ncolors) {
826 int pixel;
827 if (alphaOffset && (pixelPtr[alphaOffset] == 0))
828 pixel = 0xd9d9d9;
829 else
830 pixel = (pixelPtr[0]<<16)|(pixelPtr[greenOffset]<<8)|pixelPtr[blueOffset];
831 for (i = 0; i < ncolors && pixel != colors[i]; i += 1);
832 buf[0] = i;
833 } else if (alphaOffset && (pixelPtr[alphaOffset] == 0)) {
834 buf[0] = buf[1] = buf[2] = 0xd9;
835 } else {
836 buf[0] = pixelPtr[blueOffset];
837 buf[1] = pixelPtr[greenOffset];
838 buf[2] = pixelPtr[0];
839 }
840 tkimg_Write2(handle, (char *) buf, nbytes);
841 pixelPtr += blockPtr->pixelSize;
842 }
843 if (bperline) {
844 tkimg_Write2(handle, "\0\0\0", bperline);
845 }
846 }
847 return(TCL_OK);
848 }
849
850 static void
putint(tkimg_MFile * handle,int i)851 putint(
852 tkimg_MFile *handle,
853 int i
854 ) {
855 unsigned char buf[4];
856 buf[0] = i;
857 buf[1] = i>>8;
858 buf[2] = i>>16;
859 buf[3] = i>>24;
860 tkimg_Write2(handle, (char *) buf, 4);
861 }
862