mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-20 18:40:57 +00:00
[lua] Add Image:resize() function
Closes: https://community.aseprite.org/t/3633
This commit is contained in:
parent
b6de9d924b
commit
ad1a39714e
@ -602,6 +602,7 @@ add_library(app-lib
|
||||
util/pixel_ratio.cpp
|
||||
util/range_utils.cpp
|
||||
util/readable_time.cpp
|
||||
util/resize_image.cpp
|
||||
util/wrap_point.cpp
|
||||
xml_document.cpp
|
||||
xml_exception.cpp
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/sprite_job.h"
|
||||
#include "app/util/resize_image.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "doc/algorithm/resize_image.h"
|
||||
@ -93,38 +94,19 @@ protected:
|
||||
++cels_count;
|
||||
}
|
||||
|
||||
const gfx::SizeF scale(
|
||||
double(m_new_width) / double(sprite()->width()),
|
||||
double(m_new_height) / double(sprite()->height()));
|
||||
|
||||
// For each cel...
|
||||
int progress = 0;
|
||||
for (Cel* cel : sprite()->uniqueCels()) {
|
||||
// Get cel's image
|
||||
Image* image = cel->image();
|
||||
if (image && !cel->link()) {
|
||||
// Resize the cel bounds only if it's from a reference layer
|
||||
if (cel->layer()->isReference()) {
|
||||
gfx::RectF newBounds = scale_rect<double>(cel->boundsF());
|
||||
tx()(new cmd::SetCelBoundsF(cel, newBounds));
|
||||
}
|
||||
else {
|
||||
// Change its location
|
||||
api.setCelPosition(sprite(), cel, scale_x(cel->x()), scale_y(cel->y()));
|
||||
|
||||
// Resize the image
|
||||
int w = scale_x(image->width());
|
||||
int h = scale_y(image->height());
|
||||
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
|
||||
new_image->setMaskColor(image->maskColor());
|
||||
|
||||
doc::algorithm::fixup_image_transparent_colors(image);
|
||||
doc::algorithm::resize_image(
|
||||
image, new_image.get(),
|
||||
m_resize_method,
|
||||
sprite()->palette(cel->frame()),
|
||||
sprite()->rgbMap(cel->frame()),
|
||||
(cel->layer()->isBackground() ? -1: sprite()->transparentColor()));
|
||||
|
||||
api.replaceImage(sprite(), cel->imageRef(), new_image);
|
||||
}
|
||||
}
|
||||
resize_cel_image(
|
||||
tx(), cel, scale,
|
||||
m_resize_method,
|
||||
cel->layer()->isReference() ?
|
||||
-cel->boundsF().origin():
|
||||
gfx::PointF(-cel->bounds().origin()));
|
||||
|
||||
jobProgress((float)progress / cels_count);
|
||||
++progress;
|
||||
|
@ -181,8 +181,7 @@ int Dialog_show(lua_State* L)
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "bounds");
|
||||
if (type != LUA_TNONE &&
|
||||
type != LUA_TNIL) {
|
||||
if (VALID_LUATYPE(type)) {
|
||||
const auto rc = convert_args_into_rect(L, -1);
|
||||
if (!rc.isEmpty()) {
|
||||
dlg->window.remapWindow();
|
||||
|
@ -11,14 +11,18 @@
|
||||
|
||||
#include "app/cmd/copy_rect.h"
|
||||
#include "app/cmd/copy_region.h"
|
||||
#include "app/commands/new_params.h" // Used for enum <-> Lua conversions
|
||||
#include "app/context.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "app/script/security.h"
|
||||
#include "app/site.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/util/autocrop.h"
|
||||
#include "app/util/resize_image.h"
|
||||
#include "base/fs.h"
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
#include "doc/cel.h"
|
||||
@ -28,6 +32,9 @@
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
@ -335,6 +342,97 @@ int Image_saveAs(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Image_resize(lua_State* L)
|
||||
{
|
||||
auto obj = get_obj<ImageObj>(L, 1);
|
||||
doc::Image* img = obj->image(L);
|
||||
Cel* cel = obj->cel(L);
|
||||
ASSERT(img);
|
||||
gfx::Size newSize = img->size();
|
||||
auto method = doc::algorithm::ResizeMethod::RESIZE_METHOD_NEAREST_NEIGHBOR;
|
||||
gfx::Point pivot(0, 0);
|
||||
|
||||
if (lua_istable(L, 2)) {
|
||||
// Image:resize{ (size | width, height),
|
||||
// method [, pivot] }
|
||||
|
||||
int type = lua_getfield(L, 2, "size");
|
||||
if (VALID_LUATYPE(type)) {
|
||||
newSize = convert_args_into_size(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else {
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "width");
|
||||
if (VALID_LUATYPE(type))
|
||||
newSize.w = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "height");
|
||||
if (VALID_LUATYPE(type))
|
||||
newSize.h = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
type = lua_getfield(L, 2, "method");
|
||||
if (VALID_LUATYPE(type)) {
|
||||
// TODO improve these lua <-> enum conversions, a lot of useless
|
||||
// work is done to create this dummy NewParams, etc.
|
||||
NewParams dummyParams;
|
||||
Param<doc::algorithm::ResizeMethod> param(&dummyParams, method, "method");
|
||||
param.fromLua(L, -1);
|
||||
method = param();
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "pivot");
|
||||
if (VALID_LUATYPE(type))
|
||||
pivot = convert_args_into_point(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else {
|
||||
newSize.w = lua_tointeger(L, 2);
|
||||
newSize.h = lua_tointeger(L, 3);
|
||||
}
|
||||
|
||||
newSize.w = std::max(1, newSize.w);
|
||||
newSize.h = std::max(1, newSize.h);
|
||||
|
||||
const gfx::SizeF scale(
|
||||
double(newSize.w) / double(img->width()),
|
||||
double(newSize.h) / double(img->height()));
|
||||
|
||||
// If the destination image is not related to a sprite, we just draw
|
||||
// the source image without undo information.
|
||||
if (cel) {
|
||||
Tx tx;
|
||||
resize_cel_image(tx, cel, scale, method,
|
||||
gfx::PointF(pivot));
|
||||
tx.commit();
|
||||
obj->imageId = cel->image()->id();
|
||||
}
|
||||
else {
|
||||
Context* ctx = App::instance()->context();
|
||||
ASSERT(ctx);
|
||||
Site site = ctx->activeSite();
|
||||
const doc::Palette* pal = site.palette();
|
||||
const doc::RgbMap* rgbmap = site.rgbMap();
|
||||
|
||||
std::unique_ptr<doc::Image> newImg(
|
||||
resize_image(img, scale, method,
|
||||
pal, rgbmap));
|
||||
// Delete old image, and we put the same ID of the old image into
|
||||
// the new image so this userdata references the resized image.
|
||||
delete img;
|
||||
newImg->setId(obj->imageId);
|
||||
// Release the image from the smart pointer because now it's owned
|
||||
// by the ImageObj userdata.
|
||||
newImg.release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Image_get_width(lua_State* L)
|
||||
{
|
||||
const auto obj = get_obj<ImageObj>(L, 1);
|
||||
@ -382,6 +480,7 @@ const luaL_Reg Image_methods[] = {
|
||||
{ "isEmpty", Image_isEmpty },
|
||||
{ "isPlain", Image_isPlain },
|
||||
{ "saveAs", Image_saveAs },
|
||||
{ "resize", Image_resize },
|
||||
{ "__gc", Image_gc },
|
||||
{ "__eq", Image_eq },
|
||||
{ nullptr, nullptr }
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -23,6 +23,14 @@ extern "C" {
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
#if LUA_TNONE != -1
|
||||
#error Invalid LUA_TNONE value
|
||||
#endif
|
||||
#if LUA_TNIL != 0
|
||||
#error Invalid LUA_TNIL value
|
||||
#endif
|
||||
#define VALID_LUATYPE(type) ((type) > 0)
|
||||
|
||||
// Some of these auxiliary methods are based on code from the Skia
|
||||
// library (SkLua.cpp file) by Google Inc.
|
||||
|
||||
|
@ -25,6 +25,11 @@ Palette* Site::palette()
|
||||
return (m_sprite ? m_sprite->palette(m_frame): nullptr);
|
||||
}
|
||||
|
||||
RgbMap* Site::rgbMap() const
|
||||
{
|
||||
return (m_sprite ? m_sprite->rgbMap(m_frame): nullptr);
|
||||
}
|
||||
|
||||
const Cel* Site::cel() const
|
||||
{
|
||||
if (m_layer)
|
||||
|
@ -19,6 +19,7 @@ namespace doc {
|
||||
class Image;
|
||||
class Layer;
|
||||
class Palette;
|
||||
class RgbMap;
|
||||
class Sprite;
|
||||
} // namespace doc
|
||||
|
||||
@ -95,6 +96,7 @@ namespace app {
|
||||
doc::Palette* palette();
|
||||
doc::Image* image(int* x = nullptr, int* y = nullptr, int* opacity = nullptr) const;
|
||||
doc::Palette* palette() const;
|
||||
doc::RgbMap* rgbMap() const;
|
||||
|
||||
private:
|
||||
Focus m_focus;
|
||||
|
99
src/app/util/resize_image.cpp
Normal file
99
src/app/util/resize_image.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/util/resize_image.h"
|
||||
|
||||
#include "app/cmd/replace_image.h"
|
||||
#include "app/cmd/set_cel_bounds.h"
|
||||
#include "app/cmd/set_cel_position.h"
|
||||
#include "app/tx.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace app {
|
||||
|
||||
doc::Image* resize_image(
|
||||
doc::Image* image,
|
||||
const gfx::SizeF& scale,
|
||||
const doc::algorithm::ResizeMethod method,
|
||||
const Palette* pal,
|
||||
const RgbMap* rgbmap)
|
||||
{
|
||||
doc::ImageSpec spec = image->spec();
|
||||
spec.setWidth(std::max(1, int(scale.w*image->width())));
|
||||
spec.setHeight(std::max(1, int(scale.h*image->height())));
|
||||
std::unique_ptr<doc::Image> newImage(
|
||||
doc::Image::create(spec));
|
||||
newImage->setMaskColor(image->maskColor());
|
||||
|
||||
doc::algorithm::fixup_image_transparent_colors(newImage.get());
|
||||
doc::algorithm::resize_image(
|
||||
image, newImage.get(),
|
||||
method,
|
||||
pal,
|
||||
rgbmap,
|
||||
newImage->maskColor());
|
||||
|
||||
return newImage.release();
|
||||
}
|
||||
|
||||
void resize_cel_image(
|
||||
Tx& tx, doc::Cel* cel,
|
||||
const gfx::SizeF& scale,
|
||||
const doc::algorithm::ResizeMethod method,
|
||||
const gfx::PointF& pivot)
|
||||
{
|
||||
// Get cel's image
|
||||
doc::Image* image = cel->image();
|
||||
if (image && !cel->link()) {
|
||||
doc::Sprite* sprite = cel->sprite();
|
||||
|
||||
// Resize the cel bounds only if it's from a reference layer
|
||||
if (cel->layer()->isReference()) {
|
||||
gfx::RectF newBounds = cel->boundsF();
|
||||
newBounds.offset(-pivot);
|
||||
newBounds *= scale;
|
||||
newBounds.offset(pivot);
|
||||
tx(new cmd::SetCelBoundsF(cel, newBounds));
|
||||
}
|
||||
else {
|
||||
// Change cel location
|
||||
const int x = cel->x() + pivot.x - scale.w*pivot.x;
|
||||
const int y = cel->y() + pivot.y - scale.h*pivot.y;
|
||||
if (cel->x() != x || cel->y() != y)
|
||||
tx(new cmd::SetCelPosition(cel, x, y));
|
||||
|
||||
// Resize the image
|
||||
const int w = std::max(1, int(scale.w*image->width()));
|
||||
const int h = std::max(1, int(scale.h*image->height()));
|
||||
doc::ImageRef newImage(
|
||||
doc::Image::create(
|
||||
image->pixelFormat(), MAX(1, w), MAX(1, h)));
|
||||
newImage->setMaskColor(image->maskColor());
|
||||
|
||||
doc::algorithm::fixup_image_transparent_colors(image);
|
||||
doc::algorithm::resize_image(
|
||||
image, newImage.get(),
|
||||
method,
|
||||
sprite->palette(cel->frame()),
|
||||
sprite->rgbMap(cel->frame()),
|
||||
(cel->layer()->isBackground() ? -1: sprite->transparentColor()));
|
||||
|
||||
tx(new cmd::ReplaceImage(sprite, cel->imageRef(), newImage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
41
src/app/util/resize_image.h
Normal file
41
src/app/util/resize_image.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Aseprite
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UTIL_RESIZE_CEL_IMAGE_H_INCLUDED
|
||||
#define APP_UTIL_RESIZE_CEL_IMAGE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/algorithm/resize_image.h"
|
||||
#include "doc/color.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace doc {
|
||||
class Cel;
|
||||
class Image;
|
||||
class Palette;
|
||||
class RgbMap;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class Tx;
|
||||
|
||||
doc::Image* resize_image(
|
||||
doc::Image* image,
|
||||
const gfx::SizeF& scale,
|
||||
const doc::algorithm::ResizeMethod method,
|
||||
const doc::Palette* pal,
|
||||
const doc::RgbMap* rgbmap);
|
||||
|
||||
void resize_cel_image(
|
||||
Tx& tx, doc::Cel* cel,
|
||||
const gfx::SizeF& scale,
|
||||
const doc::algorithm::ResizeMethod method,
|
||||
const gfx::PointF& pivot);
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -41,7 +42,12 @@ void resize_image_nearest(const Image* src, Image* dst)
|
||||
}
|
||||
}
|
||||
|
||||
void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* pal, const RgbMap* rgbmap, color_t maskColor)
|
||||
void resize_image(const Image* src,
|
||||
Image* dst,
|
||||
const ResizeMethod method,
|
||||
const Palette* pal,
|
||||
const RgbMap* rgbmap,
|
||||
const color_t maskColor)
|
||||
{
|
||||
switch (method) {
|
||||
|
||||
@ -66,6 +72,17 @@ void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palet
|
||||
int v_floor, v_floor2;
|
||||
int x, y;
|
||||
|
||||
// We cannot do interpolations between RGB values on indexed
|
||||
// images without a palette/rgbmap.
|
||||
if (dst->pixelFormat() == IMAGE_INDEXED &&
|
||||
(!pal || !rgbmap)) {
|
||||
resize_image(
|
||||
src, dst,
|
||||
RESIZE_METHOD_NEAREST_NEIGHBOR,
|
||||
pal, rgbmap, maskColor);
|
||||
return;
|
||||
}
|
||||
|
||||
u = v = 0.0;
|
||||
du = (src->width()-1) * 1.0 / (dst->width()-1);
|
||||
dv = (src->height()-1) * 1.0 / (dst->height()-1);
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -29,8 +30,12 @@ namespace doc {
|
||||
// Warning: If you are using the RESIZE_METHOD_BILINEAR, it is
|
||||
// recommended to use 'fixup_image_transparent_colors' function
|
||||
// over the source image 'src' BEFORE using this routine.
|
||||
void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* palette, const RgbMap* rgbmap,
|
||||
color_t maskColor);
|
||||
void resize_image(const Image* src,
|
||||
Image* dst,
|
||||
const ResizeMethod method,
|
||||
const Palette* palette,
|
||||
const RgbMap* rgbmap,
|
||||
const color_t maskColor);
|
||||
|
||||
// It does not modify the image to the human eye, but internally
|
||||
// tries to fixup all colors that are completely transparent
|
||||
|
@ -26,8 +26,7 @@ namespace doc {
|
||||
const color_t maskColor = 0,
|
||||
const gfx::ColorSpacePtr& colorSpace = gfx::ColorSpace::MakeNone())
|
||||
: m_colorMode(colorMode),
|
||||
m_width(width),
|
||||
m_height(height),
|
||||
m_size(width, height),
|
||||
m_maskColor(maskColor),
|
||||
m_colorSpace(colorSpace) {
|
||||
ASSERT(width > 0);
|
||||
@ -35,35 +34,33 @@ namespace doc {
|
||||
}
|
||||
|
||||
ColorMode colorMode() const { return m_colorMode; }
|
||||
int width() const { return m_width; }
|
||||
int height() const { return m_height; }
|
||||
gfx::Size size() const { return gfx::Size(m_width, m_height); }
|
||||
gfx::Rect bounds() const { return gfx::Rect(0, 0, m_width, m_height); }
|
||||
int width() const { return m_size.w; }
|
||||
int height() const { return m_size.h; }
|
||||
const gfx::Size& size() const { return m_size; }
|
||||
gfx::Rect bounds() const { return gfx::Rect(m_size); }
|
||||
const gfx::ColorSpacePtr& colorSpace() const { return m_colorSpace; }
|
||||
|
||||
// The transparent color for colored images (0 by default) or just 0 for RGBA and Grayscale
|
||||
color_t maskColor() const { return m_maskColor; }
|
||||
|
||||
void setColorMode(const ColorMode colorMode) { m_colorMode = colorMode; }
|
||||
void setWidth(const int width) { m_width = width; }
|
||||
void setHeight(const int height) { m_height = height; }
|
||||
void setWidth(const int width) { m_size.w = width; }
|
||||
void setHeight(const int height) { m_size.h = height; }
|
||||
void setMaskColor(const color_t color) { m_maskColor = color; }
|
||||
void setColorSpace(const gfx::ColorSpacePtr& cs) { m_colorSpace = cs; }
|
||||
|
||||
void setSize(const int width, const int height) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
void setSize(const int width,
|
||||
const int height) {
|
||||
m_size = gfx::Size(width, height);
|
||||
}
|
||||
|
||||
void setSize(const gfx::Size& sz) {
|
||||
m_width = sz.w;
|
||||
m_height = sz.h;
|
||||
m_size = sz;
|
||||
}
|
||||
|
||||
bool operator==(const ImageSpec& that) const {
|
||||
return (m_colorMode == that.m_colorMode &&
|
||||
m_width == that.m_width &&
|
||||
m_height == that.m_height &&
|
||||
m_size == that.m_size &&
|
||||
m_maskColor == that.m_maskColor &&
|
||||
((!m_colorSpace && !that.m_colorSpace) ||
|
||||
(m_colorSpace && that.m_colorSpace &&
|
||||
@ -75,8 +72,7 @@ namespace doc {
|
||||
|
||||
private:
|
||||
ColorMode m_colorMode;
|
||||
int m_width;
|
||||
int m_height;
|
||||
gfx::Size m_size;
|
||||
color_t m_maskColor;
|
||||
gfx::ColorSpacePtr m_colorSpace;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user