mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
3c4729aaa2
Added 'pack type', intended for playtesting (random boosters/starters); Added 'default(_image)' property to ImageStyle, and added the frame fillers for magic; Added blurring and bold printing (rather hacky) to the text rendering functions (used for "double click to add image" text); Added 'symmetric overlay' combine mode, which will look really nice for hybrids; Moved the watermark choices from the game to an include file in magic-watermarks; Working on a replacement for the image scripting system that plays nicer with the rest of the code. In particular, it will be possible to compare generated images quickly, so they can be updated continuously. This is a work in progress, currently there are two versions of everything. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@327 0fc631ac-6414-0410-93d0-97cfa31319b6
657 lines
24 KiB
C++
657 lines
24 KiB
C++
//+----------------------------------------------------------------------------+
|
|
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
|
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
|
|
//| License: GNU General Public License 2 or later (see file COPYING) |
|
|
//+----------------------------------------------------------------------------+
|
|
|
|
// ----------------------------------------------------------------------------- : Includes
|
|
|
|
#include <gui/image_slice_window.hpp>
|
|
#include <gui/util.hpp>
|
|
#include <util/window_id.hpp>
|
|
#include <util/rotation.hpp>
|
|
#include <gfx/gfx.hpp>
|
|
#include <wx/spinctrl.h>
|
|
#include <wx/dcbuffer.h>
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSlice
|
|
|
|
ImageSlice::ImageSlice(const Image& source, const wxSize& target_size)
|
|
: source(source), target_size(target_size)
|
|
, selection(0, 0, source.GetWidth(), source.GetHeight())
|
|
, allow_outside(false), aspect_fixed(true)
|
|
, sharpen(true), sharpen_amount(25)
|
|
{}
|
|
|
|
void ImageSlice::constrain() {
|
|
sharpen_amount = min(100, max(0, sharpen_amount));
|
|
// minimum size
|
|
selection.width = max(1, selection.width);
|
|
selection.height = max(1, selection.height);
|
|
// inside source
|
|
if (!allow_outside) {
|
|
selection.width = min(selection.width, source.GetWidth());
|
|
selection.height = min(selection.height, source.GetHeight());
|
|
selection.x = max(selection.x, 0);
|
|
selection.y = max(selection.y, 0);
|
|
selection.x = min(selection.x, source.GetWidth() - selection.width);
|
|
selection.y = min(selection.y, source.GetHeight() - selection.height);
|
|
}
|
|
// fix aspect ratio
|
|
if (aspect_fixed) {
|
|
int diff = selection.width * target_size.GetHeight() - selection.height * target_size.GetWidth();
|
|
if (diff > 0) {
|
|
// too wide
|
|
selection.width -= int(diff / target_size.GetHeight());
|
|
} else {
|
|
// too high
|
|
selection.height -= int(-diff / target_size.GetWidth());
|
|
}
|
|
}
|
|
}
|
|
|
|
Image ImageSlice::getSlice() const {
|
|
if (selection.width == target_size.GetWidth() && selection.height == target_size.GetHeight() && selection.x == 0 && selection.y == 0) {
|
|
// exactly the right size
|
|
return source;
|
|
}
|
|
Image target(target_size.GetWidth(), target_size.GetHeight(), false);
|
|
if (sharpen && sharpen_amount > 0 && sharpen_amount <= 100) {
|
|
sharp_resample_and_clip(source, target, selection, sharpen_amount);
|
|
} else {
|
|
resample_and_clip(source, target, selection);
|
|
}
|
|
return target;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : Events
|
|
|
|
DECLARE_EVENT_TYPE(EVENT_SLICE_CHANGED, <not used>);
|
|
DEFINE_EVENT_TYPE(EVENT_SLICE_CHANGED);
|
|
|
|
/// Handle EVENT_SLICE_CHANGED events
|
|
#define EVT_SLICE_CHANGED(id, handler) EVT_COMMAND(id, EVENT_SLICE_CHANGED, handler)
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceWindow
|
|
|
|
ImageSliceWindow::ImageSliceWindow(Window* parent, const Image& source, const wxSize& target_size, const AlphaMaskP& mask)
|
|
: wxDialog(parent,wxID_ANY,_TITLE_("slice image"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
|
|
, slice(source, target_size)
|
|
{
|
|
// init controls
|
|
const wxPoint defPos = wxDefaultPosition;
|
|
const wxSize spinSize(80,-1);
|
|
selector = new ImageSliceSelector(this, ID_SELECTOR, slice);
|
|
preview = new ImageSlicePreview (this, ID_PREVIEW, slice, mask);
|
|
|
|
String sizes[] = { _("&Original Size")
|
|
, _("Size to &Fit")
|
|
, _("F&orce to Fit")
|
|
, _("&Custom Size") };
|
|
size = new wxRadioBox(this, ID_SIZE, _LABEL_("size"), defPos, wxDefaultSize, 4, sizes, 1);
|
|
|
|
left = new wxSpinCtrl(this, ID_LEFT, _(""), defPos, spinSize);
|
|
top = new wxSpinCtrl(this, ID_TOP, _(""), defPos, spinSize);
|
|
width = new wxSpinCtrl(this, ID_WIDTH, _(""), defPos, spinSize);
|
|
height = new wxSpinCtrl(this, ID_HEIGHT, _(""), defPos, spinSize);
|
|
top ->SetRange(-5000,5000);
|
|
left ->SetRange(-5000,5000);
|
|
width ->SetRange(0,5000);
|
|
height->SetRange(0,5000);
|
|
|
|
fix_aspect = new wxCheckBox(this, ID_FIX_ASPECT, _("Fix aspect ratio (width/height)"));
|
|
zoom_x = new wxSpinCtrl(this, ID_ZOOM_X, _(""), defPos, spinSize);
|
|
zoom_y = new wxSpinCtrl(this, ID_ZOOM_Y, _(""), defPos, spinSize);
|
|
zoom = new wxSpinCtrl(this, ID_ZOOM, _(""), defPos, spinSize);
|
|
zoom_x->SetRange(1,10000);
|
|
zoom_y->SetRange(1,10000);
|
|
zoom ->SetRange(1,10000);
|
|
|
|
sharpen = new wxCheckBox(this, ID_SHARPEN, _("&Sharpen Filter"));
|
|
sharpen_amount = new wxSlider(this, ID_SHARPEN_AMOUNT, 0, 0, 100);
|
|
// allowOutside= new CheckBox(&this, idSliceAllowOutside, _("Allow selection outside source"))
|
|
// bgColor = new ColorSelector(&this, wxID_ANY)
|
|
|
|
// init sizers
|
|
wxSizer* s = new wxBoxSizer(wxVERTICAL);
|
|
// top row: image editors
|
|
wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
|
|
wxSizer* s3 = new wxBoxSizer(wxVERTICAL);
|
|
s3->Add(new wxStaticText(this, wxID_ANY, _LABEL_("original")));
|
|
s3->Add(selector, 1, wxEXPAND | wxTOP, 4);
|
|
s2->Add(s3, 1, wxEXPAND | wxALL, 4);
|
|
wxSizer* s4 = new wxBoxSizer(wxVERTICAL);
|
|
s4->Add(new wxStaticText(this, wxID_ANY, _LABEL_("result")));
|
|
s4->Add(preview, 0, wxTOP, 4);
|
|
s2->Add(s4, 0, wxALL, 4);
|
|
s->Add(s2, 1, wxEXPAND);
|
|
// bottom row: controls
|
|
wxSizer* s5 = new wxBoxSizer(wxHORIZONTAL);
|
|
s5->AddStretchSpacer(1);
|
|
s5->Add(size, 0, wxEXPAND | wxALL, 4);
|
|
s5->AddStretchSpacer(1);
|
|
wxSizer* s6 = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("selection"));
|
|
wxSizer* s7 = new wxFlexGridSizer(0, 2, 4, 5);
|
|
s7->Add(new wxStaticText(this, wxID_ANY, _("&Left")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s7->Add(left, 0, wxEXPAND);
|
|
s7->Add(new wxStaticText(this, wxID_ANY, _("&Top")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s7->Add(top, 0, wxEXPAND);
|
|
s7->Add(new wxStaticText(this, wxID_ANY, _("&Width")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s7->Add(width, 0, wxEXPAND);
|
|
s7->Add(new wxStaticText(this, wxID_ANY, _("&Height")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s7->Add(height, 0, wxEXPAND);
|
|
s6->Add(s7, 1, wxEXPAND | wxALL, 4);
|
|
s5->Add(s6, 0, wxEXPAND | wxALL, 4);
|
|
s5->AddStretchSpacer(1);
|
|
wxSizer* s8 = zoom_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("zoom"));
|
|
s8->Add(fix_aspect, 0, wxEXPAND | wxALL & ~wxBOTTOM, 4);
|
|
wxSizer* s9 = zoom_fixed = new wxFlexGridSizer(0, 3, 4, 5);
|
|
s9->Add(new wxStaticText(this, wxID_ANY, _("&Zoom")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s9->Add(zoom, 0, wxEXPAND);
|
|
s9->Add(new wxStaticText(this, wxID_ANY, _("%")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s8->Add(s9, 0, wxEXPAND | wxALL, 4);
|
|
wxSizer* sA = zoom_free = new wxFlexGridSizer(0, 3, 4, 5);
|
|
sA->Add(new wxStaticText(this, wxID_ANY, _("Zoom &X")), 0, wxALIGN_CENTER_VERTICAL);
|
|
sA->Add(zoom_x, 0, wxEXPAND);
|
|
sA->Add(new wxStaticText(this, wxID_ANY, _("%")), 0, wxALIGN_CENTER_VERTICAL);
|
|
sA->Add(new wxStaticText(this, wxID_ANY, _("Zoom &Y")), 0, wxALIGN_CENTER_VERTICAL);
|
|
sA->Add(zoom_y, 0, wxEXPAND);
|
|
sA->Add(new wxStaticText(this, wxID_ANY, _("%")), 0, wxALIGN_CENTER_VERTICAL);
|
|
s8->Add(sA, 0, wxEXPAND | wxALL, 4);
|
|
s5->Add(s8, 0, wxEXPAND | wxALL, 4);
|
|
s5->AddStretchSpacer(1);
|
|
wxSizer* sB = new wxStaticBoxSizer(wxVERTICAL, this, _LABEL_("filter"));
|
|
sB->Add(sharpen, 0, wxEXPAND | wxALL & ~wxBOTTOM, 4);
|
|
sB->Add(sharpen_amount, 0, wxEXPAND | wxALL, 4);
|
|
s5->Add(sB, 0, wxEXPAND | wxALL, 4);
|
|
s5->AddStretchSpacer(1);
|
|
s->Add(s5, 0, wxEXPAND);
|
|
s->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 8);
|
|
s->SetSizeHints(this);
|
|
SetSizer(s);
|
|
updateControls();
|
|
}
|
|
|
|
void ImageSliceWindow::onOk(wxCommandEvent&) {
|
|
EndModal(wxID_OK);
|
|
}
|
|
|
|
Image ImageSliceWindow::getImage() const {
|
|
return slice.getSlice();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceWindow : Controls
|
|
|
|
void ImageSliceWindow::onChangeSize(wxCommandEvent&) {
|
|
int sel = size->GetSelection();
|
|
if (sel == 0) {
|
|
// original size
|
|
slice.selection.width = slice.target_size.GetWidth();
|
|
slice.selection.height = slice.target_size.GetHeight();
|
|
slice.aspect_fixed = true;
|
|
onUpdateFromControl();
|
|
} else if (sel == 1) {
|
|
// size to fit
|
|
slice.selection.x = slice.selection.y = 0;
|
|
slice.selection.width = slice.source.GetWidth();
|
|
slice.selection.height = slice.source.GetHeight();
|
|
slice.aspect_fixed = true;
|
|
onUpdateFromControl();
|
|
} else if (sel == 2) {
|
|
// force to fit
|
|
slice.selection.x = slice.selection.y = 0;
|
|
slice.selection.width = slice.source.GetWidth();
|
|
slice.selection.height = slice.source.GetHeight();
|
|
slice.aspect_fixed = false;
|
|
onUpdateFromControl();
|
|
}
|
|
}
|
|
|
|
void ImageSliceWindow::onChangeLeft(wxCommandEvent&) {
|
|
slice.selection.x = left->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeTop(wxCommandEvent&) {
|
|
slice.selection.y = top->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeWidth(wxCommandEvent&) {
|
|
slice.selection.width = width->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeHeight(wxCommandEvent&) {
|
|
slice.selection.height = height->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
|
|
void ImageSliceWindow::onChangeFixAspect(wxCommandEvent&) {
|
|
slice.aspect_fixed = fix_aspect->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
|
|
void ImageSliceWindow::onChangeZoom(wxSpinEvent&) {
|
|
slice.zoomX(zoom->GetValue() / 100.0);
|
|
slice.zoomY(zoom->GetValue() / 100.0);
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeZoomX(wxSpinEvent&) {
|
|
slice.zoomX(zoom_x->GetValue() / 100.0);
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeZoomY(wxSpinEvent&) {
|
|
slice.zoomY(zoom_x->GetValue() / 100.0);
|
|
onUpdateFromControl();
|
|
}
|
|
|
|
void ImageSliceWindow::onChangeSharpen(wxCommandEvent&) {
|
|
slice.sharpen = sharpen->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
void ImageSliceWindow::onChangeSharpenAmount(wxScrollEvent&) {
|
|
slice.sharpen_amount = sharpen_amount->GetValue();
|
|
onUpdateFromControl();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceWindow : Updates
|
|
|
|
void ImageSliceWindow::onSliceChange(wxCommandEvent&) {
|
|
slice.constrain();
|
|
preview->update();
|
|
selector->update();
|
|
updateControls();
|
|
}
|
|
|
|
void ImageSliceWindow::onUpdateFromControl() {
|
|
slice.constrain();
|
|
preview->update();
|
|
selector->update();
|
|
updateControls();
|
|
}
|
|
|
|
void ImageSliceWindow::updateControls() {
|
|
if (slice.selection.width == slice.target_size.GetWidth() && slice.selection.height == slice.target_size.GetHeight()) {
|
|
size->SetSelection(0); // original size
|
|
} else if (slice.selection.x == 0 && slice.selection.width == slice.source.GetWidth() &&
|
|
slice.selection.y == 0 && slice.selection.height == slice.source.GetHeight()) {
|
|
size->SetSelection(2); // force to fit
|
|
} else if (slice.selection.width <= slice.source.GetWidth() &&
|
|
slice.selection.height <= slice.source.GetHeight() &&
|
|
( (slice.selection.x == 0 && slice.selection.width == slice.source.GetWidth())
|
|
||(slice.selection.y == 0 && slice.selection.height == slice.source.GetHeight()))) {
|
|
size->SetSelection(1); // size to fit
|
|
} else {
|
|
size->SetSelection(3); // custom size
|
|
}
|
|
left ->SetValue(slice.selection.x);
|
|
top ->SetValue(slice.selection.y);
|
|
width ->SetValue(slice.selection.width);
|
|
height ->SetValue(slice.selection.height);
|
|
fix_aspect->SetValue(slice.aspect_fixed);
|
|
if (slice.aspect_fixed) {
|
|
zoom->SetValue(int(slice.zoomX() * 100));
|
|
if (zoom_x->IsShown()) {
|
|
zoom_sizer->Show(zoom_fixed, true);
|
|
zoom_sizer->Show(zoom_free, false);
|
|
Layout();
|
|
}
|
|
} else {
|
|
zoom_x->SetValue(int(slice.zoomX() * 100));
|
|
zoom_y->SetValue(int(slice.zoomY() * 100));
|
|
if (zoom->IsShown()) {
|
|
zoom_sizer->Show(zoom_fixed, false);
|
|
zoom_sizer->Show(zoom_free, true);
|
|
Layout();
|
|
}
|
|
}
|
|
sharpen ->SetValue(slice.sharpen);
|
|
sharpen_amount->SetValue(slice.sharpen_amount);
|
|
sharpen_amount->Enable(slice.sharpen);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceWindow : Event table
|
|
|
|
BEGIN_EVENT_TABLE(ImageSliceWindow, wxDialog)
|
|
EVT_BUTTON (wxID_OK, ImageSliceWindow::onOk)
|
|
EVT_RADIOBOX (ID_SIZE, ImageSliceWindow::onChangeSize)
|
|
EVT_TEXT (ID_LEFT, ImageSliceWindow::onChangeLeft)
|
|
EVT_TEXT (ID_TOP, ImageSliceWindow::onChangeTop)
|
|
EVT_TEXT (ID_WIDTH, ImageSliceWindow::onChangeWidth)
|
|
EVT_TEXT (ID_HEIGHT, ImageSliceWindow::onChangeHeight)
|
|
EVT_CHECKBOX (ID_FIX_ASPECT, ImageSliceWindow::onChangeFixAspect)
|
|
EVT_SPINCTRL (ID_ZOOM, ImageSliceWindow::onChangeZoom)
|
|
EVT_SPINCTRL (ID_ZOOM_X, ImageSliceWindow::onChangeZoomX)
|
|
EVT_SPINCTRL (ID_ZOOM_Y, ImageSliceWindow::onChangeZoomY)
|
|
EVT_CHECKBOX (ID_SHARPEN, ImageSliceWindow::onChangeSharpen)
|
|
EVT_COMMAND_SCROLL (ID_SHARPEN_AMOUNT, ImageSliceWindow::onChangeSharpenAmount)
|
|
EVT_SLICE_CHANGED (wxID_ANY, ImageSliceWindow::onSliceChange)
|
|
// EVT_SIZE ( ImageSliceWindow::onSize)
|
|
END_EVENT_TABLE ()
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSlicePreview
|
|
|
|
ImageSlicePreview::ImageSlicePreview(Window* parent, int id, ImageSlice& slice, const AlphaMaskP& mask)
|
|
: wxControl(parent, id)
|
|
, slice(slice)
|
|
, mask(mask)
|
|
, mouse_down(false)
|
|
{}
|
|
|
|
void ImageSlicePreview::update() {
|
|
bitmap = wxNullBitmap;
|
|
Refresh(false);
|
|
}
|
|
|
|
wxSize ImageSlicePreview::DoGetBestSize() const {
|
|
// We know the client size we want, calculate the size that goes with that
|
|
wxSize ws = GetSize(), cs = GetClientSize();
|
|
return slice.target_size + ws - cs;
|
|
}
|
|
|
|
void ImageSlicePreview::onPaint(wxPaintEvent&) {
|
|
wxPaintDC dc(this);
|
|
dc.BeginDrawing();
|
|
draw(dc);
|
|
dc.EndDrawing();
|
|
}
|
|
void ImageSlicePreview::draw(DC& dc) {
|
|
if (!bitmap.Ok()) {
|
|
Image image = slice.getSlice();
|
|
if (mask && mask->size == slice.target_size) {
|
|
mask->setAlpha(image);
|
|
// create bitmap
|
|
bitmap = Bitmap(image.GetWidth(), image.GetHeight());
|
|
wxMemoryDC mdc; mdc.SelectObject(bitmap);
|
|
// draw checker pattern behind image
|
|
RealRect rect = (RealRect) GetClientSize();
|
|
RotatedDC rdc(mdc, 0, rect, 1, QUALITY_LOW);
|
|
draw_checker(rdc, rect);
|
|
rdc.DrawImage(image, RealPoint(0,0));
|
|
mdc.SelectObject(wxNullBitmap);
|
|
} else {
|
|
bitmap = Bitmap(image);
|
|
}
|
|
}
|
|
if (bitmap.Ok()) {
|
|
dc.DrawBitmap(bitmap, 0, 0);
|
|
}
|
|
}
|
|
|
|
void ImageSlicePreview::onLeftDown(wxMouseEvent& ev) {
|
|
mouseX = ev.GetX();
|
|
mouseY = ev.GetY();
|
|
start_selection = slice.selection;
|
|
mouse_down = true;
|
|
CaptureMouse();
|
|
SetCursor(wxCURSOR_SIZING);
|
|
}
|
|
void ImageSlicePreview::onLeftUp(wxMouseEvent&v) {
|
|
mouse_down = false;
|
|
if (HasCapture()) ReleaseMouse();
|
|
SetCursor(wxNullCursor);
|
|
}
|
|
void ImageSlicePreview::onMotion(wxMouseEvent& ev) {
|
|
if (mouse_down) {
|
|
// drag the image
|
|
slice.selection.x = int(start_selection.x + (mouseX - ev.GetX()) / slice.zoomX());
|
|
slice.selection.y = int(start_selection.y + (mouseY - ev.GetY()) / slice.zoomY());
|
|
// Notify parent
|
|
wxCommandEvent ev(EVENT_SLICE_CHANGED, GetId());
|
|
ProcessEvent(ev);
|
|
}
|
|
}
|
|
|
|
BEGIN_EVENT_TABLE(ImageSlicePreview, wxControl)
|
|
EVT_PAINT (ImageSlicePreview::onPaint)
|
|
EVT_LEFT_DOWN (ImageSlicePreview::onLeftDown)
|
|
EVT_LEFT_UP (ImageSlicePreview::onLeftUp)
|
|
EVT_MOTION (ImageSlicePreview::onMotion)
|
|
END_EVENT_TABLE ()
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceSelector
|
|
|
|
ImageSliceSelector::ImageSliceSelector(Window* parent, int id, ImageSlice& slice)
|
|
: wxControl(parent, id)
|
|
, slice(slice)
|
|
, mouse_down(false)
|
|
{}
|
|
|
|
void ImageSliceSelector::update() {
|
|
Refresh(false);
|
|
}
|
|
|
|
void ImageSliceSelector::onSize(wxSizeEvent&) {
|
|
bitmap = wxNullBitmap;
|
|
Refresh(false);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceSelector : Drawing
|
|
|
|
void ImageSliceSelector::onPaint(wxPaintEvent&) {
|
|
wxBufferedPaintDC dc(this);
|
|
dc.BeginDrawing();
|
|
draw(dc);
|
|
dc.EndDrawing();
|
|
}
|
|
void ImageSliceSelector::draw(DC& dc) {
|
|
if (!bitmap.Ok()) createBitmap();
|
|
if (!bitmap.Ok()) return;
|
|
// Selected region
|
|
wxSize s = GetClientSize();
|
|
int left = int(slice.selection.x * scaleX + border);
|
|
int top = int(slice.selection.y * scaleY + border);
|
|
int width = int(slice.selection.width * scaleX);
|
|
int height = int(slice.selection.height * scaleY);
|
|
// background
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
dc.SetBrush(Color(128,128,128));
|
|
dc.DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
|
|
// bitmap : unselected
|
|
dc.DrawBitmap(bitmap_no_sel, border, border);
|
|
// draw selected part ungreyed over it
|
|
{
|
|
wxMemoryDC mdc;
|
|
mdc.SelectObject(bitmap);
|
|
dc.Blit(left, top, width, height, &mdc, left - border, top - border);
|
|
mdc.SelectObject(wxNullBitmap);
|
|
}
|
|
// border around source
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
dc.SetPen(*wxBLACK_PEN);
|
|
dc.DrawRectangle(left - 1, top - 1, width + 2, height + 2);
|
|
dc.SetPen(Color(64,64,64));
|
|
dc.DrawRectangle(left - 2, top - 2, width + 4, height + 4);
|
|
// Draw handles on all sides
|
|
dc.SetBrush(Color(0,0,128));
|
|
dc.SetPen(*wxWHITE_PEN);
|
|
drawHandle(dc, -1, -1);
|
|
drawHandle(dc, -1, +1);
|
|
drawHandle(dc, +1, -1);
|
|
drawHandle(dc, +1, +1);
|
|
if (!slice.aspect_fixed) {
|
|
drawHandle(dc, -1, 0);
|
|
drawHandle(dc, 0, -1);
|
|
drawHandle(dc, 0, +1);
|
|
drawHandle(dc, +1, 0);
|
|
}
|
|
}
|
|
void ImageSliceSelector::drawHandle(DC& dc, int dx, int dy) {
|
|
wxPoint p = handlePos(dx, dy);
|
|
dc.DrawRectangle(p.x - 3 + 4 * dx, p.y - 3 + 4 * dy, 6, 6);
|
|
}
|
|
|
|
int blur_pixel(Byte* in, int x, int y, int width, int height) {
|
|
return (2 * ( in[0]) + // center
|
|
(x == 0 ? in[0] : in[-3]) + // left
|
|
(y == 0 ? in[0] : in[-3*width]) + // up
|
|
(x == width - 1 ? in[0] : in[3]) + // right
|
|
(y == height - 1 ? in[0] : in[3*width]) // down
|
|
) / 6;
|
|
}
|
|
|
|
// TODO: move me
|
|
void blur_image(const Image& img_in, Image& img_out) {
|
|
int width = img_in.GetWidth(), height = img_in.GetHeight();
|
|
assert(img_out.GetWidth() == width && img_out.GetHeight() == height);
|
|
Byte* in = img_in.GetData(), *out = img_out.GetData();
|
|
for (int y = 0 ; y < height ; ++y) {
|
|
for (int x = 0 ; x < width ; ++x) {
|
|
out[0] = blur_pixel(in + 0, x, y, width, height);
|
|
out[1] = blur_pixel(in + 1, x, y, width, height);
|
|
out[2] = blur_pixel(in + 2, x, y, width, height);
|
|
in += 3; out += 3;
|
|
}
|
|
}
|
|
}
|
|
void desaturate_image(Image& img) {
|
|
int width = img.GetWidth(), height = img.GetHeight();
|
|
Byte* in = img.GetData();
|
|
for (int y = 0 ; y < height ; ++y) {
|
|
for (int x = 0 ; x < width ; ++x) {
|
|
int r = in[0], g = in[1], b = in[2];
|
|
// desaturate
|
|
in[0] = (r + g + b + 5 * r + 255) / 9;
|
|
in[1] = (r + g + b + 5 * g + 255) / 9;
|
|
in[2] = (r + g + b + 5 * b + 255) / 9;
|
|
in += 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageSliceSelector::createBitmap() {
|
|
// create image, resampled to fit in control
|
|
wxSize s = GetClientSize();
|
|
int width = s.GetWidth() - 2*border, height = s.GetHeight() - 2*border;
|
|
Image img(width, height, false);
|
|
resample(slice.source, img);
|
|
bitmap = Bitmap(img);
|
|
scaleX = (double)width / slice.source.GetWidth();
|
|
scaleY = (double)height / slice.source.GetHeight();
|
|
// Initialize bitmap_no_sel to be the same bitmap, only with a faded color and blurred
|
|
Image img_no_sel(width, height, false);
|
|
blur_image(img, img_no_sel);
|
|
blur_image(img_no_sel, img_no_sel);
|
|
blur_image(img_no_sel, img_no_sel);
|
|
desaturate_image(img_no_sel);
|
|
bitmap_no_sel = Bitmap(img_no_sel);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceSelector : Mouse
|
|
|
|
void ImageSliceSelector::onLeftDown(wxMouseEvent& ev) {
|
|
mouseX = ev.GetX();
|
|
mouseY = ev.GetY();
|
|
start_selection = slice.selection;
|
|
mouse_down = true;
|
|
onAnyHandle(ev, &dragX, &dragY);
|
|
if (slice.aspect_fixed && (dragX == 0 || dragY == 0)) {
|
|
dragX = dragY = 0; // only drag corners if aspect fixed
|
|
}
|
|
if (dragX == 0 && dragY == 0) {
|
|
SetCursor(wxCURSOR_SIZING);
|
|
}
|
|
CaptureMouse();
|
|
}
|
|
void ImageSliceSelector::onLeftUp(wxMouseEvent&) {
|
|
mouse_down = false;
|
|
if (HasCapture()) ReleaseMouse();
|
|
SetCursor(wxNullCursor);
|
|
}
|
|
|
|
void ImageSliceSelector::onMotion(wxMouseEvent& ev) {
|
|
if (mouse_down) {
|
|
double deltaX = (ev.GetX() - mouseX) / scaleX;
|
|
double deltaY = (ev.GetY() - mouseY) / scaleY;
|
|
// are we on a handle?
|
|
if (dragX == 0 && dragY == 0) {
|
|
// dragging entire selection
|
|
slice.selection.x = int(start_selection.x + deltaX);
|
|
slice.selection.y = int(start_selection.y + deltaY);
|
|
} else {
|
|
// fix aspect ratio
|
|
if (slice.aspect_fixed) {
|
|
// fixed == deltaX * height = deltaY * width
|
|
double d = (dragX * deltaX * slice.target_size.GetHeight() + dragY * deltaY * slice.target_size.GetWidth()) / 2;
|
|
deltaX = dragX * d / slice.target_size.GetHeight();
|
|
deltaY = dragY * d / slice.target_size.GetWidth();
|
|
/*if (dragX * deltaX * slice.target_size.GetWidth() <
|
|
dragY * deltaY * slice.target_size.GetHeight()) {
|
|
deltaY = dragX * dragY * deltaX * slice.target_size.GetWidth() / slice.target_size.GetHeight();
|
|
} else {
|
|
deltaX = dragX * dragY * deltaY * slice.target_size.GetHeight() / slice.target_size.GetWidth();
|
|
}*/
|
|
}
|
|
// move
|
|
if (dragX) {
|
|
slice.selection.x = int(start_selection.x + deltaX * (1 - dragX) / 2);
|
|
slice.selection.width = int(start_selection.width + deltaX * dragX);
|
|
}
|
|
if (dragY) {
|
|
slice.selection.y = int(start_selection.y + deltaY * (1 - dragY) / 2);
|
|
slice.selection.height = int(start_selection.height + deltaY * dragY);
|
|
}
|
|
}
|
|
|
|
// Notify parent
|
|
wxCommandEvent ev(EVENT_SLICE_CHANGED, GetId());
|
|
ProcessEvent(ev);
|
|
} else {
|
|
int dx, dy;
|
|
if (onAnyHandle(ev, &dx, &dy)) {
|
|
// what cursor to use?
|
|
if (dx == dy) SetCursor(wxCURSOR_SIZENWSE);
|
|
else if (dx == -dy) SetCursor(wxCURSOR_SIZENESW);
|
|
else if (dx == 0) SetCursor(wxCURSOR_SIZENS);
|
|
else if (dy == 0) SetCursor(wxCURSOR_SIZEWE);
|
|
} else {
|
|
SetCursor(*wxSTANDARD_CURSOR);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceSelector : handles
|
|
|
|
bool ImageSliceSelector::onHandle(const wxMouseEvent& ev, int dx, int dy) const {
|
|
wxPoint p = handlePos(dx, dy);
|
|
p.x = p.x - 3 + 4 * dx;
|
|
p.y = p.y - 3 + 4 * dy;
|
|
return ev.GetX() >= p.x && ev.GetX() < p.x + 6 &&
|
|
ev.GetY() >= p.y && ev.GetY() < p.y + 6;
|
|
}
|
|
bool ImageSliceSelector::onAnyHandle(const wxMouseEvent& ev, int* dxOut, int* dyOut) const {
|
|
for (int dx = -1 ; dx <= 1 ; ++dx) {
|
|
for (int dy = -1 ; dy <= 1 ; ++dy) {
|
|
if ((dx != 0 || dy != 0) && onHandle(ev, dx, dy)) { // (0,0) == center, not a handle
|
|
*dxOut = dx;
|
|
*dyOut = dy;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
*dxOut = *dyOut = 0;
|
|
return false;
|
|
}
|
|
wxPoint ImageSliceSelector::handlePos(int dx, int dy) const {
|
|
return wxPoint(
|
|
int(scaleX * (slice.selection.x + ((dx + 1) * slice.selection.width) * 0.5) + border),
|
|
int(scaleY * (slice.selection.y + ((dy + 1) * slice.selection.height) * 0.5) + border)
|
|
);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------- : ImageSliceSelector : Event table
|
|
|
|
BEGIN_EVENT_TABLE(ImageSliceSelector, wxControl)
|
|
EVT_PAINT (ImageSliceSelector::onPaint)
|
|
EVT_LEFT_DOWN (ImageSliceSelector::onLeftDown)
|
|
EVT_LEFT_UP (ImageSliceSelector::onLeftUp)
|
|
EVT_MOTION (ImageSliceSelector::onMotion)
|
|
EVT_SIZE (ImageSliceSelector::onSize)
|
|
END_EVENT_TABLE ()
|