mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Add radial gradient
Related to: * https://steamcommunity.com/app/431730/discussions/1/3182216552785250054/ * https://community.aseprite.org/t/radial-gradient/2711 * part of #1629
This commit is contained in:
parent
250d40c0f3
commit
3ec3f75d91
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -402,6 +402,8 @@
|
||||
<part id="aseprite_face_mouse" x="28" y="272" w="28" h="30" />
|
||||
<part id="aseprite_face_pushed" x="56" y="272" w="28" h="30" />
|
||||
<part id="icon_aspect_ratio" x="256" y="264" w="10" h="8" />
|
||||
<part id="linear_gradient" x="176" y="208" w="8" h="8" />
|
||||
<part id="radial_gradient" x="184" y="208" w="8" h="8" />
|
||||
</parts>
|
||||
<styles>
|
||||
<style id="box" />
|
||||
|
@ -908,9 +908,10 @@ public:
|
||||
imgPos.y *= h;
|
||||
}
|
||||
|
||||
render::render_rgba_linear_gradient(
|
||||
render::render_rgba_gradient(
|
||||
m_tmpImage.get(), imgPos, u, v, c0, c1,
|
||||
loop->getDitheringMatrix());
|
||||
loop->getDitheringMatrix(),
|
||||
loop->getGradientType());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "filters/tiled_mode.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "render/gradient.h"
|
||||
|
||||
namespace gfx {
|
||||
class Region;
|
||||
@ -233,6 +234,7 @@ namespace app {
|
||||
// For gradients
|
||||
virtual render::DitheringMatrix getDitheringMatrix() = 0;
|
||||
virtual render::DitheringAlgorithmBase* getDitheringAlgorithm() = 0;
|
||||
virtual render::GradientType getGradientType() = 0;
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -933,6 +933,27 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
class ContextBar::GradientTypeField : public ButtonSet {
|
||||
public:
|
||||
GradientTypeField() : ButtonSet(2) {
|
||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||
|
||||
addItem(theme->parts.linearGradient());
|
||||
addItem(theme->parts.radialGradient());
|
||||
|
||||
setSelectedItem(0);
|
||||
}
|
||||
|
||||
void setupTooltips(TooltipManager* tooltipManager) {
|
||||
tooltipManager->addTooltipFor(at(0), "Linear Gradient", BOTTOM);
|
||||
tooltipManager->addTooltipFor(at(1), "Radial Gradient", BOTTOM);
|
||||
}
|
||||
|
||||
render::GradientType gradientType() const {
|
||||
return (render::GradientType)selectedItem();
|
||||
}
|
||||
};
|
||||
|
||||
class ContextBar::DropPixelsField : public ButtonSet {
|
||||
public:
|
||||
DropPixelsField() : ButtonSet(2) {
|
||||
@ -1100,6 +1121,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager)
|
||||
addChild(m_tolerance = new ToleranceField());
|
||||
addChild(m_contiguous = new ContiguousField());
|
||||
addChild(m_paintBucketSettings = new PaintBucketSettingsField());
|
||||
addChild(m_gradientType = new GradientTypeField());
|
||||
addChild(m_ditheringSelector = new DitheringSelector(DitheringSelector::SelectMatrix));
|
||||
m_ditheringSelector->setUseCustomWidget(false); // Disable custom widget because the context bar is too small
|
||||
|
||||
@ -1428,6 +1450,7 @@ void ContextBar::updateForTool(tools::Tool* tool)
|
||||
m_paintBucketSettings->setVisible(hasTolerance);
|
||||
m_sprayBox->setVisible(hasSprayOptions);
|
||||
m_selectionOptionsBox->setVisible(hasSelectOptions);
|
||||
m_gradientType->setVisible(withDithering);
|
||||
m_ditheringSelector->setVisible(withDithering);
|
||||
m_selectionMode->setVisible(true);
|
||||
m_pivot->setVisible(true);
|
||||
@ -1718,6 +1741,11 @@ render::DitheringAlgorithmBase* ContextBar::ditheringAlgorithm()
|
||||
return s_dither.get();
|
||||
}
|
||||
|
||||
render::GradientType ContextBar::gradientType()
|
||||
{
|
||||
return m_gradientType->gradientType();
|
||||
}
|
||||
|
||||
void ContextBar::setupTooltips(TooltipManager* tooltipManager)
|
||||
{
|
||||
tooltipManager->addTooltipFor(m_brushBack->at(0), "Discard Brush (Esc)", BOTTOM);
|
||||
@ -1741,6 +1769,7 @@ void ContextBar::setupTooltips(TooltipManager* tooltipManager)
|
||||
"Extra paint bucket options", BOTTOM);
|
||||
|
||||
m_selectionMode->setupTooltips(tooltipManager);
|
||||
m_gradientType->setupTooltips(tooltipManager);
|
||||
m_dropPixels->setupTooltips(tooltipManager);
|
||||
m_symmetry->setupTooltips(tooltipManager);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -19,6 +19,7 @@
|
||||
#include "obs/connection.h"
|
||||
#include "obs/observable.h"
|
||||
#include "obs/signal.h"
|
||||
#include "render/gradient.h"
|
||||
#include "ui/box.h"
|
||||
|
||||
#include <vector>
|
||||
@ -48,6 +49,7 @@ namespace app {
|
||||
|
||||
class BrushSlot;
|
||||
class DitheringSelector;
|
||||
class GradientTypeSelector;
|
||||
|
||||
class ContextBar : public ui::Box
|
||||
, public obs::observable<ContextBarObserver>
|
||||
@ -83,6 +85,7 @@ namespace app {
|
||||
// For gradients
|
||||
render::DitheringMatrix ditheringMatrix();
|
||||
render::DitheringAlgorithmBase* ditheringAlgorithm();
|
||||
render::GradientType gradientType();
|
||||
|
||||
// Signals
|
||||
obs::signal<void()> BrushChange;
|
||||
@ -122,6 +125,7 @@ namespace app {
|
||||
class SprayWidthField;
|
||||
class SpraySpeedField;
|
||||
class SelectionModeField;
|
||||
class GradientTypeField;
|
||||
class TransparentColorField;
|
||||
class PivotField;
|
||||
class RotAlgorithmField;
|
||||
@ -157,6 +161,7 @@ namespace app {
|
||||
ui::Box* m_selectionOptionsBox;
|
||||
DitheringSelector* m_ditheringSelector;
|
||||
SelectionModeField* m_selectionMode;
|
||||
GradientTypeField* m_gradientType;
|
||||
TransparentColorField* m_transparentColor;
|
||||
PivotField* m_pivot;
|
||||
RotAlgorithmField* m_rotAlgo;
|
||||
|
@ -330,6 +330,15 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
render::GradientType getGradientType() override {
|
||||
#ifdef ENABLE_UI // TODO add support when UI is not enabled
|
||||
return App::instance()->contextBar()->gradientType();
|
||||
#else
|
||||
return render::GradientType::Linear;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,4 +1,5 @@
|
||||
Copyright (c) 2001-2017 David Capello
|
||||
Copyright (c) 2019 Igara Studio S.A.
|
||||
Copyright (c) 2001-2018 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -17,6 +18,26 @@
|
||||
|
||||
namespace render {
|
||||
|
||||
void render_rgba_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
const gfx::Point p0,
|
||||
const gfx::Point p1,
|
||||
doc::color_t c0,
|
||||
doc::color_t c1,
|
||||
const render::DitheringMatrix& matrix,
|
||||
const GradientType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GradientType::Linear:
|
||||
render_rgba_linear_gradient(img, imgPos, p0, p1, c0, c1, matrix);
|
||||
break;
|
||||
case GradientType::Radial:
|
||||
render_rgba_radial_gradient(img, imgPos, p0, p1, c0, c1, matrix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void render_rgba_linear_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
@ -98,7 +119,8 @@ void render_rgba_linear_gradient(
|
||||
else {
|
||||
for (int y=0; y<height; ++y) {
|
||||
for (int x=0; x<width; ++x, ++it) {
|
||||
base::Vector2d<double> q(x, y);
|
||||
base::Vector2d<double> q(imgPos.x+x,
|
||||
imgPos.y+y);
|
||||
q -= u;
|
||||
double f = (q * w) / wmag;
|
||||
|
||||
@ -108,4 +130,98 @@ void render_rgba_linear_gradient(
|
||||
}
|
||||
}
|
||||
|
||||
void render_rgba_radial_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
const gfx::Point p0,
|
||||
const gfx::Point p1,
|
||||
doc::color_t c0,
|
||||
doc::color_t c1,
|
||||
const render::DitheringMatrix& matrix)
|
||||
{
|
||||
ASSERT(img->pixelFormat() == doc::IMAGE_RGB);
|
||||
if (img->pixelFormat() != doc::IMAGE_RGB) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is no vector defining the gradient (just one point),
|
||||
// the "gradient" will be just "c0"
|
||||
if (p0 == p1) {
|
||||
img->clear(c0);
|
||||
return;
|
||||
}
|
||||
|
||||
base::Vector2d<double>
|
||||
u(p0.x, p0.y),
|
||||
v(p1.x, p1.y), w;
|
||||
w = (v - u) / 2;
|
||||
|
||||
// As we use non-premultiplied RGB values, we need correct RGB
|
||||
// values on each stop. So in case that one color has alpha=0
|
||||
// (complete transparent), use the RGB values of the
|
||||
// non-transparent color in the other stop point.
|
||||
if (doc::rgba_geta(c0) == 0 &&
|
||||
doc::rgba_geta(c1) != 0) {
|
||||
c0 = (c1 & doc::rgba_rgb_mask);
|
||||
}
|
||||
else if (doc::rgba_geta(c0) != 0 &&
|
||||
doc::rgba_geta(c1) == 0) {
|
||||
c1 = (c0 & doc::rgba_rgb_mask);
|
||||
}
|
||||
|
||||
const double r0 = double(doc::rgba_getr(c0)) / 255.0;
|
||||
const double g0 = double(doc::rgba_getg(c0)) / 255.0;
|
||||
const double b0 = double(doc::rgba_getb(c0)) / 255.0;
|
||||
const double a0 = double(doc::rgba_geta(c0)) / 255.0;
|
||||
|
||||
const double r1 = double(doc::rgba_getr(c1)) / 255.0;
|
||||
const double g1 = double(doc::rgba_getg(c1)) / 255.0;
|
||||
const double b1 = double(doc::rgba_getb(c1)) / 255.0;
|
||||
const double a1 = double(doc::rgba_geta(c1)) / 255.0;
|
||||
|
||||
doc::LockImageBits<doc::RgbTraits> bits(img);
|
||||
auto it = bits.begin();
|
||||
const int width = img->width();
|
||||
const int height = img->height();
|
||||
|
||||
if (matrix.rows() == 1 && matrix.cols() == 1) {
|
||||
for (int y=0; y<height; ++y) {
|
||||
for (int x=0; x<width; ++x, ++it) {
|
||||
base::Vector2d<double> q(imgPos.x+x,
|
||||
imgPos.y+y);
|
||||
q -= (u+v)/2;
|
||||
q.x /= std::fabs(w.x);
|
||||
q.y /= std::fabs(w.y);
|
||||
double f = std::sqrt(q.x*q.x + q.y*q.y);
|
||||
|
||||
doc::color_t c;
|
||||
if (f < 0.0) c = c0;
|
||||
else if (f > 1.0) c = c1;
|
||||
else {
|
||||
c = doc::rgba(int(255.0 * (r0 + f*(r1-r0))),
|
||||
int(255.0 * (g0 + f*(g1-g0))),
|
||||
int(255.0 * (b0 + f*(b1-b0))),
|
||||
int(255.0 * (a0 + f*(a1-a0))));
|
||||
}
|
||||
|
||||
*it = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int y=0; y<height; ++y) {
|
||||
for (int x=0; x<width; ++x, ++it) {
|
||||
base::Vector2d<double> q(imgPos.x+x,
|
||||
imgPos.y+y);
|
||||
q -= (u+v)/2;
|
||||
q.x /= std::fabs(w.x);
|
||||
q.y /= std::fabs(w.y);
|
||||
double f = std::sqrt(q.x*q.x + q.y*q.y);
|
||||
|
||||
*it = (f*(matrix.maxValue()+2) < matrix(y, x)+1 ? c0: c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace render
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -19,6 +20,21 @@ namespace render {
|
||||
|
||||
class DitheringMatrix;
|
||||
|
||||
enum class GradientType {
|
||||
Linear,
|
||||
Radial,
|
||||
};
|
||||
|
||||
void render_rgba_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
const gfx::Point p0,
|
||||
const gfx::Point p1,
|
||||
doc::color_t c0,
|
||||
doc::color_t c1,
|
||||
const render::DitheringMatrix& matrix,
|
||||
const GradientType type);
|
||||
|
||||
void render_rgba_linear_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
@ -28,6 +44,15 @@ void render_rgba_linear_gradient(
|
||||
doc::color_t c1,
|
||||
const render::DitheringMatrix& matrix);
|
||||
|
||||
void render_rgba_radial_gradient(
|
||||
doc::Image* img,
|
||||
const gfx::Point imgPos,
|
||||
const gfx::Point p0,
|
||||
const gfx::Point p1,
|
||||
doc::color_t c0,
|
||||
doc::color_t c1,
|
||||
const render::DitheringMatrix& matrix);
|
||||
|
||||
} // namespace render
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user