mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-24 09:02:31 +00:00
Merge branch 'octree' into beta
This commit is contained in:
commit
bcf53fa54a
@ -197,6 +197,7 @@
|
|||||||
<option id="load_wintab_driver" type="bool" default="true" />
|
<option id="load_wintab_driver" type="bool" default="true" />
|
||||||
<option id="flash_layer" type="bool" default="false" />
|
<option id="flash_layer" type="bool" default="false" />
|
||||||
<option id="nonactive_layers_opacity" type="int" default="255" />
|
<option id="nonactive_layers_opacity" type="int" default="255" />
|
||||||
|
<option id="rgbmap_algorithm" type="doc::RgbMapAlgorithm" default="doc::RgbMapAlgorithm::DEFAULT" />
|
||||||
</section>
|
</section>
|
||||||
<section id="news">
|
<section id="news">
|
||||||
<option id="cache_file" type="std::string" />
|
<option id="cache_file" type="std::string" />
|
||||||
@ -265,6 +266,7 @@
|
|||||||
<option id="dithering_algorithm" type="std::string" />
|
<option id="dithering_algorithm" type="std::string" />
|
||||||
<option id="dithering_factor" type="int" default="100" />
|
<option id="dithering_factor" type="int" default="100" />
|
||||||
<option id="to_gray" type="ToGrayAlgorithm" default="ToGrayAlgorithm::DEFAULT" />
|
<option id="to_gray" type="ToGrayAlgorithm" default="ToGrayAlgorithm::DEFAULT" />
|
||||||
|
<option id="advanced" type="bool" default="false" />
|
||||||
</section>
|
</section>
|
||||||
<section id="eyedropper" text="Editor">
|
<section id="eyedropper" text="Editor">
|
||||||
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
|
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
|
||||||
|
@ -697,6 +697,7 @@ Check in case that you want to establish
|
|||||||
the given option as the default option.
|
the given option as the default option.
|
||||||
END
|
END
|
||||||
reset = Reset
|
reset = Reset
|
||||||
|
advanced_options = Advanced Options
|
||||||
|
|
||||||
[gif_options]
|
[gif_options]
|
||||||
title = GIF Options
|
title = GIF Options
|
||||||
@ -996,12 +997,16 @@ background = Background:
|
|||||||
transparent = &Transparent
|
transparent = &Transparent
|
||||||
white = &White
|
white = &White
|
||||||
black = &Black
|
black = &Black
|
||||||
advanced_options = Advanced Options
|
|
||||||
pixel_ratio = Pixel Aspect Ratio:
|
pixel_ratio = Pixel Aspect Ratio:
|
||||||
square_pixels = Square Pixels (1:1)
|
square_pixels = Square Pixels (1:1)
|
||||||
double_wide = Double-wide Pixels (2:1)
|
double_wide = Double-wide Pixels (2:1)
|
||||||
double_high = Double-high Pixels (1:2)
|
double_high = Double-high Pixels (1:2)
|
||||||
|
|
||||||
|
[rgbmap_algorithm_selector]
|
||||||
|
label = RGB to palette index mapping:
|
||||||
|
rgb5a3 = Table RGB 5 bits + Alpha 3 bits
|
||||||
|
octree = Octree without Alpha
|
||||||
|
|
||||||
[open_sequence]
|
[open_sequence]
|
||||||
title = Notice
|
title = Notice
|
||||||
description = Do you want to load the following files as an animation?
|
description = Do you want to load the following files as an animation?
|
||||||
@ -1291,7 +1296,7 @@ bg_color = Background Color:
|
|||||||
|
|
||||||
[palette_from_sprite]
|
[palette_from_sprite]
|
||||||
title = Palette from Sprite
|
title = Palette from Sprite
|
||||||
new_palette = Create a new palette with a specific number of colors (or less):
|
new_palette = Create new palette, color count limit:
|
||||||
replace_palette = Replace current palette
|
replace_palette = Replace current palette
|
||||||
replace_range = Replace current range
|
replace_range = Replace current range
|
||||||
alpha_channel = Create entries with alpha component
|
alpha_channel = Create entries with alpha component
|
||||||
|
@ -13,12 +13,21 @@
|
|||||||
<slider min="0" max="100" id="factor" minwidth="100" />
|
<slider min="0" max="100" id="factor" minwidth="100" />
|
||||||
<label text="%" />
|
<label text="%" />
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
<combobox id="to_gray_combobox">
|
<combobox id="to_gray_combobox">
|
||||||
<listitem text="!Luminance" />
|
<listitem text="!Luminance" />
|
||||||
<listitem text="!HSV" />
|
<listitem text="!HSV" />
|
||||||
<listitem text="!HSL" />
|
<listitem text="!HSL" />
|
||||||
</combobox>
|
</combobox>
|
||||||
|
|
||||||
<check text="@.flatten" id="flatten" />
|
<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>
|
||||||
|
|
||||||
<separator horizontal="true" />
|
<separator horizontal="true" />
|
||||||
<hbox>
|
<hbox>
|
||||||
<slider min="0" max="100" id="progress" minwidth="100" />
|
<slider min="0" max="100" id="progress" minwidth="100" />
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<!-- Aseprite -->
|
<!-- Aseprite -->
|
||||||
<!-- Copyright (C) 2001-2018 by David Capello -->
|
<!-- Copyright (c) 2020 Igara Studio S.A. -->
|
||||||
|
<!-- Copyright (c) 2001-2018 David Capello -->
|
||||||
<gui>
|
<gui>
|
||||||
<window id="new_sprite" text="@.title">
|
<window id="new_sprite" text="@.title">
|
||||||
<box vertical="true">
|
<box vertical="true">
|
||||||
@ -26,7 +27,7 @@
|
|||||||
<item text="@.black" icon="icon_black" />
|
<item text="@.black" icon="icon_black" />
|
||||||
</buttonset>
|
</buttonset>
|
||||||
|
|
||||||
<check id="advanced_check" text="@.advanced_options" />
|
<check id="advanced_check" text="@general.advanced_options" />
|
||||||
<vbox id="advanced">
|
<vbox id="advanced">
|
||||||
<label text="@.pixel_ratio" />
|
<label text="@.pixel_ratio" />
|
||||||
<combobox id="pixel_ratio" cell_align="horizontal">
|
<combobox id="pixel_ratio" cell_align="horizontal">
|
||||||
|
@ -504,6 +504,10 @@
|
|||||||
<label text="@.non_active_layer_opacity" />
|
<label text="@.non_active_layer_opacity" />
|
||||||
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />
|
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />
|
||||||
</hbox>
|
</hbox>
|
||||||
|
<hbox>
|
||||||
|
<label text="@rgbmap_algorithm_selector.label" />
|
||||||
|
<hbox id="rgbmap_algorithm_placeholder" />
|
||||||
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
</panel>
|
</panel>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<!-- Aseprite -->
|
<!-- Aseprite -->
|
||||||
<!-- Copyright (C) 2015-2018 by David Capello -->
|
<!-- Copyright (c) 2020 Igara Studio S.A. -->
|
||||||
|
<!-- Copyright (c) 2015-2018 David Capello -->
|
||||||
<gui>
|
<gui>
|
||||||
<window id="palette_from_sprite" text="@.title">
|
<window id="palette_from_sprite" text="@.title">
|
||||||
<grid columns="2">
|
<grid columns="2">
|
||||||
@ -7,7 +8,15 @@
|
|||||||
<expr expansive="true" id="ncolors" magnet="true" />
|
<expr expansive="true" id="ncolors" magnet="true" />
|
||||||
<radio id="current_palette" text="@.replace_palette" group="1" cell_hspan="2" />
|
<radio id="current_palette" text="@.replace_palette" group="1" cell_hspan="2" />
|
||||||
<radio id="current_range" text="@.replace_range" group="1" cell_hspan="2" />
|
<radio id="current_range" text="@.replace_range" group="1" cell_hspan="2" />
|
||||||
|
|
||||||
|
<separator horizontal="true" cell_hspan="2" />
|
||||||
|
|
||||||
<check id="alpha_channel" text="@.alpha_channel" cell_hspan="2" />
|
<check id="alpha_channel" text="@.alpha_channel" cell_hspan="2" />
|
||||||
|
<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>
|
||||||
|
|
||||||
<separator horizontal="true" cell_hspan="2" />
|
<separator horizontal="true" cell_hspan="2" />
|
||||||
|
|
||||||
|
@ -367,6 +367,7 @@ if(ENABLE_UI)
|
|||||||
ui/preview_editor.cpp
|
ui/preview_editor.cpp
|
||||||
ui/recent_listbox.cpp
|
ui/recent_listbox.cpp
|
||||||
ui/resources_listbox.cpp
|
ui/resources_listbox.cpp
|
||||||
|
ui/rgbmap_algorithm_selector.cpp
|
||||||
ui/search_entry.cpp
|
ui/search_entry.cpp
|
||||||
ui/select_accelerator.cpp
|
ui/select_accelerator.cpp
|
||||||
ui/selection_mode_field.cpp
|
ui/selection_mode_field.cpp
|
||||||
|
@ -69,6 +69,7 @@ private:
|
|||||||
SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||||
const PixelFormat newFormat,
|
const PixelFormat newFormat,
|
||||||
const render::Dithering& dithering,
|
const render::Dithering& dithering,
|
||||||
|
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||||
doc::rgba_to_graya_func toGray,
|
doc::rgba_to_graya_func toGray,
|
||||||
render::TaskDelegate* delegate)
|
render::TaskDelegate* delegate)
|
||||||
: WithSprite(sprite)
|
: WithSprite(sprite)
|
||||||
@ -79,6 +80,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
SuperDelegate superDel(sprite->uniqueCels().size(), delegate);
|
SuperDelegate superDel(sprite->uniqueCels().size(), delegate);
|
||||||
|
const auto rgbMapFor = sprite->rgbMapForSprite();
|
||||||
|
|
||||||
for (Cel* cel : sprite->uniqueCels()) {
|
for (Cel* cel : sprite->uniqueCels()) {
|
||||||
ImageRef old_image = cel->imageRef();
|
ImageRef old_image = cel->imageRef();
|
||||||
@ -86,7 +88,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
|||||||
render::convert_pixel_format
|
render::convert_pixel_format
|
||||||
(old_image.get(), nullptr, newFormat,
|
(old_image.get(), nullptr, newFormat,
|
||||||
dithering,
|
dithering,
|
||||||
sprite->rgbMap(cel->frame()),
|
sprite->rgbMap(cel->frame(), rgbMapFor, mapAlgorithm),
|
||||||
sprite->palette(cel->frame()),
|
sprite->palette(cel->frame()),
|
||||||
cel->layer()->isBackground(),
|
cel->layer()->isBackground(),
|
||||||
old_image->maskColor(),
|
old_image->maskColor(),
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "app/cmd_sequence.h"
|
#include "app/cmd_sequence.h"
|
||||||
#include "doc/color.h"
|
#include "doc/color.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
class Sprite;
|
class Sprite;
|
||||||
@ -32,6 +33,7 @@ namespace cmd {
|
|||||||
SetPixelFormat(doc::Sprite* sprite,
|
SetPixelFormat(doc::Sprite* sprite,
|
||||||
const doc::PixelFormat newFormat,
|
const doc::PixelFormat newFormat,
|
||||||
const render::Dithering& dithering,
|
const render::Dithering& dithering,
|
||||||
|
const doc::RgbMapAlgorithm mapAlgorithm,
|
||||||
doc::rgba_to_graya_func toGray,
|
doc::rgba_to_graya_func toGray,
|
||||||
render::TaskDelegate* delegate);
|
render::TaskDelegate* delegate);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "app/ui/dithering_selector.h"
|
#include "app/ui/dithering_selector.h"
|
||||||
#include "app/ui/editor/editor.h"
|
#include "app/ui/editor/editor.h"
|
||||||
#include "app/ui/editor/editor_render.h"
|
#include "app/ui/editor/editor_render.h"
|
||||||
|
#include "app/ui/rgbmap_algorithm_selector.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
#include "base/thread.h"
|
#include "base/thread.h"
|
||||||
@ -89,6 +90,7 @@ public:
|
|||||||
const doc::frame_t frame,
|
const doc::frame_t frame,
|
||||||
const doc::PixelFormat pixelFormat,
|
const doc::PixelFormat pixelFormat,
|
||||||
const render::Dithering& dithering,
|
const render::Dithering& dithering,
|
||||||
|
const doc::RgbMapAlgorithm rgbMapAlgorithm,
|
||||||
const gen::ToGrayAlgorithm toGray,
|
const gen::ToGrayAlgorithm toGray,
|
||||||
const gfx::Point& pos,
|
const gfx::Point& pos,
|
||||||
const bool newBlend)
|
const bool newBlend)
|
||||||
@ -102,11 +104,13 @@ public:
|
|||||||
sprite, frame,
|
sprite, frame,
|
||||||
pixelFormat,
|
pixelFormat,
|
||||||
dithering,
|
dithering,
|
||||||
|
rgbMapAlgorithm,
|
||||||
toGray,
|
toGray,
|
||||||
newBlend]() { // Copy the matrix
|
newBlend]() { // Copy the matrix
|
||||||
run(sprite, frame,
|
run(sprite, frame,
|
||||||
pixelFormat,
|
pixelFormat,
|
||||||
dithering,
|
dithering,
|
||||||
|
rgbMapAlgorithm,
|
||||||
toGray,
|
toGray,
|
||||||
newBlend);
|
newBlend);
|
||||||
})
|
})
|
||||||
@ -131,6 +135,7 @@ private:
|
|||||||
const doc::frame_t frame,
|
const doc::frame_t frame,
|
||||||
const doc::PixelFormat pixelFormat,
|
const doc::PixelFormat pixelFormat,
|
||||||
const render::Dithering& dithering,
|
const render::Dithering& dithering,
|
||||||
|
const doc::RgbMapAlgorithm rgbMapAlgorithm,
|
||||||
const gen::ToGrayAlgorithm toGray,
|
const gen::ToGrayAlgorithm toGray,
|
||||||
const bool newBlend) {
|
const bool newBlend) {
|
||||||
doc::ImageRef tmp(
|
doc::ImageRef tmp(
|
||||||
@ -152,7 +157,9 @@ private:
|
|||||||
m_image.get(),
|
m_image.get(),
|
||||||
pixelFormat,
|
pixelFormat,
|
||||||
dithering,
|
dithering,
|
||||||
sprite->rgbMap(frame),
|
sprite->rgbMap(frame,
|
||||||
|
sprite->rgbMapForSprite(),
|
||||||
|
rgbMapAlgorithm),
|
||||||
sprite->palette(frame),
|
sprite->palette(frame),
|
||||||
(sprite->backgroundLayer() != nullptr),
|
(sprite->backgroundLayer() != nullptr),
|
||||||
0,
|
0,
|
||||||
@ -189,9 +196,11 @@ public:
|
|||||||
, m_imageBuffer(new doc::ImageBuffer)
|
, m_imageBuffer(new doc::ImageBuffer)
|
||||||
, m_selectedItem(nullptr)
|
, m_selectedItem(nullptr)
|
||||||
, m_ditheringSelector(nullptr)
|
, m_ditheringSelector(nullptr)
|
||||||
|
, m_mapAlgorithmSelector(nullptr)
|
||||||
, m_imageJustCreated(true)
|
, m_imageJustCreated(true)
|
||||||
{
|
{
|
||||||
doc::PixelFormat from = m_editor->sprite()->pixelFormat();
|
const auto& pref = Preferences::instance();
|
||||||
|
const doc::PixelFormat from = m_editor->sprite()->pixelFormat();
|
||||||
|
|
||||||
// Add the color mode in the window title
|
// Add the color mode in the window title
|
||||||
switch (from) {
|
switch (from) {
|
||||||
@ -209,22 +218,48 @@ public:
|
|||||||
m_ditheringSelector = new DitheringSelector(DitheringSelector::SelectBoth);
|
m_ditheringSelector = new DitheringSelector(DitheringSelector::SelectBoth);
|
||||||
m_ditheringSelector->setExpansive(true);
|
m_ditheringSelector->setExpansive(true);
|
||||||
|
|
||||||
|
m_mapAlgorithmSelector = new RgbMapAlgorithmSelector;
|
||||||
|
m_mapAlgorithmSelector->setExpansive(true);
|
||||||
|
|
||||||
// Select default dithering method
|
// Select default dithering method
|
||||||
{
|
{
|
||||||
int index = m_ditheringSelector->findItemIndex(
|
int index = m_ditheringSelector->findItemIndex(
|
||||||
Preferences::instance().quantization.ditheringAlgorithm());
|
pref.quantization.ditheringAlgorithm());
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
m_ditheringSelector->setSelectedItemIndex(index);
|
m_ditheringSelector->setSelectedItemIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ditheringSelector->Change.connect(
|
// Select default RgbMap algorithm
|
||||||
base::Bind<void>(&ColorModeWindow::onDithering, this));
|
m_mapAlgorithmSelector->algorithm(pref.experimental.rgbmapAlgorithm());
|
||||||
ditheringPlaceholder()->addChild(m_ditheringSelector);
|
|
||||||
|
|
||||||
factor()->Change.connect(base::Bind<void>(&ColorModeWindow::onDithering, this));
|
ditheringPlaceholder()->addChild(m_ditheringSelector);
|
||||||
|
rgbmapAlgorithmPlaceholder()->addChild(m_mapAlgorithmSelector);
|
||||||
|
|
||||||
|
const bool adv = pref.quantization.advanced();
|
||||||
|
advancedCheck()->setSelected(adv);
|
||||||
|
advanced()->setVisible(adv);
|
||||||
|
|
||||||
|
// Signals
|
||||||
|
m_ditheringSelector->Change.connect(
|
||||||
|
base::Bind<void>(&ColorModeWindow::onIndexParamChange, this));
|
||||||
|
m_mapAlgorithmSelector->Change.connect(
|
||||||
|
base::Bind<void>(&ColorModeWindow::onIndexParamChange, this));
|
||||||
|
factor()->Change.connect(
|
||||||
|
base::Bind<void>(&ColorModeWindow::onIndexParamChange, this));
|
||||||
|
|
||||||
|
advancedCheck()->Click.connect(
|
||||||
|
[this](ui::Event&){
|
||||||
|
advanced()->setVisible(advancedCheck()->isSelected());
|
||||||
|
|
||||||
|
const gfx::Rect origBounds = bounds();
|
||||||
|
setBounds(gfx::Rect(bounds().origin(), sizeHint()));
|
||||||
|
manager()->invalidateRect(origBounds);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
amount()->setVisible(false);
|
amount()->setVisible(false);
|
||||||
|
advancedCheck()->setVisible(false);
|
||||||
|
advanced()->setVisible(false);
|
||||||
}
|
}
|
||||||
if (from != IMAGE_GRAYSCALE) {
|
if (from != IMAGE_GRAYSCALE) {
|
||||||
colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE));
|
colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE));
|
||||||
@ -242,7 +277,7 @@ public:
|
|||||||
progress()->setReadOnly(true);
|
progress()->setReadOnly(true);
|
||||||
|
|
||||||
// Default dithering factor
|
// Default dithering factor
|
||||||
factor()->setValue(Preferences::instance().quantization.ditheringFactor());
|
factor()->setValue(pref.quantization.ditheringFactor());
|
||||||
|
|
||||||
// Select first option
|
// Select first option
|
||||||
colorMode()->selectIndex(0);
|
colorMode()->selectIndex(0);
|
||||||
@ -267,6 +302,13 @@ public:
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doc::RgbMapAlgorithm rgbMapAlgorithm() const {
|
||||||
|
if (m_mapAlgorithmSelector)
|
||||||
|
return m_mapAlgorithmSelector->algorithm();
|
||||||
|
else
|
||||||
|
return doc::RgbMapAlgorithm::DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
gen::ToGrayAlgorithm toGray() const {
|
gen::ToGrayAlgorithm toGray() const {
|
||||||
static_assert(
|
static_assert(
|
||||||
int(gen::ToGrayAlgorithm::LUMA) == 0 &&
|
int(gen::ToGrayAlgorithm::LUMA) == 0 &&
|
||||||
@ -280,20 +322,25 @@ public:
|
|||||||
return flatten()->isSelected();
|
return flatten()->isSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveOptions() {
|
||||||
|
auto& pref = Preferences::instance();
|
||||||
|
|
||||||
// Save the dithering method used for the future
|
// Save the dithering method used for the future
|
||||||
void saveDitheringOptions() {
|
|
||||||
if (m_ditheringSelector) {
|
if (m_ditheringSelector) {
|
||||||
if (auto item = m_ditheringSelector->getSelectedItem()) {
|
if (auto item = m_ditheringSelector->getSelectedItem()) {
|
||||||
Preferences::instance().quantization.ditheringAlgorithm(
|
pref.quantization.ditheringAlgorithm(
|
||||||
item->text());
|
item->text());
|
||||||
|
|
||||||
if (m_ditheringSelector->ditheringAlgorithm() ==
|
if (m_ditheringSelector->ditheringAlgorithm() ==
|
||||||
render::DitheringAlgorithm::ErrorDiffusion) {
|
render::DitheringAlgorithm::ErrorDiffusion) {
|
||||||
Preferences::instance().quantization.ditheringFactor(
|
pref.quantization.ditheringFactor(
|
||||||
factor()->getValue());
|
factor()->getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_mapAlgorithmSelector)
|
||||||
|
pref.quantization.advanced(advancedCheck()->isSelected());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -368,6 +415,7 @@ private:
|
|||||||
m_editor->frame(),
|
m_editor->frame(),
|
||||||
dstPixelFormat,
|
dstPixelFormat,
|
||||||
dithering(),
|
dithering(),
|
||||||
|
rgbMapAlgorithm(),
|
||||||
toGray(),
|
toGray(),
|
||||||
visibleBounds.origin(),
|
visibleBounds.origin(),
|
||||||
Preferences::instance().experimental.newBlend()));
|
Preferences::instance().experimental.newBlend()));
|
||||||
@ -375,7 +423,7 @@ private:
|
|||||||
m_timer.start();
|
m_timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDithering() {
|
void onIndexParamChange() {
|
||||||
stop();
|
stop();
|
||||||
m_selectedItem = nullptr;
|
m_selectedItem = nullptr;
|
||||||
onChangeColorMode();
|
onChangeColorMode();
|
||||||
@ -421,6 +469,7 @@ private:
|
|||||||
std::unique_ptr<ConvertThread> m_bgThread;
|
std::unique_ptr<ConvertThread> m_bgThread;
|
||||||
ConversionItem* m_selectedItem;
|
ConversionItem* m_selectedItem;
|
||||||
DitheringSelector* m_ditheringSelector;
|
DitheringSelector* m_ditheringSelector;
|
||||||
|
RgbMapAlgorithmSelector* m_mapAlgorithmSelector;
|
||||||
bool m_imageJustCreated;
|
bool m_imageJustCreated;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -441,6 +490,7 @@ private:
|
|||||||
bool m_useUI;
|
bool m_useUI;
|
||||||
doc::PixelFormat m_format;
|
doc::PixelFormat m_format;
|
||||||
render::Dithering m_dithering;
|
render::Dithering m_dithering;
|
||||||
|
doc::RgbMapAlgorithm m_rgbmap;
|
||||||
gen::ToGrayAlgorithm m_toGray;
|
gen::ToGrayAlgorithm m_toGray;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -450,6 +500,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
|
|||||||
m_useUI = true;
|
m_useUI = true;
|
||||||
m_format = IMAGE_RGB;
|
m_format = IMAGE_RGB;
|
||||||
m_dithering = render::Dithering();
|
m_dithering = render::Dithering();
|
||||||
|
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
|
||||||
m_toGray = gen::ToGrayAlgorithm::DEFAULT;
|
m_toGray = gen::ToGrayAlgorithm::DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,6 +548,15 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
|||||||
m_dithering.matrix(render::BayerMatrix(8));
|
m_dithering.matrix(render::BayerMatrix(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO change this with NewParams as in ColorQuantizationParams
|
||||||
|
std::string rgbmap = params.get("rgbmap");
|
||||||
|
if (rgbmap == "octree")
|
||||||
|
m_rgbmap = doc::RgbMapAlgorithm::OCTREE;
|
||||||
|
else if (rgbmap == "rgb5a3")
|
||||||
|
m_rgbmap = doc::RgbMapAlgorithm::RGB5A3;
|
||||||
|
else
|
||||||
|
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
|
||||||
|
|
||||||
std::string toGray = params.get("toGray");
|
std::string toGray = params.get("toGray");
|
||||||
if (toGray == "luma")
|
if (toGray == "luma")
|
||||||
m_toGray = gen::ToGrayAlgorithm::LUMA;
|
m_toGray = gen::ToGrayAlgorithm::LUMA;
|
||||||
@ -570,10 +630,11 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
|||||||
|
|
||||||
m_format = window.pixelFormat();
|
m_format = window.pixelFormat();
|
||||||
m_dithering = window.dithering();
|
m_dithering = window.dithering();
|
||||||
|
m_rgbmap = window.rgbMapAlgorithm();
|
||||||
m_toGray = window.toGray();
|
m_toGray = window.toGray();
|
||||||
flatten = window.flattenEnabled();
|
flatten = window.flattenEnabled();
|
||||||
|
|
||||||
window.saveDitheringOptions();
|
window.saveOptions();
|
||||||
}
|
}
|
||||||
#endif // ENABLE_UI
|
#endif // ENABLE_UI
|
||||||
|
|
||||||
@ -607,6 +668,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
|||||||
new cmd::SetPixelFormat(
|
new cmd::SetPixelFormat(
|
||||||
sprite, m_format,
|
sprite, m_format,
|
||||||
m_dithering,
|
m_dithering,
|
||||||
|
m_rgbmap,
|
||||||
get_gray_func(m_toGray),
|
get_gray_func(m_toGray),
|
||||||
&job)); // SpriteJob is a render::TaskDelegate
|
&job)); // SpriteJob is a render::TaskDelegate
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019 Igara Studio S.A.
|
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -20,10 +20,12 @@
|
|||||||
#include "app/sprite_job.h"
|
#include "app/sprite_job.h"
|
||||||
#include "app/transaction.h"
|
#include "app/transaction.h"
|
||||||
#include "app/ui/color_bar.h"
|
#include "app/ui/color_bar.h"
|
||||||
|
#include "app/ui/rgbmap_algorithm_selector.h"
|
||||||
#include "app/ui_context.h"
|
#include "app/ui_context.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
#include "doc/sprite.h"
|
#include "doc/sprite.h"
|
||||||
#include "render/quantization.h"
|
#include "render/quantization.h"
|
||||||
|
#include "ui/manager.h"
|
||||||
|
|
||||||
#include "palette_from_sprite.xml.h"
|
#include "palette_from_sprite.xml.h"
|
||||||
|
|
||||||
@ -36,8 +38,53 @@ struct ColorQuantizationParams : public NewParams {
|
|||||||
Param<bool> withAlpha { this, true, "withAlpha" };
|
Param<bool> withAlpha { this, true, "withAlpha" };
|
||||||
Param<int> maxColors { this, 256, "maxColors" };
|
Param<int> maxColors { this, 256, "maxColors" };
|
||||||
Param<bool> useRange { this, false, "useRange" };
|
Param<bool> useRange { this, false, "useRange" };
|
||||||
|
Param<RgbMapAlgorithm> algorithm { this, RgbMapAlgorithm::DEFAULT, "algorithm" };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if ENABLE_UI
|
||||||
|
|
||||||
|
class PaletteFromSpriteWindow : public app::gen::PaletteFromSprite {
|
||||||
|
public:
|
||||||
|
PaletteFromSpriteWindow() {
|
||||||
|
rgbmapAlgorithmPlaceholder()->addChild(&m_algoSelector);
|
||||||
|
|
||||||
|
advancedCheck()->Click.connect(
|
||||||
|
[this](ui::Event&){
|
||||||
|
advanced()->setVisible(advancedCheck()->isSelected());
|
||||||
|
|
||||||
|
const gfx::Rect origBounds = bounds();
|
||||||
|
setBounds(gfx::Rect(bounds().origin(), sizeHint()));
|
||||||
|
manager()->invalidateRect(origBounds);
|
||||||
|
});
|
||||||
|
|
||||||
|
m_algoSelector.Change.connect(
|
||||||
|
[this](){
|
||||||
|
switch (algorithm()) {
|
||||||
|
case RgbMapAlgorithm::RGB5A3:
|
||||||
|
alphaChannel()->setEnabled(true);
|
||||||
|
break;
|
||||||
|
case RgbMapAlgorithm::OCTREE:
|
||||||
|
alphaChannel()->setSelected(false);
|
||||||
|
alphaChannel()->setEnabled(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doc::RgbMapAlgorithm algorithm() {
|
||||||
|
return m_algoSelector.algorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
void algorithm(const doc::RgbMapAlgorithm mapAlgo) {
|
||||||
|
m_algoSelector.algorithm(mapAlgo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RgbMapAlgorithmSelector m_algoSelector;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
class ColorQuantizationCommand : public CommandWithNewParams<ColorQuantizationParams> {
|
class ColorQuantizationCommand : public CommandWithNewParams<ColorQuantizationParams> {
|
||||||
public:
|
public:
|
||||||
ColorQuantizationCommand();
|
ColorQuantizationCommand();
|
||||||
@ -65,8 +112,10 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
|
|||||||
const bool ui = (params().ui() && ctx->isUIAvailable());
|
const bool ui = (params().ui() && ctx->isUIAvailable());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
auto& pref = Preferences::instance();
|
||||||
bool withAlpha = params().withAlpha();
|
bool withAlpha = params().withAlpha();
|
||||||
int maxColors = params().maxColors();
|
int maxColors = params().maxColors();
|
||||||
|
RgbMapAlgorithm algorithm = params().algorithm();
|
||||||
bool createPal;
|
bool createPal;
|
||||||
|
|
||||||
Site site = ctx->activeSite();
|
Site site = ctx->activeSite();
|
||||||
@ -74,16 +123,22 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
|
|||||||
|
|
||||||
#ifdef ENABLE_UI
|
#ifdef ENABLE_UI
|
||||||
if (ui) {
|
if (ui) {
|
||||||
app::gen::PaletteFromSprite window;
|
PaletteFromSpriteWindow window;
|
||||||
{
|
{
|
||||||
ContextReader reader(ctx);
|
ContextReader reader(ctx);
|
||||||
const Palette* curPalette = site.sprite()->palette(site.frame());
|
const Palette* curPalette = site.sprite()->palette(site.frame());
|
||||||
|
|
||||||
|
if (!params().algorithm.isSet())
|
||||||
|
algorithm = pref.experimental.rgbmapAlgorithm();
|
||||||
if (!params().withAlpha.isSet())
|
if (!params().withAlpha.isSet())
|
||||||
withAlpha = App::instance()->preferences().quantization.withAlpha();
|
withAlpha = pref.quantization.withAlpha();
|
||||||
|
|
||||||
|
const bool advanced = pref.quantization.advanced();
|
||||||
|
window.advancedCheck()->setSelected(advanced);
|
||||||
|
window.advanced()->setVisible(advanced);
|
||||||
|
|
||||||
|
window.algorithm(algorithm);
|
||||||
window.newPalette()->setSelected(true);
|
window.newPalette()->setSelected(true);
|
||||||
window.alphaChannel()->setSelected(withAlpha);
|
|
||||||
window.ncolors()->setTextf("%d", maxColors);
|
window.ncolors()->setTextf("%d", maxColors);
|
||||||
|
|
||||||
if (entries.picks() > 1) {
|
if (entries.picks() > 1) {
|
||||||
@ -107,7 +162,10 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
|
|||||||
|
|
||||||
maxColors = window.ncolors()->textInt();
|
maxColors = window.ncolors()->textInt();
|
||||||
withAlpha = window.alphaChannel()->isSelected();
|
withAlpha = window.alphaChannel()->isSelected();
|
||||||
App::instance()->preferences().quantization.withAlpha(withAlpha);
|
algorithm = window.algorithm();
|
||||||
|
|
||||||
|
pref.quantization.withAlpha(withAlpha);
|
||||||
|
pref.quantization.advanced(window.advancedCheck()->isSelected());
|
||||||
|
|
||||||
if (window.newPalette()->isSelected()) {
|
if (window.newPalette()->isSelected()) {
|
||||||
createPal = true;
|
createPal = true;
|
||||||
@ -139,14 +197,15 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
|
|||||||
Palette tmpPalette(frame, entries.picks());
|
Palette tmpPalette(frame, entries.picks());
|
||||||
|
|
||||||
SpriteJob job(reader, "Color Quantization");
|
SpriteJob job(reader, "Color Quantization");
|
||||||
const bool newBlend = Preferences::instance().experimental.newBlend();
|
const bool newBlend = pref.experimental.newBlend();
|
||||||
job.startJobWithCallback(
|
job.startJobWithCallback(
|
||||||
[sprite, withAlpha, &tmpPalette, &job, newBlend]{
|
[sprite, withAlpha, &tmpPalette, &job, newBlend, algorithm]{
|
||||||
render::create_palette_from_sprite(
|
render::create_palette_from_sprite(
|
||||||
sprite, 0, sprite->lastFrame(),
|
sprite, 0, sprite->lastFrame(),
|
||||||
withAlpha, &tmpPalette,
|
withAlpha, &tmpPalette,
|
||||||
&job,
|
&job, // SpriteJob is a render::TaskDelegate
|
||||||
newBlend); // SpriteJob is a render::TaskDelegate
|
newBlend,
|
||||||
|
algorithm);
|
||||||
});
|
});
|
||||||
job.waitJob();
|
job.waitJob();
|
||||||
if (job.isCanceled())
|
if (job.isCanceled())
|
||||||
|
@ -275,7 +275,8 @@ void NewFileCommand::onExecute(Context* ctx)
|
|||||||
if (clipboardPalette.isBlack()) {
|
if (clipboardPalette.isBlack()) {
|
||||||
render::create_palette_from_sprite(
|
render::create_palette_from_sprite(
|
||||||
sprite.get(), 0, sprite->lastFrame(), true,
|
sprite.get(), 0, sprite->lastFrame(), true,
|
||||||
&clipboardPalette, nullptr, true);
|
&clipboardPalette, nullptr, true,
|
||||||
|
Preferences::instance().experimental.rgbmapAlgorithm());
|
||||||
}
|
}
|
||||||
sprite->setPalette(&clipboardPalette, false);
|
sprite->setPalette(&clipboardPalette, false);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "app/tx.h"
|
#include "app/tx.h"
|
||||||
#include "app/ui/color_button.h"
|
#include "app/ui/color_button.h"
|
||||||
#include "app/ui/pref_widget.h"
|
#include "app/ui/pref_widget.h"
|
||||||
|
#include "app/ui/rgbmap_algorithm_selector.h"
|
||||||
#include "app/ui/separator_in_view.h"
|
#include "app/ui/separator_in_view.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
@ -400,6 +401,10 @@ public:
|
|||||||
|
|
||||||
nonactiveLayersOpacity()->setValue(m_pref.experimental.nonactiveLayersOpacity());
|
nonactiveLayersOpacity()->setValue(m_pref.experimental.nonactiveLayersOpacity());
|
||||||
|
|
||||||
|
rgbmapAlgorithmPlaceholder()->addChild(&m_rgbmapAlgorithmSelector);
|
||||||
|
m_rgbmapAlgorithmSelector.setExpansive(true);
|
||||||
|
m_rgbmapAlgorithmSelector.algorithm(m_pref.experimental.rgbmapAlgorithm());
|
||||||
|
|
||||||
if (m_pref.editor.showScrollbars())
|
if (m_pref.editor.showScrollbars())
|
||||||
showScrollbars()->setSelected(true);
|
showScrollbars()->setSelected(true);
|
||||||
|
|
||||||
@ -695,6 +700,7 @@ public:
|
|||||||
m_pref.experimental.useNativeFileDialog(nativeFileDialog()->isSelected());
|
m_pref.experimental.useNativeFileDialog(nativeFileDialog()->isSelected());
|
||||||
m_pref.experimental.flashLayer(flashLayer()->isSelected());
|
m_pref.experimental.flashLayer(flashLayer()->isSelected());
|
||||||
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
|
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
|
||||||
|
m_pref.experimental.rgbmapAlgorithm(m_rgbmapAlgorithmSelector.algorithm());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
@ -1606,6 +1612,7 @@ private:
|
|||||||
int m_restoreUIScaling;
|
int m_restoreUIScaling;
|
||||||
std::vector<os::ColorSpacePtr> m_colorSpaces;
|
std::vector<os::ColorSpacePtr> m_colorSpaces;
|
||||||
std::string m_templateTextForDisplayCS;
|
std::string m_templateTextForDisplayCS;
|
||||||
|
RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OptionsCommand : public Command {
|
class OptionsCommand : public Command {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "base/string.h"
|
#include "base/string.h"
|
||||||
#include "doc/algorithm/resize_image.h"
|
#include "doc/algorithm/resize_image.h"
|
||||||
#include "doc/color_mode.h"
|
#include "doc/color_mode.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
#include "filters/color_curve.h"
|
#include "filters/color_curve.h"
|
||||||
#include "filters/hue_saturation_filter.h"
|
#include "filters/hue_saturation_filter.h"
|
||||||
#include "filters/outline_filter.h"
|
#include "filters/outline_filter.h"
|
||||||
@ -192,6 +193,17 @@ void Param<tools::InkType>::fromString(const std::string& value)
|
|||||||
setValue(tools::string_id_to_ink_type(value));
|
setValue(tools::string_id_to_ink_type(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Param<doc::RgbMapAlgorithm>::fromString(const std::string& value)
|
||||||
|
{
|
||||||
|
if (base::utf8_icmp(value, "octree") == 0)
|
||||||
|
setValue(doc::RgbMapAlgorithm::OCTREE);
|
||||||
|
else if (base::utf8_icmp(value, "rgb5a3") == 0)
|
||||||
|
setValue(doc::RgbMapAlgorithm::RGB5A3);
|
||||||
|
else
|
||||||
|
setValue(doc::RgbMapAlgorithm::DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Convert values from Lua
|
// Convert values from Lua
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -326,6 +338,15 @@ void Param<tools::InkType>::fromLua(lua_State* L, int index)
|
|||||||
script::get_value_from_lua<tools::InkType>(L, index);
|
script::get_value_from_lua<tools::InkType>(L, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void Param<doc::RgbMapAlgorithm>::fromLua(lua_State* L, int index)
|
||||||
|
{
|
||||||
|
if (lua_type(L, index) == LUA_TSTRING)
|
||||||
|
fromString(lua_tostring(L, index));
|
||||||
|
else
|
||||||
|
setValue((doc::RgbMapAlgorithm)lua_tointeger(L, index));
|
||||||
|
}
|
||||||
|
|
||||||
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
|
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
|
||||||
{
|
{
|
||||||
onResetValues();
|
onResetValues();
|
||||||
|
@ -1180,6 +1180,7 @@ void DocExporter::renderTexture(Context* ctx,
|
|||||||
sample.sprite(),
|
sample.sprite(),
|
||||||
textureImage->pixelFormat(),
|
textureImage->pixelFormat(),
|
||||||
render::Dithering(),
|
render::Dithering(),
|
||||||
|
Sprite::DefaultRgbMapAlgorithm(), // TODO add rgbmap algorithm preference
|
||||||
nullptr, // toGray is not needed because the texture is Indexed or RGB
|
nullptr, // toGray is not needed because the texture is Indexed or RGB
|
||||||
nullptr) // TODO add a delegate to show progress
|
nullptr) // TODO add a delegate to show progress
|
||||||
.execute(ctx);
|
.execute(ctx);
|
||||||
|
@ -905,7 +905,8 @@ void FileOp::postLoad()
|
|||||||
std::shared_ptr<Palette> palette(
|
std::shared_ptr<Palette> palette(
|
||||||
render::create_palette_from_sprite(
|
render::create_palette_from_sprite(
|
||||||
sprite, frame_t(0), sprite->lastFrame(), true,
|
sprite, frame_t(0), sprite->lastFrame(), true,
|
||||||
nullptr, nullptr, m_config.newBlend));
|
nullptr, nullptr, m_config.newBlend,
|
||||||
|
m_config.rgbMapAlgorithm));
|
||||||
|
|
||||||
sprite->resetPalettes();
|
sprite->resetPalettes();
|
||||||
sprite->setPalette(palette.get(), false);
|
sprite->setPalette(palette.get(), false);
|
||||||
|
@ -22,6 +22,7 @@ void FileOpConfig::fillFromPreferences()
|
|||||||
newBlend = Preferences::instance().experimental.newBlend();
|
newBlend = Preferences::instance().experimental.newBlend();
|
||||||
defaultSliceColor = Preferences::instance().slices.defaultColor();
|
defaultSliceColor = Preferences::instance().slices.defaultColor();
|
||||||
workingCS = get_working_rgb_space_from_preferences();
|
workingCS = get_working_rgb_space_from_preferences();
|
||||||
|
rgbMapAlgorithm = Preferences::instance().experimental.rgbmapAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019 Igara Studio S.A.
|
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "app/color.h"
|
#include "app/color.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
#include "gfx/color_space.h"
|
#include "gfx/color_space.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
@ -32,6 +33,9 @@ namespace app {
|
|||||||
|
|
||||||
app::Color defaultSliceColor = app::Color::fromRgb(0, 0, 255);
|
app::Color defaultSliceColor = app::Color::fromRgb(0, 0, 255);
|
||||||
|
|
||||||
|
// Algorithm used to create a palette from RGB files.
|
||||||
|
doc::RgbMapAlgorithm rgbMapAlgorithm = doc::RgbMapAlgorithm::DEFAULT;
|
||||||
|
|
||||||
void fillFromPreferences();
|
void fillFromPreferences();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1172,7 +1172,7 @@ private:
|
|||||||
const DisposalMethod disposal,
|
const DisposalMethod disposal,
|
||||||
const bool fixDuration) {
|
const bool fixDuration) {
|
||||||
std::unique_ptr<Palette> framePaletteRef;
|
std::unique_ptr<Palette> framePaletteRef;
|
||||||
std::unique_ptr<RgbMap> rgbmapRef;
|
std::unique_ptr<RgbMapRGB5A3> rgbmapRef;
|
||||||
Palette* framePalette = m_sprite->palette(frame);
|
Palette* framePalette = m_sprite->palette(frame);
|
||||||
RgbMap* rgbmap = m_sprite->rgbMap(frame);
|
RgbMap* rgbmap = m_sprite->rgbMap(frame);
|
||||||
|
|
||||||
@ -1181,9 +1181,9 @@ private:
|
|||||||
framePaletteRef.reset(createOptimizedPalette(frameBounds));
|
framePaletteRef.reset(createOptimizedPalette(frameBounds));
|
||||||
framePalette = framePaletteRef.get();
|
framePalette = framePaletteRef.get();
|
||||||
|
|
||||||
rgbmapRef.reset(new RgbMap);
|
rgbmapRef.reset(new RgbMapRGB5A3);
|
||||||
|
rgbmapRef->regenerateMap(framePalette, m_transparentIndex);
|
||||||
rgbmap = rgbmapRef.get();
|
rgbmap = rgbmapRef.get();
|
||||||
rgbmap->regenerate(framePalette, m_transparentIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will store the frameBounds pixels in frameImage, with the
|
// We will store the frameBounds pixels in frameImage, with the
|
||||||
@ -1229,6 +1229,8 @@ private:
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (rgba_geta(color) >= 128) {
|
if (rgba_geta(color) >= 128) {
|
||||||
|
color |= rgba_a_mask; // Set alpha=255
|
||||||
|
|
||||||
i = framePalette->findExactMatch(
|
i = framePalette->findExactMatch(
|
||||||
rgba_getr(color),
|
rgba_getr(color),
|
||||||
rgba_getg(color),
|
rgba_getg(color),
|
||||||
@ -1236,10 +1238,7 @@ private:
|
|||||||
255,
|
255,
|
||||||
m_transparentIndex);
|
m_transparentIndex);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
i = rgbmap->mapColor(rgba_getr(color),
|
i = rgbmap->mapColor(color);
|
||||||
rgba_getg(color),
|
|
||||||
rgba_getb(color),
|
|
||||||
255);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ASSERT(m_transparentIndex >= 0);
|
ASSERT(m_transparentIndex >= 0);
|
||||||
|
@ -54,6 +54,14 @@ Preferences::Preferences()
|
|||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
|
// Create a connection with the default RgbMapAlgorithm preferences
|
||||||
|
// to change the default algorithm in the "doc" layer.
|
||||||
|
experimental.rgbmapAlgorithm.AfterChange.connect(
|
||||||
|
[](const doc::RgbMapAlgorithm& newValue){
|
||||||
|
doc::Sprite::SetDefaultRgbMapAlgorithm(newValue);
|
||||||
|
});
|
||||||
|
doc::Sprite::SetDefaultRgbMapAlgorithm(experimental.rgbmapAlgorithm());
|
||||||
|
|
||||||
// Create a connection with the default document preferences grid
|
// Create a connection with the default document preferences grid
|
||||||
// bounds to sync the default grid bounds for new sprites in the
|
// bounds to sync the default grid bounds for new sprites in the
|
||||||
// "doc" layer.
|
// "doc" layer.
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "doc/color_mode.h"
|
#include "doc/color_mode.h"
|
||||||
#include "doc/frame.h"
|
#include "doc/frame.h"
|
||||||
#include "doc/layer_list.h"
|
#include "doc/layer_list.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
#include "doc/sprite.h"
|
#include "doc/sprite.h"
|
||||||
#include "filters/hue_saturation_filter.h"
|
#include "filters/hue_saturation_filter.h"
|
||||||
#include "filters/tiled_mode.h"
|
#include "filters/tiled_mode.h"
|
||||||
|
@ -184,6 +184,7 @@ FOR_ENUM(app::tools::RotationAlgorithm)
|
|||||||
FOR_ENUM(doc::AniDir)
|
FOR_ENUM(doc::AniDir)
|
||||||
FOR_ENUM(doc::BrushPattern)
|
FOR_ENUM(doc::BrushPattern)
|
||||||
FOR_ENUM(doc::ColorMode)
|
FOR_ENUM(doc::ColorMode)
|
||||||
|
FOR_ENUM(doc::RgbMapAlgorithm)
|
||||||
FOR_ENUM(filters::HueSaturationFilter::Mode)
|
FOR_ENUM(filters::HueSaturationFilter::Mode)
|
||||||
FOR_ENUM(filters::TiledMode)
|
FOR_ENUM(filters::TiledMode)
|
||||||
FOR_ENUM(render::OnionskinPosition)
|
FOR_ENUM(render::OnionskinPosition)
|
||||||
|
@ -292,10 +292,7 @@ public:
|
|||||||
c = m_palette->getEntry(c);
|
c = m_palette->getEntry(c);
|
||||||
|
|
||||||
c = rgba_blender_normal(c, m_color, m_opacity);
|
c = rgba_blender_normal(c, m_color, m_opacity);
|
||||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -365,10 +362,7 @@ public:
|
|||||||
c = m_palette->getEntry(c);
|
c = m_palette->getEntry(c);
|
||||||
|
|
||||||
c = rgba_blender_merge(c, m_color, m_opacity);
|
c = rgba_blender_merge(c, m_color, m_opacity);
|
||||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -520,8 +514,7 @@ public:
|
|||||||
doc::rgba(m_area.r, m_area.g, m_area.b, m_area.a),
|
doc::rgba(m_area.r, m_area.g, m_area.b, m_area.a),
|
||||||
m_opacity);
|
m_opacity);
|
||||||
|
|
||||||
*m_dstAddress = m_rgbmap->mapColor(
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
*m_dstAddress = *m_srcAddress;
|
*m_dstAddress = *m_srcAddress;
|
||||||
@ -633,8 +626,7 @@ public:
|
|||||||
color_t c = rgba_blender_normal(
|
color_t c = rgba_blender_normal(
|
||||||
m_palette->getEntry(*m_srcAddress), m_color2, m_opacity);
|
m_palette->getEntry(*m_srcAddress), m_color2, m_opacity);
|
||||||
|
|
||||||
*m_dstAddress = m_rgbmap->mapColor(
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -721,10 +713,7 @@ void JumbleInkProcessing<IndexedTraits>::processPixel(int x, int y)
|
|||||||
tc, m_opacity);
|
tc, m_opacity);
|
||||||
|
|
||||||
if (rgba_geta(c) >= 128)
|
if (rgba_geta(c) >= 128)
|
||||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
else
|
else
|
||||||
*m_dstAddress = 0;
|
*m_dstAddress = 0;
|
||||||
}
|
}
|
||||||
@ -1060,10 +1049,7 @@ void GradientInkProcessing<IndexedTraits>::processPixel(int x, int y)
|
|||||||
c0 = m_palette->getEntry(c0);
|
c0 = m_palette->getEntry(c0);
|
||||||
c = rgba_blender_normal(c0, c, m_opacity);
|
c = rgba_blender_normal(c0, c, m_opacity);
|
||||||
|
|
||||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
|
|
||||||
++m_tmpAddress;
|
++m_tmpAddress;
|
||||||
}
|
}
|
||||||
@ -1108,10 +1094,7 @@ public:
|
|||||||
|
|
||||||
void processPixel(int x, int y) {
|
void processPixel(int x, int y) {
|
||||||
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), m_color, 255);
|
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), m_color, 255);
|
||||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
40
src/app/ui/rgbmap_algorithm_selector.cpp
Normal file
40
src/app/ui/rgbmap_algorithm_selector.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2020 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/rgbmap_algorithm_selector.h"
|
||||||
|
|
||||||
|
#include "app/i18n/strings.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
RgbMapAlgorithmSelector::RgbMapAlgorithmSelector()
|
||||||
|
{
|
||||||
|
// addItem() must match the RgbMapAlgorithm enum
|
||||||
|
static_assert(int(doc::RgbMapAlgorithm::RGB5A3) == 0 &&
|
||||||
|
int(doc::RgbMapAlgorithm::OCTREE) == 1,
|
||||||
|
"Unexpected doc::RgbMapAlgorithm values");
|
||||||
|
|
||||||
|
addItem(Strings::rgbmap_algorithm_selector_rgb5a3());
|
||||||
|
addItem(Strings::rgbmap_algorithm_selector_octree());
|
||||||
|
|
||||||
|
algorithm(doc::RgbMapAlgorithm::DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc::RgbMapAlgorithm RgbMapAlgorithmSelector::algorithm()
|
||||||
|
{
|
||||||
|
return (doc::RgbMapAlgorithm)getSelectedItemIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RgbMapAlgorithmSelector::algorithm(const doc::RgbMapAlgorithm mapAlgo)
|
||||||
|
{
|
||||||
|
setSelectedItemIndex((int)mapAlgo);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace app
|
26
src/app/ui/rgbmap_algorithm_selector.h
Normal file
26
src/app/ui/rgbmap_algorithm_selector.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2020 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_MAP_ALGORITHM_SELECTOR_H_INCLUDED
|
||||||
|
#define APP_UI_MAP_ALGORITHM_SELECTOR_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
|
#include "ui/combobox.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class RgbMapAlgorithmSelector : public ui::ComboBox {
|
||||||
|
public:
|
||||||
|
RgbMapAlgorithmSelector();
|
||||||
|
|
||||||
|
doc::RgbMapAlgorithm algorithm();
|
||||||
|
void algorithm(doc::RgbMapAlgorithm mapAlgo);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
# Aseprite Document Library
|
# Aseprite Document Library
|
||||||
# Copyright (C) 2020 Igara Studio S.A.
|
# Copyright (C) 2019-2020 Igara Studio S.A.
|
||||||
# Copyright (C) 2001-2018 David Capello
|
# Copyright (C) 2001-2018 David Capello
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@ -49,11 +49,12 @@ add_library(doc-lib
|
|||||||
mask_io.cpp
|
mask_io.cpp
|
||||||
object.cpp
|
object.cpp
|
||||||
object.cpp
|
object.cpp
|
||||||
|
octree_map.cpp
|
||||||
palette.cpp
|
palette.cpp
|
||||||
palette_io.cpp
|
palette_io.cpp
|
||||||
primitives.cpp
|
primitives.cpp
|
||||||
remap.cpp
|
remap.cpp
|
||||||
rgbmap.cpp
|
rgbmap_rgb5a3.cpp
|
||||||
selected_frames.cpp
|
selected_frames.cpp
|
||||||
selected_layers.cpp
|
selected_layers.cpp
|
||||||
slice.cpp
|
slice.cpp
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019 Igara Studio S.A.
|
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -33,6 +33,7 @@
|
|||||||
#include "doc/primitives_fast.h"
|
#include "doc/primitives_fast.h"
|
||||||
#include "doc/remap.h"
|
#include "doc/remap.h"
|
||||||
#include "doc/rgbmap.h"
|
#include "doc/rgbmap.h"
|
||||||
|
#include "doc/rgbmap_rgb5a3.h"
|
||||||
#include "doc/slice.h"
|
#include "doc/slice.h"
|
||||||
#include "doc/slices.h"
|
#include "doc/slices.h"
|
||||||
#include "doc/sprite.h"
|
#include "doc/sprite.h"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019 Igara Studio S.A.
|
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -12,20 +12,25 @@
|
|||||||
namespace doc {
|
namespace doc {
|
||||||
|
|
||||||
enum class ObjectType {
|
enum class ObjectType {
|
||||||
Unknown,
|
Unknown = 0,
|
||||||
Image,
|
Image = 1,
|
||||||
Palette,
|
Palette = 2,
|
||||||
RgbMap,
|
|
||||||
Path,
|
// Deprecated values, we cannot re-use these indexes because
|
||||||
Mask,
|
// backup sessions use them (check readLayer() function in
|
||||||
Cel,
|
// src/app/crash/read_document.cpp).
|
||||||
CelData,
|
//RgbMap = 3,
|
||||||
LayerImage,
|
//Path = 4,
|
||||||
LayerGroup,
|
|
||||||
Sprite,
|
Mask = 5,
|
||||||
Document,
|
Cel = 6,
|
||||||
Tag,
|
CelData = 7,
|
||||||
Slice,
|
LayerImage = 8,
|
||||||
|
LayerGroup = 9,
|
||||||
|
Sprite = 10,
|
||||||
|
Document = 11,
|
||||||
|
Tag = 12,
|
||||||
|
Slice = 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
311
src/doc/octree_map.cpp
Normal file
311
src/doc/octree_map.cpp
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (c) 2020 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/octree_map.h"
|
||||||
|
|
||||||
|
#include "doc/palette.h"
|
||||||
|
|
||||||
|
#define MID_VALUE_COLOR ((rgba_r_mask / 2) + 1)
|
||||||
|
#define MIN_LEVEL_OCTREE_DEEP 3
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
void OctreeNode::addColor(color_t c, int level, OctreeNode* parent,
|
||||||
|
int paletteIndex, int levelDeep)
|
||||||
|
{
|
||||||
|
m_parent = parent;
|
||||||
|
if (level >= levelDeep) {
|
||||||
|
m_leafColor.add(c);
|
||||||
|
m_paletteIndex = paletteIndex;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int index = getOctet(c, level);
|
||||||
|
if (!m_children) {
|
||||||
|
m_children.reset(new std::array<OctreeNode, 8>());
|
||||||
|
}
|
||||||
|
(*m_children)[index].addColor(c, level + 1, this, paletteIndex, levelDeep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeNode::fillOrphansNodes(const Palette* palette,
|
||||||
|
const color_t upstreamBranchColor,
|
||||||
|
const int level)
|
||||||
|
{
|
||||||
|
for (int i=0; i<8; i++) {
|
||||||
|
if ((*m_children)[i].m_children)
|
||||||
|
(*m_children)[i].fillOrphansNodes(
|
||||||
|
palette,
|
||||||
|
upstreamBranchColor + octetToBranchColor(i, level),
|
||||||
|
level + 1);
|
||||||
|
else if (!((*m_children)[i].isLeaf())) {
|
||||||
|
// Here the node IS NOT a Leaf and HAS NOT children
|
||||||
|
// So, we must assign palette index to the current node
|
||||||
|
// to fill the "map holes" (i.e "death tree branches")
|
||||||
|
// BUT, if the level is low (a few bits to identify a color)
|
||||||
|
// 0, 1, 2, or 3, we need to create branchs/Leaves until
|
||||||
|
// the desired minimum color MSB bits.
|
||||||
|
if (level < MIN_LEVEL_OCTREE_DEEP) {
|
||||||
|
(*m_children)[i].fillMostSignificantNodes(level);
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int currentBranchColorAdd = octetToBranchColor(i, level);
|
||||||
|
int midColorAdd = MID_VALUE_COLOR >> (level + 1);
|
||||||
|
midColorAdd = ((midColorAdd) << rgba_r_shift)
|
||||||
|
+ ((midColorAdd) << rgba_g_shift)
|
||||||
|
+ ((midColorAdd) << rgba_b_shift);
|
||||||
|
color_t branchColorMed = rgba_a_mask
|
||||||
|
+ upstreamBranchColor
|
||||||
|
+ currentBranchColorAdd
|
||||||
|
+ midColorAdd;
|
||||||
|
int indexMed = palette->findBestfit2(rgba_getr(branchColorMed),
|
||||||
|
rgba_getg(branchColorMed),
|
||||||
|
rgba_getb(branchColorMed));
|
||||||
|
(*m_children)[i].paletteIndex(indexMed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeNode::fillMostSignificantNodes(int level)
|
||||||
|
{
|
||||||
|
if (level < MIN_LEVEL_OCTREE_DEEP) {
|
||||||
|
m_children.reset(new std::array<OctreeNode, 8>());
|
||||||
|
level++;
|
||||||
|
for (int i=0; i<8; i++)
|
||||||
|
(*m_children)[i].fillMostSignificantNodes(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int OctreeNode::mapColor(int r, int g, int b, int level) const
|
||||||
|
{
|
||||||
|
int indexLevel = ( (b >> (7 - level)) & 1) * 4
|
||||||
|
+ ((g >> (7 - level)) & 1) * 2
|
||||||
|
+ ((r >> (7 - level)) & 1);
|
||||||
|
if ((*m_children)[indexLevel].m_children)
|
||||||
|
return (*m_children)[indexLevel].mapColor(r, g, b, level+1);
|
||||||
|
return (*m_children)[indexLevel].m_paletteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeNode::collectLeafNodes(std::vector<OctreeNode*>* leavesVector, int& paletteIndex)
|
||||||
|
{
|
||||||
|
for (int i=0; i<8; i++) {
|
||||||
|
if ((*m_children)[i].isLeaf()) {
|
||||||
|
(*m_children)[i].paletteIndex(paletteIndex);
|
||||||
|
leavesVector->push_back(&(*m_children)[i]);
|
||||||
|
paletteIndex++;
|
||||||
|
}
|
||||||
|
else if ((*m_children)[i].m_children)
|
||||||
|
(*m_children)[i].collectLeafNodes(leavesVector, paletteIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeLeaves(): remove leaves from a common parent
|
||||||
|
// auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside this function.
|
||||||
|
// rootLeavesVector: i/o address of the m_root->m_leavesVector
|
||||||
|
int OctreeNode::removeLeaves(std::vector<OctreeNode*>& auxParentVector,
|
||||||
|
std::vector<OctreeNode*>& rootLeavesVector,
|
||||||
|
int octreeDeep)
|
||||||
|
{
|
||||||
|
// Apply to OctreeNode which has children which are leaf nodes
|
||||||
|
int result = 0;
|
||||||
|
for (int i=octreeDeep; i>=0; i--) {
|
||||||
|
if ((*m_children)[i].isLeaf()) {
|
||||||
|
m_leafColor.add((*m_children)[i].getLeafColor());
|
||||||
|
result++;
|
||||||
|
if (rootLeavesVector[rootLeavesVector.size()-1] == &((*m_children)[i]))
|
||||||
|
rootLeavesVector.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auxParentVector.push_back(this);
|
||||||
|
return result - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
OctreeMap::OctreeMap()
|
||||||
|
: m_palette(nullptr)
|
||||||
|
, m_modifications(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OctreeMap::makePalette(Palette* palette,
|
||||||
|
const int colorCount,
|
||||||
|
const int levelDeep)
|
||||||
|
{
|
||||||
|
if (m_root.children()) {
|
||||||
|
// We create paletteIndex to get a "global like" variable, in collectLeafNodes
|
||||||
|
// function, the purpose is having a incremental variable in the stack memory
|
||||||
|
// sharend between all recursive calls of collectLeafNodes.
|
||||||
|
int paletteIndex = 0;
|
||||||
|
m_root.collectLeafNodes(&m_leavesVector, paletteIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can improve the octree accuracy, makePalette returns false, then
|
||||||
|
// outside from this function we must re-construct the octreeMap all again with
|
||||||
|
// deep level equal to 8.
|
||||||
|
if (levelDeep == 7 && m_leavesVector.size() < colorCount)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<OctreeNode*> auxLeavesVector; // auxiliary collapsed node accumulator
|
||||||
|
bool keepReducingMap = true;
|
||||||
|
|
||||||
|
for (int level = levelDeep; level > -1; level--) {
|
||||||
|
for (int i=m_leavesVector.size()-1; i>=0; i--) {
|
||||||
|
if (m_leavesVector.size() + auxLeavesVector.size() <= colorCount) {
|
||||||
|
for (int j=0; j < auxLeavesVector.size(); j++)
|
||||||
|
m_leavesVector.push_back(auxLeavesVector[auxLeavesVector.size() - 1 - j]);
|
||||||
|
keepReducingMap = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (m_leavesVector.size() == 0) {
|
||||||
|
// When colorCount is < 8, auxLeavesVector->size() could reach the 8 size,
|
||||||
|
// if this is true and we don't stop the regular removeLeaves algorithm,
|
||||||
|
// the 8 remains colors will collapse in one.
|
||||||
|
// So, we have to reduce color with other method:
|
||||||
|
// Sort colors by pixelCount (most pixelCount on front of sortedVector),
|
||||||
|
// then:
|
||||||
|
// Blend in pairs from the least pixelCount colors.
|
||||||
|
if (auxLeavesVector.size() <= 8 && colorCount < 8 && colorCount > 0) {
|
||||||
|
// Sort colors:
|
||||||
|
std::vector<OctreeNode*> sortedVector;
|
||||||
|
int auxVectorSize = auxLeavesVector.size();
|
||||||
|
for (int k=0; k < auxVectorSize; k++) {
|
||||||
|
int maximumCount = -1;
|
||||||
|
int maximumIndex = -1;
|
||||||
|
for (int j=0; j < auxLeavesVector.size(); j++) {
|
||||||
|
if (auxLeavesVector[j]->getLeafColor().pixelCount() > maximumCount) {
|
||||||
|
maximumCount = auxLeavesVector[j]->getLeafColor().pixelCount();
|
||||||
|
maximumIndex = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortedVector.push_back(auxLeavesVector[maximumIndex]);
|
||||||
|
auxLeavesVector.erase(auxLeavesVector.begin() + maximumIndex);
|
||||||
|
}
|
||||||
|
// End Sort colors.
|
||||||
|
// Blend colors:
|
||||||
|
for(;;) {
|
||||||
|
if (sortedVector.size() <= colorCount) {
|
||||||
|
for (int k=0; k<sortedVector.size(); k++)
|
||||||
|
m_leavesVector.push_back(sortedVector[k]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sortedVector[sortedVector.size()-2]->getLeafColor()
|
||||||
|
.add(sortedVector[sortedVector.size()-1]->getLeafColor());
|
||||||
|
sortedVector.pop_back();
|
||||||
|
}
|
||||||
|
// End Blend colors:
|
||||||
|
keepReducingMap = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_leavesVector.back()->parent()->removeLeaves(auxLeavesVector, m_leavesVector);
|
||||||
|
}
|
||||||
|
if (keepReducingMap) {
|
||||||
|
// Copy collapsed leaves to m_leavesVector
|
||||||
|
int auxLeavesVectorSize = auxLeavesVector.size();
|
||||||
|
for (int i=0; i<auxLeavesVectorSize; i++)
|
||||||
|
m_leavesVector.push_back(auxLeavesVector[auxLeavesVector.size() - 1 - i]);
|
||||||
|
auxLeavesVector.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int leafCount = m_leavesVector.size();
|
||||||
|
int aux = 0;
|
||||||
|
if (m_maskColor == 0x00FFFFFF)
|
||||||
|
palette->resize(leafCount);
|
||||||
|
else{
|
||||||
|
palette->resize(leafCount + 1);
|
||||||
|
palette->setEntry(0, m_maskColor);
|
||||||
|
aux = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<leafCount; i++)
|
||||||
|
palette->setEntry(i+aux, m_leavesVector[i]->getLeafColor().normalizeColor().LeafColorToColor());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeMap::fillOrphansNodes(const Palette* palette)
|
||||||
|
{
|
||||||
|
m_root.fillOrphansNodes(palette, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeMap::feedWithImage(const Image* image,
|
||||||
|
const color_t maskColor,
|
||||||
|
const int levelDeep)
|
||||||
|
{
|
||||||
|
ASSERT(image);
|
||||||
|
ASSERT(image->pixelFormat() == IMAGE_RGB);
|
||||||
|
uint32_t color;
|
||||||
|
const LockImageBits<RgbTraits> bits(image);
|
||||||
|
auto it = bits.begin(), end = bits.end();
|
||||||
|
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
color = *it;
|
||||||
|
if (rgba_geta(color) > 0)
|
||||||
|
addColor(color, levelDeep);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_maskColor = maskColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int OctreeMap::mapColor(color_t rgba) const
|
||||||
|
{
|
||||||
|
if (m_root.children())
|
||||||
|
return m_root.mapColor(rgba_getr(rgba),
|
||||||
|
rgba_getg(rgba),
|
||||||
|
rgba_getb(rgba), 0);
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctreeMap::regenerateMap(const Palette* palette, const int maskIndex)
|
||||||
|
{
|
||||||
|
ASSERT(palette);
|
||||||
|
if (!palette)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Skip useless regenerations
|
||||||
|
if (m_palette == palette &&
|
||||||
|
m_modifications == palette->getModifications() &&
|
||||||
|
m_maskIndex == maskIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_root = OctreeNode();
|
||||||
|
m_leavesVector.clear();
|
||||||
|
m_maskIndex = maskIndex;
|
||||||
|
int maskColorBestFitIndex;
|
||||||
|
if (maskIndex < 0) {
|
||||||
|
m_maskColor = 0x00ffffff;
|
||||||
|
maskColorBestFitIndex = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_maskColor = palette->getEntry(maskIndex);
|
||||||
|
maskColorBestFitIndex = palette->findBestfit(rgba_getr(m_maskColor),
|
||||||
|
rgba_getg(m_maskColor),
|
||||||
|
rgba_getb(m_maskColor), 255, maskIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<palette->size(); i++) {
|
||||||
|
if (i == maskIndex) {
|
||||||
|
m_root.addColor(palette->entry(i), 0, &m_root, maskColorBestFitIndex, 8);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_root.addColor(palette->entry(i), 0, &m_root, i, 8);
|
||||||
|
}
|
||||||
|
m_root.fillOrphansNodes(palette, 0, 0);
|
||||||
|
|
||||||
|
m_palette = palette;
|
||||||
|
m_modifications = palette->getModifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace doc
|
176
src/doc/octree_map.h
Normal file
176
src/doc/octree_map.h
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef DOC_OCTREEMAP_H_INCLUDED
|
||||||
|
#define DOC_OCTREEMAP_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "doc/image_impl.h"
|
||||||
|
#include "doc/palette.h"
|
||||||
|
#include "doc/rgbmap.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
class OctreeNode {
|
||||||
|
private:
|
||||||
|
class LeafColor {
|
||||||
|
public:
|
||||||
|
LeafColor() :
|
||||||
|
m_r(0),
|
||||||
|
m_g(0),
|
||||||
|
m_b(0),
|
||||||
|
m_pixelCount(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
LeafColor(int r, int g, int b, int pixelCount) :
|
||||||
|
m_r((double)r),
|
||||||
|
m_g((double)g),
|
||||||
|
m_b((double)b),
|
||||||
|
m_pixelCount(pixelCount)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void add(color_t c)
|
||||||
|
{
|
||||||
|
m_r += rgba_getr(c);
|
||||||
|
m_g += rgba_getg(c);
|
||||||
|
m_b += rgba_getb(c);
|
||||||
|
m_pixelCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(LeafColor leafColor)
|
||||||
|
{
|
||||||
|
m_r += leafColor.m_r;
|
||||||
|
m_g += leafColor.m_g;
|
||||||
|
m_b += leafColor.m_b;
|
||||||
|
m_pixelCount += leafColor.m_pixelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeafColor normalizeColor()
|
||||||
|
{
|
||||||
|
int auxR = (((int)m_r) % m_pixelCount > m_pixelCount / 2)? 1 : 0;
|
||||||
|
int auxG = (((int)m_g) % m_pixelCount > m_pixelCount / 2)? 1 : 0;
|
||||||
|
int auxB = (((int)m_b) % m_pixelCount > m_pixelCount / 2)? 1 : 0;
|
||||||
|
return LeafColor(m_r / m_pixelCount + auxR,
|
||||||
|
m_g / m_pixelCount + auxG,
|
||||||
|
m_b / m_pixelCount + auxB,
|
||||||
|
m_pixelCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
color_t LeafColorToColor()
|
||||||
|
{
|
||||||
|
return 0xff000000 + (((int)m_b) << 16) + (((int)m_g) << 8) + (int)m_r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pixelCount() { return m_pixelCount; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_r;
|
||||||
|
double m_g;
|
||||||
|
double m_b;
|
||||||
|
int m_pixelCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
OctreeNode()
|
||||||
|
{
|
||||||
|
m_paletteIndex = 0;
|
||||||
|
m_children.reset(nullptr);
|
||||||
|
m_parent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getOctet(color_t c, int level)
|
||||||
|
{
|
||||||
|
int aux = c >> (7 - level);
|
||||||
|
int octet = aux & 1;
|
||||||
|
aux = aux >> (7);
|
||||||
|
octet += (aux & 2);
|
||||||
|
return octet + ((aux >> 7) & 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static color_t octetToBranchColor(int octet, int level)
|
||||||
|
{
|
||||||
|
int auxR = (octet & 1) << (7 - level);
|
||||||
|
int auxG = (octet & 2) << (14 - level);
|
||||||
|
int auxB = (octet & 4) << (21 - level);
|
||||||
|
return auxR + auxG + auxB;
|
||||||
|
}
|
||||||
|
|
||||||
|
OctreeNode* parent() const { return m_parent; }
|
||||||
|
std::array<OctreeNode, 8>* children() const { return m_children.get(); }
|
||||||
|
LeafColor getLeafColor() const { return m_leafColor; }
|
||||||
|
|
||||||
|
void addColor(color_t c, int level, OctreeNode* parent,
|
||||||
|
int paletteIndex = 0, int levelDeep = 7);
|
||||||
|
|
||||||
|
void fillOrphansNodes(const Palette* palette,
|
||||||
|
const color_t upstreamBranchColor,
|
||||||
|
const int level);
|
||||||
|
|
||||||
|
void fillMostSignificantNodes(int level);
|
||||||
|
|
||||||
|
int mapColor(int r, int g, int b, int level) const;
|
||||||
|
|
||||||
|
void collectLeafNodes(std::vector<OctreeNode*>* leavesVector, int& paletteIndex);
|
||||||
|
|
||||||
|
// removeLeaves(): remove leaves from a common parent
|
||||||
|
// auxParentVector: i/o addreess of an auxiliary parent leaf Vector from outside.
|
||||||
|
// rootLeavesVector: i/o address of the m_root->m_leavesVector
|
||||||
|
int removeLeaves(std::vector<OctreeNode*>& auxParentVector,
|
||||||
|
std::vector<OctreeNode*>& rootLeavesVector,
|
||||||
|
int octreeDeep = 7);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isLeaf() { return m_leafColor.pixelCount() > 0; }
|
||||||
|
void paletteIndex(int index) { m_paletteIndex = index; }
|
||||||
|
|
||||||
|
LeafColor m_leafColor;
|
||||||
|
int m_paletteIndex;
|
||||||
|
std::unique_ptr<std::array<OctreeNode, 8>> m_children;
|
||||||
|
OctreeNode* m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OctreeMap : public RgbMap {
|
||||||
|
public:
|
||||||
|
OctreeMap();
|
||||||
|
|
||||||
|
void addColor(color_t color, int levelDeep = 7)
|
||||||
|
{
|
||||||
|
m_root.addColor(color, 0, &m_root, 0, levelDeep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// makePalette returns true if a 7 level octreeDeep is OK, and false
|
||||||
|
// if we can add ONE level deep.
|
||||||
|
bool makePalette(Palette* palette,
|
||||||
|
const int colorCount,
|
||||||
|
const int levelDeep = 7);
|
||||||
|
|
||||||
|
void feedWithImage(const Image* image,
|
||||||
|
const color_t maskColor,
|
||||||
|
const int levelDeep = 7);
|
||||||
|
|
||||||
|
// RgbMap impl
|
||||||
|
void regenerateMap(const Palette* palette, const int maskIndex) override;
|
||||||
|
int mapColor(color_t rgba) const override;
|
||||||
|
|
||||||
|
int getModifications() const { return m_modifications; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillOrphansNodes(const Palette* palette);
|
||||||
|
|
||||||
|
OctreeNode m_root;
|
||||||
|
std::vector<OctreeNode*> m_leavesVector;
|
||||||
|
const Palette* m_palette;
|
||||||
|
int m_modifications;
|
||||||
|
int m_maskIndex;
|
||||||
|
color_t m_maskColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace doc
|
||||||
|
#endif
|
@ -272,6 +272,31 @@ int Palette::findBestfit(int r, int g, int b, int a, int mask_index) const
|
|||||||
return bestfit;
|
return bestfit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Palette::findBestfit2(int r, int g, int b) const
|
||||||
|
{
|
||||||
|
ASSERT(r >= 0 && r <= 255);
|
||||||
|
ASSERT(g >= 0 && g <= 255);
|
||||||
|
ASSERT(b >= 0 && b <= 255);
|
||||||
|
|
||||||
|
int bestfit = 0;
|
||||||
|
int lowest = std::numeric_limits<int>::max();
|
||||||
|
int size = m_colors.size();
|
||||||
|
|
||||||
|
for (int i=0; i<size; ++i) {
|
||||||
|
color_t rgb = m_colors[i];
|
||||||
|
int rDiff = r - rgba_getr(rgb);
|
||||||
|
int gDiff = g - rgba_getg(rgb);
|
||||||
|
int bDiff = b - rgba_getb(rgb);
|
||||||
|
|
||||||
|
int diff = rDiff * rDiff * 900 + gDiff * gDiff * 3481 + bDiff * bDiff * 121;
|
||||||
|
if (diff < lowest) {
|
||||||
|
lowest = diff;
|
||||||
|
bestfit = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestfit;
|
||||||
|
}
|
||||||
|
|
||||||
void Palette::applyRemap(const Remap& remap)
|
void Palette::applyRemap(const Remap& remap)
|
||||||
{
|
{
|
||||||
Palette original(*this);
|
Palette original(*this);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2018 David Capello
|
// Copyright (c) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -89,6 +90,7 @@ namespace doc {
|
|||||||
|
|
||||||
int findExactMatch(int r, int g, int b, int a, int mask_index) const;
|
int findExactMatch(int r, int g, int b, int a, int mask_index) const;
|
||||||
int findBestfit(int r, int g, int b, int a, int mask_index) const;
|
int findBestfit(int r, int g, int b, int a, int mask_index) const;
|
||||||
|
int findBestfit2(int r, int g, int b) const;
|
||||||
|
|
||||||
void applyRemap(const Remap& remap);
|
void applyRemap(const Remap& remap);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2001-2016 David Capello
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
@ -9,48 +9,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/debug.h"
|
#include "base/debug.h"
|
||||||
#include "base/disable_copying.h"
|
#include "doc/color.h"
|
||||||
#include "doc/object.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
|
||||||
class Palette;
|
class Palette;
|
||||||
|
|
||||||
// It acts like a cache for Palette:findBestfit() calls.
|
// Matches a RGBA value with an index in a color palette (doc::Palette).
|
||||||
class RgbMap : public Object {
|
class RgbMap {
|
||||||
// Bit activated on m_map entries that aren't yet calculated.
|
|
||||||
const int INVALID = 256;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RgbMap();
|
virtual ~RgbMap() { }
|
||||||
|
|
||||||
bool match(const Palette* palette) const;
|
virtual void regenerateMap(const Palette* palette, const int maskIndex) = 0;
|
||||||
void regenerate(const Palette* palette, int mask_index);
|
|
||||||
|
|
||||||
int mapColor(int r, int g, int b, int a) const {
|
// Should return the best index in a palette that matches the given RGBA values.
|
||||||
|
virtual int mapColor(const color_t rgba) const = 0;
|
||||||
|
|
||||||
|
int mapColor(const int r,
|
||||||
|
const int g,
|
||||||
|
const int b,
|
||||||
|
const int a) const {
|
||||||
ASSERT(r >= 0 && r < 256);
|
ASSERT(r >= 0 && r < 256);
|
||||||
ASSERT(g >= 0 && g < 256);
|
ASSERT(g >= 0 && g < 256);
|
||||||
ASSERT(b >= 0 && b < 256);
|
ASSERT(b >= 0 && b < 256);
|
||||||
ASSERT(a >= 0 && a < 256);
|
ASSERT(a >= 0 && a < 256);
|
||||||
// bits -> bbbbbgggggrrrrraaa
|
return mapColor(rgba(r, g, b, a));
|
||||||
int i = (a>>5) | ((b>>3) << 3) | ((g>>3) << 8) | ((r>>3) << 13);
|
|
||||||
int v = m_map[i];
|
|
||||||
return (v & INVALID) ? generateEntry(i, r, g, b, a): v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int maskIndex() const { return m_maskIndex; }
|
|
||||||
|
|
||||||
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(RgbMap);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
21
src/doc/rgbmap_algorithm.h
Normal file
21
src/doc/rgbmap_algorithm.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef DOC_RGBMAP_ALGORITHM_H_INCLUDED
|
||||||
|
#define DOC_RGBMAP_ALGORITHM_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
enum class RgbMapAlgorithm {
|
||||||
|
RGB5A3,
|
||||||
|
OCTREE,
|
||||||
|
DEFAULT = RGB5A3
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace doc
|
||||||
|
|
||||||
|
#endif
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2015 David Capello
|
// Copyright (c) 2001-2015 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -8,7 +9,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "doc/rgbmap.h"
|
#include "doc/rgbmap_rgb5a3.h"
|
||||||
|
|
||||||
#include "doc/color_scales.h"
|
#include "doc/color_scales.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
@ -21,33 +22,32 @@ namespace doc {
|
|||||||
#define ASIZE 8
|
#define ASIZE 8
|
||||||
#define MAPSIZE (RSIZE*GSIZE*BSIZE*ASIZE)
|
#define MAPSIZE (RSIZE*GSIZE*BSIZE*ASIZE)
|
||||||
|
|
||||||
RgbMap::RgbMap()
|
RgbMapRGB5A3::RgbMapRGB5A3()
|
||||||
: Object(ObjectType::RgbMap)
|
: m_map(MAPSIZE)
|
||||||
, m_map(MAPSIZE)
|
, m_palette(nullptr)
|
||||||
, m_palette(NULL)
|
|
||||||
, m_modifications(0)
|
, m_modifications(0)
|
||||||
, m_maskIndex(0)
|
, m_maskIndex(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RgbMap::match(const Palette* palette) const
|
void RgbMapRGB5A3::regenerateMap(const Palette* palette, int maskIndex)
|
||||||
{
|
{
|
||||||
return (m_palette == palette &&
|
// Skip useless regenerations
|
||||||
m_modifications == palette->getModifications());
|
if (m_palette == palette &&
|
||||||
}
|
m_modifications == palette->getModifications() &&
|
||||||
|
m_maskIndex == maskIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
void RgbMap::regenerate(const Palette* palette, int mask_index)
|
|
||||||
{
|
|
||||||
m_palette = palette;
|
m_palette = palette;
|
||||||
m_modifications = palette->getModifications();
|
m_modifications = palette->getModifications();
|
||||||
m_maskIndex = mask_index;
|
m_maskIndex = maskIndex;
|
||||||
|
|
||||||
// Mark all entries as invalid (need to be regenerated)
|
// Mark all entries as invalid (need to be regenerated)
|
||||||
for (uint16_t& entry : m_map)
|
for (uint16_t& entry : m_map)
|
||||||
entry |= INVALID;
|
entry |= INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RgbMap::generateEntry(int i, int r, int g, int b, int a) const
|
int RgbMapRGB5A3::generateEntry(int i, int r, int g, int b, int a) const
|
||||||
{
|
{
|
||||||
return m_map[i] =
|
return m_map[i] =
|
||||||
m_palette->findBestfit(
|
m_palette->findBestfit(
|
59
src/doc/rgbmap_rgb5a3.h
Normal file
59
src/doc/rgbmap_rgb5a3.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2020 Igara Studio S.A.
|
||||||
|
// Copyright (c) 2001-2016 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef DOC_RGBMAP_RGB5A3_H_INCLUDED
|
||||||
|
#define DOC_RGBMAP_RGB5A3_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/debug.h"
|
||||||
|
#include "base/disable_copying.h"
|
||||||
|
#include "doc/object.h"
|
||||||
|
#include "doc/rgbmap.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
class Palette;
|
||||||
|
|
||||||
|
// It acts like a cache for Palette:findBestfit() calls.
|
||||||
|
class RgbMapRGB5A3 : public RgbMap {
|
||||||
|
// Bit activated on m_map entries that aren't yet calculated.
|
||||||
|
const int INVALID = 256;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RgbMapRGB5A3();
|
||||||
|
|
||||||
|
// RgbMap impl
|
||||||
|
void regenerateMap(const Palette* palette, int maskIndex) override;
|
||||||
|
int mapColor(const color_t rgba) const override {
|
||||||
|
const int r = rgba_getr(rgba);
|
||||||
|
const int g = rgba_getg(rgba);
|
||||||
|
const int b = rgba_getb(rgba);
|
||||||
|
const int a = rgba_geta(rgba);
|
||||||
|
// bits -> bbbbbgggggrrrrraaa
|
||||||
|
const int i = (a>>5) | ((b>>3) << 3) | ((g>>3) << 8) | ((r>>3) << 13);
|
||||||
|
const int v = m_map[i];
|
||||||
|
return (v & INVALID) ? generateEntry(i, r, g, b, a): v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maskIndex() const { return m_maskIndex; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace doc
|
||||||
|
|
||||||
|
#endif
|
@ -18,10 +18,11 @@
|
|||||||
#include "doc/cels_range.h"
|
#include "doc/cels_range.h"
|
||||||
#include "doc/image_impl.h"
|
#include "doc/image_impl.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
|
#include "doc/octree_map.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
#include "doc/remap.h"
|
#include "doc/remap.h"
|
||||||
#include "doc/rgbmap.h"
|
#include "doc/rgbmap_rgb5a3.h"
|
||||||
#include "doc/tag.h"
|
#include "doc/tag.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -31,9 +32,7 @@
|
|||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
static RgbMapAlgorithm g_rgbMapAlgorithm = RgbMapAlgorithm::DEFAULT;
|
||||||
// Constructors/Destructor
|
|
||||||
|
|
||||||
static gfx::Rect g_defaultGridBounds(0, 0, 16, 16);
|
static gfx::Rect g_defaultGridBounds(0, 0, 16, 16);
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -48,6 +47,21 @@ void Sprite::SetDefaultGridBounds(const gfx::Rect& defGridBounds)
|
|||||||
g_defaultGridBounds = defGridBounds;
|
g_defaultGridBounds = defGridBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
RgbMapAlgorithm Sprite::DefaultRgbMapAlgorithm()
|
||||||
|
{
|
||||||
|
return g_rgbMapAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void Sprite::SetDefaultRgbMapAlgorithm(const RgbMapAlgorithm mapAlgo)
|
||||||
|
{
|
||||||
|
g_rgbMapAlgorithm = mapAlgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructors/Destructor
|
||||||
|
|
||||||
Sprite::Sprite(const ImageSpec& spec,
|
Sprite::Sprite(const ImageSpec& spec,
|
||||||
int ncolors)
|
int ncolors)
|
||||||
: Object(ObjectType::Sprite)
|
: Object(ObjectType::Sprite)
|
||||||
@ -58,7 +72,6 @@ Sprite::Sprite(const ImageSpec& spec,
|
|||||||
, m_frlens(1, 100) // First frame with 100 msecs of duration
|
, m_frlens(1, 100) // First frame with 100 msecs of duration
|
||||||
, m_root(new LayerGroup(this))
|
, m_root(new LayerGroup(this))
|
||||||
, m_gridBounds(Sprite::DefaultGridBounds())
|
, m_gridBounds(Sprite::DefaultGridBounds())
|
||||||
, m_rgbMap(nullptr) // Initial RGB map
|
|
||||||
, m_tags(this)
|
, m_tags(this)
|
||||||
, m_slices(this)
|
, m_slices(this)
|
||||||
{
|
{
|
||||||
@ -98,9 +111,6 @@ Sprite::~Sprite()
|
|||||||
for (; it != end; ++it)
|
for (; it != end; ++it)
|
||||||
delete *it; // palette
|
delete *it; // palette
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy RGB map
|
|
||||||
delete m_rgbMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -345,27 +355,46 @@ void Sprite::deletePalette(frame_t frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RgbMap* Sprite::rgbMap(frame_t frame) const
|
Sprite::RgbMapFor Sprite::rgbMapForSprite() const
|
||||||
{
|
{
|
||||||
return rgbMap(frame, backgroundLayer() ? RgbMapFor::OpaqueLayer:
|
return backgroundLayer() ? RgbMapFor::OpaqueLayer:
|
||||||
RgbMapFor::TransparentLayer);
|
RgbMapFor::TransparentLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
RgbMap* Sprite::rgbMap(frame_t frame, RgbMapFor forLayer) const
|
RgbMap* Sprite::rgbMap(const frame_t frame) const
|
||||||
|
{
|
||||||
|
return rgbMap(frame, rgbMapForSprite());
|
||||||
|
}
|
||||||
|
|
||||||
|
RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||||
|
const RgbMapFor forLayer) const
|
||||||
|
{
|
||||||
|
return rgbMap(frame,
|
||||||
|
forLayer,
|
||||||
|
g_rgbMapAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
RgbMap* Sprite::rgbMap(const frame_t frame,
|
||||||
|
const RgbMapFor forLayer,
|
||||||
|
const RgbMapAlgorithm mapAlgo) const
|
||||||
{
|
{
|
||||||
int maskIndex = (forLayer == RgbMapFor::OpaqueLayer ?
|
int maskIndex = (forLayer == RgbMapFor::OpaqueLayer ?
|
||||||
-1: transparentColor());
|
-1: transparentColor());
|
||||||
|
|
||||||
if (m_rgbMap == NULL) {
|
if (!m_rgbMap || m_rgbMapAlgorithm != mapAlgo) {
|
||||||
m_rgbMap = new RgbMap();
|
m_rgbMapAlgorithm = mapAlgo;
|
||||||
m_rgbMap->regenerate(palette(frame), maskIndex);
|
switch (m_rgbMapAlgorithm) {
|
||||||
|
case RgbMapAlgorithm::RGB5A3: m_rgbMap.reset(new RgbMapRGB5A3); break;
|
||||||
|
case RgbMapAlgorithm::OCTREE: m_rgbMap.reset(new OctreeMap); break;
|
||||||
|
default:
|
||||||
|
m_rgbMap.reset(nullptr);
|
||||||
|
ASSERT(false);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
else if (!m_rgbMap->match(palette(frame)) ||
|
|
||||||
m_rgbMap->maskIndex() != maskIndex) {
|
|
||||||
m_rgbMap->regenerate(palette(frame), maskIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_rgbMap;
|
m_rgbMap->regenerateMap(palette(frame), maskIndex);
|
||||||
|
return m_rgbMap.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -21,10 +21,12 @@
|
|||||||
#include "doc/object.h"
|
#include "doc/object.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
#include "doc/pixel_ratio.h"
|
#include "doc/pixel_ratio.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
#include "doc/slices.h"
|
#include "doc/slices.h"
|
||||||
#include "doc/tags.h"
|
#include "doc/tags.h"
|
||||||
#include "gfx/rect.h"
|
#include "gfx/rect.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define DOC_SPRITE_MAX_WIDTH 65535
|
#define DOC_SPRITE_MAX_WIDTH 65535
|
||||||
@ -42,6 +44,7 @@ namespace doc {
|
|||||||
class Palette;
|
class Palette;
|
||||||
class Remap;
|
class Remap;
|
||||||
class RgbMap;
|
class RgbMap;
|
||||||
|
class RgbMapRGB5A3;
|
||||||
class SelectedFrames;
|
class SelectedFrames;
|
||||||
|
|
||||||
typedef std::vector<Palette*> PalettesList;
|
typedef std::vector<Palette*> PalettesList;
|
||||||
@ -100,8 +103,11 @@ namespace doc {
|
|||||||
color_t transparentColor() const { return m_spec.maskColor(); }
|
color_t transparentColor() const { return m_spec.maskColor(); }
|
||||||
void setTransparentColor(color_t color);
|
void setTransparentColor(color_t color);
|
||||||
|
|
||||||
|
// Defaults
|
||||||
static gfx::Rect DefaultGridBounds();
|
static gfx::Rect DefaultGridBounds();
|
||||||
static void SetDefaultGridBounds(const gfx::Rect& defGridBounds);
|
static void SetDefaultGridBounds(const gfx::Rect& defGridBounds);
|
||||||
|
static RgbMapAlgorithm DefaultRgbMapAlgorithm();
|
||||||
|
static void SetDefaultRgbMapAlgorithm(const RgbMapAlgorithm mapAlgo);
|
||||||
|
|
||||||
const gfx::Rect& gridBounds() const { return m_gridBounds; }
|
const gfx::Rect& gridBounds() const { return m_gridBounds; }
|
||||||
void setGridBounds(const gfx::Rect& rc) { m_gridBounds = rc; }
|
void setGridBounds(const gfx::Rect& rc) { m_gridBounds = rc; }
|
||||||
@ -131,8 +137,13 @@ namespace doc {
|
|||||||
|
|
||||||
void deletePalette(frame_t frame);
|
void deletePalette(frame_t frame);
|
||||||
|
|
||||||
RgbMap* rgbMap(frame_t frame) const;
|
RgbMapFor rgbMapForSprite() const;
|
||||||
RgbMap* rgbMap(frame_t frame, RgbMapFor forLayer) const;
|
RgbMap* rgbMap(const frame_t frame) const;
|
||||||
|
RgbMap* rgbMap(const frame_t frame,
|
||||||
|
const RgbMapFor forLayer) const;
|
||||||
|
RgbMap* rgbMap(const frame_t frame,
|
||||||
|
const RgbMapFor forLayer,
|
||||||
|
const RgbMapAlgorithm mapAlgo) const;
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// Frames
|
// Frames
|
||||||
@ -199,7 +210,8 @@ namespace doc {
|
|||||||
gfx::Rect m_gridBounds; // grid settings
|
gfx::Rect m_gridBounds; // grid settings
|
||||||
|
|
||||||
// Current rgb map
|
// Current rgb map
|
||||||
mutable RgbMap* m_rgbMap;
|
mutable RgbMapAlgorithm m_rgbMapAlgorithm;
|
||||||
|
mutable std::unique_ptr<RgbMap> m_rgbMap;
|
||||||
|
|
||||||
Tags m_tags;
|
Tags m_tags;
|
||||||
Slices m_slices;
|
Slices m_slices;
|
||||||
|
@ -138,10 +138,7 @@ void BrightnessContrastFilter::applyToIndexed(FilterManager* filterMgr)
|
|||||||
|
|
||||||
color_t c = pal->getEntry(*(src_address++));
|
color_t c = pal->getEntry(*(src_address++));
|
||||||
applyFilterToRgb(target, c);
|
applyFilterToRgb(target, c);
|
||||||
*(dst_address++) = rgbmap->mapColor(rgba_getr(c),
|
*(dst_address++) = rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,10 +168,7 @@ void HueSaturationFilter::applyToIndexed(FilterManager* filterMgr)
|
|||||||
|
|
||||||
color_t c = pal->getEntry(*(src_address++));
|
color_t c = pal->getEntry(*(src_address++));
|
||||||
applyFilterToRgb(target, c);
|
applyFilterToRgb(target, c);
|
||||||
*(dst_address++) = rgbmap->mapColor(rgba_getr(c),
|
*(dst_address++) = rgbmap->mapColor(c);
|
||||||
rgba_getg(c),
|
|
||||||
rgba_getb(c),
|
|
||||||
rgba_geta(c));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
|
|
||||||
#include "doc/image_impl.h"
|
#include "doc/image_impl.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
|
#include "doc/octree_map.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
#include "doc/remap.h"
|
#include "doc/remap.h"
|
||||||
#include "doc/rgbmap.h"
|
|
||||||
#include "doc/sprite.h"
|
#include "doc/sprite.h"
|
||||||
#include "render/dithering.h"
|
#include "render/dithering.h"
|
||||||
#include "render/error_diffusion.h"
|
#include "render/error_diffusion.h"
|
||||||
@ -42,9 +42,14 @@ Palette* create_palette_from_sprite(
|
|||||||
const bool withAlpha,
|
const bool withAlpha,
|
||||||
Palette* palette,
|
Palette* palette,
|
||||||
TaskDelegate* delegate,
|
TaskDelegate* delegate,
|
||||||
const bool newBlend)
|
const bool newBlend,
|
||||||
|
const RgbMapAlgorithm mappingAlgorithm)
|
||||||
{
|
{
|
||||||
PaletteOptimizer optimizer;
|
PaletteOptimizer optimizer;
|
||||||
|
OctreeMap octreemap;
|
||||||
|
const color_t maskColor = (sprite->backgroundLayer()
|
||||||
|
&& sprite->allLayersCount() == 1) ? 0x00FFFFFF:
|
||||||
|
sprite->transparentColor();
|
||||||
|
|
||||||
if (!palette)
|
if (!palette)
|
||||||
palette = new Palette(fromFrame, 256);
|
palette = new Palette(fromFrame, 256);
|
||||||
@ -58,7 +63,15 @@ Palette* create_palette_from_sprite(
|
|||||||
render.setNewBlend(newBlend);
|
render.setNewBlend(newBlend);
|
||||||
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
|
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
|
||||||
render.renderSprite(flat_image.get(), sprite, frame);
|
render.renderSprite(flat_image.get(), sprite, frame);
|
||||||
|
|
||||||
|
switch (mappingAlgorithm) {
|
||||||
|
case RgbMapAlgorithm::RGB5A3:
|
||||||
optimizer.feedWithImage(flat_image.get(), withAlpha);
|
optimizer.feedWithImage(flat_image.get(), withAlpha);
|
||||||
|
break;
|
||||||
|
case RgbMapAlgorithm::OCTREE:
|
||||||
|
octreemap.feedWithImage(flat_image.get(), maskColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (delegate) {
|
if (delegate) {
|
||||||
if (!delegate->continueTask())
|
if (!delegate->continueTask())
|
||||||
@ -69,12 +82,35 @@ Palette* create_palette_from_sprite(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (mappingAlgorithm) {
|
||||||
|
case RgbMapAlgorithm::RGB5A3:
|
||||||
// Generate an optimized palette
|
// Generate an optimized palette
|
||||||
optimizer.calculate(
|
optimizer.calculate(
|
||||||
palette,
|
palette,
|
||||||
// Transparent color is needed if we have transparent layers
|
// Transparent color is needed if we have transparent layers
|
||||||
(sprite->backgroundLayer() &&
|
(sprite->backgroundLayer() &&
|
||||||
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()));
|
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()));
|
||||||
|
break;
|
||||||
|
case RgbMapAlgorithm::OCTREE:
|
||||||
|
if (!octreemap.makePalette(palette, palette->size())) {
|
||||||
|
// We can use an 8-bit deep octree map, instead of 7-bit of the
|
||||||
|
// first attempt.
|
||||||
|
octreemap = OctreeMap();
|
||||||
|
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
|
||||||
|
render.renderSprite(flat_image.get(), sprite, frame);
|
||||||
|
octreemap.feedWithImage(flat_image.get(), maskColor , 8);
|
||||||
|
if (delegate) {
|
||||||
|
if (!delegate->continueTask())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
delegate->notifyTaskProgress(
|
||||||
|
double(frame-fromFrame+1) / double(toFrame-fromFrame+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
octreemap.makePalette(palette, palette->size(), 8);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return palette;
|
return palette;
|
||||||
}
|
}
|
||||||
@ -179,7 +215,7 @@ Image* convert_pixel_format(
|
|||||||
if (a == 0)
|
if (a == 0)
|
||||||
*dst_it = new_mask_color;
|
*dst_it = new_mask_color;
|
||||||
else if (rgbmap)
|
else if (rgbmap)
|
||||||
*dst_it = rgbmap->mapColor(r, g, b, a);
|
*dst_it = rgbmap->mapColor(c);
|
||||||
else
|
else
|
||||||
*dst_it = palette->findBestfit(r, g, b, a, new_mask_color);
|
*dst_it = palette->findBestfit(r, g, b, a, new_mask_color);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "doc/frame.h"
|
#include "doc/frame.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
|
#include "doc/rgbmap_algorithm.h"
|
||||||
#include "render/color_histogram.h"
|
#include "render/color_histogram.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -45,7 +46,8 @@ namespace render {
|
|||||||
const bool withAlpha,
|
const bool withAlpha,
|
||||||
doc::Palette* newPalette, // Can be NULL to create a new palette
|
doc::Palette* newPalette, // Can be NULL to create a new palette
|
||||||
TaskDelegate* delegate,
|
TaskDelegate* delegate,
|
||||||
const bool newBlend);
|
const bool newBlend,
|
||||||
|
const RgbMapAlgorithm mappingAlgorithm);
|
||||||
|
|
||||||
// Changes the image pixel format. The dithering method is used only
|
// Changes the image pixel format. The dithering method is used only
|
||||||
// when you want to convert from RGB to Indexed.
|
// when you want to convert from RGB to Indexed.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user