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 <algorithm> 6 #include <deque> 7 #include <string> 8 9 #include "ppapi/cpp/graphics_2d.h" 10 #include "ppapi/cpp/image_data.h" 11 #include "ppapi/cpp/instance.h" 12 #include "ppapi/cpp/module.h" 13 #include "ppapi/cpp/var.h" 14 #include "ppapi/cpp/var_array_buffer.h" 15 #include "ppapi/utility/completion_callback_factory.h" 16 17 #ifdef WIN32 18 #undef min 19 #undef max 20 #undef PostMessage 21 22 // Allow 'this' in initializer list 23 #pragma warning(disable : 4355) 24 // Disable warning about behaviour of array initialization. 25 #pragma warning(disable : 4351) 26 #endif 27 28 namespace { 29 30 const uint32_t kBlue = 0xff4040ffu; 31 const uint32_t kBlack = 0xff000000u; 32 const size_t kHistogramSize = 256u; 33 34 } // namespace 35 36 class VarArrayBufferInstance : public pp::Instance { 37 public: 38 explicit VarArrayBufferInstance(PP_Instance instance) 39 : pp::Instance(instance), 40 callback_factory_(this), 41 flushing_(false), 42 histogram_() {} 43 virtual ~VarArrayBufferInstance() {} 44 45 private: 46 /// Handler for messages coming in from the browser via postMessage(). The 47 /// @a var_message can contain anything: a JSON string; a string that encodes 48 /// method names and arguments; etc. 49 /// 50 /// In this case, we only handle <code>pp::VarArrayBuffer</code>s. When we 51 /// receive one, we compute and display a histogram based on its contents. 52 /// 53 /// @param[in] var_message The message posted by the browser. 54 virtual void HandleMessage(const pp::Var& var_message) { 55 if (var_message.is_array_buffer()) { 56 pp::VarArrayBuffer buffer(var_message); 57 ComputeHistogram(buffer); 58 DrawHistogram(); 59 } 60 } 61 62 /// Create and return a blank (all-black) <code>pp::ImageData</code> of the 63 /// given <code>size</code>. 64 pp::ImageData MakeBlankImageData(const pp::Size& size) { 65 const bool init_to_zero = false; 66 pp::ImageData image_data = 67 pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, init_to_zero); 68 uint32_t* image_buffer = static_cast<uint32_t*>(image_data.data()); 69 for (int i = 0; i < size.GetArea(); ++i) 70 image_buffer[i] = kBlack; 71 return image_data; 72 } 73 74 /// Draw a bar of the appropriate height based on <code>value</code> at 75 /// <code>column</code> in <code>image_data</code>. <code>value</code> must be 76 /// in the range [0, 1]. 77 void DrawBar(uint32_t column, double value, pp::ImageData* image_data) { 78 assert((value >= 0.0) && (value <= 1.0)); 79 uint32_t* image_buffer = static_cast<uint32_t*>(image_data->data()); 80 const uint32_t image_height = image_data->size().height(); 81 const uint32_t image_width = image_data->size().width(); 82 assert(column < image_width); 83 int bar_height = static_cast<int>(value * image_height); 84 for (int i = 0; i < bar_height; ++i) { 85 uint32_t row = image_height - 1 - i; 86 image_buffer[row * image_width + column] = kBlue; 87 } 88 } 89 90 void PaintAndFlush(pp::ImageData* image_data) { 91 assert(!flushing_); 92 graphics_2d_context_.ReplaceContents(image_data); 93 graphics_2d_context_.Flush( 94 callback_factory_.NewCallback(&VarArrayBufferInstance::DidFlush)); 95 flushing_ = true; 96 } 97 98 /// The callback that gets invoked when a flush completes. This is bound to a 99 /// <code>CompletionCallback</code> and passed as a parameter to 100 /// <code>Flush</code>. 101 void DidFlush(int32_t error_code) { 102 flushing_ = false; 103 // If there are no images in the queue, we're done for now. 104 if (paint_queue_.empty()) 105 return; 106 // Otherwise, pop the next image off the queue and draw it. 107 pp::ImageData image_data = paint_queue_.front(); 108 paint_queue_.pop_front(); 109 PaintAndFlush(&image_data); 110 } 111 112 virtual void DidChangeView(const pp::View& view) { 113 if (size_ != view.GetRect().size()) { 114 size_ = view.GetRect().size(); 115 const bool is_always_opaque = true; 116 graphics_2d_context_ = 117 pp::Graphics2D(this, view.GetRect().size(), is_always_opaque); 118 BindGraphics(graphics_2d_context_); 119 // The images in our queue are the wrong size, so we won't paint them. 120 // We'll only draw the most recently computed histogram. 121 paint_queue_.clear(); 122 DrawHistogram(); 123 } 124 } 125 126 /// Compute and normalize a histogram based on the given VarArrayBuffer. 127 void ComputeHistogram(pp::VarArrayBuffer& buffer) { 128 std::fill_n(histogram_, kHistogramSize, 0.0); 129 uint32_t buffer_size = buffer.ByteLength(); 130 if (buffer_size == 0) 131 return; 132 uint8_t* buffer_data = static_cast<uint8_t*>(buffer.Map()); 133 for (uint32_t i = 0; i < buffer_size; ++i) 134 histogram_[buffer_data[i]] += 1.0; 135 // Normalize. 136 double max = *std::max_element(histogram_, histogram_ + kHistogramSize); 137 for (uint32_t i = 0; i < kHistogramSize; ++i) 138 histogram_[i] /= max; 139 } 140 141 /// Draw the current histogram_ in to an pp::ImageData, then paint and flush 142 /// that image. If we're already waiting on a flush, push it on to 143 /// <code>paint_queue_</code> to paint later. 144 void DrawHistogram() { 145 pp::ImageData image_data = MakeBlankImageData(size_); 146 for (int i = 0; i < std::min(static_cast<int>(kHistogramSize), 147 image_data.size().width()); 148 ++i) { 149 DrawBar(i, histogram_[i], &image_data); 150 } 151 152 if (!flushing_) 153 PaintAndFlush(&image_data); 154 else 155 paint_queue_.push_back(image_data); 156 } 157 158 pp::Graphics2D graphics_2d_context_; 159 pp::CompletionCallbackFactory<VarArrayBufferInstance> callback_factory_; 160 161 /// A queue of images to paint. We must maintain a queue because we can not 162 /// call pp::Graphics2D::Flush while a Flush is already pending. 163 std::deque<pp::ImageData> paint_queue_; 164 165 /// The size of our rectangle in the DOM, as of the last time DidChangeView 166 /// was called. 167 pp::Size size_; 168 169 /// true iff we are flushing. 170 bool flushing_; 171 172 /// Stores the most recent histogram so that we can re-draw it if we get 173 /// resized. 174 double histogram_[kHistogramSize]; 175 }; 176 177 class VarArrayBufferModule : public pp::Module { 178 public: 179 VarArrayBufferModule() : pp::Module() {} 180 virtual ~VarArrayBufferModule() {} 181 182 virtual pp::Instance* CreateInstance(PP_Instance instance) { 183 return new VarArrayBufferInstance(instance); 184 } 185 }; 186 187 namespace pp { 188 Module* CreateModule() { return new VarArrayBufferModule(); } 189 } // namespace pp 190