blob: 9f42ee61aefa5cb7b2ab49bee80a3444fbaf49d7 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <deque>
#include <string>
#include "ppapi/cpp/graphics_2d.h"
#include "ppapi/cpp/image_data.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/utility/completion_callback_factory.h"
#ifdef WIN32
#undef min
#undef max
#undef PostMessage
// Allow 'this' in initializer list
#pragma warning(disable : 4355)
// Disable warning about behaviour of array initialization.
#pragma warning(disable : 4351)
#endif
namespace {
const uint32_t kBlue = 0xff4040ffu;
const uint32_t kBlack = 0xff000000u;
const size_t kHistogramSize = 256u;
} // namespace
class VarArrayBufferInstance : public pp::Instance {
public:
explicit VarArrayBufferInstance(PP_Instance instance)
: pp::Instance(instance),
callback_factory_(this),
flushing_(false),
histogram_() {}
virtual ~VarArrayBufferInstance() {}
private:
/// Handler for messages coming in from the browser via postMessage(). The
/// @a var_message can contain anything: a JSON string; a string that encodes
/// method names and arguments; etc.
///
/// In this case, we only handle <code>pp::VarArrayBuffer</code>s. When we
/// receive one, we compute and display a histogram based on its contents.
///
/// @param[in] var_message The message posted by the browser.
virtual void HandleMessage(const pp::Var& var_message) {
if (var_message.is_array_buffer()) {
pp::VarArrayBuffer buffer(var_message);
ComputeHistogram(buffer);
DrawHistogram();
}
}
/// Create and return a blank (all-black) <code>pp::ImageData</code> of the
/// given <code>size</code>.
pp::ImageData MakeBlankImageData(const pp::Size& size) {
const bool init_to_zero = false;
pp::ImageData image_data =
pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, init_to_zero);
uint32_t* image_buffer = static_cast<uint32_t*>(image_data.data());
for (int i = 0; i < size.GetArea(); ++i)
image_buffer[i] = kBlack;
return image_data;
}
/// Draw a bar of the appropriate height based on <code>value</code> at
/// <code>column</code> in <code>image_data</code>. <code>value</code> must be
/// in the range [0, 1].
void DrawBar(uint32_t column, double value, pp::ImageData* image_data) {
assert((value >= 0.0) && (value <= 1.0));
uint32_t* image_buffer = static_cast<uint32_t*>(image_data->data());
const uint32_t image_height = image_data->size().height();
const uint32_t image_width = image_data->size().width();
assert(column < image_width);
int bar_height = static_cast<int>(value * image_height);
for (int i = 0; i < bar_height; ++i) {
uint32_t row = image_height - 1 - i;
image_buffer[row * image_width + column] = kBlue;
}
}
void PaintAndFlush(pp::ImageData* image_data) {
assert(!flushing_);
graphics_2d_context_.ReplaceContents(image_data);
graphics_2d_context_.Flush(
callback_factory_.NewCallback(&VarArrayBufferInstance::DidFlush));
flushing_ = true;
}
/// The callback that gets invoked when a flush completes. This is bound to a
/// <code>CompletionCallback</code> and passed as a parameter to
/// <code>Flush</code>.
void DidFlush(int32_t error_code) {
flushing_ = false;
// If there are no images in the queue, we're done for now.
if (paint_queue_.empty())
return;
// Otherwise, pop the next image off the queue and draw it.
pp::ImageData image_data = paint_queue_.front();
paint_queue_.pop_front();
PaintAndFlush(&image_data);
}
virtual void DidChangeView(const pp::View& view) {
if (size_ != view.GetRect().size()) {
size_ = view.GetRect().size();
const bool is_always_opaque = true;
graphics_2d_context_ =
pp::Graphics2D(this, view.GetRect().size(), is_always_opaque);
BindGraphics(graphics_2d_context_);
// The images in our queue are the wrong size, so we won't paint them.
// We'll only draw the most recently computed histogram.
paint_queue_.clear();
DrawHistogram();
}
}
/// Compute and normalize a histogram based on the given VarArrayBuffer.
void ComputeHistogram(pp::VarArrayBuffer& buffer) {
std::fill_n(histogram_, kHistogramSize, 0.0);
uint32_t buffer_size = buffer.ByteLength();
if (buffer_size == 0)
return;
uint8_t* buffer_data = static_cast<uint8_t*>(buffer.Map());
for (uint32_t i = 0; i < buffer_size; ++i)
histogram_[buffer_data[i]] += 1.0;
// Normalize.
double max = *std::max_element(histogram_, histogram_ + kHistogramSize);
for (uint32_t i = 0; i < kHistogramSize; ++i)
histogram_[i] /= max;
}
/// Draw the current histogram_ in to an pp::ImageData, then paint and flush
/// that image. If we're already waiting on a flush, push it on to
/// <code>paint_queue_</code> to paint later.
void DrawHistogram() {
pp::ImageData image_data = MakeBlankImageData(size_);
for (int i = 0; i < std::min(static_cast<int>(kHistogramSize),
image_data.size().width());
++i) {
DrawBar(i, histogram_[i], &image_data);
}
if (!flushing_)
PaintAndFlush(&image_data);
else
paint_queue_.push_back(image_data);
}
pp::Graphics2D graphics_2d_context_;
pp::CompletionCallbackFactory<VarArrayBufferInstance> callback_factory_;
/// A queue of images to paint. We must maintain a queue because we can not
/// call pp::Graphics2D::Flush while a Flush is already pending.
std::deque<pp::ImageData> paint_queue_;
/// The size of our rectangle in the DOM, as of the last time DidChangeView
/// was called.
pp::Size size_;
/// true iff we are flushing.
bool flushing_;
/// Stores the most recent histogram so that we can re-draw it if we get
/// resized.
double histogram_[kHistogramSize];
};
class VarArrayBufferModule : public pp::Module {
public:
VarArrayBufferModule() : pp::Module() {}
virtual ~VarArrayBufferModule() {}
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new VarArrayBufferInstance(instance);
}
};
namespace pp {
Module* CreateModule() { return new VarArrayBufferModule(); }
} // namespace pp