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 "Filter.h" 19 20 extern "C" int _kget_cpu_state_(int cpu); 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 (_kget_cpu_state_(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 { 925 } 926 927 BBitmap* 928 ImageProcessor::CreateDestImage(BBitmap* /* srcImage */) 929 { 930 color_space cs; 931 BBitmap* bm; 932 BRect rect; 933 934 if (GetSrcImage() == NULL) return NULL; 935 936 cs = GetSrcImage()->ColorSpace(); 937 fBPP = BytesPerPixel(cs); 938 if (fBPP < 1) return NULL; 939 940 fWidth = GetSrcImage()->Bounds().IntegerWidth(); 941 fHeight = GetSrcImage()->Bounds().IntegerHeight(); 942 943 if (fOp == kRotateClockwise || fOp == kRotateCounterClockwise) { 944 rect.Set(0, 0, fHeight, fWidth); 945 } else { 946 rect.Set(0, 0, fWidth, fHeight); 947 } 948 949 bm = new BBitmap(rect, cs); 950 if (!IsBitmapValid(bm)) { 951 delete bm; 952 return NULL; 953 } 954 955 fSrcBPR = GetSrcImage()->BytesPerRow(); 956 fDestBPR = bm->BytesPerRow(); 957 958 return bm; 959 } 960 961 int32 962 ImageProcessor::GetNumberOfUnits() 963 { 964 return GetSrcImage()->Bounds().IntegerHeight() + 1; 965 } 966 967 int32 968 ImageProcessor::BytesPerPixel(color_space cs) const 969 { 970 switch (cs) { 971 case B_RGB32: // fall through 972 case B_RGB32_BIG: // fall through 973 case B_RGBA32: // fall through 974 case B_RGBA32_BIG: return 4; 975 976 case B_RGB24_BIG: // fall through 977 case B_RGB24: return 3; 978 979 case B_RGB16: // fall through 980 case B_RGB16_BIG: // fall through 981 case B_RGB15: // fall through 982 case B_RGB15_BIG: // fall through 983 case B_RGBA15: // fall through 984 case B_RGBA15_BIG: return 2; 985 986 case B_GRAY8: // fall through 987 case B_CMAP8: return 1; 988 case B_GRAY1: return 0; 989 default: return -1; 990 } 991 } 992 993 void 994 ImageProcessor::CopyPixel(uchar* dest, int32 destX, int32 destY, const uchar* src, int32 x, int32 y) 995 { 996 // Note: On my systems (Dual Intel P3 866MHz and AMD Athlon 1GHz), replacing 997 // the multiplications below with pointer arithmethics showed no speedup at all! 998 dest += fDestBPR * destY + destX * fBPP; 999 src += fSrcBPR * y + x *fBPP; 1000 // Replacing memcpy with this switch statement is slightly faster 1001 switch (fBPP) { 1002 case 4: 1003 dest[3] = src[3]; 1004 case 3: 1005 dest[2] = src[2]; 1006 case 2: 1007 dest[1] = src[1]; 1008 case 1: 1009 dest[0] = src[0]; 1010 break; 1011 } 1012 } 1013 1014 // Note: For B_CMAP8 InvertPixel inverts the color index not the color value! 1015 void 1016 ImageProcessor::InvertPixel(int32 x, int32 y, uchar* dest, const uchar* src) 1017 { 1018 dest += fDestBPR * y + x * fBPP; 1019 src += fSrcBPR * y + x * fBPP; 1020 switch (fBPP) { 1021 case 4: 1022 // dest[3] = ~src[3]; DON'T invert alpha channel 1023 case 3: 1024 dest[2] = ~src[2]; 1025 case 2: 1026 dest[1] = ~src[1]; 1027 case 1: 1028 dest[0] = ~src[0]; 1029 break; 1030 } 1031 } 1032 1033 // Note: On my systems, the operation kInvert shows a speedup on multiple CPUs only! 1034 void 1035 ImageProcessor::Run(int32 i, int32 n) 1036 { 1037 int32 from, to; 1038 int32 height = (fHeight+1) / n; 1039 from = i * height; 1040 if (i+1 == n) { 1041 to = fHeight; 1042 } else { 1043 to = from + height - 1; 1044 } 1045 1046 int32 x, y, destX, destY; 1047 const uchar* src = (uchar*)GetSrcImage()->Bits(); 1048 uchar* dest = (uchar*)GetDestImage()->Bits(); 1049 1050 switch (fOp) { 1051 case kRotateClockwise: 1052 for (y = from; y <= to; y ++) { 1053 for (x = 0; x <= fWidth; x ++) { 1054 destX = fHeight - y; 1055 destY = x; 1056 CopyPixel(dest, destX, destY, src, x, y); 1057 } 1058 } 1059 break; 1060 case kRotateCounterClockwise: 1061 for (y = from; y <= to; y ++) { 1062 for (x = 0; x <= fWidth; x ++) { 1063 destX = y; 1064 destY = fWidth - x; 1065 CopyPixel(dest, destX, destY, src, x, y); 1066 } 1067 } 1068 break; 1069 case kFlipTopToBottom: 1070 for (y = from; y <= to; y ++) { 1071 for (x = 0; x <= fWidth; x ++) { 1072 destX = x; 1073 destY = fHeight - y; 1074 CopyPixel(dest, destX, destY, src, x, y); 1075 } 1076 } 1077 break; 1078 case kFlipLeftToRight: 1079 for (y = from; y <= to; y ++) { 1080 for (x = 0; x <= fWidth; x ++) { 1081 destX = fWidth - x; 1082 destY = y; 1083 CopyPixel(dest, destX, destY, src, x, y); 1084 } 1085 } 1086 break; 1087 case kInvert: 1088 for (y = from; y <= to; y ++) { 1089 for (x = 0; x <= fWidth; x ++) { 1090 InvertPixel(x, y, dest, src); 1091 } 1092 } 1093 break; 1094 } 1095 1096 } 1097