mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-04 08:46:09 +00:00
Merge branch 'master' into beta
This commit is contained in:
commit
dfecffd465
@ -234,7 +234,7 @@
|
||||
<option id="speed" type="int" default="32" />
|
||||
</section>
|
||||
<section id="floodfill">
|
||||
<option id="stop_at_grid" type="StopAtGrid" default="StopAtGrid::IF_VISIBLE" />
|
||||
<option id="stop_at_grid" type="StopAtGrid" default="StopAtGrid::NEVER" />
|
||||
<option id="refer_to" type="FillReferTo" default="FillReferTo::ACTIVE_LAYER" />
|
||||
</section>
|
||||
</tool>
|
||||
|
@ -95,6 +95,17 @@ ImageRef load_xml_image(const TiXmlElement* imageElem)
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else if (formatStr == "bitmap") {
|
||||
image.reset(Image::create(IMAGE_BITMAP, w, h));
|
||||
LockImageBits<BitmapTraits> pixels(image.get());
|
||||
for (auto& pixel : pixels) {
|
||||
if (it == end)
|
||||
break;
|
||||
|
||||
pixel = *it;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
@ -110,7 +121,7 @@ void save_xml_image(TiXmlElement* imageElem, const Image* image)
|
||||
case IMAGE_RGB: format = "rgba"; break;
|
||||
case IMAGE_GRAYSCALE: format = "grayscale"; break;
|
||||
case IMAGE_INDEXED: format = "indexed"; break;
|
||||
case IMAGE_BITMAP: format = "indexed"; break; // TODO add "bitmap" format
|
||||
case IMAGE_BITMAP: format = "bitmap"; break; // TODO add "bitmap" format
|
||||
}
|
||||
ASSERT(!format.empty());
|
||||
if (!format.empty())
|
||||
@ -315,13 +326,16 @@ void AppBrushes::load(const std::string& filename)
|
||||
}
|
||||
|
||||
// Brush image
|
||||
if (TiXmlElement* imageElem = brushElem->FirstChildElement("image")) {
|
||||
ImageRef image = load_xml_image(imageElem);
|
||||
if (image) {
|
||||
if (!brush)
|
||||
brush.reset(new Brush());
|
||||
brush->setImage(image.get());
|
||||
}
|
||||
ImageRef image, mask;
|
||||
if (TiXmlElement* imageElem = brushElem->FirstChildElement("image"))
|
||||
image = load_xml_image(imageElem);
|
||||
if (TiXmlElement* maskElem = brushElem->FirstChildElement("mask"))
|
||||
mask = load_xml_image(maskElem);
|
||||
|
||||
if (image) {
|
||||
if (!brush)
|
||||
brush.reset(new Brush());
|
||||
brush->setImage(image.get(), mask.get());
|
||||
}
|
||||
|
||||
// Colors
|
||||
@ -421,6 +435,12 @@ void AppBrushes::save(const std::string& filename) const
|
||||
TiXmlElement elem("image");
|
||||
save_xml_image(&elem, slot.brush()->image());
|
||||
brushElem.InsertEndChild(elem);
|
||||
|
||||
if (slot.brush()->maskBitmap()) {
|
||||
TiXmlElement maskElem("mask");
|
||||
save_xml_image(&maskElem, slot.brush()->maskBitmap());
|
||||
brushElem.InsertEndChild(maskElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
|
||||
|
||||
// New brush
|
||||
doc::BrushRef brush(new doc::Brush());
|
||||
brush->setImage(image.get());
|
||||
brush->setImage(image.get(), mask->bitmap());
|
||||
brush->setPatternOrigin(mask->bounds().origin());
|
||||
|
||||
ContextBar* ctxBar = App::instance()->contextBar();
|
||||
|
@ -922,6 +922,7 @@ public:
|
||||
m_palette = get_current_palette();
|
||||
m_brush = loop->getBrush();
|
||||
m_brushImage = m_brush->image();
|
||||
m_brushMask = m_brush->maskBitmap();
|
||||
m_opacity = loop->getOpacity();
|
||||
m_width = m_brush->bounds().w;
|
||||
m_height = m_brush->bounds().h;
|
||||
@ -946,6 +947,7 @@ private:
|
||||
const Palette* m_palette;
|
||||
const Brush* m_brush;
|
||||
const Image* m_brushImage;
|
||||
const Image* m_brushMask;
|
||||
int m_opacity;
|
||||
int m_u, m_v, m_width, m_height;
|
||||
};
|
||||
@ -953,6 +955,8 @@ private:
|
||||
template<>
|
||||
void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
if (m_brushMask && !get_pixel_fast<BitmapTraits>(m_brushMask, x, y))
|
||||
return;
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
@ -987,6 +991,8 @@ void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
template<>
|
||||
void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
if (m_brushMask && !get_pixel_fast<BitmapTraits>(m_brushMask, x, y))
|
||||
return;
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
@ -1023,6 +1029,8 @@ void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
template<>
|
||||
void BrushInkProcessing<IndexedTraits>::processPixel(int x, int y) {
|
||||
alignPixelPoint(x, y);
|
||||
if (m_brushMask && !get_pixel_fast<BitmapTraits>(m_brushMask, x, y))
|
||||
return;
|
||||
|
||||
color_t c;
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
|
@ -40,7 +40,9 @@ public:
|
||||
|
||||
void preparePointShape(ToolLoop* loop) override {
|
||||
m_brush = loop->getBrush();
|
||||
m_compressedImage.reset(new CompressedImage(m_brush->image(), false));
|
||||
m_compressedImage.reset(new CompressedImage(m_brush->image(),
|
||||
m_brush->maskBitmap(),
|
||||
false));
|
||||
m_firstPoint = true;
|
||||
}
|
||||
|
||||
|
@ -340,27 +340,23 @@ void BrushPreview::generateBoundaries()
|
||||
m_brushWidth = w;
|
||||
m_brushHeight = h;
|
||||
|
||||
ImageRef mask;
|
||||
Image* mask = nullptr;
|
||||
bool deleteMask = true;
|
||||
if (isOnePixel) {
|
||||
mask.reset(Image::create(IMAGE_BITMAP, w, w));
|
||||
mask = Image::create(IMAGE_BITMAP, 1, 1);
|
||||
mask->putPixel(0, 0, (color_t)1);
|
||||
}
|
||||
else if (brushImage->pixelFormat() != IMAGE_BITMAP) {
|
||||
mask.reset(Image::create(IMAGE_BITMAP, w, h));
|
||||
|
||||
LockImageBits<BitmapTraits> bits(mask.get());
|
||||
auto pos = bits.begin();
|
||||
for (int v=0; v<h; ++v) {
|
||||
for (int u=0; u<w; ++u) {
|
||||
*pos = get_pixel(brushImage, u, v);
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
ASSERT(brush->maskBitmap());
|
||||
deleteMask = false;
|
||||
mask = brush->maskBitmap();
|
||||
}
|
||||
|
||||
m_brushBoundaries.reset(
|
||||
new MaskBoundaries(
|
||||
(mask ? mask.get(): brushImage)));
|
||||
new MaskBoundaries(mask ? mask: brushImage));
|
||||
|
||||
if (deleteMask)
|
||||
delete mask;
|
||||
}
|
||||
|
||||
void BrushPreview::forEachBrushPixel(
|
||||
|
@ -2571,8 +2571,8 @@ gfx::Size Timeline::getScrollableSize() const
|
||||
{
|
||||
if (m_sprite) {
|
||||
return gfx::Size(
|
||||
m_sprite->totalFrames() * FRMSIZE + bounds().w/2,
|
||||
m_layers.size() * LAYSIZE + bounds().h/2);
|
||||
m_sprite->totalFrames() * FRMSIZE + getCelsBounds().w/2,
|
||||
m_layers.size() * LAYSIZE + LAYSIZE);
|
||||
}
|
||||
else
|
||||
return gfx::Size(0, 0);
|
||||
@ -2582,8 +2582,8 @@ gfx::Point Timeline::getMaxScrollablePos() const
|
||||
{
|
||||
if (m_sprite) {
|
||||
gfx::Size size = getScrollableSize();
|
||||
int max_scroll_x = size.w - bounds().w/2;
|
||||
int max_scroll_y = size.h - bounds().h/2;
|
||||
int max_scroll_x = size.w - getCelsBounds().w + 1*guiscale();
|
||||
int max_scroll_y = size.h - getCelsBounds().h + 1*guiscale();
|
||||
max_scroll_x = MAX(0, max_scroll_x);
|
||||
max_scroll_y = MAX(0, max_scroll_y);
|
||||
return gfx::Point(max_scroll_x, max_scroll_y);
|
||||
|
@ -51,6 +51,7 @@ Brush::Brush(const Brush& brush)
|
||||
m_size = brush.m_size;
|
||||
m_angle = brush.m_angle;
|
||||
m_image = brush.m_image;
|
||||
m_maskBitmap = brush.m_maskBitmap;
|
||||
m_pattern = brush.m_pattern;
|
||||
m_patternOrigin = brush.m_patternOrigin;
|
||||
m_gen = 0;
|
||||
@ -84,10 +85,24 @@ void Brush::setAngle(int angle)
|
||||
regenerate();
|
||||
}
|
||||
|
||||
void Brush::setImage(const Image* image)
|
||||
void Brush::setImage(const Image* image,
|
||||
const Image* maskBitmap)
|
||||
{
|
||||
m_type = kImageBrushType;
|
||||
m_image.reset(Image::createCopy(image));
|
||||
if (maskBitmap)
|
||||
m_maskBitmap.reset(Image::createCopy(maskBitmap));
|
||||
else {
|
||||
int w = image->width();
|
||||
int h = image->height();
|
||||
m_maskBitmap.reset(Image::create(IMAGE_BITMAP, w, h));
|
||||
LockImageBits<BitmapTraits> bits(m_maskBitmap.get());
|
||||
auto pos = bits.begin();
|
||||
for (int v=0; v<h; ++v)
|
||||
for (int u=0; u<w; ++u, ++pos)
|
||||
*pos = (get_pixel(image, u, v) != image->maskColor());
|
||||
}
|
||||
|
||||
m_backupImage.reset();
|
||||
m_mainColor.reset();
|
||||
m_bgColor.reset();
|
||||
@ -102,15 +117,21 @@ template<class ImageTraits,
|
||||
color_t alpha_mask>
|
||||
static void replace_image_colors(
|
||||
Image* image,
|
||||
Image* maskBitmap,
|
||||
const bool useMain, color_t mainColor,
|
||||
const bool useBg, color_t bgColor)
|
||||
{
|
||||
LockImageBits<ImageTraits> bits(image, Image::ReadWriteLock);
|
||||
const LockImageBits<BitmapTraits> maskBits(maskBitmap);
|
||||
bool hasAlpha = false; // True if "image" has a pixel with alpha < 255
|
||||
color_t srcMainColor, srcBgColor;
|
||||
srcMainColor = srcBgColor = 0;
|
||||
|
||||
auto mask_it = maskBits.begin();
|
||||
for (const auto& pixel : bits) {
|
||||
if (!*mask_it)
|
||||
continue;
|
||||
|
||||
if ((pixel & alpha_mask) != alpha_mask) { // If alpha != 255
|
||||
hasAlpha = true;
|
||||
}
|
||||
@ -120,6 +141,8 @@ static void replace_image_colors(
|
||||
else if (pixel != srcBgColor && srcMainColor == srcBgColor) {
|
||||
srcMainColor = pixel;
|
||||
}
|
||||
|
||||
++mask_it;
|
||||
}
|
||||
|
||||
mainColor &= color_mask;
|
||||
@ -147,16 +170,22 @@ static void replace_image_colors(
|
||||
|
||||
static void replace_image_colors_indexed(
|
||||
Image* image,
|
||||
Image* maskBitmap,
|
||||
const bool useMain, const color_t mainColor,
|
||||
const bool useBg, const color_t bgColor)
|
||||
{
|
||||
LockImageBits<IndexedTraits> bits(image, Image::ReadWriteLock);
|
||||
const LockImageBits<BitmapTraits> maskBits(maskBitmap);
|
||||
bool hasAlpha = false; // True if "image" has a pixel with the mask color
|
||||
color_t maskColor = image->maskColor();
|
||||
color_t srcMainColor, srcBgColor;
|
||||
srcMainColor = srcBgColor = maskColor;
|
||||
|
||||
auto mask_it = maskBits.begin();
|
||||
for (const auto& pixel : bits) {
|
||||
if (!*mask_it)
|
||||
continue;
|
||||
|
||||
if (pixel == maskColor) {
|
||||
hasAlpha = true;
|
||||
}
|
||||
@ -166,6 +195,8 @@ static void replace_image_colors_indexed(
|
||||
else if (pixel != srcBgColor && srcMainColor == srcBgColor) {
|
||||
srcMainColor = pixel;
|
||||
}
|
||||
|
||||
++mask_it;
|
||||
}
|
||||
|
||||
if (hasAlpha) {
|
||||
@ -201,6 +232,8 @@ void Brush::setImageColor(ImageColor imageColor, color_t color)
|
||||
else
|
||||
m_image.reset(Image::createCopy(m_backupImage.get()));
|
||||
|
||||
ASSERT(m_maskBitmap);
|
||||
|
||||
switch (imageColor) {
|
||||
case ImageColor::MainColor:
|
||||
m_mainColor.reset(new color_t(color));
|
||||
@ -214,21 +247,21 @@ void Brush::setImageColor(ImageColor imageColor, color_t color)
|
||||
|
||||
case IMAGE_RGB:
|
||||
replace_image_colors<RgbTraits, rgba_rgb_mask, rgba_a_mask>(
|
||||
m_image.get(),
|
||||
m_image.get(), m_maskBitmap.get(),
|
||||
(m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
|
||||
(m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
replace_image_colors<GrayscaleTraits, graya_v_mask, graya_a_mask>(
|
||||
m_image.get(),
|
||||
m_image.get(), m_maskBitmap.get(),
|
||||
(m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
|
||||
(m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
replace_image_colors_indexed(
|
||||
m_image.get(),
|
||||
m_image.get(), m_maskBitmap.get(),
|
||||
(m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
|
||||
(m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
|
||||
break;
|
||||
@ -240,6 +273,7 @@ void Brush::clean()
|
||||
{
|
||||
m_gen = ++generation;
|
||||
m_image.reset();
|
||||
m_maskBitmap.reset();
|
||||
m_backupImage.reset();
|
||||
}
|
||||
|
||||
@ -260,6 +294,7 @@ void Brush::regenerate()
|
||||
size = (int)std::sqrt((double)2*m_size*m_size)+2;
|
||||
|
||||
m_image.reset(Image::create(IMAGE_BITMAP, size, size));
|
||||
m_maskBitmap.reset();
|
||||
|
||||
if (size == 1) {
|
||||
clear_image(m_image.get(), BitmapTraits::max_value);
|
||||
|
@ -36,6 +36,7 @@ namespace doc {
|
||||
int size() const { return m_size; }
|
||||
int angle() const { return m_angle; }
|
||||
Image* image() const { return m_image.get(); }
|
||||
Image* maskBitmap() const { return m_maskBitmap.get(); }
|
||||
int gen() const { return m_gen; }
|
||||
|
||||
BrushPattern pattern() const { return m_pattern; }
|
||||
@ -46,7 +47,8 @@ namespace doc {
|
||||
void setType(BrushType type);
|
||||
void setSize(int size);
|
||||
void setAngle(int angle);
|
||||
void setImage(const Image* image);
|
||||
void setImage(const Image* image,
|
||||
const Image* maskBitmap);
|
||||
void setImageColor(ImageColor imageColor, color_t color);
|
||||
void setPattern(BrushPattern pattern) {
|
||||
m_pattern = pattern;
|
||||
@ -63,6 +65,7 @@ namespace doc {
|
||||
int m_size; // Size (diameter)
|
||||
int m_angle; // Angle in degrees 0-360
|
||||
ImageRef m_image; // Image of the brush
|
||||
ImageRef m_maskBitmap;
|
||||
gfx::Rect m_bounds;
|
||||
BrushPattern m_pattern; // How the image should be replicated
|
||||
gfx::Point m_patternOrigin; // From what position the brush was taken
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -11,10 +11,13 @@
|
||||
#include "doc/compressed_image.h"
|
||||
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/primitives_fast.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
CompressedImage::CompressedImage(const Image* image, bool diffColors)
|
||||
CompressedImage::CompressedImage(const Image* image,
|
||||
const Image* maskBitmap,
|
||||
bool diffColors)
|
||||
: m_image(image)
|
||||
{
|
||||
color_t c1, c2, mask = image->maskColor();
|
||||
@ -23,24 +26,36 @@ CompressedImage::CompressedImage(const Image* image, bool diffColors)
|
||||
Scanline scanline(y);
|
||||
|
||||
for (int x=0; x<image->width(); ) {
|
||||
c1 = get_pixel(image, x, y);
|
||||
if (c1 != mask) {
|
||||
scanline.color = c1;
|
||||
scanline.x = x;
|
||||
|
||||
for (++x; x<image->width(); ++x) {
|
||||
c2 = get_pixel(image, x, y);
|
||||
|
||||
if ((diffColors && c1 != c2) ||
|
||||
(!diffColors && c2 == mask))
|
||||
break;
|
||||
}
|
||||
|
||||
scanline.w = x - scanline.x;
|
||||
m_scanlines.push_back(scanline);
|
||||
}
|
||||
else
|
||||
if (maskBitmap && !get_pixel_fast<BitmapTraits>(maskBitmap, x, y)) {
|
||||
++x;
|
||||
continue;
|
||||
}
|
||||
|
||||
c1 = get_pixel(image, x, y);
|
||||
|
||||
if (!maskBitmap && c1 == mask) {
|
||||
++x;
|
||||
continue;
|
||||
}
|
||||
|
||||
scanline.color = c1;
|
||||
scanline.x = x;
|
||||
|
||||
for (++x; x<image->width(); ++x) {
|
||||
c2 = get_pixel(image, x, y);
|
||||
|
||||
if (diffColors && c1 != c2)
|
||||
break;
|
||||
|
||||
if (maskBitmap && !get_pixel_fast<BitmapTraits>(maskBitmap, x, y))
|
||||
break;
|
||||
|
||||
if (!diffColors && !maskBitmap && c2 == mask)
|
||||
break;
|
||||
}
|
||||
|
||||
scanline.w = x - scanline.x;
|
||||
m_scanlines.push_back(scanline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -30,7 +30,8 @@ namespace doc {
|
||||
// each different color. If it's false, it generates a scanline
|
||||
// for each row of consecutive pixels different than the mask
|
||||
// color.
|
||||
CompressedImage(const Image* image, bool diffColors);
|
||||
CompressedImage(const Image* image,
|
||||
const Image* maskBitmap, bool diffColors);
|
||||
|
||||
const_iterator begin() const { return m_scanlines.begin(); }
|
||||
const_iterator end() const { return m_scanlines.end(); }
|
||||
|
Loading…
Reference in New Issue
Block a user