BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / showimage / Filter.cpp
blobc8e7fc77356a2332d1e71a3a271ebf3b6519a197
1 /*
2 * Copyright 2003-2006, Haiku, Inc. All rights reserved.
3 * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserved.
4 * Copyright 2006 Bernd Korz. All Rights Reserved
5 * Distributed under the terms of the MIT License.
7 * Authors:
8 * Michael Pfeiffer, laplace@haiku-os.org
9 * Ryan Leavengood, leavengood@gmail.com
10 * yellowTAB GmbH
11 * Bernd Korz
14 #include <scheduler.h>
15 #include <Debug.h>
16 #include <Screen.h>
18 #include <syscalls.h>
20 #include "Filter.h"
23 // Implementation of FilterThread
24 FilterThread::FilterThread(Filter* filter, int32 i, int32 n,
25 bool runInCurrentThread)
27 fFilter(filter),
28 fI(i),
29 fN(n)
31 if (runInCurrentThread)
32 Run();
33 else {
34 thread_id tid;
35 tid = spawn_thread(worker_thread, "filter",
36 suggest_thread_priority(B_STATUS_RENDERING), this);
37 if (tid >= 0)
38 resume_thread(tid);
39 else
40 delete this;
45 FilterThread::~FilterThread()
47 fFilter->FilterThreadDone();
51 status_t
52 FilterThread::worker_thread(void* data)
54 FilterThread* thread = (FilterThread*)data;
55 return thread->Run();
59 status_t
60 FilterThread::Run()
62 if (fI == 0) {
63 BBitmap* bm;
64 // create destination image in first thread
65 bm = fFilter->GetBitmap();
66 if (bm == NULL) {
67 fFilter->FilterThreadInitFailed();
68 return B_ERROR;
70 // and start other filter threads
71 for (int32 i = fI + 1; i < fN; i ++) {
72 new FilterThread(fFilter, i, fN);
75 if (fFilter->GetBitmap())
76 fFilter->Run(fI, fN);
78 delete this;
79 return B_OK;
82 // Implementation of Filter
83 Filter::Filter(BBitmap* image, BMessenger listener, uint32 what)
85 fListener(listener),
86 fWhat(what),
87 fStarted(false),
88 fN(0),
89 fNumberOfThreads(0),
90 fIsRunning(false),
91 fSrcImage(image),
92 fDestImageInitialized(false),
93 fDestImage(NULL)
95 fCPUCount = NumberOfActiveCPUs();
97 fWaitForThreads = create_sem(0, "wait_for_threads");
99 #if TIME_FILTER
100 fStopWatch = NULL;
101 #endif
105 Filter::~Filter()
107 delete fDestImage;
108 delete_sem(fWaitForThreads);
112 BBitmap*
113 Filter::GetBitmap()
115 if (!fDestImageInitialized) {
116 fDestImageInitialized = true;
117 fDestImage = CreateDestImage(fSrcImage);
119 return fDestImage;
123 BBitmap*
124 Filter::DetachBitmap()
126 BBitmap* image = fDestImage;
127 fDestImage = NULL;
128 return image;
132 void
133 Filter::Start(bool async)
135 if (fStarted || fSrcImage == NULL) return;
137 #if TIME_FILTER
138 fStopWatch = new BStopWatch("Filter Time");
139 #endif
141 fN = NumberOfThreads();
142 fNumberOfThreads = fN;
143 fIsRunning = true;
144 fStarted = true;
146 // start first filter thread
147 new FilterThread(this, 0, fN, !async);
149 if (!async)
150 Wait();
154 void
155 Filter::Wait()
157 if (fStarted) {
158 // wait for threads to exit
159 while (acquire_sem_etc(fWaitForThreads, fN, 0, 0) == B_INTERRUPTED);
160 // ready to start again
161 fStarted = false;
166 void
167 Filter::Stop()
169 // tell FilterThreads to stop calculations
170 fIsRunning = false;
171 Wait();
175 bool
176 Filter::IsRunning() const
178 return fIsRunning;
182 void
183 Filter::Completed()
188 void
189 Filter::FilterThreadDone()
191 if (atomic_add(&fNumberOfThreads, -1) == 1) {
192 #if TIME_FILTER
193 delete fStopWatch; fStopWatch = NULL;
194 #endif
195 Completed();
196 if (fIsRunning)
197 fListener.SendMessage(fWhat);
199 fIsRunning = false;
201 release_sem(fWaitForThreads);
205 void
206 Filter::FilterThreadInitFailed()
208 ASSERT(fNumberOfThreads == fN);
209 fNumberOfThreads = 0;
210 Completed();
211 fIsRunning = false;
212 release_sem_etc(fWaitForThreads, fN, 0);
216 bool
217 Filter::IsBitmapValid(BBitmap* bitmap) const
219 return bitmap != NULL && bitmap->InitCheck() == B_OK && bitmap->IsValid();
223 int32
224 Filter::NumberOfThreads()
226 const int32 units = GetNumberOfUnits();
227 int32 n;
228 n = units / 32; // at least 32 units per CPU
229 if (n > CPUCount())
230 n = CPUCount();
231 else if (n <= 0)
232 n = 1; // at least one thread!
234 return n;
238 BBitmap*
239 Filter::GetSrcImage()
241 return fSrcImage;
245 BBitmap*
246 Filter::GetDestImage()
248 return fDestImage;
252 int32
253 Filter::NumberOfActiveCPUs() const
255 int count;
256 system_info info;
257 get_system_info(&info);
258 count = info.cpu_count;
259 int32 cpuCount = 0;
260 for (int i = 0; i < count; i ++) {
261 if (_kern_cpu_enabled(i))
262 cpuCount++;
264 if (cpuCount == 0)
265 cpuCount = 1;
267 return cpuCount;
271 // Implementation of (bilinear) Scaler
272 Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what,
273 bool dither)
275 Filter(image, listener, what),
276 fScaledImage(NULL),
277 fRect(rect),
278 fDither(dither)
283 Scaler::~Scaler()
285 if (GetDestImage() != fScaledImage) {
286 delete fScaledImage;
287 fScaledImage = NULL;
292 BBitmap*
293 Scaler::CreateDestImage(BBitmap* srcImage)
295 if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32
296 && srcImage->ColorSpace() != B_RGBA32))
297 return NULL;
299 BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
300 BBitmap* destImage = new BBitmap(dest,
301 fDither ? B_CMAP8 : srcImage->ColorSpace());
303 if (!IsBitmapValid(destImage)) {
304 delete destImage;
305 return NULL;
308 if (fDither)
310 BRect dest_rect(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
311 fScaledImage = new BBitmap(dest_rect, srcImage->ColorSpace());
312 if (!IsBitmapValid(fScaledImage)) {
313 delete destImage;
314 delete fScaledImage;
315 fScaledImage = NULL;
316 return NULL;
318 } else
319 fScaledImage = destImage;
321 return destImage;
325 bool
326 Scaler::Matches(BRect rect, bool dither) const
328 return fRect.IntegerWidth() == rect.IntegerWidth()
329 && fRect.IntegerHeight() == rect.IntegerHeight()
330 && fDither == dither;
334 // Scale bilinear using floating point calculations
335 typedef struct {
336 intType srcColumn;
337 float alpha0;
338 float alpha1;
339 } ColumnData;
342 void
343 Scaler::ScaleBilinear(intType fromRow, int32 toRow)
345 BBitmap* src;
346 BBitmap* dest;
347 intType srcW, srcH;
348 intType destW, destH;
349 intType x, y, i;
350 ColumnData* columnData;
351 ColumnData* cd;
352 const uchar* srcBits;
353 uchar* destBits;
354 intType srcBPR, destBPR;
355 const uchar* srcData;
356 uchar* destDataRow;
357 uchar* destData;
358 const int32 kBPP = 4;
360 src = GetSrcImage();
361 dest = fScaledImage;
363 srcW = src->Bounds().IntegerWidth();
364 srcH = src->Bounds().IntegerHeight();
365 destW = dest->Bounds().IntegerWidth();
366 destH = dest->Bounds().IntegerHeight();
368 srcBits = (uchar*)src->Bits();
369 destBits = (uchar*)dest->Bits();
370 srcBPR = src->BytesPerRow();
371 destBPR = dest->BytesPerRow();
373 columnData = new ColumnData[destW];
374 cd = columnData;
375 for (i = 0; i < destW; i++, cd++) {
376 float column = (float)i * (float)srcW / (float)destW;
377 cd->srcColumn = (intType)column;
378 cd->alpha1 = column - cd->srcColumn;
379 cd->alpha0 = 1.0 - cd->alpha1;
382 destDataRow = destBits + fromRow * destBPR;
384 for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) {
385 float row;
386 intType srcRow;
387 float alpha0, alpha1;
389 if (destH == 0)
390 row = 0;
391 else
392 row = (float)y * (float)srcH / (float)destH;
394 srcRow = (intType)row;
395 alpha1 = row - srcRow;
396 alpha0 = 1.0 - alpha1;
398 srcData = srcBits + srcRow * srcBPR;
399 destData = destDataRow;
401 if (y < destH) {
402 float a0, a1;
403 const uchar *a, *b, *c, *d;
405 for (x = 0; x < destW; x ++, destData += kBPP) {
406 a = srcData + columnData[x].srcColumn * kBPP;
407 b = a + kBPP;
408 c = a + srcBPR;
409 d = c + kBPP;
411 a0 = columnData[x].alpha0;
412 a1 = columnData[x].alpha1;
414 destData[0] = static_cast<uchar>(
415 (a[0] * a0 + b[0] * a1) * alpha0 +
416 (c[0] * a0 + d[0] * a1) * alpha1);
417 destData[1] = static_cast<uchar>(
418 (a[1] * a0 + b[1] * a1) * alpha0 +
419 (c[1] * a0 + d[1] * a1) * alpha1);
420 destData[2] = static_cast<uchar>(
421 (a[2] * a0 + b[2] * a1) * alpha0 +
422 (c[2] * a0 + d[2] * a1) * alpha1);
423 destData[3] = static_cast<uchar>(
424 (a[3] * a0 + b[3] * a1) * alpha0 +
425 (c[3] * a0 + d[3] * a1) * alpha1);
428 // right column
429 a = srcData + srcW * kBPP;
430 c = a + srcBPR;
432 destData[0] = static_cast<uchar>(a[0] * alpha0 + c[0] * alpha1);
433 destData[1] = static_cast<uchar>(a[1] * alpha0 + c[1] * alpha1);
434 destData[2] = static_cast<uchar>(a[2] * alpha0 + c[2] * alpha1);
435 destData[3] = static_cast<uchar>(a[3] * alpha0 + c[3] * alpha1);
436 } else {
437 float a0, a1;
438 const uchar *a, *b;
439 for (x = 0; x < destW; x ++, destData += kBPP) {
440 a = srcData + columnData[x].srcColumn * kBPP;
441 b = a + kBPP;
443 a0 = columnData[x].alpha0;
444 a1 = columnData[x].alpha1;
446 destData[0] = static_cast<uchar>(a[0] * a0 + b[0] * a1);
447 destData[1] = static_cast<uchar>(a[1] * a0 + b[1] * a1);
448 destData[2] = static_cast<uchar>(a[2] * a0 + b[2] * a1);
449 destData[3] = static_cast<uchar>(a[3] * a0 + b[3] * a1);
452 // bottom, right pixel
453 a = srcData + srcW * kBPP;
455 destData[0] = a[0];
456 destData[1] = a[1];
457 destData[2] = a[2];
458 destData[3] = a[3];
463 delete[] columnData;
467 // Scale bilinear using fixed point calculations
468 // Is already more than two times faster than floating point version
469 // on AMD Athlon 1 GHz and Dual Intel Pentium III 866 MHz.
471 typedef struct {
472 int32 srcColumn;
473 fixed_point alpha0;
474 fixed_point alpha1;
475 } ColumnDataFP;
478 void
479 Scaler::ScaleBilinearFP(intType fromRow, int32 toRow)
481 BBitmap* src;
482 BBitmap* dest;
483 intType srcW, srcH;
484 intType destW, destH;
485 intType x, y, i;
486 ColumnDataFP* columnData;
487 ColumnDataFP* cd;
488 const uchar* srcBits;
489 uchar* destBits;
490 intType srcBPR, destBPR;
491 const uchar* srcData;
492 uchar* destDataRow;
493 uchar* destData;
494 const int32 kBPP = 4;
496 src = GetSrcImage();
497 dest = fScaledImage;
499 srcW = src->Bounds().IntegerWidth();
500 srcH = src->Bounds().IntegerHeight();
501 destW = dest->Bounds().IntegerWidth();
502 destH = dest->Bounds().IntegerHeight();
504 srcBits = (uchar*)src->Bits();
505 destBits = (uchar*)dest->Bits();
506 srcBPR = src->BytesPerRow();
507 destBPR = dest->BytesPerRow();
509 fixed_point fpSrcW = to_fixed_point(srcW);
510 fixed_point fpDestW = to_fixed_point(destW);
511 fixed_point fpSrcH = to_fixed_point(srcH);
512 fixed_point fpDestH = to_fixed_point(destH);
514 columnData = new ColumnDataFP[destW];
515 cd = columnData;
516 for (i = 0; i < destW; i++, cd++) {
517 fixed_point column = to_fixed_point(i) * (long_fixed_point)fpSrcW
518 / fpDestW;
519 cd->srcColumn = from_fixed_point(column);
520 cd->alpha1 = tail_value(column); // weigth for left pixel value
521 cd->alpha0 = kFPOne - cd->alpha1; // weigth for right pixel value
524 destDataRow = destBits + fromRow * destBPR;
526 for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) {
527 fixed_point row;
528 intType srcRow;
529 fixed_point alpha0, alpha1;
531 if (fpDestH == 0)
532 row = 0;
533 else
534 row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH;
536 srcRow = from_fixed_point(row);
537 alpha1 = tail_value(row); // weight for row y + 1
538 alpha0 = kFPOne - alpha1; // weight for row y
540 srcData = srcBits + srcRow * srcBPR;
541 destData = destDataRow;
543 // Need mult_correction for "outer" multiplication only
544 #define I4(i) from_fixed_point(mult_correction(\
545 (a[i] * a0 + b[i] * a1) * alpha0 + \
546 (c[i] * a0 + d[i] * a1) * alpha1))
547 #define V2(i) from_fixed_point(a[i] * alpha0 + c[i] * alpha1);
548 #define H2(i) from_fixed_point(a[i] * a0 + b[i] * a1);
550 if (y < destH) {
551 fixed_point a0, a1;
552 const uchar *a, *b, *c, *d;
554 for (x = 0; x < destW; x ++, destData += kBPP) {
555 a = srcData + columnData[x].srcColumn * kBPP;
556 b = a + kBPP;
557 c = a + srcBPR;
558 d = c + kBPP;
560 a0 = columnData[x].alpha0;
561 a1 = columnData[x].alpha1;
563 destData[0] = I4(0);
564 destData[1] = I4(1);
565 destData[2] = I4(2);
566 destData[3] = I4(3);
569 // right column
570 a = srcData + srcW * kBPP;
571 c = a + srcBPR;
573 destData[0] = V2(0);
574 destData[1] = V2(1);
575 destData[2] = V2(2);
576 destData[3] = V2(3);
577 } else {
578 fixed_point a0, a1;
579 const uchar *a, *b;
580 for (x = 0; x < destW; x ++, destData += kBPP) {
581 a = srcData + columnData[x].srcColumn * kBPP;
582 b = a + kBPP;
584 a0 = columnData[x].alpha0;
585 a1 = columnData[x].alpha1;
587 destData[0] = H2(0);
588 destData[1] = H2(1);
589 destData[2] = H2(2);
590 destData[3] = H2(3);
593 // bottom, right pixel
594 a = srcData + srcW * kBPP;
596 destData[0] = a[0];
597 destData[1] = a[1];
598 destData[2] = a[2];
599 destData[3] = a[3];
603 delete[] columnData;
607 void
608 Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX,
609 intType toX, const float a0X, const float a1X, const int32 kBPP)
611 sum[0] = a0X * src[0];
612 sum[1] = a0X * src[1];
613 sum[2] = a0X * src[2];
615 src += kBPP;
617 for (int32 x = fromX + 1; x < toX; x++, src += kBPP) {
618 sum[0] += src[0];
619 sum[1] += src[1];
620 sum[2] += src[2];
623 if (toX <= srcW) {
624 sum[0] += a1X * src[0];
625 sum[1] += a1X * src[1];
626 sum[2] += a1X * src[2];
631 typedef struct {
632 int32 from;
633 int32 to;
634 float alpha0;
635 float alpha1;
636 } DownScaleColumnData;
639 void
640 Scaler::DownScaleBilinear(intType fromRow, int32 toRow)
642 BBitmap* src;
643 BBitmap* dest;
644 intType srcW, srcH;
645 intType destW, destH;
646 intType x, y;
647 const uchar* srcBits;
648 uchar* destBits;
649 intType srcBPR, destBPR;
650 const uchar* srcData;
651 uchar* destDataRow;
652 uchar* destData;
653 const int32 kBPP = 4;
654 DownScaleColumnData* columnData;
656 src = GetSrcImage();
657 dest = fScaledImage;
659 srcW = src->Bounds().IntegerWidth();
660 srcH = src->Bounds().IntegerHeight();
661 destW = dest->Bounds().IntegerWidth();
662 destH = dest->Bounds().IntegerHeight();
664 srcBits = (uchar*)src->Bits();
665 destBits = (uchar*)dest->Bits();
666 srcBPR = src->BytesPerRow();
667 destBPR = dest->BytesPerRow();
669 destDataRow = destBits + fromRow * destBPR;
671 const float deltaX = (srcW + 1.0) / (destW + 1.0);
672 const float deltaY = (srcH + 1.0) / (destH + 1.0);
673 const float deltaXY = deltaX * deltaY;
675 columnData = new DownScaleColumnData[destW + 1];
676 DownScaleColumnData* cd = columnData;
677 for (x = 0; x <= destW; x++, cd++) {
678 const float fFromX = x * deltaX;
679 const float fToX = fFromX + deltaX;
681 cd->from = (intType)fFromX;
682 cd->to = (intType)fToX;
684 cd->alpha0 = 1.0 - (fFromX - cd->from);
685 cd->alpha1 = fToX - cd->to;
688 for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
689 const float fFromY = y * deltaY;
690 const float fToY = fFromY + deltaY;
692 const intType fromY = (intType)fFromY;
693 const intType toY = (intType)fToY;
695 const float a0Y = 1.0 - (fFromY - fromY);
696 const float a1Y = fToY - toY;
698 const uchar* srcDataRow = srcBits + fromY * srcBPR;
699 destData = destDataRow;
701 cd = columnData;
702 for (x = 0; x <= destW; x++, destData += kBPP, cd++) {
703 const intType fromX = cd->from;
704 const intType toX = cd->to;
706 const float a0X = cd->alpha0;
707 const float a1X = cd->alpha1;
709 srcData = srcDataRow + fromX * kBPP;
711 float totalSum[3];
712 float sum[3];
714 RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
715 totalSum[0] = a0Y * sum[0];
716 totalSum[1] = a0Y * sum[1];
717 totalSum[2] = a0Y * sum[2];
719 srcData += srcBPR;
721 for (int32 r = fromY + 1; r < toY; r++, srcData += srcBPR) {
722 RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
723 totalSum[0] += sum[0];
724 totalSum[1] += sum[1];
725 totalSum[2] += sum[2];
728 if (toY <= srcH) {
729 RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
730 totalSum[0] += a1Y * sum[0];
731 totalSum[1] += a1Y * sum[1];
732 totalSum[2] += a1Y * sum[2];
735 destData[0] = static_cast<uchar>(totalSum[0] / deltaXY);
736 destData[1] = static_cast<uchar>(totalSum[1] / deltaXY);
737 destData[2] = static_cast<uchar>(totalSum[2] / deltaXY);
741 delete[] columnData;
745 // Flyod-Steinberg Dithering
746 // Filter (distribution of error to adjacent pixels, X is current pixel):
747 // 0 X 7
748 // 3 5 1
750 typedef struct {
751 intType error[3];
752 } DitheringColumnData;
755 uchar
756 Scaler::Limit(intType value)
758 if (value < 0) {
759 value = 0;
760 } if (value > 255) {
761 value = 255;
763 return value;
767 void
768 Scaler::Dither(int32 fromRow, int32 toRow)
770 BBitmap* src;
771 BBitmap* dest;
772 intType destW;
773 intType x, y;
775 uchar* srcBits;
776 intType srcBPR;
777 uchar* srcDataRow;
778 uchar* srcData;
780 uchar* destBits;
781 intType destBPR;
782 uchar* destDataRow;
783 uchar* destData;
784 const int32 kBPP = 4;
785 DitheringColumnData* columnData0;
786 DitheringColumnData* columnData;
787 DitheringColumnData* cd;
788 BScreen screen;
789 intType error[3], err[3];
791 src = fScaledImage;
792 dest = GetDestImage();
794 ASSERT(src->ColorSpace() == B_RGB32 || src->ColorSpace() == B_RGBA32);
795 ASSERT(dest->ColorSpace() == B_CMAP8);
796 ASSERT(src->Bounds().IntegerWidth() == dest->Bounds().IntegerWidth());
797 ASSERT(src->Bounds().IntegerHeight() == dest->Bounds().IntegerHeight());
799 destW = dest->Bounds().IntegerWidth();
801 srcBits = (uchar*)src->Bits();
802 srcBPR = src->BytesPerRow();
803 destBits = (uchar*)dest->Bits();
804 destBPR = dest->BytesPerRow();
806 // Allocate space for sentinel at left and right bounds,
807 // so that columnData[-1] and columnData[destW + 1] can be safely accessed
808 columnData0 = new DitheringColumnData[destW + 3];
809 columnData = columnData0 + 1;
811 // clear error
812 cd = columnData;
813 for (x = destW; x >= 0; x --, cd++) {
814 cd->error[0] = cd->error[1] = cd->error[2] = 0;
817 srcDataRow = srcBits + fromRow * srcBPR;
818 destDataRow = destBits + fromRow * destBPR;
819 for (y = fromRow; IsRunning() && y <= toRow; y++, srcDataRow += srcBPR,
820 destDataRow += destBPR) {
821 // left to right
822 error[0] = error[1] = error[2] = 0;
823 srcData = srcDataRow;
824 destData = destDataRow;
825 for (x = 0; x <= destW; x ++, srcData += kBPP, destData += 1) {
826 rgb_color color, actualColor;
827 uint8 index;
829 color.red = Limit(srcData[2] + error[0] / 16);
830 color.green = Limit(srcData[1] + error[1] / 16);
831 color.blue = Limit(srcData[0] + error[2] / 16);
832 color.alpha = UINT8_MAX;
834 index = screen.IndexForColor(color);
835 actualColor = screen.ColorForIndex(index);
837 *destData = index;
839 err[0] = color.red - actualColor.red;
840 err[1] = color.green - actualColor.green;
841 err[2] = color.blue - actualColor.blue;
843 // distribute error
844 // get error for next pixel
845 cd = &columnData[x + 1];
846 error[0] = cd->error[0] + 7 * err[0];
847 error[1] = cd->error[1] + 7 * err[1];
848 error[2] = cd->error[2] + 7 * err[2];
850 // set error for right pixel below current pixel
851 cd->error[0] = err[0];
852 cd->error[1] = err[1];
853 cd->error[2] = err[2];
855 // add error for pixel below current pixel
856 cd--;
857 cd->error[0] += 5 * err[0];
858 cd->error[1] += 5 * err[1];
859 cd->error[2] += 5 * err[2];
861 // add error for left pixel below current pixel
862 cd--;
863 cd->error[0] += 3 * err[0];
864 cd->error[1] += 3 * err[1];
865 cd->error[2] += 3 * err[2];
867 // Note: Alogrithm has good results with "left to right" already
868 // Optionally remove code to end of block:
869 y++;
870 srcDataRow += srcBPR; destDataRow += destBPR;
871 if (y > toRow) break;
872 // right to left
873 error[0] = error[1] = error[2] = 0;
874 srcData = srcDataRow + destW * kBPP;
875 destData = destDataRow + destW;
876 for (x = 0; x <= destW; x++, srcData -= kBPP, destData -= 1) {
877 rgb_color color, actualColor;
878 uint8 index;
880 color.red = Limit(srcData[2] + error[0] / 16);
881 color.green = Limit(srcData[1] + error[1] / 16);
882 color.blue = Limit(srcData[0] + error[2] / 16);
883 color.alpha = UINT8_MAX;
885 index = screen.IndexForColor(color);
886 actualColor = screen.ColorForIndex(index);
888 *destData = index;
890 err[0] = color.red - actualColor.red;
891 err[1] = color.green - actualColor.green;
892 err[2] = color.blue - actualColor.blue;
894 // distribute error
895 // get error for next pixel
896 cd = &columnData[x - 1];
897 error[0] = cd->error[0] + 7 * err[0];
898 error[1] = cd->error[1] + 7 * err[1];
899 error[2] = cd->error[2] + 7 * err[2];
901 // set error for left pixel below current pixel
902 cd->error[0] = err[0];
903 cd->error[1] = err[1];
904 cd->error[2] = err[2];
906 // add error for pixel below current pixel
907 cd++;
908 cd->error[0] += 5 * err[0];
909 cd->error[1] += 5 * err[1];
910 cd->error[2] += 5 * err[2];
912 // add error for right pixel below current pixel
913 cd++;
914 cd->error[0] += 3 * err[0];
915 cd->error[1] += 3 * err[1];
916 cd->error[2] += 3 * err[2];
920 delete[] columnData0;
924 int32
925 Scaler::GetNumberOfUnits()
927 return fRect.IntegerHeight() + 1;
931 void
932 Scaler::Run(int32 i, int32 n)
934 int32 from, to, height, imageHeight;
935 imageHeight = GetDestImage()->Bounds().IntegerHeight() + 1;
936 height = imageHeight / n;
937 from = i * height;
938 if (i + 1 == n)
939 to = imageHeight - 1;
940 else
941 to = from + height - 1;
943 if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width())
944 ScaleBilinearFP(from, to);
945 else
946 DownScaleBilinear(from, to);
948 if (fDither)
949 Dither(from, to);
954 void
955 Scaler::Completed()
957 if (GetDestImage() != fScaledImage)
958 delete fScaledImage;
960 fScaledImage = NULL;
964 // Implementation of ImageProcessor
965 ImageProcessor::ImageProcessor(enum operation op, BBitmap* image,
966 BMessenger listener, uint32 what)
968 Filter(image, listener, what),
969 fOp(op),
970 fBPP(0),
971 fWidth(0),
972 fHeight(0),
973 fSrcBPR(0),
974 fDestBPR(0)
979 BBitmap*
980 ImageProcessor::CreateDestImage(BBitmap* /* srcImage */)
982 color_space cs;
983 BBitmap* bm;
984 BRect rect;
986 if (GetSrcImage() == NULL)
987 return NULL;
989 cs = GetSrcImage()->ColorSpace();
990 fBPP = BytesPerPixel(cs);
991 if (fBPP < 1)
992 return NULL;
994 fWidth = GetSrcImage()->Bounds().IntegerWidth();
995 fHeight = GetSrcImage()->Bounds().IntegerHeight();
997 if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise)
998 rect.Set(0, 0, fHeight, fWidth);
999 else
1000 rect.Set(0, 0, fWidth, fHeight);
1002 bm = new BBitmap(rect, cs);
1003 if (!IsBitmapValid(bm)) {
1004 delete bm;
1005 return NULL;
1008 fSrcBPR = GetSrcImage()->BytesPerRow();
1009 fDestBPR = bm->BytesPerRow();
1011 return bm;
1015 int32
1016 ImageProcessor::GetNumberOfUnits()
1018 return GetSrcImage()->Bounds().IntegerHeight() + 1;
1022 int32
1023 ImageProcessor::BytesPerPixel(color_space cs) const
1025 switch (cs) {
1026 case B_RGB32: // fall through
1027 case B_RGB32_BIG: // fall through
1028 case B_RGBA32: // fall through
1029 case B_RGBA32_BIG: return 4;
1031 case B_RGB24_BIG: // fall through
1032 case B_RGB24: return 3;
1034 case B_RGB16: // fall through
1035 case B_RGB16_BIG: // fall through
1036 case B_RGB15: // fall through
1037 case B_RGB15_BIG: // fall through
1038 case B_RGBA15: // fall through
1039 case B_RGBA15_BIG: return 2;
1041 case B_GRAY8: // fall through
1042 case B_CMAP8: return 1;
1043 case B_GRAY1: return 0;
1044 default: return -1;
1049 void
1050 ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY,
1051 const uchar* src, int32 x, int32 y)
1053 // Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz),
1054 // replacing the multiplications below with pointer arithmethics showed
1055 // no speedup at all!
1056 dest += fDestBPR * destY + destX * fBPP;
1057 src += fSrcBPR * y + x * fBPP;
1058 // Replacing memcpy with this switch statement is slightly faster
1059 switch (fBPP) {
1060 case 4:
1061 dest[3] = src[3];
1062 case 3:
1063 dest[2] = src[2];
1064 case 2:
1065 dest[1] = src[1];
1066 case 1:
1067 dest[0] = src[0];
1068 break;
1073 // Note: For B_CMAP8 InvertPixel inverts the color index not the color value!
1074 void
1075 ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src)
1077 dest += fDestBPR * y + x * fBPP;
1078 src += fSrcBPR * y + x * fBPP;
1079 switch (fBPP) {
1080 case 4:
1081 // dest[3] = ~src[3]; DON'T invert alpha channel
1082 case 3:
1083 dest[2] = ~src[2];
1084 case 2:
1085 dest[1] = ~src[1];
1086 case 1:
1087 dest[0] = ~src[0];
1088 break;
1093 // Note: On my systems, the operation kInvert shows a speedup on
1094 // multiple CPUs only!
1095 void
1096 ImageProcessor::Run(int32 i, int32 n)
1098 int32 from, to;
1099 int32 height = (fHeight + 1) / n;
1100 from = i * height;
1101 if (i + 1 == n)
1102 to = fHeight;
1103 else
1104 to = from + height - 1;
1106 int32 x, y, destX, destY;
1107 const uchar* src = (uchar*)GetSrcImage()->Bits();
1108 uchar* dest = (uchar*)GetDestImage()->Bits();
1110 switch (fOp) {
1111 case kRotateClockwise:
1112 for (y = from; y <= to; y++) {
1113 for (x = 0; x <= fWidth; x++) {
1114 destX = fHeight - y;
1115 destY = x;
1116 CopyPixel(dest, destX, destY, src, x, y);
1119 break;
1120 case kRotateCounterClockwise:
1121 for (y = from; y <= to; y ++) {
1122 for (x = 0; x <= fWidth; x ++) {
1123 destX = y;
1124 destY = fWidth - x;
1125 CopyPixel(dest, destX, destY, src, x, y);
1128 break;
1129 case kFlipTopToBottom:
1130 for (y = from; y <= to; y ++) {
1131 for (x = 0; x <= fWidth; x ++) {
1132 destX = x;
1133 destY = fHeight - y;
1134 CopyPixel(dest, destX, destY, src, x, y);
1137 break;
1138 case kFlipLeftToRight:
1139 for (y = from; y <= to; y ++) {
1140 for (x = 0; x <= fWidth; x ++) {
1141 destX = fWidth - x;
1142 destY = y;
1143 CopyPixel(dest, destX, destY, src, x, y);
1146 break;
1147 case kInvert:
1148 for (y = from; y <= to; y ++) {
1149 for (x = 0; x <= fWidth; x ++) {
1150 InvertPixel(x, y, dest, src);
1153 break;