mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-24 12:41:08 +00:00
Add pixel ratio support to the Editor & Render
This commit is contained in:
parent
73bda9bd19
commit
37209a0f5b
@ -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<ui::display_w()+w; u+=w)
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, u, y,
|
||||
m_zoom, 255, BlendMode::NORMAL);
|
||||
255, BlendMode::NORMAL);
|
||||
break;
|
||||
case TiledMode::Y_AXIS:
|
||||
for (v=y-h; v<ui::display_h()+h; v+=h)
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, x, v,
|
||||
m_zoom, 255, BlendMode::NORMAL);
|
||||
255, BlendMode::NORMAL);
|
||||
break;
|
||||
case TiledMode::BOTH:
|
||||
for (v=y-h; v<ui::display_h()+h; v+=h)
|
||||
for (u=x-w; u<ui::display_w()+w; u+=w)
|
||||
render.renderImage(m_doublebuf, m_render, m_pal, u, v,
|
||||
m_zoom, 255, BlendMode::NORMAL);
|
||||
255, BlendMode::NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -243,7 +245,7 @@ private:
|
||||
gfx::Point m_pos;
|
||||
gfx::Point m_oldMousePos;
|
||||
gfx::Point m_delta;
|
||||
render::Zoom m_zoom;
|
||||
render::Projection m_proj;
|
||||
int m_index_bg_color;
|
||||
base::UniquePtr<Image> m_render;
|
||||
base::UniquePtr<Image> m_doublebuf;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Point padding = calcExtraPadding(zoom);
|
||||
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(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<int>(vp.w/2, vp.w - zoom.apply(m_sprite->width())),
|
||||
std::max<int>(vp.h/2, vp.h - zoom.apply(m_sprite->height())));
|
||||
std::max<int>(vp.w/2, vp.w - proj.applyX(m_sprite->width())),
|
||||
std::max<int>(vp.h/2, vp.h - proj.applyY(m_sprite->height())));
|
||||
}
|
||||
else
|
||||
return gfx::Point(0, 0);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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<T>& size) const {
|
||||
x *= size.w;
|
||||
y *= size.h;
|
||||
w *= size.w;
|
||||
h *= size.h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RectT& operator/=(const SizeT<T>& 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<T>& size) const {
|
||||
return RectT(x*size.w, y*size.h,
|
||||
w*size.w, h*size.h);
|
||||
}
|
||||
|
||||
RectT operator/(const SizeT<T>& 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 &&
|
||||
|
73
src/render/projection.h
Normal file
73
src/render/projection.h
Normal file
@ -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<typename T>
|
||||
T applyX(T x) const { return m_zoom.apply(x * m_pixelRatio.w); }
|
||||
|
||||
template<typename T>
|
||||
T applyY(T y) const { return m_zoom.apply(y * m_pixelRatio.h); }
|
||||
|
||||
template<typename T>
|
||||
T removeX(T x) const { return m_zoom.remove(x / m_pixelRatio.w); }
|
||||
|
||||
template<typename T>
|
||||
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
|
@ -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<RgbTraits, GrayscaleTraits> {
|
||||
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<RgbTraits, IndexedTraits> {
|
||||
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<IndexedTraits, IndexedTraits> {
|
||||
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<class DstTraits, class SrcTraits>
|
||||
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<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int px_x, px_y;
|
||||
|
||||
if (!area.clip(dst->width(), dst->height(),
|
||||
zoom.apply(src->width()),
|
||||
zoom.apply(src->height())))
|
||||
proj.applyX(src->width()),
|
||||
proj.applyY(src->height())))
|
||||
return;
|
||||
|
||||
int px_w = zoom.apply(1);
|
||||
int px_h = zoom.apply(1);
|
||||
BlenderHelper<DstTraits, SrcTraits> 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<typename DstTraits::pixel_t> Scanline;
|
||||
Scanline scanline(srcBounds.w);
|
||||
@ -262,32 +262,34 @@ done_with_blit:;
|
||||
}
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
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<DstTraits, SrcTraits> blender(src, pal, blend_mode);
|
||||
int unbox_w = zoom.remove(1);
|
||||
int unbox_h = zoom.remove(1);
|
||||
|
||||
if (!area.clip(dst->width(), dst->height(),
|
||||
zoom.apply(src->width()),
|
||||
zoom.apply(src->height())))
|
||||
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<DstTraits, SrcTraits> 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<SrcTraits> srcBits(src, srcBounds);
|
||||
LockImageBits<DstTraits> dstBits(dst, dstBounds);
|
||||
@ -327,12 +329,14 @@ template<class DstTraits, class SrcTraits>
|
||||
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<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blend_mode, zoom);
|
||||
if (proj.zoom().scale() >= 1.0)
|
||||
compose_scaled_image_zoom_in<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blendMode, proj);
|
||||
else
|
||||
compose_scaled_image_scale_down<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blend_mode, zoom);
|
||||
compose_scaled_image_zoom_out<DstTraits, SrcTraits>(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,
|
||||
void Render::renderImage(
|
||||
Image* dst_image, const Image* src_image,
|
||||
const Palette* pal,
|
||||
int x, int y,
|
||||
Zoom zoom, int opacity, BlendMode blend_mode)
|
||||
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<const LayerImage*>(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<const LayerFolder*>(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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
@ -169,11 +169,9 @@ TEST(Render, CheckedBackground)
|
||||
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,9 +202,9 @@ 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));
|
||||
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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user