From 7d809bd537e0c729640ba007f903ccdfc4920af0 Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 7 Jun 2021 16:14:40 -0300 Subject: [PATCH 1/4] Remove unused var --- src/app/file/file.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp index d0f0aecbb..8e1425edd 100644 --- a/src/app/file/file.cpp +++ b/src/app/file/file.cpp @@ -473,7 +473,6 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context, #ifdef ENABLE_UI // Interative if (context && context->isUIAvailable()) { - auto& showAlertOption = Preferences::instance().saveFile.showFileFormatDoesntSupportAlert; int ret; // If the error is fatal, we cannot ignore a no-op, we always From 55db5ac6f21f648520cf4e1598d914cb9fc5f8db Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 8 Jun 2021 16:44:58 -0300 Subject: [PATCH 2/4] [win] Fill the whole available space for the File Explorer thumbnail --- src/desktop/win/thumbnail_handler.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/desktop/win/thumbnail_handler.cpp b/src/desktop/win/thumbnail_handler.cpp index db5ac5f88..51c562211 100644 --- a/src/desktop/win/thumbnail_handler.cpp +++ b/src/desktop/win/thumbnail_handler.cpp @@ -1,4 +1,5 @@ // Desktop Integration +// Copyright (C) 2021 Igara Studio S.A. // Copyright (C) 2017-2018 David Capello // // This file is released under the terms of the MIT license. @@ -18,6 +19,7 @@ #include "desktop/win/thumbnail_handler.h" +#include #include #include @@ -174,6 +176,9 @@ HRESULT ThumbnailHandler::Initialize(IStream* pStream, DWORD grfMode) // IThumbnailProvider HRESULT ThumbnailHandler::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* pdwAlpha) { + if (cx < 1 || !phbmp || !pdwAlpha) + return E_INVALIDARG; + if (!m_stream.get()) return E_FAIL; @@ -186,16 +191,27 @@ HRESULT ThumbnailHandler::GetThumbnail(UINT cx, HBITMAP* phbmp, WTS_ALPHATYPE* p if (!dio::decode_file(&delegate, &adaptor)) return E_FAIL; - doc::Sprite* spr = delegate.sprite(); + const doc::Sprite* spr = delegate.sprite(); w = spr->width(); h = spr->height(); + int wh = std::max(w, h); - image.reset(doc::Image::create(doc::IMAGE_RGB, w, h)); + image.reset(doc::Image::create(doc::IMAGE_RGB, + cx * w / wh, + cx * h / wh)); + image->clear(0); #undef TRANSPARENT // Windows defines TRANSPARENT macro render::Render render; render.setBgType(render::BgType::TRANSPARENT); - render.renderSprite(image.get(), spr, 0); + render.setProjection(render::Projection(doc::PixelRatio(1, 1), + render::Zoom(cx, wh))); + render.renderSprite(image.get(), spr, 0, + gfx::ClipF(0, 0, 0, 0, + image->width(), image->height())); + + w = image->width(); + h = image->height(); } catch (const std::exception&) { // TODO convert exception into a HRESULT From 65fabf3a68acddba30aa40162247fb716c1dea56 Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 8 Jun 2021 16:55:48 -0300 Subject: [PATCH 3/4] Update README/LICENSE files in src/desktop --- src/desktop/LICENSE.txt | 3 ++- src/desktop/README.md | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/desktop/LICENSE.txt b/src/desktop/LICENSE.txt index 7abaa0425..00bfae8bd 100644 --- a/src/desktop/LICENSE.txt +++ b/src/desktop/LICENSE.txt @@ -1,4 +1,5 @@ -Copyright (C) 2017-2018 David Capello +Copyright (C) 2021 Igara Studio S.A. +Copyright (C) 2017-2018 David Capello Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/src/desktop/README.md b/src/desktop/README.md index 6081ead7c..8a0918ed0 100644 --- a/src/desktop/README.md +++ b/src/desktop/README.md @@ -1,7 +1,6 @@ # Desktop Integration -*Copyright (C) 2017-2018 David Capello* -> Distributed under [MIT license](LICENSE.txt) +[![MIT Licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt) ## Windows From 1eace2489125933f1975def6d5f3eea3344c4dc3 Mon Sep 17 00:00:00 2001 From: Gaspar Capello Date: Fri, 11 Jun 2021 14:22:19 -0300 Subject: [PATCH 4/4] Improve aspect of ellipses for 16x16 or bigger sizes (fix #2217) Now algo_ellipse() function supports additional parameters to create rounded squares in the future. --- src/app/tools/intertwiners.h | 6 +- src/doc/algo.cpp | 145 +++++++++++++++++++------ src/doc/algo.h | 6 +- src/doc/algorithm/modify_selection.cpp | 3 +- src/doc/brush.cpp | 4 +- src/doc/primitives.cpp | 8 +- src/doc/primitives.h | 6 +- 7 files changed, 131 insertions(+), 47 deletions(-) diff --git a/src/app/tools/intertwiners.h b/src/app/tools/intertwiners.h index ccf1b1ad3..442e69a9f 100644 --- a/src/app/tools/intertwiners.h +++ b/src/app/tools/intertwiners.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2020 Igara Studio S.A. +// Copyright (C) 2018-2021 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -313,7 +313,7 @@ public: const double angle = loop->getController()->getShapeAngle(); if (ABS(angle) < 0.001) { - algo_ellipse(x1, y1, x2, y2, loop, (AlgoPixel)doPointshapePoint); + algo_ellipse(x1, y1, x2, y2, 0, 0, loop, (AlgoPixel)doPointshapePoint); } else { draw_rotated_ellipse((x1+x2)/2, (y1+y2)/2, @@ -343,7 +343,7 @@ public: const double angle = loop->getController()->getShapeAngle(); if (ABS(angle) < 0.001) { - algo_ellipsefill(x1, y1, x2, y2, loop, (AlgoHLine)doPointshapeHline); + algo_ellipsefill(x1, y1, x2, y2, 0, 0, loop, (AlgoHLine)doPointshapeHline); } else { fill_rotated_ellipse((x1+x2)/2, (y1+y2)/2, diff --git a/src/doc/algo.cpp b/src/doc/algo.cpp index 09d2c4fc0..2fac7b517 100644 --- a/src/doc/algo.cpp +++ b/src/doc/algo.cpp @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (c) 2018-2020 Igara Studio S.A. +// Copyright (c) 2018-2021 Igara Studio S.A. // Copyright (c) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -171,64 +171,147 @@ void algo_line_continuous_with_fix_for_line_brush(int x0, int y0, int x1, int y1 } } +static int adjust_ellipse_args(int& x0, int& y0, int& x1, int& y1, + int& hPixels, int& vPixels) +{ + // hPixels : straight horizontal pixels added to mid region of the ellipse. + hPixels = std::max(hPixels, 0); + // vPixels : straight vertical pixels added to mid region of the ellipse. + vPixels = std::max(vPixels, 0); + + // Conditioning swapped points + if (x0 > x1) + std::swap(x0, x1); + if (y0 > y1) + std::swap(y0, y1); + int w = x1 - x0 + 1; + int h = y1 - y0 + 1; + + // hDiameter is the horizontal diameter of a circunference + // without the addition of straight pixels. + int hDiameter = w - hPixels; + // vDiameter is the vertical diameter of a circunference + // without the addition of straight pixels. + int vDiameter = h - vPixels; + + // Manual adjustment + if (w == 8 || w == 12 || w == 22) + hPixels++; + if (h == 8 || h == 12 || h == 22) + vPixels++; + + hPixels = (hDiameter > 5 ? hPixels : 0); + vPixels = (vDiameter > 5 ? vPixels : 0); + + if ((hDiameter % 2 == 0) && (hDiameter > 5)) + hPixels--; + if ((vDiameter % 2 == 0) && (vDiameter > 5)) + vPixels--; + + x1 -= hPixels; + y1 -= vPixels; + + return h; +} + // Ellipse code based on Alois Zingl work released under the MIT // license http://members.chello.at/easyfilter/bresenham.html // // Adapted for Aseprite by David Capello -void algo_ellipse(int x0, int y0, int x1, int y1, void* data, AlgoPixel proc) +void algo_ellipse(int x0, int y0, int x1, int y1, + int hPixels, int vPixels, + void* data, AlgoPixel proc) { - long a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // diameter - double dx = 4*(1.0-a)*b*b, dy = 4*(b1+1)*a*a; // error increment - double err = dx+dy+b1*a*a, e2; // error of 1.step + int h = adjust_ellipse_args(x0, y0, x1, y1, hPixels, vPixels); - if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points - if (y0 > y1) y0 = y1; // .. exchange them - y0 += (b+1)/2; y1 = y0-b1; // starting pixel - a = 8*a*a; b1 = 8*b*b; + long a = abs(x1-x0); + long b = abs(y1-y0); // diameter + long b1 = b&1; + double dx = 4*(1.0-a)*b*b; // error increment + double dy = 4*(b1+1)*a*a; // error increment + double err = dx + dy + b1*a*a; // error of 1.step + double e2; + y0 += (b+1)/2; + y1 = y0-b1; // starting pixel + a = 8*a*a; + b1 = 8*b*b; + + int initialY0 = y0; + int initialY1 = y1; + int initialX0 = x0; + int initialX1 = x1 + hPixels; do { - proc(x1, y0, data); // I. Quadrant - proc(x0, y0, data); // II. Quadrant - proc(x0, y1, data); // III. Quadrant - proc(x1, y1, data); // IV. Quadrant + proc(x1 + hPixels, y0 + vPixels, data); // I. Quadrant + proc(x0, y0 + vPixels, data); // II. Quadrant + proc(x0, y1, data); // III. Quadrant + proc(x1 + hPixels, y1, data); // IV. Quadrant + e2 = 2*err; if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step } while (x0 <= x1); - while (y0-y1 <= b) { // too early stop of flat ellipses a=1 - proc(x0-1, y0, data); // -> finish tip of ellipse - proc(x1+1, y0++, data); - proc(x0-1, y1, data); - proc(x1+1, y1--, data); + while (y0 + vPixels - y1 + 1 <= h) { // too early stop of flat ellipses a=1 + proc(x0 - 1, y0 + vPixels, data); // -> finish tip of ellipse + proc(x1 + 1 + hPixels, y0++ + vPixels, data); + proc(x0 - 1, y1, data); + proc(x1 + 1 + hPixels, y1--, data); + } + + // Extra horizontal straight pixels + if (hPixels > 0) { + for (int i = x0; i < x1 + hPixels + 1; i++) { + proc(i, y1 + 1, data); + proc(i, y0 + vPixels - 1, data); + } + } + // Extra vertical straight pixels + if (vPixels > 0) { + for (int i = initialY1 + 1; i < initialY0 + vPixels; i++) { + proc(initialX0, i, data); + proc(initialX1, i, data); + } } } -void algo_ellipsefill(int x0, int y0, int x1, int y1, void* data, AlgoHLine proc) +void algo_ellipsefill(int x0, int y0, int x1, int y1, + int hPixels, int vPixels, + void* data, AlgoHLine proc) { - long a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // diameter + int h = adjust_ellipse_args(x0, y0, x1, y1, hPixels, vPixels); + + long a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; // diameter double dx = 4*(1.0-a)*b*b, dy = 4*(b1+1)*a*a; // error increment double err = dx+dy+b1*a*a, e2; // error of 1.step - if (x0 > x1) { x0 = x1; x1 += a; } // if called with swapped points - if (y0 > y1) y0 = y1; // .. exchange them - y0 += (b+1)/2; y1 = y0-b1; // starting pixel + y0 += (b+1)/2; y1 = y0-b1; // starting pixel a = 8*a*a; b1 = 8*b*b; + int initialY0 = y0; + int initialY1 = y1; + int initialX0 = x0; + int initialX1 = x1 + hPixels; + do { - proc(x0, y0, x1, data); - proc(x0, y1, x1, data); + proc(x0, y0 + vPixels, x1 + hPixels, data); + proc(x0, y1, x1 + hPixels, data); e2 = 2*err; if (e2 <= dy) { y0++; y1--; err += dy += a; } // y step if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } // x step } while (x0 <= x1); - while (y0-y1 <= b) { // too early stop of flat ellipses a=1 - proc(x0-1, y0, x0-1, data); // -> finish tip of ellipse - proc(x1+1, y0++, x1+1, data); - proc(x0-1, y1, x0-1, data); - proc(x1+1, y1--, x1+1, data); + while (y0 + vPixels - y1 + 1 < h) { // too early stop of flat ellipses a=1 + proc(x0-1, ++y0 + vPixels, x0-1, data); // -> finish tip of ellipse + proc(x1+1 + hPixels, y0 + vPixels, x1+1 + hPixels, data); + proc(x0-1, --y1, x0-1, data); + proc(x1+1 + hPixels, y1, x1+1 + hPixels, data); + } + + if (vPixels > 0) { + for (int i = initialY1 + 1; i < initialY0 + vPixels; i++) + proc(initialX0, i, initialX1, data); } } @@ -324,7 +407,7 @@ static void draw_rotated_ellipse_rect(int x0, int y0, int x1, int y1, double zd, double w = xd*yd; if (zd == 0) - return algo_ellipse(x0, y0, x1, y1, data, proc); + return algo_ellipse(x0, y0, x1, y1, 0, 0, data, proc); if (w != 0.0) w = (w-zd) / (w+w); // squared weight of P1 diff --git a/src/doc/algo.h b/src/doc/algo.h index 687130748..51f929089 100644 --- a/src/doc/algo.h +++ b/src/doc/algo.h @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (C) 2018-2020 Igara Studio S.A. +// Copyright (C) 2018-2021 Igara Studio S.A. // Copyright (c) 2001-2018 David Capello // // This file is released under the terms of the MIT license. @@ -38,8 +38,8 @@ namespace doc { void algo_line_continuous(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc); void algo_line_continuous_with_fix_for_line_brush(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc); - void algo_ellipse(int x1, int y1, int x2, int y2, void *data, AlgoPixel proc); - void algo_ellipsefill(int x1, int y1, int x2, int y2, void *data, AlgoHLine proc); + void algo_ellipse(int x1, int y1, int x2, int y2, int hPixels, int vPixels, void *data, AlgoPixel proc); + void algo_ellipsefill(int x1, int y1, int x2, int y2, int hPixels, int vPixels, void *data, AlgoHLine proc); void draw_rotated_ellipse(int cx, int cy, int a, int b, double angle, void* data, AlgoPixel proc); void fill_rotated_ellipse(int cx, int cy, int a, int b, double angle, void* data, AlgoHLine proc); diff --git a/src/doc/algorithm/modify_selection.cpp b/src/doc/algorithm/modify_selection.cpp index 1b01c97c4..88d6bf477 100644 --- a/src/doc/algorithm/modify_selection.cpp +++ b/src/doc/algorithm/modify_selection.cpp @@ -1,4 +1,5 @@ // Aseprite Document Library +// Copyright (c) 2021 Igara Studio S.A. // Copyright (c) 2018 David Capello // // This file is released under the terms of the MIT license. @@ -41,7 +42,7 @@ void modify_selection(const SelectionModifier modifier, std::unique_ptr kernel(doc::Image::create(IMAGE_BITMAP, size, size)); doc::clear_image(kernel.get(), 0); if (brush == doc::kCircleBrushType) - doc::fill_ellipse(kernel.get(), 0, 0, size-1, size-1, 1); + doc::fill_ellipse(kernel.get(), 0, 0, size-1, size-1, 0, 0, 1); else doc::fill_rect(kernel.get(), 0, 0, size-1, size-1, 1); doc::put_pixel(kernel.get(), radius, radius, 0); diff --git a/src/doc/brush.cpp b/src/doc/brush.cpp index c70d9e08a..fb935da3d 100644 --- a/src/doc/brush.cpp +++ b/src/doc/brush.cpp @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (C) 2019 Igara Studio S.A. +// Copyright (C) 2019-2021 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This file is released under the terms of the MIT license. @@ -328,7 +328,7 @@ void Brush::regenerate() switch (m_type) { case kCircleBrushType: - fill_ellipse(m_image.get(), 0, 0, size-1, size-1, BitmapTraits::max_value); + fill_ellipse(m_image.get(), 0, 0, size-1, size-1, 0, 0, BitmapTraits::max_value); break; case kSquareBrushType: diff --git a/src/doc/primitives.cpp b/src/doc/primitives.cpp index 6fd8ac17e..6fbe611a2 100644 --- a/src/doc/primitives.cpp +++ b/src/doc/primitives.cpp @@ -282,16 +282,16 @@ void draw_line(Image* image, int x1, int y1, int x2, int y2, color_t color) algo_line_continuous(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image); } -void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color) +void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, int extraXPxs, int extraYPxs, color_t color) { Data data = { image, color }; - algo_ellipse(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image); + algo_ellipse(x1, y1, x2, y2, extraXPxs, extraYPxs, &data, (AlgoPixel)pixel_for_image); } -void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color) +void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, int extraXPxs, int extraYPxs, color_t color) { Data data = { image, color }; - algo_ellipsefill(x1, y1, x2, y2, &data, (AlgoHLine)hline_for_image); + algo_ellipsefill(x1, y1, x2, y2, extraXPxs, extraYPxs, &data, (AlgoHLine)hline_for_image); } namespace { diff --git a/src/doc/primitives.h b/src/doc/primitives.h index fb000c8c5..c13a9b7aa 100644 --- a/src/doc/primitives.h +++ b/src/doc/primitives.h @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (c) 2018-2019 Igara Studio S.A. +// Copyright (c) 2018-2021 Igara Studio S.A. // Copyright (c) 2001-2016 David Capello // // This file is released under the terms of the MIT license. @@ -38,8 +38,8 @@ namespace doc { void fill_rect(Image* image, const gfx::Rect& rc, color_t c); void blend_rect(Image* image, int x1, int y1, int x2, int y2, color_t c, int opacity); void draw_line(Image* image, int x1, int y1, int x2, int y2, color_t c); - void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t c); - void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t c); + void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, int extraXPxs, int extraYPxs, color_t color); + void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, int extraXPxs, int extraYPxs, color_t color); bool is_plain_image(const Image* img, color_t c); bool is_empty_image(const Image* img);