Add zoom out (related to issue #33)

Before the zoom was handle as a bits shift (scaled = value << zoom),
now the zoom is a fraction (scaled = value * zoom.num / zoom.den).

Changes:
* Add Zoom class to apply/remove zoom factor
* Split merge_zoomed_image template function into
  merge_zoomed_image_scale_down and merge_zoomed_image_scale_up
  to handle the special case where zoom.num/zoom.den < 1.0
This commit is contained in:
David Capello 2014-11-24 17:18:30 -03:00
parent 17b0bee0ae
commit 4fc28d5639
23 changed files with 367 additions and 148 deletions

View File

@ -259,4 +259,5 @@ add_library(app-lib
webserver.cpp
widget_loader.cpp
xml_document.cpp
xml_exception.cpp)
xml_exception.cpp
zoom.cpp)

View File

@ -99,13 +99,13 @@ void MoveMaskCommand::onExecute(Context* context)
pixels = gridBounds.h;
break;
case ZoomedPixel:
pixels = 1 << current_editor->zoom();
pixels = current_editor->zoom().apply(1);
break;
case ZoomedTileWidth:
pixels = gridBounds.w << current_editor->zoom();
pixels = current_editor->zoom().apply(gridBounds.w);
break;
case ZoomedTileHeight:
pixels = gridBounds.h << current_editor->zoom();
pixels = current_editor->zoom().apply(gridBounds.h);
break;
case ViewportWidth:
pixels = vp.h;

View File

@ -63,7 +63,8 @@ public:
, m_pal(m_sprite->getPalette(editor->frame()))
, 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())) {
, m_doublesur(she::instance()->createRgbaSurface(ui::display_w(), ui::display_h()))
, m_zoom(editor->zoom()) {
// Do not use DocumentWriter (do not lock the document) because we
// will call other sub-commands (e.g. previous frame, next frame,
// etc.).
@ -83,7 +84,6 @@ public:
m_oldMousePos = ui::get_mouse_position();
m_pos.x = -scroll.x + vp.x + editor->offsetX();
m_pos.y = -scroll.y + vp.y + editor->offsetY();
m_zoom = editor->zoom();
setFocusStop(true);
captureMouse();
@ -193,14 +193,14 @@ protected:
m_render.reset(
renderEngine.renderSprite(
0, 0, m_sprite->width(), m_sprite->height(),
m_editor->frame(), 0, false, false));
m_editor->frame(), Zoom(1, 1), false, false));
}
int x, y, w, h, u, v;
x = m_pos.x + ((m_delta.x >> m_zoom) << m_zoom);
y = m_pos.y + ((m_delta.y >> m_zoom) << m_zoom);
w = (m_sprite->width()<<m_zoom);
h = (m_sprite->height()<<m_zoom);
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());
if (m_tiled & TILED_X_AXIS) x = SGN(x) * (ABS(x)%w);
if (m_tiled & TILED_Y_AXIS) y = SGN(y) * (ABS(y)%h);
@ -243,7 +243,7 @@ private:
gfx::Point m_pos;
gfx::Point m_oldMousePos;
gfx::Point m_delta;
int m_zoom;
Zoom m_zoom;
int m_index_bg_color;
base::UniquePtr<Image> m_render;
base::UniquePtr<Image> m_doublebuf;

View File

@ -117,13 +117,13 @@ void ScrollCommand::onExecute(Context* context)
pixels = gridBounds.h;
break;
case ZoomedPixel:
pixels = 1 << current_editor->zoom();
pixels = current_editor->zoom().apply(1);
break;
case ZoomedTileWidth:
pixels = gridBounds.w << current_editor->zoom();
pixels = current_editor->zoom().apply(gridBounds.w);
break;
case ZoomedTileHeight:
pixels = gridBounds.h << current_editor->zoom();
pixels = current_editor->zoom().apply(gridBounds.h);
break;
case ViewportWidth:
pixels = vp.h;

View File

@ -74,25 +74,23 @@ bool ZoomCommand::onEnabled(Context* context)
void ZoomCommand::onExecute(Context* context)
{
int zoom = current_editor->zoom();
Zoom zoom = current_editor->zoom();
switch (m_action) {
case In:
if (zoom < 5)
++zoom;
zoom.in();
break;
case Out:
if (zoom > 0)
--zoom;
zoom.out();
break;
case Set:
switch (m_percentage) {
case 3200: zoom = 5; break;
case 1600: zoom = 4; break;
case 800: zoom = 3; break;
case 400: zoom = 2; break;
case 200: zoom = 1; break;
default: zoom = 0; break;
case 3200: zoom = Zoom(32, 1); break;
case 1600: zoom = Zoom(16, 1); break;
case 800: zoom = Zoom(8, 1); break;
case 400: zoom = Zoom(4, 1); break;
case 200: zoom = Zoom(2, 1); break;
default: zoom = Zoom(1, 1); break;
}
break;
}

View File

@ -257,8 +257,8 @@ void FilterManagerImpl::flush()
m_x+m_offset_x,
m_y+m_offset_y+m_row-1)),
gfx::Size(
(m_w << editor->zoom()),
(1 << editor->zoom())));
editor->zoom().apply(m_w),
editor->zoom().apply(1)));
gfx::Region reg1(rect);
gfx::Region reg2;

View File

@ -85,7 +85,7 @@ private:
base::UniquePtr<Image> image(renderEngine.renderSprite(
0, 0, sprite->width(), sprite->height(),
FrameNumber(0), 0, true, false));
FrameNumber(0), Zoom(1, 1), true, false));
// Calculate the thumbnail size
int thumb_w = MAX_THUMBNAIL_SIZE * image->width() / MAX(image->width(), image->height());

View File

@ -22,6 +22,7 @@
#include "app/settings/selection_mode.h"
#include "app/tools/trace_policy.h"
#include "app/zoom.h"
#include "doc/frame_number.h"
#include "filters/tiled_mode.h"
#include "gfx/point.h"
@ -104,6 +105,9 @@ namespace app {
// Gets mask X,Y origin coordinates
virtual gfx::Point getMaskOrigin() = 0;
// Returns the zoom
virtual const Zoom& zoom() = 0;
// Return the mouse button which start the tool-loop. It can be used
// by some tools that instead of using the primary/secondary color
// uses the pressed button for different behavior (like selection

View File

@ -213,7 +213,7 @@ void ToolLoopManager::doLoopStep(bool last_step)
// Calculate the area to be updated in all document observers.
Region& dirty_area = m_toolLoop->getDirtyArea();
calculateDirtyArea(m_toolLoop, points_to_interwine, dirty_area);
calculateDirtyArea(points_to_interwine, dirty_area);
if (m_toolLoop->getTracePolicy() == TracePolicyLast) {
Region prev_dirty_area = dirty_area;
@ -235,7 +235,7 @@ void ToolLoopManager::snapToGrid(Point& point)
m_toolLoop->getDocumentSettings()->snapToGrid(point);
}
void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, Region& dirty_area)
void ToolLoopManager::calculateDirtyArea(const Points& points, Region& dirty_area)
{
dirty_area.clear();
@ -245,21 +245,21 @@ void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, R
// Expand the dirty-area with the pen width
Rect r1, r2;
loop->getPointShape()->getModifiedArea(loop, minpt.x, minpt.y, r1);
loop->getPointShape()->getModifiedArea(loop, maxpt.x, maxpt.y, r2);
m_toolLoop->getPointShape()->getModifiedArea(m_toolLoop, minpt.x, minpt.y, r1);
m_toolLoop->getPointShape()->getModifiedArea(m_toolLoop, maxpt.x, maxpt.y, r2);
dirty_area.createUnion(dirty_area, Region(r1.createUnion(r2)));
}
// Apply offset mode
Point offset(loop->getOffset());
Point offset(m_toolLoop->getOffset());
dirty_area.offset(-offset);
// Apply tiled mode
TiledMode tiledMode = loop->getDocumentSettings()->getTiledMode();
TiledMode tiledMode = m_toolLoop->getDocumentSettings()->getTiledMode();
if (tiledMode != TILED_NONE) {
int w = loop->sprite()->width();
int h = loop->sprite()->height();
int w = m_toolLoop->sprite()->width();
int h = m_toolLoop->sprite()->height();
Region sprite_area(Rect(0, 0, w, h));
Region outside;
outside.createSubtraction(dirty_area, sprite_area);
@ -312,6 +312,11 @@ void ToolLoopManager::calculateMinMax(const Points& points, Point& minpt, Point&
maxpt.x = MAX(maxpt.x, points[c].x);
maxpt.y = MAX(maxpt.y, points[c].y);
}
if (m_toolLoop->zoom().scale() < 1.0) {
maxpt.x += m_toolLoop->zoom().remove(1);
maxpt.y += m_toolLoop->zoom().remove(1);
}
}
} // namespace tools

View File

@ -103,13 +103,13 @@ namespace app {
void doLoopStep(bool last_step);
void snapToGrid(gfx::Point& point);
static void calculateDirtyArea(ToolLoop* loop,
const Points& points,
gfx::Region& dirty_area);
void calculateDirtyArea(
const Points& points,
gfx::Region& dirty_area);
static void calculateMinMax(const Points& points,
gfx::Point& minpt,
gfx::Point& maxpt);
void calculateMinMax(const Points& points,
gfx::Point& minpt,
gfx::Point& maxpt);
ToolLoop* m_toolLoop;
Points m_points;

View File

@ -55,7 +55,7 @@ namespace app {
using namespace ui;
// Returns true if the cursor of the editor needs subpixel movement.
#define IS_SUBPIXEL(editor) ((editor)->m_zoom >= 2)
#define IS_SUBPIXEL(editor) ((editor)->m_zoom.scale() >= 4.0)
// Maximum quantity of colors to save pixels overlapped by the cursor.
#define MAX_SAVED 4096
@ -514,25 +514,22 @@ static void trace_thickcross_pixels(ui::Graphics* g, Editor* editor,
0, 0, 1, 1, 0, 0,
0, 0, 1, 1, 0, 0,
};
gfx::Point out;
gfx::Point out, outpt = editor->editorToScreen(pt);
int u, v;
int zoom = editor->zoom();
int size = editor->zoom().apply(thickness/2);
int size2 = editor->zoom().apply(thickness);
if (size2 == 0) size2 = 1;
for (v=0; v<6; v++) {
for (u=0; u<6; u++) {
if (cursor_cross[v*6+u]) {
out = editor->editorToScreen(pt);
if (!cursor_cross[v*6+u])
continue;
out.x += ((u<3) ?
u-((thickness>>1)<<zoom)-3:
u-((thickness>>1)<<zoom)-3+(thickness<<zoom));
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.y += ((v<3)?
v-((thickness>>1)<<zoom)-3:
v-((thickness>>1)<<zoom)-3+(thickness<<zoom));
pixelDelegate(g, out, color);
}
pixelDelegate(g, out, color);
}
}
}

View File

@ -71,7 +71,7 @@ using namespace ui;
class EditorPreRenderImpl : public EditorPreRender {
public:
EditorPreRenderImpl(Editor* editor, Image* image, const Point& offset, int zoom)
EditorPreRenderImpl(Editor* editor, Image* image, const Point& offset, Zoom zoom)
: m_editor(editor)
, m_image(image)
, m_offset(offset)
@ -92,17 +92,17 @@ public:
void fillRect(const gfx::Rect& rect, uint32_t rgbaColor, int opacity) override
{
blend_rect(m_image,
m_offset.x + (rect.x << m_zoom),
m_offset.y + (rect.y << m_zoom),
m_offset.x + ((rect.x+rect.w) << m_zoom) - 1,
m_offset.y + ((rect.y+rect.h) << m_zoom) - 1, rgbaColor, opacity);
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);
}
private:
Editor* m_editor;
Image* m_image;
Point m_offset;
int m_zoom;
Zoom m_zoom;
};
class EditorPostRenderImpl : public EditorPostRender {
@ -145,7 +145,7 @@ Editor::Editor(Document* document, EditorFlags flags)
, m_sprite(m_document->sprite())
, m_layer(m_sprite->folder()->getFirstLayer())
, m_frame(FrameNumber(0))
, m_zoom(0)
, m_zoom(1, 1)
, m_cursorThick(0)
, m_cursorScreen(0, 0)
, m_cursorEditor(0, 0)
@ -324,7 +324,7 @@ void Editor::setEditorScroll(const gfx::Point& scroll, bool blit_valid_rgn)
drawBrushPreview(m_cursorScreen);
}
void Editor::setEditorZoom(int zoom)
void Editor::setEditorZoom(Zoom zoom)
{
setZoomAndCenterInMouse(zoom,
ui::get_mouse_position(), Editor::kCofiguredZoomBehavior);
@ -338,12 +338,12 @@ void Editor::updateEditor()
void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc, int dx, int dy)
{
// Output information
int source_x = rc.x << m_zoom;
int source_y = rc.y << m_zoom;
int source_x = m_zoom.apply(rc.x);
int source_y = m_zoom.apply(rc.y);
int dest_x = dx + m_offset_x + source_x;
int dest_y = dy + m_offset_y + source_y;
int width = rc.w << m_zoom;
int height = rc.h << m_zoom;
int width = m_zoom.apply(rc.w);
int height = m_zoom.apply(rc.h);
// Clip from graphics/screen
const gfx::Rect& clip = g->getClipBounds();
@ -375,11 +375,11 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc, in
dest_y -= source_y;
source_y = 0;
}
if (source_x+width > (m_sprite->width() << m_zoom)) {
width = (m_sprite->width() << m_zoom) - source_x;
if (source_x+width > m_zoom.apply(m_sprite->width())) {
width = m_zoom.apply(m_sprite->width()) - source_x;
}
if (source_y+height > (m_sprite->height() << m_zoom)) {
height = (m_sprite->height() << m_zoom) - source_y;
if (source_y+height > m_zoom.apply(m_sprite->height())) {
height = m_zoom.apply(m_sprite->height()) - source_y;
}
// Draw the sprite
@ -402,7 +402,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc, in
// Pre-render decorator.
if ((m_flags & kShowDecorators) && m_decorator) {
EditorPreRenderImpl preRender(this, rendered,
Point(-source_x, -source_y), m_zoom);
Point(-source_x, -source_y), m_zoom);
m_decorator->preRenderDecorator(&preRender);
}
@ -423,8 +423,8 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc)
gfx::Rect spriteRect(
client.x + m_offset_x,
client.y + m_offset_y,
(m_sprite->width() << m_zoom),
(m_sprite->height() << m_zoom));
m_zoom.apply(m_sprite->width()),
m_zoom.apply(m_sprite->height()));
gfx::Rect enclosingRect = spriteRect;
// Draw the main sprite at the center.
@ -473,7 +473,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& rc)
}
// Draw the pixel grid
if ((m_zoom > 1) && docSettings->getPixelGridVisible()) {
if ((m_zoom.scale() > 1.0) && docSettings->getPixelGridVisible()) {
drawGrid(g, enclosingRect, Rect(0, 0, 1, 1), docSettings->getPixelGridColor());
}
@ -544,10 +544,10 @@ void Editor::drawMask(Graphics* g)
CheckedDrawMode checked(g, m_offset_count);
for (int c=0; c<nseg; ++c, ++seg) {
x1 = seg->x1 << m_zoom;
y1 = seg->y1 << m_zoom;
x2 = seg->x2 << m_zoom;
y2 = seg->y2 << m_zoom;
x1 = m_zoom.apply(seg->x1);
y1 = m_zoom.apply(seg->y1);
x2 = m_zoom.apply(seg->x2);
y2 = m_zoom.apply(seg->y2);
#if 1 // Bounds inside mask
if (!seg->open)
@ -863,8 +863,8 @@ gfx::Point Editor::screenToEditor(const gfx::Point& pt)
Point scroll = view->getViewScroll();
return gfx::Point(
(pt.x - vp.x + scroll.x - m_offset_x) >> m_zoom,
(pt.y - vp.y + scroll.y - m_offset_y) >> m_zoom);
m_zoom.remove(pt.x - vp.x + scroll.x - m_offset_x),
m_zoom.remove(pt.y - vp.y + scroll.y - m_offset_y));
}
Point Editor::editorToScreen(const gfx::Point& pt)
@ -874,8 +874,8 @@ Point Editor::editorToScreen(const gfx::Point& pt)
Point scroll = view->getViewScroll();
return Point(
(vp.x - scroll.x + m_offset_x + (pt.x << m_zoom)),
(vp.y - scroll.y + m_offset_y + (pt.y << m_zoom)));
(vp.x - scroll.x + m_offset_x + m_zoom.apply(pt.x)),
(vp.y - scroll.y + m_offset_y + m_zoom.apply(pt.y)));
}
Rect Editor::screenToEditor(const Rect& rc)
@ -968,8 +968,8 @@ void Editor::centerInSpritePoint(const gfx::Point& spritePos)
hideDrawingCursor();
gfx::Point scroll(
m_offset_x - (vp.w/2) + ((1<<m_zoom)>>1) + (spritePos.x << m_zoom),
m_offset_y - (vp.h/2) + ((1<<m_zoom)>>1) + (spritePos.y << m_zoom));
m_offset_x - (vp.w/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.x),
m_offset_y - (vp.h/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.y));
updateEditor();
setEditorScroll(scroll, false);
@ -1212,8 +1212,8 @@ void Editor::onPreferredSize(PreferredSizeEvent& ev)
m_offset_x = std::max<int>(vp.w/2, vp.w - m_sprite->width()/2);
m_offset_y = std::max<int>(vp.h/2, vp.h - m_sprite->height()/2);
sz.w = (m_sprite->width() << m_zoom) + m_offset_x*2;
sz.h = (m_sprite->height() << m_zoom) + m_offset_y*2;
sz.w = m_zoom.apply(m_sprite->width()) + m_offset_x*2;
sz.h = m_zoom.apply(m_sprite->height()) + m_offset_y*2;
}
else {
sz.w = 4;
@ -1311,7 +1311,7 @@ bool Editor::isInsideSelection()
m_document->mask()->containsPoint(spritePos.x, spritePos.y);
}
void Editor::setZoomAndCenterInMouse(int zoom,
void Editor::setZoomAndCenterInMouse(Zoom zoom,
const gfx::Point& mousePos, ZoomBehavior zoomBehavior)
{
View* view = View::getView(this);
@ -1343,8 +1343,8 @@ void Editor::setZoomAndCenterInMouse(int zoom,
mid.y = mousePos.y;
}
spritePos.x = m_offset_x - (mid.x - vp.x) + ((1<<zoom)>>1) + (spritePos.x << zoom);
spritePos.y = m_offset_y - (mid.y - vp.y) + ((1<<zoom)>>1) + (spritePos.y << zoom);
spritePos.x = m_offset_x - (mid.x - vp.x) + (zoom.apply(1)/2) + zoom.apply(spritePos.x);
spritePos.y = m_offset_y - (mid.y - vp.y) + (zoom.apply(1)/2) + zoom.apply(spritePos.y);
if ((m_zoom != zoom) || (m_cursorEditor != mid)) {
bool blit_valid_rgn = (m_zoom == zoom);

View File

@ -27,16 +27,14 @@
#include "app/ui/editor/editor_observers.h"
#include "app/ui/editor/editor_state.h"
#include "app/ui/editor/editor_states_history.h"
#include "app/zoom.h"
#include "base/connection.h"
#include "gfx/fwd.h"
#include "doc/frame_number.h"
#include "gfx/fwd.h"
#include "ui/base.h"
#include "ui/timer.h"
#include "ui/widget.h"
#define MIN_ZOOM 0
#define MAX_ZOOM 5
namespace doc {
class Sprite;
class Layer;
@ -122,18 +120,18 @@ namespace app {
void setLayer(const Layer* layer);
void setFrame(FrameNumber frame);
int zoom() const { return m_zoom; }
const Zoom& zoom() const { return m_zoom; }
int offsetX() const { return m_offset_x; }
int offsetY() const { return m_offset_y; }
int cursorThick() { return m_cursorThick; }
void setZoom(int zoom) { m_zoom = zoom; }
void setZoom(Zoom zoom) { m_zoom = zoom; }
void setOffsetX(int x) { m_offset_x = x; }
void setOffsetY(int y) { m_offset_y = y; }
void setDefaultScroll();
void setEditorScroll(const gfx::Point& scroll, bool blit_valid_rgn);
void setEditorZoom(int zoom);
void setEditorZoom(Zoom zoom);
// Updates the Editor's view.
void updateEditor();
@ -187,7 +185,7 @@ namespace app {
// Returns true if the cursor is inside the active mask/selection.
bool isInsideSelection();
void setZoomAndCenterInMouse(int zoom,
void setZoomAndCenterInMouse(Zoom zoom,
const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
void pasteImage(const Image* image, const gfx::Point& pos);
@ -260,7 +258,7 @@ namespace app {
Sprite* m_sprite; // Active sprite in the editor
Layer* m_layer; // Active layer in the editor
FrameNumber m_frame; // Active frame in the editor
int m_zoom; // Zoom in the editor
Zoom m_zoom; // Zoom in the editor
// Drawing cursor
int m_cursorThick;

View File

@ -179,10 +179,10 @@ void SelectBoxState::preRenderDecorator(EditorPreRender* render)
void SelectBoxState::postRenderDecorator(EditorPostRender* render)
{
Editor* editor = render->getEditor();
int zoom = editor->zoom();
Zoom zoom = editor->zoom();
gfx::Rect vp = View::getView(editor)->getViewportBounds();
vp.w += 1<<zoom;
vp.h += 1<<zoom;
vp.w += zoom.apply(1);
vp.h += zoom.apply(1);
vp = editor->screenToEditor(vp);
// Paint a grid generated by the box

View File

@ -360,10 +360,20 @@ bool StandbyState::onMouseWheel(Editor* editor, MouseMessage* msg)
case WHEEL_ZOOM: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
int zoom = MID(MIN_ZOOM, editor->zoom()-dz, MAX_ZOOM);
if (editor->zoom() != zoom)
Zoom zoom = editor->zoom();
if (dz < 0) {
while (dz++ < 0)
zoom.in();
}
else {
while (dz-- > 0)
zoom.out();
}
if (editor->zoom() != zoom) {
editor->setZoomAndCenterInMouse(zoom,
mouseMsg->position(), Editor::kDontCenterOnZoom);
}
break;
}

View File

@ -223,6 +223,7 @@ public:
bool useMask() override { return m_useMask; }
Mask* getMask() override { return m_mask; }
gfx::Point getMaskOrigin() override { return m_maskOrigin; }
const Zoom& zoom() override { return m_editor->zoom(); }
ToolLoop::Button getMouseButton() override { return m_button; }
int getPrimaryColor() override { return m_primary_color; }
void setPrimaryColor(int color) override { m_primary_color = color; }

View File

@ -196,8 +196,8 @@ gfx::Rect TransformHandles::getPivotHandleBounds(Editor* editor,
she::Surface* part = theme->get_part(PART_PIVOT_HANDLE);
gfx::Point screenPivotPos = editor->editorToScreen(transform.pivot());
screenPivotPos.x += (1 << editor->zoom()) / 2;
screenPivotPos.y += (1 << editor->zoom()) / 2;
screenPivotPos.x += editor->zoom().apply(1) / 2;
screenPivotPos.y += editor->zoom().apply(1) / 2;
return gfx::Rect(
screenPivotPos.x-part->width()/2,

View File

@ -53,12 +53,12 @@ bool ZoomingState::onMouseDown(Editor* editor, MouseMessage* msg)
bool ZoomingState::onMouseUp(Editor* editor, MouseMessage* msg)
{
int zoom = editor->zoom();
Zoom zoom = editor->zoom();
if (msg->left() && zoom < 5)
++zoom;
else if (msg->right() && zoom > 0)
--zoom;
if (msg->left())
zoom.in();
else if (msg->right())
zoom.out();
editor->setZoomAndCenterInMouse(zoom,
msg->position(), Editor::kCofiguredZoomBehavior);

View File

@ -245,7 +245,7 @@ void MiniEditorWindow::updateUsingEditor(Editor* editor)
addChild(m_docView);
miniEditor = m_docView->getEditor();
miniEditor->setZoom(0);
miniEditor->setZoom(Zoom(1, 1));
miniEditor->setState(EditorStatePtr(new EditorState));
layout();
}

View File

@ -115,9 +115,8 @@ public:
};
template<class DstTraits, class SrcTraits>
static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
int x, int y, int opacity,
int blend_mode, int zoom)
static void merge_zoomed_image_scale_up(Image* dst, const Image* src, const Palette* pal,
int x, int y, int opacity, int blend_mode, Zoom zoom)
{
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
int src_x, src_y, src_w, src_h;
@ -126,8 +125,8 @@ static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
int first_box_w, first_box_h;
int line_h, bottom;
box_w = 1<<zoom;
box_h = 1<<zoom;
box_w = zoom.apply(1);
box_h = zoom.apply(1);
src_x = 0;
src_y = 0;
@ -136,13 +135,13 @@ static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
dst_x = x;
dst_y = y;
dst_w = src->width()<<zoom;
dst_h = src->height()<<zoom;
dst_w = zoom.apply(src->width());
dst_h = zoom.apply(src->height());
// clipping...
if (dst_x < 0) {
src_x += (-dst_x)>>zoom;
src_w -= (-dst_x)>>zoom;
src_x += zoom.remove(-dst_x);
src_w -= zoom.remove(-dst_x);
dst_w -= (-dst_x);
first_box_w = box_w - ((-dst_x) % box_w);
dst_x = 0;
@ -151,8 +150,8 @@ static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
first_box_w = 0;
if (dst_y < 0) {
src_y += (-dst_y)>>zoom;
src_h -= (-dst_y)>>zoom;
src_y += zoom.remove(-dst_y);
src_h -= zoom.remove(-dst_y);
dst_h -= (-dst_y);
first_box_h = box_h - ((-dst_y) % box_h);
dst_y = 0;
@ -161,12 +160,12 @@ static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
first_box_h = 0;
if (dst_x+dst_w > dst->width()) {
src_w -= (dst_x+dst_w-dst->width()) >> zoom;
src_w -= zoom.remove(dst_x+dst_w-dst->width());
dst_w = dst->width() - dst_x;
}
if (dst_y+dst_h > dst->height()) {
src_h -= (dst_y+dst_h-dst->height()) >> zoom;
src_h -= zoom.remove(dst_y+dst_h-dst->height());
dst_h = dst->height() - dst_y;
}
@ -279,6 +278,107 @@ done_with_line:;
done_with_blit:;
}
template<class DstTraits, class SrcTraits>
static void merge_zoomed_image_scale_down(Image* dst, const Image* src, const Palette* pal,
int x, int y, int opacity, int blend_mode, Zoom zoom)
{
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
int src_x, src_y, src_w, src_h;
int dst_x, dst_y, dst_w, dst_h;
int unbox_w, unbox_h;
int bottom;
unbox_w = zoom.remove(1);
unbox_h = zoom.remove(1);
src_x = 0;
src_y = 0;
src_w = src->width();
src_h = src->height();
dst_x = x;
dst_y = y;
dst_w = zoom.apply(src->width());
dst_h = zoom.apply(src->height());
// clipping...
if (dst_x < 0) {
src_x += zoom.remove(-dst_x);
src_w -= zoom.remove(-dst_x);
dst_w -= (-dst_x);
dst_x = 0;
}
if (dst_y < 0) {
src_y += zoom.remove(-dst_y);
src_h -= zoom.remove(-dst_y);
dst_h -= (-dst_y);
dst_y = 0;
}
if (dst_x+dst_w > dst->width()) {
src_w -= zoom.remove(dst_x+dst_w-dst->width());
dst_w = dst->width() - dst_x;
}
if (dst_y+dst_h > dst->height()) {
src_h -= zoom.remove(dst_y+dst_h-dst->height());
dst_h = dst->height() - dst_y;
}
src_w = zoom.remove(zoom.apply(src_w));
src_h = zoom.remove(zoom.apply(src_h));
if ((src_w <= 0) || (src_h <= 0) ||
(dst_w <= 0) || (dst_h <= 0))
return;
bottom = dst_y+dst_h-1;
// Lock all necessary bits
const LockImageBits<SrcTraits> srcBits(src, gfx::Rect(src_x, src_y, src_w, src_h));
LockImageBits<DstTraits> dstBits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
typename LockImageBits<SrcTraits>::const_iterator src_it = srcBits.begin();
typename LockImageBits<SrcTraits>::const_iterator src_end = srcBits.end();
typename LockImageBits<DstTraits>::iterator dst_it, dst_end;
// For each line to draw of the source image...
for (y=0; y<src_h; y+=unbox_h) {
dst_it = dstBits.begin_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
dst_end = dstBits.end_area(gfx::Rect(dst_x, dst_y, dst_w, 1));
for (x=0; x<src_w; x+=unbox_w) {
ASSERT(src_it >= srcBits.begin() && src_it < src_end);
ASSERT(dst_it >= dstBits.begin() && dst_it < dst_end);
blender(*dst_it, *dst_it, *src_it, opacity);
// Skip source pixels
for (int delta=0; delta < unbox_w && src_it != src_end; ++delta)
++src_it;
++dst_it;
}
if (++dst_y > bottom)
break;
// Skip lines
for (int delta=0; delta < src_w * (unbox_h-1) && src_it != src_end; ++delta)
++src_it;
}
}
template<class DstTraits, class SrcTraits>
static void merge_zoomed_image(Image* dst, const Image* src, const Palette* pal,
int x, int y, int opacity, int blend_mode, Zoom zoom)
{
if (zoom.scale() >= 1.0)
merge_zoomed_image_scale_up<DstTraits, SrcTraits>(dst, src, pal, x, y, opacity, blend_mode, zoom);
else
merge_zoomed_image_scale_down<DstTraits, SrcTraits>(dst, src, pal, x, y, opacity, blend_mode, zoom);
}
//////////////////////////////////////////////////////////////////////
// Render Engine
@ -380,15 +480,15 @@ void RenderEngine::setPreviewImage(const Layer* layer, FrameNumber frame, Image*
in a new image and return it.
Positions source_x, source_y, width and height must have the
zoom applied (sorce_x<<zoom, source_y<<zoom, width<<zoom, etc.)
zoom applied (zoom.apply(sorce_x), zoom.apply(source_y), zoom.apply(width), etc.)
*/
Image* RenderEngine::renderSprite(int source_x, int source_y,
int width, int height,
FrameNumber frame, int zoom,
FrameNumber frame, Zoom zoom,
bool draw_tiled_bg,
bool enable_onionskin)
{
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, int);
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom);
const LayerImage* background = m_sprite->backgroundLayer();
bool need_checked_bg = (background != NULL ? !background->isVisible(): true);
uint32_t bg_color = 0;
@ -471,7 +571,7 @@ Image* RenderEngine::renderSprite(int source_x, int source_y,
// static
void RenderEngine::renderCheckedBackground(Image* image,
int source_x, int source_y,
int zoom)
Zoom zoom)
{
int x, y, u, v;
int tile_w = 16;
@ -504,13 +604,13 @@ void RenderEngine::renderCheckedBackground(Image* image,
}
if (checked_bg_zoom) {
tile_w <<= zoom;
tile_h <<= zoom;
tile_w = zoom.apply(tile_w);
tile_h = zoom.apply(tile_h);
}
// Tile size
if (tile_w < (1<<zoom)) tile_w = (1<<zoom);
if (tile_h < (1<<zoom)) tile_h = (1<<zoom);
if (tile_w < zoom.apply(1)) tile_w = zoom.apply(1);
if (tile_h < zoom.apply(1)) tile_h = zoom.apply(1);
// Tile position (u,v) is the number of tile we start in (source_x,source_y) coordinate
u = (source_x / tile_w);
@ -535,9 +635,9 @@ void RenderEngine::renderCheckedBackground(Image* image,
// static
void RenderEngine::renderImage(Image* rgb_image, Image* src_image, const Palette* pal,
int x, int y, int zoom)
int x, int y, Zoom zoom)
{
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, int);
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom);
ASSERT(rgb_image->pixelFormat() == IMAGE_RGB && "renderImage accepts RGB destination images only");
@ -566,8 +666,8 @@ void RenderEngine::renderLayer(
const Layer* layer,
Image *image,
int source_x, int source_y,
FrameNumber frame, int zoom,
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, int),
FrameNumber frame, Zoom zoom,
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom),
bool render_background,
bool render_transparent,
int blend_mode)
@ -607,8 +707,8 @@ void RenderEngine::renderLayer(
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
(*zoomed_func)(image, src_image, m_sprite->getPalette(frame),
(cel->x() << zoom) - source_x,
(cel->y() << zoom) - source_y,
zoom.apply(cel->x()) - source_x,
zoom.apply(cel->y()) - source_y,
output_opacity,
(blend_mode < 0 ?
static_cast<const LayerImage*>(layer)->getBlendMode():
@ -645,8 +745,8 @@ void RenderEngine::renderLayer(
Image* extraImage = m_document->getExtraCelImage();
(*zoomed_func)(image, extraImage, m_sprite->getPalette(frame),
(extraCel->x() << zoom) - source_x,
(extraCel->y() << zoom) - source_y,
zoom.apply(extraCel->x()) - source_x,
zoom.apply(extraCel->y()) - source_y,
extraCel->opacity(),
m_document->getExtraCelBlendMode(), zoom);
}

View File

@ -21,6 +21,7 @@
#pragma once
#include "app/color.h"
#include "app/zoom.h"
#include "doc/frame_number.h"
namespace doc {
@ -70,7 +71,7 @@ namespace app {
Image* renderSprite(int source_x, int source_y,
int width, int height,
FrameNumber frame, int zoom,
FrameNumber frame, Zoom zoom,
bool draw_tiled_bg,
bool enable_onionskin);
@ -79,18 +80,18 @@ namespace app {
static void renderCheckedBackground(Image* image,
int source_x, int source_y,
int zoom);
Zoom zoom);
static void renderImage(Image* rgb_image, Image* src_image, const Palette* pal,
int x, int y, int zoom);
int x, int y, Zoom zoom);
private:
void renderLayer(
const Layer* layer,
Image* image,
int source_x, int source_y,
FrameNumber frame, int zoom,
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, int),
FrameNumber frame, Zoom zoom,
void (*zoomed_func)(Image*, const Image*, const Palette*, int, int, int, int, Zoom),
bool render_background,
bool render_transparent,
int blend_mode);

47
src/app/zoom.cpp Normal file
View File

@ -0,0 +1,47 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/zoom.h"
namespace app {
void Zoom::in()
{
if (m_den > 1) {
m_den--;
}
else if (m_num < 64) {
m_num++;
}
}
void Zoom::out()
{
if (m_num > 1) {
m_num--;
}
else if (m_den < 32) {
m_den++;
}
}
} // namespace app

57
src/app/zoom.h Normal file
View File

@ -0,0 +1,57 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_ZOOM_H_INCLUDED
#define APP_ZOOM_H_INCLUDED
#pragma once
namespace app {
class Zoom {
public:
Zoom(int num, int den)
: m_num(num), m_den(den) {
}
double scale() const { return static_cast<double>(m_num) / static_cast<double>(m_den); }
int apply(int x) const { return x * m_num / m_den; }
int remove(int x) const { return x * m_den / m_num; }
double apply(double x) const { return x * m_num / m_den; }
double remove(double x) const { return x * m_den / m_num; }
void in();
void out();
bool operator==(const Zoom& other) const {
return m_num == other.m_num && m_den == other.m_den;
}
bool operator!=(const Zoom& other) const {
return !operator==(other);
}
private:
int m_num;
int m_den;
};
} // namespace app
#endif // APP_ZOOM_H_INCLUDED