xref: /haiku/src/apps/showimage/Filter.cpp (revision 546208a53940a26c6379c48a7854ade1a8250fc5)
1 /*
2  * Copyright 2003-2006, Haiku.
3  * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd.
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, bool runInCurrentThread)
25 	: fFilter(filter)
26 	, fI(i)
27 	, fN(n)
28 {
29 	if (runInCurrentThread) {
30 		Run();
31 	} else {
32 		thread_id tid;
33 		tid = spawn_thread(worker_thread, "filter", suggest_thread_priority(B_STATUS_RENDERING), this);
34 		if (tid >= 0) {
35 			resume_thread(tid);
36 		} else {
37 			delete this;
38 		}
39 	}
40 }
41 
42 FilterThread::~FilterThread()
43 {
44 	fFilter->FilterThreadDone();
45 }
46 
47 status_t
48 FilterThread::worker_thread(void* data)
49 {
50 	FilterThread* thread = (FilterThread*)data;
51 	return thread->Run();
52 }
53 
54 status_t
55 FilterThread::Run()
56 {
57 	if (fI == 0) {
58 		BBitmap* bm;
59 		// create destination image in first thread
60 		bm = fFilter->GetBitmap();
61 		if (bm == NULL) {
62 			fFilter->FilterThreadInitFailed();
63 			return B_ERROR;
64 		}
65 		// and start other filter threads
66 		for (int32 i = fI + 1; i < fN; i ++) {
67 			new FilterThread(fFilter, i, fN);
68 		}
69 	}
70 	if (fFilter->GetBitmap()) {
71 		fFilter->Run(fI, fN);
72 	}
73 	delete this;
74 	return B_OK;
75 }
76 
77 // Implementation of Filter
78 Filter::Filter(BBitmap* image, BMessenger listener, uint32 what)
79 	: fListener(listener)
80 	, fWhat(what)
81 	, fStarted(false)
82 	, fN(0)
83 	, fNumberOfThreads(0)
84 	, fIsRunning(false)
85 	, fSrcImage(image)
86 	, fDestImageInitialized(false)
87 	, fDestImage(NULL)
88 {
89 	fCPUCount = NumberOfActiveCPUs();
90 
91 	fWaitForThreads = create_sem(0, "wait_for_threads");
92 
93 	#if TIME_FILTER
94 	fStopWatch = NULL;
95 	#endif
96 }
97 
98 Filter::~Filter()
99 {
100 	delete fDestImage;
101 	delete_sem(fWaitForThreads);
102 }
103 
104 BBitmap*
105 Filter::GetBitmap()
106 {
107 	if (!fDestImageInitialized) {
108 		fDestImageInitialized = true;
109 		fDestImage = CreateDestImage(fSrcImage);
110 	}
111 	return fDestImage;
112 }
113 
114 BBitmap*
115 Filter::DetachBitmap()
116 {
117 	BBitmap* image = fDestImage;
118 	fDestImage = NULL;
119 	return image;
120 }
121 
122 void
123 Filter::Start(bool async)
124 {
125 	if (fStarted || fSrcImage == NULL) return;
126 
127 	#if TIME_FILTER
128 		fStopWatch = new BStopWatch("Filter Time");
129 	#endif
130 
131 	fN = NumberOfThreads();
132 	fNumberOfThreads = fN;
133 	fIsRunning = true;
134 	fStarted = true;
135 
136 	// start first filter thread
137 	new FilterThread(this, 0, fN, !async);
138 
139 	if (!async) {
140 		Wait();
141 	}
142 }
143 
144 void
145 Filter::Wait()
146 {
147 	if (fStarted) {
148 		// wait for threads to exit
149 		while (acquire_sem_etc(fWaitForThreads, fN, 0, 0) == B_INTERRUPTED);
150 		// ready to start again
151 		fStarted = false;
152 	}
153 }
154 
155 void
156 Filter::Stop()
157 {
158 	// tell FilterThreads to stop calculations
159 	fIsRunning = false;
160 	Wait();
161 }
162 
163 bool
164 Filter::IsRunning() const
165 {
166 	return fIsRunning;
167 }
168 
169 void
170 Filter::Completed()
171 {
172 }
173 
174 void
175 Filter::FilterThreadDone()
176 {
177 	if (atomic_add(&fNumberOfThreads, -1) == 1) {
178 		#if TIME_FILTER
179 			delete fStopWatch; fStopWatch = NULL;
180 		#endif
181 		Completed();
182 		if (fIsRunning) {
183 			fListener.SendMessage(fWhat);
184 		}
185 		fIsRunning = false;
186 	}
187 	release_sem(fWaitForThreads);
188 }
189 
190 void
191 Filter::FilterThreadInitFailed()
192 {
193 	ASSERT(fNumberOfThreads == fN);
194 	fNumberOfThreads = 0;
195 	Completed();
196 	fIsRunning = false;
197 	release_sem_etc(fWaitForThreads, fN, 0);
198 }
199 
200 bool
201 Filter::IsBitmapValid(BBitmap* bitmap) const
202 {
203 	return bitmap != NULL && bitmap->InitCheck() == B_OK && bitmap->IsValid();
204 }
205 
206 int32
207 Filter::NumberOfThreads()
208 {
209 	const int32 units = GetNumberOfUnits();
210 	int32 n;
211 	n = units / 32; // at least 32 units per CPU
212 	if (n > CPUCount()) {
213 		n = CPUCount();
214 	} else if (n <= 0) {
215 		n = 1; // at least one thread!
216 	}
217 	return n;
218 }
219 
220 BBitmap*
221 Filter::GetSrcImage()
222 {
223 	return fSrcImage;
224 }
225 
226 BBitmap*
227 Filter::GetDestImage()
228 {
229 	return fDestImage;
230 }
231 
232 int32
233 Filter::NumberOfActiveCPUs() const
234 {
235 	int count;
236 	system_info info;
237 	get_system_info(&info);
238 	count = info.cpu_count;
239 	int32 cpuCount = 0;
240 	for (int i = 0; i < count; i ++) {
241 		if (_kern_cpu_enabled(i))
242 			cpuCount++;
243 	}
244 	if (cpuCount == 0)
245 		cpuCount = 1;
246 
247 	return cpuCount;
248 }
249 
250 // Implementation of (bilinear) Scaler
251 Scaler::Scaler(BBitmap* image, BRect rect, BMessenger listener, uint32 what, bool dither)
252 	: Filter(image, listener, what)
253 	, fScaledImage(NULL)
254 	, fRect(rect)
255 	, fDither(dither)
256 {
257 }
258 
259 Scaler::~Scaler()
260 {
261 	if (GetDestImage() != fScaledImage) {
262 		delete fScaledImage;
263 		fScaledImage = NULL;
264 	}
265 }
266 
267 BBitmap*
268 Scaler::CreateDestImage(BBitmap* srcImage)
269 {
270 	if (srcImage == NULL || (srcImage->ColorSpace() != B_RGB32 && srcImage->ColorSpace() != B_RGBA32)) return NULL;
271 
272 	BRect dest(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
273 	BBitmap* destImage = new BBitmap(dest, fDither ? B_CMAP8 : srcImage->ColorSpace());
274 
275 	if (!IsBitmapValid(destImage)) {
276 		delete destImage;
277 		return NULL;
278 	}
279 
280 	if (fDither)
281 	{
282 		BRect dest_rect(0, 0, fRect.IntegerWidth(), fRect.IntegerHeight());
283 		fScaledImage = new BBitmap(dest_rect, srcImage->ColorSpace());
284 		if (!IsBitmapValid(fScaledImage)) {
285 			delete destImage;
286 			delete fScaledImage;
287 			fScaledImage = NULL;
288 			return NULL;
289 		}
290 	} else {
291 		fScaledImage = destImage;
292 	}
293 
294 	return destImage;
295 }
296 
297 bool
298 Scaler::Matches(BRect rect, bool dither) const
299 {
300 	return fRect.IntegerWidth() == rect.IntegerWidth() &&
301 		fRect.IntegerHeight() == rect.IntegerHeight() &&
302 		fDither == dither;
303 }
304 
305 
306 // Scale bilinear using floating point calculations
307 typedef struct {
308 	intType srcColumn;
309 	float alpha0;
310 	float alpha1;
311 } ColumnData;
312 
313 void
314 Scaler::ScaleBilinear(intType fromRow, int32 toRow)
315 {
316 	BBitmap* src;
317 	BBitmap* dest;
318 	intType srcW, srcH;
319 	intType destW, destH;
320 	intType x, y, i;
321 	ColumnData* columnData;
322 	ColumnData* cd;
323 	const uchar* srcBits;
324 	uchar* destBits;
325 	intType srcBPR, destBPR;
326 	const uchar* srcData;
327 	uchar* destDataRow;
328 	uchar* destData;
329 	const int32 kBPP = 4;
330 
331 	src = GetSrcImage();
332 	dest = fScaledImage;
333 
334 	srcW = src->Bounds().IntegerWidth();
335 	srcH = src->Bounds().IntegerHeight();
336 	destW = dest->Bounds().IntegerWidth();
337 	destH = dest->Bounds().IntegerHeight();
338 
339 	srcBits = (uchar*)src->Bits();
340 	destBits = (uchar*)dest->Bits();
341 	srcBPR = src->BytesPerRow();
342 	destBPR = dest->BytesPerRow();
343 
344 	columnData = new ColumnData[destW];
345 	cd = columnData;
346 	for (i = 0; i < destW; i ++, cd++) {
347 		float column = (float)i * (float)srcW / (float)destW;
348 		cd->srcColumn = (intType)column;
349 		cd->alpha1 = column - cd->srcColumn;
350 		cd->alpha0 = 1.0 - cd->alpha1;
351 	}
352 
353 	destDataRow = destBits + fromRow * destBPR;
354 
355 	for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
356 		float row;
357 		intType srcRow;
358 		float alpha0, alpha1;
359 
360 		if (destH == 0) {
361 			row = 0;
362 		} else {
363 			row = (float)y * (float)srcH / (float)destH;
364 		}
365 		srcRow = (intType)row;
366 		alpha1 = row - srcRow;
367 		alpha0 = 1.0 - alpha1;
368 
369 		srcData = srcBits + srcRow * srcBPR;
370 		destData = destDataRow;
371 
372 		if (y < destH) {
373 			float a0, a1;
374 			const uchar *a, *b, *c, *d;
375 
376 			for (x = 0; x < destW; x ++, destData += kBPP) {
377 				a = srcData + columnData[x].srcColumn * kBPP;
378 				b = a + kBPP;
379 				c = a + srcBPR;
380 				d = c + kBPP;
381 
382 				a0 = columnData[x].alpha0;
383 				a1 = columnData[x].alpha1;
384 
385 				destData[0] = static_cast<uchar>(
386 								(a[0] * a0 + b[0] * a1) * alpha0 +
387 								(c[0] * a0 + d[0] * a1) * alpha1);
388 				destData[1] = static_cast<uchar>(
389 								(a[1] * a0 + b[1] * a1) * alpha0 +
390 								(c[1] * a0 + d[1] * a1) * alpha1);
391 				destData[2] = static_cast<uchar>(
392 								(a[2] * a0 + b[2] * a1) * alpha0 +
393 								(c[2] * a0 + d[2] * a1) * alpha1);
394 				destData[3] = static_cast<uchar>(
395 								(a[3] * a0 + b[3] * a1) * alpha0 +
396 								(c[3] * a0 + d[3] * a1) * alpha1);
397 			}
398 
399 			// right column
400 			a = srcData + srcW * kBPP;
401 			c = a + srcBPR;
402 
403 			destData[0] = static_cast<uchar>(a[0] * alpha0 + c[0] * alpha1);
404 			destData[1] = static_cast<uchar>(a[1] * alpha0 + c[1] * alpha1);
405 			destData[2] = static_cast<uchar>(a[2] * alpha0 + c[2] * alpha1);
406 			destData[3] = static_cast<uchar>(a[3] * alpha0 + c[3] * alpha1);
407 		} else {
408 			float a0, a1;
409 			const uchar *a, *b;
410 			for (x = 0; x < destW; x ++, destData += kBPP) {
411 				a = srcData + columnData[x].srcColumn * kBPP;
412 				b = a + kBPP;
413 
414 				a0 = columnData[x].alpha0;
415 				a1 = columnData[x].alpha1;
416 
417 				destData[0] = static_cast<uchar>(a[0] * a0 + b[0] * a1);
418 				destData[1] = static_cast<uchar>(a[1] * a0 + b[1] * a1);
419 				destData[2] = static_cast<uchar>(a[2] * a0 + b[2] * a1);
420 				destData[3] = static_cast<uchar>(a[3] * a0 + b[3] * a1);
421 			}
422 
423 			// bottom, right pixel
424 			a = srcData + srcW * kBPP;
425 
426 			destData[0] = a[0];
427 			destData[1] = a[1];
428 			destData[2] = a[2];
429 			destData[3] = a[3];
430 		}
431 
432 	}
433 
434 	delete[] columnData;
435 }
436 
437 // Scale bilinear using fixed point calculations
438 // Is already more than two times faster than floating point version
439 // on AMD Athlon 1 GHz and Dual Intel Pentium III 866 MHz.
440 
441 typedef struct {
442 	int32 srcColumn;
443 	fixed_point alpha0;
444 	fixed_point alpha1;
445 } ColumnDataFP;
446 
447 void
448 Scaler::ScaleBilinearFP(intType fromRow, int32 toRow)
449 {
450 	BBitmap* src;
451 	BBitmap* dest;
452 	intType srcW, srcH;
453 	intType destW, destH;
454 	intType x, y, i;
455 	ColumnDataFP* columnData;
456 	ColumnDataFP* cd;
457 	const uchar* srcBits;
458 	uchar* destBits;
459 	intType srcBPR, destBPR;
460 	const uchar* srcData;
461 	uchar* destDataRow;
462 	uchar* destData;
463 	const int32 kBPP = 4;
464 
465 	src = GetSrcImage();
466 	dest = fScaledImage;
467 
468 	srcW = src->Bounds().IntegerWidth();
469 	srcH = src->Bounds().IntegerHeight();
470 	destW = dest->Bounds().IntegerWidth();
471 	destH = dest->Bounds().IntegerHeight();
472 
473 	srcBits = (uchar*)src->Bits();
474 	destBits = (uchar*)dest->Bits();
475 	srcBPR = src->BytesPerRow();
476 	destBPR = dest->BytesPerRow();
477 
478 	fixed_point fpSrcW = to_fixed_point(srcW);
479 	fixed_point fpDestW = to_fixed_point(destW);
480 	fixed_point fpSrcH = to_fixed_point(srcH);
481 	fixed_point fpDestH = to_fixed_point(destH);
482 
483 	columnData = new ColumnDataFP[destW];
484 	cd = columnData;
485 	for (i = 0; i < destW; i ++, cd++) {
486 		fixed_point column = to_fixed_point(i) * (long_fixed_point)fpSrcW / fpDestW;
487 		cd->srcColumn = from_fixed_point(column);
488 		cd->alpha1 = tail_value(column); // weigth for left pixel value
489 		cd->alpha0 = kFPOne - cd->alpha1; // weigth for right pixel value
490 	}
491 
492 	destDataRow = destBits + fromRow * destBPR;
493 
494 	for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
495 		fixed_point row;
496 		intType srcRow;
497 		fixed_point alpha0, alpha1;
498 
499 		if (fpDestH == 0) {
500 			row = 0;
501 		} else {
502 			row = to_fixed_point(y) * (long_fixed_point)fpSrcH / fpDestH;
503 		}
504 		srcRow = from_fixed_point(row);
505 		alpha1 = tail_value(row); // weight for row y+1
506 		alpha0 = kFPOne - alpha1; // weight for row y
507 
508 		srcData = srcBits + srcRow * srcBPR;
509 		destData = destDataRow;
510 
511 		// Need mult_correction for "outer" multiplication only
512 		#define I4(i) from_fixed_point(mult_correction(\
513 							(a[i] * a0 + b[i] * a1) * alpha0 + \
514 							(c[i] * a0 + d[i] * a1) * alpha1))
515 		#define V2(i) from_fixed_point(a[i] * alpha0 + c[i] * alpha1);
516 		#define H2(i) from_fixed_point(a[i] * a0 + b[i] * a1);
517 
518 		if (y < destH) {
519 			fixed_point a0, a1;
520 			const uchar *a, *b, *c, *d;
521 
522 			for (x = 0; x < destW; x ++, destData += kBPP) {
523 				a = srcData + columnData[x].srcColumn * kBPP;
524 				b = a + kBPP;
525 				c = a + srcBPR;
526 				d = c + kBPP;
527 
528 				a0 = columnData[x].alpha0;
529 				a1 = columnData[x].alpha1;
530 
531 				destData[0] = I4(0);
532 				destData[1] = I4(1);
533 				destData[2] = I4(2);
534 				destData[3] = I4(3);
535 			}
536 
537 			// right column
538 			a = srcData + srcW * kBPP;
539 			c = a + srcBPR;
540 
541 			destData[0] = V2(0);
542 			destData[1] = V2(1);
543 			destData[2] = V2(2);
544 			destData[3] = V2(3);
545 		} else {
546 			fixed_point a0, a1;
547 			const uchar *a, *b;
548 			for (x = 0; x < destW; x ++, destData += kBPP) {
549 				a = srcData + columnData[x].srcColumn * kBPP;
550 				b = a + kBPP;
551 
552 				a0 = columnData[x].alpha0;
553 				a1 = columnData[x].alpha1;
554 
555 				destData[0] = H2(0);
556 				destData[1] = H2(1);
557 				destData[2] = H2(2);
558 				destData[3] = H2(3);
559 			}
560 
561 			// bottom, right pixel
562 			a = srcData + srcW * kBPP;
563 
564 			destData[0] = a[0];
565 			destData[1] = a[1];
566 			destData[2] = a[2];
567 			destData[3] = a[3];
568 		}
569 
570 	}
571 
572 	delete[] columnData;
573 }
574 
575 void
576 Scaler::RowValues(float* sum, const uchar* src, intType srcW, intType fromX, intType toX, const float a0X, const float a1X, const int32 kBPP)
577 {
578 	sum[0] = a0X * src[0];
579 	sum[1] = a0X * src[1];
580 	sum[2] = a0X * src[2];
581 
582 	src += kBPP;
583 
584 	for (int32 x = fromX+1; x < toX; x ++, src += kBPP) {
585 		sum[0] += src[0];
586 		sum[1] += src[1];
587 		sum[2] += src[2];
588 	}
589 
590 	if (toX <= srcW) {
591 		sum[0] += a1X * src[0];
592 		sum[1] += a1X * src[1];
593 		sum[2] += a1X * src[2];
594 	}
595 }
596 
597 typedef struct {
598 	int32 from;
599 	int32 to;
600 	float alpha0;
601 	float alpha1;
602 } DownScaleColumnData;
603 
604 void
605 Scaler::DownScaleBilinear(intType fromRow, int32 toRow)
606 {
607 	BBitmap* src;
608 	BBitmap* dest;
609 	intType srcW, srcH;
610 	intType destW, destH;
611 	intType x, y;
612 	const uchar* srcBits;
613 	uchar* destBits;
614 	intType srcBPR, destBPR;
615 	const uchar* srcData;
616 	uchar* destDataRow;
617 	uchar* destData;
618 	const int32 kBPP = 4;
619 	DownScaleColumnData* columnData;
620 
621 	src = GetSrcImage();
622 	dest = fScaledImage;
623 
624 	srcW = src->Bounds().IntegerWidth();
625 	srcH = src->Bounds().IntegerHeight();
626 	destW = dest->Bounds().IntegerWidth();
627 	destH = dest->Bounds().IntegerHeight();
628 
629 	srcBits = (uchar*)src->Bits();
630 	destBits = (uchar*)dest->Bits();
631 	srcBPR = src->BytesPerRow();
632 	destBPR = dest->BytesPerRow();
633 
634 	destDataRow = destBits + fromRow * destBPR;
635 
636 	const float deltaX = (srcW + 1.0) / (destW + 1.0);
637 	const float deltaY = (srcH + 1.0) / (destH + 1.0);
638 	const float deltaXY = deltaX * deltaY;
639 
640 	columnData = new DownScaleColumnData[destW+1];
641 	DownScaleColumnData* cd = columnData;
642 	for (x = 0; x <= destW; x ++, cd ++) {
643 		const float fFromX = x * deltaX;
644 		const float fToX = fFromX + deltaX;
645 
646 		cd->from = (intType)fFromX;
647 		cd->to = (intType)fToX;
648 
649 		cd->alpha0 = 1.0 - (fFromX - cd->from);
650 		cd->alpha1 = fToX - cd->to;
651 	}
652 
653 	for (y = fromRow; IsRunning() && y <= toRow; y ++, destDataRow += destBPR) {
654 		const float fFromY = y * deltaY;
655 		const float fToY = fFromY + deltaY;
656 
657 		const intType fromY = (intType)fFromY;
658 		const intType toY = (intType)fToY;
659 
660 		const float a0Y = 1.0 - (fFromY - fromY);
661 		const float a1Y = fToY - toY;
662 
663 		const uchar* srcDataRow = srcBits + fromY * srcBPR;
664 		destData = destDataRow;
665 
666 		cd = columnData;
667 		for (x = 0; x <= destW; x ++, destData += kBPP, cd ++) {
668 			const intType fromX = cd->from;
669 			const intType toX = cd->to;
670 
671 			const float a0X = cd->alpha0;
672 			const float a1X = cd->alpha1;
673 
674 			srcData = srcDataRow + fromX * kBPP;
675 
676 			float totalSum[3];
677 			float sum[3];
678 
679 			RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
680 			totalSum[0] = a0Y * sum[0];
681 			totalSum[1] = a0Y * sum[1];
682 			totalSum[2] = a0Y * sum[2];
683 
684 			srcData += srcBPR;
685 
686 			for (int32 r = fromY+1; r < toY; r ++, srcData += srcBPR) {
687 				RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
688 				totalSum[0] += sum[0];
689 				totalSum[1] += sum[1];
690 				totalSum[2] += sum[2];
691 			}
692 
693 			if (toY <= srcH) {
694 				RowValues(sum, srcData, srcW, fromX, toX, a0X, a1X, kBPP);
695 				totalSum[0] += a1Y * sum[0];
696 				totalSum[1] += a1Y * sum[1];
697 				totalSum[2] += a1Y * sum[2];
698 			}
699 
700 			destData[0] = static_cast<uchar>(totalSum[0] / deltaXY);
701 			destData[1] = static_cast<uchar>(totalSum[1] / deltaXY);
702 			destData[2] = static_cast<uchar>(totalSum[2] / deltaXY);
703 		}
704 	}
705 
706 	delete[] columnData;
707 }
708 
709 // Flyod-Steinberg Dithering
710 // Filter (distribution of error to adjacent pixels, X is current pixel):
711 // 0 X 7
712 // 3 5 1
713 
714 typedef struct {
715 	intType error[3];
716 } DitheringColumnData;
717 
718 uchar
719 Scaler::Limit(intType value)
720 {
721 	if (value < 0) {
722 		value = 0;
723 	} if (value > 255) {
724 		value = 255;
725 	}
726 	return value;
727 }
728 
729 void
730 Scaler::Dither(int32 fromRow, int32 toRow)
731 {
732 	BBitmap* src;
733 	BBitmap* dest;
734 	intType destW;
735 	intType x, y;
736 
737 	uchar* srcBits;
738 	intType srcBPR;
739 	uchar* srcDataRow;
740 	uchar* srcData;
741 
742 	uchar* destBits;
743 	intType destBPR;
744 	uchar* destDataRow;
745 	uchar* destData;
746 	const int32 kBPP = 4;
747 	DitheringColumnData* columnData0;
748 	DitheringColumnData* columnData;
749 	DitheringColumnData* cd;
750 	BScreen screen;
751 	intType error[3], err[3];
752 
753 	src = fScaledImage;
754 	dest = GetDestImage();
755 
756 	ASSERT(src->ColorSpace() == B_RGB32 || src->ColorSpace() == B_RGBA32);
757 	ASSERT(dest->ColorSpace() == B_CMAP8);
758 	ASSERT(src->Bounds().IntegerWidth() == dest->Bounds().IntegerWidth());
759 	ASSERT(src->Bounds().IntegerHeight() == dest->Bounds().IntegerHeight());
760 
761 	destW = dest->Bounds().IntegerWidth();
762 
763 	srcBits = (uchar*)src->Bits();
764 	srcBPR = src->BytesPerRow();
765 	destBits = (uchar*)dest->Bits();
766 	destBPR = dest->BytesPerRow();
767 
768 	// Allocate space for sentinel at left and right bounds,
769 	// so that columnData[-1] and columnData[destW+1] can be safely accessed
770 	columnData0 = new DitheringColumnData[destW+3];
771 	columnData = columnData0 + 1;
772 
773 	// clear error
774 	cd = columnData;
775 	for (x = destW; x >= 0; x --, cd ++) {
776 		cd->error[0] = cd->error[1] = cd->error[2] =0;
777 	}
778 
779 	srcDataRow = srcBits + fromRow * srcBPR;
780 	destDataRow = destBits + fromRow * destBPR;
781 	for (y = fromRow; IsRunning() && y <= toRow; y ++, srcDataRow += srcBPR, destDataRow += destBPR) {
782 		// left to right
783 		error[0] = error[1] = error[2] = 0;
784 		srcData = srcDataRow;
785 		destData = destDataRow;
786 		for (x = 0; x <= destW; x ++, srcData += kBPP, destData += 1) {
787 			rgb_color color, actualColor;
788 			uint8 index;
789 
790 			color.red = Limit(srcData[2] + error[0] / 16);
791 			color.green = Limit(srcData[1] + error[1] / 16);
792 			color.blue = Limit(srcData[0] + error[2] / 16);
793 
794 			index = screen.IndexForColor(color);
795 			actualColor = screen.ColorForIndex(index);
796 
797 			*destData = index;
798 
799 			err[0] = color.red - actualColor.red;
800 			err[1] = color.green -actualColor.green;
801 			err[2] = color.blue -actualColor.blue;
802 
803 			// distribute error
804 			// get error for next pixel
805 			cd = &columnData[x+1];
806 			error[0] = cd->error[0] + 7 * err[0];
807 			error[1] = cd->error[1] + 7 * err[1];
808 			error[2] = cd->error[2] + 7 * err[2];
809 
810 			// set error for right pixel below current pixel
811 			cd->error[0] = err[0];
812 			cd->error[1] = err[1];
813 			cd->error[2] = err[2];
814 
815 			// add error for pixel below current pixel
816 			cd --;
817 			cd->error[0] += 5 * err[0];
818 			cd->error[1] += 5 * err[1];
819 			cd->error[2] += 5 * err[2];
820 
821 			// add error for left pixel below current pixel
822 			cd --;
823 			cd->error[0] += 3 * err[0];
824 			cd->error[1] += 3 * err[1];
825 			cd->error[2] += 3 * err[2];
826 		}
827 		// Note: Alogrithm has good results with "left to right" already
828 		// Optionally remove code to end of block:
829 		y ++;
830 		srcDataRow += srcBPR; destDataRow += destBPR;
831 		if (y > toRow) break;
832 		// right to left
833 		error[0] = error[1] = error[2] = 0;
834 		srcData = srcDataRow + destW * kBPP;
835 		destData = destDataRow + destW;
836 		for (x = 0; x <= destW; x ++, srcData -= kBPP, destData -= 1) {
837 			rgb_color color, actualColor;
838 			uint8 index;
839 
840 			color.red = Limit(srcData[2] + error[0] / 16);
841 			color.green = Limit(srcData[1] + error[1] / 16);
842 			color.blue = Limit(srcData[0] + error[2] / 16);
843 
844 			index = screen.IndexForColor(color);
845 			actualColor = screen.ColorForIndex(index);
846 
847 			*destData = index;
848 
849 			err[0] = color.red - actualColor.red;
850 			err[1] = color.green -actualColor.green;
851 			err[2] = color.blue -actualColor.blue;
852 
853 			// distribute error
854 			// get error for next pixel
855 			cd = &columnData[x-1];
856 			error[0] = cd->error[0] + 7 * err[0];
857 			error[1] = cd->error[1] + 7 * err[1];
858 			error[2] = cd->error[2] + 7 * err[2];
859 
860 			// set error for left pixel below current pixel
861 			cd->error[0] = err[0];
862 			cd->error[1] = err[1];
863 			cd->error[2] = err[2];
864 
865 			// add error for pixel below current pixel
866 			cd ++;
867 			cd->error[0] += 5 * err[0];
868 			cd->error[1] += 5 * err[1];
869 			cd->error[2] += 5 * err[2];
870 
871 			// add error for right pixel below current pixel
872 			cd ++;
873 			cd->error[0] += 3 * err[0];
874 			cd->error[1] += 3 * err[1];
875 			cd->error[2] += 3 * err[2];
876 		}
877 	}
878 
879 	delete[] columnData0;
880 }
881 
882 int32
883 Scaler::GetNumberOfUnits()
884 {
885 	return fRect.IntegerHeight() + 1;
886 }
887 
888 void
889 Scaler::Run(int32 i, int32 n)
890 {
891 	int32 from, to, height, imageHeight;
892 	imageHeight = GetDestImage()->Bounds().IntegerHeight() + 1;
893 	height = imageHeight / n;
894 	from = i * height;
895 	if (i+1 == n) {
896 		to = imageHeight - 1;
897 	} else {
898 		to = from + height - 1;
899 	}
900 	if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width()) {
901 		ScaleBilinearFP(from, to);
902 	} else {
903 		DownScaleBilinear(from, to);
904 	}
905 	if (fDither) {
906 		Dither(from, to);
907 	}
908 }
909 
910 void
911 Scaler::Completed()
912 {
913 	if (GetDestImage() != fScaledImage) {
914 		delete fScaledImage;
915 	}
916 	fScaledImage = NULL;
917 }
918 
919 // Implementation of ImageProcessor
920 ImageProcessor::ImageProcessor(enum operation op, BBitmap* image, BMessenger listener, uint32 what)
921 	: Filter(image, listener, what)
922 	, fOp(op)
923 	, fBPP(0)
924 	, fWidth(0)
925 	, fHeight(0)
926 	, fSrcBPR(0)
927 	, fDestBPR(0)
928 
929 {
930 }
931 
932 BBitmap*
933 ImageProcessor::CreateDestImage(BBitmap* /* srcImage */)
934 {
935 	color_space cs;
936 	BBitmap* bm;
937 	BRect rect;
938 
939 	if (GetSrcImage() == NULL) return NULL;
940 
941 	cs = GetSrcImage()->ColorSpace();
942 	fBPP = BytesPerPixel(cs);
943 	if (fBPP < 1) return NULL;
944 
945 	fWidth = GetSrcImage()->Bounds().IntegerWidth();
946 	fHeight = GetSrcImage()->Bounds().IntegerHeight();
947 
948 	if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise) {
949 		rect.Set(0, 0, fHeight, fWidth);
950 	} else {
951 		rect.Set(0, 0, fWidth, fHeight);
952 	}
953 
954 	bm = new BBitmap(rect, cs);
955 	if (!IsBitmapValid(bm)) {
956 		delete bm;
957 		return NULL;
958 	}
959 
960 	fSrcBPR = GetSrcImage()->BytesPerRow();
961 	fDestBPR = bm->BytesPerRow();
962 
963 	return bm;
964 }
965 
966 int32
967 ImageProcessor::GetNumberOfUnits()
968 {
969 	return GetSrcImage()->Bounds().IntegerHeight() + 1;
970 }
971 
972 int32
973 ImageProcessor::BytesPerPixel(color_space cs) const
974 {
975 	switch (cs) {
976 		case B_RGB32:      // fall through
977 		case B_RGB32_BIG:  // fall through
978 		case B_RGBA32:     // fall through
979 		case B_RGBA32_BIG: return 4;
980 
981 		case B_RGB24_BIG:  // fall through
982 		case B_RGB24:      return 3;
983 
984 		case B_RGB16:      // fall through
985 		case B_RGB16_BIG:  // fall through
986 		case B_RGB15:      // fall through
987 		case B_RGB15_BIG:  // fall through
988 		case B_RGBA15:     // fall through
989 		case B_RGBA15_BIG: return 2;
990 
991 		case B_GRAY8:      // fall through
992 		case B_CMAP8:      return 1;
993 		case B_GRAY1:      return 0;
994 		default: return -1;
995 	}
996 }
997 
998 void
999 ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* src, int32 x, int32 y)
1000 {
1001 	// Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz), replacing
1002 	// the multiplications below with pointer arithmethics showed no speedup at all!
1003 	dest += fDestBPR * destY + destX * fBPP;
1004 	src += fSrcBPR * y + x *fBPP;
1005 	// Replacing memcpy with this switch statement is slightly faster
1006 	switch (fBPP) {
1007 		case 4:
1008 			dest[3] = src[3];
1009 		case 3:
1010 			dest[2] = src[2];
1011 		case 2:
1012 			dest[1] = src[1];
1013 		case 1:
1014 			dest[0] = src[0];
1015 			break;
1016 	}
1017 }
1018 
1019 // Note: For B_CMAP8 InvertPixel inverts the color index not the color value!
1020 void
1021 ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src)
1022 {
1023 	dest += fDestBPR * y + x * fBPP;
1024 	src += fSrcBPR * y + x * fBPP;
1025 	switch (fBPP) {
1026 		case 4:
1027 			// dest[3] = ~src[3]; DON'T invert alpha channel
1028 		case 3:
1029 			dest[2] = ~src[2];
1030 		case 2:
1031 			dest[1] = ~src[1];
1032 		case 1:
1033 			dest[0] = ~src[0];
1034 			break;
1035 	}
1036 }
1037 
1038 // Note: On my systems, the operation kInvert shows a speedup on multiple CPUs only!
1039 void
1040 ImageProcessor::Run(int32 i, int32 n)
1041 {
1042 	int32 from, to;
1043 	int32 height = (fHeight+1) / n;
1044 	from = i * height;
1045 	if (i+1 == n) {
1046 		to = fHeight;
1047 	} else {
1048 		to = from + height - 1;
1049 	}
1050 
1051 	int32 x, y, destX, destY;
1052 	const uchar* src = (uchar*)GetSrcImage()->Bits();
1053 	uchar* dest = (uchar*)GetDestImage()->Bits();
1054 
1055 	switch (fOp) {
1056 		case kRotateClockwise:
1057 			for (y = from; y <= to; y ++) {
1058 				for (x = 0; x <= fWidth; x ++) {
1059 					destX = fHeight - y;
1060 					destY = x;
1061 					CopyPixel(dest, destX, destY, src, x, y);
1062 				}
1063 			}
1064 			break;
1065 		case kRotateCounterClockwise:
1066 			for (y = from; y <= to; y ++) {
1067 				for (x = 0; x <= fWidth; x ++) {
1068 					destX = y;
1069 					destY = fWidth - x;
1070 					CopyPixel(dest, destX, destY, src, x, y);
1071 				}
1072 			}
1073 			break;
1074 		case kFlipTopToBottom:
1075 			for (y = from; y <= to; y ++) {
1076 				for (x = 0; x <= fWidth; x ++) {
1077 					destX = x;
1078 					destY = fHeight - y;
1079 					CopyPixel(dest, destX, destY, src, x, y);
1080 				}
1081 			}
1082 			break;
1083 		case kFlipLeftToRight:
1084 			for (y = from; y <= to; y ++) {
1085 				for (x = 0; x <= fWidth; x ++) {
1086 					destX = fWidth - x;
1087 					destY = y;
1088 					CopyPixel(dest, destX, destY, src, x, y);
1089 				}
1090 			}
1091 			break;
1092 		case kInvert:
1093 			for (y = from; y <= to; y ++) {
1094 				for (x = 0; x <= fWidth; x ++) {
1095 					InvertPixel(x, y, dest, src);
1096 				}
1097 			}
1098 			break;
1099 	}
1100 
1101 }
1102