Advertisement
Guest User

Bmp.cpp

a guest
Sep 25th, 2013
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 27.15 KB | None | 0 0
  1. // Bmp.cpp
  2. // =======
  3. // BMP image loader
  4. // It reads only 8/24/32-bit uncompressed and 8-bit RLE compression format.
  5. //
  6. // 2006-10-17: Improved flipImage()
  7. // 2006-10-10: Added getError() to return the last error message.
  8. // 2006-10-07: Fixed handling paddings if the width is not divisible by 4.
  9. // 2006-09-25: Added 8-bit grayscale read and save (it is indexed mode).
  10. //
  11. // AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
  12. // CREATED: 2006-05-08
  13. // UPDATED: 2006-10-17
  14. ///////////////////////////////////////////////////////////////////////////////
  15.  
  16. #include <fstream>
  17. #include <iostream>
  18. #include <cstring> // for memcpy()
  19. #include "Bmp.h"
  20. using std::ifstream;
  21. using std::ofstream;
  22. using std::ios;
  23. using std::cout;
  24. using std::endl;
  25. using namespace Image;
  26.  
  27.  
  28.  
  29. ///////////////////////////////////////////////////////////////////////////////
  30. // default constructor
  31. ///////////////////////////////////////////////////////////////////////////////
  32. Bmp::Bmp() : width(0), height(0), bitCount(0), dataSize(0), data(0), dataRGB(0),
  33. errorMessage("No error.")
  34. {
  35. }
  36.  
  37.  
  38.  
  39. ///////////////////////////////////////////////////////////////////////////////
  40. // copy constructor
  41. // We need DEEP COPY for dynamic memory variables because the compiler inserts
  42. // default copy constructor automatically for you, BUT it is only SHALLOW COPY
  43. ///////////////////////////////////////////////////////////////////////////////
  44. Bmp::Bmp(const Bmp &rhs)
  45. {
  46. // copy member variables from right-hand-side object
  47. width = rhs.getWidth();
  48. height = rhs.getHeight();
  49. bitCount = rhs.getBitCount();
  50. dataSize = rhs.getDataSize();
  51. errorMessage = rhs.getError();
  52.  
  53. if(rhs.getData()) // allocate memory only if the pointer is not NULL
  54. {
  55. data = new unsigned char[dataSize];
  56. memcpy(data, rhs.getData(), dataSize); // deep copy
  57. }
  58. else
  59. data = 0; // array is not allocated yet, set to 0
  60.  
  61. if(rhs.getDataRGB()) // allocate memory only if the pointer is not NULL
  62. {
  63. dataRGB = new unsigned char[dataSize];
  64. memcpy(dataRGB, rhs.getDataRGB(), dataSize); // deep copy
  65. }
  66. else
  67. dataRGB = 0; // array is not allocated yet, set to 0
  68. }
  69.  
  70.  
  71.  
  72. ///////////////////////////////////////////////////////////////////////////////
  73. // default destructor
  74. ///////////////////////////////////////////////////////////////////////////////
  75. Bmp::~Bmp()
  76. {
  77. // deallocate data array
  78. delete [] data;
  79. data = 0;
  80. //delete [] dataRGBA;
  81. delete [] dataRGB;
  82.  
  83. dataRGB = 0;
  84. }
  85.  
  86.  
  87.  
  88. ///////////////////////////////////////////////////////////////////////////////
  89. // override assignment operator
  90. ///////////////////////////////////////////////////////////////////////////////
  91. Bmp& Bmp::operator=(const Bmp &rhs)
  92. {
  93. if(this == &rhs) // avoid self-assignment (A = A)
  94. return *this;
  95.  
  96. // copy member variables
  97. width = rhs.getWidth();
  98. height = rhs.getHeight();
  99. bitCount = rhs.getBitCount();
  100. dataSize = rhs.getDataSize();
  101. errorMessage = rhs.getError();
  102.  
  103. if(rhs.getData()) // allocate memory only if the pointer is not NULL
  104. {
  105. data = new unsigned char[dataSize];
  106. memcpy(data, rhs.getData(), dataSize);
  107. }
  108. else
  109. data = 0;
  110.  
  111. if(rhs.getDataRGB()) // allocate memory only if the pointer is not NULL
  112. {
  113. dataRGB = new unsigned char[dataSize];
  114. memcpy(dataRGB, rhs.getDataRGB(), dataSize);
  115. }
  116. else
  117. dataRGB = 0;
  118.  
  119. return *this;
  120. }
  121.  
  122.  
  123.  
  124. ///////////////////////////////////////////////////////////////////////////////
  125. // clear out the exsiting values
  126. ///////////////////////////////////////////////////////////////////////////////
  127. void Bmp::init()
  128. {
  129. width = height = bitCount = dataSize = 0;
  130. errorMessage = "No error.";
  131.  
  132. delete [] data;
  133. data = 0;
  134. delete [] dataRGB;
  135. dataRGB = 0;
  136. }
  137.  
  138.  
  139.  
  140. ///////////////////////////////////////////////////////////////////////////////
  141. // print itself for debug
  142. ///////////////////////////////////////////////////////////////////////////////
  143. void Bmp::printSelf() const
  144. {
  145. cout << "===== Bmp =====\n"
  146. << "Width: " << width << " pixels\n"
  147. << "Height: " << height << " pixels\n"
  148. << "Bit Count: " << bitCount << " bits\n"
  149. << "Data Size: " << dataSize << " bytes\n"
  150. << endl;
  151. }
  152.  
  153. ///////////////////////////////////////////////////////////////////////////////
  154. // read a BMP image header infos and datafile and load as a RGBA file
  155. ///////////////////////////////////////////////////////////////////////////////
  156. bool Bmp::read(const char* fileName, bool alpha)
  157. {
  158. Bmp::read(fileName);
  159.  
  160.  
  161. if (alpha)
  162. {
  163.  
  164. dataRGBA = new unsigned char [dataSize+(dataSize/3)]; // allocate extra memory space for RGBA.
  165.  
  166. int i, j;
  167. int dataSize = width * height * bitCount / 8;
  168.  
  169. for( i = 0, j = 0; i < dataSize; i += 3, j += 4 )
  170. {
  171.  
  172. int g_keyColor[3] = { 0, 255, 0 }; // Pure green is the color key to blend out
  173.  
  174. // Does the current pixel match the selected color key?
  175. if( dataRGB[i] == g_keyColor[0] &&
  176. dataRGB[i+1] == g_keyColor[1] &&
  177. dataRGB[i+2] == g_keyColor[2] )
  178. {
  179. dataRGBA[j+3] = 0; // If so, set alpha to fully transparent.
  180. }
  181. else
  182. {
  183. dataRGBA[j+3] = 255; // If not, set alpha to fully opaque.
  184. }
  185.  
  186. dataRGBA[j] = dataRGB[i];
  187. dataRGBA[j+1] = dataRGB[i+1];
  188. dataRGBA[j+2] = dataRGB[i+2];
  189.  
  190. }
  191.  
  192. //delete [] dataRGB; //we do not need RGB anymore.
  193. }
  194.  
  195. return true;
  196. }
  197.  
  198.  
  199. ///////////////////////////////////////////////////////////////////////////////
  200. // read a BMP image header infos and datafile and load
  201. ///////////////////////////////////////////////////////////////////////////////
  202. bool Bmp::read(const char* fileName)
  203. {
  204. this->init(); // clear out all values
  205.  
  206. // check NULL pointer
  207. if(!fileName)
  208. {
  209. errorMessage = "File name is not defined (NULL pointer).";
  210. return false;
  211. }
  212.  
  213. // open a BMP file as binary mode
  214. ifstream inFile;
  215. inFile.open(fileName, ios::binary); // binary mode
  216. if(!inFile.good())
  217. {
  218. errorMessage = "Failed to open a BMP file to read.";
  219. return false; // exit if failed
  220. }
  221.  
  222. // list of entries in BMP header
  223. char id[2]; // magic identifier "BM" (2 bytes)
  224. int fileSize; // file size in bytes (4)
  225. short reserved1; // reserved 1 (2)
  226. short reserved2; // reserved 2 (2)
  227. int dataOffset; // starting offset of bitmap data (4)
  228. int infoHeaderSize; // info header size (4)
  229. int width; // image width (4)
  230. int height; // image height (4)
  231. short planeCount; // # of planes (2)
  232. short bitCount; // # of bits per pixel (2)
  233. int compression; // compression mode (4)
  234. int dataSizeWithPaddings; // bitmap data size with paddings in bytes (4)
  235. //int xResolution; // horizontal pixels per metre (4)
  236. //int yResolution; // vertical pixels per metre (4)
  237. //int colorCount; // # of colours used (4)
  238. //int importantColorCount;// # of important colours (4)
  239.  
  240. // read BMP header infos
  241. inFile.read(id, 2); // should be "BM"
  242. inFile.read((char*)&fileSize, 4); // should be same as file size
  243. inFile.read((char*)&reserved1, 2); // should be 0
  244. inFile.read((char*)&reserved2, 2); // should be 0
  245. inFile.read((char*)&dataOffset, 4);
  246. inFile.read((char*)&infoHeaderSize, 4); // should be 40
  247. inFile.read((char*)&width, 4);
  248. inFile.read((char*)&height, 4);
  249. inFile.read((char*)&planeCount, 2); // should be 1
  250. inFile.read((char*)&bitCount, 2); // 1, 4, 8, 24, or 32
  251. inFile.read((char*)&compression, 4); // 0(uncompressed), 1(8-bit RLE), 2(4-bit RLE), 3(RGB with mask)
  252. inFile.read((char*)&dataSizeWithPaddings, 4);
  253. //inFile.read((char*)&xResolution, 4);
  254. //inFile.read((char*)&yResolution, 4);
  255. //inFile.read((char*)&colorCount, 4);
  256. //inFile.read((char*)&importantColorCount, 4);
  257.  
  258. // check magic ID, "BM"
  259. if(id[0] != 'B' && id[1] != 'M')
  260. {
  261. // it is not BMP file, close the opened file and exit
  262. inFile.close();
  263. errorMessage = "Magic ID is invalid.";
  264. return false;
  265. }
  266.  
  267. // it supports only 8-bit grayscale, 24-bit BGR or 32-bit BGRA
  268. if(bitCount < 8)
  269. {
  270. inFile.close();
  271. errorMessage = "Unsupported format.";
  272. return false;
  273. }
  274.  
  275. // it supports only uncompressed and 8-bit RLE compressed format
  276. if(compression > 1)
  277. {
  278. inFile.close();
  279. errorMessage = "Unsupported compression mode.";
  280. return false;
  281. }
  282.  
  283. // do not trust the file size in header, recalculate it
  284. inFile.seekg(0, ios::end);
  285. fileSize = inFile.tellg();
  286.  
  287. // compute the number of paddings
  288. // In BMP, each scanline must be divisible evenly by 4.
  289. // If not divisible by 4, then each line adds
  290. // extra paddings. So it can be divided evenly by 4.
  291. int paddings = (4 - ((width * bitCount / 8) % 4)) % 4;
  292.  
  293. // compute data size without paddings
  294. int dataSize = width * height * bitCount / 8;
  295.  
  296. // recompute data size with paddings (do not trust the data size in header)
  297. dataSizeWithPaddings = fileSize - dataOffset; // it maybe greater than "dataSize+(height*paddings)" because 4-byte boundary for file size
  298.  
  299. // now it is ready to store info and image data
  300. this->width = width;
  301. this->height = height;
  302. this->bitCount = bitCount;
  303. this->dataSize = dataSize;
  304.  
  305.  
  306. // allocate data arrays
  307. // add extra bytes for paddings if width is not divisible by 4
  308. data = new unsigned char [dataSizeWithPaddings];
  309. dataRGB = new unsigned char [dataSize];
  310.  
  311. /*@@ we don't use palette for 8-bit indexed grayscale mode. Instead, we use the index value as the intensity of the pixel.
  312. // for loading palette
  313. unsigned char* palette = 0; // for palette for indexed mode
  314. int paletteSize = 0;
  315.  
  316. // if bit count is 8 (256 grayscale), then it uses palette (indexed mode)
  317. // build palette lookup table = (4 * colorCount) bytes
  318. if(bitCount == 8)
  319. {
  320. // count palette size
  321. // palette is placed between BMP header and data
  322. paletteSize = dataOffset - 54; // BMP header size is 54 bytes total
  323.  
  324. // allocate palette array
  325. palette = new unsigned char[paletteSize];
  326.  
  327. // get number of colors used
  328. int colorCount = paletteSize / 4; // each palette has 4 entries(B,G,R,A)
  329.  
  330. // copy palette data
  331. inFile.seekg(54, ios::beg); // palette starts right after BMP header block (54 bytes)
  332. inFile.read((char*)palette, paletteSize);
  333. }
  334. */
  335.  
  336. if(compression == 0) // uncompressed
  337. {
  338. inFile.seekg(dataOffset, ios::beg); // move cursor to the starting position of data
  339. inFile.read((char*)data, dataSizeWithPaddings);
  340. }
  341. else if(compression == 1) // 8-bit RLE(Run Length Encode) compressed
  342. {
  343. // get length of encoded data
  344. int size = fileSize - dataOffset;
  345.  
  346. // allocate tmp array to store the encoded data
  347. unsigned char *encData = new unsigned char[size];
  348.  
  349. // read data from file
  350. inFile.seekg(dataOffset, ios::beg);
  351. inFile.read((char*)encData, size);
  352.  
  353. // decode RLE into image data buffer
  354. decodeRLE8(encData, data);
  355.  
  356. // deallocate encoded data buffer after decoding
  357. delete [] encData;
  358. }
  359.  
  360. // close it after reading
  361. inFile.close();
  362.  
  363. // we don't need paddings, trim paddings from each line
  364. // Note that there is no padding in RLE compressed data
  365. if(compression == 0 && paddings > 0)
  366. {
  367. int lineWidth = width * bitCount / 8;
  368.  
  369. // copy line by line
  370. for(int i = 1; i < height; ++i)
  371. {
  372. memcpy(&data[i*lineWidth], &data[i*(lineWidth+paddings)], lineWidth);
  373. }
  374. }
  375.  
  376. // BMP is bottom-to-top orientation by default, flip image vertically
  377. // But if the height is negative value, then it is top-to-bottom orientation.
  378. if(height > 0)
  379. flipImage(data, width, height, bitCount/8);
  380.  
  381. // the colour components order of BMP image is BGR
  382. // convert image data to RGB order for convenience
  383. memcpy(dataRGB, data, dataSize); // copy data to dataRGB first
  384. if(bitCount == 24 || bitCount == 32)
  385. swapRedBlue(dataRGB, dataSize, bitCount/8);
  386.  
  387. return true;
  388. }
  389.  
  390.  
  391. ///////////////////////////////////////////////////////////////////////////////
  392. // returns either R, G, B of a given x-y position
  393. ///////////////////////////////////////////////////////////////////////////////
  394. int Bmp::getRGBXY(int RGB, int x, int y)
  395. {
  396. int i = (getWidth() * (y * 3)) + (x * 3);
  397.  
  398. if (RGB == 1) { return dataRGB[i+0]; }
  399. if (RGB == 2) { return dataRGB[i+1]; }
  400. if (RGB == 3) { return dataRGB[i+2]; }
  401. return NULL;
  402. }
  403.  
  404. ///////////////////////////////////////////////////////////////////////////////
  405. // set the RGB information at pixel XYZ. Does not modify the file,
  406. // only what is loaded in memory
  407. ///////////////////////////////////////////////////////////////////////////////
  408. void Bmp::setRGBXY(int R, int G, int B, int x, int y)
  409. {
  410. int i = (getWidth() * (y * 3)) + (x * 3);
  411. dataRGB[i+0] = R;
  412. dataRGB[i+1] = G;
  413. dataRGB[i+2] = B;
  414. }
  415.  
  416. void Bmp::makeRGBA()
  417. {
  418. dataRGBA = new unsigned char [dataSize+(dataSize/3)]; // allocate extra memory space for RGBA.
  419. int i, j;
  420. int dataSize = width * height * bitCount / 8;
  421.  
  422. for( i = 0, j = 0; i < dataSize; i += 3, j += 4 )
  423. {
  424.  
  425. int g_keyColor[3] = { 0, 255, 0 }; // 0 255 0 is the color key to blend out
  426.  
  427. // Does the current pixel match the selected color key?
  428. if( dataRGB[i] == g_keyColor[0] &&
  429. dataRGB[i+1] == g_keyColor[1] &&
  430. dataRGB[i+2] == g_keyColor[2] )
  431. {
  432. dataRGBA[j+3] = 0; // If so, set alpha to fully transparent.
  433. }
  434. else
  435. {
  436. dataRGBA[j+3] = 255; // If not, set alpha to fully opaque.
  437. }
  438.  
  439. dataRGBA[j] = dataRGB[i];
  440. dataRGBA[j+1] = dataRGB[i+1];
  441. dataRGBA[j+2] = dataRGB[i+2];
  442.  
  443. }
  444.  
  445. delete [] dataRGB; //we do not need RGB anymore.
  446.  
  447. }
  448.  
  449.  
  450. ///////////////////////////////////////////////////////////////////////////////
  451. // save an image as an uncompressed BMP format
  452. // We assume the source image is RGB order, so it must be converted BGR order.
  453. ///////////////////////////////////////////////////////////////////////////////
  454. bool Bmp::save(const char* fileName, int w, int h, int channelCount, const unsigned char* data)
  455. {
  456. // reset error message
  457. errorMessage = "No error.";
  458.  
  459. if(!fileName || !data)
  460. {
  461. errorMessage = "File name is not specified (NULL pointer).";
  462. return false;
  463. }
  464.  
  465. if(w == 0 || h == 0)
  466. {
  467. errorMessage = "Zero width or height.";
  468. return false;
  469. }
  470.  
  471. // list of entries in BMP header
  472. char id[2]; // magic identifier "BM" (2 bytes)
  473. int fileSize; // file size in bytes (4)
  474. short reserved1; // reserved 1 (2)
  475. short reserved2; // reserved 2 (2)
  476. int dataOffset; // starting offset of bitmap data (4)
  477. int infoHeaderSize; // info header size (4)
  478. int width; // image width (4)
  479. int height; // image height (4)
  480. short planeCount; // # of planes (2)
  481. short bitCount; // # of bits per pixel (2)
  482. int compression; // compression mode (4)
  483. int dataSizeWithPaddings; // bitmap data size in bytes with padding (4)
  484. int xResolution; // horizontal pixels per metre (4)
  485. int yResolution; // vertical pixels per metre (4)
  486. int colorCount; // # of colours used (4)
  487. int importantColorCount;// # of important colours (4)
  488.  
  489. int paletteSize; // size of palette block in bytes
  490.  
  491. // compute paddings per each line
  492. // In BMP, each scanline must be divisible evenly by 4
  493. // If not, add extra paddings in each line, it can be divisible by 4.
  494. int paddings = (4 - ((w * channelCount) % 4)) % 4;
  495.  
  496. // compute data size without paddings
  497. int dataSize = w * h * channelCount;
  498.  
  499. // fill vars for BMP header infos
  500. id[0] = 'B';
  501. id[1] = 'M';
  502. reserved1 = reserved2 = 0;
  503. width = w;
  504. height = h;
  505. planeCount = 1;
  506. bitCount = channelCount * 8;
  507. compression = 0;
  508. dataSizeWithPaddings = dataSize + (h * paddings);
  509. xResolution = yResolution = 2835; // 72 pixels/inch = 2835 pixels/m
  510. colorCount = 0;
  511. importantColorCount = 0;
  512. infoHeaderSize = 40; // should be 40 bytes
  513. dataOffset = 54; // fileHeader(14) + infoHeader(40)
  514. fileSize = dataSizeWithPaddings + dataOffset;
  515.  
  516. // 8-bit grayscale image need palette
  517. // correct colorCount, dataOffset and fileSize
  518. if(channelCount == 1)
  519. {
  520. colorCount = 256; // always use max number of colors for 8-bit gray scale
  521. paletteSize = colorCount * 4; // BGRA for each
  522. dataOffset = 54 + paletteSize; // add up palette size
  523. fileSize = dataSizeWithPaddings + dataOffset; // reset file size
  524. }
  525.  
  526. // allocate output data array
  527. unsigned char* tmpData = new unsigned char [dataSize];
  528.  
  529. // copy image data
  530. memcpy(tmpData, data, dataSize);
  531.  
  532. // flip the image upside down
  533. // If height is negative, then it is bottom-top orientation (no need to flip)
  534. if(height > 0)
  535. flipImage(tmpData, width, height, channelCount);
  536.  
  537. // convert RGB to BGR order
  538. if(channelCount == 3 || channelCount == 4)
  539. swapRedBlue(tmpData, dataSize, channelCount);
  540.  
  541. // add paddings(0s) if the width of image is not divisible by 4
  542. unsigned char* dataWithPaddings = 0;
  543. if(paddings > 0)
  544. {
  545. // allocate an array
  546. // add extra bytes for paddings in case the width is not divisible by 4
  547. dataWithPaddings = new unsigned char [dataSizeWithPaddings];
  548.  
  549. int lineWidth = width * channelCount; // line width in bytes
  550.  
  551. // copy single line at a time
  552. for(int i = 0; i < height; ++i)
  553. {
  554. // restore data by adding paddings
  555. memcpy(&dataWithPaddings[i*(lineWidth+paddings)], &tmpData[i*lineWidth], lineWidth);
  556.  
  557. // insert 0s for paddings after copying the current line
  558. for(int j = 1; j <= paddings; ++j)
  559. dataWithPaddings[(i+1)*(lineWidth+paddings) - j] = (unsigned char)0;
  560. }
  561. }
  562.  
  563. // open output file to write data
  564. ofstream outFile;
  565. outFile.open(fileName, ios::binary);
  566. if(!outFile.good())
  567. {
  568. errorMessage = "Failed to open an optput file.";
  569. return false; // exit if failed
  570. }
  571.  
  572. // write header
  573. outFile.put(id[0]);
  574. outFile.put(id[1]);
  575. outFile.write((char*)&fileSize, 4);
  576. outFile.write((char*)&reserved1, 2);
  577. outFile.write((char*)&reserved2, 2);
  578. outFile.write((char*)&dataOffset, 4);
  579. outFile.write((char*)&infoHeaderSize, 4);
  580. outFile.write((char*)&width, 4);
  581. outFile.write((char*)&height, 4);
  582. outFile.write((char*)&planeCount, 2);
  583. outFile.write((char*)&bitCount, 2);
  584. outFile.write((char*)&compression, 4);
  585. outFile.write((char*)&dataSizeWithPaddings, 4);
  586. outFile.write((char*)&xResolution, 4);
  587. outFile.write((char*)&yResolution, 4);
  588. outFile.write((char*)&colorCount, 4);
  589. outFile.write((char*)&importantColorCount, 4);
  590.  
  591. // For 8-bit grayscale, insert palette between header block and data block
  592. if(bitCount == 8)
  593. {
  594. unsigned char* palette = new unsigned char[paletteSize]; // each entry has 4 bytes(B,G,R,A)
  595. buildGrayScalePalette(palette, paletteSize);
  596.  
  597. // write palette to the file
  598. outFile.write((char*)palette, paletteSize);
  599. delete [] palette;
  600. }
  601.  
  602. // write image data
  603. if(paddings == 0)
  604. outFile.write((char*)tmpData, dataSize); // without padding
  605. else
  606. outFile.write((char*)dataWithPaddings, dataSizeWithPaddings); // with paddings
  607.  
  608. // close the opened file
  609. outFile.close();
  610.  
  611. // deallocate tmp buffer
  612. delete [] tmpData;
  613. delete [] dataWithPaddings;
  614.  
  615. return true;
  616. }
  617.  
  618.  
  619.  
  620. // static shared functions ****************************************************
  621.  
  622. ///////////////////////////////////////////////////////////////////////////////
  623. // decode 8-bit RLE data into uncompressed data
  624. // This routine need 2 pointers: the pointer to the encoded input data and
  625. // the pointer to the decoded output data. The length of input array is not
  626. // necessary because the last 2 bytes of input data must be 00 and 01, which
  627. // tells the end of data. So it can stop decoding process.
  628. //
  629. // BMP uses 2-value RLE scheme: the first value contains a count of the number
  630. // of pixels in the run, and the second value contains the value of the pixel
  631. // repeated. For example, 0x3 0xFF means 0xFF 0xFF 0xFF.
  632. //
  633. // If the first value is 0x00, then it is unencoded run mode and a pixel is not
  634. // repeated any more. In unencode run mode, the second value is the the number
  635. // of unencoded pixel values that follow. If the number of pixels is odd, then
  636. // a 0x00 padding value also follows.
  637. // 1st 2nd EncodedValue DecodedValue
  638. // === === ============ ============
  639. // 00 03 FF FE FD 00 FF FE FD
  640. // 00 04 11 12 13 14 11 12 13 14
  641. //
  642. // The second value of unencoded run mode must be greater than and equal to 3.
  643. // If the second value is less than 3, then it specifies special positioning
  644. // operations and does not decode any data themselves.
  645. // 1st 2nd Meaning
  646. // === === ==============================================
  647. // 00 00 End of Scanline, Decode new data at the next line
  648. // 00 01 End of Bitmap data, Stop decoding data here
  649. // 00 02 Delta Offset, Move the cursor hori and vert direction
  650. //
  651. // Delta Offset operation requires 4-byte in size: the first and second should
  652. // be 00 and 02, and the third byte is the number of pixels forward in the
  653. // same scanline and the fourth byte is the number of rows to move. For
  654. // example, 00 02 03 04 means move the cursor 3 pixels right, and 4 pixels
  655. // upward. (Note that BMP is bottom-to-top orientation.)
  656. ///////////////////////////////////////////////////////////////////////////////
  657. bool Bmp::decodeRLE8(const unsigned char *encData, unsigned char *outData)
  658. {
  659. // check NULL pointer
  660. if(!encData || !outData)
  661. return false;
  662.  
  663. unsigned char first, second;
  664. int i;
  665. bool stop = false;
  666.  
  667. // start decoding, stop when it reaches at the end of decoded data
  668. while(!stop)
  669. {
  670. // grab 2 bytes at the current position
  671. first = *encData++;
  672. second = *encData++;
  673.  
  674. if(first) // encoded run mode
  675. {
  676. for(i=0; i < first; ++i)
  677. *outData++ = second;
  678. }
  679. else
  680. {
  681. if(second == 1) // reached the end of bitmap
  682. stop = true; // must stop decoding
  683.  
  684. else if(second == 2) // delta mark
  685. encData += 2; // do nothing, but move the cursor 2 more bytes
  686.  
  687. else // unencoded run mode (second >= 3)
  688. {
  689. for(i=0; i < second; ++i)
  690. *outData++ = *encData++;
  691.  
  692. if(second % 2) // if it is odd number, then there is a padding 0. ignore it
  693. encData++;
  694. }
  695. }
  696. }
  697.  
  698. return true;
  699. }
  700.  
  701.  
  702.  
  703. ///////////////////////////////////////////////////////////////////////////////
  704. // BMP is bottom-to-top orientation. Flip the image vertically, so the image
  705. // can be rendered from top to bottom orientation
  706. ///////////////////////////////////////////////////////////////////////////////
  707. void Bmp::flipImage(unsigned char *data, int width, int height, int channelCount)
  708. {
  709. if(!data) return;
  710.  
  711. int lineSize = width * channelCount;
  712. unsigned char* tmp = new unsigned char [lineSize];
  713. int half = height / 2;
  714.  
  715. int line1 = 0; // first line
  716. int line2 = (height - 1) * lineSize; // last line
  717.  
  718. // scan only half of height
  719. for(int i = 0; i < half; ++i)
  720. {
  721. // copy line by line
  722. memcpy(tmp, &data[line1], lineSize);
  723. memcpy(&data[line1], &data[line2], lineSize);
  724. memcpy(&data[line2], tmp, lineSize);
  725.  
  726. // move to next line
  727. line1 += lineSize;
  728. line2 -= lineSize;
  729. }
  730.  
  731. // deallocate temp arrays
  732. delete [] tmp;
  733. }
  734.  
  735.  
  736.  
  737. ///////////////////////////////////////////////////////////////////////////////
  738. // swap the position of the 1st and 3rd color components (RGB <-> BGR)
  739. ///////////////////////////////////////////////////////////////////////////////
  740. void Bmp::swapRedBlue(unsigned char *data, int dataSize, int channelCount)
  741. {
  742. if(!data) return;
  743. if(channelCount < 3) return; // must be 3 or 4
  744. if(dataSize % channelCount) return; // must be divisible by the number of channels
  745.  
  746. unsigned char tmp;
  747. int i;
  748.  
  749. // swap the position of red and blue components
  750. for(i=0; i < dataSize; i+=channelCount)
  751. {
  752. tmp = data[i];
  753. data[i] = data[i+2];
  754. data[i+2] = tmp;
  755. }
  756. }
  757.  
  758.  
  759.  
  760. ///////////////////////////////////////////////////////////////////////////////
  761. // compute the number of used colors in the 8-bit grayscale image
  762. ///////////////////////////////////////////////////////////////////////////////
  763. int Bmp::getColorCount(const unsigned char* data, int dataSize)
  764. {
  765. if(!data) return 0;
  766.  
  767. const int MAX_COLOR = 256; // max number of colors in 8-bit grayscale
  768. int i;
  769. int colorCount = 0;
  770. unsigned int colors[MAX_COLOR];
  771.  
  772. // clear all to 0s
  773. memset((void*)colors, 0, MAX_COLOR);
  774.  
  775. // increment at the same index
  776. for(i = 0; i < dataSize; ++i)
  777. colors[data[i]]++;
  778.  
  779. // count backward the number of color used in this data
  780. colorCount = MAX_COLOR;
  781. for(i = 0; i < MAX_COLOR; ++i)
  782. {
  783. if(colors[i] == 0)
  784. colorCount--;
  785. }
  786.  
  787. return colorCount;
  788. }
  789.  
  790.  
  791.  
  792. ///////////////////////////////////////////////////////////////////////////////
  793. // build palette for 8-bit grayscale image
  794. // Each component(B,G,R,A) of palette will have the same value as data value
  795. // because it is grayscale.
  796. ///////////////////////////////////////////////////////////////////////////////
  797. void Bmp::buildGrayScalePalette(unsigned char* palette, int paletteSize)
  798. {
  799. if(!palette) return;
  800.  
  801. // fill B, G, R, with same value and A is 0
  802. int i, j;
  803. for(i = 0, j = 0; i < paletteSize; i+=4, j++)
  804. {
  805. palette[i] = palette[i+1] = palette[i+2] = (unsigned char)j;
  806. palette[i+3] = (unsigned char)0;
  807. }
  808. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement