xref: /haiku/src/apps/icon-o-matic/generic/gui/panel/color_picker/ColorField.cpp (revision f068ecea0a72f16044bfa3cb932d57576fa622d3)
1 /*
2  * Copyright 2001 Werner Freytag - please read to the LICENSE file
3  *
4  * Copyright 2002-2006, Stephan Aßmus <superstippi@gmx.de>
5  * All rights reserved.
6  *
7  */
8 
9 #include "ColorField.h"
10 
11 #include <stdio.h>
12 
13 #include <Bitmap.h>
14 #include <ControlLook.h>
15 #include <LayoutUtils.h>
16 #include <OS.h>
17 #include <Region.h>
18 #include <Window.h>
19 
20 #include "support_ui.h"
21 
22 #include "rgb_hsv.h"
23 
24 #define round(x) (int)(x +.5)
25 
26 enum {
27 	MSG_UPDATE			= 'Updt',
28 };
29 
30 #define MAX_X 255
31 #define MAX_Y 255
32 
33 // constructor
ColorField(BPoint offsetPoint,SelectedColorMode mode,float fixedValue,orientation orient,border_style border)34 ColorField::ColorField(BPoint offsetPoint, SelectedColorMode mode,
35 	float fixedValue, orientation orient, border_style border)
36 	: BControl(BRect(0.0, 0.0, MAX_X + 4.0, MAX_Y + 4.0)
37 			.OffsetToCopy(offsetPoint),
38 		"ColorField", "", new BMessage(MSG_COLOR_FIELD),
39 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS)
40 {
41 	_Init(mode, fixedValue, orient, border);
42 	FrameResized(Bounds().Width(), Bounds().Height());
43 }
44 
45 // constructor
ColorField(SelectedColorMode mode,float fixedValue,orientation orient,border_style border)46 ColorField::ColorField(SelectedColorMode mode, float fixedValue,
47 	orientation orient, border_style border)
48 	: BControl("ColorField", "", new BMessage(MSG_COLOR_FIELD),
49 		B_WILL_DRAW | B_FRAME_EVENTS)
50 {
51 	_Init(mode, fixedValue, orient, border);
52 }
53 
54 // destructor
~ColorField()55 ColorField::~ColorField()
56 {
57 	delete fBitmap;
58 }
59 
60 // MinSize
61 BSize
MinSize()62 ColorField::MinSize()
63 {
64 	BSize minSize;
65 	if (fOrientation == B_VERTICAL)
66 		minSize = BSize(4 + MAX_X / 17, 4 + MAX_Y / 5);
67 	else
68 		minSize = BSize(4 + MAX_X / 5, 4 + MAX_Y / 17);
69 
70 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize);
71 }
72 
73 // PreferredSize
74 BSize
PreferredSize()75 ColorField::PreferredSize()
76 {
77 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
78 }
79 
80 // MaxSize
81 BSize
MaxSize()82 ColorField::MaxSize()
83 {
84 	BSize maxSize(4 + MAX_X, 4 + MAX_Y);
85 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
86 //	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
87 //		BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
88 }
89 
90 // Invoke
91 status_t
Invoke(BMessage * message)92 ColorField::Invoke(BMessage* message)
93 {
94 	if (message == NULL)
95 		message = Message();
96 
97 	if (message == NULL)
98 		return BControl::Invoke(message);
99 
100 	message->RemoveName("value");
101 
102 	float v1 = 0;
103 	float v2 = 0;
104 
105 	switch (fMode) {
106 		case R_SELECTED:
107 		case G_SELECTED:
108 		case B_SELECTED:
109 			v1 = fMarkerPosition.x / Width();
110 			v2 = 1.0 - fMarkerPosition.y / Height();
111 			break;
112 
113 		case H_SELECTED:
114 			if (fOrientation == B_VERTICAL) {
115 				v1 = fMarkerPosition.x / Width();
116 				v2 = 1.0 - fMarkerPosition.y / Height();
117 			} else {
118 				v1 = fMarkerPosition.y / Height();
119 				v2 = 1.0 - fMarkerPosition.x / Width();
120 			}
121 			break;
122 
123 		case S_SELECTED:
124 		case V_SELECTED:
125 			v1 = fMarkerPosition.x / Width() * 6.0;
126 			v2 = 1.0 - fMarkerPosition.y / Height();
127 			break;
128 
129 	}
130 
131 	message->AddFloat("value", v1);
132 	message->AddFloat("value", v2);
133 
134 	return BControl::Invoke(message);
135 }
136 
137 // AttachedToWindow
138 void
AttachedToWindow()139 ColorField::AttachedToWindow()
140 {
141 }
142 
143 // Draw
144 void
Draw(BRect updateRect)145 ColorField::Draw(BRect updateRect)
146 {
147 	if (fBitmapDirty && fBitmap != NULL) {
148 		_FillBitmap(fBitmap, fMode, fFixedValue, fOrientation);
149 		fBitmapDirty = false;
150 	}
151 
152 	BRect bounds = Bounds();
153 
154 	// Frame
155 	if (fBorderStyle == B_FANCY_BORDER) {
156 		rgb_color color = LowColor();
157 		be_control_look->DrawTextControlBorder(this, bounds, updateRect,
158 			color);
159 		BRegion region(bounds);
160 		ConstrainClippingRegion(&region);
161 	}
162 
163 	// Color field fill
164 	if (fBitmap != NULL)
165 		DrawBitmap(fBitmap, bounds.LeftTop());
166 	else {
167 		SetHighColor(255, 0, 0);
168 		FillRect(bounds);
169 	}
170 
171 	// Marker
172 	SetHighColor(0, 0, 0);
173 	StrokeEllipse(fMarkerPosition + bounds.LeftTop(), 5.0, 5.0);
174 	SetHighColor(255.0, 255.0, 255.0);
175 	StrokeEllipse(fMarkerPosition + bounds.LeftTop(), 4.0, 4.0);
176 }
177 
178 // FrameResized
179 void
FrameResized(float width,float height)180 ColorField::FrameResized(float width, float height)
181 {
182 	BRect r = _BitmapRect();
183 	_AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
184 	Invalidate();
185 }
186 
187 // MouseDown
188 void
MouseDown(BPoint where)189 ColorField::MouseDown(BPoint where)
190 {
191 	fMouseDown = true;
192 	SetMouseEventMask(B_POINTER_EVENTS,
193 		B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
194 	PositionMarkerAt(where);
195 
196 	if (Message() != NULL) {
197 		BMessage message(*Message());
198 		message.AddBool("begin", true);
199 		Invoke(&message);
200 	} else
201 		Invoke();
202 }
203 
204 // MouseUp
205 void
MouseUp(BPoint where)206 ColorField::MouseUp(BPoint where)
207 {
208 	fMouseDown = false;
209 }
210 
211 // MouseMoved
212 void
MouseMoved(BPoint where,uint32 transit,const BMessage * dragMessage)213 ColorField::MouseMoved(BPoint where, uint32 transit,
214 	const BMessage* dragMessage)
215 {
216 	if (dragMessage != NULL || !fMouseDown ) {
217 		BView::MouseMoved(where, transit, dragMessage);
218 		return;
219 	}
220 
221 	PositionMarkerAt(where);
222 	Invoke();
223 }
224 
225 // SetModeAndValue
226 void
SetModeAndValue(SelectedColorMode mode,float fixedValue)227 ColorField::SetModeAndValue(SelectedColorMode mode, float fixedValue)
228 {
229 	float R(0), G(0), B(0);
230 	float H(0), S(0), V(0);
231 
232 	float width = Width();
233 	float height = Height();
234 
235 	switch (fMode) {
236 
237 		case R_SELECTED: {
238 			R = fFixedValue * 255;
239 			G = round(fMarkerPosition.x / width * 255.0);
240 			B = round(255.0 - fMarkerPosition.y / height * 255.0);
241 		}; break;
242 
243 		case G_SELECTED: {
244 			R = round(fMarkerPosition.x / width * 255.0);
245 			G = fFixedValue * 255;
246 			B = round(255.0 - fMarkerPosition.y / height * 255.0);
247 		}; break;
248 
249 		case B_SELECTED: {
250 			R = round(fMarkerPosition.x / width * 255.0);
251 			G = round(255.0 - fMarkerPosition.y / height * 255.0);
252 			B = fFixedValue * 255;
253 		}; break;
254 
255 		case H_SELECTED: {
256 			H = fFixedValue;
257 			S = fMarkerPosition.x / width;
258 			V = 1.0 - fMarkerPosition.y / height;
259 		}; break;
260 
261 		case S_SELECTED: {
262 			H = fMarkerPosition.x / width * 6.0;
263 			S = fFixedValue;
264 			V = 1.0 - fMarkerPosition.y / height;
265 		}; break;
266 
267 		case V_SELECTED: {
268 			H = fMarkerPosition.x / width * 6.0;
269 			S = 1.0 - fMarkerPosition.y / height;
270 			V = fFixedValue;
271 		}; break;
272 	}
273 
274 	if (fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) {
275 		HSV_to_RGB(H, S, V, R, G, B);
276 		R *= 255.0; G *= 255.0; B *= 255.0;
277 	}
278 
279 	rgb_color color = { (uint8)round(R), (uint8)round(G), (uint8)round(B),
280 		255 };
281 
282 	if (fFixedValue != fixedValue || fMode != mode) {
283 		fFixedValue = fixedValue;
284 		fMode = mode;
285 
286 		_Update();
287 	}
288 
289 	SetMarkerToColor(color);
290 }
291 
292 // SetFixedValue
293 void
SetFixedValue(float fixedValue)294 ColorField::SetFixedValue(float fixedValue)
295 {
296 	if (fFixedValue != fixedValue) {
297 		fFixedValue = fixedValue;
298 		_Update();
299 	}
300 }
301 
302 // SetMarkerToColor
303 void
SetMarkerToColor(rgb_color color)304 ColorField::SetMarkerToColor(rgb_color color)
305 {
306 	float h, s, v;
307 	RGB_to_HSV(color.red / 255.0, color.green / 255.0, color.blue / 255.0,
308 		h, s, v );
309 
310 	fLastMarkerPosition = fMarkerPosition;
311 
312 	float width = Width();
313 	float height = Height();
314 
315 	switch (fMode) {
316 		case R_SELECTED:
317 			fMarkerPosition = BPoint(color.green / 255.0 * width,
318 				(255.0 - color.blue) / 255.0 * height);
319 			break;
320 
321 		case G_SELECTED:
322 			fMarkerPosition = BPoint(color.red / 255.0 * width,
323 				(255.0 - color.blue) / 255.0 * height);
324 			break;
325 
326 		case B_SELECTED:
327 			fMarkerPosition = BPoint(color.red / 255.0 * width,
328 				(255.0 - color.green) / 255.0 * height);
329 			break;
330 
331 		case H_SELECTED:
332 			if (fOrientation == B_VERTICAL)
333 				fMarkerPosition = BPoint(s * width, height - v * height);
334 			else
335 				fMarkerPosition = BPoint(width - v * width, s * height);
336 			break;
337 
338 		case S_SELECTED:
339 			fMarkerPosition = BPoint(h / 6.0 * width, height - v * height);
340 			break;
341 
342 		case V_SELECTED:
343 			fMarkerPosition = BPoint( h / 6.0 * width, height - s * height);
344 			break;
345 	}
346 
347 	Invalidate();
348 }
349 
350 // PositionMarkerAt
351 void
PositionMarkerAt(BPoint where)352 ColorField::PositionMarkerAt(BPoint where)
353 {
354 	BRect rect = _BitmapRect();
355 	where.ConstrainTo(rect);
356 	where -= rect.LeftTop();
357 
358 	fLastMarkerPosition = fMarkerPosition;
359 	fMarkerPosition = where;
360 	Invalidate();
361 }
362 
363 // Width
364 float
Width() const365 ColorField::Width() const
366 {
367 	return _BitmapRect().IntegerWidth() + 1;
368 }
369 
370 // Height
371 float
Height() const372 ColorField::Height() const
373 {
374 	return _BitmapRect().IntegerHeight() + 1;
375 }
376 
377 // set_bits
378 static inline void
set_bits(uint8 * bits,uint8 r,uint8 g,uint8 b)379 set_bits(uint8* bits, uint8 r, uint8 g, uint8 b)
380 {
381 	bits[0] = b;
382 	bits[1] = g;
383 	bits[2] = r;
384 	bits[3] = 255;
385 }
386 
387 // _Init
388 void
_Init(SelectedColorMode mode,float fixedValue,orientation orient,border_style border)389 ColorField::_Init(SelectedColorMode mode, float fixedValue,
390 	orientation orient, border_style border)
391 {
392 	fMode = mode;
393 	fFixedValue = fixedValue;
394 	fOrientation = orient;
395 	fBorderStyle = border;
396 
397 	fMarkerPosition = BPoint(0.0, 0.0);
398 	fLastMarkerPosition = BPoint(-1.0, -1.0);
399 	fMouseDown = false;
400 
401 	fBitmap = NULL;
402 	fBitmapDirty = true;
403 
404 	SetViewColor(B_TRANSPARENT_COLOR);
405 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
406 }
407 
408 // _AllocBitmap
409 void
_AllocBitmap(int32 width,int32 height)410 ColorField::_AllocBitmap(int32 width, int32 height)
411 {
412 	if (width < 2 || height < 2)
413 		return;
414 
415 	delete fBitmap;
416 	fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32);
417 
418 	fBitmapDirty = true;
419 }
420 
421 // _Update
422 void
_Update()423 ColorField::_Update()
424 {
425 	fBitmapDirty = true;
426 	Invalidate();
427 }
428 
429 // _BitmapRect
430 BRect
_BitmapRect() const431 ColorField::_BitmapRect() const
432 {
433 	BRect r = Bounds();
434 	if (fBorderStyle == B_FANCY_BORDER)
435 		r.InsetBy(2, 2);
436 	return r;
437 }
438 
439 // _FillBitmap
440 void
_FillBitmap(BBitmap * bitmap,SelectedColorMode mode,float fixedValue,orientation orient) const441 ColorField::_FillBitmap(BBitmap* bitmap, SelectedColorMode mode,
442 	float fixedValue, orientation orient) const
443 {
444 	int32 width = bitmap->Bounds().IntegerWidth();
445 	int32 height = bitmap->Bounds().IntegerHeight();
446 	uint32 bpr = bitmap->BytesPerRow();
447 
448 //bigtime_t now = system_time();
449 	uint8* bits = (uint8*)bitmap->Bits();
450 
451 	float r = 0;
452 	float g = 0;
453 	float b = 0;
454 	float h;
455 	float s;
456 	float v;
457 
458 	switch (mode) {
459 		case R_SELECTED:
460 			r = round(fixedValue * 255);
461 			for (int y = height; y >= 0; y--) {
462 				uint8* bitsHandle = bits;
463 				b = y * 255 / height;
464 				for (int32 x = 0; x <= width; x++) {
465 					g = x * 255 / width;
466 					set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
467 					bitsHandle += 4;
468 				}
469 				bits += bpr;
470 			}
471 			break;
472 
473 		case G_SELECTED:
474 			g = round(fixedValue * 255);
475 			for (int y = height; y >= 0; y--) {
476 				uint8* bitsHandle = bits;
477 				b = y * 255 / height;
478 				for (int x = 0; x <= width; x++) {
479 					r = x * 255 / width;
480 					set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
481 					bitsHandle += 4;
482 				}
483 				bits += bpr;
484 			}
485 			break;
486 
487 		case B_SELECTED:
488 			b = round(fixedValue * 255);
489 			for (int y = height; y >= 0; y--) {
490 				uint8* bitsHandle = bits;
491 				g = y * 255 / height;
492 				for (int x = 0; x <= width; ++x) {
493 					r = x * 255 / width;
494 					set_bits(bitsHandle, (uint8)r, (uint8)g, (uint8)b);
495 					bitsHandle += 4;
496 				}
497 				bits += bpr;
498 			}
499 			break;
500 
501 		case H_SELECTED:
502 			h = fixedValue;
503 			if (orient == B_VERTICAL) {
504 				for (int y = 0; y <= height; ++y) {
505 					v = (float)(height - y) / height;
506 					uint8* bitsHandle = bits;
507 					for (int x = 0; x <= width; ++x) {
508 						s = (float)x / width;
509 						HSV_to_RGB(h, s, v, r, g, b);
510 						set_bits(bitsHandle,
511 							round(r * 255.0),
512 							round(g * 255.0),
513 							round(b * 255.0));
514 						bitsHandle += 4;
515 					}
516 					bits += bpr;
517 				}
518 			} else {
519 				for (int y = 0; y <= height; ++y) {
520 					s = (float)y / height;
521 					uint8* bitsHandle = bits;
522 					for (int x = 0; x <= width; ++x) {
523 						v = (float)(width - x) / width;
524 						HSV_to_RGB(h, s, v, r, g, b);
525 						set_bits(bitsHandle,
526 							round(r * 255.0),
527 							round(g * 255.0),
528 							round(b * 255.0));
529 						bitsHandle += 4;
530 					}
531 					bits += bpr;
532 				}
533 			}
534 			break;
535 
536 		case S_SELECTED:
537 			s = fixedValue;
538 			for (int y = 0; y <= height; ++y) {
539 				v = (float)(height - y) / height;
540 				uint8* bitsHandle = bits;
541 				for (int x = 0; x <= width; ++x) {
542 					h = 6.0 / width * x;
543 					HSV_to_RGB(h, s, v, r, g, b);
544 					set_bits(bitsHandle,
545 						round(r * 255.0),
546 						round(g * 255.0),
547 						round(b * 255.0));
548 					bitsHandle += 4;
549 				}
550 				bits += bpr;
551 			}
552 			break;
553 
554 		case V_SELECTED:
555 			v = fixedValue;
556 			for (int y = 0; y <= height; ++y) {
557 				s = (float)(height - y) / height;
558 				uint8* bitsHandle = bits;
559 				for (int x = 0; x <= width; ++x) {
560 					h = 6.0 / width * x;
561 					HSV_to_RGB(h, s, v, r, g, b);
562 					set_bits(bitsHandle,
563 						round(r * 255.0),
564 						round(g * 255.0),
565 						round(b * 255.0));
566 					bitsHandle += 4;
567 				}
568 				bits += bpr;
569 			}
570 			break;
571 	}
572 }
573