mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 10:13:22 +00:00
Add option to put the onion skin behind the sprite
Fix #526 and fix #412
This commit is contained in:
parent
80b47419fd
commit
bf54fd26d3
@ -200,6 +200,7 @@
|
|||||||
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
|
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
|
||||||
<option id="loop_tag" type="bool" default="true" />
|
<option id="loop_tag" type="bool" default="true" />
|
||||||
<option id="current_layer" type="bool" default="false" />
|
<option id="current_layer" type="bool" default="false" />
|
||||||
|
<option id="position" type="render::OnionskinPosition" default="render::OnionskinPosition::BEHIND" />
|
||||||
</section>
|
</section>
|
||||||
<section id="save_copy">
|
<section id="save_copy">
|
||||||
<option id="filename" type="std::string" />
|
<option id="filename" type="std::string" />
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
<check id="loop_tag" text="Loop through tag frames" cell_hspan="2" />
|
<check id="loop_tag" text="Loop through tag frames" cell_hspan="2" />
|
||||||
<check id="current_layer" text="Current layer only" cell_hspan="2" />
|
<check id="current_layer" text="Current layer only" cell_hspan="2" />
|
||||||
|
<hbox cell_hspan="2">
|
||||||
|
<radio group="2" text="Behind sprite" id="behind" tooltip="Only for transparent layers. Background is not included in this onion skin mode." tooltip_dir="top" />
|
||||||
|
<radio group="2" text="In front of sprite" id="infront" tooltip="For all kind of layers (background and transparents)" />
|
||||||
|
</hbox>
|
||||||
</grid>
|
</grid>
|
||||||
</vbox>
|
</vbox>
|
||||||
</gui>
|
</gui>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "doc/layer_index.h"
|
#include "doc/layer_index.h"
|
||||||
#include "filters/tiled_mode.h"
|
#include "filters/tiled_mode.h"
|
||||||
#include "gfx/rect.h"
|
#include "gfx/rect.h"
|
||||||
|
#include "render/onionskin_position.h"
|
||||||
|
|
||||||
#include "generated_pref_types.h"
|
#include "generated_pref_types.h"
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
|
|||||||
m_box->resetOnionskin()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onResetOnionskin, this));
|
m_box->resetOnionskin()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onResetOnionskin, this));
|
||||||
m_box->loopTag()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onLoopTagChange, this));
|
m_box->loopTag()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onLoopTagChange, this));
|
||||||
m_box->currentLayer()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onCurrentLayerChange, this));
|
m_box->currentLayer()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onCurrentLayerChange, this));
|
||||||
|
m_box->behind()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onPositionChange, this));
|
||||||
|
m_box->infront()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onPositionChange, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
app::Document* ConfigureTimelinePopup::doc()
|
app::Document* ConfigureTimelinePopup::doc()
|
||||||
@ -92,6 +94,15 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
|
|||||||
m_box->tint()->setSelected(true);
|
m_box->tint()->setSelected(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (docPref.onionskin.position()) {
|
||||||
|
case render::OnionskinPosition::BEHIND:
|
||||||
|
m_box->behind()->setSelected(true);
|
||||||
|
break;
|
||||||
|
case render::OnionskinPosition::INFRONT:
|
||||||
|
m_box->infront()->setSelected(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigureTimelinePopup::onProcessMessage(ui::Message* msg)
|
bool ConfigureTimelinePopup::onProcessMessage(ui::Message* msg)
|
||||||
@ -142,6 +153,7 @@ void ConfigureTimelinePopup::onResetOnionskin()
|
|||||||
docPref.onionskin.opacityStep(docPref.onionskin.opacityStep.defaultValue());
|
docPref.onionskin.opacityStep(docPref.onionskin.opacityStep.defaultValue());
|
||||||
docPref.onionskin.loopTag(docPref.onionskin.loopTag.defaultValue());
|
docPref.onionskin.loopTag(docPref.onionskin.loopTag.defaultValue());
|
||||||
docPref.onionskin.currentLayer(docPref.onionskin.currentLayer.defaultValue());
|
docPref.onionskin.currentLayer(docPref.onionskin.currentLayer.defaultValue());
|
||||||
|
docPref.onionskin.position(docPref.onionskin.position.defaultValue());
|
||||||
|
|
||||||
updateWidgetsFromCurrentSettings();
|
updateWidgetsFromCurrentSettings();
|
||||||
}
|
}
|
||||||
@ -156,4 +168,11 @@ void ConfigureTimelinePopup::onCurrentLayerChange()
|
|||||||
docPref().onionskin.currentLayer(m_box->currentLayer()->isSelected());
|
docPref().onionskin.currentLayer(m_box->currentLayer()->isSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureTimelinePopup::onPositionChange()
|
||||||
|
{
|
||||||
|
docPref().onionskin.position(m_box->behind()->isSelected() ?
|
||||||
|
render::OnionskinPosition::BEHIND:
|
||||||
|
render::OnionskinPosition::INFRONT);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -39,6 +39,7 @@ namespace app {
|
|||||||
void onResetOnionskin();
|
void onResetOnionskin();
|
||||||
void onLoopTagChange();
|
void onLoopTagChange();
|
||||||
void onCurrentLayerChange();
|
void onCurrentLayerChange();
|
||||||
|
void onPositionChange();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateWidgetsFromCurrentSettings();
|
void updateWidgetsFromCurrentSettings();
|
||||||
|
@ -460,6 +460,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
|
|||||||
render::OnionskinType::RED_BLUE_TINT:
|
render::OnionskinType::RED_BLUE_TINT:
|
||||||
render::OnionskinType::NONE)));
|
render::OnionskinType::NONE)));
|
||||||
|
|
||||||
|
opts.position(docPref.onionskin.position());
|
||||||
opts.prevFrames(docPref.onionskin.prevFrames());
|
opts.prevFrames(docPref.onionskin.prevFrames());
|
||||||
opts.nextFrames(docPref.onionskin.nextFrames());
|
opts.nextFrames(docPref.onionskin.nextFrames());
|
||||||
opts.opacityBase(docPref.onionskin.opacityBase());
|
opts.opacityBase(docPref.onionskin.opacityBase());
|
||||||
|
@ -471,6 +471,7 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
|
|||||||
const char* id = elem->Attribute("id");
|
const char* id = elem->Attribute("id");
|
||||||
const char* text = elem->Attribute("text");
|
const char* text = elem->Attribute("text");
|
||||||
const char* tooltip = elem->Attribute("tooltip");
|
const char* tooltip = elem->Attribute("tooltip");
|
||||||
|
const char* tooltip_dir = elem->Attribute("tooltip_dir");
|
||||||
bool selected = bool_attr_is_true(elem, "selected");
|
bool selected = bool_attr_is_true(elem, "selected");
|
||||||
bool disabled = bool_attr_is_true(elem, "disabled");
|
bool disabled = bool_attr_is_true(elem, "disabled");
|
||||||
bool expansive = bool_attr_is_true(elem, "expansive");
|
bool expansive = bool_attr_is_true(elem, "expansive");
|
||||||
@ -503,12 +504,20 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
|
|||||||
if (text)
|
if (text)
|
||||||
widget->setText(text);
|
widget->setText(text);
|
||||||
|
|
||||||
if (tooltip != NULL && root != NULL) {
|
if (tooltip && root) {
|
||||||
if (!m_tooltipManager) {
|
if (!m_tooltipManager) {
|
||||||
m_tooltipManager = new ui::TooltipManager();
|
m_tooltipManager = new ui::TooltipManager();
|
||||||
root->addChild(m_tooltipManager);
|
root->addChild(m_tooltipManager);
|
||||||
}
|
}
|
||||||
m_tooltipManager->addTooltipFor(widget, tooltip, LEFT);
|
|
||||||
|
int dir = LEFT;
|
||||||
|
if (tooltip_dir) {
|
||||||
|
if (strcmp(tooltip_dir, "top") == 0) dir = TOP;
|
||||||
|
else if (strcmp(tooltip_dir, "bottom") == 0) dir = BOTTOM;
|
||||||
|
else if (strcmp(tooltip_dir, "left") == 0) dir = LEFT;
|
||||||
|
else if (strcmp(tooltip_dir, "right") == 0) dir = RIGHT;
|
||||||
|
}
|
||||||
|
m_tooltipManager->addTooltipFor(widget, tooltip, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected)
|
if (selected)
|
||||||
|
@ -462,7 +462,8 @@ void Render::renderLayer(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_globalOpacity = 255;
|
m_globalOpacity = 255;
|
||||||
renderLayer(layer, dstImage, area,
|
renderLayer(
|
||||||
|
layer, dstImage, area,
|
||||||
frame, Zoom(1, 1), scaled_func,
|
frame, Zoom(1, 1), scaled_func,
|
||||||
true, true, blend_mode);
|
true, true, blend_mode);
|
||||||
}
|
}
|
||||||
@ -521,13 +522,39 @@ void Render::renderSprite(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the current frame.
|
// Draw the background layer.
|
||||||
m_globalOpacity = 255;
|
m_globalOpacity = 255;
|
||||||
renderLayer(
|
renderLayer(
|
||||||
m_sprite->folder(), dstImage,
|
m_sprite->folder(), dstImage,
|
||||||
area, frame, zoom, scaled_func,
|
area, frame, zoom, scaled_func,
|
||||||
true, true, BlendMode::UNSPECIFIED);
|
true,
|
||||||
|
false,
|
||||||
|
BlendMode::UNSPECIFIED);
|
||||||
|
|
||||||
|
// Draw onion skin behind the sprite.
|
||||||
|
if (m_onionskin.position() == OnionskinPosition::BEHIND)
|
||||||
|
renderOnionskin(dstImage, area, frame, zoom, scaled_func);
|
||||||
|
|
||||||
|
// Draw the transparent layers.
|
||||||
|
m_globalOpacity = 255;
|
||||||
|
renderLayer(
|
||||||
|
m_sprite->folder(), dstImage,
|
||||||
|
area, frame, zoom, scaled_func,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
BlendMode::UNSPECIFIED);
|
||||||
|
|
||||||
|
// Draw onion skin in front of the sprite.
|
||||||
|
if (m_onionskin.position() == OnionskinPosition::INFRONT)
|
||||||
|
renderOnionskin(dstImage, area, frame, zoom, scaled_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render::renderOnionskin(
|
||||||
|
Image* dstImage,
|
||||||
|
const gfx::Clip& area,
|
||||||
|
frame_t frame, Zoom zoom,
|
||||||
|
RenderScaledImage scaled_func)
|
||||||
|
{
|
||||||
// Onion-skin feature: Draw previous/next frames with different
|
// Onion-skin feature: Draw previous/next frames with different
|
||||||
// opacity (<255)
|
// opacity (<255)
|
||||||
if (m_onionskin.type() != OnionskinType::NONE) {
|
if (m_onionskin.type() != OnionskinType::NONE) {
|
||||||
@ -563,18 +590,23 @@ void Render::renderSprite(
|
|||||||
m_globalOpacity = m_onionskin.opacityBase() - m_onionskin.opacityStep() * ((frameOut - frame)-1);
|
m_globalOpacity = m_onionskin.opacityBase() - m_onionskin.opacityStep() * ((frameOut - frame)-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_globalOpacity = MID(0, m_globalOpacity, 255);
|
||||||
if (m_globalOpacity > 0) {
|
if (m_globalOpacity > 0) {
|
||||||
m_globalOpacity = MID(0, m_globalOpacity, 255);
|
|
||||||
|
|
||||||
BlendMode blend_mode = BlendMode::UNSPECIFIED;
|
BlendMode blend_mode = BlendMode::UNSPECIFIED;
|
||||||
if (m_onionskin.type() == OnionskinType::MERGE)
|
if (m_onionskin.type() == OnionskinType::MERGE)
|
||||||
blend_mode = BlendMode::NORMAL;
|
blend_mode = BlendMode::NORMAL;
|
||||||
else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT)
|
else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT)
|
||||||
blend_mode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT);
|
blend_mode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT);
|
||||||
|
|
||||||
renderLayer(onionLayer, dstImage,
|
renderLayer(
|
||||||
area, frameIn, zoom, scaled_func,
|
onionLayer, dstImage,
|
||||||
true, true, blend_mode);
|
area, frameIn, zoom, scaled_func,
|
||||||
|
// Render background only for "in-front" onion skinning and
|
||||||
|
// when opacity is < 255
|
||||||
|
(m_globalOpacity < 255 &&
|
||||||
|
m_onionskin.position() == OnionskinPosition::INFRONT),
|
||||||
|
true,
|
||||||
|
blend_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "gfx/fwd.h"
|
#include "gfx/fwd.h"
|
||||||
#include "gfx/size.h"
|
#include "gfx/size.h"
|
||||||
#include "render/extra_type.h"
|
#include "render/extra_type.h"
|
||||||
|
#include "render/onionskin_position.h"
|
||||||
#include "render/zoom.h"
|
#include "render/zoom.h"
|
||||||
|
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
@ -50,6 +51,7 @@ namespace render {
|
|||||||
public:
|
public:
|
||||||
OnionskinOptions(OnionskinType type)
|
OnionskinOptions(OnionskinType type)
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
|
, m_position(OnionskinPosition::BEHIND)
|
||||||
, m_prevFrames(0)
|
, m_prevFrames(0)
|
||||||
, m_nextFrames(0)
|
, m_nextFrames(0)
|
||||||
, m_opacityBase(0)
|
, m_opacityBase(0)
|
||||||
@ -59,6 +61,7 @@ namespace render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OnionskinType type() const { return m_type; }
|
OnionskinType type() const { return m_type; }
|
||||||
|
OnionskinPosition position() const { return m_position; }
|
||||||
int prevFrames() const { return m_prevFrames; }
|
int prevFrames() const { return m_prevFrames; }
|
||||||
int nextFrames() const { return m_nextFrames; }
|
int nextFrames() const { return m_nextFrames; }
|
||||||
int opacityBase() const { return m_opacityBase; }
|
int opacityBase() const { return m_opacityBase; }
|
||||||
@ -67,6 +70,7 @@ namespace render {
|
|||||||
Layer* layer() const { return m_layer; }
|
Layer* layer() const { return m_layer; }
|
||||||
|
|
||||||
void type(OnionskinType type) { m_type = type; }
|
void type(OnionskinType type) { m_type = type; }
|
||||||
|
void position(OnionskinPosition position) { m_position = position; }
|
||||||
void prevFrames(int prevFrames) { m_prevFrames = prevFrames; }
|
void prevFrames(int prevFrames) { m_prevFrames = prevFrames; }
|
||||||
void nextFrames(int nextFrames) { m_nextFrames = nextFrames; }
|
void nextFrames(int nextFrames) { m_nextFrames = nextFrames; }
|
||||||
void opacityBase(int base) { m_opacityBase = base; }
|
void opacityBase(int base) { m_opacityBase = base; }
|
||||||
@ -76,6 +80,7 @@ namespace render {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
OnionskinType m_type;
|
OnionskinType m_type;
|
||||||
|
OnionskinPosition m_position;
|
||||||
int m_prevFrames;
|
int m_prevFrames;
|
||||||
int m_nextFrames;
|
int m_nextFrames;
|
||||||
int m_opacityBase;
|
int m_opacityBase;
|
||||||
@ -160,6 +165,12 @@ namespace render {
|
|||||||
const gfx::Clip& area,
|
const gfx::Clip& area,
|
||||||
int opacity, BlendMode blend_mode, Zoom zoom);
|
int opacity, BlendMode blend_mode, Zoom zoom);
|
||||||
|
|
||||||
|
void renderOnionskin(
|
||||||
|
Image* image,
|
||||||
|
const gfx::Clip& area,
|
||||||
|
frame_t frame, Zoom zoom,
|
||||||
|
RenderScaledImage scaled_func);
|
||||||
|
|
||||||
void renderLayer(
|
void renderLayer(
|
||||||
const Layer* layer,
|
const Layer* layer,
|
||||||
Image* image,
|
Image* image,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user