inweb-bootstrap/docs/foundation-module/6-id.html
2020-04-08 23:41:00 +01:00

141 lines
17 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Image Dimensions</title>
<meta name="viewport" content="width=device-width initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="../inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body>
<nav role="navigation">
<h1><a href="../webs.html">Sources</a></h1>
<ul>
<li><a href="../inweb/index.html">inweb</a></li>
</ul>
<h2>Foundation</h2>
<ul>
<li><a href="../foundation-module/index.html">foundation-module</a></li>
<li><a href="../foundation-test/index.html">foundation-test</a></li>
</ul>
</nav>
<main role="main">
<!--Weave of 'Image Dimensions' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">Source</a></li><li><a href="index.html">foundation</a></li><li><a href="index.html#6">Chapter 6: Media</a></li><li><b>Image Dimensions</b></li></ul><p class="purpose">These utility routines look at the headers of JPEG and PNG files to find the pixel dimensions of any images supplied by the user for cover art and figures.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. JPEG files</a></li><li><a href="#SP2">&#167;2. PNG files</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. JPEG files. </b>The following code, contributed by Toby Nelson, either finds the pixel width
and height of a given JPEG file and returns <code class="display"><span class="extract">TRUE</span></code> or, if it can't read the
file or doesn't recognise the header as having JPEG format, returns <code class="display"><span class="extract">FALSE</span></code>.
</p>
<p class="inwebparagraph">JPEG is properly speaking not a file format but a compression technique:
the routine below works with either JIF (JPEG Interchange Format) or its
simpler cousin JFIF (JPEG File Interchange Format).
</p>
<p class="inwebparagraph">We scan the file looking for "markers", each of which begins with an
<code class="display"><span class="extract">0xFF</span></code> byte and is followed by a marker-type byte which is neither <code class="display"><span class="extract">0x00</span></code>
nor <code class="display"><span class="extract">0xFF</span></code>. The compulsory marker SOI must appear at the start of the file,
providing one way to detect probable JPEGs by looking at the first two
bytes. There must also eventually be a start of frame marker, for the
actual image: this can have many forms, but in all cases tells us the
height and width.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">ImageFiles::get_JPEG_dimensions</span><span class="plain">(</span><span class="reserved">FILE</span><span class="plain"> *</span><span class="identifier">JPEG_file</span><span class="plain">, </span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *</span><span class="identifier">width</span><span class="plain">, </span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *</span><span class="identifier">height</span><span class="plain">) {</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">sig</span><span class="plain">, </span><span class="identifier">length</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">marker</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int16</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">, &amp;</span><span class="identifier">sig</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">sig</span><span class="plain"> != </span><span class="constant">0xFFD8</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">; </span><span class="comment"> <code class="display"><span class="extract">0xFF</span></code> (marker) then <code class="display"><span class="extract">0xD8</span></code> (SOI)</span>
<span class="reserved">do</span><span class="plain"> {</span>
<span class="reserved">do</span><span class="plain"> {</span>
<span class="identifier">marker</span><span class="plain"> = </span><span class="identifier">getc</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">marker</span><span class="plain"> == </span><span class="identifier">EOF</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">while</span><span class="plain"> (</span><span class="identifier">marker</span><span class="plain"> != </span><span class="constant">0xff</span><span class="plain">); </span><span class="comment"> skip to next <code class="display"><span class="extract">0xFF</span></code> byte</span>
<span class="reserved">do</span><span class="plain"> {</span>
<span class="identifier">marker</span><span class="plain"> = </span><span class="identifier">getc</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">while</span><span class="plain"> (</span><span class="identifier">marker</span><span class="plain"> == </span><span class="constant">0xff</span><span class="plain">); </span><span class="comment"> skip to next non <code class="display"><span class="extract">FF</span></code> byte</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int16</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">, &amp;</span><span class="identifier">length</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">; </span><span class="comment"> length of marker</span>
<span class="reserved">switch</span><span class="plain">(</span><span class="identifier">marker</span><span class="plain">) {</span>
<span class="comment"> all variant forms of "start of frame": e.g., <code class="display"><span class="extract">0xC0</span></code> is a baseline DCT image</span>
<span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc0</span><span class="identifier">:</span>
<span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc1</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc2</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc3</span><span class="identifier">:</span>
<span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc5</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc6</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc7</span><span class="identifier">:</span>
<span class="reserved">case</span><span class="plain"> </span><span class="constant">0xc9</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xca</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xcb</span><span class="identifier">:</span>
<span class="reserved">case</span><span class="plain"> </span><span class="constant">0xcd</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xce</span><span class="identifier">:</span><span class="plain"> </span><span class="reserved">case</span><span class="plain"> </span><span class="constant">0xcf</span><span class="identifier">:</span><span class="plain"> {</span>
<span class="comment"> fortunately these markers all then open with the same format</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">getc</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">) == </span><span class="identifier">EOF</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">; </span><span class="comment"> skip 1 byte of data precision</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int16</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">, </span><span class="identifier">height</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int16</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">, </span><span class="identifier">width</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">default:</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">fseek</span><span class="plain">(</span><span class="identifier">JPEG_file</span><span class="plain">, (</span><span class="reserved">long</span><span class="plain">) (</span><span class="identifier">length</span><span class="plain"> - </span><span class="constant">2</span><span class="plain">), </span><span class="identifier">SEEK_CUR</span><span class="plain">) != </span><span class="constant">0</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">; </span><span class="comment"> skip rest of marker</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">marker</span><span class="plain"> != </span><span class="identifier">EOF</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function ImageFiles::get_JPEG_dimensions appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. PNG files. </b>The PNG file must start with a signature which indicates that the
remainder contains a single PNG image, consisting of a series of chunks
beginning with an IHDR chunk and ending with an IEND chunk ("Portable
Network Graphics (PNG) Specification", 2nd edition, section 5.2). We only
need to scan the IHDR chunk, of which the pixel width and height are the
first two words (section 11.2.2).
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">ImageFiles::get_PNG_dimensions</span><span class="plain">(</span><span class="reserved">FILE</span><span class="plain"> *</span><span class="identifier">PNG_file</span><span class="plain">, </span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *</span><span class="identifier">width</span><span class="plain">, </span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> *</span><span class="identifier">height</span><span class="plain">) {</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">sig1</span><span class="plain">, </span><span class="identifier">sig2</span><span class="plain">, </span><span class="identifier">length</span><span class="plain">, </span><span class="identifier">type</span><span class="plain">;</span>
<span class="comment"> Check PNG signature</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, &amp;</span><span class="identifier">sig1</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, &amp;</span><span class="identifier">sig2</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">sig1</span><span class="plain"> != </span><span class="constant">0x89504e47</span><span class="plain">) || (</span><span class="identifier">sig2</span><span class="plain"> != </span><span class="constant">0x0d0a1a0a</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="comment"> Read first chunk</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, &amp;</span><span class="identifier">length</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, &amp;</span><span class="identifier">type</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="comment"> First chunk must be IHDR</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">type</span><span class="plain"> != </span><span class="constant">0x49484452</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="comment"> Width and height follow</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, </span><span class="identifier">width</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (!</span><span class="functiontext">BinaryFiles::read_int32</span><span class="plain">(</span><span class="identifier">PNG_file</span><span class="plain">, </span><span class="identifier">height</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function ImageFiles::get_PNG_dimensions appears nowhere else.</p>
<hr class="tocbar">
<ul class="toc"><li><a href="6-bf.html">Back to 'Binary Files'</a></li><li><a href="6-sd.html">Continue with 'Sound Durations'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</main>
</body>
</html>