Merge branch 'onionskin'

Conflicts:
	src/app/ui/editor/play_state.cpp
	src/render/render.cpp
	third_party/gtest
This commit is contained in:
David Capello 2015-06-05 15:53:15 -03:00
commit 0a1dfe0633
14 changed files with 235 additions and 153 deletions

View File

@ -144,6 +144,7 @@
<option id="opacity_base" type="int" default="68" migrate="Onionskin.OpacityBase" />
<option id="opacity_step" type="int" default="28" migrate="Onionskin.OpacityStep" />
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
<option id="loop_tag" type="bool" default="true" />
</section>
<section id="save_copy">
<option id="filename" type="std::string" />

View File

@ -15,6 +15,8 @@
<label text="Opacity Step:" />
<slider min="0" max="255" id="opacity_step" cell_align="horizontal" width="128" />
<check id="loop_tag" text="Loop through tag frames" cell_hspan="2" />
</grid>
</vbox>
</gui>

View File

@ -267,7 +267,6 @@ add_library(app-lib
filename_formatter.cpp
flatten.cpp
gui_xml.cpp
handle_anidir.cpp
ini_file.cpp
job.cpp
launcher.cpp

View File

@ -1,82 +0,0 @@
// 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/handle_anidir.h"
#include "doc/frame.h"
#include "doc/frame_tag.h"
#include "doc/sprite.h"
namespace app {
doc::frame_t calculate_next_frame(
doc::Sprite* sprite,
doc::frame_t frame,
doc::FrameTag* tag,
bool& pingPongForward)
{
doc::frame_t first = doc::frame_t(0);
doc::frame_t last = sprite->lastFrame();
doc::AniDir aniDir = doc::AniDir::FORWARD;
if (tag) {
doc::frame_t loopFrom, loopTo;
loopFrom = tag->fromFrame();
loopTo = tag->toFrame();
loopFrom = MID(first, loopFrom, last);
loopTo = MID(first, loopTo, last);
first = loopFrom;
last = loopTo;
aniDir = tag->aniDir();
}
switch (aniDir) {
case doc::AniDir::FORWARD:
++frame;
if (frame > last)
frame = first;
break;
case doc::AniDir::REVERSE:
--frame;
if (frame < first)
frame = last;
break;
case doc::AniDir::PING_PONG:
if (pingPongForward) {
++frame;
if (frame > last) {
frame = last-1;
if (frame < first)
frame = first;
pingPongForward = false;
}
}
else {
--frame;
if (frame < first) {
frame = first+1;
if (frame > last)
frame = last;
pingPongForward = true;
}
}
break;
}
return frame;
}
} // namespace app

View File

@ -1,29 +0,0 @@
// 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_HANDLE_ANIDIR_H_INCLUDED
#define APP_HANDLE_ANIDIR_H_INCLUDED
#pragma once
#include "doc/frame.h"
namespace doc {
class FrameTag;
class Sprite;
}
namespace app {
doc::frame_t calculate_next_frame(
doc::Sprite* sprite,
doc::frame_t frame,
doc::FrameTag* tag,
bool& pingPongForward);
} // namespace app
#endif

View File

@ -52,6 +52,7 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
m_box->opacity()->Change.connect(Bind<void>(&ConfigureTimelinePopup::onOpacity, this));
m_box->opacityStep()->Change.connect(Bind<void>(&ConfigureTimelinePopup::onOpacityStep, this));
m_box->resetOnionskin()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onResetOnionskin, this));
m_box->loopTag()->Click.connect(Bind<void>(&ConfigureTimelinePopup::onLoopTagChange, this));
}
app::Document* ConfigureTimelinePopup::doc()
@ -79,6 +80,7 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
}
m_box->opacity()->setValue(docPref.onionskin.opacityBase());
m_box->opacityStep()->setValue(docPref.onionskin.opacityStep());
m_box->loopTag()->setSelected(docPref.onionskin.loopTag());
switch (docPref.onionskin.type()) {
case app::gen::OnionskinType::MERGE:
@ -136,8 +138,14 @@ void ConfigureTimelinePopup::onResetOnionskin()
docPref.onionskin.type(docPref.onionskin.type.defaultValue());
docPref.onionskin.opacityBase(docPref.onionskin.opacityBase.defaultValue());
docPref.onionskin.opacityStep(docPref.onionskin.opacityStep.defaultValue());
docPref.onionskin.loopTag(docPref.onionskin.loopTag.defaultValue());
updateWidgetsFromCurrentSettings();
}
void ConfigureTimelinePopup::onLoopTagChange()
{
docPref().onionskin.loopTag(m_box->loopTag()->isSelected());
}
} // namespace app

View File

@ -37,6 +37,7 @@ namespace app {
void onOpacity();
void onOpacityStep();
void onResetOnionskin();
void onLoopTagChange();
private:
void updateWidgetsFromCurrentSettings();

View File

@ -427,23 +427,31 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
// Create a temporary RGB bitmap to draw all to it
rendered.reset(Image::create(IMAGE_RGB, rc.w, rc.h, m_renderBuffer));
m_renderEngine.setupBackground(m_document, rendered->pixelFormat());
m_renderEngine.setOnionskin(render::OnionskinType::NONE, 0, 0, 0, 0);
m_renderEngine.disableOnionskin();
if ((m_flags & kShowOnionskin) == kShowOnionskin) {
DocumentPreferences& docPref = Preferences::instance()
.document(m_document);
if (docPref.onionskin.active()) {
m_renderEngine.setOnionskin(
OnionskinOptions opts(
(docPref.onionskin.type() == app::gen::OnionskinType::MERGE ?
render::OnionskinType::MERGE:
(docPref.onionskin.type() == app::gen::OnionskinType::RED_BLUE_TINT ?
render::OnionskinType::RED_BLUE_TINT:
render::OnionskinType::NONE)),
docPref.onionskin.prevFrames(),
docPref.onionskin.nextFrames(),
docPref.onionskin.opacityBase(),
docPref.onionskin.opacityStep());
render::OnionskinType::MERGE:
(docPref.onionskin.type() == app::gen::OnionskinType::RED_BLUE_TINT ?
render::OnionskinType::RED_BLUE_TINT:
render::OnionskinType::NONE)));
opts.prevFrames(docPref.onionskin.prevFrames());
opts.nextFrames(docPref.onionskin.nextFrames());
opts.opacityBase(docPref.onionskin.opacityBase());
opts.opacityStep(docPref.onionskin.opacityStep());
FrameTag* tag = nullptr;
if (docPref.onionskin.loopTag())
tag = m_sprite->frameTags().innerTag(m_frame);
opts.loopTag(tag);
m_renderEngine.setOnionskin(opts);
}
}

View File

@ -13,11 +13,11 @@
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/handle_anidir.h"
#include "app/loop_tag.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/scrolling_state.h"
#include "app/ui_context.h"
#include "doc/handle_anidir.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/system.h"
@ -134,8 +134,7 @@ void PlayState::onPlaybackTick()
while (m_nextFrameTime <= 0) {
doc::frame_t frame = calculate_next_frame(
sprite,
m_editor->frame(), tag,
sprite, m_editor->frame(), frame_t(1), tag,
m_pingPongForward);
m_editor->setFrame(frame);

View File

@ -29,6 +29,7 @@ add_library(doc-lib
frame_tag.cpp
frame_tag_io.cpp
frame_tags.cpp
handle_anidir.cpp
image.cpp
image_io.cpp
images_collector.cpp

99
src/doc/handle_anidir.cpp Normal file
View File

@ -0,0 +1,99 @@
// 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/handle_anidir.h"
#include "doc/frame.h"
#include "doc/frame_tag.h"
#include "doc/sprite.h"
namespace doc {
frame_t calculate_next_frame(
const Sprite* sprite,
frame_t frame,
frame_t frameDelta,
const FrameTag* tag,
bool& pingPongForward)
{
if (frameDelta == 0)
return frame;
frame_t first = frame_t(0);
frame_t last = sprite->lastFrame();
AniDir aniDir = AniDir::FORWARD;
if (tag) {
frame_t loopFrom, loopTo;
loopFrom = tag->fromFrame();
loopTo = tag->toFrame();
loopFrom = MID(first, loopFrom, last);
loopTo = MID(first, loopTo, last);
first = loopFrom;
last = loopTo;
aniDir = tag->aniDir();
}
frame_t frameRange = (last - first + 1);
switch (aniDir) {
case AniDir::REVERSE:
frameDelta = -frameDelta;
case AniDir::FORWARD:
frame += frameDelta;
while (frame > last) frame -= frameRange;
while (frame < first) frame += frameRange;
break;
case AniDir::PING_PONG: {
bool invertPingPong;
if (frameDelta < 0) {
frameDelta = -frameDelta;
pingPongForward = !pingPongForward;
invertPingPong = true;
}
else
invertPingPong = false;
while (--frameDelta >= 0) {
if (pingPongForward) {
++frame;
if (frame > last) {
frame = last-1;
if (frame < first)
frame = first;
pingPongForward = false;
}
}
else {
--frame;
if (frame < first) {
frame = first+1;
if (frame > last)
frame = last;
pingPongForward = true;
}
}
}
if (invertPingPong)
pingPongForward = !pingPongForward;
break;
}
}
return frame;
}
} // namespace doc

27
src/doc/handle_anidir.h Normal file
View File

@ -0,0 +1,27 @@
// 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_HANDLE_ANIDIR_H_INCLUDED
#define DOC_HANDLE_ANIDIR_H_INCLUDED
#pragma once
#include "doc/frame.h"
namespace doc {
class FrameTag;
class Sprite;
frame_t calculate_next_frame(
const Sprite* sprite,
frame_t frame,
frame_t frameDelta,
const FrameTag* tag,
bool& pingPongForward);
} // namespace doc
#endif

View File

@ -11,6 +11,7 @@
#include "render/render.h"
#include "doc/doc.h"
#include "doc/handle_anidir.h"
#include "gfx/clip.h"
#include "gfx/region.h"
@ -331,7 +332,7 @@ Render::Render()
, m_selectedLayer(nullptr)
, m_selectedFrame(-1)
, m_previewImage(nullptr)
, m_onionskinType(OnionskinType::NONE)
, m_onionskin(OnionskinType::NONE)
{
}
@ -392,18 +393,14 @@ void Render::removeExtraImage()
m_extraCel = NULL;
}
void Render::setOnionskin(OnionskinType type, int prevs, int nexts, int opacityBase, int opacityStep)
void Render::setOnionskin(const OnionskinOptions& options)
{
m_onionskinType = type;
m_onionskinPrevs = prevs;
m_onionskinNexts = nexts;
m_onionskinOpacityBase = opacityBase;
m_onionskinOpacityStep = opacityStep;
m_onionskin = options;
}
void Render::disableOnionskin()
{
m_onionskinType = OnionskinType::NONE;
m_onionskin.type(OnionskinType::NONE);
}
void Render::renderSprite(
@ -510,27 +507,48 @@ void Render::renderSprite(
// Onion-skin feature: Draw previous/next frames with different
// opacity (<255)
if (m_onionskinType != OnionskinType::NONE) {
for (frame_t f = frame - m_onionskinPrevs;
f <= frame + m_onionskinNexts; ++f) {
if (f == frame || f < 0 || f > m_sprite->lastFrame())
if (m_onionskin.type() != OnionskinType::NONE) {
FrameTag* loop = m_onionskin.loopTag();
frame_t frameIn;
for (frame_t frameOut = frame - m_onionskin.prevFrames();
frameOut <= frame + m_onionskin.nextFrames();
++frameOut) {
if (loop) {
bool pingPongForward = true;
frameIn =
calculate_next_frame(m_sprite,
frame, frameOut - frame,
loop, pingPongForward);
}
else {
frameIn = frameOut;
}
if (frameIn == frame ||
frameIn < 0 ||
frameIn > m_sprite->lastFrame()) {
continue;
else if (f < frame)
m_globalOpacity = m_onionskinOpacityBase - m_onionskinOpacityStep * ((frame - f)-1);
else
m_globalOpacity = m_onionskinOpacityBase - m_onionskinOpacityStep * ((f - frame)-1);
}
if (frameOut < frame) {
m_globalOpacity = m_onionskin.opacityBase() - m_onionskin.opacityStep() * ((frame - frameOut)-1);
}
else {
m_globalOpacity = m_onionskin.opacityBase() - m_onionskin.opacityStep() * ((frameOut - frame)-1);
}
if (m_globalOpacity > 0) {
m_globalOpacity = MID(0, m_globalOpacity, 255);
int blend_mode = -1;
if (m_onionskinType == OnionskinType::MERGE)
if (m_onionskin.type() == OnionskinType::MERGE)
blend_mode = BLEND_MODE_NORMAL;
else if (m_onionskinType == OnionskinType::RED_BLUE_TINT)
blend_mode = (f < frame ? BLEND_MODE_RED_TINT: BLEND_MODE_BLUE_TINT);
else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT)
blend_mode = (frameOut < frame ? BLEND_MODE_RED_TINT: BLEND_MODE_BLUE_TINT);
renderLayer(m_sprite->folder(), dstImage,
area, f, zoom, scaled_func,
area, frameIn, zoom, scaled_func,
true, true, blend_mode);
}
}

View File

@ -8,6 +8,7 @@
#define RENDER_RENDER_H_INCLUDED
#pragma once
#include "doc/anidir.h"
#include "doc/color.h"
#include "doc/frame.h"
#include "doc/pixel_format.h"
@ -22,6 +23,7 @@ namespace gfx {
namespace doc {
class Cel;
class FrameTag;
class Image;
class Layer;
class Palette;
@ -43,6 +45,40 @@ namespace render {
RED_BLUE_TINT,
};
class OnionskinOptions {
public:
OnionskinOptions(OnionskinType type)
: m_type(type)
, m_prevFrames(0)
, m_nextFrames(0)
, m_opacityBase(0)
, m_opacityStep(0)
, m_loopTag(nullptr) {
}
OnionskinType type() const { return m_type; }
int prevFrames() const { return m_prevFrames; }
int nextFrames() const { return m_nextFrames; }
int opacityBase() const { return m_opacityBase; }
int opacityStep() const { return m_opacityStep; }
FrameTag* loopTag() const { return m_loopTag; }
void type(OnionskinType type) { m_type = type; }
void prevFrames(int prevFrames) { m_prevFrames = prevFrames; }
void nextFrames(int nextFrames) { m_nextFrames = nextFrames; }
void opacityBase(int base) { m_opacityBase = base; }
void opacityStep(int step) { m_opacityStep = step; }
void loopTag(FrameTag* loopTag) { m_loopTag = loopTag; }
private:
OnionskinType m_type;
int m_prevFrames;
int m_nextFrames;
int m_opacityBase;
int m_opacityStep;
FrameTag* m_loopTag;
};
class Render {
public:
Render();
@ -68,8 +104,7 @@ namespace render {
frame_t currentFrame);
void removeExtraImage();
void setOnionskin(OnionskinType type,
int prevs, int nexts, int opacityBase, int opacityStep);
void setOnionskin(const OnionskinOptions& options);
void disableOnionskin();
void renderSprite(
@ -160,12 +195,7 @@ namespace render {
const Layer* m_selectedLayer;
frame_t m_selectedFrame;
Image* m_previewImage;
OnionskinType m_onionskinType;
int m_onionskinPrevs;
int m_onionskinNexts;
int m_onionskinOpacityBase;
int m_onionskinOpacityStep;
OnionskinOptions m_onionskin;
};
void composite_image(Image* dst, const Image* src,