Merge branch 'main' into beta

This commit is contained in:
David Capello 2022-02-24 16:42:05 -03:00
commit 04fa9a47ab
14 changed files with 256 additions and 27 deletions

View File

@ -127,6 +127,12 @@
<value id="YES" value="1" />
<value id="NO" value="2" />
</enum>
<enum id="Downsampling">
<value id="NEAREST" value="0" />
<value id="BILINEAR" value="1" />
<value id="BILINEAR_MIPMAP" value="2" />
<value id="TRILINEAR_MIPMAP" value="3" />
</enum>
</types>
<global>
@ -176,6 +182,7 @@
some performance issue rendering huge sprites with small
zoom levels -->
<option id="auto_fit" type="bool" default="false" />
<option id="downsampling" type="Downsampling" default="Downsampling::BILINEAR_MIPMAP" />
</section>
<section id="cels">
<option id="user_data_visibility" type="bool" default="false" />

View File

@ -1,5 +1,5 @@
<!-- Aseprite -->
<!-- Copyright (C) 2018-2021 Igara Studio S.A. -->
<!-- Copyright (C) 2018-2022 Igara Studio S.A. -->
<!-- Copyright (C) 2001-2018 David Capello -->
<gui>
<window id="options" text="@.title">
@ -230,6 +230,7 @@
pref="editor.auto_fit" />
<check text="@.straight_line_preview" id="straight_line_preview" tooltip="@.straight_line_preview_tooltip" />
<check text="@.discard_brush" id="discard_brush" />
<hbox id="sampling_placeholder" />
<hbox>
<label text="@.right_click" />
<combobox id="right_click_behavior" expansive="true" />
@ -500,7 +501,8 @@
<check id="multiple_windows" text="@.multiple_windows"
pref="experimental.multiple_windows" />
<hbox>
<check text="@.new_render_engine"
<check id="new_render_engine"
text="@.new_render_engine"
pref="experimental.new_render_engine" />
<link text="(#1671)" url="https://github.com/aseprite/aseprite/issues/1671" />
</hbox>

2
laf

@ -1 +1 @@
Subproject commit f473cc6f8c68629ef0e8c480afbba5659ef5de75
Subproject commit 7c819791e50116b54e0666dbf96822e0993d1d1f

View File

@ -389,6 +389,7 @@ if(ENABLE_UI)
ui/recent_listbox.cpp
ui/resources_listbox.cpp
ui/rgbmap_algorithm_selector.cpp
ui/sampling_selector.cpp
ui/search_entry.cpp
ui/select_accelerator.cpp
ui/selection_mode_field.cpp

View File

@ -30,6 +30,7 @@
#include "app/ui/main_window.h"
#include "app/ui/pref_widget.h"
#include "app/ui/rgbmap_algorithm_selector.h"
#include "app/ui/sampling_selector.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "base/clamp.h"
@ -502,8 +503,18 @@ public:
showHome()->setSelected(m_pref.general.showHome());
// Right-click
// Editor sampling
samplingPlaceholder()->addChild(
m_samplingSelector = new SamplingSelector(
SamplingSelector::Behavior::ChangeOnSave));
m_samplingSelector->setEnabled(newRenderEngine()->isSelected());
newRenderEngine()->Click.connect(
[this]{
m_samplingSelector->setEnabled(newRenderEngine()->isSelected());
});
// Right-click
static_assert(int(app::gen::RightClickMode::PAINT_BGCOLOR) == 0, "");
static_assert(int(app::gen::RightClickMode::PICK_FGCOLOR) == 1, "");
static_assert(int(app::gen::RightClickMode::ERASE) == 2, "");
@ -671,6 +682,8 @@ public:
m_pref.editor.straightLinePreview(straightLinePreview()->isSelected());
m_pref.eyedropper.discardBrush(discardBrush()->isSelected());
m_pref.editor.rightClickMode(static_cast<app::gen::RightClickMode>(rightClickBehavior()->getSelectedItemIndex()));
if (m_samplingSelector)
m_samplingSelector->save();
m_pref.cursor.paintingCursorType(static_cast<app::gen::PaintingCursorType>(paintingCursorType()->getSelectedItemIndex()));
m_pref.cursor.cursorColor(cursorColor()->getColor());
m_pref.cursor.brushPreview(static_cast<app::gen::BrushPreview>(brushPreview()->getSelectedItemIndex()));
@ -1731,6 +1744,7 @@ private:
std::string m_templateTextForDisplayCS;
RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector;
ButtonSet* m_themeVars = nullptr;
SamplingSelector* m_samplingSelector = nullptr;
};
class OptionsCommand : public Command {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -178,6 +178,7 @@ FOR_ENUM(app::gen::BgType)
FOR_ENUM(app::gen::BrushPreview)
FOR_ENUM(app::gen::BrushType)
FOR_ENUM(app::gen::ColorProfileBehavior)
FOR_ENUM(app::gen::Downsampling)
FOR_ENUM(app::gen::EyedropperChannel)
FOR_ENUM(app::gen::EyedropperSample)
FOR_ENUM(app::gen::FillReferTo)

View File

@ -44,6 +44,7 @@
#include "app/ui/expr_entry.h"
#include "app/ui/icon_button.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/sampling_selector.h"
#include "app/ui/selection_mode_field.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
@ -59,6 +60,7 @@
#include "doc/slice.h"
#include "fmt/format.h"
#include "obs/connection.h"
#include "os/sampling.h"
#include "os/surface.h"
#include "os/system.h"
#include "render/dithering.h"
@ -1721,6 +1723,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager,
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
addChild(m_zoomButtons = new ZoomButtons);
addChild(m_samplingSelector = new SamplingSelector);
addChild(m_brushBack = new BrushBackField);
addChild(m_brushType = new BrushTypeField(this));
@ -2032,13 +2035,6 @@ void ContextBar::updateForTool(tools::Tool* tool)
(activeBrush()->type() == kSquareBrushType ||
activeBrush()->type() == kLineBrushType);
// True if the current tool is eyedropper.
const bool needZoomButtons = tool &&
(tool->getInk(0)->isZoom() ||
tool->getInk(1)->isZoom() ||
tool->getInk(0)->isScrollMovement() ||
tool->getInk(1)->isScrollMovement());
// True if the current tool is eyedropper.
const bool isEyedropper = tool &&
(tool->getInk(0)->isEyedropper() ||
@ -2091,7 +2087,7 @@ void ContextBar::updateForTool(tools::Tool* tool)
const bool supportDynamics = (!hasImageBrush);
// Show/Hide fields
m_zoomButtons->setVisible(needZoomButtons);
m_zoomButtons->setVisible(needZoomButtons(tool));
m_brushBack->setVisible(supportOpacity && hasImageBrush && !withDithering);
m_brushType->setVisible(supportOpacity && (!isFloodfill || (isFloodfill && hasImageBrush && !withDithering)));
m_brushSize->setVisible(supportOpacity && !isFloodfill && !hasImageBrush);
@ -2133,7 +2129,11 @@ void ContextBar::updateForTool(tools::Tool* tool)
if (updateShade)
m_inkShades->updateShadeFromColorBarPicks();
layout();
if (!updateSamplingVisibility(tool)) {
// updateSamplingVisibility() returns false if it doesn't layout()
// the ContextBar.
layout();
}
}
void ContextBar::updateForMovingPixels(const Transformation& t)
@ -2175,6 +2175,26 @@ void ContextBar::updateToolLoopModifiersIndicators(tools::ToolLoopModifiers modi
m_selectionMode->setSelectionMode(mode);
}
bool ContextBar::updateSamplingVisibility(tools::Tool* tool)
{
if (!tool)
tool = App::instance()->activeTool();
const bool newVisibility =
needZoomButtons(tool) &&
current_editor &&
(current_editor->projection().scaleX() < 1.0 ||
current_editor->projection().scaleY() < 1.0) &&
current_editor->isUsingNewRenderEngine();
if (newVisibility == m_samplingSelector->hasFlags(HIDDEN)) {
m_samplingSelector->setVisible(newVisibility);
layout();
return true;
}
return false;
}
void ContextBar::updateAutoSelectLayer(bool state)
{
m_autoSelectLayer->setSelected(state);
@ -2480,4 +2500,13 @@ void ContextBar::showDynamics()
m_dynamics->switchPopup();
}
bool ContextBar::needZoomButtons(tools::Tool* tool) const
{
return tool &&
(tool->getInk(0)->isZoom() ||
tool->getInk(1)->isZoom() ||
tool->getInk(0)->isScrollMovement() ||
tool->getInk(1)->isScrollMovement());
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -53,6 +53,7 @@ namespace app {
class ColorBar;
class DitheringSelector;
class GradientTypeSelector;
class SamplingSelector;
class Transformation;
class ContextBar : public DocObserverWidget<ui::HBox>
@ -68,6 +69,7 @@ namespace app {
void updateForMovingPixels(const Transformation& t);
void updateForSelectingBox(const std::string& text);
void updateToolLoopModifiersIndicators(tools::ToolLoopModifiers modifiers);
bool updateSamplingVisibility(tools::Tool* tool = nullptr);
void updateAutoSelectLayer(bool state);
bool isAutoSelectLayer() const;
@ -131,6 +133,7 @@ namespace app {
void registerCommands();
void showBrushes();
void showDynamics();
bool needZoomButtons(tools::Tool* tool) const;
class ZoomButtons;
class BrushBackField;
@ -161,6 +164,7 @@ namespace app {
class SliceFields;
ZoomButtons* m_zoomButtons;
SamplingSelector* m_samplingSelector;
BrushBackField* m_brushBack;
BrushTypeField* m_brushType;
BrushAngleField* m_brushAngle;

View File

@ -61,6 +61,7 @@
#include "doc/slice.h"
#include "fmt/format.h"
#include "os/color_space.h"
#include "os/sampling.h"
#include "os/surface.h"
#include "os/system.h"
#include "render/rasterize.h"
@ -180,6 +181,10 @@ Editor::Editor(Doc* document, EditorFlags flags)
Preferences::instance().colorBar.fgColor.AfterChange.connect(
[this]{ onFgColorChange(); });
m_samplingChangeConn =
Preferences::instance().editor.downsampling.AfterChange.connect(
[this]{ onSamplingChange(); });
m_contextBarBrushChangeConn =
App::instance()->contextBar()->BrushChange.connect(
[this]{ onContextBarBrushChange(); });
@ -245,6 +250,18 @@ bool Editor::isActive() const
return (current_editor == this);
}
bool Editor::isUsingNewRenderEngine() const
{
ASSERT(m_sprite);
return
(Preferences::instance().experimental.newRenderEngine()
// Reference layers + zoom > 100% need the old render engine for
// sub-pixel rendering.
&& (!m_sprite->hasVisibleReferenceLayers()
|| (m_proj.scaleX() <= 1.0
&& m_proj.scaleY() <= 1.0)));
}
// static
WidgetType Editor::Type()
{
@ -454,6 +471,9 @@ void Editor::setZoom(const render::Zoom& zoom)
if (m_proj.zoom() != zoom) {
m_proj.setZoom(zoom);
notifyZoomChanged();
if (isActive())
App::instance()->contextBar()->updateSamplingVisibility();
}
else {
// Just copy the zoom as the internal "Zoom::m_internalScale"
@ -626,13 +646,8 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
return;
// rc2 is the rectangle used to create a temporal rendered image of the sprite
const bool newEngine =
(Preferences::instance().experimental.newRenderEngine()
// Reference layers + zoom > 100% need the old render engine for
// sub-pixel rendering.
&& (!m_sprite->hasVisibleReferenceLayers()
|| (m_proj.scaleX() <= 1.0
&& m_proj.scaleY() <= 1.0)));
const auto& pref = Preferences::instance();
const bool newEngine = isUsingNewRenderEngine();
gfx::Rect rc2;
if (newEngine) {
rc2 = expose; // New engine, exposed rectangle (without zoom)
@ -658,11 +673,11 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
rendered.reset(Image::create(IMAGE_RGB, rc2.w, rc2.h,
m_renderEngine->getRenderImageBuffer()));
m_renderEngine->setNewBlendMethod(Preferences::instance().experimental.newBlend());
m_renderEngine->setNewBlendMethod(pref.experimental.newBlend());
m_renderEngine->setRefLayersVisiblity(true);
m_renderEngine->setSelectedLayer(m_layer);
if (m_flags & Editor::kUseNonactiveLayersOpacityWhenEnabled)
m_renderEngine->setNonactiveLayersOpacity(Preferences::instance().experimental.nonactiveLayersOpacity());
m_renderEngine->setNonactiveLayersOpacity(pref.experimental.nonactiveLayersOpacity());
else
m_renderEngine->setNonactiveLayersOpacity(255);
m_renderEngine->setProjection(
@ -755,7 +770,30 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
tmp.get(), 0, 0, 0, 0, rc2.w, rc2.h);
if (newEngine) {
g->drawSurface(tmp.get(), gfx::Rect(0, 0, rc2.w, rc2.h), dest);
os::Sampling sampling;
if (m_proj.scaleX() < 1.0) {
switch (pref.editor.downsampling()) {
case gen::Downsampling::NEAREST:
sampling = os::Sampling(os::Sampling::Filter::Nearest);
break;
case gen::Downsampling::BILINEAR:
sampling = os::Sampling(os::Sampling::Filter::Linear);
break;
case gen::Downsampling::BILINEAR_MIPMAP:
sampling = os::Sampling(os::Sampling::Filter::Linear,
os::Sampling::Mipmap::Nearest);
break;
case gen::Downsampling::TRILINEAR_MIPMAP:
sampling = os::Sampling(os::Sampling::Filter::Linear,
os::Sampling::Mipmap::Linear);
break;
}
}
g->drawSurface(tmp.get(),
gfx::Rect(0, 0, rc2.w, rc2.h),
dest,
sampling);
}
else {
g->blit(tmp.get(), 0, 0, dest.x, dest.y, dest.w, dest.h);
@ -2217,6 +2255,15 @@ void Editor::onActiveToolChange(tools::Tool* tool)
}
}
void Editor::onSamplingChange()
{
if (m_proj.scaleX() < 1.0 &&
m_proj.scaleY() < 1.0 &&
isUsingNewRenderEngine()) {
invalidate();
}
}
void Editor::onFgColorChange()
{
m_brushPreview.redraw();

View File

@ -113,6 +113,7 @@ namespace app {
static void destroyEditorSharedInternals();
bool isActive() const;
bool isUsingNewRenderEngine() const;
DocView* getDocView() { return m_docView; }
void setDocView(DocView* docView) { m_docView = docView; }
@ -324,6 +325,7 @@ namespace app {
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
void onInvalidateRegion(const gfx::Region& region) override;
void onSamplingChange();
void onFgColorChange();
void onContextBarBrushChange();
void onTiledModeBeforeChange();
@ -422,6 +424,7 @@ namespace app {
ui::Timer m_antsTimer;
int m_antsOffset;
obs::scoped_connection m_samplingChangeConn;
obs::scoped_connection m_fgColorChangeConn;
obs::scoped_connection m_contextBarBrushChangeConn;
obs::scoped_connection m_showExtrasConn;

View File

@ -0,0 +1,54 @@
// Aseprite
// Copyright (C) 2022 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/sampling_selector.h"
#include "ui/listitem.h"
namespace app {
using namespace ui;
SamplingSelector::SamplingSelector(Behavior behavior)
: m_behavior(behavior)
, m_downsamplingLabel("Downsampling:")
{
addChild(&m_downsamplingLabel);
addChild(&m_downsampling);
m_downsampling.addItem(new ListItem("Nearest"));
m_downsampling.addItem(new ListItem("Bilinear"));
m_downsampling.addItem(new ListItem("Bilinear mipmapping"));
m_downsampling.addItem(new ListItem("Trilinear mipmapping"));
m_downsampling.setSelectedItemIndex(
(int)Preferences::instance().editor.downsampling());
if (m_behavior == Behavior::ChangeOnRealTime)
m_downsampling.Change.connect([this]{ save(); });
m_samplingChangeConn =
Preferences::instance().editor.downsampling.AfterChange.connect(
[this]{ onPreferenceChange(); });
}
void SamplingSelector::save()
{
const int i = m_downsampling.getSelectedItemIndex();
Preferences::instance().editor.downsampling((gen::Downsampling)i);
}
void SamplingSelector::onPreferenceChange()
{
const int i = (int)Preferences::instance().editor.downsampling();
if (m_downsampling.getSelectedItemIndex() != i)
m_downsampling.setSelectedItemIndex(i);
}
} // namespace app

View File

@ -0,0 +1,41 @@
// Aseprite
// Copyright (C) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UI_FILE_SELECTOR_H_INCLUDED
#define APP_UI_FILE_SELECTOR_H_INCLUDED
#pragma once
#include "app/pref/preferences.h"
#include "obs/connection.h"
#include "ui/box.h"
#include "ui/combobox.h"
#include "ui/label.h"
namespace app {
class SamplingSelector : public ui::HBox {
public:
enum class Behavior {
ChangeOnRealTime,
ChangeOnSave
};
SamplingSelector(Behavior behavior = Behavior::ChangeOnRealTime);
void save();
private:
void onPreferenceChange();
Behavior m_behavior;
ui::Label m_downsamplingLabel;
ui::ComboBox m_downsampling;
obs::scoped_connection m_samplingChangeConn;
};
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -21,6 +21,7 @@
#include "gfx/size.h"
#include "os/draw_text.h"
#include "os/font.h"
#include "os/sampling.h"
#include "os/surface.h"
#include "os/system.h"
#include "os/window.h"
@ -271,6 +272,25 @@ void Graphics::drawSurface(os::Surface* surface,
gfx::Rect(dstRect).offset(m_dx, m_dy));
}
void Graphics::drawSurface(os::Surface* surface,
const gfx::Rect& srcRect,
const gfx::Rect& dstRect,
const os::Sampling& sampling,
const ui::Paint* paint)
{
dirty(gfx::Rect(m_dx+dstRect.x, m_dy+dstRect.y,
dstRect.w, dstRect.h));
os::SurfaceLock lockSrc(surface);
os::SurfaceLock lockDst(m_surface.get());
m_surface->drawSurface(
surface,
srcRect,
gfx::Rect(dstRect).offset(m_dx, m_dy),
sampling,
paint);
}
void Graphics::drawRgbaSurface(os::Surface* surface, int x, int y)
{
dirty(gfx::Rect(m_dx+x, m_dy+y, surface->width(), surface->height()));

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -30,6 +30,7 @@ namespace gfx {
namespace os {
class DrawTextDelegate;
class Sampling;
}
namespace ui {
@ -92,6 +93,11 @@ namespace ui {
void drawSurface(os::Surface* surface,
const gfx::Rect& srcRect,
const gfx::Rect& dstRect);
void drawSurface(os::Surface* surface,
const gfx::Rect& srcRect,
const gfx::Rect& dstRect,
const os::Sampling& sampling,
const ui::Paint* paint = nullptr);
void drawRgbaSurface(os::Surface* surface, int x, int y);
void drawRgbaSurface(os::Surface* surface, int srcx, int srcy, int dstx, int dsty, int w, int h);
void drawRgbaSurface(os::Surface* surface,