diff --git a/FA2pp b/FA2pp index fdd7056..9ebde96 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit fdd70563258046803c8efda07cbbbcb05ee75731 +Subproject commit 9ebde963db0bb099b88926898e4048d4bede671f diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index e54d7f2..0efc64b 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -93,7 +93,7 @@ true false true - _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;NOMINMAX;%(PreprocessorDefinitions) + _DEBUG;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;WIN32;_CRT_SECURE_NO_WARNINGS;_CONSOLE;NOMINMAX;%(PreprocessorDefinitions) false false true @@ -192,13 +192,11 @@ - - diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index aca15fd..3232614 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -99,9 +99,6 @@ 头文件 - - 头文件 - 头文件 @@ -111,9 +108,6 @@ 头文件 - - 头文件 - 头文件 diff --git a/FA2sp/Ext/CLoading/Body.LoadObjects.cpp b/FA2sp/Ext/CLoading/Body.LoadObjects.cpp index bc52b32..94413cb 100644 --- a/FA2sp/Ext/CLoading/Body.LoadObjects.cpp +++ b/FA2sp/Ext/CLoading/Body.LoadObjects.cpp @@ -325,7 +325,7 @@ void CLoadingExt::LoadBuilding(ppmfc::CString ID) { if (Variables::Rules.GetBool(ID, "TurretAnimIsVoxel")) { - int turzadjust = Variables::Rules.GetInteger(ID, "TurretAnimZAdjust"); + int turzadjust = Variables::Rules.GetInteger(ID, "TurretAnimZAdjust"); // no idea why apply it but it worked ppmfc::CString TurName = Variables::Rules.GetString(ID, "TurretAnim", ID + "tur"); ppmfc::CString BarlName = ID + "barl"; @@ -383,7 +383,7 @@ void CLoadingExt::LoadBuilding(ppmfc::CString ID) int turdeltaY = GlobalVars::INIFiles::FAData->GetInteger("BuildingVoxelTurretsRA2", pKey); VXL_Add(pTurImages[i], turrect[i][2] + turdeltaX, turrect[i][3] + turdeltaY, turrect[i][0], turrect[i][1]); - GameDeleteArray(pTurImages[i], turrect[i][0] * turrect[i][1]); + delete[] pTurImages[i]; // this buffer is created inside the lib if (pBarlImages[i]) { @@ -393,7 +393,7 @@ void CLoadingExt::LoadBuilding(ppmfc::CString ID) int barldeltaY = GlobalVars::INIFiles::FAData->GetInteger("BuildingVoxelBarrelsRA2", pKey); VXL_Add(pBarlImages[i], barlrect[i][2]+ barldeltaX, barlrect[i][3]+ barldeltaY, barlrect[i][0], barlrect[i][1]); - GameDeleteArray(pBarlImages[i], barlrect[i][0] * barlrect[i][1]); + delete[] pBarlImages[i]; } } @@ -578,7 +578,7 @@ void CLoadingExt::LoadVehicleOrAircraft(ppmfc::CString ID) if (pImage[i]) { VXL_Add(pImage[i], rect[i][2], rect[i][3], rect[i][0], rect[i][1]); - GameDeleteArray(pImage[i], rect[i][0] * rect[i][1]); + delete[] pImage[i]; } ppmfc::CString pKey; if (pTurretImage[i]) @@ -588,7 +588,7 @@ void CLoadingExt::LoadVehicleOrAircraft(ppmfc::CString ID) pKey.Format("%sY%d", ID, i); int turdeltaY = GlobalVars::INIFiles::FAData->GetInteger("VehicleVoxelTurretsRA2", pKey); VXL_Add(pTurretImage[i], turretrect[i][2] + turdeltaX, turretrect[i][3] + turdeltaY, turretrect[i][0], turretrect[i][1]); - GameDeleteArray(pTurretImage[i], turretrect[i][0] * turretrect[i][1]); + delete[] pTurretImage[i]; if (pBarrelImage[i]) { @@ -598,7 +598,7 @@ void CLoadingExt::LoadVehicleOrAircraft(ppmfc::CString ID) int barldeltaY = GlobalVars::INIFiles::FAData->GetInteger("VehicleVoxelBarrelsRA2", pKey); VXL_Add(pBarrelImage[i], barrelrect[i][2] + barldeltaX, barrelrect[i][3] + barldeltaY, barrelrect[i][0], barrelrect[i][1]); - GameDeleteArray(pBarrelImage[i], barrelrect[i][0] * barrelrect[i][1]); + delete[] pBarrelImage[i]; } } @@ -618,13 +618,12 @@ void CLoadingExt::LoadVehicleOrAircraft(ppmfc::CString ID) int outW = 0x100, outH = 0x100; VXL_Add(pImage[i], rect[i][2], rect[i][3], rect[i][0], rect[i][1]); + delete[] pImage[i]; VXL_GetAndClear(outBuffer, &outW, &outH); SetImageData(outBuffer, DictName, outW, outH, Palettes::LoadPalette(PaletteName)); } } - - } else // As SHP { @@ -909,23 +908,4 @@ void CLoadingExt::GetFullPaletteName(ppmfc::CString& PaletteName) PaletteName += "tem.pal"; return; } -} - -#include "../../Helpers/Bitmap.h" -void CLoadingExt::DumpFrameToFile(unsigned char* pBuffer, Palette* pPal, int Width, int Height, ppmfc::CString name) -{ - bitmap_image bmp; - bmp.setwidth_height(Width, Height, true); - - int count = 0; - for (int j = 0; j < bmp.height(); ++j) - { - for (int i = 0; i < bmp.width(); ++i) - { - bmp.set_pixel(i, j, pPal->GetByteColor(pBuffer[count])); - ++count; - } - } - - bmp.save_image((const char*)name); } \ No newline at end of file diff --git a/FA2sp/Ext/CScriptTypes/Body.cpp b/FA2sp/Ext/CScriptTypes/Body.cpp index d913a38..b3dfbb1 100644 --- a/FA2sp/Ext/CScriptTypes/Body.cpp +++ b/FA2sp/Ext/CScriptTypes/Body.cpp @@ -212,14 +212,14 @@ BOOL CScriptTypesExt::OnInitDialogExt() default: case 2: CScriptTypeParam::ExtParams[id].Param_ = atoi((const char*)pParseBuffer[1]); + SAFE_RELEASE(pParseBuffer[1]); case 1: CScriptTypeParam::ExtParams[id].Label_ = pParseBuffer[0]; + SAFE_RELEASE(pParseBuffer[0]); case 0: continue; } } - SAFE_RELEASE(pParseBuffer[0]); - SAFE_RELEASE(pParseBuffer[1]); } if (auto entities = fadata.GetSection("ScriptsRA2")) @@ -236,23 +236,23 @@ BOOL CScriptTypesExt::OnInitDialogExt() case 5: default: CScriptTypeAction::ExtActions[id].Description_ = pParseBuffer[4]; + SAFE_RELEASE(pParseBuffer[4]); case 4: CScriptTypeAction::ExtActions[id].Editable_ = ParseBool((const char*)pParseBuffer[3]); + SAFE_RELEASE(pParseBuffer[3]); case 3: CScriptTypeAction::ExtActions[id].Hide_ = ParseBool((const char*)pParseBuffer[2]); + SAFE_RELEASE(pParseBuffer[2]); case 2: CScriptTypeAction::ExtActions[id].ParamCode_ = atoi((const char*)pParseBuffer[1]); + SAFE_RELEASE(pParseBuffer[1]); case 1: CScriptTypeAction::ExtActions[id].Name_ = pParseBuffer[0]; + SAFE_RELEASE(pParseBuffer[0]); case 0: continue; } } - SAFE_RELEASE(pParseBuffer[0]); - SAFE_RELEASE(pParseBuffer[1]); - SAFE_RELEASE(pParseBuffer[2]); - SAFE_RELEASE(pParseBuffer[3]); - SAFE_RELEASE(pParseBuffer[4]); } int counter = 0; diff --git a/FA2sp/Ext/CTeamTypes/Body.cpp b/FA2sp/Ext/CTeamTypes/Body.cpp index e2696a2..ad04b97 100644 --- a/FA2sp/Ext/CTeamTypes/Body.cpp +++ b/FA2sp/Ext/CTeamTypes/Body.cpp @@ -109,8 +109,8 @@ void CTeamTypesExt::OnBNCloneClicked() auto copyitem = [&value, ¤tID](ppmfc::CString key) { - auto data = GlobalVars::INIFiles::CurrentDocument->GetString(currentID, key); - GlobalVars::INIFiles::CurrentDocument->WriteString(value, key, data); + if (auto ppStr = GlobalVars::INIFiles::CurrentDocument->TryGetString(currentID, key)) + GlobalVars::INIFiles::CurrentDocument->WriteString(value, key, *ppStr); }; copyitem("Max"); @@ -139,6 +139,7 @@ void CTeamTypesExt::OnBNCloneClicked() copyitem("LooseRecruit"); copyitem("VeteranLevel"); copyitem("IsBaseDefense"); + copyitem("TransportWaypoint"); copyitem("UseTransportOrigin"); copyitem("MindControlDecision"); copyitem("OnlyTargetHouseEnemy"); diff --git a/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.cpp b/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.cpp index 63fecf3..8734402 100644 --- a/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.cpp +++ b/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.cpp @@ -51,72 +51,112 @@ BOOL CALLBACK CAllieEditor::DlgProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM l return TRUE; } case WM_COMMAND: { - switch (wParam) + WORD ID = LOWORD(wParam); + WORD CODE = HIWORD(wParam); + if (CODE == BN_CLICKED) { - case IDOK: { - ppmfc::CString allies = ""; - HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox - int cnt = SendMessage(LBA, LB_GETCOUNT, NULL, NULL); - for (int i = 0; i < cnt - 1; ++i) + switch (ID) { - int TextLen = SendMessage(LBA, LB_GETTEXTLEN, i, NULL); + case IDOK: { + ppmfc::CString allies = ""; + HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox + int cnt = SendMessage(LBA, LB_GETCOUNT, NULL, NULL); + for (int i = 0; i < cnt - 1; ++i) + { + int TextLen = SendMessage(LBA, LB_GETTEXTLEN, i, NULL); + if (TextLen == LB_ERR) break; + TCHAR* str = new TCHAR[TextLen + 1]; + SendMessage(LBA, LB_GETTEXT, i, (LPARAM)str); + FA2sp::Buffer.Format("%s,", str); + delete[] str; + allies += FA2sp::Buffer; + } + int TextLen = SendMessage(LBA, LB_GETTEXTLEN, cnt - 1, NULL); if (TextLen == LB_ERR) break; TCHAR* str = new TCHAR[TextLen + 1]; - SendMessage(LBA, LB_GETTEXT, i, (LPARAM)str); - FA2sp::Buffer.Format("%s,", str); + SendMessage(LBA, LB_GETTEXT, cnt - 1, (LPARAM)str); + FA2sp::Buffer.Format("%s", str); delete[] str; allies += FA2sp::Buffer; - } - int TextLen = SendMessage(LBA, LB_GETTEXTLEN, cnt - 1, NULL); - if (TextLen == LB_ERR) break; - TCHAR* str = new TCHAR[TextLen + 1]; - SendMessage(LBA, LB_GETTEXT, cnt - 1, (LPARAM)str); - FA2sp::Buffer.Format("%s", str); - delete[] str; - allies += FA2sp::Buffer; - cHouses.CETAllies.SetWindowText(allies); - EndDialog(hwnd, NULL); - return TRUE; - } - case IDCANCEL: { - EndDialog(hwnd, NULL); - return TRUE; - } - case 6300: {//Go Allies - HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox - HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox - int EnemyCount = SendMessage(LBB, LB_GETCOUNT, NULL, NULL); - if (EnemyCount <= 0) break; - int EnemyCurSelIndex = SendMessage(LBB, LB_GETCURSEL, NULL, NULL); - if (EnemyCurSelIndex < 0 || EnemyCurSelIndex >= EnemyCount) break; - int TextLen = SendMessage(LBB, LB_GETTEXTLEN, EnemyCurSelIndex, NULL); - if (TextLen == LB_ERR) break; - TCHAR* str = new TCHAR[TextLen + 1]; - SendMessage(LBB, LB_GETTEXT, EnemyCurSelIndex, (LPARAM)str); - SendMessage(LBB, LB_DELETESTRING, EnemyCurSelIndex, NULL); - SendMessage(LBA, LB_ADDSTRING, NULL, (LPARAM)str); - delete[] str; - break; - } - case 6301: {//Go Enemies - HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox - HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox - int AllieCount = SendMessage(LBA, LB_GETCOUNT, NULL, NULL); - if (AllieCount <= 0) break; - int AllieCurSelIndex = SendMessage(LBA, LB_GETCURSEL, NULL, NULL); - if (AllieCurSelIndex < 0 || AllieCurSelIndex >= AllieCount) break; - int TextLen = SendMessage(LBA, LB_GETTEXTLEN, AllieCurSelIndex, NULL); - if (TextLen == LB_ERR) break; - TCHAR* str = new TCHAR[TextLen + 1]; - SendMessage(LBA, LB_GETTEXT, AllieCurSelIndex, (LPARAM)str); - SendMessage(LBA, LB_DELETESTRING, AllieCurSelIndex, NULL); - SendMessage(LBB, LB_ADDSTRING, NULL, (LPARAM)str); - delete[] str; - break; + cHouses.CETAllies.SetWindowText(allies); + EndDialog(hwnd, NULL); + return TRUE; + } + case IDCANCEL: { + EndDialog(hwnd, NULL); + return TRUE; + } + case 6300: {//Go Allies + HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox + HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox + int EnemyCount = SendMessage(LBB, LB_GETCOUNT, NULL, NULL); + if (EnemyCount <= 0) break; + int EnemyCurSelIndex = SendMessage(LBB, LB_GETCURSEL, NULL, NULL); + if (EnemyCurSelIndex < 0 || EnemyCurSelIndex >= EnemyCount) break; + int TextLen = SendMessage(LBB, LB_GETTEXTLEN, EnemyCurSelIndex, NULL); + if (TextLen == LB_ERR) break; + TCHAR* str = new TCHAR[TextLen + 1]; + SendMessage(LBB, LB_GETTEXT, EnemyCurSelIndex, (LPARAM)str); + SendMessage(LBB, LB_DELETESTRING, EnemyCurSelIndex, NULL); + SendMessage(LBA, LB_ADDSTRING, NULL, (LPARAM)str); + delete[] str; + break; + } + case 6301: {//Go Enemies + HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox + HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox + int AllieCount = SendMessage(LBA, LB_GETCOUNT, NULL, NULL); + if (AllieCount <= 0) break; + int AllieCurSelIndex = SendMessage(LBA, LB_GETCURSEL, NULL, NULL); + if (AllieCurSelIndex < 0 || AllieCurSelIndex >= AllieCount) break; + int TextLen = SendMessage(LBA, LB_GETTEXTLEN, AllieCurSelIndex, NULL); + if (TextLen == LB_ERR) break; + TCHAR* str = new TCHAR[TextLen + 1]; + SendMessage(LBA, LB_GETTEXT, AllieCurSelIndex, (LPARAM)str); + SendMessage(LBA, LB_DELETESTRING, AllieCurSelIndex, NULL); + SendMessage(LBB, LB_ADDSTRING, NULL, (LPARAM)str); + delete[] str; + break; + } + default: + break; + } } - default: - break; + else if (CODE == LBN_DBLCLK) + { + if (ID == 6303) // Go allies + { + HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox + HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox + int EnemyCount = SendMessage(LBB, LB_GETCOUNT, NULL, NULL); + if (EnemyCount <= 0) break; + int EnemyCurSelIndex = SendMessage(LBB, LB_GETCURSEL, NULL, NULL); + if (EnemyCurSelIndex < 0 || EnemyCurSelIndex >= EnemyCount) break; + int TextLen = SendMessage(LBB, LB_GETTEXTLEN, EnemyCurSelIndex, NULL); + if (TextLen == LB_ERR) break; + TCHAR* str = new TCHAR[TextLen + 1]; + SendMessage(LBB, LB_GETTEXT, EnemyCurSelIndex, (LPARAM)str); + SendMessage(LBB, LB_DELETESTRING, EnemyCurSelIndex, NULL); + SendMessage(LBA, LB_ADDSTRING, NULL, (LPARAM)str); + delete[] str; + } + else if (ID == 6302) // Go Enemies + { + HWND LBA = GetDlgItem(hwnd, 6302);//Allies ListBox + HWND LBB = GetDlgItem(hwnd, 6303);//Enemy ListBox + int AllieCount = SendMessage(LBA, LB_GETCOUNT, NULL, NULL); + if (AllieCount <= 0) break; + int AllieCurSelIndex = SendMessage(LBA, LB_GETCURSEL, NULL, NULL); + if (AllieCurSelIndex < 0 || AllieCurSelIndex >= AllieCount) break; + int TextLen = SendMessage(LBA, LB_GETTEXTLEN, AllieCurSelIndex, NULL); + if (TextLen == LB_ERR) break; + TCHAR* str = new TCHAR[TextLen + 1]; + SendMessage(LBA, LB_GETTEXT, AllieCurSelIndex, (LPARAM)str); + SendMessage(LBA, LB_DELETESTRING, AllieCurSelIndex, NULL); + SendMessage(LBB, LB_ADDSTRING, NULL, (LPARAM)str); + delete[] str; + } } break; } diff --git a/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.rc b/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.rc index 52f0ef4..2f516f1 100644 --- a/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.rc +++ b/FA2sp/ExtraWindow/CAllieEditor/CAllieEditor.rc @@ -9,8 +9,8 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,252,155,50,14 LTEXT "Enemies",-1,7,7,21,13,SS_SUNKEN LTEXT "Allies",-1,281,7,21,11,SS_SUNKEN - LISTBOX 6302,177,19,125,129,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - LISTBOX 6303,7,19,125,129,LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LISTBOX 6302,177,19,125,129,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP | LBS_NOTIFY + LISTBOX 6303,7,19,125,129,LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP | LBS_NOTIFY PUSHBUTTON "->",6300,137,57,36,14 PUSHBUTTON "<-",6301,137,78,36,14 EDITTEXT 6304,69,155,167,14,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP,WS_EX_STATICEDGE diff --git a/FA2sp/FA2sp.Constants.h b/FA2sp/FA2sp.Constants.h index 3b9747f..d9a3613 100644 --- a/FA2sp/FA2sp.Constants.h +++ b/FA2sp/FA2sp.Constants.h @@ -7,13 +7,13 @@ constexpr int PRODUCT_MAJOR = 1; constexpr int PRODUCT_MINOR = 0; -constexpr int PRODUCT_REVISION = 1; -constexpr char* PRODUCT_STR = "1.0.1"; +constexpr int PRODUCT_REVISION = 2; +constexpr char* PRODUCT_STR = "1.0.2"; constexpr char* DISPLAY_STR = PRODUCT_STR; -constexpr char* VERSION_STRVER = "FA2sp 1.0.1"; +constexpr char* VERSION_STRVER = "FA2sp 1.0.2"; constexpr char* PRODUCT_NAME = "FA2sp"; -constexpr char* APPLY_INFO = "Found Final Alert 2 version 1.02. Applying FA2sp 1.0.1."; +constexpr char* APPLY_INFO = "Found Final Alert 2 version 1.02. Applying FA2sp 1.0.2."; // constexpr char* APPLY_INFO = "Found Final Alert 2 version 1.02. Applying FA2sp - " __DATE__ " - " __TIME__; constexpr char* MUTEX_HASH_VAL = "b8097bca8590a4f46c975ebb43503aab2243ce7f1c87f12f7984dbe1"; diff --git a/FA2sp/Helpers/Bitmap.h b/FA2sp/Helpers/Bitmap.h deleted file mode 100644 index a858e7b..0000000 --- a/FA2sp/Helpers/Bitmap.h +++ /dev/null @@ -1,5001 +0,0 @@ -/* - ***************************************************************************** - * * - * Platform Independent * - * Bitmap Image Reader Writer Library * - * * - * Author: Arash Partow - 2002 * - * URL: http://partow.net/programming/bitmap/index.html * - * * - * Note: This library only supports 24-bits per pixel bitmap format files. * - * * - * Copyright notice: * - * Free use of the Platform Independent Bitmap Image Reader Writer Library * - * is permitted under the guidelines and in accordance with the most current * - * version of the MIT License. * - * http://www.opensource.org/licenses/MIT * - * * - ***************************************************************************** -*/ - - -#ifndef INCLUDE_BITMAP_IMAGE_HPP -#define INCLUDE_BITMAP_IMAGE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -class bitmap_image -{ -public: - - enum channel_mode { - rgb_mode = 0, - bgr_mode = 1 - }; - - enum color_plane { - blue_plane = 0, - green_plane = 1, - red_plane = 2 - }; - - struct rgb_t - { - unsigned char red; - unsigned char green; - unsigned char blue; - }; - - bitmap_image() - : file_name_(""), - width_(0), - height_(0), - row_increment_(0), - bytes_per_pixel_(3), - channel_mode_(bgr_mode) - {} - - bitmap_image(const std::string& filename) - : file_name_(filename), - width_(0), - height_(0), - row_increment_(0), - bytes_per_pixel_(0), - channel_mode_(bgr_mode) - { - load_bitmap(); - } - - bitmap_image(const unsigned int width, const unsigned int height) - : file_name_(""), - width_(width), - height_(height), - row_increment_(0), - bytes_per_pixel_(3), - channel_mode_(bgr_mode) - { - create_bitmap(); - } - - bitmap_image(const bitmap_image& image) - : file_name_(image.file_name_), - width_(image.width_), - height_(image.height_), - row_increment_(0), - bytes_per_pixel_(3), - channel_mode_(bgr_mode) - { - create_bitmap(); - data_ = image.data_; - } - - bitmap_image& operator=(const bitmap_image& image) - { - if (this != &image) - { - file_name_ = image.file_name_; - bytes_per_pixel_ = image.bytes_per_pixel_; - width_ = image.width_; - height_ = image.height_; - row_increment_ = 0; - channel_mode_ = image.channel_mode_; - create_bitmap(); - data_ = image.data_; - } - - return *this; - } - - inline bool operator!() - { - return (data_.size() == 0) || - (width_ == 0) || - (height_ == 0) || - (row_increment_ == 0); - } - - inline void clear(const unsigned char v = 0x00) - { - std::fill(data_.begin(), data_.end(), v); - } - - inline unsigned char red_channel(const unsigned int x, const unsigned int y) const - { - return data_[(y * row_increment_) + (x * bytes_per_pixel_ + 2)]; - } - - inline unsigned char green_channel(const unsigned int x, const unsigned int y) const - { - return data_[(y * row_increment_) + (x * bytes_per_pixel_ + 1)]; - } - - inline unsigned char blue_channel(const unsigned int x, const unsigned int y) const - { - return data_[(y * row_increment_) + (x * bytes_per_pixel_ + 0)]; - } - - inline void red_channel(const unsigned int x, const unsigned int y, const unsigned char value) - { - data_[(y * row_increment_) + (x * bytes_per_pixel_ + 2)] = value; - } - - inline void green_channel(const unsigned int x, const unsigned int y, const unsigned char value) - { - data_[(y * row_increment_) + (x * bytes_per_pixel_ + 1)] = value; - } - - inline void blue_channel(const unsigned int x, const unsigned int y, const unsigned char value) - { - data_[(y * row_increment_) + (x * bytes_per_pixel_ + 0)] = value; - } - - inline unsigned char* row(unsigned int row_index) const - { - return const_cast(&data_[(row_index * row_increment_)]); - } - - inline void get_pixel(const unsigned int x, const unsigned int y, - unsigned char& red, - unsigned char& green, - unsigned char& blue) const - { - const unsigned int y_offset = y * row_increment_; - const unsigned int x_offset = x * bytes_per_pixel_; - const unsigned int offset = y_offset + x_offset; - - blue = data_[offset + 0]; - green = data_[offset + 1]; - red = data_[offset + 2]; - } - - template - inline void get_pixel(const unsigned int x, const unsigned int y, RGB& colour) const - { - get_pixel(x, y, colour.red, colour.green, colour.blue); - } - - inline rgb_t get_pixel(const unsigned int x, const unsigned int y) const - { - rgb_t colour; - get_pixel(x, y, colour.red, colour.green, colour.blue); - return colour; - } - - inline void set_pixel(const unsigned int x, const unsigned int y, - const unsigned char red, - const unsigned char green, - const unsigned char blue) - { - const unsigned int y_offset = y * row_increment_; - const unsigned int x_offset = x * bytes_per_pixel_; - const unsigned int offset = y_offset + x_offset; - - data_[offset + 0] = blue; - data_[offset + 1] = green; - data_[offset + 2] = red; - } - - template - inline void set_pixel(const unsigned int x, const unsigned int y, const RGB& colour) - { - set_pixel(x, y, colour.red, colour.green, colour.blue); - } - - inline bool copy_from(const bitmap_image& image) - { - if ( - (image.height_ != height_) || - (image.width_ != width_) - ) - { - return false; - } - - data_ = image.data_; - - return true; - } - - inline bool copy_from(const bitmap_image& source_image, - const unsigned int& x_offset, - const unsigned int& y_offset) - { - if ((x_offset + source_image.width_) > width_) { return false; } - if ((y_offset + source_image.height_) > height_) { return false; } - - for (unsigned int y = 0; y < source_image.height_; ++y) - { - unsigned char* itr1 = row(y + y_offset) + x_offset * bytes_per_pixel_; - const unsigned char* itr2 = source_image.row(y); - const unsigned char* itr2_end = itr2 + source_image.width_ * bytes_per_pixel_; - - std::copy(itr2, itr2_end, itr1); - } - - return true; - } - - inline bool region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - bitmap_image& dest_image) const - { - if ((x + width) > width_) { return false; } - if ((y + height) > height_) { return false; } - - if ( - (dest_image.width_ < width_) || - (dest_image.height_ < height_) - ) - { - dest_image.setwidth_height(width, height); - } - - for (unsigned int r = 0; r < height; ++r) - { - unsigned char* itr1 = row(r + y) + x * bytes_per_pixel_; - unsigned char* itr1_end = itr1 + (width * bytes_per_pixel_); - unsigned char* itr2 = dest_image.row(r); - - std::copy(itr1, itr1_end, itr2); - } - - return true; - } - - inline bool roi_from_center(const unsigned int& cx, - const unsigned int& cy, - const unsigned int& width, - const unsigned int& height, - bitmap_image& dest_image) const - { - return region(cx - (width / 2), cy - (height / 2), - width, height, - dest_image); - } - - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const unsigned char& value) - { - if ((x + width) > width_) { return false; } - if ((y + height) > height_) { return false; } - - for (unsigned int r = 0; r < height; ++r) - { - unsigned char* itr = row(r + y) + x * bytes_per_pixel_; - unsigned char* itr_end = itr + (width * bytes_per_pixel_); - - std::fill(itr, itr_end, value); - } - - return true; - } - - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const color_plane color, - const unsigned char& value) - { - if ((x + width) > width_) { return false; } - if ((y + height) > height_) { return false; } - - const unsigned int color_plane_offset = offset(color); - - for (unsigned int r = 0; r < height; ++r) - { - unsigned char* itr = row(r + y) + x * bytes_per_pixel_ + color_plane_offset; - unsigned char* itr_end = itr + (width * bytes_per_pixel_); - - while (itr != itr_end) - { - *itr = value; - itr += bytes_per_pixel_; - } - } - - return true; - } - - inline bool set_region(const unsigned int& x, - const unsigned int& y, - const unsigned int& width, - const unsigned int& height, - const unsigned char& red, - const unsigned char& green, - const unsigned char& blue) - { - if ((x + width) > width_) { return false; } - if ((y + height) > height_) { return false; } - - for (unsigned int r = 0; r < height; ++r) - { - unsigned char* itr = row(r + y) + x * bytes_per_pixel_; - unsigned char* itr_end = itr + (width * bytes_per_pixel_); - - while (itr != itr_end) - { - *(itr++) = blue; - *(itr++) = green; - *(itr++) = red; - } - } - - return true; - } - - void reflective_image(bitmap_image& image, const bool include_diagnols = false) - { - image.setwidth_height(3 * width_, 3 * height_, true); - - image.copy_from(*this, width_, height_); - - vertical_flip(); - - image.copy_from(*this, width_, 0); - image.copy_from(*this, width_, 2 * height_); - - vertical_flip(); - horizontal_flip(); - - image.copy_from(*this, 0, height_); - image.copy_from(*this, 2 * width_, height_); - - horizontal_flip(); - - if (include_diagnols) - { - bitmap_image tile = *this; - - tile.vertical_flip(); - tile.horizontal_flip(); - - image.copy_from(tile, 0, 0); - image.copy_from(tile, 2 * width_, 0); - image.copy_from(tile, 2 * width_, 2 * height_); - image.copy_from(tile, 0, 2 * height_); - } - } - - inline unsigned int width() const - { - return width_; - } - - inline unsigned int height() const - { - return height_; - } - - inline unsigned int bytes_per_pixel() const - { - return bytes_per_pixel_; - } - - inline unsigned int pixel_count() const - { - return width_ * height_; - } - - inline void setwidth_height(const unsigned int width, - const unsigned int height, - const bool clear = false) - { - data_.clear(); - width_ = width; - height_ = height; - - create_bitmap(); - - if (clear) - { - std::fill(data_.begin(), data_.end(), static_cast(0x00)); - } - } - - void save_image(const std::string& file_name) const - { - std::ofstream stream(file_name.c_str(), std::ios::binary); - - if (!stream) - { - std::cerr << "bitmap_image::save_image(): Error - Could not open file " - << file_name << " for writing!" << std::endl; - return; - } - - bitmap_information_header bih; - - bih.width = width_; - bih.height = height_; - bih.bit_count = static_cast(bytes_per_pixel_ << 3); - bih.clr_important = 0; - bih.clr_used = 0; - bih.compression = 0; - bih.planes = 1; - bih.size = bih.struct_size(); - bih.x_pels_per_meter = 0; - bih.y_pels_per_meter = 0; - bih.size_image = (((bih.width * bytes_per_pixel_) + 3) & 0x0000FFFC) * bih.height; - - bitmap_file_header bfh; - - bfh.type = 19778; - bfh.size = bfh.struct_size() + bih.struct_size() + bih.size_image; - bfh.reserved1 = 0; - bfh.reserved2 = 0; - bfh.off_bits = bih.struct_size() + bfh.struct_size(); - - write_bfh(stream, bfh); - write_bih(stream, bih); - - unsigned int padding = (4 - ((3 * width_) % 4)) % 4; - char padding_data[4] = { 0x00, 0x00, 0x00, 0x00 }; - - for (unsigned int i = 0; i < height_; ++i) - { - const unsigned char* data_ptr = &data_[(row_increment_ * (height_ - i - 1))]; - - stream.write(reinterpret_cast(data_ptr), sizeof(unsigned char) * bytes_per_pixel_ * width_); - stream.write(padding_data, padding); - } - - stream.close(); - } - - inline void set_all_ith_bits_low(const unsigned int bitr_index) - { - unsigned char mask = static_cast(~(1 << bitr_index)); - - for (unsigned char* itr = data(); itr != end(); ++itr) - { - *itr &= mask; - } - } - - inline void set_all_ith_bits_high(const unsigned int bitr_index) - { - unsigned char mask = static_cast(1 << bitr_index); - - for (unsigned char* itr = data(); itr != end(); ++itr) - { - *itr |= mask; - } - } - - inline void set_all_ith_channels(const unsigned int& channel, const unsigned char& value) - { - for (unsigned char* itr = (data() + channel); itr < end(); itr += bytes_per_pixel_) - { - *itr = value; - } - } - - inline void set_channel(const color_plane color, const unsigned char& value) - { - for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) - { - *itr = value; - } - } - - inline void ror_channel(const color_plane color, const unsigned int& ror) - { - for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) - { - *itr = static_cast(((*itr) >> ror) | ((*itr) << (8 - ror))); - } - } - - inline void set_all_channels(const unsigned char& value) - { - for (unsigned char* itr = data(); itr < end(); ) - { - *(itr++) = value; - } - } - - inline void set_all_channels(const unsigned char& r_value, - const unsigned char& g_value, - const unsigned char& b_value) - { - for (unsigned char* itr = (data() + 0); itr < end(); itr += bytes_per_pixel_) - { - *(itr + 0) = b_value; - *(itr + 1) = g_value; - *(itr + 2) = r_value; - } - } - - inline void invert_color_planes() - { - for (unsigned char* itr = data(); itr < end(); *itr = ~(*itr), ++itr); - } - - inline void add_to_color_plane(const color_plane color, const unsigned char& value) - { - for (unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) - { - (*itr) += value; - } - } - - inline void convert_to_grayscale() - { - double r_scaler = 0.299; - double g_scaler = 0.587; - double b_scaler = 0.114; - - if (rgb_mode == channel_mode_) - { - std::swap(r_scaler, b_scaler); - } - - for (unsigned char* itr = data(); itr < end(); ) - { - unsigned char gray_value = static_cast - ( - (r_scaler * (*(itr + 2))) + - (g_scaler * (*(itr + 1))) + - (b_scaler * (*(itr + 0))) - ); - - *(itr++) = gray_value; - *(itr++) = gray_value; - *(itr++) = gray_value; - } - } - - inline const unsigned char* data() const - { - return data_.data(); - } - - inline unsigned char* data() - { - return const_cast(data_.data()); - } - - inline void bgr_to_rgb() - { - if ((bgr_mode == channel_mode_) && (3 == bytes_per_pixel_)) - { - reverse_channels(); - channel_mode_ = rgb_mode; - } - } - - inline void rgb_to_bgr() - { - if ((rgb_mode == channel_mode_) && (3 == bytes_per_pixel_)) - { - reverse_channels(); - channel_mode_ = bgr_mode; - } - } - - inline void reverse() - { - unsigned char* itr1 = data(); - unsigned char* itr2 = end() - bytes_per_pixel_; - - while (itr1 < itr2) - { - for (std::size_t i = 0; i < bytes_per_pixel_; ++i) - { - unsigned char* citr1 = itr1 + i; - unsigned char* citr2 = itr2 + i; - - std::swap(*citr1, *citr2); - } - - itr1 += bytes_per_pixel_; - itr2 -= bytes_per_pixel_; - } - } - - inline void horizontal_flip() - { - for (unsigned int y = 0; y < height_; ++y) - { - unsigned char* itr1 = row(y); - unsigned char* itr2 = itr1 + row_increment_ - bytes_per_pixel_; - - while (itr1 < itr2) - { - for (unsigned int i = 0; i < bytes_per_pixel_; ++i) - { - unsigned char* p1 = (itr1 + i); - unsigned char* p2 = (itr2 + i); - - std::swap(*p1, *p2); - } - - itr1 += bytes_per_pixel_; - itr2 -= bytes_per_pixel_; - } - } - } - - inline void vertical_flip() - { - for (unsigned int y = 0; y < (height_ / 2); ++y) - { - unsigned char* itr1 = row(y); - unsigned char* itr2 = row(height_ - y - 1); - - for (std::size_t x = 0; x < row_increment_; ++x) - { - std::swap(*(itr1 + x), *(itr2 + x)); - } - } - } - - inline void export_color_plane(const color_plane color, unsigned char* image) - { - for (unsigned char* itr = (data() + offset(color)); itr < end(); ++image, itr += bytes_per_pixel_) - { - (*image) = (*itr); - } - } - - inline void export_color_plane(const color_plane color, bitmap_image& image) - { - if ( - (width_ != image.width_) || - (height_ != image.height_) - ) - { - image.setwidth_height(width_, height_); - } - - image.clear(); - - unsigned char* itr1 = (data() + offset(color)); - unsigned char* itr1_end = end(); - unsigned char* itr2 = (image.data() + offset(color)); - - while (itr1 < itr1_end) - { - (*itr2) = (*itr1); - - itr1 += bytes_per_pixel_; - itr2 += bytes_per_pixel_; - } - } - - inline void export_response_image(const color_plane color, double* response_image) - { - double* resp_itr = response_image; - - for (unsigned char* itr = (data() + offset(color)); itr < end(); ++response_image, itr += bytes_per_pixel_) - { - *(resp_itr++) = (1.0 * (*itr)) / 256.0; - } - } - - inline void export_gray_scale_response_image(double* response_image) const - { - double* resp_itr = response_image; - - for (const unsigned char* itr = data(); itr < end(); itr += bytes_per_pixel_) - { - unsigned char gray_value = static_cast - ( - (0.299 * (*(itr + 2))) + - (0.587 * (*(itr + 1))) + - (0.114 * (*(itr + 0))) - ); - - *(resp_itr++) = (1.0 * gray_value) / 256.0; - } - } - - inline void export_rgb(double* red, double* green, double* blue) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - (*blue) = (1.0 * (*(itr++))) / 256.0; - (*green) = (1.0 * (*(itr++))) / 256.0; - (*red) = (1.0 * (*(itr++))) / 256.0; - } - } - - inline void export_rgb(float* red, float* green, float* blue) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - (*blue) = (1.0f * (*(itr++))) / 256.0f; - (*green) = (1.0f * (*(itr++))) / 256.0f; - (*red) = (1.0f * (*(itr++))) / 256.0f; - } - } - - inline void export_rgb(unsigned char* red, unsigned char* green, unsigned char* blue) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - (*blue) = *(itr++); - (*green) = *(itr++); - (*red) = *(itr++); - } - } - - inline void export_ycbcr(double* y, double* cb, double* cr) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++y, ++cb, ++cr) - { - const double blue = (1.0 * (*(itr++))); - const double green = (1.0 * (*(itr++))); - const double red = (1.0 * (*(itr++))); - - (*y) = clamp(16.0 + (1.0 / 256.0) * (65.738 * red + 129.057 * green + 25.064 * blue), 1.0, 254); - (*cb) = clamp(128.0 + (1.0 / 256.0) * (-37.945 * red - 74.494 * green + 112.439 * blue), 1.0, 254); - (*cr) = clamp(128.0 + (1.0 / 256.0) * (112.439 * red - 94.154 * green - 18.285 * blue), 1.0, 254); - } - } - - inline void export_rgb_normal(double* red, double* green, double* blue) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - (*blue) = (1.0 * (*(itr++))); - (*green) = (1.0 * (*(itr++))); - (*red) = (1.0 * (*(itr++))); - } - } - - inline void export_rgb_normal(float* red, float* green, float* blue) const - { - if (bgr_mode != channel_mode_) - return; - - for (const unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - (*blue) = (1.0f * (*(itr++))); - (*green) = (1.0f * (*(itr++))); - (*red) = (1.0f * (*(itr++))); - } - } - - inline void import_rgb(double* red, double* green, double* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(256.0 * (*blue)); - *(itr++) = static_cast(256.0 * (*green)); - *(itr++) = static_cast(256.0 * (*red)); - } - } - - inline void import_rgb(float* red, float* green, float* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(256.0f * (*blue)); - *(itr++) = static_cast(256.0f * (*green)); - *(itr++) = static_cast(256.0f * (*red)); - } - } - - inline void import_rgb(unsigned char* red, unsigned char* green, unsigned char* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = (*blue); - *(itr++) = (*green); - *(itr++) = (*red); - } - } - - inline void import_ycbcr(double* y, double* cb, double* cr) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++y, ++cb, ++cr) - { - double y_ = (*y); - double cb_ = (*cb); - double cr_ = (*cr); - - *(itr++) = static_cast(clamp((298.082 * y_ + 516.412 * cb_) / 256.0 - 276.836, 0.0, 255.0)); - *(itr++) = static_cast(clamp((298.082 * y_ - 100.291 * cb_ - 208.120 * cr_) / 256.0 + 135.576, 0.0, 255.0)); - *(itr++) = static_cast(clamp((298.082 * y_ + 408.583 * cr_) / 256.0 - 222.921, 0.0, 255.0)); - } - } - - inline void import_gray_scale_clamped(double* gray) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++gray) - { - unsigned char c = static_cast(clamp(256.0 * (*gray), 0.0, 255.0)); - - *(itr + 0) = c; - *(itr + 1) = c; - *(itr + 2) = c; - - itr += 3; - } - } - - inline void import_rgb_clamped(double* red, double* green, double* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(clamp(256.0 * (*blue), 0.0, 255.0)); - *(itr++) = static_cast(clamp(256.0 * (*green), 0.0, 255.0)); - *(itr++) = static_cast(clamp(256.0 * (*red), 0.0, 255.0)); - } - } - - inline void import_rgb_clamped(float* red, float* green, float* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(clamp(256.0f * (*blue), 0.0, 255.0)); - *(itr++) = static_cast(clamp(256.0f * (*green), 0.0, 255.0)); - *(itr++) = static_cast(clamp(256.0f * (*red), 0.0, 255.0)); - } - } - - inline void import_rgb_normal(double* red, double* green, double* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(*blue); - *(itr++) = static_cast(*green); - *(itr++) = static_cast(*red); - } - } - - inline void import_rgb_normal(float* red, float* green, float* blue) - { - if (bgr_mode != channel_mode_) - return; - - for (unsigned char* itr = data(); itr < end(); ++red, ++green, ++blue) - { - *(itr++) = static_cast(*blue); - *(itr++) = static_cast(*green); - *(itr++) = static_cast(*red); - } - } - - inline void subsample(bitmap_image& dest) const - { - /* - Half sub-sample of original image. - */ - unsigned int w = 0; - unsigned int h = 0; - - bool odd_width = false; - bool odd_height = false; - - if (0 == (width_ % 2)) - w = width_ / 2; - else - { - w = 1 + (width_ / 2); - odd_width = true; - } - - if (0 == (height_ % 2)) - h = height_ / 2; - else - { - h = 1 + (height_ / 2); - odd_height = true; - } - - unsigned int horizontal_upper = (odd_width) ? (w - 1) : w; - unsigned int vertical_upper = (odd_height) ? (h - 1) : h; - - dest.setwidth_height(w, h); - dest.clear(); - - unsigned char* s_itr[3]; - const unsigned char* itr1[3]; - const unsigned char* itr2[3]; - - s_itr[0] = dest.data() + 0; - s_itr[1] = dest.data() + 1; - s_itr[2] = dest.data() + 2; - - itr1[0] = data() + 0; - itr1[1] = data() + 1; - itr1[2] = data() + 2; - - itr2[0] = data() + row_increment_ + 0; - itr2[1] = data() + row_increment_ + 1; - itr2[2] = data() + row_increment_ + 2; - - unsigned int total = 0; - - for (unsigned int j = 0; j < vertical_upper; ++j) - { - for (unsigned int i = 0; i < horizontal_upper; ++i) - { - for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) - { - total = 0; - total += *(itr1[k]); - total += *(itr1[k]); - total += *(itr2[k]); - total += *(itr2[k]); - - itr1[k] += bytes_per_pixel_; - itr1[k] += bytes_per_pixel_; - itr2[k] += bytes_per_pixel_; - itr2[k] += bytes_per_pixel_; - - *(s_itr[k]) = static_cast(total >> 2); - } - } - - if (odd_width) - { - for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) - { - total = 0; - total += *(itr1[k]); - total += *(itr2[k]); - - itr1[k] += bytes_per_pixel_; - itr2[k] += bytes_per_pixel_; - - *(s_itr[k]) = static_cast(total >> 1); - } - } - - for (unsigned int k = 0; k < bytes_per_pixel_; ++k) - { - itr1[k] += row_increment_; - } - - if (j != (vertical_upper - 1)) - { - for (unsigned int k = 0; k < bytes_per_pixel_; ++k) - { - itr2[k] += row_increment_; - } - } - } - - if (odd_height) - { - for (unsigned int i = 0; i < horizontal_upper; ++i) - { - for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) - { - total = 0; - total += *(itr1[k]); - total += *(itr2[k]); - - itr1[k] += bytes_per_pixel_; - itr2[k] += bytes_per_pixel_; - - *(s_itr[k]) = static_cast(total >> 1); - } - } - - if (odd_width) - { - for (unsigned int k = 0; k < bytes_per_pixel_; ++k) - { - (*(s_itr[k])) = *(itr1[k]); - } - } - } - } - - inline void upsample(bitmap_image& dest) const - { - /* - 2x up-sample of original image. - */ - - dest.setwidth_height(2 * width_, 2 * height_); - dest.clear(); - - const unsigned char* s_itr[3]; - unsigned char* itr1[3]; - unsigned char* itr2[3]; - - s_itr[0] = data() + 0; - s_itr[1] = data() + 1; - s_itr[2] = data() + 2; - - itr1[0] = dest.data() + 0; - itr1[1] = dest.data() + 1; - itr1[2] = dest.data() + 2; - - itr2[0] = dest.data() + dest.row_increment_ + 0; - itr2[1] = dest.data() + dest.row_increment_ + 1; - itr2[2] = dest.data() + dest.row_increment_ + 2; - - for (unsigned int j = 0; j < height_; ++j) - { - for (unsigned int i = 0; i < width_; ++i) - { - for (unsigned int k = 0; k < bytes_per_pixel_; s_itr[k] += bytes_per_pixel_, ++k) - { - *(itr1[k]) = *(s_itr[k]); itr1[k] += bytes_per_pixel_; - *(itr1[k]) = *(s_itr[k]); itr1[k] += bytes_per_pixel_; - - *(itr2[k]) = *(s_itr[k]); itr2[k] += bytes_per_pixel_; - *(itr2[k]) = *(s_itr[k]); itr2[k] += bytes_per_pixel_; - } - } - - for (unsigned int k = 0; k < bytes_per_pixel_; ++k) - { - itr1[k] += dest.row_increment_; - itr2[k] += dest.row_increment_; - } - } - } - - inline void alpha_blend(const double& alpha, const bitmap_image& image) - { - if ( - (image.width_ != width_) || - (image.height_ != height_) - ) - { - return; - } - - if ((alpha < 0.0) || (alpha > 1.0)) - { - return; - } - - unsigned char* itr1 = data(); - const unsigned char* itr1_end = end(); - const unsigned char* itr2 = image.data(); - - double alpha_compliment = 1.0 - alpha; - - while (itr1 != itr1_end) - { - *(itr1) = static_cast((alpha * (*itr2)) + (alpha_compliment * (*itr1))); - ++itr1; - ++itr2; - } - } - - inline double psnr(const bitmap_image& image) - { - if ( - (image.width_ != width_) || - (image.height_ != height_) - ) - { - return 0.0; - } - - const unsigned char* itr1 = data(); - const unsigned char* itr2 = image.data(); - - double mse = 0.0; - - while (itr1 != end()) - { - const double v = (static_cast(*itr1) - static_cast(*itr2)); - - mse += v * v; - ++itr1; - ++itr2; - } - - if (mse <= 0.0000001) - { - return 1000000.0; - } - else - { - mse /= (3.0 * width_ * height_); - - return 20.0 * std::log10(255.0 / std::sqrt(mse)); - } - } - - inline double psnr(const unsigned int& x, - const unsigned int& y, - const bitmap_image& image) - { - if ((x + image.width()) > width_) { return 0.0; } - if ((y + image.height()) > height_) { return 0.0; } - - double mse = 0.0; - - const unsigned int height = image.height(); - const unsigned int width = image.width(); - - for (unsigned int r = 0; r < height; ++r) - { - const unsigned char* itr1 = row(r + y) + x * bytes_per_pixel_; - const unsigned char* itr1_end = itr1 + (width * bytes_per_pixel_); - const unsigned char* itr2 = image.row(r); - - while (itr1 != itr1_end) - { - double v = (static_cast(*itr1) - static_cast(*itr2)); - mse += v * v; - ++itr1; - ++itr2; - } - } - - if (mse <= 0.0000001) - { - return 1000000.0; - } - else - { - mse /= (3.0 * image.width() * image.height()); - return 20.0 * std::log10(255.0 / std::sqrt(mse)); - } - } - - inline void histogram(const color_plane color, double hist[256]) const - { - std::fill(hist, hist + 256, 0.0); - - for (const unsigned char* itr = (data() + offset(color)); itr < end(); itr += bytes_per_pixel_) - { - ++hist[(*itr)]; - } - } - - inline void histogram_normalized(const color_plane color, double hist[256]) const - { - histogram(color, hist); - - double* h_itr = hist; - const double* h_end = hist + 256; - const double pixel_count = static_cast(width_ * height_); - - while (h_end != h_itr) - { - *(h_itr++) /= pixel_count; - } - } - - inline unsigned int offset(const color_plane color) const - { - switch (channel_mode_) - { - case rgb_mode: { - switch (color) - { - case red_plane: return 0; - case green_plane: return 1; - case blue_plane: return 2; - default: return std::numeric_limits::max(); - } - } - - case bgr_mode: { - switch (color) - { - case red_plane: return 2; - case green_plane: return 1; - case blue_plane: return 0; - default: return std::numeric_limits::max(); - } - } - - default: return std::numeric_limits::max(); - } - } - - inline void incremental() - { - unsigned char current_color = 0; - - for (unsigned char* itr = data(); itr < end();) - { - (*itr++) = (current_color); - (*itr++) = (current_color); - (*itr++) = (current_color); - - ++current_color; - } - } - - inline void reverse_channels() - { - if (3 != bytes_per_pixel_) - return; - - for (unsigned char* itr = data(); itr < end(); itr += bytes_per_pixel_) - { - std::swap(*(itr + 0), *(itr + 2)); - } - } - -private: - - inline const unsigned char* end() const - { - return data_.data() + data_.size(); - } - - inline unsigned char* end() - { - return const_cast(data() + data_.size()); - } - - struct bitmap_file_header - { - unsigned short type; - unsigned int size; - unsigned short reserved1; - unsigned short reserved2; - unsigned int off_bits; - - unsigned int struct_size() const - { - return sizeof(type) + - sizeof(size) + - sizeof(reserved1) + - sizeof(reserved2) + - sizeof(off_bits); - } - - void clear() - { - std::memset(this, 0x00, sizeof(bitmap_file_header)); - } - }; - - struct bitmap_information_header - { - unsigned int size; - unsigned int width; - unsigned int height; - unsigned short planes; - unsigned short bit_count; - unsigned int compression; - unsigned int size_image; - unsigned int x_pels_per_meter; - unsigned int y_pels_per_meter; - unsigned int clr_used; - unsigned int clr_important; - - unsigned int struct_size() const - { - return sizeof(size) + - sizeof(width) + - sizeof(height) + - sizeof(planes) + - sizeof(bit_count) + - sizeof(compression) + - sizeof(size_image) + - sizeof(x_pels_per_meter) + - sizeof(y_pels_per_meter) + - sizeof(clr_used) + - sizeof(clr_important); - } - - void clear() - { - std::memset(this, 0x00, sizeof(bitmap_information_header)); - } - }; - - inline bool big_endian() const - { - unsigned int v = 0x01; - - return (1 != reinterpret_cast(&v)[0]); - } - - inline unsigned short flip(const unsigned short& v) const - { - return ((v >> 8) | (v << 8)); - } - - inline unsigned int flip(const unsigned int& v) const - { - return ( - ((v & 0xFF000000) >> 0x18) | - ((v & 0x000000FF) << 0x18) | - ((v & 0x00FF0000) >> 0x08) | - ((v & 0x0000FF00) << 0x08) - ); - } - - template - inline void read_from_stream(std::ifstream& stream, T& t) - { - stream.read(reinterpret_cast(&t), sizeof(T)); - } - - template - inline void write_to_stream(std::ofstream& stream, const T& t) const - { - stream.write(reinterpret_cast(&t), sizeof(T)); - } - - inline void read_bfh(std::ifstream& stream, bitmap_file_header& bfh) - { - read_from_stream(stream, bfh.type); - read_from_stream(stream, bfh.size); - read_from_stream(stream, bfh.reserved1); - read_from_stream(stream, bfh.reserved2); - read_from_stream(stream, bfh.off_bits); - - if (big_endian()) - { - bfh.type = flip(bfh.type); - bfh.size = flip(bfh.size); - bfh.reserved1 = flip(bfh.reserved1); - bfh.reserved2 = flip(bfh.reserved2); - bfh.off_bits = flip(bfh.off_bits); - } - } - - inline void write_bfh(std::ofstream& stream, const bitmap_file_header& bfh) const - { - if (big_endian()) - { - write_to_stream(stream, flip(bfh.type)); - write_to_stream(stream, flip(bfh.size)); - write_to_stream(stream, flip(bfh.reserved1)); - write_to_stream(stream, flip(bfh.reserved2)); - write_to_stream(stream, flip(bfh.off_bits)); - } - else - { - write_to_stream(stream, bfh.type); - write_to_stream(stream, bfh.size); - write_to_stream(stream, bfh.reserved1); - write_to_stream(stream, bfh.reserved2); - write_to_stream(stream, bfh.off_bits); - } - } - - inline void read_bih(std::ifstream& stream, bitmap_information_header& bih) - { - read_from_stream(stream, bih.size); - read_from_stream(stream, bih.width); - read_from_stream(stream, bih.height); - read_from_stream(stream, bih.planes); - read_from_stream(stream, bih.bit_count); - read_from_stream(stream, bih.compression); - read_from_stream(stream, bih.size_image); - read_from_stream(stream, bih.x_pels_per_meter); - read_from_stream(stream, bih.y_pels_per_meter); - read_from_stream(stream, bih.clr_used); - read_from_stream(stream, bih.clr_important); - - if (big_endian()) - { - bih.size = flip(bih.size); - bih.width = flip(bih.width); - bih.height = flip(bih.height); - bih.planes = flip(bih.planes); - bih.bit_count = flip(bih.bit_count); - bih.compression = flip(bih.compression); - bih.size_image = flip(bih.size_image); - bih.x_pels_per_meter = flip(bih.x_pels_per_meter); - bih.y_pels_per_meter = flip(bih.y_pels_per_meter); - bih.clr_used = flip(bih.clr_used); - bih.clr_important = flip(bih.clr_important); - } - } - - inline void write_bih(std::ofstream& stream, const bitmap_information_header& bih) const - { - if (big_endian()) - { - write_to_stream(stream, flip(bih.size)); - write_to_stream(stream, flip(bih.width)); - write_to_stream(stream, flip(bih.height)); - write_to_stream(stream, flip(bih.planes)); - write_to_stream(stream, flip(bih.bit_count)); - write_to_stream(stream, flip(bih.compression)); - write_to_stream(stream, flip(bih.size_image)); - write_to_stream(stream, flip(bih.x_pels_per_meter)); - write_to_stream(stream, flip(bih.y_pels_per_meter)); - write_to_stream(stream, flip(bih.clr_used)); - write_to_stream(stream, flip(bih.clr_important)); - } - else - { - write_to_stream(stream, bih.size); - write_to_stream(stream, bih.width); - write_to_stream(stream, bih.height); - write_to_stream(stream, bih.planes); - write_to_stream(stream, bih.bit_count); - write_to_stream(stream, bih.compression); - write_to_stream(stream, bih.size_image); - write_to_stream(stream, bih.x_pels_per_meter); - write_to_stream(stream, bih.y_pels_per_meter); - write_to_stream(stream, bih.clr_used); - write_to_stream(stream, bih.clr_important); - } - } - - inline std::size_t file_size(const std::string& file_name) const - { - std::ifstream file(file_name.c_str(), std::ios::in | std::ios::binary); - if (!file) return 0; - file.seekg(0, std::ios::end); - return static_cast(file.tellg()); - } - - void create_bitmap() - { - row_increment_ = width_ * bytes_per_pixel_; - data_.resize(height_ * row_increment_); - } - - void load_bitmap() - { - std::ifstream stream(file_name_.c_str(), std::ios::binary); - - if (!stream) - { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " - << "file " << file_name_ << " not found!" << std::endl; - return; - } - - width_ = 0; - height_ = 0; - - bitmap_file_header bfh; - bitmap_information_header bih; - - bfh.clear(); - bih.clear(); - - read_bfh(stream, bfh); - read_bih(stream, bih); - - if (bfh.type != 19778) - { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " - << "Invalid type value " << bfh.type << " expected 19778." << std::endl; - - bfh.clear(); - bih.clear(); - - stream.close(); - - return; - } - - if (bih.bit_count != 24) - { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " - << "Invalid bit depth " << bih.bit_count << " expected 24." << std::endl; - - bfh.clear(); - bih.clear(); - - stream.close(); - - return; - } - - if (bih.size != bih.struct_size()) - { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " - << "Invalid BIH size " << bih.size - << " expected " << bih.struct_size() << std::endl; - - bfh.clear(); - bih.clear(); - - stream.close(); - - return; - } - - width_ = bih.width; - height_ = bih.height; - - bytes_per_pixel_ = bih.bit_count >> 3; - - unsigned int padding = (4 - ((3 * width_) % 4)) % 4; - char padding_data[4] = { 0x00, 0x00, 0x00, 0x00 }; - - std::size_t bitmap_file_size = file_size(file_name_); - - std::size_t bitmap_logical_size = (height_ * width_ * bytes_per_pixel_) + - (height_ * padding) + - bih.struct_size() + - bfh.struct_size(); - - if (bitmap_file_size != bitmap_logical_size) - { - std::cerr << "bitmap_image::load_bitmap() ERROR: bitmap_image - " - << "Mismatch between logical and physical sizes of bitmap. " - << "Logical: " << bitmap_logical_size << " " - << "Physical: " << bitmap_file_size << std::endl; - - bfh.clear(); - bih.clear(); - - stream.close(); - - return; - } - - create_bitmap(); - - for (unsigned int i = 0; i < height_; ++i) - { - unsigned char* data_ptr = row(height_ - i - 1); // read in inverted row order - - stream.read(reinterpret_cast(data_ptr), sizeof(char) * bytes_per_pixel_ * width_); - stream.read(padding_data, padding); - } - } - - template - inline T clamp(const T& v, const T& lower_range, const T& upper_range) const - { - if (v < lower_range) - return lower_range; - else if (v > upper_range) - return upper_range; - else - return v; - } - - std::string file_name_; - unsigned int width_; - unsigned int height_; - unsigned int row_increment_; - unsigned int bytes_per_pixel_; - channel_mode channel_mode_; - std::vector data_; -}; - -typedef bitmap_image::rgb_t rgb_t; - -inline bool operator==(const rgb_t& c0, const rgb_t& c1) -{ - return (c0.red == c1.red) && - (c0.green == c1.green) && - (c0.blue == c1.blue); -} - -inline bool operator!=(const rgb_t& c0, const rgb_t& c1) -{ - return (c0.red != c1.red) || - (c0.green != c1.green) || - (c0.blue != c1.blue); -} - -inline std::size_t hamming_distance(const rgb_t& c0, const rgb_t& c1) -{ - std::size_t result = 0; - - if (c0.red != c1.red) ++result; - if (c0.green != c1.green) ++result; - if (c0.blue != c1.blue) ++result; - - return result; -} - -inline rgb_t make_colour(const unsigned int& red, const unsigned int& green, const unsigned int& blue) -{ - rgb_t result; - - result.red = static_cast(red); - result.green = static_cast(green); - result.blue = static_cast(blue); - - return result; -} - -template -inline void generate_colours(const std::size_t& steps, const rgb_t c0, const rgb_t& c1, OutputIterator out) -{ - double dr = ((double)c1.red - (double)c0.red) / steps; - double dg = ((double)c1.green - (double)c0.green) / steps; - double db = ((double)c1.blue - (double)c0.blue) / steps; - - for (std::size_t i = 0; i < steps; ++i) - { - rgb_t c; - - c.red = static_cast(c0.red + (i * dr)); - c.green = static_cast(c0.green + (i * dg)); - c.blue = static_cast(c0.blue + (i * db)); - - *(out++) = c; - } -} - -template -inline std::size_t convert_rsp_to_image(const ResponseImage& resp_image, const Palette& palette, bitmap_image& image) -{ - if ( - (resp_image.width() > image.width()) || - (resp_image.height() > image.height()) - ) - return 0; - - for (std::size_t y = 0; y < resp_image.height(); ++y) - { - for (std::size_t x = 0; x < resp_image.width(); ++x) - { - const double v = resp_image(x, y); - - unsigned int index = static_cast((v < 0) ? 0 : v > (palette.size()) ? (palette.size() - 1) : v); - - image.set_pixel(x, y, palette[index]); - } - } - - return (resp_image.width() * resp_image.height()); -} - -inline void rgb_to_ycbcr(const unsigned int& length, double* red, double* green, double* blue, - double* y, double* cb, double* cr) -{ - unsigned int i = 0; - - while (i < length) - { - (*y) = 16.0 + (65.481 * (*red) + 128.553 * (*green) + 24.966 * (*blue)); - (*cb) = 128.0 + (-37.797 * (*red) + -74.203 * (*green) + 112.000 * (*blue)); - (*cr) = 128.0 + (112.000 * (*red) + -93.786 * (*green) - 18.214 * (*blue)); - - ++i; - ++red; ++green; ++blue; - ++y; ++cb; ++cr; - } -} - -inline void ycbcr_to_rgb(const unsigned int& length, double* y, double* cb, double* cr, - double* red, double* green, double* blue) -{ - unsigned int i = 0; - - while (i < length) - { - double y_ = (*y) - 16.0; - double cb_ = (*cb) - 128.0; - double cr_ = (*cr) - 128.0; - - (*red) = 0.000456621 * y_ + 0.00625893 * cr_; - (*green) = 0.000456621 * y_ - 0.00153632 * cb_ - 0.00318811 * cr_; - (*blue) = 0.000456621 * y_ + 0.00791071 * cb_; - - ++i; - ++red; ++green; ++blue; - ++y; ++cb; ++cr; - } -} - -inline void subsample(const unsigned int& width, - const unsigned int& height, - const double* source, - unsigned int& w, - unsigned int& h, - double*& dest) -{ - /* Single channel. */ - - w = 0; - h = 0; - - bool odd_width = false; - bool odd_height = false; - - if (0 == (width % 2)) - w = width / 2; - else - { - w = 1 + (width / 2); - odd_width = true; - } - - if (0 == (height % 2)) - h = height / 2; - else - { - h = 1 + (height / 2); - odd_height = true; - } - - unsigned int horizontal_upper = (odd_width) ? w - 1 : w; - unsigned int vertical_upper = (odd_height) ? h - 1 : h; - - dest = new double[w * h]; - - double* s_itr = dest; - const double* itr1 = source; - const double* itr2 = source + width; - - for (unsigned int j = 0; j < vertical_upper; ++j) - { - for (unsigned int i = 0; i < horizontal_upper; ++i, ++s_itr) - { - (*s_itr) = *(itr1++); - (*s_itr) += *(itr1++); - (*s_itr) += *(itr2++); - (*s_itr) += *(itr2++); - (*s_itr) /= 4.0; - } - - if (odd_width) - { - (*(s_itr++)) = ((*itr1++) + (*itr2++)) / 2.0; - } - - itr1 += width; - - if (j != (vertical_upper - 1)) - { - itr2 += width; - } - } - - if (odd_height) - { - for (unsigned int i = 0; i < horizontal_upper; ++i, ++s_itr) - { - (*s_itr) += (*(itr1++)); - (*s_itr) += (*(itr1++)); - (*s_itr) /= 2.0; - } - - if (odd_width) - { - (*(s_itr++)) = (*itr1); - } - } -} - -inline void upsample(const unsigned int& width, - const unsigned int& height, - const double* source, - unsigned int& w, - unsigned int& h, - double*& dest) -{ - /* Single channel. */ - - w = 2 * width; - h = 2 * height; - - dest = new double[w * h]; - - const double* s_itr = source; - double* itr1 = dest; - double* itr2 = dest + w; - - for (unsigned int j = 0; j < height; ++j) - { - for (unsigned int i = 0; i < width; ++i, ++s_itr) - { - *(itr1++) = (*s_itr); - *(itr1++) = (*s_itr); - *(itr2++) = (*s_itr); - *(itr2++) = (*s_itr); - } - - itr1 += w; - itr2 += w; - } -} - -inline void checkered_pattern(const unsigned int x_width, - const unsigned int y_width, - const unsigned char value, - const bitmap_image::color_plane color, - bitmap_image& image) -{ - if ( - (x_width >= image.width()) || - (y_width >= image.height()) - ) - { - return; - } - - bool setter_x = false; - bool setter_y = true; - - const unsigned int color_plane_offset = image.offset(color); - const unsigned int height = image.height(); - const unsigned int width = image.width(); - - for (unsigned int y = 0; y < height; ++y) - { - if (0 == (y % y_width)) - { - setter_y = !setter_y; - } - - unsigned char* row = image.row(y) + color_plane_offset; - - for (unsigned int x = 0; x < width; ++x, row += image.bytes_per_pixel()) - { - if (0 == (x % x_width)) - { - setter_x = !setter_x; - } - - if (setter_x ^ setter_y) - { - *row = value; - } - } - } -} - -inline void checkered_pattern(const unsigned int x_width, - const unsigned int y_width, - const unsigned char red, - const unsigned char green, - const unsigned char blue, - bitmap_image& image) -{ - if ( - (x_width >= image.width()) || - (y_width >= image.height()) - ) - { - return; - } - - bool setter_x = false; - bool setter_y = true; - - const unsigned int height = image.height(); - const unsigned int width = image.width(); - - for (unsigned int y = 0; y < height; ++y) - { - if (0 == (y % y_width)) - { - setter_y = !setter_y; - } - - unsigned char* row = image.row(y); - - for (unsigned int x = 0; x < width; ++x, row += image.bytes_per_pixel()) - { - if (0 == (x % x_width)) - { - setter_x = !setter_x; - } - - if (setter_x ^ setter_y) - { - *(row + 0) = blue; - *(row + 1) = green; - *(row + 2) = red; - } - } - } -} - -inline void plasma(bitmap_image& image, - const double& x, const double& y, - const double& width, const double& height, - const double& c1, const double& c2, - const double& c3, const double& c4, - const double& roughness = 3.0, - const rgb_t colormap[] = 0) -{ - // Note: c1,c2,c3,c4 -> [0.0,1.0] - - const double half_width = (width / 2.0); - const double half_height = (height / 2.0); - - if ((width >= 1.0) || (height >= 1.0)) - { - const double corner1 = (c1 + c2) / 2.0; - const double corner2 = (c2 + c3) / 2.0; - const double corner3 = (c3 + c4) / 2.0; - const double corner4 = (c4 + c1) / 2.0; - double center = (c1 + c2 + c3 + c4) / 4.0 + - ((1.0 * ::rand() / (1.0 * RAND_MAX)) - 0.5) * // should use a better rng - ((1.0 * half_width + half_height) / (image.width() + image.height()) * roughness); - - center = std::min(std::max(0.0, center), 1.0); - - plasma(image, x, y, half_width, half_height, c1, corner1, center, corner4, roughness, colormap); - plasma(image, x + half_width, y, half_width, half_height, corner1, c2, corner2, center, roughness, colormap); - plasma(image, x + half_width, y + half_height, half_width, half_height, center, corner2, c3, corner3, roughness, colormap); - plasma(image, x, y + half_height, half_width, half_height, corner4, center, corner3, c4, roughness, colormap); - } - else - { - rgb_t color = colormap[static_cast(1000.0 * ((c1 + c2 + c3 + c4) / 4.0)) % 1000]; - - image.set_pixel(static_cast(x), static_cast(y), color); - } -} - -inline void plasma(bitmap_image& image, - const double& c1, const double& c2, - const double& c3, const double& c4, - const double& roughness = 3.0, - const rgb_t colormap[] = 0) -{ - plasma - ( - image, 0, 0, image.width(), image.height(), - c1, c2, c3, c4, - roughness, colormap - ); -} - -inline double psnr_region(const unsigned int& x, const unsigned int& y, - const unsigned int& width, const unsigned int& height, - const bitmap_image& image1, const bitmap_image& image2) -{ - if ( - (image1.width() != image2.width()) || - (image1.height() != image2.height()) - ) - { - return 0.0; - } - - if ((x + width) > image1.width()) { return 0.0; } - if ((y + height) > image1.height()) { return 0.0; } - - double mse = 0.0; - - for (unsigned int r = 0; r < height; ++r) - { - const unsigned char* itr1 = image1.row(r + y) + x * image1.bytes_per_pixel(); - const unsigned char* itr1_end = itr1 + (width * image1.bytes_per_pixel()); - const unsigned char* itr2 = image2.row(r + y) + x * image2.bytes_per_pixel(); - - while (itr1 != itr1_end) - { - double v = (static_cast(*itr1) - static_cast(*itr2)); - mse += v * v; - ++itr1; - ++itr2; - } - } - - if (mse <= 0.0000001) - { - return 1000000.0; - } - else - { - mse /= (3.0 * width * height); - return 20.0 * std::log10(255.0 / std::sqrt(mse)); - } -} - -inline void hierarchical_psnr_r(const double& x, const double& y, - const double& width, const double& height, - const bitmap_image& image1, - bitmap_image& image2, - const double& threshold, - const rgb_t colormap[]) -{ - if ((width <= 4.0) || (height <= 4.0)) - { - const double psnr = psnr_region - ( - static_cast(x), - static_cast(y), - static_cast(width), - static_cast(height), - image1, image2 - ); - - if (psnr < threshold) - { - rgb_t c = colormap[static_cast(1000.0 * (1.0 - (psnr / threshold)))]; - - image2.set_region - ( - static_cast(x), - static_cast(y), - static_cast(width + 1), - static_cast(height + 1), - c.red, c.green, c.blue - ); - } - } - else - { - const double half_width = (width / 2.0); - const double half_height = (height / 2.0); - - hierarchical_psnr_r(x, y, half_width, half_height, image1, image2, threshold, colormap); - hierarchical_psnr_r(x + half_width, y, half_width, half_height, image1, image2, threshold, colormap); - hierarchical_psnr_r(x + half_width, y + half_height, half_width, half_height, image1, image2, threshold, colormap); - hierarchical_psnr_r(x, y + half_height, half_width, half_height, image1, image2, threshold, colormap); - } -} - -inline void hierarchical_psnr(bitmap_image& image1, bitmap_image& image2, const double threshold, const rgb_t colormap[]) -{ - if ( - (image1.width() != image2.width()) || - (image1.height() != image2.height()) - ) - { - return; - } - - const double psnr = psnr_region - ( - 0, 0, image1.width(), image1.height(), - image1, image2 - ); - - if (psnr < threshold) - { - hierarchical_psnr_r - ( - 0, 0, image1.width(), image1.height(), - image1, image2, - threshold, - colormap - ); - } -} - -class image_drawer -{ -public: - - image_drawer(bitmap_image& image) - : image_(image), - pen_width_(1), - pen_color_red_(0), - pen_color_green_(0), - pen_color_blue_(0) - {} - - void rectangle(int x1, int y1, int x2, int y2) - { - line_segment(x1, y1, x2, y1); - line_segment(x2, y1, x2, y2); - line_segment(x2, y2, x1, y2); - line_segment(x1, y2, x1, y1); - } - - void triangle(int x1, int y1, int x2, int y2, int x3, int y3) - { - line_segment(x1, y1, x2, y2); - line_segment(x2, y2, x3, y3); - line_segment(x3, y3, x1, y1); - } - - void quadix(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) - { - line_segment(x1, y1, x2, y2); - line_segment(x2, y2, x3, y3); - line_segment(x3, y3, x4, y4); - line_segment(x4, y4, x1, y1); - } - - void line_segment(int x1, int y1, int x2, int y2) - { - int steep = 0; - int sx = ((x2 - x1) > 0) ? 1 : -1; - int sy = ((y2 - y1) > 0) ? 1 : -1; - int dx = abs(x2 - x1); - int dy = abs(y2 - y1); - - if (dy > dx) - { - std::swap(x1, y1); - std::swap(dx, dy); - std::swap(sx, sy); - - steep = 1; - } - - int e = 2 * dy - dx; - - for (int i = 0; i < dx; ++i) - { - if (steep) - plot_pen_pixel(y1, x1); - else - plot_pen_pixel(x1, y1); - - while (e >= 0) - { - y1 += sy; - e -= (dx << 1); - } - - x1 += sx; - e += (dy << 1); - } - - plot_pen_pixel(x2, y2); - } - - void horiztonal_line_segment(int x1, int x2, int y) - { - if (x1 > x2) - { - std::swap(x1, x2); - } - - for (int i = 0; i < (x2 - x1); ++i) - { - plot_pen_pixel(x1 + i, y); - } - } - - void vertical_line_segment(int y1, int y2, int x) - { - if (y1 > y2) - { - std::swap(y1, y2); - } - - for (int i = 0; i < (y2 - y1); ++i) - { - plot_pen_pixel(x, y1 + i); - } - } - - void ellipse(int centerx, int centery, int a, int b) - { - int t1 = a * a; - int t2 = t1 << 1; - int t3 = t2 << 1; - int t4 = b * b; - int t5 = t4 << 1; - int t6 = t5 << 1; - int t7 = a * t5; - int t8 = t7 << 1; - int t9 = 0; - - int d1 = t2 - t7 + (t4 >> 1); - int d2 = (t1 >> 1) - t8 + t5; - int x = a; - int y = 0; - - int negative_tx = centerx - x; - int positive_tx = centerx + x; - int negative_ty = centery - y; - int positive_ty = centery + y; - - while (d2 < 0) - { - plot_pen_pixel(positive_tx, positive_ty); - plot_pen_pixel(positive_tx, negative_ty); - plot_pen_pixel(negative_tx, positive_ty); - plot_pen_pixel(negative_tx, negative_ty); - - ++y; - - t9 = t9 + t3; - - if (d1 < 0) - { - d1 = d1 + t9 + t2; - d2 = d2 + t9; - } - else - { - x--; - t8 = t8 - t6; - d1 = d1 + (t9 + t2 - t8); - d2 = d2 + (t9 + t5 - t8); - negative_tx = centerx - x; - positive_tx = centerx + x; - } - - negative_ty = centery - y; - positive_ty = centery + y; - } - - do - { - plot_pen_pixel(positive_tx, positive_ty); - plot_pen_pixel(positive_tx, negative_ty); - plot_pen_pixel(negative_tx, positive_ty); - plot_pen_pixel(negative_tx, negative_ty); - - x--; - t8 = t8 - t6; - - if (d2 < 0) - { - ++y; - t9 = t9 + t3; - d2 = d2 + (t9 + t5 - t8); - negative_ty = centery - y; - positive_ty = centery + y; - } - else - d2 = d2 + (t5 - t8); - - negative_tx = centerx - x; - positive_tx = centerx + x; - } while (x >= 0); - } - - void circle(int centerx, int centery, int radius) - { - int x = 0; - int d = (1 - radius) << 1; - - while (radius >= 0) - { - plot_pen_pixel(centerx + x, centery + radius); - plot_pen_pixel(centerx + x, centery - radius); - plot_pen_pixel(centerx - x, centery + radius); - plot_pen_pixel(centerx - x, centery - radius); - - if ((d + radius) > 0) - d -= ((--radius) << 1) - 1; - if (x > d) - d += ((++x) << 1) + 1; - } - } - - void plot_pen_pixel(int x, int y) - { - switch (pen_width_) - { - case 1: plot_pixel(x, y); - break; - - case 2: { - plot_pixel(x, y); - plot_pixel(x + 1, y); - plot_pixel(x + 1, y + 1); - plot_pixel(x, y + 1); - } - break; - - case 3: { - plot_pixel(x, y - 1); - plot_pixel(x - 1, y - 1); - plot_pixel(x + 1, y - 1); - - plot_pixel(x, y); - plot_pixel(x - 1, y); - plot_pixel(x + 1, y); - - plot_pixel(x, y + 1); - plot_pixel(x - 1, y + 1); - plot_pixel(x + 1, y + 1); - } - break; - - default: plot_pixel(x, y); - break; - } - } - - void plot_pixel(int x, int y) - { - if ( - (x < 0) || - (y < 0) || - (x >= static_cast(image_.width())) || - (y >= static_cast(image_.height())) - ) - return; - - image_.set_pixel(x, y, pen_color_red_, pen_color_green_, pen_color_blue_); - } - - void pen_width(const unsigned int& width) - { - if ((width > 0) && (width < 4)) - { - pen_width_ = width; - } - } - - void pen_color(const unsigned char& red, - const unsigned char& green, - const unsigned char& blue) - { - pen_color_red_ = red; - pen_color_green_ = green; - pen_color_blue_ = blue; - } - - template - void pen_color(const RGB colour) - { - pen_color_red_ = colour.red; - pen_color_green_ = colour.green; - pen_color_blue_ = colour.blue; - } - -private: - - image_drawer(const image_drawer& id); - image_drawer& operator =(const image_drawer& id); - - bitmap_image& image_; - unsigned int pen_width_; - unsigned char pen_color_red_; - unsigned char pen_color_green_; - unsigned char pen_color_blue_; -}; - -class cartesian_canvas -{ -public: - - cartesian_canvas(const double x_length, const double y_length) - : width_div2_(0.0), - height_div2_(0.0), - min_x_(0.0), - min_y_(0.0), - max_x_(0.0), - max_y_(0.0), - draw_(image_) - { - setup_canvas(x_length, y_length); - } - - inline bool operator!() - { - return !image_; - } - - void rectangle(double x1, double y1, double x2, double y2) - { - line_segment(x1, y1, x2, y1); - line_segment(x2, y1, x2, y2); - line_segment(x2, y2, x1, y2); - line_segment(x1, y2, x1, y1); - } - - void triangle(double x1, double y1, double x2, double y2, double x3, double y3) - { - line_segment(x1, y1, x2, y2); - line_segment(x2, y2, x3, y3); - line_segment(x3, y3, x1, y1); - } - - void quadix(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) - { - line_segment(x1, y1, x2, y2); - line_segment(x2, y2, x3, y3); - line_segment(x3, y3, x4, y4); - line_segment(x4, y4, x1, y1); - } - - void line_segment(double x1, double y1, double x2, double y2) - { - if (clip(x1, y1, x2, y2)) - { - const int sc_x1 = static_cast(cart_to_screen_x(x1)); - const int sc_x2 = static_cast(cart_to_screen_x(x2)); - const int sc_y1 = static_cast(cart_to_screen_y(y1)); - const int sc_y2 = static_cast(cart_to_screen_y(y2)); - - draw_.line_segment(sc_x1, sc_y1, sc_x2, sc_y2); - } - } - - void horiztonal_line_segment(double x1, double x2, double y) - { - x1 = clamp_x(x1); - x2 = clamp_x(x2); - y = clamp_y(y); - - const int sc_x1 = static_cast(cart_to_screen_x(x1)); - const int sc_x2 = static_cast(cart_to_screen_x(x2)); - const int sc_y = static_cast(cart_to_screen_y(y)); - - draw_.horiztonal_line_segment(sc_x1, sc_x2, sc_y); - } - - void vertical_line_segment(double y1, double y2, double x) - { - y1 = clamp_y(y1); - y2 = clamp_y(y2); - x = clamp_x(x); - - const int sc_y1 = static_cast(cart_to_screen_y(y1)); - const int sc_y2 = static_cast(cart_to_screen_y(y2)); - const int sc_x = static_cast(cart_to_screen_x(x)); - - draw_.vertical_line_segment(sc_y1, sc_y2, sc_x); - } - - void ellipse(double centerx, double centery, double a, double b) - { - - const int sc_cx = static_cast(cart_to_screen_x(centerx)); - const int sc_cy = static_cast(cart_to_screen_y(centery)); - - draw_.ellipse(sc_cx, sc_cy, static_cast(a), static_cast(b)); - } - - void circle(double centerx, double centery, double radius) - { - const int sc_cx = static_cast(cart_to_screen_x(centerx)); - const int sc_cy = static_cast(cart_to_screen_y(centery)); - - draw_.circle(sc_cx, sc_cy, static_cast(radius)); - } - - void fill_rectangle(double x1, double y1, double x2, double y2) - { - if (y1 > y2) - std::swap(y1, y2); - - for (double y = y1; y <= y2; y += 0.5) - { - line_segment(x1, y, x2, y); - } - } - - void fill_triangle(double x1, double y1, double x2, double y2, double x3, double y3) - { - typedef std::pair point_t; - - std::vector p; - - p.push_back(std::make_pair(x1, y1)); - p.push_back(std::make_pair(x2, y2)); - p.push_back(std::make_pair(x3, y3)); - - if (p[0].second > p[1].second) - std::swap(p[0], p[1]); - if (p[0].second > p[2].second) - std::swap(p[0], p[2]); - if (p[1].second > p[2].second) - std::swap(p[1], p[2]); - - class draw_modes - { - private: - - cartesian_canvas& canvas; - - // Needed for incompetent and broken msvc compiler versions -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4822) -#endif - draw_modes& operator=(const draw_modes&); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - public: - - draw_modes(cartesian_canvas& c) - : canvas(c) - {} - - void bottom(const point_t& p0, const point_t& p1, const point_t& p2) - { - const double m0 = (p1.first - p0.first) / (2.0 * (p1.second - p0.second)); - const double m1 = (p2.first - p0.first) / (2.0 * (p2.second - p0.second)); - - double x0 = p0.first; - double x1 = p0.first; - - for (double y = p0.second; y <= p1.second; y += 0.5) - { - canvas.horiztonal_line_segment(x0, x1, y); - - x0 += m0; - x1 += m1; - } - } - - void top(const point_t& p0, const point_t& p1, const point_t& p2) - { - const double m0 = (p2.first - p0.first) / (2.0 * (p2.second - p0.second)); - const double m1 = (p2.first - p1.first) / (2.0 * (p2.second - p1.second)); - - double x0 = p2.first; - double x1 = p2.first; - - for (double y = p2.second; y >= p0.second; y -= 0.5) - { - canvas.horiztonal_line_segment(x0, x1, y); - - x0 -= m0; - x1 -= m1; - } - } - }; - - draw_modes dm(*this); - - const double eps = 0.00001; - - if (std::abs(p[1].second - p[2].second) < eps) - dm.bottom(p[0], p[1], p[2]); - else if (std::abs(p[0].second - p[1].second) < eps) - dm.top(p[0], p[1], p[2]); - else - { - point_t p3; - - p3.first = (p[0].first + ((p[1].second - p[0].second) / (p[2].second - p[0].second)) * (p[2].first - p[0].first)); - p3.second = p[1].second; - - dm.bottom(p[0], p[1], p3); - dm.top(p[1], p3, p[2]); - } - } - - void fill_quadix(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) - { - fill_triangle(x1, y1, x2, y2, x3, y3); - fill_triangle(x1, y1, x3, y3, x4, y4); - } - - void fill_circle(double cx, double cy, double radius) - { - const double delta = 1.0; - double x = radius; - double y = 0.0; - double dx = delta - (2.0 * delta * radius); - double dy = 0.0; - double dr = 0.0; - - while (x >= y) - { - for (double i = cx - x; i <= cx + x; i += delta) - { - horiztonal_line_segment(cx - x, cx + x, cy + y); - horiztonal_line_segment(cx - x, cx + x, cy - y); - } - - for (double i = cx - y; i <= cx + y; i += delta) - { - horiztonal_line_segment(cx - y, cx + y, cy + x); - horiztonal_line_segment(cx - y, cx + y, cy - x); - } - - y += delta; - - dr += dy; - dy += 2.0 * delta; - - if ((2.0 * delta * dr + dx) > 0) - { - x -= delta; - dr += dx; - dx += 2.0 * delta; - } - } - } - - void plot_pen_pixel(double x, double y) - { - if ((x < min_x_) || (x > max_x_)) return; - if ((y < min_y_) || (y > max_y_)) return; - - const int sc_x = static_cast(cart_to_screen_x(x)); - const int sc_y = static_cast(cart_to_screen_y(y)); - - draw_.plot_pen_pixel(sc_x, sc_y); - } - - void plot_pixel(double x, double y) - { - if ((x < min_x_) || (x > max_x_)) return; - if ((y < min_y_) || (y > max_y_)) return; - - const int sc_x = static_cast(cart_to_screen_x(x)); - const int sc_y = static_cast(cart_to_screen_y(y)); - - draw_.plot_pixel(sc_x, sc_y); - } - - void pen_width(const unsigned int& width) - { - draw_.pen_width(width); - } - - void pen_color(const unsigned char& red, - const unsigned char& green, - const unsigned char& blue) - { - draw_.pen_color(red, green, blue); - } - - template - void pen_color(const RGB colour) - { - draw_.pen_color(colour); - } - - const bitmap_image& image() const - { - return image_; - } - - bitmap_image& image() - { - return image_; - } - - void set_widthheight(const double x_length, const double y_length) - { - setup_canvas(x_length, y_length); - } - - double min_x() const { return min_x_; } - double min_y() const { return min_y_; } - double max_x() const { return max_x_; } - double max_y() const { return max_y_; } - -private: - - void setup_canvas(const double x_length, const double y_length) - { - if ((x_length < 2.0) || (y_length < 2.0)) - return; - - width_div2_ = x_length / 2.0; - height_div2_ = y_length / 2.0; - - min_x_ = -width_div2_; - min_y_ = -height_div2_; - max_x_ = width_div2_; - max_y_ = height_div2_; - - image_.setwidth_height(static_cast(x_length) + 1, static_cast(y_length) + 1); - - image_.clear(0xFF); - } - - double clamp_x(const double& x) - { - if (x < min_x_) return min_x_; - else if (x > max_x_) return max_x_; - else return x; - } - - double clamp_y(const double& y) - { - if (y < min_y_) return min_y_; - else if (y > max_y_) return max_y_; - else return y; - } - - double cart_to_screen_x(const double& x) - { - return x + width_div2_; - } - - double cart_to_screen_y(const double& y) - { - return height_div2_ - y; - } - - enum clip_code - { - e_clip_bottom = 1, - e_clip_top = 2, - e_clip_left = 4, - e_clip_right = 8 - }; - - int out_code( - const double& x, const double& y, - const double& x1, const double& y1, - const double& x2, const double& y2 - ) - { - int result = 0; - if (y < y1) result |= e_clip_bottom; - else if (y > y2) result |= e_clip_top; - - if (x < x1) result |= e_clip_left; - else if (x > x2) result |= e_clip_right; - - return result; - } - - bool clip(double& x1, double& y1, double& x2, double& y2) - { - bool result = false; - double x = 0.0; - double y = 0.0; - - int outcode0 = out_code(x1, y1, min_x_, min_y_, max_x_, max_y_); - int outcode1 = out_code(x2, y2, min_x_, min_y_, max_x_, max_y_); - int outcodeout = 0; - - while ((outcode0 != 0) || (outcode1 != 0)) - { - if ((outcode0 & outcode1) != 0) - return result; - else - { - if (outcode0 != 0) - outcodeout = outcode0; - else - outcodeout = outcode1; - - double dx = (x2 - x1); - double dy = (y2 - y1); - - if ((outcodeout & e_clip_bottom) == e_clip_bottom) - { - x = x1 + dx * (min_y_ - y1) / dy; - y = min_y_; - } - else if ((outcodeout & e_clip_top) == e_clip_top) - { - x = x1 + dx * (max_y_ - y1) / dy; - y = max_y_; - } - else if ((outcodeout & e_clip_right) == e_clip_right) - { - y = y1 + dy * (max_x_ - x1) / dx; - x = max_x_; - } - else if ((outcodeout & e_clip_left) == e_clip_left) - { - y = y1 + dy * (min_x_ - x1) / dx; - x = min_x_; - } - - if (outcodeout == outcode0) - { - x1 = x; - y1 = y; - outcode0 = out_code(x1, y1, min_x_, min_y_, max_x_, max_y_); - } - else - { - x2 = x; - y2 = y; - outcode1 = out_code(x2, y2, min_x_, min_y_, max_x_, max_y_); - } - } - } - - return true; - } - - cartesian_canvas(const cartesian_canvas&); - cartesian_canvas operator=(const cartesian_canvas&); - - double width_div2_; - double height_div2_; - double min_x_; - double min_y_; - double max_x_; - double max_y_; - bitmap_image image_; - image_drawer draw_; -}; - -inline rgb_t convert_wave_length_nm_to_rgb(const double wave_length_nm) -{ - // Credits: Dan Bruton http://www.physics.sfasu.edu/astro/color.html - double red = 0.0; - double green = 0.0; - double blue = 0.0; - - if ((380.0 <= wave_length_nm) && (wave_length_nm <= 439.0)) - { - red = -(wave_length_nm - 440.0) / (440.0 - 380.0); - green = 0.0; - blue = 1.0; - } - else if ((440.0 <= wave_length_nm) && (wave_length_nm <= 489.0)) - { - red = 0.0; - green = (wave_length_nm - 440.0) / (490.0 - 440.0); - blue = 1.0; - } - else if ((490.0 <= wave_length_nm) && (wave_length_nm <= 509.0)) - { - red = 0.0; - green = 1.0; - blue = -(wave_length_nm - 510.0) / (510.0 - 490.0); - } - else if ((510.0 <= wave_length_nm) && (wave_length_nm <= 579.0)) - { - red = (wave_length_nm - 510.0) / (580.0 - 510.0); - green = 1.0; - blue = 0.0; - } - else if ((580.0 <= wave_length_nm) && (wave_length_nm <= 644.0)) - { - red = 1.0; - green = -(wave_length_nm - 645.0) / (645.0 - 580.0); - blue = 0.0; - } - else if ((645.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) - { - red = 1.0; - green = 0.0; - blue = 0.0; - } - - double factor = 0.0; - - if ((380.0 <= wave_length_nm) && (wave_length_nm <= 419.0)) - factor = 0.3 + 0.7 * (wave_length_nm - 380.0) / (420.0 - 380.0); - else if ((420.0 <= wave_length_nm) && (wave_length_nm <= 700.0)) - factor = 1.0; - else if ((701.0 <= wave_length_nm) && (wave_length_nm <= 780.0)) - factor = 0.3 + 0.7 * (780.0 - wave_length_nm) / (780.0 - 700.0); - else - factor = 0.0; - - rgb_t result; - - const double gamma = 0.8; - const double intensity_max = 255.0; - -#define round(d) std::floor(d + 0.5) - - result.red = static_cast((red == 0.0) ? red : round(intensity_max * std::pow(red * factor, gamma))); - result.green = static_cast((green == 0.0) ? green : round(intensity_max * std::pow(green * factor, gamma))); - result.blue = static_cast((blue == 0.0) ? blue : round(intensity_max * std::pow(blue * factor, gamma))); - -#undef round - - return result; -} - -inline double weighted_distance(const unsigned char r0, const unsigned char g0, const unsigned char b0, - const unsigned char r1, const unsigned char g1, const unsigned char b1) -{ - const double diff_r = /*0.30 */ (r0 - r1); - const double diff_g = /*0.59 */ (g0 - g1); - const double diff_b = /*0.11 */ (b0 - b1); - - return std::sqrt((diff_r * diff_r) + (diff_g * diff_g) + (diff_b * diff_b)); -} - -inline double weighted_distance(const rgb_t c0, const rgb_t c1) -{ - return weighted_distance(c0.red, c0.green, c0.blue, - c1.red, c1.green, c1.blue); -} - -template -inline rgb_t find_nearest_color(const rgb_t& c, const Iterator begin, const Iterator end) -{ - if (0 == std::distance(begin, end)) - return c; - - double min_d = std::numeric_limits::max(); - rgb_t result = *begin; - - for (Iterator itr = begin; itr != end; ++itr) - { - if (c == (*itr)) - { - return (*itr); - } - - double curr_d = weighted_distance(c, *itr); - - if (curr_d < min_d) - { - min_d = curr_d; - result = *itr; - } - } - - return result; -} - -template