172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// found in the LICENSE file. 4bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/lazy_instance.h" 6bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/speech/speech_input_bubble.h" 7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 8bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "grit/generated_resources.h" 9bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "grit/theme_resources.h" 1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 1172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h" 1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/rect.h" 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/gfx/skbitmap_operations.h" 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace { 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst color_utils::HSL kGrayscaleShift = { -1, 0, 0.6 }; 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kWarmingUpAnimationStartMs = 500; 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kWarmingUpAnimationStepMs = 100; 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kRecognizingAnimationStepMs = 100; 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// A lazily initialized singleton to hold all the image used by the speech 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// input bubbles and safely destroy them on exit. 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass SpeechInputBubbleImages { 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen public: 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::vector<SkBitmap>& spinner() { return spinner_; } 27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::vector<SkBitmap>& warm_up() { return warm_up_; } 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_full() { return mic_full_; } 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_empty() { return mic_empty_; } 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_noise() { return mic_noise_; } 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_mask() { return mic_mask_; } 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen private: 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Private constructor to enforce singleton. 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen friend struct base::DefaultLazyInstanceTraits<SpeechInputBubbleImages>; 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SpeechInputBubbleImages(); 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<SkBitmap> spinner_; // Frames for the progress spinner. 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::vector<SkBitmap> warm_up_; // Frames for the warm up animation. 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // These bitmaps are owned by ResourceBundle and need not be destroyed. 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_full_; // Mic image with full volume. 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_noise_; // Mic image with full noise volume. 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_empty_; // Mic image with zero volume. 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* mic_mask_; // Gradient mask used by the volume indicator. 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}; 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenSpeechInputBubbleImages::SpeechInputBubbleImages() { 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mic_empty_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen IDR_SPEECH_INPUT_MIC_EMPTY); 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mic_noise_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen IDR_SPEECH_INPUT_MIC_NOISE); 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mic_full_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen IDR_SPEECH_INPUT_MIC_FULL); 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mic_mask_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen IDR_SPEECH_INPUT_MIC_MASK); 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // The sprite image consists of all the animation frames put together in one 59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // horizontal/wide image. Each animation frame is square in shape within the 60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // sprite. 61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap* spinner_image = ResourceBundle::GetSharedInstance().GetBitmapNamed( 62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen IDR_SPEECH_INPUT_SPINNER); 63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int frame_size = spinner_image->height(); 64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // When recording starts up, it may take a short while (few ms or even a 66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // couple of seconds) before the audio device starts really capturing data. 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // This is more apparent on first use. To cover such cases we show a warming 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // up state in the bubble starting with a blank spinner image. If audio data 69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // starts coming in within a couple hundred ms, we switch to the recording 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // UI and if it takes longer, we show the real warm up animation frames. 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // This reduces visual jank for the most part. 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap empty_spinner; 73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen empty_spinner.setConfig(SkBitmap::kARGB_8888_Config, frame_size, frame_size); 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen empty_spinner.allocPixels(); 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen empty_spinner.eraseRGB(255, 255, 255); 76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen warm_up_.push_back(empty_spinner); 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (SkIRect src_rect(SkIRect::MakeWH(frame_size, frame_size)); 79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen src_rect.fLeft < spinner_image->width(); 80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen src_rect.offset(frame_size, 0)) { 81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap frame; 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen spinner_image->extractSubset(&frame, src_rect); 83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // The bitmap created by extractSubset just points to the same pixels as 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the original and adjusts rowBytes accordingly. However that doesn't 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // render properly and gets vertically squished in Linux due to a bug in 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Skia. Until that gets fixed we work around by taking a real copy of it 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // below as the copied bitmap has the correct rowBytes and renders fine. 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SkBitmap frame_copy; 90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen frame.copyTo(&frame_copy, SkBitmap::kARGB_8888_Config); 91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen spinner_.push_back(frame_copy); 92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // The warm up spinner animation is a gray scale version of the real one. 94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen warm_up_.push_back(SkBitmapOperations::CreateHSLShiftedBitmap( 95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen frame_copy, kGrayscaleShift)); 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbase::LazyInstance<SpeechInputBubbleImages> g_images(base::LINKER_INITIALIZED); 100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} // namespace 102bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 103bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputBubble::FactoryMethod SpeechInputBubble::factory_ = NULL; 104dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenconst int SpeechInputBubble::kBubbleTargetOffsetX = 10; 105bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 106bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputBubble* SpeechInputBubble::Create(TabContents* tab_contents, 107bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen Delegate* delegate, 108bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen const gfx::Rect& element_rect) { 109bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (factory_) 110bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return (*factory_)(tab_contents, delegate, element_rect); 111bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 112bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // Has the tab already closed before bubble create request was processed? 113bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen if (!tab_contents) 114bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return NULL; 115bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 116bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen return CreateNativeBubble(tab_contents, delegate, element_rect); 117bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 118bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 1193f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenSpeechInputBubbleBase::SpeechInputBubbleBase(TabContents* tab_contents) 120bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), 1213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen display_mode_(DISPLAY_MODE_RECORDING), 1223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen tab_contents_(tab_contents) { 123bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen mic_image_.reset(new SkBitmap()); 124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen mic_image_->setConfig(SkBitmap::kARGB_8888_Config, 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen g_images.Get().mic_empty()->width(), 126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen g_images.Get().mic_empty()->height()); 127bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen mic_image_->allocPixels(); 128bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 129bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen buffer_image_.reset(new SkBitmap()); 130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen buffer_image_->setConfig(SkBitmap::kARGB_8888_Config, 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen g_images.Get().mic_empty()->width(), 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen g_images.Get().mic_empty()->height()); 133bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen buffer_image_->allocPixels(); 134bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 135bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 136bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian MonsenSpeechInputBubbleBase::~SpeechInputBubbleBase() { 137bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // This destructor is added to make sure members such as the scoped_ptr 138bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // get destroyed here and the derived classes don't have to care about such 139bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen // member variables which they don't use. 140bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 141bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputBubbleBase::SetWarmUpMode() { 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen task_factory_.RevokeAll(); 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen display_mode_ = DISPLAY_MODE_WARM_UP; 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen animation_step_ = 0; 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DoWarmingUpAnimationStep(); 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UpdateLayout(); 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputBubbleBase::DoWarmingUpAnimationStep() { 151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SetImage(g_images.Get().warm_up()[animation_step_]); 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen MessageLoop::current()->PostDelayedTask( 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen FROM_HERE, 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen task_factory_.NewRunnableMethod( 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen &SpeechInputBubbleBase::DoWarmingUpAnimationStep), 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen animation_step_ == 0 ? kWarmingUpAnimationStartMs 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen : kWarmingUpAnimationStepMs); 158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (++animation_step_ >= static_cast<int>(g_images.Get().warm_up().size())) 159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen animation_step_ = 1; // Frame 0 is skipped during the animation. 160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 162bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputBubbleBase::SetRecordingMode() { 163bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen task_factory_.RevokeAll(); 164bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen display_mode_ = DISPLAY_MODE_RECORDING; 165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SetInputVolume(0, 0); 166bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen UpdateLayout(); 167bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 168bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 169bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputBubbleBase::SetRecognizingMode() { 170bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen display_mode_ = DISPLAY_MODE_RECOGNIZING; 171bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen animation_step_ = 0; 172dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DoRecognizingAnimationStep(); 173dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen UpdateLayout(); 174bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 175bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 176bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputBubbleBase::DoRecognizingAnimationStep() { 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen SetImage(g_images.Get().spinner()[animation_step_]); 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (++animation_step_ >= static_cast<int>(g_images.Get().spinner().size())) 179bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen animation_step_ = 0; 180bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen MessageLoop::current()->PostDelayedTask( 181bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen FROM_HERE, 182bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen task_factory_.NewRunnableMethod( 183bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen &SpeechInputBubbleBase::DoRecognizingAnimationStep), 184bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen kRecognizingAnimationStepMs); 185bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 186bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 187bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid SpeechInputBubbleBase::SetMessage(const string16& text) { 188bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen task_factory_.RevokeAll(); 189bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen message_text_ = text; 190bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen display_mode_ = DISPLAY_MODE_MESSAGE; 191bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen UpdateLayout(); 192bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 193bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 194dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputBubbleBase::DrawVolumeOverlay(SkCanvas* canvas, 195dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const SkBitmap& bitmap, 196dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen float volume) { 197bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen buffer_image_->eraseARGB(0, 0, 0, 0); 198bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 199bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int width = mic_image_->width(); 200bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen int height = mic_image_->height(); 201bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SkCanvas buffer_canvas(*buffer_image_); 202bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 203bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen buffer_canvas.save(); 204dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen const int kVolumeSteps = 12; 205dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SkScalar clip_right = 206dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen (((1.0f - volume) * (width * (kVolumeSteps + 1))) - width) / kVolumeSteps; 207dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen buffer_canvas.clipRect(SkRect::MakeLTRB(0, 0, 208dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SkIntToScalar(width) - clip_right, SkIntToScalar(height))); 209dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen buffer_canvas.drawBitmap(bitmap, 0, 0); 210bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen buffer_canvas.restore(); 211bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SkPaint multiply_paint; 212bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen multiply_paint.setXfermode(SkXfermode::Create(SkXfermode::kMultiply_Mode)); 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen buffer_canvas.drawBitmap(*g_images.Get().mic_mask(), -clip_right, 0, 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen &multiply_paint); 215dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 216dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen canvas->drawBitmap(*buffer_image_.get(), 0, 0); 217dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 218dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 219dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid SpeechInputBubbleBase::SetInputVolume(float volume, float noise_volume) { 220dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen mic_image_->eraseARGB(0, 0, 0, 0); 221dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SkCanvas canvas(*mic_image_); 222bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 223dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Draw the empty volume image first and the current volume image on top, 224dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // and then the noise volume image on top of both. 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen canvas.drawBitmap(*g_images.Get().mic_empty(), 0, 0); 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DrawVolumeOverlay(&canvas, *g_images.Get().mic_full(), volume); 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DrawVolumeOverlay(&canvas, *g_images.Get().mic_noise(), noise_volume); 228bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen 229bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen SetImage(*mic_image_.get()); 230bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen} 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTabContents* SpeechInputBubbleBase::tab_contents() { 23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return tab_contents_; 23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid SpeechInputBubbleBase::SetImage(const SkBitmap& image) { 237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen icon_image_.reset(new SkBitmap(image)); 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UpdateImage(); 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenSkBitmap SpeechInputBubbleBase::icon_image() { 242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return (icon_image_ != NULL) ? *icon_image_ : SkBitmap(); 243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 244