[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