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
26enum {
27	MSG_UPDATE			= 'Updt',
28};
29
30#define MAX_X 255
31#define MAX_Y 255
32
33// constructor
34ColorField::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
46ColorField::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
55ColorField::~ColorField()
56{
57	delete fBitmap;
58}
59
60// MinSize
61BSize
62ColorField::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
74BSize
75ColorField::PreferredSize()
76{
77	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
78}
79
80// MaxSize
81BSize
82ColorField::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
91status_t
92ColorField::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
138void
139ColorField::AttachedToWindow()
140{
141}
142
143// Draw
144void
145ColorField::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
179void
180ColorField::FrameResized(float width, float height)
181{
182	BRect r = _BitmapRect();
183	_AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
184	Invalidate();
185}
186
187// MouseDown
188void
189ColorField::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
205void
206ColorField::MouseUp(BPoint where)
207{
208	fMouseDown = false;
209}
210
211// MouseMoved
212void
213ColorField::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
226void
227ColorField::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
293void
294ColorField::SetFixedValue(float fixedValue)
295{
296	if (fFixedValue != fixedValue) {
297		fFixedValue = fixedValue;
298		_Update();
299	}
300}
301
302// SetMarkerToColor
303void
304ColorField::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
351void
352ColorField::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
364float
365ColorField::Width() const
366{
367	return _BitmapRect().IntegerWidth() + 1;
368}
369
370// Height
371float
372ColorField::Height() const
373{
374	return _BitmapRect().IntegerHeight() + 1;
375}
376
377// set_bits
378static inline void
379set_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
388void
389ColorField::_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
409void
410ColorField::_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
422void
423ColorField::_Update()
424{
425	fBitmapDirty = true;
426	Invalidate();
427}
428
429// _BitmapRect
430BRect
431ColorField::_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
440void
441ColorField::_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