- 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:
+374
-31
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user