mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-28 16:20:50 +00:00
Unify all render code in one library
Changes: * Create render library (move util/render.cpp to render/render.cpp) * Move app::Zoom class to render::Zoom * Remove doc::Image::merge() member function * Add gfx::Clip helper class (to clip dst/src rectangles before a blit) * Move doc::composite_image() to render::composite_image() * Remove doc::Sprite::render() * Replace Sprite::getPixel() with render::get_sprite_pixel() * Remove doc::layer_render() function * Convert DitheringMethod to a enum class * Add AppRender to configure a render::Render with the app configuration * Move checked background preferences as document-specific configuration * Add doc::Sprite::layer() and palette() member functions * Add doc::Layer::cel() member function * Add doc::Palette::entry() member function() * Add doc::frame_t type * Move create_palette_from_rgb/convert_pixel_format to render library * ExportSpriteSheet doesn't need a temporary image now that we can specify the source rectangle in the render routine
This commit is contained in:
parent
73658399cc
commit
da1358c5dc
11
TODO.md
11
TODO.md
@ -43,8 +43,17 @@
|
||||
|
||||
# Refactoring
|
||||
|
||||
* Convert doc::PixelFormat to a enum class
|
||||
* Add doc::Spec with width/height/channels/ColorMode/ncolors
|
||||
* Convert doc::LayerIndex -> typedef int doc::layer_t;
|
||||
* Convert doc::FrameNumber -> typedef int doc::frame_t;
|
||||
* Replace doc::LayerImage::getCel() with doc::Layer::cel()
|
||||
* Replace doc::Sprite::getPalette() with doc::Sprite::palette()
|
||||
* Replace doc::Palette::getEntry() with doc::Palette::entry()
|
||||
* Remove LayerFolder, replace it with an array of layers
|
||||
* Add new "level" into Layer class
|
||||
* Refactor src/file/ in several layers.
|
||||
* Use streams instead of FILEs.
|
||||
* Use streams instead of FILEs and create load/save tests with streams.
|
||||
* Destroy modules/gui.h.
|
||||
* Convert update_screen_for_document in an event from contexts or
|
||||
something similar.
|
||||
|
@ -46,6 +46,12 @@
|
||||
<value id="FAST" value="0" />
|
||||
<value id="ROTSPRITE" value="1" />
|
||||
</enum>
|
||||
<enum id="BgType">
|
||||
<value id="CHECKED_16x16" value="0" />
|
||||
<value id="CHECKED_8x8" value="1" />
|
||||
<value id="CHECKED_4x4" value="2" />
|
||||
<value id="CHECKED_2x2" value="3" />
|
||||
</enum>
|
||||
</types>
|
||||
|
||||
<global>
|
||||
@ -116,6 +122,12 @@
|
||||
<option id="opacity" type="int" default="160" />
|
||||
<option id="auto_opacity" type="bool" default="true" />
|
||||
</section>
|
||||
<section id="bg">
|
||||
<option id="type" type="BgType" default="BgType::CHECKED_16x16" migrate="Option.CheckedBgType" />
|
||||
<option id="zoom" type="bool" default="true" migrate="Option.CheckedBgZoom" />
|
||||
<option id="color1" type="app::Color" default="app::Color::fromRgb(128, 128, 128)" migrate="Option.CheckedBgColor1" />
|
||||
<option id="color2" type="app::Color" default="app::Color::fromRgb(192, 192, 192)" migrate="Option.CheckedBgColor2" />
|
||||
</section>
|
||||
<section id="onionskin">
|
||||
<option id="active" type="bool" default="false" />
|
||||
<option id="prev_frames" type="int" default="1" />
|
||||
|
@ -28,6 +28,7 @@ set(aseprite_libraries
|
||||
cfg-lib
|
||||
css-lib
|
||||
doc-lib
|
||||
render-lib
|
||||
scripting-lib
|
||||
undo-lib
|
||||
filters-lib
|
||||
@ -210,6 +211,7 @@ add_subdirectory(base)
|
||||
add_subdirectory(cfg)
|
||||
add_subdirectory(css)
|
||||
add_subdirectory(doc)
|
||||
add_subdirectory(render)
|
||||
add_subdirectory(filters)
|
||||
add_subdirectory(fixmath)
|
||||
add_subdirectory(gen)
|
||||
@ -328,6 +330,7 @@ endfunction()
|
||||
find_tests(base base-lib ${sys_libs})
|
||||
find_tests(gfx gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||
find_tests(doc doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||
find_tests(render render-lib doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||
find_tests(css css-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||
find_tests(ui ui-lib she gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||
find_tests(app/file ${all_libs})
|
||||
|
@ -32,16 +32,17 @@ because they don't depend on any other component.
|
||||
## Level 2
|
||||
|
||||
* [filters](filters/) (base, doc, gfx): Effects for images.
|
||||
* [render](render/) (base, gfx, doc): Library to render documents.
|
||||
* [ui](ui/) (base, gfx, she): Portable UI library (buttons, windows, text fields, etc.)
|
||||
* [updater](updater/) (base, net): Component to check for updates.
|
||||
|
||||
## Level 3
|
||||
|
||||
* [iff](iff/) (base, doc): Image File Formats library (load/save documents).
|
||||
* [iff](iff/) (base, doc, render): Image File Formats library (load/save documents).
|
||||
|
||||
## Level 4
|
||||
|
||||
* [app](app/) (allegro, base, doc, filters, gfx, iff, scripting, she, ui, undo, updater, webserver)
|
||||
* [app](app/) (allegro, base, doc, filters, gfx, iff, render, scripting, she, ui, undo, updater, webserver)
|
||||
|
||||
## Level 5
|
||||
|
||||
|
@ -50,6 +50,7 @@ add_library(app-lib
|
||||
app.cpp
|
||||
app_menus.cpp
|
||||
app_options.cpp
|
||||
app_render.cpp
|
||||
backup.cpp
|
||||
check_update.cpp
|
||||
color.cpp
|
||||
@ -303,10 +304,8 @@ add_library(app-lib
|
||||
util/msk_file.cpp
|
||||
util/pic_file.cpp
|
||||
util/range_utils.cpp
|
||||
util/render.cpp
|
||||
webserver.cpp
|
||||
widget_loader.cpp
|
||||
xml_document.cpp
|
||||
xml_exception.cpp
|
||||
zoom.cpp
|
||||
${generated_files})
|
||||
|
@ -64,7 +64,6 @@
|
||||
#include "app/ui/toolbar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/boundary.h"
|
||||
#include "app/util/render.h"
|
||||
#include "app/webserver.h"
|
||||
#include "base/exception.h"
|
||||
#include "base/fs.h"
|
||||
@ -75,6 +74,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "render/render.h"
|
||||
#include "scripting/engine.h"
|
||||
#include "she/display.h"
|
||||
#include "she/error.h"
|
||||
@ -153,8 +153,6 @@ void App::initialize(const AppOptions& options)
|
||||
// init editor cursor
|
||||
Editor::editor_cursor_init();
|
||||
|
||||
// Load RenderEngine configuration
|
||||
RenderEngine::loadConfig();
|
||||
if (isPortable())
|
||||
PRINTF("Running in portable mode\n");
|
||||
|
||||
@ -492,6 +490,11 @@ App::~App()
|
||||
|
||||
m_instance = NULL;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
she::error_message(e.what());
|
||||
|
||||
// no re-throw
|
||||
}
|
||||
catch (...) {
|
||||
she::error_message("Error closing ASE.\n(uncaught exception)");
|
||||
|
||||
|
76
src/app/app_render.cpp
Normal file
76
src/app/app_render.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/* Aseprite
|
||||
* Copyright (C) 2001-2014 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app_render.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/color_utils.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
AppRender::AppRender()
|
||||
{
|
||||
}
|
||||
|
||||
AppRender::AppRender(app::Document* doc, doc::PixelFormat pixelFormat)
|
||||
{
|
||||
setupBackground(doc, pixelFormat);
|
||||
}
|
||||
|
||||
void AppRender::setupBackground(app::Document* doc, doc::PixelFormat pixelFormat)
|
||||
{
|
||||
DocumentPreferences& docPref = App::instance()->preferences().document(doc);
|
||||
render::BgType bgType;
|
||||
|
||||
gfx::Size tile;
|
||||
switch (docPref.bg.type()) {
|
||||
case app::gen::BgType::CHECKED_16x16:
|
||||
bgType = render::BgType::CHECKED;
|
||||
tile = gfx::Size(16, 16);
|
||||
break;
|
||||
case app::gen::BgType::CHECKED_8x8:
|
||||
bgType = render::BgType::CHECKED;
|
||||
tile = gfx::Size(8, 8);
|
||||
break;
|
||||
case app::gen::BgType::CHECKED_4x4:
|
||||
bgType = render::BgType::CHECKED;
|
||||
tile = gfx::Size(4, 4);
|
||||
break;
|
||||
case app::gen::BgType::CHECKED_2x2:
|
||||
bgType = render::BgType::CHECKED;
|
||||
tile = gfx::Size(2, 2);
|
||||
break;
|
||||
default:
|
||||
bgType = render::BgType::TRANSPARENT;
|
||||
break;
|
||||
}
|
||||
|
||||
setBgType(bgType);
|
||||
setBgZoom(docPref.bg.zoom());
|
||||
setBgColor1(color_utils::color_for_image(docPref.bg.color1(), pixelFormat));
|
||||
setBgColor2(color_utils::color_for_image(docPref.bg.color2(), pixelFormat));
|
||||
setBgCheckedSize(tile);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* Aseprite
|
||||
* Copyright (C) 2001-2014 David Capello
|
||||
* Copyright (C) 2001-2013 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,32 +16,24 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#ifndef APP_RENDER_H_INCLUDED
|
||||
#define APP_RENDER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/zoom.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
class Document;
|
||||
|
||||
void Zoom::in()
|
||||
{
|
||||
if (m_den > 1) {
|
||||
m_den--;
|
||||
}
|
||||
else if (m_num < 64) {
|
||||
m_num++;
|
||||
}
|
||||
}
|
||||
class AppRender : public render::Render {
|
||||
public:
|
||||
AppRender();
|
||||
AppRender(app::Document* doc, doc::PixelFormat pixelFormat);
|
||||
|
||||
void Zoom::out()
|
||||
{
|
||||
if (m_num > 1) {
|
||||
m_num--;
|
||||
}
|
||||
else if (m_den < 32) {
|
||||
m_den++;
|
||||
}
|
||||
}
|
||||
void setupBackground(app::Document* doc, doc::PixelFormat pixelFormat);
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif // APP_RENDER_H_INCLUDED
|
@ -28,6 +28,7 @@
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/point.h"
|
||||
#include "render/get_sprite_pixel.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -47,7 +48,7 @@ void ColorPicker::pickColor(const DocumentLocation& location,
|
||||
if (mode == FromComposition) { // Pick from the composed image
|
||||
m_color = app::Color::fromImage(
|
||||
location.sprite()->pixelFormat(),
|
||||
location.sprite()->getPixel(pos.x, pos.y, location.frame()));
|
||||
render::get_sprite_pixel(location.sprite(), pos.x, pos.y, location.frame()));
|
||||
|
||||
doc::CelList cels;
|
||||
location.sprite()->pickCels(pos.x, pos.y, location.frame(), 128, cels);
|
||||
|
@ -54,7 +54,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||
CmdUIOnlyFlag)
|
||||
{
|
||||
m_format = IMAGE_RGB;
|
||||
m_dithering = DITHERING_NONE;
|
||||
m_dithering = DitheringMethod::NONE;
|
||||
}
|
||||
|
||||
void ChangePixelFormatCommand::onLoadParams(Params* params)
|
||||
@ -66,9 +66,9 @@ void ChangePixelFormatCommand::onLoadParams(Params* params)
|
||||
|
||||
std::string dithering = params->get("dithering");
|
||||
if (dithering == "ordered")
|
||||
m_dithering = DITHERING_ORDERED;
|
||||
m_dithering = DitheringMethod::ORDERED;
|
||||
else
|
||||
m_dithering = DITHERING_NONE;
|
||||
m_dithering = DitheringMethod::NONE;
|
||||
}
|
||||
|
||||
bool ChangePixelFormatCommand::onEnabled(Context* context)
|
||||
@ -79,7 +79,7 @@ bool ChangePixelFormatCommand::onEnabled(Context* context)
|
||||
if (sprite != NULL &&
|
||||
sprite->pixelFormat() == IMAGE_INDEXED &&
|
||||
m_format == IMAGE_INDEXED &&
|
||||
m_dithering == DITHERING_ORDERED)
|
||||
m_dithering == DitheringMethod::ORDERED)
|
||||
return false;
|
||||
|
||||
return sprite != NULL;
|
||||
@ -93,7 +93,7 @@ bool ChangePixelFormatCommand::onChecked(Context* context)
|
||||
if (sprite != NULL &&
|
||||
sprite->pixelFormat() == IMAGE_INDEXED &&
|
||||
m_format == IMAGE_INDEXED &&
|
||||
m_dithering == DITHERING_ORDERED)
|
||||
m_dithering == DitheringMethod::ORDERED)
|
||||
return false;
|
||||
|
||||
return
|
||||
|
@ -328,17 +328,15 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
||||
columns = sheet_w / sprite->width();
|
||||
|
||||
base::UniquePtr<Image> resultImage(Image::create(sprite->pixelFormat(), sheet_w, sheet_h));
|
||||
base::UniquePtr<Image> tempImage(Image::create(sprite->pixelFormat(), sprite->width(), sprite->height()));
|
||||
doc::clear_image(resultImage, 0);
|
||||
|
||||
render::Render render;
|
||||
|
||||
int column = 0, row = 0;
|
||||
for (FrameNumber frame(0); frame<nframes; ++frame) {
|
||||
// TODO "tempImage" could not be necessary if we could specify
|
||||
// destination clipping bounds in Sprite::render() function.
|
||||
tempImage->clear(0);
|
||||
sprite->render(tempImage, 0, 0, frame);
|
||||
resultImage->copy(tempImage, column*sprite->width(), row*sprite->height(),
|
||||
0, 0, tempImage->width(), tempImage->height());
|
||||
render.renderSprite(resultImage, sprite, frame,
|
||||
gfx::Clip(column*sprite->width(), row*sprite->height(),
|
||||
sprite->bounds()));
|
||||
|
||||
if (++column >= columns) {
|
||||
column = 0;
|
||||
|
@ -165,17 +165,18 @@ protected:
|
||||
try {
|
||||
Sprite* sprite = m_document->sprite();
|
||||
FrameNumber currentFrame = m_context->activeLocation().frame();
|
||||
render::Render render;
|
||||
|
||||
// As first step, we cut each tile and add them into "animation" list.
|
||||
for (int y=m_rect.y; y<sprite->height(); y += m_rect.h) {
|
||||
for (int x=m_rect.x; x<sprite->width(); x += m_rect.w) {
|
||||
base::UniquePtr<Image> resultImage(Image::create(sprite->pixelFormat(), m_rect.w, m_rect.h));
|
||||
|
||||
// Clear the image with mask color.
|
||||
doc::clear_image(resultImage, 0);
|
||||
base::UniquePtr<Image> resultImage(
|
||||
Image::create(sprite->pixelFormat(), m_rect.w, m_rect.h));
|
||||
|
||||
// Render the portion of sheet.
|
||||
sprite->render(resultImage, -x, -y, currentFrame);
|
||||
render.renderSprite(resultImage, sprite, currentFrame,
|
||||
gfx::Clip(0, 0, x, y, m_rect.w, m_rect.h));
|
||||
|
||||
animation.push_back(resultImage);
|
||||
resultImage.release();
|
||||
}
|
||||
|
@ -98,9 +98,9 @@ void InvertMaskCommand::onExecute(Context* context)
|
||||
if (document->mask()->bitmap()) {
|
||||
// Copy the inverted region in the new mask
|
||||
doc::copy_image(mask->bitmap(),
|
||||
document->mask()->bitmap(),
|
||||
document->mask()->bounds().x,
|
||||
document->mask()->bounds().y);
|
||||
document->mask()->bitmap(),
|
||||
document->mask()->bounds().x,
|
||||
document->mask()->bounds().y);
|
||||
}
|
||||
|
||||
// We need only need the area inside the sprite
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
namespace app {
|
||||
@ -158,11 +159,11 @@ void MergeDownLayerCommand::onExecute(Context* context)
|
||||
x2-x1+1, y2-y1+1, bgcolor);
|
||||
|
||||
// Merge src_image in new_image
|
||||
doc::composite_image(new_image, src_image,
|
||||
src_cel->x()-x1,
|
||||
src_cel->y()-y1,
|
||||
src_cel->opacity(),
|
||||
static_cast<LayerImage*>(src_layer)->getBlendMode());
|
||||
render::composite_image(new_image, src_image,
|
||||
src_cel->x()-x1,
|
||||
src_cel->y()-y1,
|
||||
src_cel->opacity(),
|
||||
static_cast<LayerImage*>(src_layer)->getBlendMode());
|
||||
|
||||
if (undo.isEnabled())
|
||||
undo.pushUndoer(new undoers::SetCelPosition(undo.getObjects(), dst_cel));
|
||||
|
@ -36,10 +36,10 @@
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/util/render.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/path.h"
|
||||
#include "doc/image.h"
|
||||
#include "render/render.h"
|
||||
#include "she/system.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
@ -54,14 +54,14 @@ public:
|
||||
OptionsWindow(Context* context)
|
||||
: m_settings(context->settings())
|
||||
, m_docSettings(m_settings->getDocumentSettings(context->activeDocument()))
|
||||
, m_checked_bg_color1(new ColorButton(RenderEngine::getCheckedBgColor1(), IMAGE_RGB))
|
||||
, m_checked_bg_color2(new ColorButton(RenderEngine::getCheckedBgColor2(), IMAGE_RGB))
|
||||
, m_preferences(App::instance()->preferences())
|
||||
, m_docPref(m_preferences.document(context->activeDocument()))
|
||||
, m_checked_bg_color1(new ColorButton(m_docPref.bg.color1(), IMAGE_RGB))
|
||||
, m_checked_bg_color2(new ColorButton(m_docPref.bg.color2(), IMAGE_RGB))
|
||||
, m_pixelGridColor(new ColorButton(m_docSettings->getPixelGridColor(), IMAGE_RGB))
|
||||
, m_gridColor(new ColorButton(m_docSettings->getGridColor(), IMAGE_RGB))
|
||||
, m_cursorColor(new ColorButton(Editor::get_cursor_color(), IMAGE_RGB))
|
||||
{
|
||||
Preferences& preferences = App::instance()->preferences();
|
||||
|
||||
sectionListbox()->ChangeSelectedItem.connect(Bind<void>(&OptionsWindow::onChangeSection, this));
|
||||
cursorColorBox()->addChild(m_cursorColor);
|
||||
|
||||
@ -78,10 +78,10 @@ public:
|
||||
pixelGridAutoOpacity()->setSelected(m_docSettings->getPixelGridAutoOpacity());
|
||||
|
||||
// Others
|
||||
if (preferences.general.autoshowTimeline())
|
||||
if (m_preferences.general.autoshowTimeline())
|
||||
autotimeline()->setSelected(true);
|
||||
|
||||
if (preferences.general.expandMenubarOnMouseover())
|
||||
if (m_preferences.general.expandMenubarOnMouseover())
|
||||
expandMenubarOnMouseover()->setSelected(true);
|
||||
|
||||
if (m_settings->getCenterOnZoom())
|
||||
@ -117,10 +117,10 @@ public:
|
||||
checkedBgSize()->addItem("8x8");
|
||||
checkedBgSize()->addItem("4x4");
|
||||
checkedBgSize()->addItem("2x2");
|
||||
checkedBgSize()->setSelectedItemIndex((int)RenderEngine::getCheckedBgType());
|
||||
checkedBgSize()->setSelectedItemIndex(int(m_docPref.bg.type()));
|
||||
|
||||
// Zoom checked background
|
||||
if (RenderEngine::getCheckedBgZoom())
|
||||
if (m_docPref.bg.zoom())
|
||||
checkedBgZoom()->setSelected(true);
|
||||
|
||||
// Checked background colors
|
||||
@ -153,8 +153,6 @@ public:
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
Preferences& preferences = App::instance()->preferences();
|
||||
|
||||
Editor::set_cursor_color(m_cursorColor->getColor());
|
||||
m_docSettings->setGridColor(m_gridColor->getColor());
|
||||
m_docSettings->setGridOpacity(gridOpacity()->getValue());
|
||||
@ -163,10 +161,10 @@ public:
|
||||
m_docSettings->setPixelGridOpacity(pixelGridOpacity()->getValue());
|
||||
m_docSettings->setPixelGridAutoOpacity(pixelGridAutoOpacity()->isSelected());
|
||||
|
||||
preferences.general.autoshowTimeline(autotimeline()->isSelected());
|
||||
m_preferences.general.autoshowTimeline(autotimeline()->isSelected());
|
||||
|
||||
bool expandOnMouseover = expandMenubarOnMouseover()->isSelected();
|
||||
preferences.general.expandMenubarOnMouseover(expandOnMouseover);
|
||||
m_preferences.general.expandMenubarOnMouseover(expandOnMouseover);
|
||||
ui::MenuBar::setExpandOnMouseover(expandOnMouseover);
|
||||
|
||||
m_settings->setCenterOnZoom(centerOnZoom()->isSelected());
|
||||
@ -175,10 +173,10 @@ public:
|
||||
m_settings->setZoomWithScrollWheel(wheelZoom()->isSelected());
|
||||
m_settings->setRightClickMode(static_cast<RightClickMode>(rightClickBehavior()->getSelectedItemIndex()));
|
||||
|
||||
RenderEngine::setCheckedBgType((RenderEngine::CheckedBgType)checkedBgSize()->getSelectedItemIndex());
|
||||
RenderEngine::setCheckedBgZoom(checkedBgZoom()->isSelected());
|
||||
RenderEngine::setCheckedBgColor1(m_checked_bg_color1->getColor());
|
||||
RenderEngine::setCheckedBgColor2(m_checked_bg_color2->getColor());
|
||||
m_docPref.bg.type(app::gen::BgType(checkedBgSize()->getSelectedItemIndex()));
|
||||
m_docPref.bg.zoom(checkedBgZoom()->isSelected());
|
||||
m_docPref.bg.color1(m_checked_bg_color1->getColor());
|
||||
m_docPref.bg.color2(m_checked_bg_color2->getColor());
|
||||
|
||||
int undo_size_limit_value;
|
||||
undo_size_limit_value = undoSizeLimit()->getTextInt();
|
||||
@ -226,10 +224,10 @@ private:
|
||||
pixelGridOpacity()->setValue(200);
|
||||
pixelGridAutoOpacity()->setSelected(true);
|
||||
|
||||
checkedBgSize()->setSelectedItemIndex((int)RenderEngine::CHECKED_BG_16X16);
|
||||
checkedBgZoom()->setSelected(true);
|
||||
m_checked_bg_color1->setColor(app::Color::fromRgb(128, 128, 128));
|
||||
m_checked_bg_color2->setColor(app::Color::fromRgb(192, 192, 192));
|
||||
checkedBgSize()->setSelectedItemIndex(int(m_docPref.bg.type.defaultValue()));
|
||||
checkedBgZoom()->setSelected(m_docPref.bg.zoom.defaultValue());
|
||||
m_checked_bg_color1->setColor(m_docPref.bg.color1.defaultValue());
|
||||
m_checked_bg_color2->setColor(m_docPref.bg.color2.defaultValue());
|
||||
}
|
||||
|
||||
void onLocateCrashFolder() {
|
||||
@ -242,6 +240,8 @@ private:
|
||||
|
||||
ISettings* m_settings;
|
||||
IDocumentSettings* m_docSettings;
|
||||
Preferences& m_preferences;
|
||||
DocumentPreferences& m_docPref;
|
||||
ColorButton* m_checked_bg_color1;
|
||||
ColorButton* m_checked_bg_color2;
|
||||
ColorButton* m_pixelGridColor;
|
||||
|
@ -49,14 +49,14 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "gfx/size.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/quantization.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
#include "render/quantization.h"
|
||||
#include "ui/graphics.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
@ -587,7 +587,7 @@ void PaletteEntryEditor::onQuantizeClick(Event& ev)
|
||||
return;
|
||||
}
|
||||
|
||||
palette = quantization::create_palette_from_rgb(
|
||||
palette = render::create_palette_from_rgb(
|
||||
sprite, reader.frame(), NULL);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "ui/ui.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_render.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/context.h"
|
||||
@ -33,7 +34,6 @@
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "app/ui/keyboard_shortcuts.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/util/render.h"
|
||||
#include "doc/conversion_she.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
@ -182,18 +182,18 @@ protected:
|
||||
|
||||
virtual void onPaint(PaintEvent& ev) override {
|
||||
Graphics* g = ev.getGraphics();
|
||||
AppRender& render = Editor::renderEngine();
|
||||
render.disableOnionskin();
|
||||
render.setBgType(render::BgType::TRANSPARENT);
|
||||
|
||||
// Render sprite and leave the result in 'render' variable
|
||||
// Render sprite and leave the result in 'm_render' variable
|
||||
if (m_render == NULL) {
|
||||
RenderEngine renderEngine(
|
||||
m_doc, m_sprite,
|
||||
m_editor->layer(),
|
||||
m_editor->frame());
|
||||
|
||||
ImageBufferPtr buf = Editor::getRenderImageBuffer();
|
||||
m_render.reset(
|
||||
renderEngine.renderSprite(m_sprite->bounds(),
|
||||
m_editor->frame(), Zoom(1, 1), false, false, buf));
|
||||
m_render.reset(Image::create(IMAGE_RGB,
|
||||
m_sprite->width(), m_sprite->height(), buf));
|
||||
|
||||
render.renderSprite(
|
||||
m_render.get(), m_sprite, m_editor->frame());
|
||||
}
|
||||
|
||||
int x, y, w, h, u, v;
|
||||
@ -205,27 +205,36 @@ protected:
|
||||
if (m_tiled & TILED_X_AXIS) x = SGN(x) * (ABS(x)%w);
|
||||
if (m_tiled & TILED_Y_AXIS) y = SGN(y) * (ABS(y)%h);
|
||||
|
||||
if (m_index_bg_color == -1)
|
||||
RenderEngine::renderCheckedBackground(m_doublebuf, -m_pos.x, -m_pos.y, m_zoom);
|
||||
else
|
||||
if (m_index_bg_color == -1) {
|
||||
render.setupBackground(m_doc, m_doublebuf->pixelFormat());
|
||||
render.renderBackground(m_doublebuf,
|
||||
gfx::Clip(0, 0, -m_pos.x, -m_pos.y,
|
||||
m_doublebuf->width(), m_doublebuf->height()), m_zoom);
|
||||
}
|
||||
else {
|
||||
doc::clear_image(m_doublebuf, m_pal->getEntry(m_index_bg_color));
|
||||
}
|
||||
|
||||
switch (m_tiled) {
|
||||
case TILED_NONE:
|
||||
RenderEngine::renderImage(m_doublebuf, m_render, m_pal, x, y, m_zoom);
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, x, y,
|
||||
m_zoom, 255, BLEND_MODE_NORMAL);
|
||||
break;
|
||||
case TILED_X_AXIS:
|
||||
for (u=x-w; u<ui::display_w()+w; u+=w)
|
||||
RenderEngine::renderImage(m_doublebuf, m_render, m_pal, u, y, m_zoom);
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, u, y,
|
||||
m_zoom, 255, BLEND_MODE_NORMAL);
|
||||
break;
|
||||
case TILED_Y_AXIS:
|
||||
for (v=y-h; v<ui::display_h()+h; v+=h)
|
||||
RenderEngine::renderImage(m_doublebuf, m_render, m_pal, x, v, m_zoom);
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, x, v,
|
||||
m_zoom, 255, BLEND_MODE_NORMAL);
|
||||
break;
|
||||
case TILED_BOTH:
|
||||
for (v=y-h; v<ui::display_h()+h; v+=h)
|
||||
for (u=x-w; u<ui::display_w()+w; u+=w)
|
||||
RenderEngine::renderImage(m_doublebuf, m_render, m_pal, u, v, m_zoom);
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, u, v,
|
||||
m_zoom, 255, BLEND_MODE_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -243,7 +252,7 @@ private:
|
||||
gfx::Point m_pos;
|
||||
gfx::Point m_oldMousePos;
|
||||
gfx::Point m_delta;
|
||||
Zoom m_zoom;
|
||||
render::Zoom m_zoom;
|
||||
int m_index_bg_color;
|
||||
base::UniquePtr<Image> m_render;
|
||||
base::UniquePtr<Image> m_doublebuf;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -74,7 +75,7 @@ bool ZoomCommand::onEnabled(Context* context)
|
||||
|
||||
void ZoomCommand::onExecute(Context* context)
|
||||
{
|
||||
Zoom zoom = current_editor->zoom();
|
||||
render::Zoom zoom = current_editor->zoom();
|
||||
|
||||
switch (m_action) {
|
||||
case In:
|
||||
@ -85,12 +86,12 @@ void ZoomCommand::onExecute(Context* context)
|
||||
break;
|
||||
case Set:
|
||||
switch (m_percentage) {
|
||||
case 3200: zoom = Zoom(32, 1); break;
|
||||
case 1600: zoom = Zoom(16, 1); break;
|
||||
case 800: zoom = Zoom(8, 1); break;
|
||||
case 400: zoom = Zoom(4, 1); break;
|
||||
case 200: zoom = Zoom(2, 1); break;
|
||||
default: zoom = Zoom(1, 1); break;
|
||||
case 3200: zoom = render::Zoom(32, 1); break;
|
||||
case 1600: zoom = render::Zoom(16, 1); break;
|
||||
case 800: zoom = render::Zoom(8, 1); break;
|
||||
case 400: zoom = render::Zoom(4, 1); break;
|
||||
case 200: zoom = render::Zoom(2, 1); break;
|
||||
default: zoom = render::Zoom(1, 1); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -155,30 +155,31 @@ void FilterManagerImpl::end()
|
||||
|
||||
bool FilterManagerImpl::applyStep()
|
||||
{
|
||||
if ((m_row >= 0) && (m_row < m_h)) {
|
||||
if ((m_mask) && (m_mask->bitmap())) {
|
||||
int x = m_x - m_mask->bounds().x + m_offset_x;
|
||||
int y = m_row + m_y - m_mask->bounds().y + m_offset_y;
|
||||
|
||||
m_maskBits = m_mask->bitmap()
|
||||
->lockBits<BitmapTraits>(Image::ReadLock,
|
||||
gfx::Rect(x, y, m_w - x, m_h - y));
|
||||
|
||||
m_maskIterator = m_maskBits.begin();
|
||||
}
|
||||
|
||||
switch (m_location.sprite()->pixelFormat()) {
|
||||
case IMAGE_RGB: m_filter->applyToRgba(this); break;
|
||||
case IMAGE_GRAYSCALE: m_filter->applyToGrayscale(this); break;
|
||||
case IMAGE_INDEXED: m_filter->applyToIndexed(this); break;
|
||||
}
|
||||
++m_row;
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (m_row < 0 || m_row >= m_h)
|
||||
return false;
|
||||
|
||||
if ((m_mask) && (m_mask->bitmap())) {
|
||||
int x = m_x - m_mask->bounds().x + m_offset_x;
|
||||
int y = m_y - m_mask->bounds().y + m_offset_y + m_row;
|
||||
|
||||
if ((m_w - x < 1) || (m_h - y < 1))
|
||||
return false;
|
||||
|
||||
m_maskBits = m_mask->bitmap()
|
||||
->lockBits<BitmapTraits>(Image::ReadLock,
|
||||
gfx::Rect(x, y, m_w - x, m_h - y));
|
||||
|
||||
m_maskIterator = m_maskBits.begin();
|
||||
}
|
||||
|
||||
switch (m_location.sprite()->pixelFormat()) {
|
||||
case IMAGE_RGB: m_filter->applyToRgba(this); break;
|
||||
case IMAGE_GRAYSCALE: m_filter->applyToGrayscale(this); break;
|
||||
case IMAGE_INDEXED: m_filter->applyToIndexed(this); break;
|
||||
}
|
||||
++m_row;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilterManagerImpl::apply()
|
||||
@ -204,7 +205,7 @@ void FilterManagerImpl::apply()
|
||||
undo.pushUndoer(new undoers::ImageArea(undo.getObjects(), m_src, m_x, m_y, m_w, m_h));
|
||||
|
||||
// Copy "dst" to "src"
|
||||
copy_image(m_src, m_dst, 0, 0);
|
||||
copy_image(m_src, m_dst);
|
||||
|
||||
undo.commit();
|
||||
}
|
||||
|
@ -23,11 +23,11 @@
|
||||
#include "app/commands/filters/filter_preview.h"
|
||||
|
||||
#include "app/commands/filters/filter_manager_impl.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "ui/manager.h"
|
||||
#include "ui/message.h"
|
||||
#include "ui/widget.h"
|
||||
#include "app/util/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -75,14 +75,14 @@ bool FilterPreview::onProcessMessage(Message* msg)
|
||||
switch (msg->type()) {
|
||||
|
||||
case kOpenMessage:
|
||||
RenderEngine::setPreviewImage(
|
||||
Editor::renderEngine().setPreviewImage(
|
||||
m_filterMgr->layer(),
|
||||
m_filterMgr->frame(),
|
||||
m_filterMgr->destinationImage());
|
||||
break;
|
||||
|
||||
case kCloseMessage:
|
||||
RenderEngine::setPreviewImage(NULL, FrameNumber(0), NULL);
|
||||
Editor::renderEngine().removePreviewImage();
|
||||
|
||||
// Stop the preview timer.
|
||||
m_timer.stop();
|
||||
|
@ -59,22 +59,23 @@
|
||||
#include "app/undoers/set_sprite_transparent_color.h"
|
||||
#include "app/undoers/set_total_frames.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/context.h"
|
||||
#include "doc/document_event.h"
|
||||
#include "doc/document_observer.h"
|
||||
#include "doc/algorithm/flip_image.h"
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
#include "doc/blend.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/context.h"
|
||||
#include "doc/dirty.h"
|
||||
#include "doc/document_event.h"
|
||||
#include "doc/document_observer.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_bits.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/quantization.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -138,11 +139,10 @@ void DocumentApi::trimSprite(Sprite* sprite)
|
||||
sprite->width(),
|
||||
sprite->height()));
|
||||
Image* image = image_wrap.get();
|
||||
render::Render render;
|
||||
|
||||
for (FrameNumber frame(0); frame<sprite->totalFrames(); ++frame) {
|
||||
image->clear(0);
|
||||
|
||||
sprite->render(image, 0, 0, frame);
|
||||
render.renderSprite(image, sprite, frame);
|
||||
|
||||
// TODO configurable (what color pixel to use as "refpixel",
|
||||
// here we are using the top-left pixel by default)
|
||||
@ -190,7 +190,7 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
|
||||
}
|
||||
}
|
||||
|
||||
new_image = quantization::convert_pixel_format
|
||||
new_image = render::convert_pixel_format
|
||||
(old_image, NULL, newFormat, dithering_method, rgbmap,
|
||||
sprite->getPalette(frame),
|
||||
is_image_from_background);
|
||||
@ -726,7 +726,7 @@ void DocumentApi::moveCel(
|
||||
int blend = (srcLayer->isBackground() ?
|
||||
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
|
||||
|
||||
composite_image(dstImage, srcImage,
|
||||
render::composite_image(dstImage, srcImage,
|
||||
srcCel->x(), srcCel->y(), 255, blend);
|
||||
}
|
||||
|
||||
@ -748,7 +748,7 @@ void DocumentApi::moveCel(
|
||||
}
|
||||
|
||||
if (dstLayer->isBackground()) {
|
||||
composite_image(dstImage, srcImage,
|
||||
render::composite_image(dstImage, srcImage,
|
||||
srcCel->x(), srcCel->y(), 255, BLEND_MODE_NORMAL);
|
||||
}
|
||||
else {
|
||||
@ -792,7 +792,7 @@ void DocumentApi::copyCel(
|
||||
int blend = (srcLayer->isBackground() ?
|
||||
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
|
||||
|
||||
composite_image(dstImage, srcImage,
|
||||
render::composite_image(dstImage, srcImage,
|
||||
srcCel->x(), srcCel->y(), 255, blend);
|
||||
}
|
||||
}
|
||||
@ -996,7 +996,7 @@ void DocumentApi::backgroundFromLayer(LayerImage* layer)
|
||||
ASSERT(cel_image);
|
||||
|
||||
clear_image(bg_image, bgcolor);
|
||||
composite_image(bg_image, cel_image,
|
||||
render::composite_image(bg_image, cel_image,
|
||||
cel->x(), cel->y(),
|
||||
MID(0, cel->opacity(), 255),
|
||||
layer->getBlendMode());
|
||||
@ -1078,13 +1078,15 @@ void DocumentApi::flattenLayers(Sprite* sprite)
|
||||
configureLayerAsBackground(background);
|
||||
}
|
||||
|
||||
render::Render render;
|
||||
render.setBgType(render::BgType::NONE);
|
||||
color_t bgcolor = bgColor(background);
|
||||
|
||||
// Copy all frames to the background.
|
||||
for (FrameNumber frame(0); frame<sprite->totalFrames(); ++frame) {
|
||||
// Clear the image and render this frame.
|
||||
clear_image(image, bgcolor);
|
||||
layer_render(sprite->folder(), image, 0, 0, frame);
|
||||
render.renderSprite(image, sprite, frame);
|
||||
|
||||
cel = background->getCel(frame);
|
||||
if (cel) {
|
||||
@ -1304,17 +1306,6 @@ void DocumentApi::flipImageWithMask(Layer* layer, Image* image, const Mask* mask
|
||||
copy_image(image, flippedImage, 0, 0);
|
||||
}
|
||||
|
||||
void DocumentApi::pasteImage(Sprite* sprite, Cel* cel, const Image* src_image, int x, int y, int opacity)
|
||||
{
|
||||
ASSERT(cel != NULL);
|
||||
|
||||
Image* cel_image = cel->image();
|
||||
Image* cel_image2 = Image::createCopy(cel_image);
|
||||
composite_image(cel_image2, src_image, x-cel->x(), y-cel->y(), opacity, BLEND_MODE_NORMAL);
|
||||
|
||||
replaceStockImage(sprite, cel->imageIndex(), cel_image2); // TODO fix this, improve, avoid replacing the whole image
|
||||
}
|
||||
|
||||
void DocumentApi::copyToCurrentMask(Mask* mask)
|
||||
{
|
||||
ASSERT(m_document->mask());
|
||||
|
@ -119,7 +119,6 @@ namespace app {
|
||||
void clearMask(Cel* cel);
|
||||
void flipImage(Image* image, const gfx::Rect& bounds, doc::algorithm::FlipType flipType);
|
||||
void flipImageWithMask(Layer* layer, Image* image, const Mask* mask, doc::algorithm::FlipType flipType);
|
||||
void pasteImage(Sprite* sprite, Cel* cel, const Image* src_image, int x, int y, int opacity);
|
||||
|
||||
// Mask API
|
||||
void copyToCurrentMask(Mask* mask);
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "doc/stock.h"
|
||||
#include "gfx/packing_rects.h"
|
||||
#include "gfx/size.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
@ -380,7 +381,7 @@ void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage
|
||||
docApi.setPixelFormat(
|
||||
sample.sprite(),
|
||||
textureImage->pixelFormat(),
|
||||
DITHERING_NONE);
|
||||
DitheringMethod::NONE);
|
||||
}
|
||||
|
||||
int x = sample.inTextureBounds().x - sample.trimmedBounds().x;
|
||||
@ -443,11 +444,15 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
|
||||
|
||||
void DocumentExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int y)
|
||||
{
|
||||
render::Render render;
|
||||
|
||||
if (sample.layer()) {
|
||||
layer_render(sample.layer(), dst, x, y, sample.frame());
|
||||
render.renderLayer(dst, sample.layer(), sample.frame(),
|
||||
gfx::Clip(x, y, sample.sprite()->bounds()));
|
||||
}
|
||||
else {
|
||||
sample.sprite()->render(dst, x, y, sample.frame());
|
||||
render.renderSprite(dst, sample.sprite(), sample.frame(),
|
||||
gfx::Clip(x, y, sample.sprite()->bounds()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,9 @@
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "base/string.h"
|
||||
#include "doc/quantization.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/alert.h"
|
||||
|
||||
#include <cstring>
|
||||
@ -563,9 +564,10 @@ void fop_operate(FileOp *fop, IFileOpProgress* progress)
|
||||
fop->seq.progress_fraction = 1.0f / (double)sprite->totalFrames();
|
||||
|
||||
// For each frame in the sprite.
|
||||
render::Render render;
|
||||
for (FrameNumber frame(0); frame < sprite->totalFrames(); ++frame) {
|
||||
// Draw the "frame" in "fop->seq.image"
|
||||
sprite->render(fop->seq.image, 0, 0, frame);
|
||||
render.renderSprite(fop->seq.image, sprite, frame);
|
||||
|
||||
// Setup the palette.
|
||||
sprite->getPalette(frame)->copyColorsTo(fop->seq.palette);
|
||||
@ -672,7 +674,7 @@ void fop_post_load(FileOp* fop)
|
||||
fop->document->sprite()->getPalettes().size() <= 1 &&
|
||||
fop->document->sprite()->getPalette(FrameNumber(0))->isBlack()) {
|
||||
SharedPtr<Palette> palette
|
||||
(quantization::create_palette_from_rgb(
|
||||
(render::create_palette_from_rgb(
|
||||
fop->document->sprite(),
|
||||
FrameNumber(0), NULL));
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "app/modules/palettes.h"
|
||||
#include "base/file_handle.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
@ -160,7 +161,7 @@ bool FliFormat::onLoad(FileOp* fop)
|
||||
}
|
||||
|
||||
/* update the old image and color-map to the new ones to compare later */
|
||||
copy_image(old, bmp, 0, 0);
|
||||
copy_image(old, bmp);
|
||||
memcpy(omap, cmap, 768);
|
||||
|
||||
/* update progress */
|
||||
@ -218,6 +219,7 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
// Create the bitmaps
|
||||
base::UniquePtr<Image> bmp(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height()));
|
||||
base::UniquePtr<Image> old(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height()));
|
||||
render::Render render;
|
||||
|
||||
// Write frame by frame
|
||||
for (FrameNumber frpos(0);
|
||||
@ -232,8 +234,7 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
}
|
||||
|
||||
/* render the frame in the bitmap */
|
||||
clear_image(bmp, 0);
|
||||
layer_render(sprite->folder(), bmp, 0, 0, frpos);
|
||||
render.renderSprite(bmp, sprite, frpos);
|
||||
|
||||
/* how many times this frame should be written to get the same
|
||||
time that it has in the sprite */
|
||||
@ -250,7 +251,7 @@ bool FliFormat::onSave(FileOp* fop)
|
||||
(unsigned char *)bmp->getPixelAddress(0, 0), cmap, W_ALL);
|
||||
|
||||
/* update the old image and color-map to the new ones to compare later */
|
||||
copy_image(old, bmp, 0, 0);
|
||||
copy_image(old, bmp);
|
||||
memcpy(omap, cmap, 768);
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include "base/file_handle.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/quantization.h"
|
||||
#include "render/render.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/button.h"
|
||||
|
||||
@ -495,7 +497,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
|
||||
break;
|
||||
|
||||
case DISPOSAL_METHOD_RESTORE_PREVIOUS:
|
||||
copy_image(current_image, previous_image, 0, 0);
|
||||
copy_image(current_image, previous_image);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -504,7 +506,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
|
||||
// that we have already updated current_image from
|
||||
// previous_image).
|
||||
if (frame_it->disposal_method != DISPOSAL_METHOD_RESTORE_PREVIOUS)
|
||||
copy_image(previous_image, current_image, 0, 0);
|
||||
copy_image(previous_image, current_image);
|
||||
}
|
||||
|
||||
fop->document->sprites().add(sprite);
|
||||
@ -610,14 +612,17 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
|
||||
ColorMapObject* image_color_map = NULL;
|
||||
|
||||
render::Render render;
|
||||
render.setBgType(render::BgType::NONE);
|
||||
|
||||
// Check if the user wants one optimized palette for all frames.
|
||||
if (sprite_format != IMAGE_INDEXED &&
|
||||
gif_options->quantize() == GifOptions::QuantizeAll) {
|
||||
// Feed the optimizer with all rendered frames.
|
||||
doc::quantization::PaletteOptimizer optimizer;
|
||||
render::PaletteOptimizer optimizer;
|
||||
for (FrameNumber frame_num(0); frame_num<sprite->totalFrames(); ++frame_num) {
|
||||
clear_image(buffer_image, background_color);
|
||||
layer_render(sprite->folder(), buffer_image, 0, 0, frame_num);
|
||||
render.renderSprite(buffer_image, sprite, frame_num);
|
||||
optimizer.feedWithImage(buffer_image);
|
||||
}
|
||||
|
||||
@ -631,7 +636,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
// If the sprite is RGB or Grayscale, we must to convert it to Indexed on the fly.
|
||||
if (sprite_format != IMAGE_INDEXED) {
|
||||
clear_image(buffer_image, background_color);
|
||||
layer_render(sprite->folder(), buffer_image, 0, 0, frame_num);
|
||||
render.renderSprite(buffer_image, sprite, frame_num);
|
||||
|
||||
switch (gif_options->quantize()) {
|
||||
case GifOptions::NoQuantize:
|
||||
@ -644,7 +649,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
|
||||
std::vector<Image*> imgarray(1);
|
||||
imgarray[0] = buffer_image;
|
||||
doc::quantization::create_palette_from_images(imgarray, ¤t_palette, has_background);
|
||||
render::create_palette_from_images(imgarray, ¤t_palette, has_background);
|
||||
rgbmap.regenerate(¤t_palette, transparent_index);
|
||||
}
|
||||
break;
|
||||
@ -653,7 +658,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
break;
|
||||
}
|
||||
|
||||
quantization::convert_pixel_format(
|
||||
render::convert_pixel_format(
|
||||
buffer_image,
|
||||
current_image,
|
||||
IMAGE_INDEXED,
|
||||
@ -665,7 +670,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
// If the sprite is Indexed, we can render directly into "current_image".
|
||||
else {
|
||||
clear_image(current_image, background_color);
|
||||
layer_render(sprite->folder(), current_image, 0, 0, frame_num);
|
||||
render.renderSprite(current_image, sprite, frame_num);
|
||||
}
|
||||
|
||||
if (frame_num == 0) {
|
||||
@ -773,7 +778,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
}
|
||||
}
|
||||
|
||||
copy_image(previous_image, current_image, 0, 0);
|
||||
copy_image(previous_image, current_image);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -812,7 +817,7 @@ SharedPtr<FormatOptions> GifFormat::onGetFormatOptions(FileOp* fop)
|
||||
win.interlaced()->setSelected(gif_options->interlaced());
|
||||
|
||||
win.dither()->setEnabled(true);
|
||||
win.dither()->setSelected(gif_options->dithering() == doc::DITHERING_ORDERED);
|
||||
win.dither()->setSelected(gif_options->dithering() == doc::DitheringMethod::ORDERED);
|
||||
|
||||
win.openWindowInForeground();
|
||||
|
||||
@ -826,12 +831,12 @@ SharedPtr<FormatOptions> GifFormat::onGetFormatOptions(FileOp* fop)
|
||||
|
||||
gif_options->setInterlaced(win.interlaced()->isSelected());
|
||||
gif_options->setDithering(win.dither()->isSelected() ?
|
||||
doc::DITHERING_ORDERED:
|
||||
doc::DITHERING_NONE);
|
||||
doc::DitheringMethod::ORDERED:
|
||||
doc::DitheringMethod::NONE);
|
||||
|
||||
set_config_int("GIF", "Quantize", gif_options->quantize());
|
||||
set_config_bool("GIF", "Interlaced", gif_options->interlaced());
|
||||
set_config_int("GIF", "Dither", gif_options->dithering());
|
||||
set_config_int("GIF", "Dither", int(gif_options->dithering()));
|
||||
}
|
||||
else {
|
||||
gif_options.reset(NULL);
|
||||
|
@ -33,7 +33,7 @@ namespace app {
|
||||
GifOptions(
|
||||
Quantize quantize = QuantizeEach,
|
||||
bool interlaced = false,
|
||||
DitheringMethod dithering = doc::DITHERING_NONE)
|
||||
DitheringMethod dithering = doc::DitheringMethod::NONE)
|
||||
: m_quantize(quantize)
|
||||
, m_interlaced(interlaced)
|
||||
, m_dithering(dithering) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "base/cfile.h"
|
||||
#include "base/file_handle.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -280,9 +281,9 @@ bool IcoFormat::onSave(FileOp* fop)
|
||||
sprite->width(),
|
||||
sprite->height()));
|
||||
|
||||
render::Render render;
|
||||
for (n=FrameNumber(0); n<num; ++n) {
|
||||
clear_image(image, 0);
|
||||
layer_render(sprite->folder(), image, 0, 0, n);
|
||||
render.renderSprite(image, sprite, n);
|
||||
|
||||
bpp = (sprite->pixelFormat() == IMAGE_INDEXED) ? 8 : 24;
|
||||
bw = (((image->width() * bpp / 8) + 3) / 4) * 4;
|
||||
|
@ -21,13 +21,14 @@
|
||||
#endif
|
||||
|
||||
#include "base/unique_ptr.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -40,6 +41,7 @@ LayerImage* create_flatten_layer_copy(Sprite* dstSprite, const Layer* srcLayer,
|
||||
FrameNumber frmin, FrameNumber frmax)
|
||||
{
|
||||
base::UniquePtr<LayerImage> flatLayer(new LayerImage(dstSprite));
|
||||
render::Render render;
|
||||
|
||||
for (FrameNumber frame=frmin; frame<=frmax; ++frame) {
|
||||
// Does this frame have cels to render?
|
||||
@ -55,9 +57,9 @@ LayerImage* create_flatten_layer_copy(Sprite* dstSprite, const Layer* srcLayer,
|
||||
base::UniquePtr<Cel> cel(new Cel(frame, imageIndex));
|
||||
cel->setPosition(bounds.x, bounds.y);
|
||||
|
||||
// Clear the image and render this frame.
|
||||
image->clear(0);
|
||||
layer_render(srcLayer, image, -bounds.x, -bounds.y, frame);
|
||||
// Render this frame.
|
||||
render.renderLayer(image, srcLayer, frame,
|
||||
gfx::Clip(0, 0, bounds));
|
||||
|
||||
// Add the cel (and release the base::UniquePtr).
|
||||
flatLayer->addCel(cel);
|
||||
|
@ -57,23 +57,8 @@ void Preferences::save()
|
||||
for (auto& pair : m_tools)
|
||||
pair.second->save();
|
||||
|
||||
for (auto& pair : m_docs) {
|
||||
app::Document* doc = pair.first;
|
||||
bool specific_file = false;
|
||||
|
||||
if (doc && doc->isAssociatedToFile()) {
|
||||
push_config_state();
|
||||
set_config_file(docConfigFileName(doc).c_str());
|
||||
specific_file = true;
|
||||
}
|
||||
|
||||
pair.second->save();
|
||||
|
||||
if (specific_file) {
|
||||
flush_config_file();
|
||||
pop_config_state();
|
||||
}
|
||||
}
|
||||
for (auto& pair : m_docs)
|
||||
saveDocPref(pair.first, pair.second);
|
||||
|
||||
flush_config_file();
|
||||
}
|
||||
@ -98,8 +83,6 @@ ToolPreferences& Preferences::tool(tools::Tool* tool)
|
||||
|
||||
DocumentPreferences& Preferences::document(app::Document* document)
|
||||
{
|
||||
ASSERT(document != NULL);
|
||||
|
||||
auto it = m_docs.find(document);
|
||||
if (it != m_docs.end()) {
|
||||
return *it->second;
|
||||
@ -111,6 +94,18 @@ DocumentPreferences& Preferences::document(app::Document* document)
|
||||
}
|
||||
}
|
||||
|
||||
void Preferences::onRemoveDocument(doc::Document* doc)
|
||||
{
|
||||
ASSERT(dynamic_cast<app::Document*>(doc));
|
||||
|
||||
auto it = m_docs.find(static_cast<app::Document*>(doc));
|
||||
if (it != m_docs.end()) {
|
||||
saveDocPref(it->first, it->second);
|
||||
delete it->second;
|
||||
m_docs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Preferences::docConfigFileName(app::Document* doc)
|
||||
{
|
||||
if (!doc)
|
||||
@ -127,4 +122,22 @@ std::string Preferences::docConfigFileName(app::Document* doc)
|
||||
return rf.getFirstOrCreateDefault();
|
||||
}
|
||||
|
||||
void Preferences::saveDocPref(app::Document* doc, app::DocumentPreferences* docPref)
|
||||
{
|
||||
bool specific_file = false;
|
||||
|
||||
if (doc && doc->isAssociatedToFile()) {
|
||||
push_config_state();
|
||||
set_config_file(docConfigFileName(doc).c_str());
|
||||
specific_file = true;
|
||||
}
|
||||
|
||||
docPref->save();
|
||||
|
||||
if (specific_file) {
|
||||
flush_config_file();
|
||||
pop_config_state();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/pref/option.h"
|
||||
#include "doc/documents_observer.h"
|
||||
|
||||
#include "generated_pref_types.h"
|
||||
|
||||
@ -38,7 +39,8 @@ namespace app {
|
||||
typedef app::gen::ToolPref ToolPreferences;
|
||||
typedef app::gen::DocPref DocumentPreferences;
|
||||
|
||||
class Preferences : public app::gen::GlobalPref {
|
||||
class Preferences : public app::gen::GlobalPref
|
||||
, public doc::DocumentsObserver {
|
||||
public:
|
||||
Preferences();
|
||||
~Preferences();
|
||||
@ -47,10 +49,14 @@ namespace app {
|
||||
void save();
|
||||
|
||||
ToolPreferences& tool(tools::Tool* tool);
|
||||
DocumentPreferences& document(app::Document* document);
|
||||
DocumentPreferences& document(app::Document* doc);
|
||||
|
||||
protected:
|
||||
void onRemoveDocument(doc::Document* doc) override;
|
||||
|
||||
private:
|
||||
std::string docConfigFileName(app::Document* doc);
|
||||
void saveDocPref(app::Document* doc, app::DocumentPreferences* docPref);
|
||||
|
||||
std::map<std::string, app::ToolPreferences*> m_tools;
|
||||
std::map<app::Document*, app::DocumentPreferences*> m_docs;
|
||||
|
@ -23,10 +23,10 @@
|
||||
#include "app/thumbnail_generator.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/app_render.h"
|
||||
#include "app/document.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/file_system.h"
|
||||
#include "app/util/render.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/thread.h"
|
||||
@ -79,15 +79,14 @@ private:
|
||||
// The palette to convert the Image
|
||||
m_palette.reset(new Palette(*sprite->getPalette(FrameNumber(0))));
|
||||
|
||||
// Render the 'sprite' in one plain 'image'
|
||||
RenderEngine renderEngine(m_fop->document,
|
||||
sprite, NULL, FrameNumber(0));
|
||||
// Render first frame of the sprite in 'image'
|
||||
base::UniquePtr<Image> image(Image::create(
|
||||
sprite->pixelFormat(), sprite->width(), sprite->height()));
|
||||
|
||||
doc::ImageBufferPtr thumbnail_buffer(new doc::ImageBuffer);
|
||||
base::UniquePtr<Image> image(renderEngine.renderSprite(
|
||||
sprite->bounds(), FrameNumber(0),
|
||||
Zoom(1, 1), true, false,
|
||||
thumbnail_buffer));
|
||||
AppRender render;
|
||||
render.setupBackground(NULL, image->pixelFormat());
|
||||
render.setBgType(render::BgType::CHECKED);
|
||||
render.renderSprite(image, sprite, FrameNumber(0));
|
||||
|
||||
// Calculate the thumbnail size
|
||||
int thumb_w = MAX_THUMBNAIL_SIZE * image->width() / MAX(image->width(), image->height());
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include "app/settings/selection_mode.h"
|
||||
#include "app/tools/trace_policy.h"
|
||||
#include "app/zoom.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "filters/tiled_mode.h"
|
||||
#include "gfx/point.h"
|
||||
@ -40,6 +39,10 @@ namespace doc {
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace render {
|
||||
class Zoom;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class Context;
|
||||
class Document;
|
||||
@ -129,7 +132,7 @@ namespace app {
|
||||
virtual gfx::Point getMaskOrigin() = 0;
|
||||
|
||||
// Returns the zoom
|
||||
virtual const Zoom& zoom() = 0;
|
||||
virtual const render::Zoom& zoom() = 0;
|
||||
|
||||
// Return the mouse button which start the tool-loop. It can be used
|
||||
// by some tools that instead of using the primary/secondary color
|
||||
|
@ -29,11 +29,11 @@
|
||||
#include "app/tools/intertwine.h"
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/util/render.h"
|
||||
#include "gfx/region.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/region.h"
|
||||
|
||||
namespace app {
|
||||
namespace tools {
|
||||
@ -69,7 +69,7 @@ void ToolLoopManager::prepareLoop(const Pointer& pointer)
|
||||
|
||||
// Prepare preview image (the destination image will be our preview
|
||||
// in the tool-loop time, so we can see what we are drawing)
|
||||
RenderEngine::setPreviewImage(
|
||||
Editor::renderEngine().setPreviewImage(
|
||||
m_toolLoop->getLayer(),
|
||||
m_toolLoop->getFrame(),
|
||||
m_toolLoop->getDstImage());
|
||||
@ -78,7 +78,7 @@ void ToolLoopManager::prepareLoop(const Pointer& pointer)
|
||||
void ToolLoopManager::releaseLoop(const Pointer& pointer)
|
||||
{
|
||||
// No more preview image
|
||||
RenderEngine::setPreviewImage(NULL, FrameNumber(0), NULL);
|
||||
Editor::renderEngine().removePreviewImage();
|
||||
}
|
||||
|
||||
void ToolLoopManager::pressKey(ui::KeyScancode key)
|
||||
|
@ -53,7 +53,6 @@
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/boundary.h"
|
||||
#include "app/util/misc.h"
|
||||
#include "app/util/render.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/conversion_she.h"
|
||||
@ -70,6 +69,7 @@ namespace app {
|
||||
using namespace app::skin;
|
||||
using namespace gfx;
|
||||
using namespace ui;
|
||||
using namespace render;
|
||||
|
||||
class EditorPreRenderImpl : public EditorPreRender {
|
||||
public:
|
||||
@ -139,7 +139,11 @@ private:
|
||||
Graphics* m_g;
|
||||
};
|
||||
|
||||
static doc::ImageBufferPtr render_buffer;
|
||||
// static
|
||||
doc::ImageBufferPtr Editor::m_renderBuffer;
|
||||
|
||||
// static
|
||||
AppRender Editor::m_renderEngine;
|
||||
|
||||
Editor::Editor(Document* document, EditorFlags flags)
|
||||
: Widget(editor_type())
|
||||
@ -368,56 +372,83 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
|
||||
rc.h = clip.y+clip.h-dest_y;
|
||||
}
|
||||
|
||||
// Draw the sprite
|
||||
if ((rc.w > 0) && (rc.h > 0)) {
|
||||
RenderEngine renderEngine(m_document, m_sprite, m_layer, m_frame);
|
||||
if (rc.isEmpty())
|
||||
return;
|
||||
|
||||
// Generate the rendered image
|
||||
if (!render_buffer)
|
||||
render_buffer.reset(new doc::ImageBuffer());
|
||||
// Generate the rendered image
|
||||
if (!m_renderBuffer)
|
||||
m_renderBuffer.reset(new doc::ImageBuffer());
|
||||
|
||||
base::UniquePtr<Image> rendered(NULL);
|
||||
try {
|
||||
// Generate a "expose sprite pixels" notification. This is used by
|
||||
// tool managers that need to validate this region (copy pixels from
|
||||
// the original cel) before it can be used by the RenderEngine.
|
||||
{
|
||||
gfx::Rect expose = m_zoom.remove(rc);
|
||||
// If the zoom level is less than 100%, we add extra pixels to
|
||||
// the exposed area. Those pixels could be shown in the
|
||||
// rendering process depending on each cel position.
|
||||
// E.g. when we are drawing in a cel with position < (0,0)
|
||||
if (m_zoom.scale() < 1.0)
|
||||
expose.enlarge(1./m_zoom.scale());
|
||||
m_document->notifyExposeSpritePixels(m_sprite, gfx::Region(expose));
|
||||
}
|
||||
|
||||
rendered.reset(renderEngine.renderSprite(
|
||||
rc, m_frame, m_zoom, true,
|
||||
((m_flags & kShowOnionskin) == kShowOnionskin),
|
||||
render_buffer));
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Console::showException(e);
|
||||
base::UniquePtr<Image> rendered(NULL);
|
||||
try {
|
||||
// Generate a "expose sprite pixels" notification. This is used by
|
||||
// tool managers that need to validate this region (copy pixels from
|
||||
// the original cel) before it can be used by the RenderEngine.
|
||||
{
|
||||
gfx::Rect expose = m_zoom.remove(rc);
|
||||
// If the zoom level is less than 100%, we add extra pixels to
|
||||
// the exposed area. Those pixels could be shown in the
|
||||
// rendering process depending on each cel position.
|
||||
// E.g. when we are drawing in a cel with position < (0,0)
|
||||
if (m_zoom.scale() < 1.0)
|
||||
expose.enlarge(1./m_zoom.scale());
|
||||
m_document->notifyExposeSpritePixels(m_sprite, gfx::Region(expose));
|
||||
}
|
||||
|
||||
if (rendered) {
|
||||
// Pre-render decorator.
|
||||
if ((m_flags & kShowDecorators) && m_decorator) {
|
||||
EditorPreRenderImpl preRender(this, rendered,
|
||||
Point(-rc.x, -rc.y), m_zoom);
|
||||
m_decorator->preRenderDecorator(&preRender);
|
||||
}
|
||||
// Create a temporary RGB bitmap to draw all to it
|
||||
rendered.reset(Image::create(IMAGE_RGB, rc.w, rc.h, m_renderBuffer));
|
||||
m_renderEngine.setupBackground(m_document, rendered->pixelFormat());
|
||||
m_renderEngine.setOnionskin(render::OnionskinType::NONE, 0, 0, 0, 0);
|
||||
|
||||
// Convert the render to a she::Surface
|
||||
she::Surface* tmp(she::instance()->createRgbaSurface(rc.w, rc.h));
|
||||
if (tmp->nativeHandle()) {
|
||||
convert_image_to_surface(rendered, m_sprite->getPalette(m_frame),
|
||||
tmp, 0, 0, 0, 0, rc.w, rc.h);
|
||||
g->blit(tmp, 0, 0, dest_x, dest_y, rc.w, rc.h);
|
||||
if ((m_flags & kShowOnionskin) == kShowOnionskin) {
|
||||
IDocumentSettings* docSettings = UIContext::instance()
|
||||
->settings()->getDocumentSettings(m_document);
|
||||
|
||||
if (docSettings->getUseOnionskin()) {
|
||||
m_renderEngine.setOnionskin(
|
||||
(docSettings->getOnionskinType() == IDocumentSettings::Onionskin_Merge ?
|
||||
render::OnionskinType::MERGE:
|
||||
(docSettings->getOnionskinType() == IDocumentSettings::Onionskin_RedBlueTint ?
|
||||
render::OnionskinType::RED_BLUE_TINT:
|
||||
render::OnionskinType::NONE)),
|
||||
docSettings->getOnionskinPrevFrames(),
|
||||
docSettings->getOnionskinNextFrames(),
|
||||
docSettings->getOnionskinOpacityBase(),
|
||||
docSettings->getOnionskinOpacityStep());
|
||||
}
|
||||
tmp->dispose();
|
||||
}
|
||||
|
||||
m_renderEngine.setExtraImage(
|
||||
m_document->getExtraCel(),
|
||||
m_document->getExtraCelImage(),
|
||||
m_document->getExtraCelBlendMode(),
|
||||
m_layer, m_frame);
|
||||
|
||||
m_renderEngine.renderSprite(rendered, m_sprite, m_frame,
|
||||
gfx::Clip(0, 0, rc), m_zoom);
|
||||
|
||||
m_renderEngine.removeExtraImage();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
|
||||
if (rendered) {
|
||||
// Pre-render decorator.
|
||||
if ((m_flags & kShowDecorators) && m_decorator) {
|
||||
EditorPreRenderImpl preRender(this, rendered,
|
||||
Point(-rc.x, -rc.y), m_zoom);
|
||||
m_decorator->preRenderDecorator(&preRender);
|
||||
}
|
||||
|
||||
// Convert the render to a she::Surface
|
||||
she::Surface* tmp(she::instance()->createRgbaSurface(rc.w, rc.h));
|
||||
if (tmp->nativeHandle()) {
|
||||
convert_image_to_surface(rendered, m_sprite->getPalette(m_frame),
|
||||
tmp, 0, 0, 0, 0, rc.w, rc.h);
|
||||
g->blit(tmp, 0, 0, dest_x, dest_y, rc.w, rc.h);
|
||||
}
|
||||
tmp->dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,7 +752,7 @@ void Editor::flashCurrentLayer()
|
||||
int x, y;
|
||||
const Image* src_image = loc.image(&x, &y);
|
||||
if (src_image) {
|
||||
RenderEngine::setPreviewImage(NULL, FrameNumber(0), NULL);
|
||||
m_renderEngine.removePreviewImage();
|
||||
|
||||
m_document->prepareExtraCel(m_sprite->bounds(), 255);
|
||||
Image* flash_image = m_document->getExtraCelImage();
|
||||
@ -1471,7 +1502,7 @@ void Editor::notifyScrollChanged()
|
||||
// static
|
||||
ImageBufferPtr Editor::getRenderImageBuffer()
|
||||
{
|
||||
return render_buffer;
|
||||
return m_renderBuffer;
|
||||
}
|
||||
|
||||
void Editor::onSetTiledMode(filters::TiledMode mode)
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define APP_UI_EDITOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/app_render.h"
|
||||
#include "app/color.h"
|
||||
#include "app/document.h"
|
||||
#include "app/settings/selection_mode.h"
|
||||
@ -27,12 +28,12 @@
|
||||
#include "app/ui/editor/editor_observers.h"
|
||||
#include "app/ui/editor/editor_state.h"
|
||||
#include "app/ui/editor/editor_states_history.h"
|
||||
#include "app/zoom.h"
|
||||
#include "base/connection.h"
|
||||
#include "doc/document_observer.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "gfx/fwd.h"
|
||||
#include "render/zoom.h"
|
||||
#include "ui/base.h"
|
||||
#include "ui/timer.h"
|
||||
#include "ui/widget.h"
|
||||
@ -123,18 +124,18 @@ namespace app {
|
||||
void setLayer(const Layer* layer);
|
||||
void setFrame(FrameNumber frame);
|
||||
|
||||
const Zoom& zoom() const { return m_zoom; }
|
||||
const render::Zoom& zoom() const { return m_zoom; }
|
||||
int offsetX() const { return m_offset_x; }
|
||||
int offsetY() const { return m_offset_y; }
|
||||
int cursorThick() { return m_cursorThick; }
|
||||
|
||||
void setZoom(Zoom zoom) { m_zoom = zoom; }
|
||||
void setZoom(render::Zoom zoom) { m_zoom = zoom; }
|
||||
void setOffsetX(int x) { m_offset_x = x; }
|
||||
void setOffsetY(int y) { m_offset_y = y; }
|
||||
|
||||
void setDefaultScroll();
|
||||
void setEditorScroll(const gfx::Point& scroll, bool blit_valid_rgn);
|
||||
void setEditorZoom(Zoom zoom);
|
||||
void setEditorZoom(render::Zoom zoom);
|
||||
|
||||
// Updates the Editor's view.
|
||||
void updateEditor();
|
||||
@ -188,7 +189,7 @@ namespace app {
|
||||
// Returns true if the cursor is inside the active mask/selection.
|
||||
bool isInsideSelection();
|
||||
|
||||
void setZoomAndCenterInMouse(Zoom zoom,
|
||||
void setZoomAndCenterInMouse(render::Zoom zoom,
|
||||
const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
|
||||
|
||||
void pasteImage(const Image* image, const gfx::Point& pos);
|
||||
@ -203,6 +204,8 @@ namespace app {
|
||||
// E.g. It can be re-used by PreviewCommand
|
||||
static ImageBufferPtr getRenderImageBuffer();
|
||||
|
||||
static AppRender& renderEngine() { return m_renderEngine; }
|
||||
|
||||
// in cursor.cpp
|
||||
|
||||
static app::Color get_cursor_color();
|
||||
@ -268,7 +271,7 @@ namespace app {
|
||||
Sprite* m_sprite; // Active sprite in the editor
|
||||
Layer* m_layer; // Active layer in the editor
|
||||
FrameNumber m_frame; // Active frame in the editor
|
||||
Zoom m_zoom; // Zoom in the editor
|
||||
render::Zoom m_zoom; // Zoom in the editor
|
||||
|
||||
// Drawing cursor
|
||||
int m_cursorThick;
|
||||
@ -311,6 +314,9 @@ namespace app {
|
||||
EditorFlags m_flags;
|
||||
|
||||
bool m_secondaryButton;
|
||||
|
||||
static doc::ImageBufferPtr m_renderBuffer;
|
||||
static AppRender m_renderEngine;
|
||||
};
|
||||
|
||||
ui::WidgetType editor_type();
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "doc/mask.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/region.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -455,7 +456,7 @@ void PixelsMovement::stampImage()
|
||||
gfx::Region modifiedRegion(expand.getDestCanvas()->bounds());
|
||||
expand.validateDestCanvas(modifiedRegion);
|
||||
|
||||
composite_image(
|
||||
render::composite_image(
|
||||
expand.getDestCanvas(), image,
|
||||
-expand.getCel()->x(),
|
||||
-expand.getCel()->y(),
|
||||
|
@ -181,7 +181,7 @@ void SelectBoxState::preRenderDecorator(EditorPreRender* render)
|
||||
void SelectBoxState::postRenderDecorator(EditorPostRender* render)
|
||||
{
|
||||
Editor* editor = render->getEditor();
|
||||
Zoom zoom = editor->zoom();
|
||||
render::Zoom zoom = editor->zoom();
|
||||
gfx::Rect vp = View::getView(editor)->getViewportBounds();
|
||||
vp.w += zoom.apply(1);
|
||||
vp.h += zoom.apply(1);
|
||||
|
@ -375,7 +375,7 @@ bool StandbyState::onMouseWheel(Editor* editor, MouseMessage* msg)
|
||||
|
||||
case WHEEL_ZOOM: {
|
||||
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
||||
Zoom zoom = editor->zoom();
|
||||
render::Zoom zoom = editor->zoom();
|
||||
if (dz < 0) {
|
||||
while (dz++ < 0)
|
||||
zoom.in();
|
||||
|
@ -260,7 +260,7 @@ public:
|
||||
bool useMask() override { return m_useMask; }
|
||||
Mask* getMask() override { return m_mask; }
|
||||
gfx::Point getMaskOrigin() override { return m_maskOrigin; }
|
||||
const Zoom& zoom() override { return m_editor->zoom(); }
|
||||
const render::Zoom& zoom() override { return m_editor->zoom(); }
|
||||
ToolLoop::Button getMouseButton() override { return m_button; }
|
||||
int getPrimaryColor() override { return m_primary_color; }
|
||||
void setPrimaryColor(int color) override { m_primary_color = color; }
|
||||
|
@ -57,7 +57,7 @@ bool ZoomingState::onMouseDown(Editor* editor, MouseMessage* msg)
|
||||
bool ZoomingState::onMouseUp(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
if (!m_moved) {
|
||||
Zoom zoom = editor->zoom();
|
||||
render::Zoom zoom = editor->zoom();
|
||||
|
||||
if (msg->left())
|
||||
zoom.in();
|
||||
@ -77,7 +77,7 @@ bool ZoomingState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
{
|
||||
gfx::Point pt = (msg->position() - m_startPos);
|
||||
int length = ABS(pt.x);
|
||||
Zoom zoom = m_startZoom;
|
||||
render::Zoom zoom = m_startZoom;
|
||||
|
||||
if (length > 0) {
|
||||
if (pt.x > 0) {
|
||||
|
@ -21,8 +21,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/ui/editor/editor_state.h"
|
||||
#include "app/zoom.h"
|
||||
#include "gfx/point.h"
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -41,7 +41,7 @@ namespace app {
|
||||
|
||||
private:
|
||||
gfx::Point m_startPos;
|
||||
Zoom m_startZoom;
|
||||
render::Zoom m_startZoom;
|
||||
bool m_moved;
|
||||
};
|
||||
|
||||
|
@ -245,7 +245,7 @@ void MiniEditorWindow::updateUsingEditor(Editor* editor)
|
||||
addChild(m_docView);
|
||||
|
||||
miniEditor = m_docView->getEditor();
|
||||
miniEditor->setZoom(Zoom(1, 1));
|
||||
miniEditor->setZoom(render::Zoom(1, 1));
|
||||
miniEditor->setState(EditorStatePtr(new EditorState));
|
||||
layout();
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "app/document.h"
|
||||
#include "app/document_location.h"
|
||||
#include "app/modules/editors.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/settings/ui_settings_impl.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
#include "app/ui/document_view.h"
|
||||
@ -47,6 +48,7 @@ UIContext::UIContext()
|
||||
, m_lastSelectedView(NULL)
|
||||
{
|
||||
documents().addObserver(static_cast<UISettingsImpl*>(settings()));
|
||||
documents().addObserver(&App::instance()->preferences());
|
||||
|
||||
ASSERT(m_instance == NULL);
|
||||
m_instance = this;
|
||||
@ -58,6 +60,7 @@ UIContext::~UIContext()
|
||||
m_instance = NULL;
|
||||
|
||||
documents().removeObserver(static_cast<UISettingsImpl*>(settings()));
|
||||
documents().removeObserver(&App::instance()->preferences());
|
||||
|
||||
// The context must be empty at this point. (It's to check if the UI
|
||||
// is working correctly, i.e. closing all files when the user can
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "app/util/clipboard.h"
|
||||
#include "app/util/misc.h"
|
||||
#include "doc/doc.h"
|
||||
#include "render/quantization.h"
|
||||
#include "undo/undo_history.h"
|
||||
|
||||
#if defined WIN32
|
||||
@ -275,9 +276,9 @@ void clipboard::paste()
|
||||
else {
|
||||
RgbMap* dst_rgbmap = dstSpr->getRgbMap(editor->frame());
|
||||
|
||||
src_image = quantization::convert_pixel_format(
|
||||
src_image = render::convert_pixel_format(
|
||||
clipboard_image, NULL, dstSpr->pixelFormat(),
|
||||
DITHERING_NONE, dst_rgbmap, clipboard_palette,
|
||||
DitheringMethod::NONE, dst_rgbmap, clipboard_palette,
|
||||
false);
|
||||
}
|
||||
|
||||
|
@ -290,9 +290,10 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
fill_rect(m_srcImage, rc, m_srcImage->maskColor());
|
||||
|
||||
for (const auto& rc : rgnToValidate)
|
||||
m_srcImage->copy(m_celImage, rc.x, rc.y,
|
||||
rc.x+m_bounds.x-m_origCelPos.x,
|
||||
rc.y+m_bounds.y-m_origCelPos.y, rc.w, rc.h);
|
||||
m_srcImage->copy(m_celImage,
|
||||
gfx::Clip(rc.x, rc.y,
|
||||
rc.x+m_bounds.x-m_origCelPos.x,
|
||||
rc.y+m_bounds.y-m_origCelPos.y, rc.w, rc.h));
|
||||
}
|
||||
else {
|
||||
for (const auto& rc : rgnToValidate)
|
||||
@ -335,9 +336,10 @@ void ExpandCelCanvas::validateDestCanvas(const gfx::Region& rgn)
|
||||
fill_rect(m_dstImage, rc, m_dstImage->maskColor());
|
||||
|
||||
for (const auto& rc : rgnToValidate)
|
||||
m_dstImage->copy(src, rc.x, rc.y,
|
||||
rc.x+m_bounds.x-src_x,
|
||||
rc.y+m_bounds.y-src_y, rc.w, rc.h);
|
||||
m_dstImage->copy(src,
|
||||
gfx::Clip(rc.x, rc.y,
|
||||
rc.x+m_bounds.x-src_x,
|
||||
rc.y+m_bounds.y-src_y, rc.w, rc.h));
|
||||
}
|
||||
else {
|
||||
for (const auto& rc : rgnToValidate)
|
||||
@ -366,7 +368,8 @@ void ExpandCelCanvas::copyValidDestToSourceCanvas(const gfx::Region& rgn)
|
||||
rgn2.createIntersection(rgn2, m_validSrcRegion);
|
||||
rgn2.createIntersection(rgn2, m_validDstRegion);
|
||||
for (const auto& rc : rgn2)
|
||||
m_srcImage->copy(m_dstImage, rc.x, rc.y, rc.x, rc.y, rc.w, rc.h);
|
||||
m_srcImage->copy(m_dstImage,
|
||||
gfx::Clip(rc.x, rc.y, rc.x, rc.y, rc.w, rc.h));
|
||||
}
|
||||
|
||||
void ExpandCelCanvas::copyValidDestToOriginalCel()
|
||||
@ -374,9 +377,10 @@ void ExpandCelCanvas::copyValidDestToOriginalCel()
|
||||
// Copy valid destination region to the m_celImage
|
||||
for (const auto& rc : m_validDstRegion) {
|
||||
m_celImage->copy(m_dstImage,
|
||||
rc.x-m_bounds.x+m_origCelPos.x,
|
||||
rc.y-m_bounds.y+m_origCelPos.y,
|
||||
rc.x, rc.y, rc.w, rc.h);
|
||||
gfx::Clip(
|
||||
rc.x-m_bounds.x+m_origCelPos.x,
|
||||
rc.y-m_bounds.y+m_origCelPos.y,
|
||||
rc.x, rc.y, rc.w, rc.h));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ Mask* load_msk_file(const char* filename)
|
||||
if (image != NULL && (image->pixelFormat() == IMAGE_BITMAP)) {
|
||||
mask = new Mask();
|
||||
mask->replace(gfx::Rect(x, y, image->width(), image->height()));
|
||||
mask->bitmap()->copy(image, 0, 0, 0, 0, image->width(), image->height());
|
||||
mask->bitmap()->copy(image, gfx::Clip(image->bounds()));
|
||||
mask->shrink();
|
||||
}
|
||||
}
|
||||
|
@ -1,753 +0,0 @@
|
||||
/* Aseprite
|
||||
* Copyright (C) 2001-2014 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/util/render.h"
|
||||
|
||||
#include "app/color_utils.h"
|
||||
#include "app/document.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "doc/doc.h"
|
||||
#include "app/settings/document_settings.h"
|
||||
#include "app/settings/settings.h"
|
||||
#include "app/ui_context.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Zoomed merge
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
class BlenderHelper
|
||||
{
|
||||
BLEND_COLOR m_blend_color;
|
||||
uint32_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_color = SrcTraits::get_blender(blend_mode);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline void operator()(typename DstTraits::pixel_t& scanline,
|
||||
const typename DstTraits::pixel_t& dst,
|
||||
const typename SrcTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_mask_color)
|
||||
scanline = (*m_blend_color)(dst, src, opacity);
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, GrayscaleTraits>
|
||||
{
|
||||
BLEND_COLOR m_blend_color;
|
||||
uint32_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_color = RgbTraits::get_blender(blend_mode);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline void operator()(RgbTraits::pixel_t& scanline,
|
||||
const RgbTraits::pixel_t& dst,
|
||||
const GrayscaleTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_mask_color) {
|
||||
int v = graya_getv(src);
|
||||
scanline = (*m_blend_color)(dst, rgba(v, v, v, graya_geta(src)), opacity);
|
||||
}
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, IndexedTraits>
|
||||
{
|
||||
const Palette* m_pal;
|
||||
int m_blend_mode;
|
||||
uint32_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_mode = blend_mode;
|
||||
m_mask_color = src->maskColor();
|
||||
m_pal = pal;
|
||||
}
|
||||
inline void operator()(RgbTraits::pixel_t& scanline,
|
||||
const RgbTraits::pixel_t& dst,
|
||||
const IndexedTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (m_blend_mode == BLEND_MODE_COPY) {
|
||||
scanline = m_pal->getEntry(src);
|
||||
}
|
||||
else {
|
||||
if (src != m_mask_color) {
|
||||
scanline = rgba_blend_normal(dst, m_pal->getEntry(src), opacity);
|
||||
}
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void merge_zoomed_image_scale_up(Image* dst, const Image* src, const Palette* pal,
|
||||
int x, int y, int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int src_x, src_y, src_w, src_h;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int box_x, box_y, box_w, box_h;
|
||||
int first_box_w, first_box_h;
|
||||
int line_h, bottom;
|
||||
|
||||
box_w = zoom.apply(1);
|
||||
box_h = zoom.apply(1);
|
||||
|
||||
src_x = 0;
|
||||
src_y = 0;
|
||||
src_w = src->width();
|
||||
src_h = src->height();
|
||||
|
||||
dst_x = x;
|
||||
dst_y = y;
|
||||
dst_w = zoom.apply(src->width());
|
||||
dst_h = zoom.apply(src->height());
|
||||
|
||||
// clipping...
|
||||
if (dst_x < 0) {
|
||||
src_x += zoom.remove(-dst_x);
|
||||
src_w -= zoom.remove(-dst_x);
|
||||
dst_w -= (-dst_x);
|
||||
first_box_w = box_w - ((-dst_x) % box_w);
|
||||
dst_x = 0;
|
||||
}
|
||||
else
|
||||
first_box_w = 0;
|
||||
|
||||
if (dst_y < 0) {
|
||||
src_y += zoom.remove(-dst_y);
|
||||
src_h -= zoom.remove(-dst_y);
|
||||
dst_h -= (-dst_y);
|
||||
first_box_h = box_h - ((-dst_y) % box_h);
|
||||
dst_y = 0;
|
||||
}
|
||||
else
|
||||
first_box_h = 0;
|
||||
|
||||
if (dst_x+dst_w > dst->width()) {
|
||||
src_w -= zoom.remove(dst_x+dst_w-dst->width());
|
||||
dst_w = dst->width() - dst_x;
|
||||
}
|
||||
|
||||
if (dst_y+dst_h > dst->height()) {
|
||||
src_h -= zoom.remove(dst_y+dst_h-dst->height());
|
||||
dst_h = dst->height() - dst_y;
|
||||
}
|
||||
|
||||
if ((src_w <= 0) || (src_h <= 0) ||
|
||||
(dst_w <= 0) || (dst_h <= 0))
|
||||
return;
|
||||
|
||||
bottom = dst_y+dst_h-1;
|
||||
|
||||
// the scanline variable is used to blend src/dst pixels one time for each pixel
|
||||
typedef std::vector<typename DstTraits::pixel_t> Scanline;
|
||||
Scanline scanline(src_w);
|
||||
typename Scanline::iterator scanline_it;
|
||||
#ifdef _DEBUG
|
||||
typename Scanline::iterator scanline_end = scanline.end();
|
||||
#endif
|
||||
|
||||
// Lock all necessary bits
|
||||
const LockImageBits<SrcTraits> srcBits(src, gfx::Rect(src_x, src_y, src_w, src_h));
|
||||
LockImageBits<DstTraits> dstBits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_it = srcBits.begin();
|
||||
#ifdef _DEBUG
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_end = srcBits.end();
|
||||
#endif
|
||||
typename LockImageBits<DstTraits>::iterator dst_it, dst_end;
|
||||
|
||||
// For each line to draw of the source image...
|
||||
for (y=0; y<src_h; ++y) {
|
||||
dst_it = dstBits.begin_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
dst_end = dstBits.end_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
|
||||
// Read 'src' and 'dst' and blend them, put the result in `scanline'
|
||||
scanline_it = scanline.begin();
|
||||
for (x=0; x<src_w; ++x) {
|
||||
ASSERT(src_it >= srcBits.begin() && src_it < src_end);
|
||||
ASSERT(dst_it >= dstBits.begin() && dst_it < dst_end);
|
||||
ASSERT(scanline_it >= scanline.begin() && scanline_it < scanline_end);
|
||||
|
||||
blender(*scanline_it, *dst_it, *src_it, opacity);
|
||||
|
||||
++src_it;
|
||||
|
||||
int delta;
|
||||
if ((x == 0) && (first_box_w > 0))
|
||||
delta = first_box_w;
|
||||
else
|
||||
delta = box_w;
|
||||
|
||||
while (dst_it != dst_end && delta-- > 0)
|
||||
++dst_it;
|
||||
|
||||
++scanline_it;
|
||||
}
|
||||
|
||||
// Get the 'height' of the line to be painted in 'dst'
|
||||
if ((y == 0) && (first_box_h > 0))
|
||||
line_h = first_box_h;
|
||||
else
|
||||
line_h = box_h;
|
||||
|
||||
// Draw the line in 'dst'
|
||||
for (box_y=0; box_y<line_h; ++box_y) {
|
||||
dst_it = dstBits.begin_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
dst_end = dstBits.end_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
scanline_it = scanline.begin();
|
||||
|
||||
x = 0;
|
||||
|
||||
// first pixel
|
||||
if (first_box_w > 0) {
|
||||
for (box_x=0; box_x<first_box_w; ++box_x) {
|
||||
ASSERT(scanline_it != scanline_end);
|
||||
ASSERT(dst_it != dst_end);
|
||||
|
||||
*dst_it = *scanline_it;
|
||||
|
||||
++dst_it;
|
||||
if (dst_it == dst_end)
|
||||
goto done_with_line;
|
||||
}
|
||||
|
||||
++scanline_it;
|
||||
++x;
|
||||
}
|
||||
|
||||
// the rest of the line
|
||||
for (; x<src_w; ++x) {
|
||||
for (box_x=0; box_x<box_w; ++box_x) {
|
||||
ASSERT(dst_it != dst_end);
|
||||
|
||||
*dst_it = *scanline_it;
|
||||
|
||||
++dst_it;
|
||||
if (dst_it == dst_end)
|
||||
goto done_with_line;
|
||||
}
|
||||
|
||||
++scanline_it;
|
||||
}
|
||||
|
||||
done_with_line:;
|
||||
if (++dst_y > bottom)
|
||||
goto done_with_blit;
|
||||
}
|
||||
|
||||
// go to the next line in the source image
|
||||
++src_y;
|
||||
}
|
||||
|
||||
done_with_blit:;
|
||||
}
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void merge_zoomed_image_scale_down(Image* dst, const Image* src, const Palette* pal,
|
||||
int x, int y, int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int src_x, src_y, src_w, src_h;
|
||||
int dst_x, dst_y, dst_w, dst_h;
|
||||
int unbox_w, unbox_h;
|
||||
int bottom;
|
||||
|
||||
unbox_w = zoom.remove(1);
|
||||
unbox_h = zoom.remove(1);
|
||||
|
||||
src_x = 0;
|
||||
src_y = 0;
|
||||
src_w = src->width();
|
||||
src_h = src->height();
|
||||
|
||||
dst_x = x;
|
||||
dst_y = y;
|
||||
dst_w = zoom.apply(src->width());
|
||||
dst_h = zoom.apply(src->height());
|
||||
|
||||
// clipping...
|
||||
if (dst_x < 0) {
|
||||
src_x += zoom.remove(-dst_x);
|
||||
src_w -= zoom.remove(-dst_x);
|
||||
dst_w -= (-dst_x);
|
||||
dst_x = 0;
|
||||
}
|
||||
|
||||
if (dst_y < 0) {
|
||||
src_y += zoom.remove(-dst_y);
|
||||
src_h -= zoom.remove(-dst_y);
|
||||
dst_h -= (-dst_y);
|
||||
dst_y = 0;
|
||||
}
|
||||
|
||||
if (dst_x+dst_w > dst->width()) {
|
||||
src_w -= zoom.remove(dst_x+dst_w-dst->width());
|
||||
dst_w = dst->width() - dst_x;
|
||||
}
|
||||
|
||||
if (dst_y+dst_h > dst->height()) {
|
||||
src_h -= zoom.remove(dst_y+dst_h-dst->height());
|
||||
dst_h = dst->height() - dst_y;
|
||||
}
|
||||
|
||||
src_w = zoom.remove(zoom.apply(src_w));
|
||||
src_h = zoom.remove(zoom.apply(src_h));
|
||||
|
||||
if ((src_w <= 0) || (src_h <= 0) ||
|
||||
(dst_w <= 0) || (dst_h <= 0))
|
||||
return;
|
||||
|
||||
bottom = dst_y+dst_h-1;
|
||||
|
||||
// Lock all necessary bits
|
||||
const LockImageBits<SrcTraits> srcBits(src, gfx::Rect(src_x, src_y, src_w, src_h));
|
||||
LockImageBits<DstTraits> dstBits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_it = srcBits.begin();
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_end = srcBits.end();
|
||||
typename LockImageBits<DstTraits>::iterator dst_it, dst_end;
|
||||
|
||||
// For each line to draw of the source image...
|
||||
for (y=0; y<src_h; y+=unbox_h) {
|
||||
dst_it = dstBits.begin_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
dst_end = dstBits.end_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
|
||||
|
||||
for (x=0; x<src_w; x+=unbox_w) {
|
||||
ASSERT(src_it >= srcBits.begin() && src_it < src_end);
|
||||
ASSERT(dst_it >= dstBits.begin() && dst_it < dst_end);
|
||||
|
||||
blender(*dst_it, *dst_it, *src_it, opacity);
|
||||
|
||||
// Skip source pixels
|
||||
for (int delta=0; delta < unbox_w && src_it != src_end; ++delta)
|
||||
++src_it;
|
||||
|
||||
++dst_it;
|
||||
}
|
||||
|
||||
if (++dst_y > bottom)
|
||||
break;
|
||||
|
||||
// Skip lines
|
||||
for (int delta=0; delta < src_w * (unbox_h-1) && src_it != src_end; ++delta)
|
||||
++src_it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
|
||||
int x, int y, int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
if (zoom.scale() >= 1.0)
|
||||
merge_zoomed_image_scale_up<DstTraits, SrcTraits>(dst, src, pal, x, y, opacity, blend_mode, zoom);
|
||||
else
|
||||
merge_zoomed_image_scale_down<DstTraits, SrcTraits>(dst, src, pal, x, y, opacity, blend_mode, zoom);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Render Engine
|
||||
|
||||
static RenderEngine::CheckedBgType checked_bg_type;
|
||||
static bool checked_bg_zoom;
|
||||
static app::Color checked_bg_color1;
|
||||
static app::Color checked_bg_color2;
|
||||
|
||||
static int global_opacity = 255;
|
||||
static const Layer* selected_layer = NULL;
|
||||
static FrameNumber selected_frame(0);
|
||||
static Image* preview_image = NULL;
|
||||
|
||||
// static
|
||||
void RenderEngine::loadConfig()
|
||||
{
|
||||
checked_bg_type = (CheckedBgType)get_config_int("Options", "CheckedBgType",
|
||||
(int)RenderEngine::CHECKED_BG_16X16);
|
||||
checked_bg_zoom = get_config_bool("Options", "CheckedBgZoom", true);
|
||||
checked_bg_color1 = get_config_color("Options", "CheckedBgColor1", app::Color::fromRgb(128, 128, 128));
|
||||
checked_bg_color2 = get_config_color("Options", "CheckedBgColor2", app::Color::fromRgb(192, 192, 192));
|
||||
}
|
||||
|
||||
// static
|
||||
RenderEngine::CheckedBgType RenderEngine::getCheckedBgType()
|
||||
{
|
||||
return checked_bg_type;
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::setCheckedBgType(CheckedBgType type)
|
||||
{
|
||||
checked_bg_type = type;
|
||||
set_config_int("Options", "CheckedBgType", (int)type);
|
||||
}
|
||||
|
||||
// static
|
||||
bool RenderEngine::getCheckedBgZoom()
|
||||
{
|
||||
return checked_bg_zoom;
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::setCheckedBgZoom(bool state)
|
||||
{
|
||||
checked_bg_zoom = state;
|
||||
set_config_bool("Options", "CheckedBgZoom", state);
|
||||
}
|
||||
|
||||
// static
|
||||
app::Color RenderEngine::getCheckedBgColor1()
|
||||
{
|
||||
return checked_bg_color1;
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::setCheckedBgColor1(const app::Color& color)
|
||||
{
|
||||
checked_bg_color1 = color;
|
||||
set_config_color("Options", "CheckedBgColor1", color);
|
||||
}
|
||||
|
||||
// static
|
||||
app::Color RenderEngine::getCheckedBgColor2()
|
||||
{
|
||||
return checked_bg_color2;
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::setCheckedBgColor2(const app::Color& color)
|
||||
{
|
||||
checked_bg_color2 = color;
|
||||
set_config_color("Options", "CheckedBgColor2", color);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
RenderEngine::RenderEngine(const Document* document,
|
||||
const Sprite* sprite,
|
||||
const Layer* currentLayer,
|
||||
FrameNumber currentFrame)
|
||||
: m_document(document)
|
||||
, m_sprite(sprite)
|
||||
, m_currentLayer(currentLayer)
|
||||
, m_currentFrame(currentFrame)
|
||||
{
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::setPreviewImage(const Layer* layer, FrameNumber frame, Image* image)
|
||||
{
|
||||
selected_layer = layer;
|
||||
selected_frame = frame;
|
||||
preview_image = image;
|
||||
}
|
||||
|
||||
Image* RenderEngine::renderSprite(
|
||||
const gfx::Rect& zoomedRect,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
bool draw_tiled_bg,
|
||||
bool enable_onionskin,
|
||||
ImageBufferPtr& buffer)
|
||||
{
|
||||
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom);
|
||||
const LayerImage* background = m_sprite->backgroundLayer();
|
||||
bool need_checked_bg = (background != NULL ? !background->isVisible(): true);
|
||||
uint32_t bg_color = 0;
|
||||
Image *image;
|
||||
|
||||
switch (m_sprite->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, RgbTraits>;
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, GrayscaleTraits>;
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, IndexedTraits>;
|
||||
if (!need_checked_bg)
|
||||
bg_color = m_sprite->getPalette(frame)->getEntry(m_sprite->transparentColor());
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a temporary RGB bitmap to draw all to it
|
||||
image = Image::create(IMAGE_RGB, zoomedRect.w, zoomedRect.h, buffer);
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
// Draw checked background
|
||||
if (need_checked_bg && draw_tiled_bg)
|
||||
renderCheckedBackground(image, zoomedRect.x, zoomedRect.y, zoom);
|
||||
else
|
||||
clear_image(image, bg_color);
|
||||
|
||||
// Draw the current frame.
|
||||
global_opacity = 255;
|
||||
renderLayer(m_sprite->folder(), image,
|
||||
zoomedRect.x, zoomedRect.y,
|
||||
frame, zoom, zoomed_func, true, true, -1);
|
||||
|
||||
// Onion-skin feature: Draw previous/next frames with different
|
||||
// opacity (<255) (it is the onion-skinning)
|
||||
IDocumentSettings* docSettings = UIContext::instance()
|
||||
->settings()->getDocumentSettings(m_document);
|
||||
|
||||
if (enable_onionskin & docSettings->getUseOnionskin()) {
|
||||
int prevs = docSettings->getOnionskinPrevFrames();
|
||||
int nexts = docSettings->getOnionskinNextFrames();
|
||||
int opacity_base = docSettings->getOnionskinOpacityBase();
|
||||
int opacity_step = docSettings->getOnionskinOpacityStep();
|
||||
|
||||
for (FrameNumber f=frame.previous(prevs); f <= frame.next(nexts); ++f) {
|
||||
if (f == frame || f < 0 || f > m_sprite->lastFrame())
|
||||
continue;
|
||||
else if (f < frame)
|
||||
global_opacity = opacity_base - opacity_step * ((frame - f)-1);
|
||||
else
|
||||
global_opacity = opacity_base - opacity_step * ((f - frame)-1);
|
||||
|
||||
if (global_opacity > 0) {
|
||||
global_opacity = MID(0, global_opacity, 255);
|
||||
|
||||
int blend_mode = -1;
|
||||
if (docSettings->getOnionskinType() == IDocumentSettings::Onionskin_Merge)
|
||||
blend_mode = BLEND_MODE_NORMAL;
|
||||
else if (docSettings->getOnionskinType() == IDocumentSettings::Onionskin_RedBlueTint)
|
||||
blend_mode = (f < frame ? BLEND_MODE_RED_TINT: BLEND_MODE_BLUE_TINT);
|
||||
|
||||
renderLayer(m_sprite->folder(), image,
|
||||
zoomedRect.x, zoomedRect.y, f, zoom, zoomed_func,
|
||||
true, true, blend_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::renderCheckedBackground(Image* image,
|
||||
int source_x, int source_y, Zoom zoom)
|
||||
{
|
||||
int x, y, u, v;
|
||||
int tile_w = 16;
|
||||
int tile_h = 16;
|
||||
int c1 = color_utils::color_for_image(checked_bg_color1, image->pixelFormat());
|
||||
int c2 = color_utils::color_for_image(checked_bg_color2, image->pixelFormat());
|
||||
|
||||
switch (checked_bg_type) {
|
||||
|
||||
case CHECKED_BG_16X16:
|
||||
tile_w = 16;
|
||||
tile_h = 16;
|
||||
break;
|
||||
|
||||
case CHECKED_BG_8X8:
|
||||
tile_w = 8;
|
||||
tile_h = 8;
|
||||
break;
|
||||
|
||||
case CHECKED_BG_4X4:
|
||||
tile_w = 4;
|
||||
tile_h = 4;
|
||||
break;
|
||||
|
||||
case CHECKED_BG_2X2:
|
||||
tile_w = 2;
|
||||
tile_h = 2;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (checked_bg_zoom) {
|
||||
tile_w = zoom.apply(tile_w);
|
||||
tile_h = zoom.apply(tile_h);
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (tile_w < zoom.apply(1)) tile_w = zoom.apply(1);
|
||||
if (tile_h < zoom.apply(1)) tile_h = zoom.apply(1);
|
||||
|
||||
if (tile_w < 1) tile_w = 1;
|
||||
if (tile_h < 1) tile_h = 1;
|
||||
|
||||
// Tile position (u,v) is the number of tile we start in (source_x,source_y) coordinate
|
||||
u = (source_x / tile_w);
|
||||
v = (source_y / tile_h);
|
||||
|
||||
// Position where we start drawing the first tile in "image"
|
||||
int x_start = -(source_x % tile_w);
|
||||
int y_start = -(source_y % tile_h);
|
||||
|
||||
// Draw checked background (tile by tile)
|
||||
int u_start = u;
|
||||
for (y=y_start-tile_h; y<image->height()+tile_h; y+=tile_h) {
|
||||
for (x=x_start-tile_w; x<image->width()+tile_w; x+=tile_w) {
|
||||
fill_rect(image, x, y, x+tile_w-1, y+tile_h-1,
|
||||
(((u+v))&1)? c1: c2);
|
||||
++u;
|
||||
}
|
||||
u = u_start;
|
||||
++v;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void RenderEngine::renderImage(Image* rgb_image, Image* src_image, const Palette* pal,
|
||||
int x, int y, Zoom zoom)
|
||||
{
|
||||
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom);
|
||||
|
||||
ASSERT(rgb_image->pixelFormat() == IMAGE_RGB && "renderImage accepts RGB destination images only");
|
||||
|
||||
switch (src_image->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, RgbTraits>;
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, GrayscaleTraits>;
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
zoomed_func = merge_zoomed_image<RgbTraits, IndexedTraits>;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
(*zoomed_func)(rgb_image, src_image, pal, x, y, 255, BLEND_MODE_NORMAL, zoom);
|
||||
}
|
||||
|
||||
void RenderEngine::renderLayer(
|
||||
const Layer* layer,
|
||||
Image *image,
|
||||
int source_x, int source_y,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom),
|
||||
bool render_background,
|
||||
bool render_transparent,
|
||||
int blend_mode)
|
||||
{
|
||||
// we can't read from this layer
|
||||
if (!layer->isVisible())
|
||||
return;
|
||||
|
||||
switch (layer->type()) {
|
||||
|
||||
case ObjectType::LayerImage: {
|
||||
if ((!render_background && layer->isBackground()) ||
|
||||
(!render_transparent && !layer->isBackground()))
|
||||
break;
|
||||
|
||||
const Cel* cel = static_cast<const LayerImage*>(layer)->getCel(frame);
|
||||
if (cel != NULL) {
|
||||
Image* src_image;
|
||||
|
||||
// Is the 'preview_image' set to be used with this layer?
|
||||
if ((selected_layer == layer) &&
|
||||
(selected_frame == frame) &&
|
||||
(preview_image != NULL)) {
|
||||
src_image = preview_image;
|
||||
}
|
||||
// If not, we use the original cel-image from the images' stock
|
||||
else {
|
||||
src_image = cel->image();
|
||||
}
|
||||
|
||||
if (src_image) {
|
||||
int t, output_opacity;
|
||||
|
||||
output_opacity = MID(0, cel->opacity(), 255);
|
||||
output_opacity = INT_MULT(output_opacity, global_opacity, t);
|
||||
|
||||
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
|
||||
|
||||
(*zoomed_func)(image, src_image, m_sprite->getPalette(frame),
|
||||
zoom.apply(cel->x()) - source_x,
|
||||
zoom.apply(cel->y()) - source_y,
|
||||
output_opacity,
|
||||
(blend_mode < 0 ?
|
||||
static_cast<const LayerImage*>(layer)->getBlendMode():
|
||||
blend_mode),
|
||||
zoom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectType::LayerFolder: {
|
||||
LayerConstIterator it = static_cast<const LayerFolder*>(layer)->getLayerBegin();
|
||||
LayerConstIterator end = static_cast<const LayerFolder*>(layer)->getLayerEnd();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
renderLayer(*it, image,
|
||||
source_x, source_y,
|
||||
frame, zoom, zoomed_func,
|
||||
render_background,
|
||||
render_transparent,
|
||||
blend_mode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Draw extras
|
||||
if (m_document->getExtraCel() &&
|
||||
layer == m_currentLayer &&
|
||||
frame == m_currentFrame) {
|
||||
Cel* extraCel = m_document->getExtraCel();
|
||||
if (extraCel->opacity() > 0) {
|
||||
Image* extraImage = m_document->getExtraCelImage();
|
||||
|
||||
(*zoomed_func)(image, extraImage, m_sprite->getPalette(frame),
|
||||
zoom.apply(extraCel->x()) - source_x,
|
||||
zoom.apply(extraCel->y()) - source_y,
|
||||
extraCel->opacity(),
|
||||
m_document->getExtraCelBlendMode(), zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -1,110 +0,0 @@
|
||||
/* Aseprite
|
||||
* Copyright (C) 2001-2013 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef APP_UTIL_RENDER_H_INCLUDED
|
||||
#define APP_UTIL_RENDER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/color.h"
|
||||
#include "app/zoom.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Layer;
|
||||
class Palette;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
class Document;
|
||||
|
||||
using namespace doc;
|
||||
|
||||
class RenderEngine {
|
||||
public:
|
||||
RenderEngine(const Document* document,
|
||||
const Sprite* sprite,
|
||||
const Layer* currentLayer,
|
||||
FrameNumber currentFrame);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Checked background configuration
|
||||
|
||||
enum CheckedBgType { CHECKED_BG_16X16,
|
||||
CHECKED_BG_8X8,
|
||||
CHECKED_BG_4X4,
|
||||
CHECKED_BG_2X2 };
|
||||
|
||||
static void loadConfig();
|
||||
static CheckedBgType getCheckedBgType();
|
||||
static void setCheckedBgType(CheckedBgType type);
|
||||
static bool getCheckedBgZoom();
|
||||
static void setCheckedBgZoom(bool state);
|
||||
static app::Color getCheckedBgColor1();
|
||||
static void setCheckedBgColor1(const app::Color& color);
|
||||
static app::Color getCheckedBgColor2();
|
||||
static void setCheckedBgColor2(const app::Color& color);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Preview image
|
||||
|
||||
static void setPreviewImage(const Layer* layer, FrameNumber frame, Image* drawable);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Main function used by sprite-editors to render the sprite.
|
||||
// Draws the given sprite frame in a new image and return it.
|
||||
// Note: zoomedRect must have the zoom applied (zoomedRect = zoom.apply(spriteRect)).
|
||||
Image* renderSprite(const gfx::Rect& zoomedRect,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
bool draw_tiled_bg,
|
||||
bool enable_onionskin,
|
||||
ImageBufferPtr& buffer);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Extra functions
|
||||
|
||||
static void renderCheckedBackground(Image* image,
|
||||
int source_x, int source_y,
|
||||
Zoom zoom);
|
||||
|
||||
static void renderImage(Image* rgb_image, Image* src_image, const Palette* pal,
|
||||
int x, int y, Zoom zoom);
|
||||
|
||||
private:
|
||||
void renderLayer(
|
||||
const Layer* layer,
|
||||
Image* image,
|
||||
int source_x, int source_y,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom),
|
||||
bool render_background,
|
||||
bool render_transparent,
|
||||
int blend_mode);
|
||||
|
||||
const Document* m_document;
|
||||
const Sprite* m_sprite;
|
||||
const Layer* m_currentLayer;
|
||||
FrameNumber m_currentFrame;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -36,7 +36,6 @@ add_library(doc-lib
|
||||
palette.cpp
|
||||
palette_io.cpp
|
||||
primitives.cpp
|
||||
quantization.cpp
|
||||
rgbmap.cpp
|
||||
sprite.cpp
|
||||
sprites.cpp
|
||||
|
@ -63,7 +63,7 @@ void flip_image_with_mask(Image* image, const Mask* mask, FlipType flipType, int
|
||||
|
||||
for (int y=bounds.y; y<bounds.y+bounds.h; ++y) {
|
||||
// Copy the current row.
|
||||
copy_image(originalRow, image, -bounds.x, -y);
|
||||
originalRow->copy(image, gfx::Clip(0, 0, bounds.x, y, bounds.w, 1));
|
||||
|
||||
int u = bounds.x+bounds.w-1;
|
||||
for (int x=bounds.x; x<bounds.x+bounds.w; ++x, --u) {
|
||||
@ -82,7 +82,7 @@ void flip_image_with_mask(Image* image, const Mask* mask, FlipType flipType, int
|
||||
|
||||
for (int x=bounds.x; x<bounds.x+bounds.w; ++x) {
|
||||
// Copy the current column.
|
||||
copy_image(originalCol, image, -x, -bounds.y);
|
||||
originalCol->copy(image, gfx::Clip(0, 0, x, bounds.y, 1, bounds.h));
|
||||
|
||||
int v = bounds.y+bounds.h-1;
|
||||
for (int y=bounds.y; y<bounds.y+bounds.h; ++y, --v) {
|
||||
|
@ -74,7 +74,7 @@ private:
|
||||
void scale_image(Image *dst, Image *src, int x, int y, int w, int h)
|
||||
{
|
||||
if (w == src->width() && src->height() == h)
|
||||
composite_image(dst, src, x, y, 255, BLEND_MODE_NORMAL);
|
||||
dst->copy(src, gfx::Clip(x, y, 0, 0, w, h));
|
||||
else {
|
||||
switch (dst->pixelFormat()) {
|
||||
|
||||
|
@ -176,12 +176,12 @@ void rotsprite_image(Image* bmp, Image* spr,
|
||||
|
||||
bmp_copy->clear(bmp->maskColor());
|
||||
spr_copy->clear(maskColor);
|
||||
spr_copy->copy(spr, 0, 0, 0, 0, spr->width(), spr->height());
|
||||
spr_copy->copy(spr, gfx::Clip(spr->bounds()));
|
||||
|
||||
for (int i=0; i<3; ++i) {
|
||||
tmp_copy->clear(maskColor);
|
||||
image_scale2x(tmp_copy, spr_copy, spr->width()*(1<<i), spr->height()*(1<<i));
|
||||
spr_copy->copy(tmp_copy, 0, 0, 0, 0, tmp_copy->width(), tmp_copy->height());
|
||||
spr_copy->copy(tmp_copy, gfx::Clip(tmp_copy->bounds()));
|
||||
}
|
||||
|
||||
doc::algorithm::parallelogram(bmp_copy, spr_copy,
|
||||
|
@ -33,9 +33,8 @@ BLEND_COLOR graya_blenders[] =
|
||||
graya_blend_blackandwhite,
|
||||
};
|
||||
|
||||
/**********************************************************************/
|
||||
/* RGB blenders */
|
||||
/**********************************************************************/
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// RGB blenders
|
||||
|
||||
int rgba_blend_normal(int back, int front, int opacity)
|
||||
{
|
||||
@ -209,9 +208,8 @@ int rgba_blend_blackandwhite(int back, int front, int opacity)
|
||||
return rgba(D_v, D_v, D_v, 255);
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Grayscale blenders */
|
||||
/**********************************************************************/
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Grayscale blenders
|
||||
|
||||
int graya_blend_normal(int back, int front, int opacity)
|
||||
{
|
||||
@ -308,4 +306,12 @@ int graya_blend_blackandwhite(int back, int front, int opacity)
|
||||
return graya(D_k, 255);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Indexed blenders
|
||||
|
||||
int indexed_blend_direct(int back, int front, int opacity)
|
||||
{
|
||||
return front;
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
@ -42,6 +42,8 @@ namespace doc {
|
||||
int graya_blend_merge(int back, int front, int opacity);
|
||||
int graya_blend_blackandwhite(int back, int front, int opacity);
|
||||
|
||||
int indexed_blend_direct(int back, int front, int opacity);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -11,9 +11,9 @@
|
||||
namespace doc {
|
||||
|
||||
// Dithering methods
|
||||
enum DitheringMethod {
|
||||
DITHERING_NONE,
|
||||
DITHERING_ORDERED,
|
||||
enum class DitheringMethod {
|
||||
NONE,
|
||||
ORDERED,
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "doc/pixel_format.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/primitives_fast.h"
|
||||
#include "doc/quantization.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/stock.h"
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
namespace doc {
|
||||
|
||||
typedef int frame_t;
|
||||
|
||||
class FrameNumber {
|
||||
public:
|
||||
FrameNumber() : m_value(0) { }
|
||||
|
@ -8,13 +8,14 @@
|
||||
#define DOC_IMAGE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/size.h"
|
||||
#include "doc/blend.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "doc/object.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "gfx/clip.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
@ -72,8 +73,7 @@ namespace doc {
|
||||
virtual color_t getPixel(int x, int y) const = 0;
|
||||
virtual void putPixel(int x, int y, color_t color) = 0;
|
||||
virtual void clear(color_t color) = 0;
|
||||
virtual void copy(const Image* src, int dst_x, int dst_y, int src_x, int src_y, int w, int h) = 0;
|
||||
virtual void merge(const Image* _src, int dst_x, int dst_y, int src_x, int src_y, int w, int h, int opacity, int blend_mode) = 0;
|
||||
virtual void copy(const Image* src, gfx::Clip area) = 0;
|
||||
virtual void drawHLine(int x1, int y, int x2, color_t color) = 0;
|
||||
virtual void fillRect(int x1, int y1, int x2, int y2, color_t color) = 0;
|
||||
virtual void blendRect(int x1, int y1, int x2, int y2, color_t color, int opacity) = 0;
|
||||
|
@ -103,57 +103,28 @@ namespace doc {
|
||||
*it = color;
|
||||
}
|
||||
|
||||
void copy(const Image* _src, int dst_x, int dst_y, int src_x, int src_y, int w, int h) override {
|
||||
void copy(const Image* _src, gfx::Clip area) override {
|
||||
const ImageImpl<Traits>* src = (const ImageImpl<Traits>*)_src;
|
||||
address_t src_address;
|
||||
address_t dst_address;
|
||||
int bytes;
|
||||
|
||||
if (!clip_rects(src, dst_x, dst_y, src_x, src_y, w, h))
|
||||
if (!area.clip(width(), height(), src->width(), src->height()))
|
||||
return;
|
||||
|
||||
// Copy process
|
||||
bytes = Traits::getRowStrideBytes(w);
|
||||
bytes = Traits::getRowStrideBytes(area.size.w);
|
||||
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
src_address = src->address(src_x, src_y);
|
||||
dst_address = address(dst_x, dst_y);
|
||||
for (int end_y=area.dst.y+area.size.h;
|
||||
area.dst.y<end_y;
|
||||
++area.dst.y, ++area.src.y) {
|
||||
src_address = src->address(area.src.x, area.src.y);
|
||||
dst_address = address(area.dst.x, area.dst.y);
|
||||
|
||||
memcpy(dst_address, src_address, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
void merge(const Image* _src, int dst_x, int dst_y, int src_x, int src_y, int w, int h, int opacity, int blend_mode) override {
|
||||
BLEND_COLOR blender = Traits::get_blender(blend_mode);
|
||||
const ImageImpl<Traits>* src = (const ImageImpl<Traits>*)_src;
|
||||
ImageImpl<Traits>* dst = this;
|
||||
address_t src_address;
|
||||
address_t dst_address;
|
||||
uint32_t mask_color = src->maskColor();
|
||||
|
||||
// nothing to do
|
||||
if (!opacity)
|
||||
return;
|
||||
|
||||
if (!clip_rects(src, dst_x, dst_y, src_x, src_y, w, h))
|
||||
return;
|
||||
|
||||
// Merge process
|
||||
int end_x = dst_x+w;
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
src_address = src->address(src_x, src_y);
|
||||
dst_address = dst->address(dst_x, dst_y);
|
||||
|
||||
for (int x=dst_x; x<end_x; ++x) {
|
||||
if (*src_address != mask_color)
|
||||
*dst_address = (*blender)(*dst_address, *src_address, opacity);
|
||||
|
||||
++dst_address;
|
||||
++src_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawHLine(int x1, int y, int x2, color_t color) override {
|
||||
LockImageBits<Traits> bits(this, gfx::Rect(x1, y, x2 - x1 + 1, 1));
|
||||
typename LockImageBits<Traits>::iterator it(bits.begin());
|
||||
@ -279,61 +250,20 @@ namespace doc {
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ImageImpl<IndexedTraits>::merge(const Image* src, int dst_x, int dst_y, int src_x, int src_y, int w, int h, int opacity, int blend_mode) {
|
||||
if (!clip_rects(src, dst_x, dst_y, src_x, src_y, w, h))
|
||||
return;
|
||||
|
||||
address_t src_address;
|
||||
address_t dst_address;
|
||||
|
||||
int end_x = dst_x+w;
|
||||
|
||||
// Direct copy
|
||||
if (blend_mode == BLEND_MODE_COPY) {
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
src_address = src->getPixelAddress(src_x, src_y);
|
||||
dst_address = getPixelAddress(dst_x, dst_y);
|
||||
|
||||
for (int x=dst_x; x<end_x; ++x) {
|
||||
*dst_address = (*src_address);
|
||||
|
||||
++dst_address;
|
||||
++src_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
// With mask
|
||||
else {
|
||||
int mask_color = src->maskColor();
|
||||
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
src_address = src->getPixelAddress(src_x, src_y);
|
||||
dst_address = getPixelAddress(dst_x, dst_y);
|
||||
|
||||
for (int x=dst_x; x<end_x; ++x) {
|
||||
if (*src_address != mask_color)
|
||||
*dst_address = (*src_address);
|
||||
|
||||
++dst_address;
|
||||
++src_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ImageImpl<BitmapTraits>::copy(const Image* src, int dst_x, int dst_y, int src_x, int src_y, int w, int h) {
|
||||
if (!clip_rects(src, dst_x, dst_y, src_x, src_y, w, h))
|
||||
inline void ImageImpl<BitmapTraits>::copy(const Image* src, gfx::Clip area) {
|
||||
if (!area.clip(width(), height(), src->width(), src->height()))
|
||||
return;
|
||||
|
||||
// Copy process
|
||||
ImageConstIterator<BitmapTraits> src_it(src, gfx::Rect(src_x, src_y, w, h), src_x, src_y);
|
||||
ImageIterator<BitmapTraits> dst_it(this, gfx::Rect(dst_x, dst_y, w, h), dst_x, dst_y);
|
||||
ImageConstIterator<BitmapTraits> src_it(src, area.srcBounds(), area.src.x, area.src.y);
|
||||
ImageIterator<BitmapTraits> dst_it(this, area.dstBounds(), area.dst.x, area.dst.y);
|
||||
|
||||
int end_x = dst_x+w;
|
||||
int end_x = area.dst.x+area.size.w;
|
||||
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
for (int x=dst_x; x<end_x; ++x) {
|
||||
for (int end_y=area.dst.y+area.size.h;
|
||||
area.dst.y<end_y;
|
||||
++area.dst.y, ++area.src.y) {
|
||||
for (int x=area.dst.x; x<end_x; ++x) {
|
||||
*dst_it = *src_it;
|
||||
++src_it;
|
||||
++dst_it;
|
||||
@ -341,27 +271,6 @@ namespace doc {
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void ImageImpl<BitmapTraits>::merge(const Image* src, int dst_x, int dst_y, int src_x, int src_y, int w, int h, int opacity, int blend_mode) {
|
||||
if (!clip_rects(src, dst_x, dst_y, src_x, src_y, w, h))
|
||||
return;
|
||||
|
||||
// Merge process
|
||||
ImageConstIterator<BitmapTraits> src_it(src, gfx::Rect(src_x, src_y, w, h), src_x, src_y);
|
||||
ImageIterator<BitmapTraits> dst_it(this, gfx::Rect(dst_x, dst_y, w, h), dst_x, dst_y);
|
||||
|
||||
int end_x = dst_x+w;
|
||||
|
||||
for (int end_y=dst_y+h; dst_y<end_y; ++dst_y, ++src_y) {
|
||||
for (int x=dst_x; x<end_x; ++x) {
|
||||
if (*dst_it != 0)
|
||||
*dst_it = *src_it;
|
||||
++src_it;
|
||||
++dst_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -94,6 +94,11 @@ namespace doc {
|
||||
{
|
||||
return bytes_per_pixel * pixels_per_row;
|
||||
}
|
||||
|
||||
static inline BLEND_COLOR get_blender(int blend_mode)
|
||||
{
|
||||
return indexed_blend_direct;
|
||||
}
|
||||
};
|
||||
|
||||
struct BitmapTraits {
|
||||
|
@ -76,6 +76,11 @@ Layer* Layer::getNext() const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Cel* Layer::cel(frame_t frame) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerImage class
|
||||
|
||||
@ -124,6 +129,11 @@ void LayerImage::destroyAllCels()
|
||||
m_cels.clear();
|
||||
}
|
||||
|
||||
Cel* LayerImage::cel(frame_t frame) const
|
||||
{
|
||||
return const_cast<Cel*>(getCel(FrameNumber(frame)));
|
||||
}
|
||||
|
||||
void LayerImage::getCels(CelList& cels) const
|
||||
{
|
||||
CelConstIterator it = getCelBegin();
|
||||
@ -300,46 +310,4 @@ void LayerFolder::stackLayer(Layer* layer, Layer* after)
|
||||
m_layers.push_front(layer);
|
||||
}
|
||||
|
||||
void layer_render(const Layer* layer, Image* image, int x, int y, FrameNumber frame)
|
||||
{
|
||||
if (!layer->isVisible())
|
||||
return;
|
||||
|
||||
switch (layer->type()) {
|
||||
|
||||
case ObjectType::LayerImage: {
|
||||
const Cel* cel = static_cast<const LayerImage*>(layer)->getCel(frame);
|
||||
Image* src_image;
|
||||
|
||||
if (cel) {
|
||||
ASSERT((cel->imageIndex() >= 0) &&
|
||||
(cel->imageIndex() < layer->sprite()->stock()->size()));
|
||||
|
||||
src_image = cel->image();
|
||||
ASSERT(src_image != NULL);
|
||||
|
||||
ASSERT(src_image->maskColor() == layer->sprite()->transparentColor());
|
||||
|
||||
composite_image(image, src_image,
|
||||
cel->x() + x,
|
||||
cel->y() + y,
|
||||
MID(0, cel->opacity(), 255),
|
||||
static_cast<const LayerImage*>(layer)->getBlendMode());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectType::LayerFolder: {
|
||||
LayerConstIterator it = static_cast<const LayerFolder*>(layer)->getLayerBegin();
|
||||
LayerConstIterator end = static_cast<const LayerFolder*>(layer)->getLayerEnd();
|
||||
|
||||
for (; it != end; ++it)
|
||||
layer_render(*it, image, x, y, frame);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
@ -87,6 +87,7 @@ namespace doc {
|
||||
m_flags = LayerFlags(int(m_flags) & ~int(flags));
|
||||
}
|
||||
|
||||
virtual Cel* cel(frame_t frame) const;
|
||||
virtual void getCels(CelList& cels) const = 0;
|
||||
|
||||
private:
|
||||
@ -116,6 +117,7 @@ namespace doc {
|
||||
void moveCel(Cel *cel, FrameNumber frame);
|
||||
const Cel* getCel(FrameNumber frame) const;
|
||||
Cel* getCel(FrameNumber frame);
|
||||
Cel* cel(frame_t frame) const override;
|
||||
void getCels(CelList& cels) const override;
|
||||
Cel* getLastCel() const;
|
||||
|
||||
@ -165,8 +167,6 @@ namespace doc {
|
||||
LayerList m_layers;
|
||||
};
|
||||
|
||||
void layer_render(const Layer* layer, Image *image, int x, int y, FrameNumber frame);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -97,7 +97,7 @@ void Mask::copyFrom(const Mask* sourceMask)
|
||||
add(sourceMask->bounds());
|
||||
|
||||
// And copy the "mask" bitmap
|
||||
copy_image(m_bitmap, sourceMask->m_bitmap, 0, 0);
|
||||
copy_image(m_bitmap, sourceMask->m_bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,10 @@ namespace doc {
|
||||
FrameNumber frame() const { return m_frame; }
|
||||
void setFrame(FrameNumber frame);
|
||||
|
||||
color_t entry(int i) const {
|
||||
ASSERT(i >= 0 && i < size());
|
||||
return m_colors[i];
|
||||
}
|
||||
color_t getEntry(int i) const {
|
||||
ASSERT(i >= 0 && i < size());
|
||||
return m_colors[i];
|
||||
|
@ -65,14 +65,14 @@ void clear_image(Image* image, color_t color)
|
||||
image->clear(color);
|
||||
}
|
||||
|
||||
void copy_image(Image* dst, const Image* src, int x, int y)
|
||||
void copy_image(Image* dst, const Image* src)
|
||||
{
|
||||
dst->copy(src, x, y, 0, 0, src->width(), src->height());
|
||||
dst->copy(src, gfx::Clip(0, 0, 0, 0, src->width(), src->height()));
|
||||
}
|
||||
|
||||
void composite_image(Image* dst, const Image* src, int x, int y, int opacity, int blend_mode)
|
||||
void copy_image(Image* dst, const Image* src, int x, int y)
|
||||
{
|
||||
dst->merge(src, x, y, 0, 0, src->width(), src->height(), opacity, blend_mode);
|
||||
dst->copy(src, gfx::Clip(x, y, 0, 0, src->width(), src->height()));
|
||||
}
|
||||
|
||||
Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer)
|
||||
@ -84,7 +84,7 @@ Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, co
|
||||
trim->setMaskColor(image->maskColor());
|
||||
|
||||
clear_image(trim, bg);
|
||||
trim->copy(image, 0, 0, x, y, w, h);
|
||||
trim->copy(image, gfx::Clip(0, 0, x, y, w, h));
|
||||
|
||||
return trim;
|
||||
}
|
||||
|
@ -23,9 +23,8 @@ namespace doc {
|
||||
|
||||
void clear_image(Image* image, color_t bg);
|
||||
|
||||
void copy_image(Image* dst, const Image* src);
|
||||
void copy_image(Image* dst, const Image* src, int x, int y);
|
||||
void composite_image(Image* dst, const Image* src, int x, int y, int opacity, int blend_mode);
|
||||
|
||||
Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
||||
void rotate_image(const Image* src, Image* dst, int angle);
|
||||
|
||||
|
@ -1,62 +0,0 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_QUANTIZATION_H_INCLUDED
|
||||
#define DOC_QUANTIZATION_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/color_histogram.h"
|
||||
#include "doc/dithering_method.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/pixel_format.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Image;
|
||||
class Palette;
|
||||
class RgbMap;
|
||||
class Sprite;
|
||||
class Stock;
|
||||
|
||||
namespace quantization {
|
||||
|
||||
class PaletteOptimizer {
|
||||
public:
|
||||
void feedWithImage(Image* image);
|
||||
void calculate(Palette* palette, bool has_background_layer);
|
||||
|
||||
private:
|
||||
quantization::ColorHistogram<5, 6, 5> m_histogram;
|
||||
};
|
||||
|
||||
void create_palette_from_images(
|
||||
const std::vector<Image*>& images,
|
||||
Palette* palette,
|
||||
bool has_background_layer);
|
||||
|
||||
// Creates a new palette suitable to quantize the given RGB sprite to Indexed color.
|
||||
Palette* create_palette_from_rgb(
|
||||
const Sprite* sprite,
|
||||
FrameNumber frameNumber,
|
||||
Palette* newPalette); // Can be NULL to create a new palette
|
||||
|
||||
// Changes the image pixel format. The dithering method is used only
|
||||
// when you want to convert from RGB to Indexed.
|
||||
Image* convert_pixel_format(
|
||||
const Image* src,
|
||||
Image* dst, // Can be NULL to create a new image
|
||||
PixelFormat pixelFormat,
|
||||
DitheringMethod ditheringMethod,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette,
|
||||
bool is_background);
|
||||
|
||||
} // namespace quantization
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -13,9 +13,9 @@
|
||||
#include "base/memory.h"
|
||||
#include "base/remove_from_container.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/doc.h"
|
||||
#include "doc/image_bits.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/doc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
@ -223,6 +223,11 @@ LayerIndex Sprite::countLayers() const
|
||||
return LayerIndex(folder()->getLayersCount());
|
||||
}
|
||||
|
||||
Layer* Sprite::layer(int layerIndex) const
|
||||
{
|
||||
return indexToLayer(LayerIndex(layerIndex));
|
||||
}
|
||||
|
||||
Layer* Sprite::indexToLayer(LayerIndex index) const
|
||||
{
|
||||
if (index < LayerIndex(0))
|
||||
@ -252,6 +257,11 @@ void Sprite::getLayersList(std::vector<Layer*>& layers) const
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Palettes
|
||||
|
||||
Palette* Sprite::palette(frame_t frame) const
|
||||
{
|
||||
return getPalette(FrameNumber(frame));
|
||||
}
|
||||
|
||||
Palette* Sprite::getPalette(FrameNumber frame) const
|
||||
{
|
||||
ASSERT(frame >= 0);
|
||||
@ -457,28 +467,6 @@ void Sprite::remapImages(FrameNumber frameFrom, FrameNumber frameTo, const std::
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Drawing
|
||||
|
||||
void Sprite::render(Image* image, int x, int y, FrameNumber frame) const
|
||||
{
|
||||
fill_rect(image, x, y, x+m_width-1, y+m_height-1,
|
||||
(m_format == IMAGE_INDEXED ? transparentColor(): 0));
|
||||
|
||||
layer_render(folder(), image, x, y, frame);
|
||||
}
|
||||
|
||||
int Sprite::getPixel(int x, int y, FrameNumber frame) const
|
||||
{
|
||||
int color = 0;
|
||||
|
||||
if ((x >= 0) && (y >= 0) && (x < m_width) && (y < m_height)) {
|
||||
base::UniquePtr<Image> image(Image::create(m_format, 1, 1));
|
||||
clear_image(image, (m_format == IMAGE_INDEXED ? transparentColor(): 0));
|
||||
render(image, -x, -y, frame);
|
||||
color = get_pixel(image, 0, 0);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void Sprite::pickCels(int x, int y, FrameNumber frame, int opacityThreshold, CelList& cels) const
|
||||
{
|
||||
std::vector<Layer*> layers;
|
||||
|
@ -77,6 +77,7 @@ namespace doc {
|
||||
|
||||
LayerIndex countLayers() const;
|
||||
|
||||
Layer* layer(int layerIndex) const;
|
||||
Layer* indexToLayer(LayerIndex index) const;
|
||||
LayerIndex layerToIndex(const Layer* layer) const;
|
||||
|
||||
@ -85,6 +86,7 @@ namespace doc {
|
||||
////////////////////////////////////////
|
||||
// Palettes
|
||||
|
||||
Palette* palette(frame_t frame) const;
|
||||
Palette* getPalette(FrameNumber frame) const;
|
||||
const PalettesList& getPalettes() const;
|
||||
|
||||
@ -124,17 +126,6 @@ namespace doc {
|
||||
|
||||
void remapImages(FrameNumber frameFrom, FrameNumber frameTo, const std::vector<uint8_t>& mapping);
|
||||
|
||||
// Draws the sprite in the given image at the given position. Before
|
||||
// drawing the sprite, this function clears (with the sprite's
|
||||
// background color) the rectangle area that will occupy the drawn
|
||||
// sprite.
|
||||
void render(Image* image, int x, int y, FrameNumber frame) const;
|
||||
|
||||
// Gets a pixel from the sprite in the specified position. If in the
|
||||
// specified coordinates there're background this routine will
|
||||
// return the 0 color (the mask-color).
|
||||
int getPixel(int x, int y, FrameNumber frame) const;
|
||||
|
||||
void pickCels(int x, int y, FrameNumber frame, int opacityThreshold, CelList& cels) const;
|
||||
|
||||
private:
|
||||
|
@ -2,6 +2,7 @@
|
||||
# Copyright (C) 2001-2014 David Capello
|
||||
|
||||
add_library(gfx-lib
|
||||
clip.cpp
|
||||
hsv.cpp
|
||||
packing_rects.cpp
|
||||
region.cpp
|
||||
|
64
src/gfx/clip.cpp
Normal file
64
src/gfx/clip.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/clip.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
bool Clip::clip(
|
||||
int avail_dst_w,
|
||||
int avail_dst_h,
|
||||
int avail_src_w,
|
||||
int avail_src_h)
|
||||
{
|
||||
// Clip srcBounds
|
||||
|
||||
if (src.x < 0) {
|
||||
size.w += src.x;
|
||||
dst.x -= src.x;
|
||||
src.x = 0;
|
||||
}
|
||||
|
||||
if (src.y < 0) {
|
||||
size.h += src.y;
|
||||
dst.y -= src.y;
|
||||
src.y = 0;
|
||||
}
|
||||
|
||||
if (src.x + size.w > avail_src_w)
|
||||
size.w -= src.x + size.w - avail_src_w;
|
||||
|
||||
if (src.y + size.h > avail_src_h)
|
||||
size.h -= src.y + size.h - avail_src_h;
|
||||
|
||||
// Clip dstBounds
|
||||
|
||||
if (dst.x < 0) {
|
||||
size.w += dst.x;
|
||||
src.x -= dst.x;
|
||||
dst.x = 0;
|
||||
}
|
||||
|
||||
if (dst.y < 0) {
|
||||
size.h += dst.y;
|
||||
src.y -= dst.y;
|
||||
dst.y = 0;
|
||||
}
|
||||
|
||||
if (dst.x + size.w > avail_dst_w)
|
||||
size.w -= dst.x + size.w - avail_dst_w;
|
||||
|
||||
if (dst.y + size.h > avail_dst_h)
|
||||
size.h -= dst.y + size.h - avail_dst_h;
|
||||
|
||||
return (size.w > 0 && size.h > 0);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
85
src/gfx/clip.h
Normal file
85
src/gfx/clip.h
Normal file
@ -0,0 +1,85 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef GFX_CLIP_H_INCLUDED
|
||||
#define GFX_CLIP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/size.h"
|
||||
|
||||
namespace gfx {
|
||||
|
||||
class Clip {
|
||||
public:
|
||||
Point dst;
|
||||
Point src;
|
||||
Size size;
|
||||
|
||||
Clip()
|
||||
: dst(0, 0)
|
||||
, src(0, 0)
|
||||
, size(0, 0) {
|
||||
}
|
||||
|
||||
Clip(int w, int h)
|
||||
: dst(0, 0)
|
||||
, src(0, 0)
|
||||
, size(w, h) {
|
||||
}
|
||||
|
||||
Clip(int dst_x, int dst_y, int src_x, int src_y, int w, int h)
|
||||
: dst(dst_x, dst_y)
|
||||
, src(src_x, src_y)
|
||||
, size(w, h) {
|
||||
}
|
||||
|
||||
Clip(int dst_x, int dst_y, const Rect& srcBounds)
|
||||
: dst(dst_x, dst_y)
|
||||
, src(srcBounds.x, srcBounds.y)
|
||||
, size(srcBounds.w, srcBounds.h) {
|
||||
}
|
||||
|
||||
Clip(const Point& dst, const Point& src, const Size& size)
|
||||
: dst(dst)
|
||||
, src(src)
|
||||
, size(size) {
|
||||
}
|
||||
|
||||
Clip(const Point& dst, const Rect& srcBounds)
|
||||
: dst(dst)
|
||||
, src(srcBounds.x, srcBounds.y)
|
||||
, size(srcBounds.w, srcBounds.h) {
|
||||
}
|
||||
|
||||
Clip(const Rect& bounds)
|
||||
: dst(bounds.x, bounds.y)
|
||||
, src(bounds.x, bounds.y)
|
||||
, size(bounds.w, bounds.h) {
|
||||
}
|
||||
|
||||
Rect dstBounds() const { return Rect(dst, size); }
|
||||
Rect srcBounds() const { return Rect(src, size); }
|
||||
|
||||
bool operator==(const Clip& other) const {
|
||||
return (dst == other.dst &&
|
||||
src == other.src &&
|
||||
size == other.size);
|
||||
}
|
||||
|
||||
bool clip(
|
||||
// Available area
|
||||
int avail_dst_w,
|
||||
int avail_dst_h,
|
||||
int avail_src_w,
|
||||
int avail_src_h);
|
||||
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
#endif
|
100
src/gfx/clip_tests.cpp
Normal file
100
src/gfx/clip_tests.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/clip.h"
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const Clip& area)
|
||||
{
|
||||
return os << "("
|
||||
<< area.dst.x << ", "
|
||||
<< area.dst.y << ", "
|
||||
<< area.src.x << ", "
|
||||
<< area.src.y << ", "
|
||||
<< area.size.w << ", "
|
||||
<< area.size.h << ")";
|
||||
}
|
||||
|
||||
TEST(ScaledClip, WithoutClip)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(0, 0, 0, 0, 16, 16);
|
||||
EXPECT_TRUE(area.clip(16, 16, 16, 16));
|
||||
EXPECT_EQ(Clip(0, 0, 0, 0, 16, 16), area);
|
||||
|
||||
area = Clip(2, 2, 0, 0, 16, 16);
|
||||
EXPECT_TRUE(area.clip(32, 32, 16, 16));
|
||||
EXPECT_EQ(Clip(2, 2, 0, 0, 16, 16), area);
|
||||
}
|
||||
|
||||
TEST(ScaledClip, FullyClipped)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(32, 32, 0, 0, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
|
||||
area = Clip(-16, -16, 0, 0, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
|
||||
area = Clip(0, 0, 16, 16, 16, 16);
|
||||
EXPECT_FALSE(area.clip(32, 32, 16, 16));
|
||||
}
|
||||
|
||||
TEST(ScaledClip, WithoutZoomWithClip)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(2, 3, 1, -1, 4, 3);
|
||||
EXPECT_TRUE(area.clip(30, 29, 16, 16));
|
||||
EXPECT_EQ(Clip(2, 4, 1, 0, 4, 2), area);
|
||||
|
||||
area = Clip(0, 0, -1, -4, 8, 5);
|
||||
EXPECT_TRUE(area.clip(3, 32, 8, 8));
|
||||
EXPECT_EQ(Clip(1, 4, 0, 0, 2, 1), area);
|
||||
}
|
||||
|
||||
TEST(ScaledClip, Zoom)
|
||||
{
|
||||
Clip area;
|
||||
|
||||
area = Clip(0, 0, 0, 0, 32, 32);
|
||||
EXPECT_TRUE(area.clip(32, 32, 16, 16));
|
||||
EXPECT_EQ(Clip(0, 0, 0, 0, 16, 16), area);
|
||||
|
||||
area = Clip(0, 0, 1, 2, 32, 32);
|
||||
EXPECT_TRUE(area.clip(32, 32, 32, 32));
|
||||
EXPECT_EQ(Clip(0, 0, 1, 2, 31, 30), area);
|
||||
|
||||
// X:
|
||||
// -1 0 1 2 3 4 5
|
||||
// [ ]
|
||||
// a[a a b] b c c c DST
|
||||
// a a[a b b]b c c c SRC
|
||||
//
|
||||
// Y:
|
||||
// -1 0 1 2 3
|
||||
// [ ]
|
||||
// a[a a]b b b DST
|
||||
// [a a]a b b b c c SRC
|
||||
area = Clip(-1, 1, 1, -1, 4, 4);
|
||||
EXPECT_TRUE(area.clip(6, 4, 9, 9));
|
||||
EXPECT_EQ(Clip(0, 2, 2, 0, 3, 2), area);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -4,6 +4,10 @@
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include <cmath>
|
||||
|
@ -4,6 +4,10 @@
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
|
@ -4,6 +4,10 @@
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/packing_rects.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace gfx {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Rect& rect)
|
||||
inline std::ostream& operator<<(std::ostream& os, const Rect& rect)
|
||||
{
|
||||
return os << "("
|
||||
<< rect.x << ", "
|
||||
|
@ -4,6 +4,10 @@
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/border.h"
|
||||
|
@ -4,6 +4,10 @@
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "gfx/rgb.h"
|
||||
|
8
src/render/CMakeLists.txt
Normal file
8
src/render/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# Aseprite Render Library
|
||||
# Copyright (C) 2001-2014 David Capello
|
||||
|
||||
add_library(render-lib
|
||||
get_sprite_pixel.cpp
|
||||
quantization.cpp
|
||||
render.cpp
|
||||
zoom.cpp)
|
20
src/render/LICENSE.txt
Normal file
20
src/render/LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2001-2014 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
4
src/render/README.md
Normal file
4
src/render/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Aseprite Render Library
|
||||
*Copyright (C) 2001-2014 David Capello*
|
||||
|
||||
> Distributed under [MIT license](LICENSE.txt)
|
@ -1,11 +1,11 @@
|
||||
// Aseprite Document Library
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_COLOR_HISTOGRAM_H_INCLUDED
|
||||
#define DOC_COLOR_HISTOGRAM_H_INCLUDED
|
||||
#ifndef RENDER_COLOR_HISTOGRAM_H_INCLUDED
|
||||
#define RENDER_COLOR_HISTOGRAM_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
@ -13,11 +13,12 @@
|
||||
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_traits.h"
|
||||
#include "doc/median_cut.h"
|
||||
#include "doc/palette.h"
|
||||
|
||||
namespace doc {
|
||||
namespace quantization {
|
||||
#include "render/median_cut.h"
|
||||
|
||||
namespace render {
|
||||
using namespace doc;
|
||||
|
||||
template<int RBits, int GBits, int BBits> // Number of bits for each component in the histogram
|
||||
class ColorHistogram {
|
||||
@ -128,7 +129,6 @@ namespace quantization {
|
||||
bool m_useHighPrecision;
|
||||
};
|
||||
|
||||
} // namespace quantization
|
||||
} // namespace doc
|
||||
} // namespace render
|
||||
|
||||
#endif
|
35
src/render/get_sprite_pixel.cpp
Normal file
35
src/render/get_sprite_pixel.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/doc.h"
|
||||
#include "gfx/clip.h"
|
||||
#include "render/render.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
color_t get_sprite_pixel(const Sprite* sprite, int x, int y, FrameNumber frame)
|
||||
{
|
||||
color_t color = 0;
|
||||
|
||||
if ((x >= 0) && (y >= 0) && (x < sprite->width()) && (y < sprite->height())) {
|
||||
base::UniquePtr<Image> image(Image::create(sprite->pixelFormat(), 1, 1));
|
||||
|
||||
render::Render().renderSprite(image, sprite, frame,
|
||||
gfx::Clip(0, 0, x, y, 1, 1));
|
||||
|
||||
color = get_pixel(image, 0, 0);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
} // namespace render
|
27
src/render/get_sprite_pixel.h
Normal file
27
src/render/get_sprite_pixel.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef RENDER_GET_SPRITE_PIXEL_H_INCLUDED
|
||||
#define RENDER_GET_SPRITE_PIXEL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/frame_number.h"
|
||||
|
||||
namespace doc {
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace render {
|
||||
using namespace doc;
|
||||
|
||||
// Gets a pixel from the sprite in the specified position. If in the
|
||||
// specified coordinates there're background this routine will
|
||||
// return the 0 color (the mask-color).
|
||||
color_t get_sprite_pixel(const Sprite* sprite, int x, int y, FrameNumber frame);
|
||||
|
||||
} // namespace render
|
||||
|
||||
#endif
|
@ -1,18 +1,17 @@
|
||||
// Aseprite Document Library
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_MEDIAN_CUT_H_INCLUDED
|
||||
#define DOC_MEDIAN_CUT_H_INCLUDED
|
||||
#ifndef RENDER_MEDIAN_CUT_H_INCLUDED
|
||||
#define RENDER_MEDIAN_CUT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <queue>
|
||||
|
||||
namespace doc {
|
||||
namespace quantization {
|
||||
namespace render {
|
||||
|
||||
template<class Histogram>
|
||||
class Box {
|
||||
@ -287,7 +286,6 @@ namespace quantization {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace quantization
|
||||
} // namespace doc
|
||||
} // namespace render
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
// Aseprite Document Library
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -8,10 +8,8 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/quantization.h"
|
||||
#include "render/quantization.h"
|
||||
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "doc/blend.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_bits.h"
|
||||
@ -21,14 +19,17 @@
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "gfx/hsv.h"
|
||||
#include "gfx/rgb.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
namespace quantization {
|
||||
namespace render {
|
||||
|
||||
using namespace doc;
|
||||
using namespace gfx;
|
||||
|
||||
// Converts a RGB image to indexed with ordered dithering method.
|
||||
@ -50,15 +51,17 @@ Palette* create_palette_from_rgb(
|
||||
bool has_background_layer = (sprite->backgroundLayer() != NULL);
|
||||
Image* flat_image;
|
||||
|
||||
ImagesCollector images(sprite->folder(), // All layers
|
||||
frameNumber, // Ignored, we'll use all frames
|
||||
true, // All frames,
|
||||
false); // forWrite=false, read only
|
||||
ImagesCollector images(
|
||||
sprite->folder(), // All layers
|
||||
frameNumber, // Ignored, we'll use all frames
|
||||
true, // All frames,
|
||||
false); // forWrite=false, read only
|
||||
|
||||
// Add a flat image with the current sprite's frame rendered
|
||||
flat_image = Image::create(sprite->pixelFormat(), sprite->width(), sprite->height());
|
||||
clear_image(flat_image, 0);
|
||||
sprite->render(flat_image, 0, 0, frameNumber);
|
||||
flat_image = Image::create(sprite->pixelFormat(),
|
||||
sprite->width(), sprite->height());
|
||||
|
||||
render::Render().renderSprite(flat_image, sprite, frameNumber);
|
||||
|
||||
// Create an array of images
|
||||
size_t nimage = images.size() + 1; // +1 for flat_image
|
||||
@ -91,7 +94,7 @@ Image* convert_pixel_format(
|
||||
// RGB -> Indexed with ordered dithering
|
||||
if (image->pixelFormat() == IMAGE_RGB &&
|
||||
pixelFormat == IMAGE_INDEXED &&
|
||||
ditheringMethod == DITHERING_ORDERED) {
|
||||
ditheringMethod == DitheringMethod::ORDERED) {
|
||||
return ordered_dithering(image, new_image, 0, 0, rgbmap, palette);
|
||||
}
|
||||
|
||||
@ -108,7 +111,7 @@ Image* convert_pixel_format(
|
||||
|
||||
// RGB -> RGB
|
||||
case IMAGE_RGB:
|
||||
new_image->copy(image, 0, 0, 0, 0, image->width(), image->height());
|
||||
new_image->copy(image, gfx::Clip(image->bounds()));
|
||||
break;
|
||||
|
||||
// RGB -> Grayscale
|
||||
@ -189,7 +192,7 @@ Image* convert_pixel_format(
|
||||
|
||||
// Grayscale -> Grayscale
|
||||
case IMAGE_GRAYSCALE:
|
||||
new_image->copy(image, 0, 0, 0, 0, image->width(), image->height());
|
||||
new_image->copy(image, gfx::Clip(image->bounds()));
|
||||
break;
|
||||
|
||||
// Grayscale -> Indexed
|
||||
@ -486,5 +489,4 @@ void create_palette_from_images(const std::vector<Image*>& images, Palette* pale
|
||||
optimizer.calculate(palette, has_background_layer);
|
||||
}
|
||||
|
||||
} // namespace quantization
|
||||
} // namespace doc
|
||||
} // namespace render
|
63
src/render/quantization.h
Normal file
63
src/render/quantization.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Aseprite Rener Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef RENDER_QUANTIZATION_H_INCLUDED
|
||||
#define RENDER_QUANTIZATION_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/dithering_method.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/pixel_format.h"
|
||||
|
||||
#include "render/color_histogram.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Palette;
|
||||
class RgbMap;
|
||||
class Sprite;
|
||||
class Stock;
|
||||
}
|
||||
|
||||
namespace render {
|
||||
using namespace doc;
|
||||
|
||||
class PaletteOptimizer {
|
||||
public:
|
||||
void feedWithImage(Image* image);
|
||||
void calculate(Palette* palette, bool has_background_layer);
|
||||
|
||||
private:
|
||||
ColorHistogram<5, 6, 5> m_histogram;
|
||||
};
|
||||
|
||||
void create_palette_from_images(
|
||||
const std::vector<Image*>& images,
|
||||
Palette* palette,
|
||||
bool has_background_layer);
|
||||
|
||||
// Creates a new palette suitable to quantize the given RGB sprite to Indexed color.
|
||||
Palette* create_palette_from_rgb(
|
||||
const Sprite* sprite,
|
||||
FrameNumber frameNumber,
|
||||
Palette* newPalette); // Can be NULL to create a new palette
|
||||
|
||||
// Changes the image pixel format. The dithering method is used only
|
||||
// when you want to convert from RGB to Indexed.
|
||||
Image* convert_pixel_format(
|
||||
const Image* src,
|
||||
Image* dst, // Can be NULL to create a new image
|
||||
PixelFormat pixelFormat,
|
||||
DitheringMethod ditheringMethod,
|
||||
const RgbMap* rgbmap,
|
||||
const Palette* palette,
|
||||
bool is_background);
|
||||
|
||||
} // namespace render
|
||||
|
||||
#endif
|
738
src/render/render.cpp
Normal file
738
src/render/render.cpp
Normal file
@ -0,0 +1,738 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "render/render.h"
|
||||
|
||||
#include "doc/doc.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Scaled composite
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
class BlenderHelper
|
||||
{
|
||||
BLEND_COLOR m_blend_color;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_color = SrcTraits::get_blender(blend_mode);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline void operator()(typename DstTraits::pixel_t& scanline,
|
||||
const typename DstTraits::pixel_t& dst,
|
||||
const typename SrcTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_mask_color)
|
||||
scanline = (*m_blend_color)(dst, src, opacity);
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, GrayscaleTraits>
|
||||
{
|
||||
BLEND_COLOR m_blend_color;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_color = RgbTraits::get_blender(blend_mode);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline void operator()(RgbTraits::pixel_t& scanline,
|
||||
const RgbTraits::pixel_t& dst,
|
||||
const GrayscaleTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_mask_color) {
|
||||
int v = graya_getv(src);
|
||||
scanline = (*m_blend_color)(dst, rgba(v, v, v, graya_geta(src)), opacity);
|
||||
}
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, IndexedTraits>
|
||||
{
|
||||
const Palette* m_pal;
|
||||
int m_blend_mode;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
|
||||
{
|
||||
m_blend_mode = blend_mode;
|
||||
m_mask_color = src->maskColor();
|
||||
m_pal = pal;
|
||||
}
|
||||
inline void operator()(RgbTraits::pixel_t& scanline,
|
||||
const RgbTraits::pixel_t& dst,
|
||||
const IndexedTraits::pixel_t& src,
|
||||
int opacity)
|
||||
{
|
||||
if (m_blend_mode == BLEND_MODE_COPY) {
|
||||
scanline = m_pal->getEntry(src);
|
||||
}
|
||||
else {
|
||||
if (src != m_mask_color) {
|
||||
scanline = rgba_blend_normal(dst, m_pal->getEntry(src), opacity);
|
||||
}
|
||||
else
|
||||
scanline = dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void compose_scaled_image_scale_up(
|
||||
Image* dst, const Image* src, const Palette* pal,
|
||||
gfx::Clip area,
|
||||
int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int px_x, px_y;
|
||||
|
||||
if (!area.clip(dst->width(), dst->height(),
|
||||
zoom.apply(src->width()),
|
||||
zoom.apply(src->height())))
|
||||
return;
|
||||
|
||||
int px_w = zoom.apply(1);
|
||||
int px_h = zoom.apply(1);
|
||||
int first_px_w = px_w - (area.src.x % px_w);
|
||||
int first_px_h = px_h - (area.src.y % px_h);
|
||||
gfx::Rect srcBounds = zoom.remove(area.srcBounds());
|
||||
gfx::Rect dstBounds = area.dstBounds();
|
||||
int bottom = area.dst.y+area.size.h-1;
|
||||
int line_h;
|
||||
|
||||
if ((area.src.x+area.size.w) % px_w > 0) ++srcBounds.w;
|
||||
if ((area.src.y+area.size.h) % px_h > 0) ++srcBounds.h;
|
||||
|
||||
if (srcBounds.isEmpty())
|
||||
return;
|
||||
|
||||
// the scanline variable is used to blend src/dst pixels one time for each pixel
|
||||
typedef std::vector<typename DstTraits::pixel_t> Scanline;
|
||||
Scanline scanline(srcBounds.w);
|
||||
typename Scanline::iterator scanline_it;
|
||||
#ifdef _DEBUG
|
||||
typename Scanline::iterator scanline_end = scanline.end();
|
||||
#endif
|
||||
|
||||
// Lock all necessary bits
|
||||
const LockImageBits<SrcTraits> srcBits(src, srcBounds);
|
||||
LockImageBits<DstTraits> dstBits(dst, dstBounds);
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_it = srcBits.begin();
|
||||
#ifdef _DEBUG
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_end = srcBits.end();
|
||||
#endif
|
||||
typename LockImageBits<DstTraits>::iterator dst_it, dst_end;
|
||||
|
||||
// For each line to draw of the source image...
|
||||
dstBounds.h = 1;
|
||||
for (int y=0; y<srcBounds.h; ++y) {
|
||||
dst_it = dstBits.begin_area(dstBounds);
|
||||
dst_end = dstBits.end_area(dstBounds);
|
||||
|
||||
// Read 'src' and 'dst' and blend them, put the result in `scanline'
|
||||
scanline_it = scanline.begin();
|
||||
for (int x=0; x<srcBounds.w; ++x) {
|
||||
ASSERT(src_it >= srcBits.begin() && src_it < src_end);
|
||||
ASSERT(dst_it >= dstBits.begin() && dst_it < dst_end);
|
||||
ASSERT(scanline_it >= scanline.begin() && scanline_it < scanline_end);
|
||||
|
||||
blender(*scanline_it, *dst_it, *src_it, opacity);
|
||||
++src_it;
|
||||
|
||||
int delta;
|
||||
if (x == 0)
|
||||
delta = first_px_w;
|
||||
else
|
||||
delta = px_w;
|
||||
|
||||
while (dst_it != dst_end && delta-- > 0)
|
||||
++dst_it;
|
||||
|
||||
++scanline_it;
|
||||
}
|
||||
|
||||
// Get the 'height' of the line to be painted in 'dst'
|
||||
if ((y == 0) && (first_px_h > 0))
|
||||
line_h = first_px_h;
|
||||
else
|
||||
line_h = px_h;
|
||||
|
||||
// Draw the line in 'dst'
|
||||
for (px_y=0; px_y<line_h; ++px_y) {
|
||||
dst_it = dstBits.begin_area(dstBounds);
|
||||
dst_end = dstBits.end_area(dstBounds);
|
||||
scanline_it = scanline.begin();
|
||||
|
||||
int x = 0;
|
||||
|
||||
// first pixel
|
||||
for (px_x=0; px_x<first_px_w; ++px_x) {
|
||||
ASSERT(scanline_it != scanline_end);
|
||||
ASSERT(dst_it != dst_end);
|
||||
|
||||
*dst_it = *scanline_it;
|
||||
|
||||
++dst_it;
|
||||
if (dst_it == dst_end)
|
||||
goto done_with_line;
|
||||
}
|
||||
|
||||
++scanline_it;
|
||||
++x;
|
||||
|
||||
// the rest of the line
|
||||
for (; x<srcBounds.w; ++x) {
|
||||
for (px_x=0; px_x<px_w; ++px_x) {
|
||||
ASSERT(dst_it != dst_end);
|
||||
|
||||
*dst_it = *scanline_it;
|
||||
|
||||
++dst_it;
|
||||
if (dst_it == dst_end)
|
||||
goto done_with_line;
|
||||
}
|
||||
|
||||
++scanline_it;
|
||||
}
|
||||
|
||||
done_with_line:;
|
||||
if (++dstBounds.y > bottom)
|
||||
goto done_with_blit;
|
||||
}
|
||||
}
|
||||
|
||||
done_with_blit:;
|
||||
}
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void compose_scaled_image_scale_down(
|
||||
Image* dst, const Image* src, const Palette* pal,
|
||||
gfx::Clip area,
|
||||
int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int unbox_w = zoom.remove(1);
|
||||
int unbox_h = zoom.remove(1);
|
||||
|
||||
if (!area.clip(dst->width(), dst->height(),
|
||||
zoom.apply(src->width()),
|
||||
zoom.apply(src->height())))
|
||||
return;
|
||||
|
||||
gfx::Rect srcBounds = zoom.remove(area.srcBounds());
|
||||
gfx::Rect dstBounds = area.dstBounds();
|
||||
int bottom = area.dst.y+area.size.h-1;
|
||||
|
||||
if (srcBounds.isEmpty())
|
||||
return;
|
||||
|
||||
// Lock all necessary bits
|
||||
const LockImageBits<SrcTraits> srcBits(src, srcBounds);
|
||||
LockImageBits<DstTraits> dstBits(dst, dstBounds);
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_it = srcBits.begin();
|
||||
typename LockImageBits<SrcTraits>::const_iterator src_end = srcBits.end();
|
||||
typename LockImageBits<DstTraits>::iterator dst_it, dst_end;
|
||||
|
||||
// For each line to draw of the source image...
|
||||
dstBounds.h = 1;
|
||||
for (int y=0; y<srcBounds.h; y+=unbox_h) {
|
||||
dst_it = dstBits.begin_area(dstBounds);
|
||||
dst_end = dstBits.end_area(dstBounds);
|
||||
|
||||
for (int x=0; x<srcBounds.w; x+=unbox_w) {
|
||||
ASSERT(src_it >= srcBits.begin() && src_it < src_end);
|
||||
ASSERT(dst_it >= dstBits.begin() && dst_it < dst_end);
|
||||
|
||||
blender(*dst_it, *dst_it, *src_it, opacity);
|
||||
|
||||
// Skip source pixels
|
||||
for (int delta=0; delta < unbox_w && src_it != src_end; ++delta)
|
||||
++src_it;
|
||||
|
||||
++dst_it;
|
||||
}
|
||||
|
||||
if (++dstBounds.y > bottom)
|
||||
break;
|
||||
|
||||
// Skip lines
|
||||
for (int delta=0; delta < srcBounds.w * (unbox_h-1) && src_it != src_end; ++delta)
|
||||
++src_it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
static void compose_scaled_image(
|
||||
Image* dst, const Image* src, const Palette* pal,
|
||||
const gfx::Clip& area,
|
||||
int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
if (zoom.scale() >= 1.0)
|
||||
compose_scaled_image_scale_up<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blend_mode, zoom);
|
||||
else
|
||||
compose_scaled_image_scale_down<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blend_mode, zoom);
|
||||
}
|
||||
|
||||
Render::Render()
|
||||
: m_sprite(NULL)
|
||||
, m_currentLayer(NULL)
|
||||
, m_currentFrame(0)
|
||||
, m_extraCel(NULL)
|
||||
, m_extraImage(NULL)
|
||||
, m_bgType(BgType::TRANSPARENT)
|
||||
, m_bgCheckedSize(16, 16)
|
||||
, m_globalOpacity(255)
|
||||
, m_onionskinType(OnionskinType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
void Render::setBgType(BgType type)
|
||||
{
|
||||
m_bgType = type;
|
||||
}
|
||||
|
||||
void Render::setBgZoom(bool state)
|
||||
{
|
||||
m_bgZoom = state;
|
||||
}
|
||||
|
||||
void Render::setBgColor1(color_t color)
|
||||
{
|
||||
m_bgColor1 = color;
|
||||
}
|
||||
|
||||
void Render::setBgColor2(color_t color)
|
||||
{
|
||||
m_bgColor2 = color;
|
||||
}
|
||||
|
||||
void Render::setBgCheckedSize(const gfx::Size& size)
|
||||
{
|
||||
m_bgCheckedSize = size;
|
||||
}
|
||||
|
||||
void Render::setPreviewImage(const Layer* layer, FrameNumber frame, Image* image)
|
||||
{
|
||||
m_selectedLayer = layer;
|
||||
m_selectedFrame = frame;
|
||||
m_previewImage = image;
|
||||
}
|
||||
|
||||
void Render::setExtraImage(
|
||||
const Cel* cel, const Image* image, int blendMode,
|
||||
const Layer* currentLayer,
|
||||
FrameNumber currentFrame)
|
||||
{
|
||||
m_extraCel = cel;
|
||||
m_extraImage = image;
|
||||
m_extraBlendMode = blendMode;
|
||||
m_currentLayer = currentLayer;
|
||||
m_currentFrame = currentFrame;
|
||||
}
|
||||
|
||||
void Render::removePreviewImage()
|
||||
{
|
||||
m_previewImage = NULL;
|
||||
}
|
||||
|
||||
void Render::removeExtraImage()
|
||||
{
|
||||
m_extraCel = NULL;
|
||||
}
|
||||
|
||||
void Render::setOnionskin(OnionskinType type, int prevs, int nexts, int opacityBase, int opacityStep)
|
||||
{
|
||||
m_onionskinType = type;
|
||||
m_onionskinPrevs = prevs;
|
||||
m_onionskinNexts = nexts;
|
||||
m_onionskinOpacityBase = opacityBase;
|
||||
m_onionskinOpacityStep = opacityStep;
|
||||
}
|
||||
|
||||
void Render::disableOnionskin()
|
||||
{
|
||||
m_onionskinType = OnionskinType::NONE;
|
||||
}
|
||||
|
||||
void Render::renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame)
|
||||
{
|
||||
renderSprite(dstImage, sprite, frame,
|
||||
gfx::Clip(sprite->bounds()), Zoom(1, 1));
|
||||
}
|
||||
|
||||
void Render::renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area)
|
||||
{
|
||||
renderSprite(dstImage, sprite, frame, area, Zoom(1, 1));
|
||||
}
|
||||
|
||||
void Render::renderLayer(
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
FrameNumber frame)
|
||||
{
|
||||
renderLayer(dstImage, layer, frame,
|
||||
gfx::Clip(layer->sprite()->bounds()));
|
||||
}
|
||||
|
||||
void Render::renderLayer(
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area)
|
||||
{
|
||||
m_sprite = layer->sprite();
|
||||
|
||||
RenderScaledImage scaled_func =
|
||||
getRenderScaledImageFunc(
|
||||
dstImage->pixelFormat(),
|
||||
m_sprite->pixelFormat());
|
||||
if (!scaled_func)
|
||||
return;
|
||||
|
||||
const LayerImage* background = m_sprite->backgroundLayer();
|
||||
bool need_checked_bg = (background != NULL ? !background->isVisible(): true);
|
||||
color_t bg_color = 0;
|
||||
|
||||
m_globalOpacity = 255;
|
||||
renderLayer(layer, dstImage, area,
|
||||
frame, Zoom(1, 1), scaled_func,
|
||||
true, true, -1);
|
||||
}
|
||||
|
||||
void Render::renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area,
|
||||
Zoom zoom)
|
||||
{
|
||||
m_sprite = sprite;
|
||||
|
||||
RenderScaledImage scaled_func =
|
||||
getRenderScaledImageFunc(
|
||||
dstImage->pixelFormat(),
|
||||
m_sprite->pixelFormat());
|
||||
if (!scaled_func)
|
||||
return;
|
||||
|
||||
const LayerImage* bgLayer = m_sprite->backgroundLayer();
|
||||
color_t bg_color = 0;
|
||||
if (m_sprite->pixelFormat() == IMAGE_INDEXED) {
|
||||
switch (dstImage->pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
case IMAGE_GRAYSCALE:
|
||||
if (bgLayer && bgLayer->isVisible())
|
||||
bg_color = m_sprite->getPalette(frame)->getEntry(m_sprite->transparentColor());
|
||||
break;
|
||||
case IMAGE_INDEXED:
|
||||
bg_color = m_sprite->transparentColor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw checked background
|
||||
switch (m_bgType) {
|
||||
|
||||
case BgType::CHECKED:
|
||||
if (bgLayer && bgLayer->isVisible())
|
||||
fill_rect(dstImage, area.dstBounds(), bg_color);
|
||||
else
|
||||
renderBackground(dstImage, area, zoom);
|
||||
break;
|
||||
|
||||
case BgType::TRANSPARENT:
|
||||
fill_rect(dstImage, area.dstBounds(), bg_color);
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw the current frame.
|
||||
m_globalOpacity = 255;
|
||||
renderLayer(
|
||||
m_sprite->folder(), dstImage,
|
||||
area, frame, zoom, scaled_func,
|
||||
true, true, -1);
|
||||
|
||||
// Onion-skin feature: Draw previous/next frames with different
|
||||
// opacity (<255)
|
||||
if (m_onionskinType != OnionskinType::NONE) {
|
||||
for (FrameNumber f=frame.previous(m_onionskinPrevs);
|
||||
f <= frame.next(m_onionskinNexts); ++f) {
|
||||
if (f == frame || f < 0 || f > m_sprite->lastFrame())
|
||||
continue;
|
||||
else if (f < frame)
|
||||
m_globalOpacity = m_onionskinOpacityBase - m_onionskinOpacityStep * ((frame - f)-1);
|
||||
else
|
||||
m_globalOpacity = m_onionskinOpacityBase - m_onionskinOpacityStep * ((f - frame)-1);
|
||||
|
||||
if (m_globalOpacity > 0) {
|
||||
m_globalOpacity = MID(0, m_globalOpacity, 255);
|
||||
|
||||
int blend_mode = -1;
|
||||
if (m_onionskinType == OnionskinType::MERGE)
|
||||
blend_mode = BLEND_MODE_NORMAL;
|
||||
else if (m_onionskinType == OnionskinType::RED_BLUE_TINT)
|
||||
blend_mode = (f < frame ? BLEND_MODE_RED_TINT: BLEND_MODE_BLUE_TINT);
|
||||
|
||||
renderLayer(m_sprite->folder(), dstImage,
|
||||
area, f, zoom, scaled_func,
|
||||
true, true, blend_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render::renderBackground(Image* image,
|
||||
const gfx::Clip& area,
|
||||
Zoom zoom)
|
||||
{
|
||||
int x, y, u, v;
|
||||
int tile_w = m_bgCheckedSize.w;
|
||||
int tile_h = m_bgCheckedSize.h;
|
||||
|
||||
if (m_bgZoom) {
|
||||
tile_w = zoom.apply(tile_w);
|
||||
tile_h = zoom.apply(tile_h);
|
||||
}
|
||||
|
||||
// Tile size
|
||||
if (tile_w < zoom.apply(1)) tile_w = zoom.apply(1);
|
||||
if (tile_h < zoom.apply(1)) tile_h = zoom.apply(1);
|
||||
|
||||
if (tile_w < 1) tile_w = 1;
|
||||
if (tile_h < 1) tile_h = 1;
|
||||
|
||||
// Tile position (u,v) is the number of tile we start in "area.src" coordinate
|
||||
u = (area.src.x / tile_w);
|
||||
v = (area.src.y / tile_h);
|
||||
|
||||
// Position where we start drawing the first tile in "image"
|
||||
int x_start = -(area.src.x % tile_w);
|
||||
int y_start = -(area.src.y % tile_h);
|
||||
|
||||
gfx::Rect dstBounds = area.dstBounds();
|
||||
|
||||
// Draw checked background (tile by tile)
|
||||
int u_start = u;
|
||||
for (y=y_start-tile_h; y<image->height()+tile_h; y+=tile_h) {
|
||||
for (x=x_start-tile_w; x<image->width()+tile_w; x+=tile_w) {
|
||||
gfx::Rect fillRc = dstBounds.createIntersect(gfx::Rect(x, y, tile_w, tile_h));
|
||||
if (!fillRc.isEmpty())
|
||||
fill_rect(
|
||||
image, fillRc.x, fillRc.y, fillRc.x+fillRc.w-1, fillRc.y+fillRc.h-1,
|
||||
(((u+v))&1)? m_bgColor2: m_bgColor1);
|
||||
++u;
|
||||
}
|
||||
u = u_start;
|
||||
++v;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::renderImage(Image* dst_image, const Image* src_image,
|
||||
const Palette* pal, int x, int y, Zoom zoom, int opacity, int blend_mode)
|
||||
{
|
||||
RenderScaledImage scaled_func = getRenderScaledImageFunc(
|
||||
dst_image->pixelFormat(),
|
||||
src_image->pixelFormat());
|
||||
if (!scaled_func)
|
||||
return;
|
||||
|
||||
scaled_func(dst_image, src_image, pal,
|
||||
gfx::Clip(x, y, 0, 0,
|
||||
zoom.apply(src_image->width()),
|
||||
zoom.apply(src_image->height())),
|
||||
opacity, blend_mode, zoom);
|
||||
}
|
||||
|
||||
void Render::renderLayer(
|
||||
const Layer* layer,
|
||||
Image *image,
|
||||
const gfx::Clip& area,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
RenderScaledImage scaled_func,
|
||||
bool render_background,
|
||||
bool render_transparent,
|
||||
int blend_mode)
|
||||
{
|
||||
// we can't read from this layer
|
||||
if (!layer->isVisible())
|
||||
return;
|
||||
|
||||
switch (layer->type()) {
|
||||
|
||||
case ObjectType::LayerImage: {
|
||||
if ((!render_background && layer->isBackground()) ||
|
||||
(!render_transparent && !layer->isBackground()))
|
||||
break;
|
||||
|
||||
const Cel* cel = static_cast<const LayerImage*>(layer)->getCel(frame);
|
||||
if (cel != NULL) {
|
||||
Image* src_image;
|
||||
|
||||
// Is the 'm_previewImage' set to be used with this layer?
|
||||
if ((m_previewImage) &&
|
||||
(m_selectedLayer == layer) &&
|
||||
(m_selectedFrame == frame)) {
|
||||
src_image = m_previewImage;
|
||||
}
|
||||
// If not, we use the original cel-image from the images' stock
|
||||
else {
|
||||
src_image = cel->image();
|
||||
}
|
||||
|
||||
if (src_image) {
|
||||
int t, output_opacity;
|
||||
|
||||
output_opacity = MID(0, cel->opacity(), 255);
|
||||
output_opacity = INT_MULT(output_opacity, m_globalOpacity, t);
|
||||
|
||||
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
|
||||
|
||||
renderCel(image, src_image,
|
||||
m_sprite->getPalette(frame),
|
||||
cel, area, scaled_func,
|
||||
output_opacity,
|
||||
(blend_mode < 0 ?
|
||||
static_cast<const LayerImage*>(layer)->getBlendMode():
|
||||
blend_mode),
|
||||
zoom);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectType::LayerFolder: {
|
||||
LayerConstIterator it = static_cast<const LayerFolder*>(layer)->getLayerBegin();
|
||||
LayerConstIterator end = static_cast<const LayerFolder*>(layer)->getLayerEnd();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
renderLayer(*it, image,
|
||||
area, frame, zoom, scaled_func,
|
||||
render_background,
|
||||
render_transparent,
|
||||
blend_mode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Draw extras
|
||||
if (m_extraCel &&
|
||||
m_extraImage &&
|
||||
layer == m_currentLayer &&
|
||||
frame == m_currentFrame) {
|
||||
if (m_extraCel->opacity() > 0) {
|
||||
renderCel(image, m_extraImage,
|
||||
m_sprite->getPalette(frame),
|
||||
m_extraCel, area, scaled_func,
|
||||
m_extraCel->opacity(),
|
||||
m_extraBlendMode, zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Render::renderCel(
|
||||
Image* dst_image,
|
||||
const Image* cel_image,
|
||||
const Palette* pal,
|
||||
const Cel* cel,
|
||||
const gfx::Clip& area,
|
||||
RenderScaledImage scaled_func,
|
||||
int opacity, int blend_mode, Zoom zoom)
|
||||
{
|
||||
int cel_x = zoom.apply(cel->x());
|
||||
int cel_y = zoom.apply(cel->y());
|
||||
|
||||
gfx::Rect src_bounds =
|
||||
area.srcBounds().createIntersect(
|
||||
gfx::Rect(
|
||||
cel_x, cel_y,
|
||||
zoom.apply(cel_image->width()),
|
||||
zoom.apply(cel_image->height())));
|
||||
if (src_bounds.isEmpty())
|
||||
return;
|
||||
|
||||
(*scaled_func)(dst_image, cel_image, pal,
|
||||
gfx::Clip(
|
||||
area.dst.x + src_bounds.x - area.src.x,
|
||||
area.dst.y + src_bounds.y - area.src.y,
|
||||
src_bounds.x - cel_x,
|
||||
src_bounds.y - cel_y,
|
||||
src_bounds.w,
|
||||
src_bounds.h),
|
||||
opacity, blend_mode, zoom);
|
||||
}
|
||||
|
||||
// static
|
||||
Render::RenderScaledImage Render::getRenderScaledImageFunc(
|
||||
PixelFormat dstFormat,
|
||||
PixelFormat srcFormat)
|
||||
{
|
||||
switch (srcFormat) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
switch (dstFormat) {
|
||||
case IMAGE_RGB: return compose_scaled_image<RgbTraits, RgbTraits>;
|
||||
case IMAGE_GRAYSCALE: return compose_scaled_image<GrayscaleTraits, RgbTraits>;
|
||||
case IMAGE_INDEXED: return compose_scaled_image<IndexedTraits, RgbTraits>;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
switch (dstFormat) {
|
||||
case IMAGE_RGB: return compose_scaled_image<RgbTraits, GrayscaleTraits>;
|
||||
case IMAGE_GRAYSCALE: return compose_scaled_image<GrayscaleTraits, GrayscaleTraits>;
|
||||
case IMAGE_INDEXED: return compose_scaled_image<IndexedTraits, GrayscaleTraits>;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
switch (dstFormat) {
|
||||
case IMAGE_RGB: return compose_scaled_image<RgbTraits, IndexedTraits>;
|
||||
case IMAGE_GRAYSCALE: return compose_scaled_image<GrayscaleTraits, IndexedTraits>;
|
||||
case IMAGE_INDEXED: return compose_scaled_image<IndexedTraits, IndexedTraits>;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(false && "Invalid pixel formats");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void composite_image(Image* dst, const Image* src,
|
||||
int x, int y, int opacity, int blend_mode)
|
||||
{
|
||||
// As the background is not rendered in renderImage(), we don't need
|
||||
// to configure the Render instance's BgType.
|
||||
Render().renderImage(
|
||||
dst, src, NULL, x, y, Zoom(1, 1),
|
||||
opacity, blend_mode);
|
||||
}
|
||||
|
||||
} // namespace render
|
172
src/render/render.h
Normal file
172
src/render/render.h
Normal file
@ -0,0 +1,172 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef RENDER_RENDER_H_INCLUDED
|
||||
#define RENDER_RENDER_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/color.h"
|
||||
#include "doc/frame_number.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "gfx/fwd.h"
|
||||
#include "gfx/size.h"
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace gfx {
|
||||
class Clip;
|
||||
}
|
||||
|
||||
namespace doc {
|
||||
class Cel;
|
||||
class Image;
|
||||
class Layer;
|
||||
class Palette;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
namespace render {
|
||||
using namespace doc;
|
||||
|
||||
enum class BgType {
|
||||
NONE,
|
||||
TRANSPARENT,
|
||||
CHECKED,
|
||||
};
|
||||
|
||||
enum class OnionskinType {
|
||||
NONE,
|
||||
MERGE,
|
||||
RED_BLUE_TINT,
|
||||
};
|
||||
|
||||
class Render {
|
||||
public:
|
||||
Render();
|
||||
|
||||
// Background configuration
|
||||
void setBgType(BgType type);
|
||||
void setBgZoom(bool state);
|
||||
void setBgColor1(color_t color);
|
||||
void setBgColor2(color_t color);
|
||||
void setBgCheckedSize(const gfx::Size& size);
|
||||
|
||||
// Sets the preview image. This preview image is an alternative
|
||||
// image to be used for the given layer/frame.
|
||||
void setPreviewImage(const Layer* layer, FrameNumber frame, Image* drawable);
|
||||
void removePreviewImage();
|
||||
|
||||
// Sets an extra cel/image to be drawn after the current
|
||||
// layer/frame.
|
||||
void setExtraImage(
|
||||
const Cel* cel, const Image* image, int blendMode,
|
||||
const Layer* currentLayer,
|
||||
FrameNumber currentFrame);
|
||||
void removeExtraImage();
|
||||
|
||||
void setOnionskin(OnionskinType type,
|
||||
int prevs, int nexts, int opacityBase, int opacityStep);
|
||||
void disableOnionskin();
|
||||
|
||||
void renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame);
|
||||
|
||||
void renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area);
|
||||
|
||||
void renderLayer(
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
FrameNumber frame);
|
||||
|
||||
void renderLayer(
|
||||
Image* dstImage,
|
||||
const Layer* layer,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area);
|
||||
|
||||
// Main function used to render the sprite. Draws the given sprite
|
||||
// frame in a new image and return it. Note: zoomedRect must have
|
||||
// the zoom applied (zoomedRect = zoom.apply(spriteRect)).
|
||||
void renderSprite(
|
||||
Image* dstImage,
|
||||
const Sprite* sprite,
|
||||
FrameNumber frame,
|
||||
const gfx::Clip& area,
|
||||
Zoom zoom);
|
||||
|
||||
// Extra functions
|
||||
void renderBackground(Image* image,
|
||||
const gfx::Clip& area,
|
||||
Zoom zoom);
|
||||
|
||||
void renderImage(Image* dst_image, const Image* src_image,
|
||||
const Palette* pal, int x, int y, Zoom zoom,
|
||||
int opacity, int blend_mode);
|
||||
|
||||
private:
|
||||
typedef void (*RenderScaledImage)(
|
||||
Image* dst, const Image* src, const Palette* pal,
|
||||
const gfx::Clip& area,
|
||||
int opacity, int blend_mode, Zoom zoom);
|
||||
|
||||
void renderLayer(
|
||||
const Layer* layer,
|
||||
Image* image,
|
||||
const gfx::Clip& area,
|
||||
FrameNumber frame, Zoom zoom,
|
||||
RenderScaledImage renderScaledImage,
|
||||
bool render_background,
|
||||
bool render_transparent,
|
||||
int blend_mode);
|
||||
|
||||
void renderCel(
|
||||
Image* dst_image,
|
||||
const Image* cel_image,
|
||||
const Palette* pal,
|
||||
const Cel* cel,
|
||||
const gfx::Clip& area,
|
||||
RenderScaledImage scaled_func,
|
||||
int opacity, int blend_mode, Zoom zoom);
|
||||
|
||||
static RenderScaledImage getRenderScaledImageFunc(
|
||||
PixelFormat dstFormat,
|
||||
PixelFormat srcFormat);
|
||||
|
||||
const Sprite* m_sprite;
|
||||
const Layer* m_currentLayer;
|
||||
FrameNumber m_currentFrame;
|
||||
const Cel* m_extraCel;
|
||||
const Image* m_extraImage;
|
||||
int m_extraBlendMode;
|
||||
|
||||
BgType m_bgType;
|
||||
bool m_bgZoom;
|
||||
color_t m_bgColor1;
|
||||
color_t m_bgColor2;
|
||||
gfx::Size m_bgCheckedSize;
|
||||
int m_globalOpacity;
|
||||
const Layer* m_selectedLayer;
|
||||
FrameNumber m_selectedFrame;
|
||||
Image* m_previewImage;
|
||||
|
||||
OnionskinType m_onionskinType;
|
||||
int m_onionskinPrevs;
|
||||
int m_onionskinNexts;
|
||||
int m_onionskinOpacityBase;
|
||||
int m_onionskinOpacityStep;
|
||||
};
|
||||
|
||||
void composite_image(Image* dst, const Image* src,
|
||||
int x, int y, int opacity, int blend_mode);
|
||||
|
||||
} // namespace render
|
||||
|
||||
#endif
|
221
src/render/render_tests.cpp
Normal file
221
src/render/render_tests.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "render/render.h"
|
||||
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/context.h"
|
||||
#include "doc/document.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/primitives.h"
|
||||
|
||||
using namespace doc;
|
||||
using namespace render;
|
||||
|
||||
template<typename T>
|
||||
class RenderAllModes : public testing::Test {
|
||||
protected:
|
||||
RenderAllModes() { }
|
||||
};
|
||||
|
||||
typedef testing::Types<RgbTraits, GrayscaleTraits, IndexedTraits> ImageAllTraits;
|
||||
TYPED_TEST_CASE(RenderAllModes, ImageAllTraits);
|
||||
|
||||
// a b
|
||||
// c d
|
||||
#define EXPECT_2X2_PIXELS(image, a, b, c, d) \
|
||||
EXPECT_EQ(a, get_pixel(image, 0, 0)); \
|
||||
EXPECT_EQ(b, get_pixel(image, 1, 0)); \
|
||||
EXPECT_EQ(c, get_pixel(image, 0, 1)); \
|
||||
EXPECT_EQ(d, get_pixel(image, 1, 1))
|
||||
|
||||
// a b c d
|
||||
// e f g h
|
||||
// i j k l
|
||||
// m n o p
|
||||
#define EXPECT_4X4_PIXELS(image, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
|
||||
EXPECT_EQ(a, get_pixel(image, 0, 0)); \
|
||||
EXPECT_EQ(b, get_pixel(image, 1, 0)); \
|
||||
EXPECT_EQ(c, get_pixel(image, 2, 0)); \
|
||||
EXPECT_EQ(d, get_pixel(image, 3, 0)); \
|
||||
EXPECT_EQ(e, get_pixel(image, 0, 1)); \
|
||||
EXPECT_EQ(f, get_pixel(image, 1, 1)); \
|
||||
EXPECT_EQ(g, get_pixel(image, 2, 1)); \
|
||||
EXPECT_EQ(h, get_pixel(image, 3, 1)); \
|
||||
EXPECT_EQ(i, get_pixel(image, 0, 2)); \
|
||||
EXPECT_EQ(j, get_pixel(image, 1, 2)); \
|
||||
EXPECT_EQ(k, get_pixel(image, 2, 2)); \
|
||||
EXPECT_EQ(l, get_pixel(image, 3, 2)); \
|
||||
EXPECT_EQ(m, get_pixel(image, 0, 3)); \
|
||||
EXPECT_EQ(n, get_pixel(image, 1, 3)); \
|
||||
EXPECT_EQ(o, get_pixel(image, 2, 3)); \
|
||||
EXPECT_EQ(p, get_pixel(image, 3, 3))
|
||||
|
||||
TEST(Render, Basic)
|
||||
{
|
||||
Context ctx;
|
||||
Document* doc = ctx.documents().add(2, 2, ColorMode::RGB);
|
||||
|
||||
Image* src = doc->sprite()->layer(0)->cel(0)->image();
|
||||
clear_image(src, 2);
|
||||
|
||||
base::UniquePtr<Image> dst(Image::create(IMAGE_RGB, 2, 2));
|
||||
clear_image(dst, 1);
|
||||
EXPECT_2X2_PIXELS(dst, 1, 1, 1, 1);
|
||||
|
||||
Render render;
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
EXPECT_2X2_PIXELS(dst, 2, 2, 2, 2);
|
||||
}
|
||||
|
||||
TYPED_TEST(RenderAllModes, CheckDefaultBackgroundMode)
|
||||
{
|
||||
typedef TypeParam ImageTraits;
|
||||
|
||||
Context ctx;
|
||||
Document* doc = ctx.documents().add(2, 2,
|
||||
ColorMode(ImageTraits::pixel_format));
|
||||
|
||||
EXPECT_TRUE(!doc->sprite()->layer(0)->isBackground());
|
||||
Image* src = doc->sprite()->layer(0)->cel(0)->image();
|
||||
clear_image(src, 0);
|
||||
put_pixel(src, 1, 1, 1);
|
||||
|
||||
base::UniquePtr<Image> dst(Image::create(ImageTraits::pixel_format, 2, 2));
|
||||
clear_image(dst, 1);
|
||||
EXPECT_2X2_PIXELS(dst, 1, 1, 1, 1);
|
||||
|
||||
Render render;
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
// Default background mode is to set all pixels to transparent color
|
||||
EXPECT_2X2_PIXELS(dst, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
TEST(Render, DefaultBackgroundModeWithNonzeroTransparentIndex)
|
||||
{
|
||||
Context ctx;
|
||||
Document* doc = ctx.documents().add(2, 2, ColorMode::INDEXED);
|
||||
doc->sprite()->setTransparentColor(2); // Transparent color is index 2
|
||||
|
||||
EXPECT_TRUE(!doc->sprite()->layer(0)->isBackground());
|
||||
Image* src = doc->sprite()->layer(0)->cel(0)->image();
|
||||
clear_image(src, 2);
|
||||
put_pixel(src, 1, 1, 1);
|
||||
|
||||
base::UniquePtr<Image> dst(Image::create(IMAGE_INDEXED, 2, 2));
|
||||
clear_image(dst, 1);
|
||||
EXPECT_2X2_PIXELS(dst, 1, 1, 1, 1);
|
||||
|
||||
Render render;
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
EXPECT_2X2_PIXELS(dst, 2, 2, 2, 1); // Indexed transparent
|
||||
|
||||
dst.reset(Image::create(IMAGE_RGB, 2, 2));
|
||||
clear_image(dst, 1);
|
||||
EXPECT_2X2_PIXELS(dst, 1, 1, 1, 1);
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
color_t c1 = doc->sprite()->palette(0)->entry(1);
|
||||
EXPECT_NE(0, c1);
|
||||
EXPECT_2X2_PIXELS(dst, 0, 0, 0, c1); // RGB transparent
|
||||
}
|
||||
|
||||
TEST(Render, CheckedBackground)
|
||||
{
|
||||
Context ctx;
|
||||
Document* doc = ctx.documents().add(4, 4, ColorMode::RGB);
|
||||
|
||||
base::UniquePtr<Image> dst(Image::create(IMAGE_RGB, 4, 4));
|
||||
clear_image(dst, 0);
|
||||
|
||||
Render render;
|
||||
render.setBgType(BgType::CHECKED);
|
||||
render.setBgZoom(true);
|
||||
render.setBgColor1(1);
|
||||
render.setBgColor2(2);
|
||||
|
||||
render.setBgCheckedSize(gfx::Size(1, 1));
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
EXPECT_4X4_PIXELS(dst,
|
||||
1, 2, 1, 2,
|
||||
2, 1, 2, 1,
|
||||
1, 2, 1, 2,
|
||||
2, 1, 2, 1);
|
||||
|
||||
render.setBgCheckedSize(gfx::Size(2, 2));
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
EXPECT_4X4_PIXELS(dst,
|
||||
1, 1, 2, 2,
|
||||
1, 1, 2, 2,
|
||||
2, 2, 1, 1,
|
||||
2, 2, 1, 1);
|
||||
|
||||
render.setBgCheckedSize(gfx::Size(3, 3));
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0));
|
||||
EXPECT_4X4_PIXELS(dst,
|
||||
1, 1, 1, 2,
|
||||
1, 1, 1, 2,
|
||||
1, 1, 1, 2,
|
||||
2, 2, 2, 1);
|
||||
|
||||
render.setBgCheckedSize(gfx::Size(1, 1));
|
||||
render.renderSprite(dst,
|
||||
doc->sprite(), FrameNumber(0),
|
||||
gfx::Clip(dst->bounds()),
|
||||
Zoom(2, 1));
|
||||
EXPECT_4X4_PIXELS(dst,
|
||||
1, 1, 2, 2,
|
||||
1, 1, 2, 2,
|
||||
2, 2, 1, 1,
|
||||
2, 2, 1, 1);
|
||||
}
|
||||
|
||||
TEST(Render, ZoomAndDstBounds)
|
||||
{
|
||||
Context ctx;
|
||||
|
||||
// Create this image:
|
||||
// 0 0 0
|
||||
// 0 4 4
|
||||
// 0 4 4
|
||||
Document* doc = ctx.documents().add(3, 3, ColorMode::RGB);
|
||||
Image* src = doc->sprite()->layer(0)->cel(0)->image();
|
||||
clear_image(src, 0);
|
||||
fill_rect(src, 1, 1, 2, 2, 4);
|
||||
|
||||
base::UniquePtr<Image> dst(Image::create(IMAGE_RGB, 4, 4));
|
||||
clear_image(dst, 0);
|
||||
|
||||
Render render;
|
||||
render.setBgType(BgType::CHECKED);
|
||||
render.setBgZoom(true);
|
||||
render.setBgColor1(1);
|
||||
render.setBgColor2(2);
|
||||
render.setBgCheckedSize(gfx::Size(1, 1));
|
||||
|
||||
render.renderSprite(dst, doc->sprite(), FrameNumber(0),
|
||||
gfx::Clip(1, 1, 0, 0, 2, 2),
|
||||
Zoom(1, 1));
|
||||
EXPECT_4X4_PIXELS(dst,
|
||||
0, 0, 0, 0,
|
||||
0, 1, 2, 0,
|
||||
0, 2, 4, 0,
|
||||
0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
35
src/render/zoom.cpp
Normal file
35
src/render/zoom.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace render {
|
||||
|
||||
void Zoom::in()
|
||||
{
|
||||
if (m_den > 1) {
|
||||
m_den--;
|
||||
}
|
||||
else if (m_num < 64) {
|
||||
m_num++;
|
||||
}
|
||||
}
|
||||
|
||||
void Zoom::out()
|
||||
{
|
||||
if (m_num > 1) {
|
||||
m_num--;
|
||||
}
|
||||
else if (m_den < 32) {
|
||||
m_den++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace render
|
@ -1,28 +1,16 @@
|
||||
/* Aseprite
|
||||
* Copyright (C) 2001-2014 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2001-2014 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef APP_ZOOM_H_INCLUDED
|
||||
#define APP_ZOOM_H_INCLUDED
|
||||
#ifndef RENDER_ZOOM_H_INCLUDED
|
||||
#define RENDER_ZOOM_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace app {
|
||||
namespace render {
|
||||
|
||||
class Zoom {
|
||||
public:
|
||||
@ -67,6 +55,6 @@ namespace app {
|
||||
int m_den;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
} // namespace render
|
||||
|
||||
#endif // APP_ZOOM_H_INCLUDED
|
||||
#endif // RENDER_ZOOM_H_INCLUDED
|
Loading…
x
Reference in New Issue
Block a user