summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authordmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-28 22:14:13 +0000
committerdmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-28 22:14:13 +0000
commit82fae8fe8122737f9414ff848671818b4d605a09 (patch)
tree8fd4f9da05fcf4daf3203af23a3deadc336206bd /native_client_sdk
parent12b44060cb69b3109bcee395ddaddcc1c87b9d43 (diff)
downloadchromium_src-82fae8fe8122737f9414ff848671818b4d605a09.zip
chromium_src-82fae8fe8122737f9414ff848671818b4d605a09.tar.gz
chromium_src-82fae8fe8122737f9414ff848671818b4d605a09.tar.bz2
NaCl SDK: Add an example that uses VarArrayBuffer and file input.
BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9802011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@129499 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rwxr-xr-xnative_client_sdk/src/build_tools/build_sdk.py1
-rw-r--r--native_client_sdk/src/examples/Makefile10
-rw-r--r--native_client_sdk/src/examples/file_histogram/Makefile76
-rw-r--r--native_client_sdk/src/examples/file_histogram/file_histogram.cc226
-rw-r--r--native_client_sdk/src/examples/file_histogram/file_histogram.html132
-rw-r--r--native_client_sdk/src/examples/file_histogram/file_histogram.nmf6
-rw-r--r--native_client_sdk/src/examples/index.html6
7 files changed, 452 insertions, 5 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index 3eeafd6..02d3089 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -343,6 +343,7 @@ def BuildToolchains(pepperdir, platform, arch, pepper_ver, toolchains):
EXAMPLE_MAP = {
'newlib': [
'debugging',
+ 'file_histogram',
'fullscreen_tumbler',
'gamepad',
'geturl',
diff --git a/native_client_sdk/src/examples/Makefile b/native_client_sdk/src/examples/Makefile
index 1c891c3..16e3547 100644
--- a/native_client_sdk/src/examples/Makefile
+++ b/native_client_sdk/src/examples/Makefile
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 The Native Client Authors. All rights reserved.
+# 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.
@@ -7,10 +7,10 @@
#   http://www.gnu.org/software/make/manual/make.html
#
-PROJECTS:=dlopen fullscreen_tumbler gamepad geturl hello_world_glibc
-PROJECTS+=hello_world_interactive hello_world_newlib input_events load_progress
-PROJECTS+=mouselock multithreaded_input_events pi_generator pong sine_synth
-PROJECTS+=tumbler websocket
+PROJECTS:=dlopen file_histogram fullscreen_tumbler gamepad geturl
+PROJECTS:=hello_world_glibc hello_world_interactive hello_world_newlib
+PROJECTS:=input_events load_progress mouselock multithreaded_input_events
+PROJECTS:=pi_generator pong sine_synth tumbler websocket
# Define the default target
all:
diff --git a/native_client_sdk/src/examples/file_histogram/Makefile b/native_client_sdk/src/examples/file_histogram/Makefile
new file mode 100644
index 0000000..da2464b
--- /dev/null
+++ b/native_client_sdk/src/examples/file_histogram/Makefile
@@ -0,0 +1,76 @@
+# 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.
+
+#
+# GNU Make based build file.  For details on GNU Make see:
+#   http://www.gnu.org/software/make/manual/make.html
+#
+
+#
+# Project information
+#
+# These variables store project specific settings for the project name
+# build flags, files to copy or install.  In the examples it is typically
+# only the list of sources and project name that will actually change and
+# the rest of the makefile is boilerplate for defining build rules.
+#
+PROJECT:=file_histogram
+LDFLAGS:=-lppapi_cpp -lppapi
+CXX_SOURCES:=$(PROJECT).cc
+
+
+#
+# Get pepper directory for toolchain and includes.
+#
+# If PEPPER_ROOT is not set, then assume it can be found a two directories up,
+# from the default example directory location.
+#
+THIS_MAKEFILE:=$(abspath $(lastword $(MAKEFILE_LIST)))
+NACL_SDK_ROOT?=$(abspath $(dir $(THIS_MAKEFILE))../..)
+
+# Project Build flags
+WARNINGS:=-Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
+CXXFLAGS:=-pthread -std=gnu++98 $(WARNINGS)
+
+#
+# Compute tool paths
+#
+#
+OSNAME:=$(shell python $(NACL_SDK_ROOT)/tools/getos.py)
+TC_PATH:=$(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_x86_newlib)
+CXX:=$(TC_PATH)/bin/i686-nacl-g++
+
+#
+# Disable DOS PATH warning when using Cygwin based tools Windows
+#
+CYGWIN ?= nodosfilewarning
+export CYGWIN
+
+
+# Declare the ALL target first, to make the 'all' target the default build
+all: $(PROJECT)_x86_32.nexe $(PROJECT)_x86_64.nexe
+
+# Define 32 bit compile and link rules for C++ sources
+x86_32_OBJS:=$(patsubst %.cc,%_32.o,$(CXX_SOURCES))
+$(x86_32_OBJS) : %_32.o : %.cc $(THIS_MAKE)
+ $(CXX) -o $@ -c $< -m32 -O0 -g $(CXXFLAGS)
+
+$(PROJECT)_x86_32.nexe : $(x86_32_OBJS)
+ $(CXX) -o $@ $^ -m32 -O0 -g $(CXXFLAGS) $(LDFLAGS)
+
+# Define 64 bit compile and link rules for C++ sources
+x86_64_OBJS:=$(patsubst %.cc,%_64.o,$(CXX_SOURCES))
+$(x86_64_OBJS) : %_64.o : %.cc $(THIS_MAKE)
+ $(CXX) -o $@ -c $< -m64 -O0 -g $(CXXFLAGS)
+
+$(PROJECT)_x86_64.nexe : $(x86_64_OBJS)
+ $(CXX) -o $@ $^ -m64 -O0 -g $(CXXFLAGS) $(LDFLAGS)
+
+
+# Define a phony rule so it always runs, to build nexe and start up server.
+.PHONY: RUN
+RUN: all
+ python ../httpd.py
+
+
diff --git a/native_client_sdk/src/examples/file_histogram/file_histogram.cc b/native_client_sdk/src/examples/file_histogram/file_histogram.cc
new file mode 100644
index 0000000..30405f4
--- /dev/null
+++ b/native_client_sdk/src/examples/file_histogram/file_histogram.cc
@@ -0,0 +1,226 @@
+// 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.
+
+/// @file file_histogram.cc
+/// This example demonstrates loading, running and scripting a very simple NaCl
+/// module. To load the NaCl module, the browser first looks for the
+/// CreateModule() factory method (at the end of this file). It calls
+/// CreateModule() once to load the module code from your .nexe. After the
+/// .nexe code is loaded, CreateModule() is not called again.
+///
+/// Once the .nexe code is loaded, the browser than calls the CreateInstance()
+/// method on the object returned by CreateModule(). It calls CreateInstance()
+/// each time it encounters an <embed> tag that references your NaCl module.
+///
+/// The browser can talk to your NaCl module via the postMessage() Javascript
+/// function. When you call postMessage() on your NaCl module from the browser,
+/// this becomes a call to the HandleMessage() method of your pp::Instance
+/// subclass. You can send messages back to the browser by calling the
+/// PostMessage() method on your pp::Instance. Note that these two methods
+/// (postMessage() in Javascript and PostMessage() in C++) are asynchronous.
+/// This means they return immediately - there is no waiting for the message
+/// to be handled. This has implications in your program design, particularly
+/// when mutating property values that are exposed to both the browser and the
+/// NaCl module.
+
+#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"
+
+namespace {
+
+const uint32_t kBlue = 0xff4040ffu;
+const uint32_t kBlack = 0xff000000u;
+const size_t kHistogramSize = 256u;
+
+} // namespace
+
+/// The Instance class. One of these exists for each instance of your NaCl
+/// module on the web page. The browser will ask the Module object to create
+/// a new Instance for each occurence of the <embed> tag that has these
+/// attributes:
+/// type="application/x-nacl"
+/// src="file_histogram.nmf"
+class FileHistogramInstance : public pp::Instance {
+ public:
+ /// The constructor creates the plugin-side instance.
+ /// @param[in] instance the handle to the browser-side plugin instance.
+ explicit FileHistogramInstance(PP_Instance instance)
+ : pp::Instance(instance),
+ callback_factory_(this),
+ flushing_(false),
+ histogram_() {
+ }
+ virtual ~FileHistogramInstance() {
+ }
+
+ 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(&FileHistogramInstance::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<FileHistogramInstance> 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];
+};
+
+/// The Module class. The browser calls the CreateInstance() method to create
+/// an instance of your NaCl module on the web page. The browser creates a new
+/// instance for each <embed> tag with type="application/x-nacl".
+class FileHistogramModule : public pp::Module {
+ public:
+ FileHistogramModule() : pp::Module() {}
+ virtual ~FileHistogramModule() {}
+
+ /// Create and return a FileHistogramInstance object.
+ /// @param[in] instance The browser-side instance.
+ /// @return the plugin-side instance.
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new FileHistogramInstance(instance);
+ }
+};
+
+namespace pp {
+/// Factory function called by the browser when the module is first loaded.
+/// The browser keeps a singleton of this module. It calls the
+/// CreateInstance() method on the object you return to make instances. There
+/// is one instance per <embed> tag on the page. This is the main binding
+/// point for your NaCl module with the browser.
+Module* CreateModule() {
+ return new FileHistogramModule();
+}
+} // namespace pp
diff --git a/native_client_sdk/src/examples/file_histogram/file_histogram.html b/native_client_sdk/src/examples/file_histogram/file_histogram.html
new file mode 100644
index 0000000..23c523b
--- /dev/null
+++ b/native_client_sdk/src/examples/file_histogram/file_histogram.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+ <!--
+ 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.
+ -->
+<head>
+ <title>File Histogram</title>
+
+ <script type="text/javascript">
+ FileHistogramModule = null; // Global application object.
+ statusText = 'NO-STATUS';
+
+ // Indicate load success.
+ function moduleDidLoad() {
+ FileHistogramModule = document.getElementById('file_histogram');
+ updateStatus('SUCCESS');
+ }
+
+ // If the page loads before the Native Client module loads, then set the
+ // status message indicating that the module is still loading. Otherwise,
+ // do not change the status message.
+ function pageDidLoad() {
+ if (FileHistogramModule == null) {
+ updateStatus('LOADING...');
+ } else {
+ // It's possible that the Native Client module onload event fired
+ // before the page's onload event. In this case, the status message
+ // will reflect 'SUCCESS', but won't be displayed. This call will
+ // display the current message.
+ updateStatus();
+ }
+ }
+
+ // Set the global status message. If the element with id 'statusField'
+ // exists, then set its HTML to the status message as well.
+ // opt_message The message test. If this is null or undefined, then
+ // attempt to set the element with id 'statusField' to the value of
+ // |statusText|.
+ function updateStatus(opt_message) {
+ if (opt_message)
+ statusText = opt_message;
+ var statusField = document.getElementById('status_field');
+ if (statusField) {
+ statusField.innerHTML = statusText;
+ }
+ }
+
+ function postFileContents(file) {
+ var reader = new FileReader();
+ reader.onload = function(load_event) {
+ if (FileHistogramModule)
+ FileHistogramModule.postMessage(load_event.target.result);
+ }
+ reader.readAsArrayBuffer(file);
+ }
+
+ // Handle a file being dropped on to the plugin's rectangle.
+ function handleFileDrop(dropEvent) {
+ if (!dropEvent.dataTransfer || !dropEvent.dataTransfer.files)
+ return;
+ dropEvent.stopPropagation();
+ dropEvent.preventDefault();
+ var files = dropEvent.dataTransfer.files;
+ for(var i = 0; i < files.length; ++i)
+ postFileContents(files[i]);
+ }
+
+ // Handle a file being chosen from the <input type=file...> tag.
+ function handleFileInput() {
+ var file_input = document.getElementById("FileInput");
+ var files = file_input.files;
+ for(var i = 0; i < files.length; ++i)
+ postFileContents(files[i]);
+ }
+ </script>
+</head>
+<body onload="pageDidLoad()">
+
+<h1>File Histogram example</h1>
+This example demonstrates the use of VarArrayBuffer, as well as a way to allow
+the user to provide one or more files to your Native Client application.
+<p>
+Select one or more files by clicking "Choose Files", or Drag and drop one or
+more files on to the square below. The embedded NaCl instance will read each
+file in order and display a histogram representing its contents.
+<p>
+ <!-- Load the published .nexe. This includes the 'nacl' attribute which
+ shows how to load multi-architecture modules. Each entry in the "nexes"
+ object in the .nmf manifest file is a key-value pair: the key is the
+ instruction set architecture ('x86-32', 'x86-64', etc.); the value is a URL
+ for the desired NaCl module.
+ To load the debug versions of your .nexes, set the 'nacl' attribute to the
+ _dbg.nmf version of the manifest file.
+
+ Note: The <EMBED> element is wrapped inside a <DIV>, which has both a 'load'
+ and a 'drop' event listener attached. This wrapping method is used instead of
+ attaching the event listeners directly to the <EMBED> element to ensure that
+ the listeners are active before the NaCl module 'load' event fires.
+ -->
+ <div id="listener">
+ <script type="text/javascript">
+ var listener = document.getElementById('listener');
+ listener.addEventListener('load', moduleDidLoad, true);
+ // The "drop" event fires when the user drops something (such as a file)
+ // on to the target element (in this case, listener).
+ listener.addEventListener('drop', handleFileDrop, true);
+ </script>
+
+ <embed name="nacl_module"
+ id="file_histogram"
+ width=256 height=256
+ src="file_histogram.nmf"
+ type="application/x-nacl" />
+ <form name="FileInput"
+ action=""
+ method=""
+ onsubmit="handleFileInput()">
+ <input type="file"
+ id="FileInput"
+ onchange="this.form.onsubmit()"
+ multiple>
+ </form>
+ </div>
+</p>
+
+<h2>Status</h2>
+<div id="status_field">NO-STATUS</div>
+</body>
+</html>
diff --git a/native_client_sdk/src/examples/file_histogram/file_histogram.nmf b/native_client_sdk/src/examples/file_histogram/file_histogram.nmf
new file mode 100644
index 0000000..75bf0d9
--- /dev/null
+++ b/native_client_sdk/src/examples/file_histogram/file_histogram.nmf
@@ -0,0 +1,6 @@
+{
+ "program": {
+ "x86-64": {"url": "file_histogram_x86_64.nexe"},
+ "x86-32": {"url": "file_histogram_x86_32.nexe"}
+ }
+}
diff --git a/native_client_sdk/src/examples/index.html b/native_client_sdk/src/examples/index.html
index 470d903..34aa471 100644
--- a/native_client_sdk/src/examples/index.html
+++ b/native_client_sdk/src/examples/index.html
@@ -174,6 +174,12 @@ mulithreading...</p></dd>
send the message to the server and retrieve the reply.
<p>Teaching focus: Websockets</p>
</dd>
+ <dt><a href="file_histogram/file_histogram.html">Get URL</a></dt>
+ <dd> The File Histogram example demonstrates prompting the user for a file,
+ passing the file contents to NativeClient as a VarArrayBuffer, then drawing a
+ histogram representing the contents of the file to a 2D square.
+ <p>Teaching focus: VarArrayBuffer, 2D, File input.</p>
+ </dd>
</dl>
</body>
</html>