mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-27 06:35:16 +00:00
Merge branch 'brush'
This commit is contained in:
commit
88a0e8c806
40
data/gui.xml
40
data/gui.xml
@ -149,6 +149,44 @@
|
||||
<param name="change" value="decrement-size" />
|
||||
</key>
|
||||
|
||||
<!-- Custom brushes -->
|
||||
<key command="ChangeBrush" shortcut="Alt+1">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="1" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+2">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="2" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+3">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="3" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+4">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="4" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+5">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="5" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+6">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="6" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+7">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="7" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+8">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="8" />
|
||||
</key>
|
||||
<key command="ChangeBrush" shortcut="Alt+9">
|
||||
<param name="change" value="custom" />
|
||||
<param name="slot" value="9" />
|
||||
</key>
|
||||
|
||||
<!-- Zoom -->
|
||||
<key command="Zoom" shortcut="~"><param name="percentage" value="50" /></key>
|
||||
<key command="Zoom" shortcut="1"><param name="percentage" value="100" /></key>
|
||||
@ -309,6 +347,7 @@
|
||||
<param name="quantity" value="1" />
|
||||
</key>
|
||||
|
||||
<key command="NewBrush" shortcut="Ctrl+B" mac="Cmd+B" />
|
||||
</commands>
|
||||
|
||||
<!-- Keyboard shortcuts to select tools -->
|
||||
@ -438,6 +477,7 @@
|
||||
<param name="orientation" value="vertical" />
|
||||
</item>
|
||||
<item command="MaskContent" text="Transfor&m" />
|
||||
<item command="NewBrush" text="New &Brush" />
|
||||
<separator />
|
||||
<item command="ReplaceColor" text="R&eplace Color..." />
|
||||
<item command="InvertColor" text="&Invert" />
|
||||
|
@ -98,6 +98,9 @@
|
||||
<option id="new_version" type="std::string" />
|
||||
<option id="new_url" type="std::string" />
|
||||
</section>
|
||||
<section id="brush">
|
||||
<option id="pattern" type="doc::BrushPattern" default="doc::BrushPattern::DEFAULT" />
|
||||
</section>
|
||||
</global>
|
||||
|
||||
<tool>
|
||||
@ -109,9 +112,9 @@
|
||||
<option id="ink" type="InkType" />
|
||||
<option id="freehand_algorithm" type="FreehandAlgorithm" />
|
||||
<section id="brush">
|
||||
<option id="type" type="BrushType" />
|
||||
<option id="size" type="int" />
|
||||
<option id="angle" type="int" />
|
||||
<option id="type" type="BrushType" default="BrushType::CIRCLE" />
|
||||
<option id="size" type="int" default="1" />
|
||||
<option id="angle" type="int" default="0" />
|
||||
</section>
|
||||
<section id="spray">
|
||||
<option id="width" type="int" default="16" />
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -71,6 +71,7 @@ add_library(app-lib
|
||||
cmd/clear_cel.cpp
|
||||
cmd/clear_image.cpp
|
||||
cmd/clear_mask.cpp
|
||||
cmd/clear_rect.cpp
|
||||
cmd/configure_background.cpp
|
||||
cmd/copy_cel.cpp
|
||||
cmd/copy_frame.cpp
|
||||
@ -142,6 +143,7 @@ add_library(app-lib
|
||||
commands/cmd_cut.cpp
|
||||
commands/cmd_deselect_mask.cpp
|
||||
commands/cmd_developer_console.cpp
|
||||
commands/cmd_discard_brush.cpp
|
||||
commands/cmd_duplicate_layer.cpp
|
||||
commands/cmd_duplicate_sprite.cpp
|
||||
commands/cmd_duplicate_view.cpp
|
||||
@ -173,6 +175,7 @@ add_library(app-lib
|
||||
commands/cmd_merge_down_layer.cpp
|
||||
commands/cmd_move_cel.cpp
|
||||
commands/cmd_move_mask.cpp
|
||||
commands/cmd_new_brush.cpp
|
||||
commands/cmd_new_file.cpp
|
||||
commands/cmd_new_frame.cpp
|
||||
commands/cmd_new_frame_tag.cpp
|
||||
@ -288,6 +291,7 @@ add_library(app-lib
|
||||
transaction.cpp
|
||||
ui/ani_controls.cpp
|
||||
ui/app_menuitem.cpp
|
||||
ui/brush_popup.cpp
|
||||
ui/button_set.cpp
|
||||
ui/color_bar.cpp
|
||||
ui/color_button.cpp
|
||||
|
@ -261,7 +261,6 @@ Widget* AppMenus::createInvalidVersionMenuitem()
|
||||
{
|
||||
AppMenuItem* menuitem = new AppMenuItem("WARNING!");
|
||||
Menu* subMenu = new Menu();
|
||||
Params params;
|
||||
subMenu->addChild(new AppMenuItem(PACKAGE " is using a customized gui.xml (maybe from your HOME directory)."));
|
||||
subMenu->addChild(new AppMenuItem("You should update your customized gui.xml file to the new version to get"));
|
||||
subMenu->addChild(new AppMenuItem("the latest commands available."));
|
||||
|
87
src/app/cmd/clear_rect.cpp
Normal file
87
src/app/cmd/clear_rect.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/clear_rect.h"
|
||||
|
||||
#include "app/document.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
ClearRect::ClearRect(Cel* cel, const gfx::Rect& bounds)
|
||||
{
|
||||
app::Document* doc = static_cast<app::Document*>(cel->document());
|
||||
|
||||
Image* image = (cel ? cel->image(): NULL);
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
m_offsetX = bounds.x - cel->x();
|
||||
m_offsetY = bounds.y - cel->y();
|
||||
|
||||
gfx::Rect bounds2 =
|
||||
image->bounds().createIntersect(
|
||||
gfx::Rect(
|
||||
m_offsetX, m_offsetY,
|
||||
bounds.w, bounds.h));
|
||||
if (bounds.isEmpty())
|
||||
return;
|
||||
|
||||
m_dstImage.reset(new WithImage(image));
|
||||
m_bgcolor = doc->bgColor(cel->layer());
|
||||
|
||||
m_copy.reset(crop_image(image,
|
||||
bounds2.x, bounds2.y, bounds2.w, bounds2.h, m_bgcolor));
|
||||
}
|
||||
|
||||
void ClearRect::onExecute()
|
||||
{
|
||||
m_seq.execute(context());
|
||||
if (m_dstImage)
|
||||
clear();
|
||||
}
|
||||
|
||||
void ClearRect::onUndo()
|
||||
{
|
||||
if (m_dstImage)
|
||||
restore();
|
||||
m_seq.undo();
|
||||
}
|
||||
|
||||
void ClearRect::onRedo()
|
||||
{
|
||||
m_seq.redo();
|
||||
if (m_dstImage)
|
||||
clear();
|
||||
}
|
||||
|
||||
void ClearRect::clear()
|
||||
{
|
||||
fill_rect(m_dstImage->image(),
|
||||
m_offsetX, m_offsetY,
|
||||
m_offsetX + m_copy->width() - 1,
|
||||
m_offsetY + m_copy->height() - 1,
|
||||
m_bgcolor);
|
||||
}
|
||||
|
||||
void ClearRect::restore()
|
||||
{
|
||||
copy_image(m_dstImage->image(), m_copy.get(), m_offsetX, m_offsetY);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
54
src/app/cmd/clear_rect.h
Normal file
54
src/app/cmd/clear_rect.h
Normal file
@ -0,0 +1,54 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_CMD_CLEAR_RECT_H_INCLUDED
|
||||
#define APP_CMD_CLEAR_RECT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_image.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace doc {
|
||||
class Cel;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class ClearRect : public Cmd {
|
||||
public:
|
||||
ClearRect(Cel* cel, const gfx::Rect& bounds);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this) + m_seq.memSize() +
|
||||
(m_copy ? m_copy->getMemSize(): 0);
|
||||
}
|
||||
|
||||
private:
|
||||
void clear();
|
||||
void restore();
|
||||
|
||||
CmdSequence m_seq;
|
||||
base::UniquePtr<WithImage> m_dstImage;
|
||||
ImageRef m_copy;
|
||||
int m_offsetX, m_offsetY;
|
||||
color_t m_bgcolor;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -14,6 +14,7 @@
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/context.h"
|
||||
#include "ui/manager.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -59,11 +60,20 @@ void CancelCommand::onExecute(Context* context)
|
||||
break;
|
||||
|
||||
case All:
|
||||
// Discard brush
|
||||
{
|
||||
Command* discardBrush = CommandsModule::instance()->getCommandByName(CommandId::DiscardBrush);
|
||||
context->executeCommand(discardBrush);
|
||||
}
|
||||
|
||||
// Deselect mask
|
||||
if (context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::HasVisibleMask)) {
|
||||
Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
|
||||
context->executeCommand(cmd);
|
||||
Command* deselectMask = CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
|
||||
context->executeCommand(deselectMask);
|
||||
}
|
||||
|
||||
ui::Manager::getDefault()->invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ public:
|
||||
: m_editor(current_editor)
|
||||
, m_rect(0, 0, current_editor->sprite()->width(), current_editor->sprite()->height())
|
||||
, m_selectBoxState(new SelectBoxState(this, m_rect,
|
||||
SelectBoxState::PaintRulers |
|
||||
SelectBoxState::PaintDarkOutside)) {
|
||||
SelectBoxState::RULERS |
|
||||
SelectBoxState::DARKOUTSIDE)) {
|
||||
setWidth(m_rect.w);
|
||||
setHeight(m_rect.h);
|
||||
setLeft(0);
|
||||
@ -92,7 +92,7 @@ public:
|
||||
protected:
|
||||
|
||||
// SelectBoxDelegate impleentation
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) override {
|
||||
void onChangeRectangle(const gfx::Rect& rect) override {
|
||||
m_rect = rect;
|
||||
|
||||
updateSizeFromRect();
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include "app/context.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/brush.h"
|
||||
|
||||
namespace app {
|
||||
@ -28,10 +31,9 @@ class ChangeBrushCommand : public Command {
|
||||
DecrementSize,
|
||||
IncrementAngle,
|
||||
DecrementAngle,
|
||||
CustomBrush,
|
||||
};
|
||||
|
||||
Change m_change;
|
||||
|
||||
public:
|
||||
ChangeBrushCommand();
|
||||
|
||||
@ -39,6 +41,10 @@ protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
void onExecute(Context* context) override;
|
||||
std::string onGetFriendlyName() const override;
|
||||
|
||||
private:
|
||||
Change m_change;
|
||||
int m_slot;
|
||||
};
|
||||
|
||||
ChangeBrushCommand::ChangeBrushCommand()
|
||||
@ -47,6 +53,7 @@ ChangeBrushCommand::ChangeBrushCommand()
|
||||
CmdUIOnlyFlag)
|
||||
{
|
||||
m_change = None;
|
||||
m_slot = 0;
|
||||
}
|
||||
|
||||
void ChangeBrushCommand::onLoadParams(const Params& params)
|
||||
@ -56,6 +63,12 @@ void ChangeBrushCommand::onLoadParams(const Params& params)
|
||||
else if (change == "decrement-size") m_change = DecrementSize;
|
||||
else if (change == "increment-angle") m_change = IncrementAngle;
|
||||
else if (change == "decrement-angle") m_change = DecrementAngle;
|
||||
else if (change == "custom") m_change = CustomBrush;
|
||||
|
||||
if (m_change == CustomBrush)
|
||||
m_slot = params.get_as<int>("slot");
|
||||
else
|
||||
m_slot = 0;
|
||||
}
|
||||
|
||||
void ChangeBrushCommand::onExecute(Context* context)
|
||||
@ -84,6 +97,10 @@ void ChangeBrushCommand::onExecute(Context* context)
|
||||
if (brush->getAngle() > 0)
|
||||
brush->setAngle(brush->getAngle()-1);
|
||||
break;
|
||||
case CustomBrush:
|
||||
App::instance()->getMainWindow()->getContextBar()
|
||||
->setActiveBrushBySlot(m_slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +123,10 @@ std::string ChangeBrushCommand::onGetFriendlyName() const
|
||||
case DecrementAngle:
|
||||
text += ": Decrement Angle";
|
||||
break;
|
||||
case CustomBrush:
|
||||
text += ": Custom Brush #";
|
||||
text += base::convert_to<std::string>(m_slot);
|
||||
break;
|
||||
}
|
||||
|
||||
return text;
|
||||
|
59
src/app/commands/cmd_discard_brush.cpp
Normal file
59
src/app/commands/cmd_discard_brush.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class DiscardBrushCommand : public Command {
|
||||
public:
|
||||
DiscardBrushCommand();
|
||||
Command* clone() const override { return new DiscardBrushCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
};
|
||||
|
||||
DiscardBrushCommand::DiscardBrushCommand()
|
||||
: Command("DiscardBrush",
|
||||
"Discard Brush",
|
||||
CmdUIOnlyFlag)
|
||||
{
|
||||
}
|
||||
|
||||
bool DiscardBrushCommand::onEnabled(Context* context)
|
||||
{
|
||||
ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
|
||||
return (ctxBar->activeBrush()->type() == kImageBrushType);
|
||||
}
|
||||
|
||||
void DiscardBrushCommand::onExecute(Context* context)
|
||||
{
|
||||
ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
|
||||
ctxBar->discardActiveBrush();
|
||||
}
|
||||
|
||||
Command* CommandFactory::createDiscardBrushCommand()
|
||||
{
|
||||
return new DiscardBrushCommand();
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -13,6 +13,7 @@
|
||||
#include "app/color.h"
|
||||
#include "app/color_picker.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/settings/settings.h"
|
||||
@ -72,7 +73,13 @@ void EyedropperCommand::onExecute(Context* context)
|
||||
if (!sprite)
|
||||
return;
|
||||
|
||||
// pixel position to get
|
||||
// Discard current image brush
|
||||
{
|
||||
Command* discardBrush = CommandsModule::instance()->getCommandByName(CommandId::DiscardBrush);
|
||||
context->executeCommand(discardBrush);
|
||||
}
|
||||
|
||||
// Pixel position to get
|
||||
gfx::Point pixelPos = editor->screenToEditor(ui::get_mouse_position());
|
||||
|
||||
// Check if we've to grab alpha channel or the merged color.
|
||||
|
@ -192,8 +192,8 @@ private:
|
||||
m_editor = current_editor;
|
||||
|
||||
EditorStatePtr newState(new SelectBoxState(this, m_rect,
|
||||
SelectBoxState::PaintRulers |
|
||||
SelectBoxState::PaintGrid));
|
||||
SelectBoxState::RULERS |
|
||||
SelectBoxState::GRID));
|
||||
m_editor->setState(newState);
|
||||
}
|
||||
}
|
||||
|
170
src/app/commands/cmd_new_brush.cpp
Normal file
170
src/app/commands/cmd_new_brush.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/cmd/clear_rect.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/editor/select_box_state.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/mask.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class NewBrushCommand : public Command
|
||||
, public SelectBoxDelegate {
|
||||
public:
|
||||
NewBrushCommand();
|
||||
Command* clone() const override { return new NewBrushCommand(*this); }
|
||||
|
||||
protected:
|
||||
bool onEnabled(Context* context) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
||||
// SelectBoxDelegate impl
|
||||
void onQuickboxEnd(const gfx::Rect& rect, ui::MouseButtons buttons) override;
|
||||
void onQuickboxCancel() override;
|
||||
|
||||
private:
|
||||
void createBrush(const Mask* mask);
|
||||
};
|
||||
|
||||
NewBrushCommand::NewBrushCommand()
|
||||
: Command("NewBrush",
|
||||
"New Brush",
|
||||
CmdUIOnlyFlag)
|
||||
{
|
||||
}
|
||||
|
||||
bool NewBrushCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
|
||||
}
|
||||
|
||||
void NewBrushCommand::onExecute(Context* context)
|
||||
{
|
||||
// If there is no visible mask, the brush must be selected from the
|
||||
// current editor.
|
||||
if (!context->activeDocument()->isMaskVisible()) {
|
||||
EditorStatePtr state = current_editor->getState();
|
||||
if (dynamic_cast<SelectBoxState*>(state.get())) {
|
||||
// If already are in "SelectBoxState" state, in this way we
|
||||
// avoid creating a stack of several "SelectBoxState" states.
|
||||
return;
|
||||
}
|
||||
|
||||
current_editor->setState(
|
||||
EditorStatePtr(
|
||||
new SelectBoxState(
|
||||
this, current_editor->sprite()->bounds(),
|
||||
SelectBoxState::DARKOUTSIDE |
|
||||
SelectBoxState::QUICKBOX)));
|
||||
}
|
||||
// Create a brush from the active selection
|
||||
else {
|
||||
createBrush(context->activeDocument()->mask());
|
||||
|
||||
// Set pencil as current tool
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
tools::Tool* pencil =
|
||||
App::instance()->getToolBox()->getToolById(tools::WellKnownTools::Pencil);
|
||||
settings->setCurrentTool(pencil);
|
||||
|
||||
// Deselect mask
|
||||
Command* cmd =
|
||||
CommandsModule::instance()->getCommandByName(CommandId::DeselectMask);
|
||||
UIContext::instance()->executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void NewBrushCommand::onQuickboxEnd(const gfx::Rect& rect, ui::MouseButtons buttons)
|
||||
{
|
||||
Mask mask;
|
||||
mask.replace(rect);
|
||||
createBrush(&mask);
|
||||
|
||||
// If the right-button was used, we clear the selected area.
|
||||
if (buttons & ui::kButtonRight) {
|
||||
try {
|
||||
ContextWriter writer(UIContext::instance(), 250);
|
||||
Transaction transaction(writer.context(), "Clear");
|
||||
transaction.execute(new cmd::ClearRect(writer.cel(), rect));
|
||||
transaction.commit();
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the context bar
|
||||
// TODO find a way to avoid all these singletons. Maybe a simple
|
||||
// signal in the context like "brush has changed" could be enough.
|
||||
App::instance()->getMainWindow()->getContextBar()
|
||||
->updateFromTool(UIContext::instance()->settings()->getCurrentTool());
|
||||
|
||||
current_editor->backToPreviousState();
|
||||
}
|
||||
|
||||
void NewBrushCommand::onQuickboxCancel()
|
||||
{
|
||||
current_editor->backToPreviousState();
|
||||
}
|
||||
|
||||
void NewBrushCommand::createBrush(const Mask* mask)
|
||||
{
|
||||
doc::ImageRef image(new_image_from_mask(
|
||||
UIContext::instance()->activeSite(), mask));
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
// New brush
|
||||
doc::BrushRef brush(new doc::Brush());
|
||||
brush->setImage(image.get());
|
||||
brush->setPatternOrigin(mask->bounds().getOrigin());
|
||||
|
||||
// TODO add a active stock property in app::Context
|
||||
ContextBar* ctxBar = App::instance()->getMainWindow()->getContextBar();
|
||||
int slot = ctxBar->addBrush(brush);
|
||||
ctxBar->setActiveBrush(brush);
|
||||
|
||||
// Get the shortcut for this brush and show it to the user
|
||||
Params params;
|
||||
params.set("change", "custom");
|
||||
params.set("slot", base::convert_to<std::string>(slot).c_str());
|
||||
Key* key = KeyboardShortcuts::instance()->command(
|
||||
CommandId::ChangeBrush, params);
|
||||
if (key && !key->accels().empty()) {
|
||||
std::string tooltip;
|
||||
tooltip += "Shortcut: ";
|
||||
tooltip += key->accels().front().toString();
|
||||
StatusBar::instance()->showTip(2000, tooltip.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Command* CommandFactory::createNewBrushCommand()
|
||||
{
|
||||
return new NewBrushCommand();
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -29,6 +29,7 @@ FOR_EACH_COMMAND(Cut)
|
||||
FOR_EACH_COMMAND(DeselectMask)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(DeveloperConsole)
|
||||
FOR_EACH_COMMAND(DiscardBrush)
|
||||
FOR_EACH_COMMAND(DuplicateLayer)
|
||||
FOR_EACH_COMMAND(DuplicateSprite)
|
||||
FOR_EACH_COMMAND(DuplicateView)
|
||||
@ -67,6 +68,7 @@ FOR_EACH_COMMAND(MaskContent)
|
||||
FOR_EACH_COMMAND(MergeDownLayer)
|
||||
FOR_EACH_COMMAND(MoveCel)
|
||||
FOR_EACH_COMMAND(MoveMask)
|
||||
FOR_EACH_COMMAND(NewBrush)
|
||||
FOR_EACH_COMMAND(NewFile)
|
||||
FOR_EACH_COMMAND(NewFrame)
|
||||
FOR_EACH_COMMAND(NewFrameTag)
|
||||
|
@ -54,6 +54,7 @@ Document::Document(Sprite* sprite)
|
||||
, m_extraCel(NULL)
|
||||
, m_extraImage(NULL)
|
||||
, m_extraCelBlendMode(BLEND_MODE_NORMAL)
|
||||
, m_extraCelType(render::ExtraType::NONE)
|
||||
// Mask
|
||||
, m_mask(new Mask())
|
||||
, m_maskVisible(true)
|
||||
@ -272,6 +273,7 @@ void Document::destroyExtraCel()
|
||||
|
||||
m_extraCel = NULL;
|
||||
m_extraImage.reset(NULL);
|
||||
m_extraCelType = render::ExtraType::NONE;
|
||||
}
|
||||
|
||||
void Document::prepareExtraCel(const gfx::Rect& bounds, int opacity)
|
||||
@ -293,6 +295,11 @@ void Document::prepareExtraCel(const gfx::Rect& bounds, int opacity)
|
||||
m_extraCel->setOpacity(opacity);
|
||||
}
|
||||
|
||||
void Document::setExtraCelType(render::ExtraType type)
|
||||
{
|
||||
m_extraCelType = type;
|
||||
}
|
||||
|
||||
Cel* Document::getExtraCel() const
|
||||
{
|
||||
return m_extraCel;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "doc/pixel_format.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/transformation.h"
|
||||
#include "render/extra_type.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -123,10 +124,11 @@ namespace app {
|
||||
// Extra Cel (it is used to draw pen preview, pixels in movement, etc.)
|
||||
|
||||
void prepareExtraCel(const gfx::Rect& bounds, int opacity);
|
||||
void setExtraCelType(render::ExtraType type);
|
||||
void destroyExtraCel();
|
||||
Cel* getExtraCel() const;
|
||||
Image* getExtraCelImage() const;
|
||||
|
||||
render::ExtraType getExtraCelType() const { return m_extraCelType; }
|
||||
int getExtraCelBlendMode() const { return m_extraCelBlendMode; }
|
||||
void setExtraCelBlendMode(int mode) { m_extraCelBlendMode = mode; }
|
||||
|
||||
@ -216,6 +218,7 @@ namespace app {
|
||||
// Image of the extra cel.
|
||||
ImageRef m_extraImage;
|
||||
int m_extraCelBlendMode;
|
||||
render::ExtraType m_extraCelType;
|
||||
|
||||
// Current mask.
|
||||
base::UniquePtr<Mask> m_mask;
|
||||
|
@ -12,10 +12,6 @@
|
||||
#include "app/pref/option.h"
|
||||
#include "doc/documents_observer.h"
|
||||
|
||||
namespace filters {
|
||||
enum class TiledMode;
|
||||
}
|
||||
|
||||
#include "generated_pref_types.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "app/tools/shade_table.h"
|
||||
#include "app/tools/shading_options.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives_fast.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "filters/neighboring_pixels.h"
|
||||
@ -699,6 +700,150 @@ private:
|
||||
color_t m_color;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Brush Ink
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename ImageTraits>
|
||||
class BrushInkProcessing : public DoubleInkProcessing<BrushInkProcessing<ImageTraits>, ImageTraits> {
|
||||
public:
|
||||
BrushInkProcessing(ToolLoop* loop) {
|
||||
m_fgColor = loop->getPrimaryColor();
|
||||
m_bgColor = loop->getSecondaryColor();
|
||||
m_palette = get_current_palette();
|
||||
m_brush = loop->getBrush();
|
||||
m_brushImage = m_brush->image();
|
||||
m_opacity = loop->getOpacity();
|
||||
m_width = m_brush->bounds().w;
|
||||
m_height = m_brush->bounds().h;
|
||||
m_u = (loop->getOffset().x + m_brush->patternOrigin().x) % m_width;
|
||||
m_v = (loop->getOffset().y + m_brush->patternOrigin().y) % m_height;
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
void alignPixelPoint(int& x, int& y) {
|
||||
x = (x - m_u) % m_width;
|
||||
y = (y - m_v) % m_height;
|
||||
if (x < 0) x = m_width - ((-x) % m_width);
|
||||
if (y < 0) y = m_height - ((-y) % m_height);
|
||||
}
|
||||
|
||||
color_t m_fgColor;
|
||||
color_t m_bgColor;
|
||||
const Palette* m_palette;
|
||||
const Brush* m_brush;
|
||||
const Image* m_brushImage;
|
||||
int m_opacity;
|
||||
int m_u, m_v, m_width, m_height;
|
||||
};
|
||||
|
||||
template<>
|
||||
void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(m_brushImage, x, y);
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
c = get_pixel_fast<IndexedTraits>(m_brushImage, x, y);
|
||||
c = m_palette->getEntry(c);
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(m_brushImage, x, y);
|
||||
c = graya(m_palette->getEntry(c), graya_geta(c));
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = rgba_blend_normal(*m_srcAddress, c, m_opacity);
|
||||
}
|
||||
|
||||
template<>
|
||||
void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(m_brushImage, x, y);
|
||||
c = graya(int(rgba_getr(c)) + int(rgba_getg(c)) + int(rgba_getb(c)) / 3,
|
||||
rgba_geta(c));
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
c = get_pixel_fast<IndexedTraits>(m_brushImage, x, y);
|
||||
c = m_palette->getEntry(c);
|
||||
c = graya(int(rgba_getr(c)) + int(rgba_getg(c)) + int(rgba_getb(c)) / 3,
|
||||
rgba_geta(c));
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(m_brushImage, x, y);
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = graya_blend_normal(*m_srcAddress, c, m_opacity);
|
||||
}
|
||||
|
||||
template<>
|
||||
void BrushInkProcessing<IndexedTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(m_brushImage, x, y);
|
||||
c = m_palette->findBestfit(rgba_getr(c), rgba_getg(c), rgba_getb(c));
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
c = get_pixel_fast<IndexedTraits>(m_brushImage, x, y);
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(m_brushImage, x, y);
|
||||
c = graya_getv(c);
|
||||
c = m_palette->findBestfit(c, c, c);
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = c;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum {
|
||||
@ -711,6 +856,7 @@ enum {
|
||||
INK_JUMBLE,
|
||||
INK_SHADING,
|
||||
INK_XOR,
|
||||
INK_BRUSH,
|
||||
MAX_INKS
|
||||
};
|
||||
|
||||
@ -737,7 +883,8 @@ AlgoHLine ink_processing[][3] =
|
||||
DEFINE_INK(ReplaceInkProcessing),
|
||||
DEFINE_INK(JumbleInkProcessing),
|
||||
DEFINE_INK(ShadingInkProcessing),
|
||||
DEFINE_INK(XorInkProcessing)
|
||||
DEFINE_INK(XorInkProcessing),
|
||||
DEFINE_INK(BrushInkProcessing)
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -60,7 +60,10 @@ public:
|
||||
|
||||
switch (m_type) {
|
||||
case Opaque:
|
||||
m_proc = ink_processing[INK_OPAQUE][depth];
|
||||
if (loop->getBrush()->type() == doc::kImageBrushType)
|
||||
m_proc = ink_processing[INK_BRUSH][depth];
|
||||
else
|
||||
m_proc = ink_processing[INK_OPAQUE][depth];
|
||||
break;
|
||||
case SetAlpha:
|
||||
m_proc = ink_processing[INK_SETALPHA][depth];
|
||||
@ -69,9 +72,12 @@ public:
|
||||
m_proc = ink_processing[INK_LOCKALPHA][depth];
|
||||
break;
|
||||
default:
|
||||
m_proc = (loop->getOpacity() == 255 ?
|
||||
ink_processing[INK_OPAQUE][depth]:
|
||||
ink_processing[INK_TRANSPARENT][depth]);
|
||||
if (loop->getBrush()->type() == doc::kImageBrushType)
|
||||
m_proc = ink_processing[INK_BRUSH][depth];
|
||||
else
|
||||
m_proc = (loop->getOpacity() == 255 ?
|
||||
ink_processing[INK_OPAQUE][depth]:
|
||||
ink_processing[INK_TRANSPARENT][depth]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace app {
|
||||
virtual ~PointShape() { }
|
||||
virtual bool isFloodFill() { return false; }
|
||||
virtual bool isSpray() { return false; }
|
||||
virtual void preparePointShape(ToolLoop* loop) { }
|
||||
virtual void transformPoint(ToolLoop* loop, int x, int y) = 0;
|
||||
virtual void getModifiedArea(ToolLoop* loop, int x, int y, gfx::Rect& area) = 0;
|
||||
|
||||
|
@ -10,60 +10,80 @@ namespace tools {
|
||||
|
||||
class NonePointShape : public PointShape {
|
||||
public:
|
||||
void transformPoint(ToolLoop* loop, int x, int y)
|
||||
{
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
// Do nothing
|
||||
}
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area)
|
||||
{
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
class PixelPointShape : public PointShape {
|
||||
public:
|
||||
void transformPoint(ToolLoop* loop, int x, int y)
|
||||
{
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
doInkHline(x, y, x, loop);
|
||||
}
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area)
|
||||
{
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
area = Rect(x, y, 1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
class BrushPointShape : public PointShape {
|
||||
Brush* m_brush;
|
||||
base::SharedPtr<CompressedImage> m_compressedImage;
|
||||
bool m_firstPoint;
|
||||
|
||||
public:
|
||||
void transformPoint(ToolLoop* loop, int x, int y)
|
||||
{
|
||||
Brush* brush = loop->getBrush();
|
||||
std::vector<BrushScanline>::const_iterator scanline = brush->scanline().begin();
|
||||
int v, h = brush->bounds().h;
|
||||
|
||||
x += brush->bounds().x;
|
||||
y += brush->bounds().y;
|
||||
void preparePointShape(ToolLoop* loop) override {
|
||||
m_brush = loop->getBrush();
|
||||
m_compressedImage.reset(new CompressedImage(m_brush->image(), false));
|
||||
m_firstPoint = true;
|
||||
}
|
||||
|
||||
for (v=0; v<h; ++v) {
|
||||
if (scanline->state)
|
||||
doInkHline(x+scanline->x1, y+v, x+scanline->x2, loop);
|
||||
++scanline;
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
int h = m_brush->bounds().h;
|
||||
|
||||
x += m_brush->bounds().x;
|
||||
y += m_brush->bounds().y;
|
||||
|
||||
if (m_firstPoint) {
|
||||
m_firstPoint = false;
|
||||
if (m_brush->type() == kImageBrushType) {
|
||||
if (m_brush->pattern() == BrushPattern::ALIGNED_TO_DST ||
|
||||
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y)-loop->getOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_brush->type() == kImageBrushType &&
|
||||
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y)-loop->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto scanline : *m_compressedImage) {
|
||||
int u = x+scanline.x;
|
||||
doInkHline(u, y+scanline.y, u+scanline.w-1, loop);
|
||||
}
|
||||
}
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area)
|
||||
{
|
||||
Brush* brush = loop->getBrush();
|
||||
area = brush->bounds();
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
area = m_brush->bounds();
|
||||
area.x += x;
|
||||
area.y += y;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class FloodFillPointShape : public PointShape {
|
||||
public:
|
||||
bool isFloodFill() { return true; }
|
||||
bool isFloodFill() override { return true; }
|
||||
|
||||
void transformPoint(ToolLoop* loop, int x, int y)
|
||||
{
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
doc::algorithm::floodfill(
|
||||
const_cast<Image*>(loop->getSrcImage()), x, y,
|
||||
paintBounds(loop, x, y),
|
||||
@ -72,8 +92,7 @@ public:
|
||||
loop, (AlgoHLine)doInkHline);
|
||||
}
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area)
|
||||
{
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
area = paintBounds(loop, x, y);
|
||||
}
|
||||
|
||||
@ -112,10 +131,13 @@ class SprayPointShape : public PointShape {
|
||||
|
||||
public:
|
||||
|
||||
bool isSpray() { return true; }
|
||||
bool isSpray() override { return true; }
|
||||
|
||||
void transformPoint(ToolLoop* loop, int x, int y)
|
||||
{
|
||||
void preparePointShape(ToolLoop* loop) override {
|
||||
m_subPointShape.preparePointShape(loop);
|
||||
}
|
||||
|
||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||
int spray_width = loop->getSprayWidth();
|
||||
int spray_speed = loop->getSpraySpeed();
|
||||
int c, u, v, times = (spray_width*spray_width/4) * spray_speed / 100;
|
||||
@ -145,8 +167,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area)
|
||||
{
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
int spray_width = loop->getSprayWidth();
|
||||
Point p1(x-spray_width, y-spray_width);
|
||||
Point p2(x+spray_width, y+spray_width);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "doc/algorithm/floodfill.h"
|
||||
#include "doc/algorithm/polygon.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/compressed_image.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/mask.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
@ -40,6 +41,7 @@ namespace tools {
|
||||
using namespace gfx;
|
||||
|
||||
const char* WellKnownTools::RectangularMarquee = "rectangular_marquee";
|
||||
const char* WellKnownTools::Pencil = "pencil";
|
||||
const char* WellKnownTools::Eraser = "eraser";
|
||||
const char* WellKnownTools::Eyedropper = "eyedropper";
|
||||
|
||||
@ -70,6 +72,12 @@ const char* WellKnownIntertwiners::AsEllipses = "as_ellipses";
|
||||
const char* WellKnownIntertwiners::AsBezier = "as_bezier";
|
||||
const char* WellKnownIntertwiners::AsPixelPerfect = "as_pixel_perfect";
|
||||
|
||||
const char* WellKnownPointShapes::None = "none";
|
||||
const char* WellKnownPointShapes::Pixel = "pixel";
|
||||
const char* WellKnownPointShapes::Brush = "brush";
|
||||
const char* WellKnownPointShapes::FloodFill = "floodfill";
|
||||
const char* WellKnownPointShapes::Spray = "spray";
|
||||
|
||||
ToolBox::ToolBox()
|
||||
{
|
||||
PRINTF("Toolbox module: installing\n");
|
||||
@ -100,11 +108,11 @@ ToolBox::ToolBox()
|
||||
m_controllers["two_points"] = new TwoPointsController();
|
||||
m_controllers["four_points"] = new FourPointsController();
|
||||
|
||||
m_pointshapers["none"] = new NonePointShape();
|
||||
m_pointshapers["pixel"] = new PixelPointShape();
|
||||
m_pointshapers["brush"] = new BrushPointShape();
|
||||
m_pointshapers["floodfill"] = new FloodFillPointShape();
|
||||
m_pointshapers["spray"] = new SprayPointShape();
|
||||
m_pointshapers[WellKnownPointShapes::None] = new NonePointShape();
|
||||
m_pointshapers[WellKnownPointShapes::Pixel] = new PixelPointShape();
|
||||
m_pointshapers[WellKnownPointShapes::Brush] = new BrushPointShape();
|
||||
m_pointshapers[WellKnownPointShapes::FloodFill] = new FloodFillPointShape();
|
||||
m_pointshapers[WellKnownPointShapes::Spray] = new SprayPointShape();
|
||||
|
||||
m_intertwiners[WellKnownIntertwiners::None] = new IntertwineNone();
|
||||
m_intertwiners[WellKnownIntertwiners::AsLines] = new IntertwineAsLines();
|
||||
@ -162,6 +170,11 @@ Intertwine* ToolBox::getIntertwinerById(const std::string& id)
|
||||
return m_intertwiners[id];
|
||||
}
|
||||
|
||||
PointShape* ToolBox::getPointShapeById(const std::string& id)
|
||||
{
|
||||
return m_pointshapers[id];
|
||||
}
|
||||
|
||||
void ToolBox::loadTools()
|
||||
{
|
||||
PRINTF("Loading Aseprite tools\n");
|
||||
|
@ -22,6 +22,7 @@ namespace app {
|
||||
|
||||
namespace WellKnownTools {
|
||||
extern const char* RectangularMarquee;
|
||||
extern const char* Pencil;
|
||||
extern const char* Eraser;
|
||||
extern const char* Eyedropper;
|
||||
};
|
||||
@ -57,6 +58,14 @@ namespace app {
|
||||
extern const char* AsPixelPerfect;
|
||||
};
|
||||
|
||||
namespace WellKnownPointShapes {
|
||||
extern const char* None;
|
||||
extern const char* Pixel;
|
||||
extern const char* Brush;
|
||||
extern const char* FloodFill;
|
||||
extern const char* Spray;
|
||||
};
|
||||
|
||||
typedef std::list<Tool*> ToolList;
|
||||
typedef ToolList::iterator ToolIterator;
|
||||
typedef ToolList::const_iterator ToolConstIterator;
|
||||
@ -80,6 +89,7 @@ namespace app {
|
||||
Tool* getToolById(const std::string& id);
|
||||
Ink* getInkById(const std::string& id);
|
||||
Intertwine* getIntertwinerById(const std::string& id);
|
||||
PointShape* getPointShapeById(const std::string& id);
|
||||
int getGroupsCount() const { return m_groups.size(); }
|
||||
|
||||
private:
|
||||
|
@ -55,6 +55,7 @@ void ToolLoopManager::prepareLoop(const Pointer& pointer)
|
||||
m_toolLoop->getInk()->prepareInk(m_toolLoop);
|
||||
m_toolLoop->getIntertwine()->prepareIntertwine();
|
||||
m_toolLoop->getController()->prepareController();
|
||||
m_toolLoop->getPointShape()->preparePointShape(m_toolLoop);
|
||||
|
||||
// Prepare preview image (the destination image will be our preview
|
||||
// in the tool-loop time, so we can see what we are drawing)
|
||||
|
232
src/app/ui/brush_popup.cpp
Normal file
232
src/app/ui/brush_popup.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/ui/brush_popup.h"
|
||||
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/ui/app_menuitem.h"
|
||||
#include "app/ui/button_set.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/conversion_she.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "gfx/border.h"
|
||||
#include "gfx/region.h"
|
||||
#include "she/scoped_surface_lock.h"
|
||||
#include "she/surface.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/separator.h"
|
||||
#include "ui/tooltips.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace app::skin;
|
||||
using namespace doc;
|
||||
using namespace ui;
|
||||
|
||||
class Item : public ButtonSet::Item {
|
||||
public:
|
||||
Item(BrushPopup* popup, BrushPopupDelegate* delegate, const BrushRef& brush, int slot = -1)
|
||||
: m_popup(popup)
|
||||
, m_delegate(delegate)
|
||||
, m_brush(brush)
|
||||
, m_slot(slot) {
|
||||
setIcon(BrushPopup::createSurfaceForBrush(brush));
|
||||
}
|
||||
|
||||
~Item() {
|
||||
icon()->dispose();
|
||||
}
|
||||
|
||||
const BrushRef& brush() const {
|
||||
return m_brush;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(Message* msg) override {
|
||||
if (msg->type() == kMouseUpMessage && m_slot > 0) {
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
if (mouseMsg->buttons() == kButtonRight) {
|
||||
Menu menu;
|
||||
AppMenuItem deleteItem("Delete");
|
||||
AppMenuItem deleteAllItem("Delete All");
|
||||
deleteItem.Click.connect(&Item::onDeleteBrush, this);
|
||||
deleteAllItem.Click.connect(&Item::onDeleteAllBrushes, this);
|
||||
menu.addChild(&deleteItem);
|
||||
menu.addChild(new Separator("", JI_HORIZONTAL));
|
||||
menu.addChild(&deleteAllItem);
|
||||
|
||||
// Here we make the popup window temporaly floating, so it's
|
||||
// not closed by the popup menu.
|
||||
m_popup->makeFloating();
|
||||
menu.showPopup(mouseMsg->position());
|
||||
m_popup->makeFixed();
|
||||
m_popup->closeWindow(nullptr);
|
||||
}
|
||||
}
|
||||
return ButtonSet::Item::onProcessMessage(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
void onDeleteBrush() {
|
||||
m_delegate->onDeleteBrushSlot(m_slot);
|
||||
}
|
||||
|
||||
void onDeleteAllBrushes() {
|
||||
m_delegate->onDeleteAllBrushes();
|
||||
}
|
||||
|
||||
BrushPopup* m_popup;
|
||||
BrushPopupDelegate* m_delegate;
|
||||
BrushRef m_brush;
|
||||
int m_slot;
|
||||
};
|
||||
|
||||
static BrushRef defBrushes[3];
|
||||
|
||||
BrushPopup::BrushPopup(BrushPopupDelegate* delegate)
|
||||
: PopupWindow("", kCloseOnClickInOtherWindow)
|
||||
, m_delegate(delegate)
|
||||
{
|
||||
setAutoRemap(false);
|
||||
setBorder(gfx::Border(0));
|
||||
child_spacing = 0;
|
||||
}
|
||||
|
||||
void BrushPopup::setBrush(Brush* brush)
|
||||
{
|
||||
for (auto child : m_buttons->getChildren()) {
|
||||
Item* item = static_cast<Item*>(child);
|
||||
|
||||
// Same type and same image
|
||||
if (item->brush() &&
|
||||
item->brush()->type() == brush->type() &&
|
||||
(brush->type() != kImageBrushType ||
|
||||
item->brush()->image() == brush->image())) {
|
||||
m_buttons->setSelectedItem(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BrushPopup::regenerate(const gfx::Rect& box, const Brushes& brushes)
|
||||
{
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
int columns = 3;
|
||||
|
||||
if (m_buttons) {
|
||||
for (auto child : m_buttons->getChildren())
|
||||
m_tooltipManager->removeTooltipFor(child);
|
||||
removeChild(m_buttons.get());
|
||||
m_buttons.reset();
|
||||
}
|
||||
|
||||
if (!defBrushes[0]) {
|
||||
defBrushes[0].reset(new Brush(kCircleBrushType, 7, 0));
|
||||
defBrushes[1].reset(new Brush(kSquareBrushType, 7, 0));
|
||||
defBrushes[2].reset(new Brush(kLineBrushType, 7, 44));
|
||||
}
|
||||
|
||||
m_buttons.reset(new ButtonSet(columns));
|
||||
m_buttons->addItem(new Item(this, m_delegate, defBrushes[0]));
|
||||
m_buttons->addItem(new Item(this, m_delegate, defBrushes[1]));
|
||||
m_buttons->addItem(new Item(this, m_delegate, defBrushes[2]));
|
||||
|
||||
int slot = 1;
|
||||
for (const auto& brush : brushes) {
|
||||
Item* item = new Item(this, m_delegate, brush, slot);
|
||||
m_buttons->addItem(item);
|
||||
|
||||
Params params;
|
||||
params.set("change", "custom");
|
||||
params.set("slot", base::convert_to<std::string>(slot).c_str());
|
||||
Key* key = KeyboardShortcuts::instance()->command(
|
||||
CommandId::ChangeBrush, params);
|
||||
if (key && !key->accels().empty()) {
|
||||
std::string tooltip;
|
||||
tooltip += "Shortcut: ";
|
||||
tooltip += key->accels().front().toString();
|
||||
m_tooltipManager->addTooltipFor(item, tooltip, JI_TOP);
|
||||
}
|
||||
slot++;
|
||||
}
|
||||
// Add empty spaces
|
||||
while (((slot-1) % columns) > 0)
|
||||
m_buttons->addItem(new Item(this, m_delegate, BrushRef(nullptr), slot++));
|
||||
|
||||
m_buttons->ItemChange.connect(&BrushPopup::onButtonChange, this);
|
||||
m_buttons->setTransparent(true);
|
||||
m_buttons->setBgColor(gfx::ColorNone);
|
||||
addChild(m_buttons.get());
|
||||
|
||||
gfx::Rect rc = box;
|
||||
int buttons = m_buttons->getChildren().size();
|
||||
int rows = (buttons/columns + ((buttons%columns) > 0 ? 1: 0));
|
||||
rc.w *= columns;
|
||||
rc.h = rows * (rc.h-2*guiscale()) + 2*guiscale();
|
||||
|
||||
setBounds(rc);
|
||||
}
|
||||
|
||||
void BrushPopup::onButtonChange()
|
||||
{
|
||||
Item* item = static_cast<Item*>(m_buttons->getItem(m_buttons->selectedItem()));
|
||||
if (item->brush())
|
||||
BrushChange(item->brush());
|
||||
}
|
||||
|
||||
// static
|
||||
she::Surface* BrushPopup::createSurfaceForBrush(const BrushRef& origBrush)
|
||||
{
|
||||
Image* image = nullptr;
|
||||
BrushRef brush = origBrush;
|
||||
if (brush) {
|
||||
if (brush->type() != kImageBrushType && brush->size() > 10) {
|
||||
brush.reset(new Brush(*brush));
|
||||
brush->setSize(10);
|
||||
}
|
||||
image = brush->image();
|
||||
}
|
||||
|
||||
she::Surface* surface = she::instance()->createRgbaSurface(
|
||||
std::min(10, image ? image->width(): 4),
|
||||
std::min(10, image ? image->height(): 4));
|
||||
|
||||
if (image) {
|
||||
Palette* palette = get_current_palette();
|
||||
if (image->pixelFormat() == IMAGE_BITMAP) {
|
||||
palette = new Palette(frame_t(0), 2);
|
||||
palette->setEntry(0, rgba(0, 0, 0, 0));
|
||||
palette->setEntry(1, rgba(0, 0, 0, 255));
|
||||
}
|
||||
|
||||
convert_image_to_surface(
|
||||
image, palette, surface,
|
||||
0, 0, 0, 0, image->width(), image->height());
|
||||
|
||||
if (image->pixelFormat() == IMAGE_BITMAP)
|
||||
delete palette;
|
||||
}
|
||||
else {
|
||||
she::ScopedSurfaceLock lock(surface);
|
||||
lock->clear();
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // namespace app
|
61
src/app/ui/brush_popup.h
Normal file
61
src/app/ui/brush_popup.h
Normal file
@ -0,0 +1,61 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
#ifndef APP_UI_BRUSH_POPUP_H_INCLUDED
|
||||
#define APP_UI_BRUSH_POPUP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/shared_ptr.h"
|
||||
#include "base/signal.h"
|
||||
#include "doc/brushes.h"
|
||||
#include "ui/popup_window.h"
|
||||
|
||||
namespace doc {
|
||||
class Brush;
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
class Menu;
|
||||
class TooltipManager;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class ButtonSet;
|
||||
|
||||
class BrushPopupDelegate {
|
||||
public:
|
||||
virtual ~BrushPopupDelegate() { }
|
||||
virtual void onDeleteBrushSlot(int slot) = 0;
|
||||
virtual void onDeleteAllBrushes() = 0;
|
||||
};
|
||||
|
||||
class BrushPopup : public ui::PopupWindow {
|
||||
public:
|
||||
BrushPopup(BrushPopupDelegate* delegate);
|
||||
|
||||
void setBrush(doc::Brush* brush);
|
||||
void regenerate(const gfx::Rect& box, const doc::Brushes& brushes);
|
||||
|
||||
void setupTooltips(ui::TooltipManager* tooltipManager) {
|
||||
m_tooltipManager = tooltipManager;
|
||||
}
|
||||
|
||||
Signal1<void, const doc::BrushRef&> BrushChange;
|
||||
|
||||
static she::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
|
||||
|
||||
private:
|
||||
void onButtonChange();
|
||||
|
||||
base::SharedPtr<ButtonSet> m_buttons;
|
||||
ui::TooltipManager* m_tooltipManager;
|
||||
BrushPopupDelegate* m_delegate;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -145,7 +145,7 @@ void ButtonSet::Item::onPreferredSize(ui::PreferredSizeEvent& ev)
|
||||
|
||||
Grid::Info info = buttonSet()->getChildInfo(this);
|
||||
if (info.row == info.grid_rows-1)
|
||||
sz.h += 3;
|
||||
sz.h += 3*guiscale();
|
||||
|
||||
ev.setPreferredSize(sz*guiscale());
|
||||
}
|
||||
@ -162,6 +162,11 @@ void ButtonSet::addItem(she::Surface* icon, int hspan, int vspan)
|
||||
{
|
||||
Item* item = new Item();
|
||||
item->setIcon(icon);
|
||||
addItem(item, hspan, vspan);
|
||||
}
|
||||
|
||||
void ButtonSet::addItem(Item* item, int hspan, int vspan)
|
||||
{
|
||||
addChildInCell(item, hspan, vspan, JI_CENTER | JI_MIDDLE);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ namespace app {
|
||||
public:
|
||||
Item();
|
||||
void setIcon(she::Surface* icon);
|
||||
she::Surface* icon() const { return m_icon; }
|
||||
ButtonSet* buttonSet();
|
||||
protected:
|
||||
void onPaint(ui::PaintEvent& ev) override;
|
||||
@ -34,6 +35,7 @@ namespace app {
|
||||
ButtonSet(int columns);
|
||||
|
||||
void addItem(she::Surface* icon, int hspan = 1, int vspan = 1);
|
||||
void addItem(Item* item, int hspan = 1, int vspan = 1);
|
||||
Item* getItem(int index);
|
||||
|
||||
int selectedItem() const;
|
||||
|
@ -12,7 +12,10 @@
|
||||
#include "app/ui/context_bar.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/settings/ink_type.h"
|
||||
#include "app/settings/selection_mode.h"
|
||||
#include "app/settings/settings.h"
|
||||
@ -22,6 +25,7 @@
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/brush_popup.h"
|
||||
#include "app/ui/button_set.h"
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
@ -33,7 +37,6 @@
|
||||
#include "doc/conversion_she.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "she/scoped_surface_lock.h"
|
||||
#include "she/surface.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/button.h"
|
||||
@ -55,17 +58,16 @@ using namespace tools;
|
||||
|
||||
static bool g_updatingFromTool = false;
|
||||
|
||||
class ContextBar::BrushTypeField : public ButtonSet {
|
||||
class ContextBar::BrushTypeField : public ButtonSet
|
||||
, public BrushPopupDelegate {
|
||||
public:
|
||||
BrushTypeField()
|
||||
BrushTypeField(ContextBar* owner)
|
||||
: ButtonSet(1)
|
||||
, m_popupWindow(NULL)
|
||||
, m_brushTypeButton(NULL) {
|
||||
m_bitmap = she::instance()->createRgbaSurface(8, 8);
|
||||
she::ScopedSurfaceLock lock(m_bitmap);
|
||||
lock->clear();
|
||||
|
||||
, m_owner(owner)
|
||||
, m_bitmap(BrushPopup::createSurfaceForBrush(BrushRef(nullptr)))
|
||||
, m_popupWindow(this) {
|
||||
addItem(m_bitmap);
|
||||
m_popupWindow.BrushChange.connect(&BrushTypeField::onBrushChange, this);
|
||||
}
|
||||
|
||||
~BrushTypeField() {
|
||||
@ -74,34 +76,25 @@ public:
|
||||
m_bitmap->dispose();
|
||||
}
|
||||
|
||||
void setBrushSettings(IBrushSettings* brushSettings) {
|
||||
base::UniquePtr<Palette> palette(new Palette(frame_t(0), 2));
|
||||
palette->setEntry(0, doc::rgba(0, 0, 0, 0));
|
||||
palette->setEntry(1, doc::rgba(0, 0, 0, 255));
|
||||
|
||||
base::UniquePtr<Brush> brush(
|
||||
new Brush(
|
||||
m_brushType = brushSettings->getType(),
|
||||
std::min(10, brushSettings->getSize()),
|
||||
brushSettings->getAngle()));
|
||||
|
||||
Image* image = brush->image();
|
||||
|
||||
void updateBrush(tools::Tool* tool = nullptr) {
|
||||
if (m_bitmap)
|
||||
m_bitmap->dispose();
|
||||
|
||||
m_bitmap = she::instance()->createRgbaSurface(image->width(), image->height());
|
||||
convert_image_to_surface(image, palette, m_bitmap,
|
||||
0, 0, 0, 0, image->width(), image->height());
|
||||
m_bitmap = BrushPopup::createSurfaceForBrush(
|
||||
m_owner->activeBrush(tool));
|
||||
|
||||
getItem(0)->setIcon(m_bitmap);
|
||||
}
|
||||
|
||||
void setupTooltips(TooltipManager* tooltipManager) {
|
||||
m_popupWindow.setupTooltips(tooltipManager);
|
||||
}
|
||||
|
||||
protected:
|
||||
void onItemChange() override {
|
||||
ButtonSet::onItemChange();
|
||||
|
||||
if (!m_popupWindow || !m_popupWindow->isVisible())
|
||||
if (!m_popupWindow.isVisible())
|
||||
openPopup();
|
||||
else
|
||||
closePopup();
|
||||
@ -111,59 +104,61 @@ protected:
|
||||
ev.setPreferredSize(Size(16, 18)*guiscale());
|
||||
}
|
||||
|
||||
private:
|
||||
void openPopup() {
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
|
||||
// BrushPopupDelegate impl
|
||||
void onDeleteBrushSlot(int slot) override {
|
||||
m_owner->removeBrush(slot);
|
||||
}
|
||||
|
||||
void onDeleteAllBrushes() override {
|
||||
m_owner->removeAllBrushes();
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns a little rectangle that can be used by the popup as the
|
||||
// first brush position.
|
||||
gfx::Rect getPopupBox() {
|
||||
Rect rc = getBounds();
|
||||
rc.y += rc.h - 2*guiscale();
|
||||
rc.setSize(getPreferredSize());
|
||||
rc.w *= 3;
|
||||
m_popupWindow = new PopupWindow("", PopupWindow::kCloseOnClickInOtherWindow);
|
||||
m_popupWindow->setAutoRemap(false);
|
||||
m_popupWindow->setBorder(Border(0));
|
||||
m_popupWindow->setBounds(rc);
|
||||
m_popupWindow->child_spacing = 0;
|
||||
|
||||
Region rgn(m_popupWindow->getBounds().createUnion(getBounds()));
|
||||
m_popupWindow->setHotRegion(rgn);
|
||||
m_brushTypeButton = new ButtonSet(3);
|
||||
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_CIRCLE));
|
||||
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_SQUARE));
|
||||
m_brushTypeButton->addItem(theme->get_part(PART_BRUSH_LINE));
|
||||
m_brushTypeButton->setSelectedItem(m_brushType);
|
||||
m_brushTypeButton->ItemChange.connect(&BrushTypeField::onBrushTypeChange, this);
|
||||
m_brushTypeButton->setTransparent(true);
|
||||
m_brushTypeButton->setBgColor(gfx::ColorNone);
|
||||
|
||||
m_popupWindow->addChild(m_brushTypeButton);
|
||||
m_popupWindow->openWindow();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void closePopup() {
|
||||
if (m_popupWindow) {
|
||||
m_popupWindow->closeWindow(NULL);
|
||||
delete m_popupWindow;
|
||||
m_popupWindow = NULL;
|
||||
m_brushTypeButton = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void onBrushTypeChange() {
|
||||
m_brushType = (BrushType)m_brushTypeButton->selectedItem();
|
||||
|
||||
void openPopup() {
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
Tool* currentTool = settings->getCurrentTool();
|
||||
IBrushSettings* brushSettings = settings->getToolSettings(currentTool)->getBrush();
|
||||
brushSettings->setType(m_brushType);
|
||||
doc::BrushRef brush = m_owner->activeBrush();
|
||||
|
||||
setBrushSettings(brushSettings);
|
||||
m_popupWindow.regenerate(getPopupBox(), m_owner->getBrushes());
|
||||
m_popupWindow.setBrush(brush.get());
|
||||
|
||||
Region rgn(m_popupWindow.getBounds().createUnion(getBounds()));
|
||||
m_popupWindow.setHotRegion(rgn);
|
||||
|
||||
m_popupWindow.openWindow();
|
||||
}
|
||||
|
||||
void closePopup() {
|
||||
m_popupWindow.closeWindow(NULL);
|
||||
}
|
||||
|
||||
void onBrushChange(const BrushRef& brush) {
|
||||
if (brush->type() == kImageBrushType)
|
||||
m_owner->setActiveBrush(brush);
|
||||
else {
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
Tool* currentTool = settings->getCurrentTool();
|
||||
IBrushSettings* brushSettings = settings->getToolSettings(currentTool)->getBrush();
|
||||
brushSettings->setType(brush->type());
|
||||
|
||||
m_owner->setActiveBrush(
|
||||
ContextBar::createBrushFromSettings(brushSettings));
|
||||
}
|
||||
}
|
||||
|
||||
ContextBar* m_owner;
|
||||
she::Surface* m_bitmap;
|
||||
BrushType m_brushType;
|
||||
PopupWindow* m_popupWindow;
|
||||
ButtonSet* m_brushTypeButton;
|
||||
BrushPopup m_popupWindow;
|
||||
};
|
||||
|
||||
class ContextBar::BrushSizeField : public IntEntry
|
||||
@ -208,15 +203,59 @@ protected:
|
||||
->getBrush()
|
||||
->setAngle(getValue());
|
||||
|
||||
IToolSettings* toolSettings = settings->getToolSettings(currentTool);
|
||||
IBrushSettings* brushSettings = toolSettings->getBrush();
|
||||
m_brushType->setBrushSettings(brushSettings);
|
||||
m_brushType->updateBrush();
|
||||
}
|
||||
|
||||
private:
|
||||
BrushTypeField* m_brushType;
|
||||
};
|
||||
|
||||
class ContextBar::BrushPatternField : public ComboBox
|
||||
{
|
||||
public:
|
||||
BrushPatternField() : m_lock(false) {
|
||||
addItem("Pattern aligned to source");
|
||||
addItem("Pattern aligned to destination");
|
||||
addItem("Paint brush");
|
||||
}
|
||||
|
||||
void setBrushPattern(BrushPattern type) {
|
||||
int index = 0;
|
||||
|
||||
switch (type) {
|
||||
case BrushPattern::ALIGNED_TO_SRC: index = 0; break;
|
||||
case BrushPattern::ALIGNED_TO_DST: index = 1; break;
|
||||
case BrushPattern::PAINT_BRUSH: index = 2; break;
|
||||
}
|
||||
|
||||
m_lock = true;
|
||||
setSelectedItemIndex(index);
|
||||
m_lock = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onChange() override {
|
||||
ComboBox::onChange();
|
||||
|
||||
if (m_lock)
|
||||
return;
|
||||
|
||||
BrushPattern type = BrushPattern::ALIGNED_TO_SRC;
|
||||
|
||||
switch (getSelectedItemIndex()) {
|
||||
case 0: type = BrushPattern::ALIGNED_TO_SRC; break;
|
||||
case 1: type = BrushPattern::ALIGNED_TO_DST; break;
|
||||
case 2: type = BrushPattern::PAINT_BRUSH; break;
|
||||
}
|
||||
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
Tool* currentTool = settings->getCurrentTool();
|
||||
App::instance()->preferences().brush.pattern(type);
|
||||
}
|
||||
|
||||
bool m_lock;
|
||||
};
|
||||
|
||||
class ContextBar::ToleranceField : public IntEntry
|
||||
{
|
||||
public:
|
||||
@ -742,9 +781,10 @@ ContextBar::ContextBar()
|
||||
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField);
|
||||
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
|
||||
|
||||
addChild(m_brushType = new BrushTypeField());
|
||||
addChild(m_brushType = new BrushTypeField(this));
|
||||
addChild(m_brushSize = new BrushSizeField());
|
||||
addChild(m_brushAngle = new BrushAngleField(m_brushType));
|
||||
addChild(m_brushPatternField = new BrushPatternField());
|
||||
|
||||
addChild(m_toleranceLabel = new Label("Tolerance:"));
|
||||
addChild(m_tolerance = new ToleranceField());
|
||||
@ -796,6 +836,8 @@ ContextBar::ContextBar()
|
||||
"component is used to setup the opacity level of all drawing tools.\n\n"
|
||||
"When unchecked -the default behavior- the color is picked\n"
|
||||
"from the composition of all sprite layers.", JI_LEFT | JI_TOP);
|
||||
|
||||
m_brushType->setupTooltips(tooltipManager);
|
||||
m_selectionMode->setupTooltips(tooltipManager);
|
||||
m_dropPixels->setupTooltips(tooltipManager);
|
||||
m_freehandAlgo->setupTooltips(tooltipManager);
|
||||
@ -805,7 +847,7 @@ ContextBar::ContextBar()
|
||||
App::instance()->CurrentToolChange.connect(&ContextBar::onCurrentToolChange, this);
|
||||
m_dropPixels->DropPixels.connect(&ContextBar::onDropPixels, this);
|
||||
|
||||
onCurrentToolChange();
|
||||
setActiveBrush(createBrushFromSettings());
|
||||
}
|
||||
|
||||
ContextBar::~ContextBar()
|
||||
@ -831,30 +873,24 @@ void ContextBar::onSetOpacity(int newOpacity)
|
||||
|
||||
void ContextBar::onBrushSizeChange()
|
||||
{
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
Tool* currentTool = settings->getCurrentTool();
|
||||
IToolSettings* toolSettings = settings->getToolSettings(currentTool);
|
||||
IBrushSettings* brushSettings = toolSettings->getBrush();
|
||||
|
||||
m_brushType->setBrushSettings(brushSettings);
|
||||
m_brushSize->setTextf("%d", brushSettings->getSize());
|
||||
if (m_activeBrush->type() != kImageBrushType)
|
||||
discardActiveBrush();
|
||||
}
|
||||
|
||||
void ContextBar::onBrushAngleChange()
|
||||
{
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
Tool* currentTool = settings->getCurrentTool();
|
||||
IToolSettings* toolSettings = settings->getToolSettings(currentTool);
|
||||
IBrushSettings* brushSettings = toolSettings->getBrush();
|
||||
|
||||
m_brushType->setBrushSettings(brushSettings);
|
||||
m_brushAngle->setTextf("%d", brushSettings->getAngle());
|
||||
if (m_activeBrush->type() != kImageBrushType)
|
||||
discardActiveBrush();
|
||||
}
|
||||
|
||||
void ContextBar::onCurrentToolChange()
|
||||
{
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
updateFromTool(settings->getCurrentTool());
|
||||
if (m_activeBrush->type() != kImageBrushType)
|
||||
setActiveBrush(ContextBar::createBrushFromSettings());
|
||||
else {
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
updateFromTool(settings->getCurrentTool());
|
||||
}
|
||||
}
|
||||
|
||||
void ContextBar::onDropPixels(ContextBarObserver::DropAction action)
|
||||
@ -875,9 +911,11 @@ void ContextBar::updateFromTool(tools::Tool* tool)
|
||||
m_toolSettings = toolSettings;
|
||||
m_toolSettings->addObserver(this);
|
||||
|
||||
m_brushType->setBrushSettings(brushSettings);
|
||||
m_brushType->updateBrush(tool);
|
||||
m_brushSize->setTextf("%d", brushSettings->getSize());
|
||||
m_brushAngle->setTextf("%d", brushSettings->getAngle());
|
||||
m_brushPatternField->setBrushPattern(
|
||||
App::instance()->preferences().brush.pattern());
|
||||
|
||||
m_tolerance->setTextf("%d", toolSettings->getTolerance());
|
||||
m_contiguous->setSelected(toolSettings->getContiguous());
|
||||
@ -898,6 +936,9 @@ void ContextBar::updateFromTool(tools::Tool* tool)
|
||||
tool->getInk(1)->isPaint() ||
|
||||
tool->getInk(1)->isEffect());
|
||||
|
||||
// True if we have an image as brush
|
||||
bool hasImageBrush = (activeBrush()->type() == kImageBrushType);
|
||||
|
||||
// True if the current tool is eyedropper.
|
||||
bool isEyedropper =
|
||||
(tool->getInk(0)->isEyedropper() ||
|
||||
@ -929,10 +970,11 @@ void ContextBar::updateFromTool(tools::Tool* tool)
|
||||
|
||||
// Show/Hide fields
|
||||
m_brushType->setVisible(hasOpacity);
|
||||
m_brushSize->setVisible(hasOpacity);
|
||||
m_brushAngle->setVisible(hasOpacity);
|
||||
m_brushSize->setVisible(hasOpacity && !hasImageBrush);
|
||||
m_brushAngle->setVisible(hasOpacity && !hasImageBrush);
|
||||
m_brushPatternField->setVisible(hasOpacity && hasImageBrush);
|
||||
m_opacityLabel->setVisible(hasOpacity);
|
||||
m_inkType->setVisible(hasInk);
|
||||
m_inkType->setVisible(hasInk && !hasImageBrush);
|
||||
m_inkOpacity->setVisible(hasOpacity);
|
||||
m_grabAlpha->setVisible(isEyedropper);
|
||||
m_autoSelectLayer->setVisible(isMove);
|
||||
@ -977,4 +1019,102 @@ void ContextBar::updateAutoSelectLayer(bool state)
|
||||
m_autoSelectLayer->setSelected(state);
|
||||
}
|
||||
|
||||
int ContextBar::addBrush(const doc::BrushRef& brush)
|
||||
{
|
||||
// Use an empty slot
|
||||
for (size_t i=0; i<m_brushes.size(); ++i) {
|
||||
if (!m_brushes[i].locked ||
|
||||
!m_brushes[i].brush) {
|
||||
m_brushes[i].brush = brush;
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
|
||||
m_brushes.push_back(BrushSlot(brush));
|
||||
return (int)m_brushes.size(); // Returns the slot
|
||||
}
|
||||
|
||||
void ContextBar::removeBrush(int slot)
|
||||
{
|
||||
--slot;
|
||||
if (slot >= 0 && slot < (int)m_brushes.size()) {
|
||||
m_brushes[slot].brush.reset();
|
||||
|
||||
// Erase empty trailing slots
|
||||
while (!m_brushes.empty() &&
|
||||
!m_brushes[m_brushes.size()-1].brush)
|
||||
m_brushes.erase(--m_brushes.end());
|
||||
}
|
||||
}
|
||||
|
||||
void ContextBar::removeAllBrushes()
|
||||
{
|
||||
while (!m_brushes.empty())
|
||||
m_brushes.erase(--m_brushes.end());
|
||||
}
|
||||
|
||||
void ContextBar::setActiveBrushBySlot(int slot)
|
||||
{
|
||||
--slot;
|
||||
if (slot >= 0 && slot < (int)m_brushes.size() &&
|
||||
m_brushes[slot].brush) {
|
||||
m_brushes[slot].locked = true;
|
||||
setActiveBrush(m_brushes[slot].brush);
|
||||
}
|
||||
}
|
||||
|
||||
Brushes ContextBar::getBrushes()
|
||||
{
|
||||
Brushes brushes;
|
||||
for (const auto& slot : m_brushes)
|
||||
brushes.push_back(slot.brush);
|
||||
return brushes;
|
||||
}
|
||||
|
||||
void ContextBar::setActiveBrush(const doc::BrushRef& brush)
|
||||
{
|
||||
m_activeBrush = brush;
|
||||
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
updateFromTool(settings->getCurrentTool());
|
||||
}
|
||||
|
||||
doc::BrushRef ContextBar::activeBrush(tools::Tool* tool) const
|
||||
{
|
||||
if (!tool ||
|
||||
(tool->getInk(0)->isPaint() &&
|
||||
m_activeBrush->type() == kImageBrushType)) {
|
||||
m_activeBrush->setPattern(App::instance()->preferences().brush.pattern());
|
||||
return m_activeBrush;
|
||||
}
|
||||
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
IToolSettings* toolSettings = settings->getToolSettings(tool);
|
||||
return ContextBar::createBrushFromSettings(toolSettings->getBrush());
|
||||
}
|
||||
|
||||
void ContextBar::discardActiveBrush()
|
||||
{
|
||||
setActiveBrush(ContextBar::createBrushFromSettings());
|
||||
}
|
||||
|
||||
// static
|
||||
doc::BrushRef ContextBar::createBrushFromSettings(IBrushSettings* brushSettings)
|
||||
{
|
||||
if (brushSettings == nullptr) {
|
||||
ISettings* settings = UIContext::instance()->settings();
|
||||
tools::Tool* tool = settings->getCurrentTool();
|
||||
IToolSettings* toolSettings = settings->getToolSettings(tool);
|
||||
brushSettings = toolSettings->getBrush();
|
||||
}
|
||||
|
||||
doc::BrushRef brush;
|
||||
brush.reset(
|
||||
new Brush(
|
||||
brushSettings->getType(),
|
||||
brushSettings->getSize(),
|
||||
brushSettings->getAngle()));
|
||||
return brush;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -12,10 +12,15 @@
|
||||
#include "app/settings/settings_observers.h"
|
||||
#include "app/ui/context_bar_observer.h"
|
||||
#include "base/observable.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/brushes.h"
|
||||
#include "ui/box.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
class Box;
|
||||
class Button;
|
||||
class Label;
|
||||
}
|
||||
|
||||
@ -25,6 +30,7 @@ namespace tools {
|
||||
|
||||
namespace app {
|
||||
|
||||
class IBrushSettings;
|
||||
class IToolSettings;
|
||||
|
||||
class ContextBar : public ui::Box,
|
||||
@ -39,6 +45,21 @@ namespace app {
|
||||
void updateSelectionMode(SelectionMode mode);
|
||||
void updateAutoSelectLayer(bool state);
|
||||
|
||||
void setActiveBrush(const doc::BrushRef& brush);
|
||||
doc::BrushRef activeBrush(tools::Tool* tool = nullptr) const;
|
||||
void discardActiveBrush();
|
||||
|
||||
// Adds a new brush and returns the slot number where the brush
|
||||
// is now available.
|
||||
int addBrush(const doc::BrushRef& brush);
|
||||
void removeBrush(int slot);
|
||||
void removeAllBrushes();
|
||||
void setActiveBrushBySlot(int slot);
|
||||
doc::Brushes getBrushes();
|
||||
|
||||
static doc::BrushRef createBrushFromSettings(
|
||||
IBrushSettings* brushSettings = nullptr);
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
|
||||
@ -52,6 +73,21 @@ namespace app {
|
||||
void onCurrentToolChange();
|
||||
void onDropPixels(ContextBarObserver::DropAction action);
|
||||
|
||||
struct BrushSlot {
|
||||
// True if the user locked the brush using the shortcut key to
|
||||
// access it.
|
||||
bool locked;
|
||||
|
||||
// Can be null if the user deletes the brush.
|
||||
doc::BrushRef brush;
|
||||
|
||||
BrushSlot(const doc::BrushRef& brush)
|
||||
: locked(false), brush(brush) {
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<BrushSlot> BrushSlots;
|
||||
|
||||
class BrushTypeField;
|
||||
class BrushAngleField;
|
||||
class BrushSizeField;
|
||||
@ -65,6 +101,7 @@ namespace app {
|
||||
class TransparentColorField;
|
||||
class RotAlgorithmField;
|
||||
class FreehandAlgorithmField;
|
||||
class BrushPatternField;
|
||||
class GrabAlphaField;
|
||||
class DropPixelsField;
|
||||
class AutoSelectLayerField;
|
||||
@ -83,6 +120,7 @@ namespace app {
|
||||
AutoSelectLayerField* m_autoSelectLayer;
|
||||
ui::Box* m_freehandBox;
|
||||
FreehandAlgorithmField* m_freehandAlgo;
|
||||
BrushPatternField* m_brushPatternField;
|
||||
ui::Box* m_sprayBox;
|
||||
SprayWidthField* m_sprayWidth;
|
||||
SpraySpeedField* m_spraySpeed;
|
||||
@ -91,6 +129,8 @@ namespace app {
|
||||
TransparentColorField* m_transparentColor;
|
||||
RotAlgorithmField* m_rotAlgo;
|
||||
DropPixelsField* m_dropPixels;
|
||||
doc::BrushRef m_activeBrush;
|
||||
BrushSlots m_brushes;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -74,6 +74,10 @@ public:
|
||||
set_current_palette(editor->sprite()->palette(editor->frame()), true);
|
||||
}
|
||||
|
||||
void onAfterLayerChanged(Editor* editor) override {
|
||||
App::instance()->getMainWindow()->getPreviewEditor()->updateUsingEditor(this);
|
||||
}
|
||||
|
||||
// EditorCustomizationDelegate implementation
|
||||
tools::Tool* getQuickTool(tools::Tool* currentTool) override {
|
||||
return KeyboardShortcuts::instance()
|
||||
|
@ -17,17 +17,27 @@
|
||||
#include "app/ini_file.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/intertwine.h"
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/tool.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/editor/tool_loop_impl.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/boundary.h"
|
||||
#include "base/memory.h"
|
||||
#include "doc/algo.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/site.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/base.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/widget.h"
|
||||
@ -110,8 +120,8 @@ void Editor::set_cursor_color(const app::Color& color)
|
||||
// Slots for App signals
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static gfx::Rect lastBrushBounds;
|
||||
static int brush_size_thick = 0;
|
||||
static Brush* current_brush = NULL;
|
||||
|
||||
static void on_palette_change_update_cursor_color()
|
||||
{
|
||||
@ -136,29 +146,9 @@ static void on_brush_after_change()
|
||||
}
|
||||
}
|
||||
|
||||
static Brush* editor_get_current_brush(Editor* editor)
|
||||
static Brush* get_current_brush()
|
||||
{
|
||||
// Create the current brush from settings
|
||||
tools::Tool* tool = editor->getCurrentEditorTool();
|
||||
IBrushSettings* brush_settings = UIContext::instance()
|
||||
->settings()
|
||||
->getToolSettings(tool)
|
||||
->getBrush();
|
||||
|
||||
ASSERT(brush_settings != NULL);
|
||||
|
||||
if (!current_brush ||
|
||||
current_brush->type() != brush_settings->getType() ||
|
||||
current_brush->size() != brush_settings->getSize() ||
|
||||
current_brush->angle() != brush_settings->getAngle()) {
|
||||
delete current_brush;
|
||||
current_brush = new Brush(
|
||||
brush_settings->getType(),
|
||||
brush_settings->getSize(),
|
||||
brush_settings->getAngle());
|
||||
}
|
||||
|
||||
return current_brush;
|
||||
return App::instance()->getMainWindow()->getContextBar()->activeBrush().get();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -183,9 +173,6 @@ void Editor::editor_cursor_exit()
|
||||
|
||||
if (cursor_bound.seg != NULL)
|
||||
base_free(cursor_bound.seg);
|
||||
|
||||
delete current_brush;
|
||||
current_brush = NULL;
|
||||
}
|
||||
|
||||
// Draws the brush cursor inside the specified editor.
|
||||
@ -244,13 +231,20 @@ void Editor::drawBrushPreview(const gfx::Point& pos, bool refresh)
|
||||
->settings()
|
||||
->getToolSettings(tool);
|
||||
|
||||
Brush* brush = editor_get_current_brush(this);
|
||||
Brush* brush = get_current_brush();
|
||||
gfx::Rect brushBounds = brush->bounds();
|
||||
brushBounds.offset(spritePos);
|
||||
|
||||
// Create the extra cel to show the brush preview
|
||||
Site site = getSite();
|
||||
Cel* cel = site.cel();
|
||||
|
||||
m_document->prepareExtraCel(
|
||||
gfx::Rect(brushBounds).offset(spritePos),
|
||||
tool_settings->getOpacity());
|
||||
brushBounds,
|
||||
cel ? cel->opacity(): 255);
|
||||
m_document->setExtraCelType(render::ExtraType::NONE);
|
||||
m_document->setExtraCelBlendMode(
|
||||
m_layer ? static_cast<LayerImage*>(m_layer)->getBlendMode(): BLEND_MODE_NORMAL);
|
||||
|
||||
// In 'indexed' images, if the current color is 0, we have to use
|
||||
// a different mask color (different from 0) to draw the extra layer
|
||||
@ -259,16 +253,36 @@ void Editor::drawBrushPreview(const gfx::Point& pos, bool refresh)
|
||||
|
||||
Image* extraImage = m_document->getExtraCelImage();
|
||||
extraImage->setMaskColor(mask_color);
|
||||
draw_brush(extraImage, brush, -brushBounds.x, -brushBounds.y,
|
||||
brush_color, extraImage->maskColor());
|
||||
clear_image(extraImage, mask_color);
|
||||
|
||||
if (m_layer) {
|
||||
render::Render().renderLayer(
|
||||
extraImage, m_layer, m_frame,
|
||||
gfx::Clip(0, 0, brushBounds),
|
||||
BLEND_MODE_COPY);
|
||||
|
||||
// This extra cel is a patch for the current layer/frame
|
||||
m_document->setExtraCelType(render::ExtraType::PATCH);
|
||||
}
|
||||
|
||||
tools::ToolLoop* loop = create_tool_loop_preview(
|
||||
this, UIContext::instance(), extraImage,
|
||||
-gfx::Point(brushBounds.x,
|
||||
brushBounds.y));
|
||||
|
||||
if (loop) {
|
||||
loop->getInk()->prepareInk(loop);
|
||||
loop->getIntertwine()->prepareIntertwine();
|
||||
loop->getController()->prepareController();
|
||||
loop->getPointShape()->preparePointShape(loop);
|
||||
loop->getPointShape()->transformPoint(
|
||||
loop, -brush->bounds().x, -brush->bounds().y);
|
||||
delete loop;
|
||||
}
|
||||
|
||||
if (refresh) {
|
||||
m_document->notifySpritePixelsModified
|
||||
(m_sprite,
|
||||
gfx::Region(gfx::Rect(
|
||||
spritePos.x+brushBounds.x,
|
||||
spritePos.y+brushBounds.y,
|
||||
brushBounds.w, brushBounds.h)));
|
||||
(m_sprite, gfx::Region(lastBrushBounds = brushBounds));
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,12 +327,14 @@ void Editor::moveBrushPreview(const gfx::Point& pos, bool refresh)
|
||||
}
|
||||
|
||||
if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) {
|
||||
Brush* brush = editor_get_current_brush(this);
|
||||
gfx::Rect brushBounds = brush->bounds();
|
||||
gfx::Rect rc1(oldEditorPos.x+brushBounds.x, oldEditorPos.y+brushBounds.y, brushBounds.w, brushBounds.h);
|
||||
gfx::Rect rc2(newEditorPos.x+brushBounds.x, newEditorPos.y+brushBounds.y, brushBounds.w, brushBounds.h);
|
||||
Brush* brush = get_current_brush();
|
||||
|
||||
gfx::Rect newBrushBounds = brush->bounds();
|
||||
newBrushBounds.offset(newEditorPos);
|
||||
|
||||
m_document->notifySpritePixelsModified
|
||||
(m_sprite, gfx::Region(rc1.createUnion(rc2)));
|
||||
(m_sprite, gfx::Region(lastBrushBounds.createUnion(newBrushBounds)));
|
||||
lastBrushBounds = newBrushBounds;
|
||||
}
|
||||
|
||||
// Save area and draw the cursor
|
||||
@ -360,20 +376,10 @@ void Editor::clearBrushPreview(bool refresh)
|
||||
|
||||
// Clean pixel/brush preview
|
||||
if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) {
|
||||
Brush* brush = editor_get_current_brush(this);
|
||||
gfx::Rect brushBounds = brush->bounds();
|
||||
|
||||
m_document->prepareExtraCel(
|
||||
gfx::Rect(brushBounds).offset(pos),
|
||||
0); // Opacity = 0
|
||||
|
||||
m_document->destroyExtraCel();
|
||||
if (refresh) {
|
||||
m_document->notifySpritePixelsModified
|
||||
(m_sprite,
|
||||
gfx::Region(gfx::Rect(
|
||||
pos.x+brushBounds.x,
|
||||
pos.y+brushBounds.y,
|
||||
brushBounds.w, brushBounds.h)));
|
||||
(m_sprite, gfx::Region(lastBrushBounds));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,17 +100,14 @@ class EditorPostRenderImpl : public EditorPostRender {
|
||||
public:
|
||||
EditorPostRenderImpl(Editor* editor, Graphics* g)
|
||||
: m_editor(editor)
|
||||
, m_g(g)
|
||||
{
|
||||
, m_g(g) {
|
||||
}
|
||||
|
||||
Editor* getEditor()
|
||||
{
|
||||
Editor* getEditor() {
|
||||
return m_editor;
|
||||
}
|
||||
|
||||
void drawLine(int x1, int y1, int x2, int y2, gfx::Color screenColor)
|
||||
{
|
||||
void drawLine(int x1, int y1, int x2, int y2, gfx::Color screenColor) override {
|
||||
gfx::Point a(x1, y1);
|
||||
gfx::Point b(x2, y2);
|
||||
a = m_editor->editorToScreen(a);
|
||||
@ -123,6 +120,17 @@ public:
|
||||
m_g->drawLine(screenColor, a, b);
|
||||
}
|
||||
|
||||
void drawRectXor(const gfx::Rect& rc) override {
|
||||
gfx::Rect rc2 = m_editor->editorToScreen(rc);
|
||||
gfx::Rect bounds = m_editor->getBounds();
|
||||
rc2.x -= bounds.x;
|
||||
rc2.y -= bounds.y;
|
||||
|
||||
m_g->setDrawMode(Graphics::DrawMode::Xor);
|
||||
m_g->drawRect(gfx::rgba(255, 255, 255), rc2);
|
||||
m_g->setDrawMode(Graphics::DrawMode::Solid);
|
||||
}
|
||||
|
||||
private:
|
||||
Editor* m_editor;
|
||||
Graphics* m_g;
|
||||
@ -421,11 +429,16 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
|
||||
}
|
||||
}
|
||||
|
||||
m_renderEngine.setExtraImage(
|
||||
m_document->getExtraCel(),
|
||||
m_document->getExtraCelImage(),
|
||||
m_document->getExtraCelBlendMode(),
|
||||
m_layer, m_frame);
|
||||
if (m_document->getExtraCelType() != render::ExtraType::NONE) {
|
||||
ASSERT(m_document->getExtraCel());
|
||||
|
||||
m_renderEngine.setExtraImage(
|
||||
m_document->getExtraCelType(),
|
||||
m_document->getExtraCel(),
|
||||
m_document->getExtraCelImage(),
|
||||
m_document->getExtraCelBlendMode(),
|
||||
m_layer, m_frame);
|
||||
}
|
||||
|
||||
m_renderEngine.renderSprite(rendered, m_sprite, m_frame,
|
||||
gfx::Clip(0, 0, rc), m_zoom);
|
||||
@ -758,6 +771,8 @@ void Editor::flashCurrentLayer()
|
||||
m_renderEngine.removePreviewImage();
|
||||
|
||||
m_document->prepareExtraCel(m_sprite->bounds(), 255);
|
||||
m_document->setExtraCelType(render::ExtraType::COMPOSITE);
|
||||
|
||||
Image* flash_image = m_document->getExtraCelImage();
|
||||
|
||||
clear_image(flash_image, flash_image->maskColor());
|
||||
@ -858,9 +873,13 @@ tools::Tool* Editor::getCurrentEditorTool()
|
||||
|
||||
tools::Ink* Editor::getCurrentEditorInk()
|
||||
{
|
||||
tools::Ink* ink = m_state->getStateInk();
|
||||
if (ink)
|
||||
return ink;
|
||||
|
||||
Context* context = UIContext::instance();
|
||||
tools::Tool* tool = getCurrentEditorTool();
|
||||
tools::Ink* ink = tool->getInk(m_secondaryButton ? 1: 0);
|
||||
ink = tool->getInk(m_secondaryButton ? 1: 0);
|
||||
|
||||
if (m_quicktool)
|
||||
return ink;
|
||||
|
@ -38,6 +38,7 @@ namespace app {
|
||||
virtual ~EditorPostRender() { }
|
||||
virtual Editor* getEditor() = 0;
|
||||
virtual void drawLine(int x1, int y1, int x2, int y2, gfx::Color screenColor) = 0;
|
||||
virtual void drawRectXor(const gfx::Rect& rc) = 0;
|
||||
};
|
||||
|
||||
// Used by editor's states to pre- and post-render customized
|
||||
|
@ -26,6 +26,7 @@ namespace app {
|
||||
class EditorDecorator;
|
||||
|
||||
namespace tools {
|
||||
class Ink;
|
||||
class Tool;
|
||||
}
|
||||
|
||||
@ -107,6 +108,9 @@ namespace app {
|
||||
// Returns true if this state accept the given quicktool.
|
||||
virtual bool acceptQuickTool(tools::Tool* tool) { return true; }
|
||||
|
||||
// Custom ink in this state.
|
||||
virtual tools::Ink* getStateInk() { return nullptr; }
|
||||
|
||||
private:
|
||||
DISABLE_COPYING(EditorState);
|
||||
};
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "doc/algorithm/rotsprite.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/site.h"
|
||||
#include "doc/sprite.h"
|
||||
@ -66,6 +67,9 @@ PixelsMovement::PixelsMovement(Context* context,
|
||||
|
||||
ContextWriter writer(m_reader, 500);
|
||||
m_document->prepareExtraCel(m_sprite->bounds(), opacity);
|
||||
m_document->setExtraCelType(render::ExtraType::COMPOSITE);
|
||||
m_document->setExtraCelBlendMode(
|
||||
static_cast<LayerImage*>(m_layer)->getBlendMode());
|
||||
|
||||
redrawExtraImage();
|
||||
|
||||
|
@ -11,10 +11,12 @@
|
||||
|
||||
#include "app/ui/editor/select_box_state.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/tools/tool_box.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/system.h"
|
||||
#include "ui/view.h"
|
||||
@ -23,11 +25,12 @@ namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
SelectBoxState::SelectBoxState(SelectBoxDelegate* delegate, const gfx::Rect& rc, PaintFlags paintFlags)
|
||||
SelectBoxState::SelectBoxState(SelectBoxDelegate* delegate, const gfx::Rect& rc, Flags flags)
|
||||
: m_delegate(delegate)
|
||||
, m_rulers(4)
|
||||
, m_movingRuler(-1)
|
||||
, m_paintFlags(paintFlags)
|
||||
, m_selectingBox(false)
|
||||
, m_flags(flags)
|
||||
{
|
||||
setBoxBounds(rc);
|
||||
}
|
||||
@ -66,29 +69,51 @@ bool SelectBoxState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
if (msg->left() || msg->right()) {
|
||||
m_movingRuler = -1;
|
||||
|
||||
for (int i=0; i<(int)m_rulers.size(); ++i) {
|
||||
if (touchRuler(editor, m_rulers[i], msg->position().x, msg->position().y)) {
|
||||
m_movingRuler = i;
|
||||
break;
|
||||
if (hasFlag(RULERS)) {
|
||||
for (int i=0; i<(int)m_rulers.size(); ++i) {
|
||||
if (touchRuler(editor, m_rulers[i], msg->position().x, msg->position().y)) {
|
||||
m_movingRuler = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFlag(QUICKBOX) && m_movingRuler == -1) {
|
||||
m_selectingBox = true;
|
||||
m_selectingButtons = msg->buttons();
|
||||
m_startingPos = editor->screenToEditor(msg->position());
|
||||
setBoxBounds(gfx::Rect(m_startingPos, gfx::Size(1, 1)));
|
||||
}
|
||||
|
||||
editor->captureMouse();
|
||||
return true;
|
||||
}
|
||||
|
||||
return StandbyState::onMouseDown(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
m_movingRuler = -1;
|
||||
|
||||
if (m_selectingBox) {
|
||||
m_selectingBox = false;
|
||||
|
||||
if (m_delegate) {
|
||||
if (m_selectingButtons == msg->buttons())
|
||||
m_delegate->onQuickboxEnd(getBoxBounds(), msg->buttons());
|
||||
else
|
||||
m_delegate->onQuickboxCancel();
|
||||
}
|
||||
}
|
||||
|
||||
return StandbyState::onMouseUp(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
if (m_movingRuler >= 0) {
|
||||
bool used = false;
|
||||
|
||||
if (hasFlag(RULERS) && m_movingRuler >= 0) {
|
||||
gfx::Point pt = editor->screenToEditor(msg->position());
|
||||
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
@ -101,48 +126,85 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
m_rulers[m_movingRuler].setPosition(pt.x);
|
||||
break;
|
||||
}
|
||||
used = true;
|
||||
}
|
||||
|
||||
if (hasFlag(QUICKBOX) && m_selectingBox) {
|
||||
gfx::Point p1 = m_startingPos;
|
||||
gfx::Point p2 = editor->screenToEditor(msg->position());
|
||||
|
||||
if (p2.x < p1.x) std::swap(p1.x, p2.x);
|
||||
if (p2.y < p1.y) std::swap(p1.y, p2.y);
|
||||
++p2.x;
|
||||
++p2.y;
|
||||
|
||||
setBoxBounds(gfx::Rect(p1, p2));
|
||||
used = true;
|
||||
}
|
||||
|
||||
if (used) {
|
||||
if (m_delegate)
|
||||
m_delegate->onChangeRectangle(getBoxBounds());
|
||||
|
||||
editor->invalidate();
|
||||
return true;
|
||||
}
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
else
|
||||
return StandbyState::onMouseMove(editor, msg);
|
||||
}
|
||||
|
||||
bool SelectBoxState::onSetCursor(Editor* editor)
|
||||
{
|
||||
if (m_movingRuler >= 0) {
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
case Ruler::Horizontal: ui::set_mouse_cursor(kSizeNSCursor); return true;
|
||||
case Ruler::Vertical: ui::set_mouse_cursor(kSizeWECursor); return true;
|
||||
if (hasFlag(RULERS)) {
|
||||
if (m_movingRuler >= 0) {
|
||||
switch (m_rulers[m_movingRuler].getOrientation()) {
|
||||
case Ruler::Horizontal: ui::set_mouse_cursor(kSizeNSCursor); return true;
|
||||
case Ruler::Vertical: ui::set_mouse_cursor(kSizeWECursor); return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int x = ui::get_mouse_position().x;
|
||||
int y = ui::get_mouse_position().y;
|
||||
int x = ui::get_mouse_position().x;
|
||||
int y = ui::get_mouse_position().y;
|
||||
|
||||
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
|
||||
if (touchRuler(editor, *it, x, y)) {
|
||||
switch (it->getOrientation()) {
|
||||
case Ruler::Horizontal:
|
||||
ui::set_mouse_cursor(kSizeNSCursor);
|
||||
return true;
|
||||
case Ruler::Vertical:
|
||||
ui::set_mouse_cursor(kSizeWECursor);
|
||||
return true;
|
||||
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
|
||||
if (touchRuler(editor, *it, x, y)) {
|
||||
switch (it->getOrientation()) {
|
||||
case Ruler::Horizontal:
|
||||
ui::set_mouse_cursor(kSizeNSCursor);
|
||||
return true;
|
||||
case Ruler::Vertical:
|
||||
ui::set_mouse_cursor(kSizeWECursor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return StandbyState::onSetCursor(editor);
|
||||
}
|
||||
|
||||
bool SelectBoxState::requireBrushPreview()
|
||||
{
|
||||
if (hasFlag(QUICKBOX))
|
||||
return true;
|
||||
|
||||
// Returns false as it overrides default standby state behavior &
|
||||
// look. This state uses normal arrow cursors.
|
||||
return false;
|
||||
}
|
||||
|
||||
tools::Ink* SelectBoxState::getStateInk()
|
||||
{
|
||||
if (hasFlag(QUICKBOX))
|
||||
return App::instance()->getToolBox()->getInkById(
|
||||
tools::WellKnownInks::Selection);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SelectBoxState::preRenderDecorator(EditorPreRender* render)
|
||||
{
|
||||
// Without black shadow?
|
||||
if (!hasPaintFlag(PaintDarkOutside))
|
||||
if (!hasFlag(DARKOUTSIDE))
|
||||
return;
|
||||
|
||||
gfx::Rect rc = getBoxBounds();
|
||||
@ -177,7 +239,7 @@ void SelectBoxState::postRenderDecorator(EditorPostRender* render)
|
||||
vp = editor->screenToEditor(vp);
|
||||
|
||||
// Paint a grid generated by the box
|
||||
if (hasPaintFlag(PaintGrid)) {
|
||||
if (hasFlag(GRID)) {
|
||||
gfx::Color gridColor = gfx::rgba(100, 200, 100);
|
||||
gfx::Rect boxBounds = getBoxBounds();
|
||||
|
||||
@ -191,7 +253,7 @@ void SelectBoxState::postRenderDecorator(EditorPostRender* render)
|
||||
}
|
||||
|
||||
// Draw the rulers enclosing the box
|
||||
if (hasPaintFlag(PaintRulers)) {
|
||||
if (hasFlag(RULERS)) {
|
||||
gfx::Color rulerColor = gfx::rgba(0, 0, 255);
|
||||
|
||||
for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) {
|
||||
@ -207,6 +269,10 @@ void SelectBoxState::postRenderDecorator(EditorPostRender* render)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFlag(QUICKBOX)) {
|
||||
render->drawRectXor(getBoxBounds());
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectBoxState::touchRuler(Editor* editor, Ruler& ruler, int x, int y)
|
||||
@ -222,9 +288,9 @@ bool SelectBoxState::touchRuler(Editor* editor, Ruler& ruler, int x, int y)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SelectBoxState::hasPaintFlag(PaintFlags flag) const
|
||||
bool SelectBoxState::hasFlag(Flags flag) const
|
||||
{
|
||||
return ((m_paintFlags & flag) == flag);
|
||||
return ((m_flags & flag) == flag);
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "app/ui/editor/editor_decorator.h"
|
||||
#include "app/ui/editor/ruler.h"
|
||||
#include "app/ui/editor/standby_state.h"
|
||||
#include "ui/mouse_buttons.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -20,7 +21,15 @@ namespace app {
|
||||
class SelectBoxDelegate {
|
||||
public:
|
||||
virtual ~SelectBoxDelegate() { }
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) = 0;
|
||||
|
||||
// Called each time the selected box is modified (e.g. rulers are
|
||||
// moved).
|
||||
virtual void onChangeRectangle(const gfx::Rect& rect) { }
|
||||
|
||||
// Called only in QUICKBOX mode, when the user released the mouse
|
||||
// button.
|
||||
virtual void onQuickboxEnd(const gfx::Rect& rect, ui::MouseButtons buttons) { }
|
||||
virtual void onQuickboxCancel() { }
|
||||
};
|
||||
|
||||
class SelectBoxState : public StandbyState
|
||||
@ -28,14 +37,15 @@ namespace app {
|
||||
enum { H1, H2, V1, V2 };
|
||||
|
||||
public:
|
||||
typedef int PaintFlags;
|
||||
static const int PaintRulers = 1;
|
||||
static const int PaintDarkOutside = 2;
|
||||
static const int PaintGrid = 4;
|
||||
typedef int Flags;
|
||||
static const int RULERS = 1; // Draw rulers at each edge of the current box
|
||||
static const int DARKOUTSIDE = 2; // The outside of the box must be darker
|
||||
static const int GRID = 4; // Draw a grid
|
||||
static const int QUICKBOX = 8; // Select the box as in selection tool, drawing a boxu
|
||||
|
||||
SelectBoxState(SelectBoxDelegate* delegate,
|
||||
const gfx::Rect& rc,
|
||||
PaintFlags paintFlags);
|
||||
Flags flags);
|
||||
|
||||
// Returns the bounding box arranged by the rulers.
|
||||
gfx::Rect getBoxBounds() const;
|
||||
@ -48,10 +58,8 @@ namespace app {
|
||||
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
|
||||
virtual bool onSetCursor(Editor* editor) override;
|
||||
|
||||
// Returns false as it overrides default standby state behavior &
|
||||
// look. This state uses normal arrow cursors.
|
||||
virtual bool requireBrushPreview() override { return false; }
|
||||
virtual bool requireBrushPreview() override;
|
||||
virtual tools::Ink* getStateInk() override;
|
||||
|
||||
// EditorDecorator overrides
|
||||
virtual void preRenderDecorator(EditorPreRender* render) override;
|
||||
@ -64,12 +72,15 @@ namespace app {
|
||||
// the given ruler.
|
||||
bool touchRuler(Editor* editor, Ruler& ruler, int x, int y);
|
||||
|
||||
bool hasPaintFlag(PaintFlags flag) const;
|
||||
bool hasFlag(Flags flag) const;
|
||||
|
||||
SelectBoxDelegate* m_delegate;
|
||||
Rulers m_rulers;
|
||||
int m_movingRuler;
|
||||
PaintFlags m_paintFlags;
|
||||
bool m_selectingBox;
|
||||
ui::MouseButtons m_selectingButtons;
|
||||
gfx::Point m_startingPos;
|
||||
Flags m_flags;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/shade_table.h"
|
||||
#include "app/tools/shading_options.h"
|
||||
#include "app/tools/tool.h"
|
||||
@ -31,11 +32,14 @@
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "app/ui/context_bar.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/util/expand_cel_canvas.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/sprite.h"
|
||||
@ -45,12 +49,15 @@ namespace app {
|
||||
|
||||
using namespace ui;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For ToolLoopController
|
||||
|
||||
class ToolLoopImpl : public tools::ToolLoop,
|
||||
public tools::ShadingOptions {
|
||||
Editor* m_editor;
|
||||
Context* m_context;
|
||||
tools::Tool* m_tool;
|
||||
Brush* m_brush;
|
||||
BrushRef m_brush;
|
||||
Document* m_document;
|
||||
Sprite* m_sprite;
|
||||
Layer* m_layer;
|
||||
@ -140,18 +147,9 @@ public:
|
||||
}
|
||||
|
||||
m_previewFilled = m_toolSettings->getPreviewFilled();
|
||||
|
||||
m_sprayWidth = m_toolSettings->getSprayWidth();
|
||||
m_spraySpeed = m_toolSettings->getSpraySpeed();
|
||||
|
||||
// Create the brush
|
||||
IBrushSettings* brush_settings = m_toolSettings->getBrush();
|
||||
ASSERT(brush_settings != NULL);
|
||||
|
||||
m_brush = new Brush(
|
||||
brush_settings->getType(),
|
||||
brush_settings->getSize(),
|
||||
brush_settings->getAngle());
|
||||
m_brush = App::instance()->getMainWindow()->getContextBar()->activeBrush();
|
||||
|
||||
if (m_ink->isSelection())
|
||||
m_useMask = false;
|
||||
@ -224,7 +222,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
delete m_brush;
|
||||
delete m_shadeTable;
|
||||
|
||||
if (redraw)
|
||||
@ -232,7 +229,7 @@ public:
|
||||
}
|
||||
|
||||
tools::Tool* getTool() override { return m_tool; }
|
||||
Brush* getBrush() override { return m_brush; }
|
||||
Brush* getBrush() override { return m_brush.get(); }
|
||||
Document* getDocument() override { return m_document; }
|
||||
Sprite* sprite() override { return m_sprite; }
|
||||
Layer* getLayer() override { return m_layer; }
|
||||
@ -391,4 +388,209 @@ tools::ToolLoop* create_tool_loop(Editor* editor, Context* context)
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// For preview
|
||||
|
||||
class PreviewToolLoopImpl : public tools::ToolLoop,
|
||||
public tools::ShadingOptions {
|
||||
Editor* m_editor;
|
||||
Context* m_context;
|
||||
tools::Tool* m_tool;
|
||||
BrushRef m_brush;
|
||||
Document* m_document;
|
||||
Sprite* m_sprite;
|
||||
Layer* m_layer;
|
||||
frame_t m_frame;
|
||||
ISettings* m_settings;
|
||||
DocumentPreferences& m_docPref;
|
||||
IToolSettings* m_toolSettings;
|
||||
int m_opacity;
|
||||
int m_tolerance;
|
||||
bool m_contiguous;
|
||||
gfx::Point m_offset;
|
||||
gfx::Point m_speed;
|
||||
bool m_canceled;
|
||||
tools::ToolLoop::Button m_button;
|
||||
tools::Ink* m_ink;
|
||||
int m_primary_color;
|
||||
int m_secondary_color;
|
||||
gfx::Region m_dirtyArea;
|
||||
tools::ShadeTable8* m_shadeTable;
|
||||
Image* m_image;
|
||||
tools::PointShape* m_pointShape;
|
||||
|
||||
public:
|
||||
PreviewToolLoopImpl(
|
||||
Editor* editor,
|
||||
Context* context,
|
||||
tools::Tool* tool,
|
||||
tools::Ink* ink,
|
||||
Document* document,
|
||||
tools::ToolLoop::Button button,
|
||||
const app::Color& primary_color,
|
||||
const app::Color& secondary_color,
|
||||
Image* image,
|
||||
const gfx::Point& offset)
|
||||
: m_editor(editor)
|
||||
, m_context(context)
|
||||
, m_tool(tool)
|
||||
, m_document(document)
|
||||
, m_sprite(editor->sprite())
|
||||
, m_layer(editor->layer())
|
||||
, m_frame(editor->frame())
|
||||
, m_settings(m_context->settings())
|
||||
, m_docPref(App::instance()->preferences().document(m_document))
|
||||
, m_toolSettings(m_settings->getToolSettings(m_tool))
|
||||
, m_offset(offset)
|
||||
, m_canceled(false)
|
||||
, m_button(button)
|
||||
, m_ink(ink)
|
||||
, m_primary_color(color_utils::color_for_layer(primary_color, m_layer))
|
||||
, m_secondary_color(color_utils::color_for_layer(secondary_color, m_layer))
|
||||
, m_shadeTable(NULL)
|
||||
, m_image(image)
|
||||
{
|
||||
m_brush = App::instance()->getMainWindow()->getContextBar()->activeBrush();
|
||||
m_opacity = m_toolSettings->getOpacity();
|
||||
m_tolerance = m_toolSettings->getTolerance();
|
||||
m_contiguous = m_toolSettings->getContiguous();
|
||||
m_speed.x = 0;
|
||||
m_speed.y = 0;
|
||||
|
||||
// Avoid preview for spray and flood fill like tools
|
||||
m_pointShape = m_tool->getPointShape(m_button);
|
||||
if (m_pointShape->isSpray()) {
|
||||
m_pointShape = App::instance()->getToolBox()->getPointShapeById(
|
||||
tools::WellKnownPointShapes::Brush);
|
||||
}
|
||||
else if (m_pointShape->isFloodFill()) {
|
||||
m_pointShape = App::instance()->getToolBox()->getPointShapeById(
|
||||
tools::WellKnownPointShapes::Pixel);
|
||||
}
|
||||
}
|
||||
|
||||
// IToolLoop interface
|
||||
void dispose() override { }
|
||||
tools::Tool* getTool() override { return m_tool; }
|
||||
Brush* getBrush() override { return m_brush.get(); }
|
||||
Document* getDocument() override { return m_document; }
|
||||
Sprite* sprite() override { return m_sprite; }
|
||||
Layer* getLayer() override { return m_layer; }
|
||||
frame_t getFrame() override { return m_frame; }
|
||||
const Image* getSrcImage() override { return m_image; }
|
||||
Image* getDstImage() override { return m_image; }
|
||||
void validateSrcImage(const gfx::Region& rgn) override { }
|
||||
void validateDstImage(const gfx::Region& rgn) override { }
|
||||
void invalidateDstImage() override { }
|
||||
void invalidateDstImage(const gfx::Region& rgn) override { }
|
||||
void copyValidDstToSrcImage(const gfx::Region& rgn) override { }
|
||||
|
||||
RgbMap* getRgbMap() override { return m_sprite->rgbMap(m_frame); }
|
||||
bool useMask() override { return false; // m_useMask;
|
||||
}
|
||||
Mask* getMask() override { return nullptr; // m_mask;
|
||||
}
|
||||
void setMask(Mask* newMask) override { }
|
||||
gfx::Point getMaskOrigin() override { return gfx::Point(0, 0); // m_maskOrigin;
|
||||
}
|
||||
const render::Zoom& zoom() override { return m_editor->zoom(); }
|
||||
ToolLoop::Button getMouseButton() override { return m_button; }
|
||||
int getPrimaryColor() override { return m_primary_color; }
|
||||
void setPrimaryColor(int color) override { m_primary_color = color; }
|
||||
int getSecondaryColor() override { return m_secondary_color; }
|
||||
void setSecondaryColor(int color) override { m_secondary_color = color; }
|
||||
int getOpacity() override { return m_opacity; }
|
||||
int getTolerance() override { return m_tolerance; }
|
||||
bool getContiguous() override { return m_contiguous; }
|
||||
SelectionMode getSelectionMode() override { return m_editor->getSelectionMode(); }
|
||||
ISettings* settings() override { return m_settings; }
|
||||
filters::TiledMode getTiledMode() override { return m_docPref.tiled.mode(); }
|
||||
bool getGridVisible() override { return m_docPref.grid.visible(); }
|
||||
bool getSnapToGrid() override { return m_docPref.grid.snap(); }
|
||||
gfx::Rect getGridBounds() override { return m_docPref.grid.bounds(); }
|
||||
bool getFilled() override { return false; }
|
||||
bool getPreviewFilled() override { return false; }
|
||||
int getSprayWidth() override { return 0; }
|
||||
int getSpraySpeed() override { return 0; }
|
||||
gfx::Point getOffset() override { return m_offset; }
|
||||
void setSpeed(const gfx::Point& speed) override { m_speed = speed; }
|
||||
gfx::Point getSpeed() override { return m_speed; }
|
||||
tools::Ink* getInk() override { return m_ink; }
|
||||
tools::Controller* getController() override { return m_tool->getController(m_button); }
|
||||
tools::PointShape* getPointShape() override { return m_pointShape; }
|
||||
tools::Intertwine* getIntertwine() override { return m_tool->getIntertwine(m_button); }
|
||||
tools::TracePolicy getTracePolicy() override { return m_tool->getTracePolicy(m_button); }
|
||||
tools::ShadingOptions* getShadingOptions() override { return this; }
|
||||
|
||||
void cancel() override { }
|
||||
bool isCanceled() override { return true; }
|
||||
|
||||
gfx::Point screenToSprite(const gfx::Point& screenPoint) override {
|
||||
return m_editor->screenToEditor(screenPoint);
|
||||
}
|
||||
|
||||
gfx::Region& getDirtyArea() override {
|
||||
return m_dirtyArea;
|
||||
}
|
||||
|
||||
void updateDirtyArea() override {
|
||||
}
|
||||
|
||||
void updateStatusBar(const char* text) override {
|
||||
StatusBar::instance()->setStatusText(0, text);
|
||||
}
|
||||
|
||||
// ShadingOptions implementation
|
||||
tools::ShadeTable8* getShadeTable() override {
|
||||
if (m_shadeTable == NULL) {
|
||||
app::ColorSwatches* colorSwatches = m_settings->getColorSwatches();
|
||||
ASSERT(colorSwatches != NULL);
|
||||
m_shadeTable = new tools::ShadeTable8(*colorSwatches,
|
||||
tools::kRotateShadingMode);
|
||||
}
|
||||
return m_shadeTable;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
tools::ToolLoop* create_tool_loop_preview(
|
||||
Editor* editor, Context* context, Image* image,
|
||||
const gfx::Point& offset)
|
||||
{
|
||||
tools::Tool* current_tool = editor->getCurrentEditorTool();
|
||||
tools::Ink* current_ink = editor->getCurrentEditorInk();
|
||||
if (!current_tool || !current_ink)
|
||||
return NULL;
|
||||
|
||||
Layer* layer = editor->layer();
|
||||
if (!layer ||
|
||||
!layer->isVisible() ||
|
||||
!layer->isEditable()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get fg/bg colors
|
||||
ColorBar* colorbar = ColorBar::instance();
|
||||
app::Color fg = colorbar->getFgColor();
|
||||
app::Color bg = colorbar->getBgColor();
|
||||
if (!fg.isValid() || !bg.isValid())
|
||||
return nullptr;
|
||||
|
||||
// Create the new tool loop
|
||||
try {
|
||||
return new PreviewToolLoopImpl(
|
||||
editor, context,
|
||||
current_tool,
|
||||
current_ink,
|
||||
editor->document(),
|
||||
tools::ToolLoop::Left,
|
||||
fg, bg, image, offset);
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace app
|
||||
|
@ -9,6 +9,13 @@
|
||||
#define APP_UI_EDITOR_TOOL_LOOP_IMPL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/image_ref.h"
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class Context;
|
||||
class Editor;
|
||||
@ -17,7 +24,12 @@ namespace app {
|
||||
class ToolLoop;
|
||||
}
|
||||
|
||||
tools::ToolLoop* create_tool_loop(Editor* editor, Context* context);
|
||||
tools::ToolLoop* create_tool_loop(
|
||||
Editor* editor, Context* context);
|
||||
|
||||
tools::ToolLoop* create_tool_loop_preview(
|
||||
Editor* editor, Context* context, doc::Image* image,
|
||||
const gfx::Point& offset);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
@ -129,13 +129,6 @@ namespace app {
|
||||
PART_TARGET_FRAMES_LAYERS,
|
||||
PART_TARGET_FRAMES_LAYERS_SELECTED,
|
||||
|
||||
PART_BRUSH_CIRCLE,
|
||||
PART_BRUSH_CIRCLE_SELECTED,
|
||||
PART_BRUSH_SQUARE,
|
||||
PART_BRUSH_SQUARE_SELECTED,
|
||||
PART_BRUSH_LINE,
|
||||
PART_BRUSH_LINE_SELECTED,
|
||||
|
||||
PART_SCALE_ARROW_1,
|
||||
PART_SCALE_ARROW_2,
|
||||
PART_SCALE_ARROW_3,
|
||||
|
@ -238,12 +238,6 @@ SkinTheme::SkinTheme()
|
||||
sheet_mapping["target_layers_selected"] = PART_TARGET_LAYERS_SELECTED;
|
||||
sheet_mapping["target_frames_layers"] = PART_TARGET_FRAMES_LAYERS;
|
||||
sheet_mapping["target_frames_layers_selected"] = PART_TARGET_FRAMES_LAYERS_SELECTED;
|
||||
sheet_mapping["brush_circle"] = PART_BRUSH_CIRCLE;
|
||||
sheet_mapping["brush_circle_selected"] = PART_BRUSH_CIRCLE_SELECTED;
|
||||
sheet_mapping["brush_square"] = PART_BRUSH_SQUARE;
|
||||
sheet_mapping["brush_square_selected"] = PART_BRUSH_SQUARE_SELECTED;
|
||||
sheet_mapping["brush_line"] = PART_BRUSH_LINE;
|
||||
sheet_mapping["brush_line_selected"] = PART_BRUSH_LINE_SELECTED;
|
||||
sheet_mapping["scale_arrow_1"] = PART_SCALE_ARROW_1;
|
||||
sheet_mapping["scale_arrow_2"] = PART_SCALE_ARROW_2;
|
||||
sheet_mapping["scale_arrow_3"] = PART_SCALE_ARROW_3;
|
||||
|
@ -24,8 +24,13 @@ using namespace doc;
|
||||
|
||||
Image* new_image_from_mask(const Site& site)
|
||||
{
|
||||
const Sprite* srcSprite = site.sprite();
|
||||
const Mask* srcMask = static_cast<const app::Document*>(site.document())->mask();
|
||||
return new_image_from_mask(site, srcMask);
|
||||
}
|
||||
|
||||
doc::Image* new_image_from_mask(const doc::Site& site, const doc::Mask* srcMask)
|
||||
{
|
||||
const Sprite* srcSprite = site.sprite();
|
||||
const Image* srcMaskBitmap = srcMask->bitmap();
|
||||
const gfx::Rect& srcBounds = srcMask->bounds();
|
||||
int x, y, u, v, getx, gety;
|
||||
@ -38,7 +43,7 @@ Image* new_image_from_mask(const Site& site)
|
||||
|
||||
dst = Image::create(srcSprite->pixelFormat(), srcBounds.w, srcBounds.h);
|
||||
if (!dst)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
// Clear the new image
|
||||
dst->setMaskColor(src ? src->maskColor(): srcSprite->transparentColor());
|
||||
|
@ -11,12 +11,14 @@
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Mask;
|
||||
class Site;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
doc::Image* new_image_from_mask(const doc::Site& site);
|
||||
doc::Image* new_image_from_mask(const doc::Site& site, const doc::Mask* mask);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
@ -18,6 +18,7 @@ add_library(doc-lib
|
||||
cel_io.cpp
|
||||
cels_range.cpp
|
||||
color_scales.cpp
|
||||
compressed_image.cpp
|
||||
context.cpp
|
||||
conversion_she.cpp
|
||||
document.cpp
|
||||
|
@ -24,7 +24,7 @@ Brush::Brush()
|
||||
m_type = kCircleBrushType;
|
||||
m_size = 1;
|
||||
m_angle = 0;
|
||||
m_image = NULL;
|
||||
m_pattern = BrushPattern::DEFAULT;
|
||||
|
||||
regenerate();
|
||||
}
|
||||
@ -34,7 +34,7 @@ Brush::Brush(BrushType type, int size, int angle)
|
||||
m_type = type;
|
||||
m_size = size;
|
||||
m_angle = angle;
|
||||
m_image = NULL;
|
||||
m_pattern = BrushPattern::DEFAULT;
|
||||
|
||||
regenerate();
|
||||
}
|
||||
@ -44,6 +44,9 @@ Brush::Brush(const Brush& brush)
|
||||
m_type = brush.m_type;
|
||||
m_size = brush.m_size;
|
||||
m_angle = brush.m_angle;
|
||||
m_image = brush.m_image;
|
||||
m_pattern = brush.m_pattern;
|
||||
m_patternOrigin = brush.m_patternOrigin;
|
||||
|
||||
regenerate();
|
||||
}
|
||||
@ -56,7 +59,10 @@ Brush::~Brush()
|
||||
void Brush::setType(BrushType type)
|
||||
{
|
||||
m_type = type;
|
||||
regenerate();
|
||||
if (m_type != kImageBrushType)
|
||||
regenerate();
|
||||
else
|
||||
clean();
|
||||
}
|
||||
|
||||
void Brush::setSize(int size)
|
||||
@ -71,15 +77,19 @@ void Brush::setAngle(int angle)
|
||||
regenerate();
|
||||
}
|
||||
|
||||
void Brush::setImage(const Image* image)
|
||||
{
|
||||
m_type = kImageBrushType;
|
||||
m_image.reset(Image::createCopy(image));
|
||||
m_bounds = gfx::Rect(
|
||||
-m_image.get()->width()/2, -m_image.get()->height()/2,
|
||||
m_image.get()->width(), m_image.get()->height());
|
||||
}
|
||||
|
||||
// Cleans the brush's data (image and region).
|
||||
void Brush::clean()
|
||||
{
|
||||
if (m_image) {
|
||||
delete m_image;
|
||||
m_image = NULL;
|
||||
}
|
||||
|
||||
m_scanline.clear();
|
||||
m_image.reset();
|
||||
}
|
||||
|
||||
static void algo_hline(int x1, int y, int x2, void *data)
|
||||
@ -98,23 +108,23 @@ void Brush::regenerate()
|
||||
if (m_type == kSquareBrushType && m_angle != 0 && m_size > 2)
|
||||
size = (int)std::sqrt((double)2*m_size*m_size)+2;
|
||||
|
||||
m_image = Image::create(IMAGE_BITMAP, size, size);
|
||||
m_image.reset(Image::create(IMAGE_BITMAP, size, size));
|
||||
|
||||
if (size == 1) {
|
||||
clear_image(m_image, BitmapTraits::max_value);
|
||||
clear_image(m_image.get(), BitmapTraits::max_value);
|
||||
}
|
||||
else {
|
||||
clear_image(m_image, BitmapTraits::min_value);
|
||||
clear_image(m_image.get(), BitmapTraits::min_value);
|
||||
|
||||
switch (m_type) {
|
||||
|
||||
case kCircleBrushType:
|
||||
fill_ellipse(m_image, 0, 0, size-1, size-1, BitmapTraits::max_value);
|
||||
fill_ellipse(m_image.get(), 0, 0, size-1, size-1, BitmapTraits::max_value);
|
||||
break;
|
||||
|
||||
case kSquareBrushType:
|
||||
if (m_angle == 0 || size <= 2) {
|
||||
clear_image(m_image, BitmapTraits::max_value);
|
||||
clear_image(m_image.get(), BitmapTraits::max_value);
|
||||
}
|
||||
else {
|
||||
double a = PI * m_angle / 180;
|
||||
@ -131,7 +141,7 @@ void Brush::regenerate()
|
||||
int y4 = int(y3 - d*sin(a+PI));
|
||||
int points[8] = { x1, y1, x2, y2, x3, y3, x4, y4 };
|
||||
|
||||
doc::algorithm::polygon(4, points, m_image, algo_hline);
|
||||
doc::algorithm::polygon(4, points, m_image.get(), algo_hline);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -144,26 +154,7 @@ void Brush::regenerate()
|
||||
int x2 = int(x1 + d*cos(a));
|
||||
int y2 = int(y1 - d*sin(a));
|
||||
|
||||
draw_line(m_image, x1, y1, x2, y2, BitmapTraits::max_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_scanline.resize(m_image->height());
|
||||
for (int y=0; y<m_image->height(); y++) {
|
||||
m_scanline[y].state = false;
|
||||
|
||||
for (int x=0; x<m_image->width(); x++) {
|
||||
if (get_pixel(m_image, x, y)) {
|
||||
m_scanline[y].x1 = x;
|
||||
|
||||
for (; x<m_image->width(); x++)
|
||||
if (!get_pixel(m_image, x, y))
|
||||
break;
|
||||
|
||||
m_scanline[y].x2 = x-1;
|
||||
m_scanline[y].state = true;
|
||||
draw_line(m_image.get(), x1, y1, x2, y2, BitmapTraits::max_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -8,19 +8,16 @@
|
||||
#define DOC_BRUSH_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/brush_pattern.h"
|
||||
#include "doc/brush_type.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "doc/brush_type.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Image;
|
||||
|
||||
struct BrushScanline {
|
||||
int state, x1, x2;
|
||||
};
|
||||
|
||||
class Brush {
|
||||
public:
|
||||
static const int kMinBrushSize = 1;
|
||||
@ -34,27 +31,39 @@ namespace doc {
|
||||
BrushType type() const { return m_type; }
|
||||
int size() const { return m_size; }
|
||||
int angle() const { return m_angle; }
|
||||
Image* image() { return m_image; }
|
||||
const std::vector<BrushScanline>& scanline() const { return m_scanline; }
|
||||
Image* image() const { return m_image.get(); }
|
||||
|
||||
BrushPattern pattern() const { return m_pattern; }
|
||||
gfx::Point patternOrigin() const { return m_patternOrigin; }
|
||||
|
||||
const gfx::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
void setType(BrushType type);
|
||||
void setSize(int size);
|
||||
void setAngle(int angle);
|
||||
void setImage(const Image* image);
|
||||
void setPattern(BrushPattern pattern) {
|
||||
m_pattern = pattern;
|
||||
}
|
||||
void setPatternOrigin(const gfx::Point& patternOrigin) {
|
||||
m_patternOrigin = patternOrigin;
|
||||
}
|
||||
|
||||
private:
|
||||
void clean();
|
||||
void regenerate();
|
||||
|
||||
BrushType m_type; // Type of brush
|
||||
BrushType m_type; // Type of brush
|
||||
int m_size; // Size (diameter)
|
||||
int m_angle; // Angle in degrees 0-360
|
||||
Image* m_image; // Image of the brush
|
||||
std::vector<BrushScanline> m_scanline;
|
||||
ImageRef m_image; // Image of the brush
|
||||
gfx::Rect m_bounds;
|
||||
BrushPattern m_pattern; // How the image should be replicated
|
||||
gfx::Point m_patternOrigin; // From what position the brush was taken
|
||||
};
|
||||
|
||||
typedef base::SharedPtr<Brush> BrushRef;
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
22
src/doc/brush_pattern.h
Normal file
22
src/doc/brush_pattern.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_BRUSH_PATTERN_H_INCLUDED
|
||||
#define DOC_BRUSH_PATTERN_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace doc {
|
||||
|
||||
enum class BrushPattern {
|
||||
DEFAULT = 0,
|
||||
ALIGNED_TO_SRC = 0,
|
||||
ALIGNED_TO_DST = 1,
|
||||
PAINT_BRUSH = 2,
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -14,9 +14,10 @@ namespace doc {
|
||||
kCircleBrushType = 0,
|
||||
kSquareBrushType = 1,
|
||||
kLineBrushType = 2,
|
||||
kImageBrushType = 3,
|
||||
|
||||
kFirstBrushType = kCircleBrushType,
|
||||
kLastBrushType = kLineBrushType,
|
||||
kLastBrushType = kImageBrushType,
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
21
src/doc/brushes.h
Normal file
21
src/doc/brushes.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_BRUSHES_H_INCLUDED
|
||||
#define DOC_BRUSHES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/brush.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
typedef std::vector<BrushRef> Brushes;
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
48
src/doc/compressed_image.cpp
Normal file
48
src/doc/compressed_image.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/compressed_image.h"
|
||||
|
||||
#include "doc/primitives.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
CompressedImage::CompressedImage(const Image* image, bool diffColors)
|
||||
: m_image(image)
|
||||
{
|
||||
color_t c1, c2, mask = image->maskColor();
|
||||
|
||||
for (int y=0; y<image->height(); ++y) {
|
||||
Scanline scanline(y);
|
||||
|
||||
for (int x=0; x<image->width(); ) {
|
||||
c1 = get_pixel(image, x, y);
|
||||
if (c1 != mask) {
|
||||
scanline.color = c1;
|
||||
scanline.x = x;
|
||||
|
||||
for (++x; x<image->width(); ++x) {
|
||||
c2 = get_pixel(image, x, y);
|
||||
|
||||
if ((diffColors && c1 != c2) ||
|
||||
(!diffColors && c2 == mask))
|
||||
break;
|
||||
}
|
||||
|
||||
scanline.w = x - scanline.x;
|
||||
m_scanlines.push_back(scanline);
|
||||
}
|
||||
else
|
||||
++x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
49
src/doc/compressed_image.h
Normal file
49
src/doc/compressed_image.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_COMPRESSED_IMAGE_H_INCLUDED
|
||||
#define DOC_COMPRESSED_IMAGE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/color.h"
|
||||
#include "doc/image.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class CompressedImage {
|
||||
public:
|
||||
struct Scanline {
|
||||
int x, y, w;
|
||||
color_t color;
|
||||
Scanline(int y) : x(0), y(y), w(0), color(0) { }
|
||||
};
|
||||
|
||||
typedef std::vector<Scanline> Scanlines;
|
||||
typedef Scanlines::const_iterator const_iterator;
|
||||
|
||||
// If diffColors is true, it generates one Scanline instance for
|
||||
// each different color. If it's false, it generates a scanline
|
||||
// for each row of consecutive pixels different than the mask
|
||||
// color.
|
||||
CompressedImage(const Image* image, bool diffColors);
|
||||
|
||||
const_iterator begin() const { return m_scanlines.begin(); }
|
||||
const_iterator end() const { return m_scanlines.end(); }
|
||||
|
||||
PixelFormat pixelFormat() const { return m_image->pixelFormat(); }
|
||||
int width() const { return m_image->width(); }
|
||||
int height() const { return m_image->height(); }
|
||||
|
||||
private:
|
||||
const Image* m_image;
|
||||
Scanlines m_scanlines;
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -40,33 +40,6 @@ void put_pixel(Image* image, int x, int y, color_t color)
|
||||
image->putPixel(x, y, color);
|
||||
}
|
||||
|
||||
void draw_brush(Image* image, Brush* brush, int x, int y, color_t fg, color_t bg)
|
||||
{
|
||||
ASSERT(image);
|
||||
ASSERT(brush);
|
||||
|
||||
Image* brush_image = brush->image();
|
||||
const gfx::Rect& brushBounds = brush->bounds();
|
||||
|
||||
x += brushBounds.x;
|
||||
y += brushBounds.y;
|
||||
|
||||
if (fg == bg) {
|
||||
fill_rect(image, x, y, x+brushBounds.w-1, y+brushBounds.h-1, bg);
|
||||
}
|
||||
else {
|
||||
int u, v;
|
||||
for (v=0; v<brushBounds.h; v++) {
|
||||
for (u=0; u<brushBounds.w; u++) {
|
||||
if (get_pixel(brush_image, u, v))
|
||||
put_pixel(image, x+u, y+v, fg);
|
||||
else
|
||||
put_pixel(image, x+u, y+v, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear_image(Image* image, color_t color)
|
||||
{
|
||||
ASSERT(image);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -19,7 +19,6 @@ namespace doc {
|
||||
|
||||
color_t get_pixel(const Image* image, int x, int y);
|
||||
void put_pixel(Image* image, int x, int y, color_t c);
|
||||
void draw_brush(Image* image, Brush* brush, int x, int y, color_t fg, color_t bg);
|
||||
|
||||
void clear_image(Image* image, color_t bg);
|
||||
|
||||
|
@ -186,6 +186,7 @@ void gen_pref_header(TiXmlDocument* doc, const std::string& inputFn)
|
||||
<< "#include \"app/color.h\"\n"
|
||||
<< "#include \"app/pref/option.h\"\n"
|
||||
<< "#include \"doc/anidir.h\"\n"
|
||||
<< "#include \"doc/brush_pattern.h\"\n"
|
||||
<< "#include \"doc/frame.h\"\n"
|
||||
<< "#include \"gfx/rect.h\"\n"
|
||||
<< "#include \"filters/tiled_mode.h\"\n"
|
||||
|
27
src/render/extra_type.h
Normal file
27
src/render/extra_type.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef RENDER_EXTRA_TYPE_H_INCLUDED
|
||||
#define RENDER_EXTRA_TYPE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace render {
|
||||
|
||||
enum class ExtraType {
|
||||
NONE,
|
||||
|
||||
// The extra cel indicates a "patch" for the current layer/frame
|
||||
// given in Render::setExtraImage()
|
||||
PATCH,
|
||||
|
||||
// The extra cel indicates an extra composition for the current
|
||||
// layer/frame.
|
||||
COMPOSITE,
|
||||
};
|
||||
|
||||
} // namespace render
|
||||
|
||||
#endif
|
@ -11,6 +11,8 @@
|
||||
#include "render/render.h"
|
||||
|
||||
#include "doc/doc.h"
|
||||
#include "gfx/clip.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
@ -320,6 +322,7 @@ Render::Render()
|
||||
: m_sprite(NULL)
|
||||
, m_currentLayer(NULL)
|
||||
, m_currentFrame(0)
|
||||
, m_extraType(ExtraType::NONE)
|
||||
, m_extraCel(NULL)
|
||||
, m_extraImage(NULL)
|
||||
, m_bgType(BgType::TRANSPARENT)
|
||||
@ -362,10 +365,12 @@ void Render::setPreviewImage(const Layer* layer, frame_t frame, Image* image)
|
||||
}
|
||||
|
||||
void Render::setExtraImage(
|
||||
ExtraType type,
|
||||
const Cel* cel, const Image* image, int blendMode,
|
||||
const Layer* currentLayer,
|
||||
frame_t currentFrame)
|
||||
{
|
||||
m_extraType = type;
|
||||
m_extraCel = cel;
|
||||
m_extraImage = image;
|
||||
m_extraBlendMode = blendMode;
|
||||
@ -380,6 +385,7 @@ void Render::removePreviewImage()
|
||||
|
||||
void Render::removeExtraImage()
|
||||
{
|
||||
m_extraType = ExtraType::NONE;
|
||||
m_extraCel = NULL;
|
||||
}
|
||||
|
||||
@ -428,7 +434,8 @@ void Render::renderLayer(
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
frame_t frame,
|
||||
const gfx::Clip& area)
|
||||
const gfx::Clip& area,
|
||||
int blend_mode)
|
||||
{
|
||||
m_sprite = layer->sprite();
|
||||
|
||||
@ -442,7 +449,7 @@ void Render::renderLayer(
|
||||
m_globalOpacity = 255;
|
||||
renderLayer(layer, dstImage, area,
|
||||
frame, Zoom(1, 1), scaled_func,
|
||||
true, true, -1);
|
||||
true, true, blend_mode);
|
||||
}
|
||||
|
||||
void Render::renderSprite(
|
||||
@ -603,6 +610,26 @@ void Render::renderLayer(
|
||||
if (!layer->isVisible())
|
||||
return;
|
||||
|
||||
gfx::Rect extraArea;
|
||||
bool drawExtra = (m_extraCel &&
|
||||
m_extraImage &&
|
||||
layer == m_currentLayer &&
|
||||
frame == m_currentFrame);
|
||||
if (drawExtra) {
|
||||
extraArea = gfx::Rect(
|
||||
m_extraCel->x(),
|
||||
m_extraCel->y(),
|
||||
m_extraImage->width(),
|
||||
m_extraImage->height());
|
||||
extraArea = zoom.apply(extraArea);
|
||||
if (zoom.scale() < 1.0) {
|
||||
extraArea.w--;
|
||||
extraArea.h--;
|
||||
}
|
||||
if (extraArea.w < 1) extraArea.w = 1;
|
||||
if (extraArea.h < 1) extraArea.h = 1;
|
||||
}
|
||||
|
||||
switch (layer->type()) {
|
||||
|
||||
case ObjectType::LayerImage: {
|
||||
@ -612,6 +639,7 @@ void Render::renderLayer(
|
||||
|
||||
const Cel* cel = layer->cel(frame);
|
||||
if (cel != NULL) {
|
||||
Palette* pal = m_sprite->palette(frame);
|
||||
Image* src_image;
|
||||
|
||||
// Is the 'm_previewImage' set to be used with this layer?
|
||||
@ -633,14 +661,34 @@ void Render::renderLayer(
|
||||
|
||||
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
|
||||
|
||||
renderCel(image, src_image,
|
||||
m_sprite->palette(frame),
|
||||
cel, area, scaled_func,
|
||||
output_opacity,
|
||||
int layer_blend_mode =
|
||||
(blend_mode < 0 ?
|
||||
static_cast<const LayerImage*>(layer)->getBlendMode():
|
||||
blend_mode),
|
||||
zoom);
|
||||
static_cast<const LayerImage*>(layer)->getBlendMode():
|
||||
blend_mode);
|
||||
|
||||
// Draw parts outside the "m_extraCel" area
|
||||
if (drawExtra && m_extraType == ExtraType::PATCH) {
|
||||
gfx::Region originalAreas(area.srcBounds());
|
||||
originalAreas.createSubtraction(
|
||||
originalAreas, gfx::Region(extraArea));
|
||||
|
||||
for (auto rc : originalAreas) {
|
||||
renderCel(
|
||||
image, src_image, pal,
|
||||
cel, gfx::Clip(area.dst.x+rc.x-area.src.x,
|
||||
area.dst.y+rc.y-area.src.y, rc), scaled_func,
|
||||
output_opacity,
|
||||
layer_blend_mode, zoom);
|
||||
}
|
||||
}
|
||||
// Draw the whole cel
|
||||
else {
|
||||
renderCel(
|
||||
image, src_image, pal,
|
||||
cel, area, scaled_func,
|
||||
output_opacity,
|
||||
layer_blend_mode, zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -663,14 +711,16 @@ void Render::renderLayer(
|
||||
}
|
||||
|
||||
// Draw extras
|
||||
if (m_extraCel &&
|
||||
m_extraImage &&
|
||||
layer == m_currentLayer &&
|
||||
frame == m_currentFrame) {
|
||||
if (drawExtra && m_extraType != ExtraType::NONE) {
|
||||
if (m_extraCel->opacity() > 0) {
|
||||
renderCel(image, m_extraImage,
|
||||
renderCel(
|
||||
image, m_extraImage,
|
||||
m_sprite->palette(frame),
|
||||
m_extraCel, area, scaled_func,
|
||||
m_extraCel,
|
||||
gfx::Clip(area.dst.x+extraArea.x-area.src.x,
|
||||
area.dst.y+extraArea.y-area.src.y,
|
||||
extraArea),
|
||||
scaled_func,
|
||||
m_extraCel->opacity(),
|
||||
m_extraBlendMode, zoom);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -13,6 +13,7 @@
|
||||
#include "doc/pixel_format.h"
|
||||
#include "gfx/fwd.h"
|
||||
#include "gfx/size.h"
|
||||
#include "render/extra_type.h"
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace gfx {
|
||||
@ -45,7 +46,7 @@ namespace render {
|
||||
class Render {
|
||||
public:
|
||||
Render();
|
||||
|
||||
|
||||
// Background configuration
|
||||
void setBgType(BgType type);
|
||||
void setBgZoom(bool state);
|
||||
@ -61,6 +62,7 @@ namespace render {
|
||||
// Sets an extra cel/image to be drawn after the current
|
||||
// layer/frame.
|
||||
void setExtraImage(
|
||||
ExtraType type,
|
||||
const Cel* cel, const Image* image, int blendMode,
|
||||
const Layer* currentLayer,
|
||||
frame_t currentFrame);
|
||||
@ -90,7 +92,8 @@ namespace render {
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
frame_t frame,
|
||||
const gfx::Clip& area);
|
||||
const gfx::Clip& area,
|
||||
int blend_mode = -1);
|
||||
|
||||
// Main function used to render the sprite. Draws the given sprite
|
||||
// frame in a new image and return it. Note: zoomedRect must have
|
||||
@ -143,6 +146,7 @@ namespace render {
|
||||
const Sprite* m_sprite;
|
||||
const Layer* m_currentLayer;
|
||||
frame_t m_currentFrame;
|
||||
ExtraType m_extraType;
|
||||
const Cel* m_extraCel;
|
||||
const Image* m_extraImage;
|
||||
int m_extraBlendMode;
|
||||
|
@ -161,6 +161,7 @@ namespace she {
|
||||
void setDrawMode(DrawMode mode, int param) {
|
||||
switch (mode) {
|
||||
case DrawMode::Solid: checked_mode(-1); break;
|
||||
case DrawMode::Xor: xor_mode(TRUE); break;
|
||||
case DrawMode::Checked: checked_mode(param); break;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// SHE library
|
||||
// Copyright (C) 2012-2013 David Capello
|
||||
// Copyright (C) 2012-2013, 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -16,7 +16,8 @@ namespace she {
|
||||
|
||||
enum class DrawMode {
|
||||
Solid,
|
||||
Checked
|
||||
Checked,
|
||||
Xor
|
||||
};
|
||||
|
||||
class Surface {
|
||||
|
@ -67,6 +67,9 @@ void Graphics::setDrawMode(DrawMode mode, int param)
|
||||
case DrawMode::Solid:
|
||||
m_surface->setDrawMode(she::DrawMode::Solid);
|
||||
break;
|
||||
case DrawMode::Xor:
|
||||
m_surface->setDrawMode(she::DrawMode::Xor);
|
||||
break;
|
||||
case DrawMode::Checked:
|
||||
m_surface->setDrawMode(she::DrawMode::Checked, param);
|
||||
break;
|
||||
|
@ -33,6 +33,7 @@ namespace ui {
|
||||
public:
|
||||
enum class DrawMode {
|
||||
Solid,
|
||||
Xor,
|
||||
Checked,
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2001-2013 David Capello
|
||||
// Copyright (C) 2001-2013, 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -47,6 +47,13 @@ void TooltipManager::addTooltipFor(Widget* widget, const std::string& text, int
|
||||
m_tips[widget] = TipInfo(text, arrowAlign);
|
||||
}
|
||||
|
||||
void TooltipManager::removeTooltipFor(Widget* widget)
|
||||
{
|
||||
auto it = m_tips.find(widget);
|
||||
if (it != m_tips.end())
|
||||
m_tips.erase(it);
|
||||
}
|
||||
|
||||
bool TooltipManager::onProcessMessage(Message* msg)
|
||||
{
|
||||
switch (msg->type()) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2001-2013 David Capello
|
||||
// Copyright (C) 2001-2013, 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -8,6 +8,7 @@
|
||||
#define UI_TOOLTIPS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/unique_ptr.h"
|
||||
#include "ui/base.h"
|
||||
#include "ui/popup_window.h"
|
||||
#include "ui/window.h"
|
||||
@ -25,6 +26,7 @@ namespace ui {
|
||||
~TooltipManager();
|
||||
|
||||
void addTooltipFor(Widget* widget, const std::string& text, int arrowAlign = 0);
|
||||
void removeTooltipFor(Widget* widget);
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(Message* msg) override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user