15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <deque>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/graphics_2d.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/image_data.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/instance.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/module.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/var.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/cpp/var_array_buffer.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/utility/completion_callback_factory.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef WIN32
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef min
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef max
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#undef PostMessage
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Allow 'this' in initializer list
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(disable : 4355)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Disable warning about behaviour of array initialization.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(disable : 4351)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const uint32_t kBlue = 0xff4040ffu;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const uint32_t kBlack = 0xff000000u;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t kHistogramSize = 256u;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class VarArrayBufferInstance : public pp::Instance {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  explicit VarArrayBufferInstance(PP_Instance instance)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : pp::Instance(instance),
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        callback_factory_(this),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flushing_(false),
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        histogram_() {}
43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual ~VarArrayBufferInstance() {}
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Handler for messages coming in from the browser via postMessage().  The
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// @a var_message can contain anything: a JSON string; a string that encodes
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// method names and arguments; etc.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ///
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// In this case, we only handle <code>pp::VarArrayBuffer</code>s. When we
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// receive one, we compute and display a histogram based on its contents.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ///
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// @param[in] var_message The message posted by the browser.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void HandleMessage(const pp::Var& var_message) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (var_message.is_array_buffer()) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pp::VarArrayBuffer buffer(var_message);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ComputeHistogram(buffer);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DrawHistogram();
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Create and return a blank (all-black) <code>pp::ImageData</code> of the
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// given <code>size</code>.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pp::ImageData MakeBlankImageData(const pp::Size& size) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const bool init_to_zero = false;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    pp::ImageData image_data =
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, init_to_zero);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32_t* image_buffer = static_cast<uint32_t*>(image_data.data());
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < size.GetArea(); ++i)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      image_buffer[i] = kBlack;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return image_data;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Draw a bar of the appropriate height based on <code>value</code> at
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// <code>column</code> in <code>image_data</code>. <code>value</code> must be
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// in the range [0, 1].
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DrawBar(uint32_t column, double value, pp::ImageData* image_data) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert((value >= 0.0) && (value <= 1.0));
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32_t* image_buffer = static_cast<uint32_t*>(image_data->data());
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint32_t image_height = image_data->size().height();
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const uint32_t image_width = image_data->size().width();
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(column < image_width);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bar_height = static_cast<int>(value * image_height);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < bar_height; ++i) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uint32_t row = image_height - 1 - i;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      image_buffer[row * image_width + column] = kBlue;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void PaintAndFlush(pp::ImageData* image_data) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    assert(!flushing_);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    graphics_2d_context_.ReplaceContents(image_data);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    graphics_2d_context_.Flush(
94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        callback_factory_.NewCallback(&VarArrayBufferInstance::DidFlush));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flushing_ = true;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// The callback that gets invoked when a flush completes. This is bound to a
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// <code>CompletionCallback</code> and passed as a parameter to
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// <code>Flush</code>.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DidFlush(int32_t error_code) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flushing_ = false;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If there are no images in the queue, we're done for now.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (paint_queue_.empty())
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Otherwise, pop the next image off the queue and draw it.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pp::ImageData image_data = paint_queue_.front();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint_queue_.pop_front();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PaintAndFlush(&image_data);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void DidChangeView(const pp::View& view) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (size_ != view.GetRect().size()) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      size_ = view.GetRect().size();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const bool is_always_opaque = true;
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      graphics_2d_context_ =
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          pp::Graphics2D(this, view.GetRect().size(), is_always_opaque);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BindGraphics(graphics_2d_context_);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The images in our queue are the wrong size, so we won't paint them.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We'll only draw the most recently computed histogram.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      paint_queue_.clear();
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DrawHistogram();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Compute and normalize a histogram based on the given VarArrayBuffer.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ComputeHistogram(pp::VarArrayBuffer& buffer) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::fill_n(histogram_, kHistogramSize, 0.0);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint32_t buffer_size = buffer.ByteLength();
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (buffer_size == 0)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint8_t* buffer_data = static_cast<uint8_t*>(buffer.Map());
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (uint32_t i = 0; i < buffer_size; ++i)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histogram_[buffer_data[i]] += 1.0;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Normalize.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double max = *std::max_element(histogram_, histogram_ + kHistogramSize);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (uint32_t i = 0; i < kHistogramSize; ++i)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      histogram_[i] /= max;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Draw the current histogram_ in to an pp::ImageData, then paint and flush
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// that image. If we're already waiting on a flush, push it on to
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// <code>paint_queue_</code> to paint later.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DrawHistogram() {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pp::ImageData image_data = MakeBlankImageData(size_);
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int i = 0; i < std::min(static_cast<int>(kHistogramSize),
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                 image_data.size().width());
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++i) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DrawBar(i, histogram_[i], &image_data);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!flushing_)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PaintAndFlush(&image_data);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      paint_queue_.push_back(image_data);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pp::Graphics2D graphics_2d_context_;
159b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  pp::CompletionCallbackFactory<VarArrayBufferInstance> callback_factory_;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// A queue of images to paint. We must maintain a queue because we can not
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// call pp::Graphics2D::Flush while a Flush is already pending.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::deque<pp::ImageData> paint_queue_;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// The size of our rectangle in the DOM, as of the last time DidChangeView
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// was called.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pp::Size size_;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// true iff we are flushing.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool flushing_;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// Stores the most recent histogram so that we can re-draw it if we get
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /// resized.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double histogram_[kHistogramSize];
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
177b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class VarArrayBufferModule : public pp::Module {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
179b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  VarArrayBufferModule() : pp::Module() {}
180b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  virtual ~VarArrayBufferModule() {}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual pp::Instance* CreateInstance(PP_Instance instance) {
183b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return new VarArrayBufferInstance(instance);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace pp {
188b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)Module* CreateModule() { return new VarArrayBufferModule(); }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace pp
190