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