Browse files

Add about pages

  • Loading branch information...
1 parent 9bbeadd commit e2adce47e34880d003264f4e44c2e06a09b0420b @mnutt committed Jul 10, 2009
View
8 app/controllers/about_controller.rb
@@ -0,0 +1,8 @@
+class AboutController < ApplicationController
+ def format
+ end
+
+ def bookmarklet
+ end
+
+end
View
6 app/views/about/_nav.html.erb
@@ -0,0 +1,6 @@
+<div id="about_nav">
+ About:
+ <%= link_to "The Bookmarklet", about_url(:action => 'bookmarklet') -%>,
+ <%= link_to "Hidim Format", about_url(:action => 'format') -%>
+ <span style="float: right;"><%= link_to "Return Home", root_url -%></span>
+</div>
View
126 app/views/about/bookmarklet.html.erb
@@ -0,0 +1,126 @@
+<%= render :partial => 'nav' -%>
+
+<h3>The Bookmarklet</h3>
+
+<p>The Hid.im bookmarklet allows someone to covert a hidim to a torrent and save it to
+disk. First, it loads hidim_reader.js into the page and calls HidimReader.init(), which
+loads the reader's helper files:
+
+<pre class="sh_javascript">// Initialize helper scripts
+var helpers = ["sha1.js", "base64.js", "bdecode.js", "read_png.js"];
+for(var i = 0; i < helpers.length; i++) {
+ var n = document.createElement('script');
+ n.setAttribute('language', 'Javascript');
+ n.setAttribute('src', 'http://hid.im/javascripts/'+helpers[i]);
+ document.body.appendChild(n);
+}
+</pre>
+
+<p>
+It also begins watching all of the images for clicks, and simulating links on hover:
+</p>
+
+<pre class="sh_javascript">var images = document.getElementsByTagName('img');
+
+for(var i = 0; i < images.length; i++) {
+ var image = images[i];
+ image.addEventListener('click', this.addInfo, false);
+ image.addEventListener('mouseover',
+ function() {
+ this.style.cursor = "pointer";
+ },
+ false);
+}
+</pre>
+
+<p>
+When an image is clicked, it invokes PngReader.readPng (terrible name, I know) which
+creates a new canvas element, copies the image to it, and extracts the image data:
+<p>
+
+<pre class="sh_javascript">extractFromImg: function(img) {
+ var canvas = document.createElement('canvas');
+ var context = canvas.getContext('2d');
+
+ canvas.width = img.width;
+ canvas.height = img.height;
+ context.drawImage(img, 0, 0);
+
+ return context.getImageData(0, 0, img.width, img.height).data;
+}</pre>
+
+<p>
+One of the interesting properties of HTML5's canvas tag is that it allows direct pixel
+manipulation: getImageData returns a CanvasPixelArray which allows us to iterate through
+the whole image.
+</p>
+
+<p>
+In order to allow embedding a hidim in another image and generally avoid problems,
+we discard the alpha channel. We can do this by grouping the array into pixel values
+(red, green, blue, alpha) and lopping off the fourth element of each one. We then
+group the array of pixels into an array of rows.
+</p>
+
+<p>
+Since we want to read from bottom-to-top rather than left-to-right, we reverse the array
+and transpose. Now we have the data in a form that is useful to us.
+</p>
+
+<p>
+The next step is searching for the hidim key:
+<p>
+
+<pre class="sh_javascript">// Find the beginning of our data by looking for the key
+var dataStart = this.containsArray(torrent, key);
+</pre>
+
+<p>
+The key serves two purposes: it identifies the image as a hidim and it tells us where to
+begin reading data. We read the line height first, because it tells us how far to read
+before looping back to the next line. Next, we re-adjust the image array to only read
+from the hidim:
+</p>
+
+<pre class="sh_javascript">adjustForLineHeight: function(data, initialPosition, newHeight, imgHeight) {
+ var contentLength = newHeight * 3;
+ var lineLength = imgHeight * 3;
+ var output = [];
+ var position = initialPosition;
+
+ while(position + contentLength < data.length) {
+ output = output.concat(data.slice(position, position + contentLength));
+ position = position + lineLength;
+ }
+ return output;
+},
+</pre>
+
+<p>
+PngReader.readPng() produces a hash including the data, the SHA1 hash of the torrent,
+the length, and other details. We take the data in string form and convert it to Base64
+in order to serve it up as a file:
+</p>
+
+<pre class="sh_javascript">var data = "data:application/x-bittorrent;base64,";
+data += Base64.encode(a.file.data);
+var downloadLink = document.createElement('a');
+downloadLink.href = data;
+</pre>
+
+<p>
+Unfortunately, support for data-uri varies. Firefox opens the Open/Save File dialog box
+and offers to save the file with a random string for a filename. Safari names its file
+"DownloadedFile.torrent", but downoads it in the background with no indication that the
+downoad has succeeded. Chrome does not seem to support a data-uri of this length at all.
+</p>
+
+<p>
+To get around this problem for Firefox users there is a Firefox extension which saves
+hidims in exactly the same manner, but presents a much nicer "Save Torrent As..." dialog
+to the user.
+</p>
+
+<script type="text/javascript">
+ sh_highlightDocument();
+</script>
View
42 app/views/about/format.html.erb
@@ -0,0 +1,42 @@
+<%= render :partial => 'nav' -%>
+
+<h3>Hid.im image format</h3>
+<img style="float: right; margin: 0px 10px 0px 10px" src="/images/hidim_reading_pixels.png"/>
+
+
+<p>A hidim is a torrent that is embedded inside of a 24-bit png. The data portion of a
+hidim does not need to fill the image.</p>
+
+<p>Torrent data is read upward and to the right.</p>
+
+<p>The torrent is broken up into 3-byte segments, and each segment represents red,
+green, and blue channels in a pixel.</p>
+
+
+<p>Each hidim is identified by the hidim key:</p>
+
+<p style="clear: both"><code>[104, 105, 100, 105, 109, 32, 105, 115, 32, 116, 111, 114, 114, 101, 110, 116, 115, 33]</code></p>
+
+<p>This byte array identifies the image as a hidim and marks the start of hidim data.
+The hidim reader starts at the hidim key, reads the metadata to determine the column
+height and data length, then reads column-by-column to the right until the data length
+has been reached.</p>
+
+<h3>Metadata format</h3>
+
+<p>Metadata is similar to bencoding and has the following attributes:</p>
+<ol class="arabic simple">
+ <li>line length -- bencoded integer</li>
+ <li>filename -- bencoded string</li>
+ <li>sha1 hash of .torrent file -- bencoded string</li>
+ <li>data -- bencoded string</li>
+</ol>
+
+<p>Example metadata:</p>
+<code>i30e14:ubuntu.torrent40:f572d396fae9206628714fb2ce00f72e94f2258f30592:d8:anno...</code>
+
+<p>Metadata is stacked together without separators.</p>
+
+<script type="text/javascript">
+ sh_highlightDocument();
+</script>
View
6 app/views/hidims/index.html.erb
@@ -41,6 +41,7 @@
</div>
<% end -%>
+
<div class="section" id="recent">
<h2>Sample hidim'd torrents</h2>
<ul>
@@ -52,6 +53,11 @@
</ul>
</div>
+<p>Read more about how hidims work:
+ <%= link_to "The Bookmarklet", about_url(:action => 'bookmarklet') -%>,
+ <%= link_to "Hidim Format", about_url(:action => 'format') -%>
+</p>
+
<div class="section" id="opensource">
<h2>Open Source</h2>
<p>
View
1 config/routes.rb
@@ -1,5 +1,6 @@
ActionController::Routing::Routes.draw do |map|
map.resources :hidims
+ map.about '/about/:action', :controller => 'about'
map.root :controller => 'hidims', :action => 'index'
end
View
BIN public/images/hidim_reading_pixels.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
8 test/functional/hidims_controller_test.rb
@@ -1,8 +0,0 @@
-require 'test_helper'
-
-class HidimsControllerTest < ActionController::TestCase
- # Replace this with your real tests.
- test "the truth" do
- assert true
- end
-end
View
4 test/unit/helpers/hidims_helper_test.rb
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class HidimsHelperTest < ActionView::TestCase
-end

0 comments on commit e2adce4

Please sign in to comment.