Move dithering matrices to extensions

This commit is contained in:
David Capello 2017-06-14 16:34:09 -03:00
parent d0282d78f1
commit a20976d220
12 changed files with 273 additions and 55 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,26 @@
{
"name": "bayer-matrices",
"displayName": "Bayer Matrices for Dithering",
"description": "Dithering matrices created by Bryce E. Bayer",
"version": "1.0",
"publisher": "aseprite",
"contributes": {
"ditheringMatrices": [
{
"id": "bayer8x8",
"name": "Bayer Matrix 8x8",
"path": "./bayer8x8.bmp"
},
{
"id": "bayer4x4",
"name": "Bayer Matrix 4x4",
"path": "./bayer4x4.bmp"
},
{
"id": "bayer2x2",
"name": "Bayer Matrix 2x2",
"path": "./bayer2x2.bmp"
}
]
}
}

View File

@ -382,6 +382,7 @@ add_library(app-lib
ini_file.cpp
job.cpp
launcher.cpp
load_matrix.cpp
log.cpp
loop_tag.cpp
modules.cpp

View File

@ -14,7 +14,8 @@
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/file/file.h"
#include "app/extensions.h"
#include "app/load_matrix.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
@ -376,45 +377,21 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
m_ditheringAlgorithm = render::DitheringAlgorithm::None;
std::string matrix = params.get("dithering-matrix");
// TODO object slicing here (from BayerMatrix -> DitheringMatrix
if (!matrix.empty()) {
if (matrix == "bayer2x2")
m_ditheringMatrix = render::BayerMatrix(2);
else if (matrix == "bayer4x4")
m_ditheringMatrix = render::BayerMatrix(4);
else if (matrix == "bayer8x8")
m_ditheringMatrix = render::BayerMatrix(8);
else {
// Load a matrix from a file
base::UniquePtr<doc::Document> doc(load_document(nullptr, matrix));
if (doc) {
// Flatten layers
doc::Sprite* spr = doc->sprite();
app::Context ctx;
cmd::FlattenLayers(spr).execute(&ctx);
const doc::Layer* lay = spr->root()->firstLayer();
const doc::Image* img = (lay && lay->cel(0) ?
lay->cel(0)->image(): nullptr);
if (img) {
const int w = spr->width();
const int h = spr->height();
m_ditheringMatrix = render::DitheringMatrix(h, w);
for (int i=0; i<h; ++i)
for (int j=0; j<w; ++j)
m_ditheringMatrix(i, j) = img->getPixel(j, i);
m_ditheringMatrix.calcMaxValue();
}
else {
m_ditheringMatrix = render::DitheringMatrix();
}
}
else
throw std::runtime_error("Invalid matrix name");
// Try to get the matrix from the extensions
const render::DitheringMatrix* knownMatrix =
App::instance()->extensions().ditheringMatrix(matrix);
if (knownMatrix) {
m_ditheringMatrix = *knownMatrix;
}
// Then, if the matrix doesn't exist we try to load it from a file
else if (!load_dithering_matrix_from_sprite(matrix, m_ditheringMatrix)) {
throw std::runtime_error("Invalid matrix name");
}
}
// Default dithering matrix is BayerMatrix(8)
else {
// TODO object slicing here (from BayerMatrix -> DitheringMatrix)
m_ditheringMatrix = render::BayerMatrix(8);
}
}

View File

@ -11,12 +11,14 @@
#include "app/extensions.h"
#include "app/ini_file.h"
#include "app/load_matrix.h"
#include "app/pref/preferences.h"
#include "app/resource_finder.h"
#include "base/exception.h"
#include "base/file_handle.h"
#include "base/fs.h"
#include "base/unique_ptr.h"
#include "render/dithering_matrix.h"
#include "archive.h"
#include "archive_entry.h"
@ -153,6 +155,21 @@ private:
} // anonymous namespace
const render::DitheringMatrix& Extension::DitheringMatrixInfo::matrix() const
{
if (!m_matrix) {
m_matrix = new render::DitheringMatrix;
load_dithering_matrix_from_sprite(m_path, *m_matrix);
}
return *m_matrix;
}
void Extension::DitheringMatrixInfo::destroyMatrix()
{
if (m_matrix)
delete m_matrix;
}
Extension::Extension(const std::string& path,
const std::string& name,
const std::string& displayName,
@ -167,6 +184,13 @@ Extension::Extension(const std::string& path,
{
}
Extension::~Extension()
{
// Delete all matrices
for (auto& it : m_ditheringMatrices)
it.second.destroyMatrix();
}
void Extension::addTheme(const std::string& id, const std::string& path)
{
m_themes[id] = path;
@ -177,6 +201,14 @@ void Extension::addPalette(const std::string& id, const std::string& path)
m_palettes[id] = path;
}
void Extension::addDitheringMatrix(const std::string& id,
const std::string& path,
const std::string& name)
{
DitheringMatrixInfo info(path, name);
m_ditheringMatrices[id] = info;
}
bool Extension::canBeDisabled() const
{
return (m_isEnabled &&
@ -347,6 +379,32 @@ ExtensionItems Extensions::palettes() const
return palettes;
}
const render::DitheringMatrix* Extensions::ditheringMatrix(const std::string& matrixId)
{
for (auto ext : m_extensions) {
if (!ext->isEnabled()) // Ignore disabled themes
continue;
auto it = ext->m_ditheringMatrices.find(matrixId);
if (it != ext->m_ditheringMatrices.end())
return &it->second.matrix();
}
return nullptr;
}
std::vector<Extension::DitheringMatrixInfo> Extensions::ditheringMatrices()
{
std::vector<Extension::DitheringMatrixInfo> result;
for (auto ext : m_extensions) {
if (!ext->isEnabled()) // Ignore disabled themes
continue;
for (auto it : ext->m_ditheringMatrices)
result.push_back(it.second);
}
return result;
}
void Extensions::enableExtension(Extension* extension, const bool state)
{
extension->enable(state);
@ -493,6 +551,25 @@ Extension* Extensions::loadExtension(const std::string& path,
extension->addPalette(palId, palPath);
}
}
// Dithering matrices
auto ditheringMatrices = contributes["ditheringMatrices"];
if (ditheringMatrices.is_array()) {
for (const auto& ditheringMatrix : ditheringMatrices.get_array()) {
std::string matId = ditheringMatrix.at("id").get_string();
std::string matPath = ditheringMatrix.at("path").get_string();
std::string matName = ditheringMatrix.at("name").get_string();
// The path must be always relative to the extension
matPath = base::join_path(path, matPath);
LOG("EXT: New dithering matrix '%s' in '%s'\n",
matId.c_str(),
matPath.c_str());
extension->addDitheringMatrix(matId, matPath, matName);
}
}
}
if (extension)
@ -502,8 +579,9 @@ Extension* Extensions::loadExtension(const std::string& path,
void Extensions::generateExtensionSignals(Extension* extension)
{
if (!extension->themes().empty()) ThemesChange(extension);
if (!extension->palettes().empty()) PalettesChange(extension);
if (extension->hasThemes()) ThemesChange(extension);
if (extension->hasPalettes()) PalettesChange(extension);
if (extension->hasDitheringMatrices()) DitheringMatricesChange(extension);
}
} // namespace app

View File

@ -8,12 +8,17 @@
#define APP_EXTENSIONS_H_INCLUDED
#pragma once
#include "base/unique_ptr.h"
#include "obs/signal.h"
#include <map>
#include <string>
#include <vector>
namespace render {
class DitheringMatrix;
}
namespace app {
// Key=theme/palette/etc. id
@ -25,11 +30,29 @@ namespace app {
class Extension {
friend class Extensions;
public:
class DitheringMatrixInfo {
public:
DitheringMatrixInfo() : m_matrix(nullptr) { }
DitheringMatrixInfo(const std::string& path,
const std::string& name)
: m_path(path), m_name(name), m_matrix(nullptr) { }
const std::string& name() const { return m_name; }
const render::DitheringMatrix& matrix() const;
void destroyMatrix();
private:
std::string m_path;
std::string m_name;
mutable render::DitheringMatrix* m_matrix;
};
Extension(const std::string& path,
const std::string& name,
const std::string& displayName,
const bool isEnabled,
const bool isBuiltinExtension);
~Extension();
const std::string& path() const { return m_path; }
const std::string& name() const { return m_name; }
@ -40,12 +63,19 @@ namespace app {
void addTheme(const std::string& id, const std::string& path);
void addPalette(const std::string& id, const std::string& path);
void addDitheringMatrix(const std::string& id,
const std::string& path,
const std::string& name);
bool isEnabled() const { return m_isEnabled; }
bool isInstalled() const { return m_isInstalled; }
bool canBeDisabled() const;
bool canBeUninstalled() const;
bool hasThemes() const { return !m_themes.empty(); }
bool hasPalettes() const { return !m_palettes.empty(); }
bool hasDitheringMatrices() const { return !m_ditheringMatrices.empty(); }
private:
void enable(const bool state);
void uninstall();
@ -55,6 +85,7 @@ namespace app {
ExtensionItems m_themes;
ExtensionItems m_palettes;
std::map<std::string, DitheringMatrixInfo> m_ditheringMatrices;
std::string m_path;
std::string m_name;
std::string m_displayName;
@ -81,10 +112,13 @@ namespace app {
std::string themePath(const std::string& themeId);
std::string palettePath(const std::string& palId);
ExtensionItems palettes() const;
const render::DitheringMatrix* ditheringMatrix(const std::string& matrixId);
std::vector<Extension::DitheringMatrixInfo> ditheringMatrices();
obs::signal<void(Extension*)> NewExtension;
obs::signal<void(Extension*)> ThemesChange;
obs::signal<void(Extension*)> PalettesChange;
obs::signal<void(Extension*)> DitheringMatricesChange;
private:
Extension* loadExtension(const std::string& path,

52
src/app/load_matrix.cpp Normal file
View File

@ -0,0 +1,52 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// 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/load_matrix.h"
#include "app/context.h"
#include "app/document.h"
#include "app/file/file.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "render/dithering_matrix.h"
namespace app {
bool load_dithering_matrix_from_sprite(
const std::string& filename,
render::DitheringMatrix& matrix)
{
base::UniquePtr<doc::Document> doc(load_document(nullptr, filename));
if (!doc)
return false;
doc::Sprite* spr = doc->sprite();
const doc::Layer* lay = (spr && spr->root() ? spr->root()->firstLayer():
nullptr);
const doc::Image* img = (lay && lay->cel(0) ? lay->cel(0)->image():
nullptr);
if (img) {
const int w = spr->width();
const int h = spr->height();
matrix = render::DitheringMatrix(h, w);
for (int i=0; i<h; ++i)
for (int j=0; j<w; ++j)
matrix(i, j) = img->getPixel(j, i);
matrix.calcMaxValue();
}
else {
matrix = render::DitheringMatrix();
}
return true;
}
} // namespace app

23
src/app/load_matrix.h Normal file
View File

@ -0,0 +1,23 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_LOAD_MATRIX_H_INCLUDED
#define APP_LOAD_MATRIX_H_INCLUDED
#pragma once
namespace render {
class DitheringMatrix;
};
namespace app {
bool load_dithering_matrix_from_sprite(
const std::string& filename,
render::DitheringMatrix& matrix);
} // namespace app
#endif

View File

@ -10,6 +10,8 @@
#include "app/ui/dithering_selector.h"
#include "app/app.h"
#include "app/extensions.h"
#include "app/modules/palettes.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
@ -157,35 +159,53 @@ private:
} // anonymous namespace
DitheringSelector::DitheringSelector(Type type)
: m_type(type)
{
setUseCustomWidget(true);
Extensions& extensions = App::instance()->extensions();
switch (type) {
// If an extension with "ditheringMatrices" is disable/enable, we
// regenerate this DitheringSelector
m_extChanges =
extensions.DitheringMatricesChange.connect(
base::Bind<void>(&DitheringSelector::regenerate, this));
setUseCustomWidget(true);
regenerate();
}
void DitheringSelector::regenerate()
{
removeAllItems();
Extensions& extensions = App::instance()->extensions();
auto ditheringMatrices = extensions.ditheringMatrices();
switch (m_type) {
case SelectBoth:
addItem(new DitherItem(render::DitheringAlgorithm::None,
render::DitheringMatrix(), "No Dithering"));
addItem(new DitherItem(render::DitheringAlgorithm::Ordered,
render::BayerMatrix(8), "Ordered Dithering - Bayer Matrix 8x8"));
addItem(new DitherItem(render::DitheringAlgorithm::Ordered,
render::BayerMatrix(4), "Ordered Dithering - Bayer Matrix 4x4"));
addItem(new DitherItem(render::DitheringAlgorithm::Ordered,
render::BayerMatrix(2), "Ordered Dithering - Bayer Matrix 2x2"));
addItem(new DitherItem(render::DitheringAlgorithm::Old,
render::BayerMatrix(8), "Old Dithering - Bayer Matrix 8x8"));
addItem(new DitherItem(render::DitheringAlgorithm::Old,
render::BayerMatrix(4), "Old Dithering - Bayer Matrix 4x4"));
addItem(new DitherItem(render::DitheringAlgorithm::Old,
render::BayerMatrix(2), "Old Dithering - Bayer Matrix 2x2"));
for (const auto& it : ditheringMatrices) {
addItem(
new DitherItem(
render::DitheringAlgorithm::Ordered,
it.matrix(),
"Ordered Dithering+" + it.name()));
}
for (const auto& it : ditheringMatrices) {
addItem(
new DitherItem(
render::DitheringAlgorithm::Old,
it.matrix(),
"Old Dithering+" + it.name()));
}
break;
case SelectMatrix:
addItem(new DitherItem(render::DitheringMatrix(), "No Dithering"));
addItem(new DitherItem(render::BayerMatrix(8), "Bayer Matrix 8x8"));
addItem(new DitherItem(render::BayerMatrix(4), "Bayer Matrix 4x4"));
addItem(new DitherItem(render::BayerMatrix(2), "Bayer Matrix 2x2"));
for (auto& it : ditheringMatrices)
addItem(new DitherItem(it.matrix(), it.name()));
break;
}
setSelectedItemIndex(0);
setSizeHint(getItem(0)->sizeHint());
}

View File

@ -8,6 +8,7 @@
#define APP_UI_DITHERING_SELECTOR_H_INCLUDED
#pragma once
#include "obs/connection.h"
#include "render/dithering_algorithm.h"
#include "render/ordered_dither.h"
#include "ui/box.h"
@ -26,6 +27,12 @@ namespace app {
render::DitheringAlgorithm ditheringAlgorithm();
render::DitheringMatrix ditheringMatrix();
private:
void regenerate();
Type m_type;
obs::scoped_connection m_extChanges;
};
} // namespace app