File: | home/HaikuArchives/ArtPaint/addons/AddOns/ColorReducer/Reducer.cpp |
Warning: | line 107, column 14 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 | #include <Bitmap.h> | |||
10 | #include <Catalog.h> | |||
11 | #include <ClassInfo.h> | |||
12 | #include <LayoutBuilder.h> | |||
13 | #include <Menu.h> | |||
14 | #include <MenuField.h> | |||
15 | #include <MenuItem.h> | |||
16 | #include <PopUpMenu.h> | |||
17 | #include <Screen.h> | |||
18 | #include <StatusBar.h> | |||
19 | #include <StringView.h> | |||
20 | #include <stdio.h> | |||
21 | #include <string.h> | |||
22 | #include <Window.h> | |||
23 | ||||
24 | #include "AddOns.h" | |||
25 | #include "Reducer.h" | |||
26 | #include "color_mapper.h" | |||
27 | #include "palette_generator.h" | |||
28 | #include "ManipulatorInformer.h" | |||
29 | #include "Selection.h" | |||
30 | ||||
31 | #undef B_TRANSLATION_CONTEXT"AddOns_ColorReducer" | |||
32 | #define B_TRANSLATION_CONTEXT"AddOns_ColorReducer" "AddOns_ColorReducer" | |||
33 | ||||
34 | ||||
35 | #ifdef __cplusplus201402L | |||
36 | extern "C" { | |||
37 | #endif | |||
38 | char name[255] = B_TRANSLATE_MARK("Color reducer" B_UTF8_ELLIPSIS)("Color reducer" "\xE2\x80\xA6"); | |||
39 | char menu_help_string[255] | |||
40 | = B_TRANSLATE_MARK("Reduces the number of used colors.")("Reduces the number of used colors."); | |||
41 | int32 add_on_api_version = ADD_ON_API_VERSION; | |||
42 | add_on_types add_on_type = COLOR_ADD_ON; | |||
43 | #ifdef __cplusplus201402L | |||
44 | } | |||
45 | #endif | |||
46 | ||||
47 | ||||
48 | Manipulator* instantiate_add_on(BBitmap *bm,ManipulatorInformer *i) | |||
49 | { | |||
50 | delete i; | |||
51 | return new ReducerManipulator(bm); | |||
52 | } | |||
53 | ||||
54 | ||||
55 | ||||
56 | ReducerManipulator::ReducerManipulator(BBitmap *bm) | |||
57 | : WindowGUIManipulator() | |||
58 | { | |||
59 | preview_bitmap = NULL__null; | |||
60 | config_view = NULL__null; | |||
61 | copy_of_the_preview_bitmap = NULL__null; | |||
62 | ||||
63 | previous_settings.dither_mode = settings.dither_mode + 1; | |||
64 | ||||
65 | SetPreviewBitmap(bm); | |||
66 | } | |||
67 | ||||
68 | ||||
69 | ReducerManipulator::~ReducerManipulator() | |||
70 | { | |||
71 | delete copy_of_the_preview_bitmap; | |||
72 | delete config_view; | |||
73 | } | |||
74 | ||||
75 | ||||
76 | BBitmap* ReducerManipulator::ManipulateBitmap(ManipulatorSettings *set,BBitmap *original,Selection *selection,BStatusBar *status_bar) | |||
77 | { | |||
78 | ReducerManipulatorSettings *new_settings = cast_as(set,ReducerManipulatorSettings)(dynamic_cast<ReducerManipulatorSettings*>(set)); | |||
79 | ||||
80 | if (new_settings == NULL__null) | |||
| ||||
81 | return NULL__null; | |||
82 | ||||
83 | if (original == NULL__null) | |||
84 | return NULL__null; | |||
85 | ||||
86 | BBitmap *source_bitmap,*target_bitmap; | |||
87 | ||||
88 | if (original == preview_bitmap) { | |||
89 | if (*new_settings == previous_settings) | |||
90 | return original; | |||
91 | ||||
92 | source_bitmap = copy_of_the_preview_bitmap; | |||
93 | target_bitmap = original; | |||
94 | } | |||
95 | else { | |||
96 | source_bitmap = original; | |||
97 | target_bitmap = new BBitmap(original->Bounds(),B_RGB32,FALSE0); | |||
98 | } | |||
99 | ||||
100 | BScreen screen; | |||
101 | const rgb_color *palette; | |||
102 | if (new_settings->palette_mode == BEOS_PALETTE) | |||
103 | palette = screen.ColorMap()->color_list; | |||
104 | else if (new_settings->palette_mode == GLA_PALETTE) | |||
105 | palette = gla_palette(source_bitmap,new_settings->palette_size); | |||
106 | ||||
107 | if (palette != NULL__null) | |||
| ||||
108 | do_dither(source_bitmap,target_bitmap,palette,new_settings->palette_size,new_settings->dither_mode); | |||
109 | ||||
110 | return target_bitmap; | |||
111 | } | |||
112 | ||||
113 | int32 ReducerManipulator::PreviewBitmap(Selection *selection,bool full_quality,BRegion *updated_region) | |||
114 | { | |||
115 | if (settings == previous_settings ) { | |||
116 | return 0; | |||
117 | } | |||
118 | config_view->Window()->PostMessage(REDUCER_STARTED'Rest', config_view); | |||
119 | ||||
120 | previous_settings = settings; | |||
121 | ||||
122 | updated_region->Set(preview_bitmap->Bounds()); | |||
123 | ||||
124 | ||||
125 | BBitmap *source_bitmap,*target_bitmap; | |||
126 | target_bitmap = preview_bitmap; | |||
127 | source_bitmap = copy_of_the_preview_bitmap; | |||
128 | ||||
129 | BScreen screen; | |||
130 | const rgb_color *palette; | |||
131 | if (previous_settings.palette_mode == BEOS_PALETTE) | |||
132 | palette = screen.ColorMap()->color_list; | |||
133 | else if (previous_settings.palette_mode == GLA_PALETTE) | |||
134 | palette = gla_palette(source_bitmap,previous_settings.palette_size); | |||
135 | ||||
136 | if (palette != NULL__null) | |||
137 | do_dither(source_bitmap,target_bitmap,palette,previous_settings.palette_size,previous_settings.dither_mode); | |||
138 | ||||
139 | config_view->Window()->PostMessage(REDUCER_FINISHED'Refn', config_view); | |||
140 | return 1; | |||
141 | } | |||
142 | ||||
143 | void ReducerManipulator::MouseDown(BPoint point,uint32,BView*,bool first_click) | |||
144 | { | |||
145 | // This function does nothing in ReducerManipulator. | |||
146 | } | |||
147 | ||||
148 | ||||
149 | void ReducerManipulator::SetPreviewBitmap(BBitmap *bm) | |||
150 | { | |||
151 | if (preview_bitmap != bm) { | |||
152 | delete copy_of_the_preview_bitmap; | |||
153 | if (bm != NULL__null) { | |||
154 | preview_bitmap = bm; | |||
155 | copy_of_the_preview_bitmap = DuplicateBitmap(bm,0); | |||
156 | } | |||
157 | else { | |||
158 | preview_bitmap = NULL__null; | |||
159 | copy_of_the_preview_bitmap = NULL__null; | |||
160 | } | |||
161 | } | |||
162 | } | |||
163 | ||||
164 | ||||
165 | void ReducerManipulator::Reset(Selection*) | |||
166 | { | |||
167 | if (copy_of_the_preview_bitmap != NULL__null) { | |||
168 | // memcpy seems to be about 10-15% faster that copying with a loop. | |||
169 | uint32 *source = (uint32*)copy_of_the_preview_bitmap->Bits(); | |||
170 | uint32 *target = (uint32*)preview_bitmap->Bits(); | |||
171 | uint32 bits_length = preview_bitmap->BitsLength(); | |||
172 | ||||
173 | memcpy(target,source,bits_length); | |||
174 | } | |||
175 | } | |||
176 | ||||
177 | BView* ReducerManipulator::MakeConfigurationView(const BMessenger& target) | |||
178 | { | |||
179 | if (config_view == NULL__null) { | |||
180 | config_view = new ReducerManipulatorView(this,target); | |||
181 | config_view->ChangeSettings(&settings); | |||
182 | } | |||
183 | ||||
184 | return config_view; | |||
185 | } | |||
186 | ||||
187 | ||||
188 | ManipulatorSettings* ReducerManipulator::ReturnSettings() | |||
189 | { | |||
190 | return new ReducerManipulatorSettings(settings); | |||
191 | } | |||
192 | ||||
193 | void ReducerManipulator::ChangeSettings(ManipulatorSettings *s) | |||
194 | { | |||
195 | ReducerManipulatorSettings *new_settings; | |||
196 | new_settings = cast_as(s,ReducerManipulatorSettings)(dynamic_cast<ReducerManipulatorSettings*>(s)); | |||
197 | if (new_settings != NULL__null) { | |||
198 | settings = *new_settings; | |||
199 | } | |||
200 | } | |||
201 | ||||
202 | ||||
203 | const char* ReducerManipulator::ReturnName() | |||
204 | { | |||
205 | return B_TRANSLATE("Color reducer")BLocaleRoster::Default()->GetCatalog()->GetString(("Color reducer" ), "AddOns_ColorReducer"); | |||
206 | } | |||
207 | ||||
208 | const char* ReducerManipulator::ReturnHelpString() | |||
209 | { | |||
210 | return B_TRANSLATE("Reduces the number of used colors.")BLocaleRoster::Default()->GetCatalog()->GetString(("Reduces the number of used colors." ), "AddOns_ColorReducer"); | |||
211 | } | |||
212 | ||||
213 | ||||
214 | void ReducerManipulator::do_dither(BBitmap *source,BBitmap *target,const rgb_color *palette,int palette_size,int32 dither_mode) | |||
215 | { | |||
216 | BBitmap *reduced_map; | |||
217 | if (settings.dither_mode == NO_DITHER) { | |||
218 | reduced_map = nearest_color_mapper(source,palette,palette_size); | |||
219 | } | |||
220 | else if (settings.dither_mode == PRESERVE_SOLIDS_DITHER) { | |||
221 | reduced_map = preserve_solids_fs_color_mapper(source,palette,palette_size); | |||
222 | } | |||
223 | else if (settings.dither_mode == N_CANDIDATE_DITHER) { | |||
224 | reduced_map = n_candidate_color_mapper(source,palette,palette_size,2); | |||
225 | } | |||
226 | else { | |||
227 | reduced_map = floyd_steinberg_edd_color_mapper(source,palette,palette_size); | |||
228 | } | |||
229 | ||||
230 | uint8 *reduced_bits = (uint8*)reduced_map->Bits(); | |||
231 | uint32 reduced_bpr = reduced_map->BytesPerRow(); | |||
232 | ||||
233 | uint32 *destination_bits = (uint32*)target->Bits(); | |||
234 | uint32 destination_bpr = target->BytesPerRow()/4; | |||
235 | ||||
236 | uint32 *source_bits = (uint32*)source->Bits(); | |||
237 | uint32 source_bpr = source->BytesPerRow()/4; | |||
238 | ||||
239 | int32 width = target->Bounds().IntegerWidth(); | |||
240 | int32 height = target->Bounds().IntegerHeight(); | |||
241 | ||||
242 | int32 reduced_padding = reduced_bpr - width - 1; | |||
243 | int32 destination_padding = destination_bpr - width - 1; | |||
244 | int32 source_padding = source_bpr - width - 1; | |||
245 | ||||
246 | // Use this union to guarantee endianness compatibility. | |||
247 | union { | |||
248 | uint8 bytes[4]; | |||
249 | uint32 word; | |||
250 | } bgra32,source_bgra32; | |||
251 | ||||
252 | for (int32 y=0;y<=height;y++) { | |||
253 | for (int32 x=0;x<=width;x++) { | |||
254 | rgb_color c = palette[*reduced_bits++]; | |||
255 | source_bgra32.word = *source_bits++; | |||
256 | bgra32.bytes[0] = c.blue; | |||
257 | bgra32.bytes[1] = c.green; | |||
258 | bgra32.bytes[2] = c.red; | |||
259 | bgra32.bytes[3] = source_bgra32.bytes[3]; | |||
260 | ||||
261 | *destination_bits++ = bgra32.word; | |||
262 | } | |||
263 | destination_bits += destination_padding; | |||
264 | reduced_bits += reduced_padding; | |||
265 | source_bits += source_padding; | |||
266 | } | |||
267 | ||||
268 | delete reduced_map; | |||
269 | } | |||
270 | ||||
271 | ||||
272 | ||||
273 | ||||
274 | ||||
275 | ||||
276 | // ------------------------------------- | |||
277 | ReducerManipulatorView::ReducerManipulatorView(ReducerManipulator *manip, | |||
278 | const BMessenger& t) | |||
279 | : WindowGUIManipulatorView() | |||
280 | { | |||
281 | target = t; | |||
282 | manipulator = manip; | |||
283 | ||||
284 | BMenu *dither_menu = new BPopUpMenu("SELECT"); | |||
285 | ||||
286 | BMessage *message; | |||
287 | message = new BMessage(DITHER_MODE_CHANGED'Dmoc'); | |||
288 | message->AddInt32("dither_mode",NO_DITHER); | |||
289 | dither_menu->AddItem(new BMenuItem(B_TRANSLATE("No dithering")BLocaleRoster::Default()->GetCatalog()->GetString(("No dithering" ), "AddOns_ColorReducer"), message)); | |||
290 | ||||
291 | message = new BMessage(DITHER_MODE_CHANGED'Dmoc'); | |||
292 | message->AddInt32("dither_mode",FLOYD_STEINBERG_EDD_DITHER); | |||
293 | dither_menu->AddItem(new BMenuItem(B_TRANSLATE("Floyd-Steinberg EDD")BLocaleRoster::Default()->GetCatalog()->GetString(("Floyd-Steinberg EDD" ), "AddOns_ColorReducer"), message)); | |||
294 | ||||
295 | message = new BMessage(DITHER_MODE_CHANGED'Dmoc'); | |||
296 | message->AddInt32("dither_mode",PRESERVE_SOLIDS_DITHER); | |||
297 | dither_menu->AddItem(new BMenuItem(B_TRANSLATE("Preserve solids FS")BLocaleRoster::Default()->GetCatalog()->GetString(("Preserve solids FS" ), "AddOns_ColorReducer"), message)); | |||
298 | ||||
299 | message = new BMessage(DITHER_MODE_CHANGED'Dmoc'); | |||
300 | message->AddInt32("dither_mode",N_CANDIDATE_DITHER); | |||
301 | dither_menu->AddItem(new BMenuItem(B_TRANSLATE("N-Candidate")BLocaleRoster::Default()->GetCatalog()->GetString(("N-Candidate" ), "AddOns_ColorReducer"), message)); | |||
302 | ||||
303 | dither_mode_menu_field = new BMenuField( | |||
304 | "dither_mode_menu_field", B_TRANSLATE("Dither mode:")BLocaleRoster::Default()->GetCatalog()->GetString(("Dither mode:" ), "AddOns_ColorReducer"), dither_menu); | |||
305 | ||||
306 | BMenu *size_menu = new BPopUpMenu("SELECT"); // TODO: Find how initialized... | |||
307 | ||||
308 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
309 | message->AddInt32("palette_size",2); | |||
310 | size_menu->AddItem(new BMenuItem("2",message)); | |||
311 | ||||
312 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
313 | message->AddInt32("palette_size",4); | |||
314 | size_menu->AddItem(new BMenuItem("4",message)); | |||
315 | ||||
316 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
317 | message->AddInt32("palette_size",8); | |||
318 | size_menu->AddItem(new BMenuItem("8",message)); | |||
319 | ||||
320 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
321 | message->AddInt32("palette_size",16); | |||
322 | size_menu->AddItem(new BMenuItem("16",message)); | |||
323 | ||||
324 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
325 | message->AddInt32("palette_size",32); | |||
326 | size_menu->AddItem(new BMenuItem("32",message)); | |||
327 | ||||
328 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
329 | message->AddInt32("palette_size",64); | |||
330 | size_menu->AddItem(new BMenuItem("64",message)); | |||
331 | ||||
332 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
333 | message->AddInt32("palette_size",128); | |||
334 | size_menu->AddItem(new BMenuItem("128",message)); | |||
335 | ||||
336 | message = new BMessage(PALETTE_SIZE_CHANGED'Plsc'); | |||
337 | message->AddInt32("palette_size",256); | |||
338 | size_menu->AddItem(new BMenuItem("256",message)); | |||
339 | ||||
340 | palette_size_menu_field = new BMenuField("palette_size_menu_field", | |||
341 | B_TRANSLATE("Palette size:")BLocaleRoster::Default()->GetCatalog()->GetString(("Palette size:" ), "AddOns_ColorReducer"), size_menu); | |||
342 | ||||
343 | BMenu *mode_menu = new BPopUpMenu("SELECT"); | |||
344 | ||||
345 | message = new BMessage(PALETTE_MODE_CHANGED'Plmc'); | |||
346 | message->AddInt32("palette_mode",BEOS_PALETTE); | |||
347 | mode_menu->AddItem(new BMenuItem(B_TRANSLATE("BeOS")BLocaleRoster::Default()->GetCatalog()->GetString(("BeOS" ), "AddOns_ColorReducer"), message)); | |||
348 | ||||
349 | message = new BMessage(PALETTE_MODE_CHANGED'Plmc'); | |||
350 | message->AddInt32("palette_mode",GLA_PALETTE); | |||
351 | mode_menu->AddItem(new BMenuItem(B_TRANSLATE("Generalized Lloyd's Algorithm")BLocaleRoster::Default()->GetCatalog()->GetString(("Generalized Lloyd's Algorithm" ), "AddOns_ColorReducer"), message)); | |||
352 | ||||
353 | palette_mode_menu_field = new BMenuField("palette_mode_menu_field", | |||
354 | B_TRANSLATE("Palette mode:")BLocaleRoster::Default()->GetCatalog()->GetString(("Palette mode:" ), "AddOns_ColorReducer"), mode_menu); | |||
355 | ||||
356 | dither_menu->ItemAt(settings.dither_mode)->SetMarked(true); | |||
357 | size_menu->ItemAt(size_menu->CountItems()-1)->SetMarked(true); // slight hack... | |||
358 | mode_menu->ItemAt(settings.palette_mode)->SetMarked(true); | |||
359 | ||||
360 | busy = new BStringView("busy", B_TRANSLATE("Reducing in progress")BLocaleRoster::Default()->GetCatalog()->GetString(("Reducing in progress" ), "AddOns_ColorReducer")); | |||
361 | busy->SetHighColor(128, 0, 0); | |||
362 | busy->SetViewColor(ViewColor()); | |||
363 | busy->Hide(); | |||
364 | ||||
365 | BGridLayout* gridLayout = | |||
366 | BLayoutBuilder::Grid<>(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING) | |||
367 | .Add(dither_mode_menu_field->CreateLabelLayoutItem(), 0, 0) | |||
368 | .Add(dither_mode_menu_field->CreateMenuBarLayoutItem(), 1, 0) | |||
369 | .Add(palette_size_menu_field->CreateLabelLayoutItem(), 0, 1) | |||
370 | .Add(palette_size_menu_field->CreateMenuBarLayoutItem(), 1, 1) | |||
371 | .Add(palette_mode_menu_field->CreateLabelLayoutItem(), 0, 2) | |||
372 | .Add(palette_mode_menu_field->CreateMenuBarLayoutItem(), 1, 2); | |||
373 | gridLayout->SetMinColumnWidth(1, StringWidth("-YES-THIS-IS-A-REALLY-LONG-STRING--")); | |||
374 | ||||
375 | BLayoutBuilder::Group<>(this, B_VERTICAL) | |||
376 | .Add(gridLayout->View()) | |||
377 | .AddGroup(B_HORIZONTAL) | |||
378 | .AddGlue() | |||
379 | .Add(busy) | |||
380 | .AddGlue() | |||
381 | .End() | |||
382 | .SetInsets(B_USE_SMALL_INSETS) | |||
383 | .End(); | |||
384 | ||||
385 | } | |||
386 | ||||
387 | ||||
388 | ReducerManipulatorView::~ReducerManipulatorView() | |||
389 | { | |||
390 | } | |||
391 | ||||
392 | ||||
393 | void ReducerManipulatorView::AttachedToWindow() | |||
394 | { | |||
395 | WindowGUIManipulatorView::AttachedToWindow(); | |||
396 | } | |||
397 | ||||
398 | void ReducerManipulatorView::AllAttached() | |||
399 | { | |||
400 | dither_mode_menu_field->Menu()->SetTargetForItems(this); | |||
401 | palette_size_menu_field->Menu()->SetTargetForItems(this); | |||
402 | palette_mode_menu_field->Menu()->SetTargetForItems(this); | |||
403 | } | |||
404 | ||||
405 | ||||
406 | void ReducerManipulatorView::MessageReceived(BMessage *message) | |||
407 | { | |||
408 | switch (message->what) { | |||
409 | case DITHER_MODE_CHANGED'Dmoc': | |||
410 | { | |||
411 | int32 mode; | |||
412 | if (message->FindInt32("dither_mode",&mode) == B_OK((int)0)) { | |||
413 | settings.dither_mode = mode; | |||
414 | manipulator->ChangeSettings(&settings); | |||
415 | target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi'); | |||
416 | } | |||
417 | break; | |||
418 | } | |||
419 | ||||
420 | case PALETTE_SIZE_CHANGED'Plsc': | |||
421 | { | |||
422 | int32 size; | |||
423 | if (message->FindInt32("palette_size",&size) == B_OK((int)0)) { | |||
424 | settings.palette_size = size; | |||
425 | manipulator->ChangeSettings(&settings); | |||
426 | target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi'); | |||
427 | } | |||
428 | break; | |||
429 | } | |||
430 | ||||
431 | case PALETTE_MODE_CHANGED'Plmc': | |||
432 | { | |||
433 | int32 mode; | |||
434 | if (message->FindInt32("palette_mode",&mode) == B_OK((int)0)) { | |||
435 | settings.palette_mode = mode; | |||
436 | manipulator->ChangeSettings(&settings); | |||
437 | target.SendMessage(HS_MANIPULATOR_ADJUSTING_FINISHED'Mafi'); | |||
438 | } | |||
439 | break; | |||
440 | } | |||
441 | ||||
442 | case REDUCER_STARTED'Rest': | |||
443 | { | |||
444 | busy->Show(); | |||
445 | break; | |||
446 | } | |||
447 | ||||
448 | case REDUCER_FINISHED'Refn': | |||
449 | { | |||
450 | busy->Hide(); | |||
451 | break; | |||
452 | } | |||
453 | ||||
454 | default: | |||
455 | WindowGUIManipulatorView::MessageReceived(message); | |||
456 | break; | |||
457 | } | |||
458 | } | |||
459 | ||||
460 | ||||
461 | void ReducerManipulatorView::ChangeSettings(ManipulatorSettings *set) | |||
462 | { | |||
463 | ReducerManipulatorSettings *new_settings = cast_as(set,ReducerManipulatorSettings)(dynamic_cast<ReducerManipulatorSettings*>(set)); | |||
464 | ||||
465 | if (set != NULL__null) { | |||
466 | settings = *new_settings; | |||
467 | ||||
468 | BWindow *window = Window(); | |||
469 | if (window != NULL__null) { | |||
470 | window->Lock(); | |||
471 | window->Unlock(); | |||
472 | } | |||
473 | } | |||
474 | } |