Fix several old issues with indexed images and transparent color

- If we use a RGB color in a background layer (indexed image),
  and the first palette entry (transparent color) matches that RGB color,
  we can use that entry, because this is the background (the transparent
  color isn't transparent at all in the background layer).
- If we use the same RGB color in a transparent color, we've to avoid the
  transparent color, because the user want to paint with that specific
  solid color.
- Palette::findBestfit() receives a mask_index now, so we can find what
  color best matches a specific RGB avoiding that specific mask color,
  or we can use -1 to use any color (e.g. for background layers).
- Added app::ColorTarget() to simplify and fix color_utils::color_for_layer
  and app_get_color_to_clear_layer(), so now we can use the new findBestfit
  to return the transparent color for background layers (if a RGB color
  matches the transparent color RGB values).
- Removed fixup_color_for_layer/background() functions in color_utils
- Fix NewImageFromMask() to use the mask color to clear the image
- Improve the Editor pen preview (cursor.cpp) to draw the bounds of the
  pen when it will paint with the transparent color.
This commit is contained in:
David Capello 2014-05-08 00:30:36 -03:00
parent 5a45d1ec23
commit a6d900fc2c
21 changed files with 188 additions and 109 deletions

View File

@ -377,12 +377,15 @@ void app_default_statusbar_message()
int app_get_color_to_clear_layer(Layer* layer)
{
/* all transparent layers are cleared with the mask color */
app::Color color = app::Color::fromMask();
ASSERT(layer != NULL);
/* the `Background' is erased with the `Background Color' */
if (layer != NULL && layer->isBackground())
app::Color color;
// The `Background' is erased with the `Background Color'
if (layer->isBackground())
color = ColorBar::instance()->getBgColor();
else // All transparent layers are cleared with the mask color
color = app::Color::fromMask();
return color_utils::color_for_layer(color, layer);
}

64
src/app/color_target.h Normal file
View File

@ -0,0 +1,64 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_COLOR_TARGET_H_INCLUDED
#define APP_COLOR_TARGET_H_INCLUDED
#pragma once
#include "raster/color.h"
#include "raster/layer.h"
#include "raster/pixel_format.h"
#include "raster/sprite.h"
namespace app {
// Represents the kind of surface where we'll use a color.
class ColorTarget {
public:
enum LayerType {
BackgroundLayer,
TransparentLayer
};
ColorTarget(LayerType layerType, raster::PixelFormat pixelFormat, raster::color_t maskColor) :
m_layerType(layerType),
m_pixelFormat(pixelFormat),
m_maskColor(maskColor) {
}
ColorTarget(raster::Layer* layer) :
m_layerType(layer->isBackground() ? BackgroundLayer: TransparentLayer),
m_pixelFormat(layer->getSprite()->getPixelFormat()),
m_maskColor(layer->getSprite()->getTransparentColor()) {
}
bool isBackground() const { return m_layerType == BackgroundLayer; }
bool isTransparent() const { return m_layerType == TransparentLayer; }
LayerType layerType() const { return m_layerType; }
raster::PixelFormat pixelFormat() const { return m_pixelFormat; }
raster::color_t maskColor() const { return m_maskColor; }
private:
LayerType m_layerType;
raster::PixelFormat m_pixelFormat;
raster::color_t m_maskColor;
};
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -180,44 +180,40 @@ raster::color_t color_utils::color_for_image(const app::Color& color, PixelForma
raster::color_t color_utils::color_for_layer(const app::Color& color, Layer* layer)
{
raster::color_t pixel_color;
if (color.getType() == app::Color::MaskType) {
pixel_color = layer->getSprite()->getTransparentColor();
}
else {
PixelFormat format = layer->getSprite()->getPixelFormat();
pixel_color = color_for_image(color, format);
}
return fixup_color_for_layer(layer, pixel_color);
return color_for_target(color, ColorTarget(layer));
}
raster::color_t color_utils::fixup_color_for_layer(Layer *layer, raster::color_t color)
raster::color_t color_utils::color_for_target(const app::Color& color, const ColorTarget& colorTarget)
{
if (layer->isBackground())
return fixup_color_for_background(layer->getSprite()->getPixelFormat(), color);
else
return color;
}
if (color.getType() == app::Color::MaskType)
return colorTarget.maskColor();
raster::color_t color_utils::fixup_color_for_background(PixelFormat format, raster::color_t color)
{
switch (format) {
raster::color_t c = -1;
switch (colorTarget.pixelFormat()) {
case IMAGE_RGB:
if (rgba_geta(color) < 255) {
return rgba(rgba_getr(color),
rgba_getg(color),
rgba_getb(color), 255);
}
c = rgba(color.getRed(), color.getGreen(), color.getBlue(), 255);
break;
case IMAGE_GRAYSCALE:
if (graya_geta(color) < 255) {
return graya(graya_getv(color), 255);
c = graya(color.getGray(), 255);
break;
case IMAGE_INDEXED:
if (color.getType() == app::Color::IndexType) {
c = color.getIndex();
}
else {
c = get_current_palette()->findBestfit(
color.getRed(),
color.getGreen(),
color.getBlue(),
colorTarget.isTransparent() ?
colorTarget.maskColor(): // Don't return the mask color
-1); // Return any color, we are in a background layer.
}
break;
}
return color;
return c;
}
} // namespace app

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@
#pragma once
#include "app/color.h"
#include "app/color_target.h"
#include "raster/color.h"
#include "raster/pixel_format.h"
#include "ui/color.h"
@ -39,9 +40,7 @@ namespace app {
int color_for_allegro(const app::Color& color, int depth);
raster::color_t color_for_image(const app::Color& color, raster::PixelFormat format);
raster::color_t color_for_layer(const app::Color& color, raster::Layer* layer);
raster::color_t fixup_color_for_layer(raster::Layer* layer, raster::color_t color);
raster::color_t fixup_color_for_background(raster::PixelFormat format, raster::color_t color);
raster::color_t color_for_target(const app::Color& color, const ColorTarget& colorTarget);
} // namespace color_utils
} // namespace app

View File

@ -68,16 +68,22 @@ void BackgroundFromLayerCommand::onExecute(Context* context)
Document* document(writer.document());
Sprite* sprite(writer.sprite());
// each frame of the layer to be converted as `Background' must be
// cleared using the selected background color in the color-bar
int bgcolor = color_utils::color_for_image(context->getSettings()->getBgColor(), sprite->getPixelFormat());
bgcolor = color_utils::fixup_color_for_background(sprite->getPixelFormat(), bgcolor);
raster::color_t bgcolor =
color_utils::color_for_target(
context->getSettings()->getBgColor(),
ColorTarget(
ColorTarget::BackgroundLayer,
sprite->getPixelFormat(),
sprite->getTransparentColor()));
{
UndoTransaction undo_transaction(writer.context(), "Background from Layer");
document->getApi().backgroundFromLayer(static_cast<LayerImage*>(writer.layer()), bgcolor);
document->getApi().backgroundFromLayer(
static_cast<LayerImage*>(writer.layer()),
bgcolor);
undo_transaction.commit();
}
update_screen_for_document(document);
}

View File

@ -212,8 +212,12 @@ void CanvasSizeCommand::onExecute(Context* context)
Sprite* sprite = writer.sprite();
UndoTransaction undoTransaction(writer.context(), "Canvas Size");
DocumentApi api = document->getApi();
int bgcolor = color_utils::color_for_image(context->getSettings()->getBgColor(), sprite->getPixelFormat());
bgcolor = color_utils::fixup_color_for_background(sprite->getPixelFormat(), bgcolor);
raster::color_t bgcolor = color_utils::color_for_target(
context->getSettings()->getBgColor(),
ColorTarget(
ColorTarget::BackgroundLayer,
sprite->getPixelFormat(),
sprite->getTransparentColor()));
api.cropSprite(sprite, gfx::Rect(x1, y1, x2-x1, y2-y1), bgcolor);
undoTransaction.commit();

View File

@ -104,7 +104,7 @@ void FlipCommand::onExecute(Context* context)
// If the mask isn't a rectangular area, we've to flip the mask too.
if (mask->getBitmap() != NULL && !mask->isRectangular()) {
int bgcolor = app_get_color_to_clear_layer(writer.layer());
raster::color_t bgcolor = app_get_color_to_clear_layer(writer.layer());
// Flip the portion of image specified by the mask.
mask->offsetOrigin(-x, -y);

View File

@ -132,7 +132,7 @@ void MergeDownLayerCommand::onExecute(Context* context)
}
// With destination
else {
int x1, y1, x2, y2, bgcolor;
int x1, y1, x2, y2;
Image *new_image;
// Merge down in the background layer
@ -141,7 +141,6 @@ void MergeDownLayerCommand::onExecute(Context* context)
y1 = 0;
x2 = sprite->getWidth();
y2 = sprite->getHeight();
bgcolor = app_get_color_to_clear_layer(dst_layer);
}
// Merge down in a transparent layer
else {
@ -149,9 +148,10 @@ void MergeDownLayerCommand::onExecute(Context* context)
y1 = MIN(src_cel->getY(), dst_cel->getY());
x2 = MAX(src_cel->getX()+src_image->getWidth()-1, dst_cel->getX()+dst_image->getWidth()-1);
y2 = MAX(src_cel->getY()+src_image->getHeight()-1, dst_cel->getY()+dst_image->getHeight()-1);
bgcolor = 0;
}
raster::color_t bgcolor = app_get_color_to_clear_layer(dst_layer);
new_image = raster::crop_image(dst_image,
x1-dst_cel->getX(),
y1-dst_cel->getY(),

View File

@ -191,7 +191,11 @@ void NewFileCommand::onExecute(Context* context)
Image* image = sprite->getStock()->getImage(layerImage->getCel(FrameNumber(0))->getImage());
raster::clear_image(image,
color_utils::color_for_image(color, format, -1));
color_utils::color_for_target(color,
ColorTarget(
ColorTarget::BackgroundLayer,
sprite->getPixelFormat(),
sprite->getTransparentColor())));
}
}

View File

@ -295,8 +295,6 @@ void Document::prepareExtraCel(int x, int y, int w, int h, int opacity)
m_extraImage->getHeight() != h) {
delete m_extraImage; // image
m_extraImage = Image::create(getSprite()->getPixelFormat(), w, h);
m_extraImage->setMaskColor(0);
clear_image(m_extraImage, m_extraImage->getMaskColor());
}
}

View File

@ -263,15 +263,20 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
->getSettings()
->getCurrentTool();
// Setup the cursor type depending the current tool
// Setup the cursor type depending of several factors (current tool,
// foreground color, and layer transparency).
color_t pen_color = get_pen_color(m_sprite, m_layer);
color_t mask_color = m_sprite->getTransparentColor();
if (current_tool->getInk(0)->isSelection()) {
cursor_type = CURSOR_CROSS_ONE;
}
else if (// Use cursor bounds for inks that are effects (eraser, blur, etc.)
current_tool->getInk(0)->isEffect() ||
// or when the FG color is mask and we are not in the background layer
(UIContext::instance()->getSettings()->getFgColor().getType() == app::Color::MaskType &&
(m_layer != NULL && !m_layer->isBackground()))) {
else if (
// Use cursor bounds for inks that are effects (eraser, blur, etc.)
(current_tool->getInk(0)->isEffect()) ||
// or when the pen color is transparent and we are not in the background layer
(m_layer && !m_layer->isBackground() &&
pen_color == mask_color)) {
cursor_type = CURSOR_BOUNDS;
}
else {
@ -288,8 +293,6 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
->getSettings()
->getToolSettings(current_tool);
color_t pen_color = get_pen_color(m_sprite, m_layer);
uint32_t new_mask_color;
Pen* pen = editor_get_current_pen();
gfx::Rect penBounds = pen->getBounds();
@ -300,18 +303,11 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
// In 'indexed' images, if the current color is 0, we have to use
// a different mask color (different from 0) to draw the extra layer
if (m_sprite->getPixelFormat() == IMAGE_INDEXED && pen_color == 0) {
new_mask_color = 1;
}
else {
new_mask_color = 0;
}
if (pen_color == mask_color)
mask_color = (mask_color == 0 ? 1: 0);
Image* extraImage = m_document->getExtraCelImage();
if (extraImage->getMaskColor() != new_mask_color) {
extraImage->setMaskColor(new_mask_color);
clear_image(extraImage, new_mask_color);
}
extraImage->setMaskColor(mask_color);
put_pen(extraImage, pen, -penBounds.x, -penBounds.y,
pen_color, extraImage->getMaskColor());
@ -324,7 +320,7 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
}
}
/* save area and draw the cursor */
// Save area and draw the cursor
if (refresh) {
acquire_bitmap(ji_screen);
ji_screen->clip = false;
@ -337,7 +333,7 @@ void Editor::editor_draw_cursor(int x, int y, bool refresh)
// cursor thickness
m_cursor_thick = 1; // get_thickness_for_cursor();
/* cursor in the editor (model) */
// cursor in the editor (model)
m_cursor_editor_x = x;
m_cursor_editor_y = y;

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -61,6 +61,7 @@ PixelsMovement::PixelsMovement(Context* context,
, m_adjustPivot(false)
, m_handle(NoHandle)
, m_originalImage(Image::createCopy(moveThis))
, m_maskColor(m_sprite->getTransparentColor())
{
m_initialData = gfx::Transformation(gfx::Rect(initialX, initialY, moveThis->getWidth(), moveThis->getHeight()));
m_currentData = m_initialData;
@ -68,9 +69,7 @@ PixelsMovement::PixelsMovement(Context* context,
ContextWriter writer(m_reader);
m_document->prepareExtraCel(0, 0, m_sprite->getWidth(), m_sprite->getHeight(), opacity);
Image* extraImage = m_document->getExtraCelImage();
clear_image(extraImage, extraImage->getMaskColor());
copy_image(extraImage, moveThis, initialX, initialY);
redrawExtraImage();
m_initialMask = new Mask(*m_document->getMask());
m_currentMask = new Mask(*m_document->getMask());
@ -438,8 +437,8 @@ Image* PixelsMovement::getDraggedImageCopy(gfx::Point& origin)
int width = rightBottom.x - leftTop.x;
int height = rightBottom.y - leftTop.y;
base::UniquePtr<Image> image(Image::create(m_sprite->getPixelFormat(), width, height));
clear_image(image, image->getMaskColor());
drawParallelogram(image, m_originalImage, corners, leftTop);
drawImage(image, leftTop);
origin = leftTop;
@ -567,28 +566,21 @@ gfx::Size PixelsMovement::getInitialImageSize() const
return m_initialData.bounds().getSize();
}
void PixelsMovement::setMaskColor(uint32_t mask_color)
void PixelsMovement::setMaskColor(color_t mask_color)
{
ContextWriter writer(m_reader);
Image* extraImage = m_document->getExtraCelImage();
ASSERT(extraImage != NULL);
m_maskColor = mask_color;
extraImage->setMaskColor(mask_color);
redrawExtraImage();
update_screen_for_document(m_document);
}
void PixelsMovement::redrawExtraImage()
{
gfx::Transformation::Corners corners;
m_currentData.transformBox(corners);
// Transform the extra-cel which is the chunk of pixels that the user is moving.
Image* extraImage = m_document->getExtraCelImage();
clear_image(extraImage, extraImage->getMaskColor());
drawParallelogram(extraImage, m_originalImage, corners, gfx::Point(0, 0));
// Draw the transformed pixels in the extra-cel which is the chunk
// of pixels that the user is moving.
drawImage(m_document->getExtraCelImage(), gfx::Point(0, 0));
}
void PixelsMovement::redrawCurrentMask()
@ -607,6 +599,18 @@ void PixelsMovement::redrawCurrentMask()
m_currentMask->unfreeze();
}
void PixelsMovement::drawImage(raster::Image* dst, const gfx::Point& pt)
{
gfx::Transformation::Corners corners;
m_currentData.transformBox(corners);
dst->setMaskColor(m_sprite->getTransparentColor());
clear_image(dst, dst->getMaskColor());
m_originalImage->setMaskColor(m_maskColor);
drawParallelogram(dst, m_originalImage, corners, pt);
}
void PixelsMovement::drawParallelogram(raster::Image* dst, raster::Image* src,
const gfx::Transformation::Corners& corners,
const gfx::Point& leftTop)

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -88,7 +88,7 @@ namespace app {
gfx::Rect getImageBounds();
gfx::Size getInitialImageSize() const;
void setMaskColor(uint32_t mask_color);
void setMaskColor(color_t mask_color);
// Flips the image and mask in the given direction in "flipType".
// Flip Horizontally/Vertically commands are replaced calling this
@ -104,6 +104,7 @@ namespace app {
private:
void redrawExtraImage();
void redrawCurrentMask();
void drawImage(raster::Image* dst, const gfx::Point& pt);
void drawParallelogram(raster::Image* dst, raster::Image* src,
const gfx::Transformation::Corners& corners,
const gfx::Point& leftTop);
@ -124,6 +125,7 @@ namespace app {
gfx::Transformation m_currentData;
Mask* m_initialMask;
Mask* m_currentMask;
color_t m_maskColor;
};
inline PixelsMovement::MoveModifier& operator|=(PixelsMovement::MoveModifier& a,

View File

@ -62,7 +62,8 @@ Image* NewImageFromMask(const DocumentLocation& location)
return NULL;
// Clear the new image
clear_image(dst, 0);
dst->setMaskColor(src->getMaskColor());
clear_image(dst, dst->getMaskColor());
// Copy the masked zones
const LockImageBits<BitmapTraits> maskBits(srcMaskBitmap, gfx::Rect(0, 0, srcBounds.w, srcBounds.h));

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -77,7 +77,8 @@ Image* Image::create(PixelFormat format, int width, int height,
Image* Image::createCopy(const Image* image, const ImageBufferPtr& buffer)
{
ASSERT(image);
return crop_image(image, 0, 0, image->getWidth(), image->getHeight(), 0, buffer);
return crop_image(image, 0, 0, image->getWidth(), image->getHeight(),
image->getMaskColor(), buffer);
}
} // namespace raster

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -489,7 +489,7 @@ static void bestfit_init()
}
}
int Palette::findBestfit(int r, int g, int b) const
int Palette::findBestfit(int r, int g, int b, int mask_index) const
{
#ifdef __GNUC__
register int bestfit asm("%eax");
@ -512,7 +512,7 @@ int Palette::findBestfit(int r, int g, int b) const
g >>= 3;
b >>= 3;
i = 1;
i = 0;
while (i < size()) {
color_t rgb = m_colors[i];
@ -521,7 +521,7 @@ int Palette::findBestfit(int r, int g, int b) const
coldiff += (col_diff + 128) [ ((rgba_getr(rgb)>>3) - r) & 0x7F ];
if (coldiff < lowest) {
coldiff += (col_diff + 256) [ ((rgba_getb(rgb)>>3) - b) & 0x7F ];
if (coldiff < lowest) {
if (coldiff < lowest && i != mask_index) {
bestfit = i;
if (coldiff == 0)
return bestfit;

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -101,7 +101,7 @@ namespace raster {
static Palette* load(const char *filename);
bool save(const char *filename) const;
int findBestfit(int r, int g, int b) const;
int findBestfit(int r, int g, int b, int mask_index = 0) const;
private:
FrameNumber m_frame;

View File

@ -694,7 +694,7 @@ static void ase_parallelogram_map_standard(Image *bmp, Image *sprite,
break;
case IMAGE_INDEXED: {
IndexedDelegate delegate(bmp->getMaskColor());
IndexedDelegate delegate(sprite->getMaskColor());
ase_parallelogram_map<IndexedTraits, IndexedDelegate>(bmp, sprite, xs, ys, false, delegate);
break;
}

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -179,7 +179,7 @@ void image_rotsprite(Image* bmp, Image* spr,
base::UniquePtr<Image> tmp_copy(Image::create(spr->getPixelFormat(), spr->getWidth()*scale, spr->getHeight()*scale, buf2));
base::UniquePtr<Image> spr_copy(Image::create(spr->getPixelFormat(), spr->getWidth()*scale, spr->getHeight()*scale, buf3));
color_t maskColor = bmp->getMaskColor();
color_t maskColor = spr->getMaskColor();
bmp_copy->setMaskColor(maskColor);
tmp_copy->setMaskColor(maskColor);

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -132,7 +132,7 @@ bool Sprite::needAlpha() const
return false;
}
void Sprite::setTransparentColor(uint32_t color)
void Sprite::setTransparentColor(color_t color)
{
m_transparentColor = color;
}

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@
#pragma once
#include "base/disable_copying.h"
#include "raster/color.h"
#include "raster/frame_number.h"
#include "raster/layer_index.h"
#include "raster/object.h"
@ -69,8 +70,8 @@ namespace raster {
// alpha channel in the render.
bool needAlpha() const;
uint32_t getTransparentColor() const { return m_transparentColor; }
void setTransparentColor(uint32_t color);
color_t getTransparentColor() const { return m_transparentColor; }
void setTransparentColor(color_t color);
int getMemSize() const;
@ -153,7 +154,7 @@ namespace raster {
RgbMap* m_rgbMap;
// Transparent color used in indexed images
uint32_t m_transparentColor;
color_t m_transparentColor;
// Disable default constructor and copying
Sprite();