/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : LevelEdit * * * * $Archive:: /Commando/Code/Tools/LevelEdit/HistogramCtl.cpp $Modtime:: 7/2/99 6:28p $* * * * $Revision:: 3 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "StdAfx.H" #include "HistogramCtl.H" ///////////////////////////////////////////////////////////////////////////// // // HistogramCtlClass // ///////////////////////////////////////////////////////////////////////////// HistogramCtlClass::HistogramCtlClass (void) : m_hBitmap (NULL), m_pBits (NULL), m_ScanlineSize (0), m_BMPWidth (0), m_BMPHeight (0), m_IsDirty (true), m_AllowRefresh (true), m_LowColor (0, 0.5F, 0), m_HighColor (1, 0, 0), m_BkLowColor (0.75F, 0.75F, 0.75F), m_BkHighColor (0.1F, 0.1F, 0.1F), m_SelColor (0, 0, 0), m_BkSelColor (1, 1, 1) { m_XAxis.min = 0; m_XAxis.max = 1; m_YAxis.min = 0; m_YAxis.max = 1; m_Selection.min = -1; m_Selection.max = -1; return ; } ///////////////////////////////////////////////////////////////////////////// // // ~HistogramCtlClass // ///////////////////////////////////////////////////////////////////////////// HistogramCtlClass::~HistogramCtlClass (void) { Destroy_DIB_Section (); return ; } ///////////////////////////////////////////////////////////////////////////// // // Create_DIB_Section // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Create_DIB_Section (void) { // Start fresh Destroy_DIB_Section (); // // Set-up the fields of the BITMAPINFOHEADER // Note: Top-down DIBs use negative height in Win32. // BITMAPINFOHEADER bitmap_info = { 0 }; bitmap_info.biSize = sizeof (BITMAPINFOHEADER); bitmap_info.biWidth = m_BMPWidth; bitmap_info.biHeight = -m_BMPHeight; bitmap_info.biPlanes = 1; bitmap_info.biBitCount = 24; bitmap_info.biCompression = BI_RGB; bitmap_info.biSizeImage = ((m_BMPWidth * m_BMPHeight) * 3); bitmap_info.biXPelsPerMeter = 0; bitmap_info.biYPelsPerMeter = 0; bitmap_info.biClrUsed = 0; bitmap_info.biClrImportant = 0; // // Create a bitmap that we can access the bits directly of // HDC hscreen_dc = ::GetDC (NULL); m_hBitmap = ::CreateDIBSection (hscreen_dc, (const BITMAPINFO *)&bitmap_info, DIB_RGB_COLORS, (void **)&m_pBits, NULL, 0L); // Release our temporary screen DC ::ReleaseDC (NULL, hscreen_dc); // Window's bitmaps are DWORD aligned, so make sure // we take that into account. int alignment_offset = (m_BMPWidth * 3) % 4; alignment_offset = (alignment_offset != 0) ? (4 - alignment_offset) : 0; m_ScanlineSize = (m_BMPWidth * 3) + alignment_offset; // Force a repaint next time its necessary Set_Dirty (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Destroy_DIB_Section // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Destroy_DIB_Section (void) { if (m_hBitmap != NULL) { ::DeleteObject (m_hBitmap); m_hBitmap = NULL; m_pBits = NULL; } return ; } ///////////////////////////////////////////////////////////////////////////// // // Set_Dimensions // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Set_Dimensions (int width, int height) { if ((m_BMPWidth != width) || (m_BMPHeight != height)) { // // Recreate the BMP we use // m_BMPWidth = width; m_BMPHeight = height; Create_DIB_Section (); } return ; } ///////////////////////////////////////////////////////////////////////////// // // Paint_DIB // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Paint_DIB (void) { float x_range = m_XAxis.max - m_XAxis.min; float y_range = m_YAxis.max - m_YAxis.min; int sel_start_x = int((m_Selection.min / x_range) * (float)(m_BMPWidth-1)); int sel_end_x = int((m_Selection.max / x_range) * (float)(m_BMPWidth-1)); BYTE sel_back_red = BYTE (m_BkSelColor.X * 255); BYTE sel_back_green = BYTE (m_BkSelColor.Y * 255); BYTE sel_back_blue = BYTE (m_BkSelColor.Z * 255); BYTE sel_red = BYTE (m_SelColor.X * 255); BYTE sel_green = BYTE (m_SelColor.Y * 255); BYTE sel_blue = BYTE (m_SelColor.Z * 255); // // Paint the background // if (m_pBits != NULL) { float red = m_BkHighColor.X; float green = m_BkHighColor.Y; float blue = m_BkHighColor.Z; float red_inc = (m_BkLowColor.X - m_BkHighColor.X) / (float)m_BMPHeight; float green_inc = (m_BkLowColor.Y - m_BkHighColor.Y) / (float)m_BMPHeight; float blue_inc = (m_BkLowColor.Z - m_BkHighColor.Z) / (float)m_BMPHeight; // // Loop through all the pixels and set their background color. // int bmp_index = 0; int row_offset = m_ScanlineSize - (m_BMPWidth * 3); for (int row = 0; row < m_BMPHeight; row ++) { BYTE curr_red = BYTE(red * 255); BYTE curr_green = BYTE(green * 255); BYTE curr_blue = BYTE(blue * 255); for (int col = 0; col < m_BMPWidth; col ++) { if ((col >= sel_start_x) && (col <= sel_end_x)) { m_pBits[bmp_index ++] = sel_back_blue; m_pBits[bmp_index ++] = sel_back_green; m_pBits[bmp_index ++] = sel_back_red; } else { m_pBits[bmp_index ++] = curr_blue; m_pBits[bmp_index ++] = curr_green; m_pBits[bmp_index ++] = curr_red; } } red += red_inc; green += green_inc; blue += blue_inc; bmp_index += row_offset; } } // // Paint the columns if necessary // if ((m_pBits != NULL) && (m_ValueList.Count () > 0)) { int start_index = Find_Value_Index (m_XAxis.min); int end_index = Find_Value_Index (m_XAxis.max); int * x_vector = new int [m_BMPWidth]; ::memset (x_vector, 0, sizeof (int) * m_BMPWidth); // // Loop through all the values and combine pixel-duplicates // for (int index = start_index; index <= end_index; index ++) { VALUE &value = m_ValueList[index]; int x_pos = int((value.value / x_range) * (float)(m_BMPWidth-1)); x_vector[x_pos] += value.count; } float start_red = m_HighColor.X; float start_green = m_HighColor.Y; float start_blue = m_HighColor.Z; float red_inc = (m_LowColor.X - m_HighColor.X) / (float)m_BMPHeight; float green_inc = (m_LowColor.Y - m_HighColor.Y) / (float)m_BMPHeight; float blue_inc = (m_LowColor.Z - m_HighColor.Z) / (float)m_BMPHeight; // // Paint each column of information // for (int x_pos = 0; x_pos < m_BMPWidth; x_pos ++) { if (x_vector[x_pos] > 0) { int height = int(((float)(x_vector[x_pos]) / y_range) * (float)m_BMPHeight); height = min (height, m_BMPHeight); int y_pos = m_BMPHeight - height; // // Determine the starting red, green, blue // float curr_red = (start_red + (y_pos * red_inc)); float curr_green = (start_green + (y_pos * green_inc)); float curr_blue = (start_blue + (y_pos * blue_inc)); // // Paint the column // int bmp_index = (y_pos * m_ScanlineSize) + (x_pos * 3); for (int pixel = y_pos; pixel < m_BMPHeight; pixel ++) { // // Plot the pixel in the BMP (note, the bits are orgainzed // in an BGR format). // if ((x_pos >= sel_start_x) && (x_pos <= sel_end_x)) { m_pBits[bmp_index] = sel_blue; m_pBits[bmp_index + 1] = sel_green; m_pBits[bmp_index + 2] = sel_red; } else { m_pBits[bmp_index] = BYTE(curr_blue * 255); m_pBits[bmp_index + 1] = BYTE(curr_green * 255); m_pBits[bmp_index + 2] = BYTE(curr_red * 255); } // // Increment the current cocor // curr_red += red_inc; curr_green += green_inc; curr_blue += blue_inc; // Move down to the next scanline bmp_index += m_ScanlineSize; } } } delete [] x_vector; Set_Dirty (false); } return ; } ///////////////////////////////////////////////////////////////////////////// // // Add_Data_Point // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Add_Data_Point (float value) { int start = 0; int end = m_ValueList.Count (); end = max (end, 0); bool found = false; int index = end; while (end > start) { index = start + ((end - start) >> 1); float curr_value = m_ValueList[index].value; if (value > curr_value) { if (start == index) { index ++; break; } start = index; } else if (value < curr_value) { if (end == index) { index --; break; } end = index; } else { m_ValueList[index].count ++; found = true; break; } } ASSERT (index >=0 ); if (found == false) { m_ValueList.Insert (index, VALUE (value, 1)); } /*int start = 0; int end = m_ValueList.Count () - 1; // // Simple binary search to find the right place // to insert the new data point // bool found = false; while ((end > start) && !found) { int index = start + ((end - start) >> 1); VALUE &curr_point = m_ValueList[index]; if (index == start) { break; } else if (value > curr_point.value) { start = index; } else if (value < curr_point.value) { end = index; } else { curr_point.count ++; found = true; } } // // If we didn't find the exact point in our search, // then add the point to the list. // if (found == false) { m_ValueList.Insert (start, VALUE (value, 1)); }*/ Set_Dirty (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Find_Value_Index // ///////////////////////////////////////////////////////////////////////////// int HistogramCtlClass::Find_Value_Index (float value) { //int retval = -1; int start = 0; int end = m_ValueList.Count (); end = max (end, 0); int index = end; while (end > start) { index = start + ((end - start) >> 1); float curr_value = m_ValueList[index].value; if (value > curr_value) { if (start == index) { index ++; break; } start = index; } else if (value < curr_value) { if (end == index) { index --; break; } end = index; } else { while ((curr_value == value) && (index > 0)) { curr_value = m_ValueList[--index].value; } index = (curr_value == value) ? index : index + 1; //m_ValueList[index].count ++; //found = true; break; } } ASSERT (index >=0 ); /*if (found == false) { m_ValueList.Insert (index, VALUE (value, 1)); }*/ // // Simple binary search to find the right place // to insert the new data point // /*int start = 0; int end = m_ValueList.Count () - 1; while ((end > start) && (retval == -1)) { int index = start + ((end - start) >> 1); VALUE &curr_point = m_ValueList[index]; if (index == start) { retval = (value > curr_point.value) ? end : start; break; } else if (value > curr_point.value) { start = index; } else if (value < curr_point.value) { end = index; } else { retval = index; } }*/ /*if (retval == -1) { retval = index; }*/ return index; } ///////////////////////////////////////////////////////////////////////////// // // Render // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Render (HDC hdc, int x_pos, int y_pos) { // // Ensure we have an up-to-date graph // if (m_IsDirty) { Paint_DIB (); } if (m_hBitmap != NULL) { HDC mem_dc = ::CreateCompatibleDC (NULL); HBITMAP old_bmp = (HBITMAP)::SelectObject (mem_dc, m_hBitmap); // // Paint our graph onto the window // ::BitBlt (hdc, x_pos, y_pos, m_BMPWidth, m_BMPHeight, mem_dc, 0, 0, SRCCOPY); // Cleanup ::SelectObject (mem_dc, old_bmp); ::DeleteDC (mem_dc); } return ; } ///////////////////////////////////////////////////////////////////////////// // // Reset_Data_Points // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Reset_Data_Points (void) { m_ValueList.Delete_All (); Set_Dirty (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Set_Bk_Color // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Set_Bk_Color ( const Vector3 &low, const Vector3 &high ) { m_BkLowColor = low; m_BkHighColor = high; Set_Dirty (true); return ; } ///////////////////////////////////////////////////////////////////////////// // // Set_Color // ///////////////////////////////////////////////////////////////////////////// void HistogramCtlClass::Set_Color ( const Vector3 &low, const Vector3 &high ) { m_LowColor = low; m_HighColor = high; Set_Dirty (true); return ; }