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