| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/bind.h" | 5 #include "base/bind.h" |
| 6 #include "base/lazy_instance.h" | 6 #include "base/lazy_instance.h" |
| 7 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
| 8 #include "chrome/browser/speech/speech_input_bubble.h" | 8 #include "chrome/browser/speech/speech_recognition_bubble.h" |
| 9 #include "content/public/browser/web_contents.h" | 9 #include "content/public/browser/web_contents.h" |
| 10 #include "grit/generated_resources.h" | 10 #include "grit/generated_resources.h" |
| 11 #include "grit/theme_resources.h" | 11 #include "grit/theme_resources.h" |
| 12 #include "ui/base/resource/resource_bundle.h" | 12 #include "ui/base/resource/resource_bundle.h" |
| 13 #include "ui/gfx/canvas_skia.h" | 13 #include "ui/gfx/canvas_skia.h" |
| 14 #include "ui/gfx/rect.h" | 14 #include "ui/gfx/rect.h" |
| 15 #include "ui/gfx/skbitmap_operations.h" | 15 #include "ui/gfx/skbitmap_operations.h" |
| 16 | 16 |
| 17 using content::WebContents; | 17 using content::WebContents; |
| 18 | 18 |
| 19 namespace { | 19 namespace { |
| 20 | 20 |
| 21 const color_utils::HSL kGrayscaleShift = { -1, 0, 0.6 }; | 21 const color_utils::HSL kGrayscaleShift = { -1, 0, 0.6 }; |
| 22 const int kWarmingUpAnimationStartMs = 500; | 22 const int kWarmingUpAnimationStartMs = 500; |
| 23 const int kWarmingUpAnimationStepMs = 100; | 23 const int kWarmingUpAnimationStepMs = 100; |
| 24 const int kRecognizingAnimationStepMs = 100; | 24 const int kRecognizingAnimationStepMs = 100; |
| 25 | 25 |
| 26 // A lazily initialized singleton to hold all the image used by the speech | 26 // A lazily initialized singleton to hold all the image used by the speech |
| 27 // input bubbles and safely destroy them on exit. | 27 // recognition bubbles and safely destroy them on exit. |
| 28 class SpeechInputBubbleImages { | 28 class SpeechRecognitionBubbleImages { |
| 29 public: | 29 public: |
| 30 const std::vector<SkBitmap>& spinner() { return spinner_; } | 30 const std::vector<SkBitmap>& spinner() { return spinner_; } |
| 31 const std::vector<SkBitmap>& warm_up() { return warm_up_; } | 31 const std::vector<SkBitmap>& warm_up() { return warm_up_; } |
| 32 SkBitmap* mic_full() { return mic_full_; } | 32 SkBitmap* mic_full() { return mic_full_; } |
| 33 SkBitmap* mic_empty() { return mic_empty_; } | 33 SkBitmap* mic_empty() { return mic_empty_; } |
| 34 SkBitmap* mic_noise() { return mic_noise_; } | 34 SkBitmap* mic_noise() { return mic_noise_; } |
| 35 SkBitmap* mic_mask() { return mic_mask_; } | 35 SkBitmap* mic_mask() { return mic_mask_; } |
| 36 | 36 |
| 37 private: | 37 private: |
| 38 // Private constructor to enforce singleton. | 38 // Private constructor to enforce singleton. |
| 39 friend struct base::DefaultLazyInstanceTraits<SpeechInputBubbleImages>; | 39 friend struct base::DefaultLazyInstanceTraits<SpeechRecognitionBubbleImages>; |
| 40 SpeechInputBubbleImages(); | 40 SpeechRecognitionBubbleImages(); |
| 41 | 41 |
| 42 std::vector<SkBitmap> spinner_; // Frames for the progress spinner. | 42 std::vector<SkBitmap> spinner_; // Frames for the progress spinner. |
| 43 std::vector<SkBitmap> warm_up_; // Frames for the warm up animation. | 43 std::vector<SkBitmap> warm_up_; // Frames for the warm up animation. |
| 44 | 44 |
| 45 // These bitmaps are owned by ResourceBundle and need not be destroyed. | 45 // These bitmaps are owned by ResourceBundle and need not be destroyed. |
| 46 SkBitmap* mic_full_; // Mic image with full volume. | 46 SkBitmap* mic_full_; // Mic image with full volume. |
| 47 SkBitmap* mic_noise_; // Mic image with full noise volume. | 47 SkBitmap* mic_noise_; // Mic image with full noise volume. |
| 48 SkBitmap* mic_empty_; // Mic image with zero volume. | 48 SkBitmap* mic_empty_; // Mic image with zero volume. |
| 49 SkBitmap* mic_mask_; // Gradient mask used by the volume indicator. | 49 SkBitmap* mic_mask_; // Gradient mask used by the volume indicator. |
| 50 }; | 50 }; |
| 51 | 51 |
| 52 SpeechInputBubbleImages::SpeechInputBubbleImages() { | 52 SpeechRecognitionBubbleImages::SpeechRecognitionBubbleImages() { |
| 53 mic_empty_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 53 mic_empty_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 54 IDR_SPEECH_INPUT_MIC_EMPTY); | 54 IDR_SPEECH_INPUT_MIC_EMPTY); |
| 55 mic_noise_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 55 mic_noise_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 56 IDR_SPEECH_INPUT_MIC_NOISE); | 56 IDR_SPEECH_INPUT_MIC_NOISE); |
| 57 mic_full_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 57 mic_full_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 58 IDR_SPEECH_INPUT_MIC_FULL); | 58 IDR_SPEECH_INPUT_MIC_FULL); |
| 59 mic_mask_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 59 mic_mask_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 60 IDR_SPEECH_INPUT_MIC_MASK); | 60 IDR_SPEECH_INPUT_MIC_MASK); |
| 61 | 61 |
| 62 // The sprite image consists of all the animation frames put together in one | 62 // The sprite image consists of all the animation frames put together in one |
| (...skipping 30 matching lines...) Expand all Loading... |
| 93 SkBitmap frame_copy; | 93 SkBitmap frame_copy; |
| 94 frame.copyTo(&frame_copy, SkBitmap::kARGB_8888_Config); | 94 frame.copyTo(&frame_copy, SkBitmap::kARGB_8888_Config); |
| 95 spinner_.push_back(frame_copy); | 95 spinner_.push_back(frame_copy); |
| 96 | 96 |
| 97 // The warm up spinner animation is a gray scale version of the real one. | 97 // The warm up spinner animation is a gray scale version of the real one. |
| 98 warm_up_.push_back(SkBitmapOperations::CreateHSLShiftedBitmap( | 98 warm_up_.push_back(SkBitmapOperations::CreateHSLShiftedBitmap( |
| 99 frame_copy, kGrayscaleShift)); | 99 frame_copy, kGrayscaleShift)); |
| 100 } | 100 } |
| 101 } | 101 } |
| 102 | 102 |
| 103 base::LazyInstance<SpeechInputBubbleImages> g_images = | 103 base::LazyInstance<SpeechRecognitionBubbleImages> g_images = |
| 104 LAZY_INSTANCE_INITIALIZER; | 104 LAZY_INSTANCE_INITIALIZER; |
| 105 | 105 |
| 106 } // namespace | 106 } // namespace |
| 107 | 107 |
| 108 SpeechInputBubble::FactoryMethod SpeechInputBubble::factory_ = NULL; | 108 SpeechRecognitionBubble::FactoryMethod SpeechRecognitionBubble::factory_ = NULL; |
| 109 const int SpeechInputBubble::kBubbleTargetOffsetX = 10; | 109 const int SpeechRecognitionBubble::kBubbleTargetOffsetX = 10; |
| 110 | 110 |
| 111 SpeechInputBubble* SpeechInputBubble::Create(WebContents* web_contents, | 111 SpeechRecognitionBubble* SpeechRecognitionBubble::Create( |
| 112 Delegate* delegate, | 112 WebContents* web_contents, Delegate* delegate, |
| 113 const gfx::Rect& element_rect) { | 113 const gfx::Rect& element_rect) { |
| 114 if (factory_) | 114 if (factory_) |
| 115 return (*factory_)(web_contents, delegate, element_rect); | 115 return (*factory_)(web_contents, delegate, element_rect); |
| 116 | 116 |
| 117 // Has the tab already closed before bubble create request was processed? | 117 // Has the tab already closed before bubble create request was processed? |
| 118 if (!web_contents) | 118 if (!web_contents) |
| 119 return NULL; | 119 return NULL; |
| 120 | 120 |
| 121 return CreateNativeBubble(web_contents, delegate, element_rect); | 121 return CreateNativeBubble(web_contents, delegate, element_rect); |
| 122 } | 122 } |
| 123 | 123 |
| 124 SpeechInputBubbleBase::SpeechInputBubbleBase(WebContents* web_contents) | 124 SpeechRecognitionBubbleBase::SpeechRecognitionBubbleBase( |
| 125 WebContents* web_contents) |
| 125 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 126 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
| 126 display_mode_(DISPLAY_MODE_RECORDING), | 127 display_mode_(DISPLAY_MODE_RECORDING), |
| 127 web_contents_(web_contents) { | 128 web_contents_(web_contents) { |
| 128 mic_image_.reset(new SkBitmap()); | 129 mic_image_.reset(new SkBitmap()); |
| 129 mic_image_->setConfig(SkBitmap::kARGB_8888_Config, | 130 mic_image_->setConfig(SkBitmap::kARGB_8888_Config, |
| 130 g_images.Get().mic_empty()->width(), | 131 g_images.Get().mic_empty()->width(), |
| 131 g_images.Get().mic_empty()->height()); | 132 g_images.Get().mic_empty()->height()); |
| 132 mic_image_->allocPixels(); | 133 mic_image_->allocPixels(); |
| 133 | 134 |
| 134 buffer_image_.reset(new SkBitmap()); | 135 buffer_image_.reset(new SkBitmap()); |
| 135 buffer_image_->setConfig(SkBitmap::kARGB_8888_Config, | 136 buffer_image_->setConfig(SkBitmap::kARGB_8888_Config, |
| 136 g_images.Get().mic_empty()->width(), | 137 g_images.Get().mic_empty()->width(), |
| 137 g_images.Get().mic_empty()->height()); | 138 g_images.Get().mic_empty()->height()); |
| 138 buffer_image_->allocPixels(); | 139 buffer_image_->allocPixels(); |
| 139 } | 140 } |
| 140 | 141 |
| 141 SpeechInputBubbleBase::~SpeechInputBubbleBase() { | 142 SpeechRecognitionBubbleBase::~SpeechRecognitionBubbleBase() { |
| 142 // This destructor is added to make sure members such as the scoped_ptr | 143 // This destructor is added to make sure members such as the scoped_ptr |
| 143 // get destroyed here and the derived classes don't have to care about such | 144 // get destroyed here and the derived classes don't have to care about such |
| 144 // member variables which they don't use. | 145 // member variables which they don't use. |
| 145 } | 146 } |
| 146 | 147 |
| 147 void SpeechInputBubbleBase::SetWarmUpMode() { | 148 void SpeechRecognitionBubbleBase::SetWarmUpMode() { |
| 148 weak_factory_.InvalidateWeakPtrs(); | 149 weak_factory_.InvalidateWeakPtrs(); |
| 149 display_mode_ = DISPLAY_MODE_WARM_UP; | 150 display_mode_ = DISPLAY_MODE_WARM_UP; |
| 150 animation_step_ = 0; | 151 animation_step_ = 0; |
| 151 DoWarmingUpAnimationStep(); | 152 DoWarmingUpAnimationStep(); |
| 152 UpdateLayout(); | 153 UpdateLayout(); |
| 153 } | 154 } |
| 154 | 155 |
| 155 void SpeechInputBubbleBase::DoWarmingUpAnimationStep() { | 156 void SpeechRecognitionBubbleBase::DoWarmingUpAnimationStep() { |
| 156 SetImage(g_images.Get().warm_up()[animation_step_]); | 157 SetImage(g_images.Get().warm_up()[animation_step_]); |
| 157 MessageLoop::current()->PostDelayedTask( | 158 MessageLoop::current()->PostDelayedTask( |
| 158 FROM_HERE, | 159 FROM_HERE, |
| 159 base::Bind(&SpeechInputBubbleBase::DoWarmingUpAnimationStep, | 160 base::Bind(&SpeechRecognitionBubbleBase::DoWarmingUpAnimationStep, |
| 160 weak_factory_.GetWeakPtr()), | 161 weak_factory_.GetWeakPtr()), |
| 161 base::TimeDelta::FromMilliseconds( | 162 base::TimeDelta::FromMilliseconds( |
| 162 animation_step_ == 0 ? kWarmingUpAnimationStartMs | 163 animation_step_ == 0 ? kWarmingUpAnimationStartMs |
| 163 : kWarmingUpAnimationStepMs)); | 164 : kWarmingUpAnimationStepMs)); |
| 164 if (++animation_step_ >= static_cast<int>(g_images.Get().warm_up().size())) | 165 if (++animation_step_ >= static_cast<int>(g_images.Get().warm_up().size())) |
| 165 animation_step_ = 1; // Frame 0 is skipped during the animation. | 166 animation_step_ = 1; // Frame 0 is skipped during the animation. |
| 166 } | 167 } |
| 167 | 168 |
| 168 void SpeechInputBubbleBase::SetRecordingMode() { | 169 void SpeechRecognitionBubbleBase::SetRecordingMode() { |
| 169 weak_factory_.InvalidateWeakPtrs(); | 170 weak_factory_.InvalidateWeakPtrs(); |
| 170 display_mode_ = DISPLAY_MODE_RECORDING; | 171 display_mode_ = DISPLAY_MODE_RECORDING; |
| 171 SetInputVolume(0, 0); | 172 SetInputVolume(0, 0); |
| 172 UpdateLayout(); | 173 UpdateLayout(); |
| 173 } | 174 } |
| 174 | 175 |
| 175 void SpeechInputBubbleBase::SetRecognizingMode() { | 176 void SpeechRecognitionBubbleBase::SetRecognizingMode() { |
| 176 display_mode_ = DISPLAY_MODE_RECOGNIZING; | 177 display_mode_ = DISPLAY_MODE_RECOGNIZING; |
| 177 animation_step_ = 0; | 178 animation_step_ = 0; |
| 178 DoRecognizingAnimationStep(); | 179 DoRecognizingAnimationStep(); |
| 179 UpdateLayout(); | 180 UpdateLayout(); |
| 180 } | 181 } |
| 181 | 182 |
| 182 void SpeechInputBubbleBase::DoRecognizingAnimationStep() { | 183 void SpeechRecognitionBubbleBase::DoRecognizingAnimationStep() { |
| 183 SetImage(g_images.Get().spinner()[animation_step_]); | 184 SetImage(g_images.Get().spinner()[animation_step_]); |
| 184 if (++animation_step_ >= static_cast<int>(g_images.Get().spinner().size())) | 185 if (++animation_step_ >= static_cast<int>(g_images.Get().spinner().size())) |
| 185 animation_step_ = 0; | 186 animation_step_ = 0; |
| 186 MessageLoop::current()->PostDelayedTask( | 187 MessageLoop::current()->PostDelayedTask( |
| 187 FROM_HERE, | 188 FROM_HERE, |
| 188 base::Bind(&SpeechInputBubbleBase::DoRecognizingAnimationStep, | 189 base::Bind(&SpeechRecognitionBubbleBase::DoRecognizingAnimationStep, |
| 189 weak_factory_.GetWeakPtr()), | 190 weak_factory_.GetWeakPtr()), |
| 190 base::TimeDelta::FromMilliseconds(kRecognizingAnimationStepMs)); | 191 base::TimeDelta::FromMilliseconds(kRecognizingAnimationStepMs)); |
| 191 } | 192 } |
| 192 | 193 |
| 193 void SpeechInputBubbleBase::SetMessage(const string16& text) { | 194 void SpeechRecognitionBubbleBase::SetMessage(const string16& text) { |
| 194 weak_factory_.InvalidateWeakPtrs(); | 195 weak_factory_.InvalidateWeakPtrs(); |
| 195 message_text_ = text; | 196 message_text_ = text; |
| 196 display_mode_ = DISPLAY_MODE_MESSAGE; | 197 display_mode_ = DISPLAY_MODE_MESSAGE; |
| 197 UpdateLayout(); | 198 UpdateLayout(); |
| 198 } | 199 } |
| 199 | 200 |
| 200 void SpeechInputBubbleBase::DrawVolumeOverlay(SkCanvas* canvas, | 201 void SpeechRecognitionBubbleBase::DrawVolumeOverlay(SkCanvas* canvas, |
| 201 const SkBitmap& bitmap, | 202 const SkBitmap& bitmap, |
| 202 float volume) { | 203 float volume) { |
| 203 buffer_image_->eraseARGB(0, 0, 0, 0); | 204 buffer_image_->eraseARGB(0, 0, 0, 0); |
| 204 | 205 |
| 205 int width = mic_image_->width(); | 206 int width = mic_image_->width(); |
| 206 int height = mic_image_->height(); | 207 int height = mic_image_->height(); |
| 207 SkCanvas buffer_canvas(*buffer_image_); | 208 SkCanvas buffer_canvas(*buffer_image_); |
| 208 | 209 |
| 209 buffer_canvas.save(); | 210 buffer_canvas.save(); |
| 210 const int kVolumeSteps = 12; | 211 const int kVolumeSteps = 12; |
| 211 SkScalar clip_right = | 212 SkScalar clip_right = |
| 212 (((1.0f - volume) * (width * (kVolumeSteps + 1))) - width) / kVolumeSteps; | 213 (((1.0f - volume) * (width * (kVolumeSteps + 1))) - width) / kVolumeSteps; |
| 213 buffer_canvas.clipRect(SkRect::MakeLTRB(0, 0, | 214 buffer_canvas.clipRect(SkRect::MakeLTRB(0, 0, |
| 214 SkIntToScalar(width) - clip_right, SkIntToScalar(height))); | 215 SkIntToScalar(width) - clip_right, SkIntToScalar(height))); |
| 215 buffer_canvas.drawBitmap(bitmap, 0, 0); | 216 buffer_canvas.drawBitmap(bitmap, 0, 0); |
| 216 buffer_canvas.restore(); | 217 buffer_canvas.restore(); |
| 217 SkPaint multiply_paint; | 218 SkPaint multiply_paint; |
| 218 multiply_paint.setXfermode(SkXfermode::Create(SkXfermode::kMultiply_Mode)); | 219 multiply_paint.setXfermode(SkXfermode::Create(SkXfermode::kMultiply_Mode)); |
| 219 buffer_canvas.drawBitmap(*g_images.Get().mic_mask(), -clip_right, 0, | 220 buffer_canvas.drawBitmap(*g_images.Get().mic_mask(), -clip_right, 0, |
| 220 &multiply_paint); | 221 &multiply_paint); |
| 221 | 222 |
| 222 canvas->drawBitmap(*buffer_image_.get(), 0, 0); | 223 canvas->drawBitmap(*buffer_image_.get(), 0, 0); |
| 223 } | 224 } |
| 224 | 225 |
| 225 void SpeechInputBubbleBase::SetInputVolume(float volume, float noise_volume) { | 226 void SpeechRecognitionBubbleBase::SetInputVolume(float volume, |
| 227 float noise_volume) { |
| 226 mic_image_->eraseARGB(0, 0, 0, 0); | 228 mic_image_->eraseARGB(0, 0, 0, 0); |
| 227 SkCanvas canvas(*mic_image_); | 229 SkCanvas canvas(*mic_image_); |
| 228 | 230 |
| 229 // Draw the empty volume image first and the current volume image on top, | 231 // Draw the empty volume image first and the current volume image on top, |
| 230 // and then the noise volume image on top of both. | 232 // and then the noise volume image on top of both. |
| 231 canvas.drawBitmap(*g_images.Get().mic_empty(), 0, 0); | 233 canvas.drawBitmap(*g_images.Get().mic_empty(), 0, 0); |
| 232 DrawVolumeOverlay(&canvas, *g_images.Get().mic_full(), volume); | 234 DrawVolumeOverlay(&canvas, *g_images.Get().mic_full(), volume); |
| 233 DrawVolumeOverlay(&canvas, *g_images.Get().mic_noise(), noise_volume); | 235 DrawVolumeOverlay(&canvas, *g_images.Get().mic_noise(), noise_volume); |
| 234 | 236 |
| 235 SetImage(*mic_image_.get()); | 237 SetImage(*mic_image_.get()); |
| 236 } | 238 } |
| 237 | 239 |
| 238 WebContents* SpeechInputBubbleBase::web_contents() { | 240 WebContents* SpeechRecognitionBubbleBase::web_contents() { |
| 239 return web_contents_; | 241 return web_contents_; |
| 240 } | 242 } |
| 241 | 243 |
| 242 void SpeechInputBubbleBase::SetImage(const SkBitmap& image) { | 244 void SpeechRecognitionBubbleBase::SetImage(const SkBitmap& image) { |
| 243 icon_image_.reset(new SkBitmap(image)); | 245 icon_image_.reset(new SkBitmap(image)); |
| 244 UpdateImage(); | 246 UpdateImage(); |
| 245 } | 247 } |
| 246 | 248 |
| 247 SkBitmap SpeechInputBubbleBase::icon_image() { | 249 SkBitmap SpeechRecognitionBubbleBase::icon_image() { |
| 248 return (icon_image_ != NULL) ? *icon_image_ : SkBitmap(); | 250 return (icon_image_ != NULL) ? *icon_image_ : SkBitmap(); |
| 249 } | 251 } |
| OLD | NEW |