Add select used colors and select used tiles on palette options

This commit is contained in:
Gaspar Capello 2021-05-20 10:45:59 -03:00 committed by David Capello
parent b3e80c49a1
commit 3c6320a4b5
8 changed files with 322 additions and 7 deletions

View File

@ -1116,6 +1116,22 @@
<item command="PaletteEditor" text="@.edit_palette" />
<item command="PaletteSize" text="@.palette_size" group="palette_main" />
<separator />
<menu id="select_palette_colors" text="@.select_palette_colors">
<item command="SelectPaletteColors" text="@.used_colors">
<param name="modifier" value="used_colors" />
</item>
<item command="SelectPaletteColors" text="@.unused_colors">
<param name="modifier" value="unused_colors" />
</item>
<separator />
<item command="SelectPaletteColors" text="@.used_tiles">
<param name="modifier" value="used_tiles" />
</item>
<item command="SelectPaletteColors" text="@.unused_tiles">
<param name="modifier" value="unused_tiles" />
</item>
</menu>
<separator />
<item command="SetPaletteEntrySize" text="@.small_size">
<param name="size" value="7" />
</item>

View File

@ -446,6 +446,7 @@ Screenshot_sRGB = (sRGB Color Profile)
Screenshot_DisplayCS = (Display Color Profile)
Scroll = Scroll {0}
ScrollCenter = Scroll to center of canvas
SelectPaletteColors = Select Palette Colors
SelectTile = Select Tile
SelectTile_Add = Select Tile (Add)
SelectTile_Subtract = Select Tile (Subtract)
@ -1393,6 +1394,7 @@ open_folder = Open &Folder
[palette_popup_menu]
edit_palette = Edit &Palette
palette_size = Palette Si&ze
select_palette_colors = S&elect
small_size = &Small Size
medium_size = &Medium Size
large_size = &Large Size
@ -1407,6 +1409,10 @@ save_palette_as_preset = Save Palette as Preset
load_default_palette = Load Default Palette
save_as_default_palette = Save Palette as Default
create_palette_from_current_sprite = New Palette from Sprite
unused_colors = Unused Colors
unused_tiles = Unused Tiles
used_colors = Used Colors
used_tiles = Used Tiles
[palette_size]
title = Palette Size

View File

@ -275,6 +275,7 @@ if(ENABLE_UI)
commands/cmd_rotate.cpp
commands/cmd_save_mask.cpp
commands/cmd_save_palette.cpp
commands/cmd_select_palette.cpp
commands/cmd_scroll.cpp
commands/cmd_scroll_center.cpp
commands/cmd_select_tile.cpp

View File

@ -0,0 +1,265 @@
// Aseprite
// Copyright (C) 2021 Igara Studio SA
//
// 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/app.h"
#include "app/commands/cmd_set_palette.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/context.h"
#include "app/modules/palettes.h"
#include "app/site.h"
#include "doc/cel.h"
#include "doc/frame_range.h"
#include "doc/image_bits.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/octree_map.h"
#include "doc/palette.h"
#include "doc/sprite.h"
namespace app {
using namespace ui;
class SelectPaletteColorsCommand : public Command {
public:
typedef enum {
UsedColors,
UnusedColors,
UsedTiles,
UnusedTiles
} Modifier;
SelectPaletteColorsCommand();
protected:
bool onEnabled(Context* context) override;
void onLoadParams(const Params& params) override;
void onExecute(Context* context) override;
private:
bool selectTiles(Sprite* sprite,
Site* site,
PalettePicks& usedTilesIndices,
SelectedFrames& selectedFrames);
Modifier m_modifier;
};
SelectPaletteColorsCommand::SelectPaletteColorsCommand()
: Command(CommandId::SelectPaletteColors(), CmdRecordableFlag)
, m_modifier(Modifier::UsedColors)
{
}
bool SelectPaletteColorsCommand::onEnabled(Context* context)
{
TilemapMode tilemapMode = context->activeSite().tilemapMode();
if (m_modifier == Modifier::UsedColors || m_modifier == Modifier::UnusedColors)
return (tilemapMode == TilemapMode::Pixels) ? true : false;
else
return (tilemapMode == TilemapMode::Tiles) ? true : false;
}
void SelectPaletteColorsCommand::onLoadParams(const Params& params)
{
std::string strParam = params.get("modifier");
if (strParam == "unused_colors")
m_modifier = Modifier::UnusedColors;
else if (strParam == "used_tiles")
m_modifier = Modifier::UsedTiles;
else if (strParam == "unused_tiles")
m_modifier = Modifier::UnusedTiles;
else
m_modifier = Modifier::UsedColors;
}
bool SelectPaletteColorsCommand::selectTiles(
Sprite* sprite,
Site* site,
PalettePicks& usedTilesIndices,
SelectedFrames& selectedFrames)
{
Tileset* currentTileset = site->tileset();
Layer* layer = site->layer();
int tilesetSize = 0;
SelectedFrames::const_iterator selected_frames_it = selectedFrames.begin();
SelectedFrames::const_iterator selected_frames_end = selectedFrames.end();
CelList tilemapCels;
if (!currentTileset || !site->layer() || !layer->isTilemap())
return false;
for (;selected_frames_it != selected_frames_end; ++selected_frames_it) {
int frame = *selected_frames_it;
if (layer->cel(frame))
tilemapCels.push_back(layer->cel(frame));
}
tilesetSize = currentTileset->size();
if (usedTilesIndices.size() != tilesetSize)
usedTilesIndices.resize(tilesetSize);
usedTilesIndices.clear();
if (tilemapCels.size() <= 0 || tilesetSize <= 0)
return false;
tile_index i = 0;
for (; i<tilesetSize; ++i) {
for (auto cel : tilemapCels) {
bool skiptilesetIndex = false;
for (const doc::tile_t t : LockImageBits<TilemapTraits>(cel->imageRef().get())) {
if (t == i) {
usedTilesIndices[doc::tile_geti(t)] = true;
skiptilesetIndex = true;
break;
}
}
if (skiptilesetIndex)
break;
}
}
return true;
}
void SelectPaletteColorsCommand::onExecute(Context* context)
{
TilemapMode tilemapMode = context->activeSite().tilemapMode();
Site site = context->activeSite();
Sprite* sprite = site.sprite();
DocRange range = site.range();
SelectedFrames selectedFrames;
SelectedLayers selectedLayers;
if (range.type() == DocRange::Type::kNone) {
// If there isn't a cels range selected, it assumes the whole sprite:
range.startRange(site.layer(), 0, DocRange::Type::kFrames);
range.endRange(site.layer(), sprite->lastFrame());
selectedFrames = range.selectedFrames();
selectedLayers.selectAllLayers(sprite->root());
}
else {
selectedFrames = range.selectedFrames();
selectedLayers = range.selectedLayers();
}
if (tilemapMode == TilemapMode::Pixels) {
doc::OctreeMap octreemap;
SelectedFrames::const_iterator selected_frames_it = selectedFrames.begin();
SelectedFrames::const_iterator selected_frames_end = selectedFrames.end();
const doc::Palette* currentPalette = get_current_palette();
PalettePicks usedEntries(currentPalette->size());
// Loop throught selected layers and frames:
for (auto& layer : selectedLayers) {
selected_frames_it = selectedFrames.begin();
for (;selected_frames_it != selected_frames_end; ++selected_frames_it) {
int frame = *selected_frames_it;
if (layer->cel(frame) && layer->cel(frame)->image()) {
Image* image = layer->cel(frame)->image();
// Ordinary layer case:
if (!layer->isTilemap()) {
// INDEXED case:
if (image->pixelFormat() == IMAGE_INDEXED) {
doc::for_each_pixel<IndexedTraits>(
image,
[&usedEntries](const color_t p) {
usedEntries[p] = true;
});
}
// RGB / GRAYSCALE case:
else if (image->pixelFormat() == IMAGE_RGB ||
image->pixelFormat() == IMAGE_GRAYSCALE)
octreemap.feedWithImage(image, image->maskColor(), 8);
else
ASSERT(false);
}
// Tilemap layer case:
else if (layer->isTilemap()) {
Tileset* tileset = static_cast<LayerTilemap*>(layer)->tileset();
tile_index ti;
PalettePicks usedTiles(tileset->size());
// Looking for tiles (available in tileset) used in the tilemap image:
doc::for_each_pixel<TilemapTraits>(
image,
[&usedTiles, &tileset, &ti](const tile_t t) {
if (tileset->findTileIndex(tileset->get(t), ti))
usedTiles[ti] = true;
});
// Looking for tile matches in usedTiles. If a tile matches, then
// search into the tilemap (pixel by pixel) looking for color matches.
for (int i=0; i<usedTiles.size(); ++i) {
if (usedTiles[i]) {
// The tileset format is INDEXED:
if (tileset->get(i).get()->pixelFormat() == IMAGE_INDEXED) {
// Looking pixel by pixel in each usedTiles
doc::for_each_pixel<IndexedTraits>(
image,
[&usedEntries](const color_t p) {
usedEntries[p] = true;
});
}
// The tileset format is RGB / GRAYSCALE:
else if (tileset->get(i).get()->pixelFormat() == IMAGE_RGB ||
tileset->get(i).get()->pixelFormat() == IMAGE_GRAYSCALE)
octreemap.feedWithImage(tileset->get(i).get(), image->maskColor(), 8);
else
ASSERT(false);
}
}
}
else
ASSERT(false);
}
}
}
doc::Palette tempPalette;
octreemap.makePalette(&tempPalette, std::numeric_limits<int>::max(), 8);
for (int i=0; i < currentPalette->size(); ++i) {
if (tempPalette.findExactMatch(currentPalette->getEntry(i))) {
usedEntries[i] = true;
continue;
}
}
if (m_modifier == Modifier::UsedColors) {
context->setSelectedColors(usedEntries);
}
else if (m_modifier == Modifier::UnusedColors) {
for (int i=0; i<usedEntries.size(); ++i)
usedEntries[i] = !usedEntries[i];
context->setSelectedColors(usedEntries);
}
else
ASSERT(false);
}
else { // tilemapMode == TilemapMode::Tiles
if (!site.tileset())
return;
PalettePicks usedTileIndices(site.tileset()->size());
selectTiles(sprite, &site, usedTileIndices, selectedFrames);
if (m_modifier == Modifier::UsedTiles)
context->setSelectedTiles(usedTileIndices);
else if (m_modifier == Modifier::UnusedTiles) {
for (int i=0; i<usedTileIndices.size(); ++i)
usedTileIndices[i] = !usedTileIndices[i];
context->setSelectedTiles(usedTileIndices);
}
else
ASSERT(false);
}
}
Command* CommandFactory::createSelectPaletteColorsCommand()
{
return new SelectPaletteColorsCommand;
}
} // namespace app

View File

@ -131,6 +131,7 @@ FOR_EACH_COMMAND(ReverseFrames)
FOR_EACH_COMMAND(Rotate)
FOR_EACH_COMMAND(SaveMask)
FOR_EACH_COMMAND(SavePalette)
FOR_EACH_COMMAND(SelectPaletteColors)
FOR_EACH_COMMAND(Screenshot)
FOR_EACH_COMMAND(Scroll)
FOR_EACH_COMMAND(ScrollCenter)

View File

@ -278,8 +278,9 @@ void OctreeMap::feedWithImage(const Image* image,
const int levelDeep)
{
ASSERT(image);
ASSERT(image->pixelFormat() == IMAGE_RGB);
ASSERT(image->pixelFormat() == IMAGE_RGB || image->pixelFormat() == IMAGE_GRAYSCALE);
uint32_t color;
if (image->pixelFormat() == IMAGE_RGB) {
const LockImageBits<RgbTraits> bits(image);
auto it = bits.begin(), end = bits.end();
@ -288,6 +289,21 @@ void OctreeMap::feedWithImage(const Image* image,
if (rgba_geta(color) > 0)
addColor(color, levelDeep);
}
}
else {
const LockImageBits<GrayscaleTraits> bits(image);
auto it = bits.begin(), end = bits.end();
for (; it != end; ++it) {
color = *it;
if (graya_geta(color) > 0)
addColor(rgba(graya_getv(color),
graya_getv(color),
graya_getv(color),
255), levelDeep);
}
}
m_maskColor = maskColor;
}

View File

@ -286,6 +286,15 @@ int Palette::findExactMatch(int r, int g, int b, int a, int mask_index) const
return -1;
}
bool Palette::findExactMatch(color_t color) const
{
for (int i=0; i<(int)m_colors.size(); ++i) {
if (getEntry(i) == color)
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Based on Allegro's bestfit_color

View File

@ -97,6 +97,7 @@ namespace doc {
void makeHueGradient(int from, int to);
int findExactMatch(int r, int g, int b, int a, int mask_index) const;
bool findExactMatch(color_t color) const;
int findBestfit(int r, int g, int b, int a, int mask_index) const;
int findBestfit2(int r, int g, int b) const;