Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: Source/core/layout/LayoutMediaControls.cpp

Issue 1102353008: Split ThemePainter out of LayoutTheme (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: themePainter() -> theme().painter() Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2009 Apple Inc.
3 * Copyright (C) 2009 Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29 #include "core/layout/LayoutMediaControls.h"
30
31 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
32 #include "core/html/HTMLMediaElement.h"
33 #include "core/html/TimeRanges.h"
34 #include "core/paint/PaintInfo.h"
35 #include "platform/graphics/Gradient.h"
36 #include "platform/graphics/GraphicsContext.h"
37
38 namespace blink {
39
40 typedef WTF::HashMap<const char*, Image*> MediaControlImageMap;
41 static MediaControlImageMap* gMediaControlImageMap = 0;
42 static double kCurrentTimeBufferedDelta = 1.0;
43
44 static Image* platformResource(const char* name)
45 {
46 if (!gMediaControlImageMap)
47 gMediaControlImageMap = new MediaControlImageMap();
48 if (Image* image = gMediaControlImageMap->get(name))
49 return image;
50 if (Image* image = Image::loadPlatformResource(name).leakRef()) {
51 gMediaControlImageMap->set(name, image);
52 return image;
53 }
54 ASSERT_NOT_REACHED();
55 return 0;
56 }
57
58 static bool hasSource(const HTMLMediaElement* mediaElement)
59 {
60 return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY
61 && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE;
62 }
63
64 static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Imag e* image)
65 {
66 context->drawImage(image, rect);
67 return true;
68 }
69
70 static bool paintMediaMuteButton(LayoutObject* object, const PaintInfo& paintInf o, const IntRect& rect)
71 {
72 HTMLMediaElement* mediaElement = toParentMediaElement(object);
73 if (!mediaElement)
74 return false;
75
76 static Image* soundLevel3 = platformResource("mediaplayerSoundLevel3");
77 static Image* soundLevel2 = platformResource("mediaplayerSoundLevel2");
78 static Image* soundLevel1 = platformResource("mediaplayerSoundLevel1");
79 static Image* soundLevel0 = platformResource("mediaplayerSoundLevel0");
80 static Image* soundDisabled = platformResource("mediaplayerSoundDisabled");
81
82 if (!hasSource(mediaElement) || !mediaElement->hasAudio())
83 return paintMediaButton(paintInfo.context, rect, soundDisabled);
84
85 if (mediaElement->muted() || mediaElement->volume() <= 0)
86 return paintMediaButton(paintInfo.context, rect, soundLevel0);
87
88 if (mediaElement->volume() <= 0.33)
89 return paintMediaButton(paintInfo.context, rect, soundLevel1);
90
91 if (mediaElement->volume() <= 0.66)
92 return paintMediaButton(paintInfo.context, rect, soundLevel2);
93
94 return paintMediaButton(paintInfo.context, rect, soundLevel3);
95 }
96
97 static bool paintMediaPlayButton(LayoutObject* object, const PaintInfo& paintInf o, const IntRect& rect)
98 {
99 HTMLMediaElement* mediaElement = toParentMediaElement(object);
100 if (!mediaElement)
101 return false;
102
103 static Image* mediaPlay = platformResource("mediaplayerPlay");
104 static Image* mediaPause = platformResource("mediaplayerPause");
105 static Image* mediaPlayDisabled = platformResource("mediaplayerPlayDisabled" );
106
107 if (!hasSource(mediaElement))
108 return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled);
109
110 Image * image = !object->node()->isMediaControlElement() || mediaControlElem entType(object->node()) == MediaPlayButton ? mediaPlay : mediaPause;
111 return paintMediaButton(paintInfo.context, rect, image);
112 }
113
114 static bool paintMediaOverlayPlayButton(LayoutObject* object, const PaintInfo& p aintInfo, const IntRect& rect)
115 {
116 HTMLMediaElement* mediaElement = toParentMediaElement(object);
117 if (!mediaElement)
118 return false;
119
120 if (!hasSource(mediaElement) || !mediaElement->togglePlayStateWillPlay())
121 return false;
122
123 static Image* mediaOverlayPlay = platformResource("mediaplayerOverlayPlay");
124 return paintMediaButton(paintInfo.context, rect, mediaOverlayPlay);
125 }
126
127 static Image* getMediaSliderThumb()
128 {
129 static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb");
130 return mediaSliderThumb;
131 }
132
133 static void paintRoundedSliderBackground(const IntRect& rect, const ComputedStyl e&, GraphicsContext* context)
134 {
135 int borderRadius = rect.height() / 2;
136 IntSize radii(borderRadius, borderRadius);
137 Color sliderBackgroundColor = Color(11, 11, 11);
138 context->fillRoundedRect(FloatRoundedRect(rect, radii, radii, radii, radii), sliderBackgroundColor);
139 }
140
141 static void paintSliderRangeHighlight(const IntRect& rect, const ComputedStyle& style, GraphicsContext* context, int startPosition, int endPosition, Color start Color, Color endColor)
142 {
143 // Calculate border radius; need to avoid being smaller than half the slider height
144 // because of https://bugs.webkit.org/show_bug.cgi?id=30143.
145 int borderRadius = rect.height() / 2;
146 IntSize radii(borderRadius, borderRadius);
147
148 // Calculate highlight rectangle and edge dimensions.
149 int startOffset = startPosition;
150 int endOffset = rect.width() - endPosition;
151 int rangeWidth = endPosition - startPosition;
152
153 if (rangeWidth <= 0)
154 return;
155
156 // Make sure the range width is bigger than border radius at the edges to re tain rounded corners.
157 if (startOffset < borderRadius && rangeWidth < borderRadius)
158 rangeWidth = borderRadius;
159 if (endOffset < borderRadius && rangeWidth < borderRadius)
160 rangeWidth = borderRadius;
161
162 // Set rectangle to highlight range.
163 IntRect highlightRect = rect;
164 highlightRect.move(startOffset, 0);
165 highlightRect.setWidth(rangeWidth);
166
167 // Don't bother drawing an empty area.
168 if (highlightRect.isEmpty())
169 return;
170
171 // Calculate white-grey gradient.
172 IntPoint sliderTopLeft = highlightRect.location();
173 IntPoint sliderBottomLeft = sliderTopLeft;
174 sliderBottomLeft.move(0, highlightRect.height());
175 RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderBottomLeft );
176 gradient->addColorStop(0.0, startColor);
177 gradient->addColorStop(1.0, endColor);
178
179 // Fill highlight rectangle with gradient, potentially rounded if on left or right edge.
180 context->save();
181 context->setFillGradient(gradient);
182
183 if (startOffset < borderRadius && endOffset < borderRadius)
184 context->fillRoundedRect(FloatRoundedRect(highlightRect, radii, radii, r adii, radii), startColor);
185 else if (startOffset < borderRadius)
186 context->fillRoundedRect(FloatRoundedRect(highlightRect, radii, IntSize( 0, 0), radii, IntSize(0, 0)), startColor);
187 else if (endOffset < borderRadius)
188 context->fillRoundedRect(FloatRoundedRect(highlightRect, IntSize(0, 0), radii, IntSize(0, 0), radii), startColor);
189 else
190 context->fillRect(highlightRect);
191
192 context->restore();
193 }
194
195 const int mediaSliderThumbWidth = 32;
196
197 static bool paintMediaSlider(LayoutObject* object, const PaintInfo& paintInfo, c onst IntRect& rect)
198 {
199 HTMLMediaElement* mediaElement = toParentMediaElement(object);
200 if (!mediaElement)
201 return false;
202
203 const ComputedStyle& style = object->styleRef();
204 GraphicsContext* context = paintInfo.context;
205
206 paintRoundedSliderBackground(rect, style, context);
207
208 // Draw the buffered range. Since the element may have multiple buffered ran ges and it'd be
209 // distracting/'busy' to show all of them, show only the buffered range cont aining the current play head.
210 RefPtrWillBeRawPtr<TimeRanges> bufferedTimeRanges = mediaElement->buffered() ;
211 float duration = mediaElement->duration();
212 float currentTime = mediaElement->currentTime();
213 if (std::isnan(duration) || std::isinf(duration) || !duration || std::isnan( currentTime))
214 return true;
215
216 for (unsigned i = 0; i < bufferedTimeRanges->length(); ++i) {
217 float start = bufferedTimeRanges->start(i, ASSERT_NO_EXCEPTION);
218 float end = bufferedTimeRanges->end(i, ASSERT_NO_EXCEPTION);
219 // The delta is there to avoid corner cases when buffered
220 // ranges is out of sync with current time because of
221 // asynchronous media pipeline and current time caching in
222 // HTMLMediaElement.
223 // This is related to https://www.w3.org/Bugs/Public/show_bug.cgi?id=281 25
224 // FIXME: Remove this workaround when WebMediaPlayer
225 // has an asynchronous pause interface.
226 if (std::isnan(start) || std::isnan(end)
227 || start > currentTime + kCurrentTimeBufferedDelta || end < currentT ime)
228 continue;
229 int startPosition = int(start * rect.width() / duration);
230 int currentPosition = int(currentTime * rect.width() / duration);
231 int endPosition = int(end * rect.width() / duration);
232
233 // Add half the thumb width proportionally adjusted to the current paint ing position.
234 int thumbCenter = mediaSliderThumbWidth / 2;
235 int addWidth = thumbCenter * (1.0 - 2.0 * currentPosition / rect.width() );
236 currentPosition += addWidth;
237
238 // Draw white-ish highlight before current time.
239 Color startColor = Color(195, 195, 195);
240 Color endColor = Color(217, 217, 217);
241 if (currentPosition > startPosition)
242 paintSliderRangeHighlight(rect, style, context, startPosition, curre ntPosition, startColor, endColor);
243
244 // Draw grey-ish highlight after current time.
245 startColor = Color(60, 60, 60);
246 endColor = Color(76, 76, 76);
247
248 if (endPosition > currentPosition)
249 paintSliderRangeHighlight(rect, style, context, currentPosition, end Position, startColor, endColor);
250
251 return true;
252 }
253
254 return true;
255 }
256
257 static bool paintMediaSliderThumb(LayoutObject* object, const PaintInfo& paintIn fo, const IntRect& rect)
258 {
259 if (!object->node())
260 return false;
261
262 HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadow Host());
263 if (!mediaElement)
264 return false;
265
266 if (!hasSource(mediaElement))
267 return true;
268
269 Image* mediaSliderThumb = getMediaSliderThumb();
270 return paintMediaButton(paintInfo.context, rect, mediaSliderThumb);
271 }
272
273 const int mediaVolumeSliderThumbWidth = 24;
274
275 static bool paintMediaVolumeSlider(LayoutObject* object, const PaintInfo& paintI nfo, const IntRect& rect)
276 {
277 HTMLMediaElement* mediaElement = toParentMediaElement(object);
278 if (!mediaElement)
279 return false;
280
281 GraphicsContext* context = paintInfo.context;
282 const ComputedStyle& style = object->styleRef();
283
284 paintRoundedSliderBackground(rect, style, context);
285
286 // Calculate volume position for white background rectangle.
287 float volume = mediaElement->volume();
288 if (std::isnan(volume) || volume < 0)
289 return true;
290 if (volume > 1)
291 volume = 1;
292 if (!hasSource(mediaElement) || !mediaElement->hasAudio() || mediaElement->m uted())
293 volume = 0;
294
295 // Calculate the position relative to the center of the thumb.
296 float fillWidth = 0;
297 if (volume > 0) {
298 float thumbCenter = mediaVolumeSliderThumbWidth / 2;
299 float zoomLevel = style.effectiveZoom();
300 float positionWidth = volume * (rect.width() - (zoomLevel * thumbCenter) );
301 fillWidth = positionWidth + (zoomLevel * thumbCenter / 2);
302 }
303
304 Color startColor = Color(195, 195, 195);
305 Color endColor = Color(217, 217, 217);
306
307 paintSliderRangeHighlight(rect, style, context, 0.0, fillWidth, startColor, endColor);
308
309 return true;
310 }
311
312 static bool paintMediaVolumeSliderThumb(LayoutObject* object, const PaintInfo& p aintInfo, const IntRect& rect)
313 {
314 if (!object->node())
315 return false;
316
317 HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadow Host());
318 if (!mediaElement)
319 return false;
320
321 if (!hasSource(mediaElement) || !mediaElement->hasAudio())
322 return true;
323
324 static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSl iderThumb");
325 return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb);
326 }
327
328 static bool paintMediaFullscreenButton(LayoutObject* object, const PaintInfo& pa intInfo, const IntRect& rect)
329 {
330 HTMLMediaElement* mediaElement = toParentMediaElement(object);
331 if (!mediaElement)
332 return false;
333
334 static Image* mediaFullscreenButton = platformResource("mediaplayerFullscree n");
335 return paintMediaButton(paintInfo.context, rect, mediaFullscreenButton);
336 }
337
338 static bool paintMediaToggleClosedCaptionsButton(LayoutObject* object, const Pai ntInfo& paintInfo, const IntRect& rect)
339 {
340 HTMLMediaElement* mediaElement = toParentMediaElement(object);
341 if (!mediaElement)
342 return false;
343
344 static Image* mediaClosedCaptionButton = platformResource("mediaplayerClosed Caption");
345 static Image* mediaClosedCaptionButtonDisabled = platformResource("mediaplay erClosedCaptionDisabled");
346
347 if (mediaElement->closedCaptionsVisible())
348 return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButto n);
349
350 return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButtonDis abled);
351 }
352 static bool paintMediaCastButton(LayoutObject* object, const PaintInfo& paintInf o, const IntRect& rect)
353 {
354 HTMLMediaElement* mediaElement = toParentMediaElement(object);
355 if (!mediaElement)
356 return false;
357
358 static Image* mediaCastOn = platformResource("mediaplayerCastOn");
359 static Image* mediaCastOff = platformResource("mediaplayerCastOff");
360 // To ensure that the overlaid cast button is visible when overlaid on pale videos we use a
361 // different version of it for the overlaid case with a semi-opaque backgrou nd.
362 static Image* mediaOverlayCastOff = platformResource("mediaplayerOverlayCast Off");
363
364 switch (mediaControlElementType(object->node())) {
365 case MediaCastOnButton:
366 case MediaOverlayCastOnButton:
367 return paintMediaButton(paintInfo.context, rect, mediaCastOn);
368 case MediaCastOffButton:
369 return paintMediaButton(paintInfo.context, rect, mediaCastOff);
370 case MediaOverlayCastOffButton:
371 return paintMediaButton(paintInfo.context, rect, mediaOverlayCastOff);
372 default:
373 ASSERT_NOT_REACHED();
374 return false;
375 }
376 }
377
378 bool LayoutMediaControls::paintMediaControlsPart(MediaControlElementType part, L ayoutObject* object, const PaintInfo& paintInfo, const IntRect& rect)
379 {
380 switch (part) {
381 case MediaMuteButton:
382 case MediaUnMuteButton:
383 return paintMediaMuteButton(object, paintInfo, rect);
384 case MediaPauseButton:
385 case MediaPlayButton:
386 return paintMediaPlayButton(object, paintInfo, rect);
387 case MediaShowClosedCaptionsButton:
388 return paintMediaToggleClosedCaptionsButton(object, paintInfo, rect);
389 case MediaSlider:
390 return paintMediaSlider(object, paintInfo, rect);
391 case MediaSliderThumb:
392 return paintMediaSliderThumb(object, paintInfo, rect);
393 case MediaVolumeSlider:
394 return paintMediaVolumeSlider(object, paintInfo, rect);
395 case MediaVolumeSliderThumb:
396 return paintMediaVolumeSliderThumb(object, paintInfo, rect);
397 case MediaEnterFullscreenButton:
398 case MediaExitFullscreenButton:
399 return paintMediaFullscreenButton(object, paintInfo, rect);
400 case MediaOverlayPlayButton:
401 return paintMediaOverlayPlayButton(object, paintInfo, rect);
402 case MediaCastOffButton:
403 case MediaCastOnButton:
404 case MediaOverlayCastOffButton:
405 case MediaOverlayCastOnButton:
406 return paintMediaCastButton(object, paintInfo, rect);
407 case MediaVolumeSliderContainer:
408 case MediaTimelineContainer:
409 case MediaCurrentTimeDisplay:
410 case MediaTimeRemainingDisplay:
411 case MediaControlsPanel:
412 case MediaHideClosedCaptionsButton:
413 case MediaFullScreenVolumeSlider:
414 case MediaFullScreenVolumeSliderThumb:
415 ASSERT_NOT_REACHED();
416 break;
417 }
418 return false;
419 }
420
421 const int mediaSliderThumbHeight = 24;
422 const int mediaVolumeSliderThumbHeight = 24;
423
424 void LayoutMediaControls::adjustMediaSliderThumbSize(ComputedStyle& style)
425 {
426 static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb");
427 static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSl iderThumb");
428 int width = 0;
429 int height = 0;
430
431 Image* thumbImage = 0;
432 if (style.appearance() == MediaSliderThumbPart) {
433 thumbImage = mediaSliderThumb;
434 width = mediaSliderThumbWidth;
435 height = mediaSliderThumbHeight;
436 } else if (style.appearance() == MediaVolumeSliderThumbPart) {
437 thumbImage = mediaVolumeSliderThumb;
438 width = mediaVolumeSliderThumbWidth;
439 height = mediaVolumeSliderThumbHeight;
440 }
441
442 float zoomLevel = style.effectiveZoom();
443 if (thumbImage) {
444 style.setWidth(Length(static_cast<int>(width * zoomLevel), Fixed));
445 style.setHeight(Length(static_cast<int>(height * zoomLevel), Fixed));
446 }
447 }
448
449 static String formatChromiumMediaControlsTime(float time, float duration)
450 {
451 if (!std::isfinite(time))
452 time = 0;
453 if (!std::isfinite(duration))
454 duration = 0;
455 int seconds = static_cast<int>(fabsf(time));
456 int hours = seconds / (60 * 60);
457 int minutes = (seconds / 60) % 60;
458 seconds %= 60;
459
460 // duration defines the format of how the time is rendered
461 int durationSecs = static_cast<int>(fabsf(duration));
462 int durationHours = durationSecs / (60 * 60);
463 int durationMins = (durationSecs / 60) % 60;
464
465 if (durationHours || hours)
466 return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
467 if (durationMins > 9)
468 return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, sec onds);
469
470 return String::format("%s%01d:%02d", (time < 0 ? "-" : ""), minutes, seconds );
471 }
472
473 String LayoutMediaControls::formatMediaControlsTime(float time)
474 {
475 return formatChromiumMediaControlsTime(time, time);
476 }
477
478 String LayoutMediaControls::formatMediaControlsCurrentTime(float currentTime, fl oat duration)
479 {
480 return formatChromiumMediaControlsTime(currentTime, duration);
481 }
482
483 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/layout/LayoutMediaControls.h ('k') | Source/core/layout/LayoutTextControlSingleLine.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698