Merge branch 'dev' into timeline

This commit is contained in:
David Capello 2013-12-08 20:39:01 -03:00
commit 4218dd852d
10 changed files with 555 additions and 66 deletions

View File

@ -100,6 +100,7 @@ add_library(app-library
data_recovery.cpp
document.cpp
document_api.cpp
document_exporter.cpp
document_location.cpp
document_undo.cpp
documents.cpp

View File

@ -29,6 +29,7 @@
#include "app/commands/params.h"
#include "app/console.h"
#include "app/data_recovery.h"
#include "app/document_exporter.h"
#include "app/document_location.h"
#include "app/document_observer.h"
#include "app/drop_files.h"
@ -112,6 +113,7 @@ App::App(int argc, const char* argv[])
, m_legacy(NULL)
, m_isGui(false)
, m_isShell(false)
, m_exporter(NULL)
{
ASSERT(m_instance == NULL);
m_instance = this;
@ -124,6 +126,14 @@ App::App(int argc, const char* argv[])
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
m_files = options.files();
if (options.hasExporterParams()) {
m_exporter.reset(new DocumentExporter);
m_exporter->setDataFilename(options.data());
m_exporter->setTextureFilename(options.sheet());
m_exporter->setScale(options.scale());
}
// Register well-known image file types.
FileFormatsManager::instance().registerAllFormats();
@ -186,6 +196,7 @@ int App::run()
PRINTF("Processing options...\n");
{
UIContext* context = UIContext::instance();
Console console;
for (FileList::iterator
it = m_files.begin(),
@ -199,17 +210,29 @@ int App::run()
}
else {
// Mount and select the sprite
UIContext* context = UIContext::instance();
context->addDocument(document);
if (isGui()) {
// Recent file
// Add the given file in the argument as a "recent file" only
// if we are running in GUI mode. If the program is executed
// in batch mode this is not desirable.
if (isGui())
getRecentFiles()->addRecentFile(it->c_str());
}
// Add the document to the exporter.
if (m_exporter != NULL)
m_exporter->addDocument(document);
}
}
}
// Export
if (m_exporter != NULL) {
PRINTF("Exporting sheet...\n");
m_exporter->exportSheet();
m_exporter.reset(NULL);
}
// Run the GUI
if (isGui()) {
// Support to drop files from Windows explorer

View File

@ -39,6 +39,7 @@ namespace raster {
namespace app {
class Document;
class DocumentExporter;
class LegacyModules;
class LoggerModule;
class MainWindow;
@ -90,6 +91,7 @@ namespace app {
bool m_isShell;
base::UniquePtr<MainWindow> m_mainWindow;
FileList m_files;
base::UniquePtr<DocumentExporter> m_exporter;
};
void app_refresh_screen();

View File

@ -24,6 +24,7 @@
#include "base/path.h"
#include <cstdlib>
#include <iostream>
namespace app {
@ -35,10 +36,20 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_startUI(true)
, m_startShell(false)
, m_verbose(false)
, m_scale(1.0)
{
Option& palette = m_po.add("palette").requiresValue("GFXFILE").description("Use a specific palette by default");
Option& palette = m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default");
Option& shell = m_po.add("shell").description("Start an interactive console to execute scripts");
Option& batch = m_po.add("batch").description("Do not start the UI");
// Option& dataFormat = m_po.add("format").requiresValue("<name>").description("Select the format for the sprite sheet data");
Option& data = m_po.add("data").requiresValue("<filename>").description("File to store the sprite sheet metadata (.json file)");
//Option& textureFormat = m_po.add("texture-format").requiresValue("<name>").description("Output texture format.");
Option& sheet = m_po.add("sheet").requiresValue("<filename>").description("Image file to save the texture (.png)");
//Option& scale = m_po.add("scale").requiresValue("<float>").description("");
//Option& scaleMode = m_po.add("scale-mode").requiresValue("<mode>").description("Export the first given document to a JSON object");
//Option& splitLayers = m_po.add("split-layers").description("Specifies that each layer of the given file should be saved as a different image in the sheet.");
//Option& rotsprite = m_po.add("rotsprite").requiresValue("<angle1,angle2,...>").description("Specifies different angles to export the given image.");
//Option& merge = m_po.add("merge").requiresValue("<datafiles>").description("Merge several sprite sheets in one.");
Option& verbose = m_po.add("verbose").description("Explain what is being done (in stderr or a log file)");
Option& help = m_po.add("help").mnemonic('?').description("Display this help and exits");
Option& version = m_po.add("version").description("Output version information and exit");
@ -49,6 +60,13 @@ AppOptions::AppOptions(int argc, const char* argv[])
m_verbose = verbose.enabled();
m_paletteFileName = palette.value();
m_startShell = shell.enabled();
// m_dataFormat = dataFormat.value();
m_data = data.value();
// m_textureFormat = textureFormat.value();
m_sheet = sheet.value();
// if (scale.enabled())
// m_scale = std::strtod(scale.value().c_str(), NULL);
// m_scaleMode = scaleMode.value();
if (help.enabled()) {
showHelp();

View File

@ -41,6 +41,22 @@ public:
return m_po.values();
}
// Export options
const std::string& dataFormat() const { return m_dataFormat; }
const std::string& data() const { return m_data; }
const std::string& textureFormat() const { return m_textureFormat; }
const std::string& sheet() const { return m_sheet; }
const double scale() const { return m_scale; }
const std::string& scaleMode() const { return m_scaleMode; }
bool hasExporterParams() {
return
!m_dataFormat.empty() ||
!m_data.empty() ||
!m_textureFormat.empty() ||
!m_sheet.empty();
}
private:
void showHelp();
void showVersion();
@ -51,6 +67,13 @@ private:
bool m_startShell;
bool m_verbose;
std::string m_paletteFileName;
std::string m_dataFormat;
std::string m_data;
std::string m_textureFormat;
std::string m_sheet;
double m_scale;
std::string m_scaleMode;
};
} // namespace app

View File

@ -91,8 +91,7 @@ void DocumentApi::setSpriteSize(Sprite* sprite, int w, int h)
ASSERT(w > 0);
ASSERT(h > 0);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetSpriteSize(getObjects(), sprite));
sprite->setSize(w, h);
@ -151,8 +150,7 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
return;
// Change pixel format of the stock of images.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetStockPixelFormat(getObjects(), sprite->getStock()));
sprite->getStock()->setPixelFormat(newFormat);
@ -177,7 +175,7 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
}
// Change sprite's pixel format.
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetSpritePixelFormat(getObjects(), sprite));
sprite->setPixelFormat(newFormat);
@ -190,7 +188,7 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
// frame.
if (newFormat == IMAGE_GRAYSCALE) {
// Add undoers to revert all palette changes.
if (undo->isEnabled()) {
if (undoEnabled()) {
PalettesList palettes = sprite->getPalettes();
for (PalettesList::iterator it = palettes.begin(); it != palettes.end(); ++it) {
Palette* palette = *it;
@ -217,8 +215,7 @@ void DocumentApi::addFrame(Sprite* sprite, FrameNumber newFrame)
// Add the frame in the sprite structure, it adjusts the total
// number of frames in the sprite.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::AddFrame(getObjects(), m_document, sprite, newFrame));
sprite->addFrame(newFrame);
@ -303,8 +300,7 @@ void DocumentApi::removeFrame(Sprite* sprite, FrameNumber frame)
// Add undoers to restore the removed frame from the sprite (to
// restore the number and durations of frames).
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::RemoveFrame(getObjects(), m_document, sprite, frame));
// Remove the frame from the sprite. This is the low level
@ -355,8 +351,7 @@ void DocumentApi::setTotalFrames(Sprite* sprite, FrameNumber frames)
ASSERT(frames >= 1);
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetTotalFrames(getObjects(), m_document, sprite));
// Do the action.
@ -372,8 +367,7 @@ void DocumentApi::setTotalFrames(Sprite* sprite, FrameNumber frames)
void DocumentApi::setFrameDuration(Sprite* sprite, FrameNumber frame, int msecs)
{
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetFrameDuration(
getObjects(), sprite, frame));
@ -390,8 +384,7 @@ void DocumentApi::setFrameDuration(Sprite* sprite, FrameNumber frame, int msecs)
void DocumentApi::setConstantFrameRate(Sprite* sprite, int msecs)
{
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled()) {
if (undoEnabled()) {
for (FrameNumber fr(0); fr<sprite->getTotalFrames(); ++fr)
m_undoers->pushUndoer(new undoers::SetFrameDuration(
getObjects(), sprite, fr));
@ -489,8 +482,7 @@ void DocumentApi::addCel(LayerImage* layer, Cel* cel)
ASSERT(layer);
ASSERT(cel);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::AddCel(getObjects(), layer, cel));
layer->addCel(cel);
@ -531,8 +523,7 @@ void DocumentApi::removeCel(LayerImage* layer, Cel* cel)
if (!used)
removeImageFromStock(sprite, cel->getImage());
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::RemoveCel(getObjects(),
layer, cel));
@ -548,8 +539,7 @@ void DocumentApi::setCelFramePosition(Sprite* sprite, Cel* cel, FrameNumber fram
ASSERT(cel);
ASSERT(frame >= 0);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetCelFrame(getObjects(), cel));
cel->setFrame(frame);
@ -565,8 +555,7 @@ void DocumentApi::setCelPosition(Sprite* sprite, Cel* cel, int x, int y)
{
ASSERT(cel);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetCelPosition(getObjects(), cel));
cel->setPosition(x, y);
@ -615,8 +604,7 @@ LayerFolder* DocumentApi::newLayerFolder(Sprite* sprite)
void DocumentApi::addLayer(LayerFolder* folder, Layer* newLayer, Layer* afterThis)
{
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::AddLayer(getObjects(),
m_document, newLayer));
@ -643,8 +631,7 @@ void DocumentApi::removeLayer(Layer* layer)
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onRemoveLayer, ev);
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::RemoveLayer(getObjects(), m_document, layer));
// Do the action.
@ -655,8 +642,7 @@ void DocumentApi::removeLayer(Layer* layer)
void DocumentApi::configureLayerAsBackground(LayerImage* layer)
{
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled()) {
if (undoEnabled()) {
m_undoers->pushUndoer(new undoers::SetLayerFlags(getObjects(), layer));
m_undoers->pushUndoer(new undoers::SetLayerName(getObjects(), layer));
m_undoers->pushUndoer(new undoers::MoveLayer(getObjects(), layer));
@ -668,8 +654,7 @@ void DocumentApi::configureLayerAsBackground(LayerImage* layer)
void DocumentApi::restackLayerAfter(Layer* layer, Layer* afterThis)
{
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::MoveLayer(getObjects(), layer));
layer->getParent()->stackLayer(layer, afterThis);
@ -730,7 +715,6 @@ void DocumentApi::backgroundFromLayer(LayerImage* layer, int bgcolor)
ASSERT(layer->getSprite() != NULL);
ASSERT(layer->getSprite()->getBackgroundLayer() == NULL);
DocumentUndo* undo = m_document->getUndo();
Sprite* sprite = layer->getSprite();
// create a temporary image to draw each frame of the new
@ -765,7 +749,7 @@ void DocumentApi::backgroundFromLayer(LayerImage* layer, int bgcolor)
// same size of cel-image and bg-image
if (bg_image->getWidth() == cel_image->getWidth() &&
bg_image->getHeight() == cel_image->getHeight()) {
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::ImageArea(getObjects(),
cel_image, 0, 0, cel_image->getWidth(), cel_image->getHeight()));
@ -805,8 +789,7 @@ void DocumentApi::layerFromBackground(Layer* layer)
ASSERT(layer->getSprite() != NULL);
ASSERT(layer->getSprite()->getBackgroundLayer() != NULL);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled()) {
if (undoEnabled()) {
m_undoers->pushUndoer(new undoers::SetLayerFlags(getObjects(), layer));
m_undoers->pushUndoer(new undoers::SetLayerName(getObjects(), layer));
}
@ -821,8 +804,6 @@ void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor)
Image* cel_image;
Cel* cel;
DocumentUndo* undo = m_document->getUndo();
// Create a temporary image.
base::UniquePtr<Image> image_wrap(Image::create(sprite->getPixelFormat(),
sprite->getWidth(),
@ -851,7 +832,7 @@ void DocumentApi::flattenLayers(Sprite* sprite, int bgcolor)
ASSERT(cel_image != NULL);
// We have to save the current state of `cel_image' in the undo.
if (undo->isEnabled()) {
if (undoEnabled()) {
Dirty* dirty = new Dirty(cel_image, image, image->getBounds());
dirty->saveImagePixels(cel_image);
m_undoers->pushUndoer(new undoers::DirtyArea(
@ -895,8 +876,7 @@ int DocumentApi::addImageInStock(Sprite* sprite, Image* image)
int imageIndex = sprite->getStock()->addImage(image);
// Add undoers.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::AddImage(getObjects(),
sprite->getStock(), imageIndex));
@ -911,8 +891,7 @@ void DocumentApi::removeImageFromStock(Sprite* sprite, int imageIndex)
Image* image = sprite->getStock()->getImage(imageIndex);
ASSERT(image);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::RemoveImage(getObjects(),
sprite->getStock(), imageIndex));
@ -927,8 +906,7 @@ void DocumentApi::replaceStockImage(Sprite* sprite, int imageIndex, Image* newIm
ASSERT(oldImage);
// Replace the image in the stock.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::ReplaceImage(getObjects(),
sprite->getStock(), imageIndex));
@ -952,14 +930,13 @@ void DocumentApi::clearMask(Layer* layer, Cel* cel, int bgcolor)
return;
Mask* mask = m_document->getMask();
DocumentUndo* undo = m_document->getUndo();
// If the mask is empty or is not visible then we have to clear the
// entire image in the cel.
if (!m_document->isMaskVisible()) {
// If the layer is the background then we clear the image.
if (layer->isBackground()) {
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::ImageArea(getObjects(),
image, 0, 0, image->getWidth(), image->getHeight()));
@ -986,7 +963,7 @@ void DocumentApi::clearMask(Layer* layer, Cel* cel, int bgcolor)
if (x1 > x2 || y1 > y2)
return;
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::ImageArea(getObjects(),
image, x1, y1, x2-x1+1, y2-y1+1));
@ -1014,8 +991,7 @@ void DocumentApi::flipImage(Image* image,
raster::algorithm::FlipType flipType)
{
// Insert the undo operation.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled()) {
if (undoEnabled()) {
m_undoers->pushUndoer
(new undoers::FlipImage
(getObjects(), image, bounds, flipType));
@ -1033,8 +1009,7 @@ void DocumentApi::flipImageWithMask(Image* image, const Mask* mask, raster::algo
raster::algorithm::flip_image_with_mask(flippedImage, mask, flipType, bgcolor);
// Insert the undo operation.
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled()) {
if (undoEnabled()) {
base::UniquePtr<Dirty> dirty((new Dirty(image, flippedImage, image->getBounds())));
dirty->saveImagePixels(image);
@ -1061,8 +1036,7 @@ void DocumentApi::copyToCurrentMask(Mask* mask)
ASSERT(m_document->getMask());
ASSERT(mask);
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetMask(getObjects(),
m_document));
@ -1073,8 +1047,7 @@ void DocumentApi::setMaskPosition(int x, int y)
{
ASSERT(m_document->getMask());
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetMaskPosition(getObjects(), m_document));
m_document->getMask()->setOrigin(x, y);
@ -1083,8 +1056,7 @@ void DocumentApi::setMaskPosition(int x, int y)
void DocumentApi::deselectMask()
{
DocumentUndo* undo = m_document->getUndo();
if (undo->isEnabled())
if (undoEnabled())
m_undoers->pushUndoer(new undoers::SetMask(getObjects(),
m_document));
@ -1101,10 +1073,8 @@ void DocumentApi::setPalette(Sprite* sprite, FrameNumber frame, Palette* newPale
currentSpritePalette->countDiff(newPalette, &from, &to);
if (from >= 0 && to >= from) {
DocumentUndo* undo = m_document->getUndo();
// Add undo information to save the range of pal entries that will be modified.
if (undo->isEnabled()) {
if (undoEnabled()) {
m_undoers->pushUndoer
(new undoers::SetPaletteColors(getObjects(),
sprite, currentSpritePalette,
@ -1116,4 +1086,11 @@ void DocumentApi::setPalette(Sprite* sprite, FrameNumber frame, Palette* newPale
}
}
bool DocumentApi::undoEnabled()
{
return
m_undoers != NULL &&
m_document->getUndo()->isEnabled();
}
} // namespace app

View File

@ -118,6 +118,7 @@ namespace app {
void copyPreviousFrame(Layer* layer, FrameNumber frame);
void moveFrameBeforeLayer(Layer* layer, FrameNumber frame, FrameNumber beforeFrame);
void configureLayerAsBackground(LayerImage* layer);
bool undoEnabled();
Document* m_document;
undo::UndoersCollector* m_undoers;

View File

@ -0,0 +1,329 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/document_exporter.h"
#include "app/document.h"
#include "app/document_api.h"
#include "app/file/file.h"
#include "base/compiler_specific.h"
#include "base/path.h"
#include "base/unique_ptr.h"
#include "gfx/size.h"
#include "raster/cel.h"
#include "raster/dithering_method.h"
#include "raster/image.h"
#include "raster/layer.h"
#include "raster/palette.h"
#include "raster/sprite.h"
#include "raster/stock.h"
#include <cstdio>
#include <fstream>
#include <iostream>
using namespace raster;
namespace app {
class DocumentExporter::Sample {
public:
Sample(Document* document, Sprite* sprite,
FrameNumber frame, const std::string& filename) :
m_document(document),
m_sprite(sprite),
m_frame(frame),
m_filename(filename) {
}
Document* document() const { return m_document; }
Sprite* sprite() const { return m_sprite; }
FrameNumber frame() const { return m_frame; }
std::string filename() const { return m_filename; }
const gfx::Size& originalSize() const { return m_originalSize; }
const gfx::Rect& trimmedBounds() const { return m_trimmedBounds; }
const gfx::Rect& inTextureBounds() const { return m_inTextureBounds; }
bool trimmed() const {
return m_trimmedBounds.x > 0
|| m_trimmedBounds.y > 0
|| m_trimmedBounds.w != m_originalSize.w
|| m_trimmedBounds.h != m_originalSize.h;
}
void setOriginalSize(const gfx::Size& size) { m_originalSize = size; }
void setTrimmedBounds(const gfx::Rect& bounds) { m_trimmedBounds = bounds; }
void setInTextureBounds(const gfx::Rect& bounds) { m_inTextureBounds = bounds; }
private:
Document* m_document;
Sprite* m_sprite;
FrameNumber m_frame;
std::string m_filename;
gfx::Size m_originalSize;
gfx::Rect m_trimmedBounds;
gfx::Rect m_inTextureBounds;
};
class DocumentExporter::Samples {
public:
typedef std::list<Sample> List;
typedef List::iterator iterator;
typedef List::const_iterator const_iterator;
void addSample(const Sample& sample) {
m_samples.push_back(sample);
}
iterator begin() { return m_samples.begin(); }
iterator end() { return m_samples.end(); }
const_iterator begin() const { return m_samples.begin(); }
const_iterator end() const { return m_samples.end(); }
private:
List m_samples;
};
class DocumentExporter::LayoutSamples {
public:
virtual ~LayoutSamples() { }
virtual void layoutSamples(Samples& samples) = 0;
};
class DocumentExporter::SimpleLayoutSamples :
public DocumentExporter::LayoutSamples {
public:
void layoutSamples(Samples& samples) OVERRIDE {
const Sprite* oldSprite = NULL;
gfx::Point framePt(0, 0);
for (Samples::iterator it=samples.begin(), end=samples.end();
it != end; ++it) {
const Sprite* sprite = it->sprite();
gfx::Size size(sprite->getWidth(), sprite->getHeight());
it->setOriginalSize(size);
it->setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
it->setInTextureBounds(gfx::Rect(framePt, size));
// All frames of each sprite in one row.
if (oldSprite != NULL && oldSprite != it->sprite()) {
framePt.x = 0;
framePt.y += size.h;
}
else {
framePt.x += size.w;
}
oldSprite = it->sprite();
}
}
};
void DocumentExporter::exportSheet()
{
// We output the metadata to std::cout if the user didn't specify a file.
std::ofstream fos;
std::streambuf* osbuf;
if (m_dataFilename.empty())
osbuf = std::cout.rdbuf();
else {
fos.open(m_dataFilename.c_str(), std::ios::out);
osbuf = fos.rdbuf();
}
std::ostream os(osbuf);
// Steps for sheet construction:
// 1) Capture the samples (each sprite+frame pair)
Samples samples;
captureSamples(samples);
// 2) Layout those samples in a texture field.
SimpleLayoutSamples layout;
layout.layoutSamples(samples);
// 3) Create and render the texture.
base::UniquePtr<Document> textureDocument(
createEmptyTexture(samples));
Sprite* texture = textureDocument->getSprite();
Image* textureImage = texture->getStock()->getImage(
static_cast<LayerImage*>(texture->getFolder()->getFirstLayer())
->getCel(FrameNumber(0))->getImage());
renderTexture(samples, textureImage);
// Save the metadata.
createDataFile(samples, os, textureImage);
// Save the image files.
if (!m_textureFilename.empty()) {
textureDocument->setFilename(m_textureFilename.c_str());
save_document(textureDocument.get());
}
}
void DocumentExporter::captureSamples(Samples& samples)
{
std::vector<char> buf(32);
for (std::vector<Document*>::iterator
it = m_documents.begin(),
end = m_documents.end(); it != end; ++it) {
Document* document = *it;
Sprite* sprite = document->getSprite();
for (FrameNumber frame=FrameNumber(0);
frame<sprite->getTotalFrames(); ++frame) {
base::string filename = document->getFilename();
if (sprite->getTotalFrames() > FrameNumber(1)) {
int frameNumWidth =
(sprite->getTotalFrames() < 10)? 1:
(sprite->getTotalFrames() < 100)? 2:
(sprite->getTotalFrames() < 1000)? 3: 4;
std::sprintf(&buf[0], "%0*d", frameNumWidth, frame);
base::string path = base::get_file_path(filename);
base::string title = base::get_file_title(filename);
base::string ext = base::get_file_extension(filename);
filename = base::join_path(path, title + &buf[0] + "." + ext);
}
samples.addSample(Sample(document, sprite, frame, filename));
}
}
}
Document* DocumentExporter::createEmptyTexture(const Samples& samples)
{
Palette* palette = NULL;
PixelFormat pixelFormat = IMAGE_INDEXED;
gfx::Rect fullTextureBounds;
int maxColors = 256;
for (Samples::const_iterator
it = samples.begin(),
end = samples.end(); it != end; ++it) {
// We try to render an indexed image. But if we find a sprite with
// two or more palettes, or two of the sprites have different
// palettes, we've to use RGB format.
if (pixelFormat == IMAGE_INDEXED) {
if (it->sprite()->getPixelFormat() != IMAGE_INDEXED) {
pixelFormat = IMAGE_RGB;
}
else if (it->sprite()->getPalettes().size() > 1) {
pixelFormat = IMAGE_RGB;
}
else if (palette != NULL
&& palette->countDiff(it->sprite()->getPalette(FrameNumber(0)), NULL, NULL) > 0) {
pixelFormat = IMAGE_RGB;
}
else
palette = it->sprite()->getPalette(FrameNumber(0));
}
fullTextureBounds = fullTextureBounds.createUnion(it->inTextureBounds());
}
base::UniquePtr<Document> document(Document::createBasicDocument(pixelFormat,
fullTextureBounds.w, fullTextureBounds.h, maxColors));
if (palette != NULL)
document->getSprite()->setPalette(palette, false);
return document.release();
}
void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage)
{
textureImage->clear(0);
for (Samples::const_iterator
it = samples.begin(),
end = samples.end(); it != end; ++it) {
// Make the sprite compatible with the texture so the render()
// works correctly.
if (it->sprite()->getPixelFormat() != textureImage->getPixelFormat()) {
DocumentApi docApi(it->document(), NULL); // DocumentApi without undo
docApi.setPixelFormat(it->sprite(), textureImage->getPixelFormat(),
DITHERING_NONE);
}
it->sprite()->render(textureImage,
it->inTextureBounds().x - it->trimmedBounds().x,
it->inTextureBounds().y - it->trimmedBounds().y,
it->frame());
}
}
void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os, Image* textureImage)
{
os << "{ \"frames\": {\n";
for (Samples::const_iterator
it = samples.begin(),
end = samples.end(); it != end; ) {
gfx::Size srcSize = it->originalSize();
gfx::Rect spriteSourceBounds = it->trimmedBounds();
gfx::Rect frameBounds = it->inTextureBounds();
os << " \"" << it->filename() << "\": {\n"
<< " \"frame\": { "
<< "\"x\": " << frameBounds.x << ", "
<< "\"y\": " << frameBounds.y << ", "
<< "\"w\": " << frameBounds.w << ", "
<< "\"h\": " << frameBounds.h << " },\n"
<< " \"rotated\": false,\n"
<< " \"trimmed\": " << (it->trimmed() ? "true": "false") << ",\n"
<< " \"spriteSourceSize\": { "
<< "\"x\": " << spriteSourceBounds.x << ", "
<< "\"y\": " << spriteSourceBounds.y << ", "
<< "\"w\": " << spriteSourceBounds.w << ", "
<< "\"h\": " << spriteSourceBounds.h << " },\n"
<< " \"sourceSize\": { "
<< "\"w\": " << srcSize.w << ", "
<< "\"h\": " << srcSize.h << " },\n"
<< " \"duration\": " << it->sprite()->getFrameDuration(it->frame()) << "\n"
<< " }";
if (++it != samples.end())
os << ",\n";
else
os << "\n";
}
os << " },\n"
<< " \"meta\": {\n"
<< " \"app\": \"" << WEBSITE << "\",\n"
<< " \"version\": \"" << VERSION << "\",\n";
if (!m_textureFilename.empty())
os << " \"image\": \"" << m_textureFilename.c_str() << "\",\n";
os << " \"format\": \"" << (textureImage->getPixelFormat() == IMAGE_RGB ? "RGBA8888": "I8") << "\",\n"
<< " \"size\": { "
<< "\"w\": " << textureImage->getWidth() << ", "
<< "\"h\": " << textureImage->getHeight() << " },\n"
<< " \"scale\": \"" << m_scale << "\"\n"
<< " }\n"
<< "}\n";
}
} // namespace app

111
src/app/document_exporter.h Normal file
View File

@ -0,0 +1,111 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_DOCUMENT_EXPORTER_H_INCLUDED
#define APP_DOCUMENT_EXPORTER_H_INCLUDED
#include "base/disable_copying.h"
#include "gfx/fwd.h"
#include <iosfwd>
#include <vector>
namespace raster {
class Image;
}
namespace app {
class Document;
class DocumentExporter {
public:
enum DataFormat {
JsonDataFormat,
DefaultDataFormat = JsonDataFormat
};
enum TextureFormat {
JsonTextureFormat,
DefaultTextureFormat = JsonTextureFormat
};
enum ScaleMode {
DefaultScaleMode
};
DocumentExporter() :
m_dataFormat(DefaultDataFormat),
m_textureFormat(DefaultTextureFormat),
m_scaleMode(DefaultScaleMode) {
}
void setDataFormat(DataFormat format) {
m_dataFormat = format;
}
void setDataFilename(const std::string& filename) {
m_dataFilename = filename;
}
void setTextureFormat(TextureFormat format) {
m_textureFormat = format;
}
void setTextureFilename(const std::string& filename) {
m_textureFilename = filename;
}
void setScale(double scale) {
m_scale = scale;
}
void setScaleMode(ScaleMode mode) {
m_scaleMode = mode;
}
void addDocument(Document* document) {
m_documents.push_back(document);
}
void exportSheet();
private:
class Sample;
class Samples;
class LayoutSamples;
class SimpleLayoutSamples;
void captureSamples(Samples& samples);
Document* createEmptyTexture(const Samples& samples);
void renderTexture(const Samples& samples, raster::Image* textureImage);
void createDataFile(const Samples& samples, std::ostream& os, raster::Image* textureImage);
DataFormat m_dataFormat;
std::string m_dataFilename;
TextureFormat m_textureFormat;
std::string m_textureFilename;
double m_scale;
ScaleMode m_scaleMode;
std::vector<Document*> m_documents;
DISABLE_COPYING(DocumentExporter);
};
} // namespace app
#endif

View File

@ -134,6 +134,10 @@ void UIContext::onAddDocument(Document* document)
// base method
Context::onAddDocument(document);
// We don't create views in batch mode.
if (!App::instance()->isGui())
return;
// Add a new view for this document
DocumentView* view = new DocumentView(document, DocumentView::Normal);