mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-02 13:14:01 +00:00
Add select used colors and select used tiles on palette options
This commit is contained in:
parent
b3e80c49a1
commit
3c6320a4b5
16
data/gui.xml
16
data/gui.xml
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
265
src/app/commands/cmd_select_palette.cpp
Normal file
265
src/app/commands/cmd_select_palette.cpp
Normal 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
|
@ -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)
|
||||
|
@ -278,16 +278,32 @@ 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;
|
||||
const LockImageBits<RgbTraits> bits(image);
|
||||
auto it = bits.begin(), end = bits.end();
|
||||
if (image->pixelFormat() == IMAGE_RGB) {
|
||||
const LockImageBits<RgbTraits> bits(image);
|
||||
auto it = bits.begin(), end = bits.end();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
color = *it;
|
||||
if (rgba_geta(color) > 0)
|
||||
addColor(color, levelDeep);
|
||||
for (; it != end; ++it) {
|
||||
color = *it;
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user