PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/System/Image/FileIO/OSGGIFImageFileType.cpp

https://github.com/msteners/OpenSGDevMaster_Toolbox
C++ | 2188 lines | 1472 code | 324 blank | 392 comment | 257 complexity | 74bcb18a183c1325f76a96256b127897 MD5 | raw file
Possible License(s): LGPL-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*---------------------------------------------------------------------------*\
  2. * OpenSG *
  3. * *
  4. * *
  5. * Copyright (C) 2000-2002 by the OpenSG Forum *
  6. * *
  7. * www.opensg.org *
  8. * *
  9. * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
  10. * *
  11. \*---------------------------------------------------------------------------*/
  12. /*---------------------------------------------------------------------------*\
  13. * License *
  14. * *
  15. * This library is free software; you can redistribute it and/or modify it *
  16. * under the terms of the GNU Library General Public License as published *
  17. * by the Free Software Foundation, version 2. *
  18. * *
  19. * This library is distributed in the hope that it will be useful, but *
  20. * WITHOUT ANY WARRANTY; without even the implied warranty of *
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
  22. * Library General Public License for more details. *
  23. * *
  24. * You should have received a copy of the GNU Library General Public *
  25. * License along with this library; if not, write to the Free Software *
  26. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
  27. * *
  28. \*---------------------------------------------------------------------------*/
  29. /*---------------------------------------------------------------------------*\
  30. * Changes *
  31. * *
  32. * *
  33. * *
  34. * *
  35. * *
  36. * *
  37. \*---------------------------------------------------------------------------*/
  38. //-------------------------------
  39. // Includes
  40. //-------------------------------
  41. #include <cstdlib>
  42. #include <cstdio>
  43. #include <setjmp.h>
  44. #include <cstring>
  45. #include <cctype>
  46. #include "OSGConfig.h"
  47. #ifdef OSG_SGI_LIB
  48. #include <limits>
  49. #endif
  50. #include "OSGGIFImageFileType.h"
  51. #include "OSGLog.h"
  52. #ifndef OSG_DO_DOC
  53. # ifdef OSG_WITH_GIF
  54. # define OSG_GIF_ARG(ARG) ARG
  55. # else
  56. # define OSG_GIF_ARG(ARG)
  57. # endif
  58. #else
  59. # define OSG_GIF_ARG(ARG) ARG
  60. #endif
  61. #ifdef OSG_WITH_GIF
  62. /*! \class OSG::GIFImageFileType
  63. \ingroup GrpSystemImage
  64. Image File Type to read/write and store/restore Image objects as
  65. GIF data.
  66. All the type specific code is included in the class. Does
  67. not depend on external libs.
  68. You have to --enable-gif in the configure line to enable
  69. the singleton object.
  70. */
  71. //--- GIF-INCLUDE START ----------------------------------------------------
  72. #define GIF_MAXCOLORS 256
  73. typedef enum
  74. {
  75. gif_image,
  76. gif_comment,
  77. gif_text
  78. } GIFStreamType;
  79. typedef enum
  80. {
  81. gif_no_disposal = 0,
  82. gif_keep_disposal = 1,
  83. gif_color_restore = 2,
  84. gif_image_restore = 3
  85. }
  86. GIFDisposalType;
  87. typedef struct
  88. {
  89. int transparent; /* transparency index */
  90. int delayTime; /* Time in 1/100 of a second */
  91. int inputFlag; /* wait for input after display */
  92. GIFDisposalType disposal;
  93. } GIF89info;
  94. typedef struct GIFData
  95. {
  96. GIF89info info;
  97. int x, y;
  98. int width, height;
  99. GIFStreamType type;
  100. union
  101. {
  102. struct
  103. {
  104. int cmapSize;
  105. unsigned char cmapData[GIF_MAXCOLORS][3];
  106. unsigned char *data;
  107. int interlaced;
  108. } image;
  109. struct
  110. {
  111. int fg, bg;
  112. int cellWidth, cellHeight;
  113. int len;
  114. char *text;
  115. } text;
  116. struct
  117. {
  118. int len;
  119. char *text;
  120. } comment;
  121. } data;
  122. struct GIFData *next;
  123. } GIFData;
  124. typedef struct
  125. {
  126. int width, height;
  127. int colorResolution;
  128. int colorMapSize;
  129. int cmapSize;
  130. unsigned char cmapData[GIF_MAXCOLORS][3];
  131. int background;
  132. int aspectRatio;
  133. GIFData *data;
  134. } GIFStream;
  135. static GIFStream *GIFRead (std::istream &is);
  136. int GIFTest (char *);
  137. int GIFWrite (char *, GIFStream *, int);
  138. static int GIFWriteFP(FILE *, GIFStream *, int);
  139. static int GIFFree (GIFStream *);
  140. #endif
  141. //--- GIF INCLUDE END ----------------------------------------------------
  142. OSG_USING_NAMESPACE
  143. static const Char8 *suffixArray[] =
  144. {
  145. "gif"
  146. };
  147. GIFImageFileType GIFImageFileType::_the("image/gif",
  148. suffixArray,
  149. sizeof(suffixArray));
  150. //-------------------------------------------------------------------------
  151. /*! Tries to fill the image object with the data read from
  152. the given fileName. Returns true on success.
  153. */
  154. bool GIFImageFileType::read( Image *OSG_GIF_ARG(pImage),
  155. std::istream &OSG_GIF_ARG(is),
  156. const std::string &OSG_GIF_ARG(mimetype))
  157. {
  158. bool retCode = false;
  159. #ifdef OSG_WITH_GIF
  160. Image::PixelFormat pixelFormat = Image::OSG_INVALID_PF;
  161. GIFStream *gifStream = GIFRead(is);
  162. GIFData *gifData = 0;
  163. bool isColor;
  164. int i, j, destI, lineSize, lineEnd;
  165. unsigned red, green, blue;
  166. int transparentIndex;
  167. int width = 0, height = 0, channel = 0;
  168. int xOff = 0, yOff = 0;
  169. unsigned char *srcData = 0, *destData = 0;
  170. int colorIndex;
  171. unsigned frameCount = 0, currentFrame = 0;
  172. unsigned char *colorMap = 0;
  173. // int imageSize = 0;
  174. int colorMapSize;
  175. Time frameDelay;
  176. if(gifStream)
  177. {
  178. frameCount = 0;
  179. for(gifData = gifStream->data; gifData; gifData = gifData->next)
  180. {
  181. if(gifData->type == gif_image)
  182. frameCount++;
  183. }
  184. }
  185. FDEBUG(("GIF Frames: %d\n", frameCount));
  186. if(gifStream)
  187. {
  188. for(gifData = gifStream->data; gifData; gifData = gifData->next)
  189. {
  190. switch(gifData->type)
  191. {
  192. case gif_image:
  193. if(frameCount)
  194. {
  195. FDEBUG(("Try to copy GIF Anim Frame %d/%d\n",
  196. (currentFrame + 1), frameCount));
  197. }
  198. // get the att.
  199. transparentIndex = gifData->info.transparent;
  200. frameDelay = float(gifData->info.delayTime) / 100.0f;
  201. width = gifData->width;
  202. height = gifData->height;
  203. xOff = gifData->x;
  204. yOff = gifData->y;
  205. // check if the movie is color or greyscale
  206. isColor = false;
  207. if(gifData->data.image.cmapSize > 0)
  208. {
  209. colorMapSize = gifData->data.image.cmapSize;
  210. colorMap =
  211. reinterpret_cast<unsigned char *>(
  212. gifData->data.image.cmapData);
  213. // cout << "INFO: Use gifData colorMap" << endl;
  214. }
  215. else if(gifStream->cmapSize > 0)
  216. {
  217. colorMapSize = gifStream->cmapSize;
  218. colorMap =
  219. reinterpret_cast<unsigned char *>(
  220. gifStream->cmapData);
  221. // cout << "INFO: Use gifStream colorMap" << endl;
  222. }
  223. else
  224. {
  225. FWARNING(("Bad color map in "
  226. "GIFImageFileType::read()\n"));
  227. colorMapSize = 0;
  228. }
  229. for(i = 0; i < colorMapSize; i++)
  230. {
  231. if(i != transparentIndex)
  232. {
  233. red = colorMap[i * 3 + 0];
  234. green = colorMap[i * 3 + 1];
  235. blue = colorMap[i * 3 + 2];
  236. if(red != green || red != blue)
  237. {
  238. isColor = true;
  239. break;
  240. }
  241. }
  242. }
  243. // calculate the movie channel
  244. channel =
  245. (isColor ? 3 : 1) + (transparentIndex >= 0 ? 1 : 0);
  246. if(currentFrame)
  247. {
  248. // is not the first frame
  249. if((channel == pImage->getBpp()) &&
  250. (width == pImage->getWidth()) &&
  251. (height == pImage->getHeight()))
  252. {
  253. destData = pImage->editData(0, currentFrame);
  254. }
  255. else
  256. {
  257. destData = pImage->editData(0, currentFrame);
  258. // This is probably wrong, but it's a start
  259. switch(gifData->info.disposal)
  260. {
  261. case gif_no_disposal:
  262. break;
  263. case gif_keep_disposal:
  264. memcpy(destData,
  265. pImage->getData(0,
  266. currentFrame - 1),
  267. pImage->getWidth () *
  268. pImage->getHeight() *
  269. channel);
  270. break;
  271. case gif_color_restore:
  272. {
  273. unsigned char r,g,b,a;
  274. Int32 bgindex = gifStream->background;
  275. unsigned char *d = destData;
  276. r = colorMap[bgindex * 3 + 0];
  277. g = colorMap[bgindex * 3 + 1];
  278. b = colorMap[bgindex * 3 + 2];
  279. a = (bgindex == transparentIndex) ?
  280. 0 : 255;
  281. for(UInt32 pixel =
  282. pImage->getWidth () *
  283. pImage->getHeight();
  284. pixel > 0; --pixel, d += channel)
  285. {
  286. d[0] = r;
  287. d[1] = g;
  288. d[2] = b;
  289. if(channel == 4)
  290. d[3] = a;
  291. }
  292. }
  293. break;
  294. case gif_image_restore:
  295. memcpy(destData,
  296. pImage->getData(
  297. 0,
  298. (currentFrame >= 2) ?
  299. (currentFrame - 2) : 0),
  300. pImage->getWidth () *
  301. pImage->getHeight() *
  302. channel);
  303. break;
  304. default:
  305. FWARNING(("Unknown GIF disposal "
  306. "mode %d\n",
  307. gifData->info.disposal));
  308. break;
  309. }
  310. }
  311. }
  312. else
  313. {
  314. switch(channel)
  315. {
  316. case 1:
  317. pixelFormat = Image::OSG_L_PF;
  318. break;
  319. case 2:
  320. pixelFormat = Image::OSG_LA_PF;
  321. break;
  322. case 3:
  323. pixelFormat = Image::OSG_RGB_PF;
  324. break;
  325. case 4:
  326. pixelFormat = Image::OSG_RGBA_PF;
  327. break;
  328. };
  329. pImage->set(pixelFormat,
  330. width,
  331. height,
  332. 1, 1,
  333. frameCount, frameDelay);
  334. destData = pImage->editData();
  335. }
  336. // copy the image data)
  337. lineSize = pImage->getWidth() * channel;
  338. lineEnd = width * channel + xOff * channel;
  339. srcData = gifData->data.image.data;
  340. destData =
  341. destData + ((pImage->getHeight() - yOff - 1)*lineSize);
  342. switch(channel)
  343. {
  344. case 1: // Greyscale without Alpha
  345. destI = 0;
  346. for(i = width * height; i--;)
  347. {
  348. destData[destI++] = colorMap[*srcData++ *3];
  349. if(destI >= lineSize)
  350. {
  351. destI = 0;
  352. destData -= lineSize;
  353. }
  354. }
  355. break;
  356. case 2: // Greyscale with Alpha
  357. destI = 0;
  358. for(i = width * height; i--;)
  359. {
  360. colorIndex = *srcData++;
  361. if(colorIndex == transparentIndex)
  362. {
  363. destData[destI++] = 0;
  364. destData[destI++] = 0;
  365. }
  366. else
  367. {
  368. destData[destI++] = colorMap[colorIndex*3];
  369. destData[destI++] = 255;
  370. }
  371. if(destI >= lineSize)
  372. {
  373. destI = 0;
  374. destData -= lineSize;
  375. }
  376. }
  377. break;
  378. case 3: // RGB without Alpha
  379. destI = 0;
  380. for(i = width * height; i--;)
  381. {
  382. colorIndex = *srcData++;
  383. for(j = 0; j < 3; j++)
  384. {
  385. destData[destI++] =
  386. colorMap[colorIndex * 3 + j];
  387. }
  388. if(destI >= lineSize)
  389. {
  390. destI = 0;
  391. destData -= lineSize;
  392. }
  393. }
  394. break;
  395. case 4: // RGB with Alpha
  396. destI = xOff * 4;
  397. for(i = width * height; i--;)
  398. {
  399. colorIndex = *srcData++;
  400. if(colorIndex == transparentIndex)
  401. {
  402. #if 0
  403. for(j = 0; j < 3; j++)
  404. destData[destI++] = 0; // RGB
  405. destData[destI++] = 0; // ALPHA
  406. #endif
  407. destI += 4;
  408. }
  409. else
  410. {
  411. for(j = 0; j < 3; j++)
  412. {
  413. destData[destI++] =
  414. colorMap[colorIndex * 3 + j];// RGB
  415. }
  416. destData[destI++] = 255; // ALPHA
  417. }
  418. if(destI >= lineEnd)
  419. {
  420. destI = xOff * 4;
  421. destData -= lineSize;
  422. }
  423. }
  424. break;
  425. }
  426. retCode = true;
  427. currentFrame++;
  428. break;
  429. case gif_comment:
  430. break;
  431. case gif_text:
  432. break;
  433. }
  434. }
  435. GIFFree(gifStream);
  436. }
  437. else
  438. {
  439. retCode = false;
  440. }
  441. #endif
  442. return retCode;
  443. }
  444. //-------------------------------------------------------------------------
  445. /*! Tries to write the image object to the given fileName.
  446. Returns true on success.
  447. */
  448. bool GIFImageFileType::write(const Image * ,
  449. std::ostream &,
  450. const std::string &)
  451. {
  452. #ifdef OSG_WITH_GIF
  453. SWARNING << getMimeType() << " write is not implemented " << endLog;
  454. #else
  455. SWARNING << getMimeType()
  456. << " write is not compiled into the current binary "
  457. << endLog;
  458. #endif
  459. return false;
  460. }
  461. //-------------------------------------------------------------------------
  462. /*!
  463. Tries to determine the mime type of the data provided by an input stream
  464. by searching for magic bytes. Returns the mime type or an empty string
  465. when the function could not determine the mime type.
  466. */
  467. std::string GIFImageFileType::determineMimetypeFromStream(std::istream &is)
  468. {
  469. char filecode[4];
  470. is.read(filecode, 4);
  471. is.seekg(-4, std::ios::cur);
  472. return strncmp(filecode, "GIF8", 4) == 0 ?
  473. std::string(getMimeType()) : std::string();
  474. }
  475. bool GIFImageFileType::validateHeader(const Char8 *fileName, bool &implemented)
  476. {
  477. implemented = true;
  478. if(fileName == NULL)
  479. return false;
  480. FILE *file = fopen(fileName, "rb");
  481. if(file == NULL)
  482. return false;
  483. std::string magic;
  484. magic.resize(4);
  485. fread(static_cast<void *>(&magic[0]), 4, 1, file);
  486. fclose(file);
  487. if(magic == "GIF8")
  488. {
  489. return true;
  490. }
  491. return false;
  492. }
  493. //-------------------------------------------------------------------------
  494. /*! Constructor used for the singleton object
  495. */
  496. GIFImageFileType::GIFImageFileType(const Char8 *mimeType,
  497. const Char8 *suffixArray[],
  498. UInt16 suffixByteCount) :
  499. Inherited(mimeType,suffixArray, suffixByteCount)
  500. {
  501. }
  502. //-------------------------------------------------------------------------
  503. /*! Destructor
  504. */
  505. GIFImageFileType::~GIFImageFileType(void)
  506. {
  507. }
  508. #ifdef OSG_WITH_GIF
  509. //--- GIF-READ START ----------------------------------------------------
  510. /*
  511. ** Copyright 1994, Home Pages, Inc.
  512. **
  513. ** Please read the file COPYRIGHT for specific information.
  514. **
  515. ** Home Pages, Inc.
  516. ** 257 Castro St. Suite 219
  517. ** Mountain View, CA 94041
  518. **
  519. ** Phone: 1 415 903 5353
  520. ** Fax: 1 415 903 5345
  521. **
  522. ** EMail: support@homepages.com
  523. **
  524. */
  525. /* +-------------------------------------------------------------------+ */
  526. /* | Copyright 1990 - 1994, David Koblas. (koblas@netcom.com) | */
  527. /* | Permission to use, copy, modify, and distribute this software | */
  528. /* | and its documentation for any purpose and without fee is hereby | */
  529. /* | granted, provided that the above copyright notice appear in all | */
  530. /* | copies and that both that copyright notice and this permission | */
  531. /* | notice appear in supporting documentation. This software is | */
  532. /* | provided "as is" without express or implied warranty. | */
  533. /* +-------------------------------------------------------------------+ */
  534. #define GIF_TRUE 1
  535. #define GIF_FALSE 0
  536. #define MAX_LWZ_BITS 12
  537. #define INTERLACE 0x40
  538. #define LOCALCOLORMAP 0x80
  539. #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
  540. #define ReadOK(is, buffer, len) (is.read(reinterpret_cast<char*>(buffer), \
  541. len).gcount() == len)
  542. #define MKINT(a, b) (((b) << 8) | (a))
  543. #define NEW(x) (static_cast<x *>(malloc(sizeof(x))))
  544. /***************************************************************************
  545. *
  546. * ERROR() -- should not return
  547. * INFO_MSG() -- info message, can be ignored
  548. *
  549. ***************************************************************************/
  550. #if 0
  551. #define INFO_MSG(fmt) pm_message fmt
  552. #define ERROR(str) pm_error(str)
  553. #else
  554. #if 0
  555. #define INFO_MSG(fmt)
  556. #define ERROR(str) do { RWSetMsg(str); longjmp(setjmp_buffer, 1); } while(0)
  557. #else
  558. #define INFO_MSG(fmt) { FINFO(("Info loading gif: '%s'!\n", fmt)); }
  559. #define GIF_ERROR(str) { FWARNING(("Error loading gif: '%s'!\n", str)); \
  560. longjmp(setjmp_buffer, 1); }
  561. #endif
  562. #endif
  563. /***************************************************************************/
  564. static int readColorMap(std::istream &, int, unsigned char [GIF_MAXCOLORS][3]);
  565. static int GetDataBlock(std::istream &, unsigned char *);
  566. static void readImage (std::istream &, int, int, int, unsigned char *);
  567. static jmp_buf setjmp_buffer;
  568. static int verbose = GIF_FALSE;
  569. //static int showComment = GIF_FALSE;
  570. /* */
  571. static GIFStream *GIFRead(std::istream &is)
  572. {
  573. unsigned char buf[256];
  574. unsigned char c;
  575. GIFStream *gifStream = 0;
  576. GIFData *cur, **end;
  577. GIF89info info = {0, 0, 0, gif_no_disposal};
  578. int resetInfo = GIF_TRUE;
  579. int n;
  580. if(setjmp(setjmp_buffer))
  581. goto out;
  582. if(!ReadOK(is, buf, 6))
  583. {
  584. GIF_ERROR("error reading magic number");
  585. }
  586. if(strncmp(reinterpret_cast<char *>(buf), "GIF", 3) != 0)
  587. GIF_ERROR("not a GIF file");
  588. if((strncmp((reinterpret_cast<char *>(buf)) + 3, "87a", 3) != 0) &&
  589. (strncmp((reinterpret_cast<char *>(buf)) + 3, "89a", 3) != 0))
  590. {
  591. GIF_ERROR("bad version number, not '87a' or '89a'");
  592. }
  593. if(!ReadOK(is, buf, 7))
  594. {
  595. GIF_ERROR("failed to read screen descriptor");
  596. }
  597. gifStream = NEW(GIFStream);
  598. gifStream->width = MKINT(buf[0], buf[1]);
  599. gifStream->height = MKINT(buf[2], buf[3]);
  600. gifStream->cmapSize = 2 << (buf[4] & 0x07);
  601. gifStream->colorMapSize = gifStream->cmapSize;
  602. gifStream->colorResolution = (int(buf[4] & 0x70) >> 3) + 1;
  603. gifStream->background = buf[5];
  604. gifStream->aspectRatio = buf[6];
  605. gifStream->data = NULL;
  606. end = &gifStream->data;
  607. /*
  608. ** Global colormap is present.
  609. */
  610. if(BitSet(buf[4], LOCALCOLORMAP))
  611. {
  612. if(readColorMap(is, gifStream->cmapSize, gifStream->cmapData))
  613. {
  614. GIF_ERROR("unable to get global colormap");
  615. }
  616. }
  617. else
  618. {
  619. gifStream->cmapSize = 0;
  620. gifStream->background = -1;
  621. }
  622. if(gifStream->aspectRatio != 0 && gifStream->aspectRatio != 49)
  623. {
  624. INFO_MSG(("warning - non-square pixels"));
  625. }
  626. while(ReadOK(is, &c, 1) && c != ';')
  627. {
  628. if(resetInfo)
  629. {
  630. info.disposal = static_cast<GIFDisposalType>(0);
  631. info.inputFlag = 0;
  632. info.delayTime = 0;
  633. info.transparent = -1;
  634. resetInfo = GIF_FALSE;
  635. }
  636. cur = NULL;
  637. if(c == '!')
  638. { /* Extension */
  639. if(!ReadOK(is, &c, 1))
  640. {
  641. GIF_ERROR("EOF / read error on extention function code");
  642. }
  643. if(c == 0xf9)
  644. { /* graphic control */
  645. (void) GetDataBlock(is, buf);
  646. info.disposal =
  647. static_cast<GIFDisposalType>((buf[0] >> 2) & 0x7);
  648. info.inputFlag = (buf[0] >> 1) & 0x1;
  649. info.delayTime = MKINT(buf[1], buf[2]);
  650. if(BitSet(buf[0], 0x1))
  651. info.transparent = buf[3];
  652. while(GetDataBlock(is, buf) != 0)
  653. ;
  654. }
  655. else if(c == 0xfe || c == 0x01)
  656. {
  657. int len = 0;
  658. int size = 256;
  659. char *text = NULL;
  660. /*
  661. ** Comment or Plain Text
  662. */
  663. cur = NEW(GIFData);
  664. if(c == 0x01)
  665. {
  666. (void) GetDataBlock(is, buf);
  667. cur->type = gif_text;
  668. cur->info = info;
  669. cur->x = MKINT(buf[0], buf[1]);
  670. cur->y = MKINT(buf[2], buf[3]);
  671. cur->width = MKINT(buf[4], buf[5]);
  672. cur->height = MKINT(buf[6], buf[7]);
  673. cur->data.text.cellWidth = buf[8];
  674. cur->data.text.cellHeight = buf[9];
  675. cur->data.text.fg = buf[10];
  676. cur->data.text.bg = buf[11];
  677. resetInfo = GIF_TRUE;
  678. }
  679. else
  680. {
  681. cur->type = gif_comment;
  682. }
  683. text = static_cast<char *>(malloc(size));
  684. while((n = GetDataBlock(is, buf)) != 0)
  685. {
  686. if(n + len >= size)
  687. {
  688. text = static_cast<char *>(realloc(text, size += 256));
  689. }
  690. memcpy(text + len, buf, n);
  691. len += n;
  692. }
  693. if(c == 0x01)
  694. {
  695. cur->data.text.len = len;
  696. cur->data.text.text = text;
  697. }
  698. else
  699. {
  700. cur->data.comment.len = len;
  701. cur->data.comment.text = text;
  702. }
  703. }
  704. else
  705. {
  706. /*
  707. ** Unrecogonized extension, consume it.
  708. */
  709. while(GetDataBlock(is, buf) > 0)
  710. ;
  711. }
  712. }
  713. else if(c == ',')
  714. {
  715. if(!ReadOK(is, buf, 9))
  716. {
  717. GIF_ERROR("couldn't read left/top/width/height");
  718. }
  719. cur = NEW(GIFData);
  720. cur->type = gif_image;
  721. cur->info = info;
  722. cur->x = MKINT(buf[0], buf[1]);
  723. cur->y = MKINT(buf[2], buf[3]);
  724. cur->width = MKINT(buf[4], buf[5]);
  725. cur->height = MKINT(buf[6], buf[7]);
  726. cur->data.image.cmapSize = 1 << ((buf[8] & 0x07) + 1);
  727. if(BitSet(buf[8], LOCALCOLORMAP))
  728. {
  729. if(readColorMap(is, cur->data.image.cmapSize,
  730. cur->data.image.cmapData))
  731. {
  732. GIF_ERROR("unable to get local colormap");
  733. }
  734. }
  735. else
  736. {
  737. cur->data.image.cmapSize = 0;
  738. }
  739. cur->data.image.data = static_cast<unsigned char *>(
  740. malloc(cur->width * cur->height));
  741. cur->data.image.interlaced = BitSet(buf[8], INTERLACE);
  742. readImage(is, BitSet(buf[8], INTERLACE), cur->width, cur->height,
  743. cur->data.image.data);
  744. resetInfo = GIF_TRUE;
  745. }
  746. else
  747. {
  748. FINFO(("Info loading gif: bogus character 0x%02x, ignoring",
  749. int(c)));
  750. }
  751. if(cur != NULL)
  752. {
  753. *end = cur;
  754. end = &cur->next;
  755. cur->next = NULL;
  756. }
  757. }
  758. if(c != ';')
  759. GIF_ERROR("EOF / data stream");
  760. out:
  761. return gifStream;
  762. }
  763. /* */
  764. static int GIFFreeData(GIFData *gifData)
  765. {
  766. int retCode = 0;
  767. if(gifData)
  768. {
  769. switch(gifData->type)
  770. {
  771. case gif_image:
  772. if(gifData->data.image.data)
  773. {
  774. free(gifData->data.image.data);
  775. }
  776. break;
  777. case gif_comment:
  778. if(gifData->data.comment.text)
  779. {
  780. free(gifData->data.comment.text);
  781. }
  782. break;
  783. case gif_text:
  784. if(gifData->data.text.text)
  785. {
  786. free(gifData->data.text.text);
  787. }
  788. break;
  789. }
  790. retCode = 1;
  791. }
  792. else
  793. retCode = 0;
  794. return retCode;
  795. }
  796. /* */
  797. static int GIFFree(GIFStream *gifStream)
  798. {
  799. int retCode = 1;
  800. GIFData *gifData, *gifNext;
  801. if(gifStream)
  802. {
  803. gifData = gifStream->data;
  804. while(gifData)
  805. {
  806. gifNext = gifData->next;
  807. GIFFreeData(gifData);
  808. free(gifData);
  809. gifData = gifNext;
  810. }
  811. }
  812. return retCode;
  813. }
  814. /* */
  815. static int readColorMap(std::istream &is, int size, unsigned char data[GIF_MAXCOLORS][3])
  816. {
  817. int i;
  818. unsigned char rgb[3 * GIF_MAXCOLORS];
  819. unsigned char *cp = rgb;
  820. if(!ReadOK(is, rgb, size * 3))
  821. return GIF_TRUE;
  822. for(i = 0; i < size; i++)
  823. {
  824. data[i][0] = *cp++;
  825. data[i][1] = *cp++;
  826. data[i][2] = *cp++;
  827. }
  828. return GIF_FALSE;
  829. }
  830. /*
  831. **
  832. */
  833. static int ZeroDataBlock = GIF_FALSE;
  834. /* */
  835. static int GetDataBlock(std::istream &is, unsigned char *buf)
  836. {
  837. unsigned char count;
  838. if(!ReadOK(is, &count, 1))
  839. {
  840. INFO_MSG(("error in getting DataBlock size"));
  841. return -1;
  842. }
  843. ZeroDataBlock = count == 0;
  844. if((count != 0) && (!ReadOK(is, buf, count)))
  845. {
  846. INFO_MSG(("error in reading DataBlock"));
  847. return -1;
  848. }
  849. return count;
  850. }
  851. /*
  852. **
  853. **
  854. */
  855. /*
  856. ** Pulled out of nextCode
  857. */
  858. static int curbit, lastbit, get_done, last_byte;
  859. static int return_clear;
  860. /*
  861. ** Out of nextLWZ
  862. */
  863. static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
  864. static int code_size, set_code_size;
  865. static int max_code, max_code_size;
  866. static int clear_code, end_code;
  867. /* */
  868. static void initLWZ(int input_code_size)
  869. {
  870. // static int inited = GIF_FALSE;
  871. set_code_size = input_code_size;
  872. code_size = set_code_size + 1;
  873. clear_code = 1 << set_code_size ;
  874. end_code = clear_code + 1;
  875. max_code_size = 2 * clear_code;
  876. max_code = clear_code + 2;
  877. curbit = lastbit = 0;
  878. last_byte = 2;
  879. get_done = GIF_FALSE;
  880. return_clear = GIF_TRUE;
  881. sp = stack;
  882. }
  883. /* */
  884. static int nextCode(std::istream &is, int code_size)
  885. {
  886. static unsigned char buf[280];
  887. static int maskTbl[16] =
  888. {
  889. 0x0000,
  890. 0x0001,
  891. 0x0003,
  892. 0x0007,
  893. 0x000f,
  894. 0x001f,
  895. 0x003f,
  896. 0x007f,
  897. 0x00ff,
  898. 0x01ff,
  899. 0x03ff,
  900. 0x07ff,
  901. 0x0fff,
  902. 0x1fff,
  903. 0x3fff,
  904. 0x7fff,
  905. };
  906. int i, j, ret, end;
  907. if(return_clear)
  908. {
  909. return_clear = GIF_FALSE;
  910. return clear_code;
  911. }
  912. end = curbit + code_size;
  913. if(end >= lastbit)
  914. {
  915. int count;
  916. if(get_done)
  917. {
  918. if(curbit >= lastbit)
  919. {
  920. GIF_ERROR("ran off the end of my bits");
  921. }
  922. return -1;
  923. }
  924. buf[0] = buf[last_byte - 2];
  925. buf[1] = buf[last_byte - 1];
  926. if((count = GetDataBlock(is, &buf[2])) == 0)
  927. get_done = GIF_TRUE;
  928. last_byte = 2 + count;
  929. curbit = (curbit - lastbit) + 16;
  930. lastbit = (2 + count) * 8;
  931. end = curbit + code_size;
  932. }
  933. j = end / 8;
  934. i = curbit / 8;
  935. if(i == j)
  936. ret = buf[i];
  937. else if(i + 1 == j)
  938. ret = buf[i] | (buf[i + 1] << 8);
  939. else
  940. {
  941. ret = buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16);
  942. }
  943. ret = (ret >> (curbit % 8)) & maskTbl[code_size];
  944. curbit += code_size;
  945. return ret;
  946. }
  947. #define readLWZ(fd) ((sp > stack) ? *--sp : nextLWZ(fd))
  948. /* */
  949. static int nextLWZ(std::istream &is)
  950. {
  951. static int table[2][(1 << MAX_LWZ_BITS)];
  952. static int firstcode, oldcode;
  953. int code, incode;
  954. register int i;
  955. while((code = nextCode(is, code_size)) >= 0)
  956. {
  957. if(code == clear_code)
  958. {
  959. for(i = 0; i < clear_code; ++i)
  960. {
  961. table[0][i] = 0;
  962. table[1][i] = i;
  963. }
  964. for(; i < (1 << MAX_LWZ_BITS); ++i)
  965. table[0][i] = table[1][i] = 0;
  966. code_size = set_code_size + 1;
  967. max_code_size = 2 * clear_code;
  968. max_code = clear_code + 2;
  969. sp = stack;
  970. do
  971. {
  972. firstcode = oldcode = nextCode(is, code_size);
  973. } while(firstcode == clear_code);
  974. return firstcode;
  975. }
  976. if(code == end_code)
  977. {
  978. int count;
  979. unsigned char buf[260];
  980. if(ZeroDataBlock)
  981. return -2;
  982. while((count = GetDataBlock(is, buf)) > 0)
  983. ;
  984. if(count != 0)
  985. {
  986. INFO_MSG(("missing EOD in data stream"));
  987. }
  988. return -2;
  989. }
  990. incode = code;
  991. if(code >= max_code)
  992. {
  993. *sp++ = firstcode;
  994. code = oldcode;
  995. }
  996. while(code >= clear_code)
  997. {
  998. *sp++ = table[1][code];
  999. if(code == table[0][code])
  1000. {
  1001. GIF_ERROR("circular table entry BIG ERROR");
  1002. }
  1003. code = table[0][code];
  1004. }
  1005. *sp++ = firstcode = table[1][code];
  1006. if((code = max_code) < (1 << MAX_LWZ_BITS))
  1007. {
  1008. table[0][code] = oldcode;
  1009. table[1][code] = firstcode;
  1010. ++max_code;
  1011. if((max_code >= max_code_size) &&
  1012. (max_code_size < (1 << MAX_LWZ_BITS)))
  1013. {
  1014. max_code_size *= 2;
  1015. ++code_size;
  1016. }
  1017. }
  1018. oldcode = incode;
  1019. if(sp > stack)
  1020. return *--sp;
  1021. }
  1022. return code;
  1023. }
  1024. /* */
  1025. static void readImage(std::istream &is, int interlace, int width, int height,
  1026. unsigned char *data)
  1027. {
  1028. unsigned char *dp, c;
  1029. int v, xpos = 0, ypos = 0;
  1030. // int pass = 0;
  1031. /*
  1032. ** Initialize the Compression routines
  1033. */
  1034. if(!ReadOK(is, &c, 1))
  1035. {
  1036. GIF_ERROR("EOF / read error on image data");
  1037. }
  1038. initLWZ(c);
  1039. if(verbose)
  1040. {
  1041. FINFO(("Info loading gif: reading %d by %d%s GIF image",
  1042. width, height, interlace ? " interlaced" : ""));
  1043. }
  1044. if(interlace)
  1045. {
  1046. int i;
  1047. int pass = 0, step = 8;
  1048. for(i = 0; i < height; i++)
  1049. {
  1050. dp = &data[width * ypos];
  1051. for(xpos = 0; xpos < width; xpos++)
  1052. {
  1053. if((v = readLWZ(is)) < 0)
  1054. goto fini;
  1055. *dp++ = v;
  1056. }
  1057. if((ypos += step) >= height)
  1058. {
  1059. do
  1060. {
  1061. if(pass++ > 0)
  1062. step /= 2;
  1063. ypos = step / 2;
  1064. } while(ypos > height);
  1065. }
  1066. }
  1067. }
  1068. else
  1069. {
  1070. dp = data;
  1071. for(ypos = 0; ypos < height; ypos++)
  1072. {
  1073. for(xpos = 0; xpos < width; xpos++)
  1074. {
  1075. if((v = readLWZ(is)) < 0)
  1076. goto fini;
  1077. *dp++ = v;
  1078. }
  1079. }
  1080. }
  1081. fini:
  1082. if(readLWZ(is) >= 0)
  1083. {
  1084. INFO_MSG(("too much input data, ignoring extra..."));
  1085. }
  1086. return;
  1087. }
  1088. //--- GIF-READ END ------------------------------------------------------
  1089. //--- GIF-WRITE START ---------------------------------------------------
  1090. /*
  1091. ** Copyright 1994, Home Pages, Inc.
  1092. **
  1093. ** Please read the file COPYRIGHT for specific information.
  1094. **
  1095. ** Home Pages, Inc.
  1096. ** 257 Castro St. Suite 219
  1097. ** Mountain View, CA 94041
  1098. **
  1099. ** Phone: 1 415 903 5353
  1100. ** Fax: 1 415 903 5345
  1101. **
  1102. ** EMail: support@homepages.com
  1103. **
  1104. */
  1105. /* +-------------------------------------------------------------------+ */
  1106. /* | Copyright 1993, David Koblas (koblas@netcom.com) | */
  1107. /* | | */
  1108. /* | Permission to use, copy, modify, and to distribute this software | */
  1109. /* | and its documentation for any purpose is hereby granted without | */
  1110. /* | fee, provided that the above copyright notice appear in all | */
  1111. /* | copies and that both that copyright notice and this permission | */
  1112. /* | notice appear in supporting documentation. There is no | */
  1113. /* | representations about the suitability of this software for | */
  1114. /* | any purpose. this software is provided "as is" without express | */
  1115. /* | or implied warranty. | */
  1116. /* | | */
  1117. /* +-------------------------------------------------------------------+ */
  1118. /* ppmtogif.c - read a portable pixmap and produce a GIF file
  1119. **
  1120. ** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A
  1121. ** Lempel-Zim compression based on "compress".
  1122. **
  1123. ** Copyright (C) 1989 by Jef Poskanzer.
  1124. **
  1125. ** Permission to use, copy, modify, and distribute this software and its
  1126. ** documentation for any purpose and without fee is hereby granted, provided
  1127. ** that the above copyright notice appear in all copies and that both that
  1128. ** copyright notice and this permission notice appear in supporting
  1129. ** documentation. This software is provided "as is" without express or
  1130. ** implied warranty.
  1131. **
  1132. ** The Graphics Interchange Format(c) is the Copyright property of
  1133. ** CompuServe Incorporated. GIF(sm) is a Service Mark property of
  1134. ** CompuServe Incorporated.
  1135. */
  1136. #define GIF_TRUE 1
  1137. #define GIF_FALSE 0
  1138. #define PUTBYTE(v, fp) putc(v, fp)
  1139. #define PUTWORD(v, fp) \
  1140. do \
  1141. { \
  1142. putc(((v) & 0xff), fp); \
  1143. putc((((v) >> 8) & 0xff), fp); \
  1144. } while(0)
  1145. /*
  1146. * a code_int must be able to hold 2**BITS values of type int, and also -1
  1147. */
  1148. typedef int code_int;
  1149. typedef long int count_int;
  1150. static void putImage(FILE *, int, int, int, int, unsigned char *);
  1151. static void putColorMap(FILE *, int, unsigned char[GIF_MAXCOLORS][3]);
  1152. static void putDataBlocks(FILE *fp, int, unsigned char *);
  1153. static void putGif89Info(FILE *, GIF89info *);
  1154. static void output(code_int code);
  1155. static void cl_block(void);
  1156. static void cl_hash(count_int hsize);
  1157. static void char_init(void);
  1158. static void char_out(int c);
  1159. static void flush_char(void);
  1160. /*
  1161. **
  1162. */
  1163. struct cval
  1164. {
  1165. int idx, cnt;
  1166. };
  1167. /* */
  1168. static int cvalCMP(struct cval *a, struct cval *b)
  1169. {
  1170. return b->cnt - a->cnt;
  1171. }
  1172. /* */
  1173. static int optimizeCMAP(GIFStream *stream)
  1174. {
  1175. GIFData *cur = 0, *img = 0;
  1176. int count = 0;
  1177. for(cur = stream->data; cur != NULL; cur = cur->next)
  1178. {
  1179. if(cur->type == gif_image)
  1180. {
  1181. img = cur;
  1182. count++;
  1183. }
  1184. }
  1185. /*
  1186. ** No images, no optimizations...
  1187. ** or too many images...
  1188. */
  1189. if(count == 0 || count > 1)
  1190. return 0;
  1191. /*
  1192. ** One image, nice and simple...
  1193. ** Insure there is a global colormap, and optimize the
  1194. ** image too it.
  1195. */
  1196. {
  1197. int size;
  1198. unsigned char *dp = img->data.image.data;
  1199. unsigned char *ep = dp + img->width * img->height;
  1200. struct cval vals[256];
  1201. int i;
  1202. // int j;
  1203. unsigned char tmap[256][3], rmap[256];
  1204. if((size = img->data.image.cmapSize) == 0)
  1205. size = stream->cmapSize;
  1206. for(i = 0; i < size; i++)
  1207. {
  1208. vals[i].idx = i;
  1209. vals[i].cnt = 0;
  1210. }
  1211. for(dp = img->data.image.data, i = 0; dp < ep; i++, dp++)
  1212. vals[*dp].cnt++;
  1213. /*
  1214. ** Quite, I'm doing a bubble sort... ACK!
  1215. */
  1216. qsort(vals, size, sizeof(vals[0]),
  1217. reinterpret_cast<int(*) (const void *, const void *)>(cvalCMP));
  1218. for(i = 0; i < size; i++)
  1219. if(vals[i].idx != i)
  1220. break;
  1221. /*
  1222. ** Already sorted, no change!
  1223. */
  1224. if(i == size)
  1225. return 1;
  1226. for(i = 0; i < size; i++)
  1227. rmap[vals[i].idx] = i;
  1228. /*
  1229. ** Now reorder the colormap, and the image
  1230. */
  1231. for(dp = img->data.image.data, i = 0; dp < ep; i++, dp++)
  1232. *dp = rmap[*dp];
  1233. if(img->info.transparent != -1)
  1234. {
  1235. img->info.transparent = rmap[img->info.transparent];
  1236. }
  1237. /*
  1238. ** Toast the local colormap
  1239. */
  1240. if(img->data.image.cmapSize != 0)
  1241. {
  1242. for(i = 0; i < size; i++)
  1243. {
  1244. stream->cmapData[i][0] = img->data.image.cmapData[i][0];
  1245. stream->cmapData[i][1] = img->data.image.cmapData[i][1];
  1246. stream->cmapData[i][2] = img->data.image.cmapData[i][2];
  1247. }
  1248. img->data.image.cmapSize = 0;
  1249. stream->cmapSize = size;
  1250. }
  1251. /*
  1252. ** Now finally reorer the colormap
  1253. */
  1254. for(i = 0; i < size; i++)
  1255. {
  1256. tmap[i][0] = stream->cmapData[i][0];
  1257. tmap[i][1] = stream->cmapData[i][1];
  1258. tmap[i][2] = stream->cmapData[i][2];
  1259. }
  1260. for(i = 0; i < size; i++)
  1261. {
  1262. stream->cmapData[rmap[i]][0] = tmap[i][0];
  1263. stream->cmapData[rmap[i]][1] = tmap[i][1];
  1264. stream->cmapData[rmap[i]][2] = tmap[i][2];
  1265. }
  1266. }
  1267. return 1;
  1268. }
  1269. /*
  1270. ** Return the ceiling log of n
  1271. */
  1272. static int binaryLog(int val)
  1273. {
  1274. int i;
  1275. if(val == 0)
  1276. return 0;
  1277. for(i = 1; i <= 8; i++)
  1278. if(val <= (1 << i))
  1279. return i;
  1280. return 8;
  1281. }
  1282. #ifdef __sgi
  1283. #pragma set woff 1209
  1284. #endif
  1285. /* */
  1286. static int GIFWriteFP(FILE *fp, GIFStream *stream, int optimize)
  1287. {
  1288. GIFData *cur;
  1289. int flag = GIF_FALSE;
  1290. int c;
  1291. int globalBitsPP = 0;
  1292. int resolution;
  1293. if(fp == NULL)
  1294. return GIF_TRUE;
  1295. if(stream == NULL)
  1296. return GIF_FALSE;
  1297. /*
  1298. ** First find if this is a 87A or an 89A GIF image
  1299. ** also, figure out the color resolution of the image.
  1300. */
  1301. resolution = binaryLog(stream->cmapSize) - 1;
  1302. for(cur = stream->data; !flag && cur != NULL; cur = cur->next)
  1303. {
  1304. if(cur->type == gif_text || cur->type == gif_comment)
  1305. {
  1306. flag = GIF_TRUE;
  1307. }
  1308. else if(cur->type == gif_image)
  1309. {
  1310. int v = binaryLog(cur->data.image.cmapSize);
  1311. if(v > resolution)
  1312. resolution = v;
  1313. /*
  1314. ** Uses one of the 89 extensions.
  1315. */
  1316. if(cur->info.transparent != -1 ||
  1317. cur->info.delayTime != 0 ||
  1318. cur->info.inputFlag != 0 ||
  1319. cur->info.disposal != 0)
  1320. flag = GIF_TRUE;
  1321. }
  1322. }
  1323. /*
  1324. **
  1325. */
  1326. if(optimize)
  1327. optimize = optimizeCMAP(stream);
  1328. fwrite(flag ? "GIF89a" : "GIF87a", 1, 6, fp);
  1329. PUTWORD(stream->width, fp);
  1330. PUTWORD(stream->height, fp);
  1331. /*
  1332. ** assume 256 entry color resution, and non sorted colormap
  1333. */
  1334. c = ((resolution & 0x07) << 5) | 0x00;
  1335. if(stream->cmapSize != 0)
  1336. {
  1337. globalBitsPP = binaryLog(stream->cmapSize);
  1338. c |= 0x80;
  1339. c |= globalBitsPP - 1;
  1340. }
  1341. /*
  1342. ** Is the global colormap optimized?
  1343. */
  1344. if(optimize)
  1345. c |= 0x08;
  1346. PUTBYTE(c, fp);
  1347. PUTBYTE(stream->background, fp);
  1348. PUTBYTE(stream->aspectRatio, fp);
  1349. putColorMap(fp, stream->cmapSize, stream->cmapData);
  1350. for(cur = stream->data; cur != NULL; cur = cur->next)
  1351. {
  1352. if(cur->type == gif_image)
  1353. {
  1354. int bpp;
  1355. putGif89Info(fp, &cur->info);
  1356. PUTBYTE(0x2c, fp);
  1357. PUTWORD(cur->x, fp);
  1358. PUTWORD(cur->y, fp);
  1359. PUTWORD(cur->width, fp);
  1360. PUTWORD(cur->height, fp);
  1361. c = cur->data.image.interlaced ? 0x40 : 0x00;
  1362. if(cur->data.image.cmapSize != 0)
  1363. {
  1364. bpp = binaryLog(cur->data.image.cmapSize);
  1365. c |= 0x80;
  1366. c |= bpp;
  1367. }
  1368. else
  1369. {
  1370. bpp = globalBitsPP;
  1371. }
  1372. PUTBYTE(c, fp);
  1373. putColorMap(fp, cur->data.image.cmapSize, cur->data.image.cmapData);
  1374. putImage(fp, cur->data.image.interlaced, bpp, cur->width,
  1375. cur->height, cur->data.image.data);
  1376. }
  1377. else if(cur->type == gif_comment)
  1378. {
  1379. PUTBYTE('!', fp);
  1380. PUTBYTE(0xfe, fp);
  1381. putDataBlocks(
  1382. fp,
  1383. cur->data.comment.len,
  1384. reinterpret_cast<unsigned char *>(cur->data.comment.text));
  1385. }
  1386. else if(cur->type == gif_text)
  1387. {
  1388. putGif89Info(fp, &cur->info);
  1389. PUTBYTE('!', fp);
  1390. PUTBYTE(0x01, fp);
  1391. PUTWORD(cur->x, fp);
  1392. PUTWORD(cur->y, fp);
  1393. PUTWORD(cur->width, fp);
  1394. PUTWORD(cur->height, fp);
  1395. PUTBYTE(cur->data.text.cellWidth, fp);
  1396. PUTBYTE(cur->data.text.cellHeight, fp);
  1397. PUTBYTE(cur->data.text.fg, fp);
  1398. PUTBYTE(cur->data.text.bg, fp);
  1399. putDataBlocks(
  1400. fp,
  1401. cur->data.text.len,
  1402. reinterpret_cast<unsigned char *>(cur->data.text.text));
  1403. }
  1404. }
  1405. /*
  1406. ** Write termination
  1407. */
  1408. PUTBYTE(';', fp);
  1409. return GIF_FALSE;
  1410. }
  1411. #ifdef __sgi
  1412. #pragma reset woff 1209
  1413. #endif
  1414. /* */
  1415. int GIFWrite(char *file, GIFStream *stream, int optimize)
  1416. {
  1417. if(stream != NULL)
  1418. {
  1419. FILE *fp = fopen(file, "wb");
  1420. if(fp != NULL)
  1421. {
  1422. int s = GIFWriteFP(fp, stream, optimize);
  1423. fclose(fp);
  1424. return s;
  1425. }
  1426. }
  1427. return GIF_TRUE;
  1428. }
  1429. /* */
  1430. static void putColorMap(FILE *fp, int size, unsigned char data[GIF_MAXCOLORS][3])
  1431. {
  1432. int i;
  1433. for(i = 0; i < size; i++)
  1434. {
  1435. PUTBYTE(data[i][0], fp);
  1436. PUTBYTE(data[i][1], fp);
  1437. PUTBYTE(data[i][2], fp);
  1438. }
  1439. }
  1440. /* */
  1441. static void putDataBlocks(FILE *fp, int size, unsigned char *data)
  1442. {
  1443. int n;
  1444. while(size > 0)
  1445. {
  1446. n = size > 255 ? 255 : size;
  1447. PUTBYTE(n, fp);
  1448. fwrite(data, 1, n, fp);
  1449. data += n;
  1450. size -= n;
  1451. }
  1452. PUTBYTE(0, fp); /* End Block */
  1453. }
  1454. #ifdef __sgi
  1455. #pragma set woff 1209
  1456. #endif
  1457. /* */
  1458. static void putGi

Large files files are truncated, but you can click here to view the full file