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