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

Side by Side Diff: chrome/browser/speech/speech_input_bubble.cc

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

Powered by Google App Engine
This is Rietveld 408576698