<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[MW]]></title><description><![CDATA[Mostly about programming]]></description><link>https://www.mwguy.com/</link><generator>Ghost 0.11</generator><lastBuildDate>Sat, 22 Nov 2025 00:19:33 GMT</lastBuildDate><atom:link href="https://www.mwguy.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Decoding a PNG Image in JavaScript]]></title><description><![CDATA[<p>If you're interested in how to decode a PNG without using a library, I've outlined the steps I took to decode a simple PNG with explanations and code samples. The methods below are not made for efficiency and it is recommended to use an existing library for projects.</p>

<h4 id="loadingthefile">Loading the</h4>]]></description><link>https://www.mwguy.com/decoding-a-png-image-in-javascript/</link><guid isPermaLink="false">64eff356-aa90-4b47-b794-1498226c0991</guid><dc:creator><![CDATA[Michael Wang]]></dc:creator><pubDate>Sat, 25 Mar 2017 19:44:31 GMT</pubDate><content:encoded><![CDATA[<p>If you're interested in how to decode a PNG without using a library, I've outlined the steps I took to decode a simple PNG with explanations and code samples. The methods below are not made for efficiency and it is recommended to use an existing library for projects.</p>

<h4 id="loadingthefile">Loading the File</h4>

<p>In order to work on a PNG file the raw data will need to be retrieved. This is done using an XMLHttpRequest with the <code>responseType</code> set to <code>'arraybuffer'</code>.</p>

<pre><code>const req = new XMLHttpRequest();

req.responseType = 'arraybuffer';  
req.addEventListener('load', (e) =&gt; {  
  const arrayBuffer = e.target.response;

  if (!arrayBuffer) {
    throw new Error('No response');
  }

  // this the the byte array to decode
  const byteArray = new Uint8Array(arrayBuffer);
});
req.addEventListener('error', () =&gt; {  
  throw new Error('An error has occurred on request');
});

req.open('GET', 'foo.png', true);  
req.send();  
</code></pre>

<h4 id="checkingthesignature">Checking the Signature</h4>

<p>A PNG file must start with the following bytes: <code>137, 80, 78, 71, 13, 10, 26, 10</code>. It is also recommended to check the signature of chunks (explained in the next section) that are constant when possible. This allows us to check the <code>IHDR</code> chunk as it must be the first chunk in a PNG file and the length is always <code>13</code>.</p>

<p>Given this information, we can check that the PNG file starts with the following bytes: <code>137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82</code>.</p>

<pre><code>const signature = [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82];

for (let i = 0; i &lt; signature.length; i++) {  
  if (byteArray[i] !== signature[i]) {
    return false;
  }
}
</code></pre>

<h4 id="readingthechunkmetadata">Reading the Chunk Meta Data</h4>

<p>Before diving into how the chunks are read, understanding what a chunk is and the structure of one is required.</p>

<p>A PNG file has multiple chunks starting after the first 8 bytes (these are reserved for the PNG file signature). All chunks are structured in this order:</p>

<ol>
<li>length (4 bytes) - this includes only the length of the data portion of the chunk with a maximum value of 2^31  </li>
<li>type (4 bytes) - this is the type of chunk (must be treated as binary values)  </li>
<li>data (X bytes) - data in the chunk, may be 0  </li>
<li>CRC (4 bytes) - CRC calculated on the preceding bytes in the chunk, excludes length</li>
</ol>

<p>The <code>IHDR</code>, <code>IDAT</code>, and <code>IEND</code> chunks are required. It is also possible to have multiple chunks of the same type.</p>

<p>Armed with the knowledge of chunks, recall that the check in the previous section there were some bytes appended to the PNG signature. The checked bytes can be seen as the <code>PNG Signature</code> + <code>IHDR Chunk Length</code> + <code>IHDR Chunk Type</code>.</p>

<p>The code below reads all the chunks meta data along with the position of where the data starts to make chunks easier to work with.</p>

<pre><code>// bytesToUint32 converts the given bytes from the "start" to "count" to an unsigned 32 bit integer.
function bytesToUint32(byteArray, start, count) { ... }

const META_SIZE = 4;  
const chunks = [];  
let i = PNG_SIGNATURE.length; // skip the PNG signature  
while (i &lt; byteArray.byteLength) {  
  const dataLength = bytesToUint32(byteArray, i, META_SIZE);

  i += META_SIZE;
  const signature = bytesToString(byteArray, i, META_SIZE);
  const type = chunkSignatureType[signature];

  i += META_SIZE;
  const dataStart = i;

  i += dataLength;
  const crc = bytesToUint32(byteArray, i, META_SIZE);

  i += META_SIZE;

  const meta = {
    type,
    signature,
    crc,
    data: {
      start: dataStart,
      length: dataLength,
    }
  };

  if (type) {
    chunks.push(meta);
  }

  // IEND must be the last chunk
  if (type === 'IEND') {
    break;
  }
}
</code></pre>

<h4 id="parsingihdr">Parsing IHDR</h4>

<p>The IHDR chunk has the following form:</p>

<ol>
<li>Width: 4 bytes  </li>
<li>Height: 4 bytes  </li>
<li>Bit depth: 1 byte  </li>
<li>Color type: 1 byte  </li>
<li>Compression method: 1 byte  </li>
<li>Filter method: 1 byte  </li>
<li>Interlace method: 1 byte</li>
</ol>

<p>From the start of the data of the chunk, the values can be retrieved by reading the bytes and offsetting by that amount for the next value.</p>

<h4 id="parsingidat">Parsing IDAT</h4>

<p>IDAT chunk or chunks contain the image data. It uses an LZ77 derivative and must be decompressed before use. If there are multiple IDAT chunks the data must be concatenated before decompression.</p>

<pre><code>// I used pako for decompressing, 
const compressed = byteArray.slice(start, start + length);  
const decompressed = pako.inflate(compressed);  
</code></pre>

<p>Once decompressed, the data can be read though additional work will need to be done. An extra byte is at the start of each scanline (a row of pixels) which tells us what filter was used.</p>

<p>In the case of the sub filter, the following is used:  </p>

<pre><code>// filteredBytes represents bytes that have already been unfiltered.
// byteOffset represents the byte to start from in the unfilteredBytes array.
// bytesPerPixel is used to know how many bytes to subtract to get to the previous sample.
function setSubLine(unfilteredBytes, byteOffset, byteArray, start, length, bytesPerPixel) {  
  for (let i = 0; i &lt; length; i++) {
    const offset = byteOffset + i - bytesPerPixel;
    const current = byteArray[start + i];
    const previous = offset &gt;= byteOffset ? unfilteredBytes[offset] : 0;

    unfilteredBytes.push((current + previous) &amp; 0xFF);
  }
}
</code></pre>

<p>The unfilteredBytes array now contains the pixel information starting from the upper-left of the image.</p>

<p>More info on filters, documentation can be found <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html">here</a></p>

<h4 id="summary">Summary</h4>

<p>Reading a simple PNG in JavaScript is not too bad once the structure is known. I would recommend using a better way to read bytes and converting to them as it seemed to be the part that gave me the most trouble in keeping the code clean.</p>

<p>The finished simple PNG loading code for this post can be found at <a href="https://github.com/MWGitHub/basic-loaders/tree/master/src/png">https://github.com/MWGitHub/basic-loaders/tree/master/src/png</a></p>

<h4 id="additionalreading">Additional Reading</h4>

<p>The PNG specification which can be found <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html">here</a> is a great resource on working with the file format.</p>

<p>Unrelated but also interesting is how PNG versioning is done which can be found <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html">here</a></p>]]></content:encoded></item><item><title><![CDATA[SpotBrowser Design]]></title><description><![CDATA[<p>Recently I made a Spotify artist browser with track previewing using only native JavaScript along with some shell scripts to build it. The time to create and complete the web application was two days.</p>

<p>The web application can be found <a href="https://github.com/MWGitHub/spotbrowse">here</a>. <br>
The source code can be found <a href="https://github.com/MWGitHub/spotbrowse">here</a>.</p>

<p><img src="https://www.mwguy.com/content/images/2016/05/welcome.png" alt=""></p>

<h4 id="architecture">Architecture</h4>

<h6 id="flow">Flow</h6>]]></description><link>https://www.mwguy.com/spotbrowser-design/</link><guid isPermaLink="false">e7d1afc2-eb61-44af-b3ce-9f4939b7d319</guid><dc:creator><![CDATA[Michael Wang]]></dc:creator><pubDate>Mon, 23 May 2016 00:11:53 GMT</pubDate><content:encoded><![CDATA[<p>Recently I made a Spotify artist browser with track previewing using only native JavaScript along with some shell scripts to build it. The time to create and complete the web application was two days.</p>

<p>The web application can be found <a href="https://github.com/MWGitHub/spotbrowse">here</a>. <br>
The source code can be found <a href="https://github.com/MWGitHub/spotbrowse">here</a>.</p>

<p><img src="https://www.mwguy.com/content/images/2016/05/welcome.png" alt=""></p>

<h4 id="architecture">Architecture</h4>

<h6 id="flow">Flow</h6>

<p>The architecture is somewhat like Flux where data only goes a single direction. The flow of the program is:</p>

<ul>
<li>Page loads with blank placeholders</li>
<li>Fetch request sent which notifies the store</li>
<li>Store does nothing on send request but passes it to interested listeners for the action</li>
<li>All listening components update to display the loading animation</li>
<li>Fetch request finishes and passes the result to the store</li>
<li>Store sets data and passes it to interested listeners</li>
<li>Components that are listening asks the store for the data and handles all the required updates</li>
</ul>

<h6 id="detail">Detail</h6>

<p>There are no preset rendering methods. Some components only modify what is already on the DOM and others remove and create on update. Components generally do not modify the DOM or run functions unless a listened event triggers.</p>

<p>There is only a single store since listeners are not store based but action based.</p>

<p>The API utility is the main way to update the store to reduce the complexity of the application though it is still possible to accidentally get stuck in an update loop.  </p>

<p>The player on the parent side and the iframe player handles message sending and retrieval from each other. None of the actions directly play songs which allowed for a decoupled way of syncing states between the top tracks and player.  </p>

<h5 id="futurerefactors">Future Refactors</h5>

<p>In the beginning I wanted to create a component that handled binding variables to the DOM with the <code>block.js</code> file but decided to scrap that due to time constraints. <code>block.js</code> now only only handles the passing of information to the change callback when properties are updated. The library does not add much more functionality so I will be refactoring or removing it.</p>

<p>I also created a component <code>elemental.js</code> which creates a pre-made loader. More features will be added to it so that it can take in JSON representing what DOM elements will be created and return the root along with references to the elements when set.</p>

<p>A component base class can be made as some of the code is similar between components. Once the base class is created features can be added through composition instead.</p>]]></content:encoded></item><item><title><![CDATA[Boot]]></title><description><![CDATA[<p>The OS is now bootable by following most of the guide <a href="http://os.phil-opp.com/multiboot-kernel.html">here</a>. Some parts in the source are different as I was also reading through the <a href="http://wiki.osdev.org/Bare_Bones">osdev bare bones tutorial</a>. I had to build another cross compiler to build the kernel targeting i686.</p>

<p>The result: <br>
<img src="https://www.mwguy.com/content/images/2016/05/first-boot.png" alt="alt"></p>]]></description><link>https://www.mwguy.com/boot/</link><guid isPermaLink="false">a72ad723-7f27-4632-a0e6-c2b5749c7de9</guid><category><![CDATA[giddyos]]></category><dc:creator><![CDATA[Michael Wang]]></dc:creator><pubDate>Sun, 15 May 2016 23:23:48 GMT</pubDate><content:encoded><![CDATA[<p>The OS is now bootable by following most of the guide <a href="http://os.phil-opp.com/multiboot-kernel.html">here</a>. Some parts in the source are different as I was also reading through the <a href="http://wiki.osdev.org/Bare_Bones">osdev bare bones tutorial</a>. I had to build another cross compiler to build the kernel targeting i686.</p>

<p>The result: <br>
<img src="https://www.mwguy.com/content/images/2016/05/first-boot.png" alt="alt"></p>]]></content:encoded></item><item><title><![CDATA[First Steps into Making an OS]]></title><description><![CDATA[<p>I'm planning on making an OS with Rust. The source code can be found <a href="https://github.com/MWGitHub/GiddyOS">here</a>. There's nothing there yet!</p>

<p>In this step I compiled gcc 6.1.0 on OS X with the target as x86_64-elf and a suffix so I can have one cross compiler for the kernel</p>]]></description><link>https://www.mwguy.com/first-steps-into-making-an-os/</link><guid isPermaLink="false">0fbdc589-8dd9-48a4-bc23-40c2816d1f67</guid><category><![CDATA[giddyos]]></category><dc:creator><![CDATA[Michael Wang]]></dc:creator><pubDate>Sat, 14 May 2016 02:50:18 GMT</pubDate><content:encoded><![CDATA[<p>I'm planning on making an OS with Rust. The source code can be found <a href="https://github.com/MWGitHub/GiddyOS">here</a>. There's nothing there yet!</p>

<p>In this step I compiled gcc 6.1.0 on OS X with the target as x86_64-elf and a suffix so I can have one cross compiler for the kernel and one for user-space. Doing this allows me to build for the target on my machine before the OS can be used for development.</p>

<p>On my first attempt I installed GCC and Binutils without a target. After reading the <a href="http://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler%3F">Why do I need a Cross Compiler?</a> page I decided to build again with a target this time to avoid problems.</p>

<p>To install gcc I followed the steps with some variations from the very useful osdev cross compiler page found <a href="http://wiki.osdev.org/GCC_Cross-Compiler">here</a>. I built the dependencies (GMP, MPFR, MPC, ISL) separately instead, each with the same prefix path. Now that I have a cross compiler, the next step is to make the kernel.</p>]]></content:encoded></item><item><title><![CDATA[Focused Task List]]></title><description><![CDATA[<p>During the past few weeks I have been experimenting with types of workflows for task lists. At the moment I am using a task board with four different lists.</p>

<p><img src="https://www.mwguy.com/content/images/2015/10/focus-v1-2.png" alt="focus-board-image"></p>

<p>The first list, "Tasks", is where I create any task that I plan on doing in the future. The second list,</p>]]></description><link>https://www.mwguy.com/focused-task-list/</link><guid isPermaLink="false">c9cc1bf0-b19e-4bbd-9e1a-6d3fd3ef1978</guid><dc:creator><![CDATA[Michael Wang]]></dc:creator><pubDate>Sat, 03 Oct 2015 21:06:35 GMT</pubDate><content:encoded><![CDATA[<p>During the past few weeks I have been experimenting with types of workflows for task lists. At the moment I am using a task board with four different lists.</p>

<p><img src="https://www.mwguy.com/content/images/2015/10/focus-v1-2.png" alt="focus-board-image"></p>

<p>The first list, "Tasks", is where I create any task that I plan on doing in the future. The second list, "Tomorrow", is a list that queues tasks which move to the third list at the end of the day, which is the "Today" list. </p>

<p>There are only two ways to add tasks in the "Today" list, queuing from the "Tomorrow" list, and creating tasks in the "Today" list as extra tasks. Extra tasks will mainly be used for stat tracking so that I can get a feel of how many tasks I should normally be putting in the queue. Any task in this list will age once per day if not completed. Tasks cannot be deleted until a week has passed, which gives ample time to think about the task before dismissing it. When a task is done I check the task and it moves to the "Complete" list at the end of the day.</p>

<p>The "Complete" list is an immutable list with no interaction. It's just a way to see what you've finished recently.</p>

<p>If you want to try out this workflow I have a web app up for it at: <br>
<a href="https://www.bop.xyz/focus/">https://www.bop.xyz/focus/</a></p>]]></content:encoded></item></channel></rss>