xref: /haiku/src/apps/showimage/Filter.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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.
6  *
7  * Authors:
8  *		Michael Pfeiffer, laplace@haiku-os.org
9  *		Ryan Leavengood, leavengood@gmail.com
10  *		yellowTAB GmbH
11  *		Bernd Korz
12  */
13 
14 #include <scheduler.h>
15 #include <Debug.h>
16 #include <Screen.h>
17 
18 #include <syscalls.h>
19 
20 #include "Filter.h"
21 
22 
23 // Implementation of FilterThread
24 FilterThread::FilterThread(Filter* filter, int32 i, int32 n,
25 	bool runInCurrentThread)
26 	:
27 	fFilter(filter),
28 	fI(i),
29 	fN(n)
30 {
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;
41 	}
42 }
43 
44 
45 FilterThread::~FilterThread()
46 {
47 	fFilter->FilterThreadDone();
48 }
49 
50 
51 status_t
52 FilterThread::worker_thread(void* data)
53 {
54 	FilterThread* thread = (FilterThread*)data;
55 	return thread->Run();
56 }
57 
58 
59 status_t
60 FilterThread::Run()
61 {
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;
69 		}
70 		// and start other filter threads
71 		for (int32 i = fI + 1; i < fN; i ++) {
72 			new FilterThread(fFilter, i, fN);
73 		}
74 	}
75 	if (fFilter->GetBitmap())
76 		fFilter->Run(fI, fN);
77 
78 	delete this;
79 	return B_OK;
80 }
81 
82 // Implementation of Filter
83 Filter::Filter(BBitmap* image, BMessenger listener, uint32 what)
84 	:
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)
94 {
95 	fCPUCount = NumberOfActiveCPUs();
96 
97 	fWaitForThreads = create_sem(0, "wait_for_threads");
98 
99 	#if TIME_FILTER
100 	fStopWatch = NULL;
101 	#endif
102 }
103 
104 
105 Filter::~Filter()
106 {
107 	delete fDestImage;
108 	delete_sem(fWaitForThreads);
109 }
110 
111 
112 BBitmap*
113 Filter::GetBitmap()
114 {
115 	if (!fDestImageInitialized) {
116 		fDestImageInitialized = true;
117 		fDestImage = CreateDestImage(fSrcImage);
118 	}
119 	return fDestImage;
120 }
121 
122 
123 BBitmap*
124 Filter::DetachBitmap()
125 {
126 	BBitmap* image = fDestImage;
127 	fDestImage = NULL;
128 	return image;
129 }
130 
131 
132 void
133 Filter::Start(bool async)
134 {
135 	if (fStarted || fSrcImage == NULL) return;
136 
137 	#if TIME_FILTER
138 		fStopWatch = new BStopWatch("Filter Time");
139 	#endif
140 
141 	fN = NumberOfThreads();
142 	fNumberOfThreads = fN;
143 	fIsRunning = true;
144 	fStarted = true;
145 
146 	// start first filter thread
147 	new FilterThread(this, 0, fN, !async);
148 
149 	if (!async)
150 		Wait();
151 }
152 
153 
154 void
155 Filter::Wait()
156 {
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;
162 	}
163 }
164 
165 
166 void
167 Filter::Stop()
168 {
169 	// tell FilterThreads to stop calculations
170 	fIsRunning = false;
171 	Wait();
172 }
173 
174 
175 bool
176 Filter::IsRunning() const
177 {
178 	return fIsRunning;
179 }
180 
181 
182 void
183 Filter::Completed()
184 {
185 }
186 
187 
188 void
189 Filter::FilterThreadDone()
190 {
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);
198 
199 		fIsRunning = false;
200 	}
201 	release_sem(fWaitForThreads);
202 }
203 
204 
205 void
206 Filter::FilterThreadInitFailed()
207 {
208 	ASSERT(fNumberOfThreads == fN);
209 	fNumberOfThreads = 0;
210 	Completed();
211 	fIsRunning = false;
212 	release_sem_etc(fWaitForThreads, fN, 0);
213 }
214 
215 
216 bool
217 Filter::IsBitmapValid(BBitmap* bitmap) const
218 {
219 	return bitmap != NULL && bitmap->InitCheck() == B_OK && bitmap->IsValid();
220 }
221 
222 
223 int32
224 Filter::NumberOfThreads()
225 {
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!
233 
234 	return n;
235 }
236 
237 
238 BBitmap*
239 Filter::GetSrcImage()
240 {
241 	return fSrcImage;
242 }
243 
244 
245 BBitmap*
246 Filter::GetDestImage()
247 {
248 	return fDestImage;
249 }
250 
251 
252 int32
253 Filter::NumberOfActiveCPUs() const
254 {
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++;
263 	}
264 	if (cpuCount == 0)
265 		cpuCount = 1;
266 
267 	return cpuCount;
268 }
269 
270 
271 // Implementation of (bilinear) Scaler
272 Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what,
273 	bool dither)
274 	:
275 	Filter(image, listener, what),
276 	fScaledImage(NULL),
277 	fRect(rect),
278 	fDither(dither)
279 {
280 }
281 
282 
283 Scaler::~Scaler()
284 {
285 	if (GetDestImage() != fScaledImage) {
286 		delete fScaledImage;
287 		fScaledImage = NULL;
288 	}
289 }
290 
291 
292 BBitmap*
293 Scaler::CreateDestImage(BBitmap* srcImage)
294 {
295 	if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32
296 		&& srcImage->ColorSpace() != B_RGBA32))
297 			return NULL;
298 
299 	BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
300 	BBitmap* destImage = new BBitmap(dest,
301 		fDither ? B_CMAP8 : srcImage->ColorSpace());
302 
303 	if (!IsBitmapValid(destImage)) {
304 		delete destImage;
305 		return NULL;
306 	}
307 
308 	if (fDither)
309 	{
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;
317 		}
318 	} else
319 		fScaledImage = destImage;
320 
321 	return destImage;
322 }
323 
324 
325 bool
326 Scaler::Matches(BRect rect, bool dither) const
327 {
328 	return fRect.IntegerWidth() == rect.IntegerWidth()
329 		&& fRect.IntegerHeight() == rect.IntegerHeight()
330 		&& fDither == dither;
331 }
332 
333 
334 // Scale bilinear using floating point calculations
335 typedef struct {
336 	intType srcColumn;
337 	float alpha0;
338 	float alpha1;
339 } ColumnData;
340 
341 
342 void
343 Scaler::ScaleBilinear(intType fromRow, int32 toRow)
344 {
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;
359 
360 	src = GetSrcImage();
361 	dest = fScaledImage;
362 
363 	srcW = src->Bounds().IntegerWidth();
364 	srcH = src->Bounds().IntegerHeight();
365 	destW = dest->Bounds().IntegerWidth();
366 	destH = dest->Bounds().IntegerHeight();
367 
368 	srcBits = (uchar*)src->Bits();
369 	destBits = (uchar*)dest->Bits();
370 	srcBPR = src->BytesPerRow();
371 	destBPR = dest->BytesPerRow();
372 
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;
380 	}
381 
382 	destDataRow = destBits + fromRow * destBPR;
383 
384 	for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) {
385 		float row;
386 		intType srcRow;
387 		float alpha0, alpha1;
388 
389 		if (destH == 0)
390 			row = 0;
391 		else
392 			row = (float)y * (float)srcH / (float)destH;
393 
394 		srcRow = (intType)row;
395 		alpha1 = row - srcRow;
396 		alpha0 = 1.0 - alpha1;
397 
398 		srcData = srcBits + srcRow * srcBPR;
399 		destData = destDataRow;
400 
401 		if (y < destH) {
402 			float a0, a1;
403 			const uchar *a, *b, *c, *d;
404 
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;
410 
411 				a0 = columnData[x].alpha0;
412 				a1 = columnData[x].alpha1;
413 
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);
426 			}
427 
428 			// right column
429 			a = srcData + srcW * kBPP;
430 			c = a + srcBPR;
431 
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;
442 
443 				a0 = columnData[x].alpha0;
444 				a1 = columnData[x].alpha1;
445 
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);
450 			}
451 
452 			// bottom, right pixel
453 			a = srcData + srcW * kBPP;
454 
455 			destData[0] = a[0];
456 			destData[1] = a[1];
457 			destData[2] = a[2];
458 			destData[3] = a[3];
459 		}
460 
461 	}
462 
463 	delete[] columnData;
464 }
465 
466 
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.
470 
471 typedef struct {
472 	int32 srcColumn;
473 	fixed_point alpha0;
474 	fixed_point alpha1;
475 } ColumnDataFP;
476 
477 
478 void
479 Scaler::ScaleBilinearFP(intType fromRow, int32 toRow)
480 {
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;
495 
496 	src = GetSrcImage();
497 	dest = fScaledImage;
498 
499 	srcW = src->Bounds().IntegerWidth();
500 	srcH = src->Bounds().IntegerHeight();
501 	destW = dest->Bounds().IntegerWidth();
502 	destH = dest->Bounds().IntegerHeight();
503 
504 	srcBits = (uchar*)src->Bits();
505 	destBits = (uchar*)dest->Bits();
506 	srcBPR = src->BytesPerRow();
507 	destBPR = dest->BytesPerRow();
508 
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);
513 
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
522 	}
523 
524 	destDataRow = destBits + fromRow * destBPR;
525 
526 	for (y = fromRow; IsRunning() && y <= toRow; y++, destDataRow += destBPR) {
527 		fixed_point row;
528 		intType srcRow;
529 		fixed_point alpha0, alpha1;
530 
531 		if (fpDestH == 0)
532 			row = 0;
533 		else
534 			row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH;
535 
536 		srcRow = from_fixed_point(row);
537 		alpha1 = tail_value(row); // weight for row y + 1
538 		alpha0 = kFPOne - alpha1; // weight for row y
539 
540 		srcData = srcBits + srcRow * srcBPR;
541 		destData = destDataRow;
542 
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);
549 
550 		if (y < destH) {
551 			fixed_point a0, a1;
552 			const uchar *a, *b, *c, *d;
553 
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;
559 
560 				a0 = columnData[x].alpha0;
561 				a1 = columnData[x].alpha1;
562 
563 				destData[0] = I4(0);
564 				destData[1] = I4(1);
565 				destData[2] = I4(2);
566 				destData[3] = I4(3);
567 			}
568 
569 			// right column
570 			a = srcData + srcW * kBPP;
571 			c = a + srcBPR;
572 
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;
583 
584 				a0 = columnData[x].alpha0;
585 				a1 = columnData[x].alpha1;
586 
587 				destData[0] = H2(0);
588 				destData[1] = H2(1);
589 				destData[2] = H2(2);
590 				destData[3] = H2(3);
591 			}
592 
593 			// bottom, right pixel
594 			a = srcData + srcW * kBPP;
595 
596 			destData[0] = a[0];
597 			destData[1] = a[1];
598 			destData[2] = a[2];
599 			destData[3] = a[3];
600 		}
601 	}
602 
603 	delete[] columnData;
604 }
605 
606 
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)
610 {
611 	sum[0] = a0X * src[0];
612 	sum[1] = a0X * src[1];
613 	sum[2] = a0X * src[2];
614 
615 	src += kBPP;
616 
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];
621 	}
622 
623 	if (toX <= srcW) {
624 		sum[0] += a1X * src[0];
625 		sum[1] += a1X * src[1];
626 		sum[2] += a1X * src[2];
627 	}
628 }
629 
630 
631 typedef struct {
632 	int32 from;
633 	int32 to;
634 	float alpha0;
635 	float alpha1;
636 } DownScaleColumnData;
637 
638 
639 void
640 Scaler::DownScaleBilinear(intType fromRow, int32 toRow)
641 {
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;
655 
656 	src = GetSrcImage();
657 	dest = fScaledImage;
658 
659 	srcW = src->Bounds().IntegerWidth();
660 	srcH = src->Bounds().IntegerHeight();
661 	destW = dest->Bounds().IntegerWidth();
662 	destH = dest->Bounds().IntegerHeight();
663 
664 	srcBits = (uchar*)src->Bits();
665 	destBits = (uchar*)dest->Bits();
666 	srcBPR = src->BytesPerRow();
667 	destBPR = dest->BytesPerRow();
668 
669 	destDataRow = destBits + fromRow * destBPR;
670 
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;
674 
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;
680 
681 		cd->from = (intType)fFromX;
682 		cd->to = (intType)fToX;
683 
684 		cd->alpha0 = 1.0 - (fFromX - cd->from);
685 		cd->alpha1 = fToX - cd->to;
686 	}
687 
688 	for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
689 		const float fFromY = y * deltaY;
690 		const float fToY = fFromY + deltaY;
691 
692 		const intType fromY = (intType)fFromY;
693 		const intType toY = (intType)fToY;
694 
695 		const float a0Y = 1.0 - (fFromY - fromY);
696 		const float a1Y = fToY - toY;
697 
698 		const uchar* srcDataRow = srcBits + fromY * srcBPR;
699 		destData = destDataRow;
700 
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;
705 
706 			const float a0X = cd->alpha0;
707 			const float a1X = cd->alpha1;
708 
709 			srcData = srcDataRow + fromX * kBPP;
710 
711 			float totalSum[3];
712 			float sum[3];
713 
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];
718 
719 			srcData += srcBPR;
720 
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];
726 			}
727 
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];
733 			}
734 
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);
738 		}
739 	}
740 
741 	delete[] columnData;
742 }
743 
744 
745 // Flyod-Steinberg Dithering
746 // Filter (distribution of error to adjacent pixels, X is current pixel):
747 // 0 X 7
748 // 3 5 1
749 
750 typedef struct {
751 	intType error[3];
752 } DitheringColumnData;
753 
754 
755 uchar
756 Scaler::Limit(intType value)
757 {
758 	if (value < 0) {
759 		value = 0;
760 	} else if (value > 255) {
761 		value = 255;
762 	}
763 	return value;
764 }
765 
766 
767 void
768 Scaler::Dither(int32 fromRow, int32 toRow)
769 {
770 	BBitmap* src;
771 	BBitmap* dest;
772 	intType destW;
773 	intType x, y;
774 
775 	uchar* srcBits;
776 	intType srcBPR;
777 	uchar* srcDataRow;
778 	uchar* srcData;
779 
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];
790 
791 	src = fScaledImage;
792 	dest = GetDestImage();
793 
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());
798 
799 	destW = dest->Bounds().IntegerWidth();
800 
801 	srcBits = (uchar*)src->Bits();
802 	srcBPR = src->BytesPerRow();
803 	destBits = (uchar*)dest->Bits();
804 	destBPR = dest->BytesPerRow();
805 
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;
810 
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;
815 	}
816 
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;
828 
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;
833 
834 			index = screen.IndexForColor(color);
835 			actualColor = screen.ColorForIndex(index);
836 
837 			*destData = index;
838 
839 			err[0] = color.red - actualColor.red;
840 			err[1] = color.green - actualColor.green;
841 			err[2] = color.blue - actualColor.blue;
842 
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];
849 
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];
854 
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];
860 
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];
866 		}
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;
879 
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;
884 
885 			index = screen.IndexForColor(color);
886 			actualColor = screen.ColorForIndex(index);
887 
888 			*destData = index;
889 
890 			err[0] = color.red - actualColor.red;
891 			err[1] = color.green - actualColor.green;
892 			err[2] = color.blue - actualColor.blue;
893 
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];
900 
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];
905 
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];
911 
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];
917 		}
918 	}
919 
920 	delete[] columnData0;
921 }
922 
923 
924 int32
925 Scaler::GetNumberOfUnits()
926 {
927 	return fRect.IntegerHeight() + 1;
928 }
929 
930 
931 void
932 Scaler::Run(int32 i, int32 n)
933 {
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;
942 
943 	if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width())
944 		ScaleBilinearFP(from, to);
945 	else
946 		DownScaleBilinear(from, to);
947 
948 	if (fDither)
949 		Dither(from, to);
950 
951 }
952 
953 
954 void
955 Scaler::Completed()
956 {
957 	if (GetDestImage() != fScaledImage)
958 		delete fScaledImage;
959 
960 	fScaledImage = NULL;
961 }
962 
963 
964 // Implementation of ImageProcessor
965 ImageProcessor::ImageProcessor(enum operation op, BBitmap* image,
966 	BMessenger listener, uint32 what)
967 	:
968 	Filter(image, listener, what),
969 	fOp(op),
970 	fBPP(0),
971 	fWidth(0),
972 	fHeight(0),
973 	fSrcBPR(0),
974 	fDestBPR(0)
975 {
976 }
977 
978 
979 BBitmap*
980 ImageProcessor::CreateDestImage(BBitmap* /* srcImage */)
981 {
982 	color_space cs;
983 	BBitmap* bm;
984 	BRect rect;
985 
986 	if (GetSrcImage() == NULL)
987 		return NULL;
988 
989 	cs = GetSrcImage()->ColorSpace();
990 	fBPP = BytesPerPixel(cs);
991 	if (fBPP < 1)
992 		return NULL;
993 
994 	fWidth = GetSrcImage()->Bounds().IntegerWidth();
995 	fHeight = GetSrcImage()->Bounds().IntegerHeight();
996 
997 	if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise)
998 		rect.Set(0, 0, fHeight, fWidth);
999 	else
1000 		rect.Set(0, 0, fWidth, fHeight);
1001 
1002 	bm = new BBitmap(rect, cs);
1003 	if (!IsBitmapValid(bm)) {
1004 		delete bm;
1005 		return NULL;
1006 	}
1007 
1008 	fSrcBPR = GetSrcImage()->BytesPerRow();
1009 	fDestBPR = bm->BytesPerRow();
1010 
1011 	return bm;
1012 }
1013 
1014 
1015 int32
1016 ImageProcessor::GetNumberOfUnits()
1017 {
1018 	return GetSrcImage()->Bounds().IntegerHeight() + 1;
1019 }
1020 
1021 
1022 int32
1023 ImageProcessor::BytesPerPixel(color_space cs) const
1024 {
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;
1030 
1031 		case B_RGB24_BIG:	// fall through
1032 		case B_RGB24:		return 3;
1033 
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;
1040 
1041 		case B_GRAY8:		// fall through
1042 		case B_CMAP8:		return 1;
1043 		case B_GRAY1:		return 0;
1044 		default: return -1;
1045 	}
1046 }
1047 
1048 
1049 void
1050 ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY,
1051 	const uchar* src, int32 x, int32 y)
1052 {
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;
1069 	}
1070 }
1071 
1072 
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)
1076 {
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;
1089 	}
1090 }
1091 
1092 
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)
1097 {
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;
1105 
1106 	int32 x, y, destX, destY;
1107 	const uchar* src = (uchar*)GetSrcImage()->Bits();
1108 	uchar* dest = (uchar*)GetDestImage()->Bits();
1109 
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);
1117 				}
1118 			}
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);
1126 				}
1127 			}
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);
1135 				}
1136 			}
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);
1144 				}
1145 			}
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);
1151 				}
1152 			}
1153 			break;
1154 	}
1155 }
1156