code_twain/sane/DlgGamma.cpp

757 lines
18 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// DlgIndicator.cpp: 实现文件
//
#include "DlgGamma.h"
#include "resource.h"
#include "mem_dc.h"
#define MAX_KEY_POINTS 4 // not include (0, 0) and (255, 255)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// calculator
#include <math.h>
namespace calc
{
void solve_matrix(double** a, int n, std::vector<double>& coef)
{
int m = 0;
int i, j;
coef.clear();
for (j = 0; j < n; j++) {
double max = 0;
double imax = 0;
for (i = j; i < n; i++) {
if (imax < fabs(a[i][j])) {
imax = fabs(a[i][j]);
max = a[i][j];//得到各行中所在列最大元素
m = i;
}
}
if (fabs(a[j][j]) != max) {
double b = 0;
for (int k = j; k < n + 1; k++) {
b = a[j][k];
a[j][k] = a[m][k];
a[m][k] = b;
}
}
for (int r = j; r < n + 1; r++) {
a[j][r] = a[j][r] / max;//让该行的所在列除以所在列的第一个元素目的是让首元素为1
}
for (i = j + 1; i < n; i++) {
double c = a[i][j];
if (c == 0) continue;
for (int s = j; s < n + 1; s++) {
//double tempdata = a[i][s];
a[i][s] = a[i][s] - a[j][s] * c;//前后行数相减使下一行或者上一行的首元素为0
}
}
}
for (i = n - 2; i >= 0; i--) {
for (j = i + 1; j < n; j++) {
a[i][n] = a[i][n] - a[j][n] * a[i][j];
}
}
for (int k = 0; k < n; k++) {
coef.push_back(a[k][n]);
}
}
int power(int m, int exp)
{
int val = 1;
for (int i = 0; i < exp; ++i)
val *= m;
return val;
}
std::vector<double> coefs_from_points(const std::vector<POINT>& pt)
{
std::vector<double> coef;
double** a = new double* [pt.size()];
for (int i = 0; i < (int)pt.size(); ++i)
a[i] = new double[pt.size() + 1];
for (int i = 0; i < (int)pt.size(); ++i)
{
for (int j = 0; j < (int)pt.size(); ++j)
a[i][j] = power(pt[i].x, pt.size() - j - 1);
a[i][pt.size()] = pt[i].y;
}
solve_matrix(a, pt.size(), coef);
for (int i = 0; i < (int)pt.size(); ++i)
delete[] a[i];
delete[] a;
return coef;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// dlg_gamma
dlg_gamma::dlg_gamma(HWND parent, bool color) : dlg_base(parent, IDD_GAMMA), exit_code_(0), is_color_(color), bkgnd_(NULL)
, show_all_(false)
{
create();
}
dlg_gamma::~dlg_gamma()
{
DeleteObject(bkgnd_);
}
BOOL dlg_gamma::handle_message(UINT msg, WPARAM wp, LPARAM lp)
{
wchar_t text[40] = { 0 };
BOOL ret = TRUE;
switch (msg)
{
case WM_INITDIALOG:
on_init_dlg();
UpdateWindow(hwnd());
break;
case WM_COMMAND:
handle_command(HIWORD(wp), LOWORD(wp), (HWND)lp);
break;
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd(), &ps);
{
compatible_dc dc(hdc);
on_paint(dc.get_dc());
}
EndPaint(hwnd(), &ps);
}
break;
case WM_MOUSEMOVE:
on_mouse_move(wp, LOWORD(lp), HIWORD(lp));
break;
case WM_LBUTTONDOWN:
on_lbutton_down(LOWORD(lp), HIWORD(lp));
break;
case WM_LBUTTONUP:
on_lbutton_up(LOWORD(lp), HIWORD(lp));
break;
default:
ret = FALSE;
}
return ret;
}
void dlg_gamma::handle_command(WORD code, WORD id, HANDLE ctrl)
{
if (code == BN_CLICKED)
{
if (id == IDC_BUTTON_RESET)
{
init_curve(cur_, cur_->clr);
InvalidateRect(hwnd(), NULL, FALSE);
}
else
{
exit_code_ = id;
}
}
else if (code == EN_CHANGE)
{
if (id == IDC_EDIT_INPUT)
{
wchar_t val[40] = { 0 };
int y = 0;
GetDlgItemTextW(hwnd(), id, val, _countof(val) - 1);
y = _wtoi(val);
if (y < cur_->points[0].x)
SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, cur_->points[0].x, FALSE);
if (y > cur_->points[1].x)
SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, cur_->points[1].x, FALSE);
else
{
y = calc_value(y);
SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, y, FALSE);
}
}
}
else if (code == CBN_SELCHANGE)
{
on_combo_sel_changed(id, SendMessage((HWND)ctrl, CB_GETCURSEL, 0, 0));
}
}
void dlg_gamma::create_background(void)
{
COLORREF bclr = RGB(128, 128, 128);
HDC hdc = GetWindowDC(hwnd()),
cdc = CreateCompatibleDC(hdc);
HBRUSH bkg = CreateSolidBrush(bclr);
HPEN grid = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)),
old = (HPEN)SelectObject(cdc, grid);
HBITMAP ob = NULL;
int w = paint_area_.right - paint_area_.left + 2,
h = paint_area_.bottom - paint_area_.top + 2;
RECT r = { 1, 1, w - 1, h - 1};
if (bkgnd_)
DeleteObject(bkgnd_);
// create bitmap and select into DC
bkgnd_ = CreateCompatibleBitmap(hdc, w, h);
ob = (HBITMAP)SelectObject(cdc, bkgnd_);
SetBkColor(cdc, bclr);
// background and grid
FillRect(cdc, &r, bkg);
MoveToEx(cdc, r.left, r.top, NULL);
LineTo(cdc, r.right, r.top);
LineTo(cdc, r.right, r.bottom);
LineTo(cdc, r.left, r.bottom);
LineTo(cdc, r.left, r.top);
SelectObject(cdc, old);
DeleteObject(grid);
grid = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
SelectObject(cdc, grid);
for (int i = 1; i < 6; ++i)
{
MoveToEx(cdc, r.left + i * 50, r.bottom, NULL);
LineTo(cdc, r.left + i * 50, r.top);
MoveToEx(cdc, r.left, r.bottom - i * 50, NULL);
LineTo(cdc, r.right, r.bottom - i * 50);
}
// default curve
MoveToEx(cdc, r.left, r.bottom, NULL);
LineTo(cdc, r.right, r.top);
// free resource
SelectObject(cdc, old);
SelectObject(cdc, ob);
DeleteDC(cdc);
ReleaseDC(hwnd(), hdc);
DeleteObject(bkg);
DeleteObject(grid);
}
void dlg_gamma::init_curve(GAMMACURVE* curv, COLORREF clr)
{
curv->points.clear();
curv->points.push_back({ 0, 0 });
curv->points.push_back({ 255, 255 });
curv->coefs = calc::coefs_from_points(curv->points);
curv->drag = -1;
curv->left = 0;
curv->right = 1;
curv->clr = clr;
}
int dlg_gamma::add_drag_point(int x, int y)
{
int ind = -1;
if (cur_->points.size() < MAX_KEY_POINTS + 2)
{
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
if (x < cur_->points[i].x)
{
cur_->points.insert(cur_->points.begin() + i, { x, calc_value(x) });
ind = i;
cur_->coefs = calc::coefs_from_points(cur_->points);
break;
}
else if (x == cur_->points[i].x)
{
ind = i;
break;
}
}
if (ind == -1)
{
ind = cur_->points.size();
cur_->points.push_back({ x, calc_value(x) });
cur_->coefs = calc::coefs_from_points(cur_->points);
}
cur_->left = ind - 1;
cur_->right = ind + 1;
if (cur_->left == 1)
cur_->left = 0;
if (cur_->right >= (int)cur_->points.size())
cur_->right = 1;
}
else
{
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
if (x == cur_->points[i].x)
{
ind = i;
cur_->left = ind - 1;
cur_->right = ind + 1;
if (cur_->left == 1)
cur_->left = 0;
if (cur_->right >= (int)cur_->points.size())
cur_->right = 1;
break;
}
}
}
cur_->drag = ind;
return ind;
}
BYTE dlg_gamma::calc_value(BYTE x)
{
double y = .0f;
for(int j = 0; j < (int)cur_->points.size(); ++j)
y += cur_->coefs[j] * calc::power(x, cur_->points.size() - j - 1);
if (y > 255.0f)
y = 255.0f;
if (y < .0f)
y = .0f;
bool upper = cur_->points[1].y > cur_->points[0].y;
if (y < cur_->points[!upper].y)
y = cur_->points[!upper].y;
if (y > cur_->points[upper].y)
y = cur_->points[upper].y;
return (BYTE)(y + .5f);
}
bool dlg_gamma::is_adjacent(POINT p1, POINT p2)
{
bool neighbour = abs(p1.x - p2.x) <= 3 && abs(p1.y - p2.y) <= 3;
return neighbour;
}
bool dlg_gamma::hit_test(int* x, int* y)
{
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
POINT pt = { *x, *y };
if (is_adjacent(pt, cur_->points[i]))
{
*x = cur_->points[i].x;
*y = cur_->points[i].y;
return true;
}
}
int val = calc_value(*x), tolerant = 3;
bool hit = false;
if (abs(val - *y) < tolerant)
{
*y = val;
hit = true;
}
else
{
int l = *x - tolerant,
u = *x + tolerant;
if (l < 0)
l = 0;
if (u > 255)
u = 255;
for (; l <= u; ++l)
{
val = calc_value(l);
if (abs(val - *y) < tolerant)
{
hit = true;
*x = l;
*y = val;
break;
}
}
}
return hit;
}
void dlg_gamma::draw_ellipse(HDC hdc, POINT center, int xl, int yl)
{
center.x += paint_area_.left;
center.y = paint_area_.bottom - center.y;
HRGN rgn = CreateEllipticRgn(center.x - xl, center.y - yl, center.x + xl, center.y + yl);
HBRUSH brsh = CreateSolidBrush(RGB(255, 0, 255));
FillRgn(hdc, rgn, brsh);
DeleteObject(brsh);
DeleteObject(rgn);
}
void dlg_gamma::draw_current_curve(HDC hdc)
{
MoveToEx(hdc, paint_area_.left + cur_->points[0].x, paint_area_.bottom - cur_->points[0].y, NULL);
for (int i = 0; i < 256; ++i)
{
int y = calc_value(i);
LineTo(hdc, paint_area_.left + i, paint_area_.bottom - y);
}
}
void dlg_gamma::on_init_dlg(void)
{
HWND combo = get_item(IDC_SCHEME);
GetWindowRect(get_item(IDC_STATIC_PAINT), &paint_area_);
screen_2_client(&paint_area_);
paint_area_.right = paint_area_.left + 255;
paint_area_.bottom = paint_area_.top + 255;
create_background();
init_curve(&rgb_gray_);
init_curve(&red_, RGB(255, 0, 0));
init_curve(&green_, RGB(0, 255, 0));
init_curve(&blue_, RGB(0, 0, 255));
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u81ea\u5b9a\u4e49");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u8d1f\u7247\uff08RGB\uff09");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u5f69\u8272\u8d1f\u7247\uff08RGB\uff09");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u8f83\u6697\uff08RGB\uff09");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u8f83\u4eae\uff08RGB\uff09");
SendMessage(combo, CB_SETCURSEL, 0, 0);
combo = get_item(IDC_CHANNEL);
if (is_color_)
{
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"RGB");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u7ea2");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u7eff");
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u84dd");
}
else
SendMessageW(combo, CB_ADDSTRING, 0, (LPARAM)L"\u7070");
SendMessage(combo, CB_SETCURSEL, 0, 0);
cur_ = &rgb_gray_;
SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, 0, FALSE);
SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, 0, FALSE);
}
void dlg_gamma::on_paint(HDC hdc)
{
HPEN pen = CreatePen(PS_SOLID, 1, cur_->clr),
drag = CreatePen(PS_SOLID, 1, RGB(255, 0, 255)),
old = (HPEN)SelectObject(hdc, pen);
HDC cdc = CreateCompatibleDC(hdc);
HBITMAP ob = (HBITMAP)SelectObject(cdc, bkgnd_);
BitBlt(hdc, paint_area_.left - 1, paint_area_.top - 1, paint_area_.right - paint_area_.left + 2, paint_area_.bottom - paint_area_.top + 2,
cdc, 0, 0, SRCCOPY);
SelectObject(cdc, ob);
DeleteDC(cdc);
if (show_all_)
{
GAMMACURVE* prev = cur_;
HPEN now = NULL;
cur_ = &red_;
now = CreatePen(PS_SOLID, 1, cur_->clr);
SelectObject(hdc, now);
draw_current_curve(hdc);
cur_ = &green_;
SelectObject(hdc, old);
DeleteObject(now);
now = CreatePen(PS_SOLID, 1, cur_->clr);
SelectObject(hdc, now);
draw_current_curve(hdc);
cur_ = &blue_;
SelectObject(hdc, old);
DeleteObject(now);
now = CreatePen(PS_SOLID, 1, cur_->clr);
SelectObject(hdc, now);
draw_current_curve(hdc);
SelectObject(hdc, pen);
DeleteObject(now);
cur_ = prev;
}
else
draw_current_curve(hdc);
SelectObject(hdc, drag);
for (int i = 0; i < (int)cur_->points.size(); ++i)
{
draw_ellipse(hdc, cur_->points[i], 3, 3);
}
SelectObject(hdc, old);
DeleteObject(pen);
}
void dlg_gamma::on_mouse_move(DWORD key, int x, int y)
{
POINT pt = { x, y };
if (PtInRect(&paint_area_, pt))
{
x -= paint_area_.left;
y = paint_area_.bottom - y;
if (key == MK_LBUTTON && cur_->drag != -1)
{
if (x <= cur_->points[cur_->left].x)
x = cur_->points[cur_->left].x + 1;
else if (x >= cur_->points[cur_->right].x)
x = cur_->points[cur_->right].x - 1;
cur_->points[cur_->drag].x = x;
cur_->points[cur_->drag].y = y;
cur_->coefs = calc::coefs_from_points(cur_->points);
InvalidateRect(hwnd(), NULL, FALSE);
}
else if (key == MK_CONTROL)
{
y = calc_value(x);
pt.y = paint_area_.bottom - y;
ClientToScreen(hwnd(), &pt);
SetCursorPos(pt.x, pt.y);
}
SetDlgItemInt(hwnd(), IDC_EDIT_INPUT, x, FALSE);
SetDlgItemInt(hwnd(), IDC_EDIT_OUTPUT, y, FALSE);
}
}
void dlg_gamma::on_lbutton_down(int x, int y)
{
x -= paint_area_.left;
y = paint_area_.bottom - y;
if (hit_test(&x, &y))
{
if(add_drag_point(x, y) != -1)
SetCapture(hwnd());
}
}
void dlg_gamma::on_lbutton_up(int x, int y)
{
cur_->drag = -1;
cur_->left = 0;
cur_->right = 1;
ReleaseCapture();
}
void dlg_gamma::on_combo_sel_changed(int id, int sel)
{
show_all_ = false;
if (id == IDC_SCHEME)
{
if (sel == 1)
{
cur_ = &rgb_gray_;
init_curve(cur_);
cur_->points.clear();
cur_->points.push_back({ 0, 255 });
cur_->points.push_back({ 255, 0 });
cur_->coefs = calc::coefs_from_points(cur_->points);
}
else if (sel == 2)
{
show_all_ = true;
cur_ = &red_;
init_curve(cur_, cur_->clr);
cur_->points.clear();
cur_->points.push_back({ 33, 255 });
cur_->points.push_back({ 185, 0 });
cur_->points.push_back({ 119, 127 });
cur_->coefs = calc::coefs_from_points(cur_->points);
cur_ = &green_;
init_curve(cur_, cur_->clr);
cur_->points.clear();
cur_->points.push_back({ 28, 255 });
cur_->points.push_back({ 132, 0 });
cur_->points.push_back({ 77, 127 });
cur_->coefs = calc::coefs_from_points(cur_->points);
cur_ = &blue_;
init_curve(cur_, cur_->clr);
cur_->points.clear();
cur_->points.push_back({ 25, 255 });
cur_->points.push_back({ 108, 0 });
cur_->points.push_back({ 60, 127 });
cur_->coefs = calc::coefs_from_points(cur_->points);
cur_ = &red_;
}
else if (sel == 3)
{
cur_ = &rgb_gray_;
init_curve(cur_);
cur_->points.push_back({ 130, 101 });
cur_->coefs = calc::coefs_from_points(cur_->points);
}
else if (sel == 4)
{
cur_ = &rgb_gray_;
init_curve(cur_);
cur_->points.push_back({ 103, 125 });
cur_->coefs = calc::coefs_from_points(cur_->points);
}
InvalidateRect(hwnd(), NULL, FALSE);
}
else if (id == IDC_CHANNEL)
{
GAMMACURVE* prev = cur_, * all[] = { &rgb_gray_, &red_, &green_, &blue_ };
cur_ = all[sel];
if (prev != cur_)
{
if (cur_->points.size() == 2)
{
COLORREF clr = cur_->clr;
*cur_ = *prev;
cur_->clr = clr;
}
InvalidateRect(hwnd(), NULL, FALSE);
}
}
}
int dlg_gamma::do_modal(HWND parent)
{
BOOL enable_parent = FALSE,
got = TRUE;
MSG msg = { 0 };
if (IsWindow(parent) && parent != GetDesktopWindow())
{
EnableWindow(parent, FALSE);
enable_parent = TRUE;
}
ShowWindow(hwnd(), SW_SHOW);
while ((got = GetMessage(&msg, NULL, 0, 0)))
{
if ((DWORD)got == -1)
break;
//if (enable_parent && msg.hwnd == parent &&
// msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST &&
// msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
// continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
if (exit_code_ == IDOK || exit_code_ == IDCANCEL)
break;
}
ShowWindow(hwnd(), SW_HIDE);
if (enable_parent)
{
EnableWindow(parent, TRUE);
}
return exit_code_;
}
void dlg_gamma::get_gamma(SANE_Gamma* gamma)
{
gamma->apply_to_back = SANE_FALSE;
//if (cur_ == &rgb_gray_)
{
cur_ = &rgb_gray_;
gamma->pt_count = (SANE_Byte)(cur_->points.size() - 2);
// gamma->pt_count_r = gamma->pt_count_g = gamma->pt_count_b = 0;
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
gamma->keypoint[i - 2].x = (SANE_Byte)cur_->points[i].x;
gamma->keypoint[i - 2].y = (SANE_Byte)cur_->points[i].y;
}
for (int i = 0; i < 256; ++i)
gamma->table[i] = calc_value(i);
}
//else
{
// gamma->pt_count = 0;
cur_ = &red_;
gamma->pt_count_r = (SANE_Byte)(cur_->points.size() - 2);
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
gamma->keypoint_r[i - 2].x = (SANE_Byte)cur_->points[i].x;
gamma->keypoint_r[i - 2].y = (SANE_Byte)cur_->points[i].y;
}
for (int i = 0; i < 256; ++i)
gamma->table[i] = calc_value(i);
cur_ = &green_;
gamma->pt_count_g = (SANE_Byte)(cur_->points.size() - 2);
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
gamma->keypoint_g[i - 2].x = (SANE_Byte)cur_->points[i].x;
gamma->keypoint_g[i - 2].y = (SANE_Byte)cur_->points[i].y;
}
for (int i = 0; i < 256; ++i)
gamma->table[256 + i] = calc_value(i);
cur_ = &blue_;
gamma->pt_count_b = (SANE_Byte)(cur_->points.size() - 2);
for (int i = 2; i < (int)cur_->points.size(); ++i)
{
gamma->keypoint_b[i - 2].x = (SANE_Byte)cur_->points[i].x;
gamma->keypoint_b[i - 2].y = (SANE_Byte)cur_->points[i].y;
}
for (int i = 0; i < 256; ++i)
gamma->table[512 + i] = calc_value(i);
}
}
void dlg_gamma::set_gamma(const SANE_Gamma* gamma, bool gray)
{
int sel = 0;
SendMessage(get_item(IDC_CHANNEL), CB_RESETCONTENT, 0, 0);
is_color_ = !gray;
cur_ = &rgb_gray_;
init_curve(cur_);
for (int i = 0; i < gamma->pt_count; ++i)
{
POINT pt = { gamma->keypoint[i].x, gamma->keypoint[i].y };
cur_->points.push_back(pt);
}
cur_->coefs = calc::coefs_from_points(cur_->points);
SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, is_color_ ? (LPARAM)L"RGB" : (LPARAM)L"\u7070");
if(!gray)
{
is_color_ = true;
cur_ = &red_;
init_curve(cur_);
for (int i = 0; i < gamma->pt_count_r; ++i)
{
POINT pt = { gamma->keypoint_r[i].x, gamma->keypoint_r[i].y };
cur_->points.push_back(pt);
}
cur_->coefs = calc::coefs_from_points(cur_->points);
cur_ = &green_;
init_curve(cur_);
for (int i = 0; i < gamma->pt_count_g; ++i)
{
POINT pt = { gamma->keypoint_g[i].x, gamma->keypoint_g[i].y };
cur_->points.push_back(pt);
}
cur_->coefs = calc::coefs_from_points(cur_->points);
cur_ = &blue_;
init_curve(cur_);
for (int i = 0; i < gamma->pt_count_b; ++i)
{
POINT pt = { gamma->keypoint_b[i].x, gamma->keypoint_b[i].y };
cur_->points.push_back(pt);
}
cur_->coefs = calc::coefs_from_points(cur_->points);
// SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"RGB");
SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"\u7ea2");
SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"\u7eff");
SendMessageW(get_item(IDC_CHANNEL), CB_ADDSTRING, 0, (LPARAM)L"\u84dd");
}
cur_ = &rgb_gray_;
sel = 0;
SendMessage(get_item(IDC_CHANNEL), CB_SETCURSEL, sel, 0);
SendMessage(get_item(IDC_SCHEME), CB_SETCURSEL, 0, 0);
}