This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tools/LevelEdit/HistogramCtl.cpp

564 lines
14 KiB
C++
Raw Permalink Normal View History

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 ;
}