Merge branch 'master' into beta

This commit is contained in:
David Capello 2016-09-19 19:02:49 -03:00
commit dfecffd465
11 changed files with 135 additions and 55 deletions

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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()) {

View File

@ -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;
}

View File

@ -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(

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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(); }