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