Save grid bounds inside .aseprite files and doc::Sprite (fix #688)

This commit is contained in:
David Capello 2019-10-10 15:08:59 -03:00
parent 86ef6979e2
commit 30b2585037
35 changed files with 322 additions and 61 deletions

View File

@ -394,7 +394,7 @@
</section>
<section id="grid">
<option id="snap" type="bool" default="false" />
<option id="bounds" type="gfx::Rect" default="gfx::Rect(0, 0, 16, 16)" />
<option id="bounds" type="gfx::Rect" default="doc::Sprite::DefaultGridBounds()" />
<option id="color" type="app::Color" default="app::Color::fromRgb(0, 0, 255)" />
<option id="opacity" type="int" default="160" />
<option id="auto_opacity" type="bool" default="true" />

View File

@ -71,7 +71,12 @@ A 128-byte header (same as FLC/FLI header, but with other magic number):
BYTE Pixel width (pixel ratio is "pixel width/pixel height").
If this or pixel height field is zero, pixel ratio is 1:1
BYTE Pixel height
BYTE[92] For future (set to zero)
SHORT X position of the grid
SHORT Y position of the grid
WORD Grid width (zero if there is no grid, grid size
is 16x16 on Aseprite by default)
WORD Grid height (zero if there is no grid)
BYTE[84] For future (set to zero)
## Frames

View File

@ -452,6 +452,7 @@ add_library(app-lib
cmd/set_cel_opacity.cpp
cmd/set_cel_position.cpp
cmd/set_frame_duration.cpp
cmd/set_grid_bounds.cpp
cmd/set_last_point.cpp
cmd/set_layer_blend_mode.cpp
cmd/set_layer_flags.cpp

View File

@ -0,0 +1,54 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/set_grid_bounds.h"
#include "app/doc.h"
#include "app/doc_event.h"
#include "app/doc_observer.h"
#include "doc/sprite.h"
namespace app {
namespace cmd {
using namespace doc;
SetGridBounds::SetGridBounds(Sprite* sprite, const gfx::Rect& bounds)
: WithSprite(sprite)
, m_oldBounds(sprite->gridBounds())
, m_newBounds(bounds)
{
}
void SetGridBounds::onExecute()
{
Sprite* spr = sprite();
spr->setGridBounds(m_newBounds);
spr->incrementVersion();
}
void SetGridBounds::onUndo()
{
Sprite* spr = sprite();
spr->setGridBounds(m_oldBounds);
spr->incrementVersion();
}
void SetGridBounds::onFireNotifications()
{
Sprite* sprite = this->sprite();
Doc* doc = static_cast<Doc*>(sprite->document());
DocEvent ev(doc);
ev.sprite(sprite);
doc->notify_observers<DocEvent&>(&DocObserver::onSpriteGridBoundsChanged, ev);
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,43 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_SET_GRID_BOUNDS_H_INCLUDED
#define APP_CMD_SET_GRID_BOUNDS_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_sprite.h"
#include "gfx/rect.h"
namespace doc {
class Sprite;
}
namespace app {
namespace cmd {
class SetGridBounds : public Cmd
, public WithSprite {
public:
SetGridBounds(doc::Sprite* sprite, const gfx::Rect& bounds);
protected:
void onExecute() override;
void onUndo() override;
void onFireNotifications() override;
size_t onMemSize() const override {
return sizeof(*this);
}
private:
gfx::Rect m_oldBounds;
gfx::Rect m_newBounds;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,6 +10,7 @@
#endif
#include "app/app.h"
#include "app/cmd/set_grid_bounds.h"
#include "app/commands/command.h"
#include "app/context.h"
#include "app/context_access.h"
@ -17,6 +19,7 @@
#include "app/load_widget.h"
#include "app/modules/editors.h"
#include "app/pref/preferences.h"
#include "app/tx.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "doc/document.h"
@ -25,6 +28,8 @@
#include "grid_settings.xml.h"
#include <algorithm>
namespace app {
using namespace ui;
@ -38,12 +43,12 @@ public:
protected:
bool onChecked(Context* ctx) override {
DocumentPreferences& docPref = Preferences::instance().document(ctx->activeDocument());
auto& docPref = Preferences::instance().document(ctx->activeDocument());
return docPref.grid.snap();
}
void onExecute(Context* ctx) override {
DocumentPreferences& docPref = Preferences::instance().document(ctx->activeDocument());
auto& docPref = Preferences::instance().document(ctx->activeDocument());
bool newValue = !docPref.grid.snap();
docPref.grid.snap(newValue);
@ -59,21 +64,23 @@ public:
protected:
bool onEnabled(Context* ctx) override {
return (ctx->activeDocument() &&
ctx->activeDocument()->isMaskVisible());
return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasVisibleMask);
}
void onExecute(Context* ctx) override {
const ContextReader reader(ctx);
const Doc* document = reader.document();
const Mask* mask(document->mask());
DocumentPreferences& docPref =
Preferences::instance().document(ctx->activeDocument());
ContextWriter writer(ctx, 500);
Doc* doc = writer.document();
const Mask* mask = doc->mask();
gfx::Rect newGrid = mask->bounds();
docPref.grid.bounds(mask->bounds());
Tx tx(writer.context(), friendlyName(), ModifyDocument);
tx(new cmd::SetGridBounds(writer.sprite(), newGrid));
tx.commit();
// Make grid visible
if (!docPref.show.grid())
auto& docPref = Preferences::instance().document(doc);
docPref.grid.bounds(newGrid);
if (!docPref.show.grid()) // Make grid visible
docPref.show.grid(true);
}
};
@ -101,8 +108,8 @@ void GridSettingsCommand::onExecute(Context* context)
{
gen::GridSettings window;
DocumentPreferences& docPref = Preferences::instance().document(context->activeDocument());
Rect bounds = docPref.grid.bounds();
Site site = context->activeSite();
Rect bounds = site.gridBounds();
window.gridX()->setTextf("%d", bounds.x);
window.gridY()->setTextf("%d", bounds.y);
@ -115,13 +122,17 @@ void GridSettingsCommand::onExecute(Context* context)
bounds.y = window.gridY()->textInt();
bounds.w = window.gridW()->textInt();
bounds.h = window.gridH()->textInt();
bounds.w = MAX(bounds.w, 1);
bounds.h = MAX(bounds.h, 1);
bounds.w = std::max(bounds.w, 1);
bounds.h = std::max(bounds.h, 1);
ContextWriter writer(context, 500);
Tx tx(context, friendlyName(), ModifyDocument);
tx(new cmd::SetGridBounds(site.sprite(), bounds));
tx.commit();
auto& docPref = Preferences::instance().document(site.document());
docPref.grid.bounds(bounds);
// Make grid visible
if (!docPref.show.grid())
if (!docPref.show.grid()) // Make grid visible
docPref.show.grid(true);
}
}

View File

@ -266,7 +266,7 @@ private:
gfx::Rect defBounds = m_docPref->importSpriteSheet.bounds();
if (defBounds.isEmpty())
defBounds = m_docPref->grid.bounds();
defBounds = m_document->sprite()->gridBounds();
onChangeRectangle(defBounds);
gfx::Size defPaddingBounds = m_docPref->importSpriteSheet.paddingBounds();

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -20,6 +21,7 @@
#include "app/job.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/recent_files.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
@ -191,12 +193,27 @@ void OpenFileCommand::onExecute(Context* context)
if (fop->hasError() && !fop->isStop())
console.printf(fop->error().c_str());
Doc* document = fop->document();
if (document) {
if (context->isUIAvailable())
Doc* doc = fop->document();
if (doc) {
if (context->isUIAvailable()) {
App::instance()->recentFiles()->addRecentFile(fop->filename().c_str());
auto& docPref = Preferences::instance().document(doc);
document->setContext(context);
if (fop->hasEmbeddedGridBounds() &&
!doc->sprite()->gridBounds().isEmpty()) {
// If the sprite contains the grid bounds inside, we put
// those grid bounds into the settings (e.g. useful to
// interact with old versions of Aseprite saving the grid
// bounds in the aseprite.ini file)
docPref.grid.bounds(doc->sprite()->gridBounds());
}
else {
// Get grid bounds from preferences
doc->sprite()->setGridBounds(docPref.grid.bounds());
}
}
doc->setContext(context);
}
else if (!fop->isStop())
unrecent = true;

View File

@ -10,9 +10,11 @@
#endif
#include "app/app.h"
#include "app/cmd/set_grid_bounds.h"
#include "app/commands/command.h"
#include "app/console.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/extensions.h"
#include "app/file/file.h"
#include "app/file_selector.h"
@ -23,6 +25,7 @@
#include "app/pref/preferences.h"
#include "app/recent_files.h"
#include "app/resource_finder.h"
#include "app/tx.h"
#include "app/ui/color_button.h"
#include "app/ui/pref_widget.h"
#include "app/ui/separator_in_view.h"
@ -163,7 +166,8 @@ class OptionsWindow : public app::gen::Options {
public:
OptionsWindow(Context* context, int& curSection)
: m_pref(Preferences::instance())
: m_context(context)
, m_pref(Preferences::instance())
, m_globPref(m_pref.document(nullptr))
, m_docPref(m_pref.document(context->activeDocument()))
, m_curPref(&m_docPref)
@ -586,6 +590,14 @@ public:
}
update_displays_color_profile_from_preferences();
// Change sprite grid bounds
if (m_context && m_context->activeDocument()) {
ContextWriter writer(m_context, 500);
Tx tx(m_context, Strings::commands_GridSettings(), ModifyDocument);
tx(new cmd::SetGridBounds(writer.sprite(), gridBounds()));
tx.commit();
}
m_curPref->show.grid(gridVisible()->isSelected());
m_curPref->grid.bounds(gridBounds());
m_curPref->grid.color(gridColor()->getColor());
@ -1390,6 +1402,7 @@ private:
return paths;
}
Context* m_context;
Preferences& m_pref;
DocumentPreferences& m_globPref;
DocumentPreferences& m_docPref;

View File

@ -16,7 +16,6 @@
#include "app/i18n/strings.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/snap_to_grid.h"
#include "app/tx.h"
#include "app/ui/editor/editor.h"
@ -75,7 +74,6 @@ void SelectTileCommand::onExecute(Context* ctx)
// Lock sprite
ContextWriter writer(ctx);
Doc* doc(writer.document());
auto& docPref = Preferences::instance().document(doc);
std::unique_ptr<Mask> mask(new Mask());
@ -83,7 +81,7 @@ void SelectTileCommand::onExecute(Context* ctx)
mask->copyFrom(doc->mask());
{
gfx::Rect gridBounds = docPref.grid.bounds();
gfx::Rect gridBounds = doc->sprite()->gridBounds();
gfx::Point pos = current_editor->screenToEditor(ui::get_mouse_position());
pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin);
gridBounds.setOrigin(pos);

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
@ -12,7 +13,6 @@
#include "app/commands/params.h"
#include "app/i18n/strings.h"
#include "app/pref/preferences.h"
#include "app/ui/doc_view.h"
#include "app/ui/editor/editor.h"
#include "app/ui_context.h"
@ -78,10 +78,9 @@ gfx::Point MoveThing::getDelta(Context* context) const
if (!view)
return delta;
DocumentPreferences& docPref = Preferences::instance().document(view->document());
Editor* editor = view->editor();
gfx::Rect vp = view->viewWidget()->viewportBounds();
gfx::Rect gridBounds = docPref.grid.bounds();
gfx::Rect gridBounds = view->document()->sprite()->gridBounds();
int pixels = 0;
switch (units) {

View File

@ -183,16 +183,17 @@ bool BackupObserver::saveDocData(Doc* doc)
m_session->restoreBackupDocById(doc->id(), nullptr));
DocDiff diff = compare_docs(doc, copy.get());
if (diff.anything) {
TRACE("RECO: Differences (%s/%s/%s/%s/%s/%s/%s)\n",
diff.canvas ? "canvas": "",
diff.totalFrames ? "totalFrames": "",
diff.frameDuration ? "frameDuration": "",
diff.frameTags ? "frameTags": "",
diff.palettes ? "palettes": "",
diff.layers ? "layers": "",
diff.cels ? "cels": "",
diff.images ? "images": "",
diff.colorProfiles ? "colorProfiles": "");
TRACEARGS("RECO: Differences:",
diff.canvas ? "canvas": "",
diff.totalFrames ? "totalFrames": "",
diff.frameDuration ? "frameDuration": "",
diff.tags ? "tags": "",
diff.palettes ? "palettes": "",
diff.layers ? "layers": "",
diff.cels ? "cels": "",
diff.images ? "images": "",
diff.colorProfiles ? "colorProfiles": "",
diff.gridBounds ? "gridBounds": "");
Doc* copyDoc = copy.release();
ui::execute_from_ui_thread(

View File

@ -345,6 +345,11 @@ private:
if (colorSpace)
spr->setColorSpace(colorSpace);
// Read grid bounds
gfx::Rect gridBounds = readGridBounds(s);
if (!gridBounds.isEmpty())
spr->setGridBounds(gridBounds);
return spr.release();
}
@ -364,6 +369,15 @@ private:
return colorSpace;
}
gfx::Rect readGridBounds(std::ifstream& s) {
gfx::Rect grid;
grid.x = (int16_t)read16(s);
grid.y = (int16_t)read16(s);
grid.w = read16(s);
grid.h = read16(s);
return grid;
}
// TODO could we use doc::read_layer() here?
Layer* readLayer(std::ifstream& s) {
LayerFlags flags = (LayerFlags)read32(s);

View File

@ -170,6 +170,17 @@ private:
// Color Space
writeColorSpace(s, spr->colorSpace());
// Grid bounds
writeGridBounds(s, spr->gridBounds());
return true;
}
bool writeGridBounds(std::ofstream& s, const gfx::Rect& grid) {
write16(s, (int16_t)grid.x);
write16(s, (int16_t)grid.y);
write16(s, grid.w);
write16(s, grid.h);
return true;
}

View File

@ -334,13 +334,13 @@ void DocApi::trimSprite(Sprite* sprite, const bool byGrid)
// TODO merge this code with the code in DocExporter::captureSamples()
if (byGrid) {
Doc* doc = m_document;
auto& docPref = Preferences::instance().document(doc);
const gfx::Rect& gridBounds = doc->sprite()->gridBounds();
gfx::Point posTopLeft =
snap_to_grid(docPref.grid.bounds(),
snap_to_grid(gridBounds,
bounds.origin(),
PreferSnapTo::FloorGrid);
gfx::Point posBottomRight =
snap_to_grid(docPref.grid.bounds(),
snap_to_grid(gridBounds,
bounds.point2(),
PreferSnapTo::CeilGrid);
bounds = gfx::Rect(posTopLeft, posBottomRight);

View File

@ -144,6 +144,11 @@ DocDiff compare_docs(const Doc* a,
diff.anything = diff.colorProfiles = true;
}
// Compare grid bounds
if (a->sprite()->gridBounds() != b->sprite()->gridBounds()) {
diff.anything = diff.gridBounds = true;
}
return diff;
}

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -22,6 +23,7 @@ namespace app {
bool cels : 1;
bool images : 1;
bool colorProfiles : 1;
bool gridBounds : 1;
DocDiff() :
anything(false),
@ -33,7 +35,8 @@ namespace app {
layers(false),
cels(false),
images(false),
colorProfiles(false) {
colorProfiles(false),
gridBounds(false) {
}
};

View File

@ -698,13 +698,13 @@ void DocExporter::captureSamples(Samples& samples)
if (m_trimCels) {
// TODO merge this code with the code in DocApi::trimSprite()
if (m_trimByGrid) {
auto& docPref = Preferences::instance().document(doc);
const gfx::Rect& gridBounds = doc->sprite()->gridBounds();
gfx::Point posTopLeft =
snap_to_grid(docPref.grid.bounds(),
snap_to_grid(gridBounds,
frameBounds.origin(),
PreferSnapTo::FloorGrid);
gfx::Point posBottomRight =
snap_to_grid(docPref.grid.bounds(),
snap_to_grid(gridBounds,
frameBounds.point2(),
PreferSnapTo::CeilGrid);
frameBounds = gfx::Rect(posTopLeft, posBottomRight);

View File

@ -46,6 +46,7 @@ namespace app {
virtual void onSpriteSizeChanged(DocEvent& ev) { }
virtual void onSpriteTransparentColorChanged(DocEvent& ev) { }
virtual void onSpritePixelRatioChanged(DocEvent& ev) { }
virtual void onSpriteGridBoundsChanged(DocEvent& ev) { }
virtual void onLayerNameChange(DocEvent& ev) { }
virtual void onLayerOpacityChange(DocEvent& ev) { }

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -208,6 +208,12 @@ bool AseFormat::onLoad(FileOp* fop)
fop->setEmbeddedColorProfile();
}
// Sprite grid bounds will be set to empty (instead of
// doc::Sprite::DefaultGridBounds()) if the file doesn't contain an
// embedded grid bounds.
if (!sprite->gridBounds().isEmpty())
fop->setEmbeddedGridBounds();
return true;
}
@ -371,6 +377,10 @@ static void ase_file_prepare_header(FILE* f, dio::AsepriteHeader* header, const
header->ncolors = sprite->palette(firstFrame)->size();
header->pixel_width = sprite->pixelRatio().w;
header->pixel_height = sprite->pixelRatio().h;
header->grid_x = sprite->gridBounds().x;
header->grid_y = sprite->gridBounds().y;
header->grid_width = sprite->gridBounds().w;
header->grid_height = sprite->gridBounds().h;
}
static void ase_file_write_header(FILE* f, dio::AsepriteHeader* header)
@ -394,6 +404,10 @@ static void ase_file_write_header(FILE* f, dio::AsepriteHeader* header)
fputw(header->ncolors, f);
fputc(header->pixel_width, f);
fputc(header->pixel_height, f);
fputw(header->grid_x, f);
fputw(header->grid_y, f);
fputw(header->grid_width, f);
fputw(header->grid_height, f);
fseek(f, header->pos+128, SEEK_SET);
}

View File

@ -1190,6 +1190,7 @@ FileOp::FileOp(FileOpType type,
, m_createPaletteFromRgba(false)
, m_ignoreEmpty(false)
, m_embeddedColorProfile(false)
, m_embeddedGridBounds(false)
{
if (config)
m_config = *config;

View File

@ -209,6 +209,9 @@ namespace app {
void setEmbeddedColorProfile() { m_embeddedColorProfile = true; }
bool hasEmbeddedColorProfile() const { return m_embeddedColorProfile; }
void setEmbeddedGridBounds() { m_embeddedGridBounds = true; }
bool hasEmbeddedGridBounds() const { return m_embeddedGridBounds; }
bool newBlend() const { return m_config.newBlend; }
private:
@ -243,6 +246,9 @@ namespace app {
// True if the file contained a color profile when it was loaded.
bool m_embeddedColorProfile;
// True if the file contained a the grid bounds inside.
bool m_embeddedGridBounds;
FileOpConfig m_config;
// Options

View File

@ -25,6 +25,7 @@
#include "doc/color_mode.h"
#include "doc/frame.h"
#include "doc/layer_list.h"
#include "doc/sprite.h"
#include "filters/tiled_mode.h"
#include "gfx/rect.h"
#include "render/onionskin_position.h"

View File

@ -10,6 +10,6 @@
// Increment this value if the scripting API is modified between two
// released Aseprite versions.
#define API_VERSION 6
#define API_VERSION 7
#endif

View File

@ -18,6 +18,7 @@
#include "app/cmd/remove_layer.h"
#include "app/cmd/remove_slice.h"
#include "app/cmd/remove_tag.h"
#include "app/cmd/set_grid_bounds.h"
#include "app/cmd/set_mask.h"
#include "app/cmd/set_sprite_size.h"
#include "app/cmd/set_transparent_color.h"
@ -735,6 +736,23 @@ int Sprite_get_bounds(lua_State* L)
return 1;
}
int Sprite_get_gridBounds(lua_State* L)
{
const auto sprite = get_docobj<Sprite>(L, 1);
push_obj<gfx::Rect>(L, sprite->gridBounds());
return 1;
}
int Sprite_set_gridBounds(lua_State* L)
{
auto sprite = get_docobj<Sprite>(L, 1);
const gfx::Rect bounds = convert_args_into_rect(L, 2);
Tx tx;
tx(new cmd::SetGridBounds(sprite, bounds));
tx.commit();
return 0;
}
const luaL_Reg Sprite_methods[] = {
{ "__eq", Sprite_eq },
{ "resize", Sprite_resize },
@ -784,6 +802,7 @@ const Property Sprite_properties[] = {
{ "backgroundLayer", Sprite_get_backgroundLayer, nullptr },
{ "transparentColor", Sprite_get_transparentColor, Sprite_set_transparentColor },
{ "bounds", Sprite_get_bounds, nullptr },
{ "gridBounds", Sprite_get_gridBounds, Sprite_set_gridBounds },
{ nullptr, nullptr, nullptr }
};

View File

@ -11,10 +11,12 @@
#include "app/site.h"
#include "app/pref/preferences.h"
#include "base/base.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "ui/system.h"
namespace app {
@ -77,4 +79,20 @@ void Site::range(const DocRange& range)
}
}
gfx::Rect Site::gridBounds() const
{
gfx::Rect bounds;
if (m_sprite) {
bounds = m_sprite->gridBounds();
if (!bounds.isEmpty())
return bounds;
}
if (ui::is_ui_thread()) {
bounds = Preferences::instance().document(m_document).grid.bounds();
if (!bounds.isEmpty())
return bounds;
}
return doc::Sprite::DefaultGridBounds();
}
} // namespace app

View File

@ -13,6 +13,7 @@
#include "doc/frame.h"
#include "doc/palette_picks.h"
#include "doc/selected_objects.h"
#include "gfx/fwd.h"
namespace doc {
class Cel;
@ -98,6 +99,8 @@ namespace app {
doc::Palette* palette() const;
doc::RgbMap* rgbMap() const;
gfx::Rect gridBounds() const;
private:
Focus m_focus;
Doc* m_document;

View File

@ -728,7 +728,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
// Draw the grid
if (m_docPref.show.grid()) {
gfx::Rect gridrc = m_docPref.grid.bounds();
gfx::Rect gridrc = m_sprite->gridBounds();
if (m_proj.applyX(gridrc.w) > 2 &&
m_proj.applyY(gridrc.h) > 2) {
int alpha = m_docPref.grid.opacity();
@ -740,9 +740,10 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
alpha = MID(0, alpha, 255);
}
if (alpha > 8)
drawGrid(g, enclosingRect, m_docPref.grid.bounds(),
if (alpha > 8) {
drawGrid(g, enclosingRect, gridrc,
m_docPref.grid.color(), alpha);
}
}
}
}

View File

@ -343,8 +343,7 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
if ((moveModifier & SnapToGridMovement) == SnapToGridMovement) {
// Snap the x1,y1 point to the grid.
gfx::Rect gridBounds = App::instance()
->preferences().document(m_document).grid.bounds();
gfx::Rect gridBounds = m_document->sprite()->gridBounds();
gfx::PointF gridOffset(
snap_to_grid(
gridBounds,

View File

@ -596,7 +596,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
}
if (editor->docPref().show.grid()) {
auto gb = editor->docPref().grid.bounds();
auto gb = sprite->gridBounds();
if (!gb.isEmpty()) {
int col = int((std::floor(spritePos.x) - (gb.x % gb.w)) / gb.w);
int row = int((std::floor(spritePos.y) - (gb.y % gb.h)) / gb.h);

View File

@ -80,6 +80,7 @@ protected:
int m_opacity;
int m_tolerance;
bool m_contiguous;
gfx::Rect m_gridBounds;
gfx::Point m_celOrigin;
gfx::Point m_speed;
tools::ToolLoop::Button m_button;
@ -118,6 +119,7 @@ public:
, m_opacity(m_toolPref.opacity())
, m_tolerance(m_toolPref.tolerance())
, m_contiguous(m_toolPref.contiguous())
, m_gridBounds(site.gridBounds())
, m_button(button)
, m_ink(ink->clone())
, m_controller(controller)
@ -252,7 +254,7 @@ public:
== app::gen::PixelConnectivity::EIGHT_CONNECTED);
}
gfx::Rect getGridBounds() override { return m_docPref.grid.bounds(); }
gfx::Rect getGridBounds() override { return m_gridBounds; }
gfx::Point getCelOrigin() override { return m_celOrigin; }
void setSpeed(const gfx::Point& speed) override { m_speed = speed; }
gfx::Point getSpeed() override { return m_speed; }

View File

@ -72,6 +72,10 @@ struct AsepriteHeader {
uint16_t ncolors;
uint8_t pixel_width;
uint8_t pixel_height;
int16_t grid_x;
int16_t grid_y;
uint16_t grid_width;
uint16_t grid_height;
};
struct AsepriteFrameHeader {

View File

@ -58,6 +58,10 @@ bool AsepriteDecoder::decode()
// Set pixel ratio
sprite->setPixelRatio(doc::PixelRatio(header.pixel_width, header.pixel_height));
// Set grid bounds
sprite->setGridBounds(gfx::Rect(header.grid_x, header.grid_y,
header.grid_width, header.grid_height));
// Prepare variables for layer chunks
doc::Layer* last_layer = sprite->root();
doc::WithUserData* last_object_with_user_data = nullptr;
@ -252,6 +256,10 @@ bool AsepriteDecoder::readHeader(AsepriteHeader* header)
header->ncolors = read16();
header->pixel_width = read8();
header->pixel_height = read8();
header->grid_x = (int16_t)read16();
header->grid_y = (int16_t)read16();
header->grid_width = read16();
header->grid_height = read16();
if (header->ncolors == 0) // 0 means 256 (old .ase files)
header->ncolors = 256;
@ -588,8 +596,8 @@ doc::Cel* AsepriteDecoder::readCelChunk(doc::Sprite* sprite,
{
// Read chunk data
doc::layer_t layer_index = read16();
int x = ((short)read16());
int y = ((short)read16());
int x = ((int16_t)read16());
int y = ((int16_t)read16());
int opacity = read8();
int cel_type = read16();
readPadding(7);

View File

@ -34,6 +34,9 @@ namespace doc {
//////////////////////////////////////////////////////////////////////
// Constructors/Destructor
// static
gfx::Rect Sprite::DefaultGridBounds() { return gfx::Rect(0, 0, 16, 16); }
Sprite::Sprite(const ImageSpec& spec,
int ncolors)
: Object(ObjectType::Sprite)
@ -43,6 +46,7 @@ Sprite::Sprite(const ImageSpec& spec,
, m_frames(1)
, m_frlens(1, 100) // First frame with 100 msecs of duration
, m_root(new LayerGroup(this))
, m_gridBounds(Sprite::DefaultGridBounds())
, m_rgbMap(nullptr) // Initial RGB map
, m_tags(this)
, m_slices(this)

View File

@ -98,6 +98,10 @@ namespace doc {
color_t transparentColor() const { return m_spec.maskColor(); }
void setTransparentColor(color_t color);
static gfx::Rect DefaultGridBounds();
const gfx::Rect& gridBounds() const { return m_gridBounds; }
void setGridBounds(const gfx::Rect& rc) { m_gridBounds = rc; }
virtual int getMemSize() const override;
////////////////////////////////////////
@ -188,6 +192,7 @@ namespace doc {
std::vector<int> m_frlens; // duration per frame
PalettesList m_palettes; // list of palettes
LayerGroup* m_root; // main group of layers
gfx::Rect m_gridBounds; // grid settings
// Current rgb map
mutable RgbMap* m_rgbMap;