mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 10:13:22 +00:00
Merge branch 'onionskin'
Conflicts: src/app/ui/editor/play_state.cpp src/render/render.cpp third_party/gtest
This commit is contained in:
commit
0a1dfe0633
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -37,6 +37,7 @@ namespace app {
|
||||
void onOpacity();
|
||||
void onOpacityStep();
|
||||
void onResetOnionskin();
|
||||
void onLoopTagChange();
|
||||
|
||||
private:
|
||||
void updateWidgetsFromCurrentSettings();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
99
src/doc/handle_anidir.cpp
Normal 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
27
src/doc/handle_anidir.h
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user