mirror of https://github.com/aseprite/aseprite.git
Add opaque/transparent mode in context bar for transformations (fix #546)
With this change now we add a "mask" image/parameter in rotation functions. In this way we can identify which specific pixels are inside the original mask/selection, and in opaque mode we can include/scale/rotate all those pixels inside the mask, whatever value they are, even if they are the mask color. Fixes #730
This commit is contained in:
parent
ba4c34a70f
commit
35229e99a6
|
|
@ -110,6 +110,7 @@
|
|||
</section>
|
||||
<section id="selection">
|
||||
<option id="mode" type="app::tools::SelectionMode" default="app::tools::SelectionMode::DEFAULT" />
|
||||
<option id="opaque" type="bool" default="false" />
|
||||
<option id="transparent_color" type="app::Color" />
|
||||
<option id="rotation_algorithm" type="app::tools::RotationAlgorithm" default="app::tools::RotationAlgorithm::DEFAULT" />
|
||||
</section>
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
|
@ -375,6 +375,8 @@
|
|||
<part id="ink_default" x="144" y="144" w="16" h="16" />
|
||||
<part id="ink_composite" x="160" y="144" w="16" h="16" />
|
||||
<part id="ink_lock_alpha" x="176" y="144" w="16" h="16" />
|
||||
<part id="selection_opaque" x="208" y="176" w="16" h="10" />
|
||||
<part id="selection_masked" x="224" y="176" w="16" h="10" />
|
||||
</parts>
|
||||
|
||||
<stylesheet>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@ private:
|
|||
// Stretch the 'image'
|
||||
m_thumbnail.reset(Image::create(image->pixelFormat(), thumb_w, thumb_h));
|
||||
clear_image(m_thumbnail, 0);
|
||||
algorithm::scale_image(m_thumbnail, image, 0, 0, thumb_w, thumb_h);
|
||||
algorithm::scale_image(m_thumbnail, image,
|
||||
0, 0, thumb_w, thumb_h,
|
||||
0, 0, image->width(), image->height());
|
||||
}
|
||||
|
||||
// Close file
|
||||
|
|
|
|||
|
|
@ -477,18 +477,72 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
class ContextBar::TransparentColorField : public ColorButton
|
||||
{
|
||||
class ContextBar::TransparentColorField : public HBox {
|
||||
public:
|
||||
TransparentColorField() : ColorButton(app::Color::fromMask(), IMAGE_RGB) {
|
||||
Change.connect(Bind<void>(&TransparentColorField::onChange, this));
|
||||
TransparentColorField(ContextBar* owner)
|
||||
: m_icon(1)
|
||||
, m_maskColor(app::Color::fromMask(), IMAGE_RGB)
|
||||
, m_owner(owner) {
|
||||
addChild(&m_icon);
|
||||
addChild(&m_maskColor);
|
||||
|
||||
m_icon.addItem(static_cast<SkinTheme*>(getTheme())->get_part(PART_SELECTION_OPAQUE));
|
||||
gfx::Size sz = m_icon.getItem(0)->getPreferredSize();
|
||||
sz.w += 2*guiscale();
|
||||
m_icon.getItem(0)->setMinSize(sz);
|
||||
setOpaque(Preferences::instance().selection.opaque());
|
||||
|
||||
m_icon.ItemChange.connect(Bind<void>(&TransparentColorField::onPopup, this));
|
||||
m_maskColor.Change.connect(Bind<void>(&TransparentColorField::onChangeColor, this));
|
||||
}
|
||||
|
||||
protected:
|
||||
void onChange() {
|
||||
Preferences::instance().selection.transparentColor(getColor());
|
||||
private:
|
||||
|
||||
void onPopup() {
|
||||
gfx::Rect bounds = getBounds();
|
||||
|
||||
Menu menu;
|
||||
MenuItem
|
||||
opaque("Opaque"),
|
||||
masked("Transparent");
|
||||
menu.addChild(&opaque);
|
||||
menu.addChild(&masked);
|
||||
|
||||
if (Preferences::instance().selection.opaque())
|
||||
opaque.setSelected(true);
|
||||
else
|
||||
masked.setSelected(true);
|
||||
|
||||
opaque.Click.connect(Bind<void>(&TransparentColorField::setOpaque, this, true));
|
||||
masked.Click.connect(Bind<void>(&TransparentColorField::setOpaque, this, false));
|
||||
|
||||
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
|
||||
}
|
||||
|
||||
void onChangeColor() {
|
||||
Preferences::instance().selection.transparentColor(
|
||||
m_maskColor.getColor());
|
||||
}
|
||||
|
||||
void setOpaque(bool opaque) {
|
||||
int part = (opaque ? PART_SELECTION_OPAQUE: PART_SELECTION_MASKED);
|
||||
m_icon.getItem(0)->setIcon(
|
||||
static_cast<SkinTheme*>(getTheme())->get_part(part));
|
||||
|
||||
m_maskColor.setVisible(!opaque);
|
||||
Preferences::instance().selection.opaque(opaque);
|
||||
if (!opaque) {
|
||||
Preferences::instance().selection.transparentColor(
|
||||
m_maskColor.getColor());
|
||||
}
|
||||
|
||||
if (m_owner)
|
||||
m_owner->layout();
|
||||
}
|
||||
|
||||
ButtonSet m_icon;
|
||||
ColorButton m_maskColor;
|
||||
ContextBar* m_owner;
|
||||
};
|
||||
|
||||
class ContextBar::RotAlgorithmField : public ComboBox
|
||||
|
|
@ -860,7 +914,7 @@ ContextBar::ContextBar()
|
|||
addChild(m_selectionOptionsBox = new HBox());
|
||||
m_selectionOptionsBox->addChild(m_dropPixels = new DropPixelsField());
|
||||
m_selectionOptionsBox->addChild(m_selectionMode = new SelectionModeField);
|
||||
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField);
|
||||
m_selectionOptionsBox->addChild(m_transparentColor = new TransparentColorField(this));
|
||||
m_selectionOptionsBox->addChild(m_rotAlgo = new RotAlgorithmField());
|
||||
|
||||
addChild(m_brushType = new BrushTypeField(this));
|
||||
|
|
|
|||
|
|
@ -1417,8 +1417,11 @@ void Editor::setZoomAndCenterInMouse(Zoom zoom,
|
|||
}
|
||||
}
|
||||
|
||||
void Editor::pasteImage(const Image* image, const gfx::Point& pos)
|
||||
void Editor::pasteImage(const Image* image, const Mask* mask)
|
||||
{
|
||||
ASSERT(image);
|
||||
ASSERT(mask);
|
||||
|
||||
// Change to a selection tool: it's necessary for PixelsMovement
|
||||
// which will use the extra cel for transformation preview, and is
|
||||
// not compatible with the drawing cursor preview which overwrite
|
||||
|
|
@ -1433,8 +1436,8 @@ void Editor::pasteImage(const Image* image, const gfx::Point& pos)
|
|||
Sprite* sprite = this->sprite();
|
||||
|
||||
// Check bounds where the image will be pasted.
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
int x = mask->bounds().x;
|
||||
int y = mask->bounds().y;
|
||||
{
|
||||
// Then we check if the image will be visible by the user.
|
||||
Rect visibleBounds = getVisibleSpriteBounds().shrink(4*ui::guiscale());
|
||||
|
|
@ -1450,12 +1453,12 @@ void Editor::pasteImage(const Image* image, const gfx::Point& pos)
|
|||
// pasted image.
|
||||
m_brushPreview.hide();
|
||||
|
||||
Mask mask2(*mask);
|
||||
mask2.setOrigin(x, y);
|
||||
|
||||
PixelsMovementPtr pixelsMovement(
|
||||
new PixelsMovement(UIContext::instance(),
|
||||
getSite(), image, gfx::Point(x, y), "Paste"));
|
||||
|
||||
// Select the pasted image so the user can move it and transform it.
|
||||
pixelsMovement->maskImage(image);
|
||||
getSite(), image, &mask2, "Paste"));
|
||||
|
||||
setState(EditorStatePtr(new MovingPixelsState(this, NULL, pixelsMovement, NoHandle)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ namespace app {
|
|||
void setZoomAndCenterInMouse(render::Zoom zoom,
|
||||
const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
|
||||
|
||||
void pasteImage(const Image* image, const gfx::Point& pos);
|
||||
void pasteImage(const Image* image, const Mask* mask);
|
||||
|
||||
void startSelectionTransformation(const gfx::Point& move);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMo
|
|||
}
|
||||
|
||||
// Setup mask color
|
||||
setTransparentColor(Preferences::instance().selection.transparentColor());
|
||||
onTransparentColorChange();
|
||||
|
||||
// Hook BeforeCommandExecution signal so we know if the user wants
|
||||
// to execute other command, so we can drop pixels.
|
||||
|
|
@ -79,6 +79,9 @@ MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMo
|
|||
context->BeforeCommandExecution.connect(&MovingPixelsState::onBeforeCommandExecution, this);
|
||||
|
||||
// Listen to any change to the transparent color from the ContextBar.
|
||||
m_opaqueConn =
|
||||
Preferences::instance().selection.opaque.AfterChange.connect(
|
||||
Bind<void>(&MovingPixelsState::onTransparentColorChange, this));
|
||||
m_transparentConn =
|
||||
Preferences::instance().selection.transparentColor.AfterChange.connect(
|
||||
Bind<void>(&MovingPixelsState::onTransparentColorChange, this));
|
||||
|
|
@ -371,11 +374,13 @@ bool MovingPixelsState::onKeyDown(Editor* editor, KeyMessage* msg)
|
|||
// Copy the floating image to the clipboard.
|
||||
{
|
||||
Document* document = editor->document();
|
||||
gfx::Point origin;
|
||||
base::UniquePtr<Image> floatingImage(m_pixelsMovement->getDraggedImageCopy(origin));
|
||||
base::UniquePtr<Image> floatingImage;
|
||||
base::UniquePtr<Mask> floatingMask;
|
||||
m_pixelsMovement->getDraggedImageCopy(floatingImage, floatingMask);
|
||||
|
||||
clipboard::copy_image(floatingImage.get(),
|
||||
document->sprite()->palette(editor->frame()),
|
||||
origin);
|
||||
floatingMask.get(),
|
||||
document->sprite()->palette(editor->frame()));
|
||||
}
|
||||
|
||||
// In case of "Cut" command.
|
||||
|
|
@ -489,8 +494,12 @@ void MovingPixelsState::onBeforeLayerChanged(Editor* editor)
|
|||
|
||||
void MovingPixelsState::onTransparentColorChange()
|
||||
{
|
||||
app::Color color = Preferences::instance().selection.transparentColor();
|
||||
setTransparentColor(color);
|
||||
bool opaque = Preferences::instance().selection.opaque();
|
||||
setTransparentColor(
|
||||
opaque,
|
||||
opaque ?
|
||||
app::Color::fromMask():
|
||||
Preferences::instance().selection.transparentColor());
|
||||
}
|
||||
|
||||
void MovingPixelsState::onDropPixels(ContextBarObserver::DropAction action)
|
||||
|
|
@ -514,7 +523,7 @@ void MovingPixelsState::onDropPixels(ContextBarObserver::DropAction action)
|
|||
}
|
||||
}
|
||||
|
||||
void MovingPixelsState::setTransparentColor(const app::Color& color)
|
||||
void MovingPixelsState::setTransparentColor(bool opaque, const app::Color& color)
|
||||
{
|
||||
ASSERT(m_pixelsMovement);
|
||||
|
||||
|
|
@ -522,7 +531,7 @@ void MovingPixelsState::setTransparentColor(const app::Color& color)
|
|||
ASSERT(layer);
|
||||
|
||||
m_pixelsMovement->setMaskColor(
|
||||
color_utils::color_for_target_mask(color, ColorTarget(layer)));
|
||||
opaque, color_utils::color_for_target_mask(color, ColorTarget(layer)));
|
||||
}
|
||||
|
||||
void MovingPixelsState::dropPixels()
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ namespace app {
|
|||
// ContextObserver
|
||||
void onBeforeCommandExecution(Command* command);
|
||||
|
||||
void setTransparentColor(const app::Color& color);
|
||||
void setTransparentColor(bool opaque, const app::Color& color);
|
||||
void dropPixels();
|
||||
|
||||
bool isActiveDocument() const;
|
||||
|
|
@ -78,6 +78,7 @@ namespace app {
|
|||
bool m_discarded;
|
||||
|
||||
ScopedConnection m_ctxConn;
|
||||
ScopedConnection m_opaqueConn;
|
||||
ScopedConnection m_transparentConn;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -47,9 +47,11 @@ static inline const base::Vector2d<double> point2Vector(const gfx::PointT<T>& pt
|
|||
return base::Vector2d<double>(pt.x, pt.y);
|
||||
}
|
||||
|
||||
PixelsMovement::PixelsMovement(Context* context,
|
||||
PixelsMovement::PixelsMovement(
|
||||
Context* context,
|
||||
Site site,
|
||||
const Image* moveThis, const gfx::Point& initialPos,
|
||||
const Image* moveThis,
|
||||
const Mask* mask,
|
||||
const char* operationName)
|
||||
: m_reader(context)
|
||||
, m_site(site)
|
||||
|
|
@ -64,22 +66,25 @@ PixelsMovement::PixelsMovement(Context* context,
|
|||
, m_originalImage(Image::createCopy(moveThis))
|
||||
, m_maskColor(m_sprite->transparentColor())
|
||||
{
|
||||
m_initialData = gfx::Transformation(gfx::Rect(initialPos, gfx::Size(moveThis->width(), moveThis->height())));
|
||||
m_initialData = gfx::Transformation(mask->bounds());
|
||||
m_currentData = m_initialData;
|
||||
|
||||
m_initialMask = new Mask(*mask);
|
||||
m_currentMask = new Mask(*mask);
|
||||
|
||||
m_rotAlgoConn =
|
||||
Preferences::instance().selection.rotationAlgorithm.AfterChange.connect(
|
||||
Bind<void>(&PixelsMovement::onRotationAlgorithmChange, this));
|
||||
|
||||
// The extra cel must be null, because if it's not null, it means
|
||||
// that someone else is using it (e.g. the editor brush preview),
|
||||
// and its owner could destroy our new "extra cel".
|
||||
ASSERT(!m_document->getExtraCel());
|
||||
|
||||
redrawExtraImage();
|
||||
|
||||
m_initialMask = new Mask(*m_document->mask());
|
||||
m_currentMask = new Mask(*m_document->mask());
|
||||
|
||||
m_rotAlgoConn =
|
||||
Preferences::instance().selection.rotationAlgorithm.AfterChange.connect(
|
||||
Bind<void>(&PixelsMovement::onRotationAlgorithmChange, this));
|
||||
redrawCurrentMask();
|
||||
updateDocumentMask();
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
PixelsMovement::~PixelsMovement()
|
||||
|
|
@ -167,16 +172,6 @@ void PixelsMovement::catchImageAgain(const gfx::Point& pos, HandleType handle)
|
|||
}
|
||||
}
|
||||
|
||||
void PixelsMovement::maskImage(const Image* image)
|
||||
{
|
||||
m_currentMask->replace(m_currentData.bounds());
|
||||
m_initialMask->copyFrom(m_currentMask);
|
||||
|
||||
updateDocumentMask();
|
||||
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
|
||||
{
|
||||
gfx::Transformation::Corners oldCorners;
|
||||
|
|
@ -412,29 +407,35 @@ void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier)
|
|||
}
|
||||
}
|
||||
|
||||
Image* PixelsMovement::getDraggedImageCopy(gfx::Point& origin)
|
||||
void PixelsMovement::getDraggedImageCopy(base::UniquePtr<Image>& outputImage,
|
||||
base::UniquePtr<Mask>& outputMask)
|
||||
{
|
||||
gfx::Transformation::Corners corners;
|
||||
m_currentData.transformBox(corners);
|
||||
gfx::Rect bounds = m_currentData.transformedBounds();
|
||||
base::UniquePtr<Image> image(Image::create(m_sprite->pixelFormat(), bounds.w, bounds.h));
|
||||
|
||||
gfx::Point leftTop(corners[0]);
|
||||
gfx::Point rightBottom(corners[0]);
|
||||
for (size_t i=1; i<corners.size(); ++i) {
|
||||
if (leftTop.x > corners[i].x) leftTop.x = (int)corners[i].x;
|
||||
if (leftTop.y > corners[i].y) leftTop.y = (int)corners[i].y;
|
||||
if (rightBottom.x < corners[i].x) rightBottom.x = (int)corners[i].x;
|
||||
if (rightBottom.y < corners[i].y) rightBottom.y = (int)corners[i].y;
|
||||
drawImage(image, bounds.getOrigin(), false);
|
||||
|
||||
// Draw mask without shrinking it, so the mask size is equal to the
|
||||
// "image" render.
|
||||
base::UniquePtr<Mask> mask(new Mask);
|
||||
drawMask(mask, false);
|
||||
|
||||
// Now we can shrink and crop the image.
|
||||
gfx::Rect oldMaskBounds = mask->bounds();
|
||||
mask->shrink();
|
||||
gfx::Rect newMaskBounds = mask->bounds();
|
||||
if (newMaskBounds != oldMaskBounds) {
|
||||
newMaskBounds.x -= oldMaskBounds.x;
|
||||
newMaskBounds.y -= oldMaskBounds.y;
|
||||
image.reset(crop_image(image,
|
||||
newMaskBounds.x,
|
||||
newMaskBounds.y,
|
||||
newMaskBounds.w,
|
||||
newMaskBounds.h, 0));
|
||||
}
|
||||
|
||||
int width = rightBottom.x - leftTop.x;
|
||||
int height = rightBottom.y - leftTop.y;
|
||||
base::UniquePtr<Image> image(Image::create(m_sprite->pixelFormat(), width, height));
|
||||
|
||||
drawImage(image, leftTop, false);
|
||||
|
||||
origin = leftTop;
|
||||
|
||||
return image.release();
|
||||
outputImage.reset(image.release());
|
||||
outputMask.reset(mask.release());
|
||||
}
|
||||
|
||||
void PixelsMovement::stampImage()
|
||||
|
|
@ -572,13 +573,13 @@ gfx::Size PixelsMovement::getInitialImageSize() const
|
|||
return m_initialData.bounds().getSize();
|
||||
}
|
||||
|
||||
void PixelsMovement::setMaskColor(color_t mask_color)
|
||||
void PixelsMovement::setMaskColor(bool opaque, color_t mask_color)
|
||||
{
|
||||
ContextWriter writer(m_reader, 5000);
|
||||
|
||||
m_opaque = opaque;
|
||||
m_maskColor = mask_color;
|
||||
|
||||
redrawExtraImage();
|
||||
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
|
|
@ -601,19 +602,7 @@ void PixelsMovement::redrawExtraImage()
|
|||
|
||||
void PixelsMovement::redrawCurrentMask()
|
||||
{
|
||||
gfx::Transformation::Corners corners;
|
||||
m_currentData.transformBox(corners);
|
||||
|
||||
// Transform mask
|
||||
|
||||
gfx::Rect bounds = m_currentData.transformedBounds();
|
||||
m_currentMask->replace(bounds);
|
||||
m_currentMask->freeze();
|
||||
clear_image(m_currentMask->bitmap(), 0);
|
||||
drawParallelogram(m_currentMask->bitmap(), m_initialMask->bitmap(),
|
||||
corners, bounds.getOrigin());
|
||||
|
||||
m_currentMask->unfreeze();
|
||||
drawMask(m_currentMask, true);
|
||||
}
|
||||
|
||||
void PixelsMovement::drawImage(doc::Image* dst, const gfx::Point& pt, bool renderOriginalLayer)
|
||||
|
|
@ -622,10 +611,9 @@ void PixelsMovement::drawImage(doc::Image* dst, const gfx::Point& pt, bool rende
|
|||
|
||||
gfx::Transformation::Corners corners;
|
||||
m_currentData.transformBox(corners);
|
||||
gfx::Rect bounds = corners.bounds();
|
||||
|
||||
dst->setMaskColor(m_sprite->transparentColor());
|
||||
|
||||
gfx::Rect bounds = m_currentData.transformedBounds();
|
||||
dst->clear(dst->maskColor());
|
||||
|
||||
if (renderOriginalLayer)
|
||||
|
|
@ -634,11 +622,38 @@ void PixelsMovement::drawImage(doc::Image* dst, const gfx::Point& pt, bool rende
|
|||
gfx::Clip(bounds.x-pt.x, bounds.y-pt.y, bounds),
|
||||
BlendMode::SRC);
|
||||
|
||||
m_originalImage->setMaskColor(m_maskColor);
|
||||
drawParallelogram(dst, m_originalImage, corners, pt);
|
||||
color_t maskColor = m_maskColor;
|
||||
if (m_opaque) {
|
||||
if (m_originalImage->pixelFormat() == IMAGE_INDEXED)
|
||||
maskColor = -1;
|
||||
else
|
||||
maskColor = 0;
|
||||
}
|
||||
m_originalImage->setMaskColor(maskColor);
|
||||
|
||||
drawParallelogram(dst, m_originalImage, m_initialMask, corners, pt);
|
||||
}
|
||||
|
||||
void PixelsMovement::drawParallelogram(doc::Image* dst, doc::Image* src,
|
||||
void PixelsMovement::drawMask(doc::Mask* mask, bool shrink)
|
||||
{
|
||||
gfx::Transformation::Corners corners;
|
||||
m_currentData.transformBox(corners);
|
||||
gfx::Rect bounds = corners.bounds();
|
||||
|
||||
mask->replace(bounds);
|
||||
if (shrink)
|
||||
mask->freeze();
|
||||
clear_image(mask->bitmap(), 0);
|
||||
drawParallelogram(mask->bitmap(),
|
||||
m_initialMask->bitmap(),
|
||||
nullptr,
|
||||
corners, bounds.getOrigin());
|
||||
if (shrink)
|
||||
mask->unfreeze();
|
||||
}
|
||||
|
||||
void PixelsMovement::drawParallelogram(
|
||||
doc::Image* dst, const doc::Image* src, const doc::Mask* mask,
|
||||
const gfx::Transformation::Corners& corners,
|
||||
const gfx::Point& leftTop)
|
||||
{
|
||||
|
|
@ -658,7 +673,8 @@ retry:; // In case that we don't have enough memory for RotSprite
|
|||
switch (rotAlgo) {
|
||||
|
||||
case tools::RotationAlgorithm::FAST:
|
||||
doc::algorithm::parallelogram(dst, src,
|
||||
doc::algorithm::parallelogram(
|
||||
dst, src, (mask ? mask->bitmap(): nullptr),
|
||||
int(corners.leftTop().x-leftTop.x),
|
||||
int(corners.leftTop().y-leftTop.y),
|
||||
int(corners.rightTop().x-leftTop.x),
|
||||
|
|
@ -671,7 +687,8 @@ retry:; // In case that we don't have enough memory for RotSprite
|
|||
|
||||
case tools::RotationAlgorithm::ROTSPRITE:
|
||||
try {
|
||||
doc::algorithm::rotsprite_image(dst, src,
|
||||
doc::algorithm::rotsprite_image(
|
||||
dst, src, (mask ? mask->bitmap(): nullptr),
|
||||
int(corners.leftTop().x-leftTop.x),
|
||||
int(corners.leftTop().y-leftTop.y),
|
||||
int(corners.rightTop().x-leftTop.x),
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Mask;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
|
|
@ -45,13 +46,11 @@ namespace app {
|
|||
LockAxisMovement = 16
|
||||
};
|
||||
|
||||
// The "moveThis" image specifies the chunk of pixels to be moved.
|
||||
// The "x" and "y" parameters specify the initial position of the image.
|
||||
PixelsMovement(Context* context,
|
||||
Site site,
|
||||
const Image* moveThis,
|
||||
const gfx::Point& initialPos,
|
||||
const char* operationName);
|
||||
Site site,
|
||||
const Image* moveThis,
|
||||
const Mask* mask,
|
||||
const char* operationName);
|
||||
~PixelsMovement();
|
||||
|
||||
void cutMask();
|
||||
|
|
@ -59,17 +58,14 @@ namespace app {
|
|||
void catchImage(const gfx::Point& pos, HandleType handle);
|
||||
void catchImageAgain(const gfx::Point& pos, HandleType handle);
|
||||
|
||||
// Creates a mask for the given image. Useful when the user paste a
|
||||
// image from the clipboard.
|
||||
void maskImage(const Image* image);
|
||||
|
||||
// Moves the image to the new position (relative to the start
|
||||
// position given in the ctor).
|
||||
void moveImage(const gfx::Point& pos, MoveModifier moveModifier);
|
||||
|
||||
// Returns a copy of the current image being dragged with the
|
||||
// current transformation.
|
||||
Image* getDraggedImageCopy(gfx::Point& origin);
|
||||
void getDraggedImageCopy(base::UniquePtr<Image>& outputImage,
|
||||
base::UniquePtr<Mask>& outputMask);
|
||||
|
||||
// Copies the image being dragged in the current position.
|
||||
void stampImage();
|
||||
|
|
@ -82,7 +78,7 @@ namespace app {
|
|||
gfx::Rect getImageBounds();
|
||||
gfx::Size getInitialImageSize() const;
|
||||
|
||||
void setMaskColor(color_t mask_color);
|
||||
void setMaskColor(bool opaque, color_t mask_color);
|
||||
|
||||
// Flips the image and mask in the given direction in "flipType".
|
||||
// Flip Horizontally/Vertically commands are replaced calling this
|
||||
|
|
@ -97,7 +93,8 @@ namespace app {
|
|||
void redrawExtraImage();
|
||||
void redrawCurrentMask();
|
||||
void drawImage(doc::Image* dst, const gfx::Point& pos, bool renderOriginalLayer);
|
||||
void drawParallelogram(doc::Image* dst, doc::Image* src,
|
||||
void drawMask(doc::Mask* dst, bool shrink);
|
||||
void drawParallelogram(doc::Image* dst, const doc::Image* src, const doc::Mask* mask,
|
||||
const gfx::Transformation::Corners& corners,
|
||||
const gfx::Point& leftTop);
|
||||
void updateDocumentMask();
|
||||
|
|
@ -118,6 +115,7 @@ namespace app {
|
|||
gfx::Transformation m_currentData;
|
||||
Mask* m_initialMask;
|
||||
Mask* m_currentMask;
|
||||
bool m_opaque;
|
||||
color_t m_maskColor;
|
||||
ScopedConnection m_rotAlgoConn;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -413,13 +413,13 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT
|
|||
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
|
||||
Document* document = editor->document();
|
||||
base::UniquePtr<Image> tmpImage(new_image_from_mask(editor->getSite()));
|
||||
gfx::Point origin = document->mask()->bounds().getOrigin();
|
||||
|
||||
PixelsMovementPtr pixelsMovement(
|
||||
new PixelsMovement(UIContext::instance(),
|
||||
editor->getSite(),
|
||||
tmpImage, origin,
|
||||
"Transformation"));
|
||||
editor->getSite(),
|
||||
tmpImage,
|
||||
document->mask(),
|
||||
"Transformation"));
|
||||
|
||||
// If the Ctrl key is pressed start dragging a copy of the selection
|
||||
if (customization && customization->isCopySelectionKeyPressed())
|
||||
|
|
|
|||
|
|
@ -180,6 +180,9 @@ namespace app {
|
|||
PART_INK_COMPOSITE,
|
||||
PART_INK_LOCK_ALPHA,
|
||||
|
||||
PART_SELECTION_OPAQUE,
|
||||
PART_SELECTION_MASKED,
|
||||
|
||||
PARTS
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@ SkinTheme::SkinTheme()
|
|||
sheet_mapping["ink_default"] = PART_INK_DEFAULT;
|
||||
sheet_mapping["ink_composite"] = PART_INK_COMPOSITE;
|
||||
sheet_mapping["ink_lock_alpha"] = PART_INK_LOCK_ALPHA;
|
||||
sheet_mapping["selection_opaque"] = PART_SELECTION_OPAQUE;
|
||||
sheet_mapping["selection_masked"] = PART_SELECTION_MASKED;
|
||||
}
|
||||
|
||||
SkinTheme::~SkinTheme()
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
#include "render/quantization.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define USE_NATIVE_WIN32_CLIPBOARD
|
||||
// TODO Re-enable native clipboard with custom format to save image+mask when possible
|
||||
//#define USE_NATIVE_WIN32_CLIPBOARD
|
||||
#endif
|
||||
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
|
|
@ -80,24 +81,21 @@ namespace {
|
|||
|
||||
using namespace doc;
|
||||
|
||||
static void set_clipboard_image(Image* image, Palette* palette, bool set_system_clipboard);
|
||||
static bool copy_from_document(const Site& site);
|
||||
|
||||
static bool first_time = true;
|
||||
|
||||
static base::SharedPtr<Palette> clipboard_palette;
|
||||
static PalettePicks clipboard_picks;
|
||||
static ImageRef clipboard_image;
|
||||
static base::SharedPtr<Mask> clipboard_mask;
|
||||
static ClipboardRange clipboard_range;
|
||||
static gfx::Point clipboard_pos(0, 0);
|
||||
|
||||
static void on_exit_delete_clipboard()
|
||||
{
|
||||
clipboard_palette.reset();
|
||||
clipboard_image.reset();
|
||||
clipboard_mask.reset();
|
||||
}
|
||||
|
||||
static void set_clipboard_image(Image* image, Palette* palette, bool set_system_clipboard)
|
||||
static void set_clipboard_image(Image* image, Mask* mask, Palette* palette, bool set_system_clipboard)
|
||||
{
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
|
|
@ -107,11 +105,12 @@ static void set_clipboard_image(Image* image, Palette* palette, bool set_system_
|
|||
clipboard_palette.reset(palette);
|
||||
clipboard_picks.clear();
|
||||
clipboard_image.reset(image);
|
||||
clipboard_mask.reset(mask);
|
||||
|
||||
// copy to the Windows clipboard
|
||||
#ifdef USE_NATIVE_WIN32_CLIPBOARD
|
||||
if (set_system_clipboard)
|
||||
set_win32_clipboard_bitmap(image, palette);
|
||||
set_win32_clipboard_bitmap(image, mask, palette);
|
||||
#endif
|
||||
|
||||
clipboard_range.invalidate();
|
||||
|
|
@ -128,10 +127,12 @@ static bool copy_from_document(const Site& site)
|
|||
if (!image)
|
||||
return false;
|
||||
|
||||
clipboard_pos = document->mask()->bounds().getOrigin();
|
||||
|
||||
const Mask* mask = document->mask();
|
||||
const Palette* pal = document->sprite()->palette(site.frame());
|
||||
set_clipboard_image(image, pal ? new Palette(*pal): NULL, true);
|
||||
|
||||
set_clipboard_image(image,
|
||||
(mask ? new Mask(*mask): nullptr),
|
||||
(pal ? new Palette(*pal): nullptr), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ void clipboard::get_document_range_info(Document** document, DocumentRange* rang
|
|||
|
||||
void clipboard::clear_content()
|
||||
{
|
||||
set_clipboard_image(NULL, NULL, true);
|
||||
set_clipboard_image(nullptr, nullptr, nullptr, true);
|
||||
}
|
||||
|
||||
void clipboard::cut(ContextWriter& writer)
|
||||
|
|
@ -207,7 +208,7 @@ void clipboard::copy_range(const ContextReader& reader, const DocumentRange& ran
|
|||
|
||||
ContextWriter writer(reader);
|
||||
|
||||
set_clipboard_image(NULL, NULL, true);
|
||||
clear_content();
|
||||
clipboard_range.setRange(writer.document(), range);
|
||||
|
||||
// TODO Replace this with a signal, because here the timeline
|
||||
|
|
@ -216,12 +217,12 @@ void clipboard::copy_range(const ContextReader& reader, const DocumentRange& ran
|
|||
->getTimeline()->activateClipboardRange();
|
||||
}
|
||||
|
||||
void clipboard::copy_image(Image* image, Palette* pal, const gfx::Point& point)
|
||||
void clipboard::copy_image(const Image* image, const Mask* mask, const Palette* pal)
|
||||
{
|
||||
set_clipboard_image(Image::createCopy(image),
|
||||
pal ? new Palette(*pal): NULL, true);
|
||||
|
||||
clipboard_pos = point;
|
||||
set_clipboard_image(
|
||||
Image::createCopy(image),
|
||||
(mask ? new Mask(*mask): nullptr),
|
||||
(pal ? new Palette(*pal): nullptr), true);
|
||||
}
|
||||
|
||||
void clipboard::copy_palette(const Palette* palette, const doc::PalettePicks& picks)
|
||||
|
|
@ -229,7 +230,9 @@ void clipboard::copy_palette(const Palette* palette, const doc::PalettePicks& pi
|
|||
if (!picks.picks())
|
||||
return; // Do nothing case
|
||||
|
||||
set_clipboard_image(nullptr, new Palette(*palette), true);
|
||||
set_clipboard_image(nullptr,
|
||||
nullptr,
|
||||
new Palette(*palette), true);
|
||||
clipboard_picks = picks;
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +259,8 @@ void clipboard::paste()
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!clipboard_image)
|
||||
if (!clipboard_image ||
|
||||
!clipboard_mask)
|
||||
return;
|
||||
|
||||
Palette* dst_palette = dstSpr->palette(editor->frame());
|
||||
|
|
@ -282,7 +286,8 @@ void clipboard::paste()
|
|||
}
|
||||
|
||||
// Change to MovingPixelsState
|
||||
editor->pasteImage(src_image.get(), clipboard_pos);
|
||||
editor->pasteImage(src_image.get(),
|
||||
clipboard_mask.get());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Mask;
|
||||
class Palette;
|
||||
class PalettePicks;
|
||||
}
|
||||
|
|
@ -43,7 +44,7 @@ namespace app {
|
|||
void cut(ContextWriter& context);
|
||||
void copy(const ContextReader& context);
|
||||
void copy_range(const ContextReader& context, const DocumentRange& range);
|
||||
void copy_image(Image* image, Palette* palette, const gfx::Point& point);
|
||||
void copy_image(const Image* image, const Mask* mask, const Palette* palette);
|
||||
void copy_palette(const Palette* palette, const PalettePicks& picks);
|
||||
void paste();
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ static bool win32_clipboard_contains_bitmap()
|
|||
* Changes the Windows clipboard content to the specified image. The
|
||||
* palette is optional and only used if the image is IMAGE_INDEXED type.
|
||||
*/
|
||||
static void set_win32_clipboard_bitmap(Image* image, Palette* palette)
|
||||
static void set_win32_clipboard_bitmap(const Image* image, const Mask* mask, Palette* palette)
|
||||
{
|
||||
HWND hwnd = static_cast<HWND>(she::instance()->defaultDisplay()->nativeHandle());
|
||||
if (!win32_open_clipboard(hwnd))
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@
|
|||
#endif
|
||||
|
||||
#include "base/pi.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
#include "doc/blend_funcs.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/primitives_fast.h"
|
||||
#include "fixmath/fixmath.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
|
@ -25,34 +26,34 @@ namespace algorithm {
|
|||
|
||||
using namespace fixmath;
|
||||
|
||||
static void ase_parallelogram_map_standard(Image *bmp, Image *sprite, fixed xs[4], fixed ys[4]);
|
||||
static void ase_rotate_scale_flip_coordinates(fixed w, fixed h,
|
||||
fixed x, fixed y,
|
||||
fixed cx, fixed cy,
|
||||
fixed angle,
|
||||
fixed scale_x, fixed scale_y,
|
||||
int h_flip, int v_flip,
|
||||
fixed xs[4], fixed ys[4]);
|
||||
static void ase_parallelogram_map_standard(
|
||||
Image* bmp, const Image* sprite, const Image* mask,
|
||||
fixed xs[4], fixed ys[4]);
|
||||
|
||||
static void ase_rotate_scale_flip_coordinates(
|
||||
fixed w, fixed h,
|
||||
fixed x, fixed y,
|
||||
fixed cx, fixed cy,
|
||||
fixed angle,
|
||||
fixed scale_x, fixed scale_y,
|
||||
int h_flip, int v_flip,
|
||||
fixed xs[4], fixed ys[4]);
|
||||
|
||||
template<typename ImageTraits, typename BlendFunc>
|
||||
static void image_scale_tpl(Image* dst, const Image* src, int x, int y, int w, int h, BlendFunc blend)
|
||||
static void image_scale_tpl(
|
||||
Image* dst, const Image* src,
|
||||
int dst_x, int dst_y, int dst_w, int dst_h,
|
||||
int src_x, int src_y, int src_w, int src_h, BlendFunc blend)
|
||||
{
|
||||
int src_w = src->width();
|
||||
int src_h = src->height();
|
||||
|
||||
gfx::Clip clip(x, y, 0, 0, w, h);
|
||||
if (!clip.clip(dst->width(), dst->height(), src->width(), src->height()))
|
||||
return;
|
||||
|
||||
LockImageBits<ImageTraits> dst_bits(dst, clip.dstBounds());
|
||||
LockImageBits<ImageTraits> dst_bits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
|
||||
typename LockImageBits<ImageTraits>::iterator dst_it = dst_bits.begin();
|
||||
|
||||
for (int v=0; v<clip.size.h; ++v) {
|
||||
for (int u=0; u<clip.size.w; ++u) {
|
||||
for (int v=0; v<dst_h; ++v) {
|
||||
for (int u=0; u<dst_w; ++u) {
|
||||
color_t src_color =
|
||||
get_pixel_fast<ImageTraits>(src,
|
||||
src_w*(clip.src.x+u)/w,
|
||||
src_h*(clip.src.y+v)/h);
|
||||
src_w*(src_x+u)/dst_w,
|
||||
src_h*(src_y+v)/dst_h);
|
||||
*dst_it = blend(*dst_it, src_color);
|
||||
++dst_it;
|
||||
}
|
||||
|
|
@ -81,33 +82,52 @@ private:
|
|||
color_t m_mask;
|
||||
};
|
||||
|
||||
void scale_image(Image *dst, Image *src, int x, int y, int w, int h)
|
||||
void scale_image(Image* dst, const Image* src,
|
||||
int dst_x, int dst_y, int dst_w, int dst_h,
|
||||
int src_x, int src_y, int src_w, int src_h)
|
||||
{
|
||||
if (w == src->width() && src->height() == h)
|
||||
dst->copy(src, gfx::Clip(x, y, 0, 0, w, h));
|
||||
else {
|
||||
switch (dst->pixelFormat()) {
|
||||
gfx::Clip clip(dst_x, dst_y, src_x, src_y, dst_w, dst_h);
|
||||
if (src_w == dst_w && src_h == dst_h) {
|
||||
dst->copy(src, clip);
|
||||
return;
|
||||
}
|
||||
|
||||
case IMAGE_RGB:
|
||||
image_scale_tpl<RgbTraits>(dst, src, x, y, w, h, rgba_blender);
|
||||
break;
|
||||
if (!clip.clip(dst->width(), dst->height(), src->width(), src->height()))
|
||||
return;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
image_scale_tpl<GrayscaleTraits>(dst, src, x, y, w, h, grayscale_blender);
|
||||
break;
|
||||
switch (dst->pixelFormat()) {
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
image_scale_tpl<IndexedTraits>(dst, src, x, y, w, h, if_blender(src->maskColor()));
|
||||
break;
|
||||
case IMAGE_RGB:
|
||||
image_scale_tpl<RgbTraits>(
|
||||
dst, src,
|
||||
dst_x, dst_y, dst_w, dst_h,
|
||||
src_x, src_y, src_w, src_h, rgba_blender);
|
||||
break;
|
||||
|
||||
case IMAGE_BITMAP:
|
||||
image_scale_tpl<BitmapTraits>(dst, src, x, y, w, h, if_blender(0));
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE:
|
||||
image_scale_tpl<GrayscaleTraits>(
|
||||
dst, src,
|
||||
dst_x, dst_y, dst_w, dst_h,
|
||||
src_x, src_y, src_w, src_h, grayscale_blender);
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
image_scale_tpl<IndexedTraits>(
|
||||
dst, src,
|
||||
dst_x, dst_y, dst_w, dst_h,
|
||||
src_x, src_y, src_w, src_h, if_blender(src->maskColor()));
|
||||
break;
|
||||
|
||||
case IMAGE_BITMAP:
|
||||
image_scale_tpl<BitmapTraits>(
|
||||
dst, src,
|
||||
dst_x, dst_y, dst_w, dst_h,
|
||||
src_x, src_y, src_w, src_h, if_blender(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rotate_image(Image *dst, Image *src, int x, int y, int w, int h,
|
||||
void rotate_image(Image* dst, const Image* src, int x, int y, int w, int h,
|
||||
int cx, int cy, double angle)
|
||||
{
|
||||
fixed xs[4], ys[4];
|
||||
|
|
@ -120,35 +140,38 @@ void rotate_image(Image *dst, Image *src, int x, int y, int w, int h,
|
|||
fixdiv(itofix(h), itofix(src->height())),
|
||||
false, false, xs, ys);
|
||||
|
||||
ase_parallelogram_map_standard (dst, src, xs, ys);
|
||||
ase_parallelogram_map_standard(dst, src, nullptr, xs, ys);
|
||||
}
|
||||
|
||||
/* 1-----2
|
||||
| |
|
||||
4-----3
|
||||
*/
|
||||
void parallelogram(Image *bmp, Image *sprite,
|
||||
void parallelogram(Image* bmp, const Image* sprite, const Image* mask,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int x4, int y4)
|
||||
{
|
||||
fixed xs[4], ys[4];
|
||||
|
||||
xs[0] = itofix (x1);
|
||||
ys[0] = itofix (y1);
|
||||
xs[1] = itofix (x2);
|
||||
ys[1] = itofix (y2);
|
||||
xs[2] = itofix (x3);
|
||||
ys[2] = itofix (y3);
|
||||
xs[3] = itofix (x4);
|
||||
ys[3] = itofix (y4);
|
||||
xs[0] = itofix(x1);
|
||||
ys[0] = itofix(y1);
|
||||
xs[1] = itofix(x2);
|
||||
ys[1] = itofix(y2);
|
||||
xs[2] = itofix(x3);
|
||||
ys[2] = itofix(y3);
|
||||
xs[3] = itofix(x4);
|
||||
ys[3] = itofix(y4);
|
||||
|
||||
ase_parallelogram_map_standard (bmp, sprite, xs, ys);
|
||||
ase_parallelogram_map_standard(bmp, sprite, mask, xs, ys);
|
||||
}
|
||||
|
||||
// Scanline drawers.
|
||||
|
||||
template<class Traits, class Delegate>
|
||||
static void draw_scanline(Image *bmp, Image *spr,
|
||||
static void draw_scanline(
|
||||
Image* bmp,
|
||||
const Image* spr,
|
||||
const Image* mask,
|
||||
fixed l_bmp_x, int bmp_y_i,
|
||||
fixed r_bmp_x,
|
||||
fixed l_spr_x, fixed l_spr_y,
|
||||
|
|
@ -160,8 +183,16 @@ static void draw_scanline(Image *bmp, Image *spr,
|
|||
|
||||
delegate.lockBits(bmp, gfx::Rect(l_bmp_x, bmp_y_i, r_bmp_x - l_bmp_x + 1, 1));
|
||||
|
||||
gfx::Rect maskBounds = (mask ? mask->bounds(): spr->bounds());
|
||||
|
||||
for (int x=(int)l_bmp_x; x<=(int)r_bmp_x; ++x) {
|
||||
delegate.feedLine(spr, l_spr_x>>16, l_spr_y>>16);
|
||||
int u = l_spr_x>>16;
|
||||
int v = l_spr_y>>16;
|
||||
|
||||
if (!mask ||
|
||||
(maskBounds.contains(u, v) && get_pixel_fast<BitmapTraits>(mask, u, v)))
|
||||
delegate.putPixel(spr, u, v);
|
||||
delegate.nextPixel();
|
||||
|
||||
l_spr_x += spr_dx;
|
||||
l_spr_y += spr_dy;
|
||||
|
|
@ -183,6 +214,11 @@ public:
|
|||
m_bits.unlock();
|
||||
}
|
||||
|
||||
void nextPixel() {
|
||||
ASSERT(m_it != m_end);
|
||||
++m_it;
|
||||
}
|
||||
|
||||
private:
|
||||
ImageBits<Traits> m_bits;
|
||||
|
||||
|
|
@ -196,14 +232,12 @@ public:
|
|||
m_mask_color = mask_color;
|
||||
}
|
||||
|
||||
void feedLine(Image* spr, int spr_x, int spr_y) {
|
||||
void putPixel(const Image* spr, int spr_x, int spr_y) {
|
||||
ASSERT(m_it != m_end);
|
||||
|
||||
int c = spr->getPixel(spr_x, spr_y);
|
||||
int c = get_pixel_fast<RgbTraits>(spr, spr_x, spr_y);
|
||||
if ((rgba_geta(m_mask_color) == 0) || ((c & rgba_rgb_mask) != (m_mask_color & rgba_rgb_mask)))
|
||||
*m_it = rgba_blender_normal(*m_it, c, 255);
|
||||
|
||||
++m_it;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -216,14 +250,12 @@ public:
|
|||
m_mask_color = mask_color;
|
||||
}
|
||||
|
||||
void feedLine(Image* spr, int spr_x, int spr_y) {
|
||||
void putPixel(const Image* spr, int spr_x, int spr_y) {
|
||||
ASSERT(m_it != m_end);
|
||||
|
||||
int c = spr->getPixel(spr_x, spr_y);
|
||||
int c = get_pixel_fast<GrayscaleTraits>(spr, spr_x, spr_y);
|
||||
if ((graya_geta(m_mask_color) == 0) || ((c & graya_v_mask) != (m_mask_color & graya_v_mask)))
|
||||
*m_it = graya_blender_normal(*m_it, c, 255);
|
||||
|
||||
++m_it;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -236,13 +268,12 @@ public:
|
|||
m_mask_color(mask_color) {
|
||||
}
|
||||
|
||||
void feedLine(Image* spr, int spr_x, int spr_y) {
|
||||
void putPixel(const Image* spr, int spr_x, int spr_y) {
|
||||
ASSERT(m_it != m_end);
|
||||
|
||||
color_t c = spr->getPixel(spr_x, spr_y);
|
||||
color_t c = get_pixel_fast<IndexedTraits>(spr, spr_x, spr_y);
|
||||
if (c != m_mask_color)
|
||||
*m_it = c;
|
||||
++m_it;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -251,13 +282,12 @@ private:
|
|||
|
||||
class BitmapDelegate : public GenericDelegate<BitmapTraits> {
|
||||
public:
|
||||
void feedLine(Image* spr, int spr_x, int spr_y) {
|
||||
void putPixel(const Image* spr, int spr_x, int spr_y) {
|
||||
ASSERT(m_it != m_end);
|
||||
|
||||
int c = spr->getPixel(spr_x, spr_y);
|
||||
int c = get_pixel_fast<BitmapTraits>(spr, spr_x, spr_y);
|
||||
if (c != 0) // TODO
|
||||
*m_it = c;
|
||||
++m_it;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -287,7 +317,8 @@ public:
|
|||
*/
|
||||
template<class Traits, class Delegate>
|
||||
static void ase_parallelogram_map(
|
||||
Image *bmp, Image *spr, fixed xs[4], fixed ys[4],
|
||||
Image* bmp, const Image* spr, const Image* mask,
|
||||
fixed xs[4], fixed ys[4],
|
||||
int sub_pixel_accuracy, Delegate delegate)
|
||||
{
|
||||
/* Index in xs[] and ys[] to topmost point. */
|
||||
|
|
@ -659,7 +690,7 @@ static void ase_parallelogram_map(
|
|||
}
|
||||
}
|
||||
}
|
||||
draw_scanline<Traits, Delegate>(bmp, spr,
|
||||
draw_scanline<Traits, Delegate>(bmp, spr, mask,
|
||||
l_bmp_x_rounded, bmp_y_i, r_bmp_x_rounded,
|
||||
l_spr_x_rounded, l_spr_y_rounded,
|
||||
spr_dx, spr_dy, delegate);
|
||||
|
|
@ -690,32 +721,33 @@ static void ase_parallelogram_map(
|
|||
* _parallelogram_map() function since then you can bypass it and define
|
||||
* your own scanline drawer, eg. for anti-aliased rotations.
|
||||
*/
|
||||
static void ase_parallelogram_map_standard(Image *bmp, Image *sprite,
|
||||
fixed xs[4], fixed ys[4])
|
||||
static void ase_parallelogram_map_standard(
|
||||
Image* bmp, const Image* sprite, const Image* mask,
|
||||
fixed xs[4], fixed ys[4])
|
||||
{
|
||||
switch (bmp->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB: {
|
||||
RgbDelegate delegate(sprite->maskColor());
|
||||
ase_parallelogram_map<RgbTraits, RgbDelegate>(bmp, sprite, xs, ys, false, delegate);
|
||||
ase_parallelogram_map<RgbTraits, RgbDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_GRAYSCALE: {
|
||||
GrayscaleDelegate delegate(sprite->maskColor());
|
||||
ase_parallelogram_map<GrayscaleTraits, GrayscaleDelegate>(bmp, sprite, xs, ys, false, delegate);
|
||||
ase_parallelogram_map<GrayscaleTraits, GrayscaleDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_INDEXED: {
|
||||
IndexedDelegate delegate(sprite->maskColor());
|
||||
ase_parallelogram_map<IndexedTraits, IndexedDelegate>(bmp, sprite, xs, ys, false, delegate);
|
||||
ase_parallelogram_map<IndexedTraits, IndexedDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_BITMAP: {
|
||||
BitmapDelegate delegate;
|
||||
ase_parallelogram_map<BitmapTraits, BitmapDelegate>(bmp, sprite, xs, ys, false, delegate);
|
||||
ase_parallelogram_map<BitmapTraits, BitmapDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
|
@ -13,14 +13,15 @@ namespace doc {
|
|||
|
||||
namespace algorithm {
|
||||
|
||||
void scale_image(Image* dst, Image* src,
|
||||
int x, int y, int w, int h);
|
||||
void scale_image(Image* dst, const Image* src,
|
||||
int dst_x, int dst_y, int dst_w, int dst_h,
|
||||
int src_x, int src_y, int src_w, int src_h);
|
||||
|
||||
void rotate_image(Image* dst, Image* src,
|
||||
void rotate_image(Image* dst, const Image* src,
|
||||
int x, int y, int w, int h,
|
||||
int cx, int cy, double angle);
|
||||
|
||||
void parallelogram(Image* bmp, Image* sprite,
|
||||
void parallelogram(Image* dst, const Image* src, const Image* mask,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int x4, int y4);
|
||||
|
||||
|
|
|
|||
|
|
@ -161,15 +161,15 @@ static void image_scale2x(Image* dst, const Image* src, int src_w, int src_h)
|
|||
}
|
||||
}
|
||||
|
||||
void rotsprite_image(Image* bmp, Image* spr,
|
||||
void rotsprite_image(Image* bmp, const Image* spr, const Image* mask,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int x4, int y4)
|
||||
{
|
||||
static ImageBufferPtr buf1, buf2, buf3; // TODO non-thread safe
|
||||
static ImageBufferPtr buf[3]; // TODO non-thread safe
|
||||
|
||||
if (!buf1) buf1.reset(new ImageBuffer(1));
|
||||
if (!buf2) buf2.reset(new ImageBuffer(1));
|
||||
if (!buf3) buf3.reset(new ImageBuffer(1));
|
||||
for (int i=0; i<3; ++i)
|
||||
if (!buf[i])
|
||||
buf[i].reset(new ImageBuffer(1));
|
||||
|
||||
int xmin = MIN(x1, MIN(x2, MIN(x3, x4)));
|
||||
int xmax = MAX(x1, MAX(x2, MAX(x3, x4)));
|
||||
|
|
@ -179,9 +179,10 @@ void rotsprite_image(Image* bmp, Image* spr,
|
|||
int rot_height = ymax - ymin + 1;
|
||||
|
||||
int scale = 8;
|
||||
base::UniquePtr<Image> bmp_copy(Image::create(bmp->pixelFormat(), rot_width*scale, rot_height*scale, buf1));
|
||||
base::UniquePtr<Image> tmp_copy(Image::create(spr->pixelFormat(), spr->width()*scale, spr->height()*scale, buf2));
|
||||
base::UniquePtr<Image> spr_copy(Image::create(spr->pixelFormat(), spr->width()*scale, spr->height()*scale, buf3));
|
||||
base::UniquePtr<Image> bmp_copy(Image::create(bmp->pixelFormat(), rot_width*scale, rot_height*scale, buf[0]));
|
||||
base::UniquePtr<Image> tmp_copy(Image::create(spr->pixelFormat(), spr->width()*scale, spr->height()*scale, buf[1]));
|
||||
base::UniquePtr<Image> spr_copy(Image::create(spr->pixelFormat(), spr->width()*scale, spr->height()*scale, buf[2]));
|
||||
base::UniquePtr<Image> msk_copy;
|
||||
|
||||
color_t maskColor = spr->maskColor();
|
||||
|
||||
|
|
@ -189,23 +190,38 @@ void rotsprite_image(Image* bmp, Image* spr,
|
|||
tmp_copy->setMaskColor(maskColor);
|
||||
spr_copy->setMaskColor(maskColor);
|
||||
|
||||
bmp_copy->clear(bmp->maskColor());
|
||||
spr_copy->clear(maskColor);
|
||||
spr_copy->copy(spr, gfx::Clip(spr->bounds()));
|
||||
|
||||
for (int i=0; i<3; ++i) {
|
||||
clear_image(tmp_copy, maskColor);
|
||||
// clear_image(tmp_copy, maskColor);
|
||||
image_scale2x(tmp_copy, spr_copy, spr->width()*(1<<i), spr->height()*(1<<i));
|
||||
spr_copy->copy(tmp_copy, gfx::Clip(tmp_copy->bounds()));
|
||||
}
|
||||
|
||||
doc::algorithm::parallelogram(
|
||||
bmp_copy, spr_copy,
|
||||
if (mask) {
|
||||
// Same ImageBuffer than tmp_copy
|
||||
msk_copy.reset(Image::create(IMAGE_BITMAP, mask->width()*scale, mask->height()*scale, buf[1]));
|
||||
clear_image(msk_copy, 0);
|
||||
scale_image(msk_copy, mask,
|
||||
0, 0, msk_copy->width(), msk_copy->height(),
|
||||
0, 0, mask->width(), mask->height());
|
||||
}
|
||||
|
||||
clear_image(bmp_copy, maskColor);
|
||||
clear_image(bmp_copy, 0);
|
||||
scale_image(bmp_copy, bmp,
|
||||
0, 0, bmp_copy->width(), bmp_copy->height(),
|
||||
xmin, ymin, rot_width, rot_height);
|
||||
|
||||
parallelogram(
|
||||
bmp_copy, spr_copy, msk_copy.get(),
|
||||
(x1-xmin)*scale, (y1-ymin)*scale, (x2-xmin)*scale, (y2-ymin)*scale,
|
||||
(x3-xmin)*scale, (y3-ymin)*scale, (x4-xmin)*scale, (y4-ymin)*scale);
|
||||
|
||||
doc::algorithm::scale_image(bmp, bmp_copy,
|
||||
xmin, ymin, bmp_copy->width()/scale, bmp_copy->height()/scale);
|
||||
scale_image(bmp, bmp_copy,
|
||||
xmin, ymin, rot_width, rot_height,
|
||||
0, 0, bmp_copy->width(), bmp_copy->height());
|
||||
}
|
||||
|
||||
} // namespace algorithm
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
|
@ -13,7 +13,7 @@ namespace doc {
|
|||
|
||||
namespace algorithm {
|
||||
|
||||
void rotsprite_image(Image* bmp, Image* spr,
|
||||
void rotsprite_image(Image* dst, const Image* src, const Image* mask,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int x3, int y3, int x4, int y4);
|
||||
|
||||
|
|
|
|||
|
|
@ -198,6 +198,12 @@ public:
|
|||
pt.y >= y && pt.y < y+h;
|
||||
}
|
||||
|
||||
bool contains(const T& u, const T& v) const {
|
||||
return
|
||||
u >= x && u < x+w &&
|
||||
v >= y && v < y+h;
|
||||
}
|
||||
|
||||
// Returns true if this rectangle entirely contains the rc rectangle.
|
||||
bool contains(const RectT& rc) const {
|
||||
if (isEmpty() || rc.isEmpty())
|
||||
|
|
|
|||
|
|
@ -58,6 +58,14 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
gfx::Rect bounds() const {
|
||||
Rect bounds;
|
||||
for (int i=0; i<Corners::NUM_OF_CORNERS; ++i)
|
||||
bounds = bounds.createUnion(gfx::Rect((int)m_corners[i].x,
|
||||
(int)m_corners[i].y, 1, 1));
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PointT<double> > m_corners;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue