Merge branch 'master' into beta

This commit is contained in:
David Capello 2016-07-06 15:46:22 -03:00
commit 36ffc8d2bd
46 changed files with 1074 additions and 236 deletions

View File

@ -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" />

View File

@ -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>

View File

@ -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)

View File

@ -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)

View File

@ -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(

View File

@ -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)

View File

@ -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.

View File

@ -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 (...) {

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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());

View 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

View File

@ -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());

View File

@ -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

View File

@ -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) { }

View File

@ -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);
} }

View File

@ -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();
} }

View File

@ -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 &&

View File

@ -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);
} }

View File

@ -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():

View File

@ -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) &&

View File

@ -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()));
} }
} }

View File

@ -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;

View File

@ -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()));

View File

@ -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);

View File

@ -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));
}
} }
} }
}); });

View File

@ -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)

View File

@ -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
View 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

View File

@ -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;

View File

@ -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);

View File

@ -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; }

View File

@ -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); }

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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;
}; };

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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[]) {