mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-25 10:43:46 +00:00
Merge duplicated samples when -sheet-pack is used (fix #1316)
This commit is contained in:
parent
01821ed989
commit
51be644bf4
@ -20,6 +20,7 @@
|
|||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
#include "app/restore_visible_layers.h"
|
#include "app/restore_visible_layers.h"
|
||||||
#include "app/snap_to_grid.h"
|
#include "app/snap_to_grid.h"
|
||||||
|
#include "doc/images_map.h"
|
||||||
#include "base/convert_to.h"
|
#include "base/convert_to.h"
|
||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#include "base/fstream_path.h"
|
#include "base/fstream_path.h"
|
||||||
@ -28,6 +29,7 @@
|
|||||||
#include "doc/algorithm/shrink_bounds.h"
|
#include "doc/algorithm/shrink_bounds.h"
|
||||||
#include "doc/cel.h"
|
#include "doc/cel.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
|
#include "doc/images_map.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
#include "doc/palette.h"
|
#include "doc/palette.h"
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
@ -47,8 +49,8 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define DX_TRACE(...) // TRACEARGS
|
#define DX_TRACE(...) // TRACEARGS
|
||||||
|
|
||||||
@ -182,8 +184,9 @@ public:
|
|||||||
m_filename(filename),
|
m_filename(filename),
|
||||||
m_innerPadding(innerPadding),
|
m_innerPadding(innerPadding),
|
||||||
m_extrude(extrude),
|
m_extrude(extrude),
|
||||||
m_bounds(new SampleBounds(sprite)),
|
m_isLinked(false),
|
||||||
m_isDuplicated(false) {
|
m_isDuplicated(false),
|
||||||
|
m_bounds(new SampleBounds(sprite)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Doc* document() const { return m_document; }
|
Doc* document() const { return m_document; }
|
||||||
@ -216,15 +219,78 @@ public:
|
|||||||
void setTrimmedBounds(const gfx::Rect& bounds) { m_bounds->setTrimmedBounds(bounds); }
|
void setTrimmedBounds(const gfx::Rect& bounds) { m_bounds->setTrimmedBounds(bounds); }
|
||||||
void setInTextureBounds(const gfx::Rect& bounds) { m_bounds->setInTextureBounds(bounds); }
|
void setInTextureBounds(const gfx::Rect& bounds) { m_bounds->setInTextureBounds(bounds); }
|
||||||
|
|
||||||
|
bool isLinked() const { return m_isLinked; }
|
||||||
bool isDuplicated() const { return m_isDuplicated; }
|
bool isDuplicated() const { return m_isDuplicated; }
|
||||||
bool isEmpty() const { return m_bounds->trimmedBounds().isEmpty(); }
|
bool isEmpty() const {
|
||||||
|
// TODO trimmed bounds cannot be empty now (samples that are
|
||||||
|
// completely trimmed out are included as a sample of size 1x1)
|
||||||
|
ASSERT(!m_bounds->trimmedBounds().isEmpty());
|
||||||
|
return m_bounds->trimmedBounds().isEmpty();
|
||||||
|
}
|
||||||
SampleBoundsPtr sharedBounds() const { return m_bounds; }
|
SampleBoundsPtr sharedBounds() const { return m_bounds; }
|
||||||
|
|
||||||
|
void setLinked() { m_isLinked = true; }
|
||||||
|
void setDuplicated() { m_isDuplicated = true; }
|
||||||
|
|
||||||
void setSharedBounds(const SampleBoundsPtr& bounds) {
|
void setSharedBounds(const SampleBoundsPtr& bounds) {
|
||||||
m_isDuplicated = true;
|
|
||||||
m_bounds = bounds;
|
m_bounds = bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageRef createRender(ImageBufferPtr& imageBuf) {
|
||||||
|
ASSERT(m_sprite);
|
||||||
|
|
||||||
|
ImageRef render(
|
||||||
|
Image::create(m_sprite->pixelFormat(),
|
||||||
|
m_sprite->width(),
|
||||||
|
m_sprite->height(),
|
||||||
|
imageBuf));
|
||||||
|
render->setMaskColor(m_sprite->transparentColor());
|
||||||
|
clear_image(render.get(), m_sprite->transparentColor());
|
||||||
|
renderSample(render.get(), 0, 0, false);
|
||||||
|
return render;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderSample(doc::Image* dst, int x, int y, bool extrude) const {
|
||||||
|
RestoreVisibleLayers layersVisibility;
|
||||||
|
if (m_selLayers)
|
||||||
|
layersVisibility.showSelectedLayers(m_sprite,
|
||||||
|
*m_selLayers);
|
||||||
|
|
||||||
|
render::Render render;
|
||||||
|
render.setNewBlend(Preferences::instance().experimental.newBlend());
|
||||||
|
|
||||||
|
if (extrude) {
|
||||||
|
const gfx::Rect& trim = trimmedBounds();
|
||||||
|
|
||||||
|
// Displaced position onto the destination texture
|
||||||
|
int dx[] = { 0, 1, trim.w+1 };
|
||||||
|
int dy[] = { 0, 1, trim.h+1 };
|
||||||
|
|
||||||
|
// Starting point of the area to be copied from the original image
|
||||||
|
// taking into account the size of the trimmed sprite
|
||||||
|
int srcx[] = { trim.x, trim.x, trim.x2()-1 };
|
||||||
|
int srcy[] = { trim.y, trim.y, trim.y2()-1 };
|
||||||
|
|
||||||
|
// Size of the area to be copied from original image, starting at
|
||||||
|
// the point (srcx[i], srxy[j])
|
||||||
|
int szx[] = { 1, trim.w, 1 };
|
||||||
|
int szy[] = { 1, trim.h, 1 };
|
||||||
|
|
||||||
|
// Render a 9-patch image extruding the sample one pixel on each
|
||||||
|
// side.
|
||||||
|
for (int j=0; j<3; ++j) {
|
||||||
|
for (int i=0; i<3; ++i) {
|
||||||
|
gfx::Clip clip(x+dx[i], y+dy[j], gfx::RectT<int>(srcx[i], srcy[j], szx[i], szy[j]));
|
||||||
|
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gfx::Clip clip(x, y, trimmedBounds());
|
||||||
|
render.renderSprite(dst, m_sprite, m_frame, clip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Doc* m_document;
|
Doc* m_document;
|
||||||
Sprite* m_sprite;
|
Sprite* m_sprite;
|
||||||
@ -233,13 +299,14 @@ private:
|
|||||||
std::string m_filename;
|
std::string m_filename;
|
||||||
int m_innerPadding;
|
int m_innerPadding;
|
||||||
bool m_extrude;
|
bool m_extrude;
|
||||||
SampleBoundsPtr m_bounds;
|
bool m_isLinked;
|
||||||
bool m_isDuplicated;
|
bool m_isDuplicated;
|
||||||
|
SampleBoundsPtr m_bounds;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DocExporter::Samples {
|
class DocExporter::Samples {
|
||||||
public:
|
public:
|
||||||
typedef std::list<Sample> List;
|
typedef std::vector<Sample> List;
|
||||||
typedef List::iterator iterator;
|
typedef List::iterator iterator;
|
||||||
typedef List::const_iterator const_iterator;
|
typedef List::const_iterator const_iterator;
|
||||||
|
|
||||||
@ -249,6 +316,10 @@ public:
|
|||||||
m_samples.push_back(sample);
|
m_samples.push_back(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Sample& operator[](const size_t i) const {
|
||||||
|
return m_samples[i];
|
||||||
|
}
|
||||||
|
|
||||||
iterator begin() { return m_samples.begin(); }
|
iterator begin() { return m_samples.begin(); }
|
||||||
iterator end() { return m_samples.end(); }
|
iterator end() { return m_samples.end(); }
|
||||||
const_iterator begin() const { return m_samples.begin(); }
|
const_iterator begin() const { return m_samples.begin(); }
|
||||||
@ -264,8 +335,7 @@ public:
|
|||||||
virtual void layoutSamples(Samples& samples, int borderPadding, int shapePadding, int& width, int& height) = 0;
|
virtual void layoutSamples(Samples& samples, int borderPadding, int shapePadding, int& width, int& height) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DocExporter::SimpleLayoutSamples :
|
class DocExporter::SimpleLayoutSamples : public DocExporter::LayoutSamples {
|
||||||
public DocExporter::LayoutSamples {
|
|
||||||
public:
|
public:
|
||||||
SimpleLayoutSamples(SpriteSheetType type)
|
SimpleLayoutSamples(SpriteSheetType type)
|
||||||
: m_type(type) {
|
: m_type(type) {
|
||||||
@ -279,7 +349,7 @@ public:
|
|||||||
gfx::Size rowSize(0, 0);
|
gfx::Size rowSize(0, 0);
|
||||||
|
|
||||||
for (auto& sample : samples) {
|
for (auto& sample : samples) {
|
||||||
if (sample.isDuplicated())
|
if (sample.isLinked())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sample.isEmpty()) {
|
if (sample.isEmpty()) {
|
||||||
@ -355,18 +425,37 @@ private:
|
|||||||
SpriteSheetType m_type;
|
SpriteSheetType m_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DocExporter::BestFitLayoutSamples :
|
class DocExporter::BestFitLayoutSamples : public DocExporter::LayoutSamples {
|
||||||
public DocExporter::LayoutSamples {
|
ImageBufferPtr m_imageBuf;
|
||||||
public:
|
public:
|
||||||
|
BestFitLayoutSamples(ImageBufferPtr& buf)
|
||||||
|
: m_imageBuf(buf) {
|
||||||
|
}
|
||||||
void layoutSamples(Samples& samples, int borderPadding, int shapePadding, int& width, int& height) override {
|
void layoutSamples(Samples& samples, int borderPadding, int shapePadding, int& width, int& height) override {
|
||||||
gfx::PackingRects pr(borderPadding, shapePadding);
|
gfx::PackingRects pr(borderPadding, shapePadding);
|
||||||
|
doc::ImagesMap duplicates;
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
for (auto& sample : samples) {
|
for (auto& sample : samples) {
|
||||||
if (sample.isDuplicated() ||
|
if (sample.isLinked() ||
|
||||||
sample.isEmpty())
|
sample.isEmpty()) {
|
||||||
|
++i;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pr.add(sample.requiredSize());
|
ImageRef sampleRender(sample.createRender(m_imageBuf));
|
||||||
|
auto it = duplicates.find(sampleRender);
|
||||||
|
if (it != duplicates.end()) {
|
||||||
|
const uint32_t j = it->second;
|
||||||
|
|
||||||
|
sample.setDuplicated();
|
||||||
|
sample.setSharedBounds(samples[j].sharedBounds());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
duplicates[sampleRender] = i;
|
||||||
|
pr.add(sample.requiredSize());
|
||||||
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
@ -379,7 +468,8 @@ public:
|
|||||||
|
|
||||||
auto it = pr.begin();
|
auto it = pr.begin();
|
||||||
for (auto& sample : samples) {
|
for (auto& sample : samples) {
|
||||||
if (sample.isDuplicated() ||
|
if (sample.isLinked() ||
|
||||||
|
sample.isDuplicated() ||
|
||||||
sample.isEmpty())
|
sample.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -544,8 +634,9 @@ void DocExporter::captureSamples(Samples& samples)
|
|||||||
if (other.sprite() == sprite &&
|
if (other.sprite() == sprite &&
|
||||||
other.layer() == layer &&
|
other.layer() == layer &&
|
||||||
other.frame() == link->frame()) {
|
other.frame() == link->frame()) {
|
||||||
ASSERT(!other.isDuplicated());
|
ASSERT(!other.isLinked());
|
||||||
|
|
||||||
|
sample.setLinked();
|
||||||
sample.setSharedBounds(other.sharedBounds());
|
sample.setSharedBounds(other.sharedBounds());
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
@ -561,15 +652,7 @@ void DocExporter::captureSamples(Samples& samples)
|
|||||||
if (layer && layer->isImage() && !cel && m_ignoreEmptyCels)
|
if (layer && layer->isImage() && !cel && m_ignoreEmptyCels)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::unique_ptr<Image> sampleRender(
|
ImageRef sampleRender(sample.createRender(m_sampleRenderBuf));
|
||||||
Image::create(sprite->pixelFormat(),
|
|
||||||
sprite->width(),
|
|
||||||
sprite->height(),
|
|
||||||
m_sampleRenderBuf));
|
|
||||||
|
|
||||||
sampleRender->setMaskColor(sprite->transparentColor());
|
|
||||||
clear_image(sampleRender.get(), sprite->transparentColor());
|
|
||||||
renderSample(sample, sampleRender.get(), 0, 0, false);
|
|
||||||
|
|
||||||
gfx::Rect frameBounds;
|
gfx::Rect frameBounds;
|
||||||
doc::color_t refColor = 0;
|
doc::color_t refColor = 0;
|
||||||
@ -645,7 +728,7 @@ void DocExporter::layoutSamples(Samples& samples)
|
|||||||
{
|
{
|
||||||
switch (m_sheetType) {
|
switch (m_sheetType) {
|
||||||
case SpriteSheetType::Packed: {
|
case SpriteSheetType::Packed: {
|
||||||
BestFitLayoutSamples layout;
|
BestFitLayoutSamples layout(m_sampleRenderBuf);
|
||||||
layout.layoutSamples(
|
layout.layoutSamples(
|
||||||
samples, m_borderPadding, m_shapePadding,
|
samples, m_borderPadding, m_shapePadding,
|
||||||
m_textureWidth, m_textureHeight);
|
m_textureWidth, m_textureHeight);
|
||||||
@ -666,7 +749,8 @@ gfx::Size DocExporter::calculateSheetSize(const Samples& samples) const
|
|||||||
gfx::Rect fullTextureBounds(0, 0, m_textureWidth, m_textureHeight);
|
gfx::Rect fullTextureBounds(0, 0, m_textureWidth, m_textureHeight);
|
||||||
|
|
||||||
for (const auto& sample : samples) {
|
for (const auto& sample : samples) {
|
||||||
if (sample.isDuplicated() ||
|
if (sample.isLinked() ||
|
||||||
|
sample.isDuplicated() ||
|
||||||
sample.isEmpty())
|
sample.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -701,7 +785,8 @@ Doc* DocExporter::createEmptyTexture(const Samples& samples) const
|
|||||||
color_t transparentColor = 0;
|
color_t transparentColor = 0;
|
||||||
|
|
||||||
for (const auto& sample : samples) {
|
for (const auto& sample : samples) {
|
||||||
if (sample.isDuplicated() ||
|
if (sample.isLinked() ||
|
||||||
|
sample.isDuplicated() ||
|
||||||
sample.isEmpty())
|
sample.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -757,7 +842,8 @@ void DocExporter::renderTexture(Context* ctx, const Samples& samples, Image* tex
|
|||||||
textureImage->clear(0);
|
textureImage->clear(0);
|
||||||
|
|
||||||
for (const auto& sample : samples) {
|
for (const auto& sample : samples) {
|
||||||
if (sample.isDuplicated() ||
|
if (sample.isLinked() ||
|
||||||
|
sample.isDuplicated() ||
|
||||||
sample.isEmpty())
|
sample.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -772,7 +858,8 @@ void DocExporter::renderTexture(Context* ctx, const Samples& samples, Image* tex
|
|||||||
.execute(ctx);
|
.execute(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSample(sample, textureImage,
|
sample.renderSample(
|
||||||
|
textureImage,
|
||||||
sample.inTextureBounds().x+m_innerPadding,
|
sample.inTextureBounds().x+m_innerPadding,
|
||||||
sample.inTextureBounds().y+m_innerPadding,
|
sample.inTextureBounds().y+m_innerPadding,
|
||||||
m_extrude);
|
m_extrude);
|
||||||
@ -1034,46 +1121,4 @@ void DocExporter::createDataFile(const Samples& samples, std::ostream& os, Image
|
|||||||
<< "}\n";
|
<< "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y, bool extrude) const
|
|
||||||
{
|
|
||||||
RestoreVisibleLayers layersVisibility;
|
|
||||||
if (sample.selectedLayers())
|
|
||||||
layersVisibility.showSelectedLayers(sample.sprite(),
|
|
||||||
*sample.selectedLayers());
|
|
||||||
|
|
||||||
render::Render render;
|
|
||||||
render.setNewBlend(Preferences::instance().experimental.newBlend());
|
|
||||||
|
|
||||||
if (extrude) {
|
|
||||||
const gfx::Rect& trim = sample.trimmedBounds();
|
|
||||||
|
|
||||||
// Displaced position onto the destination texture
|
|
||||||
int dx[] = {0, 1, trim.w+1};
|
|
||||||
int dy[] = {0, 1, trim.h+1};
|
|
||||||
|
|
||||||
// Starting point of the area to be copied from the original image
|
|
||||||
// taking into account the size of the trimmed sprite
|
|
||||||
int srcx[] = {trim.x, trim.x, trim.x2()-1};
|
|
||||||
int srcy[] = {trim.y, trim.y, trim.y2()-1};
|
|
||||||
|
|
||||||
// Size of the area to be copied from original image, starting at
|
|
||||||
// the point (srcx[i], srxy[j])
|
|
||||||
int szx[] = {1, trim.w, 1};
|
|
||||||
int szy[] = {1, trim.h, 1};
|
|
||||||
|
|
||||||
// Render a 9-patch image extruding the sample one pixel on each
|
|
||||||
// side.
|
|
||||||
for(int j=0; j<3; ++j) {
|
|
||||||
for(int i=0; i<3; ++i) {
|
|
||||||
gfx::Clip clip(x+dx[i], y+dy[j], gfx::RectT<int>(srcx[i], srcy[j], szx[i], szy[j]));
|
|
||||||
render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gfx::Clip clip(x, y, sample.trimmedBounds());
|
|
||||||
render.renderSprite(dst, sample.sprite(), sample.frame(), clip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -102,7 +102,6 @@ namespace app {
|
|||||||
Doc* createEmptyTexture(const Samples& samples) const;
|
Doc* createEmptyTexture(const Samples& samples) const;
|
||||||
void renderTexture(Context* ctx, const Samples& samples, doc::Image* textureImage) const;
|
void renderTexture(Context* ctx, const Samples& samples, doc::Image* textureImage) const;
|
||||||
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage);
|
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage);
|
||||||
void renderSample(const Sample& sample, doc::Image* dst, int x, int y, bool extrude) const;
|
|
||||||
|
|
||||||
class Item {
|
class Item {
|
||||||
public:
|
public:
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/image_impl.h"
|
#include "doc/image_impl.h"
|
||||||
#include "doc/image_ref.h"
|
#include "doc/image_ref.h"
|
||||||
|
#include "doc/images_map.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
#include "doc/mask.h"
|
#include "doc/mask.h"
|
||||||
#include "doc/object.h"
|
#include "doc/object.h"
|
||||||
|
42
src/doc/images_map.h
Normal file
42
src/doc/images_map.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef DOC_IMAGES_MAP_H_INCLUDED
|
||||||
|
#define DOC_IMAGES_MAP_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "doc/image.h"
|
||||||
|
#include "doc/image_ref.h"
|
||||||
|
#include "doc/primitives.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
struct image_hash {
|
||||||
|
size_t operator()(const ImageRef& i) const {
|
||||||
|
return calculate_image_hash(i.get(), i->bounds());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct image_eq {
|
||||||
|
bool operator()(const ImageRef& a, const ImageRef& b) const {
|
||||||
|
return is_same_image(a.get(), b.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// A hash table used to match Image pixels data <-> an index
|
||||||
|
typedef std::unordered_map<ImageRef,
|
||||||
|
uint32_t,
|
||||||
|
details::image_hash,
|
||||||
|
details::image_eq> ImagesMap;
|
||||||
|
|
||||||
|
} // namespace doc
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2018 Igara Studio S.A.
|
// Copyright (c) 2018-2019 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2016 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -416,4 +416,49 @@ void remap_image(Image* image, const Remap& remap)
|
|||||||
*it = remap[*it];
|
*it = remap[*it];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO test this hash routine and find a better alternative
|
||||||
|
|
||||||
|
template <typename ImageTraits, uint32_t Mask>
|
||||||
|
static uint32_t calculate_image_hash_templ(const Image* image,
|
||||||
|
const gfx::Rect& bounds)
|
||||||
|
{
|
||||||
|
uint32_t hash = 0;
|
||||||
|
for (int y=0; y<bounds.h; ++y) {
|
||||||
|
auto p = (typename ImageTraits::address_t)image->getPixelAddress(bounds.x, bounds.y+y);
|
||||||
|
for (int x=0; x<bounds.w; ++x, ++p) {
|
||||||
|
uint32_t value = *p;
|
||||||
|
uint32_t mask = Mask;
|
||||||
|
while (mask) {
|
||||||
|
hash += value & mask & 0xff;
|
||||||
|
hash <<= 1;
|
||||||
|
value >>= 8;
|
||||||
|
mask >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t calculate_image_hash(const Image* img, const gfx::Rect& bounds)
|
||||||
|
{
|
||||||
|
switch (img->pixelFormat()) {
|
||||||
|
case IMAGE_RGB: return calculate_image_hash_templ<RgbTraits, rgba_rgb_mask>(img, bounds);
|
||||||
|
case IMAGE_GRAYSCALE: return calculate_image_hash_templ<GrayscaleTraits, graya_v_mask>(img, bounds);
|
||||||
|
case IMAGE_INDEXED: return calculate_image_hash_templ<IndexedTraits, 0xff>(img, bounds);
|
||||||
|
case IMAGE_BITMAP: return calculate_image_hash_templ<BitmapTraits, 1>(img, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash = 0;
|
||||||
|
for (int y=0; y<bounds.h; ++y) {
|
||||||
|
int bytes = img->getRowStrideSize(bounds.w);
|
||||||
|
uint8_t* p = img->getPixelAddress(bounds.x, bounds.y+y);
|
||||||
|
while (bytes-- > 0) {
|
||||||
|
hash += *p;
|
||||||
|
hash <<= 1;
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2018 Igara Studio S.A.
|
// Copyright (c) 2018-2019 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2016 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -9,6 +9,7 @@
|
|||||||
#define DOC_PRIMITIVES_H_INCLUDED
|
#define DOC_PRIMITIVES_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/ints.h"
|
||||||
#include "doc/color.h"
|
#include "doc/color.h"
|
||||||
#include "doc/image_buffer.h"
|
#include "doc/image_buffer.h"
|
||||||
#include "gfx/fwd.h"
|
#include "gfx/fwd.h"
|
||||||
@ -48,6 +49,9 @@ namespace doc {
|
|||||||
|
|
||||||
void remap_image(Image* image, const Remap& remap);
|
void remap_image(Image* image, const Remap& remap);
|
||||||
|
|
||||||
|
uint32_t calculate_image_hash(const Image* image,
|
||||||
|
const gfx::Rect& bounds);
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user