diff --git a/addons/AddOns/Sharpness/Sharpness.cpp b/addons/AddOns/Sharpness/Sharpness.cpp index d9138263..28ad1ca0 100644 --- a/addons/AddOns/Sharpness/Sharpness.cpp +++ b/addons/AddOns/Sharpness/Sharpness.cpp @@ -21,6 +21,9 @@ #include "Selection.h" #include "Sharpness.h" + +#include + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "AddOns_Sharpness" @@ -104,7 +107,7 @@ SharpnessManipulator::ManipulateBitmap( delete blurred_image; blurred_image = DuplicateBitmap(source_bitmap, 0); - ipLibrary->gaussian_blur(blurred_image, BLUR_AMOUNT); + ipLibrary->fast_gaussian_blur(blurred_image, BLUR_AMOUNT, 1); // CalculateLuminanceImage(source_bitmap); start_threads(); @@ -136,12 +139,11 @@ SharpnessManipulator::PreviewBitmap(bool full_quality, BRegion* updated_region) if (full_quality) last_calculated_resolution = min_c(1, last_calculated_resolution); - previous_settings = settings; + //previous_settings = settings; if (last_calculated_resolution > 0) { current_resolution = last_calculated_resolution; updated_region->Set(preview_bitmap->Bounds()); - target_bitmap = preview_bitmap; source_bitmap = copy_of_the_preview_bitmap; current_settings = settings; @@ -149,6 +151,8 @@ SharpnessManipulator::PreviewBitmap(bool full_quality, BRegion* updated_region) start_threads(); } + previous_settings = settings; + return last_calculated_resolution; } @@ -192,6 +196,21 @@ SharpnessManipulator::thread_function(int32 thread_number) // which in this case is the luminance image. The luminance image // is in bitmap blurred_image, which is in B_CMAP8 color-space. + if (current_settings.sharpness == previous_settings.sharpness) + { + int32 blur_size = source_bitmap->Bounds().Width() * current_settings.blur_size / 1000; + if (blur_size < 1) + blur_size = 1; + BBitmap* tmp_bitmap = DuplicateBitmap(copy_of_the_preview_bitmap, 0); + ipLibrary->fast_gaussian_blur(tmp_bitmap, blur_size, + processor_count, current_resolution); + int32* d_bits = (int32*)blurred_image->Bits(); + int32* s_bits = (int32*)tmp_bitmap->Bits(); + int32 bits_length = tmp_bitmap->BitsLength(); + memcpy(d_bits, s_bits, bits_length); + delete tmp_bitmap; + } + int32 step = current_resolution; float sharpness_coeff = current_settings.sharpness / 100.0; @@ -199,7 +218,6 @@ SharpnessManipulator::thread_function(int32 thread_number) if (progress_bar != NULL) progress_bar_window = progress_bar->Window(); - uint32* source_bits = (uint32*)source_bitmap->Bits(); uint32* target_bits = (uint32*)target_bitmap->Bits(); int32 source_bpr = source_bitmap->BytesPerRow() / 4; @@ -329,7 +347,7 @@ SharpnessManipulator::SetPreviewBitmap(BBitmap* bm) preview_bitmap = bm; copy_of_the_preview_bitmap = DuplicateBitmap(bm, 0); blurred_image = DuplicateBitmap(preview_bitmap, 0); - ipLibrary->gaussian_blur(blurred_image, settings.blur_size, processor_count); + ipLibrary->fast_gaussian_blur(blurred_image, settings.blur_size, processor_count); } else { preview_bitmap = NULL; copy_of_the_preview_bitmap = NULL; @@ -340,15 +358,15 @@ SharpnessManipulator::SetPreviewBitmap(BBitmap* bm) if (preview_bitmap != NULL) { // Let's select a resolution that can handle all the pixels at least // 10 times in a second while assuming that one pixel calculation takes - // about 50 CPU cycles. - double speed = GetSystemClockSpeed() / (10 * 50); + // about 250 CPU cycles. + double speed = GetSystemClockSpeed() / (10 * 250); BRect bounds = preview_bitmap->Bounds(); float num_pixels = (bounds.Width() + 1) * (bounds.Height() + 1); lowest_available_quality = 1; - while ((num_pixels / lowest_available_quality / lowest_available_quality) > speed) + while ((num_pixels / lowest_available_quality) > speed) lowest_available_quality *= 2; - lowest_available_quality = min_c(lowest_available_quality, 16); + lowest_available_quality = min_c(lowest_available_quality, 32); highest_available_quality = max_c(lowest_available_quality / 2, 1); } else { lowest_available_quality = 1; @@ -396,18 +414,8 @@ SharpnessManipulator::ChangeSettings(ManipulatorSettings* s) { SharpnessManipulatorSettings* new_settings; new_settings = dynamic_cast(s); - if (new_settings != NULL) { - if (new_settings->blur_size != settings.blur_size) { - int32* d_bits = (int32*)blurred_image->Bits(); - int32* s_bits = (int32*)copy_of_the_preview_bitmap->Bits(); - int32 bits_length = blurred_image->BitsLength(); - - memcpy(d_bits, s_bits, bits_length); - ipLibrary->gaussian_blur(blurred_image, new_settings->blur_size, processor_count); - } - + if (new_settings != NULL) settings = *new_settings; - } } @@ -441,7 +449,8 @@ SharpnessManipulatorView::SharpnessManipulatorView(SharpnessManipulator* manip, sharpness_slider->SetHashMarkCount(11); blur_size_slider = new BSlider("blur_size_slider", B_TRANSLATE("Effect strength:"), - new BMessage(BLUR_ADJUSTING_FINISHED), 1, 50, B_HORIZONTAL, B_TRIANGLE_THUMB); + new BMessage(BLUR_ADJUSTING_FINISHED), 1, 100, B_HORIZONTAL, B_TRIANGLE_THUMB); + blur_size_slider->SetModificationMessage(new BMessage(BLUR_ADJUSTED)); blur_size_slider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High")); blur_size_slider->SetHashMarks(B_HASH_MARKS_BOTTOM); blur_size_slider->SetHashMarkCount(11); @@ -460,6 +469,7 @@ SharpnessManipulatorView::AttachedToWindow() WindowGUIManipulatorView::AttachedToWindow(); sharpness_slider->SetTarget(BMessenger(this)); blur_size_slider->SetTarget(BMessenger(this)); + blur_size_slider->SetSnoozeAmount(30000); } @@ -491,8 +501,18 @@ SharpnessManipulatorView::MessageReceived(BMessage* message) manipulator->ChangeSettings(&settings); target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED); } break; + case BLUR_ADJUSTED: + { + settings.blur_size = blur_size_slider->Value(); + manipulator->ChangeSettings(&settings); + if (!started_adjusting) { + target.SendMessage(HS_MANIPULATOR_ADJUSTING_STARTED); + started_adjusting = TRUE; + } + } case BLUR_ADJUSTING_FINISHED: { + started_adjusting = FALSE; settings.blur_size = blur_size_slider->Value(); manipulator->ChangeSettings(&settings); target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED); diff --git a/addons/AddOns/Sharpness/Sharpness.h b/addons/AddOns/Sharpness/Sharpness.h index 3069c7ad..4b7f7d4a 100644 --- a/addons/AddOns/Sharpness/Sharpness.h +++ b/addons/AddOns/Sharpness/Sharpness.h @@ -116,6 +116,7 @@ class SharpnessManipulator : public WindowGUIManipulator { #define SHARPNESS_ADJUSTED 'Shad' #define SHARPNESS_ADJUSTING_FINISHED 'Shaf' +#define BLUR_ADJUSTED 'Blad' #define BLUR_ADJUSTING_FINISHED 'Blaf' diff --git a/addons/UtilityClasses/ImageProcessingLibrary.cpp b/addons/UtilityClasses/ImageProcessingLibrary.cpp index 0c5647b3..118d0928 100644 --- a/addons/UtilityClasses/ImageProcessingLibrary.cpp +++ b/addons/UtilityClasses/ImageProcessingLibrary.cpp @@ -58,7 +58,7 @@ ImageProcessingLibrary::gaussian_blur(BBitmap* bitmap, float radius) for (int32 dx = 0; dx < kernel_radius; dx++) *source_array_position++ = *b_bits; - for (int32 dx = 0; dx < width; dx++) + for (int32 dx = 0; dx < width; dx++) *source_array_position++ = *b_bits++; b_bits--; @@ -400,6 +400,380 @@ ImageProcessingLibrary::convolve_1d_fixed( } } +/* + +Box blur and Fast Gaussian blur + +*/ + +int* boxes_for_gauss(float sigma, int n) +{ + float wIdeal = sqrt((12. * sigma * sigma / n) + 1); + int wl = floor(wIdeal); + if (wl % 2 == 0) + --wl; + int wu = wl + 2; + + float mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) + / (-4 * wl - 4); + int m = round(mIdeal); + + int* boxes = new int[n]; + for (int i = 0; i < n; ++i) + if (i < m) + boxes[i] = wl; + else + boxes[i] = wu; + + return boxes; +} + + +status_t +ImageProcessingLibrary::box_blur(BBitmap* bitmap, float radius, int32 threadCount, + int32 resolution) +{ + int32 kernel_radius = ceil(radius); + + BRect bitmap_bounds = bitmap->Bounds(); + BRect intermediate_bounds(0, 0, bitmap_bounds.Width(), bitmap_bounds.Height()); + BBitmap* intermediate = new BBitmap(intermediate_bounds, B_RGB32, false); + intermediate->SetBits(bitmap->Bits(), bitmap->BitsLength(), 0, B_RGB32); + + int32* s_bits = (int32*)bitmap->Bits(); + int32 s_bpr = bitmap->BytesPerRow() / 4; + + int32* d_bits = (int32*)intermediate->Bits(); + int32 d_bpr = intermediate->BytesPerRow() / 4; + + // Start the threads. + thread_id blur_thread_array[8]; // TODO - Was B_MAX_CPU_COUNT, find way to decide amount of + // threads to use based on cpu count + for (int32 i = 0; i < threadCount; i++) { + int32 height = bitmap_bounds.Height() / threadCount + 1; + filter_thread_data* data = new filter_thread_data(); + + int32 top; + top = data->top = min_c(bitmap_bounds.bottom, bitmap_bounds.top + i * height); + data->bottom = min_c(bitmap_bounds.bottom, top + height - 1); + + data->s_bpr = s_bpr; + data->s_bits = s_bits + top * s_bpr; + data->d_bpr = d_bpr; + data->d_bits = d_bits; + + data->kernel_radius = kernel_radius; + data->resolution = resolution; + + blur_thread_array[i] = spawn_thread( + start_filter_box_blur_thread_h, "box_blur_h_thread", B_NORMAL_PRIORITY, data); + resume_thread(blur_thread_array[i]); + } + + for (int32 i = 0; i < threadCount; i++) { + int32 return_value; + wait_for_thread(blur_thread_array[i], &return_value); + } + + s_bits = (int32*)intermediate->Bits(); + s_bpr = intermediate->BytesPerRow() / 4; + + d_bits = (int32*)bitmap->Bits(); + d_bpr = bitmap->BytesPerRow() / 4; + + // Here blur from intermediate to bitmap horizontally and rotate + // by 90 degrees counterclockwise + // Start the threads. + for (int32 i = 0; i < threadCount; i++) { + int32 height = intermediate_bounds.Height() / threadCount + 1; + filter_thread_data* data = new filter_thread_data(); + + int32 top; + top = data->top = min_c(intermediate_bounds.bottom, intermediate_bounds.top + i * height); + data->bottom = min_c(intermediate_bounds.bottom, top + height - 1); + + data->s_bpr = s_bpr; + data->s_bits = s_bits + top * s_bpr; + data->d_bpr = d_bpr; + data->d_bits = d_bits + top * d_bpr; + + data->kernel_radius = kernel_radius; + data->resolution = resolution; + + blur_thread_array[i] = spawn_thread( + start_filter_box_blur_thread_t, "box_blur_t_thread", B_NORMAL_PRIORITY, data); + resume_thread(blur_thread_array[i]); + } + + for (int32 i = 0; i < threadCount; i++) { + int32 return_value; + wait_for_thread(blur_thread_array[i], &return_value); + } + + delete intermediate; + + return B_OK; +} + + +int32 +ImageProcessingLibrary::start_filter_box_blur_thread_h(void* d) +{ + filter_thread_data* data = (filter_thread_data*)d; + + box_blur_h(data->s_bits, data->d_bits, data->s_bpr, data->bottom - data->top, + data->kernel_radius, data->resolution); + + delete data; + + return B_OK; +} + + +int32 +ImageProcessingLibrary::start_filter_box_blur_thread_t(void* d) +{ + filter_thread_data* data = (filter_thread_data*)d; + + box_blur_t(data->s_bits, data->d_bits, data->s_bpr, data->bottom - data->top, + data->kernel_radius, data->resolution); + + delete data; + + return B_OK; +} + + +void +ImageProcessingLibrary::box_blur_h(int32* s_bits, int32* d_bits, int32 width, + int32 height, int32 kernel_radius, int32 resolution) +{ + union { + uint8 bytes[4]; + uint32 word; + } first_val, last_val, tmp, tmp2, tmp3; + + uint32 val[4]; + + float i_arr = 1. / (kernel_radius + kernel_radius + 1); + + int res = resolution; + + for (int i = 0; i < height; i += res) { + int ti = i * width; + int li = ti; + int ri = ti + kernel_radius; + + first_val.word = *(s_bits + ti); + last_val.word = *(s_bits + ti + width - 1); + + val[0] = ((kernel_radius + 1) * first_val.bytes[0]); + val[1] = ((kernel_radius + 1) * first_val.bytes[1]); + val[2] = ((kernel_radius + 1) * first_val.bytes[2]); + val[3] = ((kernel_radius + 1) * first_val.bytes[3]); + + for (int j = 0; j < kernel_radius; j += 1) { + tmp.word = *(s_bits + ti + j); + val[0] = (val[0] + tmp.bytes[0]); + val[1] = (val[1] + tmp.bytes[1]); + val[2] = (val[2] + tmp.bytes[2]); + val[3] = (val[3] + tmp.bytes[3]); + } + + for (int j = 0; j <= kernel_radius; j += 1) { + tmp.word = *(s_bits + ri); + val[0] = (val[0] + tmp.bytes[0] - first_val.bytes[0]); + val[1] = (val[1] + tmp.bytes[1] - first_val.bytes[1]); + val[2] = (val[2] + tmp.bytes[2] - first_val.bytes[2]); + val[3] = (val[3] + tmp.bytes[3] - first_val.bytes[3]); + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) { + if (i + k < height) + *(d_bits + ti + (k * width)) = uint32(tmp2.word); + } + //*(d_bits + ti) = uint32(tmp2.word); + ++ri; + ++ti; + } + + for (int j = kernel_radius + 1; j < width - kernel_radius; j += 1) { + tmp.word = *(s_bits + ri); + tmp3.word = *(s_bits + li); + val[0] += tmp.bytes[0] - tmp3.bytes[0]; + val[1] += tmp.bytes[1] - tmp3.bytes[1]; + val[2] += tmp.bytes[2] - tmp3.bytes[2]; + val[3] += tmp.bytes[3] - tmp3.bytes[3]; + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) { + if (i + k < height) + *(d_bits + ti + (k * width)) = uint32(tmp2.word); + } + //*(d_bits + ti) = uint32(tmp2.word); + ++ri; + ++li; + ++ti; + } + + for (int j = width - kernel_radius; j < width; j += 1) { + tmp3.word = *(s_bits + li); + + val[0] += last_val.bytes[0] - tmp3.bytes[0]; + val[1] += last_val.bytes[1] - tmp3.bytes[1]; + val[2] += last_val.bytes[2] - tmp3.bytes[2]; + val[3] += last_val.bytes[3] - tmp3.bytes[3]; + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) { + if (i + k < height) + *(d_bits + ti + (k * width)) = uint32(tmp2.word); + } + //*(d_bits + ti) = uint32(tmp2.word); + ++li; + ++ti; + } + } +} + + +void +ImageProcessingLibrary::box_blur_t(int32* s_bits, int32* d_bits, int32 width, + int32 height, int32 kernel_radius, int32 resolution) +{ + union { + uint8 bytes[4]; + uint32 word; + } first_val, last_val, tmp, tmp2, tmp3; + + uint32 val[4]; + + float i_arr = 1. / (kernel_radius + kernel_radius + 1); + + int res = resolution; + + for (int i = 0; i < width - 1; i += res) { + int ti = i; + int li = ti; + int ri = ti + kernel_radius * width; + + first_val.word = *(s_bits + ti); + last_val.word = *(s_bits + ti + width * (height - 1)); + + val[0] = ((kernel_radius + 1) * first_val.bytes[0]); + val[1] = ((kernel_radius + 1) * first_val.bytes[1]); + val[2] = ((kernel_radius + 1) * first_val.bytes[2]); + val[3] = ((kernel_radius + 1) * first_val.bytes[3]); + + for (int j = 0; j < kernel_radius; j += 1) { + tmp.word = *(s_bits + ti + j * width); + val[0] = (val[0] + tmp.bytes[0]); + val[1] = (val[1] + tmp.bytes[1]); + val[2] = (val[2] + tmp.bytes[2]); + val[3] = (val[3] + tmp.bytes[3]); + } + + for (int j = 0; j <= kernel_radius; j += 1) { + tmp.word = *(s_bits + ri); + val[0] = (val[0] + tmp.bytes[0] - first_val.bytes[0]); + val[1] = (val[1] + tmp.bytes[1] - first_val.bytes[1]); + val[2] = (val[2] + tmp.bytes[2] - first_val.bytes[2]); + val[3] = (val[3] + tmp.bytes[3] - first_val.bytes[3]); + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) + *(d_bits + ti + k) = uint32(tmp2.word); + ri += width; + ti += width; + } + + for (int j = kernel_radius + 1; j < height - kernel_radius; j += 1) { + tmp.word = *(s_bits + ri); + tmp3.word = *(s_bits + li); + val[0] += tmp.bytes[0] - tmp3.bytes[0]; + val[1] += tmp.bytes[1] - tmp3.bytes[1]; + val[2] += tmp.bytes[2] - tmp3.bytes[2]; + val[3] += tmp.bytes[3] - tmp3.bytes[3]; + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) + *(d_bits + ti + k) = uint32(tmp2.word); + ri += width; + li += width; + ti += width; + } + + for (int j = height - kernel_radius; j < height; j += 1) { + tmp3.word = *(s_bits + li); + + val[0] += last_val.bytes[0] - tmp3.bytes[0]; + val[1] += last_val.bytes[1] - tmp3.bytes[1]; + val[2] += last_val.bytes[2] - tmp3.bytes[2]; + val[3] += last_val.bytes[3] - tmp3.bytes[3]; + + tmp2.bytes[0] = uint32(val[0] * i_arr) & 0xff; + tmp2.bytes[1] = uint32(val[1] * i_arr) & 0xff; + tmp2.bytes[2] = uint32(val[2] * i_arr) & 0xff; + tmp2.bytes[3] = uint32(val[3] * i_arr) & 0xff; + + for (int k = 0; k < res; ++k) + *(d_bits + ti + k) = uint32(tmp2.word); + li += width; + ti += width * 1; + } + } +} + + +status_t +ImageProcessingLibrary::fast_gaussian_blur(BBitmap* bitmap, float radius, int32 threadCount, + int32 resolution) +{ + int* boxes = boxes_for_gauss((radius - 1) / 4., 3); + + box_blur(bitmap, (boxes[0] - 1.)/2., threadCount, resolution); + box_blur(bitmap, (boxes[1] - 1.)/2., threadCount, resolution); + //box_blur(bitmap, (boxes[2] - 1.)/2., threadCount, resolution); + + delete[] boxes; + + return B_OK; +} + + +status_t +ImageProcessingLibrary::fast_gaussian_blur(BBitmap* bitmap, float radius, int32 resolution) +{ + int* boxes = boxes_for_gauss((radius - 1) / 4., 3); + + box_blur(bitmap, (boxes[0] - 1.)/2., 1, resolution); + box_blur(bitmap, (boxes[1] - 1.)/2., 1, resolution); + //box_blur(bitmap, (boxes[2] - 1.)/2., 1, resolution); + + delete[] boxes; + + return B_OK; +} /* diff --git a/addons/UtilityClasses/ImageProcessingLibrary.h b/addons/UtilityClasses/ImageProcessingLibrary.h index 6f01450b..2c732a74 100644 --- a/addons/UtilityClasses/ImageProcessingLibrary.h +++ b/addons/UtilityClasses/ImageProcessingLibrary.h @@ -18,6 +18,10 @@ class ImageProcessingLibrary { status_t gaussian_blur(BBitmap *bitmap,float radius); status_t gaussian_blur(BBitmap *bitmap,float radius,int32 thread_count); +status_t box_blur(BBitmap* bitmap, float radius, int32 thread_count, int32 resolution); + +status_t fast_gaussian_blur(BBitmap* bitmap, float radius, int32 resolution); +status_t fast_gaussian_blur(BBitmap* bitmap, float radius, int32 thread_count, int32 resolution); status_t grayscale_ahe(BBitmap *bitmap,int32 regionSize); status_t grayscale_clahe(BBitmap *bitmap,int32 regionSize,int32 clipLimit); @@ -30,9 +34,17 @@ static void convolve_1d_fixed(uint32 *s,uint32 *t,int32 length,int32 *kernel,in static int32 start_filter_1d_thread_clockwise(void*); static int32 start_filter_1d_thread_counterclockwise(void*); +static int32 start_filter_box_blur_thread_h(void*); +static int32 start_filter_box_blur_thread_t(void*); + static void filter_1d_and_rotate_clockwise(int32 *s_bits,int32 s_bpr,int32 *d_bits,int32 d_bpr,int32 left,int32 right,int32 top,int32 bottom,int32 *kernel,int32 kernel_radius); static void filter_1d_and_rotate_counterclockwise(int32 *s_bits,int32 s_bpr,int32 *d_bits,int32 d_bpr,int32 left,int32 right,int32 top,int32 bottom,int32 *kernel,int32 kernel_radius); +static void box_blur_h(int32* s_bits, int32* d_bits, int32 width, int32 height, + int32 kernel_radius, int32 resolution); +static void box_blur_t(int32* s_bits, int32* d_bits, int32 width, int32 height, + int32 kernel_radius, int32 resolution); + // grayscale ahe stuff static void calculate_local_mapping_function(BBitmap *bitmap, @@ -56,6 +68,7 @@ struct filter_thread_data { int32 bottom; int32 *kernel; int32 kernel_radius; + int32 resolution; }; #endif