mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-29 21:33:12 +00:00
Add different formulas to convert RGB to Grayscale
This commit is contained in:
parent
07f1510ebd
commit
1f34d0e46e
@ -117,6 +117,12 @@
|
||||
<value id="SRGB" value="1" />
|
||||
<value id="SPECIFIC" value="2" />
|
||||
</enum>
|
||||
<enum id="ToGrayAlgorithm">
|
||||
<value id="DEFAULT" value="0" />
|
||||
<value id="LUMA" value="0" />
|
||||
<value id="HSV" value="1" />
|
||||
<value id="HSL" value="2" />
|
||||
</enum>
|
||||
</types>
|
||||
|
||||
<global>
|
||||
@ -258,6 +264,7 @@
|
||||
<option id="with_alpha" type="bool" default="true" />
|
||||
<option id="dithering_algorithm" type="std::string" />
|
||||
<option id="dithering_factor" type="int" default="100" />
|
||||
<option id="to_gray" type="ToGrayAlgorithm" default="ToGrayAlgorithm::DEFAULT" />
|
||||
</section>
|
||||
<section id="eyedropper" text="Editor">
|
||||
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2019 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2019-2020 Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2017 David Capello -->
|
||||
<gui>
|
||||
<window id="color_mode" text="@.title">
|
||||
@ -13,6 +13,11 @@
|
||||
<slider min="0" max="100" id="factor" minwidth="100" />
|
||||
<label text="%" />
|
||||
</hbox>
|
||||
<combobox id="to_gray_combobox">
|
||||
<listitem text="!Luminance" />
|
||||
<listitem text="!HSV" />
|
||||
<listitem text="!HSL" />
|
||||
</combobox>
|
||||
<check text="@.flatten" id="flatten" />
|
||||
<separator horizontal="true" />
|
||||
<hbox>
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -69,6 +69,7 @@ private:
|
||||
SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
const PixelFormat newFormat,
|
||||
const render::Dithering& dithering,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate)
|
||||
: WithSprite(sprite)
|
||||
, m_oldFormat(sprite->pixelFormat())
|
||||
@ -89,6 +90,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
|
||||
sprite->palette(cel->frame()),
|
||||
cel->layer()->isBackground(),
|
||||
old_image->maskColor(),
|
||||
toGray,
|
||||
&superDel));
|
||||
|
||||
m_seq.add(new cmd::ReplaceImage(sprite, old_image, new_image));
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "app/cmd/with_sprite.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/pixel_format.h"
|
||||
|
||||
namespace doc {
|
||||
@ -31,6 +32,7 @@ namespace cmd {
|
||||
SetPixelFormat(doc::Sprite* sprite,
|
||||
const doc::PixelFormat newFormat,
|
||||
const render::Dithering& dithering,
|
||||
doc::rgba_to_graya_func toGray,
|
||||
render::TaskDelegate* delegate);
|
||||
|
||||
protected:
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "ui/size_hint_event.h"
|
||||
|
||||
#include "color_mode.xml.h"
|
||||
#include <string>
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -51,6 +52,15 @@ using namespace ui;
|
||||
|
||||
namespace {
|
||||
|
||||
rgba_to_graya_func get_gray_func(gen::ToGrayAlgorithm toGray) {
|
||||
switch (toGray) {
|
||||
case gen::ToGrayAlgorithm::LUMA: return &rgba_to_graya_using_luma;
|
||||
case gen::ToGrayAlgorithm::HSV: return &rgba_to_graya_using_hsv;
|
||||
case gen::ToGrayAlgorithm::HSL: return &rgba_to_graya_using_hsl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class ConversionItem : public ListItem {
|
||||
public:
|
||||
ConversionItem(const doc::PixelFormat pixelFormat)
|
||||
@ -79,6 +89,7 @@ public:
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::Dithering& dithering,
|
||||
const gen::ToGrayAlgorithm toGray,
|
||||
const gfx::Point& pos,
|
||||
const bool newBlend)
|
||||
: m_image(dstImage)
|
||||
@ -91,10 +102,12 @@ public:
|
||||
sprite, frame,
|
||||
pixelFormat,
|
||||
dithering,
|
||||
toGray,
|
||||
newBlend]() { // Copy the matrix
|
||||
run(sprite, frame,
|
||||
pixelFormat,
|
||||
dithering,
|
||||
toGray,
|
||||
newBlend);
|
||||
})
|
||||
{
|
||||
@ -118,6 +131,7 @@ private:
|
||||
const doc::frame_t frame,
|
||||
const doc::PixelFormat pixelFormat,
|
||||
const render::Dithering& dithering,
|
||||
const gen::ToGrayAlgorithm toGray,
|
||||
const bool newBlend) {
|
||||
doc::ImageRef tmp(
|
||||
Image::create(sprite->pixelFormat(),
|
||||
@ -142,6 +156,7 @@ private:
|
||||
sprite->palette(frame),
|
||||
(sprite->backgroundLayer() != nullptr),
|
||||
0,
|
||||
get_gray_func(toGray),
|
||||
this);
|
||||
|
||||
m_running = false;
|
||||
@ -211,9 +226,12 @@ public:
|
||||
else {
|
||||
amount()->setVisible(false);
|
||||
}
|
||||
if (from != IMAGE_GRAYSCALE)
|
||||
if (from != IMAGE_GRAYSCALE) {
|
||||
colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE));
|
||||
|
||||
toGrayCombobox()->Change.connect(base::Bind<void>(&ColorModeWindow::onToGrayChange, this));
|
||||
}
|
||||
|
||||
colorModeView()->setMinSize(
|
||||
colorModeView()->sizeHint() +
|
||||
colorMode()->sizeHint());
|
||||
@ -249,6 +267,15 @@ public:
|
||||
return d;
|
||||
}
|
||||
|
||||
gen::ToGrayAlgorithm toGray() const {
|
||||
static_assert(
|
||||
int(gen::ToGrayAlgorithm::LUMA) == 0 &&
|
||||
int(gen::ToGrayAlgorithm::HSV) == 1 &&
|
||||
int(gen::ToGrayAlgorithm::HSL) == 2,
|
||||
"Check that 'to_gray_combobox' combobox items matches these indexes in color_mode.xml");
|
||||
return (gen::ToGrayAlgorithm)toGrayCombobox()->getSelectedItemIndex();
|
||||
}
|
||||
|
||||
bool flattenEnabled() const {
|
||||
return flatten()->isSelected();
|
||||
}
|
||||
@ -307,6 +334,11 @@ private:
|
||||
amount()->setVisible(toIndexed && errorDiff);
|
||||
}
|
||||
|
||||
{
|
||||
const bool toGray = (dstPixelFormat == doc::IMAGE_GRAYSCALE);
|
||||
toGrayCombobox()->setVisible(toGray);
|
||||
}
|
||||
|
||||
m_image.reset(
|
||||
Image::create(dstPixelFormat,
|
||||
visibleBounds.w,
|
||||
@ -336,6 +368,7 @@ private:
|
||||
m_editor->frame(),
|
||||
dstPixelFormat,
|
||||
dithering(),
|
||||
toGray(),
|
||||
visibleBounds.origin(),
|
||||
Preferences::instance().experimental.newBlend()));
|
||||
|
||||
@ -348,6 +381,12 @@ private:
|
||||
onChangeColorMode();
|
||||
}
|
||||
|
||||
void onToGrayChange() {
|
||||
stop();
|
||||
m_selectedItem = nullptr;
|
||||
onChangeColorMode();
|
||||
}
|
||||
|
||||
void onMonitorProgress() {
|
||||
ASSERT(m_bgThread);
|
||||
if (!m_bgThread)
|
||||
@ -402,6 +441,7 @@ private:
|
||||
bool m_useUI;
|
||||
doc::PixelFormat m_format;
|
||||
render::Dithering m_dithering;
|
||||
gen::ToGrayAlgorithm m_toGray;
|
||||
};
|
||||
|
||||
ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||
@ -410,6 +450,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||
m_useUI = true;
|
||||
m_format = IMAGE_RGB;
|
||||
m_dithering = render::Dithering();
|
||||
m_toGray = gen::ToGrayAlgorithm::DEFAULT;
|
||||
}
|
||||
|
||||
void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||
@ -418,7 +459,8 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||
|
||||
std::string format = params.get("format");
|
||||
if (format == "rgb") m_format = IMAGE_RGB;
|
||||
else if (format == "grayscale") m_format = IMAGE_GRAYSCALE;
|
||||
else if (format == "grayscale" ||
|
||||
format == "gray") m_format = IMAGE_GRAYSCALE;
|
||||
else if (format == "indexed") m_format = IMAGE_INDEXED;
|
||||
else
|
||||
m_useUI = true;
|
||||
@ -454,6 +496,16 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||
// TODO object slicing here (from BayerMatrix -> DitheringMatrix)
|
||||
m_dithering.matrix(render::BayerMatrix(8));
|
||||
}
|
||||
|
||||
std::string toGray = params.get("toGray");
|
||||
if (toGray == "luma")
|
||||
m_toGray = gen::ToGrayAlgorithm::LUMA;
|
||||
else if (dithering == "hsv")
|
||||
m_toGray = gen::ToGrayAlgorithm::HSV;
|
||||
else if (dithering == "hsl")
|
||||
m_toGray = gen::ToGrayAlgorithm::HSL;
|
||||
else
|
||||
m_toGray = gen::ToGrayAlgorithm::DEFAULT;
|
||||
}
|
||||
|
||||
bool ChangePixelFormatCommand::onEnabled(Context* context)
|
||||
@ -518,6 +570,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
|
||||
m_format = window.pixelFormat();
|
||||
m_dithering = window.dithering();
|
||||
m_toGray = window.toGray();
|
||||
flatten = window.flattenEnabled();
|
||||
|
||||
window.saveDitheringOptions();
|
||||
@ -554,6 +607,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
||||
new cmd::SetPixelFormat(
|
||||
sprite, m_format,
|
||||
m_dithering,
|
||||
get_gray_func(m_toGray),
|
||||
&job)); // SpriteJob is a render::TaskDelegate
|
||||
});
|
||||
job.waitJob();
|
||||
|
@ -1179,7 +1179,8 @@ void DocExporter::renderTexture(Context* ctx,
|
||||
sample.sprite(),
|
||||
textureImage->pixelFormat(),
|
||||
render::Dithering(),
|
||||
nullptr) // TODO add a delegate to show progress
|
||||
nullptr, // toGray is not needed because the texture is Indexed or RGB
|
||||
nullptr) // TODO add a delegate to show progress
|
||||
.execute(ctx);
|
||||
}
|
||||
|
||||
|
@ -770,12 +770,13 @@ private:
|
||||
Image* oldImage = cel->image();
|
||||
ImageRef newImage(
|
||||
render::convert_pixel_format
|
||||
(oldImage, NULL, IMAGE_RGB,
|
||||
(oldImage, nullptr, IMAGE_RGB,
|
||||
render::Dithering(),
|
||||
nullptr,
|
||||
m_sprite->palette(cel->frame()),
|
||||
m_opaque,
|
||||
m_bgIndex));
|
||||
m_bgIndex,
|
||||
nullptr));
|
||||
|
||||
m_sprite->replaceImage(oldImage->id(), newImage);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -162,6 +162,7 @@ FOR_ENUM(app::gen::StopAtGrid)
|
||||
FOR_ENUM(app::gen::SymmetryMode)
|
||||
FOR_ENUM(app::gen::TimelinePosition)
|
||||
FOR_ENUM(app::gen::WindowColorProfile)
|
||||
FOR_ENUM(app::gen::ToGrayAlgorithm)
|
||||
FOR_ENUM(app::tools::FreehandAlgorithm)
|
||||
FOR_ENUM(app::tools::InkType)
|
||||
FOR_ENUM(app::tools::RotationAlgorithm)
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2019 Igara Studio S.A.
|
||||
Copyright (c) 2018-2020 Igara Studio S.A.
|
||||
Copyright (c) 2001-2018 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
@ -1,4 +1,5 @@
|
||||
// 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.
|
||||
@ -10,6 +11,8 @@
|
||||
|
||||
#include "base/ints.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace doc {
|
||||
|
||||
// The greatest int type to storage a color for an image in the
|
||||
@ -87,6 +90,37 @@ namespace doc {
|
||||
return graya(v, 255);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Conversions
|
||||
|
||||
typedef color_t (*rgba_to_graya_func)(const color_t c);
|
||||
|
||||
inline color_t rgba_to_graya_using_hsv(const color_t c) {
|
||||
const uint8_t M = std::max(rgba_getr(c),
|
||||
std::max(rgba_getg(c),
|
||||
rgba_getb(c)));
|
||||
return graya(M,
|
||||
rgba_geta(c));
|
||||
}
|
||||
|
||||
inline color_t rgba_to_graya_using_hsl(const color_t c) {
|
||||
const int m = std::min(rgba_getr(c),
|
||||
std::min(rgba_getg(c),
|
||||
rgba_getb(c)));
|
||||
const int M = std::max(rgba_getr(c),
|
||||
std::max(rgba_getg(c),
|
||||
rgba_getb(c)));
|
||||
return graya((M + m) / 2,
|
||||
rgba_geta(c));
|
||||
}
|
||||
|
||||
inline color_t rgba_to_graya_using_luma(const color_t c) {
|
||||
return graya(rgb_luma(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c)),
|
||||
rgba_geta(c));
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include "doc/remap.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "render/dithering.h"
|
||||
#include "render/error_diffusion.h"
|
||||
#include "render/ordered_dither.h"
|
||||
@ -90,6 +88,7 @@ Image* convert_pixel_format(
|
||||
const Palette* palette,
|
||||
bool is_background,
|
||||
color_t new_mask_color,
|
||||
rgba_to_graya_func toGray,
|
||||
TaskDelegate* delegate)
|
||||
{
|
||||
if (!new_image)
|
||||
@ -119,6 +118,14 @@ Image* convert_pixel_format(
|
||||
return new_image;
|
||||
}
|
||||
|
||||
// RGB/Indexed -> Gray
|
||||
if ((image->pixelFormat() == IMAGE_RGB ||
|
||||
image->pixelFormat() == IMAGE_INDEXED) &&
|
||||
new_image->pixelFormat() == IMAGE_GRAYSCALE) {
|
||||
if (!toGray)
|
||||
toGray = &rgba_to_graya_using_luma;
|
||||
}
|
||||
|
||||
color_t c;
|
||||
int r, g, b, a;
|
||||
|
||||
@ -141,17 +148,12 @@ Image* convert_pixel_format(
|
||||
LockImageBits<GrayscaleTraits>::iterator dst_it = dstBits.begin();
|
||||
#ifdef _DEBUG
|
||||
LockImageBits<GrayscaleTraits>::iterator dst_end = dstBits.end();
|
||||
ASSERT(toGray);
|
||||
#endif
|
||||
|
||||
for (; src_it != src_end; ++src_it, ++dst_it) {
|
||||
ASSERT(dst_it != dst_end);
|
||||
c = *src_it;
|
||||
|
||||
g = 255 * Hsv(Rgb(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c))).valueInt() / 100;
|
||||
|
||||
*dst_it = graya(g, rgba_geta(c));
|
||||
*dst_it = (*toGray)(*src_it);
|
||||
}
|
||||
ASSERT(dst_it == dst_end);
|
||||
break;
|
||||
@ -280,6 +282,7 @@ Image* convert_pixel_format(
|
||||
LockImageBits<GrayscaleTraits>::iterator dst_it = dstBits.begin();
|
||||
#ifdef _DEBUG
|
||||
LockImageBits<GrayscaleTraits>::iterator dst_end = dstBits.end();
|
||||
ASSERT(toGray);
|
||||
#endif
|
||||
|
||||
for (; src_it != src_end; ++src_it, ++dst_it) {
|
||||
@ -290,13 +293,7 @@ Image* convert_pixel_format(
|
||||
*dst_it = graya(0, 0);
|
||||
else {
|
||||
c = palette->getEntry(c);
|
||||
r = rgba_getr(c);
|
||||
g = rgba_getg(c);
|
||||
b = rgba_getb(c);
|
||||
a = rgba_geta(c);
|
||||
|
||||
g = 255 * Hsv(Rgb(r, g, b)).valueInt() / 100;
|
||||
*dst_it = graya(g, a);
|
||||
*dst_it = (*toGray)(c);
|
||||
}
|
||||
}
|
||||
ASSERT(dst_it == dst_end);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Rener Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -58,6 +58,7 @@ namespace render {
|
||||
const doc::Palette* palette,
|
||||
bool is_background,
|
||||
doc::color_t new_mask_color,
|
||||
doc::rgba_to_graya_func toGray = nullptr,
|
||||
TaskDelegate* delegate = nullptr);
|
||||
|
||||
} // namespace render
|
||||
|
Loading…
x
Reference in New Issue
Block a user