mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-04 15:40:10 +00:00
Move dithering matrices to extensions
This commit is contained in:
parent
d0282d78f1
commit
a20976d220
BIN
data/extensions/bayer-matrices/bayer2x2.bmp
Normal file
BIN
data/extensions/bayer-matrices/bayer2x2.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
data/extensions/bayer-matrices/bayer4x4.bmp
Normal file
BIN
data/extensions/bayer-matrices/bayer4x4.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
data/extensions/bayer-matrices/bayer8x8.bmp
Normal file
BIN
data/extensions/bayer-matrices/bayer8x8.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
26
data/extensions/bayer-matrices/package.json
Normal file
26
data/extensions/bayer-matrices/package.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
52
src/app/load_matrix.cpp
Normal 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
23
src/app/load_matrix.h
Normal 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
|
@ -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());
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user