| OLD | NEW |
| (Empty) |
| 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 "chrome/renderer/pepper_devices.h" | |
| 6 | |
| 7 #include "chrome/common/render_messages.h" | |
| 8 #include "chrome/common/render_messages_params.h" | |
| 9 #include "chrome/renderer/render_thread.h" | |
| 10 #include "chrome/renderer/webplugin_delegate_pepper.h" | |
| 11 #include "skia/ext/platform_canvas.h" | |
| 12 #include "third_party/skia/include/core/SkBitmap.h" | |
| 13 #include "webkit/plugins/npapi/plugin_instance.h" | |
| 14 #include "webkit/plugins/npapi/webplugin.h" | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 const uint32 kBytesPerPixel = 4; // Only 8888 RGBA for now. | |
| 19 | |
| 20 } // namespace | |
| 21 | |
| 22 int Graphics2DDeviceContext::next_buffer_id_ = 0; | |
| 23 | |
| 24 struct Graphics2DDeviceContext::FlushCallbackData { | |
| 25 FlushCallbackData(NPDeviceFlushContextCallbackPtr f, | |
| 26 NPP n, | |
| 27 NPDeviceContext2D* c, | |
| 28 NPUserData* u) | |
| 29 : function(f), | |
| 30 npp(n), | |
| 31 context(c), | |
| 32 user_data(u) { | |
| 33 } | |
| 34 | |
| 35 NPDeviceFlushContextCallbackPtr function; | |
| 36 NPP npp; | |
| 37 NPDeviceContext2D* context; | |
| 38 NPUserData* user_data; | |
| 39 }; | |
| 40 | |
| 41 Graphics2DDeviceContext::Graphics2DDeviceContext( | |
| 42 WebPluginDelegatePepper* plugin_delegate) | |
| 43 : plugin_delegate_(plugin_delegate) { | |
| 44 } | |
| 45 | |
| 46 Graphics2DDeviceContext::~Graphics2DDeviceContext() {} | |
| 47 | |
| 48 NPError Graphics2DDeviceContext::Initialize( | |
| 49 gfx::Rect window_rect, const NPDeviceContext2DConfig* config, | |
| 50 NPDeviceContext2D* context) { | |
| 51 int width = window_rect.width(); | |
| 52 int height = window_rect.height(); | |
| 53 uint32 buffer_size = width * height * kBytesPerPixel; | |
| 54 | |
| 55 // Allocate the transport DIB and the PlatformCanvas pointing to it. | |
| 56 #if defined(OS_MACOSX) | |
| 57 // On the Mac, shared memory has to be created in the browser in order to | |
| 58 // work in the sandbox. Do this by sending a message to the browser | |
| 59 // requesting a TransportDIB (see also | |
| 60 // chrome/renderer/webplugin_delegate_proxy.cc, method | |
| 61 // WebPluginDelegateProxy::CreateBitmap() for similar code). Note that the | |
| 62 // TransportDIB is _not_ cached in the browser; this is because this memory | |
| 63 // gets flushed by the renderer into another TransportDIB that represents the | |
| 64 // page, which is then in turn flushed to the screen by the browser process. | |
| 65 // When |transport_dib_| goes out of scope in the dtor, all of its shared | |
| 66 // memory gets reclaimed. | |
| 67 TransportDIB::Handle dib_handle; | |
| 68 IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(buffer_size, | |
| 69 false, | |
| 70 &dib_handle); | |
| 71 if (!RenderThread::current()->Send(msg)) | |
| 72 return NPERR_GENERIC_ERROR; | |
| 73 if (!TransportDIB::is_valid(dib_handle)) | |
| 74 return NPERR_OUT_OF_MEMORY_ERROR; | |
| 75 transport_dib_.reset(TransportDIB::Map(dib_handle)); | |
| 76 #else | |
| 77 transport_dib_.reset(TransportDIB::Create(buffer_size, ++next_buffer_id_)); | |
| 78 if (!transport_dib_.get()) | |
| 79 return NPERR_OUT_OF_MEMORY_ERROR; | |
| 80 #endif // defined(OS_MACOSX) | |
| 81 canvas_.reset(transport_dib_->GetPlatformCanvas(width, height)); | |
| 82 if (!canvas_.get()) | |
| 83 return NPERR_OUT_OF_MEMORY_ERROR; | |
| 84 | |
| 85 // Note that we need to get the address out of the bitmap rather than | |
| 86 // using plugin_buffer_->memory(). The memory() is when the bitmap data | |
| 87 // has had "Map" called on it. For Windows, this is separate than making a | |
| 88 // bitmap using the shared section. | |
| 89 const SkBitmap& plugin_bitmap = | |
| 90 canvas_->getTopPlatformDevice().accessBitmap(true); | |
| 91 SkAutoLockPixels locker(plugin_bitmap); | |
| 92 | |
| 93 // TODO(brettw) this theoretically shouldn't be necessary. But the | |
| 94 // platform device on Windows will fill itself with green to help you | |
| 95 // catch areas you didn't paint. | |
| 96 plugin_bitmap.eraseARGB(0, 0, 0, 0); | |
| 97 | |
| 98 // Save the canvas to the output context structure and save the | |
| 99 // OpenPaintContext for future reference. | |
| 100 context->region = plugin_bitmap.getAddr32(0, 0); | |
| 101 context->stride = width * kBytesPerPixel; | |
| 102 context->dirty.left = 0; | |
| 103 context->dirty.top = 0; | |
| 104 context->dirty.right = width; | |
| 105 context->dirty.bottom = height; | |
| 106 return NPERR_NO_ERROR; | |
| 107 } | |
| 108 | |
| 109 NPError Graphics2DDeviceContext::Flush(SkBitmap* committed_bitmap, | |
| 110 NPDeviceContext2D* context, | |
| 111 NPDeviceFlushContextCallbackPtr callback, | |
| 112 NPP id, void* user_data) { | |
| 113 // Draw the bitmap to the backing store. | |
| 114 // | |
| 115 // TODO(brettw) we can optimize this in the case where the entire canvas is | |
| 116 // updated by actually taking ownership of the buffer and not telling the | |
| 117 // plugin we're done using it. This wat we can avoid the copy when the entire | |
| 118 // canvas has been updated. | |
| 119 SkIRect src_rect = { context->dirty.left, | |
| 120 context->dirty.top, | |
| 121 context->dirty.right, | |
| 122 context->dirty.bottom }; | |
| 123 SkRect dest_rect = { SkIntToScalar(context->dirty.left), | |
| 124 SkIntToScalar(context->dirty.top), | |
| 125 SkIntToScalar(context->dirty.right), | |
| 126 SkIntToScalar(context->dirty.bottom) }; | |
| 127 SkCanvas committed_canvas(*committed_bitmap); | |
| 128 | |
| 129 // We want to replace the contents of the bitmap rather than blend. | |
| 130 SkPaint paint; | |
| 131 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
| 132 committed_canvas.drawBitmapRect( | |
| 133 canvas_->getTopPlatformDevice().accessBitmap(false), | |
| 134 &src_rect, dest_rect, &paint); | |
| 135 | |
| 136 committed_bitmap->setIsOpaque(false); | |
| 137 | |
| 138 // Cause the updated part of the screen to be repainted. This will happen | |
| 139 // asynchronously. | |
| 140 // TODO(brettw) is this the coorect coordinate system? | |
| 141 gfx::Rect dest_gfx_rect(context->dirty.left, context->dirty.top, | |
| 142 context->dirty.right - context->dirty.left, | |
| 143 context->dirty.bottom - context->dirty.top); | |
| 144 | |
| 145 plugin_delegate_->instance()->webplugin()->InvalidateRect(dest_gfx_rect); | |
| 146 | |
| 147 // Save the callback to execute later. See |unpainted_flush_callbacks_| in | |
| 148 // the header file. | |
| 149 if (callback) { | |
| 150 unpainted_flush_callbacks_.push_back( | |
| 151 FlushCallbackData(callback, id, context, user_data)); | |
| 152 } | |
| 153 | |
| 154 return NPERR_NO_ERROR; | |
| 155 } | |
| 156 | |
| 157 void Graphics2DDeviceContext::RenderViewInitiatedPaint() { | |
| 158 // Move all "unpainted" callbacks to the painted state. See | |
| 159 // |unpainted_flush_callbacks_| in the header for more. | |
| 160 std::copy(unpainted_flush_callbacks_.begin(), | |
| 161 unpainted_flush_callbacks_.end(), | |
| 162 std::back_inserter(painted_flush_callbacks_)); | |
| 163 unpainted_flush_callbacks_.clear(); | |
| 164 } | |
| 165 | |
| 166 void Graphics2DDeviceContext::RenderViewFlushedPaint() { | |
| 167 // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the | |
| 168 // header for more. | |
| 169 for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) { | |
| 170 const FlushCallbackData& data = painted_flush_callbacks_[i]; | |
| 171 data.function(data.npp, data.context, NPERR_NO_ERROR, data.user_data); | |
| 172 } | |
| 173 painted_flush_callbacks_.clear(); | |
| 174 } | |
| 175 | |
| 176 AudioDeviceContext::AudioDeviceContext() | |
| 177 : context_(NULL), | |
| 178 stream_id_(0), | |
| 179 shared_memory_size_(0) { | |
| 180 } | |
| 181 | |
| 182 AudioDeviceContext::~AudioDeviceContext() { | |
| 183 if (stream_id_) { | |
| 184 OnDestroy(); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 NPError AudioDeviceContext::Initialize(AudioMessageFilter* filter, | |
| 189 const NPDeviceContextAudioConfig* config, | |
| 190 NPDeviceContextAudio* context) { | |
| 191 DCHECK(filter); | |
| 192 // Make sure we don't call init more than once. | |
| 193 DCHECK_EQ(0, stream_id_); | |
| 194 | |
| 195 if (!config || !context) { | |
| 196 return NPERR_INVALID_PARAM; | |
| 197 } | |
| 198 filter_ = filter; | |
| 199 context_= context; | |
| 200 | |
| 201 ViewHostMsg_Audio_CreateStream_Params params; | |
| 202 params.params.format = AudioParameters::AUDIO_PCM_LINEAR; | |
| 203 params.params.channels = config->outputChannelMap; | |
| 204 params.params.sample_rate = config->sampleRate; | |
| 205 switch (config->sampleType) { | |
| 206 case NPAudioSampleTypeInt16: | |
| 207 params.params.bits_per_sample = 16; | |
| 208 break; | |
| 209 case NPAudioSampleTypeFloat32: | |
| 210 params.params.bits_per_sample = 32; | |
| 211 break; | |
| 212 default: | |
| 213 return NPERR_INVALID_PARAM; | |
| 214 } | |
| 215 | |
| 216 context->config = *config; | |
| 217 params.params.samples_per_packet = config->sampleFrameCount; | |
| 218 | |
| 219 stream_id_ = filter_->AddDelegate(this); | |
| 220 filter->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params, true)); | |
| 221 return NPERR_NO_ERROR; | |
| 222 } | |
| 223 | |
| 224 void AudioDeviceContext::OnDestroy() { | |
| 225 // Make sure we don't call destroy more than once. | |
| 226 DCHECK_NE(0, stream_id_); | |
| 227 filter_->RemoveDelegate(stream_id_); | |
| 228 filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_)); | |
| 229 stream_id_ = 0; | |
| 230 if (audio_thread_.get()) { | |
| 231 socket_->Close(); | |
| 232 audio_thread_->Join(); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 void AudioDeviceContext::OnRequestPacket(AudioBuffersState buffers_state) { | |
| 237 FireAudioCallback(); | |
| 238 filter_->Send(new ViewHostMsg_NotifyAudioPacketReady(0, stream_id_, | |
| 239 shared_memory_size_)); | |
| 240 } | |
| 241 | |
| 242 void AudioDeviceContext::OnStateChanged( | |
| 243 const ViewMsg_AudioStreamState_Params& state) { | |
| 244 } | |
| 245 | |
| 246 void AudioDeviceContext::OnCreated( | |
| 247 base::SharedMemoryHandle handle, uint32 length) { | |
| 248 #if defined(OS_WIN) | |
| 249 DCHECK(handle); | |
| 250 #else | |
| 251 DCHECK_NE(-1, handle.fd); | |
| 252 #endif | |
| 253 DCHECK(length); | |
| 254 DCHECK(context_); | |
| 255 | |
| 256 shared_memory_.reset(new base::SharedMemory(handle, false)); | |
| 257 shared_memory_->Map(length); | |
| 258 shared_memory_size_ = length; | |
| 259 | |
| 260 context_->outBuffer = shared_memory_->memory(); | |
| 261 FireAudioCallback(); | |
| 262 filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_)); | |
| 263 } | |
| 264 | |
| 265 void AudioDeviceContext::OnLowLatencyCreated( | |
| 266 base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, | |
| 267 uint32 length) { | |
| 268 #if defined(OS_WIN) | |
| 269 DCHECK(handle); | |
| 270 DCHECK(socket_handle); | |
| 271 #else | |
| 272 DCHECK_NE(-1, handle.fd); | |
| 273 DCHECK_NE(-1, socket_handle); | |
| 274 #endif | |
| 275 DCHECK(length); | |
| 276 DCHECK(context_); | |
| 277 DCHECK(!audio_thread_.get()); | |
| 278 shared_memory_.reset(new base::SharedMemory(handle, false)); | |
| 279 shared_memory_->Map(length); | |
| 280 shared_memory_size_ = length; | |
| 281 | |
| 282 context_->outBuffer = shared_memory_->memory(); | |
| 283 socket_.reset(new base::SyncSocket(socket_handle)); | |
| 284 // Allow the client to pre-populate the buffer. | |
| 285 FireAudioCallback(); | |
| 286 if (context_->config.startThread) { | |
| 287 audio_thread_.reset( | |
| 288 new base::DelegateSimpleThread(this, "plugin_audio_thread")); | |
| 289 audio_thread_->Start(); | |
| 290 } | |
| 291 filter_->Send(new ViewHostMsg_PlayAudioStream(0, stream_id_)); | |
| 292 } | |
| 293 | |
| 294 void AudioDeviceContext::OnVolume(double volume) { | |
| 295 } | |
| 296 | |
| 297 void AudioDeviceContext::Run() { | |
| 298 int pending_data; | |
| 299 while (sizeof(pending_data) == socket_->Receive(&pending_data, | |
| 300 sizeof(pending_data)) && | |
| 301 pending_data >= 0) { | |
| 302 FireAudioCallback(); | |
| 303 } | |
| 304 } | |
| OLD | NEW |