- Rewrote bilinear scaling algorithm. The version that uses fixed point calculations is at least two times faster than the version that uses floating point calculations!

- Fixed off by one bugs.
- Replace "Fit to Window" with  "Shrink to Window" and "Zoom to Window".


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5409 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer
2003-11-18 21:43:44 +00:00
parent 545237514a
commit 19c76de4a0
7 changed files with 572 additions and 134 deletions
+374 -31
View File
@@ -28,21 +28,62 @@
#include <scheduler.h>
#include "Filter.h"
#include "Scale.h"
// Implementation of FilterThread
FilterThread::FilterThread(Filter* filter, int i)
: fWorkerThread(-1)
, fFilter(filter)
, fI(i)
{
fWorkerThread = spawn_thread(worker_thread, "filter", suggest_thread_priority(B_STATUS_RENDERING), this);
if (fWorkerThread >= 0) {
resume_thread(fWorkerThread);
}
}
FilterThread::~FilterThread()
{
fFilter->Done();
}
status_t
FilterThread::worker_thread(void* data)
{
FilterThread* thread = (FilterThread*)data;
return thread->Run();
}
status_t
FilterThread::Run()
{
fFilter->Run(fI, fFilter->CPUCount());
delete this;
return B_OK;
}
// Implementation of Filter
Filter::Filter(BBitmap* image, BMessenger listener, uint32 what)
: fListener(listener)
, fWhat(what)
, fStarted(false)
, fNumberOfThreads(0)
, fIsRunning(false)
, fWorkerThread(-1)
, fSrcImage(image)
, fDestImage(NULL)
{
system_info info;
get_system_info(&info);
fCPUCount = info.cpu_count;
fWaitForThreads = create_sem(0, "wait_for_threads");
#if TIME_FILTER
fStopWatch = NULL;
#endif
}
Filter::~Filter()
{
delete fDestImage;
delete_sem(fWaitForThreads);
}
BBitmap*
@@ -54,40 +95,54 @@ Filter::GetBitmap()
return fDestImage;
}
status_t
Filter::worker_thread(void* data)
{
Filter* filter = (Filter*)data;
filter->Run();
if (filter->fIsRunning) {
filter->fListener.SendMessage(filter->fWhat);
filter->fIsRunning = false;
}
return B_OK;
}
void
Filter::Start()
{
GetBitmap();
if (fSrcImage == NULL || fDestImage == NULL) return;
fWorkerThread = spawn_thread(worker_thread, "filter", suggest_thread_priority(B_STATUS_RENDERING), this);
if (fWorkerThread >= 0) {
fIsRunning = true;
resume_thread(fWorkerThread);
}
if (fStarted || fSrcImage == NULL || fDestImage == NULL) return;
#if TIME_FILTER
fStopWatch = new BStopWatch("Filter Time");
#endif
fNumberOfThreads = fCPUCount;
fIsRunning = true;
fStarted = true;
// start filter threads
for (int i = 0; i < fCPUCount; i ++) {
new FilterThread(this, i);
}
}
void
Filter::Stop()
{
if (fIsRunning) {
if (fStarted) {
// tell FilterThreads to stop calculations
fIsRunning = false;
status_t st;
wait_for_thread(fWorkerThread, &st);
// wait for threads to exit
acquire_sem_etc(fWaitForThreads, fCPUCount, 0, 0);
// ready to start again
fStarted = false;
}
}
void
Filter::Done()
{
if (atomic_add(&fNumberOfThreads, -1) == 1) {
#if TIME_FILTER
delete fStopWatch; fStopWatch = NULL;
#endif
if (fIsRunning) {
fListener.SendMessage(fWhat);
}
fIsRunning = false;
}
release_sem(fWaitForThreads);
}
bool
Filter::IsRunning() const
{
@@ -106,9 +161,10 @@ Filter::GetDestImage()
return fDestImage;
}
Scaler::Scaler(BBitmap* image, float scale, BMessenger listener, uint32 what)
// Implementation of (bilinear) Scaler
Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what)
: Filter(image, listener, what)
, fScale(scale)
, fRect(rect)
{
}
@@ -120,14 +176,301 @@ BBitmap*
Scaler::CreateDestImage(BBitmap* srcImage)
{
if (srcImage == NULL || srcImage->ColorSpace() != B_RGB32 && srcImage->ColorSpace() !=B_RGBA32) return NULL;
BRect src(srcImage->Bounds());
BRect dest(0, 0, (src.Width()+1)*fScale - 1, (src.Height()+1)*fScale - 1);
BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
BBitmap* destImage = new BBitmap(dest, srcImage->ColorSpace());
return destImage;
}
void
Scaler::Run()
{
scale(GetSrcImage(), GetDestImage(), IsRunningAddr(), fScale, fScale);
bool
Scaler::Matches(BRect rect) const {
return fRect.IntegerWidth() == rect.IntegerWidth() &&
fRect.IntegerHeight() == rect.IntegerHeight();
}
// Scale bilinear using floating point calculations
typedef int32 intType;
typedef struct {
intType srcColumn;
float alpha0;
float alpha1;
} ColumnData;
void
Scaler::ScaleBilinear(intType fromRow, int32 toRow)
{
BBitmap* src;
BBitmap* dest;
intType srcW, srcH;
intType destW, destH;
intType x, y, i;
ColumnData* columnData;
ColumnData* cd;
const uchar* srcBits;
uchar* destBits;
intType srcBPR, destBPR;
const uchar* srcData;
uchar* destDataRow;
uchar* destData;
const int32 kBPP = 4;
src = GetSrcImage();
dest = GetDestImage();
srcW = src->Bounds().IntegerWidth();
srcH = src->Bounds().IntegerHeight();
destW = dest->Bounds().IntegerWidth();
destH = dest->Bounds().IntegerHeight();
srcBits = (uchar*)src->Bits();
destBits = (uchar*)dest->Bits();
srcBPR = src->BytesPerRow();
destBPR = dest->BytesPerRow();
columnData = new ColumnData[destW];
cd = columnData;
for (i = 0; i < destW; i ++, cd++) {
float column = (float)i * (float)srcW / (float)destW;
cd->srcColumn = (intType)column;
cd->alpha1 = column - cd->srcColumn;
cd->alpha0 = 1.0 - cd->alpha1;
}
destDataRow = destBits + fromRow * destBPR;
for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
float row;
intType srcRow;
float alpha0, alpha1;
row = (float)y * (float)srcH / (float)destH;
srcRow = (intType)row;
alpha1 = row - srcRow;
alpha0 = 1.0 - alpha1;
srcData = srcBits + srcRow * srcBPR;
destData = destDataRow;
if (y < destH) {
float a0, a1;
const uchar *a, *b, *c, *d;
for (x = 0; x < destW; x ++, destData += kBPP) {
a = srcData + columnData[x].srcColumn * kBPP;
b = a + kBPP;
c = a + srcBPR;
d = c + kBPP;
a0 = columnData[x].alpha0;
a1 = columnData[x].alpha1;
destData[0] = static_cast<uchar>(
(a[0] * a0 + b[0] * a1) * alpha0 +
(c[0] * a0 + d[0] * a1) * alpha1);
destData[1] = static_cast<uchar>(
(a[1] * a0 + b[1] * a1) * alpha0 +
(c[1] * a0 + d[1] * a1) * alpha1);
destData[2] = static_cast<uchar>(
(a[2] * a0 + b[2] * a1) * alpha0 +
(c[2] * a0 + d[2] * a1) * alpha1);
}
// right column
a = srcData + srcW * kBPP;
c = a + srcBPR;
destData[0] = static_cast<uchar>(a[0] * alpha0 + c[0] * alpha1);
destData[1] = static_cast<uchar>(a[1] * alpha0 + c[1] * alpha1);
destData[2] = static_cast<uchar>(a[2] * alpha0 + c[2] * alpha1);
} else {
float a0, a1;
const uchar *a, *b;
for (x = 0; x < destW; x ++, destData += kBPP) {
a = srcData + columnData[x].srcColumn * kBPP;
b = a + kBPP;
a0 = columnData[x].alpha0;
a1 = columnData[x].alpha1;
destData[0] = static_cast<uchar>(a[0] * a0 + b[0] * a1);
destData[1] = static_cast<uchar>(a[1] * a0 + b[1] * a1);
destData[2] = static_cast<uchar>(a[2] * a0 + b[2] * a1);
}
// bottom, right pixel
a = srcData + srcW * kBPP;
destData[0] = a[0];
destData[1] = a[1];
destData[2] = a[2];
}
}
delete[] columnData;
}
// Scale bilinear using fixed point calculations
// Is already more than two times faster than floating point version
// on AMD Athlon 1 GHz and Intel Pentium III 866 MHz.
typedef int64 long_fixed_point;
typedef int32 fixed_point;
// Could use shift operator instead of multiplication and division,
// but compiler will optimize it for use anyway.
#define to_fixed_point(number) static_cast<fixed_point>((number) * kFPPrecisionFactor)
#define from_fixed_point(number) ((number) / kFPPrecisionFactor)
#define to_float(number) from_fixed_point(static_cast<float>(number))
#define int_value(number) ((number) & kFPInverseMask)
#define tail_value(number) ((number) & kFPPrecisionMask)
// Has to be called after muliplication of two fixed point values
#define mult_correction(number) ((number) / kFPPrecisionFactor)
const int32 kFPPrecision = 8; // (32-kFPPrecision).kFPPrecision
const int32 kFPPrecisionFactor = (1 << kFPPrecision);
const int32 kFPPrecisionMask = ((kFPPrecisionFactor)-1);
const int32 kFPInverseMask = (~kFPPrecisionMask);
const int32 kFPOne = to_fixed_point(1);
typedef struct {
int32 srcColumn;
fixed_point alpha0;
fixed_point alpha1;
} ColumnDataFP;
void
Scaler::ScaleBilinearFP(intType fromRow, int32 toRow)
{
BBitmap* src;
BBitmap* dest;
intType srcW, srcH;
intType destW, destH;
intType x, y, i;
ColumnDataFP* columnData;
ColumnDataFP* cd;
const uchar* srcBits;
uchar* destBits;
intType srcBPR, destBPR;
const uchar* srcData;
uchar* destDataRow;
uchar* destData;
const int32 kBPP = 4;
src = GetSrcImage();
dest = GetDestImage();
srcW = src->Bounds().IntegerWidth();
srcH = src->Bounds().IntegerHeight();
destW = dest->Bounds().IntegerWidth();
destH = dest->Bounds().IntegerHeight();
srcBits = (uchar*)src->Bits();
destBits = (uchar*)dest->Bits();
srcBPR = src->BytesPerRow();
destBPR = dest->BytesPerRow();
fixed_point fpSrcW = to_fixed_point(srcW);
fixed_point fpDestW = to_fixed_point(destW);
fixed_point fpSrcH = to_fixed_point(srcH);
fixed_point fpDestH = to_fixed_point(destH);
columnData = new ColumnDataFP[destW];
cd = columnData;
for (i = 0; i < destW; i ++, cd++) {
fixed_point column = to_fixed_point(i) * (long_fixed_point)fpSrcW / fpDestW;
cd->srcColumn = from_fixed_point(column);
cd->alpha1 = tail_value(column); // weigth for left pixel value
cd->alpha0 = kFPOne - cd->alpha1; // weigth for right pixel value
}
destDataRow = destBits + fromRow * destBPR;
for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
fixed_point row;
intType srcRow;
fixed_point alpha0, alpha1;
row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH;
srcRow = from_fixed_point(row);
alpha1 = tail_value(row); // weight for row y+1
alpha0 = kFPOne - alpha1; // weight for row y
srcData = srcBits + srcRow * srcBPR;
destData = destDataRow;
// Need mult_correction for "outer" multiplication only
#define I4(i) from_fixed_point(mult_correction(\
(a[i] * a0 + b[i] * a1) * alpha0 + \
(c[i] * a0 + d[i] * a1) * alpha1))
#define V2(i) from_fixed_point(a[i] * alpha0 + c[i] * alpha1);
#define H2(i) from_fixed_point(a[i] * a0 + b[i] * a1);
if (y < destH) {
fixed_point a0, a1;
const uchar *a, *b, *c, *d;
for (x = 0; x < destW; x ++, destData += kBPP) {
a = srcData + columnData[x].srcColumn * kBPP;
b = a + kBPP;
c = a + srcBPR;
d = c + kBPP;
a0 = columnData[x].alpha0;
a1 = columnData[x].alpha1;
destData[0] = I4(0);
destData[1] = I4(1);
destData[2] = I4(2);
}
// right column
a = srcData + srcW * kBPP;
c = a + srcBPR;
destData[0] = V2(0);
destData[1] = V2(1);
destData[2] = V2(2);
} else {
fixed_point a0, a1;
const uchar *a, *b;
for (x = 0; x < destW; x ++, destData += kBPP) {
a = srcData + columnData[x].srcColumn * kBPP;
b = a + kBPP;
a0 = columnData[x].alpha0;
a1 = columnData[x].alpha1;
destData[0] = H2(0);
destData[1] = H2(1);
destData[2] = H2(2);
}
// bottom, right pixel
a = srcData + srcW * kBPP;
destData[0] = a[0];
destData[1] = a[1];
destData[2] = a[2];
}
}
delete[] columnData;
}
void
Scaler::Run(int i, int n)
{
int32 from, to, height;
height = (GetDestImage()->Bounds().IntegerHeight()+1)/n;
from = i * height;
if (i+1 == n) {
to = (int32)GetDestImage()->Bounds().bottom;
} else {
to = from + height - 1;
}
ScaleBilinearFP(from, to);
}
+62 -8
View File
@@ -32,21 +32,66 @@
#include <OS.h>
#include <Bitmap.h>
#include <Messenger.h>
#include <StopWatch.h>
#define TIME_FILTER 0
class Filter;
// Used by class Filter
class FilterThread {
public:
FilterThread(Filter* filter, int i);
~FilterThread();
private:
status_t Run();
static status_t worker_thread(void* data);
thread_id fWorkerThread;
Filter* fFilter;
int fI;
};
class Filter {
public:
// The filter uses the input "image" as source image
// for an operation executed in Run() method which
// writes into the destination image, that can be
// retrieve using GetBitmap() method.
// GetBitmap() can be called any time, but it
// may contain "invalid" pixels.
// To start the operation Start() method has to
// be called. The operation is executed in as many
// threads as CPUs are active.
// IsRunning() is true as long as there are any
// threads running.
// The operation is complete when IsRunning() is false
// and Stop() has not been called.
// To abort an operation Stop() method has to
// be called. Stop() has to be called after Start().
// When the operation is done (and has not been aborted).
// Then listener receives a message with the specified "what" value.
Filter(BBitmap* image, BMessenger listener, uint32 what);
virtual ~Filter();
// The bitmap the filter writes into
BBitmap* GetBitmap();
// Starts one or more FilterThreads
void Start();
// Has to be called after Start() (even if IsRunning() is false)
void Stop();
// Are there any running FilterThreads?
bool IsRunning() const;
volatile bool *IsRunningAddr() { return &fIsRunning; }
// To be implemented by inherited class
virtual BBitmap* CreateDestImage(BBitmap* srcImage) = 0;
virtual void Run() = 0;
// Should calculate part i of n of the image. i starts with zero
virtual void Run(int i, int n) = 0;
// Used by FilterThread only!
void Done();
int32 CPUCount() const { return fCPUCount; }
protected:
BBitmap* GetSrcImage();
@@ -55,23 +100,32 @@ protected:
private:
BMessenger fListener;
uint32 fWhat;
static status_t worker_thread(void* data);
int32 fCPUCount;
bool fStarted;
sem_id fWaitForThreads;
volatile int32 fNumberOfThreads;
volatile bool fIsRunning;
thread_id fWorkerThread;
BBitmap* fSrcImage;
BBitmap* fDestImage;
#if TIME_FILTER
BStopWatch* fStopWatch;
#endif
};
class Scaler : public Filter {
public:
Scaler(BBitmap* image, float scale, BMessenger listener, uint32 what);
Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what);
~Scaler();
BBitmap* CreateDestImage(BBitmap* srcImage);
void Run();
float Scale() const { return fScale; }
void Run(int i, int n);
bool Matches(BRect rect) const;
private:
float fScale;
void ScaleBilinear(int32 fromRow, int32 toRow);
void ScaleBilinearFP(int32 fromRow, int32 toRow);
BRect fRect;
};
#endif
+2 -1
View File
@@ -50,7 +50,8 @@ const uint32 MSG_PAGE_PREV = 'mPGP';
const uint32 MSG_GOTO_PAGE = 'mGTP';
const uint32 MSG_FILE_NEXT = 'mFLN';
const uint32 MSG_FILE_PREV = 'mFLP';
const uint32 MSG_FIT_TO_WINDOW_SIZE = 'mFWS';
const uint32 MSG_SHRINK_TO_WINDOW = 'mSTW';
const uint32 MSG_ZOOM_TO_WINDOW = 'mZTW';
const uint32 MSG_ROTATE_90 = 'mR90';
const uint32 MSG_ROTATE_270 = 'mR27';
const uint32 MSG_MIRROR_VERTICAL = 'mMIV';
+71 -58
View File
@@ -159,7 +159,9 @@ ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode,
fDocumentCount = 1;
fAnimateSelection = true;
fbHasSelection = false;
fResizeToViewBounds = false;
fShrinkToBounds = false;
fZoomToBounds = false;
fHasBorder = true;
fHAlignment = B_ALIGN_LEFT;
fVAlignment = B_ALIGN_TOP;
fSlideShow = false;
@@ -317,10 +319,30 @@ ShowImageView::SetShowCaption(bool show)
}
void
ShowImageView::ResizeToViewBounds(bool resize)
ShowImageView::SetShrinkToBounds(bool enable)
{
if (fResizeToViewBounds != resize) {
fResizeToViewBounds = resize;
if (fShrinkToBounds != enable) {
fShrinkToBounds = enable;
FixupScrollBars();
Invalidate();
}
}
void
ShowImageView::SetZoomToBounds(bool enable)
{
if (fZoomToBounds != enable) {
fZoomToBounds = enable;
FixupScrollBars();
Invalidate();
}
}
void
ShowImageView::SetBorder(bool hasBorder)
{
if (fHasBorder != hasBorder) {
fHasBorder = hasBorder;
FixupScrollBars();
Invalidate();
}
@@ -402,29 +424,27 @@ ShowImageView::AlignBitmap()
width = Bounds().Width()-2*PEN_SIZE+1;
height = Bounds().Height()-2*PEN_SIZE+1;
if (width == 0 || height == 0) return rect;
if (fResizeToViewBounds) {
if (fShrinkToBounds && (rect.Width() >= Bounds().Width() || rect.Height() >= Bounds().Height()) ||
fZoomToBounds && rect.Width() < Bounds().Width() && rect.Height() < Bounds().Height()) {
float s;
s = width / (rect.Width()+1);
s = width / (rect.Width()+1.0);
if (s * rect.Height() <= height) {
// XXX temporary solution, fZoom should not be changed here
fZoom = s;
if (s * (rect.Height()+1.0) <= height) {
rect.right = width-1;
rect.bottom = static_cast<int>(s * (rect.Height()+1))-1;
rect.bottom = static_cast<int>(s * (rect.Height()+1.0))-1;
// center vertically
rect.OffsetBy(0, (height - rect.Height()) / 2);
} else {
// XXX temporary solution, fZoom should not be changed here
fZoom = height / (rect.Height()+1);
rect.right = static_cast<int>(fZoom * (rect.Width()+1))-1;
s = height / (rect.Height()+1.0);
rect.right = static_cast<int>(s * (rect.Width()+1.0))-1;
rect.bottom = height-1;
// center horizontally
rect.OffsetBy((width - rect.Width()) / 2, 0);
}
} else {
// zoom image
rect.right = static_cast<int>((rect.right+1)*fZoom)-1;
rect.bottom = static_cast<int>((rect.bottom+1)*fZoom)-1;
rect.right = static_cast<int>((rect.right+1.0)*fZoom)-1;
rect.bottom = static_cast<int>((rect.bottom+1.0)*fZoom)-1;
// align
switch (fHAlignment) {
case B_ALIGN_CENTER:
@@ -435,7 +455,9 @@ ShowImageView::AlignBitmap()
// fall through
default:
case B_ALIGN_LEFT:
rect.OffsetBy(BORDER_WIDTH, 0);
if (fHasBorder) {
rect.OffsetBy(BORDER_WIDTH, 0);
}
break;
}
switch (fVAlignment) {
@@ -447,7 +469,9 @@ ShowImageView::AlignBitmap()
// fall through
default:
case B_ALIGN_TOP:
rect.OffsetBy(0, BORDER_WIDTH);
if (fHasBorder) {
rect.OffsetBy(0, BORDER_WIDTH);
}
break;
}
}
@@ -460,8 +484,8 @@ ShowImageView::Setup(BRect rect)
{
fLeft = rect.left;
fTop = rect.top;
fScaleX = (rect.Width()+1.0) / (fBitmap->Bounds().Width()+1.0);
fScaleY = (rect.Height()+1.0) / (fBitmap->Bounds().Height()+1.0);
fScaleX = rect.Width() / fBitmap->Bounds().Width();
fScaleY = rect.Height() / fBitmap->Bounds().Height();
}
BPoint
@@ -572,12 +596,12 @@ ShowImageView::EraseCaption()
}
Scaler*
ShowImageView::GetScaler()
ShowImageView::GetScaler(BRect rect)
{
if (fScaler == NULL || fScaler->Scale() != fZoom) {
if (fScaler == NULL || !fScaler->Matches(rect)) {
DeleteScaler();
BMessenger msgr(this, Window());
fScaler = new Scaler(fBitmap, fZoom, msgr, MSG_INVALIDATE);
fScaler = new Scaler(fBitmap, rect, msgr, MSG_INVALIDATE);
fScaler->Start();
}
return fScaler;
@@ -587,7 +611,7 @@ void
ShowImageView::DrawImage(BRect rect)
{
if (fScaleBilinear) {
Scaler* scaler = GetScaler();
Scaler* scaler = GetScaler(rect);
if (scaler != NULL && scaler->GetBitmap() != NULL && !scaler->IsRunning()) {
BBitmap* bitmap = scaler->GetBitmap();
DrawBitmap(bitmap, BPoint(rect.left, rect.top));
@@ -847,10 +871,6 @@ ShowImageView::HandleDrop(BMessage* msg)
sendInMessage = (!saveToFile) && msg->FindString("be:types", &type) == B_OK;
fprintf(stderr, "HandleDrop saveToFile %s, sendInMessage %s\n",
saveToFile ? "yes" : "no",
sendInMessage ? "yes" : "no");
bitmap = CopySelection();
if (bitmap == NULL) return;
@@ -1109,8 +1129,6 @@ ShowImageView::LimitToRange(float v, orientation o, bool absolute)
void
ShowImageView::ScrollRestricted(float x, float y, bool absolute)
{
if (fResizeToViewBounds) return;
if (x != 0) {
x = LimitToRange(x, B_HORIZONTAL, absolute);
}
@@ -1247,46 +1265,41 @@ ShowImageView::MessageReceived(BMessage *pmsg)
}
void
ShowImageView::FixupScrollBars()
ShowImageView::FixupScrollBar(orientation o, float bitmapLength, float viewLength)
{
float prop, range;
BScrollBar *psb;
if (fResizeToViewBounds) {
psb = ScrollBar(B_HORIZONTAL);
if (psb) psb->SetRange(0, 0);
psb = ScrollBar(B_VERTICAL);
if (psb) psb->SetRange(0, 0);
return;
psb = ScrollBar(o);
if (psb) {
if (fHasBorder) {
bitmapLength += BORDER_WIDTH*2;
}
range = bitmapLength - viewLength;
if (range < 0.0) {
range = 0.0;
}
prop = viewLength / bitmapLength;
if (prop > 1.0) {
prop = 1.0;
}
psb->SetRange(0, range);
psb->SetProportion(prop);
psb->SetSteps(10, 100);
}
}
void
ShowImageView::FixupScrollBars()
{
BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0);
if (fBitmap) {
BRect rect(AlignBitmap());
rctbitmap.Set(0, 0, rect.Width(), rect.Height());
}
float prop, range;
psb = ScrollBar(B_HORIZONTAL);
if (psb) {
range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width();
if (range < 0) range = 0;
prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2));
if (prop > 1.0f) prop = 1.0f;
psb->SetRange(0, range);
psb->SetProportion(prop);
psb->SetSteps(10, 100);
}
psb = ScrollBar(B_VERTICAL);
if (psb) {
range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height();
if (range < 0) range = 0;
prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2));
if (prop > 1.0f) prop = 1.0f;
psb->SetRange(0, range);
psb->SetProportion(prop);
psb->SetSteps(10, 100);
}
FixupScrollBar(B_HORIZONTAL, rctbitmap.Width(), rctview.Width());
FixupScrollBar(B_VERTICAL, rctbitmap.Height(), rctview.Height());
}
int32
+28 -21
View File
@@ -47,8 +47,12 @@ public:
void SetImage(const entry_ref *pref);
void SetShowCaption(bool show);
void ResizeToViewBounds(bool resize);
bool GetResizeToViewBounds() const { return fResizeToViewBounds; }
void SetShrinkToBounds(bool enable);
bool GetShrinkToBounds() const { return fShrinkToBounds; }
void SetZoomToBounds(bool enable);
bool GetZoomToBounds() const { return fZoomToBounds; }
void SetBorder(bool hasBorder);
bool HasBorder() const { return fHasBorder; }
void SetAlignment(alignment horizontal, vertical_alignment vertical);
BBitmap *GetBitmap();
void GetName(BString *name);
@@ -67,6 +71,7 @@ public:
virtual void MessageReceived(BMessage *pmsg);
void FixupScrollBar(orientation o, float bitmapLength, float viewLength);
void FixupScrollBars();
int32 CurrentPage();
@@ -148,7 +153,7 @@ private:
void DrawCaption();
void EraseCaption();
void DrawSelectionBox(BRect &rect);
Scaler* GetScaler();
Scaler* GetScaler(BRect rect);
void DrawImage(BRect rect);
float LimitToRange(float v, orientation o, bool absolute);
void ScrollRestricted(float x, float y, bool absolute);
@@ -157,22 +162,24 @@ private:
void MouseWheelChanged(BMessage* msg);
void ShowPopUpMenu(BPoint screen);
entry_ref fCurrentRef;
int32 fDocumentIndex;
int32 fDocumentCount;
BBitmap *fBitmap;
BBitmap *fSelBitmap;
float fZoom;
bool fScaleBilinear;
Scaler* fScaler;
bool fResizeToViewBounds;
alignment fHAlignment;
vertical_alignment fVAlignment;
entry_ref fCurrentRef; // of the image
int32 fDocumentIndex; // of the image in the file
int32 fDocumentCount; // number of images in the file
BBitmap *fBitmap; // to be displayed
BBitmap *fSelBitmap; // the bitmap in the selection
float fZoom; // factor to be used to display the image
bool fScaleBilinear; // use bilinear scaling?
Scaler* fScaler; // holds the scaled image if bilinear scaling is enabled
bool fShrinkToBounds; // shrink images to view bounds that are larger than the view
bool fZoomToBounds; // zoom images to view bounds that are smaller than the view
bool fHasBorder; // should the image have a border?
alignment fHAlignment; // horizontal alignment (left and centered only)
vertical_alignment fVAlignment; // vertical alignment (left and centered only)
float fLeft; // the origin of the image in the view
float fTop;
float fScaleX;
float fScaleX; // to convert image from/to view coordinates
float fScaleY;
bool fMovesImage;
bool fMovesImage; // is the image being moved with the mouse
bool fMakesSelection; // is a selection being made
BPoint fFirstPoint; // first point in image space of selection
bool fAnimateSelection; // marching ants
@@ -182,12 +189,12 @@ private:
// the portion of the background bitmap the selection is made from
pattern fPatternUp, fPatternDown, fPatternLeft, fPatternRight;
bool fSlideShow;
int fSlideShowDelay;
int fSlideShowCountDown;
bool fSlideShow; // is slide show enabled?
int fSlideShowDelay; // in pulse rate units
int fSlideShowCountDown; // shows next image if it reaches zero
bool fShowCaption;
BString fCaption;
bool fShowCaption; // display caption?
BString fCaption; // caption text
};
#endif /* _ShowImageView_h */
+34 -15
View File
@@ -212,19 +212,24 @@ ShowImageWindow::BuildViewMenu(BMenu *pmenu)
AddItemMenu(pmenu, "Zoom Out", MSG_ZOOM_OUT, '-', 0, 'W', true);
AddItemMenu(pmenu, "Scale Bilinear", MSG_SCALE_BILINEAR, 0, 0, 'W', true);
pmenu->AddSeparatorItem();
AddItemMenu(pmenu, "Fit To Window Size", MSG_FIT_TO_WINDOW_SIZE, 0, 0, 'W', true);
AddItemMenu(pmenu, "Shrink to Window", MSG_SHRINK_TO_WINDOW, 0, 0, 'W', true);
AddItemMenu(pmenu, "Zoom to Window", MSG_ZOOM_TO_WINDOW, 0, 0, 'W', true);
AddItemMenu(pmenu, "Full Screen", MSG_FULL_SCREEN, B_ENTER, 0, 'W', true);
MarkMenuItem(pmenu, MSG_FULL_SCREEN, fFullScreen);
AddItemMenu(pmenu, "Show Caption in Full Screen Mode", MSG_SHOW_CAPTION, 0, 0, 'W', true);
MarkMenuItem(pmenu, MSG_SHOW_CAPTION, fShowCaption);
if (fpImageView) {
bool shrink, zoom, enabled;
MarkMenuItem(pmenu, MSG_SCALE_BILINEAR, fpImageView->GetScaleBilinear());
bool resize = fpImageView->GetResizeToViewBounds();
MarkMenuItem(pmenu, MSG_FIT_TO_WINDOW_SIZE, resize);
EnableMenuItem(pmenu, MSG_ORIGINAL_SIZE, !resize);
EnableMenuItem(pmenu, MSG_ZOOM_IN, !resize);
EnableMenuItem(pmenu, MSG_ZOOM_OUT, !resize);
shrink = fpImageView->GetShrinkToBounds();
zoom = fpImageView->GetZoomToBounds();
MarkMenuItem(pmenu, MSG_SHRINK_TO_WINDOW, shrink);
MarkMenuItem(pmenu, MSG_ZOOM_TO_WINDOW, zoom);
enabled = !(shrink || zoom);
EnableMenuItem(pmenu, MSG_ORIGINAL_SIZE, enabled);
EnableMenuItem(pmenu, MSG_ZOOM_IN, enabled);
EnableMenuItem(pmenu, MSG_ZOOM_OUT, enabled);
}
}
@@ -413,6 +418,23 @@ ShowImageWindow::MarkSlideShowDelay(float value)
}
}
void
ShowImageWindow::ResizeToWindow(bool shrink, uint32 what)
{
bool enabled;
enabled = ToggleMenuItem(what);
if (shrink) {
fpImageView->SetShrinkToBounds(enabled);
} else {
fpImageView->SetZoomToBounds(enabled);
}
enabled = !(fpImageView->GetShrinkToBounds() || fpImageView->GetZoomToBounds());
EnableMenuItem(fpBar, MSG_ORIGINAL_SIZE, enabled);
EnableMenuItem(fpBar, MSG_ZOOM_IN, enabled);
EnableMenuItem(fpBar, MSG_ZOOM_OUT, enabled);
}
void
ShowImageWindow::MessageReceived(BMessage *pmsg)
{
@@ -571,15 +593,11 @@ ShowImageWindow::MessageReceived(BMessage *pmsg)
ToggleMenuItem(pmsg->what);
break;
case MSG_FIT_TO_WINDOW_SIZE:
{
bool resize;
resize = ToggleMenuItem(pmsg->what);
fpImageView->ResizeToViewBounds(resize);
EnableMenuItem(fpBar, MSG_ORIGINAL_SIZE, !resize);
EnableMenuItem(fpBar, MSG_ZOOM_IN, !resize);
EnableMenuItem(fpBar, MSG_ZOOM_OUT, !resize);
}
case MSG_SHRINK_TO_WINDOW:
ResizeToWindow(true, pmsg->what);
break;
case MSG_ZOOM_TO_WINDOW:
ResizeToWindow(false, pmsg->what);
break;
case MSG_FILE_PREV:
@@ -768,6 +786,7 @@ ShowImageWindow::ToggleFullScreen()
SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE));
fpImageView->SetAlignment(B_ALIGN_LEFT, B_ALIGN_TOP);
}
fpImageView->SetBorder(!fFullScreen);
fpImageView->SetShowCaption(fFullScreen && fShowCaption);
MoveTo(frame.left, frame.top);
ResizeTo(frame.Width(), frame.Height());
+1
View File
@@ -73,6 +73,7 @@ private:
void EnableMenuItem(BMenu *menu, uint32 what, bool enable);
void MarkMenuItem(BMenu *menu, uint32 what, bool marked);
void MarkSlideShowDelay(float value);
void ResizeToWindow(bool shrink, uint32 what);
void SaveAs(BMessage *pmsg);
// Handle Save As submenu choice