mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-03 16:13:43 +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="PaletteEditor" text="@.edit_palette" />
|
||||||
<item command="PaletteSize" text="@.palette_size" group="palette_main" />
|
<item command="PaletteSize" text="@.palette_size" group="palette_main" />
|
||||||
<separator />
|
<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">
|
<item command="SetPaletteEntrySize" text="@.small_size">
|
||||||
<param name="size" value="7" />
|
<param name="size" value="7" />
|
||||||
</item>
|
</item>
|
||||||
|
@ -446,6 +446,7 @@ Screenshot_sRGB = (sRGB Color Profile)
|
|||||||
Screenshot_DisplayCS = (Display Color Profile)
|
Screenshot_DisplayCS = (Display Color Profile)
|
||||||
Scroll = Scroll {0}
|
Scroll = Scroll {0}
|
||||||
ScrollCenter = Scroll to center of canvas
|
ScrollCenter = Scroll to center of canvas
|
||||||
|
SelectPaletteColors = Select Palette Colors
|
||||||
SelectTile = Select Tile
|
SelectTile = Select Tile
|
||||||
SelectTile_Add = Select Tile (Add)
|
SelectTile_Add = Select Tile (Add)
|
||||||
SelectTile_Subtract = Select Tile (Subtract)
|
SelectTile_Subtract = Select Tile (Subtract)
|
||||||
@ -1393,6 +1394,7 @@ open_folder = Open &Folder
|
|||||||
[palette_popup_menu]
|
[palette_popup_menu]
|
||||||
edit_palette = Edit &Palette
|
edit_palette = Edit &Palette
|
||||||
palette_size = Palette Si&ze
|
palette_size = Palette Si&ze
|
||||||
|
select_palette_colors = S&elect
|
||||||
small_size = &Small Size
|
small_size = &Small Size
|
||||||
medium_size = &Medium Size
|
medium_size = &Medium Size
|
||||||
large_size = &Large Size
|
large_size = &Large Size
|
||||||
@ -1407,6 +1409,10 @@ save_palette_as_preset = Save Palette as Preset
|
|||||||
load_default_palette = Load Default Palette
|
load_default_palette = Load Default Palette
|
||||||
save_as_default_palette = Save Palette as Default
|
save_as_default_palette = Save Palette as Default
|
||||||
create_palette_from_current_sprite = New Palette from Sprite
|
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]
|
[palette_size]
|
||||||
title = Palette Size
|
title = Palette Size
|
||||||
|
@ -275,6 +275,7 @@ if(ENABLE_UI)
|
|||||||
commands/cmd_rotate.cpp
|
commands/cmd_rotate.cpp
|
||||||
commands/cmd_save_mask.cpp
|
commands/cmd_save_mask.cpp
|
||||||
commands/cmd_save_palette.cpp
|
commands/cmd_save_palette.cpp
|
||||||
|
commands/cmd_select_palette.cpp
|
||||||
commands/cmd_scroll.cpp
|
commands/cmd_scroll.cpp
|
||||||
commands/cmd_scroll_center.cpp
|
commands/cmd_scroll_center.cpp
|
||||||
commands/cmd_select_tile.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(Rotate)
|
||||||
FOR_EACH_COMMAND(SaveMask)
|
FOR_EACH_COMMAND(SaveMask)
|
||||||
FOR_EACH_COMMAND(SavePalette)
|
FOR_EACH_COMMAND(SavePalette)
|
||||||
|
FOR_EACH_COMMAND(SelectPaletteColors)
|
||||||
FOR_EACH_COMMAND(Screenshot)
|
FOR_EACH_COMMAND(Screenshot)
|
||||||
FOR_EACH_COMMAND(Scroll)
|
FOR_EACH_COMMAND(Scroll)
|
||||||
FOR_EACH_COMMAND(ScrollCenter)
|
FOR_EACH_COMMAND(ScrollCenter)
|
||||||
|
@ -278,16 +278,32 @@ void OctreeMap::feedWithImage(const Image* image,
|
|||||||
const int levelDeep)
|
const int levelDeep)
|
||||||
{
|
{
|
||||||
ASSERT(image);
|
ASSERT(image);
|
||||||
ASSERT(image->pixelFormat() == IMAGE_RGB);
|
ASSERT(image->pixelFormat() == IMAGE_RGB || image->pixelFormat() == IMAGE_GRAYSCALE);
|
||||||
uint32_t color;
|
uint32_t color;
|
||||||
const LockImageBits<RgbTraits> bits(image);
|
if (image->pixelFormat() == IMAGE_RGB) {
|
||||||
auto it = bits.begin(), end = bits.end();
|
const LockImageBits<RgbTraits> bits(image);
|
||||||
|
auto it = bits.begin(), end = bits.end();
|
||||||
|
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
color = *it;
|
color = *it;
|
||||||
if (rgba_geta(color) > 0)
|
if (rgba_geta(color) > 0)
|
||||||
addColor(color, levelDeep);
|
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;
|
m_maskColor = maskColor;
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,15 @@ int Palette::findExactMatch(int r, int g, int b, int a, int mask_index) const
|
|||||||
return -1;
|
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
|
// Based on Allegro's bestfit_color
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ namespace doc {
|
|||||||
void makeHueGradient(int from, int to);
|
void makeHueGradient(int from, int to);
|
||||||
|
|
||||||
int findExactMatch(int r, int g, int b, int a, int mask_index) const;
|
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 findBestfit(int r, int g, int b, int a, int mask_index) const;
|
||||||
int findBestfit2(int r, int g, int b) const;
|
int findBestfit2(int r, int g, int b) const;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user