vfs: check userland buffers before reading them.
[haiku.git] / src / apps / mediaplayer / interface / PeakView.cpp
blob86426c794500176edb7763c83f7464286818192d
1 /*
2 * Copyright (C) 2001-2010 Stephan Aßmus. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Copyright (C) 1998-1999 Be Incorporated. All rights reseved.
6 * Distributed under the terms of the Be Sample Code license.
7 */
10 #include "PeakView.h"
12 #include <new>
13 #include <stdio.h>
14 #include <string.h>
16 #include <Bitmap.h>
17 #include <Catalog.h>
18 #include <ControlLook.h>
19 #include <Locale.h>
20 #include <MenuItem.h>
21 #include <Message.h>
22 #include <MessageRunner.h>
23 #include <Messenger.h>
24 #include <PopUpMenu.h>
25 #include <Window.h>
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "MediaPlayer-PeakView"
32 using std::nothrow;
35 enum {
36 MSG_PULSE = 'puls',
37 MSG_LOCK_PEAKS = 'lpks'
41 PeakView::PeakView(const char* name, bool useGlobalPulse, bool displayLabels)
43 BView(name, (useGlobalPulse ? B_PULSE_NEEDED : 0)
44 | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
45 fUseGlobalPulse(useGlobalPulse),
46 fDisplayLabels(displayLabels),
47 fPeakLocked(false),
49 fRefreshDelay(20000),
50 fPulse(NULL),
52 fChannelInfos(NULL),
53 fChannelCount(0),
54 fGotData(true),
56 fBackBitmap(NULL),
57 fPeakNotificationWhat(0)
59 GetFontHeight(&fFontHeight);
61 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
62 SetViewColor(B_TRANSPARENT_COLOR);
64 SetChannelCount(2);
68 PeakView::~PeakView()
70 delete fPulse;
71 delete fBackBitmap;
72 delete[] fChannelInfos;
76 void
77 PeakView::MessageReceived(BMessage* message)
79 if (message->what == fPeakNotificationWhat) {
80 float max;
81 for (int32 i = 0; message->FindFloat("max", i, &max) == B_OK; i++)
82 SetMax(max, i);
83 fGotData = true;
84 return;
87 switch (message->what) {
88 case MSG_PULSE:
89 Pulse();
90 break;
92 case MSG_LOCK_PEAKS:
93 fPeakLocked = !fPeakLocked;
94 break;
96 default:
97 BView::MessageReceived(message);
98 break;
103 void
104 PeakView::AttachedToWindow()
106 if (!fUseGlobalPulse) {
107 delete fPulse;
108 BMessage message(MSG_PULSE);
109 fPulse = new BMessageRunner(BMessenger(this), &message,
110 fRefreshDelay);
115 void
116 PeakView::DetachedFromWindow()
118 delete fPulse;
119 fPulse = NULL;
123 void
124 PeakView::MouseDown(BPoint where)
126 int32 buttons;
127 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) < B_OK)
128 buttons = B_PRIMARY_MOUSE_BUTTON;
130 if (buttons & B_PRIMARY_MOUSE_BUTTON) {
131 // Reset the overshot flag and set the observed max to the current
132 // value.
133 for (uint32 i = 0; i < fChannelCount; i++) {
134 fChannelInfos[i].last_overshot_time = -5000000;
135 fChannelInfos[i].last_max = fChannelInfos[i].current_max;
137 } else if (buttons & B_TERTIARY_MOUSE_BUTTON) {
138 // Toggle locking of the observed max value.
139 fPeakLocked = !fPeakLocked;
140 } else {
141 // Display context menu
142 BPopUpMenu* menu = new BPopUpMenu("peak context menu");
143 BMenuItem* item = new BMenuItem(B_TRANSLATE("Lock Peaks"),
144 new BMessage(MSG_LOCK_PEAKS));
145 item->SetMarked(fPeakLocked);
146 menu->AddItem(item);
147 menu->SetTargetForItems(this);
149 menu->SetAsyncAutoDestruct(true);
150 menu->SetFont(be_plain_font);
152 where = ConvertToScreen(where);
153 bool keepOpen = false; // ?
154 if (keepOpen) {
155 BRect mouseRect(where, where);
156 mouseRect.InsetBy(-3.0, -3.0);
157 where += BPoint(3.0, 3.0);
158 menu->Go(where, true, false, mouseRect, true);
159 } else {
160 where += BPoint(3.0, 3.0);
161 menu->Go(where, true, false, true);
167 void
168 PeakView::Draw(BRect updateRect)
170 BRect r(_BackBitmapFrame());
171 float width = r.Width();
172 r.InsetBy(-2.0, -2.0);
174 be_control_look->DrawTextControlBorder(this, r, updateRect, LowColor());
176 // peak bitmap
177 if (fBackBitmap)
178 _DrawBitmap();
180 // dB labels
181 if (fDisplayLabels) {
182 font_height fh;
183 GetFontHeight(&fh);
184 float y = Bounds().bottom;
185 y -= fh.descent;
186 DrawString("0", BPoint(4.0 + width - StringWidth("0"), y));
187 DrawString("-6", BPoint(0.477 * width, y));
188 DrawString("-12", BPoint(0.227 * width, y));
193 void
194 PeakView::FrameResized(float width, float height)
196 BRect bitmapFrame = _BackBitmapFrame();
197 _ResizeBackBitmap(bitmapFrame.IntegerWidth() + 1, fChannelCount);
198 _UpdateBackBitmap();
202 void
203 PeakView::Pulse()
205 if (!fGotData)
206 return;
208 if (fBackBitmap == NULL)
209 return;
211 if (!fPeakLocked) {
212 for (uint32 i = 0; i < fChannelCount; i++) {
213 fChannelInfos[i].last_max *= 0.96f;
214 if (fChannelInfos[i].current_max > fChannelInfos[i].last_max)
215 fChannelInfos[i].last_max = fChannelInfos[i].current_max;
218 _UpdateBackBitmap();
220 for (uint32 i = 0; i < fChannelCount; i++)
221 fChannelInfos[i].current_max = 0.0f;
222 fGotData = false;
224 _DrawBitmap();
225 Flush();
229 BSize
230 PeakView::MinSize()
232 float minWidth = 20 + 4;
233 float minHeight = 2 * 8 - 1 + 4;
234 if (fDisplayLabels) {
235 font_height fh;
236 GetFontHeight(&fh);
237 minWidth = max_c(60.0, minWidth);
238 minHeight += ceilf(fh.ascent + fh.descent);
240 return BSize(minWidth, minHeight);
244 bool
245 PeakView::IsValid() const
247 return fBackBitmap != NULL && fBackBitmap->IsValid()
248 && fChannelInfos != NULL;
252 void
253 PeakView::SetPeakRefreshDelay(bigtime_t delay)
255 if (fRefreshDelay == delay)
256 return;
258 fRefreshDelay = delay;
260 if (fPulse != NULL)
261 fPulse->SetInterval(fRefreshDelay);
265 void
266 PeakView::SetPeakNotificationWhat(uint32 what)
268 fPeakNotificationWhat = what;
272 void
273 PeakView::SetChannelCount(uint32 channelCount)
275 if (channelCount == fChannelCount)
276 return;
278 delete[] fChannelInfos;
279 fChannelInfos = new(std::nothrow) ChannelInfo[channelCount];
280 if (fChannelInfos != NULL) {
281 fChannelCount = channelCount;
282 for (uint32 i = 0; i < fChannelCount; i++) {
283 fChannelInfos[i].current_max = 0.0f;
284 fChannelInfos[i].last_max = 0.0f;
285 fChannelInfos[i].last_overshot_time = -5000000;
287 _ResizeBackBitmap(_BackBitmapFrame().IntegerWidth() + 1,
288 fChannelCount);
289 } else
290 fChannelCount = 0;
294 void
295 PeakView::SetMax(float max, uint32 channel)
297 if (channel >= fChannelCount)
298 return;
300 if (fChannelInfos[channel].current_max < max)
301 fChannelInfos[channel].current_max = max;
303 if (fChannelInfos[channel].current_max > 1.0)
304 fChannelInfos[channel].last_overshot_time = system_time();
308 // #pragma mark -
311 BRect
312 PeakView::_BackBitmapFrame() const
314 BRect frame = Bounds();
315 frame.InsetBy(2, 2);
316 if (fDisplayLabels)
317 frame.bottom -= ceilf(fFontHeight.ascent + fFontHeight.descent);
318 return frame;
322 void
323 PeakView::_ResizeBackBitmap(int32 width, int32 channels)
325 if (fBackBitmap != NULL) {
326 if (fBackBitmap->Bounds().IntegerWidth() + 1 == width
327 && fBackBitmap->Bounds().IntegerHeight() + 1 == channels) {
328 return;
331 if (channels <= 0)
332 channels = 2;
334 delete fBackBitmap;
335 BRect bounds(0, 0, width - 1, channels - 1);
336 fBackBitmap = new(std::nothrow) BBitmap(bounds, 0, B_RGB32);
337 if (fBackBitmap == NULL || !fBackBitmap->IsValid()) {
338 delete fBackBitmap;
339 fBackBitmap = NULL;
340 return;
342 memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
343 fGotData = true;
347 void
348 PeakView::_UpdateBackBitmap()
350 if (!fBackBitmap)
351 return;
353 uint8* span = (uint8*)fBackBitmap->Bits();
354 uint32 width = fBackBitmap->Bounds().IntegerWidth() + 1;
355 for (uint32 i = 0; i < fChannelCount; i++) {
356 _RenderSpan(span, width, fChannelInfos[i].current_max,
357 fChannelInfos[i].last_max,
358 system_time() - fChannelInfos[i].last_overshot_time < 2000000);
359 span += fBackBitmap->BytesPerRow();
364 void
365 PeakView:: _RenderSpan(uint8* span, uint32 width, float current, float peak,
366 bool overshot)
368 uint8 emptyR = 15;
369 uint8 emptyG = 36;
370 uint8 emptyB = 16;
372 uint8 fillR = 41;
373 uint8 fillG = 120;
374 uint8 fillB = 45;
376 uint8 currentR = 45;
377 uint8 currentG = 255;
378 uint8 currentB = 45;
380 uint8 lastR = 255;
381 uint8 lastG = 229;
382 uint8 lastB = 87;
384 uint8 overR = 255;
385 uint8 overG = 89;
386 uint8 overB = 7;
388 uint8 kFadeFactor = 100;
390 uint32 evenWidth = width - width % 2;
391 uint32 split = (uint32)(current * (evenWidth - 1) + 0.5);
392 split += split & 1;
393 uint32 last = (uint32)(peak * (evenWidth - 1) + 0.5);
394 last += last & 1;
395 uint32 over = overshot ? evenWidth : evenWidth + 1;
396 over += over & 1;
398 for (uint32 x = 0; x < width; x += 2) {
399 uint8 fadedB = (uint8)(((int)span[0] * kFadeFactor) >> 8);
400 uint8 fadedG = (uint8)(((int)span[1] * kFadeFactor) >> 8);
401 uint8 fadedR = (uint8)(((int)span[2] * kFadeFactor) >> 8);
402 if (x < split) {
403 span[0] = max_c(fillB, fadedB);
404 span[1] = max_c(fillG, fadedG);
405 span[2] = max_c(fillR, fadedR);
406 } else if (x == split) {
407 span[0] = currentB;
408 span[1] = currentG;
409 span[2] = currentR;
410 } else if (x > split) {
411 span[0] = max_c(emptyB, fadedB);
412 span[1] = max_c(emptyG, fadedG);
413 span[2] = max_c(emptyR, fadedR);
415 if (x == last) {
416 span[0] = lastB;
417 span[1] = lastG;
418 span[2] = lastR;
420 if (x == over) {
421 span[0] = overB;
422 span[1] = overG;
423 span[2] = overR;
425 span += 8;
430 void
431 PeakView::_DrawBitmap()
433 SetHighColor(0, 0, 0);
434 BRect bitmapFrame = _BackBitmapFrame();
435 BRect bitmapRect = fBackBitmap->Bounds();
436 bitmapRect.bottom = bitmapRect.top;
437 float channelHeight = (bitmapFrame.Height() + 1) / fChannelCount;
438 for (uint32 i = 0; i < fChannelCount; i++) {
439 BRect viewRect(bitmapFrame);
440 viewRect.bottom = viewRect.top;
441 viewRect.top += floorf(i * channelHeight + 0.5);
442 if (i < fChannelCount - 1) {
443 viewRect.bottom += floorf((i + 1) * channelHeight + 0.5) - 2;
444 StrokeLine(BPoint(viewRect.left, viewRect.bottom + 1),
445 BPoint(viewRect.right, viewRect.bottom + 1));
446 } else
447 viewRect.bottom += floorf((i + 1) * channelHeight + 0.5) - 1;
448 DrawBitmapAsync(fBackBitmap, bitmapRect, viewRect);
449 bitmapRect.OffsetBy(0, 1);