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 |