File: | home/HaikuArchives/ArtPaint/addons/AddOns/Contrast/Contrast.cpp |
Warning: | line 241, column 3 Value stored to 'contrast' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright 2003, Heikki Suhonen |
3 | * Distributed under the terms of the MIT License. |
4 | * |
5 | * Authors: |
6 | * Heikki Suhonen <heikki.suhonen@gmail.com> |
7 | * |
8 | */ |
9 | #include <Bitmap.h> |
10 | #include <Catalog.h> |
11 | #include <LayoutBuilder.h> |
12 | #include <Node.h> |
13 | #include <StatusBar.h> |
14 | #include <Slider.h> |
15 | #include <string.h> |
16 | #include <Window.h> |
17 | |
18 | #include "AddOns.h" |
19 | #include "Contrast.h" |
20 | #include "ManipulatorInformer.h" |
21 | #include "Selection.h" |
22 | |
23 | #undef B_TRANSLATION_CONTEXT"AddOns_Contrast" |
24 | #define B_TRANSLATION_CONTEXT"AddOns_Contrast" "AddOns_Contrast" |
25 | |
26 | |
27 | #ifdef __cplusplus201402L |
28 | extern "C" { |
29 | #endif |
30 | char name[255] = B_TRANSLATE_MARK("Contrast" B_UTF8_ELLIPSIS)("Contrast" "\xE2\x80\xA6"); |
31 | char menu_help_string[255] = B_TRANSLATE_MARK("Adjusts the contrast.")("Adjusts the contrast."); |
32 | int32 add_on_api_version = ADD_ON_API_VERSION; |
33 | add_on_types add_on_type = COLOR_ADD_ON; |
34 | #ifdef __cplusplus201402L |
35 | } |
36 | #endif |
37 | |
38 | |
39 | Manipulator* instantiate_add_on(BBitmap *bm,ManipulatorInformer *i) |
40 | { |
41 | delete i; |
42 | return new ContrastManipulator(bm); |
43 | } |
44 | |
45 | |
46 | |
47 | ContrastManipulator::ContrastManipulator(BBitmap *bm) |
48 | : WindowGUIManipulator() |
49 | { |
50 | preview_bitmap = NULL__null; |
51 | config_view = NULL__null; |
52 | copy_of_the_preview_bitmap = NULL__null; |
53 | |
54 | previous_settings.contrast = settings.contrast + 1; |
55 | |
56 | SetPreviewBitmap(bm); |
57 | } |
58 | |
59 | |
60 | ContrastManipulator::~ContrastManipulator() |
61 | { |
62 | delete copy_of_the_preview_bitmap; |
63 | delete config_view; |
64 | } |
65 | |
66 | |
67 | BBitmap* ContrastManipulator::ManipulateBitmap(ManipulatorSettings *set,BBitmap *original,Selection *selection,BStatusBar *status_bar) |
68 | { |
69 | ContrastManipulatorSettings *new_settings = dynamic_cast<ContrastManipulatorSettings*>(set); |
70 | |
71 | if (new_settings == NULL__null) |
72 | return NULL__null; |
73 | |
74 | if (original == NULL__null) |
75 | return NULL__null; |
76 | |
77 | if (original == preview_bitmap) { |
78 | if ((*new_settings == previous_settings) && (last_calculated_resolution <= 1)) |
79 | return original; |
80 | |
81 | source_bitmap = copy_of_the_preview_bitmap; |
82 | target_bitmap = original; |
83 | } |
84 | else { |
85 | source_bitmap = original; |
86 | target_bitmap = new BBitmap(original->Bounds(),B_RGB32,FALSE0); |
87 | } |
88 | |
89 | |
90 | current_resolution = 1; |
91 | current_selection = selection; |
92 | current_settings = *new_settings; |
93 | progress_bar = status_bar; |
94 | current_average_luminance = CalculateAverageLuminance(source_bitmap); |
95 | |
96 | start_threads(); |
97 | |
98 | return target_bitmap; |
99 | } |
100 | |
101 | int32 ContrastManipulator::PreviewBitmap(Selection *selection,bool full_quality,BRegion *updated_region) |
102 | { |
103 | progress_bar = NULL__null; |
104 | current_selection = selection; |
105 | if (settings == previous_settings ) { |
106 | if ((last_calculated_resolution != highest_available_quality) && (last_calculated_resolution > 0)) |
107 | last_calculated_resolution = max_c(highest_available_quality,floor(last_calculated_resolution/2.0))((highest_available_quality)>(floor(last_calculated_resolution /2.0))?(highest_available_quality):(floor(last_calculated_resolution /2.0))); |
108 | else |
109 | last_calculated_resolution = 0; |
110 | } |
111 | else |
112 | last_calculated_resolution = lowest_available_quality; |
113 | |
114 | if (full_quality) { |
115 | last_calculated_resolution = min_c(1,last_calculated_resolution)((1)>(last_calculated_resolution)?(last_calculated_resolution ):(1)); |
116 | } |
117 | previous_settings = settings; |
118 | |
119 | if (last_calculated_resolution > 0) { |
120 | current_resolution = last_calculated_resolution; |
121 | updated_region->Set(preview_bitmap->Bounds()); |
122 | |
123 | target_bitmap = preview_bitmap; |
124 | source_bitmap = copy_of_the_preview_bitmap; |
125 | current_settings = settings; |
126 | |
127 | start_threads(); |
128 | } |
129 | |
130 | return last_calculated_resolution; |
131 | } |
132 | |
133 | |
134 | uint8 ContrastManipulator::CalculateAverageLuminance(BBitmap *bitmap) |
135 | { |
136 | uint32 *bits = (uint32*)bitmap->Bits(); |
137 | uint32 bits_length = bitmap->BitsLength()/4; |
138 | |
139 | double luminance_sum = 0; |
140 | |
141 | union { |
142 | uint8 bytes[4]; |
143 | uint32 word; |
144 | } color; |
145 | |
146 | for (int32 i=0;i<bits_length;i++) { |
147 | color.word = *bits++; |
148 | luminance_sum += 0.299 * color.bytes[0] + 0.587 * color.bytes[1] + 0.144 * color.bytes[2]; |
149 | } |
150 | |
151 | luminance_sum = luminance_sum / bits_length; |
152 | |
153 | uint8 sum = (uint8)luminance_sum; |
154 | |
155 | return sum; |
156 | } |
157 | |
158 | void ContrastManipulator::start_threads() |
159 | { |
160 | number_of_threads = GetSystemCpuCount(); |
161 | |
162 | thread_id *threads = new thread_id[number_of_threads]; |
163 | |
164 | for (int32 i=0;i<number_of_threads;i++) { |
165 | threads[i] = spawn_thread(thread_entrythread_func,"contrast_thread",B_NORMAL_PRIORITY10,this); |
166 | resume_thread(threads[i]); |
167 | send_data(threads[i],i,NULL__null,0); |
168 | } |
169 | |
170 | for (int32 i=0;i<number_of_threads;i++) { |
171 | int32 return_value; |
172 | wait_for_thread(threads[i],&return_value); |
173 | } |
174 | |
175 | delete[] threads; |
176 | } |
177 | |
178 | int32 ContrastManipulator::thread_entrythread_func(void *data) |
179 | { |
180 | int32 thread_number; |
181 | thread_number = receive_data(NULL__null,NULL__null,0); |
182 | |
183 | ContrastManipulator *this_pointer = (ContrastManipulator*)data; |
184 | |
185 | return this_pointer->thread_function(thread_number); |
186 | } |
187 | |
188 | |
189 | int32 ContrastManipulator::thread_function(int32 thread_number) |
190 | { |
191 | // This function interpolates the image with a degenerate version, |
192 | // which in this case is the average luminance. The luminance image is not actually |
193 | // used, but only implied. This function does not touch the alpha-channel. |
194 | |
195 | int32 step = current_resolution; |
196 | uint32 contrast = settings.contrast; |
197 | |
198 | BWindow *progress_bar_window = NULL__null; |
199 | if (progress_bar != NULL__null) |
200 | progress_bar_window = progress_bar->Window(); |
201 | |
202 | |
203 | uint32 *source_bits = (uint32*)source_bitmap->Bits(); |
204 | uint32 *target_bits = (uint32*)target_bitmap->Bits(); |
205 | int32 source_bpr = source_bitmap->BytesPerRow()/4; |
206 | int32 target_bpr = target_bitmap->BytesPerRow()/4; |
207 | |
208 | // This union must be used to guarantee endianness compatibility. |
209 | union { |
210 | uint8 bytes[4]; |
211 | uint32 word; |
212 | } color; |
213 | |
214 | float coeff = current_settings.contrast / 100.0; |
215 | float one_minus_coeff = 1.0 - coeff; |
216 | int32 luminance_factor = current_average_luminance*one_minus_coeff;; |
217 | |
218 | uint8 luminance_values[256]; |
219 | for (int32 i=0;i<256;i++) { |
220 | luminance_values[i] = max_c(0,min_c(255,i * coeff + luminance_factor))((0)>(((255)>(i * coeff + luminance_factor)?(i * coeff + luminance_factor):(255)))?(0):(((255)>(i * coeff + luminance_factor )?(i * coeff + luminance_factor):(255)))); |
221 | } |
222 | |
223 | if (current_selection->IsEmpty()) { |
224 | // Here handle the whole image. |
225 | int32 left = target_bitmap->Bounds().left; |
226 | int32 right = target_bitmap->Bounds().right; |
227 | int32 top = target_bitmap->Bounds().top; |
228 | int32 bottom = target_bitmap->Bounds().bottom; |
229 | |
230 | float height = bottom - top; |
231 | top = height/number_of_threads*thread_number; |
232 | top = ceil(top/(float)step); |
233 | top *= step; |
234 | bottom = min_c(bottom,top + (height+1)/number_of_threads)((bottom)>(top + (height+1)/number_of_threads)?(top + (height +1)/number_of_threads):(bottom)); |
235 | int32 update_interval = 10; |
236 | float update_amount = 100.0/(bottom-top)*update_interval/(float)number_of_threads; |
237 | float missed_update = 0; |
238 | |
239 | // Loop through all pixels in original. |
240 | uint32 sum; |
241 | contrast *= 3; |
Value stored to 'contrast' is never read | |
242 | for (int32 y=top;y<=bottom;y+=step) { |
243 | int32 y_times_source_bpr = y*source_bpr; |
244 | int32 y_times_target_bpr = y*target_bpr; |
245 | for (int32 x=left;x<=right;x+=step) { |
246 | color.word = *(source_bits + x + y_times_source_bpr); |
247 | color.bytes[0] = luminance_values[color.bytes[0]]; |
248 | color.bytes[1] = luminance_values[color.bytes[1]]; |
249 | color.bytes[2] = luminance_values[color.bytes[2]]; |
250 | *(target_bits + x + y_times_target_bpr) = color.word; |
251 | } |
252 | |
253 | // Update the status-bar |
254 | if ( ((y % update_interval) == 0) && (progress_bar_window != NULL__null) && (progress_bar_window->LockWithTimeout(0) == B_OK((int)0)) ) { |
255 | progress_bar->Update(update_amount+missed_update); |
256 | progress_bar_window->Unlock(); |
257 | missed_update = 0; |
258 | } |
259 | else if ((y % update_interval) == 0) { |
260 | missed_update += update_amount; |
261 | } |
262 | } |
263 | } |
264 | else { |
265 | // Here handle only those pixels for which selection->ContainsPoint(x,y) is true. |
266 | BRect rect = current_selection->GetBoundingRect(); |
267 | |
268 | int32 left = rect.left; |
269 | int32 right = rect.right; |
270 | int32 top = rect.top; |
271 | int32 bottom = rect.bottom; |
272 | |
273 | float height = bottom - top; |
274 | top += height/number_of_threads*thread_number; |
275 | top *= step; |
276 | top /= step; |
277 | |
278 | bottom = min_c(bottom,top + (height+1)/number_of_threads)((bottom)>(top + (height+1)/number_of_threads)?(top + (height +1)/number_of_threads):(bottom)); |
279 | |
280 | int32 update_interval = 10; |
281 | float update_amount = 100.0/(bottom-top)*update_interval/(float)number_of_threads; |
282 | |
283 | // Loop through all pixels in original. |
284 | for (int32 y=top;y<=bottom;y+=step) { |
285 | int32 y_times_source_bpr = y*source_bpr; |
286 | int32 y_times_target_bpr = y*target_bpr; |
287 | for (int32 x=left;x<=right;x+=step) { |
288 | if (current_selection->ContainsPoint(x,y)) { |
289 | color.word = *(source_bits + x + y_times_source_bpr); |
290 | color.bytes[0] = luminance_values[color.bytes[0]]; |
291 | color.bytes[1] = luminance_values[color.bytes[1]]; |
292 | color.bytes[2] = luminance_values[color.bytes[2]]; |
293 | *(target_bits + x + y_times_target_bpr) = color.word; |
294 | } |
295 | } |
296 | |
297 | // Update the status-bar |
298 | if ( ((y % update_interval) == 0) && (progress_bar_window != NULL__null) && (progress_bar_window->LockWithTimeout(0) == B_OK((int)0)) ) { |
299 | progress_bar->Update(update_amount); |
300 | progress_bar_window->Unlock(); |
301 | } |
302 | } |
303 | } |
304 | |
305 | return B_OK((int)0); |
306 | } |
307 | |
308 | |
309 | void ContrastManipulator::MouseDown(BPoint point,uint32,BView*,bool first_click) |
310 | { |
311 | // This function does nothing in ContrastManipulator. |
312 | } |
313 | |
314 | |
315 | void ContrastManipulator::SetPreviewBitmap(BBitmap *bm) |
316 | { |
317 | if (preview_bitmap != bm) { |
318 | delete copy_of_the_preview_bitmap; |
319 | if (bm != NULL__null) { |
320 | preview_bitmap = bm; |
321 | copy_of_the_preview_bitmap = DuplicateBitmap(bm,0); |
322 | } |
323 | else { |
324 | preview_bitmap = NULL__null; |
325 | copy_of_the_preview_bitmap = NULL__null; |
326 | } |
327 | } |
328 | |
329 | if (preview_bitmap != NULL__null) { |
330 | // Let's select a resolution that can handle all the pixels at least |
331 | // 10 times in a second while assuming that one pixel calculation takes |
332 | // about 50 CPU cycles. |
333 | double speed = GetSystemClockSpeed() / (10*50); |
334 | BRect bounds = preview_bitmap->Bounds(); |
335 | float num_pixels = (bounds.Width()+1) * (bounds.Height() + 1); |
336 | lowest_available_quality = 1; |
337 | while ((num_pixels/lowest_available_quality/lowest_available_quality) > speed) |
338 | lowest_available_quality *= 2; |
339 | |
340 | lowest_available_quality = min_c(lowest_available_quality,16)((lowest_available_quality)>(16)?(16):(lowest_available_quality )); |
341 | highest_available_quality = max_c(lowest_available_quality/2,1)((lowest_available_quality/2)>(1)?(lowest_available_quality /2):(1)); |
342 | |
343 | current_average_luminance = CalculateAverageLuminance(preview_bitmap); |
344 | } |
345 | else { |
346 | lowest_available_quality = 1; |
347 | highest_available_quality = 1; |
348 | } |
349 | last_calculated_resolution = lowest_available_quality; |
350 | } |
351 | |
352 | |
353 | void ContrastManipulator::Reset(Selection*) |
354 | { |
355 | if (copy_of_the_preview_bitmap != NULL__null) { |
356 | // memcpy seems to be about 10-15% faster that copying with a loop. |
357 | uint32 *source = (uint32*)copy_of_the_preview_bitmap->Bits(); |
358 | uint32 *target = (uint32*)preview_bitmap->Bits(); |
359 | uint32 bits_length = preview_bitmap->BitsLength(); |
360 | |
361 | memcpy(target,source,bits_length); |
362 | } |
363 | } |
364 | |
365 | BView* ContrastManipulator::MakeConfigurationView(const BMessenger& target) |
366 | { |
367 | if (config_view == NULL__null) { |
368 | config_view = new ContrastManipulatorView(this,target); |
369 | config_view->ChangeSettings(&settings); |
370 | } |
371 | |
372 | return config_view; |
373 | } |
374 | |
375 | |
376 | ManipulatorSettings* ContrastManipulator::ReturnSettings() |
377 | { |
378 | return new ContrastManipulatorSettings(settings); |
379 | } |
380 | |
381 | void ContrastManipulator::ChangeSettings(ManipulatorSettings *s) |
382 | { |
383 | ContrastManipulatorSettings *new_settings; |
384 | new_settings = dynamic_cast<ContrastManipulatorSettings*>(s); |
385 | |
386 | if (new_settings != NULL__null) { |
387 | settings = *new_settings; |
388 | } |
389 | } |
390 | |
391 | const char* ContrastManipulator::ReturnName() |
392 | { |
393 | return B_TRANSLATE("Contrast")BLocaleRoster::Default()->GetCatalog()->GetString(("Contrast" ), "AddOns_Contrast"); |
394 | } |
395 | |
396 | const char* ContrastManipulator::ReturnHelpString() |
397 | { |
398 | return B_TRANSLATE("Adjusts the contrast.")BLocaleRoster::Default()->GetCatalog()->GetString(("Adjusts the contrast." ), "AddOns_Contrast"); |
399 | } |
400 | |
401 | |
402 | |
403 | |
404 | // ------------------------------------- |
405 | ContrastManipulatorView::ContrastManipulatorView(ContrastManipulator *manip, |
406 | const BMessenger& t) |
407 | : WindowGUIManipulatorView() |
408 | { |
409 | target = t; |
410 | manipulator = manip; |
411 | started_adjusting = FALSE0; |
412 | |
413 | contrast_slider = new BSlider("contrast_slider", B_TRANSLATE("Contrast:")BLocaleRoster::Default()->GetCatalog()->GetString(("Contrast:" ), "AddOns_Contrast"), |
414 | new BMessage(CONTRAST_ADJUSTING_FINISHED'Thaf'), 0, 255, B_HORIZONTAL, |
415 | B_TRIANGLE_THUMB); |
416 | contrast_slider->SetModificationMessage(new BMessage(CONTRAST_ADJUSTED'Thad')); |
417 | contrast_slider->SetLimitLabels(B_TRANSLATE("Low")BLocaleRoster::Default()->GetCatalog()->GetString(("Low" ), "AddOns_Contrast"), B_TRANSLATE("High")BLocaleRoster::Default()->GetCatalog()->GetString(("High" ), "AddOns_Contrast")); |
418 | contrast_slider->SetHashMarks(B_HASH_MARKS_BOTTOM); |
419 | contrast_slider->SetHashMarkCount(11); |
420 | |
421 | BLayoutBuilder::Group<>(this, B_VERTICAL) |
422 | .Add(contrast_slider) |
423 | .SetInsets(B_USE_SMALL_INSETS) |
424 | .End(); |
425 | } |
426 | |
427 | ContrastManipulatorView::~ContrastManipulatorView() |
428 | { |
429 | } |
430 | |
431 | void ContrastManipulatorView::AttachedToWindow() |
432 | { |
433 | WindowGUIManipulatorView::AttachedToWindow(); |
434 | contrast_slider->SetTarget(BMessenger(this)); |
435 | } |
436 | |
437 | void ContrastManipulatorView::AllAttached() |
438 | { |
439 | contrast_slider->SetValue(settings.contrast); |
440 | } |
441 | |
442 | void ContrastManipulatorView::MessageReceived(BMessage *message) |
443 | { |
444 | switch (message->what) { |
445 | case CONTRAST_ADJUSTED'Thad': |
446 | settings.contrast = contrast_slider->Value(); |
447 | manipulator->ChangeSettings(&settings); |
448 | if (!started_adjusting) { |
449 | target.SendMessage(HS_MANIPULATOR_ADJUSTING_STARTED'Mast'); |
450 | started_adjusting = TRUE1; |
451 | } |
452 | break; |
453 | |
454 | case CONTRAST_ADJUSTING_FINISHED'Thaf': |
455 | started_adjusting = FALSE0; |
456 | settings.contrast = contrast_slider->Value(); |
457 | manipulator->ChangeSettings(&settings); |
458 | target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi'); |
459 | break; |
460 | |
461 | default: |
462 | WindowGUIManipulatorView::MessageReceived(message); |
463 | break; |
464 | } |
465 | } |
466 | |
467 | |
468 | void ContrastManipulatorView::ChangeSettings(ManipulatorSettings *set) |
469 | { |
470 | ContrastManipulatorSettings *new_settings = dynamic_cast<ContrastManipulatorSettings*>(set); |
471 | |
472 | if (set != NULL__null) { |
473 | settings = *new_settings; |
474 | |
475 | BWindow *window = Window(); |
476 | if (window != NULL__null) { |
477 | window->Lock(); |
478 | contrast_slider->SetValue(settings.contrast); |
479 | window->Unlock(); |
480 | } |
481 | } |
482 | } |