1/* 2 * tkImgPPM.c -- 3 * 4 * A photo image file handler for PPM (Portable PixMap) files. 5 * 6 * Copyright (c) 1994 The Australian National University. 7 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * Author: Paul Mackerras (paulus@cs.anu.edu.au), 13 * Department of Computer Science, 14 * Australian National University. 15 * 16 * RCS: @(#) $Id$ 17 */ 18 19#include "tkInt.h" 20 21/* 22 * The maximum amount of memory to allocate for data read from the file. If we 23 * need more than this, we do it in pieces. 24 */ 25 26#define MAX_MEMORY 10000 /* don't allocate > 10KB */ 27 28/* 29 * Define PGM and PPM, i.e. gray images and color images. 30 */ 31 32#define PGM 1 33#define PPM 2 34 35/* 36 * The format record for the PPM file format: 37 */ 38 39static int FileMatchPPM(Tcl_Channel chan, CONST char *fileName, 40 Tcl_Obj *format, int *widthPtr, int *heightPtr, 41 Tcl_Interp *interp); 42static int FileReadPPM(Tcl_Interp *interp, Tcl_Channel chan, 43 CONST char *fileName, Tcl_Obj *format, 44 Tk_PhotoHandle imageHandle, int destX, int destY, 45 int width, int height, int srcX, int srcY); 46static int FileWritePPM(Tcl_Interp *interp, CONST char *fileName, 47 Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr); 48static int StringWritePPM(Tcl_Interp *interp, Tcl_Obj *format, 49 Tk_PhotoImageBlock *blockPtr); 50static int StringMatchPPM(Tcl_Obj *dataObj, Tcl_Obj *format, 51 int *widthPtr, int *heightPtr, Tcl_Interp *interp); 52static int StringReadPPM(Tcl_Interp *interp, Tcl_Obj *dataObj, 53 Tcl_Obj *format, Tk_PhotoHandle imageHandle, 54 int destX, int destY, int width, int height, 55 int srcX, int srcY); 56 57Tk_PhotoImageFormat tkImgFmtPPM = { 58 "ppm", /* name */ 59 FileMatchPPM, /* fileMatchProc */ 60 StringMatchPPM, /* stringMatchProc */ 61 FileReadPPM, /* fileReadProc */ 62 StringReadPPM, /* stringReadProc */ 63 FileWritePPM, /* fileWriteProc */ 64 StringWritePPM, /* stringWriteProc */ 65}; 66 67/* 68 * Prototypes for local functions defined in this file: 69 */ 70 71static int ReadPPMFileHeader(Tcl_Channel chan, int *widthPtr, 72 int *heightPtr, int *maxIntensityPtr); 73static int ReadPPMStringHeader(Tcl_Obj *dataObj, int *widthPtr, 74 int *heightPtr, int *maxIntensityPtr, 75 unsigned char **dataBufferPtr, int *dataSizePtr); 76 77/* 78 *---------------------------------------------------------------------- 79 * 80 * FileMatchPPM -- 81 * 82 * This function is invoked by the photo image type to see if a file 83 * contains image data in PPM format. 84 * 85 * Results: 86 * The return value is >0 if the first characters in file "f" look like 87 * PPM data, and 0 otherwise. 88 * 89 * Side effects: 90 * The access position in f may change. 91 * 92 *---------------------------------------------------------------------- 93 */ 94 95static int 96FileMatchPPM( 97 Tcl_Channel chan, /* The image file, open for reading. */ 98 CONST char *fileName, /* The name of the image file. */ 99 Tcl_Obj *format, /* User-specified format string, or NULL. */ 100 int *widthPtr, int *heightPtr, 101 /* The dimensions of the image are returned 102 * here if the file is a valid raw PPM 103 * file. */ 104 Tcl_Interp *interp) /* unused */ 105{ 106 int dummy; 107 108 return ReadPPMFileHeader(chan, widthPtr, heightPtr, &dummy); 109} 110 111/* 112 *---------------------------------------------------------------------- 113 * 114 * FileReadPPM -- 115 * 116 * This function is called by the photo image type to read PPM format 117 * data from a file and write it into a given photo image. 118 * 119 * Results: 120 * A standard TCL completion code. If TCL_ERROR is returned then an error 121 * message is left in the interp's result. 122 * 123 * Side effects: 124 * The access position in file f is changed, and new data is added to the 125 * image given by imageHandle. 126 * 127 *---------------------------------------------------------------------- 128 */ 129 130static int 131FileReadPPM( 132 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ 133 Tcl_Channel chan, /* The image file, open for reading. */ 134 CONST char *fileName, /* The name of the image file. */ 135 Tcl_Obj *format, /* User-specified format string, or NULL. */ 136 Tk_PhotoHandle imageHandle, /* The photo image to write into. */ 137 int destX, int destY, /* Coordinates of top-left pixel in photo 138 * image to be written to. */ 139 int width, int height, /* Dimensions of block of photo image to be 140 * written to. */ 141 int srcX, int srcY) /* Coordinates of top-left pixel to be used in 142 * image being read. */ 143{ 144 int fileWidth, fileHeight, maxIntensity; 145 int nLines, nBytes, h, type, count; 146 unsigned char *pixelPtr; 147 Tk_PhotoImageBlock block; 148 149 type = ReadPPMFileHeader(chan, &fileWidth, &fileHeight, &maxIntensity); 150 if (type == 0) { 151 Tcl_AppendResult(interp, "couldn't read raw PPM header from file \"", 152 fileName, "\"", NULL); 153 return TCL_ERROR; 154 } 155 if ((fileWidth <= 0) || (fileHeight <= 0)) { 156 Tcl_AppendResult(interp, "PPM image file \"", fileName, 157 "\" has dimension(s) <= 0", NULL); 158 return TCL_ERROR; 159 } 160 if ((maxIntensity <= 0) || (maxIntensity >= 256)) { 161 char buffer[TCL_INTEGER_SPACE]; 162 163 sprintf(buffer, "%d", maxIntensity); 164 Tcl_AppendResult(interp, "PPM image file \"", fileName, 165 "\" has bad maximum intensity value ", buffer, NULL); 166 return TCL_ERROR; 167 } 168 169 if ((srcX + width) > fileWidth) { 170 width = fileWidth - srcX; 171 } 172 if ((srcY + height) > fileHeight) { 173 height = fileHeight - srcY; 174 } 175 if ((width <= 0) || (height <= 0) 176 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 177 return TCL_OK; 178 } 179 180 if (type == PGM) { 181 block.pixelSize = 1; 182 block.offset[0] = 0; 183 block.offset[1] = 0; 184 block.offset[2] = 0; 185 } else { 186 block.pixelSize = 3; 187 block.offset[0] = 0; 188 block.offset[1] = 1; 189 block.offset[2] = 2; 190 } 191 block.offset[3] = 0; 192 block.width = width; 193 block.pitch = block.pixelSize * fileWidth; 194 195 if (Tk_PhotoExpand(interp, imageHandle, 196 destX + width, destY + height) != TCL_OK) { 197 return TCL_ERROR; 198 } 199 200 if (srcY > 0) { 201 Tcl_Seek(chan, (Tcl_WideInt)(srcY * block.pitch), SEEK_CUR); 202 } 203 204 nLines = (MAX_MEMORY + block.pitch - 1) / block.pitch; 205 if (nLines > height) { 206 nLines = height; 207 } 208 if (nLines <= 0) { 209 nLines = 1; 210 } 211 nBytes = nLines * block.pitch; 212 pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); 213 block.pixelPtr = pixelPtr + srcX * block.pixelSize; 214 215 for (h = height; h > 0; h -= nLines) { 216 if (nLines > h) { 217 nLines = h; 218 nBytes = nLines * block.pitch; 219 } 220 count = Tcl_Read(chan, (char *) pixelPtr, nBytes); 221 if (count != nBytes) { 222 Tcl_AppendResult(interp, "error reading PPM image file \"", 223 fileName, "\": ", 224 Tcl_Eof(chan) ? "not enough data" : Tcl_PosixError(interp), 225 NULL); 226 ckfree((char *) pixelPtr); 227 return TCL_ERROR; 228 } 229 if (maxIntensity != 255) { 230 unsigned char *p; 231 232 for (p = pixelPtr; count > 0; count--, p++) { 233 *p = (((int) *p) * 255)/maxIntensity; 234 } 235 } 236 block.height = nLines; 237 if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY, 238 width, nLines, TK_PHOTO_COMPOSITE_SET) != TCL_OK) { 239 ckfree((char *) pixelPtr); 240 return TCL_ERROR; 241 } 242 destY += nLines; 243 } 244 245 ckfree((char *) pixelPtr); 246 return TCL_OK; 247} 248 249/* 250 *---------------------------------------------------------------------- 251 * 252 * FileWritePPM -- 253 * 254 * This function is invoked to write image data to a file in PPM format 255 * (although we can read PGM files, we never write them). 256 * 257 * Results: 258 * A standard TCL completion code. If TCL_ERROR is returned then an error 259 * message is left in the interp's result. 260 * 261 * Side effects: 262 * Data is written to the file given by "fileName". 263 * 264 *---------------------------------------------------------------------- 265 */ 266 267static int 268FileWritePPM( 269 Tcl_Interp *interp, 270 CONST char *fileName, 271 Tcl_Obj *format, 272 Tk_PhotoImageBlock *blockPtr) 273{ 274 Tcl_Channel chan; 275 int w, h, greenOffset, blueOffset, nBytes; 276 unsigned char *pixelPtr, *pixLinePtr; 277 char header[16 + TCL_INTEGER_SPACE * 2]; 278 279 chan = Tcl_OpenFileChannel(interp, fileName, "w", 0666); 280 if (chan == NULL) { 281 return TCL_ERROR; 282 } 283 284 if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") 285 != TCL_OK) { 286 Tcl_Close(NULL, chan); 287 return TCL_ERROR; 288 } 289 if (Tcl_SetChannelOption(interp, chan, "-encoding", "binary") 290 != TCL_OK) { 291 Tcl_Close(NULL, chan); 292 return TCL_ERROR; 293 } 294 295 sprintf(header, "P6\n%d %d\n255\n", blockPtr->width, blockPtr->height); 296 Tcl_Write(chan, header, -1); 297 298 pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0]; 299 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 300 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 301 302 if ((greenOffset == 1) && (blueOffset == 2) && (blockPtr->pixelSize == 3) 303 && (blockPtr->pitch == (blockPtr->width * 3))) { 304 nBytes = blockPtr->height * blockPtr->pitch; 305 if (Tcl_Write(chan, (char *) pixLinePtr, nBytes) != nBytes) { 306 goto writeerror; 307 } 308 } else { 309 for (h = blockPtr->height; h > 0; h--) { 310 pixelPtr = pixLinePtr; 311 for (w = blockPtr->width; w > 0; w--) { 312 if ( Tcl_Write(chan,(char *)&pixelPtr[0], 1) == -1 || 313 Tcl_Write(chan,(char *)&pixelPtr[greenOffset],1)==-1 || 314 Tcl_Write(chan,(char *)&pixelPtr[blueOffset],1) ==-1) { 315 goto writeerror; 316 } 317 pixelPtr += blockPtr->pixelSize; 318 } 319 pixLinePtr += blockPtr->pitch; 320 } 321 } 322 323 if (Tcl_Close(NULL, chan) == 0) { 324 return TCL_OK; 325 } 326 chan = NULL; 327 328 writeerror: 329 Tcl_AppendResult(interp, "error writing \"", fileName, "\": ", 330 Tcl_PosixError(interp), NULL); 331 if (chan != NULL) { 332 Tcl_Close(NULL, chan); 333 } 334 return TCL_ERROR; 335} 336 337/* 338 *---------------------------------------------------------------------- 339 * 340 * StringWritePPM -- 341 * 342 * This function is invoked to write image data to a string in PPM 343 * format. 344 * 345 * Results: 346 * A standard TCL completion code. If TCL_ERROR is returned then an error 347 * message is left in the interp's result. 348 * 349 * Side effects: 350 * None. 351 * 352 *---------------------------------------------------------------------- 353 */ 354 355static int 356StringWritePPM( 357 Tcl_Interp *interp, 358 Tcl_Obj *format, 359 Tk_PhotoImageBlock *blockPtr) 360{ 361 int w, h, size, greenOffset, blueOffset; 362 unsigned char *pixLinePtr, *byteArray; 363 char header[16 + TCL_INTEGER_SPACE * 2]; 364 Tcl_Obj *byteArrayObj; 365 366 sprintf(header, "P6\n%d %d\n255\n", blockPtr->width, blockPtr->height); 367 368 /* 369 * Construct a byte array of the right size with the header and 370 * get a pointer to the data part of it. 371 */ 372 373 size = strlen(header); 374 byteArrayObj = Tcl_NewByteArrayObj((unsigned char *)header, size); 375 byteArray = Tcl_SetByteArrayLength(byteArrayObj, 376 size + 3*blockPtr->width*blockPtr->height); 377 byteArray += size; 378 379 pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0]; 380 greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; 381 blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; 382 383 /* 384 * Check if we can do the data move in single action. 385 */ 386 387 if ((greenOffset == 1) && (blueOffset == 2) && (blockPtr->pixelSize == 3) 388 && (blockPtr->pitch == (blockPtr->width * 3))) { 389 memcpy(byteArray, pixLinePtr, 390 (unsigned)blockPtr->height * blockPtr->pitch); 391 } else { 392 for (h = blockPtr->height; h > 0; h--) { 393 unsigned char *pixelPtr = pixLinePtr; 394 395 for (w = blockPtr->width; w > 0; w--) { 396 *byteArray++ = pixelPtr[0]; 397 *byteArray++ = pixelPtr[greenOffset]; 398 *byteArray++ = pixelPtr[blueOffset]; 399 pixelPtr += blockPtr->pixelSize; 400 } 401 pixLinePtr += blockPtr->pitch; 402 } 403 } 404 405 /* 406 * Return the object in the interpreter result. 407 */ 408 409 Tcl_SetObjResult(interp, byteArrayObj); 410 return TCL_OK; 411} 412 413/* 414 *---------------------------------------------------------------------- 415 * 416 * StringMatchPPM -- 417 * 418 * This function is invoked by the photo image type to see if a string 419 * contains image data in PPM format. 420 * 421 * Results: 422 * The return value is >0 if the first characters in file "f" look like 423 * PPM data, and 0 otherwise. 424 * 425 * Side effects: 426 * The access position in f may change. 427 * 428 *---------------------------------------------------------------------- 429 */ 430 431static int 432StringMatchPPM( 433 Tcl_Obj *dataObj, /* The image data. */ 434 Tcl_Obj *format, /* User-specified format string, or NULL. */ 435 int *widthPtr, int *heightPtr, 436 /* The dimensions of the image are returned 437 * here if the file is a valid raw PPM 438 * file. */ 439 Tcl_Interp *interp) /* unused */ 440{ 441 int dummy; 442 443 return ReadPPMStringHeader(dataObj, widthPtr, heightPtr, 444 &dummy, NULL, NULL); 445} 446 447/* 448 *---------------------------------------------------------------------- 449 * 450 * StringReadPPM -- 451 * 452 * This function is called by the photo image type to read PPM format 453 * data from a string and write it into a given photo image. 454 * 455 * Results: 456 * A standard TCL completion code. If TCL_ERROR is returned then an error 457 * message is left in the interp's result. 458 * 459 * Side effects: 460 * New data is added to the image given by imageHandle. 461 * 462 *---------------------------------------------------------------------- 463 */ 464 465static int 466StringReadPPM( 467 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ 468 Tcl_Obj *dataObj, /* The image data. */ 469 Tcl_Obj *format, /* User-specified format string, or NULL. */ 470 Tk_PhotoHandle imageHandle, /* The photo image to write into. */ 471 int destX, int destY, /* Coordinates of top-left pixel in photo 472 * image to be written to. */ 473 int width, int height, /* Dimensions of block of photo image to be 474 * written to. */ 475 int srcX, int srcY) /* Coordinates of top-left pixel to be used in 476 * image being read. */ 477{ 478 int fileWidth, fileHeight, maxIntensity; 479 int nLines, nBytes, h, type, count, dataSize; 480 unsigned char *pixelPtr, *dataBuffer; 481 Tk_PhotoImageBlock block; 482 483 type = ReadPPMStringHeader(dataObj, &fileWidth, &fileHeight, 484 &maxIntensity, &dataBuffer, &dataSize); 485 if (type == 0) { 486 Tcl_AppendResult(interp, "couldn't read raw PPM header from string", 487 NULL); 488 return TCL_ERROR; 489 } 490 if ((fileWidth <= 0) || (fileHeight <= 0)) { 491 Tcl_AppendResult(interp, "PPM image data has dimension(s) <= 0", 492 NULL); 493 return TCL_ERROR; 494 } 495 if ((maxIntensity <= 0) || (maxIntensity >= 256)) { 496 char buffer[TCL_INTEGER_SPACE]; 497 498 sprintf(buffer, "%d", maxIntensity); 499 Tcl_AppendResult(interp, 500 "PPM image data has bad maximum intensity value ", buffer, 501 NULL); 502 return TCL_ERROR; 503 } 504 505 if ((srcX + width) > fileWidth) { 506 width = fileWidth - srcX; 507 } 508 if ((srcY + height) > fileHeight) { 509 height = fileHeight - srcY; 510 } 511 if ((width <= 0) || (height <= 0) 512 || (srcX >= fileWidth) || (srcY >= fileHeight)) { 513 return TCL_OK; 514 } 515 516 if (type == PGM) { 517 block.pixelSize = 1; 518 block.offset[0] = 0; 519 block.offset[1] = 0; 520 block.offset[2] = 0; 521 } else { 522 block.pixelSize = 3; 523 block.offset[0] = 0; 524 block.offset[1] = 1; 525 block.offset[2] = 2; 526 } 527 block.offset[3] = 0; 528 block.width = width; 529 block.pitch = block.pixelSize * fileWidth; 530 531 if (srcY > 0) { 532 dataBuffer += srcY * block.pitch; 533 dataSize -= srcY * block.pitch; 534 } 535 536 if (maxIntensity == 255) { 537 /* 538 * We have all the data in memory, so write everything in one go. 539 */ 540 541 if (block.pitch*height > dataSize) { 542 Tcl_AppendResult(interp, "truncated PPM data", NULL); 543 return TCL_ERROR; 544 } 545 block.pixelPtr = dataBuffer + srcX * block.pixelSize; 546 block.height = height; 547 return Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY, 548 width, height, TK_PHOTO_COMPOSITE_SET); 549 } 550 551 if (Tk_PhotoExpand(interp, imageHandle, 552 destX + width, destY + height) != TCL_OK) { 553 return TCL_ERROR; 554 } 555 556 nLines = (MAX_MEMORY + block.pitch - 1) / block.pitch; 557 if (nLines > height) { 558 nLines = height; 559 } 560 if (nLines <= 0) { 561 nLines = 1; 562 } 563 nBytes = nLines * block.pitch; 564 pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); 565 block.pixelPtr = pixelPtr + srcX * block.pixelSize; 566 567 for (h = height; h > 0; h -= nLines) { 568 unsigned char *p; 569 570 if (nLines > h) { 571 nLines = h; 572 nBytes = nLines * block.pitch; 573 } 574 if (dataSize < nBytes) { 575 ckfree((char *) pixelPtr); 576 Tcl_AppendResult(interp, "truncated PPM data", NULL); 577 return TCL_ERROR; 578 } 579 for (p=pixelPtr,count=nBytes ; count>0 ; count--,p++,dataBuffer++) { 580 *p = (((int) *dataBuffer) * 255)/maxIntensity; 581 } 582 dataSize -= nBytes; 583 block.height = nLines; 584 if (Tk_PhotoPutBlock(interp, imageHandle, &block, destX, destY, 585 width, nLines, TK_PHOTO_COMPOSITE_SET) != TCL_OK) { 586 ckfree((char *) pixelPtr); 587 return TCL_ERROR; 588 } 589 destY += nLines; 590 } 591 592 ckfree((char *) pixelPtr); 593 return TCL_OK; 594} 595 596/* 597 *---------------------------------------------------------------------- 598 * 599 * ReadPPMFileHeader -- 600 * 601 * This function reads the PPM header from the beginning of a PPM file 602 * and returns information from the header. 603 * 604 * Results: 605 * The return value is PGM if file "f" appears to start with a valid PGM 606 * header, PPM if "f" appears to start with a valid PPM header, and 0 607 * otherwise. If the header is valid, then *widthPtr and *heightPtr are 608 * modified to hold the dimensions of the image and *maxIntensityPtr is 609 * modified to hold the value of a "fully on" intensity value. 610 * 611 * Side effects: 612 * The access position in f advances. 613 * 614 *---------------------------------------------------------------------- 615 */ 616 617static int 618ReadPPMFileHeader( 619 Tcl_Channel chan, /* Image file to read the header from. */ 620 int *widthPtr, int *heightPtr, 621 /* The dimensions of the image are returned 622 * here. */ 623 int *maxIntensityPtr) /* The maximum intensity value for the image 624 * is stored here. */ 625{ 626#define BUFFER_SIZE 1000 627 char buffer[BUFFER_SIZE], c; 628 int i, numFields, type = 0; 629 630 /* 631 * Read 4 space-separated fields from the file, ignoring comments (any 632 * line that starts with "#"). 633 */ 634 635 if (Tcl_Read(chan, &c, 1) != 1) { 636 return 0; 637 } 638 i = 0; 639 for (numFields = 0; numFields < 4; numFields++) { 640 /* 641 * Skip comments and white space. 642 */ 643 644 while (1) { 645 while (isspace(UCHAR(c))) { 646 if (Tcl_Read(chan, &c, 1) != 1) { 647 return 0; 648 } 649 } 650 if (c != '#') { 651 break; 652 } 653 do { 654 if (Tcl_Read(chan, &c, 1) != 1) { 655 return 0; 656 } 657 } while (c != '\n'); 658 } 659 660 /* 661 * Read a field (everything up to the next white space). 662 */ 663 664 while (!isspace(UCHAR(c))) { 665 if (i < (BUFFER_SIZE-2)) { 666 buffer[i] = c; 667 i++; 668 } 669 if (Tcl_Read(chan, &c, 1) != 1) { 670 goto done; 671 } 672 } 673 if (i < (BUFFER_SIZE-1)) { 674 buffer[i] = ' '; 675 i++; 676 } 677 } 678 679 done: 680 buffer[i] = 0; 681 682 /* 683 * Parse the fields, which are: id, width, height, maxIntensity. 684 */ 685 686 if (strncmp(buffer, "P6 ", 3) == 0) { 687 type = PPM; 688 } else if (strncmp(buffer, "P5 ", 3) == 0) { 689 type = PGM; 690 } else { 691 return 0; 692 } 693 if (sscanf(buffer+3, "%d %d %d", widthPtr, heightPtr, maxIntensityPtr) 694 != 3) { 695 return 0; 696 } 697 return type; 698} 699 700/* 701 *---------------------------------------------------------------------- 702 * 703 * ReadPPMStringHeader -- 704 * 705 * This function reads the PPM header from the beginning of a PPM-format 706 * string and returns information from the header. 707 * 708 * Results: 709 * The return value is PGM if the string appears to start with a valid 710 * PGM header, PPM if the string appears to start with a valid PPM 711 * header, and 0 otherwise. If the header is valid, then *widthPtr and 712 * *heightPtr are modified to hold the dimensions of the image and 713 * *maxIntensityPtr is modified to hold the value of a "fully on" 714 * intensity value. 715 * 716 * Side effects: 717 * None 718 * 719 *---------------------------------------------------------------------- 720 */ 721 722static int 723ReadPPMStringHeader( 724 Tcl_Obj *dataPtr, /* Object to read the header from. */ 725 int *widthPtr, int *heightPtr, 726 /* The dimensions of the image are returned 727 * here. */ 728 int *maxIntensityPtr, /* The maximum intensity value for the image 729 * is stored here. */ 730 unsigned char **dataBufferPtr, 731 int *dataSizePtr) 732{ 733#define BUFFER_SIZE 1000 734 char buffer[BUFFER_SIZE], c; 735 int i, numFields, dataSize, type = 0; 736 unsigned char *dataBuffer; 737 738 dataBuffer = Tcl_GetByteArrayFromObj(dataPtr, &dataSize); 739 740 /* 741 * Read 4 space-separated fields from the string, ignoring comments (any 742 * line that starts with "#"). 743 */ 744 745 if (dataSize-- < 1) { 746 return 0; 747 } 748 c = (char) (*dataBuffer++); 749 i = 0; 750 for (numFields = 0; numFields < 4; numFields++) { 751 /* 752 * Skip comments and white space. 753 */ 754 755 while (1) { 756 while (isspace(UCHAR(c))) { 757 if (dataSize-- < 1) { 758 return 0; 759 } 760 c = (char) (*dataBuffer++); 761 } 762 if (c != '#') { 763 break; 764 } 765 do { 766 if (dataSize-- < 1) { 767 return 0; 768 } 769 c = (char) (*dataBuffer++); 770 } while (c != '\n'); 771 } 772 773 /* 774 * Read a field (everything up to the next white space). 775 */ 776 777 while (!isspace(UCHAR(c))) { 778 if (i < (BUFFER_SIZE-2)) { 779 buffer[i] = c; 780 i++; 781 } 782 if (dataSize-- < 1) { 783 goto done; 784 } 785 c = (char) (*dataBuffer++); 786 } 787 if (i < (BUFFER_SIZE-1)) { 788 buffer[i] = ' '; 789 i++; 790 } 791 } 792 793 done: 794 buffer[i] = 0; 795 796 /* 797 * Parse the fields, which are: id, width, height, maxIntensity. 798 */ 799 800 if (strncmp(buffer, "P6 ", 3) == 0) { 801 type = PPM; 802 } else if (strncmp(buffer, "P5 ", 3) == 0) { 803 type = PGM; 804 } else { 805 return 0; 806 } 807 if (sscanf(buffer+3, "%d %d %d", widthPtr, heightPtr, maxIntensityPtr) 808 != 3) { 809 return 0; 810 } 811 if (dataBufferPtr != NULL) { 812 *dataBufferPtr = dataBuffer; 813 *dataSizePtr = dataSize; 814 } 815 return type; 816} 817 818/* 819 * Local Variables: 820 * mode: c 821 * c-basic-offset: 4 822 * fill-column: 78 823 * End: 824 */ 825