Add option to put the onion skin behind the sprite

Fix #526 and fix #412
This commit is contained in:
David Capello 2015-07-31 15:55:06 -03:00
parent 80b47419fd
commit bf54fd26d3
9 changed files with 89 additions and 10 deletions

View File

@ -200,6 +200,7 @@
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
<option id="loop_tag" type="bool" default="true" />
<option id="current_layer" type="bool" default="false" />
<option id="position" type="render::OnionskinPosition" default="render::OnionskinPosition::BEHIND" />
</section>
<section id="save_copy">
<option id="filename" type="std::string" />

View File

@ -18,6 +18,10 @@
<check id="loop_tag" text="Loop through tag frames" 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.&#10;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>
</vbox>
</gui>

View File

@ -22,6 +22,7 @@
#include "doc/layer_index.h"
#include "filters/tiled_mode.h"
#include "gfx/rect.h"
#include "render/onionskin_position.h"
#include "generated_pref_types.h"

View File

@ -54,6 +54,8 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
m_box->resetOnionskin()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onResetOnionskin, this));
m_box->loopTag()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onLoopTagChange, 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()
@ -92,6 +94,15 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
m_box->tint()->setSelected(true);
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)
@ -142,6 +153,7 @@ void ConfigureTimelinePopup::onResetOnionskin()
docPref.onionskin.opacityStep(docPref.onionskin.opacityStep.defaultValue());
docPref.onionskin.loopTag(docPref.onionskin.loopTag.defaultValue());
docPref.onionskin.currentLayer(docPref.onionskin.currentLayer.defaultValue());
docPref.onionskin.position(docPref.onionskin.position.defaultValue());
updateWidgetsFromCurrentSettings();
}
@ -156,4 +168,11 @@ void ConfigureTimelinePopup::onCurrentLayerChange()
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

View File

@ -39,6 +39,7 @@ namespace app {
void onResetOnionskin();
void onLoopTagChange();
void onCurrentLayerChange();
void onPositionChange();
private:
void updateWidgetsFromCurrentSettings();

View File

@ -460,6 +460,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
render::OnionskinType::RED_BLUE_TINT:
render::OnionskinType::NONE)));
opts.position(docPref.onionskin.position());
opts.prevFrames(docPref.onionskin.prevFrames());
opts.nextFrames(docPref.onionskin.nextFrames());
opts.opacityBase(docPref.onionskin.opacityBase());

View File

@ -471,6 +471,7 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
const char* id = elem->Attribute("id");
const char* text = elem->Attribute("text");
const char* tooltip = elem->Attribute("tooltip");
const char* tooltip_dir = elem->Attribute("tooltip_dir");
bool selected = bool_attr_is_true(elem, "selected");
bool disabled = bool_attr_is_true(elem, "disabled");
bool expansive = bool_attr_is_true(elem, "expansive");
@ -503,12 +504,20 @@ void WidgetLoader::fillWidgetWithXmlElementAttributes(const TiXmlElement* elem,
if (text)
widget->setText(text);
if (tooltip != NULL && root != NULL) {
if (tooltip && root) {
if (!m_tooltipManager) {
m_tooltipManager = new ui::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)

View File

@ -462,7 +462,8 @@ void Render::renderLayer(
return;
m_globalOpacity = 255;
renderLayer(layer, dstImage, area,
renderLayer(
layer, dstImage, area,
frame, Zoom(1, 1), scaled_func,
true, true, blend_mode);
}
@ -521,13 +522,39 @@ void Render::renderSprite(
break;
}
// Draw the current frame.
// Draw the background layer.
m_globalOpacity = 255;
renderLayer(
m_sprite->folder(), dstImage,
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
// opacity (<255)
if (m_onionskin.type() != OnionskinType::NONE) {
@ -563,18 +590,23 @@ void Render::renderSprite(
m_globalOpacity = m_onionskin.opacityBase() - m_onionskin.opacityStep() * ((frameOut - frame)-1);
}
if (m_globalOpacity > 0) {
m_globalOpacity = MID(0, m_globalOpacity, 255);
if (m_globalOpacity > 0) {
BlendMode blend_mode = BlendMode::UNSPECIFIED;
if (m_onionskin.type() == OnionskinType::MERGE)
blend_mode = BlendMode::NORMAL;
else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT)
blend_mode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT);
renderLayer(onionLayer, dstImage,
renderLayer(
onionLayer, dstImage,
area, frameIn, zoom, scaled_func,
true, true, blend_mode);
// Render background only for "in-front" onion skinning and
// when opacity is < 255
(m_globalOpacity < 255 &&
m_onionskin.position() == OnionskinPosition::INFRONT),
true,
blend_mode);
}
}
}

View File

@ -16,6 +16,7 @@
#include "gfx/fwd.h"
#include "gfx/size.h"
#include "render/extra_type.h"
#include "render/onionskin_position.h"
#include "render/zoom.h"
namespace gfx {
@ -50,6 +51,7 @@ namespace render {
public:
OnionskinOptions(OnionskinType type)
: m_type(type)
, m_position(OnionskinPosition::BEHIND)
, m_prevFrames(0)
, m_nextFrames(0)
, m_opacityBase(0)
@ -59,6 +61,7 @@ namespace render {
}
OnionskinType type() const { return m_type; }
OnionskinPosition position() const { return m_position; }
int prevFrames() const { return m_prevFrames; }
int nextFrames() const { return m_nextFrames; }
int opacityBase() const { return m_opacityBase; }
@ -67,6 +70,7 @@ namespace render {
Layer* layer() const { return m_layer; }
void type(OnionskinType type) { m_type = type; }
void position(OnionskinPosition position) { m_position = position; }
void prevFrames(int prevFrames) { m_prevFrames = prevFrames; }
void nextFrames(int nextFrames) { m_nextFrames = nextFrames; }
void opacityBase(int base) { m_opacityBase = base; }
@ -76,6 +80,7 @@ namespace render {
private:
OnionskinType m_type;
OnionskinPosition m_position;
int m_prevFrames;
int m_nextFrames;
int m_opacityBase;
@ -160,6 +165,12 @@ namespace render {
const gfx::Clip& area,
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(
const Layer* layer,
Image* image,