bmp.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <fstream>
  4. #include <cmath>
  5. #include "bmp.h"
  6. using std::ofstream;
  7. using std::sort;
  8. using std::cout;
  9. using std::endl;
  10. using std::swap;
  11. using std::ios;
  12. // ----------------------------------------------------------------
  13. // Local Function Prototypes
  14. // ----------------------------------------------------------------
  15. static void doWrite(ofstream &out, int value);
  16. static void doWrite(ofstream &out, short value);
  17. static void doWrite(ofstream &out, const Color &theColor);
  18. // ----------------------------------------------------------------
  19. // Class: Bmp
  20. // ----------------------------------------------------------------
  21. Bmp::Bmp(int width, int height) : mWidth(width), mHeight(height)
  22. {
  23. // Initialize the entire bitmap to the default color (white)
  24. for(int i = 0; i < mHeight; i ++)
  25. {
  26. ColorRow row;
  27. for(int j = 0; j < mWidth; j ++)
  28. row.push_back(Color());
  29. mImage.push_back(row);
  30. }
  31. }
  32. // ----------------------------------------------------------------
  33. // Draw a pixel at (x, y) with color: (red, green, blue)
  34. // ----------------------------------------------------------------
  35. void Bmp::setPixel(
  36. int x,
  37. int y,
  38. unsigned char red,
  39. unsigned char green,
  40. unsigned char blue
  41. )
  42. {
  43. bool doSet = true;
  44. if(x < 0 || x >= mWidth)
  45. {
  46. cout << "Invalid value: " << x <<
  47. " (expected: 0 <= x < " << mWidth << ")" << endl;
  48. doSet = false;
  49. }
  50. if(y < 0 || y >= mHeight)
  51. {
  52. cout << "Invalid value: " << y <<
  53. " (expected: 0 <= y < " << mHeight << ")" << endl;
  54. doSet = false;
  55. }
  56. if(doSet)
  57. mImage[y][x] = Color(red, green, blue);
  58. }
  59. // ----------------------------------------------------------------
  60. // Draw a pixel at (x, y) with color: theColor
  61. // ----------------------------------------------------------------
  62. void Bmp::setPixel(
  63. int x,
  64. int y,
  65. const Color &theColor
  66. )
  67. {
  68. setPixel(x, y, theColor.mRed, theColor.mGreen, theColor.mBlue);
  69. }
  70. // ----------------------------------------------------------------
  71. // Draw a rectangle at (x, y) with the specified width and
  72. // height using color: (red, green, blue)
  73. // ----------------------------------------------------------------
  74. void Bmp::drawRectangle(
  75. int x,
  76. int y,
  77. int width,
  78. int height,
  79. unsigned char red,
  80. unsigned char green,
  81. unsigned char blue
  82. )
  83. {
  84. drawLine(x, y, x + width, y, red, green, blue);
  85. drawLine(x + width, y, x + width, y + height, red, green, blue);
  86. drawLine(x + width, y + height, x, y + height, red, green, blue);
  87. drawLine(x, y + height, x, y, red, green, blue);
  88. }
  89. // ----------------------------------------------------------------
  90. // Draw a rectangle at (x, y) with the specified width and
  91. // height using color: theColor
  92. // ----------------------------------------------------------------
  93. void Bmp::drawRectangle(
  94. int x,
  95. int y,
  96. int width,
  97. int height,
  98. const Color &theColor
  99. )
  100. {
  101. drawRectangle(
  102. x, y, width, height,
  103. theColor.mRed, theColor.mGreen, theColor.mBlue
  104. );
  105. }
  106. // ----------------------------------------------------------------
  107. // Fill a rectangle at (x, y) with the specified width and
  108. // height using color: (red, green, blue)
  109. // ----------------------------------------------------------------
  110. void Bmp::fillRectangle(
  111. int x,
  112. int y,
  113. int width,
  114. int height,
  115. unsigned char red,
  116. unsigned char green,
  117. unsigned char blue
  118. )
  119. {
  120. for(int i = 0; i < width; i ++)
  121. {
  122. drawLine(
  123. x + i, y, x + i, y + height, red, green, blue
  124. );
  125. }
  126. }
  127. // ----------------------------------------------------------------
  128. // Fill a rectangle at (x, y) with the specified width and
  129. // height using color: theColor
  130. // ----------------------------------------------------------------
  131. void Bmp::fillRectangle(
  132. int x,
  133. int y,
  134. int width,
  135. int height,
  136. const Color &theColor
  137. )
  138. {
  139. fillRectangle(
  140. x, y, width, height,
  141. theColor.mRed, theColor.mGreen, theColor.mBlue
  142. );
  143. }
  144. // ----------------------------------------------------------------
  145. // Fill a polygon using color: (red, green, blue),
  146. // expects 'points' to contain an even number of values
  147. // ----------------------------------------------------------------
  148. void Bmp::fillPolygon(
  149. const vector<int> &points,
  150. unsigned char red,
  151. unsigned char green,
  152. unsigned char blue
  153. )
  154. {
  155. int xMin = 0;
  156. int yMin = 0;
  157. int xMax = 0;
  158. int yMax = 0;
  159. for(int i = 0, n = points.size(); i < n; i += 2)
  160. {
  161. int x = points[i];
  162. int y = points[i + 1];
  163. if(i == 0)
  164. {
  165. xMin = xMax = x;
  166. yMin = yMax = y;
  167. }
  168. else
  169. {
  170. if(x < xMin) xMin = x;
  171. else if(x > xMax) xMax = x;
  172. if(y < yMin) yMin = y;
  173. else if(y > yMax) yMax = y;
  174. }
  175. }
  176. typedef vector<int> YValues;
  177. typedef vector<YValues> Buffer;
  178. int width = (xMax - xMin) + 1;
  179. Buffer theBuffer;
  180. for(int i = 0; i < width; i ++)
  181. theBuffer.push_back(YValues());
  182. for(int i = 2, n = points.size(); i < n; i += 2)
  183. {
  184. int x0 = points[i - 2];
  185. int y0 = points[i - 1];
  186. int x1 = points[i];
  187. int y1 = points[i + 1];
  188. if(x0 == x1)
  189. {
  190. theBuffer[x0 - xMin].push_back(y0);
  191. theBuffer[x0 - xMin].push_back(y1);
  192. }
  193. else
  194. {
  195. double delta = (y1 - y0) / (double)(x1 - x0);
  196. int start = 0;
  197. int stop = 0;
  198. double y = 0.0;
  199. if(x0 < x1)
  200. {
  201. y = y0;
  202. start = x0;
  203. stop = x1;
  204. }
  205. else
  206. {
  207. y = y1;
  208. start = x1;
  209. stop = x0;
  210. }
  211. for(int j = start; j < stop; j ++)
  212. {
  213. int yValue = (int)(y + 0.5);
  214. theBuffer[j - xMin].push_back(yValue);
  215. y += delta;
  216. }
  217. }
  218. }
  219. for(int i = 0, n = theBuffer.size(); i < n; i ++)
  220. sort(theBuffer[i].begin(), theBuffer[i].end());
  221. for(int i = 0, n = theBuffer.size(); i < n; i ++)
  222. {
  223. for(int j = 1, nn = theBuffer[i].size(); j < nn; j += 2)
  224. {
  225. drawLine(
  226. xMin + i,
  227. theBuffer[i][j],
  228. xMin + i,
  229. theBuffer[i][j - 1],
  230. red, green, blue
  231. );
  232. }
  233. }
  234. }
  235. // ----------------------------------------------------------------
  236. // Fill a polygon using color: theColor
  237. // ----------------------------------------------------------------
  238. void Bmp::fillPolygon(const vector<int> &points, const Color &theColor)
  239. {
  240. fillPolygon(points, theColor.mRed, theColor.mGreen, theColor.mBlue);
  241. }
  242. // ----------------------------------------------------------------
  243. // Draw a line from (x0, y0) to (x1, y1) using
  244. // color: (red, green, blue)
  245. // ----------------------------------------------------------------
  246. void Bmp::drawLine(
  247. int x0, int y0,
  248. int x1, int y1,
  249. unsigned char red,
  250. unsigned char green,
  251. unsigned char blue
  252. )
  253. {
  254. int xDelta = (x1 - x0);
  255. int yDelta = (y1 - y0);
  256. if(xDelta == 0)
  257. {
  258. // A vertical line
  259. if(y0 > y1)
  260. swap(y0, y1);
  261. for(int y = y0; y <= y1; y ++)
  262. setPixel(x0, y, red, green, blue);
  263. }
  264. else if(yDelta == 0)
  265. {
  266. // A horizontal line
  267. if(x0 > x1)
  268. swap(x0, x1);
  269. for(int x = x0; x <= x1; x ++)
  270. setPixel(x, y0, red, green, blue);
  271. }
  272. else
  273. {
  274. setPixel(x0, y0, red, green, blue);
  275. int xStep = (xDelta < 0 ? -1 : 1);
  276. int yStep = (yDelta < 0 ? -1 : 1);
  277. xDelta = abs(xDelta) / 2;
  278. yDelta = abs(yDelta) / 2;
  279. if(xDelta >= yDelta)
  280. {
  281. int error = yDelta - 2 * xDelta;
  282. while(x0 != x1)
  283. {
  284. if(error >= 0 && (error || xStep > 0))
  285. {
  286. error -= xDelta;
  287. y0 += yStep;
  288. }
  289. error += yDelta;
  290. x0 += xStep;
  291. setPixel(x0, y0, red, green, blue);
  292. }
  293. }
  294. else
  295. {
  296. int error = xDelta - 2 * yDelta;
  297. while(y0 != y1)
  298. {
  299. if(error >= 0 && (error || yStep > 0))
  300. {
  301. error -= yDelta;
  302. x0 += xStep;
  303. }
  304. error += xDelta;
  305. y0 += yStep;
  306. setPixel(x0, y0, red, green, blue);
  307. }
  308. }
  309. }
  310. }
  311. // ----------------------------------------------------------------
  312. // Draw a line from (x0, y0) to (x1, y1) using color: theColor
  313. // ----------------------------------------------------------------
  314. void Bmp::drawLine(
  315. int x0, int y0,
  316. int x1, int y1,
  317. const Color &theColor
  318. )
  319. {
  320. drawLine(
  321. x0, y0, x1, y1, theColor.mRed, theColor.mGreen, theColor.mBlue
  322. );
  323. }
  324. // ----------------------------------------------------------------
  325. // Draw a multi-part line, given points:
  326. //
  327. // { x0, y0, x1, y1, x2, y2, ... }
  328. //
  329. // A line will be drawn from: (x0, y0) to (x1, y1), from (x1, y1)
  330. // to (x2, y2), etc. Color will be: (red, green, blue)
  331. // ----------------------------------------------------------------
  332. void Bmp::drawPolyline(
  333. const vector<int> &points,
  334. unsigned char red,
  335. unsigned char green,
  336. unsigned char blue
  337. )
  338. {
  339. int xPrevious = 0;
  340. int yPrevious = 0;
  341. for(int i = 0, n = points.size(); i < n; i += 2)
  342. {
  343. if(i == 0)
  344. {
  345. xPrevious = points[i];
  346. yPrevious = points[i + 1];
  347. }
  348. else
  349. {
  350. int x = points[i];
  351. int y = points[i + 1];
  352. drawLine(xPrevious, yPrevious, x, y, red, green, blue);
  353. xPrevious = x;
  354. yPrevious = y;
  355. }
  356. }
  357. }
  358. void Bmp::drawPolyline(const vector<int> &points, const Color &theColor)
  359. {
  360. drawPolyline(points, theColor.mRed, theColor.mGreen, theColor.mBlue);
  361. }
  362. // ----------------------------------------------------------------
  363. // The bitmap width
  364. // ----------------------------------------------------------------
  365. int Bmp::getWidth() const
  366. {
  367. return(mWidth);
  368. }
  369. // ----------------------------------------------------------------
  370. // The bitmap height
  371. // ----------------------------------------------------------------
  372. int Bmp::getHeight() const
  373. {
  374. return(mHeight);
  375. }
  376. // ----------------------------------------------------------------
  377. // Write our bitmap to: fileName, return true on succes,
  378. // on error: populate errMsg, return false
  379. // ----------------------------------------------------------------
  380. bool Bmp::write(string &fileName, string &errMsg) const
  381. {
  382. ofstream out(fileName.c_str(), ios::binary);
  383. if(out.fail())
  384. {
  385. errMsg = "Could not open: [" + fileName + "]";
  386. return(false);
  387. }
  388. // Header sizes ...
  389. const int BMP_FILE_HEADER_SIZE = 14;
  390. const int BMP_INFO_HEADER_SIZE = 40;
  391. // ----------------------------------------------
  392. // The bmp file header
  393. // ----------------------------------------------
  394. out.put('B');
  395. out.put('M');
  396. int fileSize =
  397. mWidth * mHeight * 3 +
  398. BMP_FILE_HEADER_SIZE + BMP_INFO_HEADER_SIZE;
  399. doWrite(out, fileSize);
  400. short reserved = 0;
  401. doWrite(out, reserved);
  402. doWrite(out, reserved);
  403. int offset = BMP_FILE_HEADER_SIZE + BMP_INFO_HEADER_SIZE;
  404. doWrite(out, offset);
  405. // ----------------------------------------------
  406. // The bmp information header
  407. // ----------------------------------------------
  408. int headerSize = BMP_INFO_HEADER_SIZE;
  409. doWrite(out, headerSize);
  410. doWrite(out, mWidth);
  411. doWrite(out, mHeight);
  412. short colorPlanes = 1;
  413. doWrite(out, colorPlanes);
  414. short bitsPerPixel = 24;
  415. doWrite(out, bitsPerPixel);
  416. int zero = 0;
  417. for(int i = 0; i < 6; i ++)
  418. doWrite(out, zero);
  419. for(int i = 0; i < mHeight; i ++)
  420. for(int j = 0; j < mWidth; j ++)
  421. doWrite(out, mImage[i][j]);
  422. out.close();
  423. return(true);
  424. }
  425. // ----------------------------------------------------------------
  426. // Local Functions
  427. // ----------------------------------------------------------------
  428. static void doWrite(ofstream &out, int value)
  429. {
  430. out.write((const char *)&value, sizeof(int));
  431. }
  432. static void doWrite(ofstream &out, short value)
  433. {
  434. out.write((const char *)&value, sizeof(short));
  435. }
  436. static void doWrite(ofstream &out, const Color &theColor)
  437. {
  438. out.write((const char *)&theColor.mBlue, sizeof(unsigned char));
  439. out.write((const char *)&theColor.mGreen, sizeof(unsigned char));
  440. out.write((const char *)&theColor.mRed, sizeof(unsigned char));
  441. }