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