[aseprite] 138/308: Load/save custom brushes
Tobias Hansen
thansen at moszumanska.debian.org
Tue Mar 8 02:45:02 UTC 2016
This is an automated email from the git hooks/post-receive script.
thansen pushed a commit to branch master
in repository aseprite.
commit 6fb5258e510c9eb19c50e387f4eb704abaa8481d
Author: David Capello <davidcapello at gmail.com>
Date: Tue Dec 22 10:55:15 2015 -0300
Load/save custom brushes
---
src/app/app_brushes.cpp | 361 +++++++++++++++++++++++++++++++++++++++++++++
src/app/app_brushes.h | 5 +
src/app/tools/ink_type.cpp | 22 +++
src/app/tools/ink_type.h | 2 +
src/doc/CMakeLists.txt | 1 +
src/doc/brush_type.cpp | 35 +++++
src/doc/brush_type.h | 5 +
7 files changed, 431 insertions(+)
diff --git a/src/app/app_brushes.cpp b/src/app/app_brushes.cpp
index 41372f7..32efd11 100644
--- a/src/app/app_brushes.cpp
+++ b/src/app/app_brushes.cpp
@@ -10,16 +10,170 @@
#endif
#include "app/app_brushes.h"
+#include "app/resource_finder.h"
+#include "app/tools/ink_type.h"
+#include "app/xml_document.h"
+#include "app/xml_exception.h"
+#include "base/base64.h"
+#include "base/convert_to.h"
+#include "base/path.h"
+#include "base/serialization.h"
+#include "doc/brush.h"
+#include "doc/color.h"
+#include "doc/image.h"
+#include "doc/image_impl.h"
+
+#include <fstream>
namespace app {
using namespace doc;
+using namespace base::serialization;
+using namespace base::serialization::little_endian;
+
+namespace {
+
+ImageRef load_xml_image(const TiXmlElement* imageElem)
+{
+ ImageRef image;
+ int w, h;
+ if (!imageElem->QueryIntAttribute("width", &w) == TIXML_SUCCESS ||
+ !imageElem->QueryIntAttribute("height", &h) == TIXML_SUCCESS ||
+ w < 0 || w > 9999 &&
+ h < 0 || h > 9999)
+ return image;
+
+ auto formatValue = imageElem->Attribute("format");
+ if (!formatValue)
+ return image;
+
+ const char* pixels_base64 = imageElem->GetText();
+ if (!pixels_base64)
+ return image;
+
+ base::buffer data;
+ base::decode_base64(pixels_base64, data);
+ auto it = data.begin(), end = data.end();
+
+ std::string formatStr = formatValue;
+ if (formatStr == "rgba") {
+ image.reset(Image::create(IMAGE_RGB, w, h));
+ LockImageBits<RgbTraits> pixels(image.get());
+ for (auto& pixel : pixels) {
+ if ((end - it) < 4)
+ break;
+
+ int r = *it; ++it;
+ int g = *it; ++it;
+ int b = *it; ++it;
+ int a = *it; ++it;
+
+ pixel = doc::rgba(r, g, b, a);
+ }
+ }
+ else if (formatStr == "grayscale") {
+ image.reset(Image::create(IMAGE_GRAYSCALE, w, h));
+ LockImageBits<GrayscaleTraits> pixels(image.get());
+ for (auto& pixel : pixels) {
+ if ((end - it) < 2)
+ break;
+
+ int v = *it; ++it;
+ int a = *it; ++it;
+
+ pixel = doc::graya(v, a);
+ }
+ }
+ else if (formatStr == "indexed") {
+ image.reset(Image::create(IMAGE_INDEXED, w, h));
+ LockImageBits<IndexedTraits> pixels(image.get());
+ for (auto& pixel : pixels) {
+ if (it == end)
+ break;
+
+ pixel = *it;
+ ++it;
+ }
+ }
+ return image;
+}
+
+void save_xml_image(TiXmlElement* imageElem, const Image* image)
+{
+ int w = image->width();
+ int h = image->height();
+ imageElem->SetAttribute("width", w);
+ imageElem->SetAttribute("height", h);
+
+ std::string format;
+ switch (image->pixelFormat()) {
+ case IMAGE_RGB: format = "rgba"; break;
+ case IMAGE_GRAYSCALE: format = "grayscale"; break;
+ case IMAGE_INDEXED: format = "indexed"; break;
+ case IMAGE_BITMAP: format = "indexed"; break; // TODO add "bitmap" format
+ }
+ ASSERT(!format.empty());
+ if (!format.empty())
+ imageElem->SetAttribute("format", format.c_str());
+
+ base::buffer data;
+ data.reserve(h * image->getRowStrideSize());
+ switch (image->pixelFormat()) {
+ case IMAGE_RGB:{
+ const LockImageBits<RgbTraits> pixels(image);
+ for (const auto& pixel : pixels) {
+ data.push_back(doc::rgba_getr(pixel));
+ data.push_back(doc::rgba_getg(pixel));
+ data.push_back(doc::rgba_getb(pixel));
+ data.push_back(doc::rgba_geta(pixel));
+ }
+ break;
+ }
+ case IMAGE_GRAYSCALE:{
+ const LockImageBits<GrayscaleTraits> pixels(image);
+ for (const auto& pixel : pixels) {
+ data.push_back(doc::graya_getv(pixel));
+ data.push_back(doc::graya_geta(pixel));
+ }
+ break;
+ }
+ case IMAGE_INDEXED: {
+ const LockImageBits<IndexedTraits> pixels(image);
+ for (const auto& pixel : pixels) {
+ data.push_back(pixel);
+ }
+ break;
+ }
+ case IMAGE_BITMAP: {
+ // Here we save bitmap format as indexed
+ const LockImageBits<BitmapTraits> pixels(image);
+ for (const auto& pixel : pixels) {
+ data.push_back(pixel); // TODO save bitmap format as bitmap
+ }
+ break;
+ }
+ }
+
+ std::string data_base64;
+ base::encode_base64(data, data_base64);
+ TiXmlText textElem(data_base64.c_str());
+ imageElem->InsertEndChild(textElem);
+}
+
+} // anonymous namespace
AppBrushes::AppBrushes()
{
m_standard.push_back(BrushRef(new Brush(kCircleBrushType, 7, 0)));
m_standard.push_back(BrushRef(new Brush(kSquareBrushType, 7, 0)));
m_standard.push_back(BrushRef(new Brush(kLineBrushType, 7, 44)));
+
+ load(userBrushesFilename());
+}
+
+AppBrushes::~AppBrushes()
+{
+ save(userBrushesFilename());
}
AppBrushes::slot_id AppBrushes::addBrushSlot(const BrushSlot& brush)
@@ -114,4 +268,211 @@ bool AppBrushes::isBrushSlotLocked(slot_id slot) const
return false;
}
+static const int kBrushFlags =
+ int(BrushSlot::Flags::BrushType) |
+ int(BrushSlot::Flags::BrushSize) |
+ int(BrushSlot::Flags::BrushAngle);
+
+void AppBrushes::load(const std::string& filename)
+{
+ XmlDocumentRef doc = app::open_xml(filename);
+ TiXmlHandle handle(doc.get());
+ TiXmlElement* brushElem = handle
+ .FirstChild("brushes")
+ .FirstChild("brush").ToElement();
+
+ while (brushElem) {
+ // flags
+ int flags = 0;
+ BrushRef brush;
+ app::Color fgColor;
+ app::Color bgColor;
+ tools::InkType inkType = tools::InkType::DEFAULT;
+ int inkOpacity = 255;
+ Shade shade;
+ bool pixelPerfect = false;
+
+ // Brush
+ const char* type = brushElem->Attribute("type");
+ const char* size = brushElem->Attribute("size");
+ const char* angle = brushElem->Attribute("angle");
+ if (type || size || angle) {
+ if (type) flags |= int(BrushSlot::Flags::BrushType);
+ if (size) flags |= int(BrushSlot::Flags::BrushSize);
+ if (angle) flags |= int(BrushSlot::Flags::BrushAngle);
+ brush.reset(
+ new Brush(
+ string_id_to_brush_type(type),
+ (size ? base::convert_to<int>(std::string(size)): 1),
+ (angle ? base::convert_to<int>(std::string(angle)): 0)));
+ }
+
+ // Brush image
+ if (TiXmlElement* imageElem = brushElem->FirstChildElement("image")) {
+ ImageRef image = load_xml_image(imageElem);
+ if (image) {
+ if (!brush)
+ brush.reset(new Brush());
+ brush->setImage(image.get());
+ }
+ }
+
+ // Colors
+ if (TiXmlElement* fgcolorElem = brushElem->FirstChildElement("fgcolor")) {
+ if (auto value = fgcolorElem->Attribute("value")) {
+ fgColor = app::Color::fromString(value);
+ flags |= int(BrushSlot::Flags::FgColor);
+ }
+ }
+
+ if (TiXmlElement* bgcolorElem = brushElem->FirstChildElement("bgcolor")) {
+ if (auto value = bgcolorElem->Attribute("value")) {
+ bgColor = app::Color::fromString(value);
+ flags |= int(BrushSlot::Flags::BgColor);
+ }
+ }
+
+ // Ink
+ if (TiXmlElement* inkTypeElem = brushElem->FirstChildElement("inktype")) {
+ if (auto value = inkTypeElem->Attribute("value")) {
+ inkType = app::tools::string_id_to_ink_type(value);
+ flags |= int(BrushSlot::Flags::InkType);
+ }
+ }
+
+ if (TiXmlElement* inkOpacityElem = brushElem->FirstChildElement("inkopacity")) {
+ if (auto value = inkOpacityElem->Attribute("value")) {
+ inkOpacity = base::convert_to<int>(std::string(value));
+ flags |= int(BrushSlot::Flags::InkOpacity);
+ }
+ }
+
+ // Shade
+ if (TiXmlElement* shadeElem = brushElem->FirstChildElement("shade")) {
+ if (auto value = shadeElem->Attribute("value")) {
+ shade = shade_from_string(value);
+ flags |= int(BrushSlot::Flags::Shade);
+ }
+ }
+
+ // Pixel-perfect
+ if (TiXmlElement* pixelPerfectElem = brushElem->FirstChildElement("pixelperfect")) {
+ pixelPerfect = bool_attr_is_true(pixelPerfectElem, "value");
+ flags |= int(BrushSlot::Flags::PixelPerfect);
+ }
+
+ if (flags != 0)
+ flags |= int(BrushSlot::Flags::Locked);
+
+ BrushSlot brushSlot(BrushSlot::Flags(flags),
+ brush, fgColor, bgColor,
+ inkType, inkOpacity, shade,
+ pixelPerfect);
+ m_slots.push_back(brushSlot);
+
+ brushElem = brushElem->NextSiblingElement();
+ }
+}
+
+void AppBrushes::save(const std::string& filename) const
+{
+ XmlDocumentRef doc(new TiXmlDocument());
+ TiXmlElement brushesElem("brushes");
+
+ //<?xml version="1.0" encoding="utf-8"?>
+
+ for (const auto& slot : m_slots) {
+ TiXmlElement brushElem("brush");
+ if (slot.locked()) {
+ // Flags
+ int flags = int(slot.flags());
+
+ ASSERT(slot.brush());
+ if (!slot.hasBrush())
+ flags &= ~kBrushFlags;
+
+ // Brush type
+ if (slot.hasBrush()) {
+ ASSERT(slot.brush());
+
+ if (flags & int(BrushSlot::Flags::BrushType)) {
+ brushElem.SetAttribute(
+ "type", brush_type_to_string_id(slot.brush()->type()).c_str());
+ }
+
+ if (flags & int(BrushSlot::Flags::BrushSize)) {
+ brushElem.SetAttribute("size", slot.brush()->size());
+ }
+
+ if (flags & int(BrushSlot::Flags::BrushAngle)) {
+ brushElem.SetAttribute("angle", slot.brush()->angle());
+ }
+
+ if (slot.brush()->type() == kImageBrushType &&
+ slot.brush()->image()) {
+ TiXmlElement elem("image");
+ save_xml_image(&elem, slot.brush()->image());
+ brushElem.InsertEndChild(elem);
+ }
+ }
+
+ // Colors
+ if (flags & int(BrushSlot::Flags::FgColor)) {
+ TiXmlElement elem("fgcolor");
+ elem.SetAttribute("value", slot.fgColor().toString().c_str());
+ brushElem.InsertEndChild(elem);
+ }
+
+ if (flags & int(BrushSlot::Flags::BgColor)) {
+ TiXmlElement elem("bgcolor");
+ elem.SetAttribute("value", slot.bgColor().toString().c_str());
+ brushElem.InsertEndChild(elem);
+ }
+
+ // Ink
+ if (flags & int(BrushSlot::Flags::InkType)) {
+ TiXmlElement elem("inktype");
+ elem.SetAttribute(
+ "value", app::tools::ink_type_to_string_id(slot.inkType()).c_str());
+ brushElem.InsertEndChild(elem);
+ }
+
+ if (flags & int(BrushSlot::Flags::InkOpacity)) {
+ TiXmlElement elem("inkopacity");
+ elem.SetAttribute("value", slot.inkOpacity());
+ brushElem.InsertEndChild(elem);
+ }
+
+ // Shade
+ if (flags & int(BrushSlot::Flags::Shade)) {
+ TiXmlElement elem("shade");
+ elem.SetAttribute("value", shade_to_string(slot.shade()).c_str());
+ brushElem.InsertEndChild(elem);
+ }
+
+ // Pixel-perfect
+ if (flags & int(BrushSlot::Flags::PixelPerfect)) {
+ TiXmlElement elem("pixelperfect");
+ elem.SetAttribute("value", slot.pixelPerfect() ? "true": "false");
+ brushElem.InsertEndChild(elem);
+ }
+ }
+
+ brushesElem.InsertEndChild(brushElem);
+ }
+
+ TiXmlDeclaration declaration("1.0", "utf-8", "");
+ doc->InsertEndChild(declaration);
+ doc->InsertEndChild(brushesElem);
+ save_xml(doc, filename);
+}
+
+// static
+std::string AppBrushes::userBrushesFilename()
+{
+ ResourceFinder rf;
+ rf.includeUserDir("user.aseprite-brushes");
+ return rf.getFirstOrCreateDefault();
+}
+
} // namespace app
diff --git a/src/app/app_brushes.h b/src/app/app_brushes.h
index dc451c2..f8a6741 100644
--- a/src/app/app_brushes.h
+++ b/src/app/app_brushes.h
@@ -24,6 +24,7 @@ namespace app {
typedef std::vector<BrushSlot> BrushSlots;
AppBrushes();
+ ~AppBrushes();
// Adds a new brush and returns the slot number where the brush
// is now available.
@@ -43,6 +44,10 @@ namespace app {
base::Signal0<void> ItemsChange;
private:
+ void load(const std::string& filename);
+ void save(const std::string& filename) const;
+ static std::string userBrushesFilename();
+
doc::Brushes m_standard;
BrushSlots m_slots;
};
diff --git a/src/app/tools/ink_type.cpp b/src/app/tools/ink_type.cpp
index 577cdab..2f5bb80 100644
--- a/src/app/tools/ink_type.cpp
+++ b/src/app/tools/ink_type.cpp
@@ -26,5 +26,27 @@ std::string ink_type_to_string(InkType inkType)
return "Unknown";
}
+std::string ink_type_to_string_id(InkType inkType)
+{
+ switch (inkType) {
+ case tools::InkType::SIMPLE: return "simple";
+ case tools::InkType::ALPHA_COMPOSITING: return "alpha_compositing";
+ case tools::InkType::COPY_COLOR: return "copy_color";
+ case tools::InkType::LOCK_ALPHA: return "lock_alpha";
+ case tools::InkType::SHADING: return "shading";
+ }
+ return "unknown";
+}
+
+InkType string_id_to_ink_type(const std::string& s)
+{
+ if (s == "simple") return tools::InkType::SIMPLE;
+ if (s == "alpha_compositing") return tools::InkType::ALPHA_COMPOSITING;
+ if (s == "copy_color") return tools::InkType::COPY_COLOR;
+ if (s == "lock_alpha") return tools::InkType::LOCK_ALPHA;
+ if (s == "shading") return tools::InkType::SHADING;
+ return tools::InkType::DEFAULT;
+}
+
} // namespace tools
} // namespace app
diff --git a/src/app/tools/ink_type.h b/src/app/tools/ink_type.h
index 7d92b43..592e626 100644
--- a/src/app/tools/ink_type.h
+++ b/src/app/tools/ink_type.h
@@ -29,6 +29,8 @@ namespace tools {
}
std::string ink_type_to_string(InkType inkType);
+ std::string ink_type_to_string_id(InkType inkType);
+ InkType string_id_to_ink_type(const std::string& s);
} // namespace tools
} // namespace app
diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt
index 1a57c06..7dc6a1a 100644
--- a/src/doc/CMakeLists.txt
+++ b/src/doc/CMakeLists.txt
@@ -15,6 +15,7 @@ add_library(doc-lib
blend_funcs.cpp
blend_mode.cpp
brush.cpp
+ brush_type.cpp
cel.cpp
cel_data.cpp
cel_data_io.cpp
diff --git a/src/doc/brush_type.cpp b/src/doc/brush_type.cpp
new file mode 100644
index 0000000..c6f01ee
--- /dev/null
+++ b/src/doc/brush_type.cpp
@@ -0,0 +1,35 @@
+// Aseprite Document Library
+// Copyright (c) 2015 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "doc/brush_type.h"
+
+namespace doc {
+
+std::string brush_type_to_string_id(BrushType brushType)
+{
+ switch (brushType) {
+ case kCircleBrushType: return "circle";
+ case kSquareBrushType: return "square";
+ case kLineBrushType: return "line";
+ case kImageBrushType: return "image";
+ }
+ return "unknown";
+}
+
+BrushType string_id_to_brush_type(const std::string& s)
+{
+ if (s == "circle") return kCircleBrushType;
+ if (s == "square") return kSquareBrushType;
+ if (s == "line") return kLineBrushType;
+ if (s == "image") return kImageBrushType;
+ return kFirstBrushType;
+}
+
+} // namespace doc
diff --git a/src/doc/brush_type.h b/src/doc/brush_type.h
index cdb5256..81955b8 100644
--- a/src/doc/brush_type.h
+++ b/src/doc/brush_type.h
@@ -8,6 +8,8 @@
#define DOC_BRUSH_TYPE_H_INCLUDED
#pragma once
+#include <string>
+
namespace doc {
enum BrushType {
@@ -20,6 +22,9 @@ namespace doc {
kLastBrushType = kImageBrushType,
};
+ std::string brush_type_to_string_id(BrushType brushType);
+ BrushType string_id_to_brush_type(const std::string& s);
+
} // namespace doc
#endif
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git
More information about the Pkg-games-commits
mailing list