mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-16 04:13:50 +00:00
Add options to change the downsampling algorithm (fix #3183)
Also we've restored the default algorithm to bilinear + mipmapping, which was the default on the Aseprite before we switched to Skia m96. This was requested by some users.
This commit is contained in:
parent
2d3de1728c
commit
5ccf414183
@ -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="timeline">
|
||||
<option id="keep_selection" type="bool" default="false" />
|
||||
|
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit f473cc6f8c68629ef0e8c480afbba5659ef5de75
|
||||
Subproject commit 7c819791e50116b54e0666dbf96822e0993d1d1f
|
@ -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.
|
||||
@ -165,6 +165,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)
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "doc/selected_objects.h"
|
||||
#include "doc/slice.h"
|
||||
#include "obs/connection.h"
|
||||
#include "os/sampling.h"
|
||||
#include "os/surface.h"
|
||||
#include "os/system.h"
|
||||
#include "render/dithering.h"
|
||||
@ -133,6 +134,49 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class ContextBar::SamplingOptions : public HBox {
|
||||
public:
|
||||
class Item : public ListItem {
|
||||
public:
|
||||
Item(const char* label,
|
||||
const gen::Downsampling sampling)
|
||||
: ListItem(label)
|
||||
, m_sampling(sampling) {
|
||||
}
|
||||
const gen::Downsampling& sampling() const { return m_sampling; }
|
||||
private:
|
||||
gen::Downsampling m_sampling;
|
||||
};
|
||||
|
||||
SamplingOptions()
|
||||
: m_downsamplingLabel("Downsampling:")
|
||||
{
|
||||
addChild(&m_downsamplingLabel);
|
||||
addChild(&m_downsampling);
|
||||
|
||||
m_downsampling.addItem(new Item("Nearest", gen::Downsampling::NEAREST));
|
||||
m_downsampling.addItem(new Item("Bilinear", gen::Downsampling::BILINEAR));
|
||||
m_downsampling.addItem(new Item("Bilinear mipmapping", gen::Downsampling::BILINEAR_MIPMAP));
|
||||
m_downsampling.addItem(new Item("Trilinear mipmapping", gen::Downsampling::TRILINEAR_MIPMAP));
|
||||
m_downsampling.setSelectedItemIndex(
|
||||
(int)Preferences::instance().editor.downsampling());
|
||||
|
||||
m_downsampling.Change.connect([this]{ onDownsamplingChange(); });
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void onDownsamplingChange() {
|
||||
if (auto item = dynamic_cast<Item*>(m_downsampling.getSelectedItem())) {
|
||||
Preferences::instance().editor.downsampling(
|
||||
item->sampling());
|
||||
}
|
||||
}
|
||||
|
||||
Label m_downsamplingLabel;
|
||||
ComboBox m_downsampling;
|
||||
};
|
||||
|
||||
class ContextBar::BrushBackField : public ButtonSet {
|
||||
public:
|
||||
BrushBackField()
|
||||
@ -1537,6 +1581,7 @@ ContextBar::ContextBar(TooltipManager* tooltipManager,
|
||||
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
|
||||
|
||||
addChild(m_zoomButtons = new ZoomButtons);
|
||||
addChild(m_samplingOptions = new SamplingOptions);
|
||||
|
||||
addChild(m_brushBack = new BrushBackField);
|
||||
addChild(m_brushType = new BrushTypeField(this));
|
||||
@ -1843,13 +1888,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() ||
|
||||
@ -1902,7 +1940,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);
|
||||
@ -1940,7 +1978,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()
|
||||
@ -1980,6 +2022,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_samplingOptions->hasFlags(HIDDEN)) {
|
||||
m_samplingOptions->setVisible(newVisibility);
|
||||
layout();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ContextBar::updateAutoSelectLayer(bool state)
|
||||
{
|
||||
m_autoSelectLayer->setSelected(state);
|
||||
@ -2285,4 +2347,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
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 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
|
||||
@ -67,6 +67,7 @@ namespace app {
|
||||
void updateForMovingPixels();
|
||||
void updateForSelectingBox(const std::string& text);
|
||||
void updateToolLoopModifiersIndicators(tools::ToolLoopModifiers modifiers);
|
||||
bool updateSamplingVisibility(tools::Tool* tool = nullptr);
|
||||
void updateAutoSelectLayer(bool state);
|
||||
bool isAutoSelectLayer() const;
|
||||
|
||||
@ -129,8 +130,10 @@ namespace app {
|
||||
void registerCommands();
|
||||
void showBrushes();
|
||||
void showDynamics();
|
||||
bool needZoomButtons(tools::Tool* tool) const;
|
||||
|
||||
class ZoomButtons;
|
||||
class SamplingOptions;
|
||||
class BrushBackField;
|
||||
class BrushTypeField;
|
||||
class BrushAngleField;
|
||||
@ -158,6 +161,7 @@ namespace app {
|
||||
class SliceFields;
|
||||
|
||||
ZoomButtons* m_zoomButtons;
|
||||
SamplingOptions* m_samplingOptions;
|
||||
BrushBackField* m_brushBack;
|
||||
BrushTypeField* m_brushType;
|
||||
BrushAngleField* m_brushAngle;
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "doc/mask_boundaries.h"
|
||||
#include "doc/slice.h"
|
||||
#include "os/color_space.h"
|
||||
#include "os/sampling.h"
|
||||
#include "os/surface.h"
|
||||
#include "os/system.h"
|
||||
#include "os/window.h"
|
||||
@ -174,6 +175,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(); });
|
||||
@ -239,6 +244,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()
|
||||
{
|
||||
@ -410,6 +427,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"
|
||||
@ -582,13 +602,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)
|
||||
@ -614,11 +629,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(
|
||||
@ -710,7 +725,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);
|
||||
@ -2112,6 +2150,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();
|
||||
|
@ -108,6 +108,7 @@ namespace app {
|
||||
static void destroyEditorSharedInternals();
|
||||
|
||||
bool isActive() const;
|
||||
bool isUsingNewRenderEngine() const;
|
||||
|
||||
DocView* getDocView() { return m_docView; }
|
||||
void setDocView(DocView* docView) { m_docView = docView; }
|
||||
@ -311,6 +312,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();
|
||||
@ -405,6 +407,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;
|
||||
|
@ -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"
|
||||
@ -260,6 +261,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()));
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2019-2020 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 {
|
||||
@ -89,6 +90,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,
|
||||
|
Loading…
Reference in New Issue
Block a user