From 37209a0f5bc04903aef4ed9bb4b68ce8265227e7 Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 23 May 2016 19:22:02 -0300 Subject: [PATCH] Add pixel ratio support to the Editor & Render --- src/app/commands/cmd_fullscreen_preview.cpp | 28 +- .../commands/filters/filter_manager_impl.cpp | 8 +- src/app/ui/editor/brush_preview.cpp | 19 +- src/app/ui/editor/editor.cpp | 170 +++++++----- src/app/ui/editor/editor.h | 7 +- src/app/ui/editor/select_box_state.cpp | 6 +- src/app/ui/editor/tool_loop_impl.cpp | 3 +- src/app/ui/editor/transform_handles.cpp | 6 +- src/doc/doc.h | 3 +- src/gfx/rect.h | 40 ++- src/render/projection.h | 73 +++++ src/render/render.cpp | 255 +++++++++--------- src/render/render.h | 47 ++-- src/render/render_tests.cpp | 22 +- 14 files changed, 413 insertions(+), 274 deletions(-) create mode 100644 src/render/projection.h diff --git a/src/app/commands/cmd_fullscreen_preview.cpp b/src/app/commands/cmd_fullscreen_preview.cpp index a7f6b0a42..27fd0e925 100644 --- a/src/app/commands/cmd_fullscreen_preview.cpp +++ b/src/app/commands/cmd_fullscreen_preview.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -51,7 +51,7 @@ public: , m_doc(editor->document()) , m_sprite(editor->sprite()) , m_pal(m_sprite->palette(editor->frame())) - , m_zoom(editor->zoom()) + , m_proj(editor->projection()) , m_index_bg_color(-1) , m_doublebuf(Image::create(IMAGE_RGB, ui::display_w(), ui::display_h())) , m_doublesur(she::instance()->createRgbaSurface(ui::display_w(), ui::display_h())) { @@ -174,11 +174,12 @@ protected: virtual void onPaint(PaintEvent& ev) override { Graphics* g = ev.graphics(); AppRender& render = m_editor->renderEngine(); + render.setProjection(render::Projection()); render.disableOnionskin(); render.setBgType(render::BgType::TRANSPARENT); // Render sprite and leave the result in 'm_render' variable - if (m_render == NULL) { + if (m_render == nullptr) { ImageBufferPtr buf = Editor::getRenderImageBuffer(); m_render.reset(Image::create(IMAGE_RGB, m_sprite->width(), m_sprite->height(), buf)); @@ -188,19 +189,20 @@ protected: } int x, y, w, h, u, v; - x = m_pos.x + m_zoom.apply(m_zoom.remove(m_delta.x)); - y = m_pos.y + m_zoom.apply(m_zoom.remove(m_delta.y)); - w = m_zoom.apply(m_sprite->width()); - h = m_zoom.apply(m_sprite->height()); + x = m_pos.x + m_proj.applyX(m_proj.removeX(m_delta.x)); + y = m_pos.y + m_proj.applyY(m_proj.removeY(m_delta.y)); + w = m_proj.applyX(m_sprite->width()); + h = m_proj.applyY(m_sprite->height()); if (int(m_tiled) & int(TiledMode::X_AXIS)) x = SGN(x) * (ABS(x)%w); if (int(m_tiled) & int(TiledMode::Y_AXIS)) y = SGN(y) * (ABS(y)%h); + render.setProjection(m_proj); 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); + m_doublebuf->width(), m_doublebuf->height())); } else { doc::clear_image(m_doublebuf, m_pal->getEntry(m_index_bg_color)); @@ -209,23 +211,23 @@ protected: switch (m_tiled) { case TiledMode::NONE: render.renderImage(m_doublebuf, m_render, m_pal, x, y, - m_zoom, 255, BlendMode::NORMAL); + 255, BlendMode::NORMAL); break; case TiledMode::X_AXIS: for (u=x-w; u m_render; base::UniquePtr m_doublebuf; diff --git a/src/app/commands/filters/filter_manager_impl.cpp b/src/app/commands/filters/filter_manager_impl.cpp index afdede60c..2805f6c22 100644 --- a/src/app/commands/filters/filter_manager_impl.cpp +++ b/src/app/commands/filters/filter_manager_impl.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -258,9 +258,9 @@ void FilterManagerImpl::flush() m_x+m_offset_x, m_y+m_offset_y+m_row-1)), gfx::Size( - editor->zoom().apply(m_w), - (editor->zoom().scale() >= 1 ? editor->zoom().apply(1): - editor->zoom().remove(1)))); + editor->projection().applyX(m_w), + (editor->projection().scaleY() >= 1 ? editor->projection().applyY(1): + editor->projection().removeY(1)))); gfx::Region reg1(rect); gfx::Region reg2; diff --git a/src/app/ui/editor/brush_preview.cpp b/src/app/ui/editor/brush_preview.cpp index 02b695550..960c0c750 100644 --- a/src/app/ui/editor/brush_preview.cpp +++ b/src/app/ui/editor/brush_preview.cpp @@ -408,19 +408,22 @@ void BrushPreview::traceSelectionCrossPixels( 0, 0, 1, 1, 0, 0, }; gfx::Point out, outpt = m_editor->editorToScreen(pt); - int u, v; - int size = m_editor->zoom().apply(thickness/2); - int size2 = m_editor->zoom().apply(thickness); - if (size2 == 0) size2 = 1; + const render::Projection& proj = m_editor->projection(); + gfx::Size size(proj.applyX(thickness/2), + proj.applyY(thickness/2)); + gfx::Size size2(proj.applyX(thickness), + proj.applyY(thickness)); + if (size2.w == 0) size2.w = 1; + if (size2.h == 0) size2.h = 1; - for (v=0; v<6; v++) { - for (u=0; u<6; u++) { + for (int v=0; v<6; v++) { + for (int u=0; u<6; u++) { if (!cross[v*6+u]) continue; out = outpt; - out.x += ((u<3) ? u-size-3: u-size-3+size2); - out.y += ((v<3) ? v-size-3: v-size-3+size2); + out.x += ((u<3) ? u-size.w-3: u-size.w-3+size2.w); + out.y += ((v<3) ? v-size.h-3: v-size.h-3+size2.h); (this->*pixelDelegate)(g, out, color); } diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index 653ac6a55..7dd43d3f2 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -66,38 +66,38 @@ using namespace render; class EditorPreRenderImpl : public EditorPreRender { public: - EditorPreRenderImpl(Editor* editor, Image* image, const Point& offset, Zoom zoom) + EditorPreRenderImpl(Editor* editor, Image* image, + const Point& offset, + const Projection& proj) : m_editor(editor) , m_image(image) , m_offset(offset) - , m_zoom(zoom) - { + , m_proj(proj) { } - Editor* getEditor() override - { + Editor* getEditor() override { return m_editor; } - Image* getImage() override - { + Image* getImage() override { return m_image; } void fillRect(const gfx::Rect& rect, uint32_t rgbaColor, int opacity) override { - blend_rect(m_image, - m_offset.x + m_zoom.apply(rect.x), - m_offset.y + m_zoom.apply(rect.y), - m_offset.x + m_zoom.apply(rect.x+rect.w) - 1, - m_offset.y + m_zoom.apply(rect.y+rect.h) - 1, rgbaColor, opacity); + blend_rect( + m_image, + m_offset.x + m_proj.applyX(rect.x), + m_offset.y + m_proj.applyY(rect.y), + m_offset.x + m_proj.applyX(rect.x+rect.w) - 1, + m_offset.y + m_proj.applyY(rect.y+rect.h) - 1, rgbaColor, opacity); } private: Editor* m_editor; Image* m_image; Point m_offset; - Zoom m_zoom; + Projection m_proj; }; class EditorPostRenderImpl : public EditorPostRender { @@ -154,7 +154,6 @@ Editor::Editor(Document* document, EditorFlags flags) , m_sprite(m_document->sprite()) , m_layer(m_sprite->folder()->getFirstLayer()) , m_frame(frame_t(0)) - , m_zoom(1, 1) , m_docPref(Preferences::instance().document(document)) , m_brushPreview(this) , m_lastDrawingPosition(-1, -1) @@ -169,6 +168,8 @@ Editor::Editor(Document* document, EditorFlags flags) , m_secondaryButton(false) , m_aniSpeed(1.0) { + m_proj.setPixelRatio(m_sprite->pixelRatio()); + // Add the first state into the history. m_statesHistory.push(m_state); @@ -365,15 +366,15 @@ Site Editor::getSite() const void Editor::setZoom(const render::Zoom& zoom) { - if (m_zoom != zoom) { - m_zoom = zoom; + if (m_proj.zoom() != zoom) { + m_proj.setZoom(zoom); notifyZoomChanged(); } else { // Just copy the zoom as the internal "Zoom::m_internalScale" // value might be different and we want to keep this value updated // for better zooming experience in StateWithWheelBehavior. - m_zoom = zoom; + m_proj.setZoom(zoom); } } @@ -384,8 +385,8 @@ void Editor::setDefaultScroll() setEditorScroll( gfx::Point( - m_padding.x - vp.w/2 + m_zoom.apply(m_sprite->width())/2, - m_padding.y - vp.h/2 + m_zoom.apply(m_sprite->height())/2)); + m_padding.x - vp.w/2 + m_proj.applyX(m_sprite->width())/2, + m_padding.y - vp.h/2 + m_proj.applyY(m_sprite->height())/2)); } // Sets the scroll position of the editor @@ -410,7 +411,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite { // Clip from sprite and apply zoom gfx::Rect rc = m_sprite->bounds().createIntersection(spriteRectToDraw); - rc = m_zoom.apply(rc); + rc = m_proj.apply(rc); int dest_x = dx + m_padding.x + rc.x; int dest_y = dy + m_padding.y + rc.y; @@ -447,26 +448,33 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite // 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); + 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_zoom.scale() < 1.0) { - expose.enlarge(int(1./m_zoom.scale())); - } + 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_zoom.scale() > 1.0) { - expose.enlarge(1); - } + 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)); } // Create a temporary RGB bitmap to draw all to it rendered.reset(Image::create(IMAGE_RGB, rc.w, rc.h, m_renderBuffer)); + + m_renderEngine.setProjection(m_proj); m_renderEngine.setupBackground(m_document, rendered->pixelFormat()); m_renderEngine.disableOnionskin(); @@ -505,8 +513,8 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite m_layer, m_frame); } - m_renderEngine.renderSprite(rendered, m_sprite, m_frame, - gfx::Clip(0, 0, rc), m_zoom); + m_renderEngine.renderSprite( + rendered, m_sprite, m_frame, gfx::Clip(0, 0, rc)); m_renderEngine.removeExtraImage(); } @@ -518,7 +526,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite // Pre-render decorator. if ((m_flags & kShowDecorators) && m_decorator) { EditorPreRenderImpl preRender(this, rendered, - Point(-rc.x, -rc.y), m_zoom); + Point(-rc.x, -rc.y), m_proj); m_decorator->preRenderDecorator(&preRender); } @@ -549,15 +557,15 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) gfx::Rect rc = _rc; // For odd zoom scales minor than 100% we have to add an extra window // just to make sure the whole rectangle is drawn. - if (m_zoom.scale() < 1.0) - rc.inflate(int(1./m_zoom.scale()), int(1./m_zoom.scale())); + if (m_proj.scaleX() < 1.0) rc.w += int(1./m_proj.scaleX()); + if (m_proj.scaleY() < 1.0) rc.h += int(1./m_proj.scaleY()); gfx::Rect client = clientBounds(); gfx::Rect spriteRect( client.x + m_padding.x, client.y + m_padding.y, - m_zoom.apply(m_sprite->width()), - m_zoom.apply(m_sprite->height())); + m_proj.applyX(m_sprite->width()), + m_proj.applyY(m_sprite->height())); gfx::Rect enclosingRect = spriteRect; // Draw the main sprite at the center. @@ -611,11 +619,11 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) IntersectClip clip(g, cliprc); // Draw the pixel grid - if ((m_zoom.scale() > 2.0) && m_docPref.show.pixelGrid()) { + if ((m_proj.zoom().scale() > 2.0) && m_docPref.show.pixelGrid()) { int alpha = m_docPref.pixelGrid.opacity(); if (m_docPref.pixelGrid.autoOpacity()) { - alpha = int(alpha * (m_zoom.scale()-2.) / (16.-2.)); + alpha = int(alpha * (m_proj.zoom().scale()-2.) / (16.-2.)); alpha = MID(0, alpha, 255); } @@ -626,12 +634,13 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) // Draw the grid if (m_docPref.show.grid()) { gfx::Rect gridrc = m_docPref.grid.bounds(); - if (m_zoom.apply(gridrc.w) > 2 && - m_zoom.apply(gridrc.h) > 2) { + if (m_proj.applyX(gridrc.w) > 2 && + m_proj.applyY(gridrc.h) > 2) { int alpha = m_docPref.grid.opacity(); if (m_docPref.grid.autoOpacity()) { - double len = (m_zoom.apply(gridrc.w) + m_zoom.apply(gridrc.h)) / 2.; + double len = (m_proj.applyX(gridrc.w) + + m_proj.applyY(gridrc.h)) / 2.; alpha = int(alpha * len / 32.); alpha = MID(0, alpha, 255); } @@ -657,7 +666,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) if (x > 0) { gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color()); g->drawVLine(color, - spriteRect.x + m_zoom.apply(x), + spriteRect.x + m_proj.applyX(x), enclosingRect.y, enclosingRect.h); } @@ -669,7 +678,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc) gfx::Color color = color_utils::color_for_ui(m_docPref.grid.color()); g->drawHLine(color, enclosingRect.x, - spriteRect.y + m_zoom.apply(y), + spriteRect.y + m_proj.applyY(y), enclosingRect.w); } break; @@ -747,13 +756,16 @@ void Editor::drawMask(Graphics* g) for (const auto& seg : *m_document->getMaskBoundaries()) { CheckedDrawMode checked(g, m_antsOffset); - gfx::Rect bounds = m_zoom.apply(seg.bounds()); + gfx::Rect bounds = m_proj.apply(seg.bounds()); - if (m_zoom.scale() >= 1.0) { - if (!seg.open()) { - if (seg.vertical()) --bounds.x; - else --bounds.y; - } + if (m_proj.scaleX() >= 1.0) { + if (!seg.open() && seg.vertical()) + --bounds.x; + } + + if (m_proj.scaleY() >= 1.0) { + if (!seg.open() && !seg.vertical()) + --bounds.y; } // The color doesn't matter, we are using CheckedDrawMode @@ -945,8 +957,8 @@ gfx::Point Editor::screenToEditor(const gfx::Point& pt) Point scroll = view->viewScroll(); return gfx::Point( - m_zoom.remove(pt.x - vp.x + scroll.x - m_padding.x), - m_zoom.remove(pt.y - vp.y + scroll.y - m_padding.y)); + m_proj.removeX(pt.x - vp.x + scroll.x - m_padding.x), + m_proj.removeY(pt.y - vp.y + scroll.y - m_padding.y)); } Point Editor::editorToScreen(const gfx::Point& pt) @@ -956,8 +968,8 @@ Point Editor::editorToScreen(const gfx::Point& pt) Point scroll = view->viewScroll(); return Point( - (vp.x - scroll.x + m_padding.x + m_zoom.apply(pt.x)), - (vp.y - scroll.y + m_padding.y + m_zoom.apply(pt.y))); + (vp.x - scroll.x + m_padding.x + m_proj.applyX(pt.x)), + (vp.y - scroll.y + m_padding.y + m_proj.applyY(pt.y))); } Rect Editor::screenToEditor(const Rect& rc) @@ -1013,8 +1025,8 @@ void Editor::centerInSpritePoint(const gfx::Point& spritePos) Rect vp = view->viewportBounds(); gfx::Point scroll( - m_padding.x - (vp.w/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.x), - m_padding.y - (vp.h/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.y)); + m_padding.x - (vp.w/2) + m_proj.applyX(1)/2 + m_proj.applyX(spritePos.x), + m_padding.y - (vp.h/2) + m_proj.applyY(1)/2 + m_proj.applyY(spritePos.y)); updateEditor(); setEditorScroll(scroll); @@ -1316,9 +1328,9 @@ void Editor::onSizeHint(SizeHintEvent& ev) gfx::Size sz(0, 0); if (m_sprite) { - gfx::Point padding = calcExtraPadding(m_zoom); - sz.w = m_zoom.apply(m_sprite->width()) + padding.x*2; - sz.h = m_zoom.apply(m_sprite->height()) + padding.y*2; + gfx::Point padding = calcExtraPadding(m_proj); + sz.w = m_proj.applyX(m_sprite->width()) + padding.x*2; + sz.h = m_proj.applyY(m_sprite->height()) + padding.y*2; } else { sz.w = 4; @@ -1330,7 +1342,7 @@ void Editor::onSizeHint(SizeHintEvent& ev) void Editor::onResize(ui::ResizeEvent& ev) { Widget::onResize(ev); - m_padding = calcExtraPadding(m_zoom); + m_padding = calcExtraPadding(m_proj); } void Editor::onPaint(ui::PaintEvent& ev) @@ -1440,11 +1452,14 @@ bool Editor::isInsideSelection() } void Editor::setZoomAndCenterInMouse(const Zoom& zoom, - const gfx::Point& mousePos, ZoomBehavior zoomBehavior) + const gfx::Point& mousePos, + ZoomBehavior zoomBehavior) { HideBrushPreview hide(m_brushPreview); View* view = View::getView(this); Rect vp = view->viewportBounds(); + Projection proj = m_proj; + proj.setZoom(zoom); gfx::Point screenPos; gfx::Point spritePos; @@ -1461,27 +1476,36 @@ void Editor::setZoomAndCenterInMouse(const Zoom& zoom, } spritePos = screenToEditor(screenPos); - if (zoomBehavior == ZoomBehavior::MOUSE && - m_zoom.scale() > 1.0) { + if (zoomBehavior == ZoomBehavior::MOUSE) { gfx::Point screenPos2 = editorToScreen(spritePos); - subpixelPos.x = (0.5 + screenPos.x - screenPos2.x) / m_zoom.scale(); - subpixelPos.y = (0.5 + screenPos.y - screenPos2.y) / m_zoom.scale(); - if (zoom.scale() > m_zoom.scale()) { - double t = 1.0 / zoom.scale(); - if (subpixelPos.x >= 0.5-t && subpixelPos.x <= 0.5+t) subpixelPos.x = 0.5; - if (subpixelPos.y >= 0.5-t && subpixelPos.y <= 0.5+t) subpixelPos.y = 0.5; + if (m_proj.scaleX() > 1.0) { + subpixelPos.x = (0.5 + screenPos.x - screenPos2.x) / m_proj.scaleX(); + if (proj.scaleX() > m_proj.scaleX()) { + double t = 1.0 / proj.scaleX(); + if (subpixelPos.x >= 0.5-t && subpixelPos.x <= 0.5+t) + subpixelPos.x = 0.5; + } + } + + if (m_proj.scaleY() > 1.0) { + subpixelPos.y = (0.5 + screenPos.y - screenPos2.y) / m_proj.scaleY(); + if (proj.scaleY() > m_proj.scaleY()) { + double t = 1.0 / proj.scaleY(); + if (subpixelPos.y >= 0.5-t && subpixelPos.y <= 0.5+t) + subpixelPos.y = 0.5; + } } } - gfx::Point padding = calcExtraPadding(zoom); + gfx::Point padding = calcExtraPadding(proj); gfx::Point scrollPos( - padding.x - (screenPos.x-vp.x) + zoom.apply(spritePos.x+zoom.remove(1)/2) + int(zoom.apply(subpixelPos.x)), - padding.y - (screenPos.y-vp.y) + zoom.apply(spritePos.y+zoom.remove(1)/2) + int(zoom.apply(subpixelPos.y))); + padding.x - (screenPos.x-vp.x) + proj.applyX(spritePos.x+proj.removeX(1)/2) + int(proj.applyX(subpixelPos.x)), + padding.y - (screenPos.y-vp.y) + proj.applyY(spritePos.y+proj.removeY(1)/2) + int(proj.applyY(subpixelPos.y))); setZoom(zoom); - if ((m_zoom != zoom) || (screenPos != view->viewScroll())) { + if ((m_proj.zoom() != zoom) || (screenPos != view->viewScroll())) { updateEditor(); setEditorScroll(scrollPos); } @@ -1675,14 +1699,14 @@ ImageBufferPtr Editor::getRenderImageBuffer() } // static -gfx::Point Editor::calcExtraPadding(const Zoom& zoom) +gfx::Point Editor::calcExtraPadding(const Projection& proj) { View* view = View::getView(this); if (view) { Rect vp = view->viewportBounds(); return gfx::Point( - std::max(vp.w/2, vp.w - zoom.apply(m_sprite->width())), - std::max(vp.h/2, vp.h - zoom.apply(m_sprite->height()))); + std::max(vp.w/2, vp.w - proj.applyX(m_sprite->width())), + std::max(vp.h/2, vp.h - proj.applyY(m_sprite->height()))); } else return gfx::Point(0, 0); diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index 41a03797d..99e8d48da 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -131,7 +131,8 @@ namespace app { void setLayer(const Layer* layer); void setFrame(frame_t frame); - const render::Zoom& zoom() const { return m_zoom; } + const render::Projection& projection() const { return m_proj; } + const render::Zoom& zoom() const { return m_proj.zoom(); } const gfx::Point& padding() const { return m_padding; } void setZoom(const render::Zoom& zoom); @@ -260,7 +261,7 @@ namespace app { // routine. void drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc, int dx, int dy); - gfx::Point calcExtraPadding(const render::Zoom& zoom); + gfx::Point calcExtraPadding(const render::Projection& proj); void invalidateIfActive(); @@ -278,7 +279,7 @@ namespace app { Sprite* m_sprite; // Active sprite in the editor Layer* m_layer; // Active layer in the editor frame_t m_frame; // Active frame in the editor - render::Zoom m_zoom; // Zoom in the editor + render::Projection m_proj; // Zoom/pixel ratio in the editor DocumentPreferences& m_docPref; // Brush preview diff --git a/src/app/ui/editor/select_box_state.cpp b/src/app/ui/editor/select_box_state.cpp index dc9a8d707..0d17a695d 100644 --- a/src/app/ui/editor/select_box_state.cpp +++ b/src/app/ui/editor/select_box_state.cpp @@ -266,11 +266,11 @@ void SelectBoxState::preRenderDecorator(EditorPreRender* render) void SelectBoxState::postRenderDecorator(EditorPostRender* render) { Editor* editor = render->getEditor(); - render::Zoom zoom = editor->zoom(); + render::Projection proj = editor->projection(); gfx::Rect sp = editor->sprite()->bounds(); gfx::Rect vp = View::getView(editor)->viewportBounds(); - vp.w += zoom.apply(1); - vp.h += zoom.apply(1); + vp.w += proj.applyX(1); + vp.h += proj.applyY(1); vp = editor->screenToEditor(vp); // Paint a grid generated by the box diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp index f8e24255f..b637bb37f 100644 --- a/src/app/ui/editor/tool_loop_impl.cpp +++ b/src/app/ui/editor/tool_loop_impl.cpp @@ -312,8 +312,7 @@ public: m_floodfillSrcImage, m_sprite, m_frame, - gfx::Clip(m_sprite->bounds()), - render::Zoom(1, 1)); + gfx::Clip(m_sprite->bounds())); } else { Cel* cel = m_layer->cel(m_frame); diff --git a/src/app/ui/editor/transform_handles.cpp b/src/app/ui/editor/transform_handles.cpp index 4e8c202fb..e6ce85eef 100644 --- a/src/app/ui/editor/transform_handles.cpp +++ b/src/app/ui/editor/transform_handles.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -179,8 +179,8 @@ gfx::Rect TransformHandles::getPivotHandleBounds(Editor* editor, gfx::Size partSize = theme->parts.pivotHandle()->size(); gfx::Point screenPivotPos = editor->editorToScreen(transform.pivot()); - screenPivotPos.x += editor->zoom().apply(1) / 2; - screenPivotPos.y += editor->zoom().apply(1) / 2; + screenPivotPos.x += editor->projection().applyX(1) / 2; + screenPivotPos.y += editor->projection().applyY(1) / 2; return gfx::Rect( screenPivotPos.x-partSize.w/2, diff --git a/src/doc/doc.h b/src/doc/doc.h index 0e423497d..73c5a3184 100644 --- a/src/doc/doc.h +++ b/src/doc/doc.h @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (c) 2001-2015 David Capello +// Copyright (c) 2001-2016 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -28,6 +28,7 @@ #include "doc/palette.h" #include "doc/palette_picks.h" #include "doc/pixel_format.h" +#include "doc/pixel_ratio.h" #include "doc/primitives.h" #include "doc/primitives_fast.h" #include "doc/remap.h" diff --git a/src/gfx/rect.h b/src/gfx/rect.h index 1c2c07d3a..3cf936f37 100644 --- a/src/gfx/rect.h +++ b/src/gfx/rect.h @@ -1,5 +1,5 @@ // Aseprite Gfx Library -// Copyright (C) 2001-2013, 2015 David Capello +// Copyright (C) 2001-2016 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -175,6 +175,18 @@ public: return *this; } + RectT& enlargeXW(const T& unit) { + x -= unit; + w += unit<<1; + return *this; + } + + RectT& enlargeYH(const T& unit) { + y -= unit; + h += unit<<1; + return *this; + } + RectT& shrink(const T& unit) { x += unit; y += unit; @@ -259,6 +271,22 @@ public: return *this; } + RectT& operator*=(const SizeT& size) const { + x *= size.w; + y *= size.h; + w *= size.w; + h *= size.h; + return *this; + } + + RectT& operator/=(const SizeT& size) const { + x /= size.w; + y /= size.h; + w /= size.w; + h /= size.h; + return *this; + } + const RectT& operator|=(const RectT& rc) { return *this = createUnion(rc); } @@ -283,6 +311,16 @@ public: return createIntersection(other); } + RectT operator*(const SizeT& size) const { + return RectT(x*size.w, y*size.h, + w*size.w, h*size.h); + } + + RectT operator/(const SizeT& size) const { + return RectT(x/size.w, y/size.h, + w/size.w, h/size.h); + } + bool operator==(const RectT& rc) const { return x == rc.x && w == rc.w && diff --git a/src/render/projection.h b/src/render/projection.h new file mode 100644 index 000000000..495c01313 --- /dev/null +++ b/src/render/projection.h @@ -0,0 +1,73 @@ +// Aseprite Render Library +// Copyright (c) 2016 David Capello +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef RENDER_PROJECTION_H_INCLUDED +#define RENDER_PROJECTION_H_INCLUDED +#pragma once + +#include "doc/pixel_ratio.h" +#include "render/zoom.h" + +namespace render { + + class Projection { + public: + Projection() + : m_pixelRatio(1, 1), + m_zoom(1, 1) { + } + + Projection(const doc::PixelRatio& pixelRatio, + const Zoom& zoom) + : m_pixelRatio(pixelRatio), + m_zoom(zoom) { + } + + const doc::PixelRatio& pixelRatio() const { return m_pixelRatio; } + const Zoom& zoom() const { return m_zoom; } + + void setPixelRatio(const doc::PixelRatio& pixelRatio) { m_pixelRatio = pixelRatio; } + void setZoom(const Zoom& zoom) { m_zoom = zoom; } + + double scaleX() const { return m_zoom.scale() * m_pixelRatio.w; } + double scaleY() const { return m_zoom.scale() * m_pixelRatio.h; } + + template + T applyX(T x) const { return m_zoom.apply(x * m_pixelRatio.w); } + + template + T applyY(T y) const { return m_zoom.apply(y * m_pixelRatio.h); } + + template + T removeX(T x) const { return m_zoom.remove(x / m_pixelRatio.w); } + + template + T removeY(T y) const { return m_zoom.remove(y / m_pixelRatio.h); } + + gfx::Rect apply(const gfx::Rect& r) const { + int u = applyX(r.x); + int v = applyY(r.y); + return gfx::Rect(u, v, + applyX(r.x+r.w) - u, + applyY(r.y+r.h) - v); + } + + gfx::Rect remove(const gfx::Rect& r) const { + int u = removeX(r.x); + int v = removeY(r.y); + return gfx::Rect(u, v, + removeX(r.x+r.w) - u, + removeY(r.y+r.h) - v); + } + + private: + doc::PixelRatio m_pixelRatio; + Zoom m_zoom; + }; + +} // namespace render + +#endif diff --git a/src/render/render.cpp b/src/render/render.cpp index 3e0ad3505..c36d0fac7 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -29,9 +29,9 @@ class BlenderHelper { BlendFunc m_blend_func; color_t m_mask_color; public: - BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode) + BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode) { - m_blend_func = SrcTraits::get_blender(blend_mode); + m_blend_func = SrcTraits::get_blender(blendMode); m_mask_color = src->maskColor(); } inline typename DstTraits::pixel_t @@ -51,9 +51,9 @@ class BlenderHelper { BlendFunc m_blend_func; color_t m_mask_color; public: - BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode) + BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode) { - m_blend_func = RgbTraits::get_blender(blend_mode); + m_blend_func = RgbTraits::get_blender(blendMode); m_mask_color = src->maskColor(); } inline RgbTraits::pixel_t @@ -73,14 +73,14 @@ public: template<> class BlenderHelper { const Palette* m_pal; - BlendMode m_blend_mode; + BlendMode m_blendMode; BlendFunc m_blend_func; color_t m_mask_color; public: - BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode) + BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode) { - m_blend_mode = blend_mode; - m_blend_func = RgbTraits::get_blender(blend_mode); + m_blendMode = blendMode; + m_blend_func = RgbTraits::get_blender(blendMode); m_mask_color = src->maskColor(); m_pal = pal; } @@ -89,7 +89,7 @@ public: const IndexedTraits::pixel_t& src, int opacity) { - if (m_blend_mode == BlendMode::SRC) { + if (m_blendMode == BlendMode::SRC) { return m_pal->getEntry(src); } else { @@ -104,12 +104,12 @@ public: template<> class BlenderHelper { - BlendMode m_blend_mode; + BlendMode m_blendMode; color_t m_mask_color; public: - BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode) + BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode) { - m_blend_mode = blend_mode; + m_blendMode = blendMode; m_mask_color = src->maskColor(); } inline IndexedTraits::pixel_t @@ -117,7 +117,7 @@ public: const IndexedTraits::pixel_t& src, int opacity) { - if (m_blend_mode == BlendMode::SRC) { + if (m_blendMode == BlendMode::SRC) { return src; } else { @@ -130,39 +130,39 @@ public: }; template -static void compose_scaled_image_scale_up( +static void compose_scaled_image_zoom_in( Image* dst, const Image* src, const Palette* pal, gfx::Clip area, - int opacity, BlendMode blend_mode, Zoom zoom) + int opacity, BlendMode blendMode, + const Projection& proj) { ASSERT(dst); ASSERT(src); ASSERT(DstTraits::pixel_format == dst->pixelFormat()); ASSERT(SrcTraits::pixel_format == src->pixelFormat()); - 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()))) + proj.applyX(src->width()), + proj.applyY(src->height()))) return; - int px_w = zoom.apply(1); - int px_h = zoom.apply(1); + BlenderHelper blender(src, pal, blendMode); + int px_x, px_y; + int px_w = proj.applyX(1); + int px_h = proj.applyY(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 srcBounds = proj.remove(area.srcBounds()); + 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; + 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); @@ -262,32 +262,34 @@ done_with_blit:; } template -static void compose_scaled_image_scale_down( +static void compose_scaled_image_zoom_out( Image* dst, const Image* src, const Palette* pal, - gfx::Clip area, - int opacity, BlendMode blend_mode, Zoom zoom) + gfx::Clip area, int opacity, BlendMode blendMode, + const Projection& proj) { ASSERT(dst); ASSERT(src); ASSERT(DstTraits::pixel_format == dst->pixelFormat()); ASSERT(SrcTraits::pixel_format == src->pixelFormat()); - 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()))) + proj.applyX(src->width()), + proj.applyY(src->height()))) return; - gfx::Rect srcBounds = zoom.remove(area.srcBounds()); - gfx::Rect dstBounds = area.dstBounds(); - int bottom = area.dst.y+area.size.h-1; + BlenderHelper blender(src, pal, blendMode); + int unbox_w = proj.removeX(1); + int unbox_h = proj.removeY(1); + if (unbox_w < 1 || unbox_h < 1) + return; + gfx::Rect srcBounds = proj.remove(area.srcBounds()); if (srcBounds.isEmpty()) return; + gfx::Rect dstBounds = area.dstBounds(); + int bottom = area.dst.y+area.size.h-1; + // Lock all necessary bits const LockImageBits srcBits(src, srcBounds); LockImageBits dstBits(dst, dstBounds); @@ -327,12 +329,14 @@ template static void compose_scaled_image( Image* dst, const Image* src, const Palette* pal, const gfx::Clip& area, - int opacity, BlendMode blend_mode, Zoom zoom) + int opacity, + BlendMode blendMode, + const Projection& proj) { - if (zoom.scale() >= 1.0) - compose_scaled_image_scale_up(dst, src, pal, area, opacity, blend_mode, zoom); + if (proj.zoom().scale() >= 1.0) + compose_scaled_image_zoom_in(dst, src, pal, area, opacity, blendMode, proj); else - compose_scaled_image_scale_down(dst, src, pal, area, opacity, blend_mode, zoom); + compose_scaled_image_zoom_out(dst, src, pal, area, opacity, blendMode, proj); } Render::Render() @@ -353,6 +357,11 @@ Render::Render() { } +void Render::setProjection(const Projection& projection) +{ + m_proj = projection; +} + void Render::setBgType(BgType type) { m_bgType = type; @@ -427,17 +436,9 @@ void Render::renderSprite( const Sprite* sprite, frame_t frame) { - renderSprite(dstImage, sprite, frame, - gfx::Clip(sprite->bounds()), Zoom(1, 1)); -} - -void Render::renderSprite( - Image* dstImage, - const Sprite* sprite, - frame_t frame, - const gfx::Clip& area) -{ - renderSprite(dstImage, sprite, frame, area, Zoom(1, 1)); + renderSprite( + dstImage, sprite, frame, + gfx::Clip(sprite->bounds())); } void Render::renderLayer( @@ -454,7 +455,7 @@ void Render::renderLayer( const Layer* layer, frame_t frame, const gfx::Clip& area, - BlendMode blend_mode) + BlendMode blendMode) { m_sprite = layer->sprite(); @@ -467,17 +468,15 @@ void Render::renderLayer( m_globalOpacity = 255; renderLayer( - layer, dstImage, area, - frame, Zoom(1, 1), scaled_func, - true, true, blend_mode); + layer, dstImage, area, frame, + scaled_func, true, true, blendMode); } void Render::renderSprite( Image* dstImage, const Sprite* sprite, frame_t frame, - const gfx::Clip& area, - Zoom zoom) + const gfx::Clip& area) { m_sprite = sprite; @@ -511,7 +510,7 @@ void Render::renderSprite( fill_rect(dstImage, area.dstBounds(), bg_color); } else { - renderBackground(dstImage, area, zoom); + renderBackground(dstImage, area); if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) > 0) { blend_rect(dstImage, area.dst.x, area.dst.y, area.dst.x+area.size.w-1, @@ -530,27 +529,27 @@ void Render::renderSprite( m_globalOpacity = 255; renderLayer( m_sprite->folder(), dstImage, - area, frame, zoom, scaled_func, + area, frame, scaled_func, true, false, BlendMode::UNSPECIFIED); // Draw onion skin behind the sprite. if (m_onionskin.position() == OnionskinPosition::BEHIND) - renderOnionskin(dstImage, area, frame, zoom, scaled_func); + renderOnionskin(dstImage, area, frame, scaled_func); // Draw the transparent layers. m_globalOpacity = 255; renderLayer( m_sprite->folder(), dstImage, - area, frame, zoom, scaled_func, + area, frame, scaled_func, false, true, BlendMode::UNSPECIFIED); // Draw onion skin in front of the sprite. if (m_onionskin.position() == OnionskinPosition::INFRONT) - renderOnionskin(dstImage, area, frame, zoom, scaled_func); + renderOnionskin(dstImage, area, frame, scaled_func); // Overlay preview image if (m_previewImage && @@ -564,15 +563,14 @@ void Render::renderSprite( area, scaled_func, 255, - m_previewBlendMode, - zoom); + m_previewBlendMode); } } void Render::renderOnionskin( Image* dstImage, const gfx::Clip& area, - frame_t frame, Zoom zoom, + frame_t frame, RenderScaledImage scaled_func) { // Onion-skin feature: Draw previous/next frames with different @@ -612,43 +610,42 @@ void Render::renderOnionskin( m_globalOpacity = MID(0, m_globalOpacity, 255); if (m_globalOpacity > 0) { - BlendMode blend_mode = BlendMode::UNSPECIFIED; + BlendMode blendMode = BlendMode::UNSPECIFIED; if (m_onionskin.type() == OnionskinType::MERGE) - blend_mode = BlendMode::NORMAL; + blendMode = BlendMode::NORMAL; else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT) - blend_mode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT); + blendMode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT); renderLayer( onionLayer, dstImage, - area, frameIn, zoom, scaled_func, + area, frameIn, scaled_func, // Render background only for "in-front" onion skinning and // when opacity is < 255 (m_globalOpacity < 255 && m_onionskin.position() == OnionskinPosition::INFRONT), true, - blend_mode); + blendMode); } } } } -void Render::renderBackground(Image* image, - const gfx::Clip& area, - Zoom zoom) +void Render::renderBackground( + Image* image, + const gfx::Clip& area) { 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_w = m_proj.zoom().apply(tile_w); + tile_h = m_proj.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 < m_proj.applyX(1)) tile_w = m_proj.applyX(1); + if (tile_h < m_proj.applyY(1)) tile_h = m_proj.applyY(1); if (tile_w < 1) tile_w = 1; if (tile_h < 1) tile_h = 1; @@ -678,10 +675,11 @@ void Render::renderBackground(Image* image, } } -void Render::renderImage(Image* dst_image, const Image* src_image, - const Palette* pal, - int x, int y, - Zoom zoom, int opacity, BlendMode blend_mode) +void Render::renderImage( + Image* dst_image, const Image* src_image, + const Palette* pal, + int x, int y, + int opacity, BlendMode blendMode) { RenderScaledImage scaled_func = getRenderScaledImageFunc( dst_image->pixelFormat(), @@ -689,22 +687,24 @@ void Render::renderImage(Image* dst_image, const Image* src_image, if (!scaled_func) return; - scaled_func(dst_image, src_image, pal, + 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); + m_proj.applyX(src_image->width()), + m_proj.applyY(src_image->height())), + opacity, blendMode, + m_proj); } void Render::renderLayer( const Layer* layer, Image *image, const gfx::Clip& area, - frame_t frame, Zoom zoom, + frame_t frame, RenderScaledImage scaled_func, bool render_background, bool render_transparent, - BlendMode blend_mode) + BlendMode blendMode) { // we can't read from this layer if (!layer->isVisible()) @@ -724,11 +724,9 @@ void Render::renderLayer( m_extraCel->y(), m_extraImage->width(), m_extraImage->height()); - extraArea = zoom.apply(extraArea); - if (zoom.scale() < 1.0) { - extraArea.w--; - extraArea.h--; - } + extraArea = m_proj.apply(extraArea); + if (m_proj.scaleX() < 1.0) extraArea.w--; + if (m_proj.scaleY() < 1.0) extraArea.h--; if (extraArea.w < 1) extraArea.w = 1; if (extraArea.h < 1) extraArea.h = 1; } @@ -761,9 +759,9 @@ void Render::renderLayer( if (src_image) { const LayerImage* imgLayer = static_cast(layer); BlendMode layerBlendMode = - (blend_mode == BlendMode::UNSPECIFIED ? + (blendMode == BlendMode::UNSPECIFIED ? imgLayer->blendMode(): - blend_mode); + blendMode); ASSERT(cel->opacity() >= 0); ASSERT(cel->opacity() <= 255); @@ -789,7 +787,7 @@ void Render::renderLayer( image, src_image, pal, cel, gfx::Clip(area.dst.x+rc.x-area.src.x, area.dst.y+rc.y-area.src.y, rc), scaled_func, - opacity, layerBlendMode, zoom); + opacity, layerBlendMode); } } // Draw the whole cel @@ -797,7 +795,7 @@ void Render::renderLayer( renderCel( image, src_image, pal, cel, area, scaled_func, - opacity, layerBlendMode, zoom); + opacity, layerBlendMode); } } } @@ -809,11 +807,13 @@ void Render::renderLayer( LayerConstIterator end = static_cast(layer)->getLayerEnd(); for (; it != end; ++it) { - renderLayer(*it, image, - area, frame, zoom, scaled_func, + renderLayer( + *it, image, + area, frame, + scaled_func, render_background, render_transparent, - blend_mode); + blendMode); } break; } @@ -832,7 +832,7 @@ void Render::renderLayer( extraArea), scaled_func, m_extraCel->opacity(), - m_extraBlendMode, zoom); + m_extraBlendMode); } } } @@ -844,7 +844,7 @@ void Render::renderCel( const Cel* cel, const gfx::Clip& area, RenderScaledImage scaled_func, - int opacity, BlendMode blend_mode, Zoom zoom) + int opacity, BlendMode blendMode) { renderImage(dst_image, cel_image, @@ -854,8 +854,7 @@ void Render::renderCel( area, scaled_func, opacity, - blend_mode, - zoom); + blendMode); } void Render::renderImage( @@ -866,30 +865,32 @@ void Render::renderImage( const int y, const gfx::Clip& area, RenderScaledImage scaled_func, - int opacity, BlendMode blend_mode, Zoom zoom) + int opacity, BlendMode blendMode) { - int cel_x = zoom.apply(x); - int cel_y = zoom.apply(y); + int cel_x = m_proj.applyX(x); + int cel_y = m_proj.applyY(y); - gfx::Rect src_bounds = + gfx::Rect srcBounds = area.srcBounds().createIntersection( gfx::Rect( cel_x, cel_y, - zoom.apply(cel_image->width()), - zoom.apply(cel_image->height()))); - if (src_bounds.isEmpty()) + m_proj.applyX(cel_image->width()), + m_proj.applyY(cel_image->height()))); + if (srcBounds.isEmpty()) return; - (*scaled_func)(dst_image, cel_image, pal, + (*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); + area.dst.x + srcBounds.x - area.src.x, + area.dst.y + srcBounds.y - area.src.y, + srcBounds.x - cel_x, + srcBounds.y - cel_y, + srcBounds.w, + srcBounds.h), + opacity, blendMode, + m_proj); } // static @@ -931,13 +932,13 @@ Render::RenderScaledImage Render::getRenderScaledImageFunc( void composite_image(Image* dst, const Image* src, const Palette* pal, int x, int y, - int opacity, BlendMode blend_mode) + int opacity, BlendMode blendMode) { // As the background is not rendered in renderImage(), we don't need // to configure the Render instance's BgType. Render().renderImage( - dst, src, pal, x, y, Zoom(1, 1), - opacity, blend_mode); + dst, src, pal, x, y, + opacity, blendMode); } } // namespace render diff --git a/src/render/render.h b/src/render/render.h index 93a6dbe1a..d03c52a51 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -17,7 +17,7 @@ #include "gfx/size.h" #include "render/extra_type.h" #include "render/onionskin_position.h" -#include "render/zoom.h" +#include "render/projection.h" namespace gfx { class Clip; @@ -93,6 +93,9 @@ namespace render { public: Render(); + // Viewport configuration + void setProjection(const Projection& projection); + // Background configuration void setBgType(BgType type); void setBgZoom(bool state); @@ -123,12 +126,6 @@ namespace render { const Sprite* sprite, frame_t frame); - void renderSprite( - Image* dstImage, - const Sprite* sprite, - frame_t frame, - const gfx::Clip& area); - void renderLayer( Image* dstImage, const Layer* layer, @@ -139,7 +136,7 @@ namespace render { const Layer* layer, frame_t frame, const gfx::Clip& area, - BlendMode blend_mode = BlendMode::UNSPECIFIED); + BlendMode blendMode = BlendMode::UNSPECIFIED); // Main function used to render the sprite. Draws the given sprite // frame in a new image and return it. Note: zoomedRect must have @@ -148,39 +145,41 @@ namespace render { Image* dstImage, const Sprite* sprite, frame_t frame, - const gfx::Clip& area, - Zoom zoom); + const gfx::Clip& area); // Extra functions - void renderBackground(Image* image, - const gfx::Clip& area, - Zoom zoom); + void renderBackground( + Image* image, + const gfx::Clip& area); - void renderImage(Image* dst_image, const Image* src_image, - const Palette* pal, int x, int y, Zoom zoom, - int opacity, BlendMode blend_mode); + void renderImage( + Image* dst_image, const Image* src_image, + const Palette* pal, int x, int y, + int opacity, BlendMode blendMode); private: typedef void (*RenderScaledImage)( Image* dst, const Image* src, const Palette* pal, const gfx::Clip& area, - int opacity, BlendMode blend_mode, Zoom zoom); + int opacity, + BlendMode blendMode, + const Projection& proj); void renderOnionskin( Image* image, const gfx::Clip& area, - frame_t frame, Zoom zoom, + frame_t frame, RenderScaledImage scaled_func); void renderLayer( const Layer* layer, Image* image, const gfx::Clip& area, - frame_t frame, Zoom zoom, + frame_t frame, RenderScaledImage renderScaledImage, bool render_background, bool render_transparent, - BlendMode blend_mode); + BlendMode blendMode); void renderCel( Image* dst_image, @@ -189,7 +188,7 @@ namespace render { const Cel* cel, const gfx::Clip& area, RenderScaledImage scaled_func, - int opacity, BlendMode blend_mode, Zoom zoom); + int opacity, BlendMode blendMode); void renderImage( Image* dst_image, @@ -199,7 +198,7 @@ namespace render { const int y, const gfx::Clip& area, RenderScaledImage scaled_func, - int opacity, BlendMode blend_mode, Zoom zoom); + int opacity, BlendMode blendMode); static RenderScaledImage getRenderScaledImageFunc( PixelFormat dstFormat, @@ -208,11 +207,11 @@ namespace render { const Sprite* m_sprite; const Layer* m_currentLayer; frame_t m_currentFrame; + Projection m_proj; ExtraType m_extraType; const Cel* m_extraCel; const Image* m_extraImage; BlendMode m_extraBlendMode; - BgType m_bgType; bool m_bgZoom; color_t m_bgColor1; @@ -229,7 +228,7 @@ namespace render { void composite_image(Image* dst, const Image* src, const Palette* pal, int x, int y, - int opacity, BlendMode blend_mode); + int opacity, BlendMode blendMode); } // namespace render diff --git a/src/render/render_tests.cpp b/src/render/render_tests.cpp index 735903ef8..42d8f3a80 100644 --- a/src/render/render_tests.cpp +++ b/src/render/render_tests.cpp @@ -1,5 +1,5 @@ // Aseprite Document Library -// Copyright (c) 2001-2014 David Capello +// Copyright (c) 2001-2016 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. @@ -147,7 +147,7 @@ TEST(Render, CheckedBackground) render.setBgCheckedSize(gfx::Size(1, 1)); render.renderSprite(dst, doc->sprite(), frame_t(0)); - EXPECT_4X4_PIXELS(dst, + EXPECT_4X4_PIXELS(dst, 1, 2, 1, 2, 2, 1, 2, 1, 1, 2, 1, 2, @@ -155,7 +155,7 @@ TEST(Render, CheckedBackground) render.setBgCheckedSize(gfx::Size(2, 2)); render.renderSprite(dst, doc->sprite(), frame_t(0)); - EXPECT_4X4_PIXELS(dst, + EXPECT_4X4_PIXELS(dst, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, @@ -163,17 +163,15 @@ TEST(Render, CheckedBackground) render.setBgCheckedSize(gfx::Size(3, 3)); render.renderSprite(dst, doc->sprite(), frame_t(0)); - EXPECT_4X4_PIXELS(dst, + EXPECT_4X4_PIXELS(dst, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 1); + render.setProjection(Projection(PixelRatio(1, 1), Zoom(2, 1))); render.setBgCheckedSize(gfx::Size(1, 1)); - render.renderSprite(dst, - doc->sprite(), frame_t(0), - gfx::Clip(dst->bounds()), - Zoom(2, 1)); + render.renderSprite(dst, doc->sprite(), frame_t(0)); EXPECT_4X4_PIXELS(dst, 1, 1, 2, 2, 1, 1, 2, 2, @@ -204,10 +202,10 @@ TEST(Render, ZoomAndDstBounds) render.setBgColor2(2); render.setBgCheckedSize(gfx::Size(1, 1)); - render.renderSprite(dst, doc->sprite(), frame_t(0), - gfx::Clip(1, 1, 0, 0, 2, 2), - Zoom(1, 1)); - EXPECT_4X4_PIXELS(dst, + render.renderSprite( + dst, doc->sprite(), frame_t(0), + gfx::Clip(1, 1, 0, 0, 2, 2)); + EXPECT_4X4_PIXELS(dst, 0, 0, 0, 0, 0, 1, 2, 0, 0, 2, 4, 0,