Improve cmd::CopyRegion() performance

Here we replace std::stringstream with a base::buffer (std::vector<uint8_t>)
and pre-allocating the required size.
This commit is contained in:
David Capello 2019-02-20 18:47:38 -03:00
parent 6a88713213
commit 7af4365588
5 changed files with 105 additions and 41 deletions

View File

@ -575,6 +575,7 @@ add_library(app-lib
transformation.cpp
ui/layer_frame_comboboxes.cpp
util/autocrop.cpp
util/buffer_region.cpp
util/create_cel_copy.cpp
util/expand_cel_canvas.cpp
util/filetoks.cpp

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -10,10 +11,9 @@
#include "app/cmd/copy_region.h"
#include "app/util/buffer_region.h"
#include "doc/image.h"
#include <algorithm>
namespace app {
namespace cmd {
@ -22,7 +22,6 @@ CopyRegion::CopyRegion(Image* dst, const Image* src,
const gfx::Point& dstPos,
bool alreadyCopied)
: WithImage(dst)
, m_size(0)
, m_alreadyCopied(alreadyCopied)
{
// Create region to save/swap later
@ -38,16 +37,7 @@ CopyRegion::CopyRegion(Image* dst, const Image* src,
m_region.createUnion(m_region, gfx::Region(clip.dstBounds()));
}
// Save region pixels
for (const auto& rc : m_region) {
for (int y=0; y<rc.h; ++y) {
m_stream.write(
(const char*)src->getPixelAddress(rc.x-dstPos.x,
rc.y-dstPos.y+y),
src->getRowStrideSize(rc.w));
}
}
m_size = size_t(m_stream.tellp());
save_image_region_in_buffer(m_region, src, dstPos, m_buffer);
}
void CopyRegion::onExecute()
@ -69,29 +59,7 @@ void CopyRegion::onRedo()
void CopyRegion::swap()
{
Image* image = this->image();
// Save current image region in "tmp" stream
std::stringstream tmp;
for (const auto& rc : m_region)
for (int y=0; y<rc.h; ++y)
tmp.write(
(const char*)image->getPixelAddress(rc.x, rc.y+y),
image->getRowStrideSize(rc.w));
// Restore m_stream into the image
m_stream.seekg(0, std::ios_base::beg);
for (const auto& rc : m_region) {
for (int y=0; y<rc.h; ++y) {
m_stream.read(
(char*)image->getPixelAddress(rc.x, rc.y+y),
image->getRowStrideSize(rc.w));
}
}
// TODO use m_stream.swap(tmp) when clang and gcc support it
m_stream.str(tmp.str());
m_stream.clear();
swap_image_region_with_buffer(m_region, image, m_buffer);
image->incrementVersion();
}

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -10,11 +11,10 @@
#include "app/cmd.h"
#include "app/cmd/with_image.h"
#include "base/buffer.h"
#include "gfx/point.h"
#include "gfx/region.h"
#include <sstream>
namespace app {
namespace cmd {
using namespace doc;
@ -36,16 +36,15 @@ namespace cmd {
void onUndo() override;
void onRedo() override;
size_t onMemSize() const override {
return sizeof(*this) + m_size;
return sizeof(*this) + m_buffer.size();
}
private:
void swap();
size_t m_size;
bool m_alreadyCopied;
gfx::Region m_region;
std::stringstream m_stream;
base::buffer m_buffer;
};
} // namespace cmd

View File

@ -0,0 +1,63 @@
// Aseprite
// Copyright (C) 2019 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/util/buffer_region.h"
#include "doc/image.h"
#include "gfx/region.h"
#include <algorithm>
namespace app {
void save_image_region_in_buffer(
const gfx::Region& region,
const doc::Image* image,
const gfx::Point& imagePos,
base::buffer& buffer)
{
// Calculate buffer size for the region
const size_t bytesPerPixel = image->getRowStrideSize(1);
size_t reqBytes = 0;
for (const auto& rc : region)
reqBytes += bytesPerPixel*rc.w*rc.h;
// Save region pixels
buffer.resize(reqBytes);
auto it = buffer.begin();
for (const auto& rc : region) {
for (int y=0; y<rc.h; ++y) {
auto p = (const uint8_t*)image->getPixelAddress(rc.x-imagePos.x,
rc.y-imagePos.y+y);
const size_t rowBytes = bytesPerPixel*rc.w;
std::copy(p, p+rowBytes, it);
it += rowBytes;
}
}
}
void swap_image_region_with_buffer(
const gfx::Region& region,
doc::Image* image,
base::buffer& buffer)
{
const size_t bytesPerPixel = image->getRowStrideSize(1);
auto it = buffer.begin();
for (const auto& rc : region) {
for (int y=0; y<rc.h; ++y) {
auto p = (uint8_t*)image->getPixelAddress(rc.x, rc.y+y);
const size_t rowBytes = bytesPerPixel*rc.w;
std::swap_ranges(it, it+rowBytes, p);
it += rowBytes;
}
}
}
} // namespace app

View File

@ -0,0 +1,33 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_BUFFER_REGION_H_INCLUDED
#define APP_UTIL_BUFFER_REGION_H_INCLUDED
#pragma once
#include "base/buffer.h"
#include "gfx/fwd.h"
namespace doc {
class Image;
}
namespace app {
void save_image_region_in_buffer(
const gfx::Region& region,
const doc::Image* image,
const gfx::Point& imagePos,
base::buffer& buffer);
void swap_image_region_with_buffer(
const gfx::Region& region,
doc::Image* image,
base::buffer& buffer);
} // namespace app
#endif