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