mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 21:39:57 +00:00
Use threads in shrink_bounds() when it's possible
Improves the performance when we edit big images (shrink + crop + image allocations are the performance issues we have when we're editing big images). The real solution for image allocations would be to change the internal representation of images to a tile-based images with a cache of tiles. But that is not planned in the short-term.
This commit is contained in:
parent
0812ea8224
commit
9c81ed46f2
@ -215,5 +215,6 @@ endif()
|
||||
if(ENABLE_BENCHMARKS)
|
||||
include(FindBenchmarks)
|
||||
find_benchmarks(doc doc-lib)
|
||||
find_benchmarks(doc/algorithm doc-lib)
|
||||
find_benchmarks(render render-lib)
|
||||
endif()
|
||||
|
59
src/doc/algorithm/shrink_benchmark.cpp
Normal file
59
src/doc/algorithm/shrink_benchmark.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
|
||||
#include "doc/color.h"
|
||||
#include "doc/image.h"
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <memory>
|
||||
|
||||
using namespace doc;
|
||||
|
||||
void BM_ShrinkBounds(benchmark::State& state) {
|
||||
const PixelFormat pixelFormat = (PixelFormat)state.range(0);
|
||||
const int w = state.range(1);
|
||||
const int h = state.range(2);
|
||||
|
||||
std::unique_ptr<Image> img(Image::create(pixelFormat, w, h));
|
||||
img->putPixel(w/2, h/2, rgba(1, 2, 3, 4));
|
||||
gfx::Rect rc;
|
||||
while (state.KeepRunning()) {
|
||||
doc::algorithm::shrink_bounds(img.get(), rc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFARGS(MODE) \
|
||||
->Args({ MODE, 100, 100 }) \
|
||||
->Args({ MODE, 200, 200 }) \
|
||||
->Args({ MODE, 300, 300 }) \
|
||||
->Args({ MODE, 400, 400 }) \
|
||||
->Args({ MODE, 499, 500 }) \
|
||||
->Args({ MODE, 500, 500 }) \
|
||||
->Args({ MODE, 600, 600 }) \
|
||||
->Args({ MODE, 700, 700 }) \
|
||||
->Args({ MODE, 799, 800 }) \
|
||||
->Args({ MODE, 800, 800 }) \
|
||||
->Args({ MODE, 900, 900 }) \
|
||||
->Args({ MODE, 1000, 1000 }) \
|
||||
->Args({ MODE, 1500, 1500 }) \
|
||||
->Args({ MODE, 2000, 2000 }) \
|
||||
->Args({ MODE, 4000, 4000 }) \
|
||||
->Args({ MODE, 8000, 8000 })
|
||||
|
||||
BENCHMARK(BM_ShrinkBounds)
|
||||
DEFARGS(IMAGE_RGB)
|
||||
DEFARGS(IMAGE_GRAYSCALE)
|
||||
DEFARGS(IMAGE_INDEXED)
|
||||
->Unit(benchmark::kMicrosecond)
|
||||
->UseRealTime();
|
||||
|
||||
BENCHMARK_MAIN();
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -14,6 +15,8 @@
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/primitives_fast.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace doc {
|
||||
namespace algorithm {
|
||||
|
||||
@ -44,81 +47,109 @@ bool is_same_pixel<IndexedTraits>(color_t pixel1, color_t pixel2)
|
||||
return pixel1 == pixel2;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool is_same_pixel<BitmapTraits>(color_t pixel1, color_t pixel2)
|
||||
template<typename ImageTraits>
|
||||
bool shrink_bounds_left_templ(const Image* image, gfx::Rect& bounds, color_t refpixel, int rowSize)
|
||||
{
|
||||
return pixel1 == pixel2;
|
||||
int u, v;
|
||||
// Shrink left side
|
||||
for (u=bounds.x; u<bounds.x2(); ++u) {
|
||||
auto ptr = get_pixel_address_fast<ImageTraits>(image, u, v=bounds.y);
|
||||
for (; v<bounds.y2(); ++v, ptr+=rowSize) {
|
||||
ASSERT(ptr == get_pixel_address_fast<ImageTraits>(image, u, v));
|
||||
if (!is_same_pixel<ImageTraits>(*ptr, refpixel))
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
++bounds.x;
|
||||
--bounds.w;
|
||||
}
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
bool shrink_bounds_right_templ(const Image* image, gfx::Rect& bounds, color_t refpixel, int rowSize)
|
||||
{
|
||||
int u, v;
|
||||
// Shrink right side
|
||||
for (u=bounds.x2()-1; u>=bounds.x; --u) {
|
||||
auto ptr = get_pixel_address_fast<ImageTraits>(image, u, v=bounds.y);
|
||||
for (; v<bounds.y2(); ++v, ptr+=rowSize) {
|
||||
ASSERT(ptr == get_pixel_address_fast<ImageTraits>(image, u, v));
|
||||
if (!is_same_pixel<ImageTraits>(*ptr, refpixel))
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
--bounds.w;
|
||||
}
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
bool shrink_bounds_top_templ(const Image* image, gfx::Rect& bounds, color_t refpixel)
|
||||
{
|
||||
int u, v;
|
||||
// Shrink top side
|
||||
for (v=bounds.y; v<bounds.y2(); ++v) {
|
||||
auto ptr = get_pixel_address_fast<ImageTraits>(image, u=bounds.x, v);
|
||||
for (; u<bounds.x2(); ++u, ++ptr) {
|
||||
ASSERT(ptr == get_pixel_address_fast<ImageTraits>(image, u, v));
|
||||
if (!is_same_pixel<ImageTraits>(*ptr, refpixel))
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
++bounds.y;
|
||||
--bounds.h;
|
||||
}
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
bool shrink_bounds_bottom_templ(const Image* image, gfx::Rect& bounds, color_t refpixel)
|
||||
{
|
||||
int u, v;
|
||||
// Shrink bottom side
|
||||
for (v=bounds.y2()-1; v>=bounds.y; --v) {
|
||||
auto ptr = get_pixel_address_fast<ImageTraits>(image, u=bounds.x, v);
|
||||
for (; u<bounds.x2(); ++u, ++ptr) {
|
||||
ASSERT(ptr == get_pixel_address_fast<ImageTraits>(image, u, v));
|
||||
if (!is_same_pixel<ImageTraits>(*ptr, refpixel))
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
--bounds.h;
|
||||
}
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
bool shrink_bounds_templ(const Image* image, gfx::Rect& bounds, color_t refpixel)
|
||||
{
|
||||
bool shrink;
|
||||
int u, v;
|
||||
|
||||
// Shrink left side
|
||||
for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
|
||||
shrink = true;
|
||||
for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
|
||||
if (!is_same_pixel<ImageTraits>(
|
||||
get_pixel_fast<ImageTraits>(image, u, v), refpixel)) {
|
||||
shrink = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shrink)
|
||||
break;
|
||||
++bounds.x;
|
||||
--bounds.w;
|
||||
// Pixels per row
|
||||
const int rowSize = image->getRowStrideSize() / image->getRowStrideSize(1);
|
||||
const int canvasSize = image->width()*image->height();
|
||||
if ((std::thread::hardware_concurrency() >= 4) &&
|
||||
((image->pixelFormat() == IMAGE_RGB && canvasSize >= 800*800) ||
|
||||
(image->pixelFormat() != IMAGE_RGB && canvasSize >= 500*500))) {
|
||||
gfx::Rect
|
||||
leftBounds(bounds), rightBounds(bounds),
|
||||
topBounds(bounds), bottomBounds(bounds);
|
||||
std::thread left ([&]{ shrink_bounds_left_templ <ImageTraits>(image, leftBounds, refpixel, rowSize); });
|
||||
std::thread right ([&]{ shrink_bounds_right_templ <ImageTraits>(image, rightBounds, refpixel, rowSize); });
|
||||
std::thread top ([&]{ shrink_bounds_top_templ <ImageTraits>(image, topBounds, refpixel); });
|
||||
std::thread bottom([&]{ shrink_bounds_bottom_templ<ImageTraits>(image, bottomBounds, refpixel); });
|
||||
left.join();
|
||||
right.join();
|
||||
top.join();
|
||||
bottom.join();
|
||||
bounds = leftBounds;
|
||||
bounds &= rightBounds;
|
||||
bounds &= topBounds;
|
||||
bounds &= bottomBounds;
|
||||
return !bounds.isEmpty();
|
||||
}
|
||||
|
||||
// Shrink right side
|
||||
for (u=bounds.x+bounds.w-1; u>=bounds.x; --u) {
|
||||
shrink = true;
|
||||
for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
|
||||
if (!is_same_pixel<ImageTraits>(
|
||||
get_pixel_fast<ImageTraits>(image, u, v), refpixel)) {
|
||||
shrink = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shrink)
|
||||
break;
|
||||
--bounds.w;
|
||||
else {
|
||||
return
|
||||
shrink_bounds_left_templ<ImageTraits>(image, bounds, refpixel, rowSize) &&
|
||||
shrink_bounds_right_templ<ImageTraits>(image, bounds, refpixel, rowSize) &&
|
||||
shrink_bounds_top_templ<ImageTraits>(image, bounds, refpixel) &&
|
||||
shrink_bounds_bottom_templ<ImageTraits>(image, bounds, refpixel);
|
||||
}
|
||||
|
||||
// Shrink top side
|
||||
for (v=bounds.y; v<bounds.y+bounds.h; ++v) {
|
||||
shrink = true;
|
||||
for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
|
||||
if (!is_same_pixel<ImageTraits>(
|
||||
get_pixel_fast<ImageTraits>(image, u, v), refpixel)) {
|
||||
shrink = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shrink)
|
||||
break;
|
||||
++bounds.y;
|
||||
--bounds.h;
|
||||
}
|
||||
|
||||
// Shrink bottom side
|
||||
for (v=bounds.y+bounds.h-1; v>=bounds.y; --v) {
|
||||
shrink = true;
|
||||
for (u=bounds.x; u<bounds.x+bounds.w; ++u) {
|
||||
if (!is_same_pixel<ImageTraits>(
|
||||
get_pixel_fast<ImageTraits>(image, u, v), refpixel)) {
|
||||
shrink = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!shrink)
|
||||
break;
|
||||
--bounds.h;
|
||||
}
|
||||
|
||||
return (!bounds.isEmpty());
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
@ -204,10 +235,13 @@ bool shrink_bounds(const Image* image,
|
||||
case IMAGE_RGB: return shrink_bounds_templ<RgbTraits>(image, bounds, refpixel);
|
||||
case IMAGE_GRAYSCALE: return shrink_bounds_templ<GrayscaleTraits>(image, bounds, refpixel);
|
||||
case IMAGE_INDEXED: return shrink_bounds_templ<IndexedTraits>(image, bounds, refpixel);
|
||||
case IMAGE_BITMAP: return shrink_bounds_templ<BitmapTraits>(image, bounds, refpixel);
|
||||
case IMAGE_BITMAP:
|
||||
// Not supported
|
||||
break;
|
||||
}
|
||||
ASSERT(false);
|
||||
return false;
|
||||
bounds = start_bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shrink_bounds(const Image* image, gfx::Rect& bounds, color_t refpixel)
|
||||
|
Loading…
x
Reference in New Issue
Block a user