OK. You asked for it.
Sorry it's so hard to read. I haven't figured out how to change the font color in the code box.
Code: Select all
// (c) Copyright 2009, 2018 David B.Jones
// All rights reserved.
#include "stdafx.h"
#include "MyFrame.h"
#include "munsell.h"
#include "text_box.h"
#include "cubic_spline.h"
using std::min; using std::max;
wxSize inf(const wxSize& L, const wxSize& R) {
using std::min; using std::max;
return wxSize(min(L.GetX(), R.GetX()), min(L.GetY(), R.GetY()));
}
wxDEFINE_EVENT(djEVT_RESIZE, djResizeEvent);
wxDEFINE_EVENT(djEVT_UNMAXIMIZE,djResizeEvent);
MyFrame::MyFrame(const wxString& _title)
: wxFrame(NULL, wxID_ANY, _title)
, title(_title)
, image()
, oimage()
, scale(0)
, rescale(1.0)
, min_size(220, 80)
, cursor1()
, cursor2()
, font(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)
, openFileDialog_f()
, is_maximized(false)
{
// Main Icon
SetIcon(wxICON(sample));
// Main cursor
dj::captured_file &c1file(dj::main_cursor);
wxMemoryInputStream cistr(c1file.contents, c1file.size);
cursor1 = wxCursor(wxImage(cistr, wxBITMAP_TYPE_CUR));
// Key-down cursor
dj::captured_file &c2file(dj::splat_cursor);
wxMemoryInputStream c2istr(c2file.contents, c2file.size);
cursor2 = wxCursor(wxImage(c2istr, wxBITMAP_TYPE_CUR));
const int gray = 141;
background_color = wxColor(gray, gray, gray);
// The color stripe
HVC_panel.Create(this, ColorStripe, wxDefaultPosition, wxSize(300,25));
HVC_panel.SetBackgroundColour(background_color);
spacer.Create(this, Spacer, "", wxDefaultPosition, wxSize(8,8));
spacer.SetBackgroundColour(background_color);
//Font for the color stripe
HVC_panel.SetFont(font);
HVC_panel.SetLabel("");
//Initialize the main panel.
panel.Create(this, Picture);
panel.SetScrollRate(10,10);
panel.SetBackgroundColour(background_color);
panel.SetSize(10,10);
panel.SetCursor(cursor1);
panel.SetFocus();
// Cache a file dialog
std::string imagetypes = "*.png;*.jpg;*.gif;*.bmp;*.ico;*.cur;*.tif";
std::string cards = "Images (" + imagetypes + ")|" + imagetypes +
"|PNG (*.png)|*.png" +
"|JPEG (*.jpg)|*.jpg" +
"|GIF (*.gif)|*.gif" +
"|Bitmap (*.bmp)|*.bmp" +
"|TIF (*.tif)|*.tif" +
"|Cursor (*.cur)|*.cur"+
"|Icon (*.ico)|*.ico";
openFileDialog_f.reset(new wxFileDialog( this, _("Open image file"), "", "", _(cards.c_str()),
wxFD_FILE_MUST_EXIST, wxDefaultPosition));
// Put everything into a vertical sizer.
sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(&HVC_panel, 0, wxEXPAND);
sizer->Add(&spacer, 0, wxEXPAND);
sizer->Add(&panel, 1, wxEXPAND);
int dx, dy;
wxDisplaySize(&dx, &dy);
int fx, fy;
GetSize(&fx, &fy);
int cx, cy;
GetClientSize(&cx, &cy);
dy -= (fy-cy);
vmiddle = wxPoint(dx,dy)/2;
SetMinSize(min_size);
SetSizer(sizer);
// Toolbar
// The Frame will handle tb. No need to free later.
tb = CreateToolBar(wxTB_HORIZONTAL | wxTB_FLAT, ToolBar); // XXXXX This is where the two errors are reported
tb->SetMinSize(wxSize(10, 10));
tb->SetBackgroundColour(background_color);
// Buttons on toolbar
AddToolBarItem(TbOpen, dj::folder_pictures_44x44, "Open", "Open an image file.");
AddToolBarItem(TbZoom, dj::viewmag_plus_30x30, "Zoom", "Zoom in.");
AddToolBarItem(TbUnZoom, dj::viewmag_minus_30x30, "Zoom out", "Zoom out.");
// AddToolBarItem(TbControls, dj::wrench_30x30, "Configure", "Configure preferences" );
tb->Realize();
connect_handlers();
Fit();
recenter();
}
void MyFrame::connect_handlers() {
Bind(wxEVT_COMMAND_TOOL_CLICKED, &MyFrame::OnButtonClick, this);
Bind(wxEVT_LEFT_DOWN, &MyFrame::OnMouse, this);
Bind(wxEVT_LEFT_UP, &MyFrame::OnMouse, this);
Bind(wxEVT_MOTION, &MyFrame::OnMouse, this);
Bind(djEVT_RESIZE, &MyFrame::OnResize, this);
Bind(djEVT_UNMAXIMIZE, &MyFrame::OnUnmaximize, this);
Bind(wxEVT_SIZE, &MyFrame::OnSize, this);
Bind(wxEVT_MOVE, &MyFrame::OnMove, this);
}
void MyFrame::OnSize(wxSizeEvent &evt) {
evt.Skip();
bool now_maximized = IsMaximized();
if( is_maximized != now_maximized ) {
is_maximized = now_maximized;
if(!now_maximized) {
wxPostEvent(this, djResizeEvent(djEVT_UNMAXIMIZE));
} else {
// Maximized
panel.do_center(true);
}
} else {
// Cases: Loaded picture,
// User resized window, OnUnmaximize called Fit().
if(panel.GetImageHeight() > 0) {
wxPostEvent(this, djResizeEvent(djEVT_RESIZE));
}
}
}
void MyFrame::OnUnmaximize(djResizeEvent&) {
panel.do_center(false);
Fit();
recenter(vmiddle);
}
void MyFrame::OnResize(djResizeEvent& evt) {
(evt);
wxSize im = panel.GetImageSize();
wxSize pn = panel.GetClientSize();
wxSize ns = inf(im,pn);
if(ns!=pn) {
panel.SetMinClientSize(ns);
Fit(); // Veto expanding window bigger than image
}
}
void MyFrame::OnMove(wxMoveEvent& evt ) {
evt.Skip();
vmiddle = find_middle();
}
void MyFrame::OnButtonClick(wxCommandEvent&event) {
// For zooming, we reposition the pointer to stay on
// the zoom-button.
wxPoint wp = GetPosition();
wxSize ws = GetSize();
wxPoint middle = wp+ws/2;
if(is_maximized) {
middle = vmiddle;
}
int ID = event.GetId();
switch(ID) {
case TbOpen:
image_from_dialog();
recenter(middle);
break;
case TbZoom:
if(zoom(1)) {
recenter(middle);
wxPoint mp = wxGetMousePosition();
move_mouse(mp-wp);
}
break;
case TbUnZoom:
if(zoom(-1)) {
recenter(middle);
wxPoint mp = wxGetMousePosition();
move_mouse(mp-wp);
}
break;
case TbControls:
break;
default:
break;
}
}
void MyFrame::OnMouse(wxMouseEvent &event) {
if(event.LeftIsDown()) {
panel.SetCursor(cursor1);
wxPoint pt = event.GetPosition();
pick(pt.x, pt.y);
} else {
panel.SetCursor(wxStockCursor(wxCURSOR_HAND));
}
}
bool MyFrame::OnDropFile(wxCoord, wxCoord, const wxArrayString& paths, wxObject*) {
wxPoint wp = GetPosition();
wxSize ws = GetSize();
wxPoint middle = wp+ws/2;
if ( paths.size() == 1) {
if( show_new_image(paths[0]) ) {
recenter(middle);
return true;
}
return false;
} else {
wxBell();
return false;
}
}
bool MyFrame::show_new_image(wxString path, bool center_it) {
wxFileName fn(path);
wxImage new_image;
bool ok = true;
try {
new_image = wxImage(path);
ok = new_image.Ok();
} catch (...) {
ok = false;
}
if(ok) {
new_image.ConvertAlphaToMask(background_color.Red(), background_color.Green(), background_color.Blue());
image = new_image;
filename = fn.GetName()+"."+fn.GetExt();
image_file = path;
SetTitle(filename);
scale = 0;
const bool gray = false; //true;
if(gray) {
size_t sz = image.GetHeight()*image.GetWidth();
dj::color::convert_to_gray(image.GetData(), sz);
}
oimage = image;
load_image();
if(center_it) {
recenter(find_middle());
}
HVC_panel.SetBackgroundColour( background_color);
HVC_panel.SetLabel("");
return true;
} else {
wxBell();
wxMessageBox("Could not process image file.\n"+path, "", wxOK|wxICON_ERROR, this);
return false;
}
}
bool MyFrame::image_from_dialog() {
openFileDialog_f->CenterOnParent();
wxString dir;
wxString file;
wxString path;
if ( openFileDialog_f->ShowModal() == wxID_OK )
{
dir = openFileDialog_f->GetDirectory();
path.append(dir);
path.append( wxFileName::GetPathSeparator());
file = openFileDialog_f->GetFilename();
path.append(file);
} else {
return false;
}
return show_new_image(path, true);
}
void MyFrame::load_image() {
panel.do_center(IsMaximized());
panel.load_image(image);
panel.SetCursor(cursor2);
if(!IsMaximized()) {
Layout();
Fit();
}
int ih = image.GetHeight();
int ph = panel.GetClientSize().GetHeight();
int diff = ih-ph;
int scroll = std::max(4, diff/50);
panel.SetScrollRate(20, scroll);
Refresh();
}
bool MyFrame::zoom(int direction) {
bool ok = oimage.IsOk();
if(!ok) {
wxBell();
return false;
}
scale += direction;
if(scale == 0) {
rescale = 1;
image = oimage;
load_image();
SetTitle(filename);
return true;
}
double save = rescale;
int dclicks = 4; // How many clicks it takes to double or halve the size
double factor = pow(2.0,(1.0/dclicks));
rescale = pow(factor,scale);
double X = oimage.GetWidth();
double Y = oimage.GetHeight();
double x = X*rescale;
double y = Y*rescale;
if((x*y > 48e6) || ((x<32) && (x<X))) {
wxBell();
scale -= direction; // Back out
rescale = save;
return false;
}
int xx = int(x+.5);
int yy = int(y+.5);
try {
image = oimage.Scale(xx,yy,wxIMAGE_QUALITY_NORMAL);
load_image();
std::string ttitle = str(boost::format("%s (scale %d%%)") % filename % int(rescale*100+.5));
SetTitle(_(ttitle.c_str()));
} catch (...) {
wxBell();
scale -= direction;
return false;
}
return true;
}
void MyFrame::pick(int X, int Y) {
double c = 0; // Number of pixels sampled
int s = 2; // parms['pixel span']
// Calc top and bottom of box
int Xb = max(0,X-s);
int Xt = min(X+s+1, image.GetWidth());
int Yb = max(0,Y-s);
int Yt = min(Y+s+1, image.GetHeight());
double R,G,B;
R = G = B = 0; // Initialize average pix value
// Walk over the box, adding the pix values
for (int x = Xb; x<Xt; x++){
for (int y =Yb; y<Yt; y++) {
//double mul = (x==X)||(y==Y)?1.414:1;
//if((x==X)&&(y==Y)) mul = 2;
double mul = 1.0;
R += mul*image.GetRed(x,y);
G += mul*image.GetGreen(x,y);
B += mul*image.GetBlue(x,y);
c += mul;
}
}
if(0==c) {
return; // Cursor is outside of the picture
}
// Finish averaging
int r = static_cast<int>(R/c);
int g = static_cast<int>(G/c);
int b = static_cast<int>(B/c);
using namespace dj; using namespace dj::color;
// Caluculate the Munsell code
c_srgb rgb;
c_HVC mun;
rgb.r = r; rgb.g = g; rgb.b = b;
dj::rgb_to_mun(rgb, mun);
double value = mun.V;
// Get the text-string etc. for the hue.
dj::munsell_hue_notation hue = dj::mun_hue(mun.H);
// Set the color of the strip to sampled color
wxColour bg(r,g,b);
HVC_panel.SetBackgroundColour(bg);
// Set the text to Munsell notation
std::string label;
int oX = X/rescale;
int oY = Y/rescale;
if(0==mun.C || (r==b)&&(b==g)) {
// Dead neutral
label = boost::str(boost::format(" N %3.1lf/ r%d g%d b%d (%d %d)" ) %value%r%g% b %oX %oY);
} else {
label = boost::str(boost::format("%4.1lf %s %3.1lf/%4.1lf r%d g%d b%d (%d %d)")
%hue.offset %hue.hue_name %value %mun.C %r %g %b %oX %oY);
}
if(label != prev_label) {
prev_label = label;
HVC_panel.SetLabel(_(label.c_str()));
HVC_panel.Refresh();
}
return;
}
wxPoint MyFrame::find_middle() const {
wxPoint wp = GetPosition();
wxSize ws = GetSize();
return wp+ws/2;
}
void MyFrame::recenter(wxPoint middle) {
if(!IsMaximized()) {
wxPoint wp = GetPosition();
wxSize ws = GetSize();
wxPoint pos = middle-ws/2;
// Putting top of window above top of screen makes it
// impossible to move with the mouse. Too far off the screen
// to the left hides the toolbar buttons. Etc. So...
pos.y = std::max(0, pos.y);
pos.x = std::max(0, pos.x);
wxSize disp = wxGetDisplaySize();
const int toolsx = 200; // XXX Get actual good number
const int toolsy = 100; // ditto
pos.x = std::min(disp.GetX()-toolsx, pos.x);
pos.y = std::min(disp.GetY()-toolsy, pos.y);
Move(pos);
vmiddle = find_middle();
} else {
vmiddle = middle;
}
}
void MyFrame::move_mouse(wxPoint rel) {
// Move the mouse a new position. Coordinates
// are relative to the window positon.
wxPoint mp = GetPosition()+rel;
ScreenToClient(&mp.x, &mp.y);
WarpPointer(mp.x, mp.y);
}
void MyFrame::AddToolBarItem(int ID, const dj::captured_file &cfile, const char* short_help, const char *long_help) {
// For embedded files
wxMemoryInputStream istr(cfile.contents, cfile.size);
auto im = wxImage(istr, wxBITMAP_TYPE_ANY);
im.ConvertAlphaToMask(background_color.Red(), background_color.Green(), background_color.Blue());
wxBitmap bmp(im);
tb->AddTool(ID, short_help, bmp, long_help);
}