diff --git a/TODO.md b/TODO.md
index f4b456d06..7a55f7b58 100644
--- a/TODO.md
+++ b/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.
diff --git a/data/pref.xml b/data/pref.xml
index 3001d0e30..fcf2c0ac6 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -46,6 +46,12 @@
+
+
+
+
+
+
@@ -116,6 +122,12 @@
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b8d7fd652..522497e71 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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})
diff --git a/src/README.md b/src/README.md
index abab391d9..e5a2b2246 100644
--- a/src/README.md
+++ b/src/README.md
@@ -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
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 04ccd5ff7..f0c0c882f 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -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})
diff --git a/src/app/app.cpp b/src/app/app.cpp
index e49dc939c..aa3de559b 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -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)");
diff --git a/src/app/app_render.cpp b/src/app/app_render.cpp
new file mode 100644
index 000000000..ba2b2d431
--- /dev/null
+++ b/src/app/app_render.cpp
@@ -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);
+}
+
+}
diff --git a/src/app/zoom.cpp b/src/app/app_render.h
similarity index 63%
rename from src/app/zoom.cpp
rename to src/app/app_render.h
index d259d087e..87e323407 100644
--- a/src/app/zoom.cpp
+++ b/src/app/app_render.h
@@ -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
diff --git a/src/app/color_picker.cpp b/src/app/color_picker.cpp
index 250175c33..42672e3ce 100644
--- a/src/app/color_picker.cpp
+++ b/src/app/color_picker.cpp
@@ -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);
diff --git a/src/app/commands/cmd_change_pixel_format.cpp b/src/app/commands/cmd_change_pixel_format.cpp
index 7ca4d4a57..98a3798b1 100644
--- a/src/app/commands/cmd_change_pixel_format.cpp
+++ b/src/app/commands/cmd_change_pixel_format.cpp
@@ -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
diff --git a/src/app/commands/cmd_export_sprite_sheet.cpp b/src/app/commands/cmd_export_sprite_sheet.cpp
index b05d3e4e9..b3b105f0e 100644
--- a/src/app/commands/cmd_export_sprite_sheet.cpp
+++ b/src/app/commands/cmd_export_sprite_sheet.cpp
@@ -328,17 +328,15 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
columns = sheet_w / sprite->width();
base::UniquePtr resultImage(Image::create(sprite->pixelFormat(), sheet_w, sheet_h));
- base::UniquePtr 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); frameclear(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;
diff --git a/src/app/commands/cmd_import_sprite_sheet.cpp b/src/app/commands/cmd_import_sprite_sheet.cpp
index a2d05390f..cba5feff2 100644
--- a/src/app/commands/cmd_import_sprite_sheet.cpp
+++ b/src/app/commands/cmd_import_sprite_sheet.cpp
@@ -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; yheight(); y += m_rect.h) {
for (int x=m_rect.x; xwidth(); x += m_rect.w) {
- base::UniquePtr resultImage(Image::create(sprite->pixelFormat(), m_rect.w, m_rect.h));
-
- // Clear the image with mask color.
- doc::clear_image(resultImage, 0);
+ base::UniquePtr 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();
}
diff --git a/src/app/commands/cmd_invert_mask.cpp b/src/app/commands/cmd_invert_mask.cpp
index 1c3d20264..1849d6452 100644
--- a/src/app/commands/cmd_invert_mask.cpp
+++ b/src/app/commands/cmd_invert_mask.cpp
@@ -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
diff --git a/src/app/commands/cmd_merge_down_layer.cpp b/src/app/commands/cmd_merge_down_layer.cpp
index ae0c6d144..dd397adbe 100644
--- a/src/app/commands/cmd_merge_down_layer.cpp
+++ b/src/app/commands/cmd_merge_down_layer.cpp
@@ -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(src_layer)->getBlendMode());
+ render::composite_image(new_image, src_image,
+ src_cel->x()-x1,
+ src_cel->y()-y1,
+ src_cel->opacity(),
+ static_cast(src_layer)->getBlendMode());
if (undo.isEnabled())
undo.pushUndoer(new undoers::SetCelPosition(undo.getObjects(), dst_cel));
diff --git a/src/app/commands/cmd_options.cpp b/src/app/commands/cmd_options.cpp
index a5c63d433..2d87b7d54 100644
--- a/src/app/commands/cmd_options.cpp
+++ b/src/app/commands/cmd_options.cpp
@@ -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(&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(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;
diff --git a/src/app/commands/cmd_palette_editor.cpp b/src/app/commands/cmd_palette_editor.cpp
index e2d67ecfd..e3cb5d5db 100644
--- a/src/app/commands/cmd_palette_editor.cpp
+++ b/src/app/commands/cmd_palette_editor.cpp
@@ -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);
}
diff --git a/src/app/commands/cmd_preview.cpp b/src/app/commands/cmd_preview.cpp
index 7779e6140..92f2a2626 100644
--- a/src/app/commands/cmd_preview.cpp
+++ b/src/app/commands/cmd_preview.cpp
@@ -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 m_render;
base::UniquePtr m_doublebuf;
diff --git a/src/app/commands/cmd_zoom.cpp b/src/app/commands/cmd_zoom.cpp
index a419a3431..653bd43b7 100644
--- a/src/app/commands/cmd_zoom.cpp
+++ b/src/app/commands/cmd_zoom.cpp
@@ -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;
}
diff --git a/src/app/commands/filters/filter_manager_impl.cpp b/src/app/commands/filters/filter_manager_impl.cpp
index b09b172fa..54c5c4d85 100644
--- a/src/app/commands/filters/filter_manager_impl.cpp
+++ b/src/app/commands/filters/filter_manager_impl.cpp
@@ -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(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(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();
}
diff --git a/src/app/commands/filters/filter_preview.cpp b/src/app/commands/filters/filter_preview.cpp
index bdeffad11..4e58f1aee 100644
--- a/src/app/commands/filters/filter_preview.cpp
+++ b/src/app/commands/filters/filter_preview.cpp
@@ -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();
diff --git a/src/app/document_api.cpp b/src/app/document_api.cpp
index 62bd77aee..8a24aa2a5 100644
--- a/src/app/document_api.cpp
+++ b/src/app/document_api.cpp
@@ -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); frametotalFrames(); ++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); frametotalFrames(); ++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());
diff --git a/src/app/document_api.h b/src/app/document_api.h
index 8c2d32203..5b27b3dfa 100644
--- a/src/app/document_api.h
+++ b/src/app/document_api.h
@@ -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);
diff --git a/src/app/document_exporter.cpp b/src/app/document_exporter.cpp
index 90906f2d1..218c7973d 100644
--- a/src/app/document_exporter.cpp
+++ b/src/app/document_exporter.cpp
@@ -41,6 +41,7 @@
#include "doc/stock.h"
#include "gfx/packing_rects.h"
#include "gfx/size.h"
+#include "render/render.h"
#include
#include
@@ -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()));
}
}
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index a91186fdb..03bfa498e 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -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
@@ -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
- (quantization::create_palette_from_rgb(
+ (render::create_palette_from_rgb(
fop->document->sprite(),
FrameNumber(0), NULL));
diff --git a/src/app/file/fli_format.cpp b/src/app/file/fli_format.cpp
index ac70ed380..68ff1f914 100644
--- a/src/app/file/fli_format.cpp
+++ b/src/app/file/fli_format.cpp
@@ -28,6 +28,7 @@
#include "app/modules/palettes.h"
#include "base/file_handle.h"
#include "doc/doc.h"
+#include "render/render.h"
#include
@@ -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 bmp(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height()));
base::UniquePtr 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);
}
diff --git a/src/app/file/gif_format.cpp b/src/app/file/gif_format.cpp
index d17f75a1c..c40098773 100644
--- a/src/app/file/gif_format.cpp
+++ b/src/app/file/gif_format.cpp
@@ -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_numtotalFrames(); ++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 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 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 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);
diff --git a/src/app/file/gif_options.h b/src/app/file/gif_options.h
index a33dbb0f3..166bfd95b 100644
--- a/src/app/file/gif_options.h
+++ b/src/app/file/gif_options.h
@@ -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) {
diff --git a/src/app/file/ico_format.cpp b/src/app/file/ico_format.cpp
index 6d7b1334a..777df6829 100644
--- a/src/app/file/ico_format.cpp
+++ b/src/app/file/ico_format.cpp
@@ -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); nfolder(), image, 0, 0, n);
+ render.renderSprite(image, sprite, n);
bpp = (sprite->pixelFormat() == IMAGE_INDEXED) ? 8 : 24;
bw = (((image->width() * bpp / 8) + 3) / 4) * 4;
diff --git a/src/app/flatten.cpp b/src/app/flatten.cpp
index 71b9e1476..c78546882 100644
--- a/src/app/flatten.cpp
+++ b/src/app/flatten.cpp
@@ -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 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(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);
diff --git a/src/app/pref/preferences.cpp b/src/app/pref/preferences.cpp
index e139f07d2..1f46a9e0d 100644
--- a/src/app/pref/preferences.cpp
+++ b/src/app/pref/preferences.cpp
@@ -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(doc));
+
+ auto it = m_docs.find(static_cast(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
diff --git a/src/app/pref/preferences.h b/src/app/pref/preferences.h
index f23ab7406..6479d242a 100644
--- a/src/app/pref/preferences.h
+++ b/src/app/pref/preferences.h
@@ -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 m_tools;
std::map m_docs;
diff --git a/src/app/thumbnail_generator.cpp b/src/app/thumbnail_generator.cpp
index 6972f056e..8cb0e38c0 100644
--- a/src/app/thumbnail_generator.cpp
+++ b/src/app/thumbnail_generator.cpp
@@ -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::create(
+ sprite->pixelFormat(), sprite->width(), sprite->height()));
- doc::ImageBufferPtr thumbnail_buffer(new doc::ImageBuffer);
- base::UniquePtr 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());
diff --git a/src/app/tools/tool_loop.h b/src/app/tools/tool_loop.h
index 58664d0c2..374ddfcd5 100644
--- a/src/app/tools/tool_loop.h
+++ b/src/app/tools/tool_loop.h
@@ -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
diff --git a/src/app/tools/tool_loop_manager.cpp b/src/app/tools/tool_loop_manager.cpp
index 309c63288..54057b286 100644
--- a/src/app/tools/tool_loop_manager.cpp
+++ b/src/app/tools/tool_loop_manager.cpp
@@ -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)
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index f7d0cbe15..6dcbad2c8 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -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 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 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)
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index b14d9037a..ef854cfb0 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -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();
diff --git a/src/app/ui/editor/pixels_movement.cpp b/src/app/ui/editor/pixels_movement.cpp
index 6d2e02213..2596c8fae 100644
--- a/src/app/ui/editor/pixels_movement.cpp
+++ b/src/app/ui/editor/pixels_movement.cpp
@@ -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(),
diff --git a/src/app/ui/editor/select_box_state.cpp b/src/app/ui/editor/select_box_state.cpp
index 08d160efb..36104507a 100644
--- a/src/app/ui/editor/select_box_state.cpp
+++ b/src/app/ui/editor/select_box_state.cpp
@@ -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);
diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp
index 667ae86be..e368cfc7c 100644
--- a/src/app/ui/editor/standby_state.cpp
+++ b/src/app/ui/editor/standby_state.cpp
@@ -375,7 +375,7 @@ bool StandbyState::onMouseWheel(Editor* editor, MouseMessage* msg)
case WHEEL_ZOOM: {
MouseMessage* mouseMsg = static_cast(msg);
- Zoom zoom = editor->zoom();
+ render::Zoom zoom = editor->zoom();
if (dz < 0) {
while (dz++ < 0)
zoom.in();
diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp
index cdd825399..a3d35d636 100644
--- a/src/app/ui/editor/tool_loop_impl.cpp
+++ b/src/app/ui/editor/tool_loop_impl.cpp
@@ -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; }
diff --git a/src/app/ui/editor/zooming_state.cpp b/src/app/ui/editor/zooming_state.cpp
index ab52a4f2c..273eeb983 100644
--- a/src/app/ui/editor/zooming_state.cpp
+++ b/src/app/ui/editor/zooming_state.cpp
@@ -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) {
diff --git a/src/app/ui/editor/zooming_state.h b/src/app/ui/editor/zooming_state.h
index ff5d22743..2cedcb7fe 100644
--- a/src/app/ui/editor/zooming_state.h
+++ b/src/app/ui/editor/zooming_state.h
@@ -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;
};
diff --git a/src/app/ui/mini_editor.cpp b/src/app/ui/mini_editor.cpp
index 960dd0ebe..3d295bf27 100644
--- a/src/app/ui/mini_editor.cpp
+++ b/src/app/ui/mini_editor.cpp
@@ -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();
}
diff --git a/src/app/ui_context.cpp b/src/app/ui_context.cpp
index dc19936e0..43d32a795 100644
--- a/src/app/ui_context.cpp
+++ b/src/app/ui_context.cpp
@@ -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(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(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
diff --git a/src/app/util/clipboard.cpp b/src/app/util/clipboard.cpp
index b0e974f39..9a931486f 100644
--- a/src/app/util/clipboard.cpp
+++ b/src/app/util/clipboard.cpp
@@ -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);
}
diff --git a/src/app/util/expand_cel_canvas.cpp b/src/app/util/expand_cel_canvas.cpp
index 30ae99d28..e152caa85 100644
--- a/src/app/util/expand_cel_canvas.cpp
+++ b/src/app/util/expand_cel_canvas.cpp
@@ -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));
}
}
diff --git a/src/app/util/msk_file.cpp b/src/app/util/msk_file.cpp
index 73467f2af..df3e8edb0 100644
--- a/src/app/util/msk_file.cpp
+++ b/src/app/util/msk_file.cpp
@@ -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();
}
}
diff --git a/src/app/util/render.cpp b/src/app/util/render.cpp
deleted file mode 100644
index 20e9e2404..000000000
--- a/src/app/util/render.cpp
+++ /dev/null
@@ -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 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
-{
- 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
-{
- 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
-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 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 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 srcBits(src, gfx::Rect(src_x, src_y, src_w, src_h));
- LockImageBits dstBits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
- typename LockImageBits::const_iterator src_it = srcBits.begin();
-#ifdef _DEBUG
- typename LockImageBits::const_iterator src_end = srcBits.end();
-#endif
- typename LockImageBits::iterator dst_it, dst_end;
-
- // For each line to draw of the source image...
- for (y=0; y= 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 0) {
- for (box_x=0; box_x bottom)
- goto done_with_blit;
- }
-
- // go to the next line in the source image
- ++src_y;
- }
-
-done_with_blit:;
-}
-
-template
-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 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 srcBits(src, gfx::Rect(src_x, src_y, src_w, src_h));
- LockImageBits dstBits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
- typename LockImageBits::const_iterator src_it = srcBits.begin();
- typename LockImageBits::const_iterator src_end = srcBits.end();
- typename LockImageBits::iterator dst_it, dst_end;
-
- // For each line to draw of the source image...
- for (y=0; y= 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
-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(dst, src, pal, x, y, opacity, blend_mode, zoom);
- else
- merge_zoomed_image_scale_down(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;
- break;
-
- case IMAGE_GRAYSCALE:
- zoomed_func = merge_zoomed_image;
- break;
-
- case IMAGE_INDEXED:
- zoomed_func = merge_zoomed_image;
- 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; yheight()+tile_h; y+=tile_h) {
- for (x=x_start-tile_w; xwidth()+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;
- break;
-
- case IMAGE_GRAYSCALE:
- zoomed_func = merge_zoomed_image;
- break;
-
- case IMAGE_INDEXED:
- zoomed_func = merge_zoomed_image;
- 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(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(layer)->getBlendMode():
- blend_mode),
- zoom);
- }
- }
- break;
- }
-
- case ObjectType::LayerFolder: {
- LayerConstIterator it = static_cast(layer)->getLayerBegin();
- LayerConstIterator end = static_cast(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
diff --git a/src/app/util/render.h b/src/app/util/render.h
deleted file mode 100644
index ab2be6ba5..000000000
--- a/src/app/util/render.h
+++ /dev/null
@@ -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
diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt
index 218b77819..c79303e47 100644
--- a/src/doc/CMakeLists.txt
+++ b/src/doc/CMakeLists.txt
@@ -36,7 +36,6 @@ add_library(doc-lib
palette.cpp
palette_io.cpp
primitives.cpp
- quantization.cpp
rgbmap.cpp
sprite.cpp
sprites.cpp
diff --git a/src/doc/algorithm/flip_image.cpp b/src/doc/algorithm/flip_image.cpp
index 2176026b6..2b8ab77a5 100644
--- a/src/doc/algorithm/flip_image.cpp
+++ b/src/doc/algorithm/flip_image.cpp
@@ -63,7 +63,7 @@ void flip_image_with_mask(Image* image, const Mask* mask, FlipType flipType, int
for (int y=bounds.y; ycopy(image, gfx::Clip(0, 0, bounds.x, y, bounds.w, 1));
int u = bounds.x+bounds.w-1;
for (int x=bounds.x; xcopy(image, gfx::Clip(0, 0, x, bounds.y, 1, bounds.h));
int v = bounds.y+bounds.h-1;
for (int y=bounds.y; ywidth() && 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()) {
diff --git a/src/doc/algorithm/rotsprite.cpp b/src/doc/algorithm/rotsprite.cpp
index e4c5dd78c..3b2b34757 100644
--- a/src/doc/algorithm/rotsprite.cpp
+++ b/src/doc/algorithm/rotsprite.cpp
@@ -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<height()*(1<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,
diff --git a/src/doc/blend.cpp b/src/doc/blend.cpp
index 86273fefc..d87e92e80 100644
--- a/src/doc/blend.cpp
+++ b/src/doc/blend.cpp
@@ -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
diff --git a/src/doc/blend.h b/src/doc/blend.h
index 6ab30f166..f6cd406dd 100644
--- a/src/doc/blend.h
+++ b/src/doc/blend.h
@@ -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
diff --git a/src/doc/dithering_method.h b/src/doc/dithering_method.h
index 8438504ef..616ed1a7a 100644
--- a/src/doc/dithering_method.h
+++ b/src/doc/dithering_method.h
@@ -11,9 +11,9 @@
namespace doc {
// Dithering methods
- enum DitheringMethod {
- DITHERING_NONE,
- DITHERING_ORDERED,
+ enum class DitheringMethod {
+ NONE,
+ ORDERED,
};
} // namespace doc
diff --git a/src/doc/doc.h b/src/doc/doc.h
index a4bce4f80..44ccea8a9 100644
--- a/src/doc/doc.h
+++ b/src/doc/doc.h
@@ -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"
diff --git a/src/doc/frame_number.h b/src/doc/frame_number.h
index 886a99601..ffef01e4c 100644
--- a/src/doc/frame_number.h
+++ b/src/doc/frame_number.h
@@ -10,6 +10,8 @@
namespace doc {
+ typedef int frame_t;
+
class FrameNumber {
public:
FrameNumber() : m_value(0) { }
diff --git a/src/doc/image.h b/src/doc/image.h
index d465ba4b9..6f1a3646e 100644
--- a/src/doc/image.h
+++ b/src/doc/image.h
@@ -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;
diff --git a/src/doc/image_impl.h b/src/doc/image_impl.h
index 962d67b63..b60b0fa9c 100644
--- a/src/doc/image_impl.h
+++ b/src/doc/image_impl.h
@@ -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* src = (const ImageImpl*)_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_yaddress(src_x, src_y);
- dst_address = address(dst_x, dst_y);
+ for (int end_y=area.dst.y+area.size.h;
+ area.dst.yaddress(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* src = (const ImageImpl*)_src;
- ImageImpl* 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_yaddress(src_x, src_y);
- dst_address = dst->address(dst_x, dst_y);
-
- for (int x=dst_x; x bits(this, gfx::Rect(x1, y, x2 - x1 + 1, 1));
typename LockImageBits::iterator it(bits.begin());
@@ -279,61 +250,20 @@ namespace doc {
}
template<>
- inline void ImageImpl::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_ygetPixelAddress(src_x, src_y);
- dst_address = getPixelAddress(dst_x, dst_y);
-
- for (int x=dst_x; xmaskColor();
-
- for (int end_y=dst_y+h; dst_ygetPixelAddress(src_x, src_y);
- dst_address = getPixelAddress(dst_x, dst_y);
-
- for (int x=dst_x; x
- inline void ImageImpl::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::copy(const Image* src, gfx::Clip area) {
+ if (!area.clip(width(), height(), src->width(), src->height()))
return;
// Copy process
- ImageConstIterator src_it(src, gfx::Rect(src_x, src_y, w, h), src_x, src_y);
- ImageIterator dst_it(this, gfx::Rect(dst_x, dst_y, w, h), dst_x, dst_y);
+ ImageConstIterator src_it(src, area.srcBounds(), area.src.x, area.src.y);
+ ImageIterator 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
- inline void ImageImpl::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 src_it(src, gfx::Rect(src_x, src_y, w, h), src_x, src_y);
- ImageIterator 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(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(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(layer)->getBlendMode());
- }
- break;
- }
-
- case ObjectType::LayerFolder: {
- LayerConstIterator it = static_cast(layer)->getLayerBegin();
- LayerConstIterator end = static_cast(layer)->getLayerEnd();
-
- for (; it != end; ++it)
- layer_render(*it, image, x, y, frame);
-
- break;
- }
-
- }
-}
-
} // namespace doc
diff --git a/src/doc/layer.h b/src/doc/layer.h
index 36030ed3e..f58ce234a 100644
--- a/src/doc/layer.h
+++ b/src/doc/layer.h
@@ -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
diff --git a/src/doc/mask.cpp b/src/doc/mask.cpp
index a40306558..a239cee4d 100644
--- a/src/doc/mask.cpp
+++ b/src/doc/mask.cpp
@@ -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);
}
}
diff --git a/src/doc/palette.h b/src/doc/palette.h
index 91324b7ca..45a7ea12b 100644
--- a/src/doc/palette.h
+++ b/src/doc/palette.h
@@ -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];
diff --git a/src/doc/primitives.cpp b/src/doc/primitives.cpp
index 24c101ab9..aad3c9ac5 100644
--- a/src/doc/primitives.cpp
+++ b/src/doc/primitives.cpp
@@ -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;
}
diff --git a/src/doc/primitives.h b/src/doc/primitives.h
index ba6481e5a..bf87616d7 100644
--- a/src/doc/primitives.h
+++ b/src/doc/primitives.h
@@ -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);
diff --git a/src/doc/quantization.h b/src/doc/quantization.h
deleted file mode 100644
index 8f569d73f..000000000
--- a/src/doc/quantization.h
+++ /dev/null
@@ -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
-
-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& 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
diff --git a/src/doc/sprite.cpp b/src/doc/sprite.cpp
index efec6a6c8..a1b131e2a 100644
--- a/src/doc/sprite.cpp
+++ b/src/doc/sprite.cpp
@@ -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
#include
@@ -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& 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::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 layers;
diff --git a/src/doc/sprite.h b/src/doc/sprite.h
index 2e7122888..d5fe49cd0 100644
--- a/src/doc/sprite.h
+++ b/src/doc/sprite.h
@@ -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& 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:
diff --git a/src/gfx/CMakeLists.txt b/src/gfx/CMakeLists.txt
index 337b5ebfe..0c3616b5b 100644
--- a/src/gfx/CMakeLists.txt
+++ b/src/gfx/CMakeLists.txt
@@ -2,6 +2,7 @@
# Copyright (C) 2001-2014 David Capello
add_library(gfx-lib
+ clip.cpp
hsv.cpp
packing_rects.cpp
region.cpp
diff --git a/src/gfx/clip.cpp b/src/gfx/clip.cpp
new file mode 100644
index 000000000..3bf84533b
--- /dev/null
+++ b/src/gfx/clip.cpp
@@ -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
diff --git a/src/gfx/clip.h b/src/gfx/clip.h
new file mode 100644
index 000000000..2bab34163
--- /dev/null
+++ b/src/gfx/clip.h
@@ -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
diff --git a/src/gfx/clip_tests.cpp b/src/gfx/clip_tests.cpp
new file mode 100644
index 000000000..753f02393
--- /dev/null
+++ b/src/gfx/clip_tests.cpp
@@ -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
+
+#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();
+}
diff --git a/src/gfx/hsv.cpp b/src/gfx/hsv.cpp
index bba7e21fb..c1f1c43e3 100644
--- a/src/gfx/hsv.cpp
+++ b/src/gfx/hsv.cpp
@@ -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
diff --git a/src/gfx/hsv_tests.cpp b/src/gfx/hsv_tests.cpp
index a46e61f26..46fbdc163 100644
--- a/src/gfx/hsv_tests.cpp
+++ b/src/gfx/hsv_tests.cpp
@@ -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
#include "gfx/hsv.h"
diff --git a/src/gfx/packing_rects_tests.cpp b/src/gfx/packing_rects_tests.cpp
index 0bf24fb7e..a18088a1e 100644
--- a/src/gfx/packing_rects_tests.cpp
+++ b/src/gfx/packing_rects_tests.cpp
@@ -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
#include "gfx/packing_rects.h"
diff --git a/src/gfx/rect_io.h b/src/gfx/rect_io.h
index 009d29409..c455c3b2a 100644
--- a/src/gfx/rect_io.h
+++ b/src/gfx/rect_io.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 << ", "
diff --git a/src/gfx/rect_tests.cpp b/src/gfx/rect_tests.cpp
index 0ab4802ae..c890cfd5d 100644
--- a/src/gfx/rect_tests.cpp
+++ b/src/gfx/rect_tests.cpp
@@ -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
#include "gfx/border.h"
diff --git a/src/gfx/rgb_tests.cpp b/src/gfx/rgb_tests.cpp
index 920b250d0..acfc621df 100644
--- a/src/gfx/rgb_tests.cpp
+++ b/src/gfx/rgb_tests.cpp
@@ -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
#include "gfx/rgb.h"
diff --git a/src/render/CMakeLists.txt b/src/render/CMakeLists.txt
new file mode 100644
index 000000000..64d350b78
--- /dev/null
+++ b/src/render/CMakeLists.txt
@@ -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)
diff --git a/src/render/LICENSE.txt b/src/render/LICENSE.txt
new file mode 100644
index 000000000..220ac0d42
--- /dev/null
+++ b/src/render/LICENSE.txt
@@ -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.
diff --git a/src/render/README.md b/src/render/README.md
new file mode 100644
index 000000000..2f7c6cc85
--- /dev/null
+++ b/src/render/README.md
@@ -0,0 +1,4 @@
+# Aseprite Render Library
+*Copyright (C) 2001-2014 David Capello*
+
+> Distributed under [MIT license](LICENSE.txt)
diff --git a/src/doc/color_histogram.h b/src/render/color_histogram.h
similarity index 94%
rename from src/doc/color_histogram.h
rename to src/render/color_histogram.h
index 8c3fb409f..1dc07894e 100644
--- a/src/doc/color_histogram.h
+++ b/src/render/color_histogram.h
@@ -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
@@ -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 // 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
diff --git a/src/render/get_sprite_pixel.cpp b/src/render/get_sprite_pixel.cpp
new file mode 100644
index 000000000..a8fb8e8a0
--- /dev/null
+++ b/src/render/get_sprite_pixel.cpp
@@ -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::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
diff --git a/src/render/get_sprite_pixel.h b/src/render/get_sprite_pixel.h
new file mode 100644
index 000000000..cf85a44bf
--- /dev/null
+++ b/src/render/get_sprite_pixel.h
@@ -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
diff --git a/src/doc/median_cut.h b/src/render/median_cut.h
similarity index 98%
rename from src/doc/median_cut.h
rename to src/render/median_cut.h
index 35bc0bddc..a8e38bdba 100644
--- a/src/doc/median_cut.h
+++ b/src/render/median_cut.h
@@ -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
#include
-namespace doc {
-namespace quantization {
+namespace render {
template
class Box {
@@ -287,7 +286,6 @@ namespace quantization {
}
}
-} // namespace quantization
-} // namespace doc
+} // namespace render
#endif
diff --git a/src/doc/quantization.cpp b/src/render/quantization.cpp
similarity index 94%
rename from src/doc/quantization.cpp
rename to src/render/quantization.cpp
index ccdfac0a5..caa536136 100644
--- a/src/doc/quantization.cpp
+++ b/src/render/quantization.cpp
@@ -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
#include
#include
-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& images, Palette* pale
optimizer.calculate(palette, has_background_layer);
}
-} // namespace quantization
-} // namespace doc
+} // namespace render
diff --git a/src/render/quantization.h b/src/render/quantization.h
new file mode 100644
index 000000000..55078f957
--- /dev/null
+++ b/src/render/quantization.h
@@ -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
+
+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& 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
diff --git a/src/render/render.cpp b/src/render/render.cpp
new file mode 100644
index 000000000..cff30870e
--- /dev/null
+++ b/src/render/render.cpp
@@ -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 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
+{
+ 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
+{
+ 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
+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 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 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 srcBits(src, srcBounds);
+ LockImageBits dstBits(dst, dstBounds);
+ typename LockImageBits::const_iterator src_it = srcBits.begin();
+#ifdef _DEBUG
+ typename LockImageBits::const_iterator src_end = srcBits.end();
+#endif
+ typename LockImageBits::iterator dst_it, dst_end;
+
+ // For each line to draw of the source image...
+ dstBounds.h = 1;
+ for (int y=0; y= 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 bottom)
+ goto done_with_blit;
+ }
+ }
+
+done_with_blit:;
+}
+
+template
+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 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 srcBits(src, srcBounds);
+ LockImageBits dstBits(dst, dstBounds);
+ typename LockImageBits::const_iterator src_it = srcBits.begin();
+ typename LockImageBits::const_iterator src_end = srcBits.end();
+ typename LockImageBits::iterator dst_it, dst_end;
+
+ // For each line to draw of the source image...
+ dstBounds.h = 1;
+ for (int y=0; y= 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
+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(dst, src, pal, area, opacity, blend_mode, zoom);
+ else
+ compose_scaled_image_scale_down(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; yheight()+tile_h; y+=tile_h) {
+ for (x=x_start-tile_w; xwidth()+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(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(layer)->getBlendMode():
+ blend_mode),
+ zoom);
+ }
+ }
+ break;
+ }
+
+ case ObjectType::LayerFolder: {
+ LayerConstIterator it = static_cast(layer)->getLayerBegin();
+ LayerConstIterator end = static_cast(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;
+ case IMAGE_GRAYSCALE: return compose_scaled_image;
+ case IMAGE_INDEXED: return compose_scaled_image;
+ }
+ break;
+
+ case IMAGE_GRAYSCALE:
+ switch (dstFormat) {
+ case IMAGE_RGB: return compose_scaled_image;
+ case IMAGE_GRAYSCALE: return compose_scaled_image;
+ case IMAGE_INDEXED: return compose_scaled_image;
+ }
+ break;
+
+ case IMAGE_INDEXED:
+ switch (dstFormat) {
+ case IMAGE_RGB: return compose_scaled_image;
+ case IMAGE_GRAYSCALE: return compose_scaled_image;
+ case IMAGE_INDEXED: return compose_scaled_image;
+ }
+ 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
diff --git a/src/render/render.h b/src/render/render.h
new file mode 100644
index 000000000..43443487e
--- /dev/null
+++ b/src/render/render.h
@@ -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
diff --git a/src/render/render_tests.cpp b/src/render/render_tests.cpp
new file mode 100644
index 000000000..0da007ba6
--- /dev/null
+++ b/src/render/render_tests.cpp
@@ -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
+
+#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
+class RenderAllModes : public testing::Test {
+protected:
+ RenderAllModes() { }
+};
+
+typedef testing::Types 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 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 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 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 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 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();
+}
diff --git a/src/render/zoom.cpp b/src/render/zoom.cpp
new file mode 100644
index 000000000..575cd9885
--- /dev/null
+++ b/src/render/zoom.cpp
@@ -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
diff --git a/src/app/zoom.h b/src/render/zoom.h
similarity index 55%
rename from src/app/zoom.h
rename to src/render/zoom.h
index 9ffbe2949..87a363bec 100644
--- a/src/app/zoom.h
+++ b/src/render/zoom.h
@@ -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