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