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(®ion);
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