HistogramCtl.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : LevelEdit *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/LevelEdit/HistogramCtl.cpp $Modtime:: 7/2/99 6:28p $*
  25. * *
  26. * $Revision:: 3 $*
  27. * *
  28. *---------------------------------------------------------------------------------------------*
  29. * Functions: *
  30. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  31. #include "StdAfx.H"
  32. #include "HistogramCtl.H"
  33. /////////////////////////////////////////////////////////////////////////////
  34. //
  35. // HistogramCtlClass
  36. //
  37. /////////////////////////////////////////////////////////////////////////////
  38. HistogramCtlClass::HistogramCtlClass (void)
  39. : m_hBitmap (NULL),
  40. m_pBits (NULL),
  41. m_ScanlineSize (0),
  42. m_BMPWidth (0),
  43. m_BMPHeight (0),
  44. m_IsDirty (true),
  45. m_AllowRefresh (true),
  46. m_LowColor (0, 0.5F, 0),
  47. m_HighColor (1, 0, 0),
  48. m_BkLowColor (0.75F, 0.75F, 0.75F),
  49. m_BkHighColor (0.1F, 0.1F, 0.1F),
  50. m_SelColor (0, 0, 0),
  51. m_BkSelColor (1, 1, 1)
  52. {
  53. m_XAxis.min = 0;
  54. m_XAxis.max = 1;
  55. m_YAxis.min = 0;
  56. m_YAxis.max = 1;
  57. m_Selection.min = -1;
  58. m_Selection.max = -1;
  59. return ;
  60. }
  61. /////////////////////////////////////////////////////////////////////////////
  62. //
  63. // ~HistogramCtlClass
  64. //
  65. /////////////////////////////////////////////////////////////////////////////
  66. HistogramCtlClass::~HistogramCtlClass (void)
  67. {
  68. Destroy_DIB_Section ();
  69. return ;
  70. }
  71. /////////////////////////////////////////////////////////////////////////////
  72. //
  73. // Create_DIB_Section
  74. //
  75. /////////////////////////////////////////////////////////////////////////////
  76. void
  77. HistogramCtlClass::Create_DIB_Section (void)
  78. {
  79. // Start fresh
  80. Destroy_DIB_Section ();
  81. //
  82. // Set-up the fields of the BITMAPINFOHEADER
  83. // Note: Top-down DIBs use negative height in Win32.
  84. //
  85. BITMAPINFOHEADER bitmap_info = { 0 };
  86. bitmap_info.biSize = sizeof (BITMAPINFOHEADER);
  87. bitmap_info.biWidth = m_BMPWidth;
  88. bitmap_info.biHeight = -m_BMPHeight;
  89. bitmap_info.biPlanes = 1;
  90. bitmap_info.biBitCount = 24;
  91. bitmap_info.biCompression = BI_RGB;
  92. bitmap_info.biSizeImage = ((m_BMPWidth * m_BMPHeight) * 3);
  93. bitmap_info.biXPelsPerMeter = 0;
  94. bitmap_info.biYPelsPerMeter = 0;
  95. bitmap_info.biClrUsed = 0;
  96. bitmap_info.biClrImportant = 0;
  97. //
  98. // Create a bitmap that we can access the bits directly of
  99. //
  100. HDC hscreen_dc = ::GetDC (NULL);
  101. m_hBitmap = ::CreateDIBSection (hscreen_dc,
  102. (const BITMAPINFO *)&bitmap_info,
  103. DIB_RGB_COLORS,
  104. (void **)&m_pBits,
  105. NULL,
  106. 0L);
  107. // Release our temporary screen DC
  108. ::ReleaseDC (NULL, hscreen_dc);
  109. // Window's bitmaps are DWORD aligned, so make sure
  110. // we take that into account.
  111. int alignment_offset = (m_BMPWidth * 3) % 4;
  112. alignment_offset = (alignment_offset != 0) ? (4 - alignment_offset) : 0;
  113. m_ScanlineSize = (m_BMPWidth * 3) + alignment_offset;
  114. // Force a repaint next time its necessary
  115. Set_Dirty (true);
  116. return ;
  117. }
  118. /////////////////////////////////////////////////////////////////////////////
  119. //
  120. // Destroy_DIB_Section
  121. //
  122. /////////////////////////////////////////////////////////////////////////////
  123. void
  124. HistogramCtlClass::Destroy_DIB_Section (void)
  125. {
  126. if (m_hBitmap != NULL) {
  127. ::DeleteObject (m_hBitmap);
  128. m_hBitmap = NULL;
  129. m_pBits = NULL;
  130. }
  131. return ;
  132. }
  133. /////////////////////////////////////////////////////////////////////////////
  134. //
  135. // Set_Dimensions
  136. //
  137. /////////////////////////////////////////////////////////////////////////////
  138. void
  139. HistogramCtlClass::Set_Dimensions (int width, int height)
  140. {
  141. if ((m_BMPWidth != width) ||
  142. (m_BMPHeight != height)) {
  143. //
  144. // Recreate the BMP we use
  145. //
  146. m_BMPWidth = width;
  147. m_BMPHeight = height;
  148. Create_DIB_Section ();
  149. }
  150. return ;
  151. }
  152. /////////////////////////////////////////////////////////////////////////////
  153. //
  154. // Paint_DIB
  155. //
  156. /////////////////////////////////////////////////////////////////////////////
  157. void
  158. HistogramCtlClass::Paint_DIB (void)
  159. {
  160. float x_range = m_XAxis.max - m_XAxis.min;
  161. float y_range = m_YAxis.max - m_YAxis.min;
  162. int sel_start_x = int((m_Selection.min / x_range) * (float)(m_BMPWidth-1));
  163. int sel_end_x = int((m_Selection.max / x_range) * (float)(m_BMPWidth-1));
  164. BYTE sel_back_red = BYTE (m_BkSelColor.X * 255);
  165. BYTE sel_back_green = BYTE (m_BkSelColor.Y * 255);
  166. BYTE sel_back_blue = BYTE (m_BkSelColor.Z * 255);
  167. BYTE sel_red = BYTE (m_SelColor.X * 255);
  168. BYTE sel_green = BYTE (m_SelColor.Y * 255);
  169. BYTE sel_blue = BYTE (m_SelColor.Z * 255);
  170. //
  171. // Paint the background
  172. //
  173. if (m_pBits != NULL) {
  174. float red = m_BkHighColor.X;
  175. float green = m_BkHighColor.Y;
  176. float blue = m_BkHighColor.Z;
  177. float red_inc = (m_BkLowColor.X - m_BkHighColor.X) / (float)m_BMPHeight;
  178. float green_inc = (m_BkLowColor.Y - m_BkHighColor.Y) / (float)m_BMPHeight;
  179. float blue_inc = (m_BkLowColor.Z - m_BkHighColor.Z) / (float)m_BMPHeight;
  180. //
  181. // Loop through all the pixels and set their background color.
  182. //
  183. int bmp_index = 0;
  184. int row_offset = m_ScanlineSize - (m_BMPWidth * 3);
  185. for (int row = 0; row < m_BMPHeight; row ++) {
  186. BYTE curr_red = BYTE(red * 255);
  187. BYTE curr_green = BYTE(green * 255);
  188. BYTE curr_blue = BYTE(blue * 255);
  189. for (int col = 0; col < m_BMPWidth; col ++) {
  190. if ((col >= sel_start_x) && (col <= sel_end_x)) {
  191. m_pBits[bmp_index ++] = sel_back_blue;
  192. m_pBits[bmp_index ++] = sel_back_green;
  193. m_pBits[bmp_index ++] = sel_back_red;
  194. } else {
  195. m_pBits[bmp_index ++] = curr_blue;
  196. m_pBits[bmp_index ++] = curr_green;
  197. m_pBits[bmp_index ++] = curr_red;
  198. }
  199. }
  200. red += red_inc;
  201. green += green_inc;
  202. blue += blue_inc;
  203. bmp_index += row_offset;
  204. }
  205. }
  206. //
  207. // Paint the columns if necessary
  208. //
  209. if ((m_pBits != NULL) && (m_ValueList.Count () > 0)) {
  210. int start_index = Find_Value_Index (m_XAxis.min);
  211. int end_index = Find_Value_Index (m_XAxis.max);
  212. int * x_vector = new int [m_BMPWidth];
  213. ::memset (x_vector, 0, sizeof (int) * m_BMPWidth);
  214. //
  215. // Loop through all the values and combine pixel-duplicates
  216. //
  217. for (int index = start_index; index <= end_index; index ++) {
  218. VALUE &value = m_ValueList[index];
  219. int x_pos = int((value.value / x_range) * (float)(m_BMPWidth-1));
  220. x_vector[x_pos] += value.count;
  221. }
  222. float start_red = m_HighColor.X;
  223. float start_green = m_HighColor.Y;
  224. float start_blue = m_HighColor.Z;
  225. float red_inc = (m_LowColor.X - m_HighColor.X) / (float)m_BMPHeight;
  226. float green_inc = (m_LowColor.Y - m_HighColor.Y) / (float)m_BMPHeight;
  227. float blue_inc = (m_LowColor.Z - m_HighColor.Z) / (float)m_BMPHeight;
  228. //
  229. // Paint each column of information
  230. //
  231. for (int x_pos = 0; x_pos < m_BMPWidth; x_pos ++) {
  232. if (x_vector[x_pos] > 0) {
  233. int height = int(((float)(x_vector[x_pos]) / y_range) * (float)m_BMPHeight);
  234. height = min (height, m_BMPHeight);
  235. int y_pos = m_BMPHeight - height;
  236. //
  237. // Determine the starting red, green, blue
  238. //
  239. float curr_red = (start_red + (y_pos * red_inc));
  240. float curr_green = (start_green + (y_pos * green_inc));
  241. float curr_blue = (start_blue + (y_pos * blue_inc));
  242. //
  243. // Paint the column
  244. //
  245. int bmp_index = (y_pos * m_ScanlineSize) + (x_pos * 3);
  246. for (int pixel = y_pos; pixel < m_BMPHeight; pixel ++) {
  247. //
  248. // Plot the pixel in the BMP (note, the bits are orgainzed
  249. // in an BGR format).
  250. //
  251. if ((x_pos >= sel_start_x) && (x_pos <= sel_end_x)) {
  252. m_pBits[bmp_index] = sel_blue;
  253. m_pBits[bmp_index + 1] = sel_green;
  254. m_pBits[bmp_index + 2] = sel_red;
  255. } else {
  256. m_pBits[bmp_index] = BYTE(curr_blue * 255);
  257. m_pBits[bmp_index + 1] = BYTE(curr_green * 255);
  258. m_pBits[bmp_index + 2] = BYTE(curr_red * 255);
  259. }
  260. //
  261. // Increment the current cocor
  262. //
  263. curr_red += red_inc;
  264. curr_green += green_inc;
  265. curr_blue += blue_inc;
  266. // Move down to the next scanline
  267. bmp_index += m_ScanlineSize;
  268. }
  269. }
  270. }
  271. delete [] x_vector;
  272. Set_Dirty (false);
  273. }
  274. return ;
  275. }
  276. /////////////////////////////////////////////////////////////////////////////
  277. //
  278. // Add_Data_Point
  279. //
  280. /////////////////////////////////////////////////////////////////////////////
  281. void
  282. HistogramCtlClass::Add_Data_Point (float value)
  283. {
  284. int start = 0;
  285. int end = m_ValueList.Count ();
  286. end = max (end, 0);
  287. bool found = false;
  288. int index = end;
  289. while (end > start)
  290. {
  291. index = start + ((end - start) >> 1);
  292. float curr_value = m_ValueList[index].value;
  293. if (value > curr_value) {
  294. if (start == index) { index ++; break; }
  295. start = index;
  296. } else if (value < curr_value) {
  297. if (end == index) { index --; break; }
  298. end = index;
  299. } else {
  300. m_ValueList[index].count ++;
  301. found = true;
  302. break;
  303. }
  304. }
  305. ASSERT (index >=0 );
  306. if (found == false) {
  307. m_ValueList.Insert (index, VALUE (value, 1));
  308. }
  309. /*int start = 0;
  310. int end = m_ValueList.Count () - 1;
  311. //
  312. // Simple binary search to find the right place
  313. // to insert the new data point
  314. //
  315. bool found = false;
  316. while ((end > start) && !found) {
  317. int index = start + ((end - start) >> 1);
  318. VALUE &curr_point = m_ValueList[index];
  319. if (index == start) {
  320. break;
  321. } else if (value > curr_point.value) {
  322. start = index;
  323. } else if (value < curr_point.value) {
  324. end = index;
  325. } else {
  326. curr_point.count ++;
  327. found = true;
  328. }
  329. }
  330. //
  331. // If we didn't find the exact point in our search,
  332. // then add the point to the list.
  333. //
  334. if (found == false) {
  335. m_ValueList.Insert (start, VALUE (value, 1));
  336. }*/
  337. Set_Dirty (true);
  338. return ;
  339. }
  340. /////////////////////////////////////////////////////////////////////////////
  341. //
  342. // Find_Value_Index
  343. //
  344. /////////////////////////////////////////////////////////////////////////////
  345. int
  346. HistogramCtlClass::Find_Value_Index (float value)
  347. {
  348. //int retval = -1;
  349. int start = 0;
  350. int end = m_ValueList.Count ();
  351. end = max (end, 0);
  352. int index = end;
  353. while (end > start)
  354. {
  355. index = start + ((end - start) >> 1);
  356. float curr_value = m_ValueList[index].value;
  357. if (value > curr_value) {
  358. if (start == index) { index ++; break; }
  359. start = index;
  360. } else if (value < curr_value) {
  361. if (end == index) { index --; break; }
  362. end = index;
  363. } else {
  364. while ((curr_value == value) && (index > 0)) {
  365. curr_value = m_ValueList[--index].value;
  366. }
  367. index = (curr_value == value) ? index : index + 1;
  368. //m_ValueList[index].count ++;
  369. //found = true;
  370. break;
  371. }
  372. }
  373. ASSERT (index >=0 );
  374. /*if (found == false) {
  375. m_ValueList.Insert (index, VALUE (value, 1));
  376. }*/
  377. //
  378. // Simple binary search to find the right place
  379. // to insert the new data point
  380. //
  381. /*int start = 0;
  382. int end = m_ValueList.Count () - 1;
  383. while ((end > start) && (retval == -1)) {
  384. int index = start + ((end - start) >> 1);
  385. VALUE &curr_point = m_ValueList[index];
  386. if (index == start) {
  387. retval = (value > curr_point.value) ? end : start;
  388. break;
  389. } else if (value > curr_point.value) {
  390. start = index;
  391. } else if (value < curr_point.value) {
  392. end = index;
  393. } else {
  394. retval = index;
  395. }
  396. }*/
  397. /*if (retval == -1) {
  398. retval = index;
  399. }*/
  400. return index;
  401. }
  402. /////////////////////////////////////////////////////////////////////////////
  403. //
  404. // Render
  405. //
  406. /////////////////////////////////////////////////////////////////////////////
  407. void
  408. HistogramCtlClass::Render (HDC hdc, int x_pos, int y_pos)
  409. {
  410. //
  411. // Ensure we have an up-to-date graph
  412. //
  413. if (m_IsDirty) {
  414. Paint_DIB ();
  415. }
  416. if (m_hBitmap != NULL) {
  417. HDC mem_dc = ::CreateCompatibleDC (NULL);
  418. HBITMAP old_bmp = (HBITMAP)::SelectObject (mem_dc, m_hBitmap);
  419. //
  420. // Paint our graph onto the window
  421. //
  422. ::BitBlt (hdc, x_pos, y_pos, m_BMPWidth, m_BMPHeight, mem_dc, 0, 0, SRCCOPY);
  423. // Cleanup
  424. ::SelectObject (mem_dc, old_bmp);
  425. ::DeleteDC (mem_dc);
  426. }
  427. return ;
  428. }
  429. /////////////////////////////////////////////////////////////////////////////
  430. //
  431. // Reset_Data_Points
  432. //
  433. /////////////////////////////////////////////////////////////////////////////
  434. void
  435. HistogramCtlClass::Reset_Data_Points (void)
  436. {
  437. m_ValueList.Delete_All ();
  438. Set_Dirty (true);
  439. return ;
  440. }
  441. /////////////////////////////////////////////////////////////////////////////
  442. //
  443. // Set_Bk_Color
  444. //
  445. /////////////////////////////////////////////////////////////////////////////
  446. void
  447. HistogramCtlClass::Set_Bk_Color
  448. (
  449. const Vector3 &low,
  450. const Vector3 &high
  451. )
  452. {
  453. m_BkLowColor = low;
  454. m_BkHighColor = high;
  455. Set_Dirty (true);
  456. return ;
  457. }
  458. /////////////////////////////////////////////////////////////////////////////
  459. //
  460. // Set_Color
  461. //
  462. /////////////////////////////////////////////////////////////////////////////
  463. void
  464. HistogramCtlClass::Set_Color
  465. (
  466. const Vector3 &low,
  467. const Vector3 &high
  468. )
  469. {
  470. m_LowColor = low;
  471. m_HighColor = high;
  472. Set_Dirty (true);
  473. return ;
  474. }