diff --git a/data/pref.xml b/data/pref.xml
index 81fafb752..1c602a7cd 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -152,6 +152,7 @@
+
diff --git a/data/strings/en.ini b/data/strings/en.ini
index 5f0edde05..f563a33f6 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -1012,6 +1012,7 @@ disable_extension = &Disable
uninstall_extension = &Uninstall
open_extension_folder = Open &Folder
user_interface = User Interface
+new_render_engine = New render engine for sprite editor
native_clipboard = Use native clipboard
native_file_dialog = Use native file dialog
one_finger_as_mouse_movement = Interpret one finger as mouse movement
diff --git a/data/widgets/options.xml b/data/widgets/options.xml
index 402e91a1b..f9d6a434f 100644
--- a/data/widgets/options.xml
+++ b/data/widgets/options.xml
@@ -361,6 +361,11 @@
+
+
+
+
scancode() == kKeyF1) {
+ if (msg->ctrlPressed() &&
+ msg->scancode() == kKeyF1) {
try {
she::Display* display = getDisplay();
int screenScale = display->scale();
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index 34b90a108..21d464f8c 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -61,6 +61,7 @@
#include "she/system.h"
#include "ui/ui.h"
+#include
#include
#include
#include
@@ -310,6 +311,14 @@ void Editor::getInvalidDecoratoredRegion(gfx::Region& region)
// changes (e.g. symmetry handles).
if ((m_flags & kShowDecorators) && m_decorator)
m_decorator->getInvalidDecoratoredRegion(this, region);
+
+#if ENABLE_DEVMODE
+ // TODO put this in other widget
+ if (Preferences::instance().perf.showRenderTime()) {
+ if (!m_perfInfoBounds.isEmpty())
+ region |= gfx::Region(m_perfInfoBounds);
+ }
+#endif // ENABLE_DEVMODE
}
void Editor::setLayer(const Layer* layer)
@@ -488,62 +497,87 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
gfx::Rect rc = m_sprite->bounds().createIntersection(spriteRectToDraw);
rc = m_proj.apply(rc);
- int dest_x = dx + m_padding.x + rc.x;
- int dest_y = dy + m_padding.y + rc.y;
+ gfx::Rect dest(dx + m_padding.x + rc.x,
+ dy + m_padding.y + rc.y, 0, 0);
// Clip from graphics/screen
const gfx::Rect& clip = g->getClipBounds();
- if (dest_x < clip.x) {
- rc.x += clip.x - dest_x;
- rc.w -= clip.x - dest_x;
- dest_x = clip.x;
+ if (dest.x < clip.x) {
+ rc.x += clip.x - dest.x;
+ rc.w -= clip.x - dest.x;
+ dest.x = clip.x;
}
- if (dest_y < clip.y) {
- rc.y += clip.y - dest_y;
- rc.h -= clip.y - dest_y;
- dest_y = clip.y;
+ if (dest.y < clip.y) {
+ rc.y += clip.y - dest.y;
+ rc.h -= clip.y - dest.y;
+ dest.y = clip.y;
}
- if (dest_x+rc.w > clip.x+clip.w) {
- rc.w = clip.x+clip.w-dest_x;
+ if (dest.x+rc.w > clip.x+clip.w) {
+ rc.w = clip.x+clip.w-dest.x;
}
- if (dest_y+rc.h > clip.y+clip.h) {
- rc.h = clip.y+clip.h-dest_y;
+ if (dest.y+rc.h > clip.y+clip.h) {
+ rc.h = clip.y+clip.h-dest.y;
}
if (rc.isEmpty())
return;
- base::UniquePtr rendered(NULL);
+ // Bounds of pixels from the sprite canvas that will be exposed in
+ // this render cycle.
+ gfx::Rect expose = m_proj.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_proj.scaleX() < 1.0)
+ expose.enlargeXW(int(1./m_proj.scaleX()));
+ // If the zoom level is more than %100 we add an extra pixel to
+ // expose just in case the zoom requires to display it. Note:
+ // this is really necessary to avoid showing invalid destination
+ // areas in ToolLoopImpl.
+ else if (m_proj.scaleX() > 1.0)
+ expose.enlargeXW(1);
+
+ if (m_proj.scaleY() < 1.0)
+ expose.enlargeYH(int(1./m_proj.scaleY()));
+ else if (m_proj.scaleY() > 1.0)
+ expose.enlargeYH(1);
+
+ expose &= m_sprite->bounds();
+
+ const int maxw = std::max(0, m_sprite->width()-expose.x);
+ const int maxh = std::max(0, m_sprite->height()-expose.y);
+ expose.w = MID(0, expose.w, maxw);
+ expose.h = MID(0, expose.h, maxh);
+ if (expose.isEmpty())
+ return;
+
+ // rc2 is the rectangle used to create a temporal rendered image of the sprite
+ const bool newEngine = Preferences::instance().experimental.newRenderEngine();
+ gfx::Rect rc2;
+ if (newEngine) {
+ rc2 = expose; // New engine, exposed rectangle (without zoom)
+ dest.x = dx + m_padding.x + m_proj.applyX(rc2.x);
+ dest.y = dy + m_padding.y + m_proj.applyY(rc2.y);
+ dest.w = m_proj.applyX(rc2.w);
+ dest.h = m_proj.applyY(rc2.h);
+ }
+ else {
+ rc2 = rc; // Old engine, same rectangle with zoom
+ dest.w = rc.w;
+ dest.h = rc.h;
+ }
+
+ base::UniquePtr rendered(nullptr);
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_proj.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_proj.scaleX() < 1.0)
- expose.enlargeXW(int(1./m_proj.scaleX()));
- // If the zoom level is more than %100 we add an extra pixel to
- // expose just in case the zoom requires to display it. Note:
- // this is really necessary to avoid showing invalid destination
- // areas in ToolLoopImpl.
- else if (m_proj.scaleX() > 1.0)
- expose.enlargeXW(1);
-
- if (m_proj.scaleY() < 1.0)
- expose.enlargeYH(int(1./m_proj.scaleY()));
- else if (m_proj.scaleY() > 1.0)
- expose.enlargeYH(1);
-
- m_document->notifyExposeSpritePixels(m_sprite, gfx::Region(expose));
- }
+ m_document->notifyExposeSpritePixels(m_sprite, gfx::Region(expose));
// Create a temporary RGB bitmap to draw all to it
- rendered.reset(Image::create(IMAGE_RGB, rc.w, rc.h,
+ rendered.reset(Image::create(IMAGE_RGB, rc2.w, rc2.h,
m_renderEngine->getRenderImageBuffer()));
m_renderEngine->setRefLayersVisiblity(true);
@@ -552,7 +586,8 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
m_renderEngine->setNonactiveLayersOpacity(Preferences::instance().experimental.nonactiveLayersOpacity());
else
m_renderEngine->setNonactiveLayersOpacity(255);
- m_renderEngine->setProjection(m_proj);
+ m_renderEngine->setProjection(
+ newEngine ? render::Projection(): m_proj);
m_renderEngine->setupBackground(m_document, rendered->pixelFormat());
m_renderEngine->disableOnionskin();
@@ -592,7 +627,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
}
m_renderEngine->renderSprite(
- rendered, m_sprite, m_frame, gfx::Clip(0, 0, rc));
+ rendered, m_sprite, m_frame, gfx::Clip(0, 0, rc2));
m_renderEngine->removeExtraImage();
}
@@ -602,23 +637,27 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
if (rendered) {
// Convert the render to a she::Surface
- static she::Surface* tmp;
- if (!tmp || tmp->width() < rc.w || tmp->height() < rc.h) {
+ static she::Surface* tmp = nullptr; // TODO move this to other centralized place
+ if (!tmp || tmp->width() < rc2.w || tmp->height() < rc2.h) {
+ const int maxw = std::max(rc2.w, tmp ? tmp->width(): 0);
+ const int maxh = std::max(rc2.h, tmp ? tmp->height(): 0);
if (tmp)
tmp->dispose();
-
- tmp = she::instance()->createSurface(rc.w, rc.h);
+ tmp = she::instance()->createSurface(maxw, maxh);
}
-
if (tmp->nativeHandle()) {
+ if (newEngine)
+ tmp->clear(); // TODO why we need this?
+
convert_image_to_surface(rendered, m_sprite->palette(m_frame),
- tmp, 0, 0, 0, 0, rc.w, rc.h);
-
- g->blit(tmp, 0, 0, dest_x, dest_y, rc.w, rc.h);
-
- m_brushPreview.invalidateRegion(
- gfx::Region(
- gfx::Rect(dest_x, dest_y, rc.w, rc.h)));
+ tmp, 0, 0, 0, 0, rc2.w, rc2.h);
+ if (newEngine) {
+ g->drawRgbaSurface(tmp, gfx::Rect(0, 0, rc2.w, rc2.h), dest);
+ }
+ else {
+ g->blit(tmp, 0, 0, dest.x, dest.y, dest.w, dest.h);
+ }
+ m_brushPreview.invalidateRegion(gfx::Region(dest));
}
}
@@ -630,7 +669,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
m_proj.applyX(m_sprite->width()),
m_proj.applyY(m_sprite->height()));
- IntersectClip clip(g, gfx::Rect(dest_x, dest_y, rc.w, rc.h));
+ IntersectClip clip(g, dest);
if (clip) {
// Draw the pixel grid
if ((m_proj.zoom().scale() > 2.0) && m_docPref.show.pixelGrid()) {
@@ -1666,6 +1705,16 @@ bool Editor::onProcessMessage(Message* msg)
break;
case kKeyDownMessage:
+#if ENABLE_DEVMODE
+ // Switch render mode
+ if (!msg->ctrlPressed() &&
+ static_cast(msg)->scancode() == kKeyF1) {
+ Preferences::instance().experimental.newRenderEngine(
+ !Preferences::instance().experimental.newRenderEngine());
+ invalidate();
+ return true;
+ }
+#endif
if (m_sprite) {
EditorStatePtr holdState(m_state);
bool used = m_state->onKeyDown(this, static_cast(msg));
@@ -1778,18 +1827,25 @@ void Editor::onPaint(ui::PaintEvent& ev)
drawSpriteUnclippedRect(g, gfx::Rect(0, 0, m_sprite->width(), m_sprite->height()));
renderElapsed = renderChrono.elapsed();
+#if ENABLE_DEVMODE
// Show performance stats (TODO show performance stats in other widget)
if (Preferences::instance().perf.showRenderTime()) {
View* view = View::getView(this);
gfx::Rect vp = view->viewportBounds();
char buf[128];
- sprintf(buf, "%.3f", renderElapsed);
+ sprintf(buf, "%c %.4gs",
+ Preferences::instance().experimental.newRenderEngine() ? 'N': 'O',
+ renderElapsed);
g->drawText(
buf,
gfx::rgba(255, 255, 255, 255),
gfx::rgba(0, 0, 0, 255),
vp.origin() - bounds().origin());
+
+ m_perfInfoBounds.setOrigin(vp.origin());
+ m_perfInfoBounds.setSize(g->measureUIText(buf));
}
+#endif // ENABLE_DEVMODE
// Draw the mask boundaries
if (m_document->getMaskBoundaries()) {
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index 1af3e7915..3c3205429 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -405,6 +405,10 @@ namespace app {
// TODO could we avoid one extra field just to do this?
gfx::Point m_oldMainTilePos;
+#if ENABLE_DEVMODE
+ gfx::Rect m_perfInfoBounds;
+#endif
+
// The render engine must be shared between all editors so when a
// DrawingState is being used in one editor, other editors for the
// same document can show the same preview image/stroke being drawn
diff --git a/src/she/alleg4/alleg_surface.cpp b/src/she/alleg4/alleg_surface.cpp
index 9bbf7ec85..e97873bf8 100644
--- a/src/she/alleg4/alleg_surface.cpp
+++ b/src/she/alleg4/alleg_surface.cpp
@@ -1,5 +1,5 @@
// SHE library
-// Copyright (C) 2012-2017 David Capello
+// Copyright (C) 2012-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -420,4 +420,16 @@ void Alleg4Surface::drawRgbaSurface(const Surface* src, int srcx, int srcy, int
destroy_bitmap(tmp);
}
+void Alleg4Surface::drawRgbaSurface(const Surface* src, const gfx::Rect& srcRect, const gfx::Rect& dstRect)
+{
+ ASSERT(src);
+ ASSERT(static_cast(src)->m_bmp);
+ ASSERT(static_cast(this)->m_bmp);
+
+ stretch_blit(
+ static_cast(src)->m_bmp, m_bmp,
+ srcRect.x, srcRect.y, srcRect.w, srcRect.h,
+ dstRect.x, dstRect.y, dstRect.w, dstRect.h);
+}
+
} // namespace she
diff --git a/src/she/alleg4/alleg_surface.h b/src/she/alleg4/alleg_surface.h
index f5b3198b7..ff35a59a0 100644
--- a/src/she/alleg4/alleg_surface.h
+++ b/src/she/alleg4/alleg_surface.h
@@ -1,5 +1,5 @@
// SHE library
-// Copyright (C) 2012-2017 David Capello
+// Copyright (C) 2012-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -63,6 +63,7 @@ namespace she {
void drawSurface(const Surface* src, int dstx, int dsty) override;
void drawRgbaSurface(const Surface* src, int dstx, int dsty) override;
void drawRgbaSurface(const Surface* src, int srcx, int srcy, int dstx, int dsty, int w, int h) override;
+ void drawRgbaSurface(const Surface* src, const gfx::Rect& srcRect, const gfx::Rect& dstRect) override;
private:
BITMAP* m_bmp;
diff --git a/src/she/skia/skia_surface.h b/src/she/skia/skia_surface.h
index 5d6cec255..57c83d436 100644
--- a/src/she/skia/skia_surface.h
+++ b/src/she/skia/skia_surface.h
@@ -470,6 +470,21 @@ public:
SkCanvas::kStrict_SrcRectConstraint);
}
+ void drawRgbaSurface(const Surface* src, const gfx::Rect& srcRect, const gfx::Rect& dstRect) override {
+ SkRect srcRect2 = SkRect::Make(SkIRect::MakeXYWH(srcRect.x, srcRect.y, srcRect.w, srcRect.h));
+ SkRect dstRect2 = SkRect::Make(SkIRect::MakeXYWH(dstRect.x, dstRect.y, dstRect.w, dstRect.h));
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ paint.setFilterQuality(srcRect.w < dstRect.w ||
+ srcRect.h < dstRect.h ? kNone_SkFilterQuality:
+ kHigh_SkFilterQuality);
+
+ m_canvas->drawBitmapRect(
+ ((SkiaSurface*)src)->m_bitmap, srcRect2, dstRect2, &paint,
+ SkCanvas::kStrict_SrcRectConstraint);
+ }
+
void drawColoredRgbaSurface(const Surface* src, gfx::Color fg, gfx::Color bg, const gfx::Clip& clipbase) override {
gfx::Clip clip(clipbase);
if (!clip.clip(width(), height(), src->width(), src->height()))
diff --git a/src/she/surface.h b/src/she/surface.h
index a80dfa8ef..49eecf5ce 100644
--- a/src/she/surface.h
+++ b/src/she/surface.h
@@ -1,5 +1,5 @@
// SHE library
-// Copyright (C) 2012-2017 David Capello
+// Copyright (C) 2012-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -66,6 +66,7 @@ namespace she {
virtual void drawSurface(const Surface* src, int dstx, int dsty) = 0;
virtual void drawRgbaSurface(const Surface* src, int dstx, int dsty) = 0;
virtual void drawRgbaSurface(const Surface* src, int srcx, int srcy, int dstx, int dsty, int width, int height) = 0;
+ virtual void drawRgbaSurface(const Surface* surface, const gfx::Rect& srcRect, const gfx::Rect& dstRect) = 0;
virtual void drawColoredRgbaSurface(const Surface* src, gfx::Color fg, gfx::Color bg, const gfx::Clip& clip) = 0;
virtual void applyScale(int scaleFactor) = 0;
diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp
index beb5ef72e..c82d37cc6 100644
--- a/src/ui/graphics.cpp
+++ b/src/ui/graphics.cpp
@@ -1,5 +1,5 @@
// Aseprite UI Library
-// Copyright (C) 2001-2017 David Capello
+// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -201,6 +201,21 @@ void Graphics::drawRgbaSurface(she::Surface* surface, int srcx, int srcy, int ds
m_surface->drawRgbaSurface(surface, srcx, srcy, m_dx+dstx, m_dy+dsty, w, h);
}
+void Graphics::drawRgbaSurface(she::Surface* surface,
+ const gfx::Rect& srcRect,
+ const gfx::Rect& dstRect)
+{
+ dirty(gfx::Rect(m_dx+dstRect.x, m_dy+dstRect.y,
+ dstRect.w, dstRect.h));
+
+ she::SurfaceLock lockSrc(surface);
+ she::SurfaceLock lockDst(m_surface);
+ m_surface->drawRgbaSurface(
+ surface,
+ srcRect,
+ gfx::Rect(dstRect).offset(m_dx, m_dy));
+}
+
void Graphics::drawColoredRgbaSurface(she::Surface* surface, gfx::Color color, int x, int y)
{
dirty(gfx::Rect(m_dx+x, m_dy+y, surface->width(), surface->height()));
diff --git a/src/ui/graphics.h b/src/ui/graphics.h
index 01b81e5fa..a0bc2e4bc 100644
--- a/src/ui/graphics.h
+++ b/src/ui/graphics.h
@@ -1,5 +1,5 @@
// Aseprite UI Library
-// Copyright (C) 2001-2017 David Capello
+// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@@ -75,6 +75,9 @@ namespace ui {
void drawSurface(she::Surface* surface, int x, int y);
void drawRgbaSurface(she::Surface* surface, int x, int y);
void drawRgbaSurface(she::Surface* surface, int srcx, int srcy, int dstx, int dsty, int w, int h);
+ void drawRgbaSurface(she::Surface* surface,
+ const gfx::Rect& srcRect,
+ const gfx::Rect& dstRect);
void drawColoredRgbaSurface(she::Surface* surface, gfx::Color color, int x, int y);
void drawColoredRgbaSurface(she::Surface* surface, gfx::Color color, int srcx, int srcy, int dstx, int dsty, int w, int h);