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