File: | home/HaikuArchives/ArtPaint/addons/AddOns/Threshold/Threshold.cpp |
Warning: | line 295, column 56 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 | } |
1 | /* |
2 | * Copyright 2003, Heikki Suhonen |
3 | * Copyright 2009, Karsten Heimrich |
4 | * Distributed under the terms of the MIT License. |
5 | * |
6 | * Authors: |
7 | * Heikki Suhonen <heikki.suhonen@gmail.com> |
8 | * Karsten Heimrich <host.haiku@gmx.de> |
9 | * |
10 | */ |
11 | #ifndef SELECTION_H |
12 | #define SELECTION_H |
13 | |
14 | #include <Bitmap.h> |
15 | #include <View.h> |
16 | |
17 | /* |
18 | Selection-class offers a mechanism for specifying an arbitrarily-shaped, |
19 | possibly disjoint, area from an image. In constructor it gets the size of |
20 | the image. After constructing selections can be added to it or removed |
21 | from it. It can return the selection's pixels sequentially as BPoint's or |
22 | coordinate pairs. It also calculates and returns the bounding rectangle of the |
23 | selected area. It can also return whether a given point belongs to the |
24 | selection or not. |
25 | |
26 | Other major function of selection is drawing the selection to the view. |
27 | The drawing is done with the Draw-function that gets magnifying-scale |
28 | and bounding rectangle as a parameter. If everything is selected, the drawing |
29 | function should do nothing. |
30 | |
31 | The selection works in reverse. It records what is NOT selected. If everything |
32 | is selected, like is the normal case, this will not store any information about |
33 | selection. |
34 | |
35 | If selection is empty when adding the first selection, we make the selection |
36 | first full. |
37 | |
38 | */ |
39 | |
40 | class HSPolygon; |
41 | class SelectionIterator; |
42 | class SelectionData; |
43 | |
44 | |
45 | class Selection { |
46 | // This list holds pointers to HSPolygons. The polygons make up the selections. |
47 | // BList *selections; |
48 | SelectionData *selection_data; |
49 | |
50 | // This is used when rotating the selections to store the original polygons. |
51 | HSPolygon **original_selections; |
52 | |
53 | |
54 | // This is a binary-bitmap that has one bit for each pixel in the image. |
55 | // If a bit is 1 then the corresponding pixel belongs to the selection. |
56 | // Otherwise it doesn't belong to the selection. |
57 | BBitmap *selection_map; |
58 | BView *selection_view; |
59 | |
60 | uint8 *selection_bits; |
61 | uint32 selection_bpr; |
62 | |
63 | // This attribute keeps track of the selection's bounds. If it is |
64 | // not valid it should be calculated again with the calculateBoundingRect |
65 | // function. |
66 | BRect selection_bounds; |
67 | |
68 | // This attribute records the image's bounding rectangle. |
69 | BRect image_bounds; |
70 | |
71 | // This points to the view that we want to draw to. |
72 | BView *image_view; |
73 | |
74 | // // If this is true, the selection is inverted. |
75 | // bool inverted; |
76 | |
77 | bool needs_recalculating; |
78 | |
79 | float view_magnifying_scale; |
80 | |
81 | // This is used to animate the lines that bound the selected area. |
82 | int32 animation_offset; |
83 | |
84 | thread_id drawer_thread; |
85 | bool continue_drawing; |
86 | sem_id selection_mutex; |
87 | |
88 | // This function calculates the smallest rectangle that contains all the points |
89 | // that are selected. It records this fact in the bounding_rect attribute. |
90 | // Bounding rectangle is calcultated when it is needed the first time. |
91 | void calculateBoundingRect(); |
92 | |
93 | // This function deselects everything. |
94 | void deSelect(); |
95 | |
96 | static int32 thread_entry_func(void*); |
97 | int32 thread_func(); |
98 | |
99 | void SimplifySelection(); |
100 | |
101 | public: |
102 | Selection(BRect); |
103 | ~Selection(); |
104 | |
105 | |
106 | void SetSelectionData(const SelectionData*); |
107 | |
108 | |
109 | void StartDrawing(BView*,float); |
110 | // This function adds a polygon to the selection. The point list describes polygons vertices. |
111 | // The polygon is stored as a point list instead of BPolygon so that we can translate and rotate it. |
112 | // The parameter point-list is not copied and thus should not be deleted in the calling function. |
113 | void AddSelection(HSPolygon*,bool add_to_selection=TRUE1); |
114 | |
115 | |
116 | // This function adds a binary bitmap to the selection. |
117 | void AddSelection(BBitmap*,bool add_to_selection); |
118 | |
119 | // This function clears the selection. |
120 | void Clear(); |
121 | |
122 | // This dilates the selection map so that the size of the selection will increase |
123 | void Dilatate(); |
124 | |
125 | // This erodes the selection so that the size of the selection will decrease |
126 | void Erode(); |
127 | |
128 | // This will draw the selection. This function does not care about clipping region. |
129 | void Draw(); |
130 | |
131 | |
132 | // This function returns the bounding rectangle of the selection. |
133 | BRect GetBoundingRect(); |
134 | |
135 | |
136 | // This function inverts the whole selection. If everything is selected, invert does |
137 | // nothing. |
138 | void Invert(); |
139 | |
140 | |
141 | // This function returns true, if the selection is empty (i.e. everything is |
142 | // selected). |
143 | bool IsEmpty(); |
144 | |
145 | // This function rotates the selection. The parameter is in degrees with positive degrees |
146 | // clockwise. This only rotates the selection, not the actual image. |
147 | void RotateTo(BPoint,float); |
148 | |
149 | // This function translates the selection by the amount given in the parameters. |
150 | void Translate(int32,int32); |
151 | |
152 | |
153 | // This recalculates the actual contents of the selection. It can be used for example after |
154 | // rotation or translation. |
155 | void Recalculate(); |
156 | |
157 | |
158 | void ChangeMagnifyingScale(float); |
159 | |
160 | void ImageSizeChanged(BRect); |
161 | |
162 | |
163 | const SelectionData* ReturnSelectionData() { return selection_data; } |
164 | |
165 | // These functions return true if the point in parameter belongs to the selection (i.e. |
166 | // is not DEselected). The BPoint version will also check the bounds rectangle while the |
167 | // int,int version will not check bounds. The latter function can be used in combination |
168 | // with the GetBoundingRect-function. |
169 | inline bool ContainsPoint(BPoint); |
170 | inline bool ContainsPoint(int32,int32); |
171 | }; |
172 | |
173 | |
174 | bool Selection::ContainsPoint(BPoint p) |
175 | { |
176 | int32 y = (int32)p.y; |
177 | int32 x = (int32)p.x; |
178 | |
179 | return ((selection_bits == NULL__null) || (image_bounds.Contains(p) && |
180 | ((*(selection_bits + y * selection_bpr + x)) & 0x01))); |
181 | } |
182 | |
183 | |
184 | bool Selection::ContainsPoint(int32 x, int32 y) |
185 | { |
186 | return ((selection_bits == NULL__null) |
187 | || ((*(selection_bits + y * selection_bpr + x)) & 0x01)); |
188 | } |
189 | |
190 | |
191 | class PointContainer { |
192 | BPoint **hash_table; |
193 | int32 *list_length_table; |
194 | |
195 | int32 hash_value(int32 x, int32 y); |
196 | const int32 hash_table_size; |
197 | public: |
198 | PointContainer(); |
199 | ~PointContainer(); |
200 | |
201 | void InsertPoint(int32 x, int32 y); |
202 | bool HasPoint(int32 x, int32 y); |
203 | }; |
204 | |
205 | |
206 | // This class contains the vital data that describe selection. A selection |
207 | // can be archived with such data and selection can also be modified to represent |
208 | // the same selection as the SelectionData represents. |
209 | class SelectionData { |
210 | HSPolygon **selections; |
211 | int32 selections_array_size; |
212 | int32 number_of_selections; |
213 | public: |
214 | SelectionData(); |
215 | SelectionData(const SelectionData*); |
216 | ~SelectionData(); |
217 | |
218 | bool operator==(const SelectionData&); |
219 | |
220 | void AddSelection(HSPolygon*); |
221 | void EmptySelectionData(); |
222 | int32 SelectionCount() { return number_of_selections; } |
223 | HSPolygon* ReturnSelectionAt(int32 i) { return selections[i]; } |
224 | }; |
225 | |
226 | #endif |