mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-09 07:13:43 +00:00
Merge branch 'master' into beta
This commit is contained in:
commit
36ffc8d2bd
@ -292,6 +292,7 @@
|
|||||||
<section id="import_sprite_sheet">
|
<section id="import_sprite_sheet">
|
||||||
<option id="type" type="app::SpriteSheetType" default="app::SpriteSheetType::Rows" />
|
<option id="type" type="app::SpriteSheetType" default="app::SpriteSheetType::Rows" />
|
||||||
<option id="bounds" type="gfx::Rect" default="gfx::Rect(0, 0, 16, 16)" />
|
<option id="bounds" type="gfx::Rect" default="gfx::Rect(0, 0, 16, 16)" />
|
||||||
|
<option id="partial_tiles" type="bool" default="false" />
|
||||||
</section>
|
</section>
|
||||||
<section id="preview" text="Preview">
|
<section id="preview" text="Preview">
|
||||||
<option id="zoom" type="double" default="1.0" />
|
<option id="zoom" type="double" default="1.0" />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!-- ASEPRITE -->
|
<!-- ASEPRITE -->
|
||||||
<!-- Copyright (C) 2001-2015 by David Capello -->
|
<!-- Copyright (C) 2001-2016 by David Capello -->
|
||||||
<gui>
|
<gui>
|
||||||
<window id="import_sprite_sheet" text="Import Sprite Sheet">
|
<window id="import_sprite_sheet" text="Import Sprite Sheet">
|
||||||
<grid columns="4">
|
<grid columns="4">
|
||||||
@ -18,6 +18,8 @@
|
|||||||
<label text="Height" />
|
<label text="Height" />
|
||||||
<entry id="height" text="16" maxsize="4" />
|
<entry id="height" text="16" maxsize="4" />
|
||||||
|
|
||||||
|
<check id="partial_tiles" text="Include partial tiles at bottom/right edges" cell_hspan="4" />
|
||||||
|
|
||||||
<hbox cell_hspan="4">
|
<hbox cell_hspan="4">
|
||||||
<boxfiller />
|
<boxfiller />
|
||||||
<hbox>
|
<hbox>
|
||||||
|
@ -159,6 +159,12 @@ add_executable(aseprite WIN32
|
|||||||
target_link_libraries(aseprite app-lib ${PLATFORM_LIBS})
|
target_link_libraries(aseprite app-lib ${PLATFORM_LIBS})
|
||||||
add_dependencies(aseprite copy_data)
|
add_dependencies(aseprite copy_data)
|
||||||
|
|
||||||
|
if(MSVC AND USE_SKIA_BACKEND)
|
||||||
|
# Add support to expand filename wildcards in argc/argv
|
||||||
|
set_target_properties(aseprite
|
||||||
|
PROPERTIES LINK_FLAGS "-LINK wsetargv.obj -ENTRY:\"wWinMainCRTStartup\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS aseprite
|
install(TARGETS aseprite
|
||||||
RUNTIME DESTINATION bin)
|
RUNTIME DESTINATION bin)
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ set(file_formats
|
|||||||
file/jpeg_format.cpp
|
file/jpeg_format.cpp
|
||||||
file/pcx_format.cpp
|
file/pcx_format.cpp
|
||||||
file/png_format.cpp
|
file/png_format.cpp
|
||||||
|
file/pixly_format.cpp
|
||||||
file/tga_format.cpp)
|
file/tga_format.cpp)
|
||||||
if(WITH_WEBP_SUPPORT)
|
if(WITH_WEBP_SUPPORT)
|
||||||
list(APPEND file_formats file/webp_format.cpp)
|
list(APPEND file_formats file/webp_format.cpp)
|
||||||
|
@ -230,6 +230,12 @@ void App::run()
|
|||||||
she::instance()->activateApp();
|
she::instance()->activateApp();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if _DEBUG
|
||||||
|
// On OS X, when we compile Aseprite on Debug mode, we're using it
|
||||||
|
// outside an app bundle, so we must active the app explicitly.
|
||||||
|
she::instance()->activateApp();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_UPDATER
|
#ifdef ENABLE_UPDATER
|
||||||
// Launch the thread to check for updates.
|
// Launch the thread to check for updates.
|
||||||
app::CheckUpdateThreadLauncher checkUpdate(
|
app::CheckUpdateThreadLauncher checkUpdate(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
@ -74,24 +74,18 @@ void ChangeColorCommand::onExecute(Context* context)
|
|||||||
case None:
|
case None:
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
case IncrementIndex:
|
case IncrementIndex: {
|
||||||
if (color.getType() == app::Color::IndexType) {
|
int index = color.getIndex();
|
||||||
int index = color.getIndex();
|
if (index < get_current_palette()->size()-1)
|
||||||
if (index < get_current_palette()->size()-1)
|
color = app::Color::fromIndex(index+1);
|
||||||
color = app::Color::fromIndex(index+1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
color = app::Color::fromIndex(0);
|
|
||||||
break;
|
break;
|
||||||
case DecrementIndex:
|
}
|
||||||
if (color.getType() == app::Color::IndexType) {
|
case DecrementIndex: {
|
||||||
int index = color.getIndex();
|
int index = color.getIndex();
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
color = app::Color::fromIndex(index-1);
|
color = app::Color::fromIndex(index-1);
|
||||||
}
|
|
||||||
else
|
|
||||||
color = app::Color::fromIndex(0);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_background)
|
if (m_background)
|
||||||
|
@ -117,7 +117,8 @@ void FlipCommand::onExecute(Context* context)
|
|||||||
else
|
else
|
||||||
api.flipImage(image, flipBounds, m_flipType);
|
api.flipImage(image, flipBounds, m_flipType);
|
||||||
|
|
||||||
transaction.execute(new cmd::TrimCel(cel));
|
if (cel->layer()->isTransparent())
|
||||||
|
transaction.execute(new cmd::TrimCel(cel));
|
||||||
}
|
}
|
||||||
// When the mask is bigger than the cel bounds, we have to
|
// When the mask is bigger than the cel bounds, we have to
|
||||||
// expand the cel, make the flip, and shrink it again.
|
// expand the cel, make the flip, and shrink it again.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
@ -92,6 +92,10 @@ public:
|
|||||||
return (app::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
|
return (app::SpriteSheetType)(sheetType()->getSelectedItemIndex()+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool partialTilesValue() const {
|
||||||
|
return partialTiles()->isSelected();
|
||||||
|
}
|
||||||
|
|
||||||
bool ok() const {
|
bool ok() const {
|
||||||
return closer() == import();
|
return closer() == import();
|
||||||
}
|
}
|
||||||
@ -214,6 +218,7 @@ private:
|
|||||||
sheetType()->setSelectedItemIndex((int)app::SpriteSheetType::Rows-1);
|
sheetType()->setSelectedItemIndex((int)app::SpriteSheetType::Rows-1);
|
||||||
|
|
||||||
onChangeRectangle(m_docPref->importSpriteSheet.bounds());
|
onChangeRectangle(m_docPref->importSpriteSheet.bounds());
|
||||||
|
partialTiles()->setSelected(m_docPref->importSpriteSheet.partialTiles());
|
||||||
onEntriesChange();
|
onEntriesChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,6 +310,7 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
|
|||||||
Document* document = window.document();
|
Document* document = window.document();
|
||||||
DocumentPreferences* docPref = window.docPref();
|
DocumentPreferences* docPref = window.docPref();
|
||||||
gfx::Rect frameBounds = window.frameBounds();
|
gfx::Rect frameBounds = window.frameBounds();
|
||||||
|
bool partialTiles = window.partialTilesValue();
|
||||||
auto sheetType = window.sheetTypeValue();
|
auto sheetType = window.sheetTypeValue();
|
||||||
|
|
||||||
ASSERT(document);
|
ASSERT(document);
|
||||||
@ -321,21 +327,27 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
|
|||||||
|
|
||||||
// Each sprite in the sheet
|
// Each sprite in the sheet
|
||||||
std::vector<gfx::Rect> tileRects;
|
std::vector<gfx::Rect> tileRects;
|
||||||
|
int widthStop = sprite->width();
|
||||||
|
int heightStop = sprite->height();
|
||||||
|
if (partialTiles) {
|
||||||
|
widthStop += frameBounds.w-1;
|
||||||
|
heightStop += frameBounds.h-1;
|
||||||
|
}
|
||||||
|
|
||||||
switch (sheetType) {
|
switch (sheetType) {
|
||||||
case app::SpriteSheetType::Horizontal:
|
case app::SpriteSheetType::Horizontal:
|
||||||
for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
|
for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) {
|
||||||
tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h));
|
tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case app::SpriteSheetType::Vertical:
|
case app::SpriteSheetType::Vertical:
|
||||||
for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
|
for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) {
|
||||||
tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h));
|
tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case app::SpriteSheetType::Rows:
|
case app::SpriteSheetType::Rows:
|
||||||
for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
|
for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) {
|
||||||
for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
|
for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) {
|
||||||
tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
|
tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,6 +423,7 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
|
|||||||
if (docPref) {
|
if (docPref) {
|
||||||
docPref->importSpriteSheet.type(sheetType);
|
docPref->importSpriteSheet.type(sheetType);
|
||||||
docPref->importSpriteSheet.bounds(frameBounds);
|
docPref->importSpriteSheet.bounds(frameBounds);
|
||||||
|
docPref->importSpriteSheet.partialTiles(partialTiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
|
@ -30,9 +30,9 @@
|
|||||||
#include "ui/graphics.h"
|
#include "ui/graphics.h"
|
||||||
#include "ui/listitem.h"
|
#include "ui/listitem.h"
|
||||||
#include "ui/paint_event.h"
|
#include "ui/paint_event.h"
|
||||||
#include "ui/size_hint_event.h"
|
|
||||||
#include "ui/resize_event.h"
|
#include "ui/resize_event.h"
|
||||||
#include "ui/separator.h"
|
#include "ui/separator.h"
|
||||||
|
#include "ui/size_hint_event.h"
|
||||||
|
|
||||||
#include "keyboard_shortcuts.xml.h"
|
#include "keyboard_shortcuts.xml.h"
|
||||||
|
|
||||||
@ -46,6 +46,20 @@ using namespace skin;
|
|||||||
static int g_sep = 0;
|
static int g_sep = 0;
|
||||||
|
|
||||||
class KeyItem : public ListItem {
|
class KeyItem : public ListItem {
|
||||||
|
|
||||||
|
// Used to avoid deleting the Add/Change/Del buttons on
|
||||||
|
// kMouseLeaveMessage when a foreground window is popup on a signal
|
||||||
|
// generated by those same buttons.
|
||||||
|
struct LockButtons {
|
||||||
|
KeyItem* keyItem;
|
||||||
|
LockButtons(KeyItem* keyItem) : keyItem(keyItem) {
|
||||||
|
keyItem->m_lockButtons = true;
|
||||||
|
};
|
||||||
|
~LockButtons() {
|
||||||
|
keyItem->m_lockButtons = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KeyItem(const std::string& text, Key* key, AppMenuItem* menuitem, int level)
|
KeyItem(const std::string& text, Key* key, AppMenuItem* menuitem, int level)
|
||||||
: ListItem(text)
|
: ListItem(text)
|
||||||
@ -53,7 +67,8 @@ public:
|
|||||||
, m_keyOrig(key ? new Key(*key): NULL)
|
, m_keyOrig(key ? new Key(*key): NULL)
|
||||||
, m_menuitem(menuitem)
|
, m_menuitem(menuitem)
|
||||||
, m_level(level)
|
, m_level(level)
|
||||||
, m_hotAccel(-1) {
|
, m_hotAccel(-1)
|
||||||
|
, m_lockButtons(false) {
|
||||||
gfx::Border border = this->border();
|
gfx::Border border = this->border();
|
||||||
border.top(0);
|
border.top(0);
|
||||||
border.bottom(0);
|
border.bottom(0);
|
||||||
@ -73,6 +88,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
void onChangeAccel(int index) {
|
void onChangeAccel(int index) {
|
||||||
|
LockButtons lock(this);
|
||||||
Accelerator origAccel = m_key->accels()[index];
|
Accelerator origAccel = m_key->accels()[index];
|
||||||
SelectAccelerator window(origAccel, m_key->keycontext());
|
SelectAccelerator window(origAccel, m_key->keycontext());
|
||||||
window.openWindowInForeground();
|
window.openWindowInForeground();
|
||||||
@ -87,6 +103,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDeleteAccel(int index) {
|
void onDeleteAccel(int index) {
|
||||||
|
LockButtons lock(this);
|
||||||
// We need to create a copy of the accelerator because
|
// We need to create a copy of the accelerator because
|
||||||
// Key::disableAccel() will modify the accels() collection itself.
|
// Key::disableAccel() will modify the accels() collection itself.
|
||||||
ui::Accelerator accel = m_key->accels()[index];
|
ui::Accelerator accel = m_key->accels()[index];
|
||||||
@ -103,6 +120,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onAddAccel() {
|
void onAddAccel() {
|
||||||
|
LockButtons lock(this);
|
||||||
ui::Accelerator accel;
|
ui::Accelerator accel;
|
||||||
SelectAccelerator window(accel, m_key ? m_key->keycontext(): KeyContext::Any);
|
SelectAccelerator window(accel, m_key ? m_key->keycontext(): KeyContext::Any);
|
||||||
window.openWindowInForeground();
|
window.openWindowInForeground();
|
||||||
@ -221,13 +239,15 @@ private:
|
|||||||
if (m_hotAccel != i) {
|
if (m_hotAccel != i) {
|
||||||
m_hotAccel = i;
|
m_hotAccel = i;
|
||||||
|
|
||||||
|
m_changeConn = base::Connection();
|
||||||
m_changeButton.reset(new Button(""));
|
m_changeButton.reset(new Button(""));
|
||||||
m_changeButton->Click.connect(base::Bind<void>(&KeyItem::onChangeAccel, this, i));
|
m_changeConn = m_changeButton->Click.connect(base::Bind<void>(&KeyItem::onChangeAccel, this, i));
|
||||||
setup_mini_look(m_changeButton.get());
|
setup_mini_look(m_changeButton.get());
|
||||||
addChild(m_changeButton.get());
|
addChild(m_changeButton.get());
|
||||||
|
|
||||||
|
m_deleteConn = base::Connection();
|
||||||
m_deleteButton.reset(new Button(""));
|
m_deleteButton.reset(new Button(""));
|
||||||
m_deleteButton->Click.connect(base::Bind<void>(&KeyItem::onDeleteAccel, this, i));
|
m_deleteConn = m_deleteButton->Click.connect(base::Bind<void>(&KeyItem::onDeleteAccel, this, i));
|
||||||
setup_mini_look(m_deleteButton.get());
|
setup_mini_look(m_deleteButton.get());
|
||||||
addChild(m_deleteButton.get());
|
addChild(m_deleteButton.get());
|
||||||
|
|
||||||
@ -251,8 +271,9 @@ private:
|
|||||||
|
|
||||||
if (i == 0 && !m_addButton &&
|
if (i == 0 && !m_addButton &&
|
||||||
(!m_menuitem || m_menuitem->getCommand())) {
|
(!m_menuitem || m_menuitem->getCommand())) {
|
||||||
|
m_addConn = base::Connection();
|
||||||
m_addButton.reset(new Button(""));
|
m_addButton.reset(new Button(""));
|
||||||
m_addButton->Click.connect(base::Bind<void>(&KeyItem::onAddAccel, this));
|
m_addConn = m_addButton->Click.connect(base::Bind<void>(&KeyItem::onAddAccel, this));
|
||||||
setup_mini_look(m_addButton.get());
|
setup_mini_look(m_addButton.get());
|
||||||
addChild(m_addButton.get());
|
addChild(m_addButton.get());
|
||||||
|
|
||||||
@ -273,9 +294,22 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void destroyButtons() {
|
void destroyButtons() {
|
||||||
m_changeButton.reset();
|
m_changeConn = base::Connection();
|
||||||
m_deleteButton.reset();
|
m_deleteConn = base::Connection();
|
||||||
m_addButton.reset();
|
m_addConn = base::Connection();
|
||||||
|
|
||||||
|
if (!m_lockButtons) {
|
||||||
|
m_changeButton.reset();
|
||||||
|
m_deleteButton.reset();
|
||||||
|
m_addButton.reset();
|
||||||
|
}
|
||||||
|
// Just hide the buttons
|
||||||
|
else {
|
||||||
|
if (m_changeButton) m_changeButton->setVisible(false);
|
||||||
|
if (m_deleteButton) m_deleteButton->setVisible(false);
|
||||||
|
if (m_addButton) m_addButton->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
m_hotAccel = -1;
|
m_hotAccel = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +321,11 @@ private:
|
|||||||
base::SharedPtr<ui::Button> m_changeButton;
|
base::SharedPtr<ui::Button> m_changeButton;
|
||||||
base::SharedPtr<ui::Button> m_deleteButton;
|
base::SharedPtr<ui::Button> m_deleteButton;
|
||||||
base::SharedPtr<ui::Button> m_addButton;
|
base::SharedPtr<ui::Button> m_addButton;
|
||||||
|
base::ScopedConnection m_changeConn;
|
||||||
|
base::ScopedConnection m_deleteConn;
|
||||||
|
base::ScopedConnection m_addConn;
|
||||||
int m_hotAccel;
|
int m_hotAccel;
|
||||||
|
bool m_lockButtons;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
|
class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "app/commands/filters/filter_manager_impl.h"
|
#include "app/commands/filters/filter_manager_impl.h"
|
||||||
|
|
||||||
#include "app/cmd/copy_rect.h"
|
#include "app/cmd/copy_rect.h"
|
||||||
|
#include "app/cmd/patch_cel.h"
|
||||||
#include "app/cmd/unlink_cel.h"
|
#include "app/cmd/unlink_cel.h"
|
||||||
#include "app/context_access.h"
|
#include "app/context_access.h"
|
||||||
#include "app/document.h"
|
#include "app/document.h"
|
||||||
@ -19,6 +20,7 @@
|
|||||||
#include "app/modules/editors.h"
|
#include "app/modules/editors.h"
|
||||||
#include "app/transaction.h"
|
#include "app/transaction.h"
|
||||||
#include "app/ui/editor/editor.h"
|
#include "app/ui/editor/editor.h"
|
||||||
|
#include "doc/algorithm/shrink_bounds.h"
|
||||||
#include "doc/cel.h"
|
#include "doc/cel.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/images_collector.h"
|
#include "doc/images_collector.h"
|
||||||
@ -44,29 +46,23 @@ FilterManagerImpl::FilterManagerImpl(Context* context, Filter* filter)
|
|||||||
: m_context(context)
|
: m_context(context)
|
||||||
, m_site(context->activeSite())
|
, m_site(context->activeSite())
|
||||||
, m_filter(filter)
|
, m_filter(filter)
|
||||||
, m_dst(NULL)
|
, m_cel(nullptr)
|
||||||
, m_preview_mask(NULL)
|
, m_src(nullptr)
|
||||||
|
, m_dst(nullptr)
|
||||||
|
, m_mask(nullptr)
|
||||||
|
, m_previewMask(nullptr)
|
||||||
, m_progressDelegate(NULL)
|
, m_progressDelegate(NULL)
|
||||||
{
|
{
|
||||||
int offset_x, offset_y;
|
|
||||||
|
|
||||||
m_src = NULL;
|
|
||||||
m_row = 0;
|
m_row = 0;
|
||||||
m_offset_x = 0;
|
|
||||||
m_offset_y = 0;
|
|
||||||
m_mask = NULL;
|
|
||||||
m_targetOrig = TARGET_ALL_CHANNELS;
|
m_targetOrig = TARGET_ALL_CHANNELS;
|
||||||
m_target = TARGET_ALL_CHANNELS;
|
m_target = TARGET_ALL_CHANNELS;
|
||||||
|
|
||||||
Image* image = m_site.image(&offset_x, &offset_y);
|
int x, y;
|
||||||
if (image == NULL)
|
Image* image = m_site.image(&x, &y);
|
||||||
|
if (!image)
|
||||||
throw NoImageException();
|
throw NoImageException();
|
||||||
|
|
||||||
init(m_site.layer(), image, offset_x, offset_y);
|
init(m_site.cel());
|
||||||
}
|
|
||||||
|
|
||||||
FilterManagerImpl::~FilterManagerImpl()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app::Document* FilterManagerImpl::document()
|
app::Document* FilterManagerImpl::document()
|
||||||
@ -100,9 +96,8 @@ void FilterManagerImpl::begin()
|
|||||||
Document* document = static_cast<app::Document*>(m_site.document());
|
Document* document = static_cast<app::Document*>(m_site.document());
|
||||||
|
|
||||||
m_row = 0;
|
m_row = 0;
|
||||||
m_mask = (document->isMaskVisible() ? document->mask(): NULL);
|
m_mask = (document->isMaskVisible() ? document->mask(): nullptr);
|
||||||
|
updateBounds(m_mask);
|
||||||
updateMask(m_mask, m_src);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterManagerImpl::beginForPreview()
|
void FilterManagerImpl::beginForPreview()
|
||||||
@ -110,17 +105,14 @@ void FilterManagerImpl::beginForPreview()
|
|||||||
Document* document = static_cast<app::Document*>(m_site.document());
|
Document* document = static_cast<app::Document*>(m_site.document());
|
||||||
|
|
||||||
if (document->isMaskVisible())
|
if (document->isMaskVisible())
|
||||||
m_preview_mask.reset(new Mask(*document->mask()));
|
m_previewMask.reset(new Mask(*document->mask()));
|
||||||
else {
|
else {
|
||||||
m_preview_mask.reset(new Mask());
|
m_previewMask.reset(new Mask());
|
||||||
m_preview_mask->replace(
|
m_previewMask->replace(m_site.sprite()->bounds());
|
||||||
gfx::Rect(m_offset_x, m_offset_y,
|
|
||||||
m_src->width(),
|
|
||||||
m_src->height()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_row = 0;
|
m_row = 0;
|
||||||
m_mask = m_preview_mask;
|
m_mask = m_previewMask;
|
||||||
|
|
||||||
{
|
{
|
||||||
Editor* editor = current_editor;
|
Editor* editor = current_editor;
|
||||||
@ -130,16 +122,16 @@ void FilterManagerImpl::beginForPreview()
|
|||||||
vp = vp.createIntersection(sprite->bounds());
|
vp = vp.createIntersection(sprite->bounds());
|
||||||
|
|
||||||
if (vp.isEmpty()) {
|
if (vp.isEmpty()) {
|
||||||
m_preview_mask.reset(NULL);
|
m_previewMask.reset(nullptr);
|
||||||
m_row = -1;
|
m_row = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_preview_mask->intersect(vp);
|
m_previewMask->intersect(vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!updateMask(m_mask, m_src)) {
|
if (!updateBounds(m_mask)) {
|
||||||
m_preview_mask.reset(NULL);
|
m_previewMask.reset(nullptr);
|
||||||
m_row = -1;
|
m_row = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -152,19 +144,19 @@ void FilterManagerImpl::end()
|
|||||||
|
|
||||||
bool FilterManagerImpl::applyStep()
|
bool FilterManagerImpl::applyStep()
|
||||||
{
|
{
|
||||||
if (m_row < 0 || m_row >= m_h)
|
if (m_row < 0 || m_row >= m_bounds.h)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((m_mask) && (m_mask->bitmap())) {
|
if (m_mask && m_mask->bitmap()) {
|
||||||
int x = m_x - m_mask->bounds().x + m_offset_x;
|
int x = m_bounds.x - m_mask->bounds().x;
|
||||||
int y = m_y - m_mask->bounds().y + m_offset_y + m_row;
|
int y = m_bounds.y - m_mask->bounds().y + m_row;
|
||||||
|
if ((x >= m_bounds.w) ||
|
||||||
if ((m_w - x < 1) || (m_h - y < 1))
|
(y >= m_bounds.h))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_maskBits = m_mask->bitmap()
|
m_maskBits = m_mask->bitmap()
|
||||||
->lockBits<BitmapTraits>(Image::ReadLock,
|
->lockBits<BitmapTraits>(Image::ReadLock,
|
||||||
gfx::Rect(x, y, m_w - x, m_h - y));
|
gfx::Rect(x, y, m_bounds.w - x, m_bounds.h - y));
|
||||||
|
|
||||||
m_maskIterator = m_maskBits.begin();
|
m_maskIterator = m_maskBits.begin();
|
||||||
}
|
}
|
||||||
@ -187,7 +179,7 @@ void FilterManagerImpl::apply(Transaction& transaction)
|
|||||||
while (!cancelled && applyStep()) {
|
while (!cancelled && applyStep()) {
|
||||||
if (m_progressDelegate) {
|
if (m_progressDelegate) {
|
||||||
// Report progress.
|
// Report progress.
|
||||||
m_progressDelegate->reportProgress(m_progressBase + m_progressWidth * (m_row+1) / m_h);
|
m_progressDelegate->reportProgress(m_progressBase + m_progressWidth * (m_row+1) / m_bounds.h);
|
||||||
|
|
||||||
// Does the user cancelled the whole process?
|
// Does the user cancelled the whole process?
|
||||||
cancelled = m_progressDelegate->isCancelled();
|
cancelled = m_progressDelegate->isCancelled();
|
||||||
@ -195,9 +187,16 @@ void FilterManagerImpl::apply(Transaction& transaction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
// Copy "dst" to "src"
|
gfx::Rect output;
|
||||||
transaction.execute(new cmd::CopyRect(
|
if (algorithm::shrink_bounds2(m_src.get(), m_dst.get(),
|
||||||
m_src, m_dst, gfx::Clip(m_x, m_y, m_x, m_y, m_w, m_h)));
|
m_bounds, output)) {
|
||||||
|
// Patch "m_cel"
|
||||||
|
transaction.execute(
|
||||||
|
new cmd::PatchCel(
|
||||||
|
m_cel, m_dst.get(),
|
||||||
|
gfx::Region(output),
|
||||||
|
position()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,8 +232,7 @@ void FilterManagerImpl::applyToTarget()
|
|||||||
// Avoid applying the filter two times to the same image
|
// Avoid applying the filter two times to the same image
|
||||||
if (visited.find(image->id()) == visited.end()) {
|
if (visited.find(image->id()) == visited.end()) {
|
||||||
visited.insert(image->id());
|
visited.insert(image->id());
|
||||||
applyToImage(transaction, it->layer(),
|
applyToCel(transaction, it->cel());
|
||||||
image, it->cel()->x(), it->cel()->y());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there a delegate to know if the process was cancelled by the user?
|
// Is there a delegate to know if the process was cancelled by the user?
|
||||||
@ -255,10 +253,10 @@ void FilterManagerImpl::flush()
|
|||||||
gfx::Rect rect(
|
gfx::Rect rect(
|
||||||
editor->editorToScreen(
|
editor->editorToScreen(
|
||||||
gfx::Point(
|
gfx::Point(
|
||||||
m_x+m_offset_x,
|
m_bounds.x,
|
||||||
m_y+m_offset_y+m_row-1)),
|
m_bounds.y+m_row-1)),
|
||||||
gfx::Size(
|
gfx::Size(
|
||||||
editor->projection().applyX(m_w),
|
editor->projection().applyX(m_bounds.w),
|
||||||
(editor->projection().scaleY() >= 1 ? editor->projection().applyY(1):
|
(editor->projection().scaleY() >= 1 ? editor->projection().applyY(1):
|
||||||
editor->projection().removeY(1))));
|
editor->projection().removeY(1))));
|
||||||
|
|
||||||
@ -273,12 +271,12 @@ void FilterManagerImpl::flush()
|
|||||||
|
|
||||||
const void* FilterManagerImpl::getSourceAddress()
|
const void* FilterManagerImpl::getSourceAddress()
|
||||||
{
|
{
|
||||||
return m_src->getPixelAddress(m_x, m_row+m_y);
|
return m_src->getPixelAddress(m_bounds.x, m_bounds.y+m_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* FilterManagerImpl::getDestinationAddress()
|
void* FilterManagerImpl::getDestinationAddress()
|
||||||
{
|
{
|
||||||
return m_dst->getPixelAddress(m_x, m_row+m_y);
|
return m_dst->getPixelAddress(m_bounds.x, m_bounds.y+m_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FilterManagerImpl::skipPixel()
|
bool FilterManagerImpl::skipPixel()
|
||||||
@ -305,80 +303,48 @@ RgbMap* FilterManagerImpl::getRgbMap()
|
|||||||
return m_site.sprite()->rgbMap(m_site.frame());
|
return m_site.sprite()->rgbMap(m_site.frame());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterManagerImpl::init(const Layer* layer, Image* image, int offset_x, int offset_y)
|
void FilterManagerImpl::init(Cel* cel)
|
||||||
{
|
{
|
||||||
m_offset_x = offset_x;
|
ASSERT(cel);
|
||||||
m_offset_y = offset_y;
|
if (!updateBounds(static_cast<app::Document*>(m_site.document())->mask()))
|
||||||
|
|
||||||
if (!updateMask(static_cast<app::Document*>(m_site.document())->mask(), image))
|
|
||||||
throw InvalidAreaException();
|
throw InvalidAreaException();
|
||||||
|
|
||||||
m_src = image;
|
m_cel = cel;
|
||||||
m_dst.reset(crop_image(image, 0, 0, image->width(), image->height(), 0));
|
m_src.reset(
|
||||||
|
crop_image(
|
||||||
|
cel->image(),
|
||||||
|
gfx::Rect(m_site.sprite()->bounds()).offset(-cel->position()), 0));
|
||||||
|
m_dst.reset(Image::createCopy(m_src.get()));
|
||||||
|
|
||||||
m_row = -1;
|
m_row = -1;
|
||||||
m_mask = NULL;
|
m_mask = nullptr;
|
||||||
m_preview_mask.reset(NULL);
|
m_previewMask.reset(nullptr);
|
||||||
|
|
||||||
m_target = m_targetOrig;
|
m_target = m_targetOrig;
|
||||||
|
|
||||||
/* the alpha channel of the background layer can't be modified */
|
// The alpha channel of the background layer can't be modified
|
||||||
if (layer->isBackground())
|
if (cel->layer()->isBackground())
|
||||||
m_target &= ~TARGET_ALPHA_CHANNEL;
|
m_target &= ~TARGET_ALPHA_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterManagerImpl::applyToImage(Transaction& transaction, Layer* layer, Image* image, int x, int y)
|
void FilterManagerImpl::applyToCel(Transaction& transaction, Cel* cel)
|
||||||
{
|
{
|
||||||
init(layer, image, x, y);
|
init(cel);
|
||||||
apply(transaction);
|
apply(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FilterManagerImpl::updateMask(Mask* mask, const Image* image)
|
bool FilterManagerImpl::updateBounds(doc::Mask* mask)
|
||||||
{
|
{
|
||||||
int x, y, w, h;
|
gfx::Rect bounds;
|
||||||
|
if (mask && mask->bitmap() && !mask->bounds().isEmpty()) {
|
||||||
if (mask && mask->bitmap()) {
|
bounds = mask->bounds();
|
||||||
x = mask->bounds().x - m_offset_x;
|
bounds &= m_site.sprite()->bounds();
|
||||||
y = mask->bounds().y - m_offset_y;
|
|
||||||
w = mask->bounds().w;
|
|
||||||
h = mask->bounds().h;
|
|
||||||
|
|
||||||
if (x < 0) {
|
|
||||||
w += x;
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < 0) {
|
|
||||||
h += y;
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x+w-1 >= image->width()-1)
|
|
||||||
w = image->width()-x;
|
|
||||||
|
|
||||||
if (y+h-1 >= image->height()-1)
|
|
||||||
h = image->height()-y;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
x = 0;
|
bounds = m_site.sprite()->bounds();
|
||||||
y = 0;
|
|
||||||
w = image->width();
|
|
||||||
h = image->height();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((w < 1) || (h < 1)) {
|
|
||||||
m_x = 0;
|
|
||||||
m_y = 0;
|
|
||||||
m_w = 0;
|
|
||||||
m_h = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_x = x;
|
|
||||||
m_y = y;
|
|
||||||
m_w = w;
|
|
||||||
m_h = h;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
m_bounds = bounds;
|
||||||
|
return !m_bounds.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
@ -12,14 +12,17 @@
|
|||||||
#include "base/exception.h"
|
#include "base/exception.h"
|
||||||
#include "base/unique_ptr.h"
|
#include "base/unique_ptr.h"
|
||||||
#include "doc/image_impl.h"
|
#include "doc/image_impl.h"
|
||||||
|
#include "doc/image_ref.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
#include "doc/site.h"
|
#include "doc/site.h"
|
||||||
#include "filters/filter_indexed_data.h"
|
#include "filters/filter_indexed_data.h"
|
||||||
#include "filters/filter_manager.h"
|
#include "filters/filter_manager.h"
|
||||||
|
#include "gfx/rect.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
class Cel;
|
||||||
class Image;
|
class Image;
|
||||||
class Layer;
|
class Layer;
|
||||||
class Mask;
|
class Mask;
|
||||||
@ -67,7 +70,6 @@ namespace app {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FilterManagerImpl(Context* context, Filter* filter);
|
FilterManagerImpl(Context* context, Filter* filter);
|
||||||
~FilterManagerImpl();
|
|
||||||
|
|
||||||
void setProgressDelegate(IProgressDelegate* progressDelegate);
|
void setProgressDelegate(IProgressDelegate* progressDelegate);
|
||||||
|
|
||||||
@ -85,42 +87,43 @@ namespace app {
|
|||||||
doc::Sprite* sprite() { return m_site.sprite(); }
|
doc::Sprite* sprite() { return m_site.sprite(); }
|
||||||
doc::Layer* layer() { return m_site.layer(); }
|
doc::Layer* layer() { return m_site.layer(); }
|
||||||
doc::frame_t frame() { return m_site.frame(); }
|
doc::frame_t frame() { return m_site.frame(); }
|
||||||
doc::Image* destinationImage() const { return m_dst; }
|
doc::Image* destinationImage() const { return m_dst.get(); }
|
||||||
|
gfx::Point position() const { return gfx::Point(0, 0); }
|
||||||
|
|
||||||
// Updates the current editor to show the progress of the preview.
|
// Updates the current editor to show the progress of the preview.
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
// FilterManager implementation
|
// FilterManager implementation
|
||||||
const void* getSourceAddress();
|
const void* getSourceAddress() override;
|
||||||
void* getDestinationAddress();
|
void* getDestinationAddress() override;
|
||||||
int getWidth() { return m_w; }
|
int getWidth() override { return m_bounds.w; }
|
||||||
Target getTarget() { return m_target; }
|
Target getTarget() override { return m_target; }
|
||||||
FilterIndexedData* getIndexedData() { return this; }
|
FilterIndexedData* getIndexedData() override { return this; }
|
||||||
bool skipPixel();
|
bool skipPixel() override;
|
||||||
const doc::Image* getSourceImage() { return m_src; }
|
const doc::Image* getSourceImage() override { return m_src.get(); }
|
||||||
int x() { return m_x; }
|
int x() override { return m_bounds.x; }
|
||||||
int y() { return m_y+m_row; }
|
int y() override { return m_bounds.y+m_row; }
|
||||||
|
|
||||||
// FilterIndexedData implementation
|
// FilterIndexedData implementation
|
||||||
doc::Palette* getPalette();
|
doc::Palette* getPalette() override;
|
||||||
doc::RgbMap* getRgbMap();
|
doc::RgbMap* getRgbMap() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init(const doc::Layer* layer, doc::Image* image, int offset_x, int offset_y);
|
void init(doc::Cel* cel);
|
||||||
void apply(Transaction& transaction);
|
void apply(Transaction& transaction);
|
||||||
void applyToImage(Transaction& transaction, doc::Layer* layer, doc::Image* image, int x, int y);
|
void applyToCel(Transaction& transaction, doc::Cel* cel);
|
||||||
bool updateMask(doc::Mask* mask, const doc::Image* image);
|
bool updateBounds(doc::Mask* mask);
|
||||||
|
|
||||||
Context* m_context;
|
Context* m_context;
|
||||||
doc::Site m_site;
|
doc::Site m_site;
|
||||||
Filter* m_filter;
|
Filter* m_filter;
|
||||||
doc::Image* m_src;
|
doc::Cel* m_cel;
|
||||||
base::UniquePtr<doc::Image> m_dst;
|
doc::ImageRef m_src;
|
||||||
|
doc::ImageRef m_dst;
|
||||||
int m_row;
|
int m_row;
|
||||||
int m_x, m_y, m_w, m_h;
|
gfx::Rect m_bounds;
|
||||||
int m_offset_x, m_offset_y;
|
|
||||||
doc::Mask* m_mask;
|
doc::Mask* m_mask;
|
||||||
base::UniquePtr<doc::Mask> m_preview_mask;
|
base::UniquePtr<doc::Mask> m_previewMask;
|
||||||
doc::ImageBits<doc::BitmapTraits> m_maskBits;
|
doc::ImageBits<doc::BitmapTraits> m_maskBits;
|
||||||
doc::ImageBits<doc::BitmapTraits>::iterator m_maskIterator;
|
doc::ImageBits<doc::BitmapTraits>::iterator m_maskIterator;
|
||||||
Target m_targetOrig; // Original targets
|
Target m_targetOrig; // Original targets
|
||||||
|
@ -70,6 +70,7 @@ bool FilterPreview::onProcessMessage(Message* msg)
|
|||||||
m_filterMgr->layer(),
|
m_filterMgr->layer(),
|
||||||
m_filterMgr->frame(),
|
m_filterMgr->frame(),
|
||||||
m_filterMgr->destinationImage(),
|
m_filterMgr->destinationImage(),
|
||||||
|
m_filterMgr->position(),
|
||||||
static_cast<doc::LayerImage*>(m_filterMgr->layer())->blendMode());
|
static_cast<doc::LayerImage*>(m_filterMgr->layer())->blendMode());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "app/context.h"
|
||||||
#include "app/document.h"
|
#include "app/document.h"
|
||||||
#include "app/file/file.h"
|
#include "app/file/file.h"
|
||||||
#include "app/file/file_format.h"
|
#include "app/file/file_format.h"
|
||||||
@ -16,10 +17,12 @@
|
|||||||
#include "base/cfile.h"
|
#include "base/cfile.h"
|
||||||
#include "base/exception.h"
|
#include "base/exception.h"
|
||||||
#include "base/file_handle.h"
|
#include "base/file_handle.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "doc/doc.h"
|
#include "doc/doc.h"
|
||||||
|
#include "ui/alert.h"
|
||||||
#include "zlib.h"
|
#include "zlib.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
|
||||||
#define ASE_FILE_MAGIC 0xA5E0
|
#define ASE_FILE_MAGIC 0xA5E0
|
||||||
#define ASE_FILE_FRAME_MAGIC 0xF1FA
|
#define ASE_FILE_FRAME_MAGIC 0xF1FA
|
||||||
@ -132,6 +135,8 @@ static void ase_file_write_frame_tags_chunk(FILE* f, ASE_FrameHeader* frame_head
|
|||||||
const frame_t fromFrame, const frame_t toFrame);
|
const frame_t fromFrame, const frame_t toFrame);
|
||||||
static void ase_file_read_user_data_chunk(FILE* f, UserData* userData);
|
static void ase_file_read_user_data_chunk(FILE* f, UserData* userData);
|
||||||
static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_header, const UserData* userData);
|
static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_header, const UserData* userData);
|
||||||
|
static bool ase_has_groups(LayerFolder* layer);
|
||||||
|
static void ase_ungroup_all(LayerFolder* layer);
|
||||||
|
|
||||||
class ChunkWriter {
|
class ChunkWriter {
|
||||||
public:
|
public:
|
||||||
@ -169,6 +174,7 @@ class AseFormat : public FileFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool onLoad(FileOp* fop) override;
|
bool onLoad(FileOp* fop) override;
|
||||||
|
bool onPostLoad(FileOp* fop) override;
|
||||||
#ifdef ENABLE_SAVE
|
#ifdef ENABLE_SAVE
|
||||||
bool onSave(FileOp* fop) override;
|
bool onSave(FileOp* fop) override;
|
||||||
#endif
|
#endif
|
||||||
@ -340,6 +346,34 @@ bool AseFormat::onLoad(FileOp* fop)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AseFormat::onPostLoad(FileOp* fop)
|
||||||
|
{
|
||||||
|
LayerFolder* folder = fop->document()->sprite()->folder();
|
||||||
|
|
||||||
|
// Forward Compatibility: In 1.1 we convert a file with layer groups
|
||||||
|
// (saved with 1.2) as top level layers
|
||||||
|
std::string ver = VERSION;
|
||||||
|
bool flat = (ver[0] == '1' &&
|
||||||
|
ver[1] == '.' &&
|
||||||
|
ver[2] == '1');
|
||||||
|
if (flat && ase_has_groups(folder)) {
|
||||||
|
if (fop->context() &&
|
||||||
|
fop->context()->isUIAvailable() &&
|
||||||
|
ui::Alert::show("Warning"
|
||||||
|
"<<The selected file \"%s\" has layer groups."
|
||||||
|
"<<Do you want to open it with \"%s %s\" anyway?"
|
||||||
|
"<<"
|
||||||
|
"<<Note: Layers inside groups will be converted to top level layers."
|
||||||
|
"||&Yes||&No",
|
||||||
|
base::get_file_name(fop->filename()).c_str(),
|
||||||
|
PACKAGE, ver.c_str()) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ase_ungroup_all(folder);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SAVE
|
#ifdef ENABLE_SAVE
|
||||||
|
|
||||||
bool AseFormat::onSave(FileOp* fop)
|
bool AseFormat::onSave(FileOp* fop)
|
||||||
@ -1570,4 +1604,46 @@ static void ase_file_write_user_data_chunk(FILE* f, ASE_FrameHeader* frame_heade
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ase_has_groups(LayerFolder* folder)
|
||||||
|
{
|
||||||
|
for (Layer* child : folder->getLayersList()) {
|
||||||
|
if (child->isFolder())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ase_ungroup_all(LayerFolder* folder)
|
||||||
|
{
|
||||||
|
LayerFolder* root = folder->sprite()->folder();
|
||||||
|
LayerList list = folder->getLayersList();
|
||||||
|
|
||||||
|
for (Layer* child : list) {
|
||||||
|
if (child->isFolder()) {
|
||||||
|
ase_ungroup_all(static_cast<LayerFolder*>(child));
|
||||||
|
folder->removeLayer(child);
|
||||||
|
}
|
||||||
|
else if (folder != root) {
|
||||||
|
// Create a new name adding all group layer names
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
for (Layer* layer=child; layer!=root; layer=layer->parent()) {
|
||||||
|
if (!name.empty())
|
||||||
|
name.insert(0, "-");
|
||||||
|
name.insert(0, layer->name());
|
||||||
|
}
|
||||||
|
child->setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
folder->removeLayer(child);
|
||||||
|
root->addLayer(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folder != root) {
|
||||||
|
ASSERT(folder->getLayersCount() == 0);
|
||||||
|
delete folder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -309,6 +309,7 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
|
|||||||
bool fatal = false;
|
bool fatal = false;
|
||||||
|
|
||||||
// Check image type support
|
// Check image type support
|
||||||
|
// TODO add support to automatically convert the image to a supported format
|
||||||
switch (fop->m_document->sprite()->pixelFormat()) {
|
switch (fop->m_document->sprite()->pixelFormat()) {
|
||||||
|
|
||||||
case IMAGE_RGB:
|
case IMAGE_RGB:
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
extern FileFormat* CreateAseFormat();
|
extern FileFormat* CreateAseFormat();
|
||||||
|
extern FileFormat* CreatePixlyFormat();
|
||||||
extern FileFormat* CreateBmpFormat();
|
extern FileFormat* CreateBmpFormat();
|
||||||
extern FileFormat* CreateFliFormat();
|
extern FileFormat* CreateFliFormat();
|
||||||
extern FileFormat* CreateGifFormat();
|
extern FileFormat* CreateGifFormat();
|
||||||
@ -55,6 +56,7 @@ FileFormatsManager::FileFormatsManager()
|
|||||||
{
|
{
|
||||||
// The first format is the default image format in FileSelector
|
// The first format is the default image format in FileSelector
|
||||||
registerFormat(CreateAseFormat());
|
registerFormat(CreateAseFormat());
|
||||||
|
registerFormat(CreatePixlyFormat());
|
||||||
registerFormat(CreateBmpFormat());
|
registerFormat(CreateBmpFormat());
|
||||||
registerFormat(CreateFliFormat());
|
registerFormat(CreateFliFormat());
|
||||||
registerFormat(CreateGifFormat());
|
registerFormat(CreateGifFormat());
|
||||||
|
526
src/app/file/pixly_format.cpp
Normal file
526
src/app/file/pixly_format.cpp
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2016 Carlo "zED" Caputo
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Based on the code of David Capello
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "app/document.h"
|
||||||
|
#include "app/file/file.h"
|
||||||
|
#include "app/file/file_format.h"
|
||||||
|
#include "app/xml_document.h"
|
||||||
|
#include "base/file_handle.h"
|
||||||
|
#include "base/convert_to.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
#include "doc/doc.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "png.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
using namespace base;
|
||||||
|
|
||||||
|
class PixlyFormat : public FileFormat {
|
||||||
|
const char* onGetName() const override { return "anim"; }
|
||||||
|
const char* onGetExtensions() const override { return "anim"; }
|
||||||
|
int onGetFlags() const override {
|
||||||
|
return
|
||||||
|
FILE_SUPPORT_LOAD |
|
||||||
|
FILE_SUPPORT_SAVE |
|
||||||
|
FILE_SUPPORT_RGB |
|
||||||
|
FILE_SUPPORT_RGBA |
|
||||||
|
FILE_SUPPORT_LAYERS |
|
||||||
|
FILE_SUPPORT_FRAMES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onLoad(FileOp* fop) override;
|
||||||
|
#ifdef ENABLE_SAVE
|
||||||
|
bool onSave(FileOp* fop) override;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
FileFormat* CreatePixlyFormat()
|
||||||
|
{
|
||||||
|
return new PixlyFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void report_png_error(png_structp png_ptr, png_const_charp error)
|
||||||
|
{
|
||||||
|
((FileOp*)png_get_error_ptr(png_ptr))->setError("libpng: %s\n", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Any> static Any* check(Any* a, Any* alt = NULL) {
|
||||||
|
if(a == NULL) {
|
||||||
|
if(alt == NULL) {
|
||||||
|
throw Exception("bad structure");
|
||||||
|
} else {
|
||||||
|
return alt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Number> static Number check_number(const char* c_str) {
|
||||||
|
if(c_str == NULL) {
|
||||||
|
throw Exception("value not found");
|
||||||
|
} else {
|
||||||
|
std::string str = c_str;
|
||||||
|
if(str.empty()) {
|
||||||
|
throw Exception("value empty");
|
||||||
|
}
|
||||||
|
std::string::const_iterator it = str.begin();
|
||||||
|
while (it != str.end() && (std::isdigit(*it) || *it == '.')) ++it;
|
||||||
|
if(it != str.end()) {
|
||||||
|
throw Exception("value not a number");
|
||||||
|
}
|
||||||
|
return base::convert_to<Number>(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PixlyFormat::onLoad(FileOp* fop)
|
||||||
|
{
|
||||||
|
png_uint_32 width, height, y;
|
||||||
|
unsigned int sig_read = 0;
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
int bit_depth, color_type, interlace_type;
|
||||||
|
int pass, number_passes;
|
||||||
|
png_bytepp rows_pointer;
|
||||||
|
PixelFormat pixelFormat;
|
||||||
|
|
||||||
|
FileHandle handle(open_file_with_exception(base::replace_extension(fop->filename(),"png"), "rb"));
|
||||||
|
FILE* fp = handle.get();
|
||||||
|
|
||||||
|
/* Create and initialize the png_struct with the desired error handler
|
||||||
|
* functions. If you want to use the default stderr and longjump method,
|
||||||
|
* you can supply NULL for the last three parameters. We also supply the
|
||||||
|
* the compiler header file version, so that we know if the application
|
||||||
|
* was compiled with a compatible version of the library
|
||||||
|
*/
|
||||||
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)fop,
|
||||||
|
report_png_error, report_png_error);
|
||||||
|
if (png_ptr == NULL) {
|
||||||
|
fop->setError("png_create_read_struct\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate/initialize the memory for image information. */
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (info_ptr == NULL) {
|
||||||
|
fop->setError("png_create_info_struct\n");
|
||||||
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set error handling if you are using the setjmp/longjmp method (this is
|
||||||
|
* the normal method of doing things with libpng).
|
||||||
|
*/
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||||
|
fop->setError("Error reading PNG file\n");
|
||||||
|
/* Free all of the memory associated with the png_ptr and info_ptr */
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
/* If we get here, we had a problem reading the file */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the input control if you are using standard C streams */
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
|
||||||
|
/* If we have already read some of the signature */
|
||||||
|
png_set_sig_bytes(png_ptr, sig_read);
|
||||||
|
|
||||||
|
/* The call to png_read_info() gives us all of the information from the
|
||||||
|
* PNG file before the first IDAT (image data chunk).
|
||||||
|
*/
|
||||||
|
png_read_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
|
||||||
|
&interlace_type, NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
/* Set up the data transformations you want. Note that these are all
|
||||||
|
* optional. Only call them if you want/need them. Many of the
|
||||||
|
* transformations only work on specific types of images, and many
|
||||||
|
* are mutually exclusive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tell libpng to strip 16 bit/color files down to 8 bits/color */
|
||||||
|
png_set_strip_16(png_ptr);
|
||||||
|
|
||||||
|
/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
|
||||||
|
* byte into separate bytes (useful for paletted and grayscale images).
|
||||||
|
*/
|
||||||
|
png_set_packing(png_ptr);
|
||||||
|
|
||||||
|
/* Turn on interlace handling. REQUIRED if you are not using
|
||||||
|
* png_read_image(). To see how to handle interlacing passes,
|
||||||
|
* see the png_read_row() method below:
|
||||||
|
*/
|
||||||
|
number_passes = png_set_interlace_handling(png_ptr);
|
||||||
|
|
||||||
|
/* Optional call to gamma correct and add the background to the palette
|
||||||
|
* and update info structure.
|
||||||
|
*/
|
||||||
|
png_read_update_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
/* create the output image */
|
||||||
|
switch (png_get_color_type(png_ptr, info_ptr)) {
|
||||||
|
|
||||||
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||||
|
fop->sequenceSetHasAlpha(true);
|
||||||
|
pixelFormat = IMAGE_RGB;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fop->setError("Pixly loader requires a RGBA PNG\n)");
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imageWidth = png_get_image_width(png_ptr, info_ptr);
|
||||||
|
int imageHeight = png_get_image_height(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
// Allocate the memory to hold the image using the fields of info_ptr.
|
||||||
|
rows_pointer = (png_bytepp)png_malloc(png_ptr, sizeof(png_bytep) * height);
|
||||||
|
for (y = 0; y < height; y++)
|
||||||
|
rows_pointer[y] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
|
||||||
|
|
||||||
|
for (pass = 0; pass < number_passes; pass++) {
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
png_read_rows(png_ptr, rows_pointer+y, nullptr, 1);
|
||||||
|
|
||||||
|
fop->setProgress(
|
||||||
|
0.5 * ((double)((double)pass + (double)(y+1) / (double)(height))
|
||||||
|
/ (double)number_passes));
|
||||||
|
|
||||||
|
if (fop->isStop())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
try {
|
||||||
|
XmlDocumentRef doc = open_xml(fop->filename());
|
||||||
|
TiXmlHandle xml(doc.get());
|
||||||
|
fop->setProgress(0.75);
|
||||||
|
|
||||||
|
TiXmlElement* xmlAnim = check(xml.FirstChild("PixlyAnimation").ToElement());
|
||||||
|
double version = check_number<double>(xmlAnim->Attribute("version"));
|
||||||
|
if(version < 1.5) {
|
||||||
|
throw Exception("version 1.5 or above required");
|
||||||
|
}
|
||||||
|
|
||||||
|
TiXmlElement* xmlInfo = check(xmlAnim->FirstChild("Info"))->ToElement();
|
||||||
|
|
||||||
|
int layerCount = check_number<int>(xmlInfo->Attribute("layerCount"));
|
||||||
|
int frameWidth = check_number<int>(xmlInfo->Attribute("frameWidth"));
|
||||||
|
int frameHeight = check_number<int>(xmlInfo->Attribute("frameHeight"));
|
||||||
|
|
||||||
|
UniquePtr<Sprite> sprite(new Sprite(IMAGE_RGB, frameWidth, frameHeight, 0));
|
||||||
|
|
||||||
|
TiXmlElement* xmlFrames = check(xmlAnim->FirstChild("Frames"))->ToElement();
|
||||||
|
int imageCount = check_number<int>(xmlFrames->Attribute("length"));
|
||||||
|
|
||||||
|
if(layerCount <= 0 || imageCount <= 0) {
|
||||||
|
throw Exception("No cels found");
|
||||||
|
}
|
||||||
|
|
||||||
|
int frameCount = imageCount / layerCount;
|
||||||
|
sprite->setTotalFrames(frame_t(frameCount));
|
||||||
|
sprite->setDurationForAllFrames(200);
|
||||||
|
|
||||||
|
for(int i=0; i<layerCount; i++) {
|
||||||
|
sprite->folder()->addLayer(new LayerImage(sprite));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> visible(layerCount, 0);
|
||||||
|
|
||||||
|
TiXmlElement* xmlFrame = check(xmlFrames->FirstChild("Frame"))->ToElement();
|
||||||
|
while (xmlFrame) {
|
||||||
|
TiXmlElement* xmlRegion = check(xmlFrame->FirstChild("Region"))->ToElement();
|
||||||
|
TiXmlElement* xmlIndex = check(xmlFrame->FirstChild("Index"))->ToElement();
|
||||||
|
|
||||||
|
int index = check_number<int>(xmlIndex->Attribute("linear"));
|
||||||
|
frame_t frame(index / layerCount);
|
||||||
|
LayerIndex layer_index(index % layerCount);
|
||||||
|
Layer *layer = sprite->indexToLayer(layer_index);
|
||||||
|
|
||||||
|
const char * duration = xmlFrame->Attribute("duration");
|
||||||
|
if(duration) {
|
||||||
|
sprite->setFrameDuration(frame, base::convert_to<int>(std::string(duration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
visible[(int)layer_index] += (int)(std::string(check(xmlFrame->Attribute("visible"),"false")) == "true");
|
||||||
|
|
||||||
|
int x0 = check_number<int>(xmlRegion->Attribute("x"));
|
||||||
|
int y0 = check_number<int>(xmlRegion->Attribute("y")); // inverted
|
||||||
|
|
||||||
|
if(y0 < 0 || y0 + frameHeight > imageHeight || x0 < 0 || x0 + frameWidth > imageWidth) {
|
||||||
|
throw Exception("looking for cels outside the bounds of the PNG");
|
||||||
|
}
|
||||||
|
|
||||||
|
base::UniquePtr<Cel> cel;
|
||||||
|
ImageRef image(Image::create(pixelFormat, frameWidth, frameHeight));
|
||||||
|
|
||||||
|
// Convert rows_pointer into the doc::Image
|
||||||
|
for (int y = 0; y < frameHeight; y++) {
|
||||||
|
// RGB_ALPHA
|
||||||
|
uint8_t* src_address = rows_pointer[imageHeight-1 - y0 - (frameHeight-1) + y] + (x0 * 4);
|
||||||
|
uint32_t* dst_address = (uint32_t*)image->getPixelAddress(0, y);
|
||||||
|
unsigned int r, g, b, a;
|
||||||
|
|
||||||
|
for (int x=0; x<frameWidth; x++) {
|
||||||
|
r = *(src_address++);
|
||||||
|
g = *(src_address++);
|
||||||
|
b = *(src_address++);
|
||||||
|
a = *(src_address++);
|
||||||
|
*(dst_address++) = rgba(r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cel.reset(new Cel(frame, image));
|
||||||
|
static_cast<LayerImage*>(layer)->addCel(cel);
|
||||||
|
cel.release();
|
||||||
|
|
||||||
|
xmlFrame = xmlFrame->NextSiblingElement();
|
||||||
|
fop->setProgress(0.75 + 0.25 * ((float)(index+1) / (float)imageCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<layerCount; i++) {
|
||||||
|
LayerIndex layer_index(i);
|
||||||
|
Layer *layer = sprite->indexToLayer(layer_index);
|
||||||
|
layer->setVisible(visible[i] > frameCount/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fop->createDocument(sprite);
|
||||||
|
sprite.release();
|
||||||
|
}
|
||||||
|
catch(Exception &e) {
|
||||||
|
fop->setError((std::string("Pixly file format: ")+std::string(e.what())+"\n").c_str());
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
png_free(png_ptr, rows_pointer[y]);
|
||||||
|
}
|
||||||
|
png_free(png_ptr, rows_pointer);
|
||||||
|
|
||||||
|
// Clean up after the read, and free any memory allocated
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SAVE
|
||||||
|
bool PixlyFormat::onSave(FileOp* fop)
|
||||||
|
{
|
||||||
|
const Sprite* sprite = fop->document()->sprite();
|
||||||
|
|
||||||
|
auto it = sprite->folder()->getLayerBegin(),
|
||||||
|
end = sprite->folder()->getLayerEnd();
|
||||||
|
for (; it != end; ++it) { // layers
|
||||||
|
Layer *layer = *it;
|
||||||
|
if (!layer->isImage()) {
|
||||||
|
fop->setError("Pixly .anim file format does not support layer folders\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height, y;
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
png_bytepp rows_pointer;
|
||||||
|
int color_type = 0;
|
||||||
|
|
||||||
|
/* open the file */
|
||||||
|
FileHandle xml_handle(open_file_with_exception(fop->filename(), "wb"));
|
||||||
|
FILE* xml_fp = xml_handle.get();
|
||||||
|
|
||||||
|
FileHandle handle(open_file_with_exception(base::replace_extension(fop->filename(),"png"), "wb"));
|
||||||
|
FILE* fp = handle.get();
|
||||||
|
|
||||||
|
/* Create and initialize the png_struct with the desired error handler
|
||||||
|
* functions. If you want to use the default stderr and longjump method,
|
||||||
|
* you can supply NULL for the last three parameters. We also check that
|
||||||
|
* the library version is compatible with the one used at compile time,
|
||||||
|
* in case we are using dynamically linked libraries. REQUIRED.
|
||||||
|
*/
|
||||||
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)fop,
|
||||||
|
report_png_error, report_png_error);
|
||||||
|
if (png_ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate/initialize the image information data. REQUIRED */
|
||||||
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (info_ptr == NULL) {
|
||||||
|
png_destroy_write_struct(&png_ptr, NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set error handling. REQUIRED if you aren't supplying your own
|
||||||
|
* error handling functions in the png_create_write_struct() call.
|
||||||
|
*/
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||||
|
/* If we get here, we had a problem reading the file */
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up the output control if you are using standard C streams */
|
||||||
|
png_init_io(png_ptr, fp);
|
||||||
|
|
||||||
|
/* Set the image information here. Width and height are up to 2^31,
|
||||||
|
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
||||||
|
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
||||||
|
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
||||||
|
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
||||||
|
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
||||||
|
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
||||||
|
*/
|
||||||
|
|
||||||
|
int frameCount = sprite->totalFrames();
|
||||||
|
int layerCount = sprite->folder()->getLayersCount();
|
||||||
|
int imageCount = frameCount * layerCount;
|
||||||
|
|
||||||
|
int frameWidth = sprite->width();
|
||||||
|
int frameHeight = sprite->height();
|
||||||
|
|
||||||
|
int squareSide = (int)ceil(sqrt(imageCount));
|
||||||
|
|
||||||
|
width = squareSide * frameWidth;
|
||||||
|
height = squareSide * frameHeight;
|
||||||
|
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
||||||
|
|
||||||
|
png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,
|
||||||
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||||
|
|
||||||
|
/* Write the file header information. */
|
||||||
|
png_write_info(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
/* pack pixels into bytes */
|
||||||
|
png_set_packing(png_ptr);
|
||||||
|
|
||||||
|
rows_pointer = (png_bytepp)png_malloc(png_ptr, sizeof(png_bytep) * height);
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
size_t size = png_get_rowbytes(png_ptr, info_ptr);
|
||||||
|
rows_pointer[y] = (png_bytep)png_malloc(png_ptr, size);
|
||||||
|
memset(rows_pointer[y], 0, size);
|
||||||
|
fop->setProgress(0.1 * (double)(y+1) / (double)height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO XXX beware the required typo on Pixly xml: "totalCollumns" (sic)
|
||||||
|
fprintf(xml_fp,
|
||||||
|
"<PixlyAnimation version=\"1.5\">\n"
|
||||||
|
"\t<Info "
|
||||||
|
"sheetWidth=\"%d\" sheetHeight=\"%d\" "
|
||||||
|
"totalCollumns=\"%d\" totalRows=\"%d\" "
|
||||||
|
"frameWidth=\"%d\" frameHeight=\"%d\" "
|
||||||
|
"layerCount=\"%d\"/>\n"
|
||||||
|
"\t<Frames length=\"%d\">\n",
|
||||||
|
width, height,
|
||||||
|
squareSide, squareSide,
|
||||||
|
frameWidth, frameHeight,
|
||||||
|
layerCount, imageCount
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) {
|
||||||
|
auto it = sprite->folder()->getLayerBegin(),
|
||||||
|
end = sprite->folder()->getLayerEnd();
|
||||||
|
for (; it != end; ++it, ++index) { // layers
|
||||||
|
Layer *layer = *it;
|
||||||
|
|
||||||
|
int col = index % squareSide;
|
||||||
|
int row = index / squareSide;
|
||||||
|
|
||||||
|
int x0 = col * frameWidth;
|
||||||
|
int y0 = row * frameHeight; // inverted
|
||||||
|
|
||||||
|
int duration = sprite->frameDuration(frame);
|
||||||
|
|
||||||
|
// TODO XXX beware the required typo on Pixly xml: "collumn" (sic)
|
||||||
|
fprintf(xml_fp,
|
||||||
|
"\t\t<Frame duration=\"%d\" visible=\"%s\">\n"
|
||||||
|
"\t\t\t<Region x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n"
|
||||||
|
"\t\t\t<Index linear=\"%d\" collumn=\"%d\" row=\"%d\"/>\n"
|
||||||
|
"\t\t</Frame>\n",
|
||||||
|
duration, layer->isVisible() ? "true" : "false",
|
||||||
|
x0, y0, frameWidth, frameHeight,
|
||||||
|
index, col, row
|
||||||
|
);
|
||||||
|
|
||||||
|
const Cel* cel = layer->cel(frame);
|
||||||
|
if (cel) {
|
||||||
|
const Image* image = cel->image();
|
||||||
|
if (image) {
|
||||||
|
int celX = cel->x();
|
||||||
|
int celY = cel->y();
|
||||||
|
int celWidth = image->width();
|
||||||
|
int celHeight = image->height();
|
||||||
|
|
||||||
|
for (y = 0; y < celHeight; y++) {
|
||||||
|
/* RGB_ALPHA */
|
||||||
|
uint32_t* src_address = (uint32_t*)image->getPixelAddress(0, y);
|
||||||
|
uint8_t* dst_address = rows_pointer[(height - 1) - y0 - (frameHeight - 1) + celY + y] + ((x0 + celX) * 4);
|
||||||
|
int x;
|
||||||
|
unsigned int c;
|
||||||
|
|
||||||
|
for (x=0; x<celWidth; x++) {
|
||||||
|
c = *(src_address++);
|
||||||
|
*(dst_address++) = rgba_getr(c);
|
||||||
|
*(dst_address++) = rgba_getg(c);
|
||||||
|
*(dst_address++) = rgba_getb(c);
|
||||||
|
*(dst_address++) = rgba_geta(c);
|
||||||
|
} // x
|
||||||
|
} // y
|
||||||
|
|
||||||
|
} // image
|
||||||
|
} // cel
|
||||||
|
|
||||||
|
fop->setProgress(0.1 + 0.8 * (double)(index+1) / (double)imageCount);
|
||||||
|
|
||||||
|
} // layer
|
||||||
|
} // frame
|
||||||
|
|
||||||
|
fprintf(xml_fp,
|
||||||
|
"\t</Frames>\n"
|
||||||
|
"</PixlyAnimation>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* If you are only writing one row at a time, this works */
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
/* write the line */
|
||||||
|
png_write_rows(png_ptr, rows_pointer+y, 1);
|
||||||
|
|
||||||
|
fop->setProgress(0.9 + 0.1 * (double)(y+1) / (double)height);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
png_free(png_ptr, rows_pointer[y]);
|
||||||
|
}
|
||||||
|
png_free(png_ptr, rows_pointer);
|
||||||
|
|
||||||
|
/* It is REQUIRED to call this to finish writing the rest of the file */
|
||||||
|
png_write_end(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
/* clean up after the write, and free any memory allocated */
|
||||||
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||||
|
|
||||||
|
/* all right */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace app
|
@ -12,10 +12,16 @@
|
|||||||
#include "app/ini_file.h"
|
#include "app/ini_file.h"
|
||||||
|
|
||||||
#include "app/resource_finder.h"
|
#include "app/resource_finder.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/split_string.h"
|
#include "base/split_string.h"
|
||||||
#include "base/string.h"
|
#include "base/string.h"
|
||||||
#include "cfg/cfg.h"
|
#include "cfg/cfg.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include "she/logger.h"
|
||||||
|
#include "she/system.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#endif
|
#endif
|
||||||
@ -34,9 +40,48 @@ ConfigModule::ConfigModule()
|
|||||||
{
|
{
|
||||||
ResourceFinder rf;
|
ResourceFinder rf;
|
||||||
rf.includeUserDir("aseprite.ini");
|
rf.includeUserDir("aseprite.ini");
|
||||||
|
|
||||||
|
// getFirstOrCreateDefault() will create the Aseprite directory
|
||||||
|
// inside the OS configuration folder (~/.config/aseprite/, etc.).
|
||||||
std::string fn = rf.getFirstOrCreateDefault();
|
std::string fn = rf.getFirstOrCreateDefault();
|
||||||
|
|
||||||
#ifndef _WIN32 // Migrate the configuration file to the new location in Unix-like systems
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
// On OS X we migrate from ~/.config/aseprite/* -> "~/Library/Application Support/Aseprite/*"
|
||||||
|
if (!base::is_file(fn)) {
|
||||||
|
try {
|
||||||
|
std::string new_dir = base::get_file_path(fn);
|
||||||
|
|
||||||
|
// Now we try to move all old configuration files into the new
|
||||||
|
// directory.
|
||||||
|
ResourceFinder old_rf;
|
||||||
|
old_rf.includeHomeDir(".config/aseprite/aseprite.ini");
|
||||||
|
std::string old_config_fn = old_rf.defaultFilename();
|
||||||
|
if (base::is_file(old_config_fn)) {
|
||||||
|
std::string old_dir = base::get_file_path(old_config_fn);
|
||||||
|
for (std::string old_fn : base::list_files(old_dir)) {
|
||||||
|
std::string from = base::join_path(old_dir, old_fn);
|
||||||
|
std::string to = base::join_path(new_dir, old_fn);
|
||||||
|
base::move_file(from, to);
|
||||||
|
}
|
||||||
|
base::remove_directory(old_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Something failed
|
||||||
|
catch (const std::exception& ex) {
|
||||||
|
std::string err = "Error in configuration migration: ";
|
||||||
|
err += ex.what();
|
||||||
|
|
||||||
|
auto system = she::instance();
|
||||||
|
if (system && system->logger())
|
||||||
|
system->logger()->logError(err.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif !defined(_WIN32)
|
||||||
|
|
||||||
|
// On Linux we migrate the old configuration file name
|
||||||
|
// (.asepriterc -> ~/.config/aseprite/aseprite.ini)
|
||||||
{
|
{
|
||||||
ResourceFinder old_rf;
|
ResourceFinder old_rf;
|
||||||
old_rf.includeHomeDir(".asepriterc");
|
old_rf.includeHomeDir(".asepriterc");
|
||||||
@ -44,6 +89,7 @@ ConfigModule::ConfigModule()
|
|||||||
if (base::is_file(old_fn))
|
if (base::is_file(old_fn))
|
||||||
base::move_file(old_fn, fn);
|
base::move_file(old_fn, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
set_config_file(fn.c_str());
|
set_config_file(fn.c_str());
|
||||||
|
@ -160,6 +160,14 @@ void ResourceFinder::includeUserDir(const char* filename)
|
|||||||
includeHomeDir(filename);
|
includeHomeDir(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif __APPLE__
|
||||||
|
|
||||||
|
// $HOME/Library/Application Support/Aseprite/filename
|
||||||
|
addPath(
|
||||||
|
base::join_path(
|
||||||
|
base::join_path(base::get_lib_app_support_path(), PACKAGE),
|
||||||
|
filename).c_str());
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// $HOME/.config/aseprite/filename
|
// $HOME/.config/aseprite/filename
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
@ -19,6 +19,7 @@ namespace app {
|
|||||||
class PointShape {
|
class PointShape {
|
||||||
public:
|
public:
|
||||||
virtual ~PointShape() { }
|
virtual ~PointShape() { }
|
||||||
|
virtual bool isPixel() { return false; }
|
||||||
virtual bool isFloodFill() { return false; }
|
virtual bool isFloodFill() { return false; }
|
||||||
virtual bool isSpray() { return false; }
|
virtual bool isSpray() { return false; }
|
||||||
virtual void preparePointShape(ToolLoop* loop) { }
|
virtual void preparePointShape(ToolLoop* loop) { }
|
||||||
|
@ -21,6 +21,8 @@ public:
|
|||||||
|
|
||||||
class PixelPointShape : public PointShape {
|
class PixelPointShape : public PointShape {
|
||||||
public:
|
public:
|
||||||
|
bool isPixel() override { return true; }
|
||||||
|
|
||||||
void transformPoint(ToolLoop* loop, int x, int y) override {
|
void transformPoint(ToolLoop* loop, int x, int y) override {
|
||||||
doInkHline(x, y, x, loop);
|
doInkHline(x, y, x, loop);
|
||||||
}
|
}
|
||||||
|
@ -465,6 +465,17 @@ class ContextBar::InkShadesField : public HBox {
|
|||||||
parent()->parent()->layout();
|
parent()->parent()->layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateShadeFromColorBarPicks() {
|
||||||
|
auto colorBar = ColorBar::instance();
|
||||||
|
if (!colorBar)
|
||||||
|
return;
|
||||||
|
|
||||||
|
doc::PalettePicks picks;
|
||||||
|
colorBar->getPaletteView()->getSelectedEntries(picks);
|
||||||
|
if (picks.picks() >= 2)
|
||||||
|
onChangeColorBarSelection();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void onChangeColorBarSelection() {
|
void onChangeColorBarSelection() {
|
||||||
@ -710,6 +721,10 @@ public:
|
|||||||
m_shade.setShade(shade);
|
m_shade.setShade(shade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateShadeFromColorBarPicks() {
|
||||||
|
m_shade.updateShadeFromColorBarPicks();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onShowMenu() {
|
void onShowMenu() {
|
||||||
loadShades();
|
loadShades();
|
||||||
@ -1553,6 +1568,8 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
|||||||
m_spraySpeed->setValue(toolPref->spray.speed());
|
m_spraySpeed->setValue(toolPref->spray.speed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool updateShade = (!m_inkShades->isVisible() && hasInkShades);
|
||||||
|
|
||||||
m_eyedropperField->updateFromPreferences(preferences.eyedropper);
|
m_eyedropperField->updateFromPreferences(preferences.eyedropper);
|
||||||
m_autoSelectLayer->setSelected(preferences.editor.autoSelectLayer());
|
m_autoSelectLayer->setSelected(preferences.editor.autoSelectLayer());
|
||||||
|
|
||||||
@ -1631,6 +1648,10 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
|||||||
(isPaint || isEffect || hasSelectOptions));
|
(isPaint || isEffect || hasSelectOptions));
|
||||||
m_symmetry->updateWithCurrentDocument();
|
m_symmetry->updateWithCurrentDocument();
|
||||||
|
|
||||||
|
// Update ink shades with the current selected palette entries
|
||||||
|
if (updateShade)
|
||||||
|
m_inkShades->updateShadeFromColorBarPicks();
|
||||||
|
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,7 +523,8 @@ bool DocumentView::onClear(Context* ctx)
|
|||||||
transaction.execute(new cmd::ClearMask(writer.cel()));
|
transaction.execute(new cmd::ClearMask(writer.cel()));
|
||||||
|
|
||||||
// If the cel wasn't deleted by cmd::ClearMask, we trim it.
|
// If the cel wasn't deleted by cmd::ClearMask, we trim it.
|
||||||
if (writer.cel())
|
if (writer.cel() &&
|
||||||
|
writer.cel()->layer()->isTransparent())
|
||||||
transaction.execute(new cmd::TrimCel(writer.cel()));
|
transaction.execute(new cmd::TrimCel(writer.cel()));
|
||||||
|
|
||||||
if (visibleMask &&
|
if (visibleMask &&
|
||||||
|
@ -305,17 +305,19 @@ void BrushPreview::generateBoundaries()
|
|||||||
m_brushGen == brush->gen())
|
m_brushGen == brush->gen())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isFloodfill = m_editor->getCurrentEditorTool()->getPointShape(0)->isFloodFill();
|
bool isOnePixel =
|
||||||
|
(m_editor->getCurrentEditorTool()->getPointShape(0)->isPixel() ||
|
||||||
|
m_editor->getCurrentEditorTool()->getPointShape(0)->isFloodFill());
|
||||||
Image* brushImage = brush->image();
|
Image* brushImage = brush->image();
|
||||||
int w = (isFloodfill ? 1: brushImage->width());
|
int w = (isOnePixel ? 1: brushImage->width());
|
||||||
int h = (isFloodfill ? 1: brushImage->height());
|
int h = (isOnePixel ? 1: brushImage->height());
|
||||||
|
|
||||||
m_brushGen = brush->gen();
|
m_brushGen = brush->gen();
|
||||||
m_brushWidth = w;
|
m_brushWidth = w;
|
||||||
m_brushHeight = h;
|
m_brushHeight = h;
|
||||||
|
|
||||||
ImageRef mask;
|
ImageRef mask;
|
||||||
if (isFloodfill) {
|
if (isOnePixel) {
|
||||||
mask.reset(Image::create(IMAGE_BITMAP, w, w));
|
mask.reset(Image::create(IMAGE_BITMAP, w, w));
|
||||||
mask->putPixel(0, 0, (color_t)1);
|
mask->putPixel(0, 0, (color_t)1);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ void DrawingState::initToolLoop(Editor* editor, MouseMessage* msg)
|
|||||||
m_toolLoop->getLayer(),
|
m_toolLoop->getLayer(),
|
||||||
m_toolLoop->getFrame(),
|
m_toolLoop->getFrame(),
|
||||||
m_toolLoop->getDstImage(),
|
m_toolLoop->getDstImage(),
|
||||||
|
m_toolLoop->getCelOrigin(),
|
||||||
(m_toolLoop->getLayer() &&
|
(m_toolLoop->getLayer() &&
|
||||||
m_toolLoop->getLayer()->isImage() ?
|
m_toolLoop->getLayer()->isImage() ?
|
||||||
static_cast<LayerImage*>(m_toolLoop->getLayer())->blendMode():
|
static_cast<LayerImage*>(m_toolLoop->getLayer())->blendMode():
|
||||||
|
@ -256,9 +256,10 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start "moving pixels" loop
|
// Start "moving pixels" loop. Here we check only for left-click as
|
||||||
if (editor->isInsideSelection() && (msg->left() ||
|
// right-click can be used to deselect/subtract selection, so we
|
||||||
msg->right())) {
|
// should drop the selection in this later case.
|
||||||
|
if (editor->isInsideSelection() && msg->left()) {
|
||||||
// In case that the user is pressing the copy-selection keyboard shortcut.
|
// In case that the user is pressing the copy-selection keyboard shortcut.
|
||||||
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
|
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
|
||||||
if ((customization) &&
|
if ((customization) &&
|
||||||
|
@ -182,7 +182,8 @@ void PixelsMovement::cutMask()
|
|||||||
m_transaction.execute(new cmd::ClearMask(writer.cel()));
|
m_transaction.execute(new cmd::ClearMask(writer.cel()));
|
||||||
|
|
||||||
ASSERT(writer.cel());
|
ASSERT(writer.cel());
|
||||||
if (writer.cel())
|
if (writer.cel() &&
|
||||||
|
writer.cel()->layer()->isTransparent())
|
||||||
m_transaction.execute(new cmd::TrimCel(writer.cel()));
|
m_transaction.execute(new cmd::TrimCel(writer.cel()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,26 +98,18 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
|
|||||||
|
|
||||||
case WHEEL_FG:
|
case WHEEL_FG:
|
||||||
{
|
{
|
||||||
int newIndex = 0;
|
int lastIndex = get_current_palette()->size()-1;
|
||||||
if (ColorBar::instance()->getFgColor().getType() == app::Color::IndexType) {
|
int newIndex = ColorBar::instance()->getFgColor().getIndex() + int(dz);
|
||||||
int lastIndex = get_current_palette()->size()-1;
|
newIndex = MID(0, newIndex, lastIndex);
|
||||||
|
|
||||||
newIndex = ColorBar::instance()->getFgColor().getIndex() + int(dz);
|
|
||||||
newIndex = MID(0, newIndex, lastIndex);
|
|
||||||
}
|
|
||||||
ColorBar::instance()->setFgColor(app::Color::fromIndex(newIndex));
|
ColorBar::instance()->setFgColor(app::Color::fromIndex(newIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WHEEL_BG:
|
case WHEEL_BG:
|
||||||
{
|
{
|
||||||
int newIndex = 0;
|
int lastIndex = get_current_palette()->size()-1;
|
||||||
if (ColorBar::instance()->getBgColor().getType() == app::Color::IndexType) {
|
int newIndex = ColorBar::instance()->getBgColor().getIndex() + int(dz);
|
||||||
int lastIndex = get_current_palette()->size()-1;
|
newIndex = MID(0, newIndex, lastIndex);
|
||||||
|
|
||||||
newIndex = ColorBar::instance()->getBgColor().getIndex() + int(dz);
|
|
||||||
newIndex = MID(0, newIndex, lastIndex);
|
|
||||||
}
|
|
||||||
ColorBar::instance()->setBgColor(app::Color::fromIndex(newIndex));
|
ColorBar::instance()->setBgColor(app::Color::fromIndex(newIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -225,7 +225,8 @@ void cut(ContextWriter& writer)
|
|||||||
transaction.execute(new cmd::ClearMask(writer.cel()));
|
transaction.execute(new cmd::ClearMask(writer.cel()));
|
||||||
|
|
||||||
ASSERT(writer.cel());
|
ASSERT(writer.cel());
|
||||||
if (writer.cel())
|
if (writer.cel() &&
|
||||||
|
writer.cel()->layer()->isTransparent())
|
||||||
transaction.execute(new cmd::TrimCel(writer.cel()));
|
transaction.execute(new cmd::TrimCel(writer.cel()));
|
||||||
|
|
||||||
transaction.execute(new cmd::DeselectMask(writer.document()));
|
transaction.execute(new cmd::DeselectMask(writer.document()));
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "app/app.h"
|
#include "app/app.h"
|
||||||
#include "app/cmd/add_cel.h"
|
#include "app/cmd/add_cel.h"
|
||||||
#include "app/cmd/clear_cel.h"
|
#include "app/cmd/clear_cel.h"
|
||||||
|
#include "app/cmd/copy_region.h"
|
||||||
#include "app/cmd/patch_cel.h"
|
#include "app/cmd/patch_cel.h"
|
||||||
#include "app/context.h"
|
#include "app/context.h"
|
||||||
#include "app/document.h"
|
#include "app/document.h"
|
||||||
@ -198,12 +199,22 @@ void ExpandCelCanvas::commit()
|
|||||||
regionToPatch = &reduced;
|
regionToPatch = &reduced;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_transaction.execute(
|
if (m_layer->isBackground()) {
|
||||||
new cmd::PatchCel(
|
m_transaction.execute(
|
||||||
m_cel,
|
new cmd::CopyRegion(
|
||||||
m_dstImage.get(),
|
m_cel->image(),
|
||||||
*regionToPatch,
|
m_dstImage.get(),
|
||||||
m_bounds.origin()));
|
*regionToPatch,
|
||||||
|
m_bounds.origin()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_transaction.execute(
|
||||||
|
new cmd::PatchCel(
|
||||||
|
m_cel,
|
||||||
|
m_dstImage.get(),
|
||||||
|
*regionToPatch,
|
||||||
|
m_bounds.origin()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
|
@ -70,14 +70,20 @@ doc::Image* render_text(const std::string& fontfile, int fontsize,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doc::put_pixel(
|
int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t);
|
||||||
image, ximg, yimg,
|
if (output_alpha) {
|
||||||
doc::rgba_blender_normal(
|
doc::color_t output_color =
|
||||||
doc::get_pixel(image, ximg, yimg),
|
|
||||||
doc::rgba(doc::rgba_getr(color),
|
doc::rgba(doc::rgba_getr(color),
|
||||||
doc::rgba_getg(color),
|
doc::rgba_getg(color),
|
||||||
doc::rgba_getb(color),
|
doc::rgba_getb(color),
|
||||||
MUL_UN8(doc::rgba_geta(color), alpha, t))));
|
output_alpha);
|
||||||
|
|
||||||
|
doc::put_pixel(
|
||||||
|
image, ximg, yimg,
|
||||||
|
doc::rgba_blender_normal(
|
||||||
|
doc::get_pixel(image, ximg, yimg),
|
||||||
|
output_color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -59,6 +59,11 @@ set(BASE_SOURCES
|
|||||||
trim_string.cpp
|
trim_string.cpp
|
||||||
version.cpp)
|
version.cpp)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(BASE_SOURCES ${BASE_SOURCES}
|
||||||
|
fs_osx.mm)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(BASE_SOURCES ${BASE_SOURCES}
|
set(BASE_SOURCES ${BASE_SOURCES}
|
||||||
win32_exception.cpp)
|
win32_exception.cpp)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Base Library
|
// Aseprite Base Library
|
||||||
// Copyright (c) 2001-2013, 2015 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -36,6 +36,9 @@ namespace base {
|
|||||||
std::string get_app_path();
|
std::string get_app_path();
|
||||||
std::string get_temp_path();
|
std::string get_temp_path();
|
||||||
std::string get_user_docs_folder();
|
std::string get_user_docs_folder();
|
||||||
|
#if __APPLE__
|
||||||
|
std::string get_lib_app_support_path();
|
||||||
|
#endif
|
||||||
|
|
||||||
// If the given filename is a relative path, it converts the
|
// If the given filename is a relative path, it converts the
|
||||||
// filename to an absolute one.
|
// filename to an absolute one.
|
||||||
|
29
src/base/fs_osx.mm
Normal file
29
src/base/fs_osx.mm
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Aseprite Base Library
|
||||||
|
// Copyright (c) 2016 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 <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
std::string get_lib_app_support_path()
|
||||||
|
{
|
||||||
|
NSArray* dirs = NSSearchPathForDirectoriesInDomains(
|
||||||
|
NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||||
|
if (dirs) {
|
||||||
|
NSString* dir = [dirs firstObject];
|
||||||
|
if (dir)
|
||||||
|
return std::string([dir UTF8String]);
|
||||||
|
}
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
@ -85,6 +85,30 @@ std::string get_file_extension(const std::string& filename)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string replace_extension(const std::string& filename, const std::string& extension)
|
||||||
|
{
|
||||||
|
std::string::const_reverse_iterator rit;
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
// search for the first dot from the end of the string
|
||||||
|
for (rit=filename.rbegin(); rit!=filename.rend(); ++rit) {
|
||||||
|
if (is_path_separator(*rit))
|
||||||
|
return result;
|
||||||
|
else if (*rit == '.')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rit != filename.rend()) {
|
||||||
|
std::copy(filename.begin(), std::string::const_iterator(rit.base()),
|
||||||
|
std::back_inserter(result));
|
||||||
|
std::copy(extension.begin(), extension.end(),
|
||||||
|
std::back_inserter(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string get_file_title(const std::string& filename)
|
std::string get_file_title(const std::string& filename)
|
||||||
{
|
{
|
||||||
std::string::const_reverse_iterator rit;
|
std::string::const_reverse_iterator rit;
|
||||||
|
@ -28,6 +28,9 @@ namespace base {
|
|||||||
// Returns the extension of the file name (without the dot).
|
// Returns the extension of the file name (without the dot).
|
||||||
std::string get_file_extension(const std::string& filename);
|
std::string get_file_extension(const std::string& filename);
|
||||||
|
|
||||||
|
// Returns the whole path with another extension.
|
||||||
|
std::string replace_extension(const std::string& filename, const std::string& extension);
|
||||||
|
|
||||||
// Returns the file name without path and without extension.
|
// Returns the file name without path and without extension.
|
||||||
std::string get_file_title(const std::string& filename);
|
std::string get_file_title(const std::string& filename);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2001-2015 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -33,7 +33,7 @@ namespace doc {
|
|||||||
frame_t frame() const { return m_frame; }
|
frame_t frame() const { return m_frame; }
|
||||||
int x() const { return m_data->position().x; }
|
int x() const { return m_data->position().x; }
|
||||||
int y() const { return m_data->position().y; }
|
int y() const { return m_data->position().y; }
|
||||||
gfx::Point position() const { return m_data->position(); }
|
const gfx::Point& position() const { return m_data->position(); }
|
||||||
int opacity() const { return m_data->opacity(); }
|
int opacity() const { return m_data->opacity(); }
|
||||||
|
|
||||||
LayerImage* layer() const { return m_layer; }
|
LayerImage* layer() const { return m_layer; }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2001-2015 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -62,11 +62,12 @@ namespace doc {
|
|||||||
bool isImage() const { return type() == ObjectType::LayerImage; }
|
bool isImage() const { return type() == ObjectType::LayerImage; }
|
||||||
bool isFolder() const { return type() == ObjectType::LayerFolder; }
|
bool isFolder() const { return type() == ObjectType::LayerFolder; }
|
||||||
|
|
||||||
bool isBackground() const { return hasFlags(LayerFlags::Background); }
|
bool isBackground() const { return hasFlags(LayerFlags::Background); }
|
||||||
bool isVisible() const { return hasFlags(LayerFlags::Visible); }
|
bool isTransparent() const { return !hasFlags(LayerFlags::Background); }
|
||||||
bool isEditable() const { return hasFlags(LayerFlags::Editable); }
|
bool isVisible() const { return hasFlags(LayerFlags::Visible); }
|
||||||
bool isMovable() const { return !hasFlags(LayerFlags::LockMove); }
|
bool isEditable() const { return hasFlags(LayerFlags::Editable); }
|
||||||
bool isContinuous() const { return hasFlags(LayerFlags::Continuous); }
|
bool isMovable() const { return !hasFlags(LayerFlags::LockMove); }
|
||||||
|
bool isContinuous() const { return hasFlags(LayerFlags::Continuous); }
|
||||||
|
|
||||||
void setBackground(bool state) { switchFlags(LayerFlags::Background, state); }
|
void setBackground(bool state) { switchFlags(LayerFlags::Background, state); }
|
||||||
void setVisible (bool state) { switchFlags(LayerFlags::Visible, state); }
|
void setVisible (bool state) { switchFlags(LayerFlags::Visible, state); }
|
||||||
|
@ -78,6 +78,11 @@ Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, co
|
|||||||
return trim;
|
return trim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image* crop_image(const Image* image, const gfx::Rect& bounds, color_t bg, const ImageBufferPtr& buffer)
|
||||||
|
{
|
||||||
|
return crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h, bg, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void rotate_image(const Image* src, Image* dst, int angle)
|
void rotate_image(const Image* src, Image* dst, int angle)
|
||||||
{
|
{
|
||||||
ASSERT(src);
|
ASSERT(src);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2001-2015 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -26,6 +26,7 @@ namespace doc {
|
|||||||
void copy_image(Image* dst, const Image* src);
|
void copy_image(Image* dst, const Image* src);
|
||||||
void copy_image(Image* dst, const Image* src, int x, int y);
|
void copy_image(Image* dst, const Image* src, int x, int y);
|
||||||
Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
||||||
|
Image* crop_image(const Image* image, const gfx::Rect& bounds, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
||||||
void rotate_image(const Image* src, Image* dst, int angle);
|
void rotate_image(const Image* src, Image* dst, int angle);
|
||||||
|
|
||||||
void draw_hline(Image* image, int x1, int y, int x2, color_t c);
|
void draw_hline(Image* image, int x1, int y, int x2, color_t c);
|
||||||
|
@ -484,12 +484,16 @@ void Render::setBgCheckedSize(const gfx::Size& size)
|
|||||||
m_bgCheckedSize = size;
|
m_bgCheckedSize = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render::setPreviewImage(const Layer* layer, frame_t frame,
|
void Render::setPreviewImage(const Layer* layer,
|
||||||
Image* image, BlendMode blendMode)
|
const frame_t frame,
|
||||||
|
const Image* image,
|
||||||
|
const gfx::Point& pos,
|
||||||
|
const BlendMode blendMode)
|
||||||
{
|
{
|
||||||
m_selectedLayer = layer;
|
m_selectedLayer = layer;
|
||||||
m_selectedFrame = frame;
|
m_selectedFrame = frame;
|
||||||
m_previewImage = image;
|
m_previewImage = image;
|
||||||
|
m_previewPos = pos;
|
||||||
m_previewBlendMode = blendMode;
|
m_previewBlendMode = blendMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,7 +661,8 @@ void Render::renderSprite(
|
|||||||
dstImage,
|
dstImage,
|
||||||
m_previewImage,
|
m_previewImage,
|
||||||
m_sprite->palette(frame),
|
m_sprite->palette(frame),
|
||||||
0, 0,
|
m_previewPos.x,
|
||||||
|
m_previewPos.y,
|
||||||
area,
|
area,
|
||||||
compositeImage,
|
compositeImage,
|
||||||
255,
|
255,
|
||||||
@ -842,24 +847,27 @@ void Render::renderLayer(
|
|||||||
const Cel* cel = layer->cel(frame);
|
const Cel* cel = layer->cel(frame);
|
||||||
if (cel) {
|
if (cel) {
|
||||||
Palette* pal = m_sprite->palette(frame);
|
Palette* pal = m_sprite->palette(frame);
|
||||||
Image* src_image;
|
const Image* celImage;
|
||||||
|
gfx::Point celPos;
|
||||||
|
|
||||||
// Is the 'm_previewImage' set to be used with this layer?
|
// Is the 'm_previewImage' set to be used with this layer?
|
||||||
if ((m_previewImage) &&
|
if ((m_previewImage) &&
|
||||||
(m_selectedLayer == layer) &&
|
(m_selectedLayer == layer) &&
|
||||||
(m_selectedFrame == frame)) {
|
(m_selectedFrame == frame)) {
|
||||||
src_image = m_previewImage;
|
celImage = m_previewImage;
|
||||||
|
celPos = m_previewPos;
|
||||||
|
|
||||||
ASSERT(src_image->pixelFormat() == cel->image()->pixelFormat());
|
ASSERT(celImage->pixelFormat() == cel->image()->pixelFormat());
|
||||||
}
|
}
|
||||||
// If not, we use the original cel-image from the images' stock
|
// If not, we use the original cel-image from the images' stock
|
||||||
else {
|
else {
|
||||||
src_image = cel->image();
|
celImage = cel->image();
|
||||||
|
celPos = cel->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_image) {
|
if (celImage) {
|
||||||
const LayerImage* imgLayer = static_cast<const LayerImage*>(layer);
|
const LayerImage* imgLayer = static_cast<const LayerImage*>(layer);
|
||||||
BlendMode layerBlendMode =
|
const BlendMode layerBlendMode =
|
||||||
(blendMode == BlendMode::UNSPECIFIED ?
|
(blendMode == BlendMode::UNSPECIFIED ?
|
||||||
imgLayer->blendMode():
|
imgLayer->blendMode():
|
||||||
blendMode);
|
blendMode);
|
||||||
@ -875,7 +883,7 @@ void Render::renderLayer(
|
|||||||
opacity = MUL_UN8(opacity, imgLayer->opacity(), t);
|
opacity = MUL_UN8(opacity, imgLayer->opacity(), t);
|
||||||
opacity = MUL_UN8(opacity, m_globalOpacity, t);
|
opacity = MUL_UN8(opacity, m_globalOpacity, t);
|
||||||
|
|
||||||
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
|
ASSERT(celImage->maskColor() == m_sprite->transparentColor());
|
||||||
|
|
||||||
// Draw parts outside the "m_extraCel" area
|
// Draw parts outside the "m_extraCel" area
|
||||||
if (drawExtra && m_extraType == ExtraType::PATCH) {
|
if (drawExtra && m_extraType == ExtraType::PATCH) {
|
||||||
@ -885,17 +893,17 @@ void Render::renderLayer(
|
|||||||
|
|
||||||
for (auto rc : originalAreas) {
|
for (auto rc : originalAreas) {
|
||||||
renderCel(
|
renderCel(
|
||||||
image, src_image, pal,
|
image, celImage, pal, celPos,
|
||||||
cel, gfx::Clip(area.dst.x+rc.x-area.src.x,
|
gfx::Clip(area.dst.x+rc.x-area.src.x,
|
||||||
area.dst.y+rc.y-area.src.y, rc), compositeImage,
|
area.dst.y+rc.y-area.src.y, rc), compositeImage,
|
||||||
opacity, layerBlendMode);
|
opacity, layerBlendMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Draw the whole cel
|
// Draw the whole cel
|
||||||
else {
|
else {
|
||||||
renderCel(
|
renderCel(
|
||||||
image, src_image, pal,
|
image, celImage, pal,
|
||||||
cel, area, compositeImage,
|
celPos, area, compositeImage,
|
||||||
opacity, layerBlendMode);
|
opacity, layerBlendMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -927,7 +935,7 @@ void Render::renderLayer(
|
|||||||
renderCel(
|
renderCel(
|
||||||
image, m_extraImage,
|
image, m_extraImage,
|
||||||
m_sprite->palette(frame),
|
m_sprite->palette(frame),
|
||||||
m_extraCel,
|
m_extraCel->position(),
|
||||||
gfx::Clip(area.dst.x+extraArea.x-area.src.x,
|
gfx::Clip(area.dst.x+extraArea.x-area.src.x,
|
||||||
area.dst.y+extraArea.y-area.src.y,
|
area.dst.y+extraArea.y-area.src.y,
|
||||||
extraArea),
|
extraArea),
|
||||||
@ -942,7 +950,7 @@ void Render::renderCel(
|
|||||||
Image* dst_image,
|
Image* dst_image,
|
||||||
const Image* cel_image,
|
const Image* cel_image,
|
||||||
const Palette* pal,
|
const Palette* pal,
|
||||||
const Cel* cel,
|
const gfx::Point& celPos,
|
||||||
const gfx::Clip& area,
|
const gfx::Clip& area,
|
||||||
const CompositeImageFunc compositeImage,
|
const CompositeImageFunc compositeImage,
|
||||||
const int opacity,
|
const int opacity,
|
||||||
@ -951,8 +959,8 @@ void Render::renderCel(
|
|||||||
renderImage(dst_image,
|
renderImage(dst_image,
|
||||||
cel_image,
|
cel_image,
|
||||||
pal,
|
pal,
|
||||||
cel->x(),
|
celPos.x,
|
||||||
cel->y(),
|
celPos.y,
|
||||||
area,
|
area,
|
||||||
compositeImage,
|
compositeImage,
|
||||||
opacity,
|
opacity,
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include "doc/color.h"
|
#include "doc/color.h"
|
||||||
#include "doc/frame.h"
|
#include "doc/frame.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
#include "gfx/fwd.h"
|
#include "gfx/point.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/onionskin_position.h"
|
||||||
@ -114,8 +114,11 @@ namespace render {
|
|||||||
|
|
||||||
// Sets the preview image. This preview image is an alternative
|
// Sets the preview image. This preview image is an alternative
|
||||||
// image to be used for the given layer/frame.
|
// image to be used for the given layer/frame.
|
||||||
void setPreviewImage(const Layer* layer, frame_t frame,
|
void setPreviewImage(const Layer* layer,
|
||||||
Image* image, BlendMode blendMode);
|
const frame_t frame,
|
||||||
|
const Image* image,
|
||||||
|
const gfx::Point& pos,
|
||||||
|
const BlendMode blendMode);
|
||||||
void removePreviewImage();
|
void removePreviewImage();
|
||||||
|
|
||||||
// Sets an extra cel/image to be drawn after the current
|
// Sets an extra cel/image to be drawn after the current
|
||||||
@ -191,7 +194,7 @@ namespace render {
|
|||||||
Image* dst_image,
|
Image* dst_image,
|
||||||
const Image* cel_image,
|
const Image* cel_image,
|
||||||
const Palette* pal,
|
const Palette* pal,
|
||||||
const Cel* cel,
|
const gfx::Point& celPos,
|
||||||
const gfx::Clip& area,
|
const gfx::Clip& area,
|
||||||
const CompositeImageFunc compositeImage,
|
const CompositeImageFunc compositeImage,
|
||||||
const int opacity,
|
const int opacity,
|
||||||
@ -224,7 +227,8 @@ namespace render {
|
|||||||
int m_globalOpacity;
|
int m_globalOpacity;
|
||||||
const Layer* m_selectedLayer;
|
const Layer* m_selectedLayer;
|
||||||
frame_t m_selectedFrame;
|
frame_t m_selectedFrame;
|
||||||
Image* m_previewImage;
|
const Image* m_previewImage;
|
||||||
|
gfx::Point m_previewPos;
|
||||||
BlendMode m_previewBlendMode;
|
BlendMode m_previewBlendMode;
|
||||||
OnionskinOptions m_onionskin;
|
OnionskinOptions m_onionskin;
|
||||||
};
|
};
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
|
||||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app;
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app;
|
||||||
- (void)applicationWillTerminate:(NSNotification*)notification;
|
- (void)applicationWillTerminate:(NSNotification*)notification;
|
||||||
|
- (void)applicationWillResignActive:(NSNotification*)notification;
|
||||||
|
- (void)applicationDidBecomeActive:(NSNotification*)notification;
|
||||||
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames;
|
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "she/event_queue.h"
|
#include "she/event_queue.h"
|
||||||
#include "she/osx/app.h"
|
#include "she/osx/app.h"
|
||||||
#include "she/osx/generate_drop_files.h"
|
#include "she/osx/generate_drop_files.h"
|
||||||
|
#include "she/osx/view.h"
|
||||||
#include "she/system.h"
|
#include "she/system.h"
|
||||||
|
|
||||||
@implementation OSXAppDelegate
|
@implementation OSXAppDelegate
|
||||||
@ -38,6 +39,20 @@
|
|||||||
she::queue_event(ev);
|
she::queue_event(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillResignActive:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
NSEvent* event = [NSApp currentEvent];
|
||||||
|
if (event != nil)
|
||||||
|
[OSXView updateKeyFlags:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationDidBecomeActive:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
NSEvent* event = [NSApp currentEvent];
|
||||||
|
if (event != nil)
|
||||||
|
[OSXView updateKeyFlags:event];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames
|
- (BOOL)application:(NSApplication*)app openFiles:(NSArray*)filenames
|
||||||
{
|
{
|
||||||
generate_drop_files_from_nsarray(filenames);
|
generate_drop_files_from_nsarray(filenames);
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
- (void)keyDown:(NSEvent*)event;
|
- (void)keyDown:(NSEvent*)event;
|
||||||
- (void)keyUp:(NSEvent*)event;
|
- (void)keyUp:(NSEvent*)event;
|
||||||
- (void)flagsChanged:(NSEvent*)event;
|
- (void)flagsChanged:(NSEvent*)event;
|
||||||
|
+ (void)updateKeyFlags:(NSEvent*)event;
|
||||||
- (void)mouseEntered:(NSEvent*)event;
|
- (void)mouseEntered:(NSEvent*)event;
|
||||||
- (void)mouseMoved:(NSEvent*)event;
|
- (void)mouseMoved:(NSEvent*)event;
|
||||||
- (void)mouseExited:(NSEvent*)event;
|
- (void)mouseExited:(NSEvent*)event;
|
||||||
|
@ -188,7 +188,11 @@ bool is_key_pressed(KeyScancode scancode)
|
|||||||
- (void)flagsChanged:(NSEvent*)event
|
- (void)flagsChanged:(NSEvent*)event
|
||||||
{
|
{
|
||||||
[super flagsChanged:event];
|
[super flagsChanged:event];
|
||||||
|
[OSXView updateKeyFlags:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)updateKeyFlags:(NSEvent*)event
|
||||||
|
{
|
||||||
static int lastFlags = 0;
|
static int lastFlags = 0;
|
||||||
static int flags[] = {
|
static int flags[] = {
|
||||||
NSShiftKeyMask,
|
NSShiftKeyMask,
|
||||||
|
@ -51,20 +51,22 @@ void clear_keyboard_buffer()
|
|||||||
extern int app_main(int argc, char* argv[]);
|
extern int app_main(int argc, char* argv[]);
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
extern int __argc;
|
||||||
LPSTR lpCmdLine, int nCmdShow) {
|
extern wchar_t** __wargv;
|
||||||
int argc = 0;
|
|
||||||
LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc);
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||||
|
PWSTR lpCmdLine, int nCmdShow) {
|
||||||
|
int argc = __argc;
|
||||||
char** argv;
|
char** argv;
|
||||||
if (argvW && argc > 0) {
|
if (__wargv && argc > 0) {
|
||||||
argv = new char*[argc];
|
argv = new char*[argc];
|
||||||
for (int i=0; i<argc; ++i)
|
for (int i=0; i<argc; ++i)
|
||||||
argv[i] = base_strdup(base::to_utf8(std::wstring(argvW[i])).c_str());
|
argv[i] = base_strdup(base::to_utf8(std::wstring(__wargv[i])).c_str());
|
||||||
LocalFree(argvW);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
argv = new char*[1];
|
argv = new char*[1];
|
||||||
argv[0] = base_strdup("");
|
argv[0] = base_strdup("");
|
||||||
|
argc = 1;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user