speech_input_bubble.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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 "chrome/browser/tab_contents/tab_contents.h"
6#include "chrome/browser/speech/speech_input_bubble.h"
7#include "grit/generated_resources.h"
8#include "grit/theme_resources.h"
9#include "ui/base/resource/resource_bundle.h"
10#include "ui/gfx/canvas_skia.h"
11#include "ui/gfx/rect.h"
12
13SpeechInputBubble::FactoryMethod SpeechInputBubble::factory_ = NULL;
14const int SpeechInputBubble::kBubbleTargetOffsetX = 5;
15
16SkBitmap* SpeechInputBubbleBase::mic_empty_ = NULL;
17SkBitmap* SpeechInputBubbleBase::mic_full_ = NULL;
18SkBitmap* SpeechInputBubbleBase::mic_mask_ = NULL;
19SkBitmap* SpeechInputBubbleBase::spinner_ = NULL;
20const int SpeechInputBubbleBase::kRecognizingAnimationStepMs = 100;
21
22SpeechInputBubble* SpeechInputBubble::Create(TabContents* tab_contents,
23                                             Delegate* delegate,
24                                             const gfx::Rect& element_rect) {
25  if (factory_)
26    return (*factory_)(tab_contents, delegate, element_rect);
27
28  // Has the tab already closed before bubble create request was processed?
29  if (!tab_contents)
30    return NULL;
31
32  return CreateNativeBubble(tab_contents, delegate, element_rect);
33}
34
35SpeechInputBubbleBase::SpeechInputBubbleBase(TabContents* tab_contents)
36    : ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
37      display_mode_(DISPLAY_MODE_RECORDING),
38      tab_contents_(tab_contents) {
39  if (!mic_empty_) {  // Static variables.
40    mic_empty_ = ResourceBundle::GetSharedInstance().GetBitmapNamed(
41        IDR_SPEECH_INPUT_MIC_EMPTY);
42    mic_full_ = ResourceBundle::GetSharedInstance().GetBitmapNamed(
43        IDR_SPEECH_INPUT_MIC_FULL);
44    mic_mask_ = ResourceBundle::GetSharedInstance().GetBitmapNamed(
45        IDR_SPEECH_INPUT_MIC_MASK);
46    spinner_ = ResourceBundle::GetSharedInstance().GetBitmapNamed(
47        IDR_SPEECH_INPUT_SPINNER);
48  }
49
50  // Instance variables.
51  mic_image_.reset(new SkBitmap());
52  mic_image_->setConfig(SkBitmap::kARGB_8888_Config, mic_empty_->width(),
53                        mic_empty_->height());
54  mic_image_->allocPixels();
55
56  buffer_image_.reset(new SkBitmap());
57  buffer_image_->setConfig(SkBitmap::kARGB_8888_Config, mic_empty_->width(),
58                           mic_empty_->height());
59  buffer_image_->allocPixels();
60
61  // The sprite image consists of all the animation frames put together in one
62  // horizontal/wide image. Each animation frame is square in shape within the
63  // sprite.
64  const int kFrameSize = spinner_->height();
65  for (SkIRect src_rect(SkIRect::MakeWH(kFrameSize, kFrameSize));
66       src_rect.fLeft < spinner_->width();
67       src_rect.offset(kFrameSize, 0)) {
68    SkBitmap frame;
69    spinner_->extractSubset(&frame, src_rect);
70
71    // The bitmap created by extractSubset just points to the same pixels as
72    // the original and adjusts rowBytes accordingly. However that doesn't
73    // render properly and gets vertically squished in Linux due to a bug in
74    // Skia. Until that gets fixed we work around by taking a real copy of it
75    // below as the copied bitmap has the correct rowBytes and renders fine.
76    SkBitmap frame_copy;
77    frame.copyTo(&frame_copy, SkBitmap::kARGB_8888_Config);
78    animation_frames_.push_back(frame_copy);
79  }
80}
81
82SpeechInputBubbleBase::~SpeechInputBubbleBase() {
83  // This destructor is added to make sure members such as the scoped_ptr
84  // get destroyed here and the derived classes don't have to care about such
85  // member variables which they don't use.
86}
87
88void SpeechInputBubbleBase::SetRecordingMode() {
89  task_factory_.RevokeAll();
90  display_mode_ = DISPLAY_MODE_RECORDING;
91  UpdateLayout();
92}
93
94void SpeechInputBubbleBase::SetRecognizingMode() {
95  display_mode_ = DISPLAY_MODE_RECOGNIZING;
96  UpdateLayout();
97
98  animation_step_ = 0;
99  MessageLoop::current()->PostDelayedTask(
100      FROM_HERE,
101      task_factory_.NewRunnableMethod(
102          &SpeechInputBubbleBase::DoRecognizingAnimationStep),
103      kRecognizingAnimationStepMs);
104}
105
106void SpeechInputBubbleBase::DoRecognizingAnimationStep() {
107  SetImage(animation_frames_[animation_step_]);
108  if (++animation_step_ >= static_cast<int>(animation_frames_.size()))
109    animation_step_ = 0;
110  MessageLoop::current()->PostDelayedTask(
111      FROM_HERE,
112      task_factory_.NewRunnableMethod(
113          &SpeechInputBubbleBase::DoRecognizingAnimationStep),
114      kRecognizingAnimationStepMs);
115}
116
117void SpeechInputBubbleBase::SetMessage(const string16& text) {
118  task_factory_.RevokeAll();
119  message_text_ = text;
120  display_mode_ = DISPLAY_MODE_MESSAGE;
121  UpdateLayout();
122}
123
124void SpeechInputBubbleBase::SetInputVolume(float volume) {
125  mic_image_->eraseARGB(0, 0, 0, 0);
126  buffer_image_->eraseARGB(0, 0, 0, 0);
127
128  int width = mic_image_->width();
129  int height = mic_image_->height();
130  SkCanvas canvas(*mic_image_);
131  SkCanvas buffer_canvas(*buffer_image_);
132
133  // The 'full volume' mic image is drawn clipped to the current volume level,
134  // and a gradient mask is applied over it with the 'multiply' compositing
135  // operator to show soft edges at the top.
136  buffer_canvas.save();
137  SkScalar clip_top = ((1.0f - volume) * height * 3) / 2.0f - height / 2.0f;
138  buffer_canvas.clipRect(SkRect::MakeLTRB(0, clip_top,
139      SkIntToScalar(width), SkIntToScalar(height)));
140  buffer_canvas.drawBitmap(*mic_full_, 0, 0);
141  buffer_canvas.restore();
142  SkPaint multiply_paint;
143  multiply_paint.setXfermode(SkXfermode::Create(SkXfermode::kMultiply_Mode));
144  buffer_canvas.drawBitmap(*mic_mask_, 0, clip_top, &multiply_paint);
145
146  // Draw the empty volume image first and the current volume image on top.
147  canvas.drawBitmap(*mic_empty_, 0, 0);
148  canvas.drawBitmap(*buffer_image_.get(), 0, 0);
149
150  SetImage(*mic_image_.get());
151}
152
153TabContents* SpeechInputBubbleBase::tab_contents() {
154  return tab_contents_;
155}
156