* Fixed another pet peeve of mine: you can now always zoom, no matter if you

have enabled the shrink/stretch option.
* Renamed "zoom to window" to "stretch to window".
* Readded the shrink/stretch options to the full screen context menu only.
* Renamed GetShrinkToBounds() to ShrinkToBounds() as it's a simple getter.
* Changed how and when the shrink/stretch options come into play. Shrinking is
  now only set once, when the image is first shown.
* Added snapping in ZoomIn(), and ZoomOut(): they will now always choose 1.0,
  and the "fit to window" zoom level when they are close.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39129 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2010-10-24 16:36:27 +00:00
parent 062c606d62
commit 4fd570d307
5 changed files with 151 additions and 111 deletions

View File

@ -38,7 +38,7 @@ enum {
MSG_FILE_NEXT = 'mFLN',
MSG_FILE_PREV = 'mFLP',
MSG_SHRINK_TO_WINDOW = 'mSTW',
MSG_ZOOM_TO_WINDOW = 'mZTW',
MSG_STRETCH_TO_WINDOW = 'mZTW',
MSG_ROTATE_90 = 'mR90',
MSG_ROTATE_270 = 'mR27',
MSG_FLIP_LEFT_TO_RIGHT = 'mFLR',

View File

@ -197,8 +197,8 @@ ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode,
fBitmapLocationInView(0.0, 0.0),
fShrinkToBounds(true),
fZoomToBounds(true),
fShrinkOrZoomToBounds(true),
fStretchToBounds(false),
fFitToBoundsZoom(1.0),
fFullScreen(false),
fScrollingBitmap(false),
fCreatingSelection(false),
@ -218,7 +218,7 @@ ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode,
settings = my_app->Settings();
if (settings->Lock()) {
fShrinkToBounds = settings->GetBool("ShrinkToBounds", fShrinkToBounds);
fZoomToBounds = settings->GetBool("ZoomToBounds", fZoomToBounds);
fStretchToBounds = settings->GetBool("ZoomToBounds", fStretchToBounds);
fSlideShowDelay = settings->GetInt32("SlideShowDelay", fSlideShowDelay);
fScaleBilinear = settings->GetBool("ScaleBilinear", fScaleBilinear);
settings->Unlock();
@ -382,7 +382,7 @@ ShowImageView::_DeleteSelectionBitmap()
status_t
ShowImageView::SetImage(const entry_ref *ref)
ShowImageView::SetImage(const entry_ref* ref)
{
// If no file was specified, load the specified page of
// the current file.
@ -502,7 +502,9 @@ ShowImageView::SetImage(const entry_ref *ref)
fCaption << ", " << fDocumentIndex << "/" << fDocumentCount;
fCaption << ", " << fImageType;
fZoom = 1.0;
fFitToBoundsZoom = _FitToBoundsZoom();
ResetZoom();
be_roster->AddToRecentDocuments(&fCurrentRef, kApplicationSignature);
@ -571,20 +573,20 @@ ShowImageView::SetShrinkToBounds(bool enable)
if (fShrinkToBounds != enable) {
_SettingsSetBool("ShrinkToBounds", enable);
fShrinkToBounds = enable;
FixupScrollBars();
Invalidate();
if (enable)
SetZoom(fFitToBoundsZoom);
}
}
void
ShowImageView::SetZoomToBounds(bool enable)
ShowImageView::SetStretchToBounds(bool enable)
{
if (fZoomToBounds != enable) {
if (fStretchToBounds != enable) {
_SettingsSetBool("ZoomToBounds", enable);
fZoomToBounds = enable;
FixupScrollBars();
Invalidate();
fStretchToBounds = enable;
if (enable)
SetZoom(fFitToBoundsZoom);
}
}
@ -641,6 +643,7 @@ ShowImageView::SetScaleBilinear(bool enabled)
void
ShowImageView::AttachedToWindow()
{
ResetZoom();
fUndo.SetWindow(Window());
FixupScrollBars();
@ -656,6 +659,42 @@ ShowImageView::DetachedFromWindow()
}
bool
ShowImageView::_ShouldShrink() const
{
return fShrinkToBounds && fBitmap->Bounds().Width() > Bounds().Width()
&& fBitmap->Bounds().Height() > Bounds().Height();
}
bool
ShowImageView::_ShouldStretch() const
{
return fStretchToBounds && fBitmap->Bounds().Width() < Bounds().Width()
&& fBitmap->Bounds().Height() < Bounds().Height();
}
float
ShowImageView::_FitToBoundsZoom() const
{
// the width/height of the bitmap (in pixels)
float bitmapWidth = fBitmap->Bounds().Width() + 1;
float bitmapHeight = fBitmap->Bounds().Height() + 1;
// the available width/height for layouting the bitmap (in pixels)
float width = Bounds().Width() + 1;
float height = Bounds().Height() + 1;
float zoom = width / bitmapWidth;
if (zoom * bitmapHeight <= height)
return zoom;
return height / bitmapHeight;
}
BRect
ShowImageView::_AlignBitmap()
{
@ -672,54 +711,25 @@ ShowImageView::_AlignBitmap()
if (width == 0 || height == 0)
return rect;
fShrinkOrZoomToBounds = (fShrinkToBounds
&& (bitmapWidth >= width || bitmapHeight >= height))
|| (fZoomToBounds && (bitmapWidth < width && bitmapHeight < height));
if (fShrinkOrZoomToBounds) {
float s = width / bitmapWidth;
// zoom image
rect.right = floorf(bitmapWidth * fZoom) - 1;
rect.bottom = floorf(bitmapHeight * fZoom) - 1;
if (s * bitmapHeight <= height) {
rect.right = width - 1;
rect.bottom = static_cast<int>(s * bitmapHeight) - 1;
// center vertically
rect.OffsetBy(0, static_cast<int>((height - rect.Height()) / 2));
} else {
s = height / bitmapHeight;
rect.right = static_cast<int>(s * bitmapWidth) - 1;
rect.bottom = height - 1;
// center horizontally
rect.OffsetBy(static_cast<int>((width - rect.Width()) / 2), 0);
}
} else {
// zoom image
rect.right = floorf(bitmapWidth * fZoom) - 1;
rect.bottom = floorf(bitmapHeight * fZoom) - 1;
// update the bitmap size after the zoom
bitmapWidth = rect.Width() + 1.0;
bitmapHeight = rect.Height() + 1.0;
// update the bitmap size after the zoom
bitmapWidth = rect.Width() + 1.0;
bitmapHeight = rect.Height() + 1.0;
// always align in the center if the bitmap is smaller than the window
if (width > bitmapWidth)
rect.OffsetBy(floorf((width - bitmapWidth) / 2.0), 0);
// always align in the center
if (width > bitmapWidth)
rect.OffsetBy(floorf((width - bitmapWidth) / 2.0), 0);
if (height > bitmapHeight)
rect.OffsetBy(0, floorf((height - bitmapHeight) / 2.0));
}
if (height > bitmapHeight)
rect.OffsetBy(0, floorf((height - bitmapHeight) / 2.0));
return rect;
}
void
ShowImageView::_Setup(BRect rect)
{
fBitmapLocationInView.x = floorf(rect.left);
fBitmapLocationInView.y = floorf(rect.top);
fZoom = (rect.Width() + 1.0) / (fBitmap->Bounds().Width() + 1.0);
}
void
ShowImageView::_DrawBackground(BRect border)
{
@ -825,7 +835,8 @@ ShowImageView::Draw(BRect updateRect)
}
BRect rect = _AlignBitmap();
_Setup(rect);
fBitmapLocationInView.x = floorf(rect.left);
fBitmapLocationInView.y = floorf(rect.top);
_DrawBackground(rect);
_DrawImage(rect);
@ -847,9 +858,10 @@ ShowImageView::Draw(BRect updateRect)
void
ShowImageView::FrameResized(float /* width */, float /* height */)
ShowImageView::FrameResized(float /*width*/, float /*height*/)
{
FixupScrollBars();
fFitToBoundsZoom = _FitToBoundsZoom();
SetZoom(_ShouldStretch() ? fFitToBoundsZoom : fZoom);
}
@ -1910,8 +1922,19 @@ ShowImageView::_FirstFile()
void
ShowImageView::SetZoom(BPoint where, float zoom)
ShowImageView::SetZoom(float zoom, BPoint where)
{
if (zoom > 32)
zoom = 32;
if (zoom < fFitToBoundsZoom / 2)
zoom = fFitToBoundsZoom / 2;
if (zoom == fZoom) {
// window size might have changed
FixupScrollBars();
return;
}
// Invalidate before scrolling, as that prevents the app_server
// to do the scrolling server side
Invalidate();
@ -1941,16 +1964,48 @@ ShowImageView::SetZoom(BPoint where, float zoom)
void
ShowImageView::ZoomIn(BPoint where)
{
if (fZoom < 16)
SetZoom(where, fZoom * 1.2);
// snap zoom to "fit to bounds", and "original size"
float zoom = fZoom * 1.2;
float zoomSnap = fZoom * 1.25;
if (fZoom < fFitToBoundsZoom && zoomSnap > fFitToBoundsZoom)
zoom = fFitToBoundsZoom;
if (fZoom < 1.0 && zoomSnap > 1.0)
zoom = 1.0;
SetZoom(zoom, where);
}
void
ShowImageView::ZoomOut(BPoint where)
{
if (fZoom > 0.25)
SetZoom(where, fZoom * 0.8);
// snap zoom to "fit to bounds", and "original size"
float zoom = fZoom / 1.2;
float zoomSnap = fZoom / 1.25;
if (fZoom > fFitToBoundsZoom && zoomSnap < fFitToBoundsZoom)
zoom = fFitToBoundsZoom;
if (fZoom > 1.0 && zoomSnap < 1.0)
zoom = 1.0;
SetZoom(zoom, where);
}
/*! Resets the zoom to what it should be when opening an image, depending
on the current settings.
*/
void
ShowImageView::ResetZoom()
{
if (fBitmap == NULL)
return;
fFitToBoundsZoom = _FitToBoundsZoom();
if (_ShouldShrink() || _ShouldStretch())
SetZoom(fFitToBoundsZoom);
else
SetZoom(1.0);
}
@ -2006,10 +2061,10 @@ ShowImageView::_DoImageOperation(ImageProcessor::operation op, bool quiet)
// update orientation state
if (op != ImageProcessor::kInvert) {
// Note: If one of these fails, check its definition in class ImageProcessor.
ASSERT(ImageProcessor::kRotateClockwise < ImageProcessor::kNumberOfAffineTransformations);
ASSERT(ImageProcessor::kRotateCounterClockwise < ImageProcessor::kNumberOfAffineTransformations);
ASSERT(ImageProcessor::kFlipLeftToRight < ImageProcessor::kNumberOfAffineTransformations);
ASSERT(ImageProcessor::kFlipTopToBottom < ImageProcessor::kNumberOfAffineTransformations);
// ASSERT(ImageProcessor::kRotateClockwise < ImageProcessor::kNumberOfAffineTransformations);
// ASSERT(ImageProcessor::kRotateCounterClockwise < ImageProcessor::kNumberOfAffineTransformations);
// ASSERT(ImageProcessor::kFlipLeftToRight < ImageProcessor::kNumberOfAffineTransformations);
// ASSERT(ImageProcessor::kFlipTopToBottom < ImageProcessor::kNumberOfAffineTransformations);
fImageOrientation = fTransformation[op][fImageOrientation];
}

View File

@ -70,11 +70,11 @@ public:
bool GetScaleBilinear() { return fScaleBilinear; }
void SetShowCaption(bool show);
void SetShrinkToBounds(bool enable);
bool GetShrinkToBounds() const
bool ShrinkToBounds() const
{ return fShrinkToBounds; }
void SetZoomToBounds(bool enable);
bool GetZoomToBounds() const
{ return fZoomToBounds; }
void SetStretchToBounds(bool enable);
bool StretchToBounds() const
{ return fStretchToBounds; }
void SetFullScreen(bool fullScreen);
BBitmap* GetBitmap();
@ -111,9 +111,11 @@ public:
bool SlideShowStarted() const { return fSlideShow; }
void StartSlideShow();
void StopSlideShow();
void SetZoom(BPoint where, float zoom);
void SetZoom(float zoom,
BPoint where = BPoint(-1, -1));
void ZoomIn(BPoint where = BPoint(-1, -1));
void ZoomOut(BPoint where = BPoint(-1, -1));
void ResetZoom();
// Image manipulation
void Rotate(int degree); // 90 and 270 only
@ -155,21 +157,16 @@ private:
void _DeleteBitmap();
void _DeleteSelectionBitmap();
int32 _BytesPerPixel(color_space cs) const;
void _CopyPixel(uchar* dest, int32 destX,
int32 destY, int32 destBPR, uchar* src,
int32 x, int32 y, int32 bpr, int32 bpp);
void _InvertPixel(int32 x, int32 y, uchar* dest,
int32 destBPR, uchar* src, int32 bpr,
int32 bpp);
void _DoImageOperation(
enum ImageProcessor::operation op,
bool quiet = false);
void _UserDoImageOperation(
enum ImageProcessor::operation op,
bool quiet = false);
bool _ShouldShrink() const;
bool _ShouldStretch() const;
float _FitToBoundsZoom() const;
BRect _AlignBitmap();
void _Setup(BRect r);
bool _IsImage(const entry_ref* pref);
static int _CompareEntries(const void* a, const void* b);
void _FreeEntries(BList* entries);
@ -235,8 +232,8 @@ private:
BPoint fBitmapLocationInView;
bool fShrinkToBounds;
bool fZoomToBounds;
bool fShrinkOrZoomToBounds;
bool fStretchToBounds;
float fFitToBoundsZoom;
bool fFullScreen;
bool fScrollingBitmap;
bool fCreatingSelection;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
* Copyright 2003-2010, Haiku, Inc. All Rights Reserved.
* Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd.
* Copyright 2006 Bernd Korz. All Rights Reserved
* Distributed under the terms of the MIT License.
@ -12,6 +12,7 @@
* Bernd Korz
*/
#include "ShowImageWindow.h"
#include <new>
@ -213,6 +214,7 @@ ShowImageWindow::ShowImageWindow(const entry_ref* ref,
// every 1/10 second; ShowImageView needs it for marching ants
WindowRedimension(fImageView->GetBitmap());
fImageView->ResetZoom();
fImageView->MakeFocus(true); // to receive KeyDown messages
Show();
@ -292,7 +294,7 @@ ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu)
menu->AddSeparatorItem();
if (!popupMenu) {
if (!popupMenu || fFullScreen) {
_AddItemMenu(menu, B_TRANSLATE("High-quality zooming"),
MSG_SCALE_BILINEAR, 0, 0, this);
@ -300,8 +302,8 @@ ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu)
_AddItemMenu(menu, B_TRANSLATE("Shrink to window"),
MSG_SHRINK_TO_WINDOW, 0, 0, this);
_AddItemMenu(menu, B_TRANSLATE("Zoom to window"),
MSG_ZOOM_TO_WINDOW, 0, 0, this);
_AddItemMenu(menu, B_TRANSLATE("Stretch to window"),
MSG_STRETCH_TO_WINDOW, 0, 0, this);
menu->AddSeparatorItem();
}
@ -315,18 +317,8 @@ ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu)
_MarkMenuItem(menu, MSG_SHOW_CAPTION, fShowCaption);
_MarkMenuItem(menu, MSG_SCALE_BILINEAR, fImageView->GetScaleBilinear());
bool shrink, zoom, enabled;
shrink = fImageView->GetShrinkToBounds();
zoom = fImageView->GetZoomToBounds();
_MarkMenuItem(menu, MSG_SHRINK_TO_WINDOW, shrink);
_MarkMenuItem(menu, MSG_ZOOM_TO_WINDOW, zoom);
enabled = !(shrink || zoom);
_EnableMenuItem(menu, MSG_ORIGINAL_SIZE, enabled);
_EnableMenuItem(menu, MSG_ZOOM_IN, enabled);
_EnableMenuItem(menu, MSG_ZOOM_OUT, enabled);
_MarkMenuItem(menu, MSG_SHRINK_TO_WINDOW, fImageView->ShrinkToBounds());
_MarkMenuItem(menu, MSG_STRETCH_TO_WINDOW, fImageView->StretchToBounds());
if (popupMenu) {
menu->AddSeparatorItem();
@ -361,7 +353,7 @@ ShowImageWindow::AddMenus(BMenuBar* bar)
MSG_PREPARE_PRINT, 'P', 0, this);
menu->AddSeparatorItem();
_AddItemMenu(menu, B_TRANSLATE("About ShowImage" B_UTF8_ELLIPSIS),
B_ABOUT_REQUESTED, 0, 0, be_app);
B_ABOUT_REQUESTED, 0, 0, be_app);
menu->AddSeparatorItem();
_AddItemMenu(menu, B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q', 0, be_app);
bar->AddItem(menu);
@ -418,9 +410,10 @@ ShowImageWindow::AddMenus(BMenuBar* bar)
BMenuItem*
ShowImageWindow::_AddItemMenu(BMenu* menu, const char* label, uint32 what,
const char shortcut, uint32 modifier, const BHandler* target, bool enabled)
char shortcut, uint32 modifier, const BHandler* target, bool enabled)
{
BMenuItem* item = new BMenuItem(label, new BMessage(what), shortcut, modifier);
BMenuItem* item = new BMenuItem(label, new BMessage(what), shortcut,
modifier);
menu->AddItem(item);
item->SetTarget(target);
@ -553,12 +546,7 @@ ShowImageWindow::_ResizeToWindow(bool shrink, uint32 what)
if (shrink)
fImageView->SetShrinkToBounds(enabled);
else
fImageView->SetZoomToBounds(enabled);
enabled = !(fImageView->GetShrinkToBounds() || fImageView->GetZoomToBounds());
_EnableMenuItem(fBar, MSG_ORIGINAL_SIZE, enabled);
_EnableMenuItem(fBar, MSG_ZOOM_IN, enabled);
_EnableMenuItem(fBar, MSG_ZOOM_OUT, enabled);
fImageView->SetStretchToBounds(enabled);
}
@ -657,8 +645,8 @@ ShowImageWindow::MessageReceived(BMessage* message)
if (messageProvidesSize) {
_UpdateResizerWindow(fWidth, fHeight);
if (!fImageView->GetZoomToBounds()
&& !fImageView->GetShrinkToBounds()
if (!fImageView->StretchToBounds()
&& !fImageView->ShrinkToBounds()
&& !fFullScreen)
WindowRedimension(fImageView->GetBitmap());
}
@ -766,7 +754,7 @@ ShowImageWindow::MessageReceived(BMessage* message)
_ResizeToWindow(true, message->what);
break;
case MSG_ZOOM_TO_WINDOW:
case MSG_STRETCH_TO_WINDOW:
_ResizeToWindow(false, message->what);
break;
@ -866,7 +854,7 @@ ShowImageWindow::MessageReceived(BMessage* message)
break;
case MSG_ORIGINAL_SIZE:
fImageView->SetZoom(BPoint(-1, -1), 1.0);
fImageView->SetZoom(1.0);
break;
case MSG_SCALE_BILINEAR:

View File

@ -48,8 +48,8 @@ private:
void _BuildViewMenu(BMenu* menu, bool popupMenu);
BMenuItem* _AddItemMenu(BMenu* menu, const char* label,
uint32 what, const char shortcut,
uint32 modifier, const BHandler* target,
uint32 what, char shortcut, uint32 modifier,
const BHandler* target,
bool enabled = true);
BMenuItem* _AddDelayItem(BMenu* menu, const char* label,
float value);