* Copyright 2003-2006, Haiku, Inc. All rights reserved.
* Copyright 2004-2005 yellowTAB GmbH. All Rights Reserved.
* Copyright 2006 Bernd Korz. All Rights Reserved
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Pfeiffer, laplace@haiku-os.org
* Ryan Leavengood, leavengood@gmail.com
* yellowTAB GmbH
* Bernd Korz
*/
#include <scheduler.h>
#include <Debug.h>
#include <Screen.h>
#include <syscalls.h>
#include "Filter.h"
FilterThread::FilterThread(Filter* filter, int32 i, int32 n,
bool runInCurrentThread)
:
fFilter(filter),
fI(i),
fN(n)
{
if (runInCurrentThread)
Run();
else {
thread_id tid;
tid = spawn_thread(worker_thread, "filter",
suggest_thread_priority(B_STATUS_RENDERING), this);
if (tid >= 0)
resume_thread(tid);
else
delete this;
}
}
FilterThread::~FilterThread()
{
fFilter->FilterThreadDone();
}
status_t
FilterThread::worker_thread(void* data)
{
FilterThread* thread = (FilterThread*)data;
return thread->Run();
}
status_t
FilterThread::Run()
{
if (fI == 0) {
BBitmap* bm;
bm = fFilter->GetBitmap();
if (bm == NULL) {
fFilter->FilterThreadInitFailed();
return B_ERROR;
}
for (int32 i = fI + 1; i < fN; i ++) {
new FilterThread(fFilter, i, fN);
}
}
if (fFilter->GetBitmap())
fFilter->Run(fI, fN);
delete this;
return B_OK;
}
Filter::Filter(BBitmap* image, BMessenger listener, uint32 what)
:
fListener(listener),
fWhat(what),
fStarted(false),
fN(0),
fNumberOfThreads(0),
fIsRunning(false),
fSrcImage(image),
fDestImageInitialized(false),
fDestImage(NULL)
{
fCPUCount = NumberOfActiveCPUs();
fWaitForThreads = create_sem(0, "wait_for_threads");
#if TIME_FILTER
fStopWatch = NULL;
#endif
}
Filter::~Filter()
{
delete fDestImage;
delete_sem(fWaitForThreads);
}
BBitmap*
Filter::GetBitmap()
{
if (!fDestImageInitialized) {
fDestImageInitialized = true;
fDestImage = CreateDestImage(fSrcImage);
}
return fDestImage;
}
BBitmap*
Filter::DetachBitmap()
{
BBitmap* image = fDestImage;
fDestImage = NULL;
return image;
}
void
Filter::Start(bool async)
{
if (fStarted || fSrcImage == NULL) return;
#if TIME_FILTER
fStopWatch = new BStopWatch("Filter Time");
#endif
fN = NumberOfThreads();
fNumberOfThreads = fN;
fIsRunning = true;
fStarted = true;
new FilterThread(this, 0, fN, !async);
if (!async)
Wait();
}
void
Filter::Wait()
{
if (fStarted) {
while (acquire_sem_etc(fWaitForThreads, fN, 0, 0) == B_INTERRUPTED);
fStarted = false;
}
}
void
Filter::Stop()
{
fIsRunning = false;
Wait();
}
bool
Filter::IsRunning() const
{
return fIsRunning;
}
void
Filter::Completed()
{
}
void
Filter::FilterThreadDone()
{
if (atomic_add(&fNumberOfThreads, -1) == 1) {
#if TIME_FILTER
delete fStopWatch; fStopWatch = NULL;
#endif
Completed();
if (fIsRunning)
fListener.SendMessage(fWhat);
fIsRunning = false;
}
release_sem(fWaitForThreads);
}
void
Filter::FilterThreadInitFailed()
{
ASSERT(fNumberOfThreads == fN);
fNumberOfThreads = 0;
Completed();
fIsRunning = false;
release_sem_etc(fWaitForThreads, fN, 0);
}
bool
Filter::IsBitmapValid(BBitmap* bitmap) const
{
return bitmap != NULL && bitmap->InitCheck() == B_OK && bitmap->IsValid();
}
int32
Filter::NumberOfThreads()
{
const int32 units = GetNumberOfUnits();
int32 n;
n = units / 32;
if (n > CPUCount())
n = CPUCount();
else if (n <= 0)
n = 1;
return n;
}
BBitmap*
Filter::GetSrcImage()
{
return fSrcImage;
}
BBitmap*
Filter::GetDestImage()
{
return fDestImage;
}
int32
Filter::NumberOfActiveCPUs() const
{
int count;
system_info info;
get_system_info(&info);
count = info.cpu_count;
int32 cpuCount = 0;
for (int i = 0; i < count; i ++) {
if (_kern_cpu_enabled(i))
cpuCount++;
}
if (cpuCount == 0)
cpuCount = 1;
return cpuCount;
}
Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what,
bool dither)
:
Filter(image, listener, what),
fScaledImage(NULL),
fRect(rect),
fDither(dither)
{
}
Scaler::~Scaler()
{
if (GetDestImage() != fScaledImage) {
delete fScaledImage;
fScaledImage = NULL;
}
}
BBitmap*
Scaler::CreateDestImage(BBitmap* srcImage)
{
if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32
&& srcImage->ColorSpace() != B_RGBA32))
return NULL;
BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
BBitmap* destImage = new BBitmap(dest,
fDither ? B_CMAP8 : srcImage->ColorSpace());
if (!IsBitmapValid(destImage)) {
delete destImage;
return NULL;
}
if (fDither)
{
BRect dest_rect(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
fScaledImage = new BBitmap(dest_rect, srcImage->ColorSpace());
if (!IsBitmapValid(fScaledImage)) {
delete destImage;
delete fScaledImage;
fScaledImage = NULL;
return NULL;
}
} else
fScaledImage = destImage;
return destImage;
}
bool
Scaler::Matches(BRect rect, bool dither) const
{
return fRect.IntegerWidth() == rect.IntegerWidth()
&& fRect.IntegerHeight() == rect.IntegerHeight()
&& fDither == dither;
}
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 = fScaledImage;
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;
if (destH == 0)
row = 0;
else
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);
destData[3] = static_cast<uchar>(
(a[3] * a0 + b[3] * a1) * alpha0 +
(c[3] * a0 + d[3] * a1) * alpha1);
}
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);
destData[3] = static_cast<uchar>(a[3] * alpha0 + c[3] * 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);
destData[3] = static_cast<uchar>(a[3] * a0 + b[3] * a1);
}
a = srcData + srcW * kBPP;
destData[0] = a[0];
destData[1] = a[1];
destData[2] = a[2];
destData[3] = a[3];
}
}
delete[] columnData;
}
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 = fScaledImage;
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);
cd->alpha0 = kFPOne - cd->alpha1;
}
destDataRow = destBits + fromRow * destBPR;
for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) {
fixed_point row;
intType srcRow;
fixed_point alpha0, alpha1;
if (fpDestH == 0)
row = 0;
else
row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH;
srcRow = from_fixed_point(row);
alpha1 = tail_value(row);
alpha0 = kFPOne - alpha1;
srcData = srcBits + srcRow * srcBPR;
destData = destDataRow;
#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);
destData[3] = I4(3);
}
a = srcData + srcW * kBPP;
c = a + srcBPR;
destData[0] = V2(0);
destData[1] = V2(1);
destData[2] = V2(2);
destData[3] = V2(3);
} 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);
destData[3] = H2(3);
}
a = srcData + srcW * kBPP;
destData[0] = a[0];
destData[1] = a[1];
destData[2] = a[2];
destData[3] = a[3];
}
}
delete[] columnData;
}
void
Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX,
intType toX, const float a0X, const float a1X, const int32 kBPP)
{
sum[0] = a0X * src[0];
sum[1] = a0X * src[1];
sum[2] = a0X * src[2];
src += kBPP;
for (int32 x = fromX + 1; x < toX; x++, src += kBPP) {
sum[0] += src[0];
sum[1] += src[1];
sum[2] += src[2];
}
if (toX <= srcW) {
sum[0] += a1X * src[0];
sum[1] += a1X * src[1];
sum[2] += a1X * src[2];
}
}
typedef struct {
int32 from;
int32 to;
float alpha0;
float alpha1;
} DownScaleColumnData;
void
Scaler::DownScaleBilinear(intType fromRow, int32 toRow)
{
BBitmap* src;
BBitmap* dest;
intType srcW, srcH;
intType destW, destH;
intType x, y;
const uchar* srcBits;
uchar* destBits;
intType srcBPR, destBPR;
const uchar* srcData;
uchar* destDataRow;
uchar* destData;
const int32 kBPP = 4;
DownScaleColumnData* columnData;
src = GetSrcImage();
dest = fScaledImage;
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();
destDataRow = destBits + fromRow * destBPR;
const float deltaX = (srcW + 1.0) / (destW + 1.0);
const float deltaY = (srcH + 1.0) / (destH + 1.0);
const float deltaXY = deltaX * deltaY;
columnData = new DownScaleColumnData[destW + 1];
DownScaleColumnData* cd = columnData;
for (x = 0; x <= destW; x++, cd++) {
const float fFromX = x * deltaX;
const float fToX = fFromX + deltaX;
cd->from = (intType)fFromX;
cd->to = (intType)fToX;
cd->alpha0 = 1.0 - (fFromX - cd->from);
cd->alpha1 = fToX - cd->to;
}
for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
const float fFromY = y * deltaY;
const float fToY = fFromY + deltaY;
const intType fromY = (intType)fFromY;
const intType toY = (intType)fToY;
const float a0Y = 1.0 - (fFromY - fromY);
const float a1Y = fToY - toY;
const uchar* srcDataRow = srcBits + fromY * srcBPR;
destData = destDataRow;
cd = columnData;
for (x = 0; x <= destW; x++, destData += kBPP, cd++) {
const intType fromX = cd->from;
const intType toX = cd->to;
const float a0X = cd->alpha0;
const float a1X = cd->alpha1;
srcData = srcDataRow + fromX * kBPP;
float totalSum[3];
float sum[3];
RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
totalSum[0] = a0Y * sum[0];
totalSum[1] = a0Y * sum[1];
totalSum[2] = a0Y * sum[2];
srcData += srcBPR;
for (int32 r = fromY + 1; r < toY; r++, srcData += srcBPR) {
RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
totalSum[0] += sum[0];
totalSum[1] += sum[1];
totalSum[2] += sum[2];
}
if (toY <= srcH) {
RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
totalSum[0] += a1Y * sum[0];
totalSum[1] += a1Y * sum[1];
totalSum[2] += a1Y * sum[2];
}
destData[0] = static_cast<uchar>(totalSum[0] / deltaXY);
destData[1] = static_cast<uchar>(totalSum[1] / deltaXY);
destData[2] = static_cast<uchar>(totalSum[2] / deltaXY);
}
}
delete[] columnData;
}
typedef struct {
intType error[3];
} DitheringColumnData;
uchar
Scaler::Limit(intType value)
{
if (value < 0) {
value = 0;
} else if (value > 255) {
value = 255;
}
return value;
}
void
Scaler::Dither(int32 fromRow, int32 toRow)
{
BBitmap* src;
BBitmap* dest;
intType destW;
intType x, y;
uchar* srcBits;
intType srcBPR;
uchar* srcDataRow;
uchar* srcData;
uchar* destBits;
intType destBPR;
uchar* destDataRow;
uchar* destData;
const int32 kBPP = 4;
DitheringColumnData* columnData0;
DitheringColumnData* columnData;
DitheringColumnData* cd;
BScreen screen;
intType error[3], err[3];
src = fScaledImage;
dest = GetDestImage();
ASSERT(src->ColorSpace() == B_RGB32 || src->ColorSpace() == B_RGBA32);
ASSERT(dest->ColorSpace() == B_CMAP8);
ASSERT(src->Bounds().IntegerWidth() == dest->Bounds().IntegerWidth());
ASSERT(src->Bounds().IntegerHeight() == dest->Bounds().IntegerHeight());
destW = dest->Bounds().IntegerWidth();
srcBits = (uchar*)src->Bits();
srcBPR = src->BytesPerRow();
destBits = (uchar*)dest->Bits();
destBPR = dest->BytesPerRow();
columnData0 = new DitheringColumnData[destW + 3];
columnData = columnData0 + 1;
cd = columnData;
for (x = destW; x >= 0; x --, cd++) {
cd->error[0] = cd->error[1] = cd->error[2] = 0;
}
srcDataRow = srcBits + fromRow * srcBPR;
destDataRow = destBits + fromRow * destBPR;
for (y = fromRow; IsRunning() && y <= toRow; y++, srcDataRow += srcBPR,
destDataRow += destBPR) {
error[0] = error[1] = error[2] = 0;
srcData = srcDataRow;
destData = destDataRow;
for (x = 0; x <= destW; x ++, srcData += kBPP, destData += 1) {
rgb_color color, actualColor;
uint8 index;
color.red = Limit(srcData[2] + error[0] / 16);
color.green = Limit(srcData[1] + error[1] / 16);
color.blue = Limit(srcData[0] + error[2] / 16);
color.alpha = UINT8_MAX;
index = screen.IndexForColor(color);
actualColor = screen.ColorForIndex(index);
*destData = index;
err[0] = color.red - actualColor.red;
err[1] = color.green - actualColor.green;
err[2] = color.blue - actualColor.blue;
cd = &columnData[x + 1];
error[0] = cd->error[0] + 7 * err[0];
error[1] = cd->error[1] + 7 * err[1];
error[2] = cd->error[2] + 7 * err[2];
cd->error[0] = err[0];
cd->error[1] = err[1];
cd->error[2] = err[2];
cd--;
cd->error[0] += 5 * err[0];
cd->error[1] += 5 * err[1];
cd->error[2] += 5 * err[2];
cd--;
cd->error[0] += 3 * err[0];
cd->error[1] += 3 * err[1];
cd->error[2] += 3 * err[2];
}
y++;
srcDataRow += srcBPR; destDataRow += destBPR;
if (y > toRow) break;
error[0] = error[1] = error[2] = 0;
srcData = srcDataRow + destW * kBPP;
destData = destDataRow + destW;
for (x = 0; x <= destW; x++, srcData -= kBPP, destData -= 1) {
rgb_color color, actualColor;
uint8 index;
color.red = Limit(srcData[2] + error[0] / 16);
color.green = Limit(srcData[1] + error[1] / 16);
color.blue = Limit(srcData[0] + error[2] / 16);
color.alpha = UINT8_MAX;
index = screen.IndexForColor(color);
actualColor = screen.ColorForIndex(index);
*destData = index;
err[0] = color.red - actualColor.red;
err[1] = color.green - actualColor.green;
err[2] = color.blue - actualColor.blue;
cd = &columnData[x - 1];
error[0] = cd->error[0] + 7 * err[0];
error[1] = cd->error[1] + 7 * err[1];
error[2] = cd->error[2] + 7 * err[2];
cd->error[0] = err[0];
cd->error[1] = err[1];
cd->error[2] = err[2];
cd++;
cd->error[0] += 5 * err[0];
cd->error[1] += 5 * err[1];
cd->error[2] += 5 * err[2];
cd++;
cd->error[0] += 3 * err[0];
cd->error[1] += 3 * err[1];
cd->error[2] += 3 * err[2];
}
}
delete[] columnData0;
}
int32
Scaler::GetNumberOfUnits()
{
return fRect.IntegerHeight() + 1;
}
void
Scaler::Run(int32 i, int32 n)
{
int32 from, to, height, imageHeight;
imageHeight = GetDestImage()->Bounds().IntegerHeight() + 1;
height = imageHeight / n;
from = i * height;
if (i + 1 == n)
to = imageHeight - 1;
else
to = from + height - 1;
if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width())
ScaleBilinearFP(from, to);
else
DownScaleBilinear(from, to);
if (fDither)
Dither(from, to);
}
void
Scaler::Completed()
{
if (GetDestImage() != fScaledImage)
delete fScaledImage;
fScaledImage = NULL;
}
ImageProcessor::ImageProcessor(enum operation op, BBitmap* image,
BMessenger listener, uint32 what)
:
Filter(image, listener, what),
fOp(op),
fBPP(0),
fWidth(0),
fHeight(0),
fSrcBPR(0),
fDestBPR(0)
{
}
BBitmap*
ImageProcessor::CreateDestImage(BBitmap* )
{
color_space cs;
BBitmap* bm;
BRect rect;
if (GetSrcImage() == NULL)
return NULL;
cs = GetSrcImage()->ColorSpace();
fBPP = BytesPerPixel(cs);
if (fBPP < 1)
return NULL;
fWidth = GetSrcImage()->Bounds().IntegerWidth();
fHeight = GetSrcImage()->Bounds().IntegerHeight();
if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise)
rect.Set(0, 0, fHeight, fWidth);
else
rect.Set(0, 0, fWidth, fHeight);
bm = new BBitmap(rect, cs);
if (!IsBitmapValid(bm)) {
delete bm;
return NULL;
}
fSrcBPR = GetSrcImage()->BytesPerRow();
fDestBPR = bm->BytesPerRow();
return bm;
}
int32
ImageProcessor::GetNumberOfUnits()
{
return GetSrcImage()->Bounds().IntegerHeight() + 1;
}
int32
ImageProcessor::BytesPerPixel(color_space cs) const
{
switch (cs) {
case B_RGB32:
case B_RGB32_BIG:
case B_RGBA32:
case B_RGBA32_BIG: return 4;
case B_RGB24_BIG:
case B_RGB24: return 3;
case B_RGB16:
case B_RGB16_BIG:
case B_RGB15:
case B_RGB15_BIG:
case B_RGBA15:
case B_RGBA15_BIG: return 2;
case B_GRAY8:
case B_CMAP8: return 1;
case B_GRAY1: return 0;
default: return -1;
}
}
void
ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY,
const uchar* src, int32 x, int32 y)
{
dest += fDestBPR * destY + destX * fBPP;
src += fSrcBPR * y + x * fBPP;
switch (fBPP) {
case 4:
dest[3] = src[3];
case 3:
dest[2] = src[2];
case 2:
dest[1] = src[1];
case 1:
dest[0] = src[0];
break;
}
}
void
ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src)
{
dest += fDestBPR * y + x * fBPP;
src += fSrcBPR * y + x * fBPP;
switch (fBPP) {
case 4:
case 3:
dest[2] = ~src[2];
case 2:
dest[1] = ~src[1];
case 1:
dest[0] = ~src[0];
break;
}
}
void
ImageProcessor::Run(int32 i, int32 n)
{
int32 from, to;
int32 height = (fHeight + 1) / n;
from = i * height;
if (i + 1 == n)
to = fHeight;
else
to = from + height - 1;
int32 x, y, destX, destY;
const uchar* src = (uchar*)GetSrcImage()->Bits();
uchar* dest = (uchar*)GetDestImage()->Bits();
switch (fOp) {
case kRotateClockwise:
for (y = from; y <= to; y++) {
for (x = 0; x <= fWidth; x++) {
destX = fHeight - y;
destY = x;
CopyPixel(dest, destX, destY, src, x, y);
}
}
break;
case kRotateCounterClockwise:
for (y = from; y <= to; y ++) {
for (x = 0; x <= fWidth; x ++) {
destX = y;
destY = fWidth - x;
CopyPixel(dest, destX, destY, src, x, y);
}
}
break;
case kFlipTopToBottom:
for (y = from; y <= to; y ++) {
for (x = 0; x <= fWidth; x ++) {
destX = x;
destY = fHeight - y;
CopyPixel(dest, destX, destY, src, x, y);
}
}
break;
case kFlipLeftToRight:
for (y = from; y <= to; y ++) {
for (x = 0; x <= fWidth; x ++) {
destX = fWidth - x;
destY = y;
CopyPixel(dest, destX, destY, src, x, y);
}
}
break;
case kInvert:
for (y = from; y <= to; y ++) {
for (x = 0; x <= fWidth; x ++) {
InvertPixel(x, y, dest, src);
}
}
break;
}
}