Add progress bar to color conversion

Unified render tasks stop/progress notifications in render::TaskDelegate
interface.
This commit is contained in:
David Capello 2017-05-17 15:25:40 -03:00
parent 7bc0c4fba5
commit 573cad4777
8 changed files with 83 additions and 35 deletions

View File

@ -6,8 +6,8 @@
<view id="color_mode_view" expansive="true">
<listbox id="color_mode" />
</view>
<slider min="0" max="100" id="progress" minwidth="100" />
<separator horizontal="true" />
<hbox>
<boxfiller />
<hbox homogeneous="true">

View File

@ -26,6 +26,7 @@
#include "render/dithering_algorithm.h"
#include "render/quantization.h"
#include "render/render.h"
#include "render/task_delegate.h"
#include "ui/listitem.h"
#include "ui/paint_event.h"
#include "ui/size_hint_event.h"
@ -75,7 +76,7 @@ private:
render::DitheringAlgorithm m_dithering;
};
class ConvertThread {
class ConvertThread : public render::TaskDelegate {
public:
ConvertThread(const doc::ImageRef& dstImage,
const doc::Sprite* sprite,
@ -85,6 +86,7 @@ public:
: m_image(dstImage)
, m_running(true)
, m_stopFlag(false)
, m_progress(0.0)
, m_thread(
[this,
sprite, frame,
@ -106,6 +108,10 @@ public:
return m_running;
}
double progress() const {
return m_progress;
}
private:
void run(const Sprite* sprite,
const doc::frame_t frame,
@ -128,15 +134,25 @@ private:
sprite->palette(frame),
(sprite->backgroundLayer() != nullptr),
0,
&m_stopFlag);
this);
m_running = false;
}
private:
// render::TaskDelegate impl
bool continueTask() override {
return !m_stopFlag;
}
void notifyTaskProgress(double progress) override {
m_progress = progress;
}
doc::ImageRef m_image;
bool m_running;
bool m_stopFlag;
double m_progress;
base::thread m_thread;
};
@ -176,6 +192,8 @@ public:
colorMode()->Change.connect(base::Bind<void>(&ColorModeWindow::onChangeColorMode, this));
m_timer.Tick.connect(base::Bind<void>(&ColorModeWindow::onMonitorProgress, this));
progress()->setReadOnly(true);
// Select first option
colorMode()->selectIndex(0);
}
@ -230,6 +248,9 @@ private:
m_image->clear(0);
m_editor->invalidate();
progress()->setValue(0);
progress()->setVisible(true);
layout();
m_bgThread.reset(
new ConvertThread(
@ -251,7 +272,12 @@ private:
m_timer.stop();
m_bgThread->stop();
m_bgThread.reset(nullptr);
progress()->setVisible(false);
layout();
}
else
progress()->setValue(100 * m_bgThread->progress());
m_editor->invalidate();
}
@ -374,7 +400,6 @@ void ChangePixelFormatCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Transaction transaction(writer.context(), "Color Mode Change");
Document* document(writer.document());
Sprite* sprite(writer.sprite());
transaction.execute(new cmd::SetPixelFormat(sprite, m_format, m_dithering));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -25,6 +25,7 @@
#include "doc/palette.h"
#include "doc/sprite.h"
#include "render/quantization.h"
#include "render/task_delegate.h"
#include "ui/manager.h"
#include "palette_from_sprite.xml.h"
@ -42,7 +43,7 @@ protected:
};
class ColorQuantizationJob : public Job,
public render::PaletteOptimizerDelegate {
public render::TaskDelegate {
public:
ColorQuantizationJob(Sprite* sprite, bool withAlpha, Palette* palette)
: Job("Creating Palette")
@ -59,11 +60,11 @@ private:
m_withAlpha, m_palette, this);
}
bool onPaletteOptimizerContinue() override {
bool continueTask() override {
return !isCanceled();
}
void onPaletteOptimizerProgress(double progress) override {
void notifyTaskProgress(double progress) override {
jobProgress(progress);
}

View File

@ -756,7 +756,7 @@ private:
}
Palette newPalette(0, 256);
optimizer.calculate(&newPalette, m_bgIndex, nullptr);
optimizer.calculate(&newPalette, m_bgIndex);
m_sprite->setPalette(&newPalette, false);
}
@ -1245,7 +1245,7 @@ private:
}
Palette* palette = new Palette(0, 256);
optimizer.calculate(palette, m_transparentIndex, nullptr);
optimizer.calculate(palette, m_transparentIndex);
return palette;
}

View File

@ -12,6 +12,7 @@
#include "doc/image_impl.h"
#include "doc/palette.h"
#include "doc/rgbmap.h"
#include "render/task_delegate.h"
#include <limits>
@ -273,7 +274,7 @@ namespace render {
int u, int v,
const doc::RgbMap* rgbmap,
const doc::Palette* palette,
bool* stopFlag = nullptr) {
TaskDelegate* delegate = nullptr) {
const doc::LockImageBits<doc::RgbTraits> srcBits(srcImage);
doc::LockImageBits<doc::IndexedTraits> dstBits(dstImage);
auto srcIt = srcBits.begin();
@ -286,8 +287,16 @@ namespace render {
ASSERT(srcIt != srcBits.end());
ASSERT(dstIt != dstBits.end());
*dstIt = dithering.ditherRgbPixelToIndex(matrix, *srcIt, x+u, y+v, rgbmap, palette);
if (stopFlag && *stopFlag)
break;
if (delegate) {
if (!delegate->continueTask())
return;
}
}
if (delegate) {
delegate->notifyTaskProgress(
double(y+1) / double(h));
}
}
}

View File

@ -23,6 +23,7 @@
#include "gfx/rgb.h"
#include "render/ordered_dither.h"
#include "render/render.h"
#include "render/task_delegate.h"
#include <algorithm>
#include <limits>
@ -40,7 +41,7 @@ Palette* create_palette_from_sprite(
const frame_t toFrame,
const bool withAlpha,
Palette* palette,
PaletteOptimizerDelegate* delegate)
TaskDelegate* delegate)
{
PaletteOptimizer optimizer;
@ -58,10 +59,10 @@ Palette* create_palette_from_sprite(
optimizer.feedWithImage(flat_image.get(), withAlpha);
if (delegate) {
if (!delegate->onPaletteOptimizerContinue())
if (!delegate->continueTask())
return nullptr;
delegate->onPaletteOptimizerProgress(
delegate->notifyTaskProgress(
double(frame-fromFrame+1) / double(toFrame-fromFrame+1));
}
}
@ -71,8 +72,7 @@ Palette* create_palette_from_sprite(
palette,
// Transparent color is needed if we have transparent layers
(sprite->backgroundLayer() &&
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()),
delegate);
sprite->allLayersCount() == 1 ? -1: sprite->transparentColor()));
return palette;
}
@ -86,7 +86,7 @@ Image* convert_pixel_format(
const Palette* palette,
bool is_background,
color_t new_mask_color,
bool* stopFlag)
TaskDelegate* delegate)
{
if (!new_image)
new_image = Image::create(pixelFormat, image->width(), image->height());
@ -100,12 +100,12 @@ Image* convert_pixel_format(
switch (ditheringAlgorithm) {
case DitheringAlgorithm::OldOrdered: {
OrderedDither dither;
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, stopFlag);
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, delegate);
break;
}
case DitheringAlgorithm::Ordered: {
OrderedDither2 dither;
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, stopFlag);
dither_rgb_image_to_indexed(dither, matrix, image, new_image, 0, 0, rgbmap, palette, delegate);
break;
}
}
@ -388,8 +388,7 @@ void PaletteOptimizer::feedWithRgbaColor(color_t color)
m_histogram.addSamples(color, 1);
}
void PaletteOptimizer::calculate(Palette* palette, int maskIndex,
PaletteOptimizerDelegate* delegate)
void PaletteOptimizer::calculate(Palette* palette, int maskIndex)
{
bool addMask;

View File

@ -23,21 +23,13 @@ namespace doc {
}
namespace render {
class PaletteOptimizerDelegate {
public:
virtual ~PaletteOptimizerDelegate() { }
virtual void onPaletteOptimizerProgress(double progress) = 0;
virtual bool onPaletteOptimizerContinue() = 0;
};
class TaskDelegate;
class PaletteOptimizer {
public:
void feedWithImage(doc::Image* image, bool withAlpha);
void feedWithRgbaColor(doc::color_t color);
void calculate(doc::Palette* palette,
int maskIndex,
render::PaletteOptimizerDelegate* delegate);
void calculate(doc::Palette* palette, int maskIndex);
private:
render::ColorHistogram<5, 6, 5, 5> m_histogram;
@ -50,7 +42,7 @@ namespace render {
const doc::frame_t toFrame,
const bool withAlpha,
doc::Palette* newPalette, // Can be NULL to create a new palette
render::PaletteOptimizerDelegate* delegate);
TaskDelegate* delegate);
// Changes the image pixel format. The dithering method is used only
// when you want to convert from RGB to Indexed.
@ -63,7 +55,7 @@ namespace render {
const doc::Palette* palette,
bool is_background,
doc::color_t new_mask_color,
bool* stopFlag = nullptr);
TaskDelegate* delegate = nullptr);
} // namespace render

View File

@ -0,0 +1,22 @@
// Aseprite Render Library
// Copyright (c) 2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef RENDER_TASK_DELEGATE_H_INCLUDED
#define RENDER_TASK_DELEGATE_H_INCLUDED
#pragma once
namespace render {
class TaskDelegate {
public:
virtual ~TaskDelegate() { }
virtual void notifyTaskProgress(double progress) = 0;
virtual bool continueTask() = 0;
};
} // namespace render
#endif // RENDER_TASK_H_INCLUDED