Home | History | Annotate | Download | only in var_array_buffer
      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