mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 10:13:22 +00:00
This commit is contained in:
parent
e12233a19d
commit
3cc1c63274
@ -321,6 +321,7 @@
|
||||
<option id="to_gray" type="ToGrayAlgorithm" default="ToGrayAlgorithm::DEFAULT" />
|
||||
<option id="advanced" type="bool" default="false" />
|
||||
<option id="rgbmap_algorithm" type="doc::RgbMapAlgorithm" default="doc::RgbMapAlgorithm::DEFAULT" />
|
||||
<option id="fit_criteria" type="doc::FitCriteria" default="doc::FitCriteria::DEFAULT" />
|
||||
</section>
|
||||
<section id="eyedropper" text="Editor">
|
||||
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
|
||||
|
@ -1255,6 +1255,14 @@ default = Default (Octree)
|
||||
rgb5a3 = Table RGB 5 bits + Alpha 3 bits
|
||||
octree = Octree
|
||||
|
||||
[best_fit_criteria_selector]
|
||||
label = Color Best Fit Criteria:
|
||||
default = Default (Euclidean)
|
||||
rgb = RGB
|
||||
linearized_rgb = Linearized RGB
|
||||
cie_xyz = CIEXYZ
|
||||
cie_lab = CIELAB
|
||||
|
||||
[open_file]
|
||||
title = Open
|
||||
loading = Loading file
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2019-2020 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2019-2024 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2017 David Capello -->
|
||||
<gui>
|
||||
<window id="color_mode" text="@.title">
|
||||
@ -23,10 +23,12 @@
|
||||
<check text="@.flatten" id="flatten" />
|
||||
|
||||
<check id="advanced_check" text="@general.advanced_options" cell_hspan="2" />
|
||||
<hbox id="advanced" cell_hspan="2">
|
||||
<label text="@rgbmap_algorithm_selector.label" />
|
||||
<hbox id="rgbmap_algorithm_placeholder" />
|
||||
</hbox>
|
||||
<vbox id="advanced" cell_hspan="2">
|
||||
<label text="@rgbmap_algorithm_selector.label" cell_hspan="1" />
|
||||
<hbox id="rgbmap_algorithm_placeholder" cell_hspan="1" />
|
||||
<label text="@best_fit_criteria_selector.label" cell_hspan="1" />
|
||||
<hbox id="best_fit_criteria_placeholder" cell_hspan="1" />
|
||||
</vbox>
|
||||
|
||||
<separator horizontal="true" />
|
||||
<hbox>
|
||||
|
@ -587,10 +587,12 @@
|
||||
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />
|
||||
</hbox>
|
||||
<separator text="@.color_quantization" horizontal="true" />
|
||||
<hbox>
|
||||
<grid columns="2">
|
||||
<label text="@rgbmap_algorithm_selector.label" />
|
||||
<hbox id="rgbmap_algorithm_placeholder" />
|
||||
</hbox>
|
||||
<label text="@best_fit_criteria_selector.label" />
|
||||
<hbox id="best_fit_criteria_placeholder" />
|
||||
</grid>
|
||||
<separator text="@.performance" horizontal="true" />
|
||||
<hbox>
|
||||
<check id="shaders_for_color_selectors"
|
||||
|
@ -579,6 +579,7 @@ target_sources(app-lib PRIVATE
|
||||
ui/alpha_slider.cpp
|
||||
ui/app_menuitem.cpp
|
||||
ui/backup_indicator.cpp
|
||||
ui/best_fit_criteria_selector.cpp
|
||||
ui/browser_view.cpp
|
||||
ui/brush_popup.cpp
|
||||
ui/button_set.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -73,7 +73,8 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
const render::Dithering& dithering,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate)
|
||||
render::TaskDelegate* delegate,
|
||||
const FitCriteria fitCriteria)
|
||||
: WithSprite(sprite)
|
||||
, m_oldFormat(sprite->pixelFormat())
|
||||
, m_newFormat(newFormat)
|
||||
@ -108,7 +109,8 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
cel->layer()->isBackground(),
|
||||
mapAlgorithm,
|
||||
toGray,
|
||||
&superDel);
|
||||
&superDel,
|
||||
fitCriteria);
|
||||
|
||||
superDel.nextImage();
|
||||
}
|
||||
@ -208,7 +210,8 @@ void SetPixelFormat::convertImage(doc::Sprite* sprite,
|
||||
const bool isBackground,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate)
|
||||
render::TaskDelegate* delegate,
|
||||
const doc::FitCriteria fitCriteria)
|
||||
{
|
||||
ASSERT(oldImage);
|
||||
ASSERT(oldImage->pixelFormat() != IMAGE_TILEMAP);
|
||||
@ -218,7 +221,10 @@ void SetPixelFormat::convertImage(doc::Sprite* sprite,
|
||||
RgbMap* rgbmap;
|
||||
int newMaskIndex = (isBackground ? -1 : 0);
|
||||
if (m_newFormat == IMAGE_INDEXED) {
|
||||
rgbmap = sprite->rgbMap(frame, sprite->rgbMapForSprite(), mapAlgorithm);
|
||||
rgbmap = sprite->rgbMap(frame,
|
||||
sprite->rgbMapForSprite(),
|
||||
mapAlgorithm,
|
||||
fitCriteria);
|
||||
if (m_oldFormat == IMAGE_INDEXED)
|
||||
newMaskIndex = sprite->transparentColor();
|
||||
else
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -12,6 +12,7 @@
|
||||
#include "app/cmd/with_sprite.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/fit_criteria.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/pixel_format.h"
|
||||
@ -37,7 +38,8 @@ namespace cmd {
|
||||
const render::Dithering& dithering,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate);
|
||||
render::TaskDelegate* delegate,
|
||||
const doc::FitCriteria fitCriteria = doc::FitCriteria::DEFAULT);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
@ -56,7 +58,8 @@ namespace cmd {
|
||||
const bool isBackground,
|
||||
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate);
|
||||
render::TaskDelegate* delegate,
|
||||
const doc::FitCriteria fitCriteria = doc::FitCriteria::DEFAULT);
|
||||
|
||||
doc::PixelFormat m_oldFormat;
|
||||
doc::PixelFormat m_newFormat;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/sprite_job.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/best_fit_criteria_selector.h"
|
||||
#include "app/ui/dithering_selector.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/editor/editor_render.h"
|
||||
@ -69,7 +70,6 @@ public:
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::Dithering& dithering,
|
||||
const doc::RgbMapAlgorithm rgbMapAlgorithm,
|
||||
const gen::ToGrayAlgorithm toGray,
|
||||
const gfx::Point& pos,
|
||||
const bool newBlend)
|
||||
@ -83,13 +83,11 @@ public:
|
||||
sprite, frame,
|
||||
pixelFormat,
|
||||
dithering,
|
||||
rgbMapAlgorithm,
|
||||
toGray,
|
||||
newBlend]() { // Copy the matrix
|
||||
run(sprite, frame,
|
||||
pixelFormat,
|
||||
dithering,
|
||||
rgbMapAlgorithm,
|
||||
toGray,
|
||||
newBlend);
|
||||
})
|
||||
@ -114,7 +112,6 @@ private:
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::Dithering& dithering,
|
||||
const doc::RgbMapAlgorithm rgbMapAlgorithm,
|
||||
const gen::ToGrayAlgorithm toGray,
|
||||
const bool newBlend) {
|
||||
doc::ImageRef tmp(
|
||||
@ -136,9 +133,7 @@ private:
|
||||
m_image.get(),
|
||||
pixelFormat,
|
||||
dithering,
|
||||
sprite->rgbMap(frame,
|
||||
sprite->rgbMapForSprite(),
|
||||
rgbMapAlgorithm),
|
||||
sprite->rgbMap(frame),
|
||||
sprite->palette(frame),
|
||||
(sprite->backgroundLayer() != nullptr),
|
||||
0,
|
||||
@ -193,6 +188,7 @@ public:
|
||||
, m_selectedItem(nullptr)
|
||||
, m_ditheringSelector(nullptr)
|
||||
, m_mapAlgorithmSelector(nullptr)
|
||||
, m_bestFitCriteriaSelector(nullptr)
|
||||
, m_imageJustCreated(true)
|
||||
{
|
||||
const auto& pref = Preferences::instance();
|
||||
@ -219,6 +215,9 @@ public:
|
||||
m_mapAlgorithmSelector = new RgbMapAlgorithmSelector;
|
||||
m_mapAlgorithmSelector->setExpansive(true);
|
||||
|
||||
m_bestFitCriteriaSelector = new BestFitCriteriaSelector;
|
||||
m_bestFitCriteriaSelector->setExpansive(true);
|
||||
|
||||
// Select default dithering method
|
||||
{
|
||||
int index = m_ditheringSelector->findItemIndex(
|
||||
@ -230,8 +229,12 @@ public:
|
||||
// Select default RgbMap algorithm
|
||||
m_mapAlgorithmSelector->algorithm(pref.quantization.rgbmapAlgorithm());
|
||||
|
||||
// Select default best fit criteria
|
||||
m_bestFitCriteriaSelector->criteria(pref.quantization.fitCriteria());
|
||||
|
||||
ditheringPlaceholder()->addChild(m_ditheringSelector);
|
||||
rgbmapAlgorithmPlaceholder()->addChild(m_mapAlgorithmSelector);
|
||||
bestFitCriteriaPlaceholder()->addChild(m_bestFitCriteriaSelector);
|
||||
|
||||
const bool adv = pref.quantization.advanced();
|
||||
advancedCheck()->setSelected(adv);
|
||||
@ -240,6 +243,7 @@ public:
|
||||
// Signals
|
||||
m_ditheringSelector->Change.connect([this]{ onIndexParamChange(); });
|
||||
m_mapAlgorithmSelector->Change.connect([this]{ onIndexParamChange(); });
|
||||
m_bestFitCriteriaSelector->Change.connect([this]{ onIndexParamChange(); });
|
||||
factor()->Change.connect([this]{ onIndexParamChange(); });
|
||||
|
||||
advancedCheck()->Click.connect(
|
||||
@ -301,6 +305,13 @@ public:
|
||||
return doc::RgbMapAlgorithm::DEFAULT;
|
||||
}
|
||||
|
||||
doc::FitCriteria fitCriteria() const {
|
||||
if (m_bestFitCriteriaSelector)
|
||||
return m_bestFitCriteriaSelector->criteria();
|
||||
else
|
||||
return doc::FitCriteria::DEFAULT;
|
||||
}
|
||||
|
||||
gen::ToGrayAlgorithm toGray() const {
|
||||
static_assert(
|
||||
int(gen::ToGrayAlgorithm::LUMA) == 0 &&
|
||||
@ -331,7 +342,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mapAlgorithmSelector)
|
||||
if (m_mapAlgorithmSelector || m_bestFitCriteriaSelector)
|
||||
pref.quantization.advanced(advancedCheck()->isSelected());
|
||||
}
|
||||
|
||||
@ -396,6 +407,12 @@ private:
|
||||
visibleBounds.origin(),
|
||||
doc::BlendMode::SRC);
|
||||
|
||||
m_editor->sprite()->rgbMap(
|
||||
0,
|
||||
m_editor->sprite()->rgbMapForSprite(),
|
||||
rgbMapAlgorithm(),
|
||||
fitCriteria());
|
||||
|
||||
m_editor->invalidate();
|
||||
progress()->setValue(0);
|
||||
progress()->setVisible(false);
|
||||
@ -408,7 +425,6 @@ private:
|
||||
m_editor->frame(),
|
||||
dstPixelFormat,
|
||||
dithering(),
|
||||
rgbMapAlgorithm(),
|
||||
toGray(),
|
||||
visibleBounds.origin(),
|
||||
Preferences::instance().experimental.newBlend()));
|
||||
@ -463,6 +479,7 @@ private:
|
||||
ConversionItem* m_selectedItem;
|
||||
DitheringSelector* m_ditheringSelector;
|
||||
RgbMapAlgorithmSelector* m_mapAlgorithmSelector;
|
||||
BestFitCriteriaSelector* m_bestFitCriteriaSelector;
|
||||
bool m_imageJustCreated;
|
||||
};
|
||||
|
||||
@ -485,6 +502,7 @@ private:
|
||||
doc::PixelFormat m_format;
|
||||
render::Dithering m_dithering;
|
||||
doc::RgbMapAlgorithm m_rgbmap;
|
||||
doc::FitCriteria m_fitCriteria = FitCriteria::DEFAULT;
|
||||
gen::ToGrayAlgorithm m_toGray;
|
||||
};
|
||||
|
||||
@ -624,7 +642,10 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
{
|
||||
bool flatten = false;
|
||||
|
||||
if (context->isUIAvailable() && m_showDlg) {
|
||||
if (!context->isUIAvailable()) {
|
||||
// do nothing
|
||||
}
|
||||
else if (m_showDlg) {
|
||||
ColorModeWindow window(Editor::activeEditor());
|
||||
|
||||
window.remapWindow();
|
||||
@ -640,11 +661,17 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
m_format = window.pixelFormat();
|
||||
m_dithering = window.dithering();
|
||||
m_rgbmap = window.rgbMapAlgorithm();
|
||||
m_fitCriteria = window.fitCriteria();
|
||||
m_toGray = window.toGray();
|
||||
flatten = window.flattenEnabled();
|
||||
|
||||
window.saveOptions();
|
||||
}
|
||||
else {
|
||||
// TO DO: in a first approach a simple conversion to indexed color mode
|
||||
// it's just via the old fit criteria (Euclidean color distance).
|
||||
m_fitCriteria = FitCriteria::DEFAULT;
|
||||
}
|
||||
|
||||
// No conversion needed
|
||||
Doc* doc = context->activeDocument();
|
||||
@ -679,7 +706,8 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
m_dithering,
|
||||
m_rgbmap,
|
||||
get_gray_func(m_toGray),
|
||||
&job)); // SpriteJob is a render::TaskDelegate
|
||||
&job,
|
||||
m_fitCriteria)); // SpriteJob is a render::TaskDelegate
|
||||
});
|
||||
job.waitJob();
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "app/recent_files.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/best_fit_criteria_selector.h"
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/main_window.h"
|
||||
#include "app/ui/pref_widget.h"
|
||||
@ -478,6 +479,10 @@ public:
|
||||
m_rgbmapAlgorithmSelector.setExpansive(true);
|
||||
m_rgbmapAlgorithmSelector.algorithm(m_pref.quantization.rgbmapAlgorithm());
|
||||
|
||||
bestFitCriteriaPlaceholder()->addChild(&m_bestFitCriteriaSelector);
|
||||
m_bestFitCriteriaSelector.setExpansive(true);
|
||||
m_bestFitCriteriaSelector.criteria(m_pref.quantization.fitCriteria());
|
||||
|
||||
if (m_pref.editor.showScrollbars())
|
||||
showScrollbars()->setSelected(true);
|
||||
|
||||
@ -828,6 +833,7 @@ public:
|
||||
m_pref.experimental.flashLayer(flashLayer()->isSelected());
|
||||
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
|
||||
m_pref.quantization.rgbmapAlgorithm(m_rgbmapAlgorithmSelector.algorithm());
|
||||
m_pref.quantization.fitCriteria(m_bestFitCriteriaSelector.criteria());
|
||||
|
||||
#ifdef LAF_WINDOWS
|
||||
{
|
||||
@ -1842,6 +1848,7 @@ private:
|
||||
std::vector<os::ColorSpaceRef> m_colorSpaces;
|
||||
std::string m_templateTextForDisplayCS;
|
||||
RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector;
|
||||
BestFitCriteriaSelector m_bestFitCriteriaSelector;
|
||||
ButtonSet* m_themeVars = nullptr;
|
||||
SamplingSelector* m_samplingSelector = nullptr;
|
||||
};
|
||||
|
@ -342,6 +342,7 @@ FOR_ENUM(app::tools::RotationAlgorithm)
|
||||
FOR_ENUM(doc::AniDir)
|
||||
FOR_ENUM(doc::BrushPattern)
|
||||
FOR_ENUM(doc::ColorMode)
|
||||
FOR_ENUM(doc::FitCriteria)
|
||||
FOR_ENUM(doc::RgbMapAlgorithm)
|
||||
FOR_ENUM(filters::HueSaturationFilter::Mode)
|
||||
FOR_ENUM(filters::TiledMode)
|
||||
|
46
src/app/ui/best_fit_criteria_selector.cpp
Normal file
46
src/app/ui/best_fit_criteria_selector.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2024 Igara Studio S.A.
|
||||
//
|
||||
// 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/ui/best_fit_criteria_selector.h"
|
||||
|
||||
#include "app/i18n/strings.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
BestFitCriteriaSelector::BestFitCriteriaSelector()
|
||||
{
|
||||
// addItem() must match the FitCriteria enum
|
||||
static_assert(int(doc::FitCriteria::DEFAULT) == 0 &&
|
||||
int(doc::FitCriteria::RGB) == 1 &&
|
||||
int(doc::FitCriteria::linearizedRGB) == 2 &&
|
||||
int(doc::FitCriteria::CIEXYZ) == 3 &&
|
||||
int(doc::FitCriteria::CIELAB) == 4,
|
||||
"Unexpected doc::FitCriteria values");
|
||||
|
||||
addItem(Strings::best_fit_criteria_selector_default());
|
||||
addItem(Strings::best_fit_criteria_selector_rgb());
|
||||
addItem(Strings::best_fit_criteria_selector_linearized_rgb());
|
||||
addItem(Strings::best_fit_criteria_selector_cie_xyz());
|
||||
addItem(Strings::best_fit_criteria_selector_cie_lab());
|
||||
|
||||
criteria(doc::FitCriteria::DEFAULT);
|
||||
}
|
||||
|
||||
doc::FitCriteria BestFitCriteriaSelector::criteria()
|
||||
{
|
||||
return (doc::FitCriteria)getSelectedItemIndex();
|
||||
}
|
||||
|
||||
void BestFitCriteriaSelector::criteria(const doc::FitCriteria criteria)
|
||||
{
|
||||
setSelectedItemIndex((int)criteria);
|
||||
}
|
||||
|
||||
} // namespace app
|
27
src/app/ui/best_fit_criteria_selector.h
Normal file
27
src/app/ui/best_fit_criteria_selector.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2024 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UI_BEST_FIT_CRITERIA_SELECTOR_H_INCLUDED
|
||||
#define APP_UI_BEST_FIT_CRITERIA_SELECTOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/fit_criteria.h"
|
||||
#include "doc/palette.h"
|
||||
#include "ui/combobox.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
class BestFitCriteriaSelector : public ui::ComboBox {
|
||||
public:
|
||||
BestFitCriteriaSelector();
|
||||
|
||||
doc::FitCriteria criteria();
|
||||
void criteria(doc::FitCriteria criteria);
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -61,6 +61,7 @@ add_library(doc-lib
|
||||
primitives.cpp
|
||||
remap.cpp
|
||||
render_plan.cpp
|
||||
rgbmap_base.cpp
|
||||
rgbmap_rgb5a3.cpp
|
||||
selected_frames.cpp
|
||||
selected_layers.cpp
|
||||
|
23
src/doc/fit_criteria.h
Normal file
23
src/doc/fit_criteria.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (C) 2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_FIT_CRITERIA_H_INCLUDED
|
||||
#define DOC_FIT_CRITERIA_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace doc {
|
||||
|
||||
enum class FitCriteria {
|
||||
DEFAULT = 0,
|
||||
RGB = 1,
|
||||
linearizedRGB = 2,
|
||||
CIEXYZ = 3,
|
||||
CIELAB = 4
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -35,19 +35,21 @@ void OctreeNode::addColor(color_t c, int level, OctreeNode* parent,
|
||||
(*m_children)[index].addColor(c, level + 1, this, paletteIndex, levelDeep);
|
||||
}
|
||||
|
||||
int OctreeNode::mapColor(int r, int g, int b, int a, int mask_index, const Palette* palette, int level) const
|
||||
int OctreeNode::mapColor(int r, int g, int b, int a, int mask_index,
|
||||
const Palette* palette, int level,
|
||||
const OctreeMap* octree) const
|
||||
{
|
||||
// New behavior: if mapColor do not have an exact rgba match, it must calculate which
|
||||
// color of the current palette is the bestfit and memorize the index in a octree leaf.
|
||||
if (level >= 8) {
|
||||
if (m_paletteIndex == -1)
|
||||
m_paletteIndex = palette->findBestfit(r, g, b, a, mask_index);
|
||||
m_paletteIndex = octree->findBestfit(r, g, b, a, mask_index);
|
||||
return m_paletteIndex;
|
||||
}
|
||||
int index = getHextet(r, g, b, a, level);
|
||||
if (!m_children)
|
||||
m_children.reset(new std::array<OctreeNode, 16>());
|
||||
return (*m_children)[index].mapColor(r, g, b, a, mask_index, palette, level + 1);
|
||||
return (*m_children)[index].mapColor(r, g, b, a, mask_index, palette, level + 1, octree);
|
||||
}
|
||||
|
||||
void OctreeNode::collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex)
|
||||
@ -268,10 +270,13 @@ int OctreeMap::mapColor(color_t rgba) const
|
||||
rgba_getb(rgba),
|
||||
rgba_geta(rgba),
|
||||
m_maskIndex,
|
||||
m_palette, 0);
|
||||
m_palette, 0,
|
||||
this);
|
||||
}
|
||||
|
||||
void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
|
||||
void OctreeMap::regenerateMap(const Palette* palette,
|
||||
const int maskIndex,
|
||||
const FitCriteria fitCriteria)
|
||||
{
|
||||
ASSERT(palette);
|
||||
if (!palette)
|
||||
@ -280,9 +285,12 @@ void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
|
||||
// Skip useless regenerations
|
||||
if (m_palette == palette &&
|
||||
m_modifications == palette->getModifications() &&
|
||||
m_maskIndex == maskIndex)
|
||||
m_maskIndex == maskIndex &&
|
||||
m_fitCriteria == fitCriteria)
|
||||
return;
|
||||
|
||||
m_palette = palette;
|
||||
m_fitCriteria = fitCriteria;
|
||||
m_root = OctreeNode();
|
||||
m_leavesVector.clear();
|
||||
m_maskIndex = maskIndex;
|
||||
@ -293,10 +301,10 @@ void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
|
||||
}
|
||||
else {
|
||||
m_maskColor = palette->getEntry(maskIndex);
|
||||
maskColorBestFitIndex = palette->findBestfit(rgba_getr(m_maskColor),
|
||||
rgba_getg(m_maskColor),
|
||||
rgba_getb(m_maskColor),
|
||||
rgba_geta(m_maskColor), maskIndex);
|
||||
maskColorBestFitIndex = findBestfit(rgba_getr(m_maskColor),
|
||||
rgba_getg(m_maskColor),
|
||||
rgba_getb(m_maskColor),
|
||||
rgba_geta(m_maskColor), maskIndex);
|
||||
}
|
||||
|
||||
for (int i=0; i<palette->size(); i++) {
|
||||
@ -307,7 +315,6 @@ void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
|
||||
m_root.addColor(palette->entry(i), 0, &m_root, i, 8);
|
||||
}
|
||||
|
||||
m_palette = palette;
|
||||
m_modifications = palette->getModifications();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2020-2022 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -11,7 +11,7 @@
|
||||
#include "doc/color.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/rgbmap_base.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
@ -26,6 +26,8 @@
|
||||
namespace doc {
|
||||
|
||||
class OctreeNode;
|
||||
class OctreeMap;
|
||||
|
||||
using OctreeNodes = std::vector<OctreeNode*>;
|
||||
|
||||
class OctreeNode {
|
||||
@ -93,7 +95,9 @@ public:
|
||||
void addColor(color_t c, int level, OctreeNode* parent,
|
||||
int paletteIndex = 0, int levelDeep = 7);
|
||||
|
||||
int mapColor(int r, int g, int b, int a, int mask_index, const Palette* palette, int level) const;
|
||||
int mapColor(int r, int g, int b, int a, int mask_index,
|
||||
const Palette* palette, int level,
|
||||
const OctreeMap* octree) const;
|
||||
|
||||
void collectLeafNodes(OctreeNodes& leavesVector, int& paletteIndex);
|
||||
|
||||
@ -117,7 +121,7 @@ private:
|
||||
OctreeNode* m_parent = nullptr;
|
||||
};
|
||||
|
||||
class OctreeMap : public RgbMap {
|
||||
class OctreeMap : public RgbMapBase {
|
||||
public:
|
||||
void addColor(color_t color, int levelDeep = 7) {
|
||||
m_root.addColor(color, 0, &m_root, 0, levelDeep);
|
||||
@ -135,27 +139,23 @@ public:
|
||||
const int levelDeep = 7);
|
||||
|
||||
// RgbMap impl
|
||||
void regenerateMap(const Palette* palette, const int maskIndex) override;
|
||||
int mapColor(color_t rgba) const override;
|
||||
int maskIndex() const override { return m_maskIndex; }
|
||||
int mapColor(const int r, const int g,
|
||||
const int b, const int a) const
|
||||
{
|
||||
ASSERT(r >= 0 && r < 256);
|
||||
ASSERT(g >= 0 && g < 256);
|
||||
ASSERT(b >= 0 && b < 256);
|
||||
ASSERT(a >= 0 && a < 256);
|
||||
return mapColor(rgba(r, g, b, a));
|
||||
void regenerateMap(const Palette* palette,
|
||||
const int maskIndex,
|
||||
const FitCriteria fitCriteria) override;
|
||||
void regenerateMap(const Palette* palette,
|
||||
const int maskIndex) override {
|
||||
regenerateMap(palette, maskIndex, m_fitCriteria);
|
||||
}
|
||||
|
||||
int moodifications() const { return m_modifications; };
|
||||
int mapColor(color_t rgba) const override;
|
||||
|
||||
RgbMapAlgorithm rgbmapAlgorithm() const override {
|
||||
return RgbMapAlgorithm::OCTREE;
|
||||
}
|
||||
|
||||
private:
|
||||
OctreeNode m_root;
|
||||
OctreeNodes m_leavesVector;
|
||||
const Palette* m_palette = nullptr;
|
||||
int m_modifications = 0;
|
||||
int m_maskIndex = 0;
|
||||
color_t m_maskColor = 0;
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2024 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -26,14 +26,6 @@ namespace doc {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
enum class FitCriteria {
|
||||
OLD,
|
||||
RGB,
|
||||
linearizedRGB,
|
||||
CIEXYZ,
|
||||
CIELAB
|
||||
};
|
||||
|
||||
Palette::Palette()
|
||||
: Palette(0, 256)
|
||||
{
|
||||
@ -346,154 +338,46 @@ void Palette::initBestfit()
|
||||
}
|
||||
}
|
||||
|
||||
// Auxiliary function for rgbToOtherSpace()
|
||||
static double f(double t)
|
||||
{
|
||||
if (t > 0.00885645171)
|
||||
return std::pow(t, 0.3333333333333333);
|
||||
else
|
||||
return (t / 0.12841855 + 0.137931034);
|
||||
}
|
||||
|
||||
// Auxiliary function for findBestfit()
|
||||
static void rgbToOtherSpace(double& r, double& g, double& b, FitCriteria fc)
|
||||
{
|
||||
if (fc == FitCriteria::RGB)
|
||||
return;
|
||||
double Rl, Gl, Bl;
|
||||
// Linearization:
|
||||
r = r / 255.0;
|
||||
g = g / 255.0;
|
||||
b = b / 255.0;
|
||||
if (r <= 0.04045)
|
||||
Rl = r / 12.92;
|
||||
else
|
||||
Rl = std::pow((r + 0.055) / 1.055, 2.4);
|
||||
if (g <= 0.04045)
|
||||
Gl = g / 12.92;
|
||||
else
|
||||
Gl = std::pow((g + 0.055) / 1.055, 2.4);
|
||||
if (b <= 0.04045)
|
||||
Bl = b / 12.92;
|
||||
else
|
||||
Bl = std::pow((b + 0.055) / 1.055, 2.4);
|
||||
if (fc == FitCriteria::linearizedRGB) {
|
||||
r = Rl;
|
||||
g = Gl;
|
||||
b = Bl;
|
||||
return;
|
||||
}
|
||||
// Conversion lineal RGB to CIE XYZ
|
||||
r = 41.24564*Rl + 35.75761 * Gl + 18.04375 * Bl;
|
||||
g = 21.26729*Rl + 71.51522 * Gl + 7.2175 * Bl;
|
||||
b = 1.93339*Rl + 11.91920 * Gl + 95.03041 * Bl;
|
||||
switch (fc) {
|
||||
|
||||
case FitCriteria::CIEXYZ:
|
||||
return;
|
||||
|
||||
case FitCriteria::CIELAB: {
|
||||
// Converting CIEXYZ to CIELAB:
|
||||
// For Standard Illuminant D65:
|
||||
// const double xn = 95.0489;
|
||||
// const double yn = 100.0;
|
||||
// const double zn = 108.884;
|
||||
double xxn = r / 95.0489;
|
||||
double yyn = g / 100.0;
|
||||
double zzn = b / 108.884;
|
||||
double fyyn = f(yyn);
|
||||
|
||||
double Lstar = 116.0 * fyyn - 16.0;
|
||||
double aStar = 500.0 * (f(xxn) - fyyn);
|
||||
double bStar = 200.0 * (fyyn - f(zzn));
|
||||
|
||||
r = Lstar;
|
||||
g = aStar;
|
||||
b = bStar;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Palette::findBestfit(int r, int g, int b, int a, int mask_index) const
|
||||
{
|
||||
ASSERT(r >= 0 && r <= 255);
|
||||
ASSERT(g >= 0 && g <= 255);
|
||||
ASSERT(b >= 0 && b <= 255);
|
||||
ASSERT(a >= 0 && a <= 255);
|
||||
ASSERT(!col_diff.empty());
|
||||
|
||||
FitCriteria fc = FitCriteria::OLD;
|
||||
|
||||
if (fc == FitCriteria::OLD) {
|
||||
ASSERT(!col_diff.empty());
|
||||
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
a >>= 3;
|
||||
|
||||
// Mask index is like alpha = 0, so we can use it as transparent color.
|
||||
if (a == 0 && mask_index >= 0)
|
||||
return mask_index;
|
||||
|
||||
int bestfit = 0;
|
||||
int lowest = std::numeric_limits<int>::max();
|
||||
int size = std::min(256, int(m_colors.size()));
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
color_t rgb = m_colors[i];
|
||||
|
||||
int coldiff = col_diff_g[((rgba_getg(rgb)>>3) - g) & 127];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_r[(((rgba_getr(rgb)>>3) - r) & 127)];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_b[(((rgba_getb(rgb)>>3) - b) & 127)];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_a[(((rgba_geta(rgb)>>3) - a) & 127)];
|
||||
if (coldiff < lowest && i != mask_index) {
|
||||
if (coldiff == 0)
|
||||
return i;
|
||||
|
||||
bestfit = i;
|
||||
lowest = coldiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestfit;
|
||||
}
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
a >>= 3;
|
||||
|
||||
// Mask index is like alpha = 0, so we can use it as transparent color.
|
||||
if (a == 0 && mask_index >= 0)
|
||||
return mask_index;
|
||||
|
||||
int bestfit = 0;
|
||||
double lowest = std::numeric_limits<double>::max();
|
||||
int size = m_colors.size();
|
||||
// Linearice:
|
||||
double x = double(r);
|
||||
double y = double(g);
|
||||
double z = double(b);
|
||||
|
||||
rgbToOtherSpace(x, y, z, fc);
|
||||
int lowest = std::numeric_limits<int>::max();
|
||||
int size = std::min(256, int(m_colors.size()));
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
color_t rgb = m_colors[i];
|
||||
double Xpal = double(rgba_getr(rgb));
|
||||
double Ypal = double(rgba_getg(rgb));
|
||||
double Zpal = double(rgba_getb(rgb));
|
||||
// Palette color conversion RGB-->XYZ and r,g,b is assumed CIE XYZ
|
||||
rgbToOtherSpace(Xpal, Ypal, Zpal, fc);
|
||||
double xDiff = x - Xpal;
|
||||
double yDiff = y - Ypal;
|
||||
double zDiff = z - Zpal;
|
||||
double aDiff = double(a - rgba_geta(rgb)) / 128.0;
|
||||
|
||||
double diff = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff + aDiff * aDiff;
|
||||
if (diff < lowest) {
|
||||
lowest = diff;
|
||||
bestfit = i;
|
||||
int coldiff = col_diff_g[((rgba_getg(rgb)>>3) - g) & 127];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_r[(((rgba_getr(rgb)>>3) - r) & 127)];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_b[(((rgba_getb(rgb)>>3) - b) & 127)];
|
||||
if (coldiff < lowest) {
|
||||
coldiff += col_diff_a[(((rgba_geta(rgb)>>3) - a) & 127)];
|
||||
if (coldiff < lowest && i != mask_index) {
|
||||
if (coldiff == 0)
|
||||
return i;
|
||||
|
||||
bestfit = i;
|
||||
lowest = coldiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestfit;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020-2022 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/fit_criteria.h"
|
||||
#include "doc/rgbmap_algorithm.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
@ -20,13 +22,26 @@ namespace doc {
|
||||
public:
|
||||
virtual ~RgbMap() { }
|
||||
|
||||
virtual void regenerateMap(const Palette* palette, const int maskIndex) = 0;
|
||||
virtual void regenerateMap(const Palette* palette,
|
||||
const int maskIndex,
|
||||
const FitCriteria fitCriteria) = 0;
|
||||
|
||||
virtual void regenerateMap(const Palette* palette,
|
||||
const int maskIndex) = 0;
|
||||
|
||||
// Should return the best index in a palette that matches the given RGBA values.
|
||||
virtual int mapColor(const color_t rgba) const = 0;
|
||||
|
||||
virtual int maskIndex() const = 0;
|
||||
|
||||
virtual RgbMapAlgorithm rgbmapAlgorithm() const = 0;
|
||||
|
||||
virtual int modifications() const = 0;
|
||||
|
||||
// Color Best Fit Criteria used to generate the rgbmap
|
||||
virtual FitCriteria fitCriteria() const = 0;
|
||||
virtual void fitCriteria(const FitCriteria fitCriteria) = 0;
|
||||
|
||||
int mapColor(const int r,
|
||||
const int g,
|
||||
const int b,
|
||||
|
131
src/doc/rgbmap_base.cpp
Normal file
131
src/doc/rgbmap_base.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2024 Igara Studio S.A.
|
||||
//
|
||||
// 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/rgbmap_base.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Auxiliary function for rgbToOtherSpace()
|
||||
double f(double t)
|
||||
{
|
||||
if (t > 0.00885645171)
|
||||
return std::pow(t, 0.3333333333333333);
|
||||
else
|
||||
return (t / 0.12841855 + 0.137931034);
|
||||
}
|
||||
|
||||
// Auxiliary function for findBestfit()
|
||||
void RgbMapBase::rgbToOtherSpace(double& r, double& g, double& b) const
|
||||
{
|
||||
if (m_fitCriteria == FitCriteria::RGB)
|
||||
return;
|
||||
double Rl, Gl, Bl;
|
||||
// Linearization:
|
||||
r = r / 255.0;
|
||||
g = g / 255.0;
|
||||
b = b / 255.0;
|
||||
if (r <= 0.04045)
|
||||
Rl = r / 12.92;
|
||||
else
|
||||
Rl = std::pow((r + 0.055) / 1.055, 2.4);
|
||||
if (g <= 0.04045)
|
||||
Gl = g / 12.92;
|
||||
else
|
||||
Gl = std::pow((g + 0.055) / 1.055, 2.4);
|
||||
if (b <= 0.04045)
|
||||
Bl = b / 12.92;
|
||||
else
|
||||
Bl = std::pow((b + 0.055) / 1.055, 2.4);
|
||||
if (m_fitCriteria == FitCriteria::linearizedRGB) {
|
||||
r = Rl;
|
||||
g = Gl;
|
||||
b = Bl;
|
||||
return;
|
||||
}
|
||||
// Conversion lineal RGB to CIE XYZ
|
||||
r = 41.24564*Rl + 35.75761 * Gl + 18.04375 * Bl;
|
||||
g = 21.26729*Rl + 71.51522 * Gl + 7.2175 * Bl;
|
||||
b = 1.93339*Rl + 11.91920 * Gl + 95.03041 * Bl;
|
||||
switch (m_fitCriteria) {
|
||||
|
||||
case FitCriteria::CIEXYZ:
|
||||
return;
|
||||
|
||||
case FitCriteria::CIELAB: {
|
||||
// Converting CIEXYZ to CIELAB:
|
||||
// For Standard Illuminant D65:
|
||||
// const double xn = 95.0489;
|
||||
// const double yn = 100.0;
|
||||
// const double zn = 108.884;
|
||||
double xxn = r / 95.0489;
|
||||
double yyn = g / 100.0;
|
||||
double zzn = b / 108.884;
|
||||
double fyyn = f(yyn);
|
||||
|
||||
double Lstar = 116.0 * fyyn - 16.0;
|
||||
double aStar = 500.0 * (f(xxn) - fyyn);
|
||||
double bStar = 200.0 * (fyyn - f(zzn));
|
||||
|
||||
r = Lstar;
|
||||
g = aStar;
|
||||
b = bStar;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RgbMapBase::findBestfit(int r, int g, int b, int a,
|
||||
int mask_index) const
|
||||
{
|
||||
ASSERT(r >= 0 && r <= 255);
|
||||
ASSERT(g >= 0 && g <= 255);
|
||||
ASSERT(b >= 0 && b <= 255);
|
||||
ASSERT(a >= 0 && a <= 255);
|
||||
|
||||
if (m_fitCriteria == FitCriteria::DEFAULT)
|
||||
return m_palette->findBestfit(r, g, b, a, mask_index);
|
||||
|
||||
if (a == 0 && mask_index >= 0)
|
||||
return mask_index;
|
||||
|
||||
int bestfit = 0;
|
||||
double lowest = std::numeric_limits<double>::max();
|
||||
const int size = m_palette->size();
|
||||
// Linearice:
|
||||
double x = double(r);
|
||||
double y = double(g);
|
||||
double z = double(b);
|
||||
|
||||
rgbToOtherSpace(x, y, z);
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
color_t rgb = m_palette->getEntry(i);
|
||||
double Xpal = double(rgba_getr(rgb));
|
||||
double Ypal = double(rgba_getg(rgb));
|
||||
double Zpal = double(rgba_getb(rgb));
|
||||
// Palette color conversion RGB-->XYZ and r,g,b is assumed CIE XYZ
|
||||
rgbToOtherSpace(Xpal, Ypal, Zpal);
|
||||
const double xDiff = x - Xpal;
|
||||
const double yDiff = y - Ypal;
|
||||
const double zDiff = z - Zpal;
|
||||
const double aDiff = double(a - rgba_geta(rgb)) / 128.0;
|
||||
|
||||
double diff = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff + aDiff * aDiff;
|
||||
if (diff < lowest) {
|
||||
lowest = diff;
|
||||
bestfit = i;
|
||||
}
|
||||
}
|
||||
return bestfit;
|
||||
}
|
||||
|
||||
} // namespace doc
|
42
src/doc/rgbmap_base.h
Normal file
42
src/doc/rgbmap_base.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_RGBMAP_BASE_H_INCLUDED
|
||||
#define DOC_RGBMAP_BASE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/fit_criteria.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/rgbmap.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
class RgbMapBase : public RgbMap {
|
||||
public:
|
||||
int findBestfit(int r, int g, int b, int a,
|
||||
int mask_index) const;
|
||||
|
||||
// RgbMap impl
|
||||
int modifications() const override { return m_modifications; }
|
||||
int maskIndex() const override { return m_maskIndex; }
|
||||
FitCriteria fitCriteria() const override { return m_fitCriteria; }
|
||||
void fitCriteria(const FitCriteria fitCriteria) override {
|
||||
m_fitCriteria = fitCriteria;
|
||||
}
|
||||
|
||||
private:
|
||||
void rgbToOtherSpace(double& r, double& g, double& b) const;
|
||||
|
||||
protected:
|
||||
FitCriteria m_fitCriteria;
|
||||
const Palette* m_palette = nullptr;
|
||||
int m_modifications = 0;
|
||||
int m_maskIndex = 0;
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2024 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -22,23 +22,21 @@ namespace doc {
|
||||
#define ASIZE 8
|
||||
#define MAPSIZE (RSIZE*GSIZE*BSIZE*ASIZE)
|
||||
|
||||
RgbMapRGB5A3::RgbMapRGB5A3()
|
||||
: m_map(MAPSIZE)
|
||||
, m_palette(nullptr)
|
||||
, m_modifications(0)
|
||||
, m_maskIndex(0)
|
||||
{
|
||||
}
|
||||
RgbMapRGB5A3::RgbMapRGB5A3() : m_map(MAPSIZE) {}
|
||||
|
||||
void RgbMapRGB5A3::regenerateMap(const Palette* palette, int maskIndex)
|
||||
void RgbMapRGB5A3::regenerateMap(const Palette* palette,
|
||||
const int maskIndex,
|
||||
const FitCriteria fitCriteria)
|
||||
{
|
||||
// Skip useless regenerations
|
||||
if (m_palette == palette &&
|
||||
m_modifications == palette->getModifications() &&
|
||||
m_maskIndex == maskIndex)
|
||||
m_maskIndex == maskIndex &&
|
||||
m_fitCriteria == fitCriteria)
|
||||
return;
|
||||
|
||||
m_palette = palette;
|
||||
m_fitCriteria = fitCriteria;
|
||||
m_modifications = palette->getModifications();
|
||||
m_maskIndex = maskIndex;
|
||||
|
||||
@ -50,7 +48,7 @@ void RgbMapRGB5A3::regenerateMap(const Palette* palette, int maskIndex)
|
||||
int RgbMapRGB5A3::generateEntry(int i, int r, int g, int b, int a) const
|
||||
{
|
||||
return m_map[i] =
|
||||
m_palette->findBestfit(
|
||||
findBestfit(
|
||||
scale_5bits_to_8bits(r>>3),
|
||||
scale_5bits_to_8bits(g>>3),
|
||||
scale_5bits_to_8bits(b>>3),
|
||||
|
@ -13,7 +13,8 @@
|
||||
#include "base/disable_copying.h"
|
||||
#include "base/ints.h"
|
||||
#include "doc/object.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/rgbmap_base.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -22,7 +23,7 @@ namespace doc {
|
||||
class Palette;
|
||||
|
||||
// It acts like a cache for Palette:findBestfit() calls.
|
||||
class RgbMapRGB5A3 : public RgbMap {
|
||||
class RgbMapRGB5A3 : public RgbMapBase {
|
||||
// Bit activated on m_map entries that aren't yet calculated.
|
||||
const uint16_t INVALID = 256;
|
||||
|
||||
@ -30,7 +31,14 @@ namespace doc {
|
||||
RgbMapRGB5A3();
|
||||
|
||||
// RgbMap impl
|
||||
void regenerateMap(const Palette* palette, int maskIndex) override;
|
||||
void regenerateMap(const Palette* palette,
|
||||
const int maskIndex,
|
||||
const FitCriteria fitCriteria) override;
|
||||
void regenerateMap(const Palette* palette,
|
||||
const int maskIndex) override {
|
||||
regenerateMap(palette, maskIndex, m_fitCriteria);
|
||||
}
|
||||
|
||||
int mapColor(const color_t rgba) const override {
|
||||
const uint8_t r = rgba_getr(rgba);
|
||||
const uint8_t g = rgba_getg(rgba);
|
||||
@ -42,15 +50,14 @@ namespace doc {
|
||||
return (v & INVALID) ? generateEntry(i, r, g, b, a): v;
|
||||
}
|
||||
|
||||
int maskIndex() const override { return m_maskIndex; }
|
||||
RgbMapAlgorithm rgbmapAlgorithm() const override {
|
||||
return RgbMapAlgorithm::RGB5A3;
|
||||
}
|
||||
|
||||
private:
|
||||
int generateEntry(int i, int r, int g, int b, int a) const;
|
||||
|
||||
mutable std::vector<uint16_t> m_map;
|
||||
const Palette* m_palette;
|
||||
int m_modifications;
|
||||
int m_maskIndex;
|
||||
|
||||
DISABLE_COPYING(RgbMapRGB5A3);
|
||||
};
|
||||
|
@ -440,18 +440,24 @@ RgbMap* Sprite::rgbMap(const frame_t frame) const
|
||||
RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||
const RgbMapFor forLayer) const
|
||||
{
|
||||
return rgbMap(frame,
|
||||
forLayer,
|
||||
g_rgbMapAlgorithm);
|
||||
FitCriteria fc = FitCriteria::DEFAULT;
|
||||
RgbMapAlgorithm algo = g_rgbMapAlgorithm;
|
||||
if (m_rgbMap) {
|
||||
fc = m_rgbMap->fitCriteria();
|
||||
algo = m_rgbMap->rgbmapAlgorithm();
|
||||
}
|
||||
return rgbMap(frame, forLayer, algo, fc);
|
||||
}
|
||||
|
||||
RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||
const RgbMapFor forLayer,
|
||||
RgbMapAlgorithm mapAlgo) const
|
||||
const RgbMapAlgorithm mapAlgo,
|
||||
const FitCriteria fitCriteria) const
|
||||
{
|
||||
if (!m_rgbMap || m_rgbMapAlgorithm != mapAlgo) {
|
||||
m_rgbMapAlgorithm = mapAlgo;
|
||||
switch (m_rgbMapAlgorithm) {
|
||||
if (!m_rgbMap ||
|
||||
m_rgbMap->rgbmapAlgorithm() != mapAlgo ||
|
||||
m_rgbMap->fitCriteria() != fitCriteria) {
|
||||
switch (mapAlgo) {
|
||||
case RgbMapAlgorithm::RGB5A3: m_rgbMap.reset(new RgbMapRGB5A3); break;
|
||||
case RgbMapAlgorithm::DEFAULT:
|
||||
case RgbMapAlgorithm::OCTREE: m_rgbMap.reset(new OctreeMap); break;
|
||||
@ -460,6 +466,7 @@ RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||
ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
m_rgbMap->fitCriteria(fitCriteria);
|
||||
}
|
||||
int maskIndex;
|
||||
if (forLayer == RgbMapFor::OpaqueLayer)
|
||||
@ -469,7 +476,7 @@ RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||
if (maskIndex == -1)
|
||||
maskIndex = 0;
|
||||
}
|
||||
m_rgbMap->regenerateMap(palette(frame), maskIndex);
|
||||
m_rgbMap->regenerateMap(palette(frame), maskIndex, fitCriteria);
|
||||
return m_rgbMap.get();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "doc/cel_data.h"
|
||||
#include "doc/cel_list.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/fit_criteria.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "doc/image_ref.h"
|
||||
@ -167,7 +168,8 @@ namespace doc {
|
||||
const RgbMapFor forLayer) const;
|
||||
RgbMap* rgbMap(const frame_t frame,
|
||||
const RgbMapFor forLayer,
|
||||
RgbMapAlgorithm mapAlgo) const;
|
||||
const RgbMapAlgorithm mapAlgo,
|
||||
const FitCriteria fitCriteria = FitCriteria::DEFAULT) const;
|
||||
|
||||
////////////////////////////////////////
|
||||
// Frames
|
||||
@ -263,7 +265,6 @@ namespace doc {
|
||||
gfx::Rect m_gridBounds; // grid settings
|
||||
|
||||
// Current rgb map
|
||||
mutable RgbMapAlgorithm m_rgbMapAlgorithm;
|
||||
mutable std::unique_ptr<RgbMap> m_rgbMap;
|
||||
|
||||
Tags m_tags;
|
||||
|
Loading…
x
Reference in New Issue
Block a user