xref: /haiku/src/apps/showimage/Filter.cpp (revision 4466b89c65970de4c7236ac87faa2bee4589f413)
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, destH;
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 	destH = dest->Bounds().IntegerHeight();
763 
764 	srcBits = (uchar*)src->Bits();
765 	srcBPR = src->BytesPerRow();
766 	destBits = (uchar*)dest->Bits();
767 	destBPR = dest->BytesPerRow();
768 
769 	// Allocate space for sentinel at left and right bounds,
770 	// so that columnData[-1] and columnData[destW+1] can be safely accessed
771 	columnData0 = new DitheringColumnData[destW+3];
772 	columnData = columnData0 + 1;
773 
774 	// clear error
775 	cd = columnData;
776 	for (x = destW; x >= 0; x --, cd ++) {
777 		cd->error[0] = cd->error[1] = cd->error[2] =0;
778 	}
779 
780 	srcDataRow = srcBits + fromRow * srcBPR;
781 	destDataRow = destBits + fromRow * destBPR;
782 	for (y = fromRow; IsRunning() && y <= toRow; y ++, srcDataRow += srcBPR, destDataRow += destBPR) {
783 		// left to right
784 		error[0] = error[1] = error[2] = 0;
785 		srcData = srcDataRow;
786 		destData = destDataRow;
787 		for (x = 0; x <= destW; x ++, srcData += kBPP, destData += 1) {
788 			rgb_color color, actualColor;
789 			uint8 index;
790 
791 			color.red = Limit(srcData[2] + error[0] / 16);
792 			color.green = Limit(srcData[1] + error[1] / 16);
793 			color.blue = Limit(srcData[0] + error[2] / 16);
794 
795 			index = screen.IndexForColor(color);
796 			actualColor = screen.ColorForIndex(index);
797 
798 			*destData = index;
799 
800 			err[0] = color.red - actualColor.red;
801 			err[1] = color.green -actualColor.green;
802 			err[2] = color.blue -actualColor.blue;
803 
804 			// distribute error
805 			// get error for next pixel
806 			cd = &columnData[x+1];
807 			error[0] = cd->error[0] + 7 * err[0];
808 			error[1] = cd->error[1] + 7 * err[1];
809 			error[2] = cd->error[2] + 7 * err[2];
810 
811 			// set error for right pixel below current pixel
812 			cd->error[0] = err[0];
813 			cd->error[1] = err[1];
814 			cd->error[2] = err[2];
815 
816 			// add error for pixel below current pixel
817 			cd --;
818 			cd->error[0] += 5 * err[0];
819 			cd->error[1] += 5 * err[1];
820 			cd->error[2] += 5 * err[2];
821 
822 			// add error for left pixel below current pixel
823 			cd --;
824 			cd->error[0] += 3 * err[0];
825 			cd->error[1] += 3 * err[1];
826 			cd->error[2] += 3 * err[2];
827 		}
828 		// Note: Alogrithm has good results with "left to right" already
829 		// Optionally remove code to end of block:
830 		y ++;
831 		srcDataRow += srcBPR; destDataRow += destBPR;
832 		if (y > toRow) break;
833 		// right to left
834 		error[0] = error[1] = error[2] = 0;
835 		srcData = srcDataRow + destW * kBPP;
836 		destData = destDataRow + destW;
837 		for (x = 0; x <= destW; x ++, srcData -= kBPP, destData -= 1) {
838 			rgb_color color, actualColor;
839 			uint8 index;
840 
841 			color.red = Limit(srcData[2] + error[0] / 16);
842 			color.green = Limit(srcData[1] + error[1] / 16);
843 			color.blue = Limit(srcData[0] + error[2] / 16);
844 
845 			index = screen.IndexForColor(color);
846 			actualColor = screen.ColorForIndex(index);
847 
848 			*destData = index;
849 
850 			err[0] = color.red - actualColor.red;
851 			err[1] = color.green -actualColor.green;
852 			err[2] = color.blue -actualColor.blue;
853 
854 			// distribute error
855 			// get error for next pixel
856 			cd = &columnData[x-1];
857 			error[0] = cd->error[0] + 7 * err[0];
858 			error[1] = cd->error[1] + 7 * err[1];
859 			error[2] = cd->error[2] + 7 * err[2];
860 
861 			// set error for left pixel below current pixel
862 			cd->error[0] = err[0];
863 			cd->error[1] = err[1];
864 			cd->error[2] = err[2];
865 
866 			// add error for pixel below current pixel
867 			cd ++;
868 			cd->error[0] += 5 * err[0];
869 			cd->error[1] += 5 * err[1];
870 			cd->error[2] += 5 * err[2];
871 
872 			// add error for right pixel below current pixel
873 			cd ++;
874 			cd->error[0] += 3 * err[0];
875 			cd->error[1] += 3 * err[1];
876 			cd->error[2] += 3 * err[2];
877 		}
878 	}
879 
880 	delete[] columnData0;
881 }
882 
883 int32
884 Scaler::GetNumberOfUnits()
885 {
886 	return fRect.IntegerHeight() + 1;
887 }
888 
889 void
890 Scaler::Run(int32 i, int32 n)
891 {
892 	int32 from, to, height, imageHeight;
893 	imageHeight = GetDestImage()->Bounds().IntegerHeight() + 1;
894 	height = imageHeight / n;
895 	from = i * height;
896 	if (i+1 == n) {
897 		to = imageHeight - 1;
898 	} else {
899 		to = from + height - 1;
900 	}
901 	if (GetDestImage()->Bounds().Width() >= GetSrcImage()->Bounds().Width()) {
902 		ScaleBilinearFP(from, to);
903 	} else {
904 		DownScaleBilinear(from, to);
905 	}
906 	if (fDither) {
907 		Dither(from, to);
908 	}
909 }
910 
911 void
912 Scaler::Completed()
913 {
914 	if (GetDestImage() != fScaledImage) {
915 		delete fScaledImage;
916 	}
917 	fScaledImage = NULL;
918 }
919 
920 // Implementation of ImageProcessor
921 ImageProcessor::ImageProcessor(enum operation op, BBitmap* image, BMessenger listener, uint32 what)
922 	: Filter(image, listener, what)
923 	, fOp(op)
924 	, fBPP(0)
925 	, fWidth(0)
926 	, fHeight(0)
927 	, fSrcBPR(0)
928 	, fDestBPR(0)
929 
930 {
931 }
932 
933 BBitmap*
934 ImageProcessor::CreateDestImage(BBitmap* /* srcImage */)
935 {
936 	color_space cs;
937 	BBitmap* bm;
938 	BRect rect;
939 
940 	if (GetSrcImage() == NULL) return NULL;
941 
942 	cs = GetSrcImage()->ColorSpace();
943 	fBPP = BytesPerPixel(cs);
944 	if (fBPP < 1) return NULL;
945 
946 	fWidth = GetSrcImage()->Bounds().IntegerWidth();
947 	fHeight = GetSrcImage()->Bounds().IntegerHeight();
948 
949 	if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise) {
950 		rect.Set(0, 0, fHeight, fWidth);
951 	} else {
952 		rect.Set(0, 0, fWidth, fHeight);
953 	}
954 
955 	bm = new BBitmap(rect, cs);
956 	if (!IsBitmapValid(bm)) {
957 		delete bm;
958 		return NULL;
959 	}
960 
961 	fSrcBPR = GetSrcImage()->BytesPerRow();
962 	fDestBPR = bm->BytesPerRow();
963 
964 	return bm;
965 }
966 
967 int32
968 ImageProcessor::GetNumberOfUnits()
969 {
970 	return GetSrcImage()->Bounds().IntegerHeight() + 1;
971 }
972 
973 int32
974 ImageProcessor::BytesPerPixel(color_space cs) const
975 {
976 	switch (cs) {
977 		case B_RGB32:      // fall through
978 		case B_RGB32_BIG:  // fall through
979 		case B_RGBA32:     // fall through
980 		case B_RGBA32_BIG: return 4;
981 
982 		case B_RGB24_BIG:  // fall through
983 		case B_RGB24:      return 3;
984 
985 		case B_RGB16:      // fall through
986 		case B_RGB16_BIG:  // fall through
987 		case B_RGB15:      // fall through
988 		case B_RGB15_BIG:  // fall through
989 		case B_RGBA15:     // fall through
990 		case B_RGBA15_BIG: return 2;
991 
992 		case B_GRAY8:      // fall through
993 		case B_CMAP8:      return 1;
994 		case B_GRAY1:      return 0;
995 		default: return -1;
996 	}
997 }
998 
999 void
1000 ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* src, int32 x, int32 y)
1001 {
1002 	// Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz), replacing
1003 	// the multiplications below with pointer arithmethics showed no speedup at all!
1004 	dest += fDestBPR * destY + destX * fBPP;
1005 	src += fSrcBPR * y + x *fBPP;
1006 	// Replacing memcpy with this switch statement is slightly faster
1007 	switch (fBPP) {
1008 		case 4:
1009 			dest[3] = src[3];
1010 		case 3:
1011 			dest[2] = src[2];
1012 		case 2:
1013 			dest[1] = src[1];
1014 		case 1:
1015 			dest[0] = src[0];
1016 			break;
1017 	}
1018 }
1019 
1020 // Note: For B_CMAP8 InvertPixel inverts the color index not the color value!
1021 void
1022 ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src)
1023 {
1024 	dest += fDestBPR * y + x * fBPP;
1025 	src += fSrcBPR * y + x * fBPP;
1026 	switch (fBPP) {
1027 		case 4:
1028 			// dest[3] = ~src[3]; DON'T invert alpha channel
1029 		case 3:
1030 			dest[2] = ~src[2];
1031 		case 2:
1032 			dest[1] = ~src[1];
1033 		case 1:
1034 			dest[0] = ~src[0];
1035 			break;
1036 	}
1037 }
1038 
1039 // Note: On my systems, the operation kInvert shows a speedup on multiple CPUs only!
1040 void
1041 ImageProcessor::Run(int32 i, int32 n)
1042 {
1043 	int32 from, to;
1044 	int32 height = (fHeight+1) / n;
1045 	from = i * height;
1046 	if (i+1 == n) {
1047 		to = fHeight;
1048 	} else {
1049 		to = from + height - 1;
1050 	}
1051 
1052 	int32 x, y, destX, destY;
1053 	const uchar* src = (uchar*)GetSrcImage()->Bits();
1054 	uchar* dest = (uchar*)GetDestImage()->Bits();
1055 
1056 	switch (fOp) {
1057 		case kRotateClockwise:
1058 			for (y = from; y <= to; y ++) {
1059 				for (x = 0; x <= fWidth; x ++) {
1060 					destX = fHeight - y;
1061 					destY = x;
1062 					CopyPixel(dest, destX, destY, src, x, y);
1063 				}
1064 			}
1065 			break;
1066 		case kRotateCounterClockwise:
1067 			for (y = from; y <= to; y ++) {
1068 				for (x = 0; x <= fWidth; x ++) {
1069 					destX = y;
1070 					destY = fWidth - x;
1071 					CopyPixel(dest, destX, destY, src, x, y);
1072 				}
1073 			}
1074 			break;
1075 		case kFlipTopToBottom:
1076 			for (y = from; y <= to; y ++) {
1077 				for (x = 0; x <= fWidth; x ++) {
1078 					destX = x;
1079 					destY = fHeight - y;
1080 					CopyPixel(dest, destX, destY, src, x, y);
1081 				}
1082 			}
1083 			break;
1084 		case kFlipLeftToRight:
1085 			for (y = from; y <= to; y ++) {
1086 				for (x = 0; x <= fWidth; x ++) {
1087 					destX = fWidth - x;
1088 					destY = y;
1089 					CopyPixel(dest, destX, destY, src, x, y);
1090 				}
1091 			}
1092 			break;
1093 		case kInvert:
1094 			for (y = from; y <= to; y ++) {
1095 				for (x = 0; x <= fWidth; x ++) {
1096 					InvertPixel(x, y, dest, src);
1097 				}
1098 			}
1099 			break;
1100 	}
1101 
1102 }
1103