Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

695 lines
19 KiB

  1. //---------------------------------------------------------------
  2. // File: pcximage.cpp
  3. //
  4. // Image manipulation functions for PCX format images.
  5. //---------------------------------------------------------------
  6. #include "stdafx.h"
  7. #include "global.h"
  8. #include "pbrush.h"
  9. #include "pbrusfrm.h"
  10. #include "pbrusvw.h"
  11. #include "minifwnd.h"
  12. #include "bmobject.h"
  13. #include "imgsuprt.h"
  14. #include "imgwnd.h"
  15. #include "imgbrush.h"
  16. #include "imgwell.h"
  17. #include "imgtools.h"
  18. #include "toolbox.h"
  19. #include "imgfile.h"
  20. #include "imgcolor.h"
  21. #include "undo.h"
  22. #include "props.h"
  23. #include "ferr.h"
  24. #include "ctype.h"
  25. #include "cmpmsg.h"
  26. #define COLORMAPLENGTH 48
  27. #define FILLERLENGTH 58
  28. #ifdef PCX_SUPPORT
  29. struct PCXHeader
  30. {
  31. unsigned char manufacturer;
  32. unsigned char version;
  33. unsigned char encoding;
  34. unsigned char bits_per_pixel_per_plane;
  35. short xmin;
  36. short ymin;
  37. short xmax;
  38. short ymax;
  39. unsigned short hresolution;
  40. unsigned short vresolution;
  41. unsigned char colormap[COLORMAPLENGTH];
  42. unsigned char reserved;
  43. unsigned char nplanes;
  44. unsigned short bytes_per_line;
  45. short palette_info;
  46. unsigned char filler[FILLERLENGTH]; // Header is 128 bytes
  47. };
  48. #endif
  49. class CFileBuffer : public CObject
  50. {
  51. DECLARE_DYNCREATE( CFileBuffer )
  52. public:
  53. enum Type
  54. {
  55. READ,
  56. WRITE
  57. };
  58. CFileBuffer();
  59. ~CFileBuffer();
  60. BOOL Create( CFile* pfile, Type IO );
  61. short Get ( void );
  62. BOOL Put ( BYTE cByte );
  63. long Seek ( long lOff, UINT nFrom );
  64. BOOL Flush ( void );
  65. private:
  66. void Fill ( void );
  67. enum { MAX_BUFFER = 2048 };
  68. CFile* m_pFile;
  69. int m_iBuffPos;
  70. int m_iBuffSize;
  71. BYTE* m_pBuffer;
  72. };
  73. IMPLEMENT_DYNCREATE( CFileBuffer, CObject )
  74. #include "memtrace.h"
  75. /****************************************************************************/
  76. CFileBuffer::CFileBuffer() : CObject()
  77. {
  78. m_pFile = 0;
  79. m_iBuffPos = 0;
  80. m_iBuffSize = 0;
  81. m_pBuffer = 0;
  82. }
  83. /****************************************************************************/
  84. CFileBuffer::~CFileBuffer()
  85. {
  86. if (m_pBuffer)
  87. delete [] m_pBuffer;
  88. }
  89. /****************************************************************************/
  90. BOOL CFileBuffer::Create( CFile* pfile, Type IO )
  91. {
  92. ASSERT( pfile != NULL );
  93. if (pfile == NULL)
  94. return FALSE;
  95. m_pFile = pfile;
  96. m_pBuffer = new BYTE[MAX_BUFFER];
  97. if (! m_pBuffer)
  98. {
  99. theApp.SetMemoryEmergency();
  100. return FALSE;
  101. }
  102. if (IO == READ)
  103. {
  104. Fill();
  105. if (! m_iBuffSize)
  106. {
  107. theApp.SetFileError( IDS_ERROR_READLOAD, ferrIllformedFile );
  108. return FALSE;
  109. }
  110. }
  111. return TRUE;
  112. }
  113. /****************************************************************************/
  114. short CFileBuffer::Get( void )
  115. {
  116. if (! m_iBuffSize)
  117. return EOF;
  118. short sByte = (short)(unsigned short)m_pBuffer[m_iBuffPos++];
  119. if (m_iBuffPos == m_iBuffSize)
  120. Fill();
  121. return sByte;
  122. }
  123. /****************************************************************************/
  124. BOOL CFileBuffer::Put( BYTE cByte )
  125. {
  126. m_pBuffer[m_iBuffSize++] = cByte;
  127. if (m_iBuffSize == MAX_BUFFER)
  128. return Flush();
  129. return TRUE;
  130. }
  131. /****************************************************************************/
  132. long CFileBuffer::Seek( long lOff, UINT nFrom )
  133. {
  134. long lPos = m_pFile->Seek( lOff, nFrom );
  135. Fill();
  136. return lPos;
  137. }
  138. /****************************************************************************/
  139. void CFileBuffer::Fill()
  140. {
  141. m_iBuffSize = m_pFile->Read( m_pBuffer, MAX_BUFFER );
  142. m_iBuffPos = 0;
  143. }
  144. /****************************************************************************/
  145. BOOL CFileBuffer::Flush( void )
  146. {
  147. TRY {
  148. m_pFile->Write( m_pBuffer, m_iBuffSize );
  149. }
  150. CATCH( CFileException, ex )
  151. {
  152. m_pFile->Abort();
  153. theApp.SetFileError( IDS_ERROR_SAVE, ex->m_cause );
  154. return FALSE;
  155. }
  156. END_CATCH
  157. m_iBuffSize = 0;
  158. return TRUE;
  159. }
  160. /****************************************************************************/
  161. /****************************************************************************/
  162. #ifdef PCX_SUPPORT
  163. BOOL CBitmapObj::ReadPCX( CFile* pfile )
  164. {
  165. if (! pfile->GetLength())
  166. {
  167. if (m_hThing)
  168. Free();
  169. m_bDirty = TRUE;
  170. return TRUE;
  171. }
  172. // if a PCX extension try to load this as a PCX image.
  173. PCXHeader hdr;
  174. PBITMAP p_dib; // Device independent bitmap
  175. short bytes_per_line;
  176. pfile->Read( (unsigned char*)&hdr, sizeof( PCXHeader ) );
  177. // Check if image file format is acceptable
  178. if (hdr.manufacturer != 0x0a)
  179. {
  180. theApp.SetFileError( IDS_ERROR_READLOAD, ferrCantDetermineType );
  181. return FALSE;
  182. }
  183. // We only handle 1, 4, 8, or 24-bit images
  184. short bits_per_pixel = hdr.nplanes * hdr.bits_per_pixel_per_plane;
  185. if (bits_per_pixel != 1
  186. && bits_per_pixel != 4
  187. && bits_per_pixel != 8
  188. && bits_per_pixel != 24)
  189. {
  190. theApp.SetFileError( IDS_ERROR_READLOAD, ferrCantDetermineType );
  191. return FALSE;
  192. }
  193. short image_width = hdr.xmax - hdr.xmin + 1;
  194. short image_height = hdr.ymax - hdr.ymin + 1;
  195. // Allocate space where the PCX image will be unpacked.
  196. long pcx_image_size = (long) hdr.nplanes *
  197. (long) image_height *
  198. (long) hdr.bytes_per_line;
  199. BYTE* image = (BYTE*) new BYTE[pcx_image_size];
  200. if (image == NULL)
  201. {
  202. theApp.SetMemoryEmergency();
  203. return FALSE;
  204. }
  205. // Read in PCX image into this area.
  206. CFileBuffer FileBuffer;
  207. if (! FileBuffer.Create( pfile, CFileBuffer::READ ))
  208. {
  209. delete [] image;
  210. return FALSE;
  211. }
  212. // Decode run-length encoded image data
  213. short i;
  214. short byte;
  215. short count;
  216. long pos = 0L;
  217. while ((byte = FileBuffer.Get()) != EOF)
  218. {
  219. if ((byte & 0xc0) == 0xc0)
  220. {
  221. count = byte & 0x3f;
  222. if ((byte = FileBuffer.Get()) != EOF)
  223. {
  224. for (i = 0; i < count; i++)
  225. {
  226. if (pos >= pcx_image_size)
  227. break;
  228. image[pos] = (CHAR)byte;
  229. pos++;
  230. }
  231. }
  232. }
  233. else
  234. {
  235. if (pos >= pcx_image_size)
  236. break;
  237. image[pos] = (CHAR)byte;
  238. pos++;
  239. }
  240. }
  241. // Allocate memory for the device independent bitmap (DIB)
  242. // Note that the number of bytes in each line of a DIB image
  243. // must be a multiple of 4.
  244. short bytes_per_line_per_plane = (image_width *
  245. hdr.bits_per_pixel_per_plane + 7) / 8;
  246. short actual_bytes_per_line = (image_width *
  247. hdr.nplanes *
  248. hdr.bits_per_pixel_per_plane + 7) / 8;
  249. bytes_per_line = actual_bytes_per_line;
  250. if ( bytes_per_line % 4)
  251. bytes_per_line = 4 * ( bytes_per_line / 4 + 1);
  252. // Make room for a palette
  253. short palettesize = 16;
  254. if (bits_per_pixel == 1)
  255. palettesize = 2;
  256. if (hdr.version >= 5
  257. && bits_per_pixel > 4)
  258. {
  259. // Go back 769 bytes from the end of the file
  260. FileBuffer.Seek( -769, CFile::end );
  261. if (FileBuffer.Get() == 12)
  262. {
  263. // There is a 256-color palette following this byte
  264. palettesize = 256;
  265. }
  266. }
  267. // If image has more than 256 colors then there is no palette
  268. if (bits_per_pixel > 8)
  269. palettesize = 0;
  270. // Allocate space for the bitmap
  271. if (m_hThing)
  272. Free();
  273. m_lMemSize = sizeof( BITMAPINFOHEADER ) + palettesize * sizeof( RGBQUAD )
  274. + (long)bytes_per_line * (long)image_height;
  275. if (! Alloc())
  276. return FALSE;
  277. p_dib = (PBITMAP) GlobalLock(m_hThing);
  278. // Set up bitmap info header
  279. LPBITMAPINFOHEADER p_bminfo = (LPBITMAPINFOHEADER)p_dib;
  280. p_bminfo->biSize = sizeof(BITMAPINFOHEADER);
  281. p_bminfo->biWidth = image_width;
  282. p_bminfo->biHeight = image_height;
  283. p_bminfo->biPlanes = 1;
  284. p_bminfo->biBitCount = hdr.bits_per_pixel_per_plane * hdr.nplanes;
  285. p_bminfo->biCompression = BI_RGB;
  286. p_bminfo->biSizeImage = (long)image_height * (long) bytes_per_line;
  287. p_bminfo->biXPelsPerMeter = (long)hdr.hresolution;
  288. p_bminfo->biYPelsPerMeter = (long)hdr.vresolution;
  289. p_bminfo->biClrUsed = 0;
  290. p_bminfo->biClrImportant = 0;
  291. // Set up the color palette
  292. if (palettesize > 0)
  293. {
  294. //***** RGBQUAD *palette = (RGBQUAD*) ((LPSTR)imdata->p_dib
  295. LPRGBQUAD palette = LPRGBQUAD((LPSTR)p_dib + sizeof(BITMAPINFOHEADER));
  296. short palindex;
  297. for (palindex = 0; palindex < palettesize; palindex++)
  298. {
  299. if (palettesize == 256)
  300. {
  301. // Read palette from file
  302. palette[palindex].rgbRed = (BYTE)FileBuffer.Get();
  303. palette[palindex].rgbGreen = (BYTE)FileBuffer.Get();
  304. palette[palindex].rgbBlue = (BYTE)FileBuffer.Get();
  305. palette[palindex].rgbReserved = 0;
  306. }
  307. if (palettesize == 16)
  308. {
  309. // 16-color palette from PCX header
  310. palette[palindex].rgbRed = (BYTE)hdr.colormap[3*palindex];
  311. palette[palindex].rgbGreen = (BYTE)hdr.colormap[3*palindex+1];
  312. palette[palindex].rgbBlue = (BYTE)hdr.colormap[3*palindex+2];
  313. palette[palindex].rgbReserved = 0;
  314. }
  315. if (palettesize == 2)
  316. {
  317. // Set up palette for black and white images
  318. palette[palindex].rgbRed = palindex * 255;
  319. palette[palindex].rgbGreen = palindex * 255;
  320. palette[palindex].rgbBlue = palindex * 255;
  321. palette[palindex].rgbReserved = 0;
  322. }
  323. }
  324. }
  325. // Load image data into the DIB. Note the DIB image must be
  326. // stored "bottom to top" line order. That's why we position
  327. // data at the end of the array so that the image can be
  328. // stored backwards--from the last line to the first.
  329. BYTE* data = (BYTE*)p_dib + ((long)sizeof( BITMAPINFOHEADER )
  330. + palettesize * sizeof( RGBQUAD )
  331. + (image_height - 1) * bytes_per_line);
  332. // Define a macro to access bytes in the PCX image according
  333. // to specified line and plane index.
  334. short lineindex, byteindex, planeindex;
  335. #define bytepos(lineindex, planeindex, byteindex) \
  336. ((long)(lineindex)*(long)hdr.bytes_per_line* \
  337. (long)hdr.nplanes + \
  338. (long)(planeindex)*(long)hdr.bytes_per_line + \
  339. (long)(byteindex))
  340. // Construct packed pixels out of decoded PCX image.
  341. short loc;
  342. unsigned short onebyte;
  343. unsigned short bits_copied;
  344. unsigned short few_bits;
  345. unsigned short k;
  346. unsigned short bbpb = 8/hdr.bits_per_pixel_per_plane;
  347. // Build a mask to pick out bits from each byte of the PCX image
  348. unsigned short himask = 0x80, mask;
  349. if (hdr.bits_per_pixel_per_plane > 1)
  350. for (i = 0; i < hdr.bits_per_pixel_per_plane - 1;
  351. i++) himask = 0x80 | (himask >> 1);
  352. for (lineindex = 0; lineindex < image_height;
  353. lineindex++, data -= bytes_per_line)
  354. {
  355. if (actual_bytes_per_line < bytes_per_line)
  356. for (loc = actual_bytes_per_line; loc < bytes_per_line; loc++)
  357. data[loc] = 0;
  358. loc = 0;
  359. onebyte = 0;
  360. bits_copied = 0;
  361. for (byteindex = 0; byteindex < bytes_per_line_per_plane; byteindex++)
  362. {
  363. for (k = 0, mask = himask; k < bbpb; k++,
  364. mask >>= hdr.bits_per_pixel_per_plane)
  365. {
  366. // Go through all scan line for all planes and copy bits into
  367. // the data array
  368. for (planeindex = 0; planeindex < hdr.nplanes; planeindex++)
  369. {
  370. few_bits = image[bytepos(lineindex,
  371. planeindex, byteindex)] & mask;
  372. // Shift the selected bits to the most significant position
  373. if (k > 0)
  374. few_bits <<= (k*hdr.bits_per_pixel_per_plane);
  375. // OR the bits with current pixel after shifting them right
  376. if (bits_copied > 0)
  377. few_bits >>= bits_copied;
  378. onebyte |= few_bits;
  379. bits_copied += hdr.bits_per_pixel_per_plane;
  380. if (bits_copied >= 8)
  381. {
  382. data[loc] = (UCHAR)onebyte;
  383. loc++;
  384. bits_copied = 0;
  385. onebyte = 0;
  386. }
  387. }
  388. }
  389. }
  390. }
  391. // Success!
  392. delete [] (BYTE*)image;
  393. GlobalUnlock(m_hThing);
  394. return TRUE;
  395. }
  396. /****************************************************************************/
  397. #define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4)
  398. BOOL CBitmapObj::WritePCX( CFile* pfile )
  399. {
  400. if (m_pImg == NULL)
  401. {
  402. // The image has not been loaded, so we'll just copy the
  403. // original out to the file...
  404. ASSERT( m_hThing );
  405. if (! m_hThing)
  406. return FALSE;
  407. }
  408. else
  409. {
  410. // The image has been loaded and may have been edited, so
  411. // we'll convert it back to a dib to save...
  412. if (! m_hThing)
  413. SaveResource( FALSE );
  414. if (! m_hThing)
  415. return FALSE;
  416. }
  417. // build pcx file from the DIB
  418. PBITMAP p_dib = (PBITMAP)GlobalLock(m_hThing); // Device independent bitmap
  419. PCXHeader hdr; // PCX bitmap header
  420. LPBITMAPINFOHEADER p_bminfo = (LPBITMAPINFOHEADER)p_dib; // Set up bitmap info header
  421. short palettesize = DIBNumColors( (LPSTR)p_dib); // Get palette size
  422. hdr.manufacturer = 10;
  423. // hdr.version = (char)((hPalette || (GetDeviceCaps(fileDC, RASTERCAPS) & RC_PALETTE)) ? 5 : 3);
  424. hdr.version = (CHAR)( palettesize ? 5 : 3);
  425. hdr.encoding = 1;
  426. hdr.xmin = hdr.ymin = 0;
  427. hdr.xmax = p_bminfo->biWidth - 1;
  428. hdr.ymax = p_bminfo->biHeight- 1;
  429. // hdr.hresolution = theApp.ScreenDeviceInfo.iWidthinPels;
  430. // hdr.vresolution = theApp.ScreenDeviceInfo.iHeightinPels;
  431. hdr.hresolution = (WORD)p_bminfo->biXPelsPerMeter;
  432. hdr.vresolution = (WORD)p_bminfo->biYPelsPerMeter;
  433. hdr.reserved = 0;
  434. hdr.nplanes = (BYTE)p_bminfo->biPlanes; //biPlanes should always be 1
  435. hdr.palette_info = (BYTE)p_dib->bmWidthBytes;
  436. hdr.bits_per_pixel_per_plane = (CHAR) p_bminfo->biBitCount;
  437. hdr.bytes_per_line = WIDTHBYTES( (LONG) (p_bminfo->biBitCount * p_bminfo->biWidth) );
  438. // Clean up filler
  439. for (int index = FILLERLENGTH; index--; )
  440. hdr.filler[index] ='\0';
  441. // If there are at most 16 colors place them in header
  442. LPRGBQUAD palette = LPRGBQUAD((LPSTR)p_dib + sizeof(BITMAPINFOHEADER));
  443. LPSTR lpDst = (LPSTR)hdr.colormap;
  444. // Clean up colormap
  445. for (index = COLORMAPLENGTH; index--; )
  446. lpDst[index] ='\0';
  447. if (palettesize <= 16)
  448. for (index = palettesize; index--; )
  449. {
  450. *lpDst++ = palette->rgbRed; /* swap RED and BLUE components */
  451. *lpDst++ = palette->rgbGreen;
  452. *lpDst++ = palette->rgbBlue;
  453. palette++;
  454. }
  455. pfile->Write( (unsigned char*)&hdr, sizeof( PCXHeader ) );
  456. // Now pack the image
  457. // Load image data from the DIB. Note the DIB image is
  458. // stored "bottom to top" line order. That's why we position
  459. // data at the end of the array so that the image can be
  460. // stored backwards--from the last line to the first.
  461. CFileBuffer FileBuffer;
  462. if (! FileBuffer.Create( pfile, CFileBuffer::WRITE ))
  463. {
  464. GlobalUnlock(m_hThing);
  465. return FALSE;
  466. }
  467. // find the start of the bitmap data then go to the end of the data
  468. // the PCX is stored in reverse order of the DIB
  469. int TopofData = sizeof( BITMAPINFOHEADER ) + palettesize * sizeof( RGBQUAD );
  470. BYTE* data = (BYTE*)p_dib + TopofData + hdr.bytes_per_line * (p_bminfo->biHeight );
  471. for (index = p_bminfo->biHeight; index--; )
  472. {
  473. data -= hdr.bytes_per_line;
  474. if (! PackBuff( &FileBuffer, data, hdr.bytes_per_line )) //convert to run length encoding.
  475. {
  476. GlobalUnlock(m_hThing);
  477. return FALSE;
  478. }
  479. }
  480. if (palettesize == 256) // Write palette to file
  481. {
  482. if (! FileBuffer.Put( 12 )) // Tag number for palette information
  483. {
  484. GlobalUnlock(m_hThing);
  485. return FALSE;
  486. }
  487. for (index = 0; index < palettesize; index++)
  488. {
  489. if (! FileBuffer.Put( palette[index].rgbRed )
  490. || ! FileBuffer.Put( palette[index].rgbGreen )
  491. || ! FileBuffer.Put( palette[index].rgbBlue ))
  492. {
  493. GlobalUnlock(m_hThing);
  494. return FALSE;
  495. }
  496. }
  497. }
  498. GlobalUnlock(m_hThing);
  499. return FileBuffer.Flush();
  500. }
  501. #endif //PCX_SUPPORT
  502. /****************************************************************************/
  503. /* run length encoding equates */
  504. #define MINcount 2
  505. #define MAXcount 63
  506. #define ESCbits 0xC0
  507. #define BUFFER_SIZE 1024
  508. /* bitmaps are ordered <b, g, r, i> but PCX is ordered <r, g, b, i> ... */
  509. BOOL CBitmapObj::PackBuff(CFileBuffer *FileBuffer, BYTE *PtrDib, int byteWidth )
  510. {
  511. BYTE runChar;
  512. BYTE runCount;
  513. BYTE* endPtr = PtrDib + byteWidth;
  514. for (runCount = 1, runChar = *PtrDib++; PtrDib <= endPtr; ++PtrDib)
  515. {
  516. if (PtrDib != endPtr && *PtrDib == runChar && runCount < MAXcount)
  517. ++runCount;
  518. else
  519. if (*PtrDib != runChar
  520. && runCount < MINcount
  521. && (runChar & ESCbits) != ESCbits)
  522. {
  523. while (runCount--)
  524. if (! FileBuffer->Put( runChar ))
  525. return FALSE;
  526. runCount = 1;
  527. runChar = *PtrDib;
  528. }
  529. else
  530. {
  531. runCount |= ESCbits;
  532. if (! FileBuffer->Put( runCount )
  533. || ! FileBuffer->Put( runChar ))
  534. return FALSE;
  535. runCount = 1;
  536. runChar = *PtrDib;
  537. }
  538. }
  539. return TRUE;
  540. }
  541. /****************************************************************************/